Routing di chatbots kompleks dengan kerangka kerja Hobot



Setelah mulai mengembangkan bot untuk Telegram beberapa tahun yang lalu, saya menemukan kinerja, kesederhanaan dan fleksibilitas bekerja dengan mereka sebagai kasus khusus dari antarmuka baris perintah. Karakteristik ini, tersedia bagi banyak orang saat ini, sebagian besar disebabkan oleh kerangka telegraf.js yang populer dan sejenisnya, yang menyediakan metode yang disederhanakan untuk bekerja dengan API Telegram.

Pada saat yang sama, arsitektur proyek terletak sepenuhnya di pundak pengembang, dan dilihat dari jumlah bot kompleks dan multifungsi yang sederhana, kami masih memiliki ruang untuk tumbuh dalam hal ini.

Pada artikel ini saya ingin berbicara tentang kerangka routing chatbot kecil, yang tanpanya pengembangan proyek kami tidak mungkin dilakukan.

Beberapa informasi dasar


Dalam chatbots dan CLI, eksekusi satu tindakan logis sering terdiri dari beberapa langkah penyempurnaan atau langkah percabangan. Ini mengharuskan program untuk menyimpan koordinat tertentu untuk mengingat di mana pengguna berada dalam aliran dan menjalankan perintahnya sesuai dengan koordinat ini.

Ilustrasi paling sederhana adalah eksekusi perintah npm init , di mana program meminta Anda untuk menentukan satu atau beberapa data lain untuk package.json secara bergantian.

Pada langkah pertama, ia memberi tahu pengguna bahwa ia sedang menunggu input teks dari nama paket - dan apa yang dikirimkan oleh pengguna dengan perintah berikutnya akan disimpan sebagai nama paket berkat variabel tempat penantian ini ditulis.

Kami menyebut jalur variabel ini - jalur tempat kode ini atau itu dilampirkan secara logis. Selalu diperlukan jika bot memiliki navigasi atau perintah yang dikeluarkan dalam beberapa langkah.

Praktek hari ini dalam arsitektur bot


Pendekatan yang pertama kali saya lihat oleh pengembang lain tampak seperti ini: untuk setiap pembaruan yang datang dari pengguna, daftar cek untuk nilai tertentu dari variabel path ditulis dan logika bisnis dan navigasi lebih lanjut ditempatkan di dalam cek ini dalam bentuk paling dasar:

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

Jika Anda hanya memiliki beberapa tim dan beberapa langkah untuk setiap tim, solusi ini optimal. Mencapai tim ketiga dan ketujuh jika Anda mulai berpikir ada sesuatu yang salah.

Dalam salah satu bot yang kami punya kesempatan untuk bekerja dengan pada tahap akhir, inti dari fungsional adalah selembar 4000 baris dan ~ 70 kondisi hanya tingkat atas yang tumbuh dari dua seandainya, dengan memeriksa apa yang pergi ke hati - kadang-kadang jalan, kadang-kadang perintah, terkadang jalur dan perintah. Semua kondisi ini diperiksa untuk setiap aksi pengguna dan mengakses fungsi tambahan dari objek lembar tetangga, yang juga tumbuh dari beberapa baris. Tak perlu dikatakan, seberapa lambat dan menyedihkan proyek ini berjalan?

Kerangka kerja hobot


Memulai ActualizeBot, kami sudah membayangkan seberapa besar itu, dan tugas pertama kami adalah untuk mempertahankan ekstensibilitas dan kecepatan pengembangan.

Untuk melakukan ini, kami memecah logika klien menjadi pengontrol yang ditugaskan ke lintasan dan menulis abstraksi kecil untuk menavigasi antara pengontrol ini dan memproses pesan yang diterima dari pengguna di dalamnya.

Semua ini, berdasarkan proyek-proyek besar, ditulis dalam TypeScript dan menerima nama elegan Hobot, yang mengisyaratkan, tentu saja, pada jalur pipa navigasi.

Kontroler adalah objek sederhana dari tiga properti:

  • path - string identifier dari path yang digunakan untuk menginisialisasi dan menavigasi path yang diinisialisasi
  • get — , , hobot.gotoPath(ctx, path, data?). — data ,
  • post — . , , . updateType — , : text, callback_query . updateType ctx

Contoh pengontrol:

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');
        }
    }
}

Ini terlihat sedikit lebih rumit daripada di awal, tetapi kesulitannya akan tetap sama ketika Anda memiliki 100 atau 200 jalur.

Logika internal sepele dan mengejutkan bahwa belum ada yang melakukan ini :
Pengontrol ini ditambahkan ke objek yang kunci-nya adalah nilai-nilai properti path dan dipanggil darinya oleh kunci-kunci ini selama tindakan pengguna atau saat bernavigasi menggunakan hobot.gotoPath (ctx, path, data? ) .

Navigasi dialokasikan dalam metode terpisah agar tidak menyentuh jalur variabel dan logika navigasi, tetapi hanya memikirkan logika bisnis, meskipun Anda selalu dapat mengubah ctx.session.path dengan tangan Anda, yang tentu saja tidak disarankan.

Yang perlu Anda lakukan untuk membuat bot baru Anda dengan pekerjaan struktur yang tidak dapat dihancurkan adalah meluncurkan bot telegraf biasa dan meneruskannya dan objek konfigurasi ke konstruktor Hobot. Objek config terdiri dari pengontrol yang ingin Anda inisialisasi, jalur default, dan pasangan perintah / pengontrol.

//   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();

Kesimpulannya


Keuntungan implisit membagi lembar menjadi pengontrol:

  • Kemampuan untuk menempatkan fungsi, metode, dan antarmuka yang terisolasi yang dikunci dengan logika pengontrol ini dalam file terpisah di sebelah pengontrol
  • Secara signifikan mengurangi risiko tidak sengaja melanggar segalanya
  • Modularitas: menyalakan / mematikan / memberikan segmen tertentu dari penonton logika ini atau itu dapat dilakukan hanya dengan menambah dan menghapus controller dari array, termasuk dengan memperbarui konfigurasi tanpa pemrograman - untuk ini, tentu saja, kita perlu menulis beberapa huruf, karena kita belum menulis tercapai
  • Kemampuan untuk dengan jelas memberi tahu pengguna apa yang sebenarnya diharapkan darinya ketika ia (yang sering terjadi) melakukan sesuatu yang salah - dan melakukannya di tempat ini - di akhir pemrosesan metode posting

Paket kami :

Artikel ini menjelaskan skrip dasar untuk bekerja dengan Hobot pengiriman pesan teks.
Jika topiknya ternyata relevan, kami akan membagikan seluk beluk teknis lainnya dari kerangka kerja dan pengamatan kami dari praktik mengembangkan dan menggunakan bot telegram di artikel mendatang.

Tautan :

Memasang bot terlihat seperti ini: npm i -s hobot
Repositori dengan langkah-langkah di README.MD dan bot Ready kotak pasir yang bekerja dalam produksi berdasarkan pada Hobot. Terima kasih atas perhatian Anda, saya akan senang mendengar pertanyaan Anda, saran atau ide untuk bot baru!



All Articles