Skip to content

Commit 9b9d9dd

Browse files
authored
Merge pull request #27 from Aidenable/dev
v1.3.5: `eval`, `bash`, `stats`, `help` commands; list paginator; docs improvements
2 parents 0404be5 + 5636d1c commit 9b9d9dd

File tree

37 files changed

+880
-56
lines changed

37 files changed

+880
-56
lines changed
52.8 KB
Loading

docs/source/plugins/keyboards.rst

Lines changed: 72 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -77,53 +77,103 @@ Both decorators accept optional arguments:
7777
Examples
7878
~~~~~~~~~~~~
7979

80-
Static layout:
80+
Static inline keyboard:
8181
^^^^^^^^^^^^^^^^
8282

8383
.. code-block:: python
8484
8585
from raito import rt
8686
87-
@rt.keyboard.static(inline=True)
88-
def faq_buttons():
87+
@rt.keyboard.static()
88+
def info_markup():
8989
return [
90-
("Terms of Service", "tos"),
91-
("Privacy", "privacy"),
90+
[("💬 Support", "support")],
91+
[("🔒 Privacy", "privacy"), ("📄 TOS", "terms_of_use")]
9292
]
9393
94-
Dynamic layout:
94+
@router.message(...)
95+
async def handler(message: Message):
96+
await message.answer("Buttons:", reply_markup=info_markup())
97+
98+
Static reply keyboard:
99+
^^^^^^^^^^^^^^^^
100+
101+
.. code-block:: python
102+
103+
from raito import rt
104+
105+
@rt.keyboard.static(inline=False)
106+
def info_markup():
107+
return [
108+
["💬 Support"],
109+
[["🔒 Privacy"], ["📄 TOS"]]
110+
]
111+
112+
@router.message(...)
113+
async def handler(message: Message):
114+
await message.answer("Buttons:", reply_markup=info_markup())
115+
116+
Dynamic inline keyboard:
95117
^^^^^^^^^^^^^^^^
96118

97119
.. code-block:: python
98120
99121
from aiogram.utils.keyboard import InlineKeyboardBuilder
100122
from raito import rt
101123
102-
from ... import Player
124+
@rt.keyboard.dynamic(1, 2)
125+
def info_markup(builder: InlineKeyboardBuilder, privacy_url: str, tos_url: str):
126+
builder.button(text="💬 Support", callback_data="support")
127+
builder.button(text="🔒 Privacy", url=privacy_url)
128+
builder.button(text="📄 TOS", url=tos_url)
129+
130+
@router.message(...)
131+
async def handler(message: Message):
132+
await message.answer("Buttons:", reply_markup=info_markup(
133+
privacy_url="https://example.com/privacy",
134+
tos_url="https://example.com/tos",
135+
))
103136
104-
@rt.keyboard.dynamic()
105-
def leaderboard(builder: InlineKeyboardBuilder, players: list[Player]):
106-
for player in players:
107-
builder.button(text=f"{player.name}", callback_data=f"player:{player.id}")
137+
Dynamic reply keyboard:
138+
^^^^^^^^^^^^^^^^
139+
140+
.. code-block:: python
108141
109-
builder.adjust(1, 2, 2)
142+
from aiogram.utils.keyboard import ReplyKeyboardBuilder
143+
from raito import rt
110144
111-
Dynamic pagination-like example:
112-
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
145+
@rt.keyboard.dynamic(1, 2, inline=False)
146+
def info_markup(builder: ReplyKeyboardBuilder):
147+
builder.button(text="💬 Support")
148+
builder.button(text="🔒 Privacy")
149+
builder.button(text="📄 TOS")
150+
151+
@router.message(...)
152+
async def handler(message: Message):
153+
await message.answer("Buttons:", reply_markup=info_markup())
154+
155+
Custom adjust:
156+
^^^^^^^^^^^^^^^^
113157

114158
.. code-block:: python
115159
116160
from aiogram.utils.keyboard import InlineKeyboardBuilder
117161
from raito import rt
118162
119-
from ... import Player
120-
121163
@rt.keyboard.dynamic(adjust=False)
122-
def leaderboard(builder: InlineKeyboardBuilder, players: list[Player]):
123-
for i, player in enumerate(players, start=1):
124-
builder.button(text=f"#{i} {player.name}", callback_data=f"player:{player.id}")
164+
def admin_markup(builder: InlineKeyboardBuilder, show_balance_management: bool = False):
165+
adjust = []
166+
167+
builder.button(text="👤 Users", callback_data="users")
168+
adjust.append(1)
169+
170+
if show_balance_management:
171+
builder.button(text="📤 Withdraw", callback_data="withdraw")
172+
builder.button(text="📥 Deposit", callback_data="deposit")
173+
adjust.append(2)
125174
126-
builder.button(text="◀️", callback_data="prev")
127-
builder.button(text="▶️", callback_data="next")
175+
builder.adjust(*adjust)
128176
129-
builder.adjust(3)
177+
@router.message(...)
178+
async def handler(message: Message):
179+
await message.answer("Buttons:", reply_markup=admin_markup(True))

docs/source/plugins/pagination.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,11 @@ To manually attach navigation:
7474

7575
.. code-block:: python
7676
77+
builder = InlineKeyboardBuilder()
7778
navigation = paginator.build_navigation()
79+
7880
builder.attach(builder.from_markup(navigation))
81+
builder.button(text="Back", callback_data="menu")
7982
8083
---------
8184

docs/source/plugins/roles.rst

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ Use Telegram commands to manage roles:
4949
5050
To manage roles, you must already have one of the following:
5151

52-
- `developer`
52+
- `developer` (defined in ``Raito(developers=[...]``)
5353
- `owner`
5454
- `administrator`
5555

@@ -113,8 +113,81 @@ Access control is enforced on ``assign`` / ``revoke``:
113113
- Only trusted roles can assign
114114
- You can't assign or revoke your own role
115115

116+
.. code-block:: python
117+
118+
from aiogram import Router, filters, types
119+
120+
from raito import Raito, rt
121+
from raito.plugins.roles import ADMINISTRATOR, DEVELOPER, OWNER
122+
123+
router = Router(name="tester")
124+
125+
126+
@router.message(filters.Command("give_tester"), DEVELOPER | OWNER | ADMINISTRATOR)
127+
@rt.params(user_id=int)
128+
async def give_tester(message: types.Message, raito: Raito, user_id: int) -> None:
129+
if not message.from_user or not message.bot:
130+
return
131+
132+
await raito.role_manager.assign_role(message.bot.id, message.from_user.id, user_id, "tester")
133+
await message.answer(
134+
text=f"User with ID <code>{user_id}</code> is now a tester!",
135+
parse_mode="HTML",
136+
)
137+
138+
139+
@router.message(filters.Command("testers"), DEVELOPER | OWNER | ADMINISTRATOR)
140+
async def testers(message: types.Message, raito: Raito) -> None:
141+
if not message.from_user or not message.bot:
142+
return
143+
144+
testers = await raito.role_manager.get_users(message.bot.id, "tester")
145+
user_ids = [str(user_id) for user_id in testers]
146+
await message.answer(text="🧪 Testers: " + ", ".join(user_ids))
147+
148+
116149
-----
117150

151+
Available Roles
152+
~~~~~~~~~~~~
153+
154+
.. list-table::
155+
:header-rows: 1
156+
:widths: 20 10 70
157+
158+
* - Role
159+
- Slug
160+
- Description
161+
* - 🖥️ Developer
162+
- developer
163+
- Has full access to all internal features, including debug tools and unsafe operations.
164+
* - 👑 Owner
165+
- owner
166+
- Top-level administrator with permissions to manage administrators and global settings.
167+
* - 💼 Administrator
168+
- administrator
169+
- Can manage users, moderate content, and configure most system settings.
170+
* - 🛡️ Moderator
171+
- moderator
172+
- Can moderate user activity, issue warnings, and enforce rules within their scope.
173+
* - 📊 Manager
174+
- manager
175+
- Oversees non-technical operations like campaigns, tasks, or content planning.
176+
* - ❤️ Sponsor
177+
- sponsor
178+
- Supporter of the project. Usually does not have administrative privileges.
179+
* - 👤 Guest
180+
- guest
181+
- Has temporary access to specific internal features (e.g., analytics). Typically invited users.
182+
* - 💬 Support
183+
- support
184+
- Handles user support requests and assists with onboarding or issues.
185+
* - 🧪 Tester
186+
- tester
187+
- Helps test new features and provide feedback. May have access to experimental tools.
188+
189+
------
190+
118191
Custom Roles
119192
~~~~~~~~~~~~
120193

docs/source/quick_start.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,11 @@ What's happening here?
5151
await message.answer("Pong! 🏓")
5252
5353
📦 Not installed yet? See :doc:`installation`
54+
55+
-----
56+
57+
Also, Raito has a built-in commands. Send ``.rt help`` to your bot to see the list.
58+
59+
.. image:: /_static/help-command.png
60+
:alt: Raito Help Command Example
61+
:align: left

docs/source/utils/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ Utils
77
suppress_not_modified
88
logging
99
retry
10+
truncate

docs/source/utils/truncate.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
✂️ truncate
2+
=============================
3+
4+
Truncates a string to a given length.
5+
6+
.. code-block:: python
7+
8+
from raito.utils.helpers.truncate import truncate
9+
10+
truncate("Hello, world!", 8) # Returns "Hello..."

examples/02-lifespan/handlers/lifespan.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,7 @@ async def lifespan(raito: Raito, bot: Bot, dispatcher: Dispatcher) -> AsyncGener
1717

1818
yield
1919

20-
rt.debug("Removing webhook...")
21-
await bot.delete_webhook()
22-
23-
rt.log.debug("Closing dispatcher storages...")
20+
rt.log.debug("Closing dispatcher storage...")
2421
await dispatcher.storage.close()
25-
await dispatcher.fsm.storage.close()
2622

2723
rt.log.info("👋 Bye!")

examples/03-commands/handlers/events/lifespan.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,7 @@ async def lifespan(raito: Raito, bot: Bot, dispatcher: Dispatcher) -> AsyncGener
1717

1818
yield
1919

20-
rt.debug("Removing webhook...")
21-
await bot.delete_webhook()
22-
23-
rt.log.debug("Closing dispatcher storages...")
20+
rt.log.debug("Closing dispatcher storage...")
2421
await dispatcher.storage.close()
25-
await dispatcher.fsm.storage.close()
2622

2723
rt.log.info("👋 Bye!")

examples/05-roles/handlers/dev/eval.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from aiogram.types import Message
33

44
from raito import rt
5-
from raito.plugins.roles.roles import DEVELOPER
5+
from raito.plugins.roles import DEVELOPER
66

77
router = Router(name="eval")
88

0 commit comments

Comments
 (0)