musigate 0.1.0a2__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.
- musigate-0.1.0a2/LICENSE +21 -0
- musigate-0.1.0a2/PKG-INFO +233 -0
- musigate-0.1.0a2/README.md +193 -0
- musigate-0.1.0a2/pyproject.toml +72 -0
- musigate-0.1.0a2/setup.cfg +4 -0
- musigate-0.1.0a2/src/musigate/__init__.py +1 -0
- musigate-0.1.0a2/src/musigate/adapters/__init__.py +0 -0
- musigate-0.1.0a2/src/musigate/adapters/loader.py +149 -0
- musigate-0.1.0a2/src/musigate/cli.py +442 -0
- musigate-0.1.0a2/src/musigate/gateway/__init__.py +0 -0
- musigate-0.1.0a2/src/musigate/gateway/engine.py +47 -0
- musigate-0.1.0a2/src/musigate/gateway/executor.py +186 -0
- musigate-0.1.0a2/src/musigate/gateway/selector.py +63 -0
- musigate-0.1.0a2/src/musigate/resources/__init__.py +1 -0
- musigate-0.1.0a2/src/musigate/resources/bots/music163.yaml +40 -0
- musigate-0.1.0a2/src/musigate/resources/bots/music_v1.yaml +40 -0
- musigate-0.1.0a2/src/musigate/resources/config/settings.yaml +20 -0
- musigate-0.1.0a2/src/musigate/telegram/__init__.py +0 -0
- musigate-0.1.0a2/src/musigate/telegram/auth.py +95 -0
- musigate-0.1.0a2/src/musigate/telegram/client.py +32 -0
- musigate-0.1.0a2/src/musigate/telegram/listener.py +204 -0
- musigate-0.1.0a2/src/musigate/utils/__init__.py +0 -0
- musigate-0.1.0a2/src/musigate/utils/config.py +162 -0
- musigate-0.1.0a2/src/musigate/utils/downloader.py +126 -0
- musigate-0.1.0a2/src/musigate/utils/helper.py +38 -0
- musigate-0.1.0a2/src/musigate.egg-info/PKG-INFO +233 -0
- musigate-0.1.0a2/src/musigate.egg-info/SOURCES.txt +37 -0
- musigate-0.1.0a2/src/musigate.egg-info/dependency_links.txt +1 -0
- musigate-0.1.0a2/src/musigate.egg-info/entry_points.txt +2 -0
- musigate-0.1.0a2/src/musigate.egg-info/requires.txt +12 -0
- musigate-0.1.0a2/src/musigate.egg-info/top_level.txt +1 -0
- musigate-0.1.0a2/tests/test_cli.py +152 -0
- musigate-0.1.0a2/tests/test_config.py +31 -0
- musigate-0.1.0a2/tests/test_downloader.py +51 -0
- musigate-0.1.0a2/tests/test_engine.py +283 -0
- musigate-0.1.0a2/tests/test_listener.py +142 -0
- musigate-0.1.0a2/tests/test_loader.py +47 -0
- musigate-0.1.0a2/tests/test_selector.py +38 -0
- musigate-0.1.0a2/tests/test_telegram_client.py +40 -0
musigate-0.1.0a2/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 musigate contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: musigate
|
|
3
|
+
Version: 0.1.0a2
|
|
4
|
+
Summary: YAML-driven Telegram Bot orchestration engine for automated interactions
|
|
5
|
+
Author: musigate contributors
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/opxqo/musigate
|
|
8
|
+
Project-URL: Repository, https://github.com/opxqo/musigate
|
|
9
|
+
Project-URL: Issues, https://github.com/opxqo/musigate/issues
|
|
10
|
+
Keywords: telegram,telegram-bot,music-download,yaml,cli,bot-automation,orchestration
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: Communications :: Chat
|
|
23
|
+
Classifier: Topic :: Multimedia :: Sound/Audio
|
|
24
|
+
Classifier: Typing :: Typed
|
|
25
|
+
Requires-Python: >=3.10
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
License-File: LICENSE
|
|
28
|
+
Requires-Dist: telethon>=1.33.0
|
|
29
|
+
Requires-Dist: PySocks>=1.7.1
|
|
30
|
+
Requires-Dist: typer>=0.9.0
|
|
31
|
+
Requires-Dist: pyyaml>=6.0.1
|
|
32
|
+
Requires-Dist: rich>=13.7.0
|
|
33
|
+
Requires-Dist: aiofiles>=23.2.1
|
|
34
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
35
|
+
Requires-Dist: pydantic>=2.5.3
|
|
36
|
+
Provides-Extra: dev
|
|
37
|
+
Requires-Dist: pytest>=7.4.3; extra == "dev"
|
|
38
|
+
Requires-Dist: pytest-asyncio>=0.23.2; extra == "dev"
|
|
39
|
+
Dynamic: license-file
|
|
40
|
+
|
|
41
|
+
# musigate
|
|
42
|
+
|
|
43
|
+
[English](#english) | [中文](#中文)
|
|
44
|
+
|
|
45
|
+
## English
|
|
46
|
+
|
|
47
|
+
`musigate` is a Python CLI for searching and downloading music through Telegram bots with a YAML-driven adapter system.
|
|
48
|
+
|
|
49
|
+
This release is an alpha build. The core CLI flow is working, but bot behavior still depends on upstream Telegram bots and may change over time.
|
|
50
|
+
|
|
51
|
+
### Install
|
|
52
|
+
|
|
53
|
+
From a published package:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
pip install musigate
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
From a local wheel:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
pip install dist/musigate-0.1.0a2-py3-none-any.whl
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
For development:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
pip install -e .[dev]
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Setup
|
|
72
|
+
|
|
73
|
+
Copy `.env.example` to `.env`, then fill in your Telegram API credentials:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
cp .env.example .env
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Required values:
|
|
80
|
+
|
|
81
|
+
- `TELEGRAM_API_ID`
|
|
82
|
+
- `TELEGRAM_API_HASH`
|
|
83
|
+
|
|
84
|
+
### Built-in Bots
|
|
85
|
+
|
|
86
|
+
- `music163`
|
|
87
|
+
- `music_v1`
|
|
88
|
+
|
|
89
|
+
### Agent Skills
|
|
90
|
+
|
|
91
|
+
The repository now includes a local [skills](skills) directory for agent ecosystems such as Codex or OpenClaw.
|
|
92
|
+
|
|
93
|
+
Current skills:
|
|
94
|
+
|
|
95
|
+
- [skills/musigate-cli](skills/musigate-cli)
|
|
96
|
+
- [skills/add-bot-adapter](skills/add-bot-adapter)
|
|
97
|
+
|
|
98
|
+
### Usage
|
|
99
|
+
|
|
100
|
+
Login to Telegram:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
musigate login
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Search only:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
musigate search "Numb" --bot music163
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Download with smart matching:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
musigate download "Numb" --bot music163
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Download a specific numbered search result:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
musigate download "海底" --bot music_v1 --pick 2
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Write files to a custom directory:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
musigate download "海底" --bot music_v1 --pick 2 --output ./downloads/favorites
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Machine-readable JSON output:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
musigate search "Numb" --bot music163 --json
|
|
134
|
+
musigate download "Numb" --bot music163 --json
|
|
135
|
+
musigate list-bots --json
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Normal CLI downloads show periodic progress updates including speed and ETA.
|
|
139
|
+
|
|
140
|
+
## 中文
|
|
141
|
+
|
|
142
|
+
`musigate` 是一个 Python 命令行工具,用来通过 Telegram 机器人搜索和下载音乐,核心采用 YAML 驱动的适配器架构。
|
|
143
|
+
|
|
144
|
+
当前版本是 alpha 初版。CLI 主流程已经可用,但具体机器人的行为仍依赖上游 Telegram 机器人,后续可能会变化。
|
|
145
|
+
|
|
146
|
+
### 安装
|
|
147
|
+
|
|
148
|
+
从已发布的包安装:
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
pip install musigate
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
从本地 wheel 安装:
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
pip install dist/musigate-0.1.0a2-py3-none-any.whl
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
开发模式安装:
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
pip install -e .[dev]
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### 配置
|
|
167
|
+
|
|
168
|
+
把 `.env.example` 复制为 `.env`,然后填入你的 Telegram API 凭据:
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
cp .env.example .env
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
必填项:
|
|
175
|
+
|
|
176
|
+
- `TELEGRAM_API_ID`
|
|
177
|
+
- `TELEGRAM_API_HASH`
|
|
178
|
+
|
|
179
|
+
### 内置 Bot
|
|
180
|
+
|
|
181
|
+
- `music163`
|
|
182
|
+
- `music_v1`
|
|
183
|
+
|
|
184
|
+
### 智能体 Skills
|
|
185
|
+
|
|
186
|
+
仓库根目录新增了一个本地 [skills](skills) 目录,方便 Codex、OpenClaw 这类智能体快速接入。
|
|
187
|
+
|
|
188
|
+
当前提供:
|
|
189
|
+
|
|
190
|
+
- [skills/musigate-cli](skills/musigate-cli)
|
|
191
|
+
- [skills/add-bot-adapter](skills/add-bot-adapter)
|
|
192
|
+
|
|
193
|
+
### 使用方式
|
|
194
|
+
|
|
195
|
+
登录 Telegram:
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
musigate login
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
仅搜索,不下载:
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
musigate search "Numb" --bot music163
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
按智能匹配下载:
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
musigate download "Numb" --bot music163
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
按搜索结果编号精确下载:
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
musigate download "海底" --bot music_v1 --pick 2
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
下载到自定义目录:
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
musigate download "海底" --bot music_v1 --pick 2 --output ./downloads/favorites
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
输出机器可读 JSON:
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
musigate search "Numb" --bot music163 --json
|
|
229
|
+
musigate download "Numb" --bot music163 --json
|
|
230
|
+
musigate list-bots --json
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
普通 CLI 下载模式会定期显示进度、速度和预计剩余时间。
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# musigate
|
|
2
|
+
|
|
3
|
+
[English](#english) | [中文](#中文)
|
|
4
|
+
|
|
5
|
+
## English
|
|
6
|
+
|
|
7
|
+
`musigate` is a Python CLI for searching and downloading music through Telegram bots with a YAML-driven adapter system.
|
|
8
|
+
|
|
9
|
+
This release is an alpha build. The core CLI flow is working, but bot behavior still depends on upstream Telegram bots and may change over time.
|
|
10
|
+
|
|
11
|
+
### Install
|
|
12
|
+
|
|
13
|
+
From a published package:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pip install musigate
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
From a local wheel:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pip install dist/musigate-0.1.0a2-py3-none-any.whl
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
For development:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install -e .[dev]
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Setup
|
|
32
|
+
|
|
33
|
+
Copy `.env.example` to `.env`, then fill in your Telegram API credentials:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
cp .env.example .env
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Required values:
|
|
40
|
+
|
|
41
|
+
- `TELEGRAM_API_ID`
|
|
42
|
+
- `TELEGRAM_API_HASH`
|
|
43
|
+
|
|
44
|
+
### Built-in Bots
|
|
45
|
+
|
|
46
|
+
- `music163`
|
|
47
|
+
- `music_v1`
|
|
48
|
+
|
|
49
|
+
### Agent Skills
|
|
50
|
+
|
|
51
|
+
The repository now includes a local [skills](skills) directory for agent ecosystems such as Codex or OpenClaw.
|
|
52
|
+
|
|
53
|
+
Current skills:
|
|
54
|
+
|
|
55
|
+
- [skills/musigate-cli](skills/musigate-cli)
|
|
56
|
+
- [skills/add-bot-adapter](skills/add-bot-adapter)
|
|
57
|
+
|
|
58
|
+
### Usage
|
|
59
|
+
|
|
60
|
+
Login to Telegram:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
musigate login
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Search only:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
musigate search "Numb" --bot music163
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Download with smart matching:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
musigate download "Numb" --bot music163
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Download a specific numbered search result:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
musigate download "海底" --bot music_v1 --pick 2
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Write files to a custom directory:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
musigate download "海底" --bot music_v1 --pick 2 --output ./downloads/favorites
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Machine-readable JSON output:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
musigate search "Numb" --bot music163 --json
|
|
94
|
+
musigate download "Numb" --bot music163 --json
|
|
95
|
+
musigate list-bots --json
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Normal CLI downloads show periodic progress updates including speed and ETA.
|
|
99
|
+
|
|
100
|
+
## 中文
|
|
101
|
+
|
|
102
|
+
`musigate` 是一个 Python 命令行工具,用来通过 Telegram 机器人搜索和下载音乐,核心采用 YAML 驱动的适配器架构。
|
|
103
|
+
|
|
104
|
+
当前版本是 alpha 初版。CLI 主流程已经可用,但具体机器人的行为仍依赖上游 Telegram 机器人,后续可能会变化。
|
|
105
|
+
|
|
106
|
+
### 安装
|
|
107
|
+
|
|
108
|
+
从已发布的包安装:
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
pip install musigate
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
从本地 wheel 安装:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
pip install dist/musigate-0.1.0a2-py3-none-any.whl
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
开发模式安装:
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
pip install -e .[dev]
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### 配置
|
|
127
|
+
|
|
128
|
+
把 `.env.example` 复制为 `.env`,然后填入你的 Telegram API 凭据:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
cp .env.example .env
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
必填项:
|
|
135
|
+
|
|
136
|
+
- `TELEGRAM_API_ID`
|
|
137
|
+
- `TELEGRAM_API_HASH`
|
|
138
|
+
|
|
139
|
+
### 内置 Bot
|
|
140
|
+
|
|
141
|
+
- `music163`
|
|
142
|
+
- `music_v1`
|
|
143
|
+
|
|
144
|
+
### 智能体 Skills
|
|
145
|
+
|
|
146
|
+
仓库根目录新增了一个本地 [skills](skills) 目录,方便 Codex、OpenClaw 这类智能体快速接入。
|
|
147
|
+
|
|
148
|
+
当前提供:
|
|
149
|
+
|
|
150
|
+
- [skills/musigate-cli](skills/musigate-cli)
|
|
151
|
+
- [skills/add-bot-adapter](skills/add-bot-adapter)
|
|
152
|
+
|
|
153
|
+
### 使用方式
|
|
154
|
+
|
|
155
|
+
登录 Telegram:
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
musigate login
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
仅搜索,不下载:
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
musigate search "Numb" --bot music163
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
按智能匹配下载:
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
musigate download "Numb" --bot music163
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
按搜索结果编号精确下载:
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
musigate download "海底" --bot music_v1 --pick 2
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
下载到自定义目录:
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
musigate download "海底" --bot music_v1 --pick 2 --output ./downloads/favorites
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
输出机器可读 JSON:
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
musigate search "Numb" --bot music163 --json
|
|
189
|
+
musigate download "Numb" --bot music163 --json
|
|
190
|
+
musigate list-bots --json
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
普通 CLI 下载模式会定期显示进度、速度和预计剩余时间。
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=64", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "musigate"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "YAML-driven Telegram Bot orchestration engine for automated interactions"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "MIT" }
|
|
11
|
+
authors = [
|
|
12
|
+
{ name = "musigate contributors" }
|
|
13
|
+
]
|
|
14
|
+
requires-python = ">=3.10"
|
|
15
|
+
keywords = [
|
|
16
|
+
"telegram",
|
|
17
|
+
"telegram-bot",
|
|
18
|
+
"music-download",
|
|
19
|
+
"yaml",
|
|
20
|
+
"cli",
|
|
21
|
+
"bot-automation",
|
|
22
|
+
"orchestration",
|
|
23
|
+
]
|
|
24
|
+
classifiers = [
|
|
25
|
+
"Development Status :: 3 - Alpha",
|
|
26
|
+
"Environment :: Console",
|
|
27
|
+
"Intended Audience :: Developers",
|
|
28
|
+
"Intended Audience :: End Users/Desktop",
|
|
29
|
+
"License :: OSI Approved :: MIT License",
|
|
30
|
+
"Operating System :: OS Independent",
|
|
31
|
+
"Programming Language :: Python :: 3",
|
|
32
|
+
"Programming Language :: Python :: 3.10",
|
|
33
|
+
"Programming Language :: Python :: 3.11",
|
|
34
|
+
"Programming Language :: Python :: 3.12",
|
|
35
|
+
"Programming Language :: Python :: 3.13",
|
|
36
|
+
"Topic :: Communications :: Chat",
|
|
37
|
+
"Topic :: Multimedia :: Sound/Audio",
|
|
38
|
+
"Typing :: Typed",
|
|
39
|
+
]
|
|
40
|
+
dependencies = [
|
|
41
|
+
"telethon>=1.33.0",
|
|
42
|
+
"PySocks>=1.7.1",
|
|
43
|
+
"typer>=0.9.0",
|
|
44
|
+
"pyyaml>=6.0.1",
|
|
45
|
+
"rich>=13.7.0",
|
|
46
|
+
"aiofiles>=23.2.1",
|
|
47
|
+
"python-dotenv>=1.0.0",
|
|
48
|
+
"pydantic>=2.5.3"
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
[project.urls]
|
|
52
|
+
Homepage = "https://github.com/opxqo/musigate"
|
|
53
|
+
Repository = "https://github.com/opxqo/musigate"
|
|
54
|
+
Issues = "https://github.com/opxqo/musigate/issues"
|
|
55
|
+
|
|
56
|
+
[project.optional-dependencies]
|
|
57
|
+
dev = [
|
|
58
|
+
"pytest>=7.4.3",
|
|
59
|
+
"pytest-asyncio>=0.23.2"
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
[project.scripts]
|
|
63
|
+
musigate = "musigate.cli:app"
|
|
64
|
+
|
|
65
|
+
[tool.setuptools.packages.find]
|
|
66
|
+
where = ["src"]
|
|
67
|
+
|
|
68
|
+
[tool.setuptools.dynamic]
|
|
69
|
+
version = { attr = "musigate.__version__" }
|
|
70
|
+
|
|
71
|
+
[tool.setuptools.package-data]
|
|
72
|
+
"musigate.resources" = ["bots/*.yaml", "config/*.yaml"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.0a2"
|
|
File without changes
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
from importlib import resources
|
|
2
|
+
from importlib.resources.abc import Traversable
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Any, Dict, List, Optional
|
|
5
|
+
|
|
6
|
+
import yaml
|
|
7
|
+
from pydantic import BaseModel, Field
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Step(BaseModel):
|
|
11
|
+
action: str
|
|
12
|
+
content: Optional[str] = None
|
|
13
|
+
expect: Optional[str] = None
|
|
14
|
+
timeout: Optional[int] = None
|
|
15
|
+
strategy: Optional[str] = None
|
|
16
|
+
query: Optional[str] = None
|
|
17
|
+
output: Optional[str] = None
|
|
18
|
+
cases: Optional[List[Dict[str, Any]]] = None
|
|
19
|
+
extract: Optional[Dict[str, str]] = None
|
|
20
|
+
message: Optional[str] = None
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class CommandDef(BaseModel):
|
|
24
|
+
steps: List[Step]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class BotSettings(BaseModel):
|
|
28
|
+
timeout: int = 15
|
|
29
|
+
retry: int = 2
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class BotConfig(BaseModel):
|
|
33
|
+
name: str
|
|
34
|
+
bot_username: str
|
|
35
|
+
description: str = ""
|
|
36
|
+
version: str = "1.0"
|
|
37
|
+
settings: BotSettings = Field(default_factory=BotSettings)
|
|
38
|
+
commands: Dict[str, CommandDef]
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
PROJECT_ROOT = Path(__file__).resolve().parents[3]
|
|
42
|
+
RESOURCE_PACKAGE = "musigate.resources"
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _bot_filename(bot_name: str) -> str:
|
|
46
|
+
return bot_name if bot_name.endswith(".yaml") else f"{bot_name}.yaml"
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _dedupe_paths(paths: list[Path]) -> list[Path]:
|
|
50
|
+
unique_paths: list[Path] = []
|
|
51
|
+
seen: set[str] = set()
|
|
52
|
+
for path in paths:
|
|
53
|
+
key = str(path.expanduser().resolve(strict=False)).lower()
|
|
54
|
+
if key in seen:
|
|
55
|
+
continue
|
|
56
|
+
seen.add(key)
|
|
57
|
+
unique_paths.append(path)
|
|
58
|
+
return unique_paths
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _external_bot_dirs() -> list[Path]:
|
|
62
|
+
return _dedupe_paths(
|
|
63
|
+
[
|
|
64
|
+
Path.cwd() / "bots",
|
|
65
|
+
PROJECT_ROOT / "bots",
|
|
66
|
+
]
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _explicit_bot_candidates(bot_name: str) -> list[Path]:
|
|
71
|
+
candidate = Path(bot_name).expanduser()
|
|
72
|
+
candidates: list[Path] = []
|
|
73
|
+
|
|
74
|
+
if candidate.is_absolute() or candidate.parent != Path("."):
|
|
75
|
+
candidates.append(candidate if candidate.is_absolute() else (Path.cwd() / candidate))
|
|
76
|
+
elif candidate.suffix == ".yaml":
|
|
77
|
+
candidates.extend(
|
|
78
|
+
[
|
|
79
|
+
Path.cwd() / candidate,
|
|
80
|
+
PROJECT_ROOT / candidate,
|
|
81
|
+
]
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
return _dedupe_paths(candidates)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _packaged_bot_resource(filename: str) -> Traversable | None:
|
|
88
|
+
resource = resources.files(RESOURCE_PACKAGE).joinpath("bots", filename)
|
|
89
|
+
return resource if resource.is_file() else None
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _read_yaml(source: Path | Traversable) -> dict:
|
|
93
|
+
if isinstance(source, Path):
|
|
94
|
+
raw = source.read_text(encoding="utf-8")
|
|
95
|
+
else:
|
|
96
|
+
raw = source.read_text(encoding="utf-8")
|
|
97
|
+
return yaml.safe_load(raw) or {}
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _resolve_bot_source(bot_name: str) -> Path | Traversable:
|
|
101
|
+
for candidate in _explicit_bot_candidates(bot_name):
|
|
102
|
+
if candidate.is_file():
|
|
103
|
+
return candidate
|
|
104
|
+
|
|
105
|
+
filename = _bot_filename(bot_name)
|
|
106
|
+
for directory in _external_bot_dirs():
|
|
107
|
+
candidate = directory / filename
|
|
108
|
+
if candidate.is_file():
|
|
109
|
+
return candidate
|
|
110
|
+
|
|
111
|
+
packaged = _packaged_bot_resource(filename)
|
|
112
|
+
if packaged is not None:
|
|
113
|
+
return packaged
|
|
114
|
+
|
|
115
|
+
searched = [str(path) for path in _explicit_bot_candidates(bot_name)]
|
|
116
|
+
searched.extend(str(directory / filename) for directory in _external_bot_dirs())
|
|
117
|
+
raise FileNotFoundError(f"Bot config not found: {filename}. Searched: {', '.join(searched)}")
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def load_bot(bot_name: str) -> dict:
|
|
121
|
+
"""Load and validate a bot configuration."""
|
|
122
|
+
data = _read_yaml(_resolve_bot_source(bot_name))
|
|
123
|
+
config = BotConfig(**data)
|
|
124
|
+
return config.model_dump()
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def list_bots() -> list[dict]:
|
|
128
|
+
configs: list[dict] = []
|
|
129
|
+
seen_names: set[str] = set()
|
|
130
|
+
|
|
131
|
+
for directory in _external_bot_dirs():
|
|
132
|
+
if not directory.exists():
|
|
133
|
+
continue
|
|
134
|
+
for bot_file in sorted(directory.glob("*.yaml")):
|
|
135
|
+
if bot_file.name == "template.yaml" or bot_file.name in seen_names:
|
|
136
|
+
continue
|
|
137
|
+
configs.append(load_bot(str(bot_file)))
|
|
138
|
+
seen_names.add(bot_file.name)
|
|
139
|
+
|
|
140
|
+
packaged_dir = resources.files(RESOURCE_PACKAGE).joinpath("bots")
|
|
141
|
+
for bot_file in sorted(packaged_dir.iterdir(), key=lambda item: item.name):
|
|
142
|
+
if not bot_file.is_file() or not bot_file.name.endswith(".yaml"):
|
|
143
|
+
continue
|
|
144
|
+
if bot_file.name == "template.yaml" or bot_file.name in seen_names:
|
|
145
|
+
continue
|
|
146
|
+
configs.append(load_bot(bot_file.name))
|
|
147
|
+
seen_names.add(bot_file.name)
|
|
148
|
+
|
|
149
|
+
return configs
|