nicotin 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.
nicotin-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,68 @@
1
+ Metadata-Version: 2.4
2
+ Name: nicotin
3
+ Version: 0.1.0
4
+ Summary: An async Rubika library with a Pyrogram-style API surface.
5
+ Requires-Python: >=3.10
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: httpx>=0.24
8
+
9
+ # NICOTIN
10
+
11
+ یک کتابخانه‌ی async برای روبیکا (Rubika) که **عمداً** از نظر نام متدها، پراپرتی‌ها، فیلترها، ساختار Handler و حتی سبک مستندات، شبیه کتابخانه‌های معروف تلگرام (به‌خصوص Pyrogram) طراحی شده — تا هر کسی که با بات‌نویسی تلگرام آشناست، بدون یادگیری از صفر بتواند برای روبیکا هم بات بنویسد.
12
+
13
+ ```bash
14
+ pip install -e .
15
+ ```
16
+
17
+ ## شروع سریع
18
+
19
+ ```python
20
+ from nicotin import Client, filters
21
+
22
+ app = Client("my_account", auth="AUTH_KEY_INJA")
23
+
24
+ @app.on_message(filters.command("start"))
25
+ async def start(client, message):
26
+ await message.reply("سلام از نیکوتین 👋")
27
+
28
+ @app.on_message(filters.text & filters.private)
29
+ async def echo(client, message):
30
+ await message.reply(message.text)
31
+
32
+ app.run()
33
+ ```
34
+
35
+ ## ساختار پروژه (مثل Pyrogram)
36
+
37
+ ```
38
+ nicotin/
39
+ ├── client.py # کلاس Client — متدهای send_message, send_photo, edit_message_text, ...
40
+ ├── types/ # Message, User, Chat, File, CallbackQuery, Update
41
+ ├── filters.py # filters.text, filters.command(), filters.regex(), ترکیب با & | ~
42
+ ├── handlers/ # MessageHandler, CallbackQueryHandler, ...
43
+ ├── enums/ # ChatType, MessageType, ChatActivity, ParseMode
44
+ ├── errors/ # NicotinError, RPCError, FloodWait, AuthError, ...
45
+ └── network/ # Session — لایه‌ی ارتباط HTTP با API روبیکا
46
+ ```
47
+
48
+ ## نگاشت متدها (Telegram ↔ Rubika)
49
+
50
+ | در NICOTIN | مشابه در Pyrogram | چه کاری می‌کند در روبیکا |
51
+ |---|---|---|
52
+ | `Client.send_message()` | `Client.send_message()` | ارسال پیام متنی |
53
+ | `Client.send_photo()` / `send_video()` / `send_document()` | همنام | ارسال رسانه |
54
+ | `Client.edit_message_text()` | همنام | ویرایش پیام |
55
+ | `Client.delete_messages()` | همنام | حذف پیام (Local/Global) |
56
+ | `Client.get_chat_history()` | همنام | گرفتن تاریخچه‌ی پیام‌ها |
57
+ | `Client.ban_chat_member()` | همنام | حذف/بن عضو گروه |
58
+ | `message.reply()` | همنام | ریپلای به همان پیام |
59
+ | `filters.text/photo/command()` | همنام | فیلتر روی نوع/محتوای پیام |
60
+ | `@app.on_message()` | همنام | ثبت هندلر پیام |
61
+
62
+ ## نکته‌ی مهم درباره‌ی لایه‌ی شبکه
63
+
64
+ `nicotin/network/session.py` اسکلت درخواست‌های HTTPS به API روبیکا را می‌سازد، اما رمزنگاری واقعی AES بدنه‌ی درخواست/پاسخ (که روبیکا برای `data_enc` نیاز دارد) به‌صورت عمدی به‌عنوان دو متد قابل جایگزینی (`Session.encrypt` / `Session.decrypt`) گذاشته شده — چون آن بخش وابسته به کلید احراز هویت واقعی هر اکانت است. برای استفاده‌ی واقعی، این دو متد را با `pycryptodome` کامل کنید (کلید AES از هش auth شما مشتق می‌شود).
65
+
66
+ ## مجوز
67
+
68
+ MIT
@@ -0,0 +1,60 @@
1
+ # NICOTIN
2
+
3
+ یک کتابخانه‌ی async برای روبیکا (Rubika) که **عمداً** از نظر نام متدها، پراپرتی‌ها، فیلترها، ساختار Handler و حتی سبک مستندات، شبیه کتابخانه‌های معروف تلگرام (به‌خصوص Pyrogram) طراحی شده — تا هر کسی که با بات‌نویسی تلگرام آشناست، بدون یادگیری از صفر بتواند برای روبیکا هم بات بنویسد.
4
+
5
+ ```bash
6
+ pip install -e .
7
+ ```
8
+
9
+ ## شروع سریع
10
+
11
+ ```python
12
+ from nicotin import Client, filters
13
+
14
+ app = Client("my_account", auth="AUTH_KEY_INJA")
15
+
16
+ @app.on_message(filters.command("start"))
17
+ async def start(client, message):
18
+ await message.reply("سلام از نیکوتین 👋")
19
+
20
+ @app.on_message(filters.text & filters.private)
21
+ async def echo(client, message):
22
+ await message.reply(message.text)
23
+
24
+ app.run()
25
+ ```
26
+
27
+ ## ساختار پروژه (مثل Pyrogram)
28
+
29
+ ```
30
+ nicotin/
31
+ ├── client.py # کلاس Client — متدهای send_message, send_photo, edit_message_text, ...
32
+ ├── types/ # Message, User, Chat, File, CallbackQuery, Update
33
+ ├── filters.py # filters.text, filters.command(), filters.regex(), ترکیب با & | ~
34
+ ├── handlers/ # MessageHandler, CallbackQueryHandler, ...
35
+ ├── enums/ # ChatType, MessageType, ChatActivity, ParseMode
36
+ ├── errors/ # NicotinError, RPCError, FloodWait, AuthError, ...
37
+ └── network/ # Session — لایه‌ی ارتباط HTTP با API روبیکا
38
+ ```
39
+
40
+ ## نگاشت متدها (Telegram ↔ Rubika)
41
+
42
+ | در NICOTIN | مشابه در Pyrogram | چه کاری می‌کند در روبیکا |
43
+ |---|---|---|
44
+ | `Client.send_message()` | `Client.send_message()` | ارسال پیام متنی |
45
+ | `Client.send_photo()` / `send_video()` / `send_document()` | همنام | ارسال رسانه |
46
+ | `Client.edit_message_text()` | همنام | ویرایش پیام |
47
+ | `Client.delete_messages()` | همنام | حذف پیام (Local/Global) |
48
+ | `Client.get_chat_history()` | همنام | گرفتن تاریخچه‌ی پیام‌ها |
49
+ | `Client.ban_chat_member()` | همنام | حذف/بن عضو گروه |
50
+ | `message.reply()` | همنام | ریپلای به همان پیام |
51
+ | `filters.text/photo/command()` | همنام | فیلتر روی نوع/محتوای پیام |
52
+ | `@app.on_message()` | همنام | ثبت هندلر پیام |
53
+
54
+ ## نکته‌ی مهم درباره‌ی لایه‌ی شبکه
55
+
56
+ `nicotin/network/session.py` اسکلت درخواست‌های HTTPS به API روبیکا را می‌سازد، اما رمزنگاری واقعی AES بدنه‌ی درخواست/پاسخ (که روبیکا برای `data_enc` نیاز دارد) به‌صورت عمدی به‌عنوان دو متد قابل جایگزینی (`Session.encrypt` / `Session.decrypt`) گذاشته شده — چون آن بخش وابسته به کلید احراز هویت واقعی هر اکانت است. برای استفاده‌ی واقعی، این دو متد را با `pycryptodome` کامل کنید (کلید AES از هش auth شما مشتق می‌شود).
57
+
58
+ ## مجوز
59
+
60
+ MIT
@@ -0,0 +1,47 @@
1
+ """
2
+ NICOTIN
3
+ ~~~~~~~
4
+
5
+ An async Python library for the Rubika messenger, with an API surface
6
+ (method names, property names, filters, handler system, docs style)
7
+ deliberately modeled after the Telegram bot ecosystem (Pyrogram /
8
+ python-telegram-bot), so anyone coming from Telegram bot development
9
+ feels at home immediately.
10
+
11
+ from nicotin import Client, filters
12
+
13
+ app = Client("my_account", auth="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
14
+
15
+ @app.on_message(filters.text & filters.private)
16
+ async def echo(client, message):
17
+ await message.reply(message.text)
18
+
19
+ app.run()
20
+
21
+ :copyright: NICOTIN contributors
22
+ :license: MIT
23
+ """
24
+
25
+ from .client import Client
26
+ from .types import Update, Message, User, Chat, File, CallbackQuery
27
+ from . import filters
28
+ from . import enums
29
+ from .errors import NicotinError, RPCError, AuthError, FloodWait
30
+
31
+ __all__ = [
32
+ "Client",
33
+ "Update",
34
+ "Message",
35
+ "User",
36
+ "Chat",
37
+ "File",
38
+ "CallbackQuery",
39
+ "filters",
40
+ "enums",
41
+ "NicotinError",
42
+ "RPCError",
43
+ "AuthError",
44
+ "FloodWait",
45
+ ]
46
+
47
+ __version__ = "0.1.0"
@@ -0,0 +1,375 @@
1
+ """
2
+ nicotin.client
3
+ ~~~~~~~~~~~~~~~
4
+
5
+ The :class:`Client` class: NICOTIN's single entry point, deliberately
6
+ shaped like ``pyrogram.Client`` — same method names, same decorator
7
+ style, same ``run()`` convenience, same "everything is a coroutine"
8
+ philosophy — but talking to Rubika instead of Telegram.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import asyncio
14
+ import inspect
15
+ from pathlib import Path
16
+ from typing import Callable
17
+
18
+ from .network import Session
19
+ from .types import Message, Chat, User, File, CallbackQuery, Update
20
+ from .enums import ChatType, ChatActivity, ParseMode
21
+ from .filters import Filter, all as filters_all
22
+ from .handlers import (
23
+ Handler,
24
+ MessageHandler,
25
+ EditedMessageHandler,
26
+ DeletedMessagesHandler,
27
+ CallbackQueryHandler,
28
+ )
29
+ from .errors import AuthError
30
+
31
+
32
+ class Client:
33
+ """
34
+ NICOTIN's main class — one instance per Rubika account/bot.
35
+
36
+ :param name: a local session name, used for the ``{name}.session`` file.
37
+ :param auth: the account's auth key. If omitted, NICOTIN will look
38
+ for a saved ``{name}.session`` file on disk.
39
+ :param platform: reported client platform (``"web"``, ``"android"``, ``"rubx"``).
40
+ :param workers: number of concurrent update-dispatch workers, exactly
41
+ like Pyrogram's own ``workers`` parameter.
42
+
43
+ Example::
44
+
45
+ from nicotin import Client, filters
46
+
47
+ app = Client("my_account", auth="...")
48
+
49
+ @app.on_message(filters.command("start"))
50
+ async def start(client, message):
51
+ await message.reply("سلام از نیکوتین 👋")
52
+
53
+ app.run()
54
+ """
55
+
56
+ def __init__(
57
+ self,
58
+ name: str,
59
+ auth: str | None = None,
60
+ *,
61
+ platform: str = "web",
62
+ workers: int = 4,
63
+ session_dir: str = ".",
64
+ ):
65
+ self.name = name
66
+ self.workers = workers
67
+ self.session_dir = Path(session_dir)
68
+ self._auth = auth or self._load_session()
69
+ if not self._auth:
70
+ raise AuthError(
71
+ f"No auth key provided and no session file found for '{name}'. "
72
+ f"Pass auth=... on first run."
73
+ )
74
+
75
+ self.session = Session(self._auth, platform=platform)
76
+ self._handlers: list[Handler] = []
77
+ self._polling_task: asyncio.Task | None = None
78
+ self.me: User | None = None
79
+
80
+ # ------------------------------------------------------------------
81
+ # Session persistence
82
+ # ------------------------------------------------------------------
83
+
84
+ def _session_path(self) -> Path:
85
+ return self.session_dir / f"{self.name}.session"
86
+
87
+ def _load_session(self) -> str | None:
88
+ path = self._session_path()
89
+ return path.read_text().strip() if path.exists() else None
90
+
91
+ def _save_session(self):
92
+ self._session_path().write_text(self._auth)
93
+
94
+ # ------------------------------------------------------------------
95
+ # Lifecycle — mirrors pyrogram.Client.start / stop / run / idle
96
+ # ------------------------------------------------------------------
97
+
98
+ async def start(self) -> "Client":
99
+ """Connect and begin listening for updates. Returns ``self`` for chaining."""
100
+ self._save_session()
101
+ self.me = await self.get_me()
102
+ self._polling_task = asyncio.create_task(self._update_loop())
103
+ return self
104
+
105
+ async def stop(self):
106
+ """Disconnect and stop dispatching updates."""
107
+ if self._polling_task:
108
+ self._polling_task.cancel()
109
+ await self.session.close()
110
+
111
+ def run(self, coroutine=None):
112
+ """
113
+ Blocking helper exactly like ``pyrogram.Client.run()``: starts the
114
+ client, optionally awaits ``coroutine``, then idles until Ctrl+C.
115
+ """
116
+ async def _runner():
117
+ await self.start()
118
+ try:
119
+ if coroutine:
120
+ await coroutine
121
+ else:
122
+ await self._idle()
123
+ finally:
124
+ await self.stop()
125
+
126
+ asyncio.run(_runner())
127
+
128
+ async def _idle(self):
129
+ try:
130
+ while True:
131
+ await asyncio.sleep(3600)
132
+ except asyncio.CancelledError:
133
+ pass
134
+
135
+ async def _update_loop(self):
136
+ """Long-polls for new updates and dispatches them to handlers."""
137
+ while True:
138
+ try:
139
+ raw_updates = await self.session.invoke("getUpdates", {"limit": 100})
140
+ for raw in raw_updates.get("updates", []):
141
+ update = self._parse_update(raw)
142
+ await self._dispatch(update)
143
+ except asyncio.CancelledError:
144
+ raise
145
+ except Exception:
146
+ # A production client would log this; kept silent here
147
+ # to match a minimal skeleton.
148
+ pass
149
+ await asyncio.sleep(1)
150
+
151
+ def _parse_update(self, raw: dict) -> Update:
152
+ update_type = raw.get("type")
153
+ chat_id = raw.get("object_guid", "")
154
+
155
+ if update_type == "NewMessage":
156
+ return Update(message=Message._parse(self, raw.get("message", {}), chat_id))
157
+ if update_type == "EditedMessage":
158
+ return Update(edited_message=Message._parse(self, raw.get("message", {}), chat_id))
159
+ if update_type == "RemovedMessage":
160
+ return Update(deleted_message_ids=raw.get("message_ids", []))
161
+ if update_type == "CallbackQuery":
162
+ msg = Message._parse(self, raw.get("message", {}), chat_id)
163
+ cq = CallbackQuery(
164
+ client=self,
165
+ id=raw.get("callback_id", ""),
166
+ from_user=User(client=self, id=raw.get("user_guid", ""), first_name=""),
167
+ message=msg,
168
+ data=raw.get("data", ""),
169
+ )
170
+ return Update(callback_query=cq)
171
+ return Update()
172
+
173
+ async def _dispatch(self, update: Update):
174
+ target = (
175
+ update.message
176
+ or update.edited_message
177
+ or update.callback_query
178
+ or update.deleted_message_ids
179
+ )
180
+ for handler in self._handlers:
181
+ if update.message and isinstance(handler, MessageHandler):
182
+ event = update.message
183
+ elif update.edited_message and isinstance(handler, EditedMessageHandler):
184
+ event = update.edited_message
185
+ elif update.callback_query and isinstance(handler, CallbackQueryHandler):
186
+ event = update.callback_query
187
+ elif update.deleted_message_ids and isinstance(handler, DeletedMessagesHandler):
188
+ event = update.deleted_message_ids
189
+ else:
190
+ continue
191
+
192
+ if isinstance(event, Message) or isinstance(event, CallbackQuery):
193
+ matched = await handler.check(self, getattr(event, "message", event) if isinstance(event, CallbackQuery) else event)
194
+ else:
195
+ matched = True
196
+
197
+ if matched:
198
+ await handler.callback(self, event)
199
+
200
+ # ------------------------------------------------------------------
201
+ # Handler registration — decorators mirror pyrogram.Client exactly
202
+ # ------------------------------------------------------------------
203
+
204
+ def add_handler(self, handler: Handler):
205
+ self._handlers.append(handler)
206
+ return handler
207
+
208
+ def remove_handler(self, handler: Handler):
209
+ self._handlers.remove(handler)
210
+
211
+ def on_message(self, filters: Filter = filters_all):
212
+ def decorator(func: Callable):
213
+ self.add_handler(MessageHandler(func, filters))
214
+ return func
215
+ return decorator
216
+
217
+ def on_edited_message(self, filters: Filter = filters_all):
218
+ def decorator(func: Callable):
219
+ self.add_handler(EditedMessageHandler(func, filters))
220
+ return func
221
+ return decorator
222
+
223
+ def on_deleted_messages(self):
224
+ def decorator(func: Callable):
225
+ self.add_handler(DeletedMessagesHandler(func))
226
+ return func
227
+ return decorator
228
+
229
+ def on_callback_query(self, filters: Filter = filters_all):
230
+ def decorator(func: Callable):
231
+ self.add_handler(CallbackQueryHandler(func, filters))
232
+ return func
233
+ return decorator
234
+
235
+ # ------------------------------------------------------------------
236
+ # API methods — names/signatures mirror pyrogram.Client's own
237
+ # ------------------------------------------------------------------
238
+
239
+ async def get_me(self) -> User:
240
+ """Fetch info about the currently logged-in account."""
241
+ data = await self.session.invoke("getUserInfo", {})
242
+ return User._parse(self, data.get("user", {}))
243
+
244
+ async def send_message(
245
+ self,
246
+ chat_id: str,
247
+ text: str,
248
+ *,
249
+ reply_to_message_id: str | None = None,
250
+ parse_mode: ParseMode = ParseMode.NONE,
251
+ ) -> Message:
252
+ """Send a text message to ``chat_id``."""
253
+ data = {"object_guid": chat_id, "text": text, "rnd": _rnd()}
254
+ if reply_to_message_id:
255
+ data["reply_to_message_id"] = reply_to_message_id
256
+ result = await self.session.invoke("sendMessage", data)
257
+ return Message._parse(self, result, chat_id)
258
+
259
+ async def send_photo(self, chat_id: str, photo: str, *, caption: str | None = None, **kwargs) -> Message:
260
+ """Send a photo (local path or previously uploaded ``file_id``) to ``chat_id``."""
261
+ return await self._send_media(chat_id, photo, "Image", caption=caption, **kwargs)
262
+
263
+ async def send_video(self, chat_id: str, video: str, *, caption: str | None = None, **kwargs) -> Message:
264
+ return await self._send_media(chat_id, video, "Video", caption=caption, **kwargs)
265
+
266
+ async def send_voice(self, chat_id: str, voice: str, **kwargs) -> Message:
267
+ return await self._send_media(chat_id, voice, "Voice", **kwargs)
268
+
269
+ async def send_document(self, chat_id: str, document: str, *, caption: str | None = None, **kwargs) -> Message:
270
+ return await self._send_media(chat_id, document, "File", caption=caption, **kwargs)
271
+
272
+ async def _send_media(
273
+ self,
274
+ chat_id: str,
275
+ media: str,
276
+ media_type: str,
277
+ *,
278
+ caption: str | None = None,
279
+ reply_to_message_id: str | None = None,
280
+ ) -> Message:
281
+ file_id = await self._upload(media)
282
+ data = {
283
+ "object_guid": chat_id,
284
+ "rnd": _rnd(),
285
+ "file_inline": {"file_id": file_id, "type": media_type},
286
+ }
287
+ if caption:
288
+ data["text"] = caption
289
+ if reply_to_message_id:
290
+ data["reply_to_message_id"] = reply_to_message_id
291
+ result = await self.session.invoke("sendMessage", data)
292
+ return Message._parse(self, result, chat_id)
293
+
294
+ async def _upload(self, path: str) -> str:
295
+ """Upload a local file to Rubika's storage and return its ``file_id``."""
296
+ result = await self.session.invoke("requestSendFile", {"file_name": Path(path).name})
297
+ return result.get("file_id", "")
298
+
299
+ async def download_media(self, file: File, file_path: str | None = None) -> str:
300
+ """Download ``file`` to ``file_path`` (or the file's own name) and return the local path."""
301
+ dest = file_path or (file.name or file.id)
302
+ result = await self.session.invoke(
303
+ "requestFile", {"file_id": file.id, "access_hash_rec": file.access_hash_rec}
304
+ )
305
+ Path(dest).write_bytes(result.get("bytes", b""))
306
+ return dest
307
+
308
+ async def edit_message_text(self, chat_id: str, message_id: str, text: str) -> Message:
309
+ result = await self.session.invoke(
310
+ "editMessage", {"object_guid": chat_id, "message_id": message_id, "text": text}
311
+ )
312
+ return Message._parse(self, result, chat_id)
313
+
314
+ async def delete_messages(self, chat_id: str, message_ids: list[str], *, revoke: bool = True):
315
+ return await self.session.invoke(
316
+ "deleteMessages",
317
+ {"object_guid": chat_id, "message_ids": message_ids, "type": "Global" if revoke else "Local"},
318
+ )
319
+
320
+ async def forward_messages(self, to_chat_id: str, from_chat_id: str, message_ids: list[str]):
321
+ return await self.session.invoke(
322
+ "forwardMessages",
323
+ {
324
+ "from_object_guid": from_chat_id,
325
+ "to_object_guid": to_chat_id,
326
+ "message_ids": message_ids,
327
+ "rnd": _rnd(),
328
+ },
329
+ )
330
+
331
+ async def get_chat(self, chat_id: str) -> Chat:
332
+ """Fetch full info about a chat, group or channel."""
333
+ data = await self.session.invoke("getObjectInfo", {"object_guid": chat_id})
334
+ return Chat._parse(self, data)
335
+
336
+ async def get_chat_history(self, chat_id: str, *, limit: int = 50) -> list[Message]:
337
+ data = await self.session.invoke(
338
+ "getMessages", {"object_guid": chat_id, "limit": limit}
339
+ )
340
+ return [Message._parse(self, m, chat_id) for m in data.get("messages", [])]
341
+
342
+ async def ban_chat_member(self, chat_id: str, user_id: str):
343
+ return await self.session.invoke(
344
+ "banGroupMember", {"group_guid": chat_id, "member_guid": user_id, "action": "Set"}
345
+ )
346
+
347
+ async def unban_chat_member(self, chat_id: str, user_id: str):
348
+ return await self.session.invoke(
349
+ "banGroupMember", {"group_guid": chat_id, "member_guid": user_id, "action": "Unset"}
350
+ )
351
+
352
+ async def leave_chat(self, chat_id: str):
353
+ return await self.session.invoke("leaveGroup", {"group_guid": chat_id})
354
+
355
+ async def pin_chat_message(self, chat_id: str, message_id: str):
356
+ return await self.session.invoke(
357
+ "setPinMessage", {"object_guid": chat_id, "message_id": message_id, "action": "Pin"}
358
+ )
359
+
360
+ async def send_chat_action(self, chat_id: str, action: ChatActivity = ChatActivity.TYPING):
361
+ """Show a typing/recording/uploading indicator, like Pyrogram's ``send_chat_action``."""
362
+ return await self.session.invoke(
363
+ "sendChatActivity", {"object_guid": chat_id, "activity": action.value}
364
+ )
365
+
366
+ async def answer_callback_query(self, callback_query_id: str, *, text: str | None = None, show_alert: bool = False):
367
+ return await self.session.invoke(
368
+ "answerCallback",
369
+ {"callback_id": callback_query_id, "text": text, "show_alert": show_alert},
370
+ )
371
+
372
+
373
+ def _rnd() -> int:
374
+ import random
375
+ return random.randint(100000, 999999999)
@@ -0,0 +1,76 @@
1
+ """
2
+ nicotin.enums
3
+ ~~~~~~~~~~~~~
4
+
5
+ Enumerations mirroring the shape of Pyrogram's ``enums`` module, adapted
6
+ to Rubika's own vocabulary (object_guid types, chat kinds, message
7
+ kinds, etc).
8
+ """
9
+
10
+ from enum import Enum, auto
11
+
12
+
13
+ class ChatType(Enum):
14
+ """The kind of chat / object_guid a message belongs to."""
15
+
16
+ PRIVATE = auto() #: A direct one-to-one chat (``u_...`` guid)
17
+ GROUP = auto() #: A group chat (``g_...`` guid)
18
+ CHANNEL = auto() #: A channel (``c_...`` guid)
19
+ BOT = auto() #: A chat with a bot (``b_...`` guid)
20
+
21
+
22
+ class MessageType(Enum):
23
+ """The content type carried by a :class:`~nicotin.types.Message`."""
24
+
25
+ TEXT = auto()
26
+ PHOTO = auto()
27
+ VIDEO = auto()
28
+ GIF = auto()
29
+ VOICE = auto()
30
+ MUSIC = auto()
31
+ FILE = auto()
32
+ STICKER = auto()
33
+ LOCATION = auto()
34
+ CONTACT = auto()
35
+ POLL = auto()
36
+ LIVE_LOCATION = auto()
37
+ FORWARDED = auto()
38
+ DELETED = auto()
39
+ UNKNOWN = auto()
40
+
41
+
42
+ class ChatActivity(Enum):
43
+ """Typing / recording indicator, used with :meth:`Client.send_chat_activity`."""
44
+
45
+ TYPING = "Typing"
46
+ RECORDING = "Recording"
47
+ UPLOADING = "Uploading"
48
+ NONE = "None"
49
+
50
+
51
+ class ParseMode(Enum):
52
+ """How :meth:`Client.send_message` should parse ``text`` for entities."""
53
+
54
+ NONE = auto()
55
+ MARKDOWN = auto()
56
+ HTML = auto()
57
+
58
+
59
+ class MessageMedia(Enum):
60
+ """Restricts :func:`nicotin.filters` media matching to a specific kind."""
61
+
62
+ PHOTO = "Image"
63
+ VIDEO = "Video"
64
+ GIF = "Gif"
65
+ VOICE = "Voice"
66
+ MUSIC = "Music"
67
+ FILE = "File"
68
+
69
+
70
+ __all__ = [
71
+ "ChatType",
72
+ "MessageType",
73
+ "ChatActivity",
74
+ "ParseMode",
75
+ "MessageMedia",
76
+ ]