lo-aiogram 0.2.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.
- lo_aiogram-0.2.0/LICENSE +21 -0
- lo_aiogram-0.2.0/PKG-INFO +30 -0
- lo_aiogram-0.2.0/README.md +19 -0
- lo_aiogram-0.2.0/pyproject.toml +16 -0
- lo_aiogram-0.2.0/setup.cfg +4 -0
- lo_aiogram-0.2.0/src/lo_aiogram/__init__.py +86 -0
- lo_aiogram-0.2.0/src/lo_aiogram/contract.json +1304 -0
- lo_aiogram-0.2.0/src/lo_aiogram.egg-info/PKG-INFO +30 -0
- lo_aiogram-0.2.0/src/lo_aiogram.egg-info/SOURCES.txt +10 -0
- lo_aiogram-0.2.0/src/lo_aiogram.egg-info/dependency_links.txt +1 -0
- lo_aiogram-0.2.0/src/lo_aiogram.egg-info/requires.txt +1 -0
- lo_aiogram-0.2.0/src/lo_aiogram.egg-info/top_level.txt +1 -0
lo_aiogram-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 LO 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,30 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: lo-aiogram
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: LO compatibility session for aiogram 3
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Requires-Python: >=3.11
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Requires-Dist: aiogram<4,>=3.31
|
|
10
|
+
Dynamic: license-file
|
|
11
|
+
|
|
12
|
+
# lo-aiogram
|
|
13
|
+
|
|
14
|
+
LO HTTP session for aiogram 3.31–3.x. The session serializes aiogram methods for LO and preserves requested operations and errors.
|
|
15
|
+
|
|
16
|
+
```python
|
|
17
|
+
import os
|
|
18
|
+
from aiogram import Bot
|
|
19
|
+
from lo_aiogram import LoAiohttpSession
|
|
20
|
+
|
|
21
|
+
bot = Bot(os.environ["LO_BOT_TOKEN"], session=LoAiohttpSession())
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Close the session when the bot stops. Use HTTPS for custom base_url. Tests may explicitly allow loopback HTTP. Single files and thumbnails use parameter-named parts; album files use attach:// references.
|
|
25
|
+
|
|
26
|
+
Unsupported fields, mixed albums, disabled video uploads and unimplemented methods fail explicitly. The adapter never strips fields, changes a video into a document, splits an album, retries a mutation or returns success for an unsupported operation. Existing Telegram handlers must be adapted to the documented LO contract. Read installation capabilities explicitly through getMe when needed.
|
|
27
|
+
|
|
28
|
+
Structured server failure parameters are exposed as lo_reason and lo_parameter on aiogram exceptions. Non-JSON HTTP 5xx responses become TelegramServerError without exposing HTML diagnostics. Secretary requests with business_connection_id are rejected locally; use the native Bot SDK with LO consent context.
|
|
29
|
+
|
|
30
|
+
The packaged contract describes accepted requests. Tests run real aiogram clients against the strict LO emulator; the emulator does not reproduce production storage, authentication, transcoding or permissions.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# lo-aiogram
|
|
2
|
+
|
|
3
|
+
LO HTTP session for aiogram 3.31–3.x. The session serializes aiogram methods for LO and preserves requested operations and errors.
|
|
4
|
+
|
|
5
|
+
```python
|
|
6
|
+
import os
|
|
7
|
+
from aiogram import Bot
|
|
8
|
+
from lo_aiogram import LoAiohttpSession
|
|
9
|
+
|
|
10
|
+
bot = Bot(os.environ["LO_BOT_TOKEN"], session=LoAiohttpSession())
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Close the session when the bot stops. Use HTTPS for custom base_url. Tests may explicitly allow loopback HTTP. Single files and thumbnails use parameter-named parts; album files use attach:// references.
|
|
14
|
+
|
|
15
|
+
Unsupported fields, mixed albums, disabled video uploads and unimplemented methods fail explicitly. The adapter never strips fields, changes a video into a document, splits an album, retries a mutation or returns success for an unsupported operation. Existing Telegram handlers must be adapted to the documented LO contract. Read installation capabilities explicitly through getMe when needed.
|
|
16
|
+
|
|
17
|
+
Structured server failure parameters are exposed as lo_reason and lo_parameter on aiogram exceptions. Non-JSON HTTP 5xx responses become TelegramServerError without exposing HTML diagnostics. Secretary requests with business_connection_id are rejected locally; use the native Bot SDK with LO consent context.
|
|
18
|
+
|
|
19
|
+
The packaged contract describes accepted requests. Tests run real aiogram clients against the strict LO emulator; the emulator does not reproduce production storage, authentication, transcoding or permissions.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "lo-aiogram"
|
|
7
|
+
version = "0.2.0"
|
|
8
|
+
description = "LO compatibility session for aiogram 3"
|
|
9
|
+
requires-python = ">=3.11"
|
|
10
|
+
dependencies = ["aiogram>=3.31,<4"]
|
|
11
|
+
license = "MIT"
|
|
12
|
+
readme = "README.md"
|
|
13
|
+
license-files = ["LICENSE"]
|
|
14
|
+
|
|
15
|
+
[tool.setuptools.package-data]
|
|
16
|
+
lo_aiogram = ["contract.json"]
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"""LO HTTP serialization for aiogram, without automatic request adaptation."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from urllib.parse import urlsplit
|
|
5
|
+
|
|
6
|
+
from aiogram.client.session.aiohttp import AiohttpSession
|
|
7
|
+
from aiogram.client.telegram import TelegramAPIServer
|
|
8
|
+
from aiogram.exceptions import TelegramBadRequest, TelegramServerError
|
|
9
|
+
from aiogram.types import InputFile
|
|
10
|
+
from aiohttp import FormData
|
|
11
|
+
|
|
12
|
+
__all__ = ["LoAiohttpSession"]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class LoAiohttpSession(AiohttpSession):
|
|
16
|
+
"""Send the requested operation once, with parameter-named upload parts."""
|
|
17
|
+
|
|
18
|
+
def __init__(self, *, base_url="https://api.lo.ink", allow_insecure_loopback=False, **kwargs):
|
|
19
|
+
parsed = urlsplit(base_url)
|
|
20
|
+
if (
|
|
21
|
+
not parsed.hostname
|
|
22
|
+
or base_url.strip() != base_url
|
|
23
|
+
or parsed.username
|
|
24
|
+
or parsed.password
|
|
25
|
+
or parsed.query
|
|
26
|
+
or parsed.fragment
|
|
27
|
+
or parsed.scheme != "https"
|
|
28
|
+
and not (
|
|
29
|
+
allow_insecure_loopback
|
|
30
|
+
and parsed.scheme == "http"
|
|
31
|
+
and parsed.hostname in ("localhost", "127.0.0.1", "::1")
|
|
32
|
+
)
|
|
33
|
+
):
|
|
34
|
+
raise ValueError("Use HTTPS or explicitly enabled loopback HTTP")
|
|
35
|
+
super().__init__(api=TelegramAPIServer.from_base(base_url.rstrip("/")), **kwargs)
|
|
36
|
+
|
|
37
|
+
def build_form_data(self, bot, method):
|
|
38
|
+
if getattr(method, "business_connection_id", None) is not None:
|
|
39
|
+
raise ValueError("LO secretary operations require native consent context")
|
|
40
|
+
form = FormData(quote_fields=False)
|
|
41
|
+
attachments: dict[str, InputFile] = {}
|
|
42
|
+
for key, original in method.model_dump(warnings=False).items():
|
|
43
|
+
original = getattr(method, key, original)
|
|
44
|
+
if isinstance(original, InputFile):
|
|
45
|
+
form.add_field(key, original.read(bot), filename=original.filename or key)
|
|
46
|
+
else:
|
|
47
|
+
value = self.prepare_value(original, bot=bot, files=attachments)
|
|
48
|
+
if value is not None and value != "":
|
|
49
|
+
form.add_field(key, value)
|
|
50
|
+
for key, value in attachments.items():
|
|
51
|
+
form.add_field(key, value.read(bot), filename=value.filename or key)
|
|
52
|
+
return form
|
|
53
|
+
|
|
54
|
+
def check_response(self, bot, method, status_code, content):
|
|
55
|
+
if status_code >= 500:
|
|
56
|
+
try:
|
|
57
|
+
parsed = json.loads(content)
|
|
58
|
+
except (ValueError, TypeError):
|
|
59
|
+
parsed = None
|
|
60
|
+
if not isinstance(parsed, dict) or not isinstance(parsed.get("ok"), bool):
|
|
61
|
+
raise TelegramServerError(
|
|
62
|
+
method=method, message="LO Bot API is temporarily unavailable"
|
|
63
|
+
)
|
|
64
|
+
try:
|
|
65
|
+
return super().check_response(bot, method, status_code, content)
|
|
66
|
+
except (TelegramBadRequest, TelegramServerError) as error:
|
|
67
|
+
try:
|
|
68
|
+
parameters = json.loads(content).get("parameters", {})
|
|
69
|
+
except (ValueError, TypeError, AttributeError):
|
|
70
|
+
parameters = {}
|
|
71
|
+
if isinstance(parameters, dict):
|
|
72
|
+
metadata = {}
|
|
73
|
+
reason = parameters.get("reason")
|
|
74
|
+
parameter = parameters.get("parameter")
|
|
75
|
+
if reason in (
|
|
76
|
+
"unsupported_parameter",
|
|
77
|
+
"upload_only",
|
|
78
|
+
"feature_disabled",
|
|
79
|
+
"method_not_implemented",
|
|
80
|
+
):
|
|
81
|
+
metadata["lo_reason"] = reason
|
|
82
|
+
if isinstance(parameter, str) and parameter.isidentifier():
|
|
83
|
+
metadata["lo_parameter"] = parameter
|
|
84
|
+
for attribute, value in metadata.items():
|
|
85
|
+
setattr(error, attribute, value)
|
|
86
|
+
raise
|