التوجيه في برامج الدردشة المعقدة باستخدام إطار Hobot



بعد أن بدأت في تطوير برامج الروبوت لـ Telegram قبل عدة سنوات ، اكتشفت الأداء والبساطة والمرونة في العمل معهم كحالة خاصة لواجهة سطر الأوامر. هذه الخصائص ، المتاحة للكثيرين اليوم ، ترجع إلى حد كبير إلى إطار telegraf.js الشهير وما شابه ذلك ، والذي يوفر طرقًا مبسطة للعمل مع Telegram API.

تقع بنية المشروع في الوقت نفسه بالكامل على عاتق المطور ، وبالحكم على العدد المتواضع من الروبوتات المعقدة والمتعددة الوظائف ، لا يزال لدينا مجال للنمو في هذا الصدد.

في هذه المقالة ، أود أن أتحدث عن إطار توجيه صغير لبرنامج الدردشة الآلية ، والذي بدونه سيكون من المستحيل تطوير مشروعنا.

بعض المعلومات الأساسية


في روبوتات المحادثة و CLIs ، غالبًا ما يتكون تنفيذ إجراء منطقي واحد من عدة خطوات تحسين أو خطوات متفرعة. يتطلب هذا من البرنامج تخزين إحداثيات معينة لتذكر مكان وجود المستخدم في التدفق وتنفيذ أمره وفقًا لهذا الإحداثيات.

أبسط توضيح هو تنفيذ الأمر npm init ، حيث يطلب منك البرنامج تحديد بيانات أو أخرى لـ package.json بدوره.

في الخطوة الأولى ، تخبر المستخدم أنها تنتظر إدخال النص لاسم الحزمة - وسيتم حفظ ما يرسله لها المستخدم باستخدام الأمر التالي كاسم الحزمة بفضل المتغير الذي تمت كتابة هذا الانتظار فيه.

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

ممارسة اليوم في هندسة البوت


يبدو النهج الذي نظرت إليه في البداية من مطورين آخرين كما يلي: بالنسبة لأي تحديث قادم من المستخدم ، يتم كتابة قائمة عمليات التحقق لقيمة معينة لمتغير المسار ومنطق الأعمال ويتم وضع المزيد من التنقل داخل عمليات التحقق هذه في النموذج الأولي:

onUserInput(ctx, input) {
    switch(ctx.session.path) {
        case 'firstPath':
	    if (input === '!') {
               // -  
	        ctx.reply('!');
	        ctx.session.path = 'secondPath';
	    } else {
	        ctx.reply(' "!"');
	    }
	    break;
        case '...':
       	    //   
    }
}

إذا كان لديك فريقان فقط وبخطوتين لكل فريق ، فإن هذا الحل هو الأفضل. الوصول إلى الفريق الثالث والسابع إذا بدأت تعتقد أن هناك خطأ ما.

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

إطار Hobot


بداية من ActualizeBot ، تخيلنا بالفعل كم سيكون حجمها ، وكانت مهمتنا الأولى الحفاظ على قابلية التطوير وسرعة التطوير.

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

كل هذا ، استنادًا إلى المشاريع الكبيرة ، تمت كتابته في TypeScript وتم إعطاؤه اسم Hobot الأنيق ، ملمحًا بالطبع إلى خطوط الأنابيب الملاحية.

وحدة التحكم هي كائن بسيط من ثلاث خصائص:

  • المسار - معرف السلسلة للمسار المستخدم لتهيئة المسارات التي تمت تهيئتها والتنقل فيها
  • get — , , hobot.gotoPath(ctx, path, data?). — data ,
  • post — . , , . updateType — , : text, callback_query . updateType ctx

مثال المتحكم:

const anotherController = {
    path: 'firstPath',
    get: async (ctx, data) => 
        await ctx.reply('Welcome to this path! Say "Hi"'),
    post: async (ctx, updateType) => {
        //     : text / callback_query / etc...
        if (updateType === updateTypes.text && ctx.update.message.text === 'Hi') {
            await ctx.reply("Thank you!");
            // hobot       this:
            this.hobot.gotoPath(ctx, 'secondPath', { userJustSaid: "Hi" });
        } else {
            //     ,       
            await ctx.reply('We expect "Hi" text message here');
        }
    }
}

يبدو الأمر أكثر تعقيدًا قليلاً مما كان عليه في البداية ، لكن الصعوبة ستظل كما هي عندما يكون لديك 100 أو 200 مسار.

المنطق الداخلي تافه ومن المدهش أنه لم يقم أحد بذلك بعد :
تتم إضافة وحدات التحكم هذه إلى كائن مفاتيحه هي قيم خاصية المسار ويتم استدعاؤها من خلال هذه المفاتيح أثناء إجراءات المستخدم أو عند التنقل باستخدام hobot.gotoPath (ctx ، المسار ، البيانات؟ ) .

يتم تخصيص التنقل بطريقة منفصلة حتى لا تلمس المسار المتغير ومنطق التنقل ، ولكن للتفكير فقط في منطق الأعمال ، على الرغم من أنه يمكنك دائمًا تغيير ctx.session.path بيديك ، وهو أمر غير موصى به بالطبع.

كل ما عليك القيام به لجعل روبوتك الجديد مع عمل هيكل غير قابل للتدمير هو إطلاق روبوت برقية عادي وتمريره وكائن التكوين إلى مُنشئ Hobot. يتكون كائن التكوين من وحدات التحكم التي تريد تهيئتها والمسار الافتراضي وأزواج الأوامر / وحدة التحكم.

//   telegraf-
const bot = new Telegraf('_');

//  
export const hobot = new Hobot(bot, {
    defaultPath: 'firstPath',
    commands: [
        //  ,     
        // get    :
        { command: 'start', path: 'firstPath' }
    ],
    controllers: [
        // -,    
        startController,
        nextController
    ]
});

// C telegraf-,       
bot.launch();

فى الختام


المزايا الضمنية لتقسيم الورقة إلى وحدات تحكم:

  • القدرة على وضع الوظائف والأساليب والواجهات المعزولة التي يتم تأمينها لمنطق وحدة التحكم هذه في ملفات منفصلة بجوار وحدات التحكم
  • انخفاض كبير في خطر كسر كل شيء عن طريق الخطأ
  • Modularity: تشغيل / إيقاف / إعطاء جزء معين من الجمهور هذا المنطق أو ذاك يمكن القيام به ببساطة عن طريق إضافة وحدات تحكم وإزالتها من المصفوفة ، بما في ذلك عن طريق تحديث التكوين بدون برمجة - لهذا ، بالطبع ، نحتاج إلى كتابة حرفين ، لأننا ما زلنا لا نحتاج إلى هذه وصلت
  • القدرة على إخبار المستخدم بوضوح عما هو متوقع منه بالضبط عندما يفعل (وهو ما يحدث غالبًا) شيئًا خاطئًا - ويفعله في هذا المكان - في نهاية معالجة طريقة النشر

خططنا :

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

الروابط :

يبدو تثبيت برنامج التتبُّع كالتالي: npm i -s hobot
مستودع مع تجول في README.MD وصندوق رمل
جاهز يعمل في الإنتاج استنادًا إلى Hobot.

شكرًا لك على اهتمامك ، يسعدني أن أسمع أسئلتك أو اقتراحاتك أو أفكارك عن برامج الروبوت الجديدة!

All Articles