python-vk-bot 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.
- python_vk_bot/.agents/skills/telegram-to-vk/SKILL.md +38 -0
- python_vk_bot/.agents/skills/telegram-to-vk/references/migration-mapping.md +159 -0
- python_vk_bot/__init__.py +30 -0
- python_vk_bot/_config.py +22 -0
- python_vk_bot/application.py +221 -0
- python_vk_bot/bot.py +184 -0
- python_vk_bot/callback.py +115 -0
- python_vk_bot/context.py +19 -0
- python_vk_bot/errors.py +42 -0
- python_vk_bot/filters.py +40 -0
- python_vk_bot/handlers.py +60 -0
- python_vk_bot/long_poll.py +91 -0
- python_vk_bot/models/__init__.py +4 -0
- python_vk_bot/models/api.py +37 -0
- python_vk_bot/models/base.py +9 -0
- python_vk_bot/models/events.py +67 -0
- python_vk_bot/models/messages.py +57 -0
- python_vk_bot/py.typed +0 -0
- python_vk_bot/settings.py +26 -0
- python_vk_bot/transport.py +154 -0
- python_vk_bot-0.1.0.dist-info/METADATA +237 -0
- python_vk_bot-0.1.0.dist-info/RECORD +24 -0
- python_vk_bot-0.1.0.dist-info/WHEEL +4 -0
- python_vk_bot-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"""Reusable HTTP transport; request credentials never become client defaults."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import re
|
|
5
|
+
from collections.abc import Mapping
|
|
6
|
+
from types import TracebackType
|
|
7
|
+
from typing import Self, cast
|
|
8
|
+
|
|
9
|
+
import httpx
|
|
10
|
+
from pydantic import JsonValue, SecretStr, ValidationError
|
|
11
|
+
|
|
12
|
+
from ._config import TransportConfig
|
|
13
|
+
from .errors import (
|
|
14
|
+
APIError,
|
|
15
|
+
HTTPError,
|
|
16
|
+
InvalidResponseError,
|
|
17
|
+
LifecycleError,
|
|
18
|
+
NetworkError,
|
|
19
|
+
RateLimitError,
|
|
20
|
+
)
|
|
21
|
+
from .models.api import APIEnvelope
|
|
22
|
+
|
|
23
|
+
type Parameter = str | int | float | bool | None
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class _RedactQuery(logging.Filter):
|
|
27
|
+
"""HTTPX INFO logs otherwise expose the Long Poll key in request URLs."""
|
|
28
|
+
|
|
29
|
+
def filter(self, record: logging.LogRecord) -> bool:
|
|
30
|
+
if isinstance(record.args, tuple):
|
|
31
|
+
record.args = tuple(
|
|
32
|
+
value.copy_with(query=None)
|
|
33
|
+
if isinstance(value, httpx.URL) and value.query
|
|
34
|
+
else value
|
|
35
|
+
for value in record.args
|
|
36
|
+
)
|
|
37
|
+
return True
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class HTTPTransport:
|
|
41
|
+
"""Own a client or borrow an injected client without closing it."""
|
|
42
|
+
|
|
43
|
+
def __init__(self, config: TransportConfig, *, client: httpx.AsyncClient | None = None) -> None:
|
|
44
|
+
self.config = config
|
|
45
|
+
self._client = client
|
|
46
|
+
self._owns_client = client is None
|
|
47
|
+
self._started = False
|
|
48
|
+
httpx_logger = logging.getLogger("httpx")
|
|
49
|
+
if not any(isinstance(item, _RedactQuery) for item in httpx_logger.filters):
|
|
50
|
+
httpx_logger.addFilter(_RedactQuery())
|
|
51
|
+
|
|
52
|
+
async def initialize(self) -> None:
|
|
53
|
+
if self._started:
|
|
54
|
+
return
|
|
55
|
+
if self._client is None:
|
|
56
|
+
self._client = httpx.AsyncClient(timeout=self.config.timeout)
|
|
57
|
+
if self._client.is_closed:
|
|
58
|
+
raise LifecycleError("The injected HTTP client is closed")
|
|
59
|
+
if "authorization" in self._client.headers:
|
|
60
|
+
raise ValueError("Injected clients must not define a default Authorization header")
|
|
61
|
+
if {"access_token", "key", "secret"}.intersection(self._client.params):
|
|
62
|
+
raise ValueError("Injected clients must not define credential query parameters")
|
|
63
|
+
self._started = True
|
|
64
|
+
|
|
65
|
+
async def shutdown(self) -> None:
|
|
66
|
+
self._started = False
|
|
67
|
+
if self._owns_client and self._client is not None:
|
|
68
|
+
await self._client.aclose()
|
|
69
|
+
self._client = None
|
|
70
|
+
|
|
71
|
+
async def __aenter__(self) -> Self:
|
|
72
|
+
await self.initialize()
|
|
73
|
+
return self
|
|
74
|
+
|
|
75
|
+
async def __aexit__(
|
|
76
|
+
self,
|
|
77
|
+
exc_type: type[BaseException] | None,
|
|
78
|
+
exc: BaseException | None,
|
|
79
|
+
traceback: TracebackType | None,
|
|
80
|
+
) -> None:
|
|
81
|
+
await self.shutdown()
|
|
82
|
+
|
|
83
|
+
async def _request(
|
|
84
|
+
self,
|
|
85
|
+
method: str,
|
|
86
|
+
url: str,
|
|
87
|
+
*,
|
|
88
|
+
data: Mapping[str, str] | None = None,
|
|
89
|
+
params: Mapping[str, str] | None = None,
|
|
90
|
+
headers: Mapping[str, str] | None = None,
|
|
91
|
+
timeout: float | None = None,
|
|
92
|
+
) -> JsonValue:
|
|
93
|
+
if not self._started or self._client is None:
|
|
94
|
+
raise LifecycleError("Initialize the bot or use its async context manager")
|
|
95
|
+
try:
|
|
96
|
+
response = await self._client.request(
|
|
97
|
+
method,
|
|
98
|
+
url,
|
|
99
|
+
data=data,
|
|
100
|
+
params=params,
|
|
101
|
+
headers=headers,
|
|
102
|
+
timeout=self.config.timeout if timeout is None else timeout,
|
|
103
|
+
follow_redirects=False,
|
|
104
|
+
auth=None,
|
|
105
|
+
)
|
|
106
|
+
except httpx.RequestError:
|
|
107
|
+
raise NetworkError("VK HTTP request could not complete") from None
|
|
108
|
+
if not response.is_success:
|
|
109
|
+
raise HTTPError(response.status_code)
|
|
110
|
+
try:
|
|
111
|
+
return cast(JsonValue, response.json())
|
|
112
|
+
except ValueError:
|
|
113
|
+
raise InvalidResponseError("VK returned invalid JSON") from None
|
|
114
|
+
|
|
115
|
+
async def call(self, method: str, parameters: Mapping[str, Parameter]) -> JsonValue:
|
|
116
|
+
if re.fullmatch(r"[A-Za-z]+\.[A-Za-z]+", method) is None:
|
|
117
|
+
raise ValueError("Expected a VK method name such as messages.send")
|
|
118
|
+
if {"v", "access_token"}.intersection(parameters):
|
|
119
|
+
raise ValueError("Version and credentials belong to the transport configuration")
|
|
120
|
+
data = {
|
|
121
|
+
key: str(int(value)) if isinstance(value, bool) else str(value)
|
|
122
|
+
for key, value in parameters.items()
|
|
123
|
+
if value is not None
|
|
124
|
+
}
|
|
125
|
+
data["v"] = self.config.api_version
|
|
126
|
+
payload = await self._request(
|
|
127
|
+
"POST",
|
|
128
|
+
f"https://api.vk.ru/method/{method}",
|
|
129
|
+
data=data,
|
|
130
|
+
headers={"Authorization": f"Bearer {self.config.token.get_secret_value()}"},
|
|
131
|
+
)
|
|
132
|
+
try:
|
|
133
|
+
envelope = APIEnvelope.model_validate(payload)
|
|
134
|
+
except ValidationError:
|
|
135
|
+
raise InvalidResponseError("Invalid VK API envelope") from None
|
|
136
|
+
if envelope.error is not None:
|
|
137
|
+
code = envelope.error.error_code
|
|
138
|
+
error_type = RateLimitError if code == 6 else APIError
|
|
139
|
+
raise error_type(code, method)
|
|
140
|
+
if "response" not in envelope.model_fields_set:
|
|
141
|
+
raise InvalidResponseError("VK API envelope has no response")
|
|
142
|
+
return envelope.response
|
|
143
|
+
|
|
144
|
+
async def poll(self, server: str, *, key: SecretStr, ts: str, wait: int) -> JsonValue:
|
|
145
|
+
url = httpx.URL(server)
|
|
146
|
+
if url.scheme != "https" or not url.host or url.userinfo or url.query or url.fragment:
|
|
147
|
+
raise InvalidResponseError("Unexpected Long Poll server URL")
|
|
148
|
+
# The community token is only sent to api.vk.ru, never to the polling server.
|
|
149
|
+
return await self._request(
|
|
150
|
+
"GET",
|
|
151
|
+
server,
|
|
152
|
+
params={"act": "a_check", "key": key.get_secret_value(), "ts": ts, "wait": str(wait)},
|
|
153
|
+
timeout=max(self.config.timeout, wait + 10.0),
|
|
154
|
+
)
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: python-vk-bot
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: An asynchronous framework for VK community bots.
|
|
5
|
+
Author-email: Aleksei Fomenko <fomenko_ai@proton.me>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Keywords: asyncio,bot,vk
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
12
|
+
Classifier: Typing :: Typed
|
|
13
|
+
Requires-Python: >=3.14
|
|
14
|
+
Requires-Dist: httpx<1,>=0.28
|
|
15
|
+
Requires-Dist: pydantic-settings<3,>=2.12
|
|
16
|
+
Requires-Dist: pydantic<3,>=2.12
|
|
17
|
+
Provides-Extra: callback
|
|
18
|
+
Requires-Dist: starlette<1,>=0.49; extra == 'callback'
|
|
19
|
+
Requires-Dist: uvicorn<1,>=0.38; extra == 'callback'
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
|
|
22
|
+
<p align="center">
|
|
23
|
+
<img src="logo.png" alt="python-vk-bot logo" width="720">
|
|
24
|
+
</p>
|
|
25
|
+
|
|
26
|
+
# python-vk-bot
|
|
27
|
+
|
|
28
|
+
An asynchronous framework for VK community bots, inspired by python-telegram-bot.
|
|
29
|
+
Development targets **Python 3.14** and VK API **5.199**. The regression suite runs without
|
|
30
|
+
network access; see [verification results](docs/development/verification.md).
|
|
31
|
+
|
|
32
|
+
## Installation
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
uv add python-vk-bot
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Install from this checkout
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
uv sync --locked
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
For the optional Callback HTTP receiver:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
uv sync --locked --extra callback
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Long Poll bot
|
|
51
|
+
|
|
52
|
+
Enable Bots Long Poll and the `message_new` event in the community settings. Configure
|
|
53
|
+
its event version to match the supported API schema. Supply a community token with the
|
|
54
|
+
required permissions through the environment; messaging is subject to VK's conversation
|
|
55
|
+
and permission rules.
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
import asyncio
|
|
59
|
+
|
|
60
|
+
from python_vk_bot import Application, Bot, BotSettings, Context, MessageHandler, Update, filters
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
async def echo(update: Update, context: Context) -> None:
|
|
64
|
+
message = update.message
|
|
65
|
+
if message is not None:
|
|
66
|
+
await context.bot.send_message(message.peer_id, message.text)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
async def main() -> None:
|
|
70
|
+
settings = BotSettings()
|
|
71
|
+
bot = Bot(settings.token, api_version=settings.api_version, timeout=settings.timeout)
|
|
72
|
+
app = Application(bot, settings.group_id)
|
|
73
|
+
app.add_handler(MessageHandler(echo, predicate=filters.TEXT))
|
|
74
|
+
await app.run_polling(wait=settings.polling_wait)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
asyncio.run(main())
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
With `VK_TOKEN` and `VK_GROUP_ID` already set, run the included example:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
uv run python examples/echo.py
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
`run_polling()` is awaitable. Cancellation closes owned resources. Handlers run
|
|
87
|
+
sequentially, with the first match in each numeric group. `Context` exposes `bot`,
|
|
88
|
+
`user_data`, `peer_data` and `bot_data`. Use `MessageEventHandler` for button events,
|
|
89
|
+
`filters.LOCATION` for messages containing geo, and `CommandHandler` for `/command` tokens.
|
|
90
|
+
|
|
91
|
+
## Settings
|
|
92
|
+
|
|
93
|
+
Import `BotSettings` from `python_vk_bot` or `python_vk_bot.settings`. It uses
|
|
94
|
+
`pydantic-settings` to load and validate configuration when you create an instance.
|
|
95
|
+
Importing the module does not read configuration or require credentials.
|
|
96
|
+
|
|
97
|
+
| Field | Environment variable | Default |
|
|
98
|
+
| --- | --- | --- |
|
|
99
|
+
| `token` | `VK_TOKEN` | Required, nonempty `SecretStr` |
|
|
100
|
+
| `group_id` | `VK_GROUP_ID` | Required, positive integer |
|
|
101
|
+
| `api_version` | `VK_API_VERSION` | `"5.199"` |
|
|
102
|
+
| `timeout` | `VK_TIMEOUT` | `40.0` seconds, positive and finite |
|
|
103
|
+
| `polling_wait` | `VK_POLLING_WAIT` | `25` seconds, between 1 and 90 |
|
|
104
|
+
|
|
105
|
+
Use environment variables directly:
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
from python_vk_bot import BotSettings
|
|
109
|
+
|
|
110
|
+
settings = BotSettings() # Read VK_* variables; no automatic .env loading.
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Alternatively, opt into a dotenv file and override a value for this instance:
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
from pathlib import Path
|
|
117
|
+
|
|
118
|
+
from python_vk_bot import BotSettings
|
|
119
|
+
|
|
120
|
+
settings = BotSettings(_env_file=Path(".env"), timeout=15.0)
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Explicit constructor values take precedence over environment variables, which take
|
|
124
|
+
precedence over dotenv values and field defaults. Unrelated dotenv entries are ignored,
|
|
125
|
+
so a project may share its `.env` with other components. Unknown constructor fields are
|
|
126
|
+
also ignored; use `extra="forbid"` in a subclass if you prefer strict configuration.
|
|
127
|
+
Settings only contain configuration: pass their values to `Bot`, `Application` and
|
|
128
|
+
`run_polling` explicitly as shown above. Keep real tokens and `.env` out of Git.
|
|
129
|
+
|
|
130
|
+
Extend the class with your project's fields:
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
from pathlib import Path
|
|
134
|
+
|
|
135
|
+
from pydantic import Field
|
|
136
|
+
|
|
137
|
+
from python_vk_bot import BotSettings
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
class ProjectSettings(BotSettings):
|
|
141
|
+
admin_ids: list[int] = Field(default_factory=list)
|
|
142
|
+
debug: bool = False
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
settings = ProjectSettings(_env_file=Path(".env"))
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
These fields read `VK_ADMIN_IDS` (a JSON array such as `[123, 456]`) and `VK_DEBUG`
|
|
149
|
+
(for example, `true`). Override `model_config` with `SettingsConfigDict(env_prefix="MYBOT_")`
|
|
150
|
+
to use another prefix. A field with `Field(validation_alias="DATABASE_URL")` instead reads
|
|
151
|
+
that exact variable name, without the prefix. Token values remain `SecretStr` and can be
|
|
152
|
+
passed directly to `Bot`.
|
|
153
|
+
|
|
154
|
+
## Bot without polling
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
async with Bot(token) as bot:
|
|
158
|
+
user = await bot.get_user_info(user_id)
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
An injected `httpx.AsyncClient` remains caller-owned. Methods cover sending, editing,
|
|
162
|
+
forwarding, fetching a message/user, answering message events, and obtaining Long Poll
|
|
163
|
+
connection data. `Bot.request(method, parameters)` exposes raw JSON results for additional
|
|
164
|
+
VK methods; nested request parameters must be JSON encoded explicitly.
|
|
165
|
+
|
|
166
|
+
Messages use `peer_id`; `Message` exposes both `id` and `conversation_message_id`.
|
|
167
|
+
`send_message` returns an integer message ID and accepts an optional nonzero int32
|
|
168
|
+
`random_id`. Sends are never retried automatically. `edit_message` accepts exactly one
|
|
169
|
+
message identifier and maps `conversation_message_id` to VK's `cmid` parameter.
|
|
170
|
+
|
|
171
|
+
## Callback API
|
|
172
|
+
|
|
173
|
+
The [Callback example](examples/callback.py) extends the same base settings with its
|
|
174
|
+
required secrets. For example, to construct a receiver from the environment:
|
|
175
|
+
|
|
176
|
+
```python
|
|
177
|
+
from pydantic import Field, SecretStr
|
|
178
|
+
|
|
179
|
+
from python_vk_bot import Application, Bot, BotSettings, Callback
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
class CallbackSettings(BotSettings):
|
|
183
|
+
callback_secret: SecretStr = Field(min_length=1, repr=False)
|
|
184
|
+
callback_confirmation: SecretStr = Field(min_length=1, repr=False)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
settings = CallbackSettings()
|
|
188
|
+
bot = Bot(settings.token, api_version=settings.api_version, timeout=settings.timeout)
|
|
189
|
+
application = Application(bot, settings.group_id)
|
|
190
|
+
# Register your handlers before starting the ASGI application.
|
|
191
|
+
callback = Callback(
|
|
192
|
+
application,
|
|
193
|
+
secret=settings.callback_secret,
|
|
194
|
+
confirmation_code=settings.callback_confirmation,
|
|
195
|
+
)
|
|
196
|
+
asgi_app = callback.create_app()
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Set `VK_TOKEN`, `VK_GROUP_ID`, `VK_CALLBACK_SECRET`, and `VK_CALLBACK_CONFIRMATION`, then:
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
uv run --extra callback uvicorn examples.callback:create_app --factory
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Expose `/callback` through your HTTPS deployment and configure the same secret in VK.
|
|
206
|
+
ASGI lifespan manages startup/shutdown. `Callback.handle()` is also available for other
|
|
207
|
+
HTTP frameworks, provided the caller manages Application's async lifecycle.
|
|
208
|
+
|
|
209
|
+
Confirmation returns the configured confirmation code. Ordinary events return `ok` only
|
|
210
|
+
after successful processing. A bounded in-memory cache skips successful duplicates.
|
|
211
|
+
Partial handler failures, restarts and multiple workers can repeat side effects; this
|
|
212
|
+
is not a durable queue or an exactly-once processing guarantee. See the architecture
|
|
213
|
+
for delivery limitations and resource limits.
|
|
214
|
+
|
|
215
|
+
## Agent Skills
|
|
216
|
+
|
|
217
|
+
The bundled **telegram-to-vk** Agent Skill helps AI coding agents migrate
|
|
218
|
+
`python-telegram-bot` applications to VK using `python-vk-bot`.
|
|
219
|
+
|
|
220
|
+
After installing a package version containing the skill, run from your bot project's root:
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
uvx library-skills
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Select `telegram-to-vk` and the `.agents/skills` target for a compatible AI coding agent.
|
|
227
|
+
The skill adds no runtime dependencies and does not change the framework's behavior.
|
|
228
|
+
See [Agent Skills](docs/agent-skills.md) for package installation, installing from a local
|
|
229
|
+
checkout, and usage examples.
|
|
230
|
+
|
|
231
|
+
## Documentation
|
|
232
|
+
|
|
233
|
+
- [Architecture and lifecycle](docs/architecture/project-architecture.md)
|
|
234
|
+
- [python-telegram-bot design mapping](docs/architecture/python-telegram-bot-compatibility.md)
|
|
235
|
+
- [VK API support](docs/development/api-compatibility.md)
|
|
236
|
+
- [Contributing and verification](CONTRIBUTING.md)
|
|
237
|
+
- [Changelog](CHANGELOG.md)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
python_vk_bot/__init__.py,sha256=4Qy_CyBUsznCQJ5h3I2VVLMQNWUoTlQc5mOCjYVUMIM,693
|
|
2
|
+
python_vk_bot/_config.py,sha256=Rju-sjCKAfk70ajQbpPMzOuefBIjtgF8tKjOuSYo0AE,744
|
|
3
|
+
python_vk_bot/application.py,sha256=rHjKVJRUTGwqgpp7DRJrZOV1eP-_LZ9_ei90EYsTQcs,8741
|
|
4
|
+
python_vk_bot/bot.py,sha256=rZgunz4lG-P2EFxLMuTl3Smory8soeIbe00YOVkyJ78,6386
|
|
5
|
+
python_vk_bot/callback.py,sha256=9j11jtl4rpTnfUThvOf2g6kb7wYQXAQerHbuk3IYw3o,4726
|
|
6
|
+
python_vk_bot/context.py,sha256=pzsnUSdJsVLCAsCeItf0L804HclyDtzG2laZrLlHDpk,445
|
|
7
|
+
python_vk_bot/errors.py,sha256=36sWLepB5JoZh2-n89wA2CiaEzbKES86Qc818dLkfCw,1148
|
|
8
|
+
python_vk_bot/filters.py,sha256=5fpOdxTZvFmrgokWUvYj1nowCWvPlLt9EUhRmB-0HVg,1285
|
|
9
|
+
python_vk_bot/handlers.py,sha256=iG91bzA40evZFY6Qp78nvoz_O4pjG0lE6p1IwLuCQSY,1974
|
|
10
|
+
python_vk_bot/long_poll.py,sha256=rmAxflq0E3H-tMDybQ7fBf1cKsJQFnaU4VbyQS3BSw4,3777
|
|
11
|
+
python_vk_bot/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
|
+
python_vk_bot/settings.py,sha256=JcbDihGRJGRGvBBSeB3j_LPaoX7dkPHfh-WviKQCrwk,915
|
|
13
|
+
python_vk_bot/transport.py,sha256=sfNIEtiKGvaUgU2wm4IrWjrIU1CXMwtR9TnaoBXUzfM,5833
|
|
14
|
+
python_vk_bot/.agents/skills/telegram-to-vk/SKILL.md,sha256=WMrNoxLkhhlu8wnS9rFpZi3OlwBlVNHCYFSNtKmkJzM,2828
|
|
15
|
+
python_vk_bot/.agents/skills/telegram-to-vk/references/migration-mapping.md,sha256=KFxmCQyKbUK7Z0JuS39FocyoFdtnslLLp9j2pNeJAds,9019
|
|
16
|
+
python_vk_bot/models/__init__.py,sha256=L9huCyAM2WWt5JKXyaC8dS0OfTydubdtrGUmvvTnyak,196
|
|
17
|
+
python_vk_bot/models/api.py,sha256=WrPGsBOIO1yFD376b0nlVY75rZWhJPXEJ4VzZeUW6uI,931
|
|
18
|
+
python_vk_bot/models/base.py,sha256=LYcbevtU8jq5QVLo8b1mOv70RzgEYjPtTmhMMe-JN3A,249
|
|
19
|
+
python_vk_bot/models/events.py,sha256=bS8XC0TJrj_AG0u6H9fs35kMkg47EeVoxtaUH7ya1gM,2222
|
|
20
|
+
python_vk_bot/models/messages.py,sha256=CmchlimGmKfj_i-ogHPDZbAk0vPwHKUymfMr_bsWlPc,1223
|
|
21
|
+
python_vk_bot-0.1.0.dist-info/METADATA,sha256=61pB87s_jpj9l0CI6CvvMe-r_av1dGnwonXWexwQJ9Y,8275
|
|
22
|
+
python_vk_bot-0.1.0.dist-info/WHEEL,sha256=2iZrJkDIkeDh0k9SikKMqFuRP-ua6mb4Ir1AsER4BEw,87
|
|
23
|
+
python_vk_bot-0.1.0.dist-info/licenses/LICENSE,sha256=F-35ycqPcYW243OjxMHCggr_AHQgZLX2zvtZgobVEvo,1072
|
|
24
|
+
python_vk_bot-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Aleksei Fomenko
|
|
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.
|