Name | robka JSON |
Version |
2.4.2
JSON |
| download |
home_page | https://pypi.org/project/robka/ |
Summary | A professional and localized Python library for interacting with Rubika Bot API, compatible with various systems. |
upload_time | 2025-08-21 18:35:24 |
maintainer | httex |
docs_url | None |
author | httex |
requires_python | >=3.6 |
license | None |
keywords |
|
VCS |
|
bugtrack_url |
|
requirements |
No requirements were recorded.
|
Travis-CI |
No Travis.
|
coveralls test coverage |
No coveralls.
|
# 📚 Rubka Bot Python Library Documentation
# نمونه تنظیم وبهوک (Webhook) در کتابخونه rubla
برای مشاهده مستندات کامل و آخرین نسخه راهنما، لطفاً به آدرس زیر مراجعه کنید:
[github.com/Mahdy-Ahmadi](https://github.com/Mahdy-Ahmadi/rubka/blob/main/webhook.md))
## 🧠 Introduction
`rubka` is a Python library to interact with the [Rubika Bot API](https://rubika.ir/). This library helps you create Telegram-like bots with support for messages, inline buttons, chat keypads, and callback handling.
---
## ⚙️ Installation
```bash
pip install rubka
```
If `importlib.metadata` is not available, it installs `importlib-metadata` automatically.
---
## 🚀 Getting Started
```python
from rubka import Robot
from rubka.context import Message
bot = Robot(token="YOUR_TOKEN_HERE")
@bot.on_message(commands=["start"])
def start(bot: Robot, message: Message):
message.reply("سلام! خوش آمدید!")
bot.run()
```
---
## 📬 Handling Messages
You can handle incoming text messages using `@bot.on_message()`:
```python
@bot.on_message(commands=["hello"])
def greet(bot: Robot, message: Message):
message.reply("سلام کاربر عزیز 👋")
```
You can also add filters.
---
## 🎮 Handling Callback Buttons
```python
from rubka.keypad import ChatKeypadBuilder
@bot.on_message(commands=["gender"])
def gender(bot: Robot, message: Message):
keypad = ChatKeypadBuilder().row(
ChatKeypadBuilder().button(id="male", text="👨 مرد"),
ChatKeypadBuilder().button(id="female", text="👩 زن")
).build()
message.reply_keypad("جنسیت خود را انتخاب کنید:", keypad)
@bot.on_callback("male")
def on_male(bot: Robot, message: Message):
message.reply("شما مرد هستید")
@bot.on_callback("female")
def on_female(bot: Robot, message: Message):
message.reply("شما زن هستید")
```
---
## 🔘 Inline Button Builder
```python
from rubka.button import InlineBuilder
builder = InlineBuilder().row(
InlineBuilder().button_simple(id="info", text="اطلاعات")
).build()
```
---
## 🔄 Check if User Joined a Channel
```python
channel_guid = "c0xABCDEF..."
@bot.on_message(commands=["check"])
def check(bot: Robot, message: Message):
if bot.check_join(channel_guid, message.chat_id):
message.reply("✅ شما عضو کانال هستید")
else:
message.reply("❌ لطفاً ابتدا در کانال عضو شوید")
```
---
## 💬 Utility Methods
| Method | Description |
|--------|-------------|
| `get_chat(chat_id)` | دریافت اطلاعات چت |
| `get_name(chat_id)` | دریافت نام کاربر |
| `get_username(chat_id)` | دریافت نامکاربری |
| `send_message(...)` | ارسال پیام متنی |
| `edit_message_text(...)` | ویرایش پیام |
| `delete_message(...)` | حذف پیام |
| `send_location(...)` | ارسال موقعیت مکانی |
| `send_poll(...)` | ارسال نظرسنجی |
| `send_contact(...)` | ارسال مخاطب |
| `forward_message(...)` | فوروارد پیام |
---
## 🎛 Inline Query Support
```python
@bot.on_inline_query()
def inline(bot: Robot, message: InlineMessage):
message.answer("نتیجه اینلاین")
```
---
## 🧱 Button Types
Supported inline button types include:
- `Simple`
- `Payment`
- `Calendar`
- `Location`
- `CameraImage`, `CameraVideo`
- `GalleryImage`, `GalleryVideo`
- `File`, `Audio`, `RecordAudio`
- `MyPhoneNumber`, `MyLocation`
- `Textbox`, `Barcode`, `Link`
See `InlineBuilder` for more.
---
## 🧩 Dynamic Chat Keypad
```python
builder = ChatKeypadBuilder()
keypad = builder.row(
builder.button(id="play", text="🎮 بازی کن"),
builder.button(id="exit", text="❌ خروج")
).build()
```
---
## 🧪 Set Commands
```python
bot.set_commands([
{"command": "start", "description": "شروع"},
{"command": "help", "description": "راهنما"}
])
```
---
## 🔄 Update Offset Automatically
Bot updates are handled using `get_updates()` and `offset_id` is managed internally.
---
## 🛠 Advanced Features
- `update_bot_endpoint()` – تنظیم webhook یا polling
- `remove_keypad()` – حذف صفحهکلید چت
- `edit_chat_keypad()` – ویرایش یا افزودن صفحهکلید چت
---
# 📘 Rubka Bot Method Reference
مستندات مربوط به متدهای اصلی کلاس `Robot` در کتابخانه Rubka.
---
## ✅ پیامها و هندلرها
### `on_message(filters=None, commands=None)`
**توضیح:** ثبت هندلر برای پیامهای ورودی.
- `filters`: تابع شرطی برای فیلتر پیامها (اختیاری)
- `commands`: لیست دستورهایی که شروع با `/` هستند (اختیاری)
### `on_callback(button_id=None)`
**توضیح:** ثبت هندلر برای دکمههای فشردهشده
- `button_id`: آیدی دکمهای که باید هندل شود (اختیاری)
### `on_inline_query()`
**توضیح:** ثبت هندلر برای پیامهای اینلاین (inline query)
---
## 📨 ارسال پیام
### `send_message(...)`
**توضیح:** ارسال پیام متنی به چت
- `chat_id`: آیدی چت مقصد *(str)* ✅
- `text`: محتوای پیام *(str)* ✅
- `chat_keypad`: کیپد معمولی *(dict)*
- `inline_keypad`: کیپد اینلاین *(dict)*
- `reply_to_message_id`: پاسخ به پیام خاص *(str)*
- `disable_notification`: بدون نوتیف *(bool)*
- `chat_keypad_type`: حالت کیپد *("New" | "Removed")*
---
## 📁 ارسال فایلها
### متدهای مشترک (فایل، موزیک، ویس، گیف، عکس):
- `send_document(...)`
- `send_music(...)`
- `send_voice(...)`
- `send_gif(...)`
- `send_image(...)`
**پارامترهای اصلی:**
- `chat_id`: آیدی چت
- `path`: مسیر فایل یا URL (اختیاری)
- `file_id`: اگر فایل قبلاً آپلود شده باشد
- `text`: کپشن فایل
- `file_name`: نام فایل
- `inline_keypad`, `chat_keypad`, `reply_to_message_id`, `disable_notification`, `chat_keypad_type`
---
## 📍 سایر متدهای مهم
### `get_me()`
دریافت اطلاعات ربات
### `get_chat(chat_id)`
دریافت اطلاعات یک چت
### `get_name(chat_id)`
دریافت نام مخاطب بر اساس `first_name` و `last_name`
### `get_username(chat_id)`
دریافت نام کاربری چت (در صورت وجود)
### `check_join(channel_guid, chat_id)`
بررسی عضویت کاربر در کانال خاص
### `remove_keypad(chat_id)`
حذف کیپد معمولی چت
### `edit_chat_keypad(chat_id, chat_keypad)`
ویرایش یا اضافه کردن کیپد چت
### `edit_message_text(chat_id, message_id, text)`
ویرایش متن پیام ارسالشده
### `edit_inline_keypad(chat_id, message_id, inline_keypad)`
ویرایش کیپد اینلاین پیام
### `delete_message(chat_id, message_id)`
حذف پیام از چت
### `send_poll(chat_id, question, options)`
ارسال نظرسنجی به چت
### `send_location(chat_id, latitude, longitude, ...)`
ارسال موقعیت مکانی به چت
### `send_contact(chat_id, first_name, last_name, phone_number)`
ارسال مخاطب به چت
### `forward_message(from_chat_id, message_id, to_chat_id)`
فروارد کردن پیام از یک چت به چت دیگر
### `set_commands(bot_commands)`
تنظیم دستورات رسمی ربات (برای `/help` و ...)
### `update_bot_endpoint(url, type)`
تنظیم وبهوک یا polling برای دریافت پیامها
---
## 📦 مدیریت فایل و آپلود
### `get_upload_url(media_type)`
دریافت آدرس آپلود فایل برای انواع مختلف: File, Image, Voice, Music, Gif
### `upload_media_file(upload_url, name, path)`
آپلود فایل از مسیر محلی یا URL به Rubika و دریافت `file_id`
---
## 🔄 دریافت بروزرسانیها
### `get_updates(offset_id=None, limit=None)`
دریافت بروزرسانیها (برای polling)
---
# 📦 Rubka `Message` Class & Media Reply API Documentation
## 🧾 معرفی کلاس `Message`
کلاس `Message` در کتابخانه Rubka ابزاری کلیدی برای مدیریت پیامهای دریافتی در ربات است. این کلاس، قابلیتهایی همچون پاسخ به پیام، ارسال مدیا، حذف یا ویرایش پیام، و استفاده از صفحهکلید و دکمههای اینلاین را فراهم میکند.
---
## ⚙️ مشخصات کلاس `Message`
```python
Message(bot, chat_id, message_id, sender_id, text=None, raw_data=None)
```
### پارامترها:
| پارامتر | توضیح |
| ------------ | ---------------------------------------- |
| `bot` | نمونهی شی ربات |
| `chat_id` | شناسه چت |
| `message_id` | آیدی پیام |
| `sender_id` | شناسه فرستنده |
| `text` | متن پیام |
| `raw_data` | دادهی خام پیام (دیکشنری دریافتی از API) |
### ویژگیها (Attributes):
- `reply_to_message_id` – اگر پیام در پاسخ ارسال شده باشد، آیدی پیام اولیه
- `file`, `sticker`, `poll`, `contact_message`, `location`, ... – دادههای مربوطه اگر وجود داشته باشند
---
## 📩 متدهای پاسخدهی
### ✉️ `reply(text: str, **kwargs)`
پاسخ متنی به پیام با قابلیت ارسال دکمه و گزینههای اضافی.
### 📊 `reply_poll(question, options, **kwargs)`
ارسال نظرسنجی در پاسخ به پیام.
### 📎 `reply_document(...)`
ارسال فایل یا سند با متن اختیاری و دکمه.
### 🖼 `reply_image(...)`
ارسال تصویر با قابلیت reply همراه دکمههای chat یا inline.
### 🎵 `reply_music(...)`
ارسال موزیک در پاسخ.
### 🎤 `reply_voice(...)`
ارسال پیام صوتی (voice).
### 🎞 `reply_gif(...)`
ارسال گیف در پاسخ به پیام.
### 🗺 `reply_location(latitude, longitude, **kwargs)`
ارسال لوکیشن در پاسخ.
### 📇 `reply_contact(first_name, last_name, phone_number, **kwargs)`
ارسال مخاطب در پاسخ.
---
## 🔘 پاسخ با دکمهها
### `reply_keypad(text, keypad, **kwargs)`
ارسال پیام با صفحهکلید چتی (ChatKeypad).
### `reply_inline(text, inline_keypad, **kwargs)`
ارسال پیام با دکمههای شیشهای (Inline).
---
## 📦 پاسخ با فایلها و استیکر
### `reply_sticker(sticker_id, **kwargs)`
ارسال استیکر در پاسخ به پیام.
### `reply_file(file_id, **kwargs)`
ارسال فایل بر اساس File ID.
---
## ✏️ ویرایش و حذف
### `edit(new_text)`
ویرایش متن پیام.
### `delete()`
حذف پیام فعلی.
---
## 📤 مثال کاربردی کامل
```python
@bot.on_message()
def handler(bot: Robot, message: Message):
# پاسخ با تصویر و دکمههای مختلف
message.reply_image(
path="https://s6.uupload.ir/files/sample.png",
text="📷 تصویر پاسخدادهشده با دکمهها",
inline_keypad=inline_keypad
)
message.reply_image(
path="https://s6.uupload.ir/files/sample.png",
text="📷 تصویر دوم با صفحهکلید",
chat_keypad=chat_keypad,
chat_keypad_type="New"
)
@bot.on_callback()
def callback_handler(bot: Robot, message: Message):
data = message.aux_data.button_id
if data == "btn_male":
message.reply("سلام آقا 👨")
elif data == "btn_female":
message.reply("سلام خانم 👩")
else:
message.reply(f"دکمه ناشناخته: {data}")
```
---
## 🧠 نکته
تمامی متدهای `reply_*` بهصورت خودکار پیام جدید را در پاسخ به پیام اصلی ارسال میکنند (`reply_to_message_id` بهصورت داخلی تنظیم میشود).
---
---
## 📤 مثال کاربردی کامل
```python
from rubka import Robot
from rubka.keypad import ChatKeypadBuilder
from rubka.button import InlineBuilder
from rubka.context import Message
chat_keypad = ChatKeypadBuilder().row(
ChatKeypadBuilder().button(id="btn_female", text="زن"),
ChatKeypadBuilder().button(id="btn_male", text="مرد")
).build()
inline_keypad = (
InlineBuilder()
.row(
InlineBuilder().button_simple("btn_bets", "button1"),
InlineBuilder().button_simple("btn_rps", "button2")
)
.row(
InlineBuilder().button_simple("btn_chatid", "butthon3")
)
.build()
)
bot = Robot("توکن شما")
@bot.on_message()
def handler(bot: Robot, message: Message):
message.reply_image(
path="https://s6.uupload.ir/files/chatgpt_image_jul_20,_2025,_10_22_47_pm_oiql.png",
text="📷 عکس ریپلای شده دکمه شیشه ای",
inline_keypad=inline_keypad
)
message.reply_image(
path="https://s6.uupload.ir/files/chatgpt_image_jul_20,_2025,_10_22_47_pm_oiql.png",
text="📷 عکس ریپلای شده دکمه کیبوردی",
chat_keypad=chat_keypad,
chat_keypad_type="New"
)
@bot.on_callback()
def callback_handler(bot: Robot, message: Message):
data = message.aux_data.button_id
if data == "btn_male":
message.reply("سلام مرد")
elif data == "btn_female":
message.reply("سلام زن")
else:
message.reply(f"دکمه ناشناخته: {data}")
bot.run()
```
---
## 🧱 مستندات کلاس `InlineBuilder`
کلاس `InlineBuilder` برای ساخت دکمههای اینلاین استفاده میشود که در پیامهای ربات قابل استفاده هستند.
### ✅ روش استفاده
```python
from rubka.button import InlineBuilder
builder = InlineBuilder()
inline_keypad = builder.row(
builder.button_simple("btn_1", "دکمه ۱"),
builder.button_simple("btn_2", "دکمه ۲")
).build()
```
### 📚 دکمههای پشتیبانیشده
- `button_simple(id, text)` – دکمه ساده
- `button_payment(id, title, amount, description=None)` – پرداخت
- `button_calendar(id, title, type_, ...)` – انتخاب تاریخ
- `button_location(id, type_, image_url, ...)` – ارسال موقعیت مکانی
- `button_string_picker(...)` – انتخاب گزینه از لیست
- `button_number_picker(...)` – انتخاب عدد از بازه
- `button_textbox(...)` – فیلد ورود متنی
- `button_selection(...)` – انتخاب چندگزینهای پیشرفته
- `button_camera_image(...)`, `button_camera_video(...)`
- `button_gallery_image(...)`, `button_gallery_video(...)`
- `button_file(...)`, `button_audio(...)`, `button_record_audio(...)`
- `button_my_phone_number(...)`, `button_my_location(...)`
- `button_ask_my_phone_number(...)`, `button_ask_location(...)`
- `button_barcode(...)`
- `button_link(id, title, url)` – لینک خارجی
### 🧱 ساخت نهایی
```python
keypad = builder.build()
```
خروجی به صورت دیکشنری با کلید `rows` خواهد بود که میتوانید در متد `send_message` یا `reply_*` استفاده کنید.
---
## ⌨️ مستندات کلاس `ChatKeypadBuilder`
کلاس `ChatKeypadBuilder` برای ساخت صفحهکلید چتی (chat keypad) استفاده میشود.
### 🛠 روش استفاده
```python
from rubka.keypad import ChatKeypadBuilder
keypad = ChatKeypadBuilder().row(
ChatKeypadBuilder().button("btn_1", "دکمه ۱"),
ChatKeypadBuilder().button("btn_2", "دکمه ۲")
).build()
```
### 📋 متدها
- `button(id, text, type="Simple")` – ساخت یک دکمه ساده یا از نوع خاص
- `row(*buttons)` – افزودن یک ردیف به کیبورد (دکمهها باید با `button()` ساخته شوند)
- `build(resize_keyboard=True, on_time_keyboard=False)` – ساخت خروجی نهایی برای ارسال به کاربر
### 📦 خروجی `build()`
```json
{
"rows": [
{"buttons": [
{"id": "btn_1", "type": "Simple", "button_text": "دکمه ۱"},
{"id": "btn_2", "type": "Simple", "button_text": "دکمه ۲"}
]}
],
"resize_keyboard": true,
"on_time_keyboard": false
}
```
---
# مستندات پروژه: تایمر پیام در ربات Rubika
این پروژه یک ربات بر پایه کتابخانهی `rubka` است که به کاربر امکان میدهد با استفاده از کیپد، یک تایمر تنظیم کرده و پس از پایان تایمر، پیامی برای او ارسال شود. تمرکز اصلی این مستند، بر روی کلاس `Job` است که برای زمانبندی اجرای دستورات استفاده شده است.
## ساختار کلی پروژه
- استفاده از کتابخانه `rubka` برای ارتباط با Rubika Bot API
- تعریف یک کیپد با گزینههای تاخیر زمانی مختلف (۱۰ الی ۱۵۰ ثانیه)
- استفاده از کلاس `Job` برای مدیریت اجرای زمانبندیشده یک تابع
- نمایش شمارش معکوس با بهروزرسانی مداوم پیام
---
## کلاس `Job` چیست؟
کلاس `Job` در فایل `rubka.jobs` تعریف شده و هدف آن اجرای یک تابع خاص پس از گذشت یک بازه زمانی مشخص است.
### نحوه استفاده:
```python
from rubka.jobs import Job
job = Job(delay_in_seconds, callback_function)
```
### پارامترها:
| پارامتر | نوع | توضیح |
|--------|-----|-------|
| `delay_in_seconds` | `int` | مدت زمانی که باید قبل از اجرای تابع منتظر بماند |
| `callback_function` | `function` | تابعی که بعد از پایان زمان باید اجرا شود |
### ویژگیها:
- اجرای غیرهمزمان (با استفاده از Thread داخلی)
- مناسب برای سناریوهایی مانند تایمرها، یادآورها و اعلانهای زمانبندی شده
---
## مثال از استفاده در پروژه:
```python
def delayed_send():
if user_id not in active_jobs:
return
bot.send_message(
message.chat_id,
f"✅ کاربر {user_id} : زمان {seconds} ثانیه گذشت و دستور اجرا شد! ⏰"
)
active_jobs.pop(user_id, None)
job = Job(seconds, delayed_send)
active_jobs[user_id] = job
```
در این مثال، پس از انتخاب تاخیر زمانی توسط کاربر، یک شی از کلاس `Job` ساخته میشود که تابع `delayed_send` را پس از `seconds` ثانیه اجرا میکند.
---
## تابع `countdown_edit`
این تابع تایمر فعال را به صورت زنده باقیمانده زمان را بهروزرسانی میکند:
```python
def countdown_edit(chat_id, message_id, duration_sec):
# اجرای یک Thread برای بهروزرسانی پیام در هر ثانیه
```
---
## نمونه کد ساخته شده
```python
from rubka import Robot
from rubka.context import Message
from rubka.keypad import ChatKeypadBuilder
from rubka.jobs import Job
from datetime import datetime, timedelta
import threading
import time
bot = Robot("token")
active_jobs = {}
def build_delay_keypad():
delays = [10, 20, 30, 40, 50, 60, 75, 90, 120, 150]
builder = ChatKeypadBuilder()
buttons = []
for sec in delays:
buttons.append(builder.button(id=f"delay_{sec}", text=f"⏳ بعد از {sec} ثانیه"))
buttons.append(builder.button(id="cancel", text="❌ انصراف"))
rows = [buttons[i:i+3] for i in range(0, len(buttons), 3)]
keypad = ChatKeypadBuilder()
for row in rows:
keypad.row(*row)
return keypad.build()
def countdown_edit(chat_id: str, message_id: str, duration_sec: int):
start_time = datetime.now()
end_time = start_time + timedelta(seconds=duration_sec)
def run():
while True:
now = datetime.now()
if now >= end_time:
try:
bot.edit_message_text(chat_id, message_id, "⏰ زمان تمام شد!")
except Exception as e:
print("خطا در ویرایش پیام:", e)
break
remaining = end_time - now
text = (
f"⏳ تایمر فعال است...\n"
f"🕰 شروع: {start_time.strftime('%H:%M:%S')}\n"
f"⏲ پایان: {end_time.strftime('%H:%M:%S')}\n"
f"⌛ باقیمانده: {str(remaining).split('.')[0]}"
)
try:
bot.edit_message_text(chat_id, message_id, text)
except Exception as e:
print("خطا در ویرایش پیام:", e)
time.sleep(1)
threading.Thread(target=run, daemon=True).start()
@bot.on_message(commands=["start"])
def start_handler(bot: Robot, message: Message):
keypad = build_delay_keypad()
message.reply_keypad(
"سلام 👋\n"
"یک زمان برای ارسال پیام انتخاب کنید:\n"
"📅 تاریخ و ساعت فعلی: " + datetime.now().strftime("%Y/%m/%d %H:%M:%S"),
keypad
)
@bot.on_callback()
def callback_delay(bot: Robot, message: Message):
btn_id = message.aux_data.button_id
user_id = message.sender_id
if btn_id == "cancel":
if user_id in active_jobs:
active_jobs.pop(user_id)
message.reply("❌ همه ارسالهای زمانبندی شده لغو شدند.")
else:
message.reply("⚠️ شما هیچ ارسال زمانبندی شدهای ندارید.")
return
if not btn_id.startswith("delay_"):
message.reply("❌ دکمه نامعتبر است!")
return
seconds = int(btn_id.split("_")[1])
if user_id in active_jobs:
active_jobs.pop(user_id)
sent_msg = bot.send_message(
message.chat_id,
f"⏳ تایمر {seconds} ثانیهای شروع شد...\n🕰 زمان شروع: {datetime.now().strftime('%H:%M:%S')}"
)
countdown_edit(message.chat_id, sent_msg['data']['message_id'], seconds)
def delayed_send():
if user_id not in active_jobs:
return
bot.send_message(
message.chat_id,
f"✅ کاربر {user_id} : زمان {seconds} ثانیه گذشت و دستور اجرا شد! ⏰"
)
active_jobs.pop(user_id, None)
job = Job(seconds, delayed_send)
active_jobs[user_id] = job
message.reply(
f"⏳ ثبت شد! پیام شما پس از {seconds} ثانیه ارسال خواهد شد.\n"
f"🕰 زمان شروع ثبت شده: {datetime.now().strftime('%H:%M:%S')}"
)
bot.run()
```
##مثال ساده تر
```python
from rubka import Robot
from rubka.context import Message
from rubka.jobs import Job
from datetime import datetime
bot = Robot("")
active_jobs = {}
@bot.on_message(commands=["timer"])
def timer_handler(bot: Robot, message: Message):
user_id = message.sender_id
chat_id = message.chat_id
parts = message.text.split()
if len(parts) != 2 or not parts[1].isdigit():
return message.reply("⚠️ لطفاً مدت زمان را به صورت صحیح وارد کنید. مثل: `/timer 30`", parse_mode="markdown")
seconds = int(parts[1])
if user_id in active_jobs:
active_jobs.pop(user_id)
message.reply(f"⏳ تایمر {seconds} ثانیهای شروع شد!\n🕰 زمان شروع: {datetime.now().strftime('%H:%M:%S')}")
def after_delay():
if user_id not in active_jobs:
return
bot.send_message(chat_id, f"✅ تایمر {seconds} ثانیهای تمام شد! ⏰")
active_jobs.pop(user_id, None)
job = Job(seconds, after_delay)
active_jobs[user_id] = job
bot.run()
```
##نمونه کد ادیت تایم و کرون جاب با اینلاین کیبورد
```python
from rubka import Robot
from rubka.context import Message
from rubka.keypad import ChatKeypadBuilder
from rubka.jobs import Job
from datetime import datetime, timedelta
import threading
import time
bot = Robot("token")
bot.edit_inline_keypad
active_jobs = {}
def build_delay_keypad():
delays = [10, 20, 30, 40, 50, 60, 75, 90, 120, 150]
builder = ChatKeypadBuilder()
buttons = []
for sec in delays:
buttons.append(builder.button(id=f"delay_{sec}", text=f"⏳ بعد از {sec} ثانیه"))
buttons.append(builder.button(id="cancel", text="❌ انصراف"))
rows = [buttons[i:i+3] for i in range(0, len(buttons), 3)]
keypad = ChatKeypadBuilder()
for row in rows:
keypad.row(*row)
return keypad.build()
def countdown_edit(chat_id: str, message_id: str, duration_sec: int):
start_time = datetime.now()
end_time = start_time + timedelta(seconds=duration_sec)
def run():
while True:
now = datetime.now()
if now >= end_time:
try:
bot.edit_message_text(chat_id, message_id, "⏰ زمان تمام شد!")
except Exception as e:
print("خطا در ویرایش پیام:", e)
break
remaining = end_time - now
text = (
f"⏳ تایمر فعال است...\n"
f"🕰 شروع: {start_time.strftime('%H:%M:%S')}\n"
f"⏲ پایان: {end_time.strftime('%H:%M:%S')}\n"
f"⌛ باقیمانده: {str(remaining).split('.')[0]}"
)
try:
bot.edit_message_text(chat_id, message_id, text)
except Exception as e:
print("خطا در ویرایش پیام:", e)
time.sleep(1)
threading.Thread(target=run, daemon=True).start()
@bot.on_message(commands=["start"])
def start_handler(bot: Robot, message: Message):
keypad = build_delay_keypad()
message.reply_keypad(
"سلام 👋\n"
"یک زمان برای ارسال پیام انتخاب کنید:\n"
"📅 تاریخ و ساعت فعلی: " + datetime.now().strftime("%Y/%m/%d %H:%M:%S"),
keypad
)
@bot.on_callback()
def callback_delay(bot: Robot, message: Message):
btn_id = message.aux_data.button_id
user_id = message.sender_id
if btn_id == "cancel":
if user_id in active_jobs:
active_jobs.pop(user_id)
message.reply("❌ همه ارسالهای زمانبندی شده لغو شدند.")
else:
message.reply("⚠️ شما هیچ ارسال زمانبندی شدهای ندارید.")
return
if not btn_id.startswith("delay_"):
message.reply("❌ دکمه نامعتبر است!")
return
seconds = int(btn_id.split("_")[1])
if user_id in active_jobs:
active_jobs.pop(user_id)
sent_msg = bot.edit_inline_keypad(
message.chat_id,
f"⏳ تایمر {seconds} ثانیهای شروع شد...\n🕰 زمان شروع: {datetime.now().strftime('%H:%M:%S')}"
)
print(sent_msg)
countdown_edit(message.chat_id, sent_msg['data']['message_id'], seconds)
def delayed_send():
if user_id not in active_jobs:
return
bot.send_message(
message.chat_id,
f"✅ کاربر {user_id} : زمان {seconds} ثانیه گذشت و دستور اجرا شد! ⏰"
)
active_jobs.pop(user_id, None)
job = Job(seconds, delayed_send)
active_jobs[user_id] = job
message.reply(
f"⏳ ثبت شد! پیام شما پس از {seconds} ثانیه ارسال خواهد شد.\n"
f"🕰 زمان شروع ثبت شده: {datetime.now().strftime('%H:%M:%S')}"
)
bot.run()
```
# ✅ Force Join (اجبار به عضویت در کانال) — Rubka Bot
این مستند نحوه استفاده از قابلیت **اجبار به عضویت در یک کانال (Force Join)** در رباتهای ساختهشده با کتابخانه Rubka را توضیح میدهد.
---
## 🎯 هدف
اطمینان از اینکه کاربر عضو یک کانال خاص است، قبل از ادامه تعامل با ربات. اگر عضو نبود، به او اطلاع داده شود یا لینک عضویت ارسال گردد.
---
## 📦 پیشنیازها
- نصب و راهاندازی کتابخانه `rubka`
- توکن معتبر ربات Rubika
- دسترسی به `channel_guid` (شناسه عددی کانال)
- ربات باید در کانال، **ادمین** باشد
---
## 💡 نحوه استفاده
### کد بهینهشده:
```python
from rubka import Robot
from rubka.context import Message
bot = Robot(token="your_token")
CHANNEL_GUID = "c0xABCDEF..." # GUID کانال هدف
@bot.on_message()
def handle_force_join(bot: Robot, message: Message):
name = bot.get_name(message.chat_id)
if bot.check_join(CHANNEL_GUID, message.chat_id):
message.reply(f"سلام {name} 👋\nشما عضو کانال هستید ✅")
else:
join_link = "https://rubika.ir/rubka_library"
message.reply(
f"سلام {name} 👋\nشما عضو کانال نیستید ❌\n\n"
f"لطفاً ابتدا عضو کانال شوید سپس دوباره تلاش کنید:\n{join_link}"
)
bot.run()
```
---
## 🔍 شرح متدها
| متد | کاربرد |
| ------------------------------------- | ------------------------------------ |
| `check_join(channel_guid, user_guid)` | بررسی عضویت کاربر در کانال مشخصشده |
| `get_name(user_guid)` | دریافت نام نمایشی کاربر از طریق GUID |
| `message.reply(text)` | پاسخ مستقیم به پیام دریافتشده |
---
## 🔐 نکات مهم امنیتی
- ربات باید حتماً **ادمین کانال** باشد.
- در صورت عدم عضویت، بهتر است لینک دعوت به کانال نمایش داده شود.
##Mahdi Ahmadi
Raw data
{
"_id": null,
"home_page": "https://pypi.org/project/robka/",
"name": "robka",
"maintainer": "httex",
"docs_url": null,
"requires_python": ">=3.6",
"maintainer_email": null,
"keywords": null,
"author": "httex",
"author_email": null,
"download_url": "https://files.pythonhosted.org/packages/1f/89/18d0c2be36e17cc7e15fd0c6ce0e49b2d67285ddc0a72bf5ff66b5ce41cc/robka-2.4.2.tar.gz",
"platform": null,
"description": "# \ud83d\udcda Rubka Bot Python Library Documentation\n# \u0646\u0645\u0648\u0646\u0647 \u062a\u0646\u0638\u06cc\u0645 \u0648\u0628\u200c\u0647\u0648\u06a9 (Webhook) \u062f\u0631 \u06a9\u062a\u0627\u0628\u062e\u0648\u0646\u0647 rubla\n\n\u0628\u0631\u0627\u06cc \u0645\u0634\u0627\u0647\u062f\u0647 \u0645\u0633\u062a\u0646\u062f\u0627\u062a \u06a9\u0627\u0645\u0644 \u0648 \u0622\u062e\u0631\u06cc\u0646 \u0646\u0633\u062e\u0647 \u0631\u0627\u0647\u0646\u0645\u0627\u060c \u0644\u0637\u0641\u0627\u064b \u0628\u0647 \u0622\u062f\u0631\u0633 \u0632\u06cc\u0631 \u0645\u0631\u0627\u062c\u0639\u0647 \u06a9\u0646\u06cc\u062f: \n[github.com/Mahdy-Ahmadi](https://github.com/Mahdy-Ahmadi/rubka/blob/main/webhook.md))\n\n\n## \ud83e\udde0 Introduction\n`rubka` is a Python library to interact with the [Rubika Bot API](https://rubika.ir/). This library helps you create Telegram-like bots with support for messages, inline buttons, chat keypads, and callback handling.\n\n---\n\n## \u2699\ufe0f Installation\n\n```bash\npip install rubka\n```\n\nIf `importlib.metadata` is not available, it installs `importlib-metadata` automatically.\n\n---\n\n## \ud83d\ude80 Getting Started\n\n```python\nfrom rubka import Robot\nfrom rubka.context import Message\n\nbot = Robot(token=\"YOUR_TOKEN_HERE\")\n\n@bot.on_message(commands=[\"start\"])\ndef start(bot: Robot, message: Message):\n message.reply(\"\u0633\u0644\u0627\u0645! \u062e\u0648\u0634 \u0622\u0645\u062f\u06cc\u062f!\")\n\nbot.run()\n```\n\n---\n\n## \ud83d\udcec Handling Messages\n\nYou can handle incoming text messages using `@bot.on_message()`:\n\n```python\n@bot.on_message(commands=[\"hello\"])\ndef greet(bot: Robot, message: Message):\n message.reply(\"\u0633\u0644\u0627\u0645 \u06a9\u0627\u0631\u0628\u0631 \u0639\u0632\u06cc\u0632 \ud83d\udc4b\")\n```\n\nYou can also add filters.\n\n---\n\n## \ud83c\udfae Handling Callback Buttons\n\n```python\nfrom rubka.keypad import ChatKeypadBuilder\n\n@bot.on_message(commands=[\"gender\"])\ndef gender(bot: Robot, message: Message):\n keypad = ChatKeypadBuilder().row(\n ChatKeypadBuilder().button(id=\"male\", text=\"\ud83d\udc68 \u0645\u0631\u062f\"),\n ChatKeypadBuilder().button(id=\"female\", text=\"\ud83d\udc69 \u0632\u0646\")\n ).build()\n message.reply_keypad(\"\u062c\u0646\u0633\u06cc\u062a \u062e\u0648\u062f \u0631\u0627 \u0627\u0646\u062a\u062e\u0627\u0628 \u06a9\u0646\u06cc\u062f:\", keypad)\n\n@bot.on_callback(\"male\")\ndef on_male(bot: Robot, message: Message):\n message.reply(\"\u0634\u0645\u0627 \u0645\u0631\u062f \u0647\u0633\u062a\u06cc\u062f\")\n\n@bot.on_callback(\"female\")\ndef on_female(bot: Robot, message: Message):\n message.reply(\"\u0634\u0645\u0627 \u0632\u0646 \u0647\u0633\u062a\u06cc\u062f\")\n```\n\n---\n\n## \ud83d\udd18 Inline Button Builder\n\n```python\nfrom rubka.button import InlineBuilder\n\nbuilder = InlineBuilder().row(\n InlineBuilder().button_simple(id=\"info\", text=\"\u0627\u0637\u0644\u0627\u0639\u0627\u062a\")\n).build()\n```\n\n---\n\n## \ud83d\udd04 Check if User Joined a Channel\n\n```python\nchannel_guid = \"c0xABCDEF...\"\n\n@bot.on_message(commands=[\"check\"])\ndef check(bot: Robot, message: Message):\n if bot.check_join(channel_guid, message.chat_id):\n message.reply(\"\u2705 \u0634\u0645\u0627 \u0639\u0636\u0648 \u06a9\u0627\u0646\u0627\u0644 \u0647\u0633\u062a\u06cc\u062f\")\n else:\n message.reply(\"\u274c \u0644\u0637\u0641\u0627\u064b \u0627\u0628\u062a\u062f\u0627 \u062f\u0631 \u06a9\u0627\u0646\u0627\u0644 \u0639\u0636\u0648 \u0634\u0648\u06cc\u062f\")\n```\n\n---\n\n## \ud83d\udcac Utility Methods\n\n| Method | Description |\n|--------|-------------|\n| `get_chat(chat_id)` | \u062f\u0631\u06cc\u0627\u0641\u062a \u0627\u0637\u0644\u0627\u0639\u0627\u062a \u0686\u062a |\n| `get_name(chat_id)` | \u062f\u0631\u06cc\u0627\u0641\u062a \u0646\u0627\u0645 \u06a9\u0627\u0631\u0628\u0631 |\n| `get_username(chat_id)` | \u062f\u0631\u06cc\u0627\u0641\u062a \u0646\u0627\u0645\u200c\u06a9\u0627\u0631\u0628\u0631\u06cc |\n| `send_message(...)` | \u0627\u0631\u0633\u0627\u0644 \u067e\u06cc\u0627\u0645 \u0645\u062a\u0646\u06cc |\n| `edit_message_text(...)` | \u0648\u06cc\u0631\u0627\u06cc\u0634 \u067e\u06cc\u0627\u0645 |\n| `delete_message(...)` | \u062d\u0630\u0641 \u067e\u06cc\u0627\u0645 |\n| `send_location(...)` | \u0627\u0631\u0633\u0627\u0644 \u0645\u0648\u0642\u0639\u06cc\u062a \u0645\u06a9\u0627\u0646\u06cc |\n| `send_poll(...)` | \u0627\u0631\u0633\u0627\u0644 \u0646\u0638\u0631\u0633\u0646\u062c\u06cc |\n| `send_contact(...)` | \u0627\u0631\u0633\u0627\u0644 \u0645\u062e\u0627\u0637\u0628 |\n| `forward_message(...)` | \u0641\u0648\u0631\u0648\u0627\u0631\u062f \u067e\u06cc\u0627\u0645 |\n\n---\n\n## \ud83c\udf9b Inline Query Support\n\n```python\n@bot.on_inline_query()\ndef inline(bot: Robot, message: InlineMessage):\n message.answer(\"\u0646\u062a\u06cc\u062c\u0647 \u0627\u06cc\u0646\u0644\u0627\u06cc\u0646\")\n```\n\n---\n\n## \ud83e\uddf1 Button Types\n\nSupported inline button types include:\n\n- `Simple`\n- `Payment`\n- `Calendar`\n- `Location`\n- `CameraImage`, `CameraVideo`\n- `GalleryImage`, `GalleryVideo`\n- `File`, `Audio`, `RecordAudio`\n- `MyPhoneNumber`, `MyLocation`\n- `Textbox`, `Barcode`, `Link`\n\nSee `InlineBuilder` for more.\n\n---\n\n## \ud83e\udde9 Dynamic Chat Keypad\n\n```python\nbuilder = ChatKeypadBuilder()\nkeypad = builder.row(\n builder.button(id=\"play\", text=\"\ud83c\udfae \u0628\u0627\u0632\u06cc \u06a9\u0646\"),\n builder.button(id=\"exit\", text=\"\u274c \u062e\u0631\u0648\u062c\")\n).build()\n```\n\n---\n\n## \ud83e\uddea Set Commands\n\n```python\nbot.set_commands([\n {\"command\": \"start\", \"description\": \"\u0634\u0631\u0648\u0639\"},\n {\"command\": \"help\", \"description\": \"\u0631\u0627\u0647\u0646\u0645\u0627\"}\n])\n```\n\n---\n\n## \ud83d\udd04 Update Offset Automatically\n\nBot updates are handled using `get_updates()` and `offset_id` is managed internally.\n\n---\n\n## \ud83d\udee0 Advanced Features\n\n- `update_bot_endpoint()` \u2013 \u062a\u0646\u0638\u06cc\u0645 webhook \u06cc\u0627 polling\n- `remove_keypad()` \u2013 \u062d\u0630\u0641 \u0635\u0641\u062d\u0647\u200c\u06a9\u0644\u06cc\u062f \u0686\u062a\n- `edit_chat_keypad()` \u2013 \u0648\u06cc\u0631\u0627\u06cc\u0634 \u06cc\u0627 \u0627\u0641\u0632\u0648\u062f\u0646 \u0635\u0641\u062d\u0647\u200c\u06a9\u0644\u06cc\u062f \u0686\u062a\n\n---\n\n# \ud83d\udcd8 Rubka Bot Method Reference\n\n\u0645\u0633\u062a\u0646\u062f\u0627\u062a \u0645\u0631\u0628\u0648\u0637 \u0628\u0647 \u0645\u062a\u062f\u0647\u0627\u06cc \u0627\u0635\u0644\u06cc \u06a9\u0644\u0627\u0633 `Robot` \u062f\u0631 \u06a9\u062a\u0627\u0628\u062e\u0627\u0646\u0647 Rubka.\n\n---\n\n## \u2705 \u067e\u06cc\u0627\u0645\u200c\u0647\u0627 \u0648 \u0647\u0646\u062f\u0644\u0631\u0647\u0627\n\n### `on_message(filters=None, commands=None)`\n**\u062a\u0648\u0636\u06cc\u062d:** \u062b\u0628\u062a \u0647\u0646\u062f\u0644\u0631 \u0628\u0631\u0627\u06cc \u067e\u06cc\u0627\u0645\u200c\u0647\u0627\u06cc \u0648\u0631\u0648\u062f\u06cc.\n- `filters`: \u062a\u0627\u0628\u0639 \u0634\u0631\u0637\u06cc \u0628\u0631\u0627\u06cc \u0641\u06cc\u0644\u062a\u0631 \u067e\u06cc\u0627\u0645\u200c\u0647\u0627 (\u0627\u062e\u062a\u06cc\u0627\u0631\u06cc)\n- `commands`: \u0644\u06cc\u0633\u062a \u062f\u0633\u062a\u0648\u0631\u0647\u0627\u06cc\u06cc \u06a9\u0647 \u0634\u0631\u0648\u0639 \u0628\u0627 `/` \u0647\u0633\u062a\u0646\u062f (\u0627\u062e\u062a\u06cc\u0627\u0631\u06cc)\n\n### `on_callback(button_id=None)`\n**\u062a\u0648\u0636\u06cc\u062d:** \u062b\u0628\u062a \u0647\u0646\u062f\u0644\u0631 \u0628\u0631\u0627\u06cc \u062f\u06a9\u0645\u0647\u200c\u0647\u0627\u06cc \u0641\u0634\u0631\u062f\u0647\u200c\u0634\u062f\u0647\n- `button_id`: \u0622\u06cc\u062f\u06cc \u062f\u06a9\u0645\u0647\u200c\u0627\u06cc \u06a9\u0647 \u0628\u0627\u06cc\u062f \u0647\u0646\u062f\u0644 \u0634\u0648\u062f (\u0627\u062e\u062a\u06cc\u0627\u0631\u06cc)\n\n### `on_inline_query()`\n**\u062a\u0648\u0636\u06cc\u062d:** \u062b\u0628\u062a \u0647\u0646\u062f\u0644\u0631 \u0628\u0631\u0627\u06cc \u067e\u06cc\u0627\u0645\u200c\u0647\u0627\u06cc \u0627\u06cc\u0646\u0644\u0627\u06cc\u0646 (inline query)\n\n---\n\n## \ud83d\udce8 \u0627\u0631\u0633\u0627\u0644 \u067e\u06cc\u0627\u0645\n\n### `send_message(...)`\n**\u062a\u0648\u0636\u06cc\u062d:** \u0627\u0631\u0633\u0627\u0644 \u067e\u06cc\u0627\u0645 \u0645\u062a\u0646\u06cc \u0628\u0647 \u0686\u062a\n- `chat_id`: \u0622\u06cc\u062f\u06cc \u0686\u062a \u0645\u0642\u0635\u062f *(str)* \u2705\n- `text`: \u0645\u062d\u062a\u0648\u0627\u06cc \u067e\u06cc\u0627\u0645 *(str)* \u2705\n- `chat_keypad`: \u06a9\u06cc\u200c\u067e\u062f \u0645\u0639\u0645\u0648\u0644\u06cc *(dict)*\n- `inline_keypad`: \u06a9\u06cc\u200c\u067e\u062f \u0627\u06cc\u0646\u0644\u0627\u06cc\u0646 *(dict)*\n- `reply_to_message_id`: \u067e\u0627\u0633\u062e \u0628\u0647 \u067e\u06cc\u0627\u0645 \u062e\u0627\u0635 *(str)*\n- `disable_notification`: \u0628\u062f\u0648\u0646 \u0646\u0648\u062a\u06cc\u0641 *(bool)*\n- `chat_keypad_type`: \u062d\u0627\u0644\u062a \u06a9\u06cc\u200c\u067e\u062f *(\"New\" | \"Removed\")*\n\n---\n\n## \ud83d\udcc1 \u0627\u0631\u0633\u0627\u0644 \u0641\u0627\u06cc\u0644\u200c\u0647\u0627\n\n### \u0645\u062a\u062f\u0647\u0627\u06cc \u0645\u0634\u062a\u0631\u06a9 (\u0641\u0627\u06cc\u0644\u060c \u0645\u0648\u0632\u06cc\u06a9\u060c \u0648\u06cc\u0633\u060c \u06af\u06cc\u0641\u060c \u0639\u06a9\u0633):\n- `send_document(...)`\n- `send_music(...)`\n- `send_voice(...)`\n- `send_gif(...)`\n- `send_image(...)`\n\n**\u067e\u0627\u0631\u0627\u0645\u062a\u0631\u0647\u0627\u06cc \u0627\u0635\u0644\u06cc:**\n- `chat_id`: \u0622\u06cc\u062f\u06cc \u0686\u062a\n- `path`: \u0645\u0633\u06cc\u0631 \u0641\u0627\u06cc\u0644 \u06cc\u0627 URL (\u0627\u062e\u062a\u06cc\u0627\u0631\u06cc)\n- `file_id`: \u0627\u06af\u0631 \u0641\u0627\u06cc\u0644 \u0642\u0628\u0644\u0627\u064b \u0622\u067e\u0644\u0648\u062f \u0634\u062f\u0647 \u0628\u0627\u0634\u062f\n- `text`: \u06a9\u067e\u0634\u0646 \u0641\u0627\u06cc\u0644\n- `file_name`: \u0646\u0627\u0645 \u0641\u0627\u06cc\u0644\n- `inline_keypad`, `chat_keypad`, `reply_to_message_id`, `disable_notification`, `chat_keypad_type`\n\n---\n\n## \ud83d\udccd \u0633\u0627\u06cc\u0631 \u0645\u062a\u062f\u0647\u0627\u06cc \u0645\u0647\u0645\n\n### `get_me()`\n\u062f\u0631\u06cc\u0627\u0641\u062a \u0627\u0637\u0644\u0627\u0639\u0627\u062a \u0631\u0628\u0627\u062a\n\n### `get_chat(chat_id)`\n\u062f\u0631\u06cc\u0627\u0641\u062a \u0627\u0637\u0644\u0627\u0639\u0627\u062a \u06cc\u06a9 \u0686\u062a\n\n### `get_name(chat_id)`\n\u062f\u0631\u06cc\u0627\u0641\u062a \u0646\u0627\u0645 \u0645\u062e\u0627\u0637\u0628 \u0628\u0631 \u0627\u0633\u0627\u0633 `first_name` \u0648 `last_name`\n\n### `get_username(chat_id)`\n\u062f\u0631\u06cc\u0627\u0641\u062a \u0646\u0627\u0645 \u06a9\u0627\u0631\u0628\u0631\u06cc \u0686\u062a (\u062f\u0631 \u0635\u0648\u0631\u062a \u0648\u062c\u0648\u062f)\n\n### `check_join(channel_guid, chat_id)`\n\u0628\u0631\u0631\u0633\u06cc \u0639\u0636\u0648\u06cc\u062a \u06a9\u0627\u0631\u0628\u0631 \u062f\u0631 \u06a9\u0627\u0646\u0627\u0644 \u062e\u0627\u0635\n\n### `remove_keypad(chat_id)`\n\u062d\u0630\u0641 \u06a9\u06cc\u200c\u067e\u062f \u0645\u0639\u0645\u0648\u0644\u06cc \u0686\u062a\n\n### `edit_chat_keypad(chat_id, chat_keypad)`\n\u0648\u06cc\u0631\u0627\u06cc\u0634 \u06cc\u0627 \u0627\u0636\u0627\u0641\u0647 \u06a9\u0631\u062f\u0646 \u06a9\u06cc\u200c\u067e\u062f \u0686\u062a\n\n### `edit_message_text(chat_id, message_id, text)`\n\u0648\u06cc\u0631\u0627\u06cc\u0634 \u0645\u062a\u0646 \u067e\u06cc\u0627\u0645 \u0627\u0631\u0633\u0627\u0644\u200c\u0634\u062f\u0647\n\n### `edit_inline_keypad(chat_id, message_id, inline_keypad)`\n\u0648\u06cc\u0631\u0627\u06cc\u0634 \u06a9\u06cc\u200c\u067e\u062f \u0627\u06cc\u0646\u0644\u0627\u06cc\u0646 \u067e\u06cc\u0627\u0645\n\n### `delete_message(chat_id, message_id)`\n\u062d\u0630\u0641 \u067e\u06cc\u0627\u0645 \u0627\u0632 \u0686\u062a\n\n### `send_poll(chat_id, question, options)`\n\u0627\u0631\u0633\u0627\u0644 \u0646\u0638\u0631\u0633\u0646\u062c\u06cc \u0628\u0647 \u0686\u062a\n\n### `send_location(chat_id, latitude, longitude, ...)`\n\u0627\u0631\u0633\u0627\u0644 \u0645\u0648\u0642\u0639\u06cc\u062a \u0645\u06a9\u0627\u0646\u06cc \u0628\u0647 \u0686\u062a\n\n### `send_contact(chat_id, first_name, last_name, phone_number)`\n\u0627\u0631\u0633\u0627\u0644 \u0645\u062e\u0627\u0637\u0628 \u0628\u0647 \u0686\u062a\n\n### `forward_message(from_chat_id, message_id, to_chat_id)`\n\u0641\u0631\u0648\u0627\u0631\u062f \u06a9\u0631\u062f\u0646 \u067e\u06cc\u0627\u0645 \u0627\u0632 \u06cc\u06a9 \u0686\u062a \u0628\u0647 \u0686\u062a \u062f\u06cc\u06af\u0631\n\n### `set_commands(bot_commands)`\n\u062a\u0646\u0638\u06cc\u0645 \u062f\u0633\u062a\u0648\u0631\u0627\u062a \u0631\u0633\u0645\u06cc \u0631\u0628\u0627\u062a (\u0628\u0631\u0627\u06cc `/help` \u0648 ...)\n\n### `update_bot_endpoint(url, type)`\n\u062a\u0646\u0638\u06cc\u0645 \u0648\u0628\u200c\u0647\u0648\u06a9 \u06cc\u0627 polling \u0628\u0631\u0627\u06cc \u062f\u0631\u06cc\u0627\u0641\u062a \u067e\u06cc\u0627\u0645\u200c\u0647\u0627\n\n---\n\n## \ud83d\udce6 \u0645\u062f\u06cc\u0631\u06cc\u062a \u0641\u0627\u06cc\u0644 \u0648 \u0622\u067e\u0644\u0648\u062f\n\n### `get_upload_url(media_type)`\n\u062f\u0631\u06cc\u0627\u0641\u062a \u0622\u062f\u0631\u0633 \u0622\u067e\u0644\u0648\u062f \u0641\u0627\u06cc\u0644 \u0628\u0631\u0627\u06cc \u0627\u0646\u0648\u0627\u0639 \u0645\u062e\u062a\u0644\u0641: File, Image, Voice, Music, Gif\n\n### `upload_media_file(upload_url, name, path)`\n\u0622\u067e\u0644\u0648\u062f \u0641\u0627\u06cc\u0644 \u0627\u0632 \u0645\u0633\u06cc\u0631 \u0645\u062d\u0644\u06cc \u06cc\u0627 URL \u0628\u0647 Rubika \u0648 \u062f\u0631\u06cc\u0627\u0641\u062a `file_id`\n\n---\n\n## \ud83d\udd04 \u062f\u0631\u06cc\u0627\u0641\u062a \u0628\u0631\u0648\u0632\u0631\u0633\u0627\u0646\u06cc\u200c\u0647\u0627\n\n### `get_updates(offset_id=None, limit=None)`\n\u062f\u0631\u06cc\u0627\u0641\u062a \u0628\u0631\u0648\u0632\u0631\u0633\u0627\u0646\u06cc\u200c\u0647\u0627 (\u0628\u0631\u0627\u06cc polling)\n\n\n---\n\n# \ud83d\udce6 Rubka `Message` Class & Media Reply API Documentation\n\n## \ud83e\uddfe \u0645\u0639\u0631\u0641\u06cc \u06a9\u0644\u0627\u0633 `Message`\n\n\u06a9\u0644\u0627\u0633 `Message` \u062f\u0631 \u06a9\u062a\u0627\u0628\u062e\u0627\u0646\u0647 Rubka \u0627\u0628\u0632\u0627\u0631\u06cc \u06a9\u0644\u06cc\u062f\u06cc \u0628\u0631\u0627\u06cc \u0645\u062f\u06cc\u0631\u06cc\u062a \u067e\u06cc\u0627\u0645\u200c\u0647\u0627\u06cc \u062f\u0631\u06cc\u0627\u0641\u062a\u06cc \u062f\u0631 \u0631\u0628\u0627\u062a \u0627\u0633\u062a. \u0627\u06cc\u0646 \u06a9\u0644\u0627\u0633\u060c \u0642\u0627\u0628\u0644\u06cc\u062a\u200c\u0647\u0627\u06cc\u06cc \u0647\u0645\u0686\u0648\u0646 \u067e\u0627\u0633\u062e \u0628\u0647 \u067e\u06cc\u0627\u0645\u060c \u0627\u0631\u0633\u0627\u0644 \u0645\u062f\u06cc\u0627\u060c \u062d\u0630\u0641 \u06cc\u0627 \u0648\u06cc\u0631\u0627\u06cc\u0634 \u067e\u06cc\u0627\u0645\u060c \u0648 \u0627\u0633\u062a\u0641\u0627\u062f\u0647 \u0627\u0632 \u0635\u0641\u062d\u0647\u200c\u06a9\u0644\u06cc\u062f \u0648 \u062f\u06a9\u0645\u0647\u200c\u0647\u0627\u06cc \u0627\u06cc\u0646\u0644\u0627\u06cc\u0646 \u0631\u0627 \u0641\u0631\u0627\u0647\u0645 \u0645\u06cc\u200c\u06a9\u0646\u062f.\n\n---\n\n## \u2699\ufe0f \u0645\u0634\u062e\u0635\u0627\u062a \u06a9\u0644\u0627\u0633 `Message`\n\n```python\nMessage(bot, chat_id, message_id, sender_id, text=None, raw_data=None)\n```\n\n### \u067e\u0627\u0631\u0627\u0645\u062a\u0631\u0647\u0627:\n\n| \u067e\u0627\u0631\u0627\u0645\u062a\u0631 | \u062a\u0648\u0636\u06cc\u062d |\n| ------------ | ---------------------------------------- |\n| `bot` | \u0646\u0645\u0648\u0646\u0647\u200c\u06cc \u0634\u06cc \u0631\u0628\u0627\u062a |\n| `chat_id` | \u0634\u0646\u0627\u0633\u0647 \u0686\u062a |\n| `message_id` | \u0622\u06cc\u062f\u06cc \u067e\u06cc\u0627\u0645 |\n| `sender_id` | \u0634\u0646\u0627\u0633\u0647 \u0641\u0631\u0633\u062a\u0646\u062f\u0647 |\n| `text` | \u0645\u062a\u0646 \u067e\u06cc\u0627\u0645 |\n| `raw_data` | \u062f\u0627\u062f\u0647\u200c\u06cc \u062e\u0627\u0645 \u067e\u06cc\u0627\u0645 (\u062f\u06cc\u06a9\u0634\u0646\u0631\u06cc \u062f\u0631\u06cc\u0627\u0641\u062a\u06cc \u0627\u0632 API) |\n\n### \u0648\u06cc\u0698\u06af\u06cc\u200c\u0647\u0627 (Attributes):\n\n- `reply_to_message_id` \u2013 \u0627\u06af\u0631 \u067e\u06cc\u0627\u0645 \u062f\u0631 \u067e\u0627\u0633\u062e \u0627\u0631\u0633\u0627\u0644 \u0634\u062f\u0647 \u0628\u0627\u0634\u062f\u060c \u0622\u06cc\u062f\u06cc \u067e\u06cc\u0627\u0645 \u0627\u0648\u0644\u06cc\u0647\n- `file`, `sticker`, `poll`, `contact_message`, `location`, ... \u2013 \u062f\u0627\u062f\u0647\u200c\u0647\u0627\u06cc \u0645\u0631\u0628\u0648\u0637\u0647 \u0627\u06af\u0631 \u0648\u062c\u0648\u062f \u062f\u0627\u0634\u062a\u0647 \u0628\u0627\u0634\u0646\u062f\n\n---\n\n## \ud83d\udce9 \u0645\u062a\u062f\u0647\u0627\u06cc \u067e\u0627\u0633\u062e\u200c\u062f\u0647\u06cc\n\n### \u2709\ufe0f `reply(text: str, **kwargs)`\n\n\u067e\u0627\u0633\u062e \u0645\u062a\u0646\u06cc \u0628\u0647 \u067e\u06cc\u0627\u0645 \u0628\u0627 \u0642\u0627\u0628\u0644\u06cc\u062a \u0627\u0631\u0633\u0627\u0644 \u062f\u06a9\u0645\u0647 \u0648 \u06af\u0632\u06cc\u0646\u0647\u200c\u0647\u0627\u06cc \u0627\u0636\u0627\u0641\u06cc.\n\n### \ud83d\udcca `reply_poll(question, options, **kwargs)`\n\n\u0627\u0631\u0633\u0627\u0644 \u0646\u0638\u0631\u0633\u0646\u062c\u06cc \u062f\u0631 \u067e\u0627\u0633\u062e \u0628\u0647 \u067e\u06cc\u0627\u0645.\n\n### \ud83d\udcce `reply_document(...)`\n\n\u0627\u0631\u0633\u0627\u0644 \u0641\u0627\u06cc\u0644 \u06cc\u0627 \u0633\u0646\u062f \u0628\u0627 \u0645\u062a\u0646 \u0627\u062e\u062a\u06cc\u0627\u0631\u06cc \u0648 \u062f\u06a9\u0645\u0647.\n\n### \ud83d\uddbc `reply_image(...)`\n\n\u0627\u0631\u0633\u0627\u0644 \u062a\u0635\u0648\u06cc\u0631 \u0628\u0627 \u0642\u0627\u0628\u0644\u06cc\u062a reply \u0647\u0645\u0631\u0627\u0647 \u062f\u06a9\u0645\u0647\u200c\u0647\u0627\u06cc chat \u06cc\u0627 inline.\n\n### \ud83c\udfb5 `reply_music(...)`\n\n\u0627\u0631\u0633\u0627\u0644 \u0645\u0648\u0632\u06cc\u06a9 \u062f\u0631 \u067e\u0627\u0633\u062e.\n\n### \ud83c\udfa4 `reply_voice(...)`\n\n\u0627\u0631\u0633\u0627\u0644 \u067e\u06cc\u0627\u0645 \u0635\u0648\u062a\u06cc (voice).\n\n### \ud83c\udf9e `reply_gif(...)`\n\n\u0627\u0631\u0633\u0627\u0644 \u06af\u06cc\u0641 \u062f\u0631 \u067e\u0627\u0633\u062e \u0628\u0647 \u067e\u06cc\u0627\u0645.\n\n### \ud83d\uddfa `reply_location(latitude, longitude, **kwargs)`\n\n\u0627\u0631\u0633\u0627\u0644 \u0644\u0648\u06a9\u06cc\u0634\u0646 \u062f\u0631 \u067e\u0627\u0633\u062e.\n\n### \ud83d\udcc7 `reply_contact(first_name, last_name, phone_number, **kwargs)`\n\n\u0627\u0631\u0633\u0627\u0644 \u0645\u062e\u0627\u0637\u0628 \u062f\u0631 \u067e\u0627\u0633\u062e.\n\n---\n\n## \ud83d\udd18 \u067e\u0627\u0633\u062e \u0628\u0627 \u062f\u06a9\u0645\u0647\u200c\u0647\u0627\n\n### `reply_keypad(text, keypad, **kwargs)`\n\n\u0627\u0631\u0633\u0627\u0644 \u067e\u06cc\u0627\u0645 \u0628\u0627 \u0635\u0641\u062d\u0647\u200c\u06a9\u0644\u06cc\u062f \u0686\u062a\u06cc (ChatKeypad).\n\n### `reply_inline(text, inline_keypad, **kwargs)`\n\n\u0627\u0631\u0633\u0627\u0644 \u067e\u06cc\u0627\u0645 \u0628\u0627 \u062f\u06a9\u0645\u0647\u200c\u0647\u0627\u06cc \u0634\u06cc\u0634\u0647\u200c\u0627\u06cc (Inline).\n\n---\n\n## \ud83d\udce6 \u067e\u0627\u0633\u062e \u0628\u0627 \u0641\u0627\u06cc\u0644\u200c\u0647\u0627 \u0648 \u0627\u0633\u062a\u06cc\u06a9\u0631\n\n### `reply_sticker(sticker_id, **kwargs)`\n\n\u0627\u0631\u0633\u0627\u0644 \u0627\u0633\u062a\u06cc\u06a9\u0631 \u062f\u0631 \u067e\u0627\u0633\u062e \u0628\u0647 \u067e\u06cc\u0627\u0645.\n\n### `reply_file(file_id, **kwargs)`\n\n\u0627\u0631\u0633\u0627\u0644 \u0641\u0627\u06cc\u0644 \u0628\u0631 \u0627\u0633\u0627\u0633 File ID.\n\n---\n\n## \u270f\ufe0f \u0648\u06cc\u0631\u0627\u06cc\u0634 \u0648 \u062d\u0630\u0641\n\n### `edit(new_text)`\n\n\u0648\u06cc\u0631\u0627\u06cc\u0634 \u0645\u062a\u0646 \u067e\u06cc\u0627\u0645.\n\n### `delete()`\n\n\u062d\u0630\u0641 \u067e\u06cc\u0627\u0645 \u0641\u0639\u0644\u06cc.\n\n---\n\n## \ud83d\udce4 \u0645\u062b\u0627\u0644 \u06a9\u0627\u0631\u0628\u0631\u062f\u06cc \u06a9\u0627\u0645\u0644\n\n```python\n@bot.on_message()\ndef handler(bot: Robot, message: Message):\n # \u067e\u0627\u0633\u062e \u0628\u0627 \u062a\u0635\u0648\u06cc\u0631 \u0648 \u062f\u06a9\u0645\u0647\u200c\u0647\u0627\u06cc \u0645\u062e\u062a\u0644\u0641\n message.reply_image(\n path=\"https://s6.uupload.ir/files/sample.png\",\n text=\"\ud83d\udcf7 \u062a\u0635\u0648\u06cc\u0631 \u067e\u0627\u0633\u062e\u200c\u062f\u0627\u062f\u0647\u200c\u0634\u062f\u0647 \u0628\u0627 \u062f\u06a9\u0645\u0647\u200c\u0647\u0627\",\n inline_keypad=inline_keypad\n )\n\n message.reply_image(\n path=\"https://s6.uupload.ir/files/sample.png\",\n text=\"\ud83d\udcf7 \u062a\u0635\u0648\u06cc\u0631 \u062f\u0648\u0645 \u0628\u0627 \u0635\u0641\u062d\u0647\u200c\u06a9\u0644\u06cc\u062f\",\n chat_keypad=chat_keypad,\n chat_keypad_type=\"New\"\n )\n\n@bot.on_callback()\ndef callback_handler(bot: Robot, message: Message):\n data = message.aux_data.button_id\n if data == \"btn_male\":\n message.reply(\"\u0633\u0644\u0627\u0645 \u0622\u0642\u0627 \ud83d\udc68\")\n elif data == \"btn_female\":\n message.reply(\"\u0633\u0644\u0627\u0645 \u062e\u0627\u0646\u0645 \ud83d\udc69\")\n else:\n message.reply(f\"\u062f\u06a9\u0645\u0647 \u0646\u0627\u0634\u0646\u0627\u062e\u062a\u0647: {data}\")\n```\n\n---\n\n## \ud83e\udde0 \u0646\u06a9\u062a\u0647\n\n\u062a\u0645\u0627\u0645\u06cc \u0645\u062a\u062f\u0647\u0627\u06cc `reply_*` \u0628\u0647\u200c\u0635\u0648\u0631\u062a \u062e\u0648\u062f\u06a9\u0627\u0631 \u067e\u06cc\u0627\u0645 \u062c\u062f\u06cc\u062f \u0631\u0627 \u062f\u0631 \u067e\u0627\u0633\u062e \u0628\u0647 \u067e\u06cc\u0627\u0645 \u0627\u0635\u0644\u06cc \u0627\u0631\u0633\u0627\u0644 \u0645\u06cc\u200c\u06a9\u0646\u0646\u062f (`reply_to_message_id` \u0628\u0647\u200c\u0635\u0648\u0631\u062a \u062f\u0627\u062e\u0644\u06cc \u062a\u0646\u0638\u06cc\u0645 \u0645\u06cc\u200c\u0634\u0648\u062f).\n\n---\n\n---\n\n## \ud83d\udce4 \u0645\u062b\u0627\u0644 \u06a9\u0627\u0631\u0628\u0631\u062f\u06cc \u06a9\u0627\u0645\u0644\n\n```python\nfrom rubka import Robot\nfrom rubka.keypad import ChatKeypadBuilder\nfrom rubka.button import InlineBuilder\nfrom rubka.context import Message\n\nchat_keypad = ChatKeypadBuilder().row(\n ChatKeypadBuilder().button(id=\"btn_female\", text=\"\u0632\u0646\"),\n ChatKeypadBuilder().button(id=\"btn_male\", text=\"\u0645\u0631\u062f\")\n).build()\n\ninline_keypad = (\n InlineBuilder()\n .row(\n InlineBuilder().button_simple(\"btn_bets\", \"button1\"),\n InlineBuilder().button_simple(\"btn_rps\", \"button2\")\n )\n .row(\n InlineBuilder().button_simple(\"btn_chatid\", \"butthon3\")\n )\n .build()\n)\n\nbot = Robot(\"\u062a\u0648\u06a9\u0646 \u0634\u0645\u0627\")\n\n@bot.on_message()\ndef handler(bot: Robot, message: Message):\n message.reply_image(\n path=\"https://s6.uupload.ir/files/chatgpt_image_jul_20,_2025,_10_22_47_pm_oiql.png\",\n text=\"\ud83d\udcf7 \u0639\u06a9\u0633 \u0631\u06cc\u067e\u0644\u0627\u06cc \u0634\u062f\u0647 \u062f\u06a9\u0645\u0647 \u0634\u06cc\u0634\u0647 \u0627\u06cc\",\n inline_keypad=inline_keypad\n )\n\n message.reply_image(\n path=\"https://s6.uupload.ir/files/chatgpt_image_jul_20,_2025,_10_22_47_pm_oiql.png\",\n text=\"\ud83d\udcf7 \u0639\u06a9\u0633 \u0631\u06cc\u067e\u0644\u0627\u06cc \u0634\u062f\u0647 \u062f\u06a9\u0645\u0647 \u06a9\u06cc\u0628\u0648\u0631\u062f\u06cc\",\n chat_keypad=chat_keypad,\n chat_keypad_type=\"New\"\n )\n\n@bot.on_callback()\ndef callback_handler(bot: Robot, message: Message):\n data = message.aux_data.button_id\n if data == \"btn_male\":\n message.reply(\"\u0633\u0644\u0627\u0645 \u0645\u0631\u062f\")\n elif data == \"btn_female\":\n message.reply(\"\u0633\u0644\u0627\u0645 \u0632\u0646\")\n else:\n message.reply(f\"\u062f\u06a9\u0645\u0647 \u0646\u0627\u0634\u0646\u0627\u062e\u062a\u0647: {data}\")\n\nbot.run()\n```\n\n---\n\n## \ud83e\uddf1 \u0645\u0633\u062a\u0646\u062f\u0627\u062a \u06a9\u0644\u0627\u0633 `InlineBuilder`\n\n\u06a9\u0644\u0627\u0633 `InlineBuilder` \u0628\u0631\u0627\u06cc \u0633\u0627\u062e\u062a \u062f\u06a9\u0645\u0647\u200c\u0647\u0627\u06cc \u0627\u06cc\u0646\u0644\u0627\u06cc\u0646 \u0627\u0633\u062a\u0641\u0627\u062f\u0647 \u0645\u06cc\u200c\u0634\u0648\u062f \u06a9\u0647 \u062f\u0631 \u067e\u06cc\u0627\u0645\u200c\u0647\u0627\u06cc \u0631\u0628\u0627\u062a \u0642\u0627\u0628\u0644 \u0627\u0633\u062a\u0641\u0627\u062f\u0647 \u0647\u0633\u062a\u0646\u062f.\n\n### \u2705 \u0631\u0648\u0634 \u0627\u0633\u062a\u0641\u0627\u062f\u0647\n\n```python\nfrom rubka.button import InlineBuilder\n\nbuilder = InlineBuilder()\ninline_keypad = builder.row(\n builder.button_simple(\"btn_1\", \"\u062f\u06a9\u0645\u0647 \u06f1\"),\n builder.button_simple(\"btn_2\", \"\u062f\u06a9\u0645\u0647 \u06f2\")\n).build()\n```\n\n### \ud83d\udcda \u062f\u06a9\u0645\u0647\u200c\u0647\u0627\u06cc \u067e\u0634\u062a\u06cc\u0628\u0627\u0646\u06cc\u200c\u0634\u062f\u0647\n\n- `button_simple(id, text)` \u2013 \u062f\u06a9\u0645\u0647 \u0633\u0627\u062f\u0647\n- `button_payment(id, title, amount, description=None)` \u2013 \u067e\u0631\u062f\u0627\u062e\u062a\n- `button_calendar(id, title, type_, ...)` \u2013 \u0627\u0646\u062a\u062e\u0627\u0628 \u062a\u0627\u0631\u06cc\u062e\n- `button_location(id, type_, image_url, ...)` \u2013 \u0627\u0631\u0633\u0627\u0644 \u0645\u0648\u0642\u0639\u06cc\u062a \u0645\u06a9\u0627\u0646\u06cc\n- `button_string_picker(...)` \u2013 \u0627\u0646\u062a\u062e\u0627\u0628 \u06af\u0632\u06cc\u0646\u0647 \u0627\u0632 \u0644\u06cc\u0633\u062a\n- `button_number_picker(...)` \u2013 \u0627\u0646\u062a\u062e\u0627\u0628 \u0639\u062f\u062f \u0627\u0632 \u0628\u0627\u0632\u0647\n- `button_textbox(...)` \u2013 \u0641\u06cc\u0644\u062f \u0648\u0631\u0648\u062f \u0645\u062a\u0646\u06cc\n- `button_selection(...)` \u2013 \u0627\u0646\u062a\u062e\u0627\u0628 \u0686\u0646\u062f\u06af\u0632\u06cc\u0646\u0647\u200c\u0627\u06cc \u067e\u06cc\u0634\u0631\u0641\u062a\u0647\n- `button_camera_image(...)`, `button_camera_video(...)`\n- `button_gallery_image(...)`, `button_gallery_video(...)`\n- `button_file(...)`, `button_audio(...)`, `button_record_audio(...)`\n- `button_my_phone_number(...)`, `button_my_location(...)`\n- `button_ask_my_phone_number(...)`, `button_ask_location(...)`\n- `button_barcode(...)`\n- `button_link(id, title, url)` \u2013 \u0644\u06cc\u0646\u06a9 \u062e\u0627\u0631\u062c\u06cc\n\n### \ud83e\uddf1 \u0633\u0627\u062e\u062a \u0646\u0647\u0627\u06cc\u06cc\n\n```python\nkeypad = builder.build()\n```\n\n\u062e\u0631\u0648\u062c\u06cc \u0628\u0647 \u0635\u0648\u0631\u062a \u062f\u06cc\u06a9\u0634\u0646\u0631\u06cc \u0628\u0627 \u06a9\u0644\u06cc\u062f `rows` \u062e\u0648\u0627\u0647\u062f \u0628\u0648\u062f \u06a9\u0647 \u0645\u06cc\u200c\u062a\u0648\u0627\u0646\u06cc\u062f \u062f\u0631 \u0645\u062a\u062f `send_message` \u06cc\u0627 `reply_*` \u0627\u0633\u062a\u0641\u0627\u062f\u0647 \u06a9\u0646\u06cc\u062f.\n\n---\n\n## \u2328\ufe0f \u0645\u0633\u062a\u0646\u062f\u0627\u062a \u06a9\u0644\u0627\u0633 `ChatKeypadBuilder`\n\n\u06a9\u0644\u0627\u0633 `ChatKeypadBuilder` \u0628\u0631\u0627\u06cc \u0633\u0627\u062e\u062a \u0635\u0641\u062d\u0647\u200c\u06a9\u0644\u06cc\u062f \u0686\u062a\u06cc (chat keypad) \u0627\u0633\u062a\u0641\u0627\u062f\u0647 \u0645\u06cc\u200c\u0634\u0648\u062f.\n\n### \ud83d\udee0 \u0631\u0648\u0634 \u0627\u0633\u062a\u0641\u0627\u062f\u0647\n\n```python\nfrom rubka.keypad import ChatKeypadBuilder\n\nkeypad = ChatKeypadBuilder().row(\n ChatKeypadBuilder().button(\"btn_1\", \"\u062f\u06a9\u0645\u0647 \u06f1\"),\n ChatKeypadBuilder().button(\"btn_2\", \"\u062f\u06a9\u0645\u0647 \u06f2\")\n).build()\n```\n\n### \ud83d\udccb \u0645\u062a\u062f\u0647\u0627\n\n- `button(id, text, type=\"Simple\")` \u2013 \u0633\u0627\u062e\u062a \u06cc\u06a9 \u062f\u06a9\u0645\u0647 \u0633\u0627\u062f\u0647 \u06cc\u0627 \u0627\u0632 \u0646\u0648\u0639 \u062e\u0627\u0635\n- `row(*buttons)` \u2013 \u0627\u0641\u0632\u0648\u062f\u0646 \u06cc\u06a9 \u0631\u062f\u06cc\u0641 \u0628\u0647 \u06a9\u06cc\u0628\u0648\u0631\u062f (\u062f\u06a9\u0645\u0647\u200c\u0647\u0627 \u0628\u0627\u06cc\u062f \u0628\u0627 `button()` \u0633\u0627\u062e\u062a\u0647 \u0634\u0648\u0646\u062f)\n- `build(resize_keyboard=True, on_time_keyboard=False)` \u2013 \u0633\u0627\u062e\u062a \u062e\u0631\u0648\u062c\u06cc \u0646\u0647\u0627\u06cc\u06cc \u0628\u0631\u0627\u06cc \u0627\u0631\u0633\u0627\u0644 \u0628\u0647 \u06a9\u0627\u0631\u0628\u0631\n\n### \ud83d\udce6 \u062e\u0631\u0648\u062c\u06cc `build()`\n\n```json\n{\n \"rows\": [\n {\"buttons\": [\n {\"id\": \"btn_1\", \"type\": \"Simple\", \"button_text\": \"\u062f\u06a9\u0645\u0647 \u06f1\"},\n {\"id\": \"btn_2\", \"type\": \"Simple\", \"button_text\": \"\u062f\u06a9\u0645\u0647 \u06f2\"}\n ]}\n ],\n \"resize_keyboard\": true,\n \"on_time_keyboard\": false\n}\n```\n\n---\n\n# \u0645\u0633\u062a\u0646\u062f\u0627\u062a \u067e\u0631\u0648\u0698\u0647: \u062a\u0627\u06cc\u0645\u0631 \u067e\u06cc\u0627\u0645 \u062f\u0631 \u0631\u0628\u0627\u062a Rubika\n\n\u0627\u06cc\u0646 \u067e\u0631\u0648\u0698\u0647 \u06cc\u06a9 \u0631\u0628\u0627\u062a \u0628\u0631 \u067e\u0627\u06cc\u0647 \u06a9\u062a\u0627\u0628\u062e\u0627\u0646\u0647\u200c\u06cc `rubka` \u0627\u0633\u062a \u06a9\u0647 \u0628\u0647 \u06a9\u0627\u0631\u0628\u0631 \u0627\u0645\u06a9\u0627\u0646 \u0645\u06cc\u200c\u062f\u0647\u062f \u0628\u0627 \u0627\u0633\u062a\u0641\u0627\u062f\u0647 \u0627\u0632 \u06a9\u06cc\u200c\u067e\u062f\u060c \u06cc\u06a9 \u062a\u0627\u06cc\u0645\u0631 \u062a\u0646\u0638\u06cc\u0645 \u06a9\u0631\u062f\u0647 \u0648 \u067e\u0633 \u0627\u0632 \u067e\u0627\u06cc\u0627\u0646 \u062a\u0627\u06cc\u0645\u0631\u060c \u067e\u06cc\u0627\u0645\u06cc \u0628\u0631\u0627\u06cc \u0627\u0648 \u0627\u0631\u0633\u0627\u0644 \u0634\u0648\u062f. \u062a\u0645\u0631\u06a9\u0632 \u0627\u0635\u0644\u06cc \u0627\u06cc\u0646 \u0645\u0633\u062a\u0646\u062f\u060c \u0628\u0631 \u0631\u0648\u06cc \u06a9\u0644\u0627\u0633 `Job` \u0627\u0633\u062a \u06a9\u0647 \u0628\u0631\u0627\u06cc \u0632\u0645\u0627\u0646\u200c\u0628\u0646\u062f\u06cc \u0627\u062c\u0631\u0627\u06cc \u062f\u0633\u062a\u0648\u0631\u0627\u062a \u0627\u0633\u062a\u0641\u0627\u062f\u0647 \u0634\u062f\u0647 \u0627\u0633\u062a.\n\n## \u0633\u0627\u062e\u062a\u0627\u0631 \u06a9\u0644\u06cc \u067e\u0631\u0648\u0698\u0647\n- \u0627\u0633\u062a\u0641\u0627\u062f\u0647 \u0627\u0632 \u06a9\u062a\u0627\u0628\u062e\u0627\u0646\u0647 `rubka` \u0628\u0631\u0627\u06cc \u0627\u0631\u062a\u0628\u0627\u0637 \u0628\u0627 Rubika Bot API\n- \u062a\u0639\u0631\u06cc\u0641 \u06cc\u06a9 \u06a9\u06cc\u200c\u067e\u062f \u0628\u0627 \u06af\u0632\u06cc\u0646\u0647\u200c\u0647\u0627\u06cc \u062a\u0627\u062e\u06cc\u0631 \u0632\u0645\u0627\u0646\u06cc \u0645\u062e\u062a\u0644\u0641 (\u06f1\u06f0 \u0627\u0644\u06cc \u06f1\u06f5\u06f0 \u062b\u0627\u0646\u06cc\u0647)\n- \u0627\u0633\u062a\u0641\u0627\u062f\u0647 \u0627\u0632 \u06a9\u0644\u0627\u0633 `Job` \u0628\u0631\u0627\u06cc \u0645\u062f\u06cc\u0631\u06cc\u062a \u0627\u062c\u0631\u0627\u06cc \u0632\u0645\u0627\u0646\u200c\u0628\u0646\u062f\u06cc\u200c\u0634\u062f\u0647 \u06cc\u06a9 \u062a\u0627\u0628\u0639\n- \u0646\u0645\u0627\u06cc\u0634 \u0634\u0645\u0627\u0631\u0634 \u0645\u0639\u06a9\u0648\u0633 \u0628\u0627 \u0628\u0647\u200c\u0631\u0648\u0632\u0631\u0633\u0627\u0646\u06cc \u0645\u062f\u0627\u0648\u0645 \u067e\u06cc\u0627\u0645\n\n---\n\n## \u06a9\u0644\u0627\u0633 `Job` \u0686\u06cc\u0633\u062a\u061f\n\u06a9\u0644\u0627\u0633 `Job` \u062f\u0631 \u0641\u0627\u06cc\u0644 `rubka.jobs` \u062a\u0639\u0631\u06cc\u0641 \u0634\u062f\u0647 \u0648 \u0647\u062f\u0641 \u0622\u0646 \u0627\u062c\u0631\u0627\u06cc \u06cc\u06a9 \u062a\u0627\u0628\u0639 \u062e\u0627\u0635 \u067e\u0633 \u0627\u0632 \u06af\u0630\u0634\u062a \u06cc\u06a9 \u0628\u0627\u0632\u0647 \u0632\u0645\u0627\u0646\u06cc \u0645\u0634\u062e\u0635 \u0627\u0633\u062a.\n\n### \u0646\u062d\u0648\u0647 \u0627\u0633\u062a\u0641\u0627\u062f\u0647:\n```python\nfrom rubka.jobs import Job\n\njob = Job(delay_in_seconds, callback_function)\n```\n\n### \u067e\u0627\u0631\u0627\u0645\u062a\u0631\u0647\u0627:\n| \u067e\u0627\u0631\u0627\u0645\u062a\u0631 | \u0646\u0648\u0639 | \u062a\u0648\u0636\u06cc\u062d |\n|--------|-----|-------|\n| `delay_in_seconds` | `int` | \u0645\u062f\u062a \u0632\u0645\u0627\u0646\u06cc \u06a9\u0647 \u0628\u0627\u06cc\u062f \u0642\u0628\u0644 \u0627\u0632 \u0627\u062c\u0631\u0627\u06cc \u062a\u0627\u0628\u0639 \u0645\u0646\u062a\u0638\u0631 \u0628\u0645\u0627\u0646\u062f |\n| `callback_function` | `function` | \u062a\u0627\u0628\u0639\u06cc \u06a9\u0647 \u0628\u0639\u062f \u0627\u0632 \u067e\u0627\u06cc\u0627\u0646 \u0632\u0645\u0627\u0646 \u0628\u0627\u06cc\u062f \u0627\u062c\u0631\u0627 \u0634\u0648\u062f |\n\n### \u0648\u06cc\u0698\u06af\u06cc\u200c\u0647\u0627:\n- \u0627\u062c\u0631\u0627\u06cc \u063a\u06cc\u0631\u0647\u0645\u0632\u0645\u0627\u0646 (\u0628\u0627 \u0627\u0633\u062a\u0641\u0627\u062f\u0647 \u0627\u0632 Thread \u062f\u0627\u062e\u0644\u06cc)\n- \u0645\u0646\u0627\u0633\u0628 \u0628\u0631\u0627\u06cc \u0633\u0646\u0627\u0631\u06cc\u0648\u0647\u0627\u06cc\u06cc \u0645\u0627\u0646\u0646\u062f \u062a\u0627\u06cc\u0645\u0631\u0647\u0627\u060c \u06cc\u0627\u062f\u0622\u0648\u0631\u0647\u0627 \u0648 \u0627\u0639\u0644\u0627\u0646\u200c\u0647\u0627\u06cc \u0632\u0645\u0627\u0646\u200c\u0628\u0646\u062f\u06cc \u0634\u062f\u0647\n\n---\n\n## \u0645\u062b\u0627\u0644 \u0627\u0632 \u0627\u0633\u062a\u0641\u0627\u062f\u0647 \u062f\u0631 \u067e\u0631\u0648\u0698\u0647:\n\n```python\ndef delayed_send():\n if user_id not in active_jobs:\n return\n bot.send_message(\n message.chat_id,\n f\"\u2705 \u06a9\u0627\u0631\u0628\u0631 {user_id} : \u0632\u0645\u0627\u0646 {seconds} \u062b\u0627\u0646\u06cc\u0647 \u06af\u0630\u0634\u062a \u0648 \u062f\u0633\u062a\u0648\u0631 \u0627\u062c\u0631\u0627 \u0634\u062f! \u23f0\"\n )\n active_jobs.pop(user_id, None)\n\njob = Job(seconds, delayed_send)\nactive_jobs[user_id] = job\n```\n\n\u062f\u0631 \u0627\u06cc\u0646 \u0645\u062b\u0627\u0644\u060c \u067e\u0633 \u0627\u0632 \u0627\u0646\u062a\u062e\u0627\u0628 \u062a\u0627\u062e\u06cc\u0631 \u0632\u0645\u0627\u0646\u06cc \u062a\u0648\u0633\u0637 \u06a9\u0627\u0631\u0628\u0631\u060c \u06cc\u06a9 \u0634\u06cc \u0627\u0632 \u06a9\u0644\u0627\u0633 `Job` \u0633\u0627\u062e\u062a\u0647 \u0645\u06cc\u200c\u0634\u0648\u062f \u06a9\u0647 \u062a\u0627\u0628\u0639 `delayed_send` \u0631\u0627 \u067e\u0633 \u0627\u0632 `seconds` \u062b\u0627\u0646\u06cc\u0647 \u0627\u062c\u0631\u0627 \u0645\u06cc\u200c\u06a9\u0646\u062f.\n\n---\n\n## \u062a\u0627\u0628\u0639 `countdown_edit`\n\u0627\u06cc\u0646 \u062a\u0627\u0628\u0639 \u062a\u0627\u06cc\u0645\u0631 \u0641\u0639\u0627\u0644 \u0631\u0627 \u0628\u0647 \u0635\u0648\u0631\u062a \u0632\u0646\u062f\u0647 \u0628\u0627\u0642\u06cc\u0645\u0627\u0646\u062f\u0647 \u0632\u0645\u0627\u0646 \u0631\u0627 \u0628\u0647\u200c\u0631\u0648\u0632\u0631\u0633\u0627\u0646\u06cc \u0645\u06cc\u200c\u06a9\u0646\u062f:\n```python\ndef countdown_edit(chat_id, message_id, duration_sec):\n # \u0627\u062c\u0631\u0627\u06cc \u06cc\u06a9 Thread \u0628\u0631\u0627\u06cc \u0628\u0647\u200c\u0631\u0648\u0632\u0631\u0633\u0627\u0646\u06cc \u067e\u06cc\u0627\u0645 \u062f\u0631 \u0647\u0631 \u062b\u0627\u0646\u06cc\u0647\n```\n\n---\n## \u0646\u0645\u0648\u0646\u0647 \u06a9\u062f \u0633\u0627\u062e\u062a\u0647 \u0634\u062f\u0647\n```python\nfrom rubka import Robot\nfrom rubka.context import Message\nfrom rubka.keypad import ChatKeypadBuilder\nfrom rubka.jobs import Job\nfrom datetime import datetime, timedelta\nimport threading\nimport time\n\nbot = Robot(\"token\")\n\nactive_jobs = {}\n\ndef build_delay_keypad():\n delays = [10, 20, 30, 40, 50, 60, 75, 90, 120, 150]\n builder = ChatKeypadBuilder()\n buttons = []\n for sec in delays:\n buttons.append(builder.button(id=f\"delay_{sec}\", text=f\"\u23f3 \u0628\u0639\u062f \u0627\u0632 {sec} \u062b\u0627\u0646\u06cc\u0647\"))\n buttons.append(builder.button(id=\"cancel\", text=\"\u274c \u0627\u0646\u0635\u0631\u0627\u0641\"))\n \n rows = [buttons[i:i+3] for i in range(0, len(buttons), 3)]\n keypad = ChatKeypadBuilder()\n for row in rows:\n keypad.row(*row)\n return keypad.build()\n\ndef countdown_edit(chat_id: str, message_id: str, duration_sec: int):\n start_time = datetime.now()\n end_time = start_time + timedelta(seconds=duration_sec)\n\n def run():\n while True:\n now = datetime.now()\n if now >= end_time:\n try:\n bot.edit_message_text(chat_id, message_id, \"\u23f0 \u0632\u0645\u0627\u0646 \u062a\u0645\u0627\u0645 \u0634\u062f!\")\n except Exception as e:\n print(\"\u062e\u0637\u0627 \u062f\u0631 \u0648\u06cc\u0631\u0627\u06cc\u0634 \u067e\u06cc\u0627\u0645:\", e)\n break\n\n remaining = end_time - now\n text = (\n f\"\u23f3 \u062a\u0627\u06cc\u0645\u0631 \u0641\u0639\u0627\u0644 \u0627\u0633\u062a...\\n\"\n f\"\ud83d\udd70 \u0634\u0631\u0648\u0639: {start_time.strftime('%H:%M:%S')}\\n\"\n f\"\u23f2 \u067e\u0627\u06cc\u0627\u0646: {end_time.strftime('%H:%M:%S')}\\n\"\n f\"\u231b \u0628\u0627\u0642\u06cc\u200c\u0645\u0627\u0646\u062f\u0647: {str(remaining).split('.')[0]}\"\n )\n try:\n bot.edit_message_text(chat_id, message_id, text)\n except Exception as e:\n print(\"\u062e\u0637\u0627 \u062f\u0631 \u0648\u06cc\u0631\u0627\u06cc\u0634 \u067e\u06cc\u0627\u0645:\", e)\n time.sleep(1)\n\n threading.Thread(target=run, daemon=True).start()\n\n@bot.on_message(commands=[\"start\"])\ndef start_handler(bot: Robot, message: Message):\n keypad = build_delay_keypad()\n message.reply_keypad(\n \"\u0633\u0644\u0627\u0645 \ud83d\udc4b\\n\"\n \"\u06cc\u06a9 \u0632\u0645\u0627\u0646 \u0628\u0631\u0627\u06cc \u0627\u0631\u0633\u0627\u0644 \u067e\u06cc\u0627\u0645 \u0627\u0646\u062a\u062e\u0627\u0628 \u06a9\u0646\u06cc\u062f:\\n\"\n \"\ud83d\udcc5 \u062a\u0627\u0631\u06cc\u062e \u0648 \u0633\u0627\u0639\u062a \u0641\u0639\u0644\u06cc: \" + datetime.now().strftime(\"%Y/%m/%d %H:%M:%S\"),\n keypad\n )\n\n@bot.on_callback()\ndef callback_delay(bot: Robot, message: Message):\n btn_id = message.aux_data.button_id\n user_id = message.sender_id\n \n if btn_id == \"cancel\":\n if user_id in active_jobs:\n active_jobs.pop(user_id)\n message.reply(\"\u274c \u0647\u0645\u0647 \u0627\u0631\u0633\u0627\u0644\u200c\u0647\u0627\u06cc \u0632\u0645\u0627\u0646\u200c\u0628\u0646\u062f\u06cc \u0634\u062f\u0647 \u0644\u063a\u0648 \u0634\u062f\u0646\u062f.\")\n else:\n message.reply(\"\u26a0\ufe0f \u0634\u0645\u0627 \u0647\u06cc\u0686 \u0627\u0631\u0633\u0627\u0644 \u0632\u0645\u0627\u0646\u200c\u0628\u0646\u062f\u06cc \u0634\u062f\u0647\u200c\u0627\u06cc \u0646\u062f\u0627\u0631\u06cc\u062f.\")\n return\n \n if not btn_id.startswith(\"delay_\"):\n message.reply(\"\u274c \u062f\u06a9\u0645\u0647 \u0646\u0627\u0645\u0639\u062a\u0628\u0631 \u0627\u0633\u062a!\")\n return\n \n seconds = int(btn_id.split(\"_\")[1])\n \n if user_id in active_jobs:\n active_jobs.pop(user_id)\n\n sent_msg = bot.send_message(\n message.chat_id,\n f\"\u23f3 \u062a\u0627\u06cc\u0645\u0631 {seconds} \u062b\u0627\u0646\u06cc\u0647\u200c\u0627\u06cc \u0634\u0631\u0648\u0639 \u0634\u062f...\\n\ud83d\udd70 \u0632\u0645\u0627\u0646 \u0634\u0631\u0648\u0639: {datetime.now().strftime('%H:%M:%S')}\"\n )\n \n countdown_edit(message.chat_id, sent_msg['data']['message_id'], seconds)\n def delayed_send():\n if user_id not in active_jobs:\n return\n bot.send_message(\n message.chat_id,\n f\"\u2705 \u06a9\u0627\u0631\u0628\u0631 {user_id} : \u0632\u0645\u0627\u0646 {seconds} \u062b\u0627\u0646\u06cc\u0647 \u06af\u0630\u0634\u062a \u0648 \u062f\u0633\u062a\u0648\u0631 \u0627\u062c\u0631\u0627 \u0634\u062f! \u23f0\"\n )\n active_jobs.pop(user_id, None)\n\n job = Job(seconds, delayed_send)\n active_jobs[user_id] = job\n\n message.reply(\n f\"\u23f3 \u062b\u0628\u062a \u0634\u062f! \u067e\u06cc\u0627\u0645 \u0634\u0645\u0627 \u067e\u0633 \u0627\u0632 {seconds} \u062b\u0627\u0646\u06cc\u0647 \u0627\u0631\u0633\u0627\u0644 \u062e\u0648\u0627\u0647\u062f \u0634\u062f.\\n\"\n f\"\ud83d\udd70 \u0632\u0645\u0627\u0646 \u0634\u0631\u0648\u0639 \u062b\u0628\u062a \u0634\u062f\u0647: {datetime.now().strftime('%H:%M:%S')}\"\n )\nbot.run()\n```\n\n##\u0645\u062b\u0627\u0644 \u0633\u0627\u062f\u0647 \u062a\u0631\n```python\nfrom rubka import Robot\nfrom rubka.context import Message\nfrom rubka.jobs import Job\nfrom datetime import datetime\n\nbot = Robot(\"\")\n\nactive_jobs = {}\n\n@bot.on_message(commands=[\"timer\"])\ndef timer_handler(bot: Robot, message: Message):\n user_id = message.sender_id\n chat_id = message.chat_id\n parts = message.text.split()\n\n if len(parts) != 2 or not parts[1].isdigit():\n return message.reply(\"\u26a0\ufe0f \u0644\u0637\u0641\u0627\u064b \u0645\u062f\u062a \u0632\u0645\u0627\u0646 \u0631\u0627 \u0628\u0647 \u0635\u0648\u0631\u062a \u0635\u062d\u06cc\u062d \u0648\u0627\u0631\u062f \u06a9\u0646\u06cc\u062f. \u0645\u062b\u0644: `/timer 30`\", parse_mode=\"markdown\")\n\n seconds = int(parts[1])\n if user_id in active_jobs:\n active_jobs.pop(user_id)\n\n message.reply(f\"\u23f3 \u062a\u0627\u06cc\u0645\u0631 {seconds} \u062b\u0627\u0646\u06cc\u0647\u200c\u0627\u06cc \u0634\u0631\u0648\u0639 \u0634\u062f!\\n\ud83d\udd70 \u0632\u0645\u0627\u0646 \u0634\u0631\u0648\u0639: {datetime.now().strftime('%H:%M:%S')}\")\n\n def after_delay():\n if user_id not in active_jobs:\n return\n bot.send_message(chat_id, f\"\u2705 \u062a\u0627\u06cc\u0645\u0631 {seconds} \u062b\u0627\u0646\u06cc\u0647\u200c\u0627\u06cc \u062a\u0645\u0627\u0645 \u0634\u062f! \u23f0\")\n active_jobs.pop(user_id, None)\n\n job = Job(seconds, after_delay)\n active_jobs[user_id] = job\n\nbot.run()\n\n```\n\n##\u0646\u0645\u0648\u0646\u0647 \u06a9\u062f \u0627\u062f\u06cc\u062a \u062a\u0627\u06cc\u0645 \u0648 \u06a9\u0631\u0648\u0646 \u062c\u0627\u0628 \u0628\u0627 \u0627\u06cc\u0646\u0644\u0627\u06cc\u0646 \u06a9\u06cc\u0628\u0648\u0631\u062f \n\n```python\nfrom rubka import Robot\nfrom rubka.context import Message\nfrom rubka.keypad import ChatKeypadBuilder\nfrom rubka.jobs import Job\nfrom datetime import datetime, timedelta\nimport threading\nimport time\n\nbot = Robot(\"token\")\nbot.edit_inline_keypad\nactive_jobs = {}\n\ndef build_delay_keypad():\n delays = [10, 20, 30, 40, 50, 60, 75, 90, 120, 150]\n builder = ChatKeypadBuilder()\n buttons = []\n for sec in delays:\n buttons.append(builder.button(id=f\"delay_{sec}\", text=f\"\u23f3 \u0628\u0639\u062f \u0627\u0632 {sec} \u062b\u0627\u0646\u06cc\u0647\"))\n buttons.append(builder.button(id=\"cancel\", text=\"\u274c \u0627\u0646\u0635\u0631\u0627\u0641\"))\n \n rows = [buttons[i:i+3] for i in range(0, len(buttons), 3)]\n keypad = ChatKeypadBuilder()\n for row in rows:\n keypad.row(*row)\n return keypad.build()\n\ndef countdown_edit(chat_id: str, message_id: str, duration_sec: int):\n start_time = datetime.now()\n end_time = start_time + timedelta(seconds=duration_sec)\n\n def run():\n while True:\n now = datetime.now()\n if now >= end_time:\n try:\n bot.edit_message_text(chat_id, message_id, \"\u23f0 \u0632\u0645\u0627\u0646 \u062a\u0645\u0627\u0645 \u0634\u062f!\")\n except Exception as e:\n print(\"\u062e\u0637\u0627 \u062f\u0631 \u0648\u06cc\u0631\u0627\u06cc\u0634 \u067e\u06cc\u0627\u0645:\", e)\n break\n\n remaining = end_time - now\n text = (\n f\"\u23f3 \u062a\u0627\u06cc\u0645\u0631 \u0641\u0639\u0627\u0644 \u0627\u0633\u062a...\\n\"\n f\"\ud83d\udd70 \u0634\u0631\u0648\u0639: {start_time.strftime('%H:%M:%S')}\\n\"\n f\"\u23f2 \u067e\u0627\u06cc\u0627\u0646: {end_time.strftime('%H:%M:%S')}\\n\"\n f\"\u231b \u0628\u0627\u0642\u06cc\u200c\u0645\u0627\u0646\u062f\u0647: {str(remaining).split('.')[0]}\"\n )\n try:\n bot.edit_message_text(chat_id, message_id, text)\n except Exception as e:\n print(\"\u062e\u0637\u0627 \u062f\u0631 \u0648\u06cc\u0631\u0627\u06cc\u0634 \u067e\u06cc\u0627\u0645:\", e)\n time.sleep(1)\n\n threading.Thread(target=run, daemon=True).start()\n\n@bot.on_message(commands=[\"start\"])\ndef start_handler(bot: Robot, message: Message):\n keypad = build_delay_keypad()\n message.reply_keypad(\n \"\u0633\u0644\u0627\u0645 \ud83d\udc4b\\n\"\n \"\u06cc\u06a9 \u0632\u0645\u0627\u0646 \u0628\u0631\u0627\u06cc \u0627\u0631\u0633\u0627\u0644 \u067e\u06cc\u0627\u0645 \u0627\u0646\u062a\u062e\u0627\u0628 \u06a9\u0646\u06cc\u062f:\\n\"\n \"\ud83d\udcc5 \u062a\u0627\u0631\u06cc\u062e \u0648 \u0633\u0627\u0639\u062a \u0641\u0639\u0644\u06cc: \" + datetime.now().strftime(\"%Y/%m/%d %H:%M:%S\"),\n keypad\n )\n\n@bot.on_callback()\ndef callback_delay(bot: Robot, message: Message):\n btn_id = message.aux_data.button_id\n user_id = message.sender_id\n \n if btn_id == \"cancel\":\n if user_id in active_jobs:\n active_jobs.pop(user_id)\n message.reply(\"\u274c \u0647\u0645\u0647 \u0627\u0631\u0633\u0627\u0644\u200c\u0647\u0627\u06cc \u0632\u0645\u0627\u0646\u200c\u0628\u0646\u062f\u06cc \u0634\u062f\u0647 \u0644\u063a\u0648 \u0634\u062f\u0646\u062f.\")\n else:\n message.reply(\"\u26a0\ufe0f \u0634\u0645\u0627 \u0647\u06cc\u0686 \u0627\u0631\u0633\u0627\u0644 \u0632\u0645\u0627\u0646\u200c\u0628\u0646\u062f\u06cc \u0634\u062f\u0647\u200c\u0627\u06cc \u0646\u062f\u0627\u0631\u06cc\u062f.\")\n return\n if not btn_id.startswith(\"delay_\"):\n message.reply(\"\u274c \u062f\u06a9\u0645\u0647 \u0646\u0627\u0645\u0639\u062a\u0628\u0631 \u0627\u0633\u062a!\")\n return\n seconds = int(btn_id.split(\"_\")[1])\n if user_id in active_jobs:\n active_jobs.pop(user_id)\n sent_msg = bot.edit_inline_keypad(\n message.chat_id,\n f\"\u23f3 \u062a\u0627\u06cc\u0645\u0631 {seconds} \u062b\u0627\u0646\u06cc\u0647\u200c\u0627\u06cc \u0634\u0631\u0648\u0639 \u0634\u062f...\\n\ud83d\udd70 \u0632\u0645\u0627\u0646 \u0634\u0631\u0648\u0639: {datetime.now().strftime('%H:%M:%S')}\"\n )\n print(sent_msg)\n countdown_edit(message.chat_id, sent_msg['data']['message_id'], seconds)\n def delayed_send():\n if user_id not in active_jobs:\n return\n \n bot.send_message(\n message.chat_id,\n f\"\u2705 \u06a9\u0627\u0631\u0628\u0631 {user_id} : \u0632\u0645\u0627\u0646 {seconds} \u062b\u0627\u0646\u06cc\u0647 \u06af\u0630\u0634\u062a \u0648 \u062f\u0633\u062a\u0648\u0631 \u0627\u062c\u0631\u0627 \u0634\u062f! \u23f0\"\n )\n active_jobs.pop(user_id, None)\n\n job = Job(seconds, delayed_send)\n active_jobs[user_id] = job\n\n message.reply(\n f\"\u23f3 \u062b\u0628\u062a \u0634\u062f! \u067e\u06cc\u0627\u0645 \u0634\u0645\u0627 \u067e\u0633 \u0627\u0632 {seconds} \u062b\u0627\u0646\u06cc\u0647 \u0627\u0631\u0633\u0627\u0644 \u062e\u0648\u0627\u0647\u062f \u0634\u062f.\\n\"\n f\"\ud83d\udd70 \u0632\u0645\u0627\u0646 \u0634\u0631\u0648\u0639 \u062b\u0628\u062a \u0634\u062f\u0647: {datetime.now().strftime('%H:%M:%S')}\"\n )\n\nbot.run()\n```\n# \u2705 Force Join (\u0627\u062c\u0628\u0627\u0631 \u0628\u0647 \u0639\u0636\u0648\u06cc\u062a \u062f\u0631 \u06a9\u0627\u0646\u0627\u0644) \u2014 Rubka Bot\n\n\u0627\u06cc\u0646 \u0645\u0633\u062a\u0646\u062f \u0646\u062d\u0648\u0647 \u0627\u0633\u062a\u0641\u0627\u062f\u0647 \u0627\u0632 \u0642\u0627\u0628\u0644\u06cc\u062a **\u0627\u062c\u0628\u0627\u0631 \u0628\u0647 \u0639\u0636\u0648\u06cc\u062a \u062f\u0631 \u06cc\u06a9 \u06a9\u0627\u0646\u0627\u0644 (Force Join)** \u062f\u0631 \u0631\u0628\u0627\u062a\u200c\u0647\u0627\u06cc \u0633\u0627\u062e\u062a\u0647\u200c\u0634\u062f\u0647 \u0628\u0627 \u06a9\u062a\u0627\u0628\u062e\u0627\u0646\u0647 Rubka \u0631\u0627 \u062a\u0648\u0636\u06cc\u062d \u0645\u06cc\u200c\u062f\u0647\u062f.\n\n---\n\n## \ud83c\udfaf \u0647\u062f\u0641\n\n\u0627\u0637\u0645\u06cc\u0646\u0627\u0646 \u0627\u0632 \u0627\u06cc\u0646\u06a9\u0647 \u06a9\u0627\u0631\u0628\u0631 \u0639\u0636\u0648 \u06cc\u06a9 \u06a9\u0627\u0646\u0627\u0644 \u062e\u0627\u0635 \u0627\u0633\u062a\u060c \u0642\u0628\u0644 \u0627\u0632 \u0627\u062f\u0627\u0645\u0647 \u062a\u0639\u0627\u0645\u0644 \u0628\u0627 \u0631\u0628\u0627\u062a. \u0627\u06af\u0631 \u0639\u0636\u0648 \u0646\u0628\u0648\u062f\u060c \u0628\u0647 \u0627\u0648 \u0627\u0637\u0644\u0627\u0639 \u062f\u0627\u062f\u0647 \u0634\u0648\u062f \u06cc\u0627 \u0644\u06cc\u0646\u06a9 \u0639\u0636\u0648\u06cc\u062a \u0627\u0631\u0633\u0627\u0644 \u06af\u0631\u062f\u062f.\n\n---\n\n## \ud83d\udce6 \u067e\u06cc\u0634\u200c\u0646\u06cc\u0627\u0632\u0647\u0627\n\n- \u0646\u0635\u0628 \u0648 \u0631\u0627\u0647\u200c\u0627\u0646\u062f\u0627\u0632\u06cc \u06a9\u062a\u0627\u0628\u062e\u0627\u0646\u0647 `rubka`\n- \u062a\u0648\u06a9\u0646 \u0645\u0639\u062a\u0628\u0631 \u0631\u0628\u0627\u062a Rubika\n- \u062f\u0633\u062a\u0631\u0633\u06cc \u0628\u0647 `channel_guid` (\u0634\u0646\u0627\u0633\u0647 \u0639\u062f\u062f\u06cc \u06a9\u0627\u0646\u0627\u0644)\n- \u0631\u0628\u0627\u062a \u0628\u0627\u06cc\u062f \u062f\u0631 \u06a9\u0627\u0646\u0627\u0644\u060c **\u0627\u062f\u0645\u06cc\u0646** \u0628\u0627\u0634\u062f\n\n---\n\n## \ud83d\udca1 \u0646\u062d\u0648\u0647 \u0627\u0633\u062a\u0641\u0627\u062f\u0647\n\n### \u06a9\u062f \u0628\u0647\u06cc\u0646\u0647\u200c\u0634\u062f\u0647:\n\n```python\nfrom rubka import Robot\nfrom rubka.context import Message\n\nbot = Robot(token=\"your_token\")\nCHANNEL_GUID = \"c0xABCDEF...\" # GUID \u06a9\u0627\u0646\u0627\u0644 \u0647\u062f\u0641\n\n@bot.on_message()\ndef handle_force_join(bot: Robot, message: Message):\n name = bot.get_name(message.chat_id)\n\n if bot.check_join(CHANNEL_GUID, message.chat_id):\n message.reply(f\"\u0633\u0644\u0627\u0645 {name} \ud83d\udc4b\\n\u0634\u0645\u0627 \u0639\u0636\u0648 \u06a9\u0627\u0646\u0627\u0644 \u0647\u0633\u062a\u06cc\u062f \u2705\")\n else:\n join_link = \"https://rubika.ir/rubka_library\"\n message.reply(\n f\"\u0633\u0644\u0627\u0645 {name} \ud83d\udc4b\\n\u0634\u0645\u0627 \u0639\u0636\u0648 \u06a9\u0627\u0646\u0627\u0644 \u0646\u06cc\u0633\u062a\u06cc\u062f \u274c\\n\\n\"\n f\"\u0644\u0637\u0641\u0627\u064b \u0627\u0628\u062a\u062f\u0627 \u0639\u0636\u0648 \u06a9\u0627\u0646\u0627\u0644 \u0634\u0648\u06cc\u062f \u0633\u067e\u0633 \u062f\u0648\u0628\u0627\u0631\u0647 \u062a\u0644\u0627\u0634 \u06a9\u0646\u06cc\u062f:\\n{join_link}\"\n )\n\nbot.run()\n```\n\n---\n\n## \ud83d\udd0d \u0634\u0631\u062d \u0645\u062a\u062f\u0647\u0627\n\n| \u0645\u062a\u062f | \u06a9\u0627\u0631\u0628\u0631\u062f |\n| ------------------------------------- | ------------------------------------ |\n| `check_join(channel_guid, user_guid)` | \u0628\u0631\u0631\u0633\u06cc \u0639\u0636\u0648\u06cc\u062a \u06a9\u0627\u0631\u0628\u0631 \u062f\u0631 \u06a9\u0627\u0646\u0627\u0644 \u0645\u0634\u062e\u0635\u200c\u0634\u062f\u0647 |\n| `get_name(user_guid)` | \u062f\u0631\u06cc\u0627\u0641\u062a \u0646\u0627\u0645 \u0646\u0645\u0627\u06cc\u0634\u06cc \u06a9\u0627\u0631\u0628\u0631 \u0627\u0632 \u0637\u0631\u06cc\u0642 GUID |\n| `message.reply(text)` | \u067e\u0627\u0633\u062e \u0645\u0633\u062a\u0642\u06cc\u0645 \u0628\u0647 \u067e\u06cc\u0627\u0645 \u062f\u0631\u06cc\u0627\u0641\u062a\u200c\u0634\u062f\u0647 |\n\n---\n\n## \ud83d\udd10 \u0646\u06a9\u0627\u062a \u0645\u0647\u0645 \u0627\u0645\u0646\u06cc\u062a\u06cc\n\n- \u0631\u0628\u0627\u062a \u0628\u0627\u06cc\u062f \u062d\u062a\u0645\u0627\u064b **\u0627\u062f\u0645\u06cc\u0646 \u06a9\u0627\u0646\u0627\u0644** \u0628\u0627\u0634\u062f.\n- \u062f\u0631 \u0635\u0648\u0631\u062a \u0639\u062f\u0645 \u0639\u0636\u0648\u06cc\u062a\u060c \u0628\u0647\u062a\u0631 \u0627\u0633\u062a \u0644\u06cc\u0646\u06a9 \u062f\u0639\u0648\u062a \u0628\u0647 \u06a9\u0627\u0646\u0627\u0644 \u0646\u0645\u0627\u06cc\u0634 \u062f\u0627\u062f\u0647 \u0634\u0648\u062f.\n\n##Mahdi Ahmadi\n\n\n",
"bugtrack_url": null,
"license": null,
"summary": "A professional and localized Python library for interacting with Rubika Bot API, compatible with various systems.",
"version": "2.4.2",
"project_urls": {
"Homepage": "https://pypi.org/project/robka/"
},
"split_keywords": [],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "a79eefb2204b25fabd23d51efc3466a9158e4e0112acdf249a23a2da670f974f",
"md5": "70d20f9faf39bc0981b646ea9f053f03",
"sha256": "573d20f47b183e336ca9be24a831aa562429b55f5f9ff49c068baf8db47cd877"
},
"downloads": -1,
"filename": "robka-2.4.2-py3-none-any.whl",
"has_sig": false,
"md5_digest": "70d20f9faf39bc0981b646ea9f053f03",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.6",
"size": 54411,
"upload_time": "2025-08-21T18:35:23",
"upload_time_iso_8601": "2025-08-21T18:35:23.112384Z",
"url": "https://files.pythonhosted.org/packages/a7/9e/efb2204b25fabd23d51efc3466a9158e4e0112acdf249a23a2da670f974f/robka-2.4.2-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "1f8918d0c2be36e17cc7e15fd0c6ce0e49b2d67285ddc0a72bf5ff66b5ce41cc",
"md5": "1cff22b69614c886a4e5dde8c981fdb1",
"sha256": "f8c0b04b4142ae1e290a751af287819fb3f404e2cc6bbde204189bca1a4a1cbf"
},
"downloads": -1,
"filename": "robka-2.4.2.tar.gz",
"has_sig": false,
"md5_digest": "1cff22b69614c886a4e5dde8c981fdb1",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.6",
"size": 61591,
"upload_time": "2025-08-21T18:35:24",
"upload_time_iso_8601": "2025-08-21T18:35:24.142962Z",
"url": "https://files.pythonhosted.org/packages/1f/89/18d0c2be36e17cc7e15fd0c6ce0e49b2d67285ddc0a72bf5ff66b5ce41cc/robka-2.4.2.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-08-21 18:35:24",
"github": false,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"lcname": "robka"
}