myl 0.8.7__py3-none-any.whl → 0.9.3__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.7.dist-info → myl-0.9.3.dist-info}/METADATA +22 -9
- myl-0.9.3.dist-info/RECORD +7 -0
- {myl-0.8.7.dist-info → myl-0.9.3.dist-info}/WHEEL +1 -1
- myl.py +376 -83
- myl-0.8.7.dist-info/RECORD +0 -7
- {myl-0.8.7.dist-info → myl-0.9.3.dist-info}/LICENSE +0 -0
- {myl-0.8.7.dist-info → myl-0.9.3.dist-info}/entry_points.txt +0 -0
- {myl-0.8.7.dist-info → myl-0.9.3.dist-info}/top_level.txt +0 -0
@@ -1,9 +1,9 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.2
|
2
2
|
Name: myl
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.9.3
|
4
4
|
Summary: Dead simple IMAP CLI client
|
5
5
|
Author-email: Philipp Schmitt <philipp@schmitt.co>
|
6
|
-
License:
|
6
|
+
License: GNU GENERAL PUBLIC LICENSE
|
7
7
|
Version 3, 29 June 2007
|
8
8
|
|
9
9
|
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
@@ -678,14 +678,19 @@ License: GNU GENERAL PUBLIC LICENSE
|
|
678
678
|
Public License instead of this License. But first, please read
|
679
679
|
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
680
680
|
|
681
|
+
Project-URL: homepage, https://github.com/pschmitt/myl
|
682
|
+
Project-URL: documentation, https://github.com/pschmitt/myl/blob/head/readme.md
|
683
|
+
Project-URL: repository, https://github.com/pschmitt/myl
|
684
|
+
Project-URL: issues, https://github.com/pschmitt/myl/issues
|
681
685
|
Keywords: imap,email
|
682
686
|
Classifier: Programming Language :: Python :: 3
|
683
687
|
Requires-Python: >=3.8
|
684
688
|
Description-Content-Type: text/markdown
|
685
689
|
License-File: LICENSE
|
686
|
-
Requires-Dist: imap-tools
|
687
|
-
Requires-Dist: myl-discovery
|
688
|
-
Requires-Dist: rich
|
690
|
+
Requires-Dist: imap-tools<2.0.0,>=1.5.0
|
691
|
+
Requires-Dist: myl-discovery>=0.6.1.dev0
|
692
|
+
Requires-Dist: rich<14.0.0,>=13.0.0
|
693
|
+
Requires-Dist: html2text>=2024.2.26
|
689
694
|
|
690
695
|
# 📧 myl
|
691
696
|
|
@@ -716,15 +721,23 @@ straightforward way to interact with IMAP servers.
|
|
716
721
|
|
717
722
|
To install myl, follow these steps:
|
718
723
|
|
719
|
-
```
|
724
|
+
```shell
|
720
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:pschmitt/myl -- --help
|
721
734
|
```
|
722
735
|
|
723
736
|
## 🛠️ Usage
|
724
737
|
|
725
738
|
Here's how you can use myl:
|
726
739
|
|
727
|
-
```
|
740
|
+
```shell
|
728
741
|
myl --help
|
729
742
|
```
|
730
743
|
|
@@ -732,7 +745,7 @@ This command will display the help information for the `myl` command.
|
|
732
745
|
|
733
746
|
Here are some examples of using flags with the `myl` command:
|
734
747
|
|
735
|
-
```
|
748
|
+
```shell
|
736
749
|
# Connect to an IMAP server
|
737
750
|
myl --server imap.example.com --port 143 --starttls --username "$username" --password "$password"
|
738
751
|
|
@@ -0,0 +1,7 @@
|
|
1
|
+
myl.py,sha256=8CCrgsD1DhmbZycNJk24XLf_hgESRsf6ku1d6DyUOfA,13954
|
2
|
+
myl-0.9.3.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
3
|
+
myl-0.9.3.dist-info/METADATA,sha256=jy8hkerCv6mVFN2Qwv4JoLFE7ZOMoLcKL_zs_cRzTag,43447
|
4
|
+
myl-0.9.3.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
5
|
+
myl-0.9.3.dist-info/entry_points.txt,sha256=q6nr0Kzim7JzreXQE3BTU4asLh2sx5-D0w1yLBOcHxc,33
|
6
|
+
myl-0.9.3.dist-info/top_level.txt,sha256=Wn88OJVVWyYSsKVoqzlHXxfFxh5IbrJ_Yw-ldNLe7Po,4
|
7
|
+
myl-0.9.3.dist-info/RECORD,,
|
myl.py
CHANGED
@@ -1,14 +1,33 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
# coding: utf-8
|
3
|
+
|
4
|
+
from importlib.metadata import version, PackageNotFoundError
|
1
5
|
import argparse
|
2
6
|
import logging
|
7
|
+
import ssl
|
3
8
|
import sys
|
9
|
+
from json import dumps as json_dumps
|
4
10
|
|
5
|
-
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
|
6
20
|
from myldiscovery import autodiscover
|
7
|
-
from rich import print
|
21
|
+
from rich import print, print_json
|
8
22
|
from rich.console import Console
|
9
23
|
from rich.logging import RichHandler
|
10
24
|
from rich.table import Table
|
11
25
|
|
26
|
+
try:
|
27
|
+
__version__ = version("myl")
|
28
|
+
except PackageNotFoundError:
|
29
|
+
pass
|
30
|
+
|
12
31
|
LOGGER = logging.getLogger(__name__)
|
13
32
|
IMAP_PORT = 993
|
14
33
|
GMAIL_IMAP_SERVER = "imap.gmail.com"
|
@@ -17,13 +36,94 @@ GMAIL_SENT_FOLDER = "[Gmail]/Sent Mail"
|
|
17
36
|
# GMAIL_ALL_FOLDER = "[Gmail]/All Mail"
|
18
37
|
|
19
38
|
|
39
|
+
class MissingServerException(Exception):
|
40
|
+
pass
|
41
|
+
|
42
|
+
|
20
43
|
def error_msg(msg):
|
21
44
|
print(f"[red]{msg}[/red]", file=sys.stderr)
|
22
45
|
|
23
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
|
+
|
24
74
|
def parse_args():
|
25
75
|
parser = argparse.ArgumentParser()
|
26
|
-
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
|
27
127
|
parser.add_argument(
|
28
128
|
"-s", "--server", help="IMAP server address", required=False
|
29
129
|
)
|
@@ -44,9 +144,30 @@ def parse_args():
|
|
44
144
|
parser.add_argument(
|
45
145
|
"-P", "--port", help="IMAP server port", default=IMAP_PORT
|
46
146
|
)
|
147
|
+
parser.add_argument("--ssl", help="SSL", action="store_true", default=True)
|
148
|
+
parser.add_argument(
|
149
|
+
"--starttls", help="STARTTLS", action="store_true", default=False
|
150
|
+
)
|
151
|
+
parser.add_argument(
|
152
|
+
"--insecure",
|
153
|
+
help="Disable cert validation",
|
154
|
+
action="store_true",
|
155
|
+
default=False,
|
156
|
+
)
|
157
|
+
|
158
|
+
# Credentials
|
47
159
|
parser.add_argument(
|
48
|
-
"
|
160
|
+
"-u", "--username", help="IMAP username", required=True
|
161
|
+
)
|
162
|
+
password_group = parser.add_mutually_exclusive_group(required=True)
|
163
|
+
password_group.add_argument("-p", "--password", help="IMAP password")
|
164
|
+
password_group.add_argument(
|
165
|
+
"--password-file",
|
166
|
+
help="IMAP password (file path)",
|
167
|
+
type=argparse.FileType("r"),
|
49
168
|
)
|
169
|
+
|
170
|
+
# Display preferences
|
50
171
|
parser.add_argument(
|
51
172
|
"-c",
|
52
173
|
"--count",
|
@@ -55,17 +176,22 @@ def parse_args():
|
|
55
176
|
type=int,
|
56
177
|
)
|
57
178
|
parser.add_argument(
|
58
|
-
"-
|
59
|
-
)
|
60
|
-
parser.add_argument(
|
61
|
-
"-u", "--username", help="IMAP username", required=True
|
179
|
+
"-t", "--no-title", help="Do not show title", action="store_true"
|
62
180
|
)
|
63
181
|
parser.add_argument(
|
64
|
-
"-
|
182
|
+
"--date-format", help="Date format", default="%H:%M %d/%m/%Y"
|
65
183
|
)
|
184
|
+
|
185
|
+
# IMAP actions
|
66
186
|
parser.add_argument(
|
67
|
-
"-
|
187
|
+
"-m",
|
188
|
+
"--mark-seen",
|
189
|
+
help="Mark seen",
|
190
|
+
action="store_true",
|
191
|
+
default=False,
|
68
192
|
)
|
193
|
+
|
194
|
+
# Email filtering
|
69
195
|
parser.add_argument("-f", "--folder", help="IMAP folder", default="INBOX")
|
70
196
|
parser.add_argument(
|
71
197
|
"--sent",
|
@@ -73,8 +199,28 @@ def parse_args():
|
|
73
199
|
action="store_true",
|
74
200
|
)
|
75
201
|
parser.add_argument("-S", "--search", help="Search string", default="ALL")
|
76
|
-
parser.add_argument(
|
77
|
-
|
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
|
+
)
|
217
|
+
parser.add_argument(
|
218
|
+
"-j",
|
219
|
+
"--json",
|
220
|
+
help="JSON output",
|
221
|
+
action="store_true",
|
222
|
+
default=False,
|
223
|
+
)
|
78
224
|
parser.add_argument(
|
79
225
|
"-r",
|
80
226
|
"--raw",
|
@@ -82,23 +228,14 @@ def parse_args():
|
|
82
228
|
action="store_true",
|
83
229
|
default=False,
|
84
230
|
)
|
85
|
-
parser.add_argument("MAILID", help="Mail ID to fetch", nargs="?")
|
86
|
-
parser.add_argument(
|
87
|
-
"ATTACHMENT", help="Name of the attachment to fetch", nargs="?"
|
88
|
-
)
|
89
231
|
|
90
232
|
return parser.parse_args()
|
91
233
|
|
92
234
|
|
93
|
-
def
|
94
|
-
|
95
|
-
|
96
|
-
logging.basicConfig(
|
97
|
-
format="%(message)s",
|
98
|
-
handlers=[RichHandler(console=console)],
|
99
|
-
level=logging.DEBUG if args.debug else logging.INFO,
|
235
|
+
def mb_connect(console, args) -> BaseMailBox:
|
236
|
+
imap_password = args.password or (
|
237
|
+
args.password_file and args.password_file.read()
|
100
238
|
)
|
101
|
-
LOGGER.debug(args)
|
102
239
|
|
103
240
|
if args.google:
|
104
241
|
args.server = GMAIL_IMAP_SERVER
|
@@ -113,17 +250,21 @@ def main():
|
|
113
250
|
if args.auto:
|
114
251
|
try:
|
115
252
|
settings = autodiscover(
|
116
|
-
args.username,
|
117
|
-
|
253
|
+
args.username,
|
254
|
+
password=imap_password,
|
255
|
+
insecure=args.insecure,
|
256
|
+
).get("imap", {})
|
118
257
|
except Exception:
|
119
258
|
error_msg("Failed to autodiscover IMAP settings")
|
120
259
|
if args.debug:
|
121
260
|
console.print_exception(show_locals=True)
|
122
|
-
|
261
|
+
raise
|
262
|
+
|
123
263
|
LOGGER.debug(f"Discovered settings: {settings})")
|
124
264
|
args.server = settings.get("server")
|
125
265
|
args.port = settings.get("port", IMAP_PORT)
|
126
266
|
args.starttls = settings.get("starttls")
|
267
|
+
args.ssl = settings.get("ssl")
|
127
268
|
|
128
269
|
if args.sent:
|
129
270
|
args.folder = "Sent"
|
@@ -136,76 +277,228 @@ def main():
|
|
136
277
|
"- set --google if you are using a Gmail account\n"
|
137
278
|
"- use --auto to attempt autodiscovery"
|
138
279
|
)
|
139
|
-
|
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
|
140
296
|
|
297
|
+
mailbox = mb(**mb_kwargs)
|
298
|
+
mailbox.login(args.username, imap_password, args.folder)
|
299
|
+
return mailbox
|
300
|
+
|
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
|
+
):
|
358
|
+
json_data = []
|
141
359
|
table = Table(
|
142
|
-
|
143
|
-
show_header=not args.no_title,
|
360
|
+
show_header=not no_title,
|
144
361
|
header_style="bold",
|
362
|
+
expand=True,
|
145
363
|
show_lines=False,
|
364
|
+
show_edge=False,
|
365
|
+
pad_edge=False,
|
146
366
|
box=None,
|
367
|
+
row_styles=["", "dim"],
|
147
368
|
)
|
148
|
-
table.add_column("ID", style="red", no_wrap=
|
149
|
-
table.add_column(
|
150
|
-
|
369
|
+
table.add_column("ID", style="red", no_wrap=True)
|
370
|
+
table.add_column("Subject", style="green", no_wrap=True, ratio=3)
|
371
|
+
table.add_column("From", style="blue", no_wrap=True, ratio=2)
|
372
|
+
table.add_column("Date", style="cyan", no_wrap=True)
|
373
|
+
|
374
|
+
if unread_only:
|
375
|
+
search = AND(seen=False)
|
376
|
+
|
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))
|
404
|
+
else:
|
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
|
151
423
|
)
|
152
|
-
|
153
|
-
|
424
|
+
mailbox.flag(
|
425
|
+
[str(x) for x in mail_ids],
|
426
|
+
flag_set=(MailMessageFlags.SEEN),
|
427
|
+
value=value,
|
428
|
+
)
|
429
|
+
return 0
|
430
|
+
|
154
431
|
|
155
|
-
|
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)
|
156
449
|
|
157
450
|
try:
|
158
|
-
with
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
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,
|
166
464
|
)
|
167
|
-
if args.ATTACHMENT:
|
168
|
-
for att in msg.attachments:
|
169
|
-
if att.filename == args.ATTACHMENT:
|
170
|
-
sys.stdout.buffer.write(att.payload)
|
171
|
-
return 0
|
172
|
-
print(
|
173
|
-
f"Attachment {args.ATTACHMENT} not found",
|
174
|
-
file=sys.stderr,
|
175
|
-
)
|
176
|
-
return 1
|
177
|
-
else:
|
178
|
-
if args.raw:
|
179
|
-
print(msg.obj.as_string())
|
180
|
-
return 0
|
181
|
-
print(msg.text if not args.html else msg.html)
|
182
|
-
for att in msg.attachments:
|
183
|
-
print(
|
184
|
-
f"📎 Attachment: {att.filename}", file=sys.stderr
|
185
|
-
)
|
186
|
-
return 0
|
187
465
|
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
subj_prefix
|
200
|
-
+ (msg.subject if msg.subject else "<no-subject>"),
|
201
|
-
msg.from_,
|
202
|
-
msg.date.strftime("%H:%M %d/%m/%Y") if msg.date else "???",
|
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,
|
203
477
|
)
|
204
|
-
if len(table.rows) >= args.count:
|
205
|
-
break
|
206
478
|
|
207
|
-
|
208
|
-
|
479
|
+
# mark emails as read
|
480
|
+
elif args.command in ["read"]:
|
481
|
+
return mark_read(
|
482
|
+
mailbox=mailbox,
|
483
|
+
mail_ids=args.MAILIDS,
|
484
|
+
)
|
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
|
+
|
209
502
|
except Exception:
|
210
503
|
console.print_exception(show_locals=True)
|
211
504
|
return 1
|
myl-0.8.7.dist-info/RECORD
DELETED
@@ -1,7 +0,0 @@
|
|
1
|
-
myl.py,sha256=tyXYwSNAkaq2rNasB1OtBldpDE209l6IzvaF_Qa1aZQ,6843
|
2
|
-
myl-0.8.7.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
3
|
-
myl-0.8.7.dist-info/METADATA,sha256=yz8_wODPddW5YKNmsJT7M6wT4af5kHwLeUzBVxsCICw,43013
|
4
|
-
myl-0.8.7.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
5
|
-
myl-0.8.7.dist-info/entry_points.txt,sha256=q6nr0Kzim7JzreXQE3BTU4asLh2sx5-D0w1yLBOcHxc,33
|
6
|
-
myl-0.8.7.dist-info/top_level.txt,sha256=Wn88OJVVWyYSsKVoqzlHXxfFxh5IbrJ_Yw-ldNLe7Po,4
|
7
|
-
myl-0.8.7.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|