neko-speaker 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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Fukuneko
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,105 @@
1
+ Metadata-Version: 2.4
2
+ Name: neko-speaker
3
+ Version: 0.1.0
4
+ Summary: Асинхронный клиент VK API с удобной обёрткой для ботов сообществ для быстрого развертывания ботов
5
+ Author-email: Fukuneko <lcotyt112@gmail.com>
6
+ License: MIT
7
+ Project-URL: Repository, https://github.com/LuckyCot112/maneki2
8
+ Keywords: vk,vk-api,bot,async,neko
9
+ Requires-Python: >=3.9
10
+ Description-Content-Type: text/markdown
11
+ License-File: LICENSE
12
+ Requires-Dist: aiohttp>=3.8
13
+ Requires-Dist: typing-extensions
14
+ Provides-Extra: dev
15
+ Requires-Dist: pytest; extra == "dev"
16
+ Requires-Dist: pytest-asyncio; extra == "dev"
17
+ Requires-Dist: aresponses; extra == "dev"
18
+ Requires-Dist: black; extra == "dev"
19
+ Requires-Dist: flake8; extra == "dev"
20
+ Requires-Dist: mypy; extra == "dev"
21
+ Requires-Dist: python-dotenv; extra == "dev"
22
+ Dynamic: license-file
23
+
24
+
25
+ # neko-speaker
26
+
27
+ Асинхронный клиент для работы с API социальных платформ. Библиотека предоставляет удобный интерфейс для взаимодействия с API VK и упрощает разработку ботов и интеграций. В будущем планируется поддержка дополнительных платформ.
28
+
29
+ ## Установка
30
+
31
+ ```bash
32
+ pip install neko-speaker
33
+ ```
34
+
35
+ Для разработки с тестовыми и вспомогательными инструментами:
36
+
37
+ ```bash
38
+ pip install "neko-speaker[dev]"
39
+ ```
40
+ Быстрый старт
41
+ Вставьте сюда ваш пример кода для быстрого начала работы:
42
+
43
+ ```python
44
+ # Пример использования
45
+ import os
46
+ import asyncio
47
+ import aiohttp
48
+ import logging
49
+
50
+ from src.neko_speaker.auth import VkBotLongPoll
51
+
52
+ # Create your own event handling file
53
+ from example_handler import EventHandler
54
+
55
+ from src.neko_speaker.exceptions import *
56
+ from dotenv import load_dotenv
57
+ load_dotenv()
58
+
59
+ # use only if you need debugging
60
+ logging.basicConfig(
61
+ level=logging.DEBUG,
62
+ format="%(asctime)s [%(levelname)s] %(name)s: %(message)s"
63
+ )
64
+
65
+ # create .env file with this constantas
66
+ TOKEN = os.getenv("VkLongpollBotAPIToken")
67
+ GROUP_ID = os.getenv("GroupID")
68
+
69
+ async def main():
70
+ async with VkBotLongPoll(token=TOKEN, group_id=GROUP_ID) as bot:
71
+ await bot.update_longpoll_server()
72
+ print("LongPoll инициализирован")
73
+
74
+ while bot._is_work:
75
+ events = await bot.listen()
76
+ for event in events:
77
+ try:
78
+ await EventHandler.event_handler(bot, event)
79
+ except Exception as e:
80
+ print(f"Ошибка при обработке события: {e}")
81
+
82
+
83
+ if __name__ == "__main__":
84
+ asyncio.run(main())
85
+ ```
86
+
87
+
88
+ Основные возможности
89
+ Асинхронная работа на базе aiohttp.
90
+ Структурированный доступ к методам API по разделам (сообщения, стена, фото и др.).
91
+ Обработка ошибок и типовых сценариев ответов API.
92
+ Типизированные интерфейсы для предсказуемой работы с данными.
93
+ Тестирование
94
+ Для запуска тестов используйте:
95
+
96
+ ```bash
97
+ pytest
98
+ ```
99
+
100
+
101
+ Инструменты для проверки стиля и типов (black, flake8, mypy) включены в группу зависимостей dev.
102
+
103
+ Вклад в проект
104
+ Предложения по улучшению, отчёты об ошибках и запросы на добавление функционала принимаются через систему Issues в репозитории. Для удобства используйте предоставленные шаблоны.
105
+ https://github.com/LuckyCot112/maneki2/issues
@@ -0,0 +1,82 @@
1
+
2
+ # neko-speaker
3
+
4
+ Асинхронный клиент для работы с API социальных платформ. Библиотека предоставляет удобный интерфейс для взаимодействия с API VK и упрощает разработку ботов и интеграций. В будущем планируется поддержка дополнительных платформ.
5
+
6
+ ## Установка
7
+
8
+ ```bash
9
+ pip install neko-speaker
10
+ ```
11
+
12
+ Для разработки с тестовыми и вспомогательными инструментами:
13
+
14
+ ```bash
15
+ pip install "neko-speaker[dev]"
16
+ ```
17
+ Быстрый старт
18
+ Вставьте сюда ваш пример кода для быстрого начала работы:
19
+
20
+ ```python
21
+ # Пример использования
22
+ import os
23
+ import asyncio
24
+ import aiohttp
25
+ import logging
26
+
27
+ from src.neko_speaker.auth import VkBotLongPoll
28
+
29
+ # Create your own event handling file
30
+ from example_handler import EventHandler
31
+
32
+ from src.neko_speaker.exceptions import *
33
+ from dotenv import load_dotenv
34
+ load_dotenv()
35
+
36
+ # use only if you need debugging
37
+ logging.basicConfig(
38
+ level=logging.DEBUG,
39
+ format="%(asctime)s [%(levelname)s] %(name)s: %(message)s"
40
+ )
41
+
42
+ # create .env file with this constantas
43
+ TOKEN = os.getenv("VkLongpollBotAPIToken")
44
+ GROUP_ID = os.getenv("GroupID")
45
+
46
+ async def main():
47
+ async with VkBotLongPoll(token=TOKEN, group_id=GROUP_ID) as bot:
48
+ await bot.update_longpoll_server()
49
+ print("LongPoll инициализирован")
50
+
51
+ while bot._is_work:
52
+ events = await bot.listen()
53
+ for event in events:
54
+ try:
55
+ await EventHandler.event_handler(bot, event)
56
+ except Exception as e:
57
+ print(f"Ошибка при обработке события: {e}")
58
+
59
+
60
+ if __name__ == "__main__":
61
+ asyncio.run(main())
62
+ ```
63
+
64
+
65
+ Основные возможности
66
+ Асинхронная работа на базе aiohttp.
67
+ Структурированный доступ к методам API по разделам (сообщения, стена, фото и др.).
68
+ Обработка ошибок и типовых сценариев ответов API.
69
+ Типизированные интерфейсы для предсказуемой работы с данными.
70
+ Тестирование
71
+ Для запуска тестов используйте:
72
+
73
+ ```bash
74
+ pytest
75
+ ```
76
+
77
+
78
+ Инструменты для проверки стиля и типов (black, flake8, mypy) включены в группу зависимостей dev.
79
+
80
+ Вклад в проект
81
+ Предложения по улучшению, отчёты об ошибках и запросы на добавление функционала принимаются через систему Issues в репозитории. Для удобства используйте предоставленные шаблоны.
82
+ https://github.com/LuckyCot112/maneki2/issues
@@ -0,0 +1,37 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "neko-speaker"
7
+ version = "0.1.0"
8
+ description = "Асинхронный клиент VK API с удобной обёрткой для ботов сообществ для быстрого развертывания ботов"
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ license = { text = "MIT" }
12
+ authors = [
13
+ { name = "Fukuneko", email = "lcotyt112@gmail.com" }
14
+ ]
15
+ keywords = ["vk", "vk-api", "bot", "async", "neko"]
16
+ dependencies = [
17
+ "aiohttp>=3.8",
18
+ "typing-extensions"
19
+ ]
20
+
21
+ [project.optional-dependencies]
22
+ dev = [
23
+ "pytest",
24
+ "pytest-asyncio",
25
+ "aresponses",
26
+ "black",
27
+ "flake8",
28
+ "mypy",
29
+ "python-dotenv" # ← только для разработки
30
+ ]
31
+
32
+ [project.urls]
33
+ Repository = "https://github.com/LuckyCot112/maneki2"
34
+
35
+ [tool.setuptools.packages.find]
36
+ where = ["src"]
37
+ include = ["neko_speaker*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
File without changes
@@ -0,0 +1,188 @@
1
+ import os
2
+ import asyncio
3
+ import aiohttp
4
+ import src.neko_speaker.bot_types as Type
5
+ from typing import Optional, Any, Dict, Union
6
+ # from dotenv import load_dotenv
7
+ # load_dotenv()
8
+
9
+ from src.neko_speaker.utils import Message, Attachment, Group, User
10
+ import logging
11
+
12
+ logger = logging.getLogger("nekobot.longpoll")
13
+
14
+ # API = os.getenv("VkApiAddress")
15
+
16
+
17
+ class VkBotLongPoll(Type.VkBotProtocol):
18
+ def __init__(self, token, group_id, session: aiohttp.ClientSession | None = None, wait_time=25, preserve_original_params = True):
19
+ self._token = token
20
+ self._group_id = group_id
21
+ self._wait_time = wait_time
22
+
23
+ self._url = os.getenv("VkApiAddress")
24
+ self._key: Optional[str] = None
25
+ self._server: Optional[str] = None
26
+ self._ts: Optional[int] = None
27
+ self._is_work = True
28
+
29
+ # для метода message.edit. Если True, при редактировании будет сначала получать сообщение, сохраняя неуказанные при редактировании параметры.
30
+ # Если передать в редактирование только текст, клавиатура, вложения и прочее сохранятся при True и удалятся при False. Значение можно передавать в метод, данный параметр отвечает за стандартное значение
31
+ self._preserve_original_params = preserve_original_params
32
+ self._session = session
33
+
34
+ self.message = Message(self)
35
+ self.attachment = Attachment(self)
36
+ self.group = Group(self)
37
+ self.user = User(self)
38
+
39
+ if session is None:
40
+ self._session = None
41
+
42
+ # asyncio.create_task(self.update_longpoll_server())
43
+
44
+
45
+ async def __aenter__(self):
46
+ if self._session is None:
47
+ self._session = aiohttp.ClientSession()
48
+ return self
49
+
50
+
51
+ async def __aexit__(self, exc_type, exc, tb):
52
+ if self._session:
53
+ await self._session.close()
54
+
55
+
56
+ @property
57
+ def session(self) -> aiohttp.ClientSession:
58
+ if self._session is None:
59
+ raise RuntimeError(
60
+ f"Сессия не инициализирована в {self.__class__.__name__}. "
61
+ "Используйте async with VKBot(...) as bot:"
62
+ )
63
+ return self._session
64
+
65
+
66
+ # __slots__ = ('vk', 'wait', 'group_id', 'url', 'session', 'key', 'server', 'ts')
67
+
68
+
69
+ async def update_longpoll_server(self, update_ts=True) -> Dict[str, Any]:
70
+ """
71
+ Обновляет данные Long Poll сервера.
72
+
73
+ Args:
74
+ update_ts: Если True, обновляет self._ts из ответа VK.
75
+
76
+ Returns:
77
+ dict: Полный объект response от VK API.
78
+
79
+ Raises:
80
+ ValueError: Если VK вернул ошибку.
81
+ aiohttp.ClientError: При сетевых ошибках.
82
+ RuntimeError: При проблемах с JSON.
83
+ """
84
+
85
+ logger.debug("Starting update_longpoll_server (update_ts=%s)", update_ts)
86
+ url = f"{self._url}groups.getLongPollServer"
87
+ params = {
88
+ "access_token": self._token,
89
+ "group_id": self._group_id,
90
+ "v": "5.199",
91
+ }
92
+
93
+ async with self.session.get(url, params=params) as response:
94
+ response.raise_for_status()
95
+ data = await response.json()
96
+
97
+ if 'error' in data:
98
+ err = data["error"]
99
+ logger.error("VK API error in groups.getLongPollServer: %s (%s)",
100
+ err.get('error_code'), err.get('error_msg'))
101
+ raise ValueError(f"VK API error: {err.get('error_code')} - {err.get('error_msg')}")
102
+
103
+ response = data['response']
104
+ self._key = response['key']
105
+ self._server = response['server']
106
+
107
+ if update_ts:
108
+ self._ts = response['ts']
109
+ logger.info("Long Poll server updated: key=%s, server=%s, ts=%s",
110
+ self._key, self._server, self._ts)
111
+ else:
112
+ logger.info("Long Poll credentials updated (ts preserved): key=%s, server=%s",
113
+ self._key, self._server)
114
+
115
+ return response
116
+
117
+
118
+ async def listen(self) -> Union[Type.VkEvent, Type.VkError]:
119
+ """
120
+ Запускает прослушивание событий сервера.
121
+ Автоматически обновляет сервер при истечении времени ожидания и ошибках получения данных.
122
+
123
+ Returns:
124
+ dict: Полный объект события от VK API.
125
+
126
+ Raises:
127
+ ValueError: Если VK вернул ошибку.
128
+ aiohttp.ClientError: При сетевых ошибках.
129
+ RuntimeError: При проблемах с JSON.
130
+ """
131
+
132
+ logger.debug("Starting Long Poll request with ts=%s", self._ts)
133
+ params={
134
+ 'act': 'a_check',
135
+ 'key': self._key,
136
+ 'ts': self._ts,
137
+ 'wait': self._wait_time
138
+ }
139
+
140
+ try:
141
+ async with self.session.get(self._server, params=params, timeout=self._wait_time + 10) as response:
142
+ response.raise_for_status()
143
+ data: Union[Type.VkEvent, Type.VkError] = await response.json()
144
+ except aiohttp.ClientError as e:
145
+ logger.warning("Network error during Long Poll request: %s", e)
146
+ raise
147
+ except ValueError as e:
148
+ logger.error("JSON parsing error in Long Poll response: %s", e)
149
+ raise RuntimeError("Invalid JSON response from VK Long Poll server") from e
150
+
151
+ if 'error' in data:
152
+ err: Type.VkError = data['error']
153
+ logger.error("VK API error in Long Poll: %s (%s)", err.get('error_code'), err.get('error_msg'))
154
+ raise ValueError(f"VK API error: {err.get('error_code')} - {err.get('error_msg')}")
155
+
156
+ # self._ts = data["ts"]
157
+
158
+ failed = data.get('failed')
159
+
160
+ if failed is None:
161
+ self._ts = data["ts"]
162
+ logger.debug("Long Poll succeeded, new ts=%s, updates count=%s", self._ts, len(data.get('updates', [])))
163
+ return data.get('updates', [])
164
+
165
+ logger.info("Long Poll failed code=%s, handling recovery", failed)
166
+
167
+ if failed == 1:
168
+ self._ts = data.get('ts', self._ts)
169
+ logger.info("Key expired (failed=1), ts updated to %s", self._ts)
170
+ elif failed == 2:
171
+ await self.update_longpoll_server(update_ts=False)
172
+ logger.info("TS reset required (failed=2), server credentials updated")
173
+ elif failed == 3:
174
+ await self.update_longpoll_server()
175
+ logger.info("Server changed (failed=3), full Long Poll config updated")
176
+ else:
177
+ logger.warning("Unknown failed code=%s, skipping recovery logic", failed)
178
+
179
+ logger.debug("Raw failed response: %s", data)
180
+ return []
181
+
182
+
183
+ async def main():
184
+ print("auth.py not standalone")
185
+
186
+
187
+ if __name__ == "__main__":
188
+ asyncio.run(main())