telekit 2.2.0a1__tar.gz → 2.2.0a2__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.
- {telekit-2.2.0a1/telekit.egg-info → telekit-2.2.0a2}/PKG-INFO +24 -10
- telekit-2.2.0a2/telekit/_user.py +199 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/_version.py +1 -1
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/example/example_handlers/entry.py +1 -3
- {telekit-2.2.0a1 → telekit-2.2.0a2/telekit.egg-info}/PKG-INFO +24 -10
- telekit-2.2.0a1/telekit/_user.py +0 -116
- {telekit-2.2.0a1 → telekit-2.2.0a2}/LICENSE +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/README.md +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/setup.cfg +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/setup.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/__init__.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/_buildtext/__init__.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/_buildtext/formatter.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/_buildtext/styles.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/_callback_query_handler.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/_chain.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/_chain_base.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/_chain_entry_logic.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/_chain_inline_keyboards_logic.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/_chapters/__init__.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/_chapters/chapters.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/_handler.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/_init.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/_inline_buttons.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/_input_handler.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/_logger.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/_on.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/_snapvault/__init__.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/_snapvault/snapcode.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/_snapvault/snapvault.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/_state.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/_telekit_dsl/__init__.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/_telekit_dsl/mixin.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/_telekit_dsl/parser/__init__.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/_telekit_dsl/parser/builder.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/_telekit_dsl/parser/canvas_parser.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/_telekit_dsl/parser/lexer.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/_telekit_dsl/parser/nodes.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/_telekit_dsl/parser/parser.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/_telekit_dsl/parser/token.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/_telekit_dsl/telekit_dsl.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/_telekit_dsl/telekit_orm.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/_timeout.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/dices.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/example/__init__.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/example/example_handlers/__init__.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/example/example_handlers/complete_hotel.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/example/example_handlers/counter.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/example/example_handlers/dsl.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/example/example_handlers/faq.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/example/example_handlers/hotel.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/example/example_handlers/on_text.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/example/example_handlers/pages.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/example/example_handlers/pyapi.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/example/example_handlers/qr.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/example/example_handlers/quiz.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/example/example_handlers/spells.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/example/example_handlers/start.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/example/example_handlers/text_document.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/example/example_server.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/inline_buttons.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/parameters.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/senders.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/server.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/styles.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/types.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit/utils.py +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit.egg-info/SOURCES.txt +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit.egg-info/dependency_links.txt +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit.egg-info/requires.txt +0 -0
- {telekit-2.2.0a1 → telekit-2.2.0a2}/telekit.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: telekit
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.0a2
|
|
4
4
|
Summary: Declarative, developer-friendly library for building Telegram bots
|
|
5
5
|
Home-page: https://github.com/Romashkaa/telekit
|
|
6
6
|
Author: romashka
|
|
@@ -378,15 +378,29 @@ It tries to make Telegram bot development easier.
|
|
|
378
378
|
|
|
379
379
|
---
|
|
380
380
|
|
|
381
|
-
# Changes in version 2.2.
|
|
381
|
+
# Changes in version 2.2.0a2
|
|
382
382
|
|
|
383
383
|
## New Button Types
|
|
384
384
|
|
|
385
|
-
| **Name**
|
|
386
|
-
|
|
387
|
-
| `AlertButton`
|
|
388
|
-
| `NotificationButton` | A callback button that shows a
|
|
389
|
-
|
|
390
|
-
## User
|
|
391
|
-
|
|
392
|
-
|
|
385
|
+
| **Name** | **Description** |
|
|
386
|
+
|----------------------|----------------------------------------------------------|
|
|
387
|
+
| `AlertButton` | A callback button that shows a popup alert when pressed. |
|
|
388
|
+
| `NotificationButton` | A callback button that shows a notification. |
|
|
389
|
+
|
|
390
|
+
## User Improvements
|
|
391
|
+
|
|
392
|
+
| **Name** | **Description** |
|
|
393
|
+
| ---------------------- | --------------------------------------------------------- |
|
|
394
|
+
| `bio` | Bio of the user or description of the chat. |
|
|
395
|
+
| `birthdate` | Birthdate of the user, if set and visible. |
|
|
396
|
+
| `description` | Description of the group or channel. |
|
|
397
|
+
| `mention` | `tg://user?id=` deep link, works even without a username. |
|
|
398
|
+
| `is_private` | Whether the message was sent in a private chat. |
|
|
399
|
+
| `is_group` | Whether the message was sent in a group. |
|
|
400
|
+
| `is_supergroup` | Whether the message was sent in a supergroup. |
|
|
401
|
+
| `is_channel` | Whether the message was sent in a channel. |
|
|
402
|
+
| `avatar` | File ID of the user's most recent profile photo. |
|
|
403
|
+
| `profile_photos_count` | Total number of profile photos the user has set. |
|
|
404
|
+
|
|
405
|
+
- Refactor: `User` now accepts a `Message` object instead of `chat_id` + `from_user`. All properties are derived from `_sender` (`from_user` or `chat`) and migrated to `cached_property`. Fixed broken `get_id` and `get_full_name` references.
|
|
406
|
+
- Added `__repr__`.
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (C) 2026 Romashka
|
|
3
|
+
#
|
|
4
|
+
# This file is part of Telekit.
|
|
5
|
+
#
|
|
6
|
+
# Telekit is free software: you can redistribute it and/or modify it
|
|
7
|
+
# under the terms of the GNU General Public License as published by
|
|
8
|
+
# the Free Software Foundation, either version 3 of the License, or
|
|
9
|
+
# (at your option) any later version.
|
|
10
|
+
#
|
|
11
|
+
# Telekit is distributed in the hope that it will be useful,
|
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty
|
|
13
|
+
# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
|
|
14
|
+
# the GNU General Public License for more details.
|
|
15
|
+
#
|
|
16
|
+
# You should have received a copy of the GNU General Public License
|
|
17
|
+
# along with Telekit. If not, see <https://www.gnu.org/licenses/>.
|
|
18
|
+
#
|
|
19
|
+
from functools import cached_property
|
|
20
|
+
from typing import Literal
|
|
21
|
+
|
|
22
|
+
import telebot
|
|
23
|
+
import telebot.types
|
|
24
|
+
|
|
25
|
+
from ._logger import logger
|
|
26
|
+
|
|
27
|
+
__all__ = ["User"]
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class User:
|
|
31
|
+
|
|
32
|
+
bot: telebot.TeleBot
|
|
33
|
+
|
|
34
|
+
@classmethod
|
|
35
|
+
def _init(cls, bot: telebot.TeleBot) -> None:
|
|
36
|
+
cls.bot = bot
|
|
37
|
+
|
|
38
|
+
def __init__(self, message: telebot.types.Message) -> None:
|
|
39
|
+
self._message: telebot.types.Message = message
|
|
40
|
+
self._chat_id: int = message.chat.id
|
|
41
|
+
self.logger = logger.users(self._chat_id)
|
|
42
|
+
|
|
43
|
+
# ── Internal ──────────────────────────────────────────────────
|
|
44
|
+
|
|
45
|
+
@cached_property
|
|
46
|
+
def _sender(self) -> telebot.types.User | telebot.types.Chat:
|
|
47
|
+
"""Resolves to `from_user` if available, otherwise falls back to `chat`."""
|
|
48
|
+
return self._message.from_user or self._message.chat
|
|
49
|
+
|
|
50
|
+
# ── Logging ───────────────────────────────────────────────────
|
|
51
|
+
|
|
52
|
+
def enable_logging(self, *user_ids: int | str) -> None:
|
|
53
|
+
"""
|
|
54
|
+
Enable logging for this user or for additional user IDs.
|
|
55
|
+
If no arguments are passed, enables logging for this instance's chat ID.
|
|
56
|
+
"""
|
|
57
|
+
logger.enable_user_logging(*(user_ids or (self._chat_id,)))
|
|
58
|
+
|
|
59
|
+
# ── Telegram native fields ────────────────────────────────────
|
|
60
|
+
|
|
61
|
+
@property
|
|
62
|
+
def id(self) -> int:
|
|
63
|
+
"""Unique identifier of the user or chat."""
|
|
64
|
+
return self._sender.id
|
|
65
|
+
|
|
66
|
+
@property
|
|
67
|
+
def username(self) -> str | None:
|
|
68
|
+
"""Telegram username without the leading `@`, or `None` if not set."""
|
|
69
|
+
return self._sender.username
|
|
70
|
+
|
|
71
|
+
@cached_property
|
|
72
|
+
def first_name(self) -> str | None:
|
|
73
|
+
"""First name of the user, or the group/channel title as a fallback."""
|
|
74
|
+
if isinstance(self._sender, telebot.types.User):
|
|
75
|
+
return self._sender.first_name
|
|
76
|
+
return self._sender.first_name or self._sender.title
|
|
77
|
+
|
|
78
|
+
@property
|
|
79
|
+
def last_name(self) -> str | None:
|
|
80
|
+
"""Last name of the user, or `None` for chats and users without one."""
|
|
81
|
+
return self._sender.last_name
|
|
82
|
+
|
|
83
|
+
@cached_property
|
|
84
|
+
def is_bot(self) -> bool:
|
|
85
|
+
"""Whether the sender is a bot. Always `False` for chats."""
|
|
86
|
+
if isinstance(self._sender, telebot.types.User):
|
|
87
|
+
return self._sender.is_bot
|
|
88
|
+
return False
|
|
89
|
+
|
|
90
|
+
@cached_property
|
|
91
|
+
def language_code(self) -> str | None:
|
|
92
|
+
"""
|
|
93
|
+
IETF language code of the user's Telegram client (e.g. `"en"`, `"uk"`).
|
|
94
|
+
Always `None` for chats.
|
|
95
|
+
"""
|
|
96
|
+
if isinstance(self._sender, telebot.types.User):
|
|
97
|
+
return self._sender.language_code
|
|
98
|
+
return None
|
|
99
|
+
|
|
100
|
+
@cached_property
|
|
101
|
+
def is_premium(self) -> bool:
|
|
102
|
+
"""Whether the user has an active Telegram Premium subscription.
|
|
103
|
+
Always `False` for chats and bots.
|
|
104
|
+
"""
|
|
105
|
+
if isinstance(self._sender, telebot.types.User):
|
|
106
|
+
return bool(self._sender.is_premium)
|
|
107
|
+
return False
|
|
108
|
+
|
|
109
|
+
@cached_property
|
|
110
|
+
def added_to_attachment_menu(self) -> bool:
|
|
111
|
+
"""Whether this bot has been added to the user's attachment menu.
|
|
112
|
+
Always `False` for chats.
|
|
113
|
+
"""
|
|
114
|
+
if isinstance(self._sender, telebot.types.User):
|
|
115
|
+
return bool(self._sender.added_to_attachment_menu)
|
|
116
|
+
return False
|
|
117
|
+
|
|
118
|
+
@property
|
|
119
|
+
def chat_type(self) -> Literal["private", "group", "supergroup", "channel"]:
|
|
120
|
+
"""Type of the chat the message was sent in."""
|
|
121
|
+
return self._message.chat.type # pyright: ignore[reportReturnType]
|
|
122
|
+
|
|
123
|
+
@property
|
|
124
|
+
def bio(self) -> str | None:
|
|
125
|
+
"""Bio of the user or description of the chat, as set in Telegram."""
|
|
126
|
+
return self._message.chat.bio
|
|
127
|
+
|
|
128
|
+
@property
|
|
129
|
+
def birthdate(self) -> telebot.types.Birthdate | None:
|
|
130
|
+
"""Birthdate of the user, if set and visible."""
|
|
131
|
+
return self._message.chat.birthdate
|
|
132
|
+
|
|
133
|
+
@property
|
|
134
|
+
def description(self) -> str | None:
|
|
135
|
+
"""Description of the group or channel. `None` for private chats."""
|
|
136
|
+
return self._message.chat.description
|
|
137
|
+
|
|
138
|
+
# ── Computed helpers ──────────────────────────────────────────
|
|
139
|
+
|
|
140
|
+
@cached_property
|
|
141
|
+
def full_name(self) -> str | None:
|
|
142
|
+
"""Full name of the user (`first_name + last_name`), or the chat title.
|
|
143
|
+
Returns `None` if neither is available.
|
|
144
|
+
"""
|
|
145
|
+
if isinstance(self._sender, telebot.types.User):
|
|
146
|
+
return self._sender.full_name
|
|
147
|
+
return " ".join(filter(None, [self.first_name, self.last_name])) or None
|
|
148
|
+
|
|
149
|
+
@property
|
|
150
|
+
def mention(self) -> str:
|
|
151
|
+
"""
|
|
152
|
+
A `tg://user?id=` deep link that mentions the user in any context,
|
|
153
|
+
even without a username.
|
|
154
|
+
"""
|
|
155
|
+
return f"tg://user?id={self.id}"
|
|
156
|
+
|
|
157
|
+
@property
|
|
158
|
+
def is_private(self) -> bool:
|
|
159
|
+
"""Whether the message was sent in a private chat."""
|
|
160
|
+
return self.chat_type == "private"
|
|
161
|
+
|
|
162
|
+
@property
|
|
163
|
+
def is_group(self) -> bool:
|
|
164
|
+
"""Whether the message was sent in a group chat."""
|
|
165
|
+
return self.chat_type == "group"
|
|
166
|
+
|
|
167
|
+
@property
|
|
168
|
+
def is_supergroup(self) -> bool:
|
|
169
|
+
"""Whether the message was sent in a supergroup."""
|
|
170
|
+
return self.chat_type == "supergroup"
|
|
171
|
+
|
|
172
|
+
@property
|
|
173
|
+
def is_channel(self) -> bool:
|
|
174
|
+
"""Whether the message was sent in a channel."""
|
|
175
|
+
return self.chat_type == "channel"
|
|
176
|
+
|
|
177
|
+
@property
|
|
178
|
+
def avatar(self) -> str | None:
|
|
179
|
+
"""
|
|
180
|
+
File ID of the user's most recent profile photo, or `None` if not set.
|
|
181
|
+
Makes an API call each time — cache the result if called frequently.
|
|
182
|
+
"""
|
|
183
|
+
photos = self.bot.get_user_profile_photos(self.id, limit=1)
|
|
184
|
+
if photos.total_count == 0:
|
|
185
|
+
return None
|
|
186
|
+
return photos.photos[0].file_id
|
|
187
|
+
|
|
188
|
+
@property
|
|
189
|
+
def profile_photos_count(self) -> int:
|
|
190
|
+
"""
|
|
191
|
+
Total number of profile photos the user has set.
|
|
192
|
+
Makes an API call each time.
|
|
193
|
+
"""
|
|
194
|
+
return self.bot.get_user_profile_photos(self.id).total_count
|
|
195
|
+
|
|
196
|
+
# ── Dunder ────────────────────────────────────────────────────
|
|
197
|
+
|
|
198
|
+
def __repr__(self) -> str:
|
|
199
|
+
return f"User(id={self.id}, username={self.username!r})"
|
|
@@ -63,9 +63,7 @@ class EntryHandler(telekit.Handler):
|
|
|
63
63
|
# Priority:
|
|
64
64
|
# 1. Previous response (internal database)
|
|
65
65
|
# 2. Telegram username (fallback)
|
|
66
|
-
name: str | None = self._user_data.get_name(
|
|
67
|
-
default=self.user.username
|
|
68
|
-
)
|
|
66
|
+
name: str | None = self._user_data.get_name() or self.user.username
|
|
69
67
|
|
|
70
68
|
if name:
|
|
71
69
|
self.chain.set_entry_suggestions([name])
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: telekit
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.0a2
|
|
4
4
|
Summary: Declarative, developer-friendly library for building Telegram bots
|
|
5
5
|
Home-page: https://github.com/Romashkaa/telekit
|
|
6
6
|
Author: romashka
|
|
@@ -378,15 +378,29 @@ It tries to make Telegram bot development easier.
|
|
|
378
378
|
|
|
379
379
|
---
|
|
380
380
|
|
|
381
|
-
# Changes in version 2.2.
|
|
381
|
+
# Changes in version 2.2.0a2
|
|
382
382
|
|
|
383
383
|
## New Button Types
|
|
384
384
|
|
|
385
|
-
| **Name**
|
|
386
|
-
|
|
387
|
-
| `AlertButton`
|
|
388
|
-
| `NotificationButton` | A callback button that shows a
|
|
389
|
-
|
|
390
|
-
## User
|
|
391
|
-
|
|
392
|
-
|
|
385
|
+
| **Name** | **Description** |
|
|
386
|
+
|----------------------|----------------------------------------------------------|
|
|
387
|
+
| `AlertButton` | A callback button that shows a popup alert when pressed. |
|
|
388
|
+
| `NotificationButton` | A callback button that shows a notification. |
|
|
389
|
+
|
|
390
|
+
## User Improvements
|
|
391
|
+
|
|
392
|
+
| **Name** | **Description** |
|
|
393
|
+
| ---------------------- | --------------------------------------------------------- |
|
|
394
|
+
| `bio` | Bio of the user or description of the chat. |
|
|
395
|
+
| `birthdate` | Birthdate of the user, if set and visible. |
|
|
396
|
+
| `description` | Description of the group or channel. |
|
|
397
|
+
| `mention` | `tg://user?id=` deep link, works even without a username. |
|
|
398
|
+
| `is_private` | Whether the message was sent in a private chat. |
|
|
399
|
+
| `is_group` | Whether the message was sent in a group. |
|
|
400
|
+
| `is_supergroup` | Whether the message was sent in a supergroup. |
|
|
401
|
+
| `is_channel` | Whether the message was sent in a channel. |
|
|
402
|
+
| `avatar` | File ID of the user's most recent profile photo. |
|
|
403
|
+
| `profile_photos_count` | Total number of profile photos the user has set. |
|
|
404
|
+
|
|
405
|
+
- Refactor: `User` now accepts a `Message` object instead of `chat_id` + `from_user`. All properties are derived from `_sender` (`from_user` or `chat`) and migrated to `cached_property`. Fixed broken `get_id` and `get_full_name` references.
|
|
406
|
+
- Added `__repr__`.
|
telekit-2.2.0a1/telekit/_user.py
DELETED
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
#
|
|
2
|
-
# Copyright (C) 2026 Romashka
|
|
3
|
-
#
|
|
4
|
-
# This file is part of Telekit.
|
|
5
|
-
#
|
|
6
|
-
# Telekit is free software: you can redistribute it and/or modify it
|
|
7
|
-
# under the terms of the GNU General Public License as published by
|
|
8
|
-
# the Free Software Foundation, either version 3 of the License, or
|
|
9
|
-
# (at your option) any later version.
|
|
10
|
-
#
|
|
11
|
-
# Telekit is distributed in the hope that it will be useful,
|
|
12
|
-
# but WITHOUT ANY WARRANTY; without even the implied warranty
|
|
13
|
-
# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
|
|
14
|
-
# the GNU General Public License for more details.
|
|
15
|
-
#
|
|
16
|
-
# You should have received a copy of the GNU General Public License
|
|
17
|
-
# along with Telekit. If not, see <https://www.gnu.org/licenses/>.
|
|
18
|
-
#
|
|
19
|
-
from functools import cached_property
|
|
20
|
-
from typing import Literal
|
|
21
|
-
|
|
22
|
-
import telebot
|
|
23
|
-
import telebot.types
|
|
24
|
-
|
|
25
|
-
from ._logger import logger
|
|
26
|
-
|
|
27
|
-
__all__ = ["User"]
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
class User:
|
|
31
|
-
|
|
32
|
-
bot: telebot.TeleBot
|
|
33
|
-
|
|
34
|
-
@classmethod
|
|
35
|
-
def _init(cls, bot: telebot.TeleBot) -> None:
|
|
36
|
-
cls.bot = bot
|
|
37
|
-
|
|
38
|
-
def __init__(self, message: telebot.types.Message) -> None:
|
|
39
|
-
self._message = message
|
|
40
|
-
self._chat_id = message.chat.id
|
|
41
|
-
self.logger = logger.users(self._chat_id)
|
|
42
|
-
|
|
43
|
-
# ── sender ────────────────────────────────────────────────────
|
|
44
|
-
|
|
45
|
-
@cached_property
|
|
46
|
-
def _sender(self) -> telebot.types.User | telebot.types.Chat:
|
|
47
|
-
return self._message.from_user or self._message.chat
|
|
48
|
-
|
|
49
|
-
# ── logging ───────────────────────────────────────────────────
|
|
50
|
-
|
|
51
|
-
def enable_logging(self, *user_ids: int | str) -> None:
|
|
52
|
-
"""Enable logging for this user or for additional user IDs."""
|
|
53
|
-
logger.enable_user_logging(*(user_ids or (self._chat_id,)))
|
|
54
|
-
|
|
55
|
-
# ── identity ──────────────────────────────────────────────────
|
|
56
|
-
|
|
57
|
-
@cached_property
|
|
58
|
-
def id(self) -> int:
|
|
59
|
-
return self._sender.id
|
|
60
|
-
|
|
61
|
-
@cached_property
|
|
62
|
-
def is_bot(self) -> bool:
|
|
63
|
-
if isinstance(self._sender, telebot.types.User):
|
|
64
|
-
return self._sender.is_bot
|
|
65
|
-
return False
|
|
66
|
-
|
|
67
|
-
# ── name ──────────────────────────────────────────────────────
|
|
68
|
-
|
|
69
|
-
@cached_property
|
|
70
|
-
def first_name(self) -> str | None:
|
|
71
|
-
if isinstance(self._sender, telebot.types.User):
|
|
72
|
-
return self._sender.first_name
|
|
73
|
-
return self._sender.first_name or self._sender.title
|
|
74
|
-
|
|
75
|
-
@cached_property
|
|
76
|
-
def last_name(self) -> str | None:
|
|
77
|
-
return self._sender.last_name
|
|
78
|
-
|
|
79
|
-
@cached_property
|
|
80
|
-
def full_name(self) -> str | None:
|
|
81
|
-
if isinstance(self._sender, telebot.types.User):
|
|
82
|
-
return self._sender.full_name
|
|
83
|
-
return " ".join(filter(None, [self.first_name, self.last_name])) or None
|
|
84
|
-
|
|
85
|
-
@cached_property
|
|
86
|
-
def username(self) -> str | None:
|
|
87
|
-
return self._sender.username
|
|
88
|
-
|
|
89
|
-
# ── locale ────────────────────────────────────────────────────
|
|
90
|
-
|
|
91
|
-
@cached_property
|
|
92
|
-
def language_code(self) -> str | None:
|
|
93
|
-
if isinstance(self._sender, telebot.types.User):
|
|
94
|
-
return self._sender.language_code
|
|
95
|
-
return None
|
|
96
|
-
|
|
97
|
-
# ── premium & extras ──────────────────────────────────────────
|
|
98
|
-
|
|
99
|
-
@cached_property
|
|
100
|
-
def is_premium(self) -> bool:
|
|
101
|
-
if isinstance(self._sender, telebot.types.User):
|
|
102
|
-
return bool(self._sender.is_premium)
|
|
103
|
-
return False
|
|
104
|
-
|
|
105
|
-
@cached_property
|
|
106
|
-
def added_to_attachment_menu(self) -> bool:
|
|
107
|
-
if isinstance(self._sender, telebot.types.User):
|
|
108
|
-
return bool(self._sender.added_to_attachment_menu)
|
|
109
|
-
return False
|
|
110
|
-
|
|
111
|
-
# ── chat-only ─────────────────────────────────────────────────
|
|
112
|
-
|
|
113
|
-
@cached_property
|
|
114
|
-
def chat_type(self) -> Literal["private", "group", "supergroup", "channel"]:
|
|
115
|
-
"""Type of the chat: private, group, supergroup, or channel."""
|
|
116
|
-
return self._message.chat.type # pyright: ignore[reportReturnType]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|