steeper 0.1.0__py3-none-any.whl

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.
steeper/__init__.py ADDED
@@ -0,0 +1,16 @@
1
+ """Steeper — Telegram bot middleware for the Steeper platform."""
2
+
3
+ __version__ = "0.1.0"
4
+
5
+
6
+ def __getattr__(name: str):
7
+ if name == "SteeperConfig":
8
+ from steeper._config import SteeperConfig
9
+ return SteeperConfig
10
+ if name == "SteeperClient":
11
+ from steeper._client import SteeperClient
12
+ return SteeperClient
13
+ raise AttributeError(f"module 'steeper' has no attribute {name!r}")
14
+
15
+
16
+ __all__ = ["SteeperConfig", "SteeperClient"]
steeper/_client.py ADDED
@@ -0,0 +1,59 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ import time
5
+ from typing import Any
6
+
7
+ import httpx
8
+
9
+ from steeper._config import SteeperConfig
10
+
11
+ logger = logging.getLogger("steeper")
12
+
13
+
14
+ class SteeperClient:
15
+ """Async HTTP client that forwards data to the Steeper backend."""
16
+
17
+ def __init__(self, config: SteeperConfig, *, timeout: float = 10.0) -> None:
18
+ self._config = config
19
+ self._http = httpx.AsyncClient(timeout=timeout)
20
+
21
+ async def forward_update(self, update: dict[str, Any]) -> None:
22
+ """POST a raw Telegram Update to the Steeper webhook endpoint."""
23
+ try:
24
+ resp = await self._http.post(
25
+ self._config.webhook_url,
26
+ json=update,
27
+ headers={
28
+ "x-telegram-bot-api-secret-token": self._config.token_hash,
29
+ },
30
+ )
31
+ resp.raise_for_status()
32
+ except httpx.HTTPError as exc:
33
+ logger.warning("Steeper webhook failed: %s", exc)
34
+
35
+ async def log_bot_message(
36
+ self,
37
+ chat_id: int,
38
+ text: str,
39
+ message_id: int,
40
+ date: int | None = None,
41
+ ) -> None:
42
+ """POST a bot-sent message to the Steeper bot-message endpoint."""
43
+ payload = {
44
+ "chat_id": chat_id,
45
+ "text": text,
46
+ "message_id": message_id,
47
+ "date": date or int(time.time()),
48
+ }
49
+ try:
50
+ resp = await self._http.post(
51
+ self._config.bot_message_url,
52
+ json=payload,
53
+ )
54
+ resp.raise_for_status()
55
+ except httpx.HTTPError as exc:
56
+ logger.warning("Steeper bot-message log failed: %s", exc)
57
+
58
+ async def close(self) -> None:
59
+ await self._http.aclose()
steeper/_config.py ADDED
@@ -0,0 +1,34 @@
1
+ from __future__ import annotations
2
+
3
+ import hashlib
4
+ from dataclasses import dataclass
5
+
6
+
7
+ @dataclass(frozen=True, slots=True)
8
+ class SteeperConfig:
9
+ """Immutable configuration for the Steeper middleware.
10
+
11
+ Args:
12
+ base_url: Steeper backend URL (e.g. ``http://localhost:8000``).
13
+ bot_id: UUID of the bot registered in Steeper.
14
+ bot_token: Raw Telegram bot token from BotFather.
15
+ """
16
+
17
+ base_url: str
18
+ bot_id: str
19
+ bot_token: str
20
+
21
+ @property
22
+ def token_hash(self) -> str:
23
+ """SHA-256 hex digest of the bot token, used by the backend for auth."""
24
+ return hashlib.sha256(self.bot_token.encode()).hexdigest()
25
+
26
+ @property
27
+ def webhook_url(self) -> str:
28
+ base = self.base_url.rstrip("/")
29
+ return f"{base}/v1/communications/webhook/{self.bot_id}"
30
+
31
+ @property
32
+ def bot_message_url(self) -> str:
33
+ base = self.base_url.rstrip("/")
34
+ return f"{base}/v1/communications/webhook/{self.token_hash}/bot-message"
File without changes
@@ -0,0 +1,136 @@
1
+ """Steeper middleware for **aiogram v3**.
2
+
3
+ Usage::
4
+
5
+ from aiogram import Bot, Dispatcher
6
+ from steeper.integrations.aiogram import SteeperMiddleware
7
+
8
+ bot = Bot(token=BOT_TOKEN)
9
+ dp = Dispatcher()
10
+
11
+ steeper = SteeperMiddleware(
12
+ base_url="http://localhost:8000",
13
+ bot_id="<uuid>",
14
+ bot_token=BOT_TOKEN,
15
+ )
16
+ steeper.setup(dp, bot)
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import logging
22
+ from typing import Any, Callable, Awaitable
23
+
24
+ from steeper._client import SteeperClient
25
+ from steeper._config import SteeperConfig
26
+
27
+ logger = logging.getLogger("steeper.aiogram")
28
+
29
+ try:
30
+ from aiogram import BaseMiddleware, Bot, Dispatcher
31
+ from aiogram.types import Update, Message
32
+ except ImportError as _exc:
33
+ raise ImportError(
34
+ "aiogram>=3.0 is required for this integration. "
35
+ "Install it with: pip install steeper[aiogram]"
36
+ ) from _exc
37
+
38
+
39
+ class _IncomingMiddleware(BaseMiddleware):
40
+ """Outer middleware on ``Update`` — forwards raw updates to Steeper."""
41
+
42
+ def __init__(self, client: SteeperClient) -> None:
43
+ self._client = client
44
+
45
+ async def __call__(
46
+ self,
47
+ handler: Callable[[Update, dict[str, Any]], Awaitable[Any]],
48
+ event: Update,
49
+ data: dict[str, Any],
50
+ ) -> Any:
51
+ raw = event.model_dump(mode="json")
52
+ await self._client.forward_update(raw)
53
+ return await handler(event, data)
54
+
55
+
56
+ class _OutgoingMiddleware(BaseMiddleware):
57
+ """Outer middleware on ``Message`` (response) — catches bot replies."""
58
+
59
+ def __init__(self, client: SteeperClient) -> None:
60
+ self._client = client
61
+
62
+ async def __call__(
63
+ self,
64
+ handler: Callable[[Message, dict[str, Any]], Awaitable[Any]],
65
+ event: Message,
66
+ data: dict[str, Any],
67
+ ) -> Any:
68
+ result = await handler(event, data)
69
+ if isinstance(result, Message):
70
+ await self._client.log_bot_message(
71
+ chat_id=result.chat.id,
72
+ text=result.text or result.caption or "",
73
+ message_id=result.message_id,
74
+ date=int(result.date.timestamp()) if result.date else None,
75
+ )
76
+ return result
77
+
78
+
79
+ def _wrap_bot_send(bot: Bot, client: SteeperClient) -> None:
80
+ """Monkey-patch ``Bot.send_message`` to also log to Steeper."""
81
+ _original_send = bot.send_message
82
+
83
+ async def _patched_send(*args: Any, **kwargs: Any) -> Message:
84
+ result: Message = await _original_send(*args, **kwargs)
85
+ try:
86
+ await client.log_bot_message(
87
+ chat_id=result.chat.id,
88
+ text=result.text or result.caption or "",
89
+ message_id=result.message_id,
90
+ date=int(result.date.timestamp()) if result.date else None,
91
+ )
92
+ except Exception:
93
+ logger.debug("Failed to log outgoing message", exc_info=True)
94
+ return result
95
+
96
+ bot.send_message = _patched_send # type: ignore[assignment]
97
+
98
+
99
+ class SteeperMiddleware:
100
+ """All-in-one Steeper integration for aiogram v3.
101
+
102
+ Call :meth:`setup` to register both incoming and outgoing hooks.
103
+ """
104
+
105
+ def __init__(
106
+ self,
107
+ base_url: str,
108
+ bot_id: str,
109
+ bot_token: str,
110
+ *,
111
+ timeout: float = 10.0,
112
+ ) -> None:
113
+ self._config = SteeperConfig(
114
+ base_url=base_url,
115
+ bot_id=bot_id,
116
+ bot_token=bot_token,
117
+ )
118
+ self._client = SteeperClient(self._config, timeout=timeout)
119
+
120
+ def setup(self, dp: Dispatcher, bot: Bot) -> None:
121
+ """Register Steeper on the dispatcher and bot.
122
+
123
+ - Incoming updates are forwarded via an outer Update middleware.
124
+ - Outgoing ``send_message`` calls are patched to log bot replies.
125
+ """
126
+ dp.update.outer_middleware(self._incoming)
127
+ _wrap_bot_send(bot, self._client)
128
+ logger.info("Steeper middleware registered for aiogram")
129
+
130
+ @property
131
+ def _incoming(self) -> _IncomingMiddleware:
132
+ return _IncomingMiddleware(self._client)
133
+
134
+ @property
135
+ def client(self) -> SteeperClient:
136
+ return self._client
@@ -0,0 +1,176 @@
1
+ """Steeper middleware for **python-telegram-bot** (PTB v20+).
2
+
3
+ Usage::
4
+
5
+ from telegram.ext import ApplicationBuilder
6
+ from steeper.integrations.ptb import SteeperMiddleware
7
+
8
+ app = ApplicationBuilder().token(BOT_TOKEN).build()
9
+
10
+ steeper = SteeperMiddleware(
11
+ base_url="http://localhost:8000",
12
+ bot_id="<uuid>",
13
+ bot_token=BOT_TOKEN,
14
+ )
15
+ steeper.setup(app)
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ import logging
21
+ import time
22
+ from typing import Any
23
+
24
+ from steeper._client import SteeperClient
25
+ from steeper._config import SteeperConfig
26
+
27
+ logger = logging.getLogger("steeper.ptb")
28
+
29
+ try:
30
+ from telegram import Message, Update, User, Chat
31
+ from telegram.ext import (
32
+ Application,
33
+ BaseHandler,
34
+ ContextTypes,
35
+ )
36
+ except ImportError as _exc:
37
+ raise ImportError(
38
+ "python-telegram-bot>=20.0 is required for this integration. "
39
+ "Install it with: pip install steeper[ptb]"
40
+ ) from _exc
41
+
42
+
43
+ def _update_to_dict(update: Update) -> dict[str, Any]:
44
+ """Convert a PTB Update to a Telegram-compatible dict."""
45
+ raw: dict[str, Any] = {"update_id": update.update_id}
46
+
47
+ if update.message:
48
+ raw["message"] = _message_to_dict(update.message)
49
+ if update.edited_message:
50
+ raw["edited_message"] = _message_to_dict(update.edited_message)
51
+ return raw
52
+
53
+
54
+ def _user_to_dict(user: User) -> dict[str, Any]:
55
+ data: dict[str, Any] = {
56
+ "id": user.id,
57
+ "is_bot": user.is_bot,
58
+ "first_name": user.first_name,
59
+ }
60
+ if user.last_name:
61
+ data["last_name"] = user.last_name
62
+ if user.username:
63
+ data["username"] = user.username
64
+ if user.language_code:
65
+ data["language_code"] = user.language_code
66
+ return data
67
+
68
+
69
+ def _chat_to_dict(chat: Chat) -> dict[str, Any]:
70
+ data: dict[str, Any] = {"id": chat.id, "type": chat.type}
71
+ if chat.title:
72
+ data["title"] = chat.title
73
+ if chat.username:
74
+ data["username"] = chat.username
75
+ if chat.first_name:
76
+ data["first_name"] = chat.first_name
77
+ if chat.last_name:
78
+ data["last_name"] = chat.last_name
79
+ return data
80
+
81
+
82
+ def _message_to_dict(msg: Message) -> dict[str, Any]:
83
+ data: dict[str, Any] = {
84
+ "message_id": msg.message_id,
85
+ "chat": _chat_to_dict(msg.chat),
86
+ "date": int(msg.date.timestamp()) if msg.date else int(time.time()),
87
+ }
88
+ if msg.from_user:
89
+ data["from"] = _user_to_dict(msg.from_user)
90
+ if msg.text:
91
+ data["text"] = msg.text
92
+ if msg.caption:
93
+ data["caption"] = msg.caption
94
+ return data
95
+
96
+
97
+ class _SteeperHandler(BaseHandler[Update, ContextTypes.DEFAULT_TYPE]):
98
+ """Low-priority handler that intercepts every update for Steeper logging."""
99
+
100
+ def __init__(self, client: SteeperClient) -> None:
101
+ super().__init__(callback=self._noop)
102
+ self._client = client
103
+
104
+ def check_update(self, update: object) -> bool:
105
+ return isinstance(update, Update)
106
+
107
+ async def handle_update(
108
+ self,
109
+ update: Update,
110
+ application: Application, # type: ignore[type-arg]
111
+ check_result: Any,
112
+ context: ContextTypes.DEFAULT_TYPE,
113
+ ) -> None:
114
+ raw = _update_to_dict(update)
115
+ await self._client.forward_update(raw)
116
+
117
+ @staticmethod
118
+ async def _noop(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
119
+ pass
120
+
121
+
122
+ def _wrap_bot_send(application: Application, client: SteeperClient) -> None: # type: ignore[type-arg]
123
+ """Patch ``Bot.send_message`` to also log to Steeper."""
124
+ bot = application.bot
125
+ _original_send = bot.send_message
126
+
127
+ async def _patched_send(*args: Any, **kwargs: Any) -> Message:
128
+ result: Message = await _original_send(*args, **kwargs)
129
+ try:
130
+ await client.log_bot_message(
131
+ chat_id=result.chat.id,
132
+ text=result.text or result.caption or "",
133
+ message_id=result.message_id,
134
+ date=int(result.date.timestamp()) if result.date else None,
135
+ )
136
+ except Exception:
137
+ logger.debug("Failed to log outgoing message", exc_info=True)
138
+ return result
139
+
140
+ bot.send_message = _patched_send # type: ignore[assignment]
141
+
142
+
143
+ class SteeperMiddleware:
144
+ """All-in-one Steeper integration for python-telegram-bot v20+.
145
+
146
+ Call :meth:`setup` to register both incoming and outgoing hooks.
147
+ """
148
+
149
+ def __init__(
150
+ self,
151
+ base_url: str,
152
+ bot_id: str,
153
+ bot_token: str,
154
+ *,
155
+ timeout: float = 10.0,
156
+ ) -> None:
157
+ self._config = SteeperConfig(
158
+ base_url=base_url,
159
+ bot_id=bot_id,
160
+ bot_token=bot_token,
161
+ )
162
+ self._client = SteeperClient(self._config, timeout=timeout)
163
+
164
+ def setup(self, application: Application) -> None: # type: ignore[type-arg]
165
+ """Register Steeper hooks on a PTB Application.
166
+
167
+ - Incoming: a low-priority handler that captures every Update.
168
+ - Outgoing: ``Bot.send_message`` is patched to log bot replies.
169
+ """
170
+ application.add_handler(_SteeperHandler(self._client), group=-1)
171
+ _wrap_bot_send(application, self._client)
172
+ logger.info("Steeper middleware registered for python-telegram-bot")
173
+
174
+ @property
175
+ def client(self) -> SteeperClient:
176
+ return self._client
@@ -0,0 +1,167 @@
1
+ """Steeper middleware for **pyTelegramBotAPI** (telebot).
2
+
3
+ Usage::
4
+
5
+ import telebot
6
+ from steeper.integrations.telebot import SteeperMiddleware
7
+
8
+ bot = telebot.TeleBot(BOT_TOKEN)
9
+
10
+ steeper = SteeperMiddleware(
11
+ base_url="http://localhost:8000",
12
+ bot_id="<uuid>",
13
+ bot_token=BOT_TOKEN,
14
+ )
15
+ steeper.setup(bot)
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ import asyncio
21
+ import logging
22
+ import time
23
+ from typing import Any
24
+
25
+ from steeper._client import SteeperClient
26
+ from steeper._config import SteeperConfig
27
+
28
+ logger = logging.getLogger("steeper.telebot")
29
+
30
+ try:
31
+ import telebot as _telebot
32
+ from telebot import types as tg_types
33
+ except ImportError as _exc:
34
+ raise ImportError(
35
+ "pyTelegramBotAPI>=4.0 is required for this integration. "
36
+ "Install it with: pip install steeper[telebot]"
37
+ ) from _exc
38
+
39
+
40
+ def _run_async(coro: Any) -> None:
41
+ """Fire-and-forget an async coroutine from sync context."""
42
+ try:
43
+ loop = asyncio.get_running_loop()
44
+ loop.create_task(coro)
45
+ except RuntimeError:
46
+ asyncio.run(coro)
47
+
48
+
49
+ def _update_to_dict(update: tg_types.Update) -> dict[str, Any]:
50
+ """Convert a telebot Update to a Telegram-compatible dict."""
51
+ raw: dict[str, Any] = {"update_id": update.update_id}
52
+
53
+ if update.message:
54
+ raw["message"] = _message_to_dict(update.message)
55
+ if update.edited_message:
56
+ raw["edited_message"] = _message_to_dict(update.edited_message)
57
+ return raw
58
+
59
+
60
+ def _message_to_dict(msg: tg_types.Message) -> dict[str, Any]:
61
+ """Convert a telebot Message to a Telegram-compatible dict."""
62
+ data: dict[str, Any] = {
63
+ "message_id": msg.message_id,
64
+ "chat": {"id": msg.chat.id, "type": msg.chat.type},
65
+ "date": msg.date or int(time.time()),
66
+ }
67
+ if msg.chat.title:
68
+ data["chat"]["title"] = msg.chat.title
69
+ if msg.chat.username:
70
+ data["chat"]["username"] = msg.chat.username
71
+ if msg.chat.first_name:
72
+ data["chat"]["first_name"] = msg.chat.first_name
73
+ if msg.chat.last_name:
74
+ data["chat"]["last_name"] = msg.chat.last_name
75
+
76
+ if msg.from_user:
77
+ data["from"] = {
78
+ "id": msg.from_user.id,
79
+ "is_bot": msg.from_user.is_bot,
80
+ "first_name": msg.from_user.first_name,
81
+ }
82
+ if msg.from_user.last_name:
83
+ data["from"]["last_name"] = msg.from_user.last_name
84
+ if msg.from_user.username:
85
+ data["from"]["username"] = msg.from_user.username
86
+ if msg.from_user.language_code:
87
+ data["from"]["language_code"] = msg.from_user.language_code
88
+
89
+ if msg.text:
90
+ data["text"] = msg.text
91
+ if msg.caption:
92
+ data["caption"] = msg.caption
93
+ return data
94
+
95
+
96
+ class SteeperMiddleware:
97
+ """All-in-one Steeper integration for pyTelegramBotAPI.
98
+
99
+ Call :meth:`setup` to register both incoming and outgoing hooks.
100
+ """
101
+
102
+ def __init__(
103
+ self,
104
+ base_url: str,
105
+ bot_id: str,
106
+ bot_token: str,
107
+ *,
108
+ timeout: float = 10.0,
109
+ ) -> None:
110
+ self._config = SteeperConfig(
111
+ base_url=base_url,
112
+ bot_id=bot_id,
113
+ bot_token=bot_token,
114
+ )
115
+ self._client = SteeperClient(self._config, timeout=timeout)
116
+
117
+ def setup(self, bot: _telebot.TeleBot) -> None:
118
+ """Register Steeper hooks on a sync TeleBot instance.
119
+
120
+ - Incoming: ``middleware_handler`` that fires on every update.
121
+ - Outgoing: ``send_message`` is patched to log bot replies.
122
+ """
123
+ bot.use_class_middlewares = True
124
+
125
+ class _Incoming(_telebot.handler_backends.BaseMiddleware):
126
+ update_types = ["message", "edited_message"]
127
+
128
+ def __init__(self_inner) -> None: # noqa: N805
129
+ super().__init__()
130
+ self_inner.client = self._client
131
+
132
+ def pre_process(self_inner, message: tg_types.Message, data: dict[str, Any]) -> None: # noqa: N805
133
+ update_dict: dict[str, Any] = {
134
+ "update_id": 0,
135
+ "message": _message_to_dict(message),
136
+ }
137
+ _run_async(self_inner.client.forward_update(update_dict))
138
+
139
+ def post_process(self_inner, message: tg_types.Message, data: dict[str, Any], exception: BaseException | None) -> None: # noqa: N805
140
+ pass
141
+
142
+ bot.register_middleware_handler(_Incoming())
143
+
144
+ _original_send = bot.send_message
145
+
146
+ def _patched_send(*args: Any, **kwargs: Any) -> tg_types.Message:
147
+ result: tg_types.Message = _original_send(*args, **kwargs)
148
+ try:
149
+ _run_async(
150
+ self._client.log_bot_message(
151
+ chat_id=result.chat.id,
152
+ text=result.text or result.caption or "",
153
+ message_id=result.message_id,
154
+ date=result.date or int(time.time()),
155
+ )
156
+ )
157
+ except Exception:
158
+ logger.debug("Failed to log outgoing message", exc_info=True)
159
+ return result
160
+
161
+ bot.send_message = _patched_send # type: ignore[assignment]
162
+
163
+ logger.info("Steeper middleware registered for pyTelegramBotAPI")
164
+
165
+ @property
166
+ def client(self) -> SteeperClient:
167
+ return self._client
steeper/py.typed ADDED
File without changes
@@ -0,0 +1,130 @@
1
+ Metadata-Version: 2.4
2
+ Name: steeper
3
+ Version: 0.1.0
4
+ Summary: Telegram bot middleware for Steeper — intercepts updates and outgoing messages to sync with the Steeper platform.
5
+ Project-URL: Homepage, https://github.com/steeper/steeper
6
+ Project-URL: Repository, https://github.com/steeper/steeper
7
+ License-Expression: MIT
8
+ Keywords: aiogram,bot,middleware,python-telegram-bot,steeper,telebot,telegram
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Framework :: AsyncIO
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Topic :: Communications :: Chat
19
+ Classifier: Typing :: Typed
20
+ Requires-Python: >=3.10
21
+ Requires-Dist: httpx>=0.25
22
+ Provides-Extra: aiogram
23
+ Requires-Dist: aiogram>=3.0; extra == 'aiogram'
24
+ Provides-Extra: all
25
+ Requires-Dist: aiogram>=3.0; extra == 'all'
26
+ Requires-Dist: pytelegrambotapi>=4.0; extra == 'all'
27
+ Requires-Dist: python-telegram-bot>=20.0; extra == 'all'
28
+ Provides-Extra: ptb
29
+ Requires-Dist: python-telegram-bot>=20.0; extra == 'ptb'
30
+ Provides-Extra: telebot
31
+ Requires-Dist: pytelegrambotapi>=4.0; extra == 'telebot'
32
+ Description-Content-Type: text/markdown
33
+
34
+ # Steeper
35
+
36
+ Telegram bot middleware that syncs incoming user messages and outgoing bot replies with the **Steeper** platform.
37
+
38
+ ## Installation
39
+
40
+ ```bash
41
+ # Core (pick one extra for your framework)
42
+ pip install steeper[aiogram] # aiogram v3
43
+ pip install steeper[telebot] # pyTelegramBotAPI
44
+ pip install steeper[ptb] # python-telegram-bot v20+
45
+ ```
46
+
47
+ ## Configuration
48
+
49
+ Every integration requires three values:
50
+
51
+ | Parameter | Description |
52
+ |-------------|----------------------------------------------|
53
+ | `base_url` | Steeper backend URL (e.g. `http://localhost:8000`) |
54
+ | `bot_id` | UUID of the bot registered in Steeper |
55
+ | `bot_token` | Raw Telegram bot token from BotFather |
56
+
57
+ ## Usage
58
+
59
+ ### aiogram v3
60
+
61
+ ```python
62
+ from aiogram import Bot, Dispatcher
63
+ from steeper.integrations.aiogram import SteeperMiddleware
64
+
65
+ BOT_TOKEN = "123456:ABC-DEF..."
66
+ bot = Bot(token=BOT_TOKEN)
67
+ dp = Dispatcher()
68
+
69
+ steeper = SteeperMiddleware(
70
+ base_url="http://localhost:8000",
71
+ bot_id="your-bot-uuid",
72
+ bot_token=BOT_TOKEN,
73
+ )
74
+ steeper.setup(dp, bot)
75
+
76
+ # ... register your handlers as usual ...
77
+ dp.run_polling(bot)
78
+ ```
79
+
80
+ ### pyTelegramBotAPI (telebot)
81
+
82
+ ```python
83
+ import telebot
84
+ from steeper.integrations.telebot import SteeperMiddleware
85
+
86
+ BOT_TOKEN = "123456:ABC-DEF..."
87
+ bot = telebot.TeleBot(BOT_TOKEN)
88
+
89
+ steeper = SteeperMiddleware(
90
+ base_url="http://localhost:8000",
91
+ bot_id="your-bot-uuid",
92
+ bot_token=BOT_TOKEN,
93
+ )
94
+ steeper.setup(bot)
95
+
96
+ # ... register your handlers as usual ...
97
+ bot.polling()
98
+ ```
99
+
100
+ ### python-telegram-bot v20+
101
+
102
+ ```python
103
+ from telegram.ext import ApplicationBuilder
104
+ from steeper.integrations.ptb import SteeperMiddleware
105
+
106
+ BOT_TOKEN = "123456:ABC-DEF..."
107
+ app = ApplicationBuilder().token(BOT_TOKEN).build()
108
+
109
+ steeper = SteeperMiddleware(
110
+ base_url="http://localhost:8000",
111
+ bot_id="your-bot-uuid",
112
+ bot_token=BOT_TOKEN,
113
+ )
114
+ steeper.setup(app)
115
+
116
+ # ... register your handlers as usual ...
117
+ app.run_polling()
118
+ ```
119
+
120
+ ## How it works
121
+
122
+ 1. **Incoming messages** — the middleware intercepts every Telegram Update, serializes it to the standard Telegram JSON format, and POSTs it to the Steeper webhook endpoint. Your handlers still run normally.
123
+
124
+ 2. **Outgoing messages** — `Bot.send_message` is patched so that every bot reply is also logged to the Steeper bot-message endpoint.
125
+
126
+ Both operations are fire-and-forget: if the Steeper backend is unreachable, a warning is logged but your bot continues to work.
127
+
128
+ ## License
129
+
130
+ MIT
@@ -0,0 +1,11 @@
1
+ steeper/__init__.py,sha256=lf6SiHgcInqYbkhHmqUskqlS3VJvjdd-2vqFd9nSefc,462
2
+ steeper/_client.py,sha256=4ElOj0SVFnCFU98W0s_FJgzj95fl1g9kgFF46bb-W2k,1756
3
+ steeper/_config.py,sha256=GKWDYbIB_t_S4erWjwv9Swmvvk44jW0L9rjzr68tIDg,987
4
+ steeper/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ steeper/integrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ steeper/integrations/aiogram.py,sha256=P7YsQPDtltkSNoV95O5KT4nDhb4GZbChPHxOYhk5_WE,4109
7
+ steeper/integrations/ptb.py,sha256=DB0lyq1wEeO3aN_gZe31sj_NOX0CpxgK0nHqmdkAA70,5313
8
+ steeper/integrations/telebot.py,sha256=YI_cWHS8lbLldfIdjPDDSJ4yhm9s-1IfV_ad6aI10CQ,5319
9
+ steeper-0.1.0.dist-info/METADATA,sha256=EHGee0FWIavTQ5PurY9x1BLcVYEl1g36X9gk5lm9p2M,3877
10
+ steeper-0.1.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
11
+ steeper-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.29.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any