---
title: "تحليلات Telegram المدمجة لمواضع الإعلانات"
description: "كيفية قياس مواضع الإعلانات باستخدام روابط Telegram فريدة: روابط الدعوة للقنوات، ومعاملا start وstartapp للبوتات والتطبيقات المصغرة، وأمثلة على تنفيذ الإسناد."
outline: [2,3]
---
# تحليلات Telegram المدمجة
يوفّر Telegram أدوات لتحليل نتائج مواضع الإعلانات باستخدام روابط فريدة. يمكنك إنشاء رابط منفصل لكل موضع إعلاني. بالنسبة للقنوات والمجموعات، تتوفر إحصاءات الانضمام مباشرةً في Telegram، أما بالنسبة للبوتات والتطبيقات المصغرة (Mini Apps)، فيجب عليك إعداد حفظ معاملات الرابط وجمع البيانات من جهة مشروعك.

> [!WARNING] تنبيه
> موضع إعلاني واحد = رابط فريد واحد. إذا استُخدم الرابط نفسه في عدة إعلانات، فستختلط مصادر الزيارات ولن يمكن فصلها لاحقًا.

يعمل هذا النوع من التحليلات بشكل منفصل عن إحصاءات حملات AdsGram وتتبع التحويلات.

## الجزء 1. لمديري الحملات من جهة المعلن

### 1. أي رابط يجب استخدامه؟
لمقارنة نتائج المواضع الإعلانية المختلفة، استخدم رابطًا فريدًا لكل منها.

على سبيل المثال، إذا كنت تروّج لمشروع في ثلاث قنوات على Telegram، فأنشئ ثلاثة روابط منفصلة. سيتيح لك ذلك تحديد المواضع التي تجذب أكبر عدد من المستخدمين.

يعتمد نوع الرابط على وجهة الإعلان:

| وجهة الإعلان | صيغة الرابط | أين تتابع النتائج |
|---|---|---|
| قناة أو مجموعة Telegram | رابط دعوة منفصل | Telegram: إعدادات القناة أو المجموعة → `Invite links` |
| بوت | `t.me/YourBot?start=ad_sep_anna` | بوتك، إذا كان يحفظ المعامل |
| Mini App | `t.me/YourBot?startapp=ad_sep_anna` | خادم تطبيقك، إذا كان يحفظ المعامل |

استبدل `YourBot` باسم مستخدم بوتك، و`ad_sep_anna` بمعامل فريد للموضع الإعلاني.

### 2. القنوات والمجموعات: روابط الدعوة
أنشئ رابط دعوة منفصلًا لكل موضع إعلاني.

1. افتح القناة أو المجموعة واضغط على اسمها.
2. افتح إعدادات التعديل أو الإدارة. في نسخة سطح المكتب، يمكنك استخدام ⋮ → `Manage channel` / `Manage group`.
3. افتح `Invite links` واختر خيار إنشاء رابط جديد.
4. سمِّ الرابط باسم الموضع، مثل `ad_sep_anna`. الاسم مرئي للمشرفين فقط، وليس للمستخدمين الذين يفتحون الرابط.
5. إذا لزم الأمر، حدّد تاريخ انتهاء صلاحية أو حدًا لعدد المستخدمين أو اشترط موافقة المشرف.
6. استخدم الرابط في موضع إعلاني واحد فقط.

**أين ترى الإحصاءات:** تعرض شاشة `Invite links` في Telegram عدد المستخدمين الذين انضموا عبر كل رابط. افتح رابطًا معيّنًا لرؤية قائمة المستخدمين.

الرابط العام العادي مثل `t.me/yourchannel` لا يتيح فصل عمليات الانضمام حسب الموضع الإعلاني. يمكن للقنوات العامة أيضًا إنشاء روابط دعوة، لذا استخدم روابط دعوة منفصلة في إعلاناتك.

### 3. البوتات: روابط بمعامل `start`
استخدم صيغة الرابط التالية:

```
https://t.me/<bot>?start=<parameter>
```

على سبيل المثال:

```
https://t.me/YourBot?start=ad_sep_anna
```

عندما يفتح المستخدم الرابط ويضغط على `Start`، يستقبل البوت أمرًا يحتوي على المعامل:

```
/start ad_sep_anna
```

لا يُبلغ Telegram تلقائيًا عن إسناد الإعلانات لعمليات تشغيل البوت هذه. يجب على البوت حفظ المعامل عند إنشاء سجل المستخدم لأول مرة.

احسب المستخدمين الجدد فقط إذا كان مقياسك هو اكتساب مستخدمين جدد. فتح مستخدم حالي لرابط إعلاني لا يجعله مستخدمًا جديدًا.

### 4. التطبيقات المصغرة: روابط بمعامل `startapp`
بالنسبة لتطبيق Mini App الرئيسي للبوت، استخدم:

```
https://t.me/<bot>?startapp=<parameter>
```

بالنسبة لتطبيق Mini App أُنشئ عبر `/newapp` في `@BotFather`، يمكنك استخدام:

```
https://t.me/<bot>/<app>?startapp=<parameter>
```

على سبيل المثال:

```
https://t.me/YourBot?startapp=ad_sep_anna
```

إذا لزم الأمر، أضف معامل وضع العرض:
- `&mode=compact` — الوضع المضغوط.
- `&mode=fullscreen` — وضع ملء الشاشة.

يستقبل Mini App المعامل في الحقل `start_param`. يجب أن يحفظه خادم تطبيقك عندما يشغّل المستخدم التطبيق لأول مرة.

إذا فتح المستخدم Mini App دون رابط إعلاني — مثلًا عبر زر القائمة أو ملف البوت — فقد لا يوجد معامل إعلاني. اعتبر هذا التشغيل عضويًا ما لم يتوفر مصدر إسناد موثوق آخر.

### 5. قواعد المعاملات
- الحد الأقصى للطول:
    - المعامل `start` — **64 حرفًا**
    - المعامل `startapp` — **512 حرفًا**
- الرموز المسموح بها:
    - `A-Z`، `a-z`، `0-9`، `_`، `-`
    - لا تستخدم المسافات أو الأحرف السيريلية أو النقاط. لتجنب مشكلات التوافق، التزم بمجموعة الأحرف المسموح بها.
- لا تُدرج البيانات الشخصية للمستخدمين في المعاملات.
- نمط التسمية الموصى به. استخدم صيغة موحّدة ليسهل التعرف على المواضع في التقارير: `ad_<month>_<source>_<creative>`

  أمثلة:
    - `ad_sep_tgads_video1`
    - `ad_sep_blogger_anna`

### 6. روابط الحملات ليست روابط إحالة
**رابط الإحالة** يتيح للمستخدم دعوة أشخاص آخرين والحصول على مكافأة.

**الرابط الإعلاني** يقيس الزيارات القادمة من موضع إعلاني. ولا يجب أن يؤدي بحد ذاته إلى منح مكافأة إحالة.

افصل بين النوعين باستخدام البادئات:
- `ref_…` — روابط إحالة المستخدمين.
- `ad_…` — روابط المواضع الإعلانية.

إنشاء رابط إحالة منفصل لكل حملة إعلانية ليس إلا حلًا مؤقتًا. فقد يؤدي إلى دفع مكافآت إحالة مقابل زيارات إعلانية وإلى خلط الإحصاءات.

### 7. كيفية تتبع النقرات
لا يوفّر Telegram إحصاءات النقرات لروابط `t.me`.

إذا كنت بحاجة إلى عدّ النقرات، فوجّه المستخدمين أولًا عبر إعادة توجيه خاصة بك ثم إلى Telegram.

مثال:

```
https://yourdomain.com/go/ad_sep_anna → https://t.me/YourBot?start=ad_sep_anna
```

يسجّل خادمك النقرة ويعيد توجيه المستخدم إلى الرابط النهائي. يتطلب ذلك تنفيذًا خاصًا بك، ولا يغني عن تتبع عمليات التشغيل أو المستخدمين الجدد أو الانضمام إلى القنوات.

### 8. الاحتفاظ بجدول النتائج
أنشئ جدولًا يحتوي على صف واحد لكل رابط فريد.

الأعمدة الموصى بها:

| الحقل | ما يجب تسجيله |
|---|---|
| معامل الرابط | على سبيل المثال، `ad_sep_anna` |
| الموضع | اسم القناة أو المنشور أو المؤثر أو الإعلان |
| التواريخ | فترة الموضع |
| الإنفاق | تكلفة الإعلان |
| المستخدمون الجدد | عدد المستخدمين الجدد المنسوبين إلى الرابط |
| التكلفة لكل مستخدم | الإنفاق ÷ عدد المستخدمين الجدد |

### 9. قائمة التحقق قبل الإطلاق
قبل نشر الإعلان، تأكد من أن:
- لكل موضع رابطه الخاص؛ ولا يُعاد استخدام الروابط في مواضع أخرى.
- المعامل لا يتجاوز الحد الأقصى للطول ويحتوي على الأحرف المسموح بها فقط.
- تم اختبار الرابط بحساب ثانٍ: يُحتسب الانضمام، أو يحفظ البوت أو Mini App المعامل.
- تمت إضافة الرابط واسم الموضع والإنفاق إلى الجدول.

## الجزء 2. التنفيذ للمطورين
تستخدم الأمثلة أدناه Python وSQL و`aiogram 3` للبوت. ويمكن تطبيق المبادئ نفسها بتقنيات أخرى.

يقدّم الكود أساسًا للتنفيذ وليس تكاملًا كاملًا جاهزًا للاستخدام. يجب تكييف دوال الوصول إلى قاعدة البيانات وبعض تفاصيل التكامل مع تطبيقك.

### 10. ما البيانات التي يجب تخزينها
يتطلب الإسناد ثلاثة كيانات:
1. سجل للروابط الإعلانية والحملات.
2. مصدر اكتساب المستخدم، ويُحفظ عند إنشاء سجل المستخدم.
3. سجل زيارات روابط الحملات، بما في ذلك زيارات المستخدمين الحاليين.

مثال على مخطط SQL:

```sql
-- صف واحد لكل رابط إعلاني.
-- على سبيل المثال، المعامل "ad_sep_anna" يقابل الرمز "sep_anna".
CREATE TABLE campaigns (
    code       text PRIMARY KEY,
    name       text NOT NULL,
    created_at timestamptz NOT NULL DEFAULT now()
);

-- يُحدَّد مصدر اكتساب المستخدم عند إنشائه.
-- NULL تعني مستخدمًا عضويًا.
ALTER TABLE users
ADD COLUMN campaign text REFERENCES campaigns (code);

-- جميع المستخدمين الذين فتحوا روابط الحملات،
-- بما في ذلك المستخدمون الجدد والحاليون.
CREATE TABLE campaign_visits (
    code     text REFERENCES campaigns (code),
    user_id  bigint,
    first_at timestamptz NOT NULL DEFAULT now(),
    PRIMARY KEY (code, user_id)
);
```

يجب أن يحتوي الحقل `code` على رمز قصير للحملة، مثل `sep_anna`. ويصف الحقل `name` الموضع، على سبيل المثال `قناة آنا، منشور سبتمبر`.

يتتبع الجدول `campaign_visits` الزيارات بشكل منفصل عن مصدر الاكتساب الأصلي للمستخدم. ولا يجب الكتابة فوق المصدر الأصلي في كل مرة يفتح فيها المستخدم رابطًا آخر.

### 11. تحليل المعاملات: محلّل واحد للبوت وMini App
استخدم محلّلًا واحدًا لمعاملات الإعلانات والإحالة.

```python
import re

AD = re.compile(r"ad_([A-Za-z0-9_-]{1,61})")
REF = re.compile(r"ref_([A-Za-z0-9]{4,32})")


def parse_start(param: str | None) -> tuple[str, str] | None:
    """يعيد ("ad", code) أو ("ref", code) أو None في أي حالة أخرى."""
    if not param:
        return None

    if m := AD.fullmatch(param):
        return "ad", m[1]

    if m := REF.fullmatch(param):
        return "ref", m[1]

    return None
```

بعد التحليل، تحقق مما إذا كان الرمز الناتج موجودًا في الجدول `campaigns`.
- لا تنسب رمزًا غير معروف إلى حملة موجودة. اعتبر هذه الزيارة عضوية أو غير منسوبة.
- لا تخزّن قيم المعاملات الخام دون التحقق منها.
- طبّق قواعد التحليل نفسها على المعاملات التي يستقبلها البوت وMini App.

### 12. البوت: معالجة `/start`
عندما يشغّل المستخدم البوت، يمرّر Telegram المعامل في وسيطات الأمر `/start`.

مثال باستخدام `aiogram 3`:

```python
@router.message(CommandStart())
async def start(message: Message, command: CommandObject):
    source = parse_start(command.args)
    # "/start ad_sep_anna" -> ("ad", "sep_anna")

    async with db.transaction():
        await create_user_if_new(message.from_user, source)

        if source and source[0] == "ad":
            await record_visit(source[1], message.from_user.id)
```

يجب أن تنشئ الدالة `create_user_if_new` سجل المستخدم فقط إذا لم يكن المستخدم موجودًا بالفعل.

لمنع تكرار السجلات، استخدم عملية ذرية مثل:

```sql
-- create_user_if_new: عملية ذرية، آمنة عند وصول طلبين من البوت والتطبيق المصغر في الوقت نفسه
INSERT INTO users (id, campaign, referrer_id)
VALUES ($1, $2, $3)
ON CONFLICT (id) DO NOTHING
RETURNING id; -- إرجاع صف = مستخدم جديد؛ لا شيء = مستخدم حالي، يبقى كما هو
```

إذا أعاد الاستعلام صفًا، فقد تم إنشاء المستخدم. وإذا لم يُعد أي صف، فالمستخدم موجود بالفعل ويجب أن يبقى مصدر اكتسابه الأصلي دون تغيير.

لتسجيل زيارة رابط حملة، استخدم:

```sql
-- record_visit
INSERT INTO campaign_visits (code, user_id)
VALUES ($1, $2)
ON CONFLICT DO NOTHING;
```

إذا فتح مستخدم حالي رابطًا آخر واستقبل `/start ad_…`، فسجّل الزيارة لكن **لا تكتب فوق** حقل `campaign` الخاص به.

بالنسبة لإسناد الإحالات، عيّن `referrer_id` للمستخدمين الجدد فقط. ولا تسمح للمستخدمين بإحالة أنفسهم.

### 13. Mini App: معالجة التشغيل الأول
يمكن لـ Mini App استقبال المعامل `start_param` ضمن بيانات تشغيل Telegram Web App.

في الواجهة الأمامية، أرسل البيانات الموقّعة من Telegram إلى خادمك:

```javascript
// الواجهة الأمامية: كل طلب يحمل بيانات التهيئة الموقّعة من Telegram
fetch("/api/me", { headers: { Authorization: `tma ${Telegram.WebApp.initData}` } })
```

على الخادم، تحقّق من صحة `initData` وعمرها قبل استخدام معرّف المستخدم أو `start_param`.

مثال على دالة التحقق في Python:

```python
import hashlib, hmac, json, time
from urllib.parse import parse_qsl


def check_init_data(
    raw: str,
    bot_token: str,
    max_age: int = 86400
) -> dict:
    fields = dict(
        parse_qsl(
            raw,
            keep_blank_values=True,
            strict_parsing=True
        )
    )

    received = fields.pop("hash", "")
    # جميع الحقول باستثناء hash، بما فيها "signature"، مرتبة، زوج key=value واحد في كل سطر
    data = "\n".join(
        f"{k}={v}" for k, v in sorted(fields.items())
    )

    secret = hmac.new(
        b"WebAppData",
        bot_token.encode(),
        hashlib.sha256
    ).digest()

    expected = hmac.new(
        secret,
        data.encode(),
        hashlib.sha256
    ).hexdigest()

    if not hmac.compare_digest(expected, received):
        raise PermissionError("bad signature")

    if time.time() - int(fields["auth_date"]) > max_age:
        raise PermissionError("expired")

    return {
        "user": json.loads(fields["user"]),
        "start_param": fields.get("start_param")
    }
```

بعد نجاح التحقق، اتبع العملية نفسها المتبعة في البوت (`create_user_if_new` و`record_visit`):
1. استخرج `user` و`start_param` من البيانات التي تم التحقق منها.
2. حلّل المعامل باستخدام `parse_start`.
3. أنشئ المستخدم فقط إذا لم يكن موجودًا بالفعل.
4. سجّل زيارة رابط الحملة إذا كان المعامل يطابق حملة إعلانية معروفة.

**الأمان:** استخدم فقط `initData` الذي تم التحقق منه. لا تعتمد على `initDataUnsafe` أو معاملات URL كمصادر لهوية المستخدم أو الإسناد، لأنه يمكن تزويرها.

يُمرَّر المعامل `start_param` عند تشغيل التطبيق عبر الرابط المقابل. وقد يغيب في عمليات التشغيل اللاحقة من القائمة. لذلك، عيّن مصدر الاكتساب الأصلي عند إنشاء المستخدم، وليس عند كل تشغيل للتطبيق.

### 14. مستخدم واحد، نقطتا دخول
يمكن للبوت وMini App مشاركة جدول `users` واحد، يُعرَّف فيه كل مستخدم بمعرّف Telegram user ID الخاص به.

على سبيل المثال، قد يشغّل المستخدم البوت أولًا ثم يفتح Mini App، أو العكس.

قواعد الإسناد:
- أول معالج ينشئ سجل المستخدم يحدّد مصدر الاكتساب الأصلي.
- يجب ألا يكتب المعالج الثاني فوق هذا المصدر.
- يمكن تسجيل الزيارات اللاحقة بشكل منفصل في `campaign_visits`.

استخدم إدراجًا ذريًا مثل `INSERT … ON CONFLICT DO NOTHING`. يساعد ذلك على تجنب الأخطاء إذا وصلت الطلبات من البوت وMini App في الوقت نفسه تقريبًا.

### 15. القنوات: تتبع الانضمام حسب رابط الدعوة
إذا كنت بحاجة إلى تتبع خاص بك لعمليات الانضمام بدلًا من الاعتماد على واجهة Telegram فقط، فأنشئ رابط دعوة منفصلًا لكل حملة واحفظه مع رمز الحملة.

مثال على إنشاء رابط في Python:

```python
# أنشئ رابطًا لكل حملة واحفظه مع الرمز
link = await bot.create_chat_invite_link(
    CHANNEL_ID,
    name="ad_sep_anna"
)  # الاسم: من 1 إلى 32 حرفًا
save_channel_link("ad_sep_anna", link.invite_link)
```

يقتصر اسم رابط الدعوة في هذا المثال على 1–32 حرفًا.

لاستقبال أحداث الانضمام، يجب أن يكون البوت مشرفًا في القناة وأن يستقبل تحديثات `chat_member` ذات الصلة. وإذا كانت طلبات الانضمام تتطلب موافقة، فيجب أن يستقبل أيضًا تحديثات `chat_join_request`.

مثال على إعداد polling:

```python
# تصل عمليات الانضمام كتحديثات chat_member: يجب أن يكون البوت مشرفًا وأن يطلبها
await dp.start_polling(
    bot,
    allowed_updates=[
        "message",
        "chat_member",
        "chat_join_request"
    ]
)
```

مثال على معالج الانضمام:

```python
@router.chat_member()
async def member(update: ChatMemberUpdated):
    joined = (
        update.old_chat_member.status in ("left", "kicked")
        and update.new_chat_member.status == "member"
    )

    if joined and update.invite_link:
        save_channel_join(
            update.invite_link.name,
            update.new_chat_member.user.id
        )
```

إذا كان الرابط يتطلب موافقة المشرف، فعالج `chat_join_request`. رابط الدعوة متاح في `update.invite_link`؛ وبعد مراجعة الطلب يمكنك استدعاء `approve_chat_join_request`.

يمكنك أيضًا تسجيل مغادرة المستخدمين للقناة لمعرفة من بقي فيها.

يعرض Telegram بالفعل للمشرفين عدد عمليات الانضمام لكل رابط دعوة. تكون قاعدة بياناتك الخاصة مفيدة إذا أردت دمج بيانات الانضمام إلى القناة مع معلومات مستخدمي البوت أو تحليلات أخرى.

### 16. اختياري: تتبع النقرات عبر إعادة التوجيه
لعدّ النقرات قبل وصول المستخدمين إلى Telegram، أنشئ نقطة نهاية خاصة بك لإعادة التوجيه.

مثال بلغة Python لـ Mini App:

```python
@app.get("/go/{code}")
async def go(code: str, request: Request):
    agent = request.headers.get("user-agent", "")

    if code_exists(code) and "TelegramBot" not in agent:  # تجاهل زاحف معاينة الروابط في Telegram
        record_click(code)

    return RedirectResponse(
        f"https://t.me/{BOT}?startapp=ad_{code}",
        status_code=302
    )
```

يتحقق هذا المثال من الرمز ويستبعد الطلبات التي يحتوي User-Agent فيها على `TelegramBot`، لتجنب احتساب طلبات معاينة الروابط في Telegram كنقرات مستخدمين عادية.

كيّف التنفيذ مع بنيتك التحتية واختبره بروابط حقيقية. يجب أن تقود إعادة التوجيه إلى رابط البوت أو Mini App الصحيح مع المعامل المناسب.

### 17. مثال على تقرير
يعرض الاستعلام التالي لكل حملة عدد الزوار الفريدين لروابط الحملة، والمستخدمين الجدد، والمستخدمين النشطين.

```sql
SELECT
    c.code,
    c.name,
    (
        SELECT count(*)
        FROM campaign_visits v
        WHERE v.code = c.code
    ) AS visitors,
    (
        SELECT count(*)
        FROM users u
        WHERE u.campaign = c.code
    ) AS new_users,
    (
        SELECT count(*)
        FROM users u
        WHERE u.campaign = c.code
          AND u.first_action_at IS NOT NULL
    ) AS active
FROM campaigns c
ORDER BY new_users DESC;
-- first_action_at: وقت أول تنفيذ للمستخدم للإجراء الذي تدفع الحملة مقابله (مهمة، عملية شراء)
```

في هذا المثال، `first_action_at` هو الوقت الذي يكمل فيه المستخدم لأول مرة الإجراء المستهدف الذي يدفع المعلن مقابله، مثل إكمال مهمة أو إجراء عملية شراء.

يمثل المقياس `visitors` المستخدمين الذين لديهم زيارة مسجّلة للحملة المقابلة في `campaign_visits`. وهو لا يساوي عدد النقرات على الرابط ما لم تُسجَّل النقرات بشكل منفصل عبر إعادة التوجيه.

### 18. الأخطاء الشائعة والقيود
- **يستقبل البوت المعامل فقط عندما يضغط المستخدم على `Start`.** ليس كل من ينقر على الرابط يشغّل البوت، لذا قد يكون عدد عمليات التشغيل أقل من عدد النقرات.
- **إعادة التشغيل لا تعني مستخدمًا جديدًا.** إذا حذف المستخدم المحادثة وشغّل البوت مرة أخرى، فقد يُمرَّر المعامل مجددًا، لكن المستخدم ليس جديدًا.
- **لا تكتب فوق مصدر الاكتساب الأصلي.** استخدم جدول زيارات منفصلًا بطوابع زمنية لتحليل الزيارات اللاحقة.
- **افصل بين الحسابات الجديدة والحالية.** إذا كان الهدف هو الاكتساب، فلا يجب أن تزيد الزيارات المتكررة عدد المستخدمين الجدد.
- **استبعد البوتات والإحالات الذاتية.** تحقق من `is_bot` ولا تسمح للمستخدمين بإحالة أنفسهم.
- **التزم بحدود المعاملات.** يجب ألا يتجاوز الطول الإجمالي للمعامل `start` ‏64 حرفًا. استخدم رموزًا قصيرة مثل `sep_anna` لترك مساحة للبادئة `ad_` أو `ref_`.
- **افصل بين إسناد الإحالات وإسناد الإعلانات.** تساعد البادئات وقواعد المعالجة المختلفة على تجنب المكافآت والتقارير الخاطئة.
