Vk-io: Arsitektur: Apa cara terbaik `resmi` untuk mengatur bot di VK-IO

Dibuat pada 14 Des 2020  Β·  2Komentar  Β·  Sumber: negezor/vk-io

Halo, Saya melihat analisis bot Anda di YouTube, dan mereka semua memiliki satu masalah besar: semua kode adalah alas kaki untuk 5k baris. Karena itu, saya punya pertanyaan: bagaimana Anda melihat bahwa proyek dengan VK-IO harus diatur. Karena dalam bentuknya saat ini, itu adalah footcloth atau indeks + konfigurasi + sekumpulan file yang terlihat seperti ini:

Frame 4

/** <strong i="8">@filename</strong>: config.ts */
export const vk = new VK({ /* ... */ })
/** <strong i="11">@filename</strong>: feat1.ts */
import { vk } from "./config"

vk.updates.on('message', async(ctx, next) => {
  /* code */
})
/** <strong i="14">@filename</strong>: index.ts */
import { vk } from "./config"
import "./feat1"

vk.updates.startPolling().then(() => console.log("Bot works!"))

Dan itu juga memiliki masalah:

  1. Urutan penyertaan fitur dalam indeks memengaruhi perilaku bot
  2. Anda bisa lupa untuk menghubungkan fitur
  3. Untuk membuatnya nyaman untuk bekerja, instance VK yang diinisialisasi harus dalam konfigurasi
  4. Sulit untuk melacak hubungan antar fitur

Akan senang mendengar jawaban Anda untuk ini dan menggandakannya dengan sangat eksplisit dalam dokumentasi, dan mungkin README.md

vk-io

Komentar yang paling membantu

Saya tidak akan mengatakan bahwa pendekatan "resmi", saya hanya akan membagikan preferensi pribadi saya dan sedikit bagian praktisnya.

Desain umum

Saya lebih suka menggunakan pendekatan monorepo untuk mengatur modul terdistribusi (perpustakaan benar-benar menggunakannya). Anda dapat mengambil template siap pakai untuk layanan, dan memisahkan implementasi bot dari perpustakaan menggunakan abstraksi (karena setiap perubahan yang melanggar akan membutuhkan banyak perhatian untuk menyesuaikannya). Ada baiknya juga menggunakan mesin virtual untuk kondisi yang identik dalam pengembangan dan produksi, misalnya Docker akan membantu di sini.

Arsitektur

Ketergantungan bot harus eksplisit, mis. tidak menambahkan "fitur" dengan satu impor, jika tidak, mimpi buruk debugging nyata akan dimulai di sini. Kode abstrak:

// commands/random.ts
import { Command } from '@my-project/core';
import { getRandomIntegerInRange } from '@my-project/utils';

export const randomCommand = new Command({
    slug: 'random',

    aliases: [
        'Ρ€Π°Π½Π΄ΠΎΠΌ',
        'random'
    ],

    description = 'Ρ€Π°Π½Π΄ΠΌΠΎΠ½ΠΎΠ΅ число Π² ΠΏΡ€ΠΎΠΌΠ΅ΠΆΡƒΡ‚ΠΊΠ΅';

    arguments: [
        {
            type: 'integer',
            key: 'min',
            label: 'минк/макс',
            default: null
        },
        {
            type: 'integer',
            key: 'max',
            label: 'минк/макс',
            default: null
        }
    ],

    handler(context) {
        // Π Π°Π±ΠΎΡ‚Π°Π΅ΠΌ с Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚Π°ΠΌΠΈ, Π° Π½Π΅ тСкстом
        let { min = null, max = null } = context.commander.params;

        if (min === null && max === null) {
            min = 0;
            max = 100;
        } else if (max === null) {
            max = min;
            min = 0;
        }

        const result = getRandomIntegerInRange(min, max);

        return context.answer({
            text: `число Π² ΠΏΡ€ΠΎΠΌΠ΅ΠΆΡƒΡ‚ΠΊΠ΅ ${min} - ${max}: ${result}`
        });
    }
});

// commands/index.ts
export * from './random';

// bot.ts
import {
    Bot,

    SessionManager,
    RedisSessionStorage,

    RateLimitManager,

    CommanderManager
} from '@my-project/core';

import * as commands from './commands';

const sessionManager = new SessionManager({
    storage: new RedisSessionStorage({})
});

const rateLimitManager = new RateLimitManager({
    maxPerSecond: 1
});

const commanderManager = new CommanderManager();

for (const command of Object.values(commands)) {
    commanderManager.add(command);
}

const bot = new Bot({
    // ...options
});

// Π­Ρ‚ΠΎ ΠΌΠΎΠΆΠ΅Ρ‚ Π±Ρ‹Ρ‚ΡŒ кастомная Ρ†Π΅ΠΏΠΎΡ‡ΠΊΠ° middleware Π² Π±ΠΎΡ‚Π΅
bot.incoming.on('message', sessionManager.middleware);
bot.incoming.on('message', rateLimitManager.middleware);
bot.incoming.on('message', commanderManager.middleware);

bot.start()
    .then(() => {
        console.log('Bot started', error);
    })
    .catch((error: Error) => {
        console.error('Error starting bot', error);

        process.exit(1);
    });

Hal-hal penting dari kode di atas:

  1. Menerapkan paket dasar @my-project/core , yang berisi hal-hal yang diperlukan untuk bot.
  2. Perintah menggunakan parameter, sehingga mewujudkan Dispatcher . Mengapa ini perlu? Semuanya sangat sederhana - Anda dapat memanggil perintah dari mana saja dengan parameter yang ditentukan. Dari teks, kami menguraikan argumen yang dijelaskan dalam perintah dengan cara apa pun yang nyaman, dan tombol-tombol di keyboard sudah diatur dengan mereka dan dialamatkan ke perintah yang kami butuhkan. Jadi, kami menghindari duplikasi logika dan mengatur validasi argumen. Misalnya memanggil satu perintah dari yang lain:
export const dndCommand = new Command({
    // ...
    handler(context) {
        return context.commander.enter('random', {
            params: {
                min: 1,
                max: 20
            }
        });
    }
});
  1. Setiap modul manajer yang telah digunakan memiliki area tanggung jawab masing-masing dan memberikan pemahaman yang jelas tentang apa yang digunakan dan tersedia.

Kesimpulan

Ini adalah pendekatan yang saya gunakan di bot saya, dan ternyata cukup nyaman untuk diterapkan dari bot yang sederhana hingga yang rumit. Paling-paling, paket @my-project/core seharusnya hanya menjadi alias perpustakaan yang telah menerapkan dan menguji semuanya, dan file terlihat seperti ini:

export { Bot, Command } from 'super-bot-library';

export { ViewerManager } from './middlewares';

Dalam kasus perubahan di perpustakaan, maka dimungkinkan untuk mengganti salah satu antarmuka. Tapi tidak ada yang melarang menyimpan semua logika hanya untuk proyek.

Ada implementasi lain yang menarik dari logika bot pada kait, yang digunakan misalnya dalam React atau Vue , Anda dapat menyodoknya langsung menggunakan kode ini .

Semua 2 komentar

Saya tidak akan mengatakan bahwa pendekatan "resmi", saya hanya akan membagikan preferensi pribadi saya dan sedikit bagian praktisnya.

Desain umum

Saya lebih suka menggunakan pendekatan monorepo untuk mengatur modul terdistribusi (perpustakaan benar-benar menggunakannya). Anda dapat mengambil template siap pakai untuk layanan, dan memisahkan implementasi bot dari perpustakaan menggunakan abstraksi (karena setiap perubahan yang melanggar akan membutuhkan banyak perhatian untuk menyesuaikannya). Ada baiknya juga menggunakan mesin virtual untuk kondisi yang identik dalam pengembangan dan produksi, misalnya Docker akan membantu di sini.

Arsitektur

Ketergantungan bot harus eksplisit, mis. tidak menambahkan "fitur" dengan satu impor, jika tidak, mimpi buruk debugging nyata akan dimulai di sini. Kode abstrak:

// commands/random.ts
import { Command } from '@my-project/core';
import { getRandomIntegerInRange } from '@my-project/utils';

export const randomCommand = new Command({
    slug: 'random',

    aliases: [
        'Ρ€Π°Π½Π΄ΠΎΠΌ',
        'random'
    ],

    description = 'Ρ€Π°Π½Π΄ΠΌΠΎΠ½ΠΎΠ΅ число Π² ΠΏΡ€ΠΎΠΌΠ΅ΠΆΡƒΡ‚ΠΊΠ΅';

    arguments: [
        {
            type: 'integer',
            key: 'min',
            label: 'минк/макс',
            default: null
        },
        {
            type: 'integer',
            key: 'max',
            label: 'минк/макс',
            default: null
        }
    ],

    handler(context) {
        // Π Π°Π±ΠΎΡ‚Π°Π΅ΠΌ с Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚Π°ΠΌΠΈ, Π° Π½Π΅ тСкстом
        let { min = null, max = null } = context.commander.params;

        if (min === null && max === null) {
            min = 0;
            max = 100;
        } else if (max === null) {
            max = min;
            min = 0;
        }

        const result = getRandomIntegerInRange(min, max);

        return context.answer({
            text: `число Π² ΠΏΡ€ΠΎΠΌΠ΅ΠΆΡƒΡ‚ΠΊΠ΅ ${min} - ${max}: ${result}`
        });
    }
});

// commands/index.ts
export * from './random';

// bot.ts
import {
    Bot,

    SessionManager,
    RedisSessionStorage,

    RateLimitManager,

    CommanderManager
} from '@my-project/core';

import * as commands from './commands';

const sessionManager = new SessionManager({
    storage: new RedisSessionStorage({})
});

const rateLimitManager = new RateLimitManager({
    maxPerSecond: 1
});

const commanderManager = new CommanderManager();

for (const command of Object.values(commands)) {
    commanderManager.add(command);
}

const bot = new Bot({
    // ...options
});

// Π­Ρ‚ΠΎ ΠΌΠΎΠΆΠ΅Ρ‚ Π±Ρ‹Ρ‚ΡŒ кастомная Ρ†Π΅ΠΏΠΎΡ‡ΠΊΠ° middleware Π² Π±ΠΎΡ‚Π΅
bot.incoming.on('message', sessionManager.middleware);
bot.incoming.on('message', rateLimitManager.middleware);
bot.incoming.on('message', commanderManager.middleware);

bot.start()
    .then(() => {
        console.log('Bot started', error);
    })
    .catch((error: Error) => {
        console.error('Error starting bot', error);

        process.exit(1);
    });

Hal-hal penting dari kode di atas:

  1. Menerapkan paket dasar @my-project/core , yang berisi hal-hal yang diperlukan untuk bot.
  2. Perintah menggunakan parameter, sehingga mewujudkan Dispatcher . Mengapa ini perlu? Semuanya sangat sederhana - Anda dapat memanggil perintah dari mana saja dengan parameter yang ditentukan. Dari teks, kami menguraikan argumen yang dijelaskan dalam perintah dengan cara apa pun yang nyaman, dan tombol-tombol di keyboard sudah diatur dengan mereka dan dialamatkan ke perintah yang kami butuhkan. Jadi, kami menghindari duplikasi logika dan mengatur validasi argumen. Misalnya memanggil satu perintah dari yang lain:
export const dndCommand = new Command({
    // ...
    handler(context) {
        return context.commander.enter('random', {
            params: {
                min: 1,
                max: 20
            }
        });
    }
});
  1. Setiap modul manajer yang telah digunakan memiliki area tanggung jawab masing-masing dan memberikan pemahaman yang jelas tentang apa yang digunakan dan tersedia.

Kesimpulan

Ini adalah pendekatan yang saya gunakan di bot saya, dan ternyata cukup nyaman untuk diterapkan dari bot yang sederhana hingga yang rumit. Paling-paling, paket @my-project/core seharusnya hanya menjadi alias perpustakaan yang telah menerapkan dan menguji semuanya, dan file terlihat seperti ini:

export { Bot, Command } from 'super-bot-library';

export { ViewerManager } from './middlewares';

Dalam kasus perubahan di perpustakaan, maka dimungkinkan untuk mengganti salah satu antarmuka. Tapi tidak ada yang melarang menyimpan semua logika hanya untuk proyek.

Ada implementasi lain yang menarik dari logika bot pada kait, yang digunakan misalnya dalam React atau Vue , Anda dapat menyodoknya langsung menggunakan kode ini .

Terima kasih atas jawaban yang sangat baik dan terperinci. Dan saya ingin meminta Anda untuk membiarkan masalah ini terbuka sehingga orang lain dapat membacanya juga

Apakah halaman ini membantu?
0 / 5 - 0 peringkat

Masalah terkait

SOS
helix-team picture helix-team  Β·  4Komentar

T1MOXA picture T1MOXA  Β·  22Komentar

Jengas picture Jengas  Β·  15Komentar

Saiv46 picture Saiv46  Β·  9Komentar

Pacmard picture Pacmard  Β·  3Komentar