mirror of
https://github.com/citra-emu/discord-bot.git
synced 2025-07-08 22:00:32 +00:00
Detailed Device Info
Add select button to grab detailed devices results Restrict button interactions to only whoever sent the initial command Change formatting for some things Additional data can now be easily added to the detailed results embed, if needed.
This commit is contained in:
parent
89ebc0f5fb
commit
95694e3912
|
@ -1,14 +1,13 @@
|
||||||
import { ActionRowBuilder, ButtonBuilder, ButtonStyle, Client, EmbedBuilder, Interaction, Message, MessageActionRow, MessageButton, MessageEmbed, sentMessage } from 'discord.js';
|
import { ActionRowBuilder, ButtonBuilder, ButtonStyle, Client, EmbedBuilder, Interaction, Message, MessageActionRow, MessageButton, MessageEmbed, sentMessage } from 'discord.js';
|
||||||
import { search as fakeapi } from 'gsmarena-api';
|
import { search as fakeapi_search, catalog as fakeapi_detail } from 'gsmarena-api';
|
||||||
import logger from '../logging';
|
import logger from '../logging';
|
||||||
|
|
||||||
// Variables you might want to change:
|
// Variables you might want to change:
|
||||||
// Allowed roles:
|
// Allowed roles:
|
||||||
export const roles = ["@everyone"];
|
export const roles = ["@everyone"];
|
||||||
// How long before previous messages can no longer be interacted with. default is 200000 (200 seconds)
|
// How long before previous messages can no longer be interacted with:
|
||||||
const mtime = 200000 // default 200000 (200 seconds)
|
const mtime = 200000 // default 200000 (200 seconds)
|
||||||
|
|
||||||
|
|
||||||
// Button config:
|
// Button config:
|
||||||
const next = new ButtonBuilder()
|
const next = new ButtonBuilder()
|
||||||
.setCustomId('next')
|
.setCustomId('next')
|
||||||
|
@ -19,12 +18,77 @@ const previous = new ButtonBuilder()
|
||||||
.setCustomId('previous')
|
.setCustomId('previous')
|
||||||
.setLabel('< Previous Result')
|
.setLabel('< Previous Result')
|
||||||
.setStyle(ButtonStyle.Secondary);
|
.setStyle(ButtonStyle.Secondary);
|
||||||
|
|
||||||
|
const select = new ButtonBuilder()
|
||||||
|
.setCustomId('select')
|
||||||
|
.setLabel('Select')
|
||||||
|
.setStyle(ButtonStyle.Success);
|
||||||
|
|
||||||
|
|
||||||
const row = new ActionRowBuilder()
|
const row = new ActionRowBuilder()
|
||||||
.addComponents(previous, next);
|
.addComponents(previous, select, next);
|
||||||
|
|
||||||
|
// Create detailed embed with selected device information.
|
||||||
|
async function createDetailedEmbed(selectedDevice: any, searchQuery: string, results: number, index: number, searchResults: array) {
|
||||||
|
let device = await fakeapi_detail.getDevice(selectedDevice.id);
|
||||||
|
|
||||||
|
let name = device.name; // very messy and long code for parsing data
|
||||||
|
let platformChipset = device.detailSpec.find(spec => spec.category === 'Platform')?.specifications.find(spec => spec.name === 'Chipset')?.value || 'Unknown';
|
||||||
|
let platformGPU = device.detailSpec.find(spec => spec.category === 'Platform')?.specifications.find(spec => spec.name === 'GPU')?.value || 'Unknown';
|
||||||
|
let memoryInternal = device.detailSpec.find(spec => spec.category === 'Memory')?.specifications.find(spec => spec.name === 'Internal')?.value || 'Unknown';
|
||||||
|
let display = device.quickSpec.find(spec => spec.name === 'Display size')?.value || 'Unknown';
|
||||||
|
let battery = device.quickSpec.find(spec => spec.name === 'Battery size')?.value || 'Unknown';
|
||||||
|
let year = device.detailSpec.find(spec => spec.category === 'Launch')?.specifications.find(spec => spec.name === 'Announced')?.value.split(',')[0] || 'Unknown';
|
||||||
|
let misc = 'Announced in '+year+' '+display+' display, '+battery+' battery.';
|
||||||
|
|
||||||
|
// format storage and ram nicely
|
||||||
|
let storage = [...new Set(memoryInternal.match(/\d+(GB|TB)(?= \d+GB RAM)/g))].join('/') || 'Unknown ';
|
||||||
|
let ram = "Unknown "
|
||||||
|
if (memoryInternal) { // prevent an error when attempting to get detailed info on devices with no information on GSMArena
|
||||||
|
let matches = memoryInternal.match(/\d+GB RAM/g);
|
||||||
|
if (matches) {
|
||||||
|
ram = [...new Set(matches.map(x => x.replace(' RAM', '')))].join('/');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Messsy json parsing continues with a touch of formatting the cpu and gpu info nicely
|
||||||
|
let snapdragon = platformChipset.includes('Snapdragon') ? 'Snapdragon ' + platformChipset.split('Snapdragon')[1].split('(')[0].trim() : undefined;
|
||||||
|
let exynos = platformChipset.includes('Exynos') ? 'Exynos ' + platformChipset.split('Exynos')[1].split(' ')[1] : undefined;
|
||||||
|
let dimensity = platformChipset.includes('Dimensity') ? 'Dimensity ' + platformChipset.split('Dimensity')[1].split(' ')[1] : undefined;
|
||||||
|
let formattedChipset = [snapdragon, exynos, dimensity].filter(word => word !== undefined).join(' / ');
|
||||||
|
if (formattedChipset === '') { // prevent errors when formattedChipset is null
|
||||||
|
formattedChipset = device.quickSpec.find(spec => spec.name === 'Chipset')?.value || 'Unknown';
|
||||||
|
}
|
||||||
|
|
||||||
|
let adreno = platformGPU.includes('Adreno') ? 'Adreno ' + platformGPU.split('Adreno')[1].split(' ')[1] : undefined;
|
||||||
|
let xclipse = platformGPU.includes('Xclipse') ? 'Xclipse ' + platformGPU.split('Xclipse')[1].split(' ')[1] : undefined;
|
||||||
|
let mali = platformGPU.includes('Mali') ? 'Mali-' + platformGPU.split('Mali-')[1].split(' ').slice(0, 2).join(' ') : undefined;
|
||||||
|
let formattedGPU = [adreno, xclipse, mali].filter(word => word !== undefined).join(' / ');
|
||||||
|
if (formattedGPU === '') {
|
||||||
|
formattedGPU = platformGPU
|
||||||
|
}
|
||||||
|
|
||||||
|
const imageUrl = device.img;
|
||||||
|
let embed = new EmbedBuilder() // Assemble Embed
|
||||||
|
.setColor('#0099ff')
|
||||||
|
.setTitle(`Result for: ${searchQuery}`)
|
||||||
|
.setDescription('Source: https://www.gsmarena.com')
|
||||||
|
.addFields(
|
||||||
|
{ name: 'Device:', value: name },
|
||||||
|
{ name: 'Chipset:', value: formattedChipset },
|
||||||
|
{ name: 'GPU', value: formattedGPU },
|
||||||
|
{ name: 'Ram:', value: ram, inline: true },
|
||||||
|
{ name: 'Storage:', value: storage, inline: true },
|
||||||
|
{ name: 'Misc:', value: misc },
|
||||||
|
)
|
||||||
|
.setThumbnail(imageUrl)
|
||||||
|
.setFooter({ text: searchResults.length > 1 ? "Showing Detailed Result...." : "This is the only result for your query." });
|
||||||
|
|
||||||
|
return embed;
|
||||||
|
}
|
||||||
|
|
||||||
// Create embed with current device information.
|
// Create embed with current device information.
|
||||||
function createEmbed(device: any, searchQuery: string, results: number, index: number, devices: array) {
|
function createEmbed(device: any, searchQuery: string, results: number, index: number, searchResults: array) {
|
||||||
|
|
||||||
let name = device.name;
|
let name = device.name;
|
||||||
let chipset = device.description.match(/(?:display,)(.*)(?:chipset)/)?.[1].trim() || 'Unknown'; // parsing json data
|
let chipset = device.description.match(/(?:display,)(.*)(?:chipset)/)?.[1].trim() || 'Unknown'; // parsing json data
|
||||||
let ram = device.description.match(/(?:\s)([\d]+)\sGB RAM/)?.[1] + 'GB' || 'Unknown';
|
let ram = device.description.match(/(?:\s)([\d]+)\sGB RAM/)?.[1] + 'GB' || 'Unknown';
|
||||||
|
@ -43,9 +107,9 @@ function createEmbed(device: any, searchQuery: string, results: number, index: n
|
||||||
|
|
||||||
|
|
||||||
const imageUrl = device?.img;
|
const imageUrl = device?.img;
|
||||||
const embed = new EmbedBuilder()
|
let embed = new EmbedBuilder()
|
||||||
.setColor('#0099ff')
|
.setColor('#0099ff')
|
||||||
.setTitle(devices.length > 1 ? `Search results for: ${searchQuery}` : `Result for: ${searchQuery}`)
|
.setTitle(searchResults.length > 1 ? `Preview results for: ${searchQuery}` : `Result for: ${searchQuery}`)
|
||||||
.setDescription('Source: https://www.gsmarena.com')
|
.setDescription('Source: https://www.gsmarena.com')
|
||||||
.addFields(
|
.addFields(
|
||||||
{ name: 'Device:', value: name },
|
{ name: 'Device:', value: name },
|
||||||
|
@ -55,22 +119,23 @@ function createEmbed(device: any, searchQuery: string, results: number, index: n
|
||||||
{ name: 'Misc:', value: misc },
|
{ name: 'Misc:', value: misc },
|
||||||
)
|
)
|
||||||
.setThumbnail(imageUrl)
|
.setThumbnail(imageUrl)
|
||||||
.setFooter({ text: devices.length > 1 ? results+' total results... '+(index + 1)+'/'+devices.length : "This is the only result for your query (:" });
|
.setFooter({ text: searchResults.length > 1 ? results+' total results... '+(index + 1)+'/'+searchResults.length : "This is the only result for your query (:" });
|
||||||
|
|
||||||
return embed;
|
return embed;
|
||||||
}
|
}
|
||||||
|
|
||||||
// End listener function to disable buttons after message collector times out
|
// End listener function to disable buttons after message collector times out
|
||||||
function addEndListener(collector, previous, next, row, sentMessage, devices) {
|
function addEndListener(collector, previous, next, row, sentMessage, searchResults) {
|
||||||
let endListenerAdded = false;
|
let endListenerAdded = false;
|
||||||
|
|
||||||
// Function to be called when the collector ends to disable buttons and update message.
|
// Function to be called when the collector ends to disable buttons and update message.
|
||||||
const endListener = async () => {
|
const endListener = async () => {
|
||||||
previous.setDisabled(true);
|
previous.setDisabled(true);
|
||||||
next.setDisabled(true);
|
next.setDisabled(true);
|
||||||
|
select.setDisabled(true);
|
||||||
|
|
||||||
// Update footer text on timeout only if there were multiple results.
|
// Update footer text on timeout only if there were multiple results.
|
||||||
if (devices.length > 1) {
|
if (searchResults.length > 1) {
|
||||||
const newEmbed = new EmbedBuilder(sentMessage.embeds[0])
|
const newEmbed = new EmbedBuilder(sentMessage.embeds[0])
|
||||||
.setFooter({ text: "Interaction timeout: Buttons disabled." });
|
.setFooter({ text: "Interaction timeout: Buttons disabled." });
|
||||||
await sentMessage.edit({ components: [row] }).catch(console.error);
|
await sentMessage.edit({ components: [row] }).catch(console.error);
|
||||||
|
@ -78,7 +143,7 @@ function addEndListener(collector, previous, next, row, sentMessage, devices) {
|
||||||
}
|
}
|
||||||
collector.stop();
|
collector.stop();
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add the end listener only if it hasn't already been added.
|
// Add the end listener only if it hasn't already been added.
|
||||||
if (!endListenerAdded) {
|
if (!endListenerAdded) {
|
||||||
collector.on('end', endListener);
|
collector.on('end', endListener);
|
||||||
|
@ -93,65 +158,76 @@ export async function command(message: Message) {
|
||||||
// Reset index and button state.
|
// Reset index and button state.
|
||||||
let index = 0;
|
let index = 0;
|
||||||
previous.setDisabled(true);
|
previous.setDisabled(true);
|
||||||
|
select.setDisabled(false);
|
||||||
next.setDisabled(false);
|
next.setDisabled(false);
|
||||||
// Split message into array.
|
// Split message into array.
|
||||||
const words = message.content.split(' ');
|
const words = message.content.split(' ');
|
||||||
|
|
||||||
// Remove the command and pass the rest of the message along to the gsmarena-api.
|
// Remove the command and pass the rest of the message along to the gsmarena-api.
|
||||||
const searchQuery = words.slice(1).join(' ');
|
const searchQuery = words.slice(1).join(' ');
|
||||||
const devices = await fakeapi.search(searchQuery);
|
const searchResults = await fakeapi_search.search(searchQuery);
|
||||||
|
|
||||||
if (devices.length === 0) {
|
if (searchResults.length === 0) {
|
||||||
message.channel.send('No device(s) found for "' + searchQuery + '"');
|
message.channel.send('No device(s) found for "' + searchQuery + '"');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the initial embed with the first device information and reset button state.
|
// Create the initial embed with the first device information and reset button state.
|
||||||
const embed = createEmbed(devices[index], searchQuery, devices.length, index, devices);
|
const embed = createEmbed(searchResults[index], searchQuery, searchResults.length, index, searchResults);
|
||||||
// Only send buttons if there are multiple results
|
// Only send buttons if there are multiple results
|
||||||
let sentMessage;
|
let sentMessage;
|
||||||
if (devices.length > 1) {
|
if (searchResults.length > 1) {
|
||||||
sentMessage = await message.channel.send({ embeds: [embed], components: [row] });
|
sentMessage = await message.channel.send({ embeds: [embed], components: [row] });
|
||||||
} else {
|
} else {
|
||||||
sentMessage = await message.channel.send({ embeds: [embed] });
|
let detailedEmbed = await createDetailedEmbed(searchResults[index], searchQuery, searchResults.length, index, searchResults);
|
||||||
|
sentMessage = await message.channel.send({ embeds: [detailedEmbed] });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the interaction collector -- handles button presses.
|
// Create the interaction collector -- handles button presses.
|
||||||
const filter = (interaction: Interaction) => interaction.isButton() && (interaction.customId === 'next' || interaction.customId === 'previous');
|
const filter = (interaction: Interaction) => interaction.isButton() && (interaction.customId === 'next' || interaction.customId === 'previous' || interaction.customId === 'select');
|
||||||
const collector = sentMessage.createMessageComponentCollector({ filter, time: mtime });
|
const collector = sentMessage.createMessageComponentCollector({ filter, time: mtime });
|
||||||
addEndListener(collector, previous, next, row, sentMessage, devices);
|
addEndListener(collector, previous, next, row, sentMessage, searchResults);
|
||||||
|
|
||||||
|
// Logic to update the message and components when cycling through pages.
|
||||||
// Logic to update the message and components when cycling through pages.
|
collector.on('collect', async (interaction: Interaction) => {
|
||||||
collector.on('collect', async (interaction: Interaction) => {
|
if (interaction.user.id !== message.author.id) { // Only allow the original command user to interact with the buttons.
|
||||||
try {
|
// Send an ephemeral reply to the user
|
||||||
if (interaction.customId === 'next') {
|
await interaction.reply({ content: 'Only the original sender of the command can interact with buttons.', ephemeral: true });
|
||||||
index++;
|
return;
|
||||||
} else if (interaction.customId === 'previous') {
|
}
|
||||||
index--;
|
let selectButtonPushed = false;
|
||||||
}
|
try {
|
||||||
index = Math.max(0, Math.min(index, devices.length - 1));
|
if (interaction.customId === 'next') {
|
||||||
|
index++;
|
||||||
// Update button states based on current index.
|
} else if (interaction.customId === 'previous') {
|
||||||
previous.setDisabled(index === 0);
|
index--;
|
||||||
next.setDisabled(index === devices.length - 1);
|
} else if (interaction.customId == 'select') {
|
||||||
|
const detailedEmbed = await createDetailedEmbed(searchResults[index], searchQuery, searchResults.length, index, searchResults);
|
||||||
// Create a new embed with the updated device information.
|
await interaction.deferUpdate();
|
||||||
const newEmbed = createEmbed(devices[index], searchQuery, devices.length, index, devices);
|
await interaction.editReply({ embeds: [detailedEmbed], components: [] });
|
||||||
|
selectButtonPushed = true;
|
||||||
// Update the message with the new embed and components.
|
}
|
||||||
await interaction.deferUpdate();
|
index = Math.max(0, Math.min(index, searchResults.length - 1));
|
||||||
await interaction.editReply({ embeds: [newEmbed], components: [row] });
|
|
||||||
|
|
||||||
|
// Update button states based on current index.
|
||||||
|
previous.setDisabled(index === 0);
|
||||||
|
next.setDisabled(index === searchResults.length - 1);
|
||||||
|
|
||||||
|
// Create a new embed with the updated device information.
|
||||||
|
if (!selectButtonPushed) {
|
||||||
|
let newEmbed = createEmbed(searchResults[index], searchQuery, searchResults.length, index, searchResults);
|
||||||
|
await interaction.deferUpdate();
|
||||||
|
await interaction.editReply({ embeds: [newEmbed], components: [row] });
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Collect Error:', error);
|
||||||
|
await interaction.followUp('An error occurred while processing the button click.')
|
||||||
|
} finally {
|
||||||
|
previous.setDisabled();
|
||||||
|
next.setDisabled();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
logger.error('Collect Error:', error);
|
|
||||||
await interaction.followUp('An error occurred while processing the button click.')
|
|
||||||
} finally {
|
|
||||||
previous.setDisabled();
|
|
||||||
next.setDisabled();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Command Error:', error);
|
logger.error('Command Error:', error);
|
||||||
message.channel.send('An error occurred while processing your request. It would appear whatever you just did broke the bot... Congratulations!');
|
message.channel.send('An error occurred while processing your request. It would appear whatever you just did broke the bot... Congratulations!');
|
||||||
|
|
Loading…
Reference in a new issue