معرفی
ربات چیست و API چطور کار میکند.
ربات یک حساب ویژه است که بهجای آدم، برنامهٔ شما پشتش کار میکند. هر ربات یک توکن دارد و با فرستادن درخواستهای ساده (HTTPS) به سرور 7eve9، پیام میگیرد و پیام میفرستد، دکمه نشان میدهد، فایل رد و بدل میکند، در گروهها مدیریت میکند و حتی یک مینیاپ (وباپ داخل پیامرسان) باز میکند.
مهمترین نکته: API ما با Bot API تلگرام سازگار است. فرمت درخواست و پاسخ یکی است، پس هر کتابخانه یا کدی که برای ربات تلگرام ساختهاید، فقط با عوض کردن آدرس سرور کار میکند.
https://api.7echat.ir/bot<TOKEN>/<METHOD>سازگار با تلگرام
۸۳ متد، همان نامها و همان قالب JSON. کتابخانههای معروف بدون تغییر کار میکنند.
دو روش دریافت پیام
long polling با getUpdates یا وبهوک که سرور شما را صدا میزند.
مینیاپ
صفحهٔ وب خودتان داخل پیامرسان باز میشود و کاربر را با امضای امن میشناسد.
تا ۲۰ ربات
هر حساب تا ۲۰ ربات میتواند بسازد و همه را از داخل برنامه مدیریت کند.
اگر قبلاً با Bot API تلگرام کار کردهاید، از بخش «محدودیتها و تفاوتها» شروع کنید تا ببینید چه چیزهایی فرق دارد.
شروع سریع
از صفر تا اولین پیام ربات، در حدود پنج دقیقه.
- با BotFather گفتگو کنیددر برنامه عبارت
botfatherرا جستجو کنید (یا https://7echat.ir/botfather را باز کنید) و/newbotرا بفرستید. - یک نام نمایشی بدهیدمثلاً «ربات فروشگاه من». نام میتواند فارسی باشد و تا ۶۴ نویسه.
- یک یوزرنیم انتخاب کنیدباید با
botتمام شود، با حرف انگلیسی شروع شود و فقط حروف انگلیسی، عدد و_داشته باشد (۵ تا ۳۲ نویسه)، مثلmy_shop_bot. - توکن را بگیریدBotFather توکن را میفرستد؛ شبیه
154457014047128:AAH…. آن را مثل رمز عبور نگه دارید و در کد عمومی نگذارید.
قوانین ساخت ربات
ربات باید مطابق قوانین کشور و قوانین 7eve9 باشد؛ محتوای مجرمانه، کلاهبرداری و فیشینگ، اسپم، جمعآوری یا سوءاستفاده از اطلاعات کاربران و تبلیغ موارد غیرقانونی ممنوع است.
اگر رباتتان غیرمجاز باشد، ربات مسدود میشود و حساب کاربری شما هم بن میشود. در صورت پیگیری رسمی مراجع قانونی از جمله پلیس فتا، اطلاعات ثبتشدهٔ شما طبق قانون در اختیار آنها قرار میگیرد. با ساخت ربات، این قوانین را میپذیرید.
هر کسی توکن را داشته باشد ربات را کامل کنترل میکند. اگر لو رفت، همان لحظه در BotFather دستور /revoke را بزنید؛ توکن قبلی از کار میافتد و توکن تازه میگیرید.
اولین درخواست
توکن را در یک متغیر محیطی بگذارید و getMe را صدا بزنید تا مطمئن شوید همهچیز درست است:
export TOKEN="154457014047128:AAH-your-token-here"
curl "https://api.7echat.ir/bot$TOKEN/getMe"const TOKEN = process.env.TOKEN;
const r = await fetch(`https://api.7echat.ir/bot${TOKEN}/getMe`);
console.log(await r.json());import os, requests
TOKEN = os.environ["TOKEN"]
print(requests.get(f"https://api.7echat.ir/bot{TOKEN}/getMe").json())پاسخ درست این شکل است (مقادیر نمونهاند):
{
"ok": true,
"result": {
"id": 154457014047128,
"is_bot": true,
"first_name": "ربات فروشگاه من",
"username": "my_shop_bot",
"language_code": "fa",
"can_join_groups": true,
"can_read_all_group_messages": false,
"supports_inline_queries": false,
"has_main_web_app": false
}
}اولین پیام
- ربات را Start کنیدربات خودش نمیتواند به کسی پیام بدهد؛ اول باید از حساب خودتان به ربات
/startبفرستید. - پیام را بخوانیدبا
getUpdatesمیبینید ربات چه گرفته است.chat.idهمان شناسهٔ شما است. - جواب بدهیدبا
sendMessageو همانchat.id.
# 1) ببینید چه آپدیتی آمده
curl "https://api.7echat.ir/bot$TOKEN/getUpdates"
# 2) جواب بدهید (CHAT_ID را از خروجی بالا بردارید)
curl "https://api.7echat.ir/bot$TOKEN/sendMessage" \
-d chat_id=CHAT_ID \
--data-urlencode "text=سلام! من ربات شما هستم."خروجی getUpdates برای /start این شکل است:
{
"ok": true,
"result": [{
"update_id": 1,
"message": {
"message_id": 300002506,
"date": 1792326271,
"chat": { "id": 109270360158361, "type": "private", "first_name": "نیما" },
"from": { "id": 109270360158361, "is_bot": false, "first_name": "نیما", "language_code": "fa" },
"text": "/start",
"entities": [{ "type": "bot_command", "offset": 0, "length": 6 }]
}
}]
}برای یک ربات واقعی از بخش دریافت پیامها و نمونههای کامل ادامه دهید؛ آنجا یک ربات کامل با دکمه آمده است.
ساخت ربات با BotFather
BotFather ربات رسمی مدیریت رباتهاست؛ هر چیزی دربارهٔ ربات شما از همینجا تنظیم میشود.
همهٔ دستورها را در چت botfather میفرستید. اگر چند ربات دارید، BotFather فهرست دکمهای نشان میدهد؛ یا آخر دستور یوزرنیم بنویسید، مثل /setname @my_shop_bot. دستور /cancel هر مرحله را لغو میکند و /help راهنمای کامل را میفرستد.
| دستور | کاربرد |
|---|---|
/newbot | ساخت ربات تازه (نام، یوزرنیم، دریافت توکن). حداکثر ۲۰ ربات برای هر حساب. |
/mybots | فهرست رباتهایتان با دکمههای توکن، ویرایش، تنظیمات و حذف. |
/token | نمایش توکن یک ربات. |
/revoke | باطل کردن توکن و گرفتن توکن تازه (وقتی لو رفته). |
/setname | تغییر نام نمایشی (تا ۶۴ نویسه). |
/setdescription | متنی که قبل از زدن Start، در صفحهٔ خالی چت با عنوان «این ربات چه کاری میکند؟» دیده میشود. |
/setabouttext | بیوی کوتاه در پروفایل ربات (تا ۱۲۰ نویسه). پاک کردن: /empty. |
/setuserpic | عکس پروفایل ربات (یک عکس بفرستید). |
/setcommands | فهرست دستورات (هر خط command - توضیح)؛ وقتی کاربر «/» میزند نشان داده میشود. |
/deletecommands | پاک کردن فهرست دستورات. |
/setinline | روشن کردن حالت اینلاین با متن راهنمای کادر پیام. خاموش: /empty. |
/setinlinefeedback | دریافت آپدیت chosen_inline_result (انتخاب کاربر). |
/setjoingroups | اجازهٔ اضافه شدن ربات به گروهها (Enable / Disable). |
/setprivacy | حالت حریم خصوصی در گروهها؛ توضیح. |
/setmenubutton | دکمهٔ منوی کنار کادر پیام را به یک مینیاپ (آدرس https) وصل میکند. برگرداندن: /empty. |
/setwebapp | تنظیم مینیاپ اصلی ربات (آدرس https و متن دکمه در خط دوم). |
/setwebhook | تنظیم آدرس وبهوک. حذف و برگشت به getUpdates: /empty یا /deletewebhook. |
/newgame، /listgames | ساخت و فهرست بازیهای ربات (short_name انگلیسی). |
/botinfo | اطلاعات و وضعیت ربات. |
/deletebot | حذف همیشگی ربات. برای تأیید باید دقیقاً بنویسید: Yes, I am totally sure. |
نام، عکس و بیوی ربات را از داخل برنامه هم میتوانید عوض کنید (پروفایل ربات ← ویرایش)، و تغییرها فوری برای همه اعمال میشود.
درخواست و پاسخ
قالب همهٔ درخواستها و خطاها.
آدرس و روش
هر متد یک آدرس دارد: https://api.7echat.ir/bot<TOKEN>/<METHOD>. نام متد به بزرگی و کوچکی حروف حساس نیست (getme هم کار میکند). فقط GET و POST مجازند؛ روشهای دیگر خطای 405 میگیرند.
پارامترها
پارامترها را به هر یک از این شکلها میتوانید بفرستید و با هم قاطی هم میشوند:
- query string (
?chat_id=1&text=hi) - فرم
application/x-www-form-urlencoded application/json(پیشنهادی برای ساختارهای تودرتو مثلreply_markup)multipart/form-data(برای آپلود فایل)
پاسخ
پاسخ همیشه JSON است. موفق: {"ok": true, "result": …}. ناموفق: {"ok": false, "error_code": …, "description": "…"}.
| کد | معنی | نمونه توضیح |
|---|---|---|
400 | درخواست نادرست | Bad Request: chat not found، message is too long، message text is empty، invalid file_id |
401 | توکن نامعتبر | Unauthorized |
404 | متد وجود ندارد | Not Found: method not found |
405 | روش HTTP مجاز نیست | فقط GET و POST |
409 | تداخل وبهوک و getUpdates | Conflict: can't use getUpdates method while webhook is active… |
413 | حجم بدنهٔ درخواست زیاد است | بیش از ۵۰ مگابایت |
403 | اجازه ندارید | Forbidden: bot was blocked by the user، bot is not a member of the chat |
429 | درخواستهای زیاد | کمی صبر کنید؛ parameters.retry_after میگوید چند ثانیه |
شناسهها
| شناسه | قاعده |
|---|---|
user.id | عدد صحیح بزرگ (کمتر از ۲۵۳؛ در همهٔ زبانها بدون مشکل در عدد معمولی جا میشود). |
chat.id | چت خصوصی = شناسهٔ همان کاربر (عدد مثبت). گروه و کانال = عدد منفی. بهجای آن میتوانید @username هم بدهید. |
message_id | عدد صحیح؛ در همهٔ متدها (ویرایش، حذف، ریپلای…) قابل استفاده است. |
دریافت پیامها
چطور ربات بفهمد کسی چیزی نوشته یا دکمهای زده.
هر رویداد (پیام تازه، زدن دکمه، …) یک آپدیت است. دو راه برای گرفتنش هست و همزمان فقط یکی فعال است.
روش اول: getUpdates (long polling)
ربات خودش مدام میپرسد «چیزی آمده؟». ساده است و برای شروع و برنامههایی که آدرس عمومی ندارند عالی است.
| پارامتر | توضیح |
|---|---|
offset | شناسهٔ آپدیتی که میخواهید از آن شروع شود. بعد از پردازش هر آپدیت، update_id + 1 را بدهید تا آپدیتهای قبلی پاک شوند. |
timeout | چند ثانیه سرور منتظر آپدیت تازه بماند (حداکثر ۵۰). با 0 فوراً جواب میدهد. |
limit | تعداد آپدیت در هر پاسخ، ۱ تا ۱۰۰. |
allowed_updates | آرایهٔ نوع آپدیتهایی که میخواهید (مثل ["message","callback_query"]). |
آپدیتهای خواندهنشده ۲۴ ساعت نگه داشته میشوند. اگر وبهوک فعال باشد، getUpdates خطای 409 میدهد.
روش دوم: وبهوک
سرور 7eve9 هر آپدیت را با POST به آدرس شما میفرستد. سریعتر است و برای رباتهای بزرگ مناسبتر.
curl "https://api.7echat.ir/bot$TOKEN/setWebhook" \
-d url=https://example.com/hook \
-d secret_token=یک-رشته-تصادفی-بلند \
-d 'allowed_updates=["message","callback_query"]'- آدرس باید عمومی باشد؛ آدرسهای داخلی و محلی (مثل
127.0.0.1) رد میشوند. - هر آپدیت با هدر
X-Telegram-Bot-Api-Secret-Token(همانsecret_tokenشما) میآید؛ آن را چک کنید تا کسی نتواند آپدیت جعلی بفرستد. - مهلت پاسخ ۱۵ ثانیه است و تغییر مسیر (redirect) دنبال نمیشود. با خطا، سه بار دیگر تلاش میشود (بعد از ۵ ثانیه، ۳۰ ثانیه و ۲ دقیقه).
- وضعیت را با
getWebhookInfoببینید؛ برای حذف:deleteWebhook(باdrop_pending_updates=trueآپدیتهای منتظر را هم پاک میکند).
انواع آپدیت
| فیلد | چه وقت میآید |
|---|---|
message | پیام تازه (متن، عکس، فایل، موقعیت، …) در چت خصوصی یا گروه. |
edited_message | پیامی ویرایش شد. |
channel_post / edited_channel_post | پست کانال (فقط وقتی ربات ادمین کانال است). |
callback_query | کاربر یک دکمهٔ شیشهای را زد. |
inline_query / chosen_inline_result | حالت اینلاین. |
my_chat_member | ربات اضافه، حذف یا ادمین شد. |
chat_join_request | درخواست عضویت در گروه/کانال با تأیید ادمین. |
poll_answer | کسی در نظرسنجی رأی داد. |
message_reaction | واکنش (ایموجی) روی پیامی تغییر کرد. |
هر آپدیت update_id دارد و دقیقاً یکی از فیلدهای بالا. نمونهٔ callback_query (وقتی کاربر دکمهای با callback_data=vote:yes میزند):
{
"update_id": 2,
"callback_query": {
"id": "1792326314626932",
"from": { "id": 109270360158361, "is_bot": false, "first_name": "نیما", "language_code": "fa" },
"chat_instance": "154457014047173",
"message": {
"message_id": 300002509,
"date": 1792326314,
"chat": { "id": 109270360158361, "type": "private", "first_name": "نیما" },
"from": { "id": 154457014047128, "is_bot": true, "first_name": "ربات فروشگاه من", "username": "my_shop_bot" },
"text": "یکی را انتخاب کنید:",
"reply_markup": { "inline_keyboard": [[ { "text": "بله", "callback_data": "vote:yes" }, { "text": "خیر", "callback_data": "vote:no" } ]] }
},
"data": "vote:yes"
}
}ارسال و ویرایش پیام
sendMessage و دوستانش.
پارامتر sendMessage | توضیح |
|---|---|
chat_id | شناسهٔ چت (یا @username). برای چت خصوصی، کاربر باید قبلاً ربات را Start کرده باشد. |
text | متن پیام، حداکثر ۴۰۹۶ نویسه. |
parse_mode | HTML، MarkdownV2 یا Markdown؛ یا بهجایش آرایهٔ entities. |
reply_markup | دکمهها؛ بخش ۷. |
reply_parameters / reply_to_message_id | ریپلای به یک پیام (reply_parameters شامل message_id؛ با allow_sending_without_reply اگر پیام نبود خطا نمیدهد). |
disable_web_page_preview / link_preview_options | خاموش کردن پیشنمایش لینک ({"is_disabled": true}). |
curl "https://api.7echat.ir/bot$TOKEN/sendMessage" \
-H "Content-Type: application/json" \
-d '{
"chat_id": 109270360158361,
"text": "<b>سلام!</b> این یک پیام <i>قالببندیشده</i> است.",
"parse_mode": "HTML"
}'await fetch(`https://api.7echat.ir/bot${TOKEN}/sendMessage`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
chat_id: 109270360158361,
text: "<b>سلام!</b> این یک پیام <i>قالببندیشده</i> است.",
parse_mode: "HTML",
}),
});requests.post(f"https://api.7echat.ir/bot{TOKEN}/sendMessage", json={
"chat_id": 109270360158361,
"text": "<b>سلام!</b> این یک پیام <i>قالببندیشده</i> است.",
"parse_mode": "HTML",
})پاسخ، خود پیام فرستادهشده است (با message_id که برای ویرایش و حذف لازم دارید):
{
"ok": true,
"result": {
"message_id": 300002507,
"date": 1792326272,
"chat": { "id": 109270360158361, "type": "private", "first_name": "نیما" },
"from": { "id": 154457014047128, "is_bot": true, "first_name": "ربات فروشگاه من", "username": "my_shop_bot" },
"text": "سلام! این یک پیام قالببندیشده است.",
"entities": [{ "type": "bold", "offset": 0, "length": 5 }, { "type": "italic", "offset": 18, "length": 13 }]
}
}قالببندی متن
| حالت | چه چیزهایی |
|---|---|
HTML | <b> <i> <u> <s> <tg-spoiler> <a href="…"> <code> <pre> <blockquote>. منشن: <a href="tg://user?id=123">نام</a> |
MarkdownV2 | *bold* _italic_ __underline__ ~strike~ ||spoiler|| `code` [متن](url) و نقلقول با > اول خط. نویسههای خاص را با \ بگریزانید. |
Markdown | نسخهٔ قدیمی و سادهتر. |
در حالت HTML فقط سه نویسهٔ < > & باید به < > & تبدیل شوند؛ برای متن کاربر از HTML استفاده کنید تا گریز دادن سادهتر باشد.
ویرایش، حذف و واکنش
editMessageText،editMessageCaption،editMessageReplyMarkup،editMessageMedia: باchat_idوmessage_idپیام خودِ ربات را عوض کنید.deleteMessageوdeleteMessages: حذف پیام.forwardMessage،copyMessage(و نسخهٔ چندتایی): فوروارد و کپی بدون نام فرستنده.setMessageReaction: واکنش ربات روی یک پیام.sendChatActionباaction=typingو… نشان میدهد ربات در حال نوشتن است.sendLocation،sendVenue،sendContact،sendPoll،sendDice: انواع دیگر پیام.
دکمهها و کیبورد
دکمههای شیشهای زیر پیام و کیبورد جایگزین.
دکمهٔ شیشهای (inline keyboard)
آرایهای از ردیفها، هر ردیف آرایهای از دکمهها:
{
"chat_id": 109270360158361,
"text": "یکی را انتخاب کنید:",
"reply_markup": {
"inline_keyboard": [
[ { "text": "بله", "callback_data": "vote:yes" }, { "text": "خیر", "callback_data": "vote:no" } ],
[ { "text": "باز کردن سایت", "url": "https://7echat.ir" } ],
[ { "text": "مینیاپ", "web_app": { "url": "https://example.com/app" } } ],
[ { "text": "جستجو در هر چت", "switch_inline_query": "" } ]
]
}
}| فیلد دکمه | رفتار |
|---|---|
callback_data | با زدن دکمه، آپدیت callback_query میآید و data همین رشته است. |
url | باز کردن یک لینک. |
web_app | باز کردن مینیاپ (آدرس https). |
switch_inline_query | کاربر را به چت دیگری میبرد تا حالت اینلاین ربات را با آن عبارت شروع کند. |
جواب دادن به دکمه
وقتی callback_query آمد، حتماً با answerCallbackQuery جواب بدهید تا نشانهٔ «در حال بارگذاری» روی دکمه تمام شود. پارامترها: callback_query_id، text (پیام کوتاه)، show_alert (true = پنجرهٔ هشدار) و url (برای بازی).
برنامه فقط ۶ ثانیه منتظر جواب میماند. بعد از آن شناسهٔ callback منقضی میشود و answerCallbackQuery خطای query is too old… میدهد. جواب را اول بدهید و کار سنگین را بعدش.
if (update.callback_query) {
const q = update.callback_query;
await call("answerCallbackQuery", { callback_query_id: q.id, text: "ثبت شد" }); // اول جواب
await call("editMessageText", { // بعد پیام را عوض کن
chat_id: q.message.chat.id,
message_id: q.message.message_id,
text: q.data === "vote:yes" ? "رأی شما: بله" : "رأی شما: خیر",
});
}کیبورد معمولی
بهجای دکمههای زیر پیام، دکمههایی جای کیبورد مینشینند و با زدن، متن همان دکمه بهعنوان پیام فرستاده میشود:
{ "keyboard": [[{ "text": "منو" }, { "text": "سفارش من" }]], "resize_keyboard": true, "one_time_keyboard": true }- حذف کیبورد:
{"remove_keyboard": true} - مجبور کردن کاربر به ریپلای:
{"force_reply": true}
فایل و رسانه
فرستادن و گرفتن عکس، ویدیو، صدا و فایل.
متدهای sendPhoto، sendVideo، sendAnimation، sendAudio، sendVoice، sendDocument، sendSticker، sendVideoNote و sendMediaGroup (۱ تا ۱۰ مورد) هر کدام یک پارامتر فایل میگیرند. فایل را به سه شکل میتوانید بدهید:
۱. آدرس اینترنتی
یک آدرس http(s). سرور 7eve9 خودش دانلود میکند (تا ۵۰ مگابایت).
۲. file_id
شناسهٔ فایلی که قبلاً فرستاده یا گرفتهاید؛ بدون آپلود دوباره.
۳. آپلود (multipart)
خود فایل را در multipart/form-data بفرستید (تا ۵۰ مگابایت).
# با آدرس اینترنتی
curl "https://api.7echat.ir/bot$TOKEN/sendPhoto" -d chat_id=CHAT_ID \
-d photo=https://7echat.ir/site-assets/logo.png --data-urlencode "caption=لوگوی 7eve9"
# با آپلود فایل
curl "https://api.7echat.ir/bot$TOKEN/sendDocument" -F chat_id=CHAT_ID -F document=@report.pdfپاسخ شامل file_id و file_unique_id است؛ برای عکس، آرایهای از اندازهها:
"photo": [{ "file_id": "FL3VwbG9hZHMvYm90LTNh…", "file_unique_id": "Ay8TjbgtFWEs1QTG", "width": 192, "height": 192, "file_size": 28977 }]دانلود فایلی که کاربر فرستاده
- file_id را برداریداز آپدیت پیام (مثلاً
message.photo[-1].file_idیاmessage.document.file_id). getFileرا صدا بزنیدپاسخfile_pathدارد.- دانلود کنید
https://api.7echat.ir/file/bot<TOKEN>/<file_path>(فقط GET).
گروه و کانال
ربات در گروهها و کانالها، مدیریت و حریم خصوصی.
اضافه کردن ربات
اگر /setjoingroups روی Enable باشد (پیشفرض)، کاربر میتواند ربات را به گروه اضافه کند. ربات آپدیت my_chat_member میگیرد. برای کانال، ربات باید ادمین باشد تا channel_post بیاید.
حالت حریم خصوصی (Privacy mode)
بهطور پیشفرض ربات در گروه فقط اینها را میبیند: دستورات (/cmd یا /cmd@your_bot)، پیامهایی که منشنش میکنند، ریپلای به پیامهای خودش و ورود و خروج اعضا. برای دیدن همهٔ پیامها، با /setprivacy آن را Disable کنید یا ربات را ادمین گروه کنید.
شناسهٔ گروه
chat.id گروهها و کانالها منفی است. برای چت با یوزرنیم عمومی، @username هم کار میکند.
متدهای مدیریت
ربات باید در گروه ادمین باشد و همان دسترسی را داشته باشد (مثلاً «مسدود کردن کاربران»)؛ دسترسیها دقیقاً مثل ادمینهای انسانی اعمال میشوند.
def ban_if_reply(update):
m = update["message"]
if m.get("text", "").startswith("/ban") and "reply_to_message" in m:
call("banChatMember", chat_id=m["chat"]["id"],
user_id=m["reply_to_message"]["from"]["id"])
call("sendMessage", chat_id=m["chat"]["id"], text="کاربر مسدود شد.")متدهای گروه: banChatMember، unbanChatMember، restrictChatMember، promoteChatMember، setChatAdministratorCustomTitle، setChatPermissions، setChatTitle، setChatDescription، setChatPhoto، deleteChatPhoto، setChatSlowModeDelay، pinChatMessage، unpinChatMessage، unpinAllChatMessages، لینکهای دعوت (exportChatInviteLink، createChatInviteLink، editChatInviteLink، revokeChatInviteLink)، درخواستهای عضویت (approveChatJoinRequest، declineChatJoinRequest) و leaveChat. اطلاعات: getChat، getChatMember، getChatAdministrators، getChatMemberCount.
حالت اینلاین
استفاده از ربات در هر چتی، بدون اضافه کردنش.
با /setinline این حالت را روشن میکنید. کاربر در هر چتی مینویسد @your_bot عبارت و ربات آپدیت inline_query میگیرد؛ با answerInlineQuery فهرست نتیجه میدهید و کاربر یکی را انتخاب میکند تا همانجا فرستاده شود.
if (update.inline_query) {
const q = update.inline_query; // { id, from, query, offset }
await call("answerInlineQuery", {
inline_query_id: q.id,
cache_time: 10,
results: [
{ type: "article", id: "1", title: `جستجو: ${q.query}`,
input_message_content: { message_text: `نتیجهٔ ${q.query}` } },
],
});
}انواع نتیجه: article، photo، gif، mpeg4_gif، video، audio، voice، document، location، venue، contact و sticker. پارامترهای answerInlineQuery: results (تا ۵۰ مورد)، next_offset، cache_time، is_personal و button. اگر /setinlinefeedback را روشن کنید، انتخاب کاربر هم با chosen_inline_result میآید.
مثل callback، به inline_query هم باید سریع جواب بدهید؛ شناسهٔ قدیمی خطای query is too old… میدهد.
مینیاپ
یک وباپ کامل داخل پیامرسان.
مینیاپ یک صفحهٔ وب معمولی (HTML، CSS، JavaScript) روی سرور خودتان است که داخل 7eve9 باز میشود. مزیتش این است که کاربر را بدون ثبتنام میشناسد: برنامه یک رشتهٔ امضاشده به نام initData به صفحه میدهد و سرور شما امضا را بررسی میکند.
۱. اتصال به ربات
- دکمهٔ منو: در BotFather
/setmenubuttonو آدرس https (در خط دوم، متن دکمه). کنار کادر پیام یک دکمه ظاهر میشود. - دکمهٔ زیر پیام: در
reply_markupیک دکمه با{"text":"باز کردن","web_app":{"url":"https://…"}}. - مینیاپ اصلی:
/setwebapp(آدرس و متن دکمه). - با API:
setChatMenuButtonبا{"type":"web_app","text":"…","web_app":{"url":"https://…"}}.
آدرس مینیاپ حتماً https باشد.
۲. گرفتن initData در صفحه
برنامه داده را در بخش hash آدرس (بعد از #) میگذارد:
const params = new URLSearchParams(location.hash.slice(1));
const initData = params.get("tgWebAppData"); // رشتهٔ امضاشده؛ آن را به سرور خودتان بفرستید
const theme = JSON.parse(params.get("tgWebAppThemeParams") || "{}"); // رنگهای تم برنامه
const res = await fetch("/api/me", { headers: { "X-Init-Data": initData } });پروتکل مینیاپ با تلگرام سازگار است: صفحه با TelegramWebviewProxy.postEvent به برنامه پیام میدهد (مثل بستن یا تنظیم رنگ) و برنامه با window.Telegram.WebView.receiveEvent رویدادها (مثل تغییر تم) را به صفحه میدهد.
۳. بررسی امضا در سرور (حتماً)
initData را هرگز بدون بررسی امضا باور نکنید؛ هر کسی میتواند یک رشتهٔ ساختگی بفرستد. امضا با HMAC-SHA256 و توکن ربات ساخته میشود، دقیقاً مثل تلگرام:
- hash را جدا کنیدهمهٔ جفتهای
key=valueرا بخوانید وhashرا کنار بگذارید. - رشتهٔ بررسی بسازیدبقیه را بر اساس نام کلید مرتب کنید و با
\nبه هم بچسبانید. - کلید مخفی
HMAC_SHA256(key="WebAppData", data=bot_token) - مقایسه
HMAC_SHA256(key=secret, data=رشتهٔ بررسی)(hex) باید باhashبرابر باشد (مقایسهٔ زمانثابت). - تازگی
auth_dateرا چک کنید، مثلاً بیش از یک روز قدیمی رد شود.
import crypto from "node:crypto";
export function verifyInitData(initData, botToken, maxAgeSec = 86400) {
const p = new URLSearchParams(initData);
const hash = p.get("hash");
if (!hash) return null;
p.delete("hash");
const check = [...p.entries()].map(([k, v]) => `${k}=${v}`).sort().join("\n");
const secret = crypto.createHmac("sha256", "WebAppData").update(botToken).digest();
const ok = crypto.createHmac("sha256", secret).update(check).digest("hex");
if (ok.length !== hash.length || !crypto.timingSafeEqual(Buffer.from(ok), Buffer.from(hash))) return null;
if (Date.now() / 1000 - Number(p.get("auth_date") || 0) > maxAgeSec) return null;
return JSON.parse(p.get("user") || "{}"); // { id, first_name, last_name?, username?, language_code }
}import hmac, hashlib, json, time
from urllib.parse import parse_qsl
def verify_init_data(init_data: str, bot_token: str, max_age: int = 86400):
pairs = dict(parse_qsl(init_data, keep_blank_values=True))
got = pairs.pop("hash", None)
if not got:
return None
check = "\n".join(f"{k}={v}" for k, v in sorted(pairs.items()))
secret = hmac.new(b"WebAppData", bot_token.encode(), hashlib.sha256).digest()
ok = hmac.new(secret, check.encode(), hashlib.sha256).hexdigest()
if not hmac.compare_digest(ok, got):
return None
if time.time() - int(pairs.get("auth_date", 0)) > max_age:
return None
return json.loads(pairs.get("user", "{}"))۴. نکتهٔ مهم: نمایش داخل نسخهٔ وب
در اپ اندروید، مینیاپ داخل WebView باز میشود؛ اما در نسخهٔ وب (web.7echat.ir) داخل یک iframe است. پس سرور شما نباید جلوی قرار گرفتن در iframe را بگیرد:
- هدر
X-Frame-Options: DENYیاSAMEORIGINنفرستید. - اگر
Content-Security-Policyدارید، فقط این دو را مجاز کنید:frame-ancestors https://web.7echat.ir https://7echat.ir
علامت این مشکل: در اپ اندروید صفحه باز میشود ولی در نسخهٔ وب پیام «اجازهٔ نمایش داخل سایت دیگر را نمیدهد» میبینید.
۵. ارسال نتیجه به چت
answerWebAppQuery: نتیجهای (بهشکل یکInlineQueryResult) از طرف کاربر داخل چت فرستاده میشود.- مینیاپ میتواند با
sendDataدادهای به ربات بفرستد که بهشکل پیامweb_app_dataمیرسد.
بازیها
با /newgame بازی میسازید، با sendGame میفرستید و وقتی کاربر Play میزند، آپدیت callback_query با game_short_name میآید و با answerCallbackQuery(url=…) صفحهٔ بازی باز میشود.
ثبت و خواندن امتیاز بازی (setGameScore و getGameHighScores) هنوز در Bot API نیست و صدا زدنشان 404 میدهد.
مرجع متدها
همهٔ ۸۳ متدی که الان کار میکنند. نامها و پارامترها مثل Bot API تلگرام است.
متدی که اینجا نیست (مثل پرداخت، مدیریت استیکرست یا تاپیکهای فروم) جواب 404 Not Found: method not found میدهد.
کتابخانهها
کتابخانههای آمادهٔ تلگرام را فقط با عوض کردن آدرس سرور استفاده کنید.
from telegram.ext import Application
app = (Application.builder().token(TOKEN)
.base_url("https://api.7echat.ir/bot")
.base_file_url("https://api.7echat.ir/file/bot")
.build())from aiogram import Bot
from aiogram.client.session.aiohttp import AiohttpSession
from aiogram.client.telegram import TelegramAPIServer
session = AiohttpSession(api=TelegramAPIServer.from_base("https://api.7echat.ir"))
bot = Bot(TOKEN, session=session)import { Telegraf } from "telegraf";
const bot = new Telegraf(TOKEN, { telegram: { apiRoot: "https://api.7echat.ir" } });import TelegramBot from "node-telegram-bot-api";
const bot = new TelegramBot(TOKEN, { polling: true, baseApiUrl: "https://api.7echat.ir" });import telebot
telebot.apihelper.API_URL = "https://api.7echat.ir/bot{0}/{1}"
bot = telebot.TeleBot(TOKEN)نمونههای کامل
کدهایی که میتوانید کپی کنید و اجرا کنید.
ربات با دکمه (long polling)
به /start سلام میدهد و دو دکمه نشان میدهد؛ با زدن هر دکمه پیام ویرایش میشود.
// node bot.mjs (Node 18 به بالا؛ متغیر BOT_TOKEN را تنظیم کنید)
const TOKEN = process.env.BOT_TOKEN;
const API = "https://api.7echat.ir";
async function call(method, params = {}) {
const r = await fetch(`${API}/bot${TOKEN}/${method}`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(params),
});
const j = await r.json();
if (!j.ok) throw new Error(`${method}: ${j.error_code} ${j.description}`);
return j.result;
}
const menu = { inline_keyboard: [[
{ text: "بله", callback_data: "vote:yes" },
{ text: "خیر", callback_data: "vote:no" },
]] };
let offset = 0;
console.log("ربات روشن شد");
for (;;) {
try {
const updates = await call("getUpdates", { offset, timeout: 30, allowed_updates: ["message", "callback_query"] });
for (const u of updates) {
offset = u.update_id + 1;
if (u.message?.text === "/start") {
await call("sendMessage", { chat_id: u.message.chat.id, text: "سلام! نظر شما؟", reply_markup: menu });
} else if (u.message?.text) {
await call("sendMessage", { chat_id: u.message.chat.id, text: `گفتید: ${u.message.text}` });
}
if (u.callback_query) {
const q = u.callback_query;
await call("answerCallbackQuery", { callback_query_id: q.id, text: "ثبت شد" });
await call("editMessageText", {
chat_id: q.message.chat.id,
message_id: q.message.message_id,
text: q.data === "vote:yes" ? "رأی شما: بله" : "رأی شما: خیر",
});
}
}
} catch (e) {
console.error(e.message);
await new Promise((r) => setTimeout(r, 3000));
}
}import os, time, requests
TOKEN = os.environ["BOT_TOKEN"]
API = "https://api.7echat.ir"
def call(method, **params):
r = requests.post(f"{API}/bot{TOKEN}/{method}", json=params, timeout=70).json()
if not r["ok"]:
raise RuntimeError(f'{method}: {r["error_code"]} {r["description"]}')
return r["result"]
menu = {"inline_keyboard": [[
{"text": "بله", "callback_data": "vote:yes"},
{"text": "خیر", "callback_data": "vote:no"},
]]}
offset = 0
print("ربات روشن شد")
while True:
try:
for u in call("getUpdates", offset=offset, timeout=30, allowed_updates=["message", "callback_query"]):
offset = u["update_id"] + 1
m = u.get("message")
if m and m.get("text") == "/start":
call("sendMessage", chat_id=m["chat"]["id"], text="سلام! نظر شما؟", reply_markup=menu)
elif m and m.get("text"):
call("sendMessage", chat_id=m["chat"]["id"], text=f'گفتید: {m["text"]}')
q = u.get("callback_query")
if q:
call("answerCallbackQuery", callback_query_id=q["id"], text="ثبت شد")
call("editMessageText", chat_id=q["message"]["chat"]["id"],
message_id=q["message"]["message_id"],
text="رأی شما: بله" if q["data"] == "vote:yes" else "رأی شما: خیر")
except Exception as e:
print(e)
time.sleep(3)وبهوک با Express
import express from "express";
const TOKEN = process.env.BOT_TOKEN;
const SECRET = process.env.HOOK_SECRET; // همان secret_token که در setWebhook دادید
const app = express();
app.use(express.json());
app.post("/hook", async (req, res) => {
if (req.get("X-Telegram-Bot-Api-Secret-Token") !== SECRET) return res.sendStatus(403);
res.sendStatus(200); // اول جواب بدهید؛ پردازش بعدش
const m = req.body.message;
if (m?.text) {
await fetch(`https://api.7echat.ir/bot${TOKEN}/sendMessage`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ chat_id: m.chat.id, text: `گفتید: ${m.text}` }),
});
}
});
app.listen(3000);
// سپس: setWebhook?url=https://your.site/hook&secret_token=...سرور مینیاپ (Express + بررسی امضا)
import express from "express";
import { verifyInitData } from "./verify.js"; // تابع بخش مینیاپ
const app = express();
app.use((_req, res, next) => {
// اجازهٔ نمایش داخل نسخهٔ وب (iframe)
res.set("Content-Security-Policy", "frame-ancestors https://web.7echat.ir https://7echat.ir");
next();
});
app.use(express.static("public")); // صفحهٔ HTML مینیاپ
app.get("/api/me", (req, res) => {
const user = verifyInitData(req.get("X-Init-Data") || "", process.env.BOT_TOKEN);
if (!user) return res.status(401).json({ error: "initData نامعتبر است" });
res.json({ id: user.id, name: user.first_name });
});
app.listen(3000);محدودیتها و تفاوتها
اعدادی که باید بدانید، و تفاوتها با Bot API تلگرام.
| مورد | مقدار |
|---|---|
| طول متن پیام | حداکثر ۴۰۹۶ نویسه |
| حجم آپلود فایل | حداکثر ۵۰ مگابایت |
sendMediaGroup | ۱ تا ۱۰ مورد |
getUpdates | timeout تا ۵۰ ثانیه، limit ۱ تا ۱۰۰، نگهداری آپدیتها ۲۴ ساعت |
| وبهوک | مهلت ۱۵ ثانیه، بدون redirect، ۳ تلاش مجدد (۵ ثانیه، ۳۰ ثانیه، ۲ دقیقه)، فقط آدرس عمومی |
| پاسخ به callback | ۶ ثانیه |
| تعداد ربات | تا ۲۰ ربات برای هر حساب |
| نام ربات / بیو | ۶۴ نویسه / ۱۲۰ نویسه |
| نرخ درخواست | حدود ۲۰ درخواست در ثانیه برای هر آدرس IP و حدود ۳۰ درخواست در ثانیه برای هر ربات (با اندکی تحمل برای اوجهای کوتاه). بیشتر از آن 429 میگیرید. |
| نرخ ارسال پیام | تا ۳۰ پیام در ثانیه برای هر ربات، تا ۱۵ پیام در ۱۰ ثانیه به هر کاربر، و تا ۲۰ پیام در دقیقه در هر گروه یا کانال. بیشتر از آن 429 با parameters.retry_after (ثانیه) برمیگردد. |
| حجم فایل ذخیرهشده | هر ربات در هر ساعت تا ۳۰۰ مگابایت فایل (آپلود یا دریافت از آدرس) میتواند اضافه کند. |
getUpdates همزمان | حداکثر ۳ درخواست long polling همزمان برای هر ربات؛ بیشتر از آن 409. |
| توکن اشتباه | بعد از حدود ۳۰ توکن نادرست در یک دقیقه از یک آدرس، درخواستهای با توکن اشتباه 429 میگیرند. توکن درست هیچوقت تحت تأثیر نیست. |
قواعد امنیتی
این قواعد هم از کاربران، هم از خود رباتها و هم از پیامرسان محافظت میکنند:
- ربات فقط چتهایی را میبیند که عضوشان است. خواندن، فوروارد یا کپی از گروه یا کانالی که ربات در آن نیست، جواب
chat not foundمیگیرد (مگر گروه یا کانال عمومی با@username). - کاربر میتواند ربات را مسدود کند. ربات به کسی که آن را مسدود کرده پیام نمیتواند بفرستد (
403 bot was blocked by the user). - ربات پیام اول را نمیفرستد و عکس پروفایل را فقط برای کسانی میبیند که با آن چت دارند (
getUserProfilePhotos). - دکمهها اعتبارسنجی میشوند:
callback_dataحداکثر ۶۴ بایت، دکمهٔurlفقطhttp(s)://یاtg://، وweb_appفقطhttps. نسخههای ناامن (مثلjavascript:) رد میشوند. - حذف پیام کاربر در چت خصوصی فقط تا ۴۸ ساعت بعد از ارسال ممکن است.
- نام و یوزرنیمهایی که شبیه خود پیامرسان هستند (مثل 7eve9، botfather، support، پشتیبانی، رسمی) برای جلوگیری از جعل هویت رزرو شدهاند.
- آدرس وبهوک و فایلها هرگز به شبکهٔ داخلی یا سرورهای خود 7eve9 دسترسی ندارند، و برای فایلهای دریافتی فقط پسوندهای امن نگه داشته میشود.
- آپلود بدون توکن ممکن نیست: درخواست ابتدا احراز هویت میشود، بعد بدنه خوانده میشود.
فرق با Bot API تلگرام
- ربات نمیتواند اول به کاربر پیام بدهد؛ کاربر باید Start کند.
- اینها وجود ندارند و
404میدهند: پرداخت و فاکتور، مدیریت استیکرست، تاپیکهای فروم، Passport،setGameScoreوgetGameHighScores. getUserChatBoostsوgetAvailableGiftsبرای سازگاری هستند و فهرست خالی برمیگردانند.setChatSlowModeDelayیک متد اضافه است (در تلگرام نیست).- وبهوک: آدرس باید عمومی باشد (آدرسهای محلی پذیرفته نمیشوند).
عیبیابی
خطاهای رایج و راه رفعشان.
| پیام | علت و راه حل |
|---|---|
401 Unauthorized | توکن اشتباه است یا باطل شده. از /token دوباره بگیرید و مطمئن شوید بین توکن و bot در آدرس فاصله یا نویسهٔ اضافه نیست. |
400 chat not found | این کاربر هنوز ربات را Start نکرده، یا chat_id اشتباه است. شناسه را از آپدیت بردارید، نه از حدس. |
409 Conflict … webhook is active | وبهوک فعال است؛ یا deleteWebhook بزنید یا از وبهوک استفاده کنید. چند نمونه از یک ربات همزمان با getUpdates کار نکنند. |
400 message is too long | متن بیشتر از ۴۰۹۶ نویسه است؛ تکهتکه بفرستید. |
400 message text is empty | text خالی یا فرستاده نشده است. |
400 bad webhook: the URL must point to a public address | آدرس وبهوک محلی یا داخلی است؛ یک آدرس عمومی (ترجیحاً https) بدهید. |
400 query is too old… | دیر به callback یا inline query جواب دادهاید (callback: ۶ ثانیه). جواب را اول بدهید. |
400 invalid file_id | شناسهٔ فایل نامعتبر است. از همان ربات و از آپدیت یا پاسخ قبلی بگیرید. |
404 method not found | نام متد غلط است یا متد در 7eve9 نیست (مرجع). |
429 Too Many Requests | سرعت درخواستها زیاد است؛ کمی صبر کنید. بهجای درخواستهای پشتسرهم از getUpdates با timeout استفاده کنید. |
| مینیاپ در وب باز نمیشود | سرور شما جلوی iframe را میگیرد؛ بخش ۴ مینیاپ. |
سؤال یا مشکلی بود؟ داخل برنامه چت «پشتیبانی 7eve9Chat» را باز کنید و بنویسید.
دربارهٔ تیم 7eve9
ما تیمی از توسعهدهندگان ایرانی هستیم که تصمیم گرفتیم یک پیامرسان مستقل را از پایه بسازیم؛ پیامرسانی که سرورهایش داخل ایران است و با سرعت و روان کار میکند، و همهٔ کارهایی که روزانه از اینترنت و موبایلتان میخواهید را در همان یک جا برایتان فراهم کند.
گفتگو و تماس، گروه و کانال، ربات و مینیاپ، نقشهٔ ایران و جستجو، مترجم آفلاین، ویکیپدیا، حتی بازی با دوستان؛ همه روی همین بستر و روی زیرساخت خودمان اجرا میشود. این مستندات هم برای این است که شما هم بتوانید خدمات خودتان را روی همین بستر بسازید.
برای ارتباط با ما، داخل برنامه چت «پشتیبانی 7eve9Chat» را باز کنید.