kurigram-addons 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.
Files changed (41) hide show
  1. kurigram_addons-0.1.0/LICENSE +21 -0
  2. kurigram_addons-0.1.0/PKG-INFO +33 -0
  3. kurigram_addons-0.1.0/README.md +15 -0
  4. kurigram_addons-0.1.0/pykeyboard/README.md +308 -0
  5. kurigram_addons-0.1.0/pykeyboard/__init__.py +21 -0
  6. kurigram_addons-0.1.0/pykeyboard/inline_keyboard.py +187 -0
  7. kurigram_addons-0.1.0/pykeyboard/keyboard_base.py +64 -0
  8. kurigram_addons-0.1.0/pykeyboard/reply_keyboard.py +74 -0
  9. kurigram_addons-0.1.0/pyproject.toml +18 -0
  10. kurigram_addons-0.1.0/pyrogram_patch/README.md +231 -0
  11. kurigram_addons-0.1.0/pyrogram_patch/__init__.py +4 -0
  12. kurigram_addons-0.1.0/pyrogram_patch/dispatcher.py +124 -0
  13. kurigram_addons-0.1.0/pyrogram_patch/fsm/__init__.py +4 -0
  14. kurigram_addons-0.1.0/pyrogram_patch/fsm/base_storage.py +25 -0
  15. kurigram_addons-0.1.0/pyrogram_patch/fsm/filter.py +15 -0
  16. kurigram_addons-0.1.0/pyrogram_patch/fsm/states.py +43 -0
  17. kurigram_addons-0.1.0/pyrogram_patch/fsm/storages/__init__.py +4 -0
  18. kurigram_addons-0.1.0/pyrogram_patch/fsm/storages/memory_storage.py +33 -0
  19. kurigram_addons-0.1.0/pyrogram_patch/fsm/storages/redis_storage.py +54 -0
  20. kurigram_addons-0.1.0/pyrogram_patch/middlewares/__init__.py +5 -0
  21. kurigram_addons-0.1.0/pyrogram_patch/middlewares/middleware_types/__init__.py +19 -0
  22. kurigram_addons-0.1.0/pyrogram_patch/middlewares/middleware_types/middlewares.py +122 -0
  23. kurigram_addons-0.1.0/pyrogram_patch/patch.py +36 -0
  24. kurigram_addons-0.1.0/pyrogram_patch/patch_data_pool.py +32 -0
  25. kurigram_addons-0.1.0/pyrogram_patch/patch_helper.py +71 -0
  26. kurigram_addons-0.1.0/pyrogram_patch/router/__init__.py +3 -0
  27. kurigram_addons-0.1.0/pyrogram_patch/router/patched_decorators/__init__.py +47 -0
  28. kurigram_addons-0.1.0/pyrogram_patch/router/patched_decorators/on_callback_query.py +58 -0
  29. kurigram_addons-0.1.0/pyrogram_patch/router/patched_decorators/on_chat_join_request.py +56 -0
  30. kurigram_addons-0.1.0/pyrogram_patch/router/patched_decorators/on_chat_member_updated.py +56 -0
  31. kurigram_addons-0.1.0/pyrogram_patch/router/patched_decorators/on_chosen_inline_result.py +57 -0
  32. kurigram_addons-0.1.0/pyrogram_patch/router/patched_decorators/on_deleted_messages.py +57 -0
  33. kurigram_addons-0.1.0/pyrogram_patch/router/patched_decorators/on_disconnect.py +47 -0
  34. kurigram_addons-0.1.0/pyrogram_patch/router/patched_decorators/on_edited_message.py +57 -0
  35. kurigram_addons-0.1.0/pyrogram_patch/router/patched_decorators/on_inline_query.py +58 -0
  36. kurigram_addons-0.1.0/pyrogram_patch/router/patched_decorators/on_message.py +57 -0
  37. kurigram_addons-0.1.0/pyrogram_patch/router/patched_decorators/on_poll.py +57 -0
  38. kurigram_addons-0.1.0/pyrogram_patch/router/patched_decorators/on_raw_update.py +51 -0
  39. kurigram_addons-0.1.0/pyrogram_patch/router/patched_decorators/on_user_status.py +56 -0
  40. kurigram_addons-0.1.0/pyrogram_patch/router/router.py +16 -0
  41. kurigram_addons-0.1.0/setup.py +37 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2020 PyMaster
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,33 @@
1
+ Metadata-Version: 2.1
2
+ Name: kurigram-addons
3
+ Version: 0.1.0
4
+ Summary: This package brings together several addons to kurigram including pykeyboard and FSM
5
+ License: MIT
6
+ Author: Johnnie
7
+ Author-email: 99084912+johnnie-610@users.noreply.github.com
8
+ Requires-Python: >=3.8,<4.0
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.8
12
+ Classifier: Programming Language :: Python :: 3.9
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Requires-Dist: kurigram (>=2.1.35,<3.0.0)
16
+ Description-Content-Type: text/markdown
17
+
18
+ <img align=center src=./logo.png length=80 width = 400>
19
+
20
+
21
+ > This library is a collection of popular Addons and patches for pyrogram/Kurigram.
22
+ > Currently, Pykeyboard and Pyrogram-patch have been added. You're welcome to add more.
23
+
24
+ # Installation
25
+
26
+ ```shell
27
+ pip install kurigram-addons
28
+ ```
29
+
30
+ # Usage
31
+
32
+ Please refer to specific readme.md for each addon.
33
+
@@ -0,0 +1,15 @@
1
+ <img align=center src=./logo.png length=80 width = 400>
2
+
3
+
4
+ > This library is a collection of popular Addons and patches for pyrogram/Kurigram.
5
+ > Currently, Pykeyboard and Pyrogram-patch have been added. You're welcome to add more.
6
+
7
+ # Installation
8
+
9
+ ```shell
10
+ pip install kurigram-addons
11
+ ```
12
+
13
+ # Usage
14
+
15
+ Please refer to specific readme.md for each addon.
@@ -0,0 +1,308 @@
1
+ <div align="center">
2
+ <p align="center">
3
+ <img src="https://raw.githubusercontent.com/johnnie-610/pykeyboard/main/docs/source/images/logo.png" alt="pykeyboard">
4
+ </p>
5
+
6
+ <p><h2>🎉This is pykeyboard for <a href="https://pypi.org/project/Kurigram/"</a>Kuigram 🎉</h2></p>
7
+ <br>
8
+ <p><strong><em>No need to change your code, just install the library and you're good to go.</em></strong></p>
9
+
10
+ </div>
11
+
12
+ # Pykeyboard
13
+
14
+ - [Pykeyboard](#pykeyboard)
15
+ - [What&#39;s new?](#whats-new)
16
+ - [Installation](#installation)
17
+ - [Documentation](#documentation)
18
+ - [Inline Keyboard](#inline-keyboard)
19
+ - [Parameters:](#parameters)
20
+ - [Inline Keyboard add buttons](#inline-keyboard-add-buttons)
21
+ - [Code](#code)
22
+ - [Result](#result)
23
+ - [Inline Keyboard row buttons](#inline-keyboard-row-buttons)
24
+ - [Code](#code-1)
25
+ - [Result](#result-1)
26
+ - [Pagination inline keyboard](#pagination-inline-keyboard)
27
+ - [Parameters:](#parameters-1)
28
+ - [Pagination 3 pages](#pagination-3-pages)
29
+ - [Code](#code-2)
30
+ - [Result](#result-2)
31
+ - [Pagination 5 pages](#pagination-5-pages)
32
+ - [Code](#code-3)
33
+ - [Result](#result-3)
34
+ - [Pagination 9 pages](#pagination-9-pages)
35
+ - [Code](#code-4)
36
+ - [Result](#result-4)
37
+ - [Pagination 100 pages](#pagination-100-pages)
38
+ - [Code](#code-5)
39
+ - [Result](#result-5)
40
+ - [Pagination 150 pages and buttons](#pagination-150-pages-and-buttons)
41
+ - [Code](#code-6)
42
+ - [Result](#result-6)
43
+ - [Languages inline keyboard](#languages-inline-keyboard)
44
+ - [Parameters:](#parameters-2)
45
+ - [Code](#code-7)
46
+ - [Result](#result-7)
47
+ - [Reply Keyboard](#reply-keyboard)
48
+ - [Parameters:](#parameters-3)
49
+ - [Reply Keyboard add buttons](#reply-keyboard-add-buttons)
50
+ - [Code](#code-8)
51
+ - [Result](#result-8)
52
+ - [Reply Keyboard row buttons](#reply-keyboard-row-buttons)
53
+ - [Code](#code-9)
54
+ - [Result](#result-9)
55
+
56
+ # What's new?
57
+
58
+ - Minor changes due to update in Kurigram.
59
+
60
+ # Documentation
61
+
62
+ ## Inline Keyboard
63
+
64
+ ```python
65
+ from pykeyboard import InlineKeyboard
66
+ ```
67
+
68
+ ##### Parameters:
69
+
70
+ - row_width (integer, default 3)
71
+
72
+ ### Inline Keyboard add buttons
73
+
74
+ #### Code
75
+
76
+ ```python
77
+ from pykeyboard import InlineKeyboard, InlineButton
78
+
79
+
80
+ keyboard = InlineKeyboard(row_width=3)
81
+ keyboard.add(
82
+ InlineButton('1', 'inline_keyboard:1'),
83
+ InlineButton('2', 'inline_keyboard:2'),
84
+ InlineButton('3', 'inline_keyboard:3'),
85
+ InlineButton('4', 'inline_keyboard:4'),
86
+ InlineButton('5', 'inline_keyboard:5'),
87
+ InlineButton('6', 'inline_keyboard:6'),
88
+ InlineButton('7', 'inline_keyboard:7')
89
+ )
90
+ ```
91
+
92
+ #### Result
93
+
94
+ <p><img src="https://raw.githubusercontent.com/johnnie-610/pykeyboard/main/docs/source/images/add_inline_button.png" alt="add_inline_button"></p>
95
+
96
+ ### Inline Keyboard row buttons
97
+
98
+ #### Code
99
+
100
+ ```python
101
+ from pykeyboard import InlineKeyboard, InlineButton
102
+
103
+
104
+ keyboard = InlineKeyboard()
105
+ keyboard.row(InlineButton('1', 'inline_keyboard:1'))
106
+ keyboard.row(
107
+ InlineButton('2', 'inline_keyboard:2'),
108
+ InlineButton('3', 'inline_keyboard:3')
109
+ )
110
+ keyboard.row(InlineButton('4', 'inline_keyboard:4'))
111
+ keyboard.row(
112
+ InlineButton('5', 'inline_keyboard:5'),
113
+ InlineButton('6', 'inline_keyboard:6')
114
+ )
115
+ ```
116
+
117
+ #### Result
118
+
119
+ <p><img src="https://raw.githubusercontent.com/johnnie-610/pykeyboard/main/docs/source/images/row_inline_button.png" alt="row_inline_button"></p>
120
+
121
+ ### Pagination inline keyboard
122
+
123
+ ```python
124
+ from pykeyboard import InlineKeyboard
125
+ ```
126
+
127
+ #### Parameters:
128
+
129
+ - count_pages (integer)
130
+ - current_page (integer)
131
+ - callback_pattern (string) - use of the `{number}` pattern is `<ins>`required`</ins>`
132
+
133
+ #### Pagination 3 pages
134
+
135
+ #### Code
136
+
137
+ ```python
138
+ from pykeyboard import InlineKeyboard
139
+
140
+ keyboard = InlineKeyboard()
141
+ keyboard.paginate(3, 3, 'pagination_keyboard:{number}')
142
+ ```
143
+
144
+ #### Result
145
+
146
+ <p><img src="https://raw.githubusercontent.com/johnnie-610/pykeyboard/main/docs/source/images/pagination_keyboard_3.png" alt="pagination_keyboard_3"></p>
147
+
148
+ #### Pagination 5 pages
149
+
150
+ #### Code
151
+
152
+ ```python
153
+ from pykeyboard import InlineKeyboard
154
+
155
+ keyboard = InlineKeyboard()
156
+ keyboard.paginate(5, 3, 'pagination_keyboard:{number}')
157
+ ```
158
+
159
+ #### Result
160
+
161
+ <p><img src="https://raw.githubusercontent.com/johnnie-610/pykeyboard/main/docs/source/images/pagination_keyboard_5.png" alt="pagination_keyboard_5"></p>
162
+
163
+ #### Pagination 9 pages
164
+
165
+ #### Code
166
+
167
+ ```python
168
+ from pykeyboard import InlineKeyboard
169
+
170
+ keyboard = InlineKeyboard()
171
+ keyboard.paginate(9, 5, 'pagination_keyboard:{number}')
172
+ ```
173
+
174
+ #### Result
175
+
176
+ <p><img src="https://raw.githubusercontent.com/johnnie-610/pykeyboard/main/docs/source/images/pagination_keyboard_9.png" alt="pagination_keyboard_9"></p>
177
+
178
+ #### Pagination 100 pages
179
+
180
+ #### Code
181
+
182
+ ```python
183
+ from pykeyboard import InlineKeyboard
184
+
185
+ keyboard = InlineKeyboard()
186
+ keyboard.paginate(100, 100, 'pagination_keyboard:{number}')
187
+ ```
188
+
189
+ #### Result
190
+
191
+ <p><img src="https://raw.githubusercontent.com/johnnie-610/pykeyboard/main/docs/source/images/pagination_keyboard_100.png" alt="pagination_keyboard_100"></p>
192
+
193
+ #### Pagination 150 pages and buttons
194
+
195
+ #### Code
196
+
197
+ ```python
198
+ from pykeyboard import InlineKeyboard, InlineButton
199
+
200
+ keyboard = InlineKeyboard()
201
+ keyboard.paginate(150, 123, 'pagination_keyboard:{number}')
202
+ keyboard.row(
203
+ InlineButton('Back', 'pagination_keyboard:back'),
204
+ InlineButton('Close', 'pagination_keyboard:close')
205
+ )
206
+ ```
207
+
208
+ #### Result
209
+
210
+ <p><img src="https://raw.githubusercontent.com/johnnie-610/pykeyboard/main/docs/source/images/pagination_keyboard_150.png" alt="pagination_keyboard_150"></p>
211
+
212
+ ### Languages inline keyboard
213
+
214
+ ```python
215
+ from pykeyboard import InlineKeyboard
216
+ ```
217
+
218
+ #### Parameters:
219
+
220
+ - callback_pattern (string) - use of the `{locale}` pattern is `<ins>`required`</ins>`
221
+ - locales (string | list) - list of language codes
222
+ - be_BY - Belarusian
223
+ - de_DE - German
224
+ - zh_CN - Chinese
225
+ - en_US - English
226
+ - fr_FR - French
227
+ - id_ID - Indonesian
228
+ - it_IT - Italian
229
+ - ko_KR - Korean
230
+ - tr_TR - Turkish
231
+ - ru_RU - Russian
232
+ - es_ES - Spanish
233
+ - uk_UA - Ukrainian
234
+ - uz_UZ - Uzbek
235
+ - row_width (integer, default 2)
236
+
237
+ #### Code
238
+
239
+ ```python
240
+ from pykeyboard import InlineKeyboard
241
+
242
+
243
+ keyboard = InlineKeyboard(row_width=3)
244
+ keyboard.languages(
245
+ 'languages:{locale}', ['en_US', 'ru_RU', 'id_ID'], 2
246
+ )
247
+ ```
248
+
249
+ #### Result
250
+
251
+ <p><img src="https://raw.githubusercontent.com/johnnie-610/pykeyboard/main/docs/source/images/languages_keyboard.png" alt="languages_keyboard"></p>
252
+
253
+ ## Reply Keyboard
254
+
255
+ ```python
256
+ from pykeyboard import ReplyKeyboard
257
+ ```
258
+
259
+ #### Parameters:
260
+
261
+ - resize_keyboard (bool, optional)
262
+ - one_time_keyboard (bool, optional)
263
+ - selective (bool, optional)
264
+ - row_width (integer, default 3)
265
+
266
+ ### Reply Keyboard add buttons
267
+
268
+ #### Code
269
+
270
+ ```python
271
+ from pykeyboard import ReplyKeyboard, ReplyButton
272
+
273
+
274
+ keyboard = ReplyKeyboard(row_width=3)
275
+ keyboard.add(
276
+ ReplyButton('Reply button 1'),
277
+ ReplyButton('Reply button 2'),
278
+ ReplyButton('Reply button 3'),
279
+ ReplyButton('Reply button 4'),
280
+ ReplyButton('Reply button 5')
281
+ )
282
+ ```
283
+
284
+ #### Result
285
+
286
+ <p><img src="https://raw.githubusercontent.com/johnnie-610/pykeyboard/main/docs/source/images/add_reply_button.png" alt="add_reply_button"></p>
287
+
288
+ ### Reply Keyboard row buttons
289
+
290
+ #### Code
291
+
292
+ ```python
293
+ from pykeyboard import ReplyKeyboard, ReplyButton
294
+
295
+
296
+ keyboard = ReplyKeyboard()
297
+ keyboard.row(ReplyButton('Reply button 1'))
298
+ keyboard.row(
299
+ ReplyButton('Reply button 2'),
300
+ ReplyButton('Reply button 3')
301
+ )
302
+ keyboard.row(ReplyButton('Reply button 4'))
303
+ keyboard.row(ReplyButton('Reply button 5'))
304
+ ```
305
+
306
+ #### Result
307
+
308
+ <p><img src="https://raw.githubusercontent.com/johnnie-610/pykeyboard/main/docs/source/images/row_reply_button.png" alt="row_reply_button"></p>
@@ -0,0 +1,21 @@
1
+ # Copyright (c) 2025 Johnnie
2
+ #
3
+ # This software is released under the MIT License.
4
+ # https://opensource.org/licenses/MIT
5
+
6
+ from .keyboard_base import Button, InlineButton
7
+ from .inline_keyboard import InlineKeyboard
8
+ from .reply_keyboard import ReplyKeyboard, ReplyButton, ReplyKeyboardRemove, ForceReply
9
+
10
+ __version__ = "0.1.2"
11
+ __all__ = [
12
+ "Button",
13
+ "InlineButton",
14
+ "InlineKeyboard",
15
+ "ReplyKeyboard",
16
+ "ReplyButton",
17
+ "ReplyKeyboardRemove",
18
+ "ForceReply",
19
+ ]
20
+
21
+ __author__ = "Johnnie"
@@ -0,0 +1,187 @@
1
+ # Copyright (c) 2025 Johnnie
2
+ #
3
+ # This software is released under the MIT License.
4
+ # https://opensource.org/licenses/MIT
5
+
6
+ from pyrogram.emoji import *
7
+ from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton
8
+ from dataclasses import dataclass
9
+ from .keyboard_base import KeyboardBase, InlineButton
10
+ from functools import lru_cache
11
+
12
+
13
+ @dataclass
14
+ class InlineKeyboard(InlineKeyboardMarkup, KeyboardBase):
15
+ _PAGINATION_SYMBOLS = {
16
+ "first": "« {}",
17
+ "prev": "‹ {}",
18
+ "current": "· {} ·",
19
+ "next": "{} ›",
20
+ "last": "{} »",
21
+ }
22
+
23
+ _LOCALES: dict[str, str] = {
24
+ "be_BY": f"{FLAG_BELARUS} Беларуская", # Belarusian - Belarus
25
+ "de_DE": f"{FLAG_GERMANY} Deutsch", # German - Germany
26
+ "zh_CN": f"{FLAG_CHINA} 中文", # Chinese - China
27
+ # English - United States
28
+ "en_US": f"{FLAG_UNITED_KINGDOM} English",
29
+ "fr_FR": f"{FLAG_FRANCE} Français", # French - France
30
+ # Indonesian - Indonesia
31
+ "id_ID": f"{FLAG_INDONESIA} Bahasa Indonesia",
32
+ "it_IT": f"{FLAG_ITALY} Italiano", # Italian - Italy
33
+ "ko_KR": f"{FLAG_SOUTH_KOREA} 한국어", # Korean - Korea
34
+ "tr_TR": f"{FLAG_TURKEY} Türkçe", # Turkish - Turkey
35
+ "ru_RU": f"{FLAG_RUSSIA} Русский", # Russian - Russia
36
+ "es_ES": f"{FLAG_SPAIN} Español", # Spanish - Spain
37
+ "uk_UA": f"{FLAG_UKRAINE} Українська", # Ukrainian - Ukraine
38
+ "uz_UZ": f"{FLAG_UZBEKISTAN} Oʻzbekcha", # Uzbek - Uzbekistan
39
+ }
40
+
41
+ def __post_init__(self):
42
+ super().__init__(inline_keyboard=self.keyboard)
43
+
44
+ @lru_cache(maxsize=128)
45
+ def _create_button(
46
+ self, text: str, callback_data: str | int
47
+ ) -> InlineKeyboardButton:
48
+ """Create cached button to avoid recreation of identical buttons."""
49
+ return InlineButton(
50
+ text=text, callback_data=self.callback_pattern.format(number=callback_data)
51
+ )
52
+
53
+ def paginate(
54
+ self, count_pages: int, current_page: int, callback_pattern: str
55
+ ) -> None:
56
+ """Optimized pagination with better edge case handling.
57
+
58
+ Args:
59
+ count_pages (int): Total number of pages.
60
+ current_page (int): The page number currently being viewed.
61
+ callback_pattern (str): The pattern used for callback data.
62
+
63
+ Example:
64
+ >>> keyboard = InlineKeyboard()
65
+ >>> keyboard.paginate(10, 1, 'page_{number}')
66
+ >>> print(keyboard.keyboard)
67
+ [[InlineKeyboardButton(text='· 1 ·', callback_data='page_1'), ...]]
68
+
69
+ """
70
+ self.count_pages = max(1, count_pages)
71
+ self.current_page = max(1, min(current_page, self.count_pages))
72
+ self.callback_pattern = callback_pattern
73
+
74
+ if self.count_pages <= 5:
75
+ pagination = self._build_small_pagination()
76
+ else:
77
+ pagination = self._build_large_pagination()
78
+
79
+ self.keyboard.append(pagination)
80
+
81
+ def _build_small_pagination(self) -> list[InlineKeyboardButton]:
82
+ return [
83
+ self._create_button(
84
+ (
85
+ self._PAGINATION_SYMBOLS["current"].format(i)
86
+ if i == self.current_page
87
+ else str(i)
88
+ ),
89
+ i,
90
+ )
91
+ for i in range(1, self.count_pages + 1)
92
+ ]
93
+
94
+ def _build_large_pagination(self) -> list[InlineKeyboardButton]:
95
+ if self.current_page <= 3:
96
+ return self._build_left_pagination()
97
+ elif self.current_page > self.count_pages - 3:
98
+ return self._build_right_pagination()
99
+ return self._build_middle_pagination()
100
+
101
+ def _build_left_pagination(self) -> list[InlineKeyboardButton]:
102
+ buttons = []
103
+ for i in range(1, 6):
104
+ if i == self.current_page:
105
+ text = self._PAGINATION_SYMBOLS["current"].format(i)
106
+ elif i == 4:
107
+ text = self._PAGINATION_SYMBOLS["next"].format(i)
108
+ elif i == 5:
109
+ text = self._PAGINATION_SYMBOLS["last"].format(self.count_pages)
110
+ i = self.count_pages
111
+ else:
112
+ text = str(i)
113
+ buttons.append(self._create_button(text, i))
114
+ return buttons
115
+
116
+ def _build_middle_pagination(self) -> list[InlineKeyboardButton]:
117
+ return [
118
+ self._create_button(self._PAGINATION_SYMBOLS["first"].format(1), 1),
119
+ self._create_button(
120
+ self._PAGINATION_SYMBOLS["prev"].format(self.current_page - 1),
121
+ self.current_page - 1,
122
+ ),
123
+ self._create_button(
124
+ self._PAGINATION_SYMBOLS["current"].format(self.current_page),
125
+ self.current_page,
126
+ ),
127
+ self._create_button(
128
+ self._PAGINATION_SYMBOLS["next"].format(self.current_page + 1),
129
+ self.current_page + 1,
130
+ ),
131
+ self._create_button(
132
+ self._PAGINATION_SYMBOLS["last"].format(self.count_pages),
133
+ self.count_pages,
134
+ ),
135
+ ]
136
+
137
+ def _build_right_pagination(self) -> list[InlineKeyboardButton]:
138
+ buttons = [
139
+ self._create_button(self._PAGINATION_SYMBOLS["first"].format(1), 1),
140
+ self._create_button(
141
+ self._PAGINATION_SYMBOLS["prev"].format(self.count_pages - 3),
142
+ self.count_pages - 3,
143
+ ),
144
+ ]
145
+
146
+ for i in range(self.count_pages - 2, self.count_pages + 1):
147
+ text = (
148
+ self._PAGINATION_SYMBOLS["current"].format(i)
149
+ if i == self.current_page
150
+ else str(i)
151
+ )
152
+ buttons.append(self._create_button(text, i))
153
+
154
+ return buttons
155
+
156
+ def languages(
157
+ self, callback_pattern: str, locales: str | list[str], row_width: int = 2
158
+ ) -> None:
159
+ """Optimized language selection keyboard.
160
+
161
+ Args:
162
+ callback_pattern (str): The pattern used for callback data.
163
+ locales (str | list[str]): One or more locales to be used in the keyboard.
164
+ row_width (int, optional): The number of buttons per row. Defaults to 2.
165
+
166
+ Example:
167
+ >>> keyboard = InlineKeyboard()
168
+ >>> keyboard.languages('lang_{locale}', ['en_US', 'ru_RU', 'uk_UA'])
169
+ >>> print(keyboard.keyboard)
170
+ [[InlineKeyboardButton(text='English', callback_data='lang_en_US'), InlineKeyboardButton(text='Русский', callback_data='lang_ru_RU')], [InlineKeyboardButton(text='Українська', callback_data='lang_uk_UA')]]
171
+
172
+ """
173
+ if isinstance(locales, str):
174
+ locales = [locales]
175
+
176
+ buttons = [
177
+ InlineButton(
178
+ text=self._LOCALES.get(locale, "Invalid locale"),
179
+ callback_data=callback_pattern.format(locale=locale),
180
+ )
181
+ for locale in locales
182
+ if locale in self._LOCALES
183
+ ]
184
+
185
+ self.keyboard = [
186
+ buttons[i : i + row_width] for i in range(0, len(buttons), row_width)
187
+ ]
@@ -0,0 +1,64 @@
1
+ # Copyright (c) 2025 Johnnie
2
+ #
3
+ # This software is released under the MIT License.
4
+ # https://opensource.org/licenses/MIT
5
+
6
+ from dataclasses import dataclass, field
7
+ from pyrogram.types import InlineKeyboardButton, WebAppInfo, CallbackGame, LoginUrl
8
+
9
+
10
+ @dataclass
11
+ class KeyboardBase:
12
+ row_width: int = 3
13
+ keyboard: list[list] = field(default_factory=list)
14
+
15
+ def add(self, *args: object) -> None:
16
+ self.keyboard = [
17
+ list(args[i : i + self.row_width])
18
+ for i in range(0, len(args), self.row_width)
19
+ ]
20
+
21
+ def row(self, *args: object) -> None:
22
+ self.keyboard.append(list(args))
23
+
24
+
25
+ @dataclass
26
+ class Button:
27
+
28
+ text: str
29
+
30
+ def __post_init__(self):
31
+ if not isinstance(self.text, str):
32
+ raise ValueError("Button text must be a string")
33
+
34
+
35
+ @dataclass
36
+ class InlineButton(InlineKeyboardButton, Button):
37
+ callback_data: str | bytes | None = None
38
+ url: str | None = None
39
+ web_app: WebAppInfo | None = None
40
+ login_url: LoginUrl | None = None
41
+ user_id: int | None = None
42
+ switch_inline_query: str | None = None
43
+ switch_inline_query_current_chat: str | None = None
44
+ callback_game: CallbackGame | None = None
45
+ requires_password: bool | None = None
46
+ pay: bool | None = None
47
+ copy_text: str | None = None
48
+
49
+ def __post_init__(self):
50
+ super().__post_init__()
51
+ super(InlineKeyboardButton, self).__init__(
52
+ text=self.text,
53
+ callback_data=self.callback_data,
54
+ url=self.url,
55
+ web_app=self.web_app,
56
+ login_url=self.login_url,
57
+ user_id=self.user_id,
58
+ switch_inline_query=self.switch_inline_query,
59
+ switch_inline_query_current_chat=self.switch_inline_query_current_chat,
60
+ callback_game=self.callback_game,
61
+ requires_password=self.requires_password,
62
+ pay=self.pay,
63
+ copy_text=self.copy_text,
64
+ )