mirror of
https://github.com/SrIzan10/vinci.git
synced 2026-06-06 01:07:00 +00:00
feat: wikipedia and rps
This commit is contained in:
12
TODO.md
12
TODO.md
@@ -2,9 +2,8 @@
|
||||
|
||||
## Fun Commands
|
||||
- [ ] /animal - Animal pictures with voting system (cat, dog, capybara, fox, raccoon)
|
||||
- [ ] /chiste - Joke command fetching from API
|
||||
- [ ] /rps - Rock Paper Scissors game
|
||||
- [ ] /tictactoe - Tic-tac-toe game (currently disabled)
|
||||
- [x] /chiste - Joke command fetching from API
|
||||
- [x] /rps - Rock Paper Scissors game
|
||||
- [x] /8ball - Magic 8-ball responses
|
||||
- [x] /megamind - Megamind meme generator with canvas
|
||||
- [x] /makesweet - Heart locket image generator
|
||||
@@ -13,16 +12,14 @@
|
||||
## Miscellaneous Commands
|
||||
- [ ] /rolemenu - Role selection menu (owner only)
|
||||
- [ ] /creditos - Bot credits and acknowledgments
|
||||
- [ ] /infinitecraft - InfiniteCraft recipe solver
|
||||
- [x] ~~/infinitecraft - InfiniteCraft recipe solver~~
|
||||
- [ ] /letra - Song lyrics search via Genius API
|
||||
- [x] /google - Google search results
|
||||
- [x] /sugerencias - Suggestion system with upvote/downvote
|
||||
- [ ] /wikipedia - Wikipedia search (Spanish/English)
|
||||
- [x] /wikipedia - Wikipedia search (Spanish/English)
|
||||
- [ ] /faq - FAQ system with Minecraft questions
|
||||
- [ ] /afk - AFK status management
|
||||
- [ ] /stats - Bot statistics (currently disabled)
|
||||
- [ ] /acortar - URL shortener (currently disabled)
|
||||
- [ ] /askjavi - Question system (disabled)
|
||||
|
||||
## Minecraft Commands
|
||||
- [ ] /ip - Minecraft server IP information
|
||||
@@ -40,7 +37,6 @@
|
||||
|
||||
# Utility Systems to Rewrite
|
||||
- [ ] Resolver - Role/user resolution utility
|
||||
- [ ] InfiniteCraft Finder - Recipe pathfinding algorithm
|
||||
- [ ] Wikipedia utility - Wikipedia search helper
|
||||
|
||||
# Plugin Systems
|
||||
|
||||
3
bun.lock
3
bun.lock
@@ -12,6 +12,7 @@
|
||||
"dotenv": "^16.3.1",
|
||||
"mongodb": "^6.17.0",
|
||||
"node-html-parser": "^7.0.1",
|
||||
"rockpaperscissors-checker": "^1.2.0",
|
||||
"sharp": "^0.34.2",
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -384,6 +385,8 @@
|
||||
|
||||
"restore-cursor": ["restore-cursor@4.0.0", "", { "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" } }, "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg=="],
|
||||
|
||||
"rockpaperscissors-checker": ["rockpaperscissors-checker@1.2.0", "", {}, "sha512-JfndRzDvMo1st+iK9dKZIEToFDouc+53+whmVFs+zyBOp0zv/sMNY9hUZI5J7ulygmW1fI6QmvRyXmfpN9Sh8Q=="],
|
||||
|
||||
"safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
|
||||
|
||||
"semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"dotenv": "^16.3.1",
|
||||
"mongodb": "^6.17.0",
|
||||
"node-html-parser": "^7.0.1",
|
||||
"rockpaperscissors-checker": "^1.2.0",
|
||||
"sharp": "^0.34.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
69
src/commands/misc/wikipedia.ts
Normal file
69
src/commands/misc/wikipedia.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import { commandModule, CommandType } from '@sern/handler'
|
||||
import { ApplicationCommandOptionType, AutocompleteInteraction, CacheType, CommandInteractionOptionResolver } from 'discord.js';
|
||||
import { getWikipedia, searchWikipedia } from '../../utils/wikipedia';
|
||||
|
||||
export default commandModule({
|
||||
type: CommandType.Slash,
|
||||
plugins: [],
|
||||
description: 'Busca cosas por wikipedia',
|
||||
//alias : [],
|
||||
options: [
|
||||
{
|
||||
name: 'español',
|
||||
description: 'Busca cosas por Wikipedia en español',
|
||||
type: ApplicationCommandOptionType.Subcommand,
|
||||
options: [
|
||||
{
|
||||
name: 'busqueda',
|
||||
description: 'Escribe qué buscar.',
|
||||
type: ApplicationCommandOptionType.String,
|
||||
required: true,
|
||||
autocomplete: true,
|
||||
command: {
|
||||
onEvent: [],
|
||||
execute: async (ctx) => {
|
||||
const search = await searchWikipedia('es', ctx as AutocompleteInteraction)
|
||||
await ctx.respond(
|
||||
search.map(res => ({ name: res.title.toString(), value: res.pageid.toString() }))
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'ingles',
|
||||
description: 'Busca cosas por Wikipedia en inglés',
|
||||
type: ApplicationCommandOptionType.Subcommand,
|
||||
options: [
|
||||
{
|
||||
name: 'search',
|
||||
description: 'Escribe qué buscar.',
|
||||
type: ApplicationCommandOptionType.String,
|
||||
required: true,
|
||||
autocomplete: true,
|
||||
command: {
|
||||
onEvent: [],
|
||||
execute: async (ctx) => {
|
||||
const search = await searchWikipedia('en', ctx as AutocompleteInteraction)
|
||||
await ctx.respond(
|
||||
search.map(res => ({ name: res.title.toString(), value: res.pageid.toString() }))
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
execute: async (ctx) => {
|
||||
const options = ctx.options as unknown as CommandInteractionOptionResolver<CacheType>
|
||||
switch (ctx.options.getSubcommand()) {
|
||||
case 'español': {
|
||||
getWikipedia('es', ctx, options);
|
||||
} break;
|
||||
case 'ingles': {
|
||||
getWikipedia('en', ctx, options);
|
||||
} break;
|
||||
}
|
||||
},
|
||||
});
|
||||
148
src/commands/silly/rps.ts
Normal file
148
src/commands/silly/rps.ts
Normal file
@@ -0,0 +1,148 @@
|
||||
import { commandModule, CommandType } from '@sern/handler';
|
||||
import {
|
||||
ActionRowBuilder,
|
||||
ApplicationCommandOptionType,
|
||||
ButtonBuilder,
|
||||
ButtonStyle,
|
||||
ComponentType,
|
||||
EmbedBuilder,
|
||||
GuildMember,
|
||||
} from 'discord.js';
|
||||
|
||||
import rockpaperscissors from 'rockpaperscissors-checker';
|
||||
|
||||
export default commandModule({
|
||||
type: CommandType.Slash,
|
||||
plugins: [],
|
||||
description: 'Juega piedra papel tijeras',
|
||||
options: [
|
||||
{
|
||||
name: 'usuario',
|
||||
description: 'El usuario con el que enfrentarse',
|
||||
type: ApplicationCommandOptionType.User,
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
execute: async (ctx) => {
|
||||
let player1: number, player2: number, winner, bothResponded: boolean;
|
||||
const option = ctx.options.getMember('usuario') as GuildMember;
|
||||
if (ctx.user.id === option.id) {
|
||||
return await ctx.reply({ content: `no puedes jugar contigo mismo 💀`, ephemeral: true });
|
||||
}
|
||||
if (option.user.bot) {
|
||||
return await ctx.reply({ content: `no puedes seleccionar a un bot.`, ephemeral: true });
|
||||
}
|
||||
const waitingEmbed = new EmbedBuilder()
|
||||
.setColor('Red')
|
||||
.setAuthor({ name: ctx.user.username, iconURL: ctx.user.displayAvatarURL() })
|
||||
.setTitle(`Piedra, papel o tijera? <:PauseChamp:1030169623070519388>`)
|
||||
.setDescription(
|
||||
`Esperando a que ambos jugadores eligan...\nJugador 1: ${ctx.user}\nJugador 2: ${option}`
|
||||
)
|
||||
.setFooter({ text: `Hay un máximo de 30 segundos para elegir.` });
|
||||
const winEmbed = new EmbedBuilder()
|
||||
.setColor('Green')
|
||||
.setAuthor({ name: ctx.user.username, iconURL: ctx.user.displayAvatarURL() })
|
||||
.setFooter({ text: `Gracias por jugar!` });
|
||||
const tieEmbed = new EmbedBuilder()
|
||||
.setColor('Yellow')
|
||||
.setAuthor({ name: ctx.user.username, iconURL: ctx.user.displayAvatarURL() })
|
||||
.setTitle(`Ha habido un empate <:Sadge:1015764348385382451>`)
|
||||
.setDescription(`Qué sadge, ha habido un empate...`)
|
||||
.setFooter({ text: `Volvemos a intentarlo?` });
|
||||
const timeUpEmbed = new EmbedBuilder()
|
||||
.setColor('Red')
|
||||
.setAuthor({ name: ctx.user.username, iconURL: ctx.user.displayAvatarURL() })
|
||||
.setTitle(`Se acabó!`)
|
||||
.setDescription(
|
||||
`Uno de los dos jugadores no han respondido en los 30 segundos, así que se acabó la partida!`
|
||||
)
|
||||
.setFooter({ text: `Volvemos a intentarlo?` });
|
||||
const buttons = ['Piedra', 'Papel', 'Tijera'].map((choice) => {
|
||||
return new ButtonBuilder()
|
||||
.setLabel(choice)
|
||||
.setCustomId(`rps-${choice.toLowerCase()}`)
|
||||
.setStyle(ButtonStyle.Secondary);
|
||||
});
|
||||
const row = new ActionRowBuilder<ButtonBuilder>();
|
||||
const message = await ctx.interaction.reply({
|
||||
content: `${option}, te han retado a Piedra Papel o Tijera!`,
|
||||
embeds: [waitingEmbed],
|
||||
fetchReply: true,
|
||||
components: [row.setComponents(buttons)],
|
||||
});
|
||||
const collector = message.createMessageComponentCollector({
|
||||
time: 30_000,
|
||||
componentType: ComponentType.Button,
|
||||
filter: (i) => [ctx.user.id, option.id].includes(i.user.id),
|
||||
});
|
||||
collector.on('collect', async (i) => {
|
||||
await i.deferReply({ ephemeral: true });
|
||||
if (i.customId === 'rps-piedra') {
|
||||
if (i.user.id === ctx.user.id) {
|
||||
player1 = 1;
|
||||
await i.editReply({
|
||||
content: `Se ha respondido **piedra** correctamente, buena suerte!\n[Volver al mensaje](${message.url})`,
|
||||
});
|
||||
} else if (i.user.id === option.id) {
|
||||
player2 = 1;
|
||||
await i.editReply({
|
||||
content: `Se ha respondido **piedra** correctamente, buena suerte!\n[Volver al mensaje](${message.url})`,
|
||||
});
|
||||
}
|
||||
} else if (i.customId === 'rps-papel') {
|
||||
if (i.user.id === ctx.user.id) {
|
||||
player1 = 2;
|
||||
await i.editReply({
|
||||
content: `Se ha respondido **papel** correctamente, buena suerte!\n[Volver al mensaje](${message.url})`,
|
||||
});
|
||||
} else if (i.user.id === option.id) {
|
||||
player2 = 2;
|
||||
await i.editReply({
|
||||
content: `Se ha respondido **papel** correctamente, buena suerte!\n[Volver al mensaje](${message.url})`,
|
||||
});
|
||||
}
|
||||
} else if (i.customId === 'rps-tijera') {
|
||||
if (i.user.id === ctx.user.id) {
|
||||
player1 = 3;
|
||||
await i.editReply({
|
||||
content: `Se ha respondido **tijera** correctamente, buena suerte!\n[Volver al mensaje](${message.url})`,
|
||||
});
|
||||
} else if (i.user.id === option.id) {
|
||||
player2 = 3;
|
||||
await i.editReply({
|
||||
content: `Se ha respondido **tijera** correctamente, buena suerte!\n[Volver al mensaje](${message.url})`,
|
||||
});
|
||||
}
|
||||
}
|
||||
if (player1 && player2) {
|
||||
const checker = rockpaperscissors(player1, player2);
|
||||
bothResponded = true;
|
||||
if (checker === 'player1') {
|
||||
winner = ctx.user.username;
|
||||
const setDescription = winEmbed
|
||||
.setDescription(`Tenemos resultados!\n**${winner}** ha ganado.`)
|
||||
.setTitle(`Ha ganado ${winner}! <:Pog:1030169609178976346>`);
|
||||
await message.edit({ embeds: [setDescription], components: [], content: `` });
|
||||
message.react('<:Pog:1030169609178976346>');
|
||||
} else if (checker === 'player2') {
|
||||
winner = option.user.username;
|
||||
const setDescription = winEmbed
|
||||
.setDescription(`Tenemos resultados!\n**${winner}** ha ganado.`)
|
||||
.setTitle(`Ha ganado ${winner}! <:Pog:1030169609178976346>`);
|
||||
await message.edit({ embeds: [setDescription], components: [], content: `` });
|
||||
message.react('<:Pog:1030169609178976346>');
|
||||
} else if (checker === 'tie') {
|
||||
await message.edit({ embeds: [tieEmbed], components: [], content: `` });
|
||||
}
|
||||
}
|
||||
});
|
||||
collector.on('ignore', async (i) => {
|
||||
await i.reply({ content: 'No estás jugando!', ephemeral: true });
|
||||
});
|
||||
collector.on('end', async () => {
|
||||
if (bothResponded) return;
|
||||
await message.edit({ embeds: [timeUpEmbed], components: [], content: `` });
|
||||
});
|
||||
},
|
||||
});
|
||||
93
src/utils/wikipedia.ts
Normal file
93
src/utils/wikipedia.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
import {
|
||||
ActionRowBuilder,
|
||||
AutocompleteInteraction,
|
||||
ButtonBuilder,
|
||||
ButtonStyle,
|
||||
CacheType,
|
||||
CommandInteractionOptionResolver,
|
||||
EmbedBuilder,
|
||||
} from 'discord.js';
|
||||
|
||||
import { Context } from '@sern/handler';
|
||||
|
||||
/**
|
||||
Search Wikipedia for a given input string in the specified language using the Wikipedia API.
|
||||
@async
|
||||
@function searchWikipedia
|
||||
@param {string} lang - The language code for the Wikipedia language edition to search in.
|
||||
@param {AutocompleteInteraction} autocomplete - The autocomplete interaction object containing the input to search for.
|
||||
@returns {Promise<SearchWikipediaObject[]>} - A promise that resolves with an array of search results.
|
||||
**/
|
||||
export async function searchWikipedia(
|
||||
lang: string,
|
||||
autocomplete: AutocompleteInteraction<CacheType>
|
||||
) {
|
||||
const input = autocomplete.options.getFocused();
|
||||
if (!input) {
|
||||
return [
|
||||
{
|
||||
ns: 0,
|
||||
title: 'Empieza a escribir para buscar!',
|
||||
pageid: 0,
|
||||
size: 0,
|
||||
wordcount: 0,
|
||||
snippet: '',
|
||||
timestamp: '',
|
||||
},
|
||||
] as SearchWikipediaObject[];
|
||||
}
|
||||
const request = await fetch(
|
||||
`https://${lang}.wikipedia.org/w/api.php?action=query&list=search&format=json&srsearch=${input}`
|
||||
).then(r => r.json())
|
||||
return request.query.search as SearchWikipediaObject[];
|
||||
}
|
||||
|
||||
export async function getWikipedia(
|
||||
lang: string,
|
||||
ctx: Context,
|
||||
options: CommandInteractionOptionResolver<CacheType>
|
||||
) {
|
||||
const pageid = options.getString(lang === 'es' ? 'busqueda' : 'search', true);
|
||||
if (Number.isNaN(Number(pageid)))
|
||||
return ctx.reply({ content: 'Elige en el autocompletado el artículo.', ephemeral: true });
|
||||
const request = await fetch(
|
||||
`https://${lang}.wikipedia.org/w/api.php?action=query&prop=extracts&exintro&explaintext&pageids=${pageid}&format=json`
|
||||
).then(r => r.json());
|
||||
const firstParagraph = request.query.pages[pageid].extract.split('\n\n')[0] as string;
|
||||
const title = request.query.pages[pageid].title as string;
|
||||
|
||||
const canonicalArticleURL = await fetch(
|
||||
`https://${lang}.wikipedia.org/w/api.php?action=query&prop=info&pageids=${pageid}&inprop=url&format=json`
|
||||
)
|
||||
.then(async (res) => {
|
||||
return (await res.json()).query.pages[pageid].canonicalurl as string;
|
||||
});
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setTitle(title)
|
||||
.setColor('Random')
|
||||
.setAuthor({ name: ctx.user.username, iconURL: ctx.user.displayAvatarURL() })
|
||||
.setDescription(`${firstParagraph.slice(0, 500)}...`)
|
||||
.setFooter({
|
||||
text: `Resultado de Wikipedia en ${lang === 'es' ? 'español' : 'inglés'}`,
|
||||
iconURL: 'https://i.imgur.com/pcpfc8i.png',
|
||||
});
|
||||
const button = new ActionRowBuilder<ButtonBuilder>().addComponents(
|
||||
new ButtonBuilder()
|
||||
.setLabel('URL al artículo')
|
||||
.setURL(canonicalArticleURL)
|
||||
.setStyle(ButtonStyle.Link)
|
||||
);
|
||||
|
||||
await ctx.reply({ embeds: [embed], components: [button] });
|
||||
}
|
||||
|
||||
export interface SearchWikipediaObject {
|
||||
ns: number;
|
||||
title: string;
|
||||
pageid: number;
|
||||
size: number;
|
||||
wordcount: number;
|
||||
snippet: string;
|
||||
timestamp: string;
|
||||
}
|
||||
Reference in New Issue
Block a user