minia_telegram 0.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.
- minia_telegram-0.1.0/PKG-INFO +7 -0
- minia_telegram-0.1.0/minia_telegram/__init__.py +14 -0
- minia_telegram-0.1.0/minia_telegram/bridge.py +229 -0
- minia_telegram-0.1.0/minia_telegram/client.py +83 -0
- minia_telegram-0.1.0/minia_telegram/main.py +74 -0
- minia_telegram-0.1.0/minia_telegram.egg-info/PKG-INFO +7 -0
- minia_telegram-0.1.0/minia_telegram.egg-info/SOURCES.txt +11 -0
- minia_telegram-0.1.0/minia_telegram.egg-info/dependency_links.txt +1 -0
- minia_telegram-0.1.0/minia_telegram.egg-info/entry_points.txt +2 -0
- minia_telegram-0.1.0/minia_telegram.egg-info/requires.txt +2 -0
- minia_telegram-0.1.0/minia_telegram.egg-info/top_level.txt +1 -0
- minia_telegram-0.1.0/pyproject.toml +23 -0
- minia_telegram-0.1.0/setup.cfg +4 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""minia_telegram - Telegram integration for the minia agent framework.
|
|
2
|
+
|
|
3
|
+
This package provides a Telegram-specific implementation of the interaction layer,
|
|
4
|
+
allowing the agent to communicate with users via Telegram's Bot API.
|
|
5
|
+
|
|
6
|
+
Exports:
|
|
7
|
+
- TelegramClient: Low-level client for the Telegram Bot API.
|
|
8
|
+
- TelegramBridge: High-level bridge that maps Telegram updates to internal messages.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from minia_telegram.bridge import TelegramBridge
|
|
12
|
+
from minia_telegram.client import TelegramClient
|
|
13
|
+
|
|
14
|
+
__all__ = ["TelegramClient", "TelegramBridge"]
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
"""Bridge between Telegram's raw data and the internal minia system.
|
|
2
|
+
|
|
3
|
+
This gets incoming Telegram updates into the internal format (text for now)
|
|
4
|
+
and provides a way for the agent to route responses back to the Telegram client.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import asyncio
|
|
10
|
+
import contextlib
|
|
11
|
+
import logging
|
|
12
|
+
from collections.abc import AsyncIterator
|
|
13
|
+
|
|
14
|
+
import httpx
|
|
15
|
+
|
|
16
|
+
from minia_contracts.config.settings import config
|
|
17
|
+
from minia_contracts.protocol.ask_user import AskUserTracker
|
|
18
|
+
from minia_contracts.protocol.commands import CommandSender
|
|
19
|
+
from minia_contracts.protocol.dispatch import as_event_consumer
|
|
20
|
+
from minia_contracts.protocol.events import run_event_socket_client
|
|
21
|
+
from minia_contracts.reports import ReportEvent, ReportEventPayload, cmd_answer, cmd_input
|
|
22
|
+
from minia_telegram.client import TelegramClient
|
|
23
|
+
|
|
24
|
+
log = logging.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class TelegramBridge:
|
|
28
|
+
"""Handles translation between Telegram and internal message formats.
|
|
29
|
+
|
|
30
|
+
Connects to the agent's event socket as a client (not a server), consuming
|
|
31
|
+
report events and forwarding them back through Telegram. Incoming user
|
|
32
|
+
messages from Telegram are sent to the agent via the command socket.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
def __init__(self, client: TelegramClient) -> None:
|
|
36
|
+
self.client = client
|
|
37
|
+
# Ensure chat_id is always a string (TOML may parse it as an integer).
|
|
38
|
+
telegram_cfg = getattr(config, "telegram", None)
|
|
39
|
+
raw_chat_id = telegram_cfg.chat_id if telegram_cfg else ""
|
|
40
|
+
self.chat_id = str(raw_chat_id) if raw_chat_id else ""
|
|
41
|
+
log.info("[Telegram] Bridge initialized with chat_id=%s", self.chat_id)
|
|
42
|
+
self._sender: CommandSender | None = None
|
|
43
|
+
self._ask_users = AskUserTracker()
|
|
44
|
+
|
|
45
|
+
# ------------------------------------------------------------------
|
|
46
|
+
# Incoming: Telegram → agent
|
|
47
|
+
# ------------------------------------------------------------------
|
|
48
|
+
|
|
49
|
+
async def send_response(self, chat_id: str, text: str) -> None:
|
|
50
|
+
"""Send a response back through the Telegram client, truncating if needed."""
|
|
51
|
+
if not chat_id:
|
|
52
|
+
log.error("[Telegram] Cannot send: chat_id is empty")
|
|
53
|
+
return
|
|
54
|
+
|
|
55
|
+
# Telegram's limit is 4096 characters per message
|
|
56
|
+
MAX_LENGTH = 4090
|
|
57
|
+
if len(text) > MAX_LENGTH:
|
|
58
|
+
original_len = len(text)
|
|
59
|
+
text = text[:MAX_LENGTH]
|
|
60
|
+
log.warning(
|
|
61
|
+
"[Telegram] Truncated response from %d to %d chars", original_len, MAX_LENGTH
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
try:
|
|
65
|
+
await self.client.send_message(chat_id, text)
|
|
66
|
+
log.info("[Telegram] Sent message to chat %s (%d chars)", chat_id, len(text))
|
|
67
|
+
except Exception as exc:
|
|
68
|
+
log.error("[Telegram] Failed to send message to chat %s: %s", exc)
|
|
69
|
+
|
|
70
|
+
async def get_updates(self) -> AsyncIterator[str]:
|
|
71
|
+
"""Bridge method to fetch and map Telegram updates to internal Messages."""
|
|
72
|
+
async for update in self.client.poll_updates(self.chat_id):
|
|
73
|
+
yield update["message"]["text"]
|
|
74
|
+
|
|
75
|
+
def _update_ask_user(self, event: ReportEvent, payload: ReportEventPayload) -> None:
|
|
76
|
+
"""Update the shared ask_user tracker from a report event.
|
|
77
|
+
|
|
78
|
+
Marks the agent when an ``ask_user`` tool call is observed; clears it
|
|
79
|
+
on the matching tool result, a final response, or an error.
|
|
80
|
+
"""
|
|
81
|
+
tool = payload.get("tool", "")
|
|
82
|
+
agent = payload.get("agent") or "main"
|
|
83
|
+
if event == ReportEvent.TOOL_CALL and tool == "ask_user":
|
|
84
|
+
self._ask_users.mark(agent)
|
|
85
|
+
elif (event == ReportEvent.TOOL_RESULT and tool == "ask_user") or event in (
|
|
86
|
+
ReportEvent.FINAL_RESPONSE,
|
|
87
|
+
ReportEvent.ERROR,
|
|
88
|
+
):
|
|
89
|
+
self._ask_users.clear(agent)
|
|
90
|
+
|
|
91
|
+
async def send_to_agent(self, text: str) -> None:
|
|
92
|
+
"""Send a message to the agent via the command socket."""
|
|
93
|
+
if self._sender is None:
|
|
94
|
+
self._sender = CommandSender(
|
|
95
|
+
config.default.cmd_socket_path,
|
|
96
|
+
logger=log,
|
|
97
|
+
)
|
|
98
|
+
if self._ask_users.any_pending():
|
|
99
|
+
await self._sender.send(cmd_answer(text))
|
|
100
|
+
self._ask_users.clear_most_recent()
|
|
101
|
+
else:
|
|
102
|
+
await self._sender.send(cmd_input(text))
|
|
103
|
+
|
|
104
|
+
def _sanitize_for_telegram(self, text: str) -> str:
|
|
105
|
+
"""Remove things that Telegram's Markdown parser can't handle."""
|
|
106
|
+
return text
|
|
107
|
+
|
|
108
|
+
# ------------------------------------------------------------------
|
|
109
|
+
# Outgoing: agent → Telegram (event socket client)
|
|
110
|
+
# ------------------------------------------------------------------
|
|
111
|
+
|
|
112
|
+
def handle(self, event: ReportEvent, payload: ReportEventPayload) -> None:
|
|
113
|
+
"""Handle report events from the agent and forward to Telegram.
|
|
114
|
+
|
|
115
|
+
Implements :class:`minia_contracts.protocol.interfaces.EventConsumer`.
|
|
116
|
+
Only ``FINAL_RESPONSE`` events are forwarded as visible messages.
|
|
117
|
+
"""
|
|
118
|
+
log.debug("[Telegram] handle() called with event=%s", event.value)
|
|
119
|
+
content = payload.get("content", "")
|
|
120
|
+
|
|
121
|
+
self._update_ask_user(event, payload)
|
|
122
|
+
|
|
123
|
+
match event:
|
|
124
|
+
case ReportEvent.FINAL_RESPONSE:
|
|
125
|
+
if content:
|
|
126
|
+
if payload.get("agent") == "main":
|
|
127
|
+
clean_content = self._sanitize_for_telegram(content)
|
|
128
|
+
log.info("[Telegram] Forwarding final response: '%s'", clean_content[:200])
|
|
129
|
+
asyncio.create_task(self.send_response(self.chat_id, clean_content))
|
|
130
|
+
else:
|
|
131
|
+
log.warning("[Telegram] FINAL_RESPONSE with empty content")
|
|
132
|
+
case ReportEvent.REASONING_CONTENT:
|
|
133
|
+
log.debug("[Telegram] Agent thinking: '%s'", content[:200])
|
|
134
|
+
case ReportEvent.ERROR:
|
|
135
|
+
log.warning("[Telegram] Agent error: %s", content[:200])
|
|
136
|
+
case ReportEvent.TOOL_CALL:
|
|
137
|
+
tool = payload.get("tool", "")
|
|
138
|
+
if tool == "ask_user":
|
|
139
|
+
question = payload.get("args", {}).get("question", "")
|
|
140
|
+
if question:
|
|
141
|
+
clean_question = self._sanitize_for_telegram(question)
|
|
142
|
+
log.info("[Telegram] ask_user question: '%s'", clean_question[:200])
|
|
143
|
+
asyncio.create_task(
|
|
144
|
+
self.send_response(self.chat_id, f"💬 {clean_question}")
|
|
145
|
+
)
|
|
146
|
+
else:
|
|
147
|
+
log.debug("[Telegram] Tool call: %s", content[:200])
|
|
148
|
+
case ReportEvent.TOOL_RESULT:
|
|
149
|
+
tool = payload.get("tool", "")
|
|
150
|
+
if tool == "ask_user":
|
|
151
|
+
log.info("[Telegram] ask_user answered: '%s'", content[:200])
|
|
152
|
+
else:
|
|
153
|
+
log.debug("[Telegram] Tool result: %s", content[:200])
|
|
154
|
+
case ReportEvent.USER_INPUT:
|
|
155
|
+
log.debug("[Telegram] USER_INPUT: '%s'", payload.get("content", "")[:200])
|
|
156
|
+
case (
|
|
157
|
+
ReportEvent.ITERATION_START
|
|
158
|
+
| ReportEvent.ITERATION_END
|
|
159
|
+
| ReportEvent.REFUSAL
|
|
160
|
+
| ReportEvent.LIVE_TOKEN
|
|
161
|
+
| ReportEvent.STATS
|
|
162
|
+
| ReportEvent.TOKEN_WARNING
|
|
163
|
+
| ReportEvent.RETRY
|
|
164
|
+
| ReportEvent.WARNING
|
|
165
|
+
| ReportEvent.COMMAND_RECEIVED
|
|
166
|
+
| ReportEvent.TTS_START
|
|
167
|
+
| ReportEvent.TTS_END
|
|
168
|
+
| ReportEvent.TTS_STOP
|
|
169
|
+
| ReportEvent.RUN_START
|
|
170
|
+
| ReportEvent.RUN_END
|
|
171
|
+
| ReportEvent.RUN_STOPPED
|
|
172
|
+
| ReportEvent.RUN_HEARTBEAT
|
|
173
|
+
| ReportEvent.BACKGROUND_DONE
|
|
174
|
+
| ReportEvent.DELEGATE_RELAY_DONE
|
|
175
|
+
| ReportEvent.PROGRESS_UPDATE
|
|
176
|
+
):
|
|
177
|
+
log.debug("[Telegram] Event %s: '%s'", event.value, content[:100])
|
|
178
|
+
|
|
179
|
+
async def connect_to_event_socket(self) -> None:
|
|
180
|
+
"""Connect to the agent's event socket and start consuming events."""
|
|
181
|
+
log.info("[Telegram] Connecting to event socket: %s", config.default.event_socket_path)
|
|
182
|
+
try:
|
|
183
|
+
await run_event_socket_client(
|
|
184
|
+
config.default.event_socket_path,
|
|
185
|
+
as_event_consumer(self),
|
|
186
|
+
auto_reconnect=True,
|
|
187
|
+
client_type="telegram",
|
|
188
|
+
)
|
|
189
|
+
log.info("[Telegram] Event socket connection established")
|
|
190
|
+
except Exception as exc:
|
|
191
|
+
log.error("[Telegram] Failed to connect to event socket: %s", exc)
|
|
192
|
+
raise
|
|
193
|
+
|
|
194
|
+
async def run(self) -> None:
|
|
195
|
+
"""Run the bidirectional bridge.
|
|
196
|
+
|
|
197
|
+
Runs both directions concurrently:
|
|
198
|
+
- Incoming: polls Telegram for user messages, sends them to agent
|
|
199
|
+
- Outgoing: connects to event socket, forwards agent responses to Telegram
|
|
200
|
+
"""
|
|
201
|
+
log.info("[Telegram] Starting bidirectional bridge")
|
|
202
|
+
|
|
203
|
+
# Start event socket consumer in background
|
|
204
|
+
event_task = asyncio.create_task(self.connect_to_event_socket())
|
|
205
|
+
event_task.add_done_callback(
|
|
206
|
+
lambda t: (
|
|
207
|
+
log.info("[Telegram] Event socket task completed successfully")
|
|
208
|
+
if not t.cancelled() and not t.exception()
|
|
209
|
+
else log.error("[Telegram] Event socket task failed: %s", t.exception())
|
|
210
|
+
)
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
try:
|
|
214
|
+
# Poll for incoming messages from Telegram and forward to agent
|
|
215
|
+
async for message in self.get_updates():
|
|
216
|
+
if message.strip():
|
|
217
|
+
log.info("[Telegram] Received user message: '%s'", message[:200])
|
|
218
|
+
await self.send_to_agent(message)
|
|
219
|
+
except (ConnectionError, httpx.ConnectTimeout, httpx.ConnectError) as exc:
|
|
220
|
+
log.error("[Telegram] Network error during polling: %s", exc)
|
|
221
|
+
# Don't crash - just log and let the event socket reconnect
|
|
222
|
+
except asyncio.CancelledError:
|
|
223
|
+
log.info("[Telegram] Bridge cancelled")
|
|
224
|
+
event_task.cancel()
|
|
225
|
+
raise
|
|
226
|
+
finally:
|
|
227
|
+
event_task.cancel()
|
|
228
|
+
with contextlib.suppress(asyncio.CancelledError):
|
|
229
|
+
await event_task
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"""Telegram client for the minia agent framework.
|
|
2
|
+
|
|
3
|
+
This module provides a Telegram-specific implementation of the interaction layer,
|
|
4
|
+
allowing the agent to communicate with users via Telegram's Bot API."""
|
|
5
|
+
|
|
6
|
+
import asyncio
|
|
7
|
+
import logging
|
|
8
|
+
import re
|
|
9
|
+
from collections.abc import AsyncIterator
|
|
10
|
+
|
|
11
|
+
import httpx
|
|
12
|
+
|
|
13
|
+
log = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class TelegramClient:
|
|
17
|
+
"""A client to interact with the Telegram Bot API."""
|
|
18
|
+
|
|
19
|
+
_TOKEN_PATTERN = re.compile(r"^\d{8,}:[A-Za-z0-9_-]{9,}$")
|
|
20
|
+
|
|
21
|
+
def __init__(self, bot_token: str) -> None:
|
|
22
|
+
"""Initialize the Telegram client with a valid bot token.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
bot_token: The Telegram Bot API token (format: '1234567890:ABCdef...').
|
|
26
|
+
|
|
27
|
+
Raises:
|
|
28
|
+
ValueError: If the token is invalid or doesn't match expected format.
|
|
29
|
+
"""
|
|
30
|
+
if not bot_token or not self._TOKEN_PATTERN.match(bot_token):
|
|
31
|
+
raise ValueError(
|
|
32
|
+
f"Invalid Telegram bot token: {bot_token!r}. "
|
|
33
|
+
"Expected format '1234567890:ABCdefGhIjKlMnOpQrStUvWxYz'."
|
|
34
|
+
)
|
|
35
|
+
self.bot_token = bot_token
|
|
36
|
+
self.api_base = f"https://api.telegram.org/bot{self.bot_token}"
|
|
37
|
+
|
|
38
|
+
async def send_message(self, chat_id: str, text: str) -> None:
|
|
39
|
+
"""Send a message to a specific Telegram chat."""
|
|
40
|
+
url = f"{self.api_base}/sendMessage"
|
|
41
|
+
payload = {"chat_id": chat_id, "text": text, "parse_mode": "markdown"}
|
|
42
|
+
async with httpx.AsyncClient() as client:
|
|
43
|
+
response = await client.post(url, json=payload)
|
|
44
|
+
if response.status_code == 200:
|
|
45
|
+
log.debug("Successfully sent message to Telegram chat %s", chat_id)
|
|
46
|
+
else:
|
|
47
|
+
log.error("Failed to send message to Telegram: %s", response.text)
|
|
48
|
+
|
|
49
|
+
async def poll_updates(self, chat_id: str | None = None) -> AsyncIterator[dict]:
|
|
50
|
+
"""Continuously poll for new updates from the Telegram API.
|
|
51
|
+
|
|
52
|
+
Includes a 25-second sleep between iterations as requested.
|
|
53
|
+
"""
|
|
54
|
+
offset = 0
|
|
55
|
+
while True:
|
|
56
|
+
url = f"{self.api_base}/getUpdates"
|
|
57
|
+
params = {"offset": offset}
|
|
58
|
+
|
|
59
|
+
async with httpx.AsyncClient() as client:
|
|
60
|
+
response = await client.get(url, params=params)
|
|
61
|
+
|
|
62
|
+
if response.status_code == 200:
|
|
63
|
+
updates = response.json().get("result", [])
|
|
64
|
+
|
|
65
|
+
for update in updates:
|
|
66
|
+
offset = update["update_id"] + 1
|
|
67
|
+
|
|
68
|
+
# Ensure we only process messages for the configured chat.
|
|
69
|
+
# Convert chat_id to str to handle TOML config where
|
|
70
|
+
# chat_id may be an integer (e.g. 1071941511) — always
|
|
71
|
+
# compare strings so the check works regardless of config type.
|
|
72
|
+
if chat_id and "message" in update:
|
|
73
|
+
msg_chat = str(update["message"]["chat"]["id"])
|
|
74
|
+
if msg_chat != str(chat_id):
|
|
75
|
+
continue
|
|
76
|
+
|
|
77
|
+
yield update
|
|
78
|
+
|
|
79
|
+
# The 25-second pause between polls
|
|
80
|
+
await asyncio.sleep(25)
|
|
81
|
+
else:
|
|
82
|
+
log.error("Failed to poll updates from Telegram: %s", response.text)
|
|
83
|
+
await asyncio.sleep(25)
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"""minia_telegram - Telegram client for minia.
|
|
2
|
+
|
|
3
|
+
Polls incoming messages from Telegram and sends them to the minia agent input socket.
|
|
4
|
+
|
|
5
|
+
Usage:
|
|
6
|
+
minia-telegram
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import asyncio
|
|
12
|
+
|
|
13
|
+
from minia_contracts.config.settings import config
|
|
14
|
+
from minia_contracts.utils.cli import apply_config_flag, run_cli
|
|
15
|
+
from minia_contracts.utils.logging import get_logger, resolve_log_level
|
|
16
|
+
from minia_telegram.bridge import TelegramBridge
|
|
17
|
+
from minia_telegram.client import TelegramClient
|
|
18
|
+
|
|
19
|
+
logger = get_logger(__name__)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class TelegramListener:
|
|
23
|
+
"""Poll Telegram -> bridge to internal Message format -> send to agent."""
|
|
24
|
+
|
|
25
|
+
def __init__(self) -> None:
|
|
26
|
+
# Ensure chat_id is always a string (TOML may parse it as an integer).
|
|
27
|
+
raw_chat_id = getattr(config.telegram, "chat_id", "") or ""
|
|
28
|
+
self.chat_id = str(raw_chat_id) if raw_chat_id else ""
|
|
29
|
+
|
|
30
|
+
async def start(self) -> None:
|
|
31
|
+
"""Initialize the Telegram client and bridge."""
|
|
32
|
+
logger.info("[Telegram] Initializing...")
|
|
33
|
+
|
|
34
|
+
bot_token = getattr(config.telegram, "bot_token", "")
|
|
35
|
+
if not bot_token:
|
|
36
|
+
raise RuntimeError(
|
|
37
|
+
"[Telegram] Missing bot_token in config. Set it under [telegram] section."
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
self.client = TelegramClient(bot_token)
|
|
41
|
+
self.bridge = TelegramBridge(self.client)
|
|
42
|
+
|
|
43
|
+
logger.info("[Telegram] Ready")
|
|
44
|
+
|
|
45
|
+
async def run(self) -> None:
|
|
46
|
+
"""Main entry point. Runs the bidirectional bridge."""
|
|
47
|
+
assert self.bridge is not None, "start() must be called before run()"
|
|
48
|
+
logger.info("[Telegram] Starting bidirectional bridge")
|
|
49
|
+
|
|
50
|
+
try:
|
|
51
|
+
await self.bridge.run()
|
|
52
|
+
except asyncio.CancelledError:
|
|
53
|
+
logger.info("[Telegram] Bridge cancelled")
|
|
54
|
+
raise
|
|
55
|
+
except Exception:
|
|
56
|
+
logger.exception("[Telegram] Error in bridge")
|
|
57
|
+
raise
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
async def _main() -> None:
|
|
61
|
+
listener = TelegramListener()
|
|
62
|
+
await listener.start()
|
|
63
|
+
await listener.run()
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def main() -> None:
|
|
67
|
+
"""Entry point for minia-telegram."""
|
|
68
|
+
apply_config_flag()
|
|
69
|
+
log_level = resolve_log_level(config, "telegram")
|
|
70
|
+
run_cli(log_level, _main, add_console=True)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
if __name__ == "__main__":
|
|
74
|
+
main()
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
pyproject.toml
|
|
2
|
+
minia_telegram/__init__.py
|
|
3
|
+
minia_telegram/bridge.py
|
|
4
|
+
minia_telegram/client.py
|
|
5
|
+
minia_telegram/main.py
|
|
6
|
+
minia_telegram.egg-info/PKG-INFO
|
|
7
|
+
minia_telegram.egg-info/SOURCES.txt
|
|
8
|
+
minia_telegram.egg-info/dependency_links.txt
|
|
9
|
+
minia_telegram.egg-info/entry_points.txt
|
|
10
|
+
minia_telegram.egg-info/requires.txt
|
|
11
|
+
minia_telegram.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
minia_telegram
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "minia_telegram"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Telegram integration for the minia agent framework"
|
|
9
|
+
requires-python = ">=3.11,<3.14"
|
|
10
|
+
dependencies = [
|
|
11
|
+
"httpx",
|
|
12
|
+
"minia_contracts",
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
[project.scripts]
|
|
16
|
+
minia-telegram = "minia_telegram.main:main"
|
|
17
|
+
|
|
18
|
+
[tool.setuptools.packages.find]
|
|
19
|
+
where = ["."]
|
|
20
|
+
include = ["minia_telegram", "minia_telegram.*"]
|
|
21
|
+
|
|
22
|
+
[tool.uv.sources]
|
|
23
|
+
minia_contracts = { workspace = true }
|