وثائق المطوّرين
العربية

تطوير البوتات

إنشاء بوت

أنشئ بوتاً عبر ‎@BotFather‎، واستقبل الرسائل وردّ عليها، ثم الأزرار والأوامر والصور والمجموعات — درس تطبيقي تتبعه من البداية إلى النهاية.

بنهاية هذا الدرس سيكون لديك بوت يرد على الرسائل، ويضيف الأزرار، ويتعامل مع قائمة الأوامر والصور، ويعمل في المجموعات أيضاً. كل ما تحتاج إليه هو تطبيق Pabal، وحاسوب لتشغيل برنامج البوت، وواحد من الاثنين: Python 3.10 أو أحدث، أو Node.js 18 أو أحدث.

كيف يعمل البوت

البوت في Pabal حساب يتحكم فيه برنامج. الرسائل المرسلة إلى البوت تتجمع في صندوق رسائله، ويسأل برنامج البوت الخادم «هل وصل شيء جديد؟» (getUpdates) ليأخذها، ثم يرسل الرد (sendMessage). ويعمل برنامج البوت خارج الخادم، على حاسوبك أنت.

⁧شخص (التطبيق)⁩ ⁧خادم Pabal⁩ برنامج البوت ① getUpdates?offset=0&timeout=30 ⁧ينتظر 30 ثانية كحد أقصى حتى يصل جديد⁩ ⁧② يرسل «مرحباً»⁩ ③ [{update_id: 1, message: "مرحباً"}] ④ sendMessage(chat_id, "أهلاً بك!") ⁧⑤ يصل الرد إلى التطبيق (فوراً)⁩ ⑥ getUpdates?offset=2 — استُلم رقم 1، والآن التالي
الترتيب الأساسي: الاستلام عبر getUpdates والرد عبر sendMessage

شرح المخطط

  • ثلاثة خطوط: على اليسار الشخص الذي يستخدم التطبيق، وفي الوسط خادم Pabal، وعلى اليمين برنامج البوت الخاص بك. والخطوط العمودية المنقّطة تمثّل اتجاه مرور الوقت.
  • الأسهم الرمادية طلبات يبدأها البوت، والأسهم الزرقاء هي الرسائل التي تنتقل نتيجةً لها. برنامج البوت يرسل الطلبات فقط، ولا يبادر الخادم بالاتصال بالبوت (ما لم تستخدم webhook).
  • الانتظار في ① (الاستطلاع الطويل، long polling) هو الأساس: عندما تمرّر timeout=30 لا يعيد الخادم استجابة فارغة فوراً إن لم يكن هناك جديد، بل يحتفظ بالطلب حتى 30 ثانية، ويعيده (③) لحظة وصول رسالة (②). لذلك تكون الاستجابة سريعة وعدد الطلبات قليلاً.
  • قيمة offset في ⑥ هي علامة «تم الاستلام»: إذا أرسلت آخر update_id عالجته مضافاً إليه 1، يحذف الخادم ذلك التحديث وكل ما قبله. وإذا لم ترفع offset فستستلم التحديث نفسه مراراً.
  • ⑤ يسلك الطريق نفسه الذي تسلكه رسائل الأشخاص: يُدفع رد البوت إلى جميع أجهزة التطبيق، ويظهر في قائمة المحادثات أيضاً.

1. إنشاء بوت عبر ‎@BotFather

تُنشئ البوت بالتحدث إلى @BotFather داخل تطبيق Pabal. وBotFather بوت موجود داخل خادم Pabal نفسه.

  1. اكتب BotFather في مربع البحث في التطبيق وافتح BotFather. عندما تضغط ابدأ تصلك قائمة الأوامر.
  2. أرسل /newbot.
  3. أرسل اسم البوت. هذا هو الاسم الظاهر في قائمة المحادثات، لذا يمكن كتابته بالعربية أيضاً.
  4. أرسل اسم المستخدم للبوت. يتكوّن من 5 إلى 32 حرفاً من الأحرف اللاتينية والأرقام والشرطة السفلية، ويبدأ بحرف لاتيني، ويجب أن ينتهي بـ bot.
  5. عندما يصلك الرد الذي يحتوي على الرمز المميّز (token) تكون قد انتهيت. انسخ الرمز واحتفظ به.
/newbot
BotFather새 봇을 만듭니다. 봇의 이름을 알려 주세요. (대화 목록에 보이는 이름이에요)
بوت مرحباً
BotFather좋아요. 이제 봇의 사용자명을 정해 주세요. …
hello_test_bot
BotFather완료! 새 봇 @hello_test_bot 를 만들었어요. 검색해서 대화를 시작할 수 있어요. 봇 토큰: 100003:AbCdEfGhIjKlMnOpQrStUvWxYz0123456789 토큰은 비밀번호처럼 안전하게 보관하세요. …

يرد BotFather حالياً باللغة الكورية. في هذه المحادثة يطلب أولاً اسم البوت (الاسم الذي يظهر في قائمة المحادثات)، ثم اسم المستخدم للبوت، وأخيراً يعلن أن البوت @hello_test_bot قد أُنشئ، ويسلّمك الرمز المميّز للبوت (السطر الذي يبدأ بـ 100003:) مع تنبيه بأن تحفظه بأمان كما تحفظ كلمة المرور.

أمر BotFatherما يفعله
/newbotإنشاء بوت جديد (الاسم ← اسم المستخدم ← الرمز المميّز)
/mybotsقائمة البوتات التي أنشأتها
/tokenعرض الرمز المميّز للبوت مرة أخرى
/revokeإصدار رمز مميّز جديد — يُلغى الرمز السابق فوراً، وتُقطع أيضاً الاتصالات التي كانت قائمة به
/setcommandsضبط قائمة الأوامر (command - description، أي الأمر ثم وصفه، في كل سطر)
/deletebotحذف البوت — للتأكيد أرسل 네, 삭제합니다 (أي «نعم، احذفه»). ويُحرَّر اسم المستخدم ليصبح قابلاً للاستخدام من جديد
/cancelإلغاء العملية الجارية

إذا أرسلت الأمر مع اسم المستخدم، مثل /token @hello_test_bot، فستتخطى خطوة «أي بوت؟».

2. التعامل مع الرمز المميّز

الرمز المميّز بالشكل <bot ID>:<secret>. الرقم الذي في البداية هو معرّف المستخدم الخاص بالبوت، وما بعده هو السر. وبما أن رمزاً واحداً يكفي للتحكم الكامل في البوت، فتعامل معه ككلمة مرور.

  • لا تكتبه في الشيفرة، بل ضعه في متغير بيئة (BOT_TOKEN) أو في مخزن للأسرار. ولا ترفعه إلى مستودع عام.
  • إذا تسرّب فأرسل /revoke إلى BotFather. عندها يُرفض الرمز السابق فوراً (401 Unauthorized)، وتُقطع أيضاً جلسات البوت التي كانت متصلة بـ MTProto بالرمز السابق.
  • بما أن الرمز يوضع داخل العنوان (URL)، فاحرص على ألا يسجّل برنامج البوت عناوين الطلبات في سجلاته. وخادم Pabal أيضاً لا يسجّل عناوين Bot API.

3. الطلب الأول — getMe

عنوان كل الطلبات هو https://pabal.me/bot<token>/<method>. لنتحقق من صحة الرمز عبر getMe.

export BOT_TOKEN='100003:AbCdEf…'
curl -s "https://pabal.me/bot$BOT_TOKEN/getMe"
# pip install requests
import os
import requests

r = requests.get(f"https://pabal.me/bot{os.environ['BOT_TOKEN']}/getMe", timeout=10)
print(r.json())
// Node.js 18 أو أحدث — fetch مدمجة فيه
const res = await fetch(`https://pabal.me/bot${process.env.BOT_TOKEN}/getMe`);
console.log(await res.json());

عند النجاح تصلك استجابة كهذه. كل الاستجابات بصيغة JSON تحتوي على ok وresult (عند النجاح) أو error_code وdescription (عند الفشل).

{
  "ok": true,
  "result": {
    "id": 100003,
    "is_bot": true,
    "first_name": "بوت مرحباً",
    "username": "hello_test_bot",
    "can_join_groups": true,
    "can_read_all_group_messages": true,
    "supports_inline_queries": false,
    "can_connect_to_business": false,
    "has_main_web_app": false
  }
}

إذا كان الرمز خاطئاً فستصلك HTTP 401 مع {"ok": false, "error_code": 401, "description": "Unauthorized"}.

4. استلام الرسائل — getUpdates

افتح البوت في التطبيق واضغط ابدأ أو أرسل أي شيء، ثم اجلب التحديثات الجديدة.

curl -s "https://pabal.me/bot$BOT_TOKEN/getUpdates?timeout=30"
{
  "ok": true,
  "result": [
    {
      "update_id": 1,
      "message": {
        "message_id": 1,
        "from": { "id": 100001, "is_bot": false, "first_name": "هناء" },
        "chat": { "id": 100001, "first_name": "هناء", "type": "private" },
        "date": 1789805661,
        "text": "/start",
        "entities": [ { "type": "bot_command", "offset": 0, "length": 6 } ]
      }
    }
  ]
}
  • update_id: رقم يزيد بمقدار 1 مع كل تحديث. بعد المعالجة، إذا مرّرت offset=update_id+1 في الطلب التالي، يُحذف ذلك التحديث وما قبله باعتبارها «مُستلَمة».
  • timeout: عدد الثواني التي ينتظرها الخادم عندما لا يوجد جديد (من 0 إلى 50). إذا كانت 0 يعيد قائمة فارغة فوراً. ننصح بقيمة بين 25 و30.
  • chat.id: الوجهة التي ترسل إليها الرد. في المحادثة الفردية هو معرّف الشخص (عدد موجب)، وفي المجموعة عدد سالب.
  • التحديثات التي يمكن استلامها ثلاثة أنواع: message (رسالة جديدة)، وedited_message (رسالة معدّلة)، وcallback_query (ضغط زر).
قائمة الانتظار في ذاكرة الخادم

التحديثات التي لم تُجلب تتراكم في ذاكرة الخادم، حتى آخر 1,000 تحديث لكل بوت. وعند إعادة تشغيل الخادم يضيع ما لم يُجلب منها (أما الرسائل نفسها فتبقى في المحادثة). فلا تترك البوت متوقفاً مدة طويلة.

5. إرسال الرد — sendMessage

curl -s "https://pabal.me/bot$BOT_TOKEN/sendMessage" \
  -H 'Content-Type: application/json' \
  -d '{"chat_id": 100001, "text": "أهلاً بك!"}'
requests.post(f"https://pabal.me/bot{os.environ['BOT_TOKEN']}/sendMessage",
              json={"chat_id": 100001, "text": "أهلاً بك!"}, timeout=10)
await fetch(`https://pabal.me/bot${process.env.BOT_TOKEN}/sendMessage`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ chat_id: 100001, text: 'أهلاً بك!' }),
});

يمكنك إرسال المعاملات بالصيغة التي تناسبك: جسم JSON، أو نموذج (application/x-www-form-urlencoded)، أو multipart/form-data عند رفع الملفات، أو سلسلة استعلام بعد العنوان. والنتيجة هي الرسالة المرسلة (Message).

لا يستطيع البوت أن يبدأ المحادثة

لا يمكن للبوت أن يرسل إلى شخص إلا بعد أن يرسل ذلك الشخص إليه رسالة واحدة على الأقل. وإلا فالنتيجة 403 Forbidden: bot can't initiate conversation with a user. وهي القاعدة نفسها المعمول بها في Telegram.

6. إكمال بوت يكرّر ما يُقال له

بتكرار الاستلام والإرسال يصبح لديك بوت. هذه هي الشيفرة الكاملة مكتوبة دون أي مكتبة.

# echo.py — pip install requests
# التشغيل: BOT_TOKEN='100003:…' python3 echo.py
import os
import requests

API = f"https://pabal.me/bot{os.environ['BOT_TOKEN']}"


def call(method, **params):
    r = requests.post(f"{API}/{method}", json=params, timeout=60)
    data = r.json()
    if not data["ok"]:
        raise RuntimeError(f"{method}: {data['description']}")
    return data["result"]


offset = 0
print("البوت يعمل الآن. للإيقاف اضغط Ctrl+C")
while True:
    for update in call("getUpdates", offset=offset, timeout=30):
        offset = update["update_id"] + 1          # علامة «تم الاستلام»
        message = update.get("message")
        if message and "text" in message:
            call("sendMessage", chat_id=message["chat"]["id"], text=message["text"])
// echo.mjs — Node.js 18 أو أحدث، دون مكتبة
// التشغيل: BOT_TOKEN='100003:…' node echo.mjs
const API = `https://pabal.me/bot${process.env.BOT_TOKEN}`;

async function call(method, params = {}) {
  const res = await fetch(`${API}/${method}`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(params),
  });
  const data = await res.json();
  if (!data.ok) throw new Error(`${method}: ${data.description}`);
  return data.result;
}

let offset = 0;
console.log('البوت يعمل الآن. للإيقاف اضغط Ctrl+C');
for (;;) {
  const updates = await call('getUpdates', { offset, timeout: 30 });
  for (const update of updates) {
    offset = update.update_id + 1;              // علامة «تم الاستلام»
    const message = update.message;
    if (message?.text) {
      await call('sendMessage', { chat_id: message.chat.id, text: message.text });
    }
  }
}

7. البناء باستخدام مكتبة

تحتوي مكتبات بوتات Telegram على إعداد لتغيير عنوان الخادم، وبهذا الإعداد وحده تعمل على Pabal كما هي. وعند نقل بوت مبني لـ Telegram يكفي أن تغيّر هذا الإعداد، وأن تحصل على رمز مميّز جديد من BotFather في Pabal.

المكتبةالإعداد الذي تغيّرهالإصدار الذي تحققنا منه
python-telegram-bot.base_url("https://pabal.me/bot")، .base_file_url("https://pabal.me/file/bot")22.8
aiogramAiohttpSession(api=TelegramAPIServer.from_base("https://pabal.me"))3.31
دون مكتبة (HTTP)بداية العنوان: https://api.telegram.orghttps://pabal.me
# hello_bot.py — pip install python-telegram-bot
# التشغيل: BOT_TOKEN='100003:…' python3 hello_bot.py
import os

from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update
from telegram.ext import (Application, CallbackQueryHandler, CommandHandler, ContextTypes,
                          MessageHandler, filters)

SERVER = "https://pabal.me"


def buttons():
    return InlineKeyboardMarkup([[InlineKeyboardButton("👍 أعجبني", callback_data="like"),
                                  InlineKeyboardButton("🔢 زيادة العدد", callback_data="count")]])


async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
    await update.message.reply_text("أهلاً بك! جرّب الضغط على الأزرار.", reply_markup=buttons())


async def button(update: Update, context: ContextTypes.DEFAULT_TYPE):
    query = update.callback_query
    if query.data == "like":
        await query.answer("شكراً لك!")                  # نص يظهر لحظات على شاشة من ضغط الزر
    else:
        n = context.chat_data.get("n", 0) + 1
        context.chat_data["n"] = n
        await query.answer()                             # أجب أولاً
        await query.edit_message_text(f"العدد: {n}", reply_markup=buttons())   # ثم عدّل الرسالة


async def echo(update: Update, context: ContextTypes.DEFAULT_TYPE):
    await update.message.reply_text(f"قلتَ: '{update.message.text}'")


def main():
    app = (Application.builder().token(os.environ["BOT_TOKEN"])
           .base_url(f"{SERVER}/bot")                   # Pabal بدلاً من api.telegram.org
           .base_file_url(f"{SERVER}/file/bot")
           .build())
    app.add_handler(CommandHandler("start", start))
    app.add_handler(CallbackQueryHandler(button))
    app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, echo))
    print("البوت يعمل الآن. للإيقاف اضغط Ctrl+C")
    app.run_polling()


if __name__ == "__main__":
    main()
# echo_aiogram.py — pip install aiogram
# التشغيل: BOT_TOKEN='100003:…' python3 echo_aiogram.py
import asyncio
import os

from aiogram import Bot, Dispatcher
from aiogram.client.session.aiohttp import AiohttpSession
from aiogram.client.telegram import TelegramAPIServer
from aiogram.filters import CommandStart

dp = Dispatcher()


@dp.message(CommandStart())
async def start(message):
    await message.answer("أهلاً بك! أرسل إليّ أي شيء.")


@dp.message()
async def echo(message):
    if message.text:
        await message.answer(message.text)


async def main():
    session = AiohttpSession(api=TelegramAPIServer.from_base("https://pabal.me"))   # Pabal
    bot = Bot(os.environ["BOT_TOKEN"], session=session)
    print("البوت يعمل الآن. للإيقاف اضغط Ctrl+C")
    await dp.start_polling(bot)


asyncio.run(main())

8. الأزرار والاستدعاءات (callbacks)

لإضافة أزرار مضمّنة (inline) إلى رسالة، مرّر في reply_markup الحقل inline_keyboard (مصفوفة من الصفوف، وكل صف مصفوفة من الأزرار). والأزرار نوعان: زر callback_data الذي يُبلغ البوت عند الضغط عليه، وزر url الذي يفتح رابطاً.

curl -s "https://pabal.me/bot$BOT_TOKEN/sendMessage" -H 'Content-Type: application/json' -d '{
  "chat_id": 100001,
  "text": "اختر من فضلك",
  "reply_markup": {
    "inline_keyboard": [
      [ {"text": "👍 أعجبني", "callback_data": "like"}, {"text": "👎 لم يعجبني", "callback_data": "dislike"} ],
      [ {"text": "افتح وثائق Pabal", "url": "https://pabal.me/docs/"} ]
    ]
  }
}'

عندما يضغط شخص زر callback_data يستلم البوت تحديث callback_query.

{
  "update_id": 7,
  "callback_query": {
    "id": "5812039457730125441",
    "from": { "id": 100001, "is_bot": false, "first_name": "هناء" },
    "message": { "message_id": 4, "chat": { "id": 100001, "type": "private", "first_name": "هناء" }, "text": "اختر من فضلك", … },
    "chat_instance": "8413962145072395171",
    "data": "like"
  }
}

على البوت أن يرد خلال 10 ثوانٍ عبر answerCallbackQuery. وفي أثناء ذلك يعرض التطبيق علامة ساعة على الزر وينتظر.

# نص يظهر لحظات أعلى الشاشة (ومع show_alert: true تظهر نافذة تأكيد)
curl -s "https://pabal.me/bot$BOT_TOKEN/answerCallbackQuery" -H 'Content-Type: application/json' \
  -d '{"callback_query_id": "5812039457730125441", "text": "شكراً لك!"}'

# غيّر نص الرسالة التي ضُغط زرها وأزرارها
curl -s "https://pabal.me/bot$BOT_TOKEN/editMessageText" -H 'Content-Type: application/json' \
  -d '{"chat_id": 100001, "message_id": 4, "text": "ضغطتَ «أعجبني» 👍"}'
  • طول callback_data من 1 إلى 64 بايت. والحد الأقصى 100 زر في الرسالة الواحدة.
  • إذا لم تُجب على الاستدعاء، يتوقف التطبيق عن الانتظار بعد 10 ثوانٍ. وإذا كان البوت متوقفاً، ينهي الخادم الانتظار فوراً.
  • الرد صالح في المرة الأولى فقط. بعض المكتبات ترسل رداً فارغاً أولاً عند تعديل الرسالة، لذا أرسل الرد الذي يحمل نصاً أولاً.
  • إذا عدّلت الرسالة بالنص نفسه والأزرار نفسها فالنتيجة 400 Bad Request: message is not modified (كما في Telegram).

لوحة المفاتيح أسفل حقل الإدخال

عندما تمرّر keyboard تظهر لوحة أزرار بدلاً من حقل الإدخال، وعند الضغط على زر يُرسل نصه كرسالة. أخفِها بـ remove_keyboard، وفعّل وضع الرد بـ force_reply.

{
  "chat_id": 100001,
  "text": "أيّهما تختار؟",
  "reply_markup": {
    "keyboard": [ [ {"text": "نعم"}, {"text": "لا"} ], [ {"text": "أرسل موقعي", "request_location": true} ] ],
    "resize_keyboard": true,
    "one_time_keyboard": true
  }
}

9. قائمة الأوامر

هي القائمة التي تظهر عندما تضغط / في نافذة المحادثة أو تضغط زر القائمة. تحددها في الشيفرة أو عبر الأمر /setcommands لدى BotFather.

curl -s "https://pabal.me/bot$BOT_TOKEN/setMyCommands" -H 'Content-Type: application/json' -d '{
  "commands": [
    {"command": "start", "description": "البدء"},
    {"command": "help",  "description": "المساعدة"}
  ]
}'

الأمر من 1 إلى 32 حرفاً من الأحرف اللاتينية الصغيرة والأرقام والشرطة السفلية، والوصف من 1 إلى 256 حرفاً، وبحد أقصى 100 أمر. إذا مرّرت language_code تُحفظ قائمة منفصلة لكل لغة، لكن التطبيق لا يعرض حالياً إلا القائمة الافتراضية المحددة دون رمز لغة. والأوامر التي يرسلها الأشخاص، مثل /start، تصل مُعلَّمة بالنوع bot_command في entities الخاصة بالرسالة.

10. إرسال الصور واستلامها

الإرسال

ارفع الملف بصيغة multipart/form-data، أو أعد استخدام file_id لصورة استلمتها من قبل. الحد الأقصى للصورة 10MB، وللوصف (caption) 1,024 حرفاً. والإرسال عبر URL غير مدعوم بعد.

curl -s "https://pabal.me/bot$BOT_TOKEN/sendPhoto" \
  -F chat_id=100001 -F caption='صورة اليوم' -F photo=@sunset.jpg
with open("sunset.jpg", "rb") as f:
    requests.post(f"{API}/sendPhoto", data={"chat_id": 100001, "caption": "صورة اليوم"},
                  files={"photo": f}, timeout=60)

الاستلام

تصل الصورة التي يرسلها شخص في الحقل photo من الرسالة (قائمة بحسب الحجم، وفي Pabal عنصر واحد هو الأصل). احصل على المسار عبر getFile ثم نزّلها.

# 1) file_id → file_path
curl -s "https://pabal.me/bot$BOT_TOKEN/getFile?file_id=AQAAAAAAAAB7…"
# {"ok":true,"result":{"file_id":"AQAA…","file_unique_id":"AQAA…","file_size":48213,"file_path":"photos/AQAA….jpg"}}

# 2) التنزيل — يحتوي العنوان على /file/
curl -s -o photo.jpg "https://pabal.me/file/bot$BOT_TOKEN/photos/AQAA….jpg"

11. في المجموعات

  • أضف البوت عند إنشاء المجموعة في التطبيق، أو من معلومات المجموعة ← إضافة أعضاء، بالبحث عن اسم المستخدم الخاص به.
  • البوت الذي ينضم إلى مجموعة يستلم كل رسائل المجموعة (مثل «وضع الخصوصية مُعطّل» في Telegram). وتكون قيمة chat.type هي "group"، وقيمة chat.id عدداً سالباً.
  • إذا استدعيت sendMessage بقيمة chat.id هذه فستُرسل الرسالة إلى المجموعة. والأزرار والصور والتعديل تعمل تماماً كما في المحادثة الفردية.
  • إذا خرج البوت من المجموعة فالإرسال إليها يعيد 403 Forbidden: bot is not a member of the group chat.

12. التحويل إلى webhook

إذا كان بوتك يعمل على خادم له عنوان HTTPS عام، فبدلاً من السؤال عبر getUpdates يمكنك أن تجعل خادم Pabal يرسل التحديثات الجديدة إلى ذلك العنوان.

curl -s "https://pabal.me/bot$BOT_TOKEN/setWebhook" -H 'Content-Type: application/json' \
  -d '{"url": "https://bot.example.com/pabal-webhook", "secret_token": "سلسلة-عشوائية-طويلة"}'

الإعداد والتحقق وإعادة المحاولة والرد من خلال الاستجابة، كلها مشروحة في مستند Webhooks.

بوت يتصل عبر MTProto ‏(Telethon)

يمكن للبوت أيضاً أن يتصل عبر MTProto نفسه الذي يستخدمه التطبيق. وهذا مريح إن كنت تستخدم أصلاً أدوات مخصّصة لحسابات الأشخاص (مثل Telethon). وبما أنه حساب البوت نفسه، يمكنك المزج بينه وبين HTTP.

# pip install telethon==1.42.0   ← استخدم 1.42 (الشرح أدناه)
# المفتاح العام للخادم: نزّل https://pabal.me/docs/server-key.pem وضعه في المجلد نفسه
import asyncio
import os

from telethon import TelegramClient, events
from telethon.crypto import rsa
from telethon.sessions import StringSession

rsa.add_key(open("server-key.pem").read(), old=False)        # المفتاح العام لخادم Pabal
client = TelegramClient(StringSession(), api_id=1, api_hash="0" * 32)
client.session.set_dc(2, "122.34.175.215", 8443)


@client.on(events.NewMessage(incoming=True))
async def echo(event):
    await event.reply(event.raw_text)


async def main():
    await client.start(bot_token=os.environ["BOT_TOKEN"])      # auth.importBotAuthorization
    print("البوت يعمل الآن. للإيقاف اضغط Ctrl+C")
    await client.run_until_disconnected()


asyncio.run(main())
  • استخدم Telethon 1.42. يتحدث Pabal بالطبقة 216، أما إصدارات Telethon الأحدث فتحاول قراءة الاستجابات بطبقة أعلى، فتفشل منذ تسجيل الدخول (TypeNotFoundError).
  • لا يتحقق Pabal من api_id وapi_hash، لذا تصلح أي قيمة.
  • يدفع الخادم الرسائل الجديدة فوراً إلى بوتات MTProto (فلا حاجة إلى getUpdates ولا إلى webhook). وعند الرد على الاستدعاءات استدعِ event.answer("…") قبل event.edit(…).

القواعد والحدود

البندالقيمة
عدد أحرف الرسالة / وصف الصورة4,096 حرفاً / 1,024 حرفاً
حجم الصورة (الرفع عبر sendPhoto)10MB
حجم جسم الطلب12MB
getUpdates: timeout · limitمن 0 إلى 50 ثانية · من 1 إلى 100 تحديث
الاحتفاظ بالتحديثات التي لم تُجلبآخر 1,000 تحديث لكل بوت، في ذاكرة الخادم (تضيع عند إعادة التشغيل)
انتظار الرد على الاستدعاء10 ثوانٍ
callback_data · عدد الأزرارمن 1 إلى 64 بايت · 100 زر لكل رسالة
الأوامرمن 1 إلى 32 حرفاً من الأحرف اللاتينية الصغيرة والأرقام والشرطة السفلية، والوصف من 1 إلى 256 حرفاً، وبحد أقصى 100 أمر
بدء المحادثة من جهة البوتغير ممكن — يجب أن يرسل الشخص إلى البوت أولاً

حل المشكلات

العَرَضالسبب والحل
401 Unauthorizedالرمز المميّز خاطئ أو تغيّر بسبب /revoke. أرسل /token إلى BotFather للتحقق.
404 Not Found: method not foundطريقة لا يدعمها Pabal بعد. راجع قائمة الطرق.
403 Forbidden: bot can't initiate conversation with a userلم يبدأ ذلك الشخص أي محادثة مع البوت بعد. اطلب منه أن يفتح البوت في التطبيق ويضغط ابدأ.
409 Conflict: can't use getUpdates method while webhook is activeهناك webhook مضبوط. استدعِ deleteWebhook أو استلم التحديثات عبر webhook.
لا يحدث شيء عند الضغط على الزرالبوت متوقف، أو أنه لم يستدعِ answerCallbackQuery.
أستلم الرسالة نفسها مراراًلم ترفع offset. مرّر في الطلب التالي update_id + 1 لآخر تحديث عالجته.
تنسيق الغامق والروابط لا يعملparse_mode غير مدعوم بعد، فيُرسل النص كما هو. أما الأوامر، والإشارات (@mention)، وعناوين URL، والوسوم (#tag) فتُعرض تلقائياً قابلة للنقر.
البوت صامت في المجموعةالبوت ليس عضواً في المجموعة. أضفه من معلومات المجموعة ← إضافة أعضاء.