build: migrate dependency management to uv
This commit is contained in:
1
.python-version
Normal file
1
.python-version
Normal file
@@ -0,0 +1 @@
|
|||||||
|
3.12
|
||||||
@@ -5,8 +5,8 @@ Repository guidance for automated coding tools and contributors.
|
|||||||
## Running the bot
|
## Running the bot
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pip install -r requirements.txt
|
uv sync
|
||||||
python main.py
|
uv run python main.py
|
||||||
```
|
```
|
||||||
|
|
||||||
Logs go to `bot.log` by default (rotating, 10 MB, 3 backups). Override with `LOG_FILE=<path>`.
|
Logs go to `bot.log` by default (rotating, 10 MB, 3 backups). Override with `LOG_FILE=<path>`.
|
||||||
@@ -14,7 +14,7 @@ Logs go to `bot.log` by default (rotating, 10 MB, 3 backups). Override with `LOG
|
|||||||
Run the test suite with:
|
Run the test suite with:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python -m unittest discover -s tests -v
|
uv run python -m unittest discover -s tests -v
|
||||||
```
|
```
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
@@ -36,6 +36,8 @@ AI prompt templates are always loaded from `prompts/MASTER.txt` and `prompts/USE
|
|||||||
### Boot path
|
### Boot path
|
||||||
`main.py` → `load_app_config()` → `AccountManager` → one Pyrogram (`kurigram`) `Client` per account → `register_common_handlers()` → `pyrogram.idle()`.
|
`main.py` → `load_app_config()` → `AccountManager` → one Pyrogram (`kurigram`) `Client` per account → `register_common_handlers()` → `pyrogram.idle()`.
|
||||||
|
|
||||||
|
On supported platforms, `main.py` runs the application with `uvloop`; Windows automatically falls back to the standard `asyncio` event loop.
|
||||||
|
|
||||||
### Command system (`app/commands/`)
|
### Command system (`app/commands/`)
|
||||||
|
|
||||||
- **Dispatcher** (`dispatcher.py`): parses `.command` (owner prefix) or `..command` (shared/guest prefix) from message text, resolves to a `CommandDefinition`, runs pre-checks, calls handler.
|
- **Dispatcher** (`dispatcher.py`): parses `.command` (owner prefix) or `..command` (shared/guest prefix) from message text, resolves to a `CommandDefinition`, runs pre-checks, calls handler.
|
||||||
@@ -85,6 +87,7 @@ Shared commands use `..mycommand` for guests and `.mycommand` for the owner. Own
|
|||||||
## Change guidelines
|
## Change guidelines
|
||||||
|
|
||||||
- Preserve unrelated local and untracked changes.
|
- Preserve unrelated local and untracked changes.
|
||||||
|
- Manage dependencies through `pyproject.toml` and `uv.lock`; use `uv sync` instead of `pip install`.
|
||||||
- Register commands through `command_registry`; do not add separate Pyrogram handlers for individual commands.
|
- Register commands through `command_registry`; do not add separate Pyrogram handlers for individual commands.
|
||||||
- Keep shared commands behind `shared_access_check()` and persist ACL changes through `RuntimeAclStore`.
|
- Keep shared commands behind `shared_access_check()` and persist ACL changes through `RuntimeAclStore`.
|
||||||
- Add or update focused `unittest` coverage for behavioral changes.
|
- Add or update focused `unittest` coverage for behavioral changes.
|
||||||
|
|||||||
8
main.py
8
main.py
@@ -10,6 +10,11 @@ from app.config import load_app_config
|
|||||||
from app.manager import AccountManager
|
from app.manager import AccountManager
|
||||||
from app.pyrogram_compat import install_kurigram_compat
|
from app.pyrogram_compat import install_kurigram_compat
|
||||||
|
|
||||||
|
try:
|
||||||
|
import uvloop
|
||||||
|
except ImportError: # uvloop does not support Windows.
|
||||||
|
uvloop = None
|
||||||
|
|
||||||
LOG_FORMAT = "%(asctime)s | %(levelname)s | %(name)s | %(message)s"
|
LOG_FORMAT = "%(asctime)s | %(levelname)s | %(name)s | %(message)s"
|
||||||
|
|
||||||
|
|
||||||
@@ -46,4 +51,7 @@ async def main() -> None:
|
|||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
configure_logging(_ROOT)
|
configure_logging(_ROOT)
|
||||||
|
if uvloop is None:
|
||||||
asyncio.run(main())
|
asyncio.run(main())
|
||||||
|
else:
|
||||||
|
uvloop.run(main())
|
||||||
|
|||||||
@@ -1,3 +1,46 @@
|
|||||||
|
/*
|
||||||
|
Template variables (both {variable} and {{variable}} syntax are supported):
|
||||||
|
|
||||||
|
caller_user.id
|
||||||
|
caller_user.name
|
||||||
|
caller_user.username
|
||||||
|
|
||||||
|
owner.id
|
||||||
|
owner.name
|
||||||
|
owner.username
|
||||||
|
|
||||||
|
is_owner
|
||||||
|
text
|
||||||
|
|
||||||
|
chat.id
|
||||||
|
chat.title
|
||||||
|
chat.type
|
||||||
|
chat.username
|
||||||
|
|
||||||
|
message.id
|
||||||
|
message.text
|
||||||
|
message.date
|
||||||
|
message.user.id
|
||||||
|
message.user.name
|
||||||
|
message.user.username
|
||||||
|
message.is_reply
|
||||||
|
|
||||||
|
reply.id
|
||||||
|
reply.text
|
||||||
|
reply.user.id
|
||||||
|
reply.user.name
|
||||||
|
reply.user.username
|
||||||
|
|
||||||
|
account.name
|
||||||
|
account.session
|
||||||
|
|
||||||
|
provider
|
||||||
|
thinking
|
||||||
|
context_messages
|
||||||
|
|
||||||
|
Missing Telegram values are rendered as an empty string.
|
||||||
|
*/
|
||||||
|
|
||||||
You are a helpful assistant embedded in a Telegram client. Answer in the user's language.
|
You are a helpful assistant embedded in a Telegram client. Answer in the user's language.
|
||||||
|
|
||||||
The caller is {{caller_user.name}} (id={{caller_user.id}}, username={{caller_user.username}}).
|
The caller is {{caller_user.name}} (id={{caller_user.id}}, username={{caller_user.username}}).
|
||||||
|
|||||||
@@ -1,2 +1,45 @@
|
|||||||
|
/*
|
||||||
|
Template variables (both {variable} and {{variable}} syntax are supported):
|
||||||
|
|
||||||
|
caller_user.id
|
||||||
|
caller_user.name
|
||||||
|
caller_user.username
|
||||||
|
|
||||||
|
owner.id
|
||||||
|
owner.name
|
||||||
|
owner.username
|
||||||
|
|
||||||
|
is_owner
|
||||||
|
text
|
||||||
|
|
||||||
|
chat.id
|
||||||
|
chat.title
|
||||||
|
chat.type
|
||||||
|
chat.username
|
||||||
|
|
||||||
|
message.id
|
||||||
|
message.text
|
||||||
|
message.date
|
||||||
|
message.user.id
|
||||||
|
message.user.name
|
||||||
|
message.user.username
|
||||||
|
message.is_reply
|
||||||
|
|
||||||
|
reply.id
|
||||||
|
reply.text
|
||||||
|
reply.user.id
|
||||||
|
reply.user.name
|
||||||
|
reply.user.username
|
||||||
|
|
||||||
|
account.name
|
||||||
|
account.session
|
||||||
|
|
||||||
|
provider
|
||||||
|
thinking
|
||||||
|
context_messages
|
||||||
|
|
||||||
|
Missing Telegram values are rendered as an empty string.
|
||||||
|
*/
|
||||||
|
|
||||||
[Telegram message from {{message.user.name}} at {{message.date}}; reply={{message.is_reply}}]
|
[Telegram message from {{message.user.name}} at {{message.date}}; reply={{message.is_reply}}]
|
||||||
{{text}}
|
{{text}}
|
||||||
|
|||||||
@@ -2,4 +2,24 @@
|
|||||||
name = "multi-bot-tg"
|
name = "multi-bot-tg"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
requires-python = ">=3.12"
|
requires-python = ">=3.12"
|
||||||
dependencies = []
|
dependencies = [
|
||||||
|
"aiohttp",
|
||||||
|
"kurigram==2.2.24",
|
||||||
|
"micro-agents==0.1.5",
|
||||||
|
"micro-agents-tools==0.1.5",
|
||||||
|
"python-dotenv",
|
||||||
|
"tgcrypto",
|
||||||
|
"uvloop; sys_platform != 'win32'",
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.uv]
|
||||||
|
package = false
|
||||||
|
|
||||||
|
[[tool.uv.index]]
|
||||||
|
name = "azimkin"
|
||||||
|
url = "https://git.azimkin.dev/api/packages/Azimkin/pypi/simple/"
|
||||||
|
explicit = true
|
||||||
|
|
||||||
|
[tool.uv.sources]
|
||||||
|
micro-agents = { index = "azimkin" }
|
||||||
|
micro-agents-tools = { index = "azimkin" }
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
--index-url https://git.azimkin.dev/api/packages/Azimkin/pypi/simple/
|
|
||||||
--extra-index-url https://pypi.org/simple
|
|
||||||
|
|
||||||
kurigram==2.2.24
|
|
||||||
tgcrypto
|
|
||||||
python-dotenv
|
|
||||||
aiohttp
|
|
||||||
micro-agents==0.1.5
|
|
||||||
micro-agents-tools==0.1.5
|
|
||||||
Reference in New Issue
Block a user