前往
大廳
主題

漫畫搜尋器

老爸爸 | 2022-06-21 20:18:39 | 巴幣 122 | 人氣 388

漫畫搜尋器



這篇雖然是以 Discord 機器人作為演示,但還是著重於 shentai 這個套件本身。

套件說明


根據作者描述適合用於 REST API, Bots, Sites,所以這次我以 Discord 機器人來使用這個套件。

套件的功能大致包含:
  • 獲取當前熱門漫畫
  • 獲取當前最新漫畫
  • 隨機產生漫畫
  • 獲取指定 id 的漫畫
  • 獲取指定作者的漫畫
  • 獲取指定關鍵字的漫畫
其他用法可參考 shentai

不過獲取熱門 / 最新漫畫,目前測試下來除了獲取的資料數量外,並沒有太大的差距,所以以下功能並不包含獲取最新漫畫這個功能。

製作過程


1. 安裝套件

安裝套件至 Discord 機器人的專案當中。

npm i shentai@1.2.3

2. 建立命令

使用 @discordjs/builders 內置的 SlashCommandBuilder 方法建立命令。

data: new SlashCommandBuilder()
    .setName('comic')
    .setDescription('漫畫搜尋器')

3. 建立交互方式

使用 discord.js 內置的 MessageSelectMenu 方法建立下拉式選單作為交互方式。


const menu = new MessageActionRow()
    .addComponents(
        new MessageSelectMenu()
            .setCustomId('comicMenu')
            .setPlaceholder('選擇模式')
            .addOptions([
                {
                    label: 'popular',
                    description: '顯示當前熱度最高的漫畫',
                    value: 'popular',
                    emoji: '<a:blobreach:984878619497230346>'
                }, {
                    label: 'random',
                    description: '隨機推薦漫畫',
                    value: 'random',
                    emoji: '<a:blobreach:984878612954107944>'
                }, {
                    label: 'id',
                    description: '根據編號查詢漫畫',
                    value: 'id',
                    emoji: '<a:blobreach:984878611293171713>'
                }, {
                    label: 'keyword',
                    description: '根據關鍵字查詢漫畫',
                    value: 'keyword',
                    emoji: '<a:blobreach:984878618025021450>'
                }, {
                    label: 'author',
                    description: '根據作者查詢漫畫',
                    value: 'author',
                    emoji: '<a:blobreach:984878614669566022>'
                }
            ]
        ));

4. 獲取下拉式選單的交互結果

當使用者選取下拉式選單後,根據選取值執行相對應的動作。

client.on('interactionCreate', async (interaction) => {
    if (interaction.isSelectMenu()) {
        if (interaction.customId === 'comicMenu') {
            const menu = require('./src/comic/menu');
            
            try {
                await menu.execute(interaction);
            } catch (error) {
                console.error(error);
                interaction.reply({ content: '執行此命令時出錯!', ephemeral: true });
            }
        }
    }
}

5. 建立嵌入訊息

使用 discord.js 內置的 MessageEmbed 方法建立嵌入訊息,因為 api 會回傳 2 種資料,一種為漫畫的物件,另一種為搜尋結果,所以先建立兩種嵌入訊息,作為將來使用。

單個漫畫物件:


const setComicEmbed = (comic) => new MessageEmbed()
    .setTitle(comic.titles.english)
    .setDescription(comic.titles.original)
    .setColor('#f23857')
    .addField('編號', `\`${comic.id}\``, true)
    .addField('頁數', `\`${comic.pages.length}\``, true)
    .setImage(comic.cover);

多個漫畫物件:


const setMoreComicEmbed = (comic) => comic.map((item) => new MessageEmbed()
    .setTitle(item.titles.english)
    .setDescription(`#${item.id}`)
    .setColor('#f23857')
    .setThumbnail(item.cover));


6. 判斷選擇值

使用者選擇完下拉式選單後,選擇結果會儲存至 interaction.values[0] 當中,判斷並執行對應的功能。

當前熱門漫畫
使用 sHentai 套件內的 getPopular() 方法取得搜尋結果。

comic = await sHentai.getPopular();
await interaction.update({ embeds: setMoreComicEmbed(comic), components: [] });

隨機產生漫畫
使用 sHentai 套件內的 getRandom() 方法取得漫畫物件。

comic = await sHentai.getRandom();
await interaction.update({ embeds: [setComicEmbed(comic)], components: [] });

根據 id / 關鍵字 / 作者搜尋漫畫
這三個功能皆須要使用者再輸入對應的查詢條件,才能進行使用,所以當選擇這三個功能時,使用  discord.js 內置的 ModalTextInputComponent 方法建立一個互動視窗,供使用者進行輸入,而透過 id 進行搜尋則限制為最大只能輸入 6 位數字,確保使用者能搜尋到漫畫。

原先打算在互動式窗內進行功能選擇,但目前 DiscordJS 沒有這個做法,甚至 Discord 本身也沒有。


let customId, info, valueActionRow;

let components = new TextInputComponent()
    .setCustomId('value')
    .setLabel("請輸入查詢條件")
    .setStyle('SHORT')
    .setRequired(true);

if (menuValue === 'id') {
    customId = 'comicIdModel';
    info = '編號查詢';
    components = new TextInputComponent()
        .setCustomId('value')
        .setLabel("請輸入查詢條件")
        .setStyle('SHORT')
        .setMaxLength(6)
        .setRequired(true);
} else if (menuValue === 'keyword') {
    customId = 'comicKeywordModel';
    info = '關鍵字查詢';
} else if (menuValue === 'author') {
    customId = 'comicAuthorModel';
    info = '作者查詢';
}

valueActionRow = new MessageActionRow().addComponents(components);

const modal = new Modal()
    .setCustomId(customId)
    .setTitle(info);

modal.addComponents(valueActionRow);

await interaction.showModal(modal);

7. 獲取互動視窗交互結果

當使用者點擊送出後,根據輸入值執行相對應的動作。

client.on('interactionCreate', async (interaction) => {
    if (interaction.isModalSubmit()) {
        let modal;

        const customId = [
            'comicIdModel',
            'comicKeywordModel',
            'comicAuthorModel'
        ];

        if (customId.includes(interaction.customId)) {
            modal = require('./src/comic/modal');
        }

        try {
            await modal.execute(interaction);
        } catch (error) {
            console.error(error);
            interaction.reply({ content: '執行此命令時出錯!', ephemeral: true });
        }
    }
}

8. 判斷使用者使用的互動視窗

使用 interaction.customId,獲取並判斷使用者所使用的互動視窗,並用 interaction.fields.getTextInputValue('value') 取得輸入值後,執行對應的功能。

id 搜尋漫畫
先使用 正則表達式 判斷是否為正整數,接著使用 sHentai 套件內的 getDoujin(value) 方法取得搜尋結果。

if (/^[0-9]*$/.test(value)) {
    comic = await sHentai.getDoujin(value);

    const embed = comic.status ? errorEmbed('看起來你要找的東西不在這裡!') : setComicEmbed(comic);

    await interaction.update({ embeds: [embed], components: [] });
} else {
    await interaction.update({ embeds: [errorEmbed('請輸入純數字!')], components: [] });
}

關鍵搜尋漫畫
使用 sHentai 套件內的 byAuthor(value) 方法取得搜尋結果。

comic = await sHentai.byAuthor(value);

const authorEmbed = comic.status ? errorEmbed('看起來你要找的東西不在這裡!') : setMoreComicEmbed(comic.results);

await interaction.update({ embeds: [authorEmbed], components: [] });

作者搜尋漫畫
使用 sHentai 套件內的 search(value) 方法取得搜尋結果。
comic = await sHentai.search(value);

const keyWordEmbed = comic.status ? errorEmbed('看起來你要找的東西不在這裡!') : setMoreComicEmbed(comic.results);

await interaction.update({ embeds: [keyWordEmbed], components: [] });

回傳值介紹


漫畫物件

{
  id: String,                       // id
  author: String,               // 作者
  titles: {
    english: String,            // 英文標題
    original: String,            // 原始標題
  },
  pages: Array<String>,   // 內容連結 (字元陣列)
  tags: Array<String>,      // 標籤 (字元陣列)
  cover: String                  // 封面
}

搜尋類

{
  results: Object[],                                   // 結果 (物件)
  allDoujinsCount: Number,                     // 結果總數
  lastPage: Number,                                 // 總頁數
  currentPage: Number,                           // 當前頁數
  next: Promise<SearchResult>,           // 下一頁
  prev: Promise<SearchResult>,           // 前一頁
  first: Promise<SearchResult>,            // 第一頁
  last: Promise<SearchResult>,            // 最後一頁
  getPage: Promise<SearchResult>,    // 指定頁
}

搜尋結果

[
  {
    id: String,            // id
    titles: Object,      // 標題
    cover: String       // 封面
  },
]
送禮物贊助創作者 !
0
留言

創作回應

更多創作