myl 0.8.13__py3-none-any.whl → 0.9.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {myl-0.8.13.dist-info → myl-0.9.1.dist-info}/METADATA +12 -4
- myl-0.9.1.dist-info/RECORD +7 -0
- myl.py +341 -158
- myl-0.8.13.dist-info/RECORD +0 -7
- {myl-0.8.13.dist-info → myl-0.9.1.dist-info}/LICENSE +0 -0
- {myl-0.8.13.dist-info → myl-0.9.1.dist-info}/WHEEL +0 -0
- {myl-0.8.13.dist-info → myl-0.9.1.dist-info}/entry_points.txt +0 -0
- {myl-0.8.13.dist-info → myl-0.9.1.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: myl
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.9.1
|
4
4
|
Summary: Dead simple IMAP CLI client
|
5
5
|
Author-email: Philipp Schmitt <philipp@schmitt.co>
|
6
6
|
License: GNU GENERAL PUBLIC LICENSE
|
@@ -721,15 +721,23 @@ straightforward way to interact with IMAP servers.
|
|
721
721
|
|
722
722
|
To install myl, follow these steps:
|
723
723
|
|
724
|
-
```
|
724
|
+
```shell
|
725
725
|
pipx install myl
|
726
|
+
# or:
|
727
|
+
pip install --user myl
|
728
|
+
```
|
729
|
+
|
730
|
+
on nix you can do this:
|
731
|
+
|
732
|
+
```shell
|
733
|
+
nix run github.com:pschmitt/myl
|
726
734
|
```
|
727
735
|
|
728
736
|
## 🛠️ Usage
|
729
737
|
|
730
738
|
Here's how you can use myl:
|
731
739
|
|
732
|
-
```
|
740
|
+
```shell
|
733
741
|
myl --help
|
734
742
|
```
|
735
743
|
|
@@ -737,7 +745,7 @@ This command will display the help information for the `myl` command.
|
|
737
745
|
|
738
746
|
Here are some examples of using flags with the `myl` command:
|
739
747
|
|
740
|
-
```
|
748
|
+
```shell
|
741
749
|
# Connect to an IMAP server
|
742
750
|
myl --server imap.example.com --port 143 --starttls --username "$username" --password "$password"
|
743
751
|
|
@@ -0,0 +1,7 @@
|
|
1
|
+
myl.py,sha256=3zCtNscTGN8MDDhVdgL6JWttpRI1FYTj6lHXtMtX1ec,13953
|
2
|
+
myl-0.9.1.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
3
|
+
myl-0.9.1.dist-info/METADATA,sha256=9Orxf5-uV24nLZTPVwFevbKRaYtmRH25yLlip5NZpFo,43420
|
4
|
+
myl-0.9.1.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
|
5
|
+
myl-0.9.1.dist-info/entry_points.txt,sha256=q6nr0Kzim7JzreXQE3BTU4asLh2sx5-D0w1yLBOcHxc,33
|
6
|
+
myl-0.9.1.dist-info/top_level.txt,sha256=Wn88OJVVWyYSsKVoqzlHXxfFxh5IbrJ_Yw-ldNLe7Po,4
|
7
|
+
myl-0.9.1.dist-info/RECORD,,
|
myl.py
CHANGED
@@ -1,20 +1,33 @@
|
|
1
1
|
#!/usr/bin/env python3
|
2
2
|
# coding: utf-8
|
3
3
|
|
4
|
+
from importlib.metadata import version, PackageNotFoundError
|
4
5
|
import argparse
|
5
|
-
import html2text
|
6
|
-
import json
|
7
6
|
import logging
|
8
7
|
import ssl
|
9
8
|
import sys
|
9
|
+
from json import dumps as json_dumps
|
10
10
|
|
11
|
-
import
|
11
|
+
import html2text
|
12
|
+
from imap_tools.consts import MailMessageFlags
|
13
|
+
from imap_tools.mailbox import (
|
14
|
+
BaseMailBox,
|
15
|
+
MailBox,
|
16
|
+
MailBoxTls,
|
17
|
+
MailBoxUnencrypted,
|
18
|
+
)
|
19
|
+
from imap_tools.query import AND
|
12
20
|
from myldiscovery import autodiscover
|
13
21
|
from rich import print, print_json
|
14
22
|
from rich.console import Console
|
15
23
|
from rich.logging import RichHandler
|
16
24
|
from rich.table import Table
|
17
25
|
|
26
|
+
try:
|
27
|
+
__version__ = version("myl")
|
28
|
+
except PackageNotFoundError:
|
29
|
+
pass
|
30
|
+
|
18
31
|
LOGGER = logging.getLogger(__name__)
|
19
32
|
IMAP_PORT = 993
|
20
33
|
GMAIL_IMAP_SERVER = "imap.gmail.com"
|
@@ -23,13 +36,94 @@ GMAIL_SENT_FOLDER = "[Gmail]/Sent Mail"
|
|
23
36
|
# GMAIL_ALL_FOLDER = "[Gmail]/All Mail"
|
24
37
|
|
25
38
|
|
39
|
+
class MissingServerException(Exception):
|
40
|
+
pass
|
41
|
+
|
42
|
+
|
26
43
|
def error_msg(msg):
|
27
44
|
print(f"[red]{msg}[/red]", file=sys.stderr)
|
28
45
|
|
29
46
|
|
47
|
+
def mail_to_dict(msg, date_format="%Y-%m-%d %H:%M:%S"):
|
48
|
+
return {
|
49
|
+
"uid": msg.uid,
|
50
|
+
"subject": msg.subject,
|
51
|
+
"from": msg.from_,
|
52
|
+
"to": msg.to,
|
53
|
+
"date": msg.date.strftime(date_format),
|
54
|
+
"timestamp": str(int(msg.date.timestamp())),
|
55
|
+
"unread": mail_is_unread(msg),
|
56
|
+
"flags": msg.flags,
|
57
|
+
"content": {
|
58
|
+
"raw": msg.obj.as_string(),
|
59
|
+
"html": msg.html,
|
60
|
+
"text": msg.text,
|
61
|
+
},
|
62
|
+
"attachments": msg.attachments,
|
63
|
+
}
|
64
|
+
|
65
|
+
|
66
|
+
def mail_to_json(msg, date_format="%Y-%m-%d %H:%M:%S"):
|
67
|
+
return json_dumps(mail_to_dict(msg, date_format))
|
68
|
+
|
69
|
+
|
70
|
+
def mail_is_unread(msg):
|
71
|
+
return MailMessageFlags.SEEN not in msg.flags
|
72
|
+
|
73
|
+
|
30
74
|
def parse_args():
|
31
75
|
parser = argparse.ArgumentParser()
|
32
|
-
parser.
|
76
|
+
subparsers = parser.add_subparsers(
|
77
|
+
dest="command", help="Available commands"
|
78
|
+
)
|
79
|
+
parser.add_argument(
|
80
|
+
"-V",
|
81
|
+
"--version",
|
82
|
+
action="version",
|
83
|
+
version=f"%(prog)s {__version__}",
|
84
|
+
)
|
85
|
+
|
86
|
+
# Default command: list all emails
|
87
|
+
subparsers.add_parser("list", help="List all emails")
|
88
|
+
|
89
|
+
# Get/show email command
|
90
|
+
get_parser = subparsers.add_parser(
|
91
|
+
"get", help="Retrieve a specific email or attachment"
|
92
|
+
)
|
93
|
+
get_parser.add_argument("MAILID", help="Mail ID to fetch", type=int)
|
94
|
+
get_parser.add_argument(
|
95
|
+
"ATTACHMENT",
|
96
|
+
help="Name of the attachment to fetch",
|
97
|
+
nargs="?",
|
98
|
+
default=None,
|
99
|
+
)
|
100
|
+
|
101
|
+
# Delete email command
|
102
|
+
delete_parser = subparsers.add_parser("delete", help="Delete an email")
|
103
|
+
delete_parser.add_argument(
|
104
|
+
"MAILIDS", help="Mail ID(s) to delete", type=int, nargs="+"
|
105
|
+
)
|
106
|
+
|
107
|
+
# Mark email as read/unread
|
108
|
+
mark_read_parser = subparsers.add_parser(
|
109
|
+
"read", help="mark an email as read"
|
110
|
+
)
|
111
|
+
mark_read_parser.add_argument(
|
112
|
+
"MAILIDS", help="Mail ID(s) to mark as read", type=int, nargs="+"
|
113
|
+
)
|
114
|
+
mark_unread_parser = subparsers.add_parser(
|
115
|
+
"unread", help="mark an email as unread"
|
116
|
+
)
|
117
|
+
mark_unread_parser.add_argument(
|
118
|
+
"MAILIDS", help="Mail ID(s) to mark as unread", type=int, nargs="+"
|
119
|
+
)
|
120
|
+
|
121
|
+
# Optional arguments
|
122
|
+
parser.add_argument(
|
123
|
+
"-d", "--debug", help="Enable debug mode", action="store_true"
|
124
|
+
)
|
125
|
+
|
126
|
+
# IMAP connection settings
|
33
127
|
parser.add_argument(
|
34
128
|
"-s", "--server", help="IMAP server address", required=False
|
35
129
|
)
|
@@ -45,7 +139,7 @@ def parse_args():
|
|
45
139
|
"--auto",
|
46
140
|
help="Autodiscovery of the required server and port",
|
47
141
|
action="store_true",
|
48
|
-
default=
|
142
|
+
default=True,
|
49
143
|
)
|
50
144
|
parser.add_argument(
|
51
145
|
"-P", "--port", help="IMAP server port", default=IMAP_PORT
|
@@ -60,16 +154,8 @@ def parse_args():
|
|
60
154
|
action="store_true",
|
61
155
|
default=False,
|
62
156
|
)
|
63
|
-
|
64
|
-
|
65
|
-
"--count",
|
66
|
-
help="Number of messages to fetch",
|
67
|
-
default=10,
|
68
|
-
type=int,
|
69
|
-
)
|
70
|
-
parser.add_argument(
|
71
|
-
"-m", "--mark-seen", help="Mark seen", action="store_true"
|
72
|
-
)
|
157
|
+
|
158
|
+
# Credentials
|
73
159
|
parser.add_argument(
|
74
160
|
"-u", "--username", help="IMAP username", required=True
|
75
161
|
)
|
@@ -80,9 +166,32 @@ def parse_args():
|
|
80
166
|
help="IMAP password (file path)",
|
81
167
|
type=argparse.FileType("r"),
|
82
168
|
)
|
169
|
+
|
170
|
+
# Display preferences
|
171
|
+
parser.add_argument(
|
172
|
+
"-c",
|
173
|
+
"--count",
|
174
|
+
help="Number of messages to fetch",
|
175
|
+
default=10,
|
176
|
+
type=int,
|
177
|
+
)
|
83
178
|
parser.add_argument(
|
84
179
|
"-t", "--no-title", help="Do not show title", action="store_true"
|
85
180
|
)
|
181
|
+
parser.add_argument(
|
182
|
+
"--date-format", help="Date format", default="%H:%M %d/%m/%Y"
|
183
|
+
)
|
184
|
+
|
185
|
+
# IMAP actions
|
186
|
+
parser.add_argument(
|
187
|
+
"-m",
|
188
|
+
"--mark-seen",
|
189
|
+
help="Mark seen",
|
190
|
+
action="store_true",
|
191
|
+
default=False,
|
192
|
+
)
|
193
|
+
|
194
|
+
# Email filtering
|
86
195
|
parser.add_argument("-f", "--folder", help="IMAP folder", default="INBOX")
|
87
196
|
parser.add_argument(
|
88
197
|
"--sent",
|
@@ -90,7 +199,21 @@ def parse_args():
|
|
90
199
|
action="store_true",
|
91
200
|
)
|
92
201
|
parser.add_argument("-S", "--search", help="Search string", default="ALL")
|
93
|
-
parser.add_argument(
|
202
|
+
parser.add_argument(
|
203
|
+
"--unread",
|
204
|
+
help="Limit to unread emails",
|
205
|
+
action="store_true",
|
206
|
+
default=False,
|
207
|
+
)
|
208
|
+
|
209
|
+
# Output preferences
|
210
|
+
parser.add_argument(
|
211
|
+
"-H",
|
212
|
+
"--html",
|
213
|
+
help="Show HTML email",
|
214
|
+
action="store_true",
|
215
|
+
default=False,
|
216
|
+
)
|
94
217
|
parser.add_argument(
|
95
218
|
"-j",
|
96
219
|
"--json",
|
@@ -105,24 +228,11 @@ def parse_args():
|
|
105
228
|
action="store_true",
|
106
229
|
default=False,
|
107
230
|
)
|
108
|
-
parser.add_argument("MAILID", help="Mail ID to fetch", nargs="?")
|
109
|
-
parser.add_argument(
|
110
|
-
"ATTACHMENT", help="Name of the attachment to fetch", nargs="?"
|
111
|
-
)
|
112
231
|
|
113
232
|
return parser.parse_args()
|
114
233
|
|
115
234
|
|
116
|
-
def
|
117
|
-
console = Console()
|
118
|
-
args = parse_args()
|
119
|
-
logging.basicConfig(
|
120
|
-
format="%(message)s",
|
121
|
-
handlers=[RichHandler(console=console)],
|
122
|
-
level=logging.DEBUG if args.debug else logging.INFO,
|
123
|
-
)
|
124
|
-
LOGGER.debug(args)
|
125
|
-
|
235
|
+
def mb_connect(console, args) -> BaseMailBox:
|
126
236
|
imap_password = args.password or (
|
127
237
|
args.password_file and args.password_file.read()
|
128
238
|
)
|
@@ -143,12 +253,13 @@ def main():
|
|
143
253
|
args.username,
|
144
254
|
password=imap_password,
|
145
255
|
insecure=args.insecure,
|
146
|
-
).get("imap")
|
256
|
+
).get("imap", {})
|
147
257
|
except Exception:
|
148
258
|
error_msg("Failed to autodiscover IMAP settings")
|
149
259
|
if args.debug:
|
150
260
|
console.print_exception(show_locals=True)
|
151
|
-
|
261
|
+
raise
|
262
|
+
|
152
263
|
LOGGER.debug(f"Discovered settings: {settings})")
|
153
264
|
args.server = settings.get("server")
|
154
265
|
args.port = settings.get("port", IMAP_PORT)
|
@@ -166,11 +277,87 @@ def main():
|
|
166
277
|
"- set --google if you are using a Gmail account\n"
|
167
278
|
"- use --auto to attempt autodiscovery"
|
168
279
|
)
|
169
|
-
|
280
|
+
raise MissingServerException()
|
281
|
+
|
282
|
+
ssl_context = ssl.create_default_context()
|
283
|
+
if args.insecure:
|
284
|
+
ssl_context.check_hostname = False
|
285
|
+
ssl_context.verify_mode = ssl.CERT_NONE
|
286
|
+
|
287
|
+
mb_kwargs = {"host": args.server, "port": args.port}
|
288
|
+
if args.ssl:
|
289
|
+
mb = MailBox
|
290
|
+
mb_kwargs["ssl_context"] = ssl_context
|
291
|
+
elif args.starttls:
|
292
|
+
mb = MailBoxTls
|
293
|
+
mb_kwargs["ssl_context"] = ssl_context
|
294
|
+
else:
|
295
|
+
mb = MailBoxUnencrypted
|
296
|
+
|
297
|
+
mailbox = mb(**mb_kwargs)
|
298
|
+
mailbox.login(args.username, imap_password, args.folder)
|
299
|
+
return mailbox
|
300
|
+
|
170
301
|
|
302
|
+
def display_single_mail(
|
303
|
+
mailbox: BaseMailBox,
|
304
|
+
mail_id: int,
|
305
|
+
attachment: str | None = None,
|
306
|
+
mark_seen: bool = False,
|
307
|
+
raw: bool = False,
|
308
|
+
html: bool = False,
|
309
|
+
json: bool = False,
|
310
|
+
):
|
311
|
+
LOGGER.debug("Fetch mail %s", mail_id)
|
312
|
+
msg = next(mailbox.fetch(f"UID {mail_id}", mark_seen=mark_seen))
|
313
|
+
LOGGER.debug("Fetched mail %s", msg)
|
314
|
+
|
315
|
+
if attachment:
|
316
|
+
for att in msg.attachments:
|
317
|
+
if att.filename == attachment:
|
318
|
+
sys.stdout.buffer.write(att.payload)
|
319
|
+
return 0
|
320
|
+
print(
|
321
|
+
f"attachment {attachment} not found",
|
322
|
+
file=sys.stderr,
|
323
|
+
)
|
324
|
+
return 1
|
325
|
+
|
326
|
+
if html:
|
327
|
+
output = msg.text
|
328
|
+
if raw:
|
329
|
+
output = msg.html
|
330
|
+
else:
|
331
|
+
output = html2text.html2text(msg.html)
|
332
|
+
print(output)
|
333
|
+
elif raw:
|
334
|
+
print(msg.obj.as_string())
|
335
|
+
return 0
|
336
|
+
elif json:
|
337
|
+
print_json(mail_to_json(msg))
|
338
|
+
return 0
|
339
|
+
else:
|
340
|
+
print(msg.text)
|
341
|
+
|
342
|
+
for att in msg.attachments:
|
343
|
+
print(f"📎 Attachment: {att.filename}", file=sys.stderr)
|
344
|
+
return 0
|
345
|
+
|
346
|
+
|
347
|
+
def display_emails(
|
348
|
+
mailbox,
|
349
|
+
console,
|
350
|
+
no_title=False,
|
351
|
+
search="ALL",
|
352
|
+
unread_only=False,
|
353
|
+
count=10,
|
354
|
+
mark_seen=False,
|
355
|
+
json=False,
|
356
|
+
date_format="%H:%M %d/%m/%Y",
|
357
|
+
):
|
171
358
|
json_data = []
|
172
359
|
table = Table(
|
173
|
-
show_header=not
|
360
|
+
show_header=not no_title,
|
174
361
|
header_style="bold",
|
175
362
|
expand=True,
|
176
363
|
show_lines=False,
|
@@ -184,138 +371,134 @@ def main():
|
|
184
371
|
table.add_column("From", style="blue", no_wrap=True, ratio=2)
|
185
372
|
table.add_column("Date", style="cyan", no_wrap=True)
|
186
373
|
|
187
|
-
|
188
|
-
|
189
|
-
ssl_context.check_hostname = False
|
190
|
-
ssl_context.verify_mode = ssl.CERT_NONE
|
374
|
+
if unread_only:
|
375
|
+
search = AND(seen=False)
|
191
376
|
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
377
|
+
for msg in mailbox.fetch(
|
378
|
+
criteria=search,
|
379
|
+
reverse=True,
|
380
|
+
bulk=True,
|
381
|
+
limit=count,
|
382
|
+
mark_seen=mark_seen,
|
383
|
+
headers_only=False, # required for attachments
|
384
|
+
):
|
385
|
+
subj_prefix = "🆕 " if mail_is_unread(msg) else ""
|
386
|
+
subj_prefix += "📎 " if len(msg.attachments) > 0 else ""
|
387
|
+
subject = (
|
388
|
+
msg.subject.replace("\n", "") if msg.subject else "<no-subject>"
|
389
|
+
)
|
390
|
+
if json:
|
391
|
+
json_data.append(mail_to_dict(msg))
|
392
|
+
else:
|
393
|
+
table.add_row(
|
394
|
+
msg.uid if msg.uid else "???",
|
395
|
+
f"{subj_prefix}{subject}",
|
396
|
+
msg.from_,
|
397
|
+
(msg.date.strftime(date_format) if msg.date else "???"),
|
398
|
+
)
|
399
|
+
if table.row_count >= count:
|
400
|
+
break
|
401
|
+
|
402
|
+
if json:
|
403
|
+
print_json(json_dumps(json_data))
|
199
404
|
else:
|
200
|
-
|
405
|
+
console.print(table)
|
406
|
+
if table.row_count == 0:
|
407
|
+
print(
|
408
|
+
"[yellow italic]No messages[/yellow italic]",
|
409
|
+
file=sys.stderr,
|
410
|
+
)
|
411
|
+
return 0
|
412
|
+
|
413
|
+
|
414
|
+
def delete_emails(mailbox: BaseMailBox, mail_ids: list):
|
415
|
+
LOGGER.warning("Deleting mails %s", mail_ids)
|
416
|
+
mailbox.delete([str(x) for x in mail_ids])
|
417
|
+
return 0
|
418
|
+
|
419
|
+
|
420
|
+
def set_seen(mailbox: BaseMailBox, mail_ids: list, value=True):
|
421
|
+
LOGGER.info(
|
422
|
+
"Marking mails as %s: %s", "read" if value else "unread", mail_ids
|
423
|
+
)
|
424
|
+
mailbox.flag(
|
425
|
+
[str(x) for x in mail_ids],
|
426
|
+
flag_set=(MailMessageFlags.SEEN),
|
427
|
+
value=value,
|
428
|
+
)
|
429
|
+
return 0
|
430
|
+
|
431
|
+
|
432
|
+
def mark_read(mailbox: BaseMailBox, mail_ids: list):
|
433
|
+
return set_seen(mailbox, mail_ids, value=True)
|
434
|
+
|
435
|
+
|
436
|
+
def mark_unread(mailbox: BaseMailBox, mail_ids: list):
|
437
|
+
return set_seen(mailbox, mail_ids, value=False)
|
438
|
+
|
439
|
+
|
440
|
+
def main() -> int:
|
441
|
+
console = Console()
|
442
|
+
args = parse_args()
|
443
|
+
logging.basicConfig(
|
444
|
+
format="%(message)s",
|
445
|
+
handlers=[RichHandler(console=console)],
|
446
|
+
level=logging.DEBUG if args.debug else logging.INFO,
|
447
|
+
)
|
448
|
+
LOGGER.debug(args)
|
201
449
|
|
202
450
|
try:
|
203
|
-
with
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
451
|
+
with mb_connect(console, args) as mailbox:
|
452
|
+
# inbox display
|
453
|
+
if args.command in ["list", None]:
|
454
|
+
return display_emails(
|
455
|
+
mailbox=mailbox,
|
456
|
+
console=console,
|
457
|
+
no_title=args.no_title,
|
458
|
+
search=args.search,
|
459
|
+
unread_only=args.unread,
|
460
|
+
count=args.count,
|
461
|
+
mark_seen=args.mark_seen,
|
462
|
+
json=args.json,
|
463
|
+
date_format=args.date_format,
|
211
464
|
)
|
212
|
-
if args.ATTACHMENT:
|
213
|
-
for att in msg.attachments:
|
214
|
-
if att.filename == args.ATTACHMENT:
|
215
|
-
sys.stdout.buffer.write(att.payload)
|
216
|
-
return 0
|
217
|
-
print(
|
218
|
-
f"Attachment {args.ATTACHMENT} not found",
|
219
|
-
file=sys.stderr,
|
220
|
-
)
|
221
|
-
return 1
|
222
|
-
else:
|
223
|
-
if args.raw:
|
224
|
-
print(msg.obj.as_string())
|
225
|
-
return 0
|
226
|
-
elif args.json:
|
227
|
-
print_json(
|
228
|
-
json.dumps(
|
229
|
-
{
|
230
|
-
"uid": msg.uid,
|
231
|
-
"subject": msg.subject,
|
232
|
-
"from": msg.from_,
|
233
|
-
"to": msg.to,
|
234
|
-
"date": msg.date.strftime(
|
235
|
-
"%Y-%m-%d %H:%M:%S"
|
236
|
-
),
|
237
|
-
"timestamp": str(
|
238
|
-
int(msg.date.timestamp())
|
239
|
-
),
|
240
|
-
"content": {
|
241
|
-
"raw": msg.obj.as_string(),
|
242
|
-
"html": msg.html,
|
243
|
-
"text": msg.text,
|
244
|
-
},
|
245
|
-
"attachments": msg.attachments,
|
246
|
-
}
|
247
|
-
)
|
248
|
-
)
|
249
|
-
return 0
|
250
|
-
|
251
|
-
output = msg.text
|
252
|
-
if args.html:
|
253
|
-
if args.raw:
|
254
|
-
output = msg.html
|
255
|
-
else:
|
256
|
-
output = html2text.html2text(msg.html)
|
257
|
-
print(output)
|
258
|
-
for att in msg.attachments:
|
259
|
-
print(
|
260
|
-
f"📎 Attachment: {att.filename}", file=sys.stderr
|
261
|
-
)
|
262
|
-
return 0
|
263
465
|
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
if msg.subject
|
276
|
-
else "<no-subject>"
|
466
|
+
# single email
|
467
|
+
# FIXME $ myl 219 raises an argparse error
|
468
|
+
elif args.command in ["get", "show", "display"]:
|
469
|
+
return display_single_mail(
|
470
|
+
mailbox=mailbox,
|
471
|
+
mail_id=args.MAILID,
|
472
|
+
attachment=args.ATTACHMENT,
|
473
|
+
mark_seen=args.mark_seen,
|
474
|
+
raw=args.raw,
|
475
|
+
html=args.html,
|
476
|
+
json=args.json,
|
277
477
|
)
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
"to": msg.to,
|
285
|
-
"date": msg.date.strftime("%Y-%m-%d %H:%M:%S"),
|
286
|
-
"timestamp": str(int(msg.date.timestamp())),
|
287
|
-
"content": {
|
288
|
-
"raw": msg.obj.as_string(),
|
289
|
-
"html": msg.html,
|
290
|
-
"text": msg.text,
|
291
|
-
},
|
292
|
-
"attachments": msg.attachments,
|
293
|
-
}
|
294
|
-
)
|
295
|
-
else:
|
296
|
-
table.add_row(
|
297
|
-
msg.uid if msg.uid else "???",
|
298
|
-
f"{subj_prefix}{subject}",
|
299
|
-
msg.from_,
|
300
|
-
(
|
301
|
-
msg.date.strftime("%H:%M %d/%m/%Y")
|
302
|
-
if msg.date
|
303
|
-
else "???"
|
304
|
-
),
|
305
|
-
)
|
306
|
-
if table.row_count >= args.count:
|
307
|
-
break
|
308
|
-
|
309
|
-
if args.json:
|
310
|
-
print_json(json.dumps(json_data))
|
311
|
-
else:
|
312
|
-
console.print(table)
|
313
|
-
if table.row_count == 0:
|
314
|
-
print(
|
315
|
-
"[yellow italic]No messages[/yellow italic]",
|
316
|
-
file=sys.stderr,
|
478
|
+
|
479
|
+
# mark emails as read
|
480
|
+
elif args.command in ["read"]:
|
481
|
+
return mark_read(
|
482
|
+
mailbox=mailbox,
|
483
|
+
mail_ids=args.MAILIDS,
|
317
484
|
)
|
318
|
-
|
485
|
+
|
486
|
+
elif args.command in ["unread"]:
|
487
|
+
return mark_unread(
|
488
|
+
mailbox=mailbox,
|
489
|
+
mail_ids=args.MAILIDS,
|
490
|
+
)
|
491
|
+
|
492
|
+
# delete email
|
493
|
+
elif args.command in ["delete", "remove"]:
|
494
|
+
return delete_emails(
|
495
|
+
mailbox=mailbox,
|
496
|
+
mail_ids=args.MAILIDS,
|
497
|
+
)
|
498
|
+
else:
|
499
|
+
error_msg(f"Unknown command: {args.command}")
|
500
|
+
return 1
|
501
|
+
|
319
502
|
except Exception:
|
320
503
|
console.print_exception(show_locals=True)
|
321
504
|
return 1
|
myl-0.8.13.dist-info/RECORD
DELETED
@@ -1,7 +0,0 @@
|
|
1
|
-
myl.py,sha256=LWpXsRzp4TJDlZo9FmvU05MapAOymm5zbDoTIM2r0JM,10715
|
2
|
-
myl-0.8.13.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
3
|
-
myl-0.8.13.dist-info/METADATA,sha256=DWSnwsMfGtJlBJxGCGXcxC6czJFQjOHCoGBGHo3qFwM,43318
|
4
|
-
myl-0.8.13.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
|
5
|
-
myl-0.8.13.dist-info/entry_points.txt,sha256=q6nr0Kzim7JzreXQE3BTU4asLh2sx5-D0w1yLBOcHxc,33
|
6
|
-
myl-0.8.13.dist-info/top_level.txt,sha256=Wn88OJVVWyYSsKVoqzlHXxfFxh5IbrJ_Yw-ldNLe7Po,4
|
7
|
-
myl-0.8.13.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|