Skip to main content

A simple and modern async framework for building Telegram bots

Project description

Titan

إطار عمل Python غير متزامن لبناء بوتات Telegram.

بسيط. واضح. لا يتغير تحت قدميك.

CI

🌐 English version → README.en.md


التثبيت

pip install titanx

البداية السريعة

from titan import Titan

bot = Titan("YOUR_TOKEN")

@bot.command("start")
async def start(ctx):
    await ctx.reply("أهلاً! أنا جاهز.")

bot.run()

المفاهيم الأساسية

Titan توفر خمس طرق للتفاعل مع بوتك. كل طريقة لها دور محدد.

الطريقة الدور
bot.on(event) استقبال أي حدث Telegram بالاسم
bot.command(name) استقبال أمر محدد مثل /start
bot.callback(data) استقبال ضغطة زر inline محددة
bot.middleware تشغيل منطق قبل كل handler
bot.telegram استدعاء Telegram API مباشرة

bot.on — استقبال الأحداث

@bot.on("message")
async def on_message(ctx):
    await ctx.reply("وصلتني رسالتك.")

@bot.on("callback")
async def on_callback(ctx):
    await ctx.answer_callback()

@bot.on("channel")
async def on_channel(ctx):
    pass  # منشورات القناة

الأحداث المدعومة: message، callback، channel، new_member، left_member.

bot.command — استقبال الأوامر

@bot.command("start")
async def start(ctx):
    await ctx.reply("مرحباً!")

@bot.command("help")
async def help(ctx):
    await ctx.reply("أرسل أي رسالة للبدء.")

bot.callback — استقبال أزرار Inline

@bot.callback("confirm")
async def on_confirm(ctx):
    await ctx.answer_callback("تم التأكيد.")

@bot.callback("cancel")
async def on_cancel(ctx):
    await ctx.answer_callback("تم الإلغاء.")

إذا لم يوجد handler مطابق لـ callback_data، يُحوَّل التحديث إلى bot.on("callback").

bot.middleware — منطق ما قبل التنفيذ

يعمل قبل كل handler. استخدمه للـ logging، التحقق من الصلاحيات، ومنع الـ spam.

@bot.middleware
async def guard(ctx, next):
    print(f"تحديث من المستخدم {ctx.user_id}")
    await next()
  • await next() ← يكمل التنفيذ للـ handler
  • return بدون استدعاء next() ← يوقف التحديث هنا

bot.telegram — الوصول المباشر للـ API

للعمليات خارج سياق الرسالة الواحدة.

await bot.telegram.send_message(chat_id=123, text="رسالة مباشرة.")
await bot.telegram.get_chat_member(chat_id=123, user_id=456)
await bot.telegram.pin_message(chat_id=123, message_id=789)

bot.telegram يتجاوز الـ middleware والـ ctx — للاستدعاء المباشر والصريح فقط.


السياق (ctx)

كل handler يستلم كائن ctx يحمل بيانات التحديث الحالي وأدوات التفاعل.

البيانات

ctx.user_id        # int | None — معرّف المستخدم
ctx.chat_id        # int | None — معرّف الشات
ctx.text           # str | None — نص الرسالة
ctx.callback_data  # str | None — بيانات الزر المضغوط
ctx.is_banned      # bool — هل المستخدم في قائمة الحظر

ctx.sender         # بيانات المرسل: .id, .first_name, .username, .is_bot
ctx.chat           # بيانات الشات: .id, .type, .title, .username
ctx.message        # بيانات الرسالة: .id, .text

الأفعال

await ctx.reply("مرحباً")
await ctx.send("مرحباً")
await ctx.edit("نص محدّث")        # داخل callback handlers فقط
await ctx.delete_message()
await ctx.ban_user()
await ctx.leave()
await ctx.answer_callback("تم")
await ctx.fetch_permissions()      # يفحص صلاحية البوت في الشات

الوصول الخام

ctx.raw  # JSON الكامل القادم من Telegram

وجود ctx.raw مضمون وجزء من العقد. لكن بنيته تتبع Telegram API وقد تتغير — استخدمه فقط عند الحاجة لبيانات غير متاحة عبر ctx مباشرة.


أزرار Inline

from titan import Titan, InlineKeyboard

bot = Titan("YOUR_TOKEN")

@bot.command("start")
async def start(ctx):
    keyboard = (
        InlineKeyboard()
        .row()
        .button("نعم ✅", callback_data="confirm")
        .button("لا ❌", callback_data="cancel")
    )
    await ctx.reply("هل أنت متأكد؟", reply_markup=keyboard)

@bot.callback("confirm")
async def on_confirm(ctx):
    await ctx.answer_callback("تم التأكيد!")

@bot.callback("cancel")
async def on_cancel(ctx):
    await ctx.answer_callback("تم الإلغاء.")

bot.run()

Router

يتيح لك تقسيم الـ handlers عبر ملفات متعددة ودمجها في البوت الرئيسي.

# admin.py
from titan import Router

router = Router()

@router.command("ban")
async def ban(ctx):
    await ctx.ban_user()
    await ctx.reply("تم الحظر.")

@router.callback("confirm_ban")
async def confirm_ban(ctx):
    await ctx.answer_callback()
# main.py
from titan import Titan
from admin import router

bot = Titan("YOUR_TOKEN")
bot.include(router)
bot.run()

Router يدعم: on()، command()، callback().

Router لا يدعم: middleware() أو include() المتداخل.


الأسماء البديلة (Aliases)

تتيح لك تعريف أسماء مخصصة لـ methods الـ ctx عبر AliasMap من titan.extras.

from titan.extras.alias import AliasMap

aliases = AliasMap()
aliases.register("قل", "reply")
aliases.register("اطرد", "ban_user")

bot.middleware(aliases.as_middleware())

@bot.on("message")
async def handler(ctx):
    await ctx.قل("مرحباً")   # نفس ctx.reply()
    await ctx.اطرد()          # نفس ctx.ban_user()

الاسم الأصلي يبقى متاحاً بدون أي تغيير. Aliases طبقة تسمية فقط — لا تغير في السلوك.

AliasMap كيان مستقل — يمكن مشاركته بين routers متعددة:

router1.middleware(aliases.as_middleware())
router2.middleware(aliases.as_middleware())

نظام الحظر

bot.banned_users  # set[int] — تديرها أنت بالكامل

bot.banned_users.add(user_id)
bot.banned_users.discard(user_id)

عندما يكون المستخدم في bot.banned_users، تكون ctx.is_banned قيمتها True قبل تشغيل الـ middleware. Titan لا تتصرف تلقائياً — الـ middleware تقرر ماذا تفعل.

@bot.middleware
async def guard(ctx, next):
    if ctx.is_banned:
        return
    await next()

تشغيل البوت

# متزامن — الأنسب في أغلب الحالات
bot.run()

# غير متزامن — عندما تُدير event loop خاص بك
import asyncio
asyncio.run(bot.run_async())

كلا الأسلوبين ينفّذان نفس المنطق الداخلي.


حواف حادة معروفة


مشاكل موثَّقة (سلوك غير صحيح)

bot.include() — حالة جزئية عند التعارض

إذا احتوى الـ Router على handlers وأمر يتعارض مع أمر مسجَّل مسبقاً، فإن bot.include() تُضيف الـ handlers أولاً ثم تُرمى TitanError. الـ mutation لا تُعكس.

@bot.command("start")
async def existing(ctx): ...

router = Router()

@router.on("message")
async def handler(ctx): ...  # يُضاف للبوت

@router.command("start")
async def conflict(ctx): ...  # يُسبب TitanError

bot.include(router)
# TitanError مُرماة — لكن handler("message") أُضيف بالفعل

التعامل معه: تحقق من التعارضات قبل استدعاء include(). إذا وقع الخطأ، أعد تهيئة البوت بدلاً من المتابعة.


bot.callback("") — لا يُنفَّذ أبداً

التسجيل بـ callback_data فارغة ("") ينجح بدون خطأ، لكن الـ handler لا يُستدعى أبداً عند وصول update يحمل callback_data="". المنطق الداخلي يتعامل مع "" كغياب البيانات.

@bot.callback("")        # يُسجَّل بنجاح
async def handler(ctx):  # لا يُنفَّذ أبداً
    ...

التعامل معه: لا تستخدم سلسلة فارغة كـ callback_data. استخدم قيمة وصفية دائماً مثل "noop" أو "skip".


ضمانات غائبة (سلوك صحيح لكن بلا حماية)

هذه ليست أخطاء — Titan تتصرف بشكل متسق، لكنها لا تحمي من أنماط الاستخدام الخاطئة التالية:

استدعاء next() مرتين في middleware يؤدي إلى تنفيذ كل handler مرتين. Titan لا تكتشف هذا. اتبع قاعدة: استدعاء next() مرة واحدة فقط.

تسجيل أمر بشرطة مائلة: bot.command("/start") يُسجَّل بنجاح لكن لا يُطابق أي update أبداً. الاسم الصحيح هو "start" بدون شرطة مائلة.

تسجيل error_handler أكثر من مرة الأخير فقط يبقى — الأول يُحذف بصمت. لا يوجد تحذير.

InlineButton بدون callback_data ولا url Titan تقبل الزر. Telegram API سترفضه عند إرسال الرسالة.

AliasMap.register() باسم يطابق خاصية موجودة في ctx إذا اخترت اسماً يطابق خاصية موجودة مثل text أو chat_id، ستُكتَب الخاصية الأصلية بصمت.

aliases.register("text", "reply")
# ctx.text الآن تُشير إلى reply — الخاصية الأصلية اختفت

استخدم أسماء لا تتعارض مع خصائص ctx الموجودة.

تمرير async def إلى on_offset on_offset تتوقع دالة عادية (synchronous). تمرير async def ينتج كوروتيناً لا يُنفَّذ أبداً، بدون خطأ أو تحذير.

async def save(offset):   # خطأ — دالة async
    ...

bot.run(on_offset=save)   # الكوروتين يُنشأ ويُتجاهل عند كل تحديث

استخدم دالة عادية، وابدأ event loop منفصلاً إذا احتجت async داخلها.


الفلسفة

Titan إطار عمل مبني على الاستقرار.

  • الأشياء البسيطة تبقى بسيطة.
  • الأشياء المعقدة ممكنة عبر bot.telegram.
  • الـ API لا يتغير بدون إصدار جديد.
  • لا سلوك خفي. لا magic. لا سباق ميزات.

الرخصة

W.A.S.L v1.0 — رخصة واصل التجارية للبرمجيات راجع ملف LICENSE للاطلاع على الشروط الكاملة.


© Copyright by Waheed. All rights reserved. (Alien's Zone ~ building real robots to help humanity thrive and stay alive! ^^)

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

titanx-1.0.0a2.tar.gz (132.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

titanx-1.0.0a2-py3-none-any.whl (97.4 kB view details)

Uploaded Python 3

File details

Details for the file titanx-1.0.0a2.tar.gz.

File metadata

  • Download URL: titanx-1.0.0a2.tar.gz
  • Upload date:
  • Size: 132.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.19

File hashes

Hashes for titanx-1.0.0a2.tar.gz
Algorithm Hash digest
SHA256 9c6ff565dd8694ecc02835cd80c67c6acae4ae306bbb27b0a4dcf2b69a56d5ac
MD5 c33d24327536f1b29943f43ef2211f65
BLAKE2b-256 7e47eee9987eb3bae80a5377bae0fd5f5866ec98fb38774de5732440c466f656

See more details on using hashes here.

File details

Details for the file titanx-1.0.0a2-py3-none-any.whl.

File metadata

  • Download URL: titanx-1.0.0a2-py3-none-any.whl
  • Upload date:
  • Size: 97.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.19

File hashes

Hashes for titanx-1.0.0a2-py3-none-any.whl
Algorithm Hash digest
SHA256 d57eec6a90c4d07816b9b901e5d26220a16d2ba446e02c596d8cb2258113719d
MD5 041c1bac9813075f909e22aad0449fc0
BLAKE2b-256 e0f7acb4efe1e8f4324b475fd95080f5e096df2944ba3e9a3934a4f690145e21

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page