Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
Memri
simplegmail
Commits
9d8f48f9
Commit
9d8f48f9
authored
1 year ago
by
Aziz Berkay Yesilyurt
Browse files
Options
Download
Email Patches
Plain Diff
formatting
parent
d9e3975d
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
simplegmail/__init__.py
+1
-1
simplegmail/__init__.py
simplegmail/attachment.py
+26
-26
simplegmail/attachment.py
simplegmail/label.py
+16
-16
simplegmail/label.py
simplegmail/message.py
+50
-32
simplegmail/message.py
simplegmail/query.py
+37
-42
simplegmail/query.py
with
130 additions
and
117 deletions
+130
-117
simplegmail/__init__.py
+
1
-
1
View file @
9d8f48f9
...
...
@@ -2,4 +2,4 @@ from simplegmail.gmail import Gmail
from
simplegmail
import
query
from
simplegmail
import
label
__all__
=
[
'
Gmail
'
,
'
query
'
,
'
label
'
]
__all__
=
[
"
Gmail
"
,
"
query
"
,
"
label
"
]
This diff is collapsed.
Click to expand it.
simplegmail/attachment.py
+
26
-
26
View file @
9d8f48f9
...
...
@@ -6,12 +6,13 @@ This module contains the implementation of the Attachment object.
"""
import
base64
# for base64.urlsafe_b64decode
import
os
# for os.path.exists
import
os
# for os.path.exists
from
typing
import
Optional
class
Attachment
(
object
):
"""
The Attachment class for attachments to emails in your Gmail mailbox. This
The Attachment class for attachments to emails in your Gmail mailbox. This
class should not be manually instantiated.
Args:
...
...
@@ -33,16 +34,16 @@ class Attachment(object):
data (bytes): The raw data of the file.
"""
def
__init__
(
self
,
service
:
'
googleapiclient.discovery.Resource
'
,
service
:
"
googleapiclient.discovery.Resource
"
,
user_id
:
str
,
msg_id
:
str
,
att_id
:
str
,
filename
:
str
,
filetype
:
str
,
data
:
Optional
[
bytes
]
=
None
data
:
Optional
[
bytes
]
=
None
,
)
->
None
:
self
.
_service
=
service
self
.
user_id
=
user_id
...
...
@@ -55,42 +56,42 @@ class Attachment(object):
def
download
(
self
)
->
None
:
"""
Downloads the data for an attachment if it does not exist.
Raises:
googleapiclient.errors.HttpError: There was an error executing the
googleapiclient.errors.HttpError: There was an error executing the
HTTP request.
"""
if
self
.
data
is
not
None
:
return
res
=
self
.
_service
.
users
().
messages
().
attachments
().
get
(
userId
=
self
.
user_id
,
messageId
=
self
.
msg_id
,
id
=
self
.
id
).
execute
()
res
=
(
self
.
_service
.
users
()
.
messages
()
.
attachments
()
.
get
(
userId
=
self
.
user_id
,
messageId
=
self
.
msg_id
,
id
=
self
.
id
)
.
execute
()
)
data
=
res
[
'
data
'
]
data
=
res
[
"
data
"
]
self
.
data
=
base64
.
urlsafe_b64decode
(
data
)
def
save
(
self
,
filepath
:
Optional
[
str
]
=
None
,
overwrite
:
bool
=
False
)
->
None
:
def
save
(
self
,
filepath
:
Optional
[
str
]
=
None
,
overwrite
:
bool
=
False
)
->
None
:
"""
Saves the attachment. Downloads file data if not downloaded.
Args:
filepath: where to save the attachment. Default None, which uses
filepath: where to save the attachment. Default None, which uses
the filename stored.
overwrite: whether to overwrite existing files. Default False.
Raises:
FileExistsError: if the call would overwrite an existing file and
FileExistsError: if the call would overwrite an existing file and
overwrite is not set to True.
"""
if
filepath
is
None
:
filepath
=
self
.
filename
...
...
@@ -103,6 +104,5 @@ class Attachment(object):
f
"you would like to overwrite the file."
)
with
open
(
filepath
,
'
wb
'
)
as
f
:
with
open
(
filepath
,
"
wb
"
)
as
f
:
f
.
write
(
self
.
data
)
This diff is collapsed.
Click to expand it.
simplegmail/label.py
+
16
-
16
View file @
9d8f48f9
...
...
@@ -10,9 +10,9 @@ class Label:
"""
A Gmail label object.
This class should not typically be constructed directly but rather returned
This class should not typically be constructed directly but rather returned
from Gmail.list_labels().
Args:
name: The name of the Label.
id: The ID of the label.
...
...
@@ -28,7 +28,7 @@ class Label:
self
.
id
=
id
def
__repr__
(
self
)
->
str
:
return
f
'
Label(name=
{
self
.
name
!r}
, id=
{
self
.
id
!r}
)
'
return
f
"
Label(name=
{
self
.
name
!r}
, id=
{
self
.
id
!r}
)
"
def
__str__
(
self
)
->
str
:
return
self
.
name
...
...
@@ -46,16 +46,16 @@ class Label:
return
False
INBOX
=
Label
(
'
INBOX
'
,
'
INBOX
'
)
SPAM
=
Label
(
'
SPAM
'
,
'
SPAM
'
)
TRASH
=
Label
(
'
TRASH
'
,
'
TRASH
'
)
UNREAD
=
Label
(
'
UNREAD
'
,
'
UNREAD
'
)
STARRED
=
Label
(
'
STARRED
'
,
'
STARRED
'
)
SENT
=
Label
(
'
SENT
'
,
'
SENT
'
)
IMPORTANT
=
Label
(
'
IMPORTANT
'
,
'
IMPORTANT
'
)
DRAFT
=
Label
(
'
DRAFT
'
,
'
DRAFT
'
)
PERSONAL
=
Label
(
'
CATEGORY_PERSONAL
'
,
'
CATEGORY_PERSONAL
'
)
SOCIAL
=
Label
(
'
CATEGORY_SOCIAL
'
,
'
CATEGORY_SOCIAL
'
)
PROMOTIONS
=
Label
(
'
CATEGORY_PROMOTIONS
'
,
'
CATEGORY_PROMOTIONS
'
)
UPDATES
=
Label
(
'
CATEGORY_UPDATES
'
,
'
CATEGORY_UPDATES
'
)
FORUMS
=
Label
(
'
CATEGORY_FORUMS
'
,
'
CATEGORY_FORUMS
'
)
INBOX
=
Label
(
"
INBOX
"
,
"
INBOX
"
)
SPAM
=
Label
(
"
SPAM
"
,
"
SPAM
"
)
TRASH
=
Label
(
"
TRASH
"
,
"
TRASH
"
)
UNREAD
=
Label
(
"
UNREAD
"
,
"
UNREAD
"
)
STARRED
=
Label
(
"
STARRED
"
,
"
STARRED
"
)
SENT
=
Label
(
"
SENT
"
,
"
SENT
"
)
IMPORTANT
=
Label
(
"
IMPORTANT
"
,
"
IMPORTANT
"
)
DRAFT
=
Label
(
"
DRAFT
"
,
"
DRAFT
"
)
PERSONAL
=
Label
(
"
CATEGORY_PERSONAL
"
,
"
CATEGORY_PERSONAL
"
)
SOCIAL
=
Label
(
"
CATEGORY_SOCIAL
"
,
"
CATEGORY_SOCIAL
"
)
PROMOTIONS
=
Label
(
"
CATEGORY_PROMOTIONS
"
,
"
CATEGORY_PROMOTIONS
"
)
UPDATES
=
Label
(
"
CATEGORY_UPDATES
"
,
"
CATEGORY_UPDATES
"
)
FORUMS
=
Label
(
"
CATEGORY_FORUMS
"
,
"
CATEGORY_FORUMS
"
)
This diff is collapsed.
Click to expand it.
simplegmail/message.py
+
50
-
32
View file @
9d8f48f9
...
...
@@ -61,8 +61,8 @@ class Message(object):
def
__init__
(
self
,
service
:
'
googleapiclient.discovery.Resource
'
,
creds
:
'
oauth2client.client.OAuth2Credentials
'
,
service
:
"
googleapiclient.discovery.Resource
"
,
creds
:
"
oauth2client.client.OAuth2Credentials
"
,
user_id
:
str
,
msg_id
:
str
,
thread_id
:
str
,
...
...
@@ -77,7 +77,7 @@ class Message(object):
attachments
:
Optional
[
List
[
Attachment
]]
=
None
,
headers
:
Optional
[
dict
]
=
None
,
cc
:
Optional
[
List
[
str
]]
=
None
,
bcc
:
Optional
[
List
[
str
]]
=
None
bcc
:
Optional
[
List
[
str
]]
=
None
,
)
->
None
:
self
.
_service
=
service
self
.
creds
=
creds
...
...
@@ -98,7 +98,7 @@ class Message(object):
self
.
bcc
=
bcc
or
[]
@
property
def
service
(
self
)
->
'
googleapiclient.discovery.Resource
'
:
def
service
(
self
)
->
"
googleapiclient.discovery.Resource
"
:
if
self
.
creds
.
access_token_expired
:
self
.
creds
.
refresh
(
Http
())
...
...
@@ -107,9 +107,7 @@ class Message(object):
def
__repr__
(
self
)
->
str
:
"""Represents the object by its sender, recipient, and id."""
return
(
f
'Message(to:
{
self
.
recipient
}
, from:
{
self
.
sender
}
, id:
{
self
.
id
}
)'
)
return
f
"Message(to:
{
self
.
recipient
}
, from:
{
self
.
sender
}
, id:
{
self
.
id
}
)"
def
mark_as_read
(
self
)
->
None
:
"""
...
...
@@ -238,19 +236,26 @@ class Message(object):
"""
try
:
res
=
self
.
_service
.
users
().
messages
().
trash
(
userId
=
self
.
user_id
,
id
=
self
.
id
,
).
execute
()
res
=
(
self
.
_service
.
users
()
.
messages
()
.
trash
(
userId
=
self
.
user_id
,
id
=
self
.
id
,
)
.
execute
()
)
except
HttpError
as
error
:
# Pass error along
raise
error
else
:
assert
label
.
TRASH
in
res
[
'labelIds'
],
\
f
'An error occurred in a call to `trash`.'
assert
(
label
.
TRASH
in
res
[
"labelIds"
]
),
f
"An error occurred in a call to `trash`."
self
.
label_ids
=
res
[
'
labelIds
'
]
self
.
label_ids
=
res
[
"
labelIds
"
]
def
untrash
(
self
)
->
None
:
"""
...
...
@@ -263,19 +268,26 @@ class Message(object):
"""
try
:
res
=
self
.
_service
.
users
().
messages
().
untrash
(
userId
=
self
.
user_id
,
id
=
self
.
id
,
).
execute
()
res
=
(
self
.
_service
.
users
()
.
messages
()
.
untrash
(
userId
=
self
.
user_id
,
id
=
self
.
id
,
)
.
execute
()
)
except
HttpError
as
error
:
# Pass error along
raise
error
else
:
assert
label
.
TRASH
not
in
res
[
'labelIds'
],
\
f
'An error occurred in a call to `untrash`.'
assert
(
label
.
TRASH
not
in
res
[
"labelIds"
]
),
f
"An error occurred in a call to `untrash`."
self
.
label_ids
=
res
[
'
labelIds
'
]
self
.
label_ids
=
res
[
"
labelIds
"
]
def
move_from_inbox
(
self
,
to
:
Union
[
Label
,
str
])
->
None
:
"""
...
...
@@ -355,7 +367,7 @@ class Message(object):
def
modify_labels
(
self
,
to_add
:
Union
[
Label
,
str
,
List
[
Label
],
List
[
str
]],
to_remove
:
Union
[
Label
,
str
,
List
[
Label
],
List
[
str
]]
to_remove
:
Union
[
Label
,
str
,
List
[
Label
],
List
[
str
]]
,
)
->
None
:
"""
Adds or removes the specified label.
...
...
@@ -377,26 +389,32 @@ class Message(object):
to_remove
=
[
to_remove
]
try
:
res
=
self
.
_service
.
users
().
messages
().
modify
(
userId
=
self
.
user_id
,
id
=
self
.
id
,
body
=
self
.
_create_update_labels
(
to_add
,
to_remove
)
).
execute
()
res
=
(
self
.
_service
.
users
()
.
messages
()
.
modify
(
userId
=
self
.
user_id
,
id
=
self
.
id
,
body
=
self
.
_create_update_labels
(
to_add
,
to_remove
),
)
.
execute
()
)
except
HttpError
as
error
:
# Pass along error
raise
error
else
:
assert
all
([
lbl
in
res
[
'
labelIds
'
]
for
lbl
in
to_add
])
\
and
all
(
[
lbl
not
in
res
[
'
labelIds
'
]
for
lbl
in
to_remove
]
),
\
'
An error occurred while modifying message label.
'
assert
all
([
lbl
in
res
[
"
labelIds
"
]
for
lbl
in
to_add
])
and
all
(
[
lbl
not
in
res
[
"
labelIds
"
]
for
lbl
in
to_remove
]
),
"
An error occurred while modifying message label.
"
self
.
label_ids
=
res
[
'
labelIds
'
]
self
.
label_ids
=
res
[
"
labelIds
"
]
def
_create_update_labels
(
self
,
to_add
:
Union
[
List
[
Label
],
List
[
str
]]
=
None
,
to_remove
:
Union
[
List
[
Label
],
List
[
str
]]
=
None
to_remove
:
Union
[
List
[
Label
],
List
[
str
]]
=
None
,
)
->
dict
:
"""
Creates an object for updating message label.
...
...
@@ -417,10 +435,10 @@ class Message(object):
to_remove
=
[]
return
{
'
addLabelIds
'
:
[
"
addLabelIds
"
:
[
lbl
.
id
if
isinstance
(
lbl
,
Label
)
else
lbl
for
lbl
in
to_add
],
'
removeLabelIds
'
:
[
"
removeLabelIds
"
:
[
lbl
.
id
if
isinstance
(
lbl
,
Label
)
else
lbl
for
lbl
in
to_remove
]
]
,
}
This diff is collapsed.
Click to expand it.
simplegmail/query.py
+
37
-
42
View file @
9d8f48f9
...
...
@@ -159,20 +159,20 @@ def construct_query(*query_dicts, **query_terms) -> str:
terms
=
[]
for
key
,
val
in
query_terms
.
items
():
exclude
=
False
if
key
.
startswith
(
'
exclude
'
):
if
key
.
startswith
(
"
exclude
"
):
exclude
=
True
key
=
key
[
len
(
'
exclude_
'
)
:]
key
=
key
[
len
(
"
exclude_
"
)
:]
query_fn
=
globals
()[
f
"_
{
key
}
"
]
conjunction
=
_and
if
isinstance
(
val
,
tuple
)
else
_or
if
key
in
[
'
newer_than
'
,
'
older_than
'
,
'
near_words
'
]:
if
key
in
[
"
newer_than
"
,
"
older_than
"
,
"
near_words
"
]:
if
isinstance
(
val
[
0
],
(
tuple
,
list
)):
term
=
conjunction
([
query_fn
(
*
v
)
for
v
in
val
])
else
:
term
=
query_fn
(
*
val
)
elif
key
==
'
labels
'
:
elif
key
==
"
labels
"
:
if
isinstance
(
val
[
0
],
(
tuple
,
list
)):
term
=
conjunction
([
query_fn
(
labels
)
for
labels
in
val
])
else
:
...
...
@@ -225,7 +225,7 @@ def _or(queries: List[str]) -> str:
if
len
(
queries
)
==
1
:
return
queries
[
0
]
return
'{'
+
' '
.
join
(
queries
)
+
'}'
return
"{"
+
" "
.
join
(
queries
)
+
"}"
def
_exclude
(
term
:
str
)
->
str
:
...
...
@@ -240,7 +240,7 @@ def _exclude(term: str) -> str:
"""
return
f
'
-
{
term
}
'
return
f
"
-
{
term
}
"
def
_sender
(
sender
:
str
)
->
str
:
...
...
@@ -255,7 +255,7 @@ def _sender(sender: str) -> str:
"""
return
f
'
from:
{
sender
}
'
return
f
"
from:
{
sender
}
"
def
_recipient
(
recipient
:
str
)
->
str
:
...
...
@@ -270,7 +270,7 @@ def _recipient(recipient: str) -> str:
"""
return
f
'
to:
{
recipient
}
'
return
f
"
to:
{
recipient
}
"
def
_subject
(
subject
:
str
)
->
str
:
...
...
@@ -285,7 +285,7 @@ def _subject(subject: str) -> str:
"""
return
f
'
subject:
{
subject
}
'
return
f
"
subject:
{
subject
}
"
def
_labels
(
labels
:
Union
[
List
[
str
],
str
])
->
str
:
...
...
@@ -320,7 +320,7 @@ def _label(label: str) -> str:
"""
return
f
'
label:
{
label
}
'
return
f
"
label:
{
label
}
"
def
_spec_attachment
(
name_or_type
:
str
)
->
str
:
...
...
@@ -336,7 +336,7 @@ def _spec_attachment(name_or_type: str) -> str:
"""
return
f
'
filename:
{
name_or_type
}
'
return
f
"
filename:
{
name_or_type
}
"
def
_exact_phrase
(
phrase
:
str
)
->
str
:
...
...
@@ -357,31 +357,31 @@ def _exact_phrase(phrase: str) -> str:
def
_starred
()
->
str
:
"""Returns a query term matching messages that are starred."""
return
'
is:starred
'
return
"
is:starred
"
def
_snoozed
()
->
str
:
"""Returns a query term matching messages that are snoozed."""
return
'
is:snoozed
'
return
"
is:snoozed
"
def
_unread
()
->
str
:
"""Returns a query term matching messages that are unread."""
return
'
is:unread
'
return
"
is:unread
"
def
_read
()
->
str
:
"""Returns a query term matching messages that are read."""
return
'
is:read
'
return
"
is:read
"
def
_important
()
->
str
:
"""Returns a query term matching messages that are important."""
return
'
is:important
'
return
"
is:important
"
def
_cc
(
recipient
:
str
)
->
str
:
...
...
@@ -397,7 +397,7 @@ def _cc(recipient: str) -> str:
"""
return
f
'
cc:
{
recipient
}
'
return
f
"
cc:
{
recipient
}
"
def
_bcc
(
recipient
:
str
)
->
str
:
...
...
@@ -413,7 +413,7 @@ def _bcc(recipient: str) -> str:
"""
return
f
'
bcc:
{
recipient
}
'
return
f
"
bcc:
{
recipient
}
"
def
_after
(
date
:
str
)
->
str
:
...
...
@@ -428,7 +428,7 @@ def _after(date: str) -> str:
"""
return
f
'
after:
{
date
}
'
return
f
"
after:
{
date
}
"
def
_before
(
date
:
str
)
->
str
:
...
...
@@ -443,7 +443,7 @@ def _before(date: str) -> str:
"""
return
f
'
before:
{
date
}
'
return
f
"
before:
{
date
}
"
def
_older_than
(
number
:
int
,
unit
:
str
)
->
str
:
...
...
@@ -459,7 +459,7 @@ def _older_than(number: int, unit: str) -> str:
"""
return
f
'
older_than:
{
number
}{
unit
[
0
]
}
'
return
f
"
older_than:
{
number
}{
unit
[
0
]
}
"
def
_newer_than
(
number
:
int
,
unit
:
str
)
->
str
:
...
...
@@ -475,15 +475,10 @@ def _newer_than(number: int, unit: str) -> str:
"""
return
f
'
newer_than:
{
number
}{
unit
[
0
]
}
'
return
f
"
newer_than:
{
number
}{
unit
[
0
]
}
"
def
_near_words
(
first
:
str
,
second
:
str
,
distance
:
int
,
exact
:
bool
=
False
)
->
str
:
def
_near_words
(
first
:
str
,
second
:
str
,
distance
:
int
,
exact
:
bool
=
False
)
->
str
:
"""
Returns a query term matching messages that two words within a certain
distance of each other.
...
...
@@ -499,7 +494,7 @@ def _near_words(
"""
query
=
f
'
{
first
}
AROUND
{
distance
}
{
second
}
'
query
=
f
"
{
first
}
AROUND
{
distance
}
{
second
}
"
if
exact
:
query
=
'"'
+
query
+
'"'
...
...
@@ -509,7 +504,7 @@ def _near_words(
def
_attachment
()
->
str
:
"""Returns a query term matching messages that have attachments."""
return
'
has:attachment
'
return
"
has:attachment
"
def
_drive
()
->
str
:
...
...
@@ -518,7 +513,7 @@ def _drive() -> str:
"""
return
'
has:drive
'
return
"
has:drive
"
def
_docs
()
->
str
:
...
...
@@ -527,7 +522,7 @@ def _docs() -> str:
"""
return
'
has:document
'
return
"
has:document
"
def
_sheets
()
->
str
:
...
...
@@ -536,7 +531,7 @@ def _sheets() -> str:
"""
return
'
has:spreadsheet
'
return
"
has:spreadsheet
"
def
_slides
()
->
str
:
...
...
@@ -545,7 +540,7 @@ def _slides() -> str:
"""
return
'
has:presentation
'
return
"
has:presentation
"
def
_list
(
list_name
:
str
)
->
str
:
...
...
@@ -560,7 +555,7 @@ def _list(list_name: str) -> str:
"""
return
f
'
list:
{
list_name
}
'
return
f
"
list:
{
list_name
}
"
def
_in
(
folder_name
:
str
)
->
str
:
...
...
@@ -575,7 +570,7 @@ def _in(folder_name: str) -> str:
"""
return
f
'
in:
{
folder_name
}
'
return
f
"
in:
{
folder_name
}
"
def
_delivered_to
(
address
:
str
)
->
str
:
...
...
@@ -590,7 +585,7 @@ def _delivered_to(address: str) -> str:
"""
return
f
'
deliveredto:
{
address
}
'
return
f
"
deliveredto:
{
address
}
"
def
_category
(
category
:
str
)
->
str
:
...
...
@@ -605,7 +600,7 @@ def _category(category: str) -> str:
"""
return
f
'
category:
{
category
}
'
return
f
"
category:
{
category
}
"
def
_larger
(
size
:
str
)
->
str
:
...
...
@@ -621,7 +616,7 @@ def _larger(size: str) -> str:
"""
return
f
'
larger:
{
size
}
'
return
f
"
larger:
{
size
}
"
def
_smaller
(
size
:
str
)
->
str
:
...
...
@@ -637,7 +632,7 @@ def _smaller(size: str) -> str:
"""
return
f
'
smaller:
{
size
}
'
return
f
"
smaller:
{
size
}
"
def
_id
(
message_id
:
str
)
->
str
:
...
...
@@ -652,7 +647,7 @@ def _id(message_id: str) -> str:
"""
return
f
'
rfc822msgid:
{
message_id
}
'
return
f
"
rfc822msgid:
{
message_id
}
"
def
_has
(
attribute
:
str
)
->
str
:
...
...
@@ -667,4 +662,4 @@ def _has(attribute: str) -> str:
"""
return
f
'
has:
{
attribute
}
'
return
f
"
has:
{
attribute
}
"
This diff is collapsed.
Click to expand it.
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment