deltabot-cli 2.0.0__tar.gz → 3.0.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-3.0.0/.github/workflows/python-ci.yml +56 -0
- deltabot-cli-3.0.0/.gitignore +13 -0
- {deltabot-cli-2.0.0 → deltabot-cli-3.0.0}/PKG-INFO +4 -4
- {deltabot-cli-2.0.0 → deltabot-cli-3.0.0}/README.md +1 -1
- {deltabot-cli-2.0.0 → deltabot-cli-3.0.0}/deltabot_cli/__init__.py +1 -0
- {deltabot-cli-2.0.0 → deltabot-cli-3.0.0}/deltabot_cli/cli.py +11 -16
- {deltabot-cli-2.0.0 → deltabot-cli-3.0.0}/deltabot_cli/client.py +5 -4
- {deltabot-cli-2.0.0 → deltabot-cli-3.0.0}/deltabot_cli/events.py +41 -39
- deltabot-cli-3.0.0/deltabot_cli/utils.py +17 -0
- {deltabot-cli-2.0.0 → deltabot-cli-3.0.0}/deltabot_cli.egg-info/PKG-INFO +4 -4
- {deltabot-cli-2.0.0 → deltabot-cli-3.0.0}/deltabot_cli.egg-info/SOURCES.txt +7 -1
- deltabot-cli-3.0.0/examples/echobot.py +21 -0
- deltabot-cli-3.0.0/examples/echobot_advanced.py +46 -0
- deltabot-cli-3.0.0/pylama.ini +4 -0
- {deltabot-cli-2.0.0 → deltabot-cli-3.0.0}/pyproject.toml +7 -4
- {deltabot-cli-2.0.0 → deltabot-cli-3.0.0}/LICENSE +0 -0
- {deltabot-cli-2.0.0 → deltabot-cli-3.0.0}/deltabot_cli/_utils.py +0 -0
- {deltabot-cli-2.0.0 → deltabot-cli-3.0.0}/deltabot_cli/const.py +0 -0
- {deltabot-cli-2.0.0 → deltabot-cli-3.0.0}/deltabot_cli/rpc.py +0 -0
- {deltabot-cli-2.0.0 → deltabot-cli-3.0.0}/deltabot_cli.egg-info/dependency_links.txt +0 -0
- {deltabot-cli-2.0.0 → deltabot-cli-3.0.0}/deltabot_cli.egg-info/requires.txt +0 -0
- {deltabot-cli-2.0.0 → deltabot-cli-3.0.0}/deltabot_cli.egg-info/top_level.txt +0 -0
- {deltabot-cli-2.0.0 → deltabot-cli-3.0.0}/setup.cfg +0 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ master ]
|
|
6
|
+
tags:
|
|
7
|
+
- '*.*.*'
|
|
8
|
+
pull_request:
|
|
9
|
+
branches: [ master ]
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
test:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
strategy:
|
|
15
|
+
matrix:
|
|
16
|
+
python-version: ['3.9', '3.11']
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v2
|
|
19
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
20
|
+
uses: actions/setup-python@v2
|
|
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
|
+
- name: Check code with black
|
|
28
|
+
run: |
|
|
29
|
+
black --check .
|
|
30
|
+
- name: Lint code
|
|
31
|
+
run: |
|
|
32
|
+
pylama
|
|
33
|
+
- name: Test with pytest
|
|
34
|
+
run: |
|
|
35
|
+
#pytest
|
|
36
|
+
|
|
37
|
+
deploy:
|
|
38
|
+
needs: test
|
|
39
|
+
runs-on: ubuntu-latest
|
|
40
|
+
steps:
|
|
41
|
+
- uses: actions/checkout@v2
|
|
42
|
+
- uses: actions/setup-python@v2
|
|
43
|
+
with:
|
|
44
|
+
python-version: '3.x'
|
|
45
|
+
- id: check-tag
|
|
46
|
+
run: |
|
|
47
|
+
if [[ "${{ github.event.ref }}" =~ ^refs/tags/[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
|
48
|
+
echo ::set-output name=match::true
|
|
49
|
+
fi
|
|
50
|
+
- name: Create PyPI release
|
|
51
|
+
uses: casperdcl/deploy-pypi@v2
|
|
52
|
+
with:
|
|
53
|
+
password: ${{ secrets.PYPI_TOKEN }}
|
|
54
|
+
build: true
|
|
55
|
+
# only upload if a tag is pushed (otherwise just build & check)
|
|
56
|
+
upload: ${{ github.event_name == 'push' && steps.check-tag.outputs.match == 'true' }}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: deltabot-cli
|
|
3
|
-
Version:
|
|
3
|
+
Version: 3.0.0
|
|
4
4
|
Summary: Library to speedup Delta Chat bot development
|
|
5
|
-
Author-email: adbenitez <
|
|
5
|
+
Author-email: adbenitez <adb@merlinux.eu>
|
|
6
6
|
Keywords: deltachat,bot,deltabot-cli
|
|
7
7
|
Classifier: Development Status :: 4 - Beta
|
|
8
8
|
Classifier: Programming Language :: Python :: 3
|
|
9
9
|
Classifier: Intended Audience :: Developers
|
|
10
|
-
Requires-Python: >=3.
|
|
10
|
+
Requires-Python: >=3.8
|
|
11
11
|
Description-Content-Type: text/markdown
|
|
12
12
|
License-File: LICENSE
|
|
13
13
|
Requires-Dist: deltachat-rpc-server>=1.133.2
|
|
@@ -64,4 +64,4 @@ if __name__ == "__main__":
|
|
|
64
64
|
If you run the above script you will have a bot CLI, that allows to configure and run a bot.
|
|
65
65
|
A progress bar is displayed while the bot is configuring, and logs are pretty-printed.
|
|
66
66
|
|
|
67
|
-
For more examples check the [examples](
|
|
67
|
+
For more examples check the [examples](./examples) folder.
|
|
@@ -40,4 +40,4 @@ if __name__ == "__main__":
|
|
|
40
40
|
If you run the above script you will have a bot CLI, that allows to configure and run a bot.
|
|
41
41
|
A progress bar is displayed while the bot is configuring, and logs are pretty-printed.
|
|
42
42
|
|
|
43
|
-
For more examples check the [examples](
|
|
43
|
+
For more examples check the [examples](./examples) folder.
|
|
@@ -13,10 +13,13 @@ from rich.logging import RichHandler
|
|
|
13
13
|
|
|
14
14
|
from ._utils import AttrDict, ConfigProgressBar, parse_docstring
|
|
15
15
|
from .client import Bot
|
|
16
|
-
from .const import
|
|
17
|
-
from .events import EventFilter, HookCollection,
|
|
16
|
+
from .const import EventType
|
|
17
|
+
from .events import EventFilter, HookCollection, HookDecorator, RawEvent
|
|
18
18
|
from .rpc import JsonRpcError, Rpc
|
|
19
19
|
|
|
20
|
+
CliEventHook = Callable[[Bot, Namespace], None]
|
|
21
|
+
CmdCallback = Callable[["BotCli", Bot, Namespace], None]
|
|
22
|
+
|
|
20
23
|
|
|
21
24
|
class BotCli:
|
|
22
25
|
"""Class implementing a bot CLI.
|
|
@@ -32,15 +35,15 @@ class BotCli:
|
|
|
32
35
|
self._parser = ArgumentParser(app_name)
|
|
33
36
|
self._subparsers = self._parser.add_subparsers(title="subcommands")
|
|
34
37
|
self._hooks = HookCollection()
|
|
35
|
-
self._init_hooks: Set[
|
|
36
|
-
self._start_hooks: Set[
|
|
38
|
+
self._init_hooks: Set[CliEventHook] = set()
|
|
39
|
+
self._start_hooks: Set[CliEventHook] = set()
|
|
37
40
|
self._bot: Bot
|
|
38
41
|
|
|
39
|
-
def on(self, event: Union[type, EventFilter]) ->
|
|
42
|
+
def on(self, event: Union[type, EventFilter]) -> HookDecorator: # noqa
|
|
40
43
|
"""Register decorated function as listener for the given event."""
|
|
41
44
|
return self._hooks.on(event)
|
|
42
45
|
|
|
43
|
-
def on_init(self, func:
|
|
46
|
+
def on_init(self, func: CliEventHook) -> CliEventHook:
|
|
44
47
|
"""Register function to be called before the bot starts serving requests.
|
|
45
48
|
|
|
46
49
|
The function will receive the bot instance and the CLI arguments received.
|
|
@@ -52,7 +55,7 @@ class BotCli:
|
|
|
52
55
|
for func in self._init_hooks:
|
|
53
56
|
func(bot, args)
|
|
54
57
|
|
|
55
|
-
def on_start(self, func:
|
|
58
|
+
def on_start(self, func: CliEventHook) -> CliEventHook:
|
|
56
59
|
"""Register function to be called when the bot is about to start serving requests.
|
|
57
60
|
|
|
58
61
|
The function will receive the bot instance.
|
|
@@ -64,14 +67,6 @@ class BotCli:
|
|
|
64
67
|
for func in self._start_hooks:
|
|
65
68
|
func(bot, args)
|
|
66
69
|
|
|
67
|
-
def is_not_known_command(self, event: AttrDict) -> bool:
|
|
68
|
-
if not event.command.startswith(COMMAND_PREFIX):
|
|
69
|
-
return True
|
|
70
|
-
for hook in self._bot._hooks.get(NewMessage, []): # pylint:disable=W0212
|
|
71
|
-
if event.command == hook[1].command:
|
|
72
|
-
return False
|
|
73
|
-
return True
|
|
74
|
-
|
|
75
70
|
def add_generic_option(self, *flags, **kwargs) -> None:
|
|
76
71
|
"""Add a generic argument option to the CLI."""
|
|
77
72
|
if not (flags and flags[0].startswith("-")):
|
|
@@ -80,7 +75,7 @@ class BotCli:
|
|
|
80
75
|
|
|
81
76
|
def add_subcommand(
|
|
82
77
|
self,
|
|
83
|
-
func:
|
|
78
|
+
func: CmdCallback,
|
|
84
79
|
**kwargs,
|
|
85
80
|
) -> ArgumentParser:
|
|
86
81
|
"""Add a subcommand to the CLI."""
|
|
@@ -14,6 +14,7 @@ from .events import (
|
|
|
14
14
|
EventFilter,
|
|
15
15
|
GroupImageChanged,
|
|
16
16
|
GroupNameChanged,
|
|
17
|
+
HookCallback,
|
|
17
18
|
MemberListChanged,
|
|
18
19
|
NewMessage,
|
|
19
20
|
RawEvent,
|
|
@@ -27,7 +28,7 @@ class Client:
|
|
|
27
28
|
def __init__(
|
|
28
29
|
self,
|
|
29
30
|
rpc: Rpc,
|
|
30
|
-
hooks: Optional[Iterable[Tuple[
|
|
31
|
+
hooks: Optional[Iterable[Tuple[HookCallback, Union[type, EventFilter]]]] = None,
|
|
31
32
|
logger: Optional[logging.Logger] = None,
|
|
32
33
|
) -> None:
|
|
33
34
|
self.rpc = rpc
|
|
@@ -36,11 +37,11 @@ class Client:
|
|
|
36
37
|
self._should_process_messages = 0
|
|
37
38
|
self.add_hooks(hooks or [])
|
|
38
39
|
|
|
39
|
-
def add_hooks(self, hooks: Iterable[Tuple[
|
|
40
|
+
def add_hooks(self, hooks: Iterable[Tuple[HookCallback, Union[type, EventFilter]]]) -> None:
|
|
40
41
|
for hook, event in hooks:
|
|
41
42
|
self.add_hook(hook, event)
|
|
42
43
|
|
|
43
|
-
def add_hook(self, hook:
|
|
44
|
+
def add_hook(self, hook: HookCallback, event: Union[type, EventFilter] = RawEvent) -> None:
|
|
44
45
|
"""Register hook for the given event filter."""
|
|
45
46
|
if isinstance(event, type):
|
|
46
47
|
event = event()
|
|
@@ -53,7 +54,7 @@ class Client:
|
|
|
53
54
|
)
|
|
54
55
|
self._hooks.setdefault(type(event), set()).add((hook, event))
|
|
55
56
|
|
|
56
|
-
def remove_hook(self, hook:
|
|
57
|
+
def remove_hook(self, hook: HookCallback, event: Union[type, EventFilter]) -> None:
|
|
57
58
|
"""Unregister hook from the given event filter."""
|
|
58
59
|
if isinstance(event, type):
|
|
59
60
|
event = event()
|
|
@@ -17,6 +17,10 @@ from .const import EventType
|
|
|
17
17
|
|
|
18
18
|
if TYPE_CHECKING:
|
|
19
19
|
from ._utils import AttrDict
|
|
20
|
+
from .client import Bot
|
|
21
|
+
FilterCallback = Callable[["Bot", "AttrDict"], bool]
|
|
22
|
+
HookCallback = Callable[["Bot", "AttrDict"], None]
|
|
23
|
+
HookDecorator = Callable[[HookCallback], HookCallback]
|
|
20
24
|
|
|
21
25
|
|
|
22
26
|
def _tuple_of(obj, type_: type) -> tuple:
|
|
@@ -33,12 +37,11 @@ def _tuple_of(obj, type_: type) -> tuple:
|
|
|
33
37
|
class EventFilter(ABC):
|
|
34
38
|
"""The base event filter.
|
|
35
39
|
|
|
36
|
-
:param func: A Callable
|
|
37
|
-
|
|
38
|
-
should be dispatched or not.
|
|
40
|
+
:param func: A Callable that should accept the bot and event as input parameters,
|
|
41
|
+
and return a bool value indicating whether the event should be dispatched or not.
|
|
39
42
|
"""
|
|
40
43
|
|
|
41
|
-
def __init__(self, func: Optional[
|
|
44
|
+
def __init__(self, func: Optional[FilterCallback] = None):
|
|
42
45
|
self.func = func
|
|
43
46
|
|
|
44
47
|
@abstractmethod
|
|
@@ -52,13 +55,13 @@ class EventFilter(ABC):
|
|
|
52
55
|
def __ne__(self, other):
|
|
53
56
|
return not self == other
|
|
54
57
|
|
|
55
|
-
def _call_func(self, event) -> bool:
|
|
58
|
+
def _call_func(self, bot: "Bot", event: "AttrDict") -> bool:
|
|
56
59
|
if not self.func:
|
|
57
60
|
return True
|
|
58
|
-
return self.func(event)
|
|
61
|
+
return self.func(bot, event)
|
|
59
62
|
|
|
60
63
|
@abstractmethod
|
|
61
|
-
def filter(self, event):
|
|
64
|
+
def filter(self, bot: "Bot", event: "AttrDict"):
|
|
62
65
|
"""Return True-like value if the event passed the filter and should be
|
|
63
66
|
used, or False-like value otherwise.
|
|
64
67
|
"""
|
|
@@ -68,13 +71,16 @@ class RawEvent(EventFilter):
|
|
|
68
71
|
"""Matches raw core events.
|
|
69
72
|
|
|
70
73
|
:param types: The types of event to match.
|
|
71
|
-
:param func: A Callable
|
|
72
|
-
|
|
73
|
-
should be dispatched or not.
|
|
74
|
+
:param func: A Callable that should accept the bot and event as input parameter,
|
|
75
|
+
and return a bool value indicating whether the event should be dispatched or not.
|
|
74
76
|
"""
|
|
75
77
|
|
|
76
|
-
def __init__(
|
|
77
|
-
|
|
78
|
+
def __init__(
|
|
79
|
+
self,
|
|
80
|
+
types: Union[None, EventType, Iterable[EventType]] = None,
|
|
81
|
+
func: Optional[FilterCallback] = None,
|
|
82
|
+
):
|
|
83
|
+
super().__init__(func=func)
|
|
78
84
|
try:
|
|
79
85
|
self.types = _tuple_of(types, EventType)
|
|
80
86
|
except TypeError as err:
|
|
@@ -88,10 +94,10 @@ class RawEvent(EventFilter):
|
|
|
88
94
|
return (self.types, self.func) == (other.types, other.func)
|
|
89
95
|
return False
|
|
90
96
|
|
|
91
|
-
def filter(self, event: "AttrDict") -> bool:
|
|
97
|
+
def filter(self, bot: "Bot", event: "AttrDict") -> bool:
|
|
92
98
|
if self.types and event.kind not in self.types:
|
|
93
99
|
return False
|
|
94
|
-
return self._call_func(event)
|
|
100
|
+
return self._call_func(bot, event)
|
|
95
101
|
|
|
96
102
|
|
|
97
103
|
class NewMessage(EventFilter):
|
|
@@ -110,9 +116,8 @@ class NewMessage(EventFilter):
|
|
|
110
116
|
:param is_info: If set to True only match info/system messages, if set to False
|
|
111
117
|
only match messages that are not info/system messages. If omitted
|
|
112
118
|
info/system messages as well as normal messages will be matched.
|
|
113
|
-
:param func: A Callable
|
|
114
|
-
|
|
115
|
-
should be dispatched or not.
|
|
119
|
+
:param func: A Callable that should accept the bot and the event as input parameter,
|
|
120
|
+
and return a bool value indicating whether the event should be dispatched or not.
|
|
116
121
|
"""
|
|
117
122
|
|
|
118
123
|
def __init__(
|
|
@@ -126,7 +131,7 @@ class NewMessage(EventFilter):
|
|
|
126
131
|
command: Optional[str] = None,
|
|
127
132
|
is_bot: Optional[bool] = False,
|
|
128
133
|
is_info: Optional[bool] = None,
|
|
129
|
-
func: Optional[
|
|
134
|
+
func: Optional[FilterCallback] = None,
|
|
130
135
|
) -> None:
|
|
131
136
|
super().__init__(func=func)
|
|
132
137
|
self.is_bot = is_bot
|
|
@@ -165,7 +170,7 @@ class NewMessage(EventFilter):
|
|
|
165
170
|
)
|
|
166
171
|
return False
|
|
167
172
|
|
|
168
|
-
def filter(self, event: "AttrDict") -> bool:
|
|
173
|
+
def filter(self, bot: "Bot", event: "AttrDict") -> bool:
|
|
169
174
|
if self.is_bot is not None and self.is_bot != event.msg.is_bot:
|
|
170
175
|
return False
|
|
171
176
|
if self.is_info is not None and self.is_info != event.msg.is_info:
|
|
@@ -176,7 +181,7 @@ class NewMessage(EventFilter):
|
|
|
176
181
|
match = self.pattern(event.msg.text)
|
|
177
182
|
if not match:
|
|
178
183
|
return False
|
|
179
|
-
return super()._call_func(event)
|
|
184
|
+
return super()._call_func(bot, event)
|
|
180
185
|
|
|
181
186
|
|
|
182
187
|
class MemberListChanged(EventFilter):
|
|
@@ -188,9 +193,8 @@ class MemberListChanged(EventFilter):
|
|
|
188
193
|
:param added: If set to True only match if a member was added, if set to False
|
|
189
194
|
only match if a member was removed. If omitted both, member additions
|
|
190
195
|
and removals, will be matched.
|
|
191
|
-
:param func: A Callable
|
|
192
|
-
|
|
193
|
-
should be dispatched or not.
|
|
196
|
+
:param func: A Callable that should accept the bot and event as input parameter,
|
|
197
|
+
and return a bool value indicating whether the event should be dispatched or not.
|
|
194
198
|
"""
|
|
195
199
|
|
|
196
200
|
def __init__(self, added: Optional[bool] = None, **kwargs):
|
|
@@ -205,10 +209,10 @@ class MemberListChanged(EventFilter):
|
|
|
205
209
|
return (self.added, self.func) == (other.added, other.func)
|
|
206
210
|
return False
|
|
207
211
|
|
|
208
|
-
def filter(self, event: "AttrDict") -> bool:
|
|
212
|
+
def filter(self, bot: "Bot", event: "AttrDict") -> bool:
|
|
209
213
|
if self.added is not None and self.added != event.member_added:
|
|
210
214
|
return False
|
|
211
|
-
return self._call_func(event)
|
|
215
|
+
return self._call_func(bot, event)
|
|
212
216
|
|
|
213
217
|
|
|
214
218
|
class GroupImageChanged(EventFilter):
|
|
@@ -220,9 +224,8 @@ class GroupImageChanged(EventFilter):
|
|
|
220
224
|
:param deleted: If set to True only match if the image was deleted, if set to False
|
|
221
225
|
only match if a new image was set. If omitted both, image changes and
|
|
222
226
|
removals, will be matched.
|
|
223
|
-
:param func: A Callable
|
|
224
|
-
|
|
225
|
-
should be dispatched or not.
|
|
227
|
+
:param func: A Callable that should accept the bot and event as input parameter,
|
|
228
|
+
and return a bool value indicating whether the event should be dispatched or not.
|
|
226
229
|
"""
|
|
227
230
|
|
|
228
231
|
def __init__(self, deleted: Optional[bool] = None, **kwargs):
|
|
@@ -237,10 +240,10 @@ class GroupImageChanged(EventFilter):
|
|
|
237
240
|
return (self.deleted, self.func) == (other.deleted, other.func)
|
|
238
241
|
return False
|
|
239
242
|
|
|
240
|
-
def filter(self, event: "AttrDict") -> bool:
|
|
243
|
+
def filter(self, bot: "Bot", event: "AttrDict") -> bool:
|
|
241
244
|
if self.deleted is not None and self.deleted != event.image_deleted:
|
|
242
245
|
return False
|
|
243
|
-
return self._call_func(event)
|
|
246
|
+
return self._call_func(bot, event)
|
|
244
247
|
|
|
245
248
|
|
|
246
249
|
class GroupNameChanged(EventFilter):
|
|
@@ -249,9 +252,8 @@ class GroupNameChanged(EventFilter):
|
|
|
249
252
|
Warning: registering a handler for this event will cause the messages
|
|
250
253
|
to be marked as read. Its usage is mainly intended for bots.
|
|
251
254
|
|
|
252
|
-
:param func: A Callable
|
|
253
|
-
|
|
254
|
-
should be dispatched or not.
|
|
255
|
+
:param func: A Callable that should accept the bot and event as input parameter,
|
|
256
|
+
and return a bool value indicating whether the event should be dispatched or not.
|
|
255
257
|
"""
|
|
256
258
|
|
|
257
259
|
def __hash__(self) -> int:
|
|
@@ -262,8 +264,8 @@ class GroupNameChanged(EventFilter):
|
|
|
262
264
|
return self.func == other.func
|
|
263
265
|
return False
|
|
264
266
|
|
|
265
|
-
def filter(self, event: "AttrDict") -> bool:
|
|
266
|
-
return self._call_func(event)
|
|
267
|
+
def filter(self, bot: "Bot", event: "AttrDict") -> bool:
|
|
268
|
+
return self._call_func(bot, event)
|
|
267
269
|
|
|
268
270
|
|
|
269
271
|
class HookCollection:
|
|
@@ -272,18 +274,18 @@ class HookCollection:
|
|
|
272
274
|
"""
|
|
273
275
|
|
|
274
276
|
def __init__(self) -> None:
|
|
275
|
-
self._hooks: Set[Tuple[
|
|
277
|
+
self._hooks: Set[Tuple[HookCallback, Union[type, EventFilter]]] = set()
|
|
276
278
|
|
|
277
|
-
def __iter__(self) -> Iterator[Tuple[
|
|
279
|
+
def __iter__(self) -> Iterator[Tuple[HookCallback, Union[type, EventFilter]]]:
|
|
278
280
|
return iter(self._hooks)
|
|
279
281
|
|
|
280
|
-
def on(self, event: Union[type, EventFilter]) ->
|
|
282
|
+
def on(self, event: Union[type, EventFilter]) -> HookDecorator: # noqa
|
|
281
283
|
"""Register decorated function as listener for the given event."""
|
|
282
284
|
if isinstance(event, type):
|
|
283
285
|
event = event()
|
|
284
286
|
assert isinstance(event, EventFilter), "Invalid event filter"
|
|
285
287
|
|
|
286
|
-
def _decorator(func) ->
|
|
288
|
+
def _decorator(func: HookCallback) -> HookCallback:
|
|
287
289
|
self._hooks.add((func, event))
|
|
288
290
|
return func
|
|
289
291
|
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Utilities"""
|
|
2
|
+
|
|
3
|
+
from ._utils import AttrDict
|
|
4
|
+
from .client import Bot
|
|
5
|
+
from .const import COMMAND_PREFIX
|
|
6
|
+
from .events import NewMessage
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def is_not_known_command(bot: Bot, event: AttrDict) -> bool:
|
|
10
|
+
"""Filter helper to be used by NewMessage filters.
|
|
11
|
+
Matches all NewMessage that don't match a previously registered command filter."""
|
|
12
|
+
if not event.command.startswith(COMMAND_PREFIX):
|
|
13
|
+
return True
|
|
14
|
+
for hook in bot._hooks.get(NewMessage, []): # pylint:disable=W0212
|
|
15
|
+
if event.command == hook[1].command:
|
|
16
|
+
return False
|
|
17
|
+
return True
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: deltabot-cli
|
|
3
|
-
Version:
|
|
3
|
+
Version: 3.0.0
|
|
4
4
|
Summary: Library to speedup Delta Chat bot development
|
|
5
|
-
Author-email: adbenitez <
|
|
5
|
+
Author-email: adbenitez <adb@merlinux.eu>
|
|
6
6
|
Keywords: deltachat,bot,deltabot-cli
|
|
7
7
|
Classifier: Development Status :: 4 - Beta
|
|
8
8
|
Classifier: Programming Language :: Python :: 3
|
|
9
9
|
Classifier: Intended Audience :: Developers
|
|
10
|
-
Requires-Python: >=3.
|
|
10
|
+
Requires-Python: >=3.8
|
|
11
11
|
Description-Content-Type: text/markdown
|
|
12
12
|
License-File: LICENSE
|
|
13
13
|
Requires-Dist: deltachat-rpc-server>=1.133.2
|
|
@@ -64,4 +64,4 @@ if __name__ == "__main__":
|
|
|
64
64
|
If you run the above script you will have a bot CLI, that allows to configure and run a bot.
|
|
65
65
|
A progress bar is displayed while the bot is configuring, and logs are pretty-printed.
|
|
66
66
|
|
|
67
|
-
For more examples check the [examples](
|
|
67
|
+
For more examples check the [examples](./examples) folder.
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
.gitignore
|
|
1
2
|
LICENSE
|
|
2
3
|
README.md
|
|
4
|
+
pylama.ini
|
|
3
5
|
pyproject.toml
|
|
6
|
+
.github/workflows/python-ci.yml
|
|
4
7
|
deltabot_cli/__init__.py
|
|
5
8
|
deltabot_cli/_utils.py
|
|
6
9
|
deltabot_cli/cli.py
|
|
@@ -8,8 +11,11 @@ deltabot_cli/client.py
|
|
|
8
11
|
deltabot_cli/const.py
|
|
9
12
|
deltabot_cli/events.py
|
|
10
13
|
deltabot_cli/rpc.py
|
|
14
|
+
deltabot_cli/utils.py
|
|
11
15
|
deltabot_cli.egg-info/PKG-INFO
|
|
12
16
|
deltabot_cli.egg-info/SOURCES.txt
|
|
13
17
|
deltabot_cli.egg-info/dependency_links.txt
|
|
14
18
|
deltabot_cli.egg-info/requires.txt
|
|
15
|
-
deltabot_cli.egg-info/top_level.txt
|
|
19
|
+
deltabot_cli.egg-info/top_level.txt
|
|
20
|
+
examples/echobot.py
|
|
21
|
+
examples/echobot_advanced.py
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Minimal echo-bot example."""
|
|
3
|
+
|
|
4
|
+
from deltabot_cli import BotCli, events
|
|
5
|
+
|
|
6
|
+
cli = BotCli("echobot")
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@cli.on(events.RawEvent)
|
|
10
|
+
def log_event(bot, _accid, event):
|
|
11
|
+
bot.logger.info(event)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@cli.on(events.NewMessage)
|
|
15
|
+
def echo(bot, accid, event):
|
|
16
|
+
msg = event.msg
|
|
17
|
+
bot.rpc.misc_send_text_message(accid, msg.chat_id, msg.text)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
if __name__ == "__main__":
|
|
21
|
+
cli.start()
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Advanced echo bot example."""
|
|
3
|
+
from deltabot_cli import BotCli, EventType, events
|
|
4
|
+
|
|
5
|
+
cli = BotCli("echobot")
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@cli.on(events.RawEvent)
|
|
9
|
+
def log_event(bot, _accid, event):
|
|
10
|
+
if event.kind == EventType.INFO:
|
|
11
|
+
bot.logger.info(event.msg)
|
|
12
|
+
elif event.kind == EventType.WARNING:
|
|
13
|
+
bot.logger.warning(event.msg)
|
|
14
|
+
elif event.kind == EventType.ERROR:
|
|
15
|
+
bot.logger.error(event.msg)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@cli.on(events.NewMessage)
|
|
19
|
+
def echo(bot, accid, event):
|
|
20
|
+
msg = event.msg
|
|
21
|
+
bot.rpc.misc_send_text_message(accid, msg.chat_id, msg.text)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@cli.on_init
|
|
25
|
+
def on_init(bot, args):
|
|
26
|
+
bot.logger.info("Initializing bot with args: %s", args)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@cli.on_start
|
|
30
|
+
def on_start(bot, _args):
|
|
31
|
+
bot.logger.info("Running bot...")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test(_cli, bot, args):
|
|
35
|
+
"""just some example subcommand"""
|
|
36
|
+
bot.logger.info("Hello %s, this is an example subcommand!", args.name)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
if __name__ == "__main__":
|
|
40
|
+
subcmd = cli.add_subcommand(test)
|
|
41
|
+
subcmd.add_argument("name", help="your name")
|
|
42
|
+
|
|
43
|
+
try:
|
|
44
|
+
cli.start()
|
|
45
|
+
except KeyboardInterrupt:
|
|
46
|
+
pass
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
[build-system]
|
|
2
|
-
requires = ["setuptools"]
|
|
2
|
+
requires = ["setuptools>=64", "setuptools_scm>=8"]
|
|
3
3
|
build-backend = "setuptools.build_meta"
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
|
-
version = "2.0.0"
|
|
7
6
|
name = "deltabot-cli"
|
|
8
7
|
description = "Library to speedup Delta Chat bot development"
|
|
8
|
+
dynamic = ["version"]
|
|
9
9
|
readme = "README.md"
|
|
10
|
-
requires-python = ">=3.
|
|
10
|
+
requires-python = ">=3.8"
|
|
11
11
|
license = {file = "LICENSE.txt"}
|
|
12
12
|
keywords = ["deltachat", "bot", "deltabot-cli"]
|
|
13
13
|
authors = [
|
|
14
|
-
{name = "adbenitez", email = "
|
|
14
|
+
{name = "adbenitez", email = "adb@merlinux.eu"},
|
|
15
15
|
]
|
|
16
16
|
classifiers = [
|
|
17
17
|
"Development Status :: 4 - Beta",
|
|
@@ -35,6 +35,9 @@ dev = [
|
|
|
35
35
|
"pytest",
|
|
36
36
|
]
|
|
37
37
|
|
|
38
|
+
[tool.setuptools_scm]
|
|
39
|
+
# can be empty if no extra settings are needed, presence enables setuptools_scm
|
|
40
|
+
|
|
38
41
|
[tool.black]
|
|
39
42
|
line-length = 100
|
|
40
43
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|