كل ما هو جديد قديم طي النسيان!
الآن كثير من الناس يكتبون روبوتات مختلفة تتواصل مع المستخدم في الرسائل الفورية وتساعد المستخدم بطريقة ما على العيش.

إذا نظرت إلى رمز العديد من برامج الروبوت ، فعادةً ما يأتي في نفس النمط:
- تصل الرسالة
- يتم تمريره إلى معالج رسالة المستخدم (
callback
)
هذا هو عموما وسيلة عالمية لكتابة الروبوتات. انها مناسبة للدردشة شخص واحد وللروبوتات المتصلة مجموعات. من خلال هذه الطريقة ، كل شيء على ما يرام باستثناء طريقة واحدة: غالبًا ما تكون شفرة برامج الروبوت البسيطة مربكة للغاية.
دعونا نحاول كشفها.
سأبدأ بالإخلاء:
- ما هو موضح في هذه المقالة مناسب لبوتات من النوع
<->
. - الكود في هذه المقالة هو رمز للرسم. مكتوبة خصيصا لهذا المقال في 15 دقيقة. لذلك لا تحكم بدقة.
- استخدمت نهجًا مشابهًا في العمل: مع موازنة التحميل. ولكن ، للأسف ، يحتوي رمز الإنتاج الخاص بي على الكثير من تبعيات البنية التحتية ومن السهل جدًا عدم نشرها. لذلك ، يتم استخدام هذا المخطط في المقالة. سوف أتطرق إلى قضايا تطوير النموذج (سأصف أين وكيف قمنا بتطوير).
حسنا ، دعنا نذهب الآن.
كدعم ، النظر في مكتبة غير متزامن aiogram ، python3.7 + . يحتوي الرابط على مثال لبوت الصدى البسيط.
سوف انسخها هنا:
عرض الكود """ This is a echo bot. It echoes any incoming text messages. """ import logging from aiogram import Bot, Dispatcher, executor, types API_TOKEN = 'BOT TOKEN HERE'
نرى أن تنظيم الروبوت تقليدي. في كل مرة يكتب المستخدم شيئًا لنا ، تُسمى وظيفة المعالج.
ما هو الخطأ في هذا النموذج؟
يجب أن تقوم وظيفة المعالج لتطبيق مربعات الحوار المعقدة باستعادة حالته من نوع من التخزين على كل مكالماته.
إذا نظرت إلى معظم برامج الروبوت التي تدعم نوعًا ما من الأعمال (على سبيل المثال ، التوظيف) ، فإنها تطرح على المستخدم أسئلة 1..N ، ثم يفعلون شيئًا بناءً على نتائج هذه الأسئلة (على سبيل المثال ، يقومون بحفظ ملف التعريف في قاعدة البيانات).
إذا كان من الممكن كتابة روبوت بنمط تقليدي (بدلاً من نمط رنين) ، فسيكون من الممكن تخزين بيانات المستخدم مباشرة في المجموعة.
دعنا نحاول القيام بذلك.
لقد رسمت مخططًا للوحدة ، حيث يمكنك الاتصال مع هذه المكتبة:
الشرح القليل:
يتم ChatDispatcher
فئة ChatDispatcher
مع المعلمات التالية:
- وظائف المشاركة للرسائل الواردة (لماذا يطلق عليها المشاركة - لاحقًا ، عندما نتناول أحمالًا كبيرة). ترجع الدالة رقمًا فريدًا يشير إلى مربع حوار. في المثال ، تقوم ببساطة بإرجاع معرف المستخدم.
- الوظائف التي ستؤدي عمل خدمة الدردشة.
- قيمة المهلة لعدم نشاط المستخدم.
الوصف الوظيفي:
- استجابة للرسالة الأولى للمستخدم ، يتم إنشاء مهمة غير متزامنة تخدم الحوار. ستعمل هذه المهمة حتى يكتمل الحوار.
- لتلقي رسالة من مستخدم ، نطلبها صراحة. مثال
echo
الدردشة:
async def chat(get_message): message = await get_message() await message.answer(message.text)
- نرد على الرسائل لأن المكتبة تقدم لنا (
message.answer
).
دعونا نحاول كتابة روبوت في هذا النموذج
مثال بوت مكتوب - إنه ببساطة يضيف بضعة أرقام وينتج نتيجة.
النتيجة تبدو مثل هذا:

حسنًا ، دعنا الآن نلقي نظرة فاحصة على الكود. يجب أن لا تثير الأمثلة أسئلة.
يتم التكامل مع المخطط الخاص بنا في المعالج await chat_dispatcher.handle(message)
الذي نسميه في await chat_dispatcher.handle(message)
. وصفنا chat
في وظيفة chat
، سأكرر رمزها هنا:
async def chat(get_message): try: message = await get_message() await message.answer(' , ') first = await get_message() if not re.match('^\d+$', str(first.text)): await first.answer(' , : /start') return await first.answer(' ') second = await get_message() if not re.match('^\d+$', str(second.text)): await second.answer(' , : /start') return result = int(first.text) + int(second.text) await second.answer(' %s (/start - )' % result) except ChatDispatcher.Timeout as te: await te.last_message.answer('- , ') await te.last_message.answer(' - /start')
رمز خدمة الدردشة - فقط يسأل عن البيانات واحدة تلو الأخرى من المستخدم. يتم تكديس استجابات المستخدم ببساطة على المكدس (المتغيرات first
second
message
).
قد get_message
وظيفة get_message
استثناء إذا لم يقم المستخدم بإدخال أي شيء خلال المهلة المحددة (ويمكنك تمرير المهلة إليها في نفس المكان).
حالة الحوار - ترتبط مباشرة برقم السطر داخل هذه الوظيفة. عند تحريك الكود ، ننتقل إلى مخطط الحوار . إجراء تغييرات على موضوع الحوار ليس بالأمر السهل ، ولكنه بسيط للغاية!
وبالتالي ، ليست هناك حاجة آلات الدولة. في هذا النموذج ، يمكنك كتابة مربعات حوار معقدة للغاية وفهم أن التعليمات البرمجية الخاصة بها ستكون أبسط بكثير من التعليمات البرمجية مع عمليات callback
.
القصور
أين بدونهم.
- لكل مستخدم نشط ، هناك corutin مهمة واحدة. في المتوسط ، تخدم وحدة المعالجة المركزية الواحدة عادة حوالي 1000 مستخدم ، ثم تبدأ التأخيرات.
- إعادة تشغيل البرنامج الخفي بالكامل - إنهاء جميع مربعات الحوار (وإعادة تشغيلها).
- لم يتم تكييف الكود [من المثال] لتحميل التحجيم والتدويل.
إذا كان من الواضح فيما يتعلق بالمشكلة الثانية ما يجب فعله: اعتراض إشارة التوقف وإخبار المستخدمين "لدي حالة طوارئ هنا ، حريق ، سأعود بعد قليل". هذه المشكلة الأخيرة يمكن أن يسبب صعوبات. دعونا ننظر في الأمر:
تحميل التحجيم
من الواضح ، يجب تشغيل الروبوتات المحملة على العديد من الوصلات الخلفية دفعة واحدة. وفقًا لذلك ، سيتم استخدام وضع webHook
للعملية.
إذا كنت تقوم فقط بموازنة webHook
بين ، على سبيل المثال ، webHook
، فمن الواضح أنك بحاجة إلى التأكد بطريقة أو بأخرى من أن المستخدم نفسه يأتي إلى نفس coroutine الذي يتحدث إليه.
لقد فعلنا هذا على النحو التالي.
- على الموازن ، قم بتحليل JSON للرسالة الواردة (
message
) - اختر معرف مستخدم منه
- باستخدام المعرف ، نحسب رقم الواجهة الخلفية (== قشرة). على سبيل المثال ، باستخدام
user_id % Nshards
. - نقوم بإعادة توجيه الطلب إلى القشرة.
معرّف المستخدم - يصبح مفتاح المشاركة بين مجموعة المفاتيح في مربعات الحوار والأساس لحساب عدد الأحرف الخلفية للواجهة الخلفية في الموازن.
رمز مثل هذا الموازن بسيط - إنه مكتوب بأي لغة في 10 دقائق. لن أحضره.
استنتاج
إذا كتبت روبوتات في هذا النموذج ، فيمكنك ببساطة إعادة الحوارات من واحدة إلى أخرى. علاوة على ذلك ، المهم أن يفهم المبرمج الجديد بسهولة رمز مربعات الحوار التي قام بها شخص ما قبله.
لماذا يكتب معظم الناس روبوتات في هندسة الحلقات - لا أعرف.
كانوا يكتبون في مثل هذا النموذج. خدمة غرف الدردشة على هذا النمط تم اعتمادها في عصر IRC والبوتات من أجله. لذلك أنا لا أدعي أن يكون أي نوع من الجدة.
و اكثر إذا كنت تستخدم هذا النموذج بلغة مع مشغل goto
، فسيكون هذا مجرد مثال جميل لاستخدام goto
(يتم إجراء حلقات في مربعات الحوار بشكل جميل على goto
). لسوء الحظ ، هذا ليس حول بيثون.