deltabot-cli 7.2.0__tar.gz → 8.1.0__tar.gz
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.
- {deltabot_cli-7.2.0 → deltabot_cli-8.1.0}/.github/workflows/python-ci.yml +15 -21
- {deltabot_cli-7.2.0 → deltabot_cli-8.1.0}/.gitignore +1 -0
- deltabot_cli-8.1.0/.prospector.yaml +16 -0
- {deltabot_cli-7.2.0/deltabot_cli.egg-info → deltabot_cli-8.1.0}/PKG-INFO +5 -8
- {deltabot_cli-7.2.0 → deltabot_cli-8.1.0}/deltabot_cli/__init__.py +1 -2
- {deltabot_cli-7.2.0 → deltabot_cli-8.1.0}/deltabot_cli/cli.py +136 -118
- {deltabot_cli-7.2.0 → deltabot_cli-8.1.0/deltabot_cli.egg-info}/PKG-INFO +5 -8
- {deltabot_cli-7.2.0 → deltabot_cli-8.1.0}/deltabot_cli.egg-info/SOURCES.txt +3 -1
- deltabot_cli-8.1.0/deltabot_cli.egg-info/requires.txt +8 -0
- {deltabot_cli-7.2.0 → deltabot_cli-8.1.0}/examples/echobot_advanced.py +1 -0
- deltabot_cli-8.1.0/examples/send_and_exit.py +53 -0
- {deltabot_cli-7.2.0 → deltabot_cli-8.1.0}/pyproject.toml +4 -7
- deltabot_cli-7.2.0/deltabot_cli.egg-info/requires.txt +0 -11
- {deltabot_cli-7.2.0 → deltabot_cli-8.1.0}/LICENSE +0 -0
- {deltabot_cli-7.2.0 → deltabot_cli-8.1.0}/README.md +0 -0
- {deltabot_cli-7.2.0 → deltabot_cli-8.1.0}/deltabot_cli/_utils.py +0 -0
- {deltabot_cli-7.2.0 → deltabot_cli-8.1.0}/deltabot_cli.egg-info/dependency_links.txt +0 -0
- {deltabot_cli-7.2.0 → deltabot_cli-8.1.0}/deltabot_cli.egg-info/top_level.txt +0 -0
- {deltabot_cli-7.2.0 → deltabot_cli-8.1.0}/examples/echobot.py +0 -0
- {deltabot_cli-7.2.0 → deltabot_cli-8.1.0}/pylama.ini +0 -0
- {deltabot_cli-7.2.0 → deltabot_cli-8.1.0}/setup.cfg +0 -0
|
@@ -13,33 +13,27 @@ jobs:
|
|
|
13
13
|
runs-on: ubuntu-latest
|
|
14
14
|
strategy:
|
|
15
15
|
matrix:
|
|
16
|
-
python-version: ['3.
|
|
16
|
+
python-version: ['3.10', '3.13']
|
|
17
17
|
steps:
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
run:
|
|
29
|
-
|
|
30
|
-
- name: Lint code
|
|
31
|
-
run: |
|
|
32
|
-
pylama
|
|
33
|
-
- name: Test with pytest
|
|
34
|
-
run: |
|
|
35
|
-
#pytest
|
|
18
|
+
- uses: actions/checkout@v6
|
|
19
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
20
|
+
uses: actions/setup-python@v6
|
|
21
|
+
with:
|
|
22
|
+
python-version: ${{ matrix.python-version }}
|
|
23
|
+
- name: Install dependencies
|
|
24
|
+
run: |
|
|
25
|
+
python -m pip install --upgrade pip
|
|
26
|
+
python -m pip install '.[dev]'
|
|
27
|
+
- run: isort --check .
|
|
28
|
+
- run: black --check .
|
|
29
|
+
- run: prospector
|
|
36
30
|
|
|
37
31
|
deploy:
|
|
38
32
|
needs: test
|
|
39
33
|
runs-on: ubuntu-latest
|
|
40
34
|
steps:
|
|
41
|
-
- uses: actions/checkout@
|
|
42
|
-
- uses: actions/setup-python@
|
|
35
|
+
- uses: actions/checkout@v6
|
|
36
|
+
- uses: actions/setup-python@v6
|
|
43
37
|
with:
|
|
44
38
|
python-version: '3.x'
|
|
45
39
|
- id: check-tag
|
|
@@ -1,27 +1,24 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: deltabot-cli
|
|
3
|
-
Version:
|
|
3
|
+
Version: 8.1.0
|
|
4
4
|
Summary: Library to speedup Delta Chat bot development
|
|
5
|
-
Author-email: adbenitez <adb@
|
|
5
|
+
Author-email: adbenitez <adb@arcanechat.me>
|
|
6
6
|
Project-URL: Homepage, https://github.com/deltachat-bot/deltabot-cli-py
|
|
7
7
|
Keywords: deltachat,bot,deltabot-cli
|
|
8
8
|
Classifier: Development Status :: 4 - Beta
|
|
9
9
|
Classifier: Programming Language :: Python :: 3
|
|
10
10
|
Classifier: Intended Audience :: Developers
|
|
11
11
|
Classifier: License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)
|
|
12
|
-
Requires-Python: >=3.
|
|
12
|
+
Requires-Python: >=3.10
|
|
13
13
|
Description-Content-Type: text/markdown
|
|
14
14
|
License-File: LICENSE
|
|
15
|
-
Requires-Dist: deltachat2[full]>=0.
|
|
15
|
+
Requires-Dist: deltachat2[full]>=0.10.0
|
|
16
16
|
Requires-Dist: appdirs>=1.4.4
|
|
17
17
|
Requires-Dist: rich>=12.6.0
|
|
18
18
|
Provides-Extra: dev
|
|
19
19
|
Requires-Dist: black; extra == "dev"
|
|
20
|
-
Requires-Dist: mypy; extra == "dev"
|
|
21
20
|
Requires-Dist: isort; extra == "dev"
|
|
22
|
-
Requires-Dist:
|
|
23
|
-
Requires-Dist: pylama; extra == "dev"
|
|
24
|
-
Requires-Dist: pytest; extra == "dev"
|
|
21
|
+
Requires-Dist: prospector[with-mypy]; extra == "dev"
|
|
25
22
|
Dynamic: license-file
|
|
26
23
|
|
|
27
24
|
# deltabot-cli for Python
|
|
@@ -8,7 +8,7 @@ import time
|
|
|
8
8
|
from argparse import ArgumentParser, Namespace
|
|
9
9
|
from pathlib import Path
|
|
10
10
|
from threading import Thread
|
|
11
|
-
from typing import Callable,
|
|
11
|
+
from typing import Callable, Union
|
|
12
12
|
|
|
13
13
|
from appdirs import user_config_dir
|
|
14
14
|
from deltachat2 import Bot, CoreEvent, Event, EventType, IOTransport, JsonRpcError, Rpc
|
|
@@ -32,11 +32,12 @@ class BotCli:
|
|
|
32
32
|
def __init__(self, app_name: str, log_level: str = "info") -> None:
|
|
33
33
|
self.app_name = app_name
|
|
34
34
|
self.log_level = log_level
|
|
35
|
+
self._base_parser = ArgumentParser(add_help=False)
|
|
35
36
|
self._parser = ArgumentParser(app_name)
|
|
36
37
|
self._subparsers = self._parser.add_subparsers(title="subcommands")
|
|
37
38
|
self._hooks = HookCollection()
|
|
38
|
-
self._init_hooks:
|
|
39
|
-
self._start_hooks:
|
|
39
|
+
self._init_hooks: set[CliEventHook] = set()
|
|
40
|
+
self._start_hooks: set[CliEventHook] = set()
|
|
40
41
|
self._bot: Bot
|
|
41
42
|
|
|
42
43
|
def on(self, event: Union[type, EventFilter]) -> HookDecorator:
|
|
@@ -72,9 +73,14 @@ class BotCli:
|
|
|
72
73
|
func(bot, args)
|
|
73
74
|
|
|
74
75
|
def add_generic_option(self, *flags, **kwargs) -> None:
|
|
75
|
-
"""Add a generic argument option to the CLI.
|
|
76
|
+
"""Add a generic argument option to the CLI.
|
|
77
|
+
|
|
78
|
+
For the generic option to be usable also after a subcommand,
|
|
79
|
+
it must be registered before any subcommand.
|
|
80
|
+
"""
|
|
76
81
|
if not (flags and flags[0].startswith("-")):
|
|
77
82
|
raise ValueError("can not generically add positional args")
|
|
83
|
+
self._base_parser.add_argument(*flags, **kwargs)
|
|
78
84
|
self._parser.add_argument(*flags, **kwargs)
|
|
79
85
|
|
|
80
86
|
def add_subcommand(
|
|
@@ -87,6 +93,8 @@ class BotCli:
|
|
|
87
93
|
kwargs["name"] = func.__name__
|
|
88
94
|
if not kwargs.get("help") and not kwargs.get("description"):
|
|
89
95
|
kwargs["help"], kwargs["description"] = parse_docstring(func.__doc__)
|
|
96
|
+
if "parents" not in kwargs:
|
|
97
|
+
kwargs["parents"] = [self._base_parser]
|
|
90
98
|
subparser = self._subparsers.add_parser(**kwargs)
|
|
91
99
|
subparser.set_defaults(cmd=func)
|
|
92
100
|
return subparser
|
|
@@ -108,8 +116,9 @@ class BotCli:
|
|
|
108
116
|
self.add_generic_option(
|
|
109
117
|
"--account",
|
|
110
118
|
"-a",
|
|
111
|
-
help="operate only over the given
|
|
112
|
-
metavar="
|
|
119
|
+
help="operate only over the profile with the given ID when running any subcommand",
|
|
120
|
+
metavar="ID",
|
|
121
|
+
type=int,
|
|
113
122
|
)
|
|
114
123
|
choices = ["debug", "info", "warning", "error"]
|
|
115
124
|
assert (
|
|
@@ -124,8 +133,15 @@ class BotCli:
|
|
|
124
133
|
)
|
|
125
134
|
|
|
126
135
|
init_parser = self.add_subcommand(_init_cmd, name="init")
|
|
127
|
-
init_parser.add_argument(
|
|
128
|
-
|
|
136
|
+
init_parser.add_argument(
|
|
137
|
+
"addr",
|
|
138
|
+
help=("the e-mail address to use or a DCACCOUNT URI ex. DCACCOUNT:nine.testrun.org"),
|
|
139
|
+
)
|
|
140
|
+
init_parser.add_argument(
|
|
141
|
+
"password",
|
|
142
|
+
nargs="?",
|
|
143
|
+
help="account password, this field is required only if addr is not a DCACCOUNT URI",
|
|
144
|
+
)
|
|
129
145
|
|
|
130
146
|
config_parser = self.add_subcommand(_config_cmd, name="config")
|
|
131
147
|
config_parser.add_argument("option", help="option name", nargs="?")
|
|
@@ -134,11 +150,25 @@ class BotCli:
|
|
|
134
150
|
import_parser = self.add_subcommand(_import_cmd, name="import")
|
|
135
151
|
import_parser.add_argument("path", help="path to the account backup", type=Path)
|
|
136
152
|
|
|
153
|
+
export_parser = self.add_subcommand(_export_cmd, name="export")
|
|
154
|
+
export_parser.add_argument(
|
|
155
|
+
"folder",
|
|
156
|
+
help="folder where the backup will be exported to",
|
|
157
|
+
type=Path,
|
|
158
|
+
default=Path(),
|
|
159
|
+
nargs="?",
|
|
160
|
+
)
|
|
161
|
+
|
|
137
162
|
self.add_subcommand(_serve_cmd, name="serve")
|
|
138
163
|
self.add_subcommand(_link_cmd, name="link")
|
|
139
164
|
self.add_subcommand(_admin_cmd, name="admin")
|
|
140
165
|
self.add_subcommand(_list_cmd, name="list")
|
|
141
|
-
self.add_subcommand(_remove_cmd, name="remove")
|
|
166
|
+
remove_parser = self.add_subcommand(_remove_cmd, name="remove")
|
|
167
|
+
remove_parser.add_argument(
|
|
168
|
+
"address",
|
|
169
|
+
help="address to remove, if not provided the whole account is removed",
|
|
170
|
+
nargs="?",
|
|
171
|
+
)
|
|
142
172
|
|
|
143
173
|
def get_accounts_dir(self, args: Namespace) -> str:
|
|
144
174
|
"""Get bot's account folder."""
|
|
@@ -146,32 +176,6 @@ class BotCli:
|
|
|
146
176
|
os.makedirs(args.config_dir)
|
|
147
177
|
return os.path.join(args.config_dir, "accounts")
|
|
148
178
|
|
|
149
|
-
def get_or_create_account(self, rpc: Rpc, addr: str) -> int:
|
|
150
|
-
"""Get account for address, if no account exists create a new one."""
|
|
151
|
-
accid = self.get_account(rpc, addr)
|
|
152
|
-
if not accid:
|
|
153
|
-
accid = rpc.add_account()
|
|
154
|
-
rpc.set_config(accid, "addr", addr)
|
|
155
|
-
return accid
|
|
156
|
-
|
|
157
|
-
def get_account(self, rpc: Rpc, addr: str) -> int:
|
|
158
|
-
"""Get account id for address.
|
|
159
|
-
If no account exists with the given address, zero is returned.
|
|
160
|
-
"""
|
|
161
|
-
try:
|
|
162
|
-
return int(addr)
|
|
163
|
-
except ValueError:
|
|
164
|
-
for accid in rpc.get_all_account_ids():
|
|
165
|
-
if addr == self.get_address(rpc, accid):
|
|
166
|
-
return accid
|
|
167
|
-
|
|
168
|
-
return 0
|
|
169
|
-
|
|
170
|
-
def get_address(self, rpc: Rpc, accid: int) -> str:
|
|
171
|
-
if rpc.is_configured(accid):
|
|
172
|
-
return rpc.get_config(accid, "configured_addr")
|
|
173
|
-
return rpc.get_config(accid, "addr")
|
|
174
|
-
|
|
175
179
|
def is_admin(self, rpc: Rpc, accid: int, contactid: int) -> bool:
|
|
176
180
|
"""Return True if the contact is an administrator.
|
|
177
181
|
Administrators are the members of the Administration group.
|
|
@@ -190,7 +194,12 @@ class BotCli:
|
|
|
190
194
|
if not rpc.is_configured(accid):
|
|
191
195
|
return 0
|
|
192
196
|
chatid = int(rpc.get_config(accid, "ui.admin_chat") or 0)
|
|
193
|
-
|
|
197
|
+
try:
|
|
198
|
+
if rpc.can_send(accid, chatid):
|
|
199
|
+
return chatid
|
|
200
|
+
except JsonRpcError:
|
|
201
|
+
pass
|
|
202
|
+
return self.reset_admin_chat(rpc, accid)
|
|
194
203
|
|
|
195
204
|
def reset_admin_chat(self, rpc: Rpc, accid: int) -> int:
|
|
196
205
|
"""Reset the bot administration group and return the new group id.
|
|
@@ -225,40 +234,58 @@ class BotCli:
|
|
|
225
234
|
self._parser.parse_args(["-h"])
|
|
226
235
|
|
|
227
236
|
|
|
228
|
-
def
|
|
237
|
+
def _ensure_one_acc(bot: Bot, args: Namespace) -> int:
|
|
238
|
+
if args.account:
|
|
239
|
+
accounts = [args.account]
|
|
240
|
+
else:
|
|
241
|
+
accounts = bot.rpc.get_all_account_ids()
|
|
242
|
+
|
|
243
|
+
if len(accounts) == 1:
|
|
244
|
+
return accounts[0]
|
|
245
|
+
bot.logger.error(
|
|
246
|
+
"There is more than one account, please provide an account id with -a/--account option"
|
|
247
|
+
)
|
|
248
|
+
sys.exit(1)
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def _init_cmd(_cli: BotCli, bot: Bot, args: Namespace) -> None:
|
|
229
252
|
"""initialize the account"""
|
|
230
253
|
|
|
231
|
-
def
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
254
|
+
def process_events() -> None:
|
|
255
|
+
events = (EventType.INFO, EventType.WARNING, EventType.ERROR)
|
|
256
|
+
while True:
|
|
257
|
+
raw_event = bot.rpc.get_next_event()
|
|
258
|
+
accid = raw_event.context_id
|
|
259
|
+
event = CoreEvent(raw_event.event)
|
|
260
|
+
if event.kind == EventType.CONFIGURE_PROGRESS:
|
|
261
|
+
if event.comment:
|
|
262
|
+
bot.logger.info(event.comment)
|
|
263
|
+
pbar.set_progress(event.progress)
|
|
264
|
+
elif event.kind in events:
|
|
265
|
+
bot._on_event(Event(accid, event), RawEvent) # noqa
|
|
266
|
+
if pbar.progress in (-1, pbar.total):
|
|
267
|
+
break
|
|
236
268
|
|
|
237
269
|
if args.account:
|
|
238
|
-
accid =
|
|
239
|
-
if not accid:
|
|
240
|
-
bot.logger.error(f"unknown account: {args.account!r}")
|
|
241
|
-
sys.exit(1)
|
|
270
|
+
accid = args.account
|
|
242
271
|
else:
|
|
243
|
-
accid =
|
|
272
|
+
accid = bot.rpc.add_account()
|
|
244
273
|
|
|
245
274
|
bot.logger.info("Starting configuration process...")
|
|
246
|
-
|
|
247
|
-
task.start()
|
|
275
|
+
bot.rpc.set_config(accid, "bot", "1")
|
|
248
276
|
pbar = ConfigProgressBar()
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
task.join()
|
|
277
|
+
task = Thread(target=process_events, daemon=True)
|
|
278
|
+
task.start()
|
|
279
|
+
try:
|
|
280
|
+
if not args.password:
|
|
281
|
+
bot.rpc.add_transport_from_qr(accid, args.addr)
|
|
282
|
+
else:
|
|
283
|
+
params = {"addr": args.addr, "password": args.password}
|
|
284
|
+
bot.rpc.add_or_update_transport(accid, params)
|
|
285
|
+
task.join()
|
|
286
|
+
except JsonRpcError as err:
|
|
287
|
+
bot.logger.error(err)
|
|
288
|
+
pbar.progress = -1
|
|
262
289
|
pbar.close()
|
|
263
290
|
if pbar.progress == -1:
|
|
264
291
|
bot.logger.error("Configuration failed.")
|
|
@@ -271,27 +298,25 @@ def _serve_cmd(cli: BotCli, bot: Bot, args: Namespace) -> None:
|
|
|
271
298
|
"""start processing messages"""
|
|
272
299
|
rpc = bot.rpc
|
|
273
300
|
if args.account:
|
|
274
|
-
accounts = [
|
|
275
|
-
if not accounts[0]:
|
|
276
|
-
bot.logger.error(f"unknown account: {args.account!r}")
|
|
277
|
-
sys.exit(1)
|
|
301
|
+
accounts = [args.account]
|
|
278
302
|
else:
|
|
279
303
|
accounts = rpc.get_all_account_ids()
|
|
280
|
-
|
|
304
|
+
configured = False
|
|
281
305
|
for accid in accounts:
|
|
282
|
-
if rpc.is_configured(accid):
|
|
283
|
-
|
|
306
|
+
if bot.rpc.is_configured(accid):
|
|
307
|
+
configured = True
|
|
308
|
+
link = bot.rpc.get_chat_securejoin_qr_code(accid, None)
|
|
309
|
+
bot.logger.info(f"Listening at: {link}")
|
|
284
310
|
else:
|
|
285
311
|
bot.logger.error(f"account {accid} not configured")
|
|
286
|
-
if
|
|
287
|
-
bot.logger.info(f"Listening at: {', '.join(addrs)}")
|
|
312
|
+
if configured:
|
|
288
313
|
cli._on_start(bot, args) # noqa
|
|
289
314
|
while True:
|
|
290
315
|
try:
|
|
291
316
|
bot.run_forever(accounts[0] if args.account else 0)
|
|
292
317
|
except KeyboardInterrupt:
|
|
293
318
|
return
|
|
294
|
-
except Exception as ex:
|
|
319
|
+
except Exception as ex:
|
|
295
320
|
bot.logger.exception(ex)
|
|
296
321
|
time.sleep(5)
|
|
297
322
|
else:
|
|
@@ -299,18 +324,14 @@ def _serve_cmd(cli: BotCli, bot: Bot, args: Namespace) -> None:
|
|
|
299
324
|
sys.exit(1)
|
|
300
325
|
|
|
301
326
|
|
|
302
|
-
def _config_cmd(
|
|
327
|
+
def _config_cmd(_cli: BotCli, bot: Bot, args: Namespace) -> None:
|
|
303
328
|
"""set/get account configuration values"""
|
|
304
329
|
if args.account:
|
|
305
|
-
accounts = [
|
|
306
|
-
if not accounts[0]:
|
|
307
|
-
bot.logger.error(f"unknown account: {args.account!r}")
|
|
308
|
-
sys.exit(1)
|
|
330
|
+
accounts = [args.account]
|
|
309
331
|
else:
|
|
310
332
|
accounts = bot.rpc.get_all_account_ids()
|
|
311
333
|
for accid in accounts:
|
|
312
|
-
|
|
313
|
-
print(f"Account #{accid} ({addr}):")
|
|
334
|
+
print(f"Account #{accid}:")
|
|
314
335
|
_config_cmd_for_acc(bot, accid, args)
|
|
315
336
|
print("")
|
|
316
337
|
if not accounts:
|
|
@@ -339,15 +360,11 @@ def _admin_cmd(cli: BotCli, bot: Bot, args: Namespace) -> None:
|
|
|
339
360
|
"""print the invitation link to the bot administration group.
|
|
340
361
|
WARNING: don't share this, anyone joining will become admin of the bot"""
|
|
341
362
|
if args.account:
|
|
342
|
-
accounts = [
|
|
343
|
-
if not accounts[0]:
|
|
344
|
-
bot.logger.error(f"unknown account: {args.account!r}")
|
|
345
|
-
sys.exit(1)
|
|
363
|
+
accounts = [args.account]
|
|
346
364
|
else:
|
|
347
365
|
accounts = bot.rpc.get_all_account_ids()
|
|
348
366
|
for accid in accounts:
|
|
349
|
-
|
|
350
|
-
print(f"Account #{accid} ({addr}):")
|
|
367
|
+
print(f"Account #{accid}:")
|
|
351
368
|
_admin_cmd_for_acc(cli, bot, accid)
|
|
352
369
|
print("")
|
|
353
370
|
if not accounts:
|
|
@@ -365,18 +382,14 @@ def _admin_cmd_for_acc(cli: BotCli, bot: Bot, accid: int) -> None:
|
|
|
365
382
|
bot.logger.error("account not configured")
|
|
366
383
|
|
|
367
384
|
|
|
368
|
-
def _link_cmd(
|
|
385
|
+
def _link_cmd(_cli: BotCli, bot: Bot, args: Namespace) -> None:
|
|
369
386
|
"""print the bot's chat invitation link"""
|
|
370
387
|
if args.account:
|
|
371
|
-
accounts = [
|
|
372
|
-
if not accounts[0]:
|
|
373
|
-
bot.logger.error(f"unknown account: {args.account!r}")
|
|
374
|
-
sys.exit(1)
|
|
388
|
+
accounts = [args.account]
|
|
375
389
|
else:
|
|
376
390
|
accounts = bot.rpc.get_all_account_ids()
|
|
377
391
|
for accid in accounts:
|
|
378
|
-
|
|
379
|
-
print(f"Account #{accid} ({addr}):")
|
|
392
|
+
print(f"Account #{accid}:")
|
|
380
393
|
_link_cmd_for_acc(bot, accid)
|
|
381
394
|
print("")
|
|
382
395
|
if not accounts:
|
|
@@ -393,38 +406,27 @@ def _link_cmd_for_acc(bot: Bot, accid: int) -> None:
|
|
|
393
406
|
bot.logger.error("account not configured")
|
|
394
407
|
|
|
395
408
|
|
|
396
|
-
def _list_cmd(
|
|
409
|
+
def _list_cmd(_cli: BotCli, bot: Bot, _args: Namespace) -> None:
|
|
397
410
|
"""show a list of existing bot accounts"""
|
|
398
411
|
rpc = bot.rpc
|
|
399
412
|
accounts = rpc.get_all_account_ids()
|
|
400
413
|
for accid in accounts:
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
print(f"#{accid} - {
|
|
414
|
+
transports = bot.rpc.list_transports(accid)
|
|
415
|
+
addrs = [params["addr"] for params in transports]
|
|
416
|
+
info = ", ".join(addrs) or "(not configured)"
|
|
417
|
+
print(f"#{accid} - {info}")
|
|
405
418
|
|
|
406
419
|
|
|
407
|
-
def _remove_cmd(
|
|
420
|
+
def _remove_cmd(_cli: BotCli, bot: Bot, args: Namespace) -> None:
|
|
408
421
|
"""remove Delta Chat accounts from the bot"""
|
|
409
|
-
|
|
410
|
-
accid = cli.get_account(bot.rpc, args.account)
|
|
411
|
-
if not accid:
|
|
412
|
-
bot.logger.error(f"unknown account: {args.account!r}")
|
|
413
|
-
sys.exit(1)
|
|
414
|
-
else:
|
|
415
|
-
accounts = bot.rpc.get_all_account_ids()
|
|
416
|
-
if len(accounts) == 1:
|
|
417
|
-
accid = accounts[0]
|
|
418
|
-
else:
|
|
419
|
-
bot.logger.error(
|
|
420
|
-
"There are more than one account, to remove one of them, pass the account"
|
|
421
|
-
" address with -a/--account option"
|
|
422
|
-
)
|
|
423
|
-
sys.exit(1)
|
|
422
|
+
accid = _ensure_one_acc(bot, args)
|
|
424
423
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
424
|
+
if args.address:
|
|
425
|
+
bot.rpc.delete_transport(accid, args.address)
|
|
426
|
+
print(f"Account #{accid}: removed address {args.address}")
|
|
427
|
+
else:
|
|
428
|
+
bot.rpc.remove_account(accid)
|
|
429
|
+
print(f"Account #{accid} removed successfully.")
|
|
428
430
|
|
|
429
431
|
|
|
430
432
|
def _import_cmd(_cli: BotCli, bot: Bot, args: Namespace) -> None:
|
|
@@ -440,5 +442,21 @@ def _import_cmd(_cli: BotCli, bot: Bot, args: Namespace) -> None:
|
|
|
440
442
|
bot.logger.exception(ex)
|
|
441
443
|
sys.exit(1)
|
|
442
444
|
else:
|
|
443
|
-
|
|
444
|
-
|
|
445
|
+
print(f"Account #{accid} imported successfully.")
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
def _export_cmd(_cli: BotCli, bot: Bot, args: Namespace) -> None:
|
|
449
|
+
"""export account backup"""
|
|
450
|
+
accid = _ensure_one_acc(bot, args)
|
|
451
|
+
|
|
452
|
+
if not args.folder.exists():
|
|
453
|
+
bot.logger.error(f"folder doesn't exist: {str(args.folder)!r}")
|
|
454
|
+
sys.exit(1)
|
|
455
|
+
|
|
456
|
+
try:
|
|
457
|
+
bot.rpc.export_backup(accid, str(args.folder), None)
|
|
458
|
+
except JsonRpcError as ex:
|
|
459
|
+
bot.logger.exception(ex)
|
|
460
|
+
sys.exit(1)
|
|
461
|
+
else:
|
|
462
|
+
print(f"Account #{accid} exported successfully.")
|
|
@@ -1,27 +1,24 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: deltabot-cli
|
|
3
|
-
Version:
|
|
3
|
+
Version: 8.1.0
|
|
4
4
|
Summary: Library to speedup Delta Chat bot development
|
|
5
|
-
Author-email: adbenitez <adb@
|
|
5
|
+
Author-email: adbenitez <adb@arcanechat.me>
|
|
6
6
|
Project-URL: Homepage, https://github.com/deltachat-bot/deltabot-cli-py
|
|
7
7
|
Keywords: deltachat,bot,deltabot-cli
|
|
8
8
|
Classifier: Development Status :: 4 - Beta
|
|
9
9
|
Classifier: Programming Language :: Python :: 3
|
|
10
10
|
Classifier: Intended Audience :: Developers
|
|
11
11
|
Classifier: License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)
|
|
12
|
-
Requires-Python: >=3.
|
|
12
|
+
Requires-Python: >=3.10
|
|
13
13
|
Description-Content-Type: text/markdown
|
|
14
14
|
License-File: LICENSE
|
|
15
|
-
Requires-Dist: deltachat2[full]>=0.
|
|
15
|
+
Requires-Dist: deltachat2[full]>=0.10.0
|
|
16
16
|
Requires-Dist: appdirs>=1.4.4
|
|
17
17
|
Requires-Dist: rich>=12.6.0
|
|
18
18
|
Provides-Extra: dev
|
|
19
19
|
Requires-Dist: black; extra == "dev"
|
|
20
|
-
Requires-Dist: mypy; extra == "dev"
|
|
21
20
|
Requires-Dist: isort; extra == "dev"
|
|
22
|
-
Requires-Dist:
|
|
23
|
-
Requires-Dist: pylama; extra == "dev"
|
|
24
|
-
Requires-Dist: pytest; extra == "dev"
|
|
21
|
+
Requires-Dist: prospector[with-mypy]; extra == "dev"
|
|
25
22
|
Dynamic: license-file
|
|
26
23
|
|
|
27
24
|
# deltabot-cli for Python
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
.gitignore
|
|
2
|
+
.prospector.yaml
|
|
2
3
|
LICENSE
|
|
3
4
|
README.md
|
|
4
5
|
pylama.ini
|
|
@@ -13,4 +14,5 @@ deltabot_cli.egg-info/dependency_links.txt
|
|
|
13
14
|
deltabot_cli.egg-info/requires.txt
|
|
14
15
|
deltabot_cli.egg-info/top_level.txt
|
|
15
16
|
examples/echobot.py
|
|
16
|
-
examples/echobot_advanced.py
|
|
17
|
+
examples/echobot_advanced.py
|
|
18
|
+
examples/send_and_exit.py
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""An example bot that just sends a message and exits, without needing to be running all the time.
|
|
3
|
+
This useful, for example, for bots that are used only to send notifications/alerts,
|
|
4
|
+
and are integrated into other systems that would run the bot command with data parameters
|
|
5
|
+
every time there is a message to be sent.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from argparse import Namespace
|
|
9
|
+
|
|
10
|
+
from deltachat2 import Bot, EventType, MsgData, events
|
|
11
|
+
|
|
12
|
+
from deltabot_cli import BotCli
|
|
13
|
+
|
|
14
|
+
cli = BotCli("sendbot")
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@cli.on(events.RawEvent)
|
|
18
|
+
def log_event(bot: Bot, _accid: int, event) -> None:
|
|
19
|
+
if event.kind == EventType.INFO:
|
|
20
|
+
bot.logger.debug(event.msg)
|
|
21
|
+
elif event.kind == EventType.WARNING:
|
|
22
|
+
bot.logger.warning(event.msg)
|
|
23
|
+
elif event.kind == EventType.ERROR:
|
|
24
|
+
bot.logger.error(event.msg)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def send(_cli: BotCli, bot: Bot, args: Namespace) -> None:
|
|
28
|
+
"""send a message"""
|
|
29
|
+
accid = bot.rpc.get_all_account_ids()[0]
|
|
30
|
+
|
|
31
|
+
# first fetch incoming messages to have updated chats state
|
|
32
|
+
bot.logger.info("first syncing chats state...")
|
|
33
|
+
bot.rpc.accounts_background_fetch(60)
|
|
34
|
+
|
|
35
|
+
bot.logger.info("sending message...")
|
|
36
|
+
chatid = cli.get_admin_chat(bot.rpc, accid)
|
|
37
|
+
msgid = bot.rpc.send_msg(accid, chatid, MsgData(text=args.text, file=args.file))
|
|
38
|
+
bot.run_until(
|
|
39
|
+
lambda ev: ev.event.kind in (EventType.MSG_DELIVERED, EventType.MSG_FAILED)
|
|
40
|
+
and ev.event.msg_id == msgid
|
|
41
|
+
)
|
|
42
|
+
bot.logger.info("Done, message sent")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
if __name__ == "__main__":
|
|
46
|
+
send_subcmd = cli.add_subcommand(send)
|
|
47
|
+
send_subcmd.add_argument("text", help="the message's text")
|
|
48
|
+
send_subcmd.add_argument(
|
|
49
|
+
"file",
|
|
50
|
+
nargs="?",
|
|
51
|
+
help="path to a file to send as attachment",
|
|
52
|
+
)
|
|
53
|
+
cli.start()
|
|
@@ -7,10 +7,10 @@ name = "deltabot-cli"
|
|
|
7
7
|
description = "Library to speedup Delta Chat bot development"
|
|
8
8
|
dynamic = ["version"]
|
|
9
9
|
readme = "README.md"
|
|
10
|
-
requires-python = ">=3.
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
11
|
keywords = ["deltachat", "bot", "deltabot-cli"]
|
|
12
12
|
authors = [
|
|
13
|
-
{name = "adbenitez", email = "adb@
|
|
13
|
+
{name = "adbenitez", email = "adb@arcanechat.me"},
|
|
14
14
|
]
|
|
15
15
|
classifiers = [
|
|
16
16
|
"Development Status :: 4 - Beta",
|
|
@@ -19,7 +19,7 @@ classifiers = [
|
|
|
19
19
|
"License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)",
|
|
20
20
|
]
|
|
21
21
|
dependencies = [
|
|
22
|
-
"deltachat2[full]>=0.
|
|
22
|
+
"deltachat2[full]>=0.10.0",
|
|
23
23
|
"appdirs>=1.4.4",
|
|
24
24
|
"rich>=12.6.0",
|
|
25
25
|
]
|
|
@@ -30,11 +30,8 @@ Homepage = "https://github.com/deltachat-bot/deltabot-cli-py"
|
|
|
30
30
|
[project.optional-dependencies]
|
|
31
31
|
dev = [
|
|
32
32
|
"black",
|
|
33
|
-
"mypy",
|
|
34
33
|
"isort",
|
|
35
|
-
"
|
|
36
|
-
"pylama",
|
|
37
|
-
"pytest",
|
|
34
|
+
"prospector[with-mypy]",
|
|
38
35
|
]
|
|
39
36
|
|
|
40
37
|
[tool.setuptools_scm]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|