roboherd 0.1.7__tar.gz → 0.1.9__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.
Potentially problematic release.
This version of roboherd might be problematic. Click here for more details.
- {roboherd-0.1.7 → roboherd-0.1.9}/.woodpecker/create_release.yml +1 -2
- {roboherd-0.1.7 → roboherd-0.1.9}/.woodpecker/publish_docker.yml +1 -1
- {roboherd-0.1.7 → roboherd-0.1.9}/CHANGES.md +11 -0
- roboherd-0.1.9/PKG-INFO +78 -0
- roboherd-0.1.9/README.md +57 -0
- {roboherd-0.1.7 → roboherd-0.1.9}/docs/index.md +10 -1
- {roboherd-0.1.7 → roboherd-0.1.9}/mkdocs.yml +2 -0
- {roboherd-0.1.7 → roboherd-0.1.9}/pyproject.toml +2 -2
- {roboherd-0.1.7 → roboherd-0.1.9}/roboherd/__main__.py +1 -0
- {roboherd-0.1.7 → roboherd-0.1.9}/roboherd/cow/__init__.py +20 -19
- roboherd-0.1.9/roboherd/cow/test_init.py +41 -0
- {roboherd-0.1.7 → roboherd-0.1.9}/roboherd/examples/rooster.py +1 -0
- {roboherd-0.1.7 → roboherd-0.1.9}/roboherd/herd/__init__.py +2 -0
- {roboherd-0.1.7 → roboherd-0.1.9}/roboherd/herd/manager/config.py +30 -6
- {roboherd-0.1.7 → roboherd-0.1.9}/roboherd/herd/manager/test_config.py +2 -0
- {roboherd-0.1.7 → roboherd-0.1.9}/roboherd/herd/processor.py +4 -1
- {roboherd-0.1.7 → roboherd-0.1.9}/roboherd/herd/scheduler.py +19 -4
- roboherd-0.1.9/uv.lock +1886 -0
- roboherd-0.1.7/PKG-INFO +0 -27
- roboherd-0.1.7/README.md +0 -6
- roboherd-0.1.7/roboherd/cow/test_init.py +0 -57
- roboherd-0.1.7/uv.lock +0 -1663
- {roboherd-0.1.7 → roboherd-0.1.9}/.gitignore +0 -0
- {roboherd-0.1.7 → roboherd-0.1.9}/.woodpecker/publish_pypi.yml +0 -0
- {roboherd-0.1.7 → roboherd-0.1.9}/.woodpecker/test.yml +0 -0
- {roboherd-0.1.7 → roboherd-0.1.9}/.woodpecker/website.yml +0 -0
- {roboherd-0.1.7 → roboherd-0.1.9}/docs/annotations.md +0 -0
- {roboherd-0.1.7 → roboherd-0.1.9}/docs/assets/bull-horns.png +0 -0
- {roboherd-0.1.7 → roboherd-0.1.9}/docs/assets/mastodon.png +0 -0
- {roboherd-0.1.7 → roboherd-0.1.9}/docs/cli.md +0 -0
- {roboherd-0.1.7 → roboherd-0.1.9}/docs/cow.md +0 -0
- {roboherd-0.1.7 → roboherd-0.1.9}/docs/herd.md +0 -0
- {roboherd-0.1.7 → roboherd-0.1.9}/docs/main.md +0 -0
- {roboherd-0.1.7 → roboherd-0.1.9}/docs/util.md +0 -0
- {roboherd-0.1.7 → roboherd-0.1.9}/resources/docker/Dockerfile +0 -0
- {roboherd-0.1.7 → roboherd-0.1.9}/resources/docker/build.sh +0 -0
- {roboherd-0.1.7 → roboherd-0.1.9}/roboherd/__init__.py +0 -0
- {roboherd-0.1.7 → roboherd-0.1.9}/roboherd/annotations/__init__.py +0 -0
- {roboherd-0.1.7 → roboherd-0.1.9}/roboherd/annotations/bovine.py +0 -0
- {roboherd-0.1.7 → roboherd-0.1.9}/roboherd/annotations/common.py +0 -0
- {roboherd-0.1.7 → roboherd-0.1.9}/roboherd/cow/const.py +0 -0
- {roboherd-0.1.7 → roboherd-0.1.9}/roboherd/cow/handlers.py +0 -0
- {roboherd-0.1.7 → roboherd-0.1.9}/roboherd/cow/profile.py +0 -0
- {roboherd-0.1.7 → roboherd-0.1.9}/roboherd/cow/test_handlers.py +0 -0
- {roboherd-0.1.7 → roboherd-0.1.9}/roboherd/cow/test_profile.py +0 -0
- {roboherd-0.1.7 → roboherd-0.1.9}/roboherd/cow/test_util.py +0 -0
- {roboherd-0.1.7 → roboherd-0.1.9}/roboherd/cow/types.py +0 -0
- {roboherd-0.1.7 → roboherd-0.1.9}/roboherd/cow/util.py +0 -0
- {roboherd-0.1.7 → roboherd-0.1.9}/roboherd/examples/__init__.py +0 -0
- {roboherd-0.1.7 → roboherd-0.1.9}/roboherd/examples/dev_null.py +0 -0
- {roboherd-0.1.7 → roboherd-0.1.9}/roboherd/examples/json_echo.py +0 -0
- {roboherd-0.1.7 → roboherd-0.1.9}/roboherd/examples/meta.py +0 -0
- {roboherd-0.1.7 → roboherd-0.1.9}/roboherd/examples/moocow.py +0 -0
- {roboherd-0.1.7 → roboherd-0.1.9}/roboherd/examples/number.py +0 -0
- {roboherd-0.1.7 → roboherd-0.1.9}/roboherd/examples/scarecrow.py +0 -0
- {roboherd-0.1.7 → roboherd-0.1.9}/roboherd/herd/builder.py +0 -0
- {roboherd-0.1.7 → roboherd-0.1.9}/roboherd/herd/manager/__init__.py +0 -0
- {roboherd-0.1.7 → roboherd-0.1.9}/roboherd/herd/manager/load.py +0 -0
- {roboherd-0.1.7 → roboherd-0.1.9}/roboherd/herd/manager/test_load.py +0 -0
- {roboherd-0.1.7 → roboherd-0.1.9}/roboherd/herd/manager/test_manager.py +0 -0
- {roboherd-0.1.7 → roboherd-0.1.9}/roboherd/herd/test_herd.py +0 -0
- {roboherd-0.1.7 → roboherd-0.1.9}/roboherd/herd/test_scheduler.py +0 -0
- {roboherd-0.1.7 → roboherd-0.1.9}/roboherd/herd/types.py +0 -0
- {roboherd-0.1.7 → roboherd-0.1.9}/roboherd/register.py +0 -0
- {roboherd-0.1.7 → roboherd-0.1.9}/roboherd/test_validators.py +0 -0
- {roboherd-0.1.7 → roboherd-0.1.9}/roboherd/util.py +0 -0
- {roboherd-0.1.7 → roboherd-0.1.9}/roboherd/validators.py +0 -0
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Changes to roboherd
|
|
2
2
|
|
|
3
|
+
## 0.1.9
|
|
4
|
+
|
|
5
|
+
- Ensure roboherd terminates on connection close [roboherd#1](https://codeberg.org/bovine/roboherd/issues/1)
|
|
6
|
+
- Make doc links clickable [roboherd#44](https://codeberg.org/bovine/roboherd/issues/44)
|
|
7
|
+
- Document how to work with cattle_grid in `README.md`
|
|
8
|
+
|
|
9
|
+
## 0.1.8 ([Milestone](https://codeberg.org/bovine/roboherd/milestone/11129))
|
|
10
|
+
|
|
11
|
+
- Add ConfigOverrides model [roboherd#43](https://codeberg.org/bovine/roboherd/issues/43)
|
|
12
|
+
- Allow to skip startup [roboherd#41](https://codeberg.org/bovine/roboherd/issues/41)
|
|
13
|
+
|
|
3
14
|
## 0.1.7 ([Milestone](https://codeberg.org/bovine/roboherd/milestone/10741))
|
|
4
15
|
|
|
5
16
|
- Repair docker file [roboherd#38](https://codeberg.org/bovine/roboherd/issues/38)
|
roboherd-0.1.9/PKG-INFO
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: roboherd
|
|
3
|
+
Version: 0.1.9
|
|
4
|
+
Summary: A Fediverse bot framework
|
|
5
|
+
Project-URL: Documentation, https://bovine.codeberg.page/roboherd/
|
|
6
|
+
Project-URL: Repository, https://codeberg.org/bovine/roboherd
|
|
7
|
+
Requires-Python: >=3.11
|
|
8
|
+
Requires-Dist: aiohttp>=3.11.12
|
|
9
|
+
Requires-Dist: almabtrieb[mqtt]>=0.2
|
|
10
|
+
Requires-Dist: apscheduler>=3.11.0
|
|
11
|
+
Requires-Dist: click>=8.1.8
|
|
12
|
+
Requires-Dist: cron-descriptor>=1.4.5
|
|
13
|
+
Requires-Dist: dynaconf>=3.2.6
|
|
14
|
+
Requires-Dist: fast-depends>=2.4.12
|
|
15
|
+
Requires-Dist: tomli-w>=1.1.0
|
|
16
|
+
Requires-Dist: watchfiles>=1.0.4
|
|
17
|
+
Provides-Extra: bovine
|
|
18
|
+
Requires-Dist: bovine>=0.5.15; extra == 'bovine'
|
|
19
|
+
Requires-Dist: markdown>=3.7; extra == 'bovine'
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
|
|
22
|
+
# Roboherd
|
|
23
|
+
|
|
24
|
+
Roboherd is a framework for building Fediverse bots
|
|
25
|
+
using the [Cattle Drive Protocol](https://bovine.codeberg.page/cattle_grid/cattle_drive/).
|
|
26
|
+
|
|
27
|
+
For more information, see the [documentation](https://bovine.codeberg.page/roboherd/) or the [repository](https://codeberg.org/bovine/roboherd/).
|
|
28
|
+
|
|
29
|
+
## Developping with cattle_grid
|
|
30
|
+
|
|
31
|
+
In your catle_grid `config` directory add a roboherd user, e.g.
|
|
32
|
+
a file `testing.toml` with content
|
|
33
|
+
|
|
34
|
+
```toml
|
|
35
|
+
[testing]
|
|
36
|
+
enable = true
|
|
37
|
+
|
|
38
|
+
[[testing.accounts]]
|
|
39
|
+
name = "herd"
|
|
40
|
+
password = "pass"
|
|
41
|
+
permissions = ["admin"]
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Configure roboherd via `roboherd.toml`, e.g.
|
|
45
|
+
|
|
46
|
+
```toml
|
|
47
|
+
base_url = "http://abel"
|
|
48
|
+
connection_string = "ws://herd:pass@localhost:3000/ws/"
|
|
49
|
+
|
|
50
|
+
[cow.rooster]
|
|
51
|
+
bot = "roboherd.examples.rooster:bot"
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
This will trigger a periodic message to cattle_grid.
|
|
55
|
+
|
|
56
|
+
### nginx for cattle_grid
|
|
57
|
+
|
|
58
|
+
The nginx in the `cattle_grid` configuration should forward the path `/ws/` to
|
|
59
|
+
rabbitmq (supporting mqtt over websockets)
|
|
60
|
+
|
|
61
|
+
```nginx
|
|
62
|
+
server {
|
|
63
|
+
listen 80;
|
|
64
|
+
|
|
65
|
+
location /ws/ {
|
|
66
|
+
proxy_pass http://rabbitmq:15675;
|
|
67
|
+
proxy_http_version 1.1;
|
|
68
|
+
proxy_set_header Upgrade $http_upgrade;
|
|
69
|
+
proxy_set_header Connection $connection_upgrade;
|
|
70
|
+
proxy_read_timeout 86400; # neccessary to avoid websocket timeout disconnect
|
|
71
|
+
proxy_send_timeout 86400; # neccessary to avoid websocket timeout disconnect
|
|
72
|
+
proxy_redirect off;
|
|
73
|
+
proxy_buffering off;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
similarly `nginx` should forward port 80 to 3000 (in the docker compose file).
|
roboherd-0.1.9/README.md
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Roboherd
|
|
2
|
+
|
|
3
|
+
Roboherd is a framework for building Fediverse bots
|
|
4
|
+
using the [Cattle Drive Protocol](https://bovine.codeberg.page/cattle_grid/cattle_drive/).
|
|
5
|
+
|
|
6
|
+
For more information, see the [documentation](https://bovine.codeberg.page/roboherd/) or the [repository](https://codeberg.org/bovine/roboherd/).
|
|
7
|
+
|
|
8
|
+
## Developping with cattle_grid
|
|
9
|
+
|
|
10
|
+
In your catle_grid `config` directory add a roboherd user, e.g.
|
|
11
|
+
a file `testing.toml` with content
|
|
12
|
+
|
|
13
|
+
```toml
|
|
14
|
+
[testing]
|
|
15
|
+
enable = true
|
|
16
|
+
|
|
17
|
+
[[testing.accounts]]
|
|
18
|
+
name = "herd"
|
|
19
|
+
password = "pass"
|
|
20
|
+
permissions = ["admin"]
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Configure roboherd via `roboherd.toml`, e.g.
|
|
24
|
+
|
|
25
|
+
```toml
|
|
26
|
+
base_url = "http://abel"
|
|
27
|
+
connection_string = "ws://herd:pass@localhost:3000/ws/"
|
|
28
|
+
|
|
29
|
+
[cow.rooster]
|
|
30
|
+
bot = "roboherd.examples.rooster:bot"
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
This will trigger a periodic message to cattle_grid.
|
|
34
|
+
|
|
35
|
+
### nginx for cattle_grid
|
|
36
|
+
|
|
37
|
+
The nginx in the `cattle_grid` configuration should forward the path `/ws/` to
|
|
38
|
+
rabbitmq (supporting mqtt over websockets)
|
|
39
|
+
|
|
40
|
+
```nginx
|
|
41
|
+
server {
|
|
42
|
+
listen 80;
|
|
43
|
+
|
|
44
|
+
location /ws/ {
|
|
45
|
+
proxy_pass http://rabbitmq:15675;
|
|
46
|
+
proxy_http_version 1.1;
|
|
47
|
+
proxy_set_header Upgrade $http_upgrade;
|
|
48
|
+
proxy_set_header Connection $connection_upgrade;
|
|
49
|
+
proxy_read_timeout 86400; # neccessary to avoid websocket timeout disconnect
|
|
50
|
+
proxy_send_timeout 86400; # neccessary to avoid websocket timeout disconnect
|
|
51
|
+
proxy_redirect off;
|
|
52
|
+
proxy_buffering off;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
similarly `nginx` should forward port 80 to 3000 (in the docker compose file).
|
|
@@ -5,6 +5,15 @@ so by connecting to a server through the [Cattle Drive protocol](https://bovine.
|
|
|
5
5
|
configured the connection, all other tasks can be done using
|
|
6
6
|
python code and a toml configuration file.
|
|
7
7
|
|
|
8
|
+
The serer roboherd connects to is specified through a connection string,
|
|
9
|
+
see [Configuration](#configuration).
|
|
10
|
+
|
|
11
|
+
## Usage examples
|
|
12
|
+
|
|
13
|
+
- Examples of basic usage can be found in [roboherd.examples](https://codeberg.org/bovine/roboherd/src/branch/main/roboherd/examples)
|
|
14
|
+
- [release_helper](https://codeberg.org/helge/release_helper) automates the release of projects on codeberg and subsequent announcement on the Fediverse
|
|
15
|
+
|
|
16
|
+
|
|
8
17
|
## Tour of the functionality
|
|
9
18
|
|
|
10
19
|
We will now tour how to write bots with Roboherd. We start with
|
|
@@ -122,7 +131,7 @@ handle = "nothingness"
|
|
|
122
131
|
```
|
|
123
132
|
|
|
124
133
|
one would instead create a bot with the handle `acct:nothingness@dev.bovine.social`.
|
|
125
|
-
|
|
134
|
+
The full list of options can be found in [roboherd.herd.manager.config.ConfigOverrides][].
|
|
126
135
|
|
|
127
136
|
### Posting on startup
|
|
128
137
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "roboherd"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.9"
|
|
4
4
|
description = "A Fediverse bot framework"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.11"
|
|
7
7
|
dependencies = [
|
|
8
8
|
"aiohttp>=3.11.12",
|
|
9
|
-
"almabtrieb[mqtt]>=0.
|
|
9
|
+
"almabtrieb[mqtt]>=0.2",
|
|
10
10
|
"apscheduler>=3.11.0",
|
|
11
11
|
"click>=8.1.8",
|
|
12
12
|
"cron-descriptor>=1.4.5",
|
|
@@ -76,6 +76,13 @@ class RoboCow:
|
|
|
76
76
|
),
|
|
77
77
|
)
|
|
78
78
|
|
|
79
|
+
skip_profile_update: bool = field(
|
|
80
|
+
default=False,
|
|
81
|
+
metadata=dict(
|
|
82
|
+
description="When set to True the profile is not updated automatically. Useful when managing a cow from multiple scripts."
|
|
83
|
+
),
|
|
84
|
+
)
|
|
85
|
+
|
|
79
86
|
internals: RoboCowInternals = field(
|
|
80
87
|
default_factory=RoboCowInternals,
|
|
81
88
|
metadata=dict(description="Internal data for the cow"),
|
|
@@ -166,19 +173,6 @@ class RoboCow:
|
|
|
166
173
|
|
|
167
174
|
self.internals.startup_routine = func
|
|
168
175
|
|
|
169
|
-
def needs_update(self):
|
|
170
|
-
"""Checks if the cow needs to be updated"""
|
|
171
|
-
if self.internals.profile is None:
|
|
172
|
-
return True
|
|
173
|
-
|
|
174
|
-
if self.information.name != self.internals.profile.get("name"):
|
|
175
|
-
return True
|
|
176
|
-
|
|
177
|
-
if self.information.description != self.internals.profile.get("summary"):
|
|
178
|
-
return True
|
|
179
|
-
|
|
180
|
-
return False
|
|
181
|
-
|
|
182
176
|
async def run_startup(self, connection: Almabtrieb):
|
|
183
177
|
"""Runs when the cow is birthed"""
|
|
184
178
|
|
|
@@ -198,12 +192,8 @@ class RoboCow:
|
|
|
198
192
|
)
|
|
199
193
|
self.information.frequency = frequency
|
|
200
194
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
if update:
|
|
204
|
-
logger.info("Updating profile for %s", self.information.handle)
|
|
205
|
-
|
|
206
|
-
await connection.trigger("update_actor", update)
|
|
195
|
+
if not self.skip_profile_update:
|
|
196
|
+
await self._run_profile_update(connection)
|
|
207
197
|
|
|
208
198
|
if self.internals.startup_routine:
|
|
209
199
|
await inject(self.internals.startup_routine)(
|
|
@@ -211,3 +201,14 @@ class RoboCow:
|
|
|
211
201
|
connection=connection, # type:ignore
|
|
212
202
|
actor_id=self.internals.actor_id, # type:ignore
|
|
213
203
|
) # type:ignore
|
|
204
|
+
|
|
205
|
+
async def _run_profile_update(self, connection: Almabtrieb):
|
|
206
|
+
if self.internals.profile is None:
|
|
207
|
+
raise ValueError("Profile is not set")
|
|
208
|
+
|
|
209
|
+
update = determine_profile_update(self.information, self.internals.profile)
|
|
210
|
+
|
|
211
|
+
if update:
|
|
212
|
+
logger.info("Updating profile for %s", self.information.handle)
|
|
213
|
+
|
|
214
|
+
await connection.trigger("update_actor", update)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from unittest.mock import AsyncMock
|
|
2
|
+
|
|
3
|
+
from . import RoboCow
|
|
4
|
+
from .types import Information
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def test_cron():
|
|
8
|
+
info = Information(handle="testcow")
|
|
9
|
+
cow = RoboCow(information=info)
|
|
10
|
+
|
|
11
|
+
@cow.cron("* * * * *")
|
|
12
|
+
async def test_func():
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
assert len(cow.internals.cron_entries) == 1
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
async def test_startup():
|
|
19
|
+
info = Information(handle="testcow")
|
|
20
|
+
cow = RoboCow(information=info)
|
|
21
|
+
cow.internals.profile = {"id": "http://host.test/actor/cow"}
|
|
22
|
+
mock = AsyncMock()
|
|
23
|
+
connection = AsyncMock()
|
|
24
|
+
|
|
25
|
+
cow.startup(mock)
|
|
26
|
+
|
|
27
|
+
await cow.run_startup(connection=connection)
|
|
28
|
+
|
|
29
|
+
mock.assert_called_once()
|
|
30
|
+
connection.trigger.assert_awaited_once()
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
async def test_skip_startup():
|
|
34
|
+
info = Information(handle="testcow")
|
|
35
|
+
cow = RoboCow(information=info, skip_profile_update=True)
|
|
36
|
+
cow.internals.profile = {"id": "http://host.test/actor/cow"}
|
|
37
|
+
|
|
38
|
+
connection = AsyncMock()
|
|
39
|
+
await cow.run_startup(connection=connection)
|
|
40
|
+
|
|
41
|
+
connection.trigger.assert_not_awaited()
|
|
@@ -1,9 +1,32 @@
|
|
|
1
|
+
from pydantic import BaseModel, Field
|
|
2
|
+
|
|
1
3
|
from dataclasses import dataclass, field
|
|
2
4
|
|
|
3
5
|
from roboherd.cow import RoboCow
|
|
4
6
|
from .load import load_cow
|
|
5
7
|
|
|
6
8
|
|
|
9
|
+
class ConfigOverrides(BaseModel):
|
|
10
|
+
"""Values used in `roboherd.toml` to overide the default
|
|
11
|
+
values in the imported cow. This class is meant as a
|
|
12
|
+
reference, and not meant to be directly used."""
|
|
13
|
+
|
|
14
|
+
name: str | None = Field(
|
|
15
|
+
default=None, description="set to override the name", examples=["New name"]
|
|
16
|
+
)
|
|
17
|
+
handle: str | None = Field(
|
|
18
|
+
default=None, description="set to override the handle", examples=["new-handle"]
|
|
19
|
+
)
|
|
20
|
+
base_url: str | None = Field(
|
|
21
|
+
default=None,
|
|
22
|
+
description="set to override the base url",
|
|
23
|
+
examples=["https://other.example"],
|
|
24
|
+
)
|
|
25
|
+
skip_profile_update: bool | None = Field(
|
|
26
|
+
default=None, description="set to skip updating the profile", examples=[True]
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
7
30
|
@dataclass
|
|
8
31
|
class CowConfig:
|
|
9
32
|
name: str = field(metadata={"description": "Name of the cow, must be unique"})
|
|
@@ -20,13 +43,14 @@ class CowConfig:
|
|
|
20
43
|
def load(self) -> RoboCow:
|
|
21
44
|
cow = load_cow(self.module, self.attribute)
|
|
22
45
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
46
|
+
overrides = ConfigOverrides(**self.config)
|
|
47
|
+
|
|
48
|
+
for value in ["name", "handle", "base_url"]:
|
|
49
|
+
if getattr(overrides, value):
|
|
50
|
+
setattr(cow.information, value, getattr(overrides, value))
|
|
27
51
|
|
|
28
|
-
if
|
|
29
|
-
cow.
|
|
52
|
+
if overrides.skip_profile_update:
|
|
53
|
+
cow.skip_profile_update = overrides.skip_profile_update
|
|
30
54
|
|
|
31
55
|
return cow
|
|
32
56
|
|
|
@@ -44,6 +44,7 @@ def test_from_name_and_dict_with_new_name():
|
|
|
44
44
|
"bot": "roboherd.examples.moocow:moocow",
|
|
45
45
|
"handle": "new_handle",
|
|
46
46
|
"name": "new name",
|
|
47
|
+
"skip_profile_update": True,
|
|
47
48
|
}
|
|
48
49
|
|
|
49
50
|
config = CowConfig.from_name_and_dict(name, value)
|
|
@@ -52,6 +53,7 @@ def test_from_name_and_dict_with_new_name():
|
|
|
52
53
|
|
|
53
54
|
assert cow.information.name == "new name"
|
|
54
55
|
assert cow.information.handle == "new_handle"
|
|
56
|
+
assert cow.skip_profile_update is True
|
|
55
57
|
|
|
56
58
|
|
|
57
59
|
def test_load_config(test_config):
|
|
@@ -28,12 +28,15 @@ class HerdProcessor:
|
|
|
28
28
|
for cow in self.incoming_handlers:
|
|
29
29
|
actor_id_to_cow_map[cow.internals.actor_id] = cow
|
|
30
30
|
|
|
31
|
+
logger.info("Incoming processing started for %s cows", len(actor_id_to_cow_map))
|
|
32
|
+
|
|
31
33
|
async for msg in connection.incoming():
|
|
32
34
|
actor_id = msg["actor"]
|
|
33
35
|
|
|
34
36
|
cow = actor_id_to_cow_map.get(actor_id)
|
|
35
|
-
logger.info(cow)
|
|
36
37
|
if cow:
|
|
37
38
|
await cow.internals.handlers.handle(
|
|
38
39
|
msg, "incoming", connection, actor_id, cow=cow
|
|
39
40
|
)
|
|
41
|
+
|
|
42
|
+
logger.warning("Process incoming ended")
|
|
@@ -19,22 +19,30 @@ logger = logging.getLogger(__name__)
|
|
|
19
19
|
class HerdScheduler:
|
|
20
20
|
entries: List[Tuple[RoboCow, CronEntry]]
|
|
21
21
|
connection: Almabtrieb
|
|
22
|
+
scheduler: AsyncIOScheduler | None = None
|
|
23
|
+
task: asyncio.Task | None = None
|
|
22
24
|
|
|
23
25
|
def create_task(self, task_group: asyncio.TaskGroup):
|
|
24
26
|
if len(self.entries) == 0:
|
|
25
27
|
logger.info("No tasks to schedule")
|
|
26
28
|
return
|
|
27
|
-
|
|
29
|
+
if self.task:
|
|
30
|
+
raise Exception("Task already running")
|
|
31
|
+
|
|
32
|
+
self.task = task_group.create_task(self.run())
|
|
28
33
|
|
|
29
34
|
async def run(self):
|
|
30
35
|
if len(self.entries) == 0:
|
|
31
36
|
return
|
|
32
37
|
|
|
33
|
-
scheduler
|
|
38
|
+
if self.scheduler:
|
|
39
|
+
raise Exception("Scheduler already exists")
|
|
40
|
+
|
|
41
|
+
self.scheduler = AsyncIOScheduler()
|
|
34
42
|
|
|
35
43
|
for cow, entry in self.entries:
|
|
36
44
|
trigger = CronTrigger.from_crontab(entry.crontab)
|
|
37
|
-
scheduler.add_job(
|
|
45
|
+
self.scheduler.add_job(
|
|
38
46
|
inject(entry.func),
|
|
39
47
|
trigger=trigger,
|
|
40
48
|
kwargs={
|
|
@@ -44,7 +52,14 @@ class HerdScheduler:
|
|
|
44
52
|
},
|
|
45
53
|
)
|
|
46
54
|
|
|
47
|
-
scheduler.start()
|
|
55
|
+
self.scheduler.start()
|
|
48
56
|
|
|
49
57
|
while True:
|
|
50
58
|
await asyncio.sleep(60 * 60)
|
|
59
|
+
|
|
60
|
+
async def stop(self):
|
|
61
|
+
logger.warning("Stopping scheduler")
|
|
62
|
+
if self.scheduler:
|
|
63
|
+
self.scheduler.shutdown()
|
|
64
|
+
if self.task:
|
|
65
|
+
self.task.cancel()
|