From ddfc82e1c54c36d01a76f276afe2571b41d0864e Mon Sep 17 00:00:00 2001 From: sarah Date: Sun, 2 Aug 2026 23:08:29 +0200 Subject: [PATCH] feat: add Cat module with /cat and /catgif commands using TheCatAPI --- .env.example | 3 +++ AGENTS-BOT.md | 4 +++- README.md | 2 ++ src/commands/utility/cat.ts | 40 ++++++++++++++++++++++++++++++++++ src/commands/utility/catgif.ts | 31 ++++++++++++++++++++++++++ src/structures/CatManager.ts | 26 ++++++++++++++++++++++ 6 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 src/commands/utility/cat.ts create mode 100644 src/commands/utility/catgif.ts create mode 100644 src/structures/CatManager.ts diff --git a/.env.example b/.env.example index b4a2e69..9cb401d 100644 --- a/.env.example +++ b/.env.example @@ -6,5 +6,8 @@ TWITCH_USERNAME=dein_twitch_username TWITCH_OAUTH_TOKEN=oauth:xxxxxxxxxxxxxxxxxx BOT_OWNER_ID=deine_discord_user_id +# Optional: TheCatAPI Key für höheres Ratenlimit (default: leer / kostenlos) +THECATAPI_KEY= + # Optional: Automatische Slash-Command Registrierung bei Bot-Start (default: true) AUTO_DEPLOY=true \ No newline at end of file diff --git a/AGENTS-BOT.md b/AGENTS-BOT.md index 5097e63..c58c08d 100644 --- a/AGENTS-BOT.md +++ b/AGENTS-BOT.md @@ -19,7 +19,8 @@ The bot uses a **Modular Command & Event Loading** pattern with **ESM (ECMAScrip 10. **Welcome/Goodbye System:** Guild member add/remove events with customizable messages. 11. **Logging System:** Configurable event logging (messages, roles, moderation, etc.) via centralized `EventLogger`. 12. **Role Selection System:** Self-service role assignment via select menus with support for exclusive categories and max-role limits. -13. **Centralized Event Logger:** Kapsel event logs into `EventLogger.sendLog()` to eliminate duplicated logging logic across event files. +13. **Cat & CatGIF System:** TheCatAPI integration for random cat images and GIFs (`CatManager`). +14. **Centralized Event Logger:** Kapsel event logs into `EventLogger.sendLog()` to eliminate duplicated logging logic across event files. ## 📁 Project Structure @@ -37,6 +38,7 @@ pixelpoebel/ │ ├── Command.ts │ ├── Database.ts # Database with Transaction, Indexes & Surgical Caching │ ├── EventLogger.ts # Centralized event logging helper +│ ├── CatManager.ts # TheCatAPI HTTP request helper │ ├── TwitchManager.ts # Batch Polling & Transaction logic │ ├── TwitchMonitor.ts # IRC Monitoring & Webhook reuse │ ├── TwitchCache.ts # IRC Message FIFO Cache diff --git a/README.md b/README.md index 8ec7d7d..dc99735 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,8 @@ npm run deploy ### Öffentlich - `/ping` – Latenz test - `/help` – Alle Befehle +- `/cat [typ]` – Zufälliges Katzenbild oder Katzen-GIF (Auswahl: Bild / GIF) +- `/catgif` – Zufälliges Katzen-GIF direkt posten - `/twitch online ` – Twitch-Status - `/twitch list` – Überwachte Kanäle - `/twitch help` – Twitch-Hilfe diff --git a/src/commands/utility/cat.ts b/src/commands/utility/cat.ts new file mode 100644 index 0000000..7d3ce20 --- /dev/null +++ b/src/commands/utility/cat.ts @@ -0,0 +1,40 @@ +import { SlashCommandBuilder, EmbedBuilder } from 'discord.js'; +import { Command } from '../../structures/Command.js'; +import { CatManager } from '../../structures/CatManager.js'; + +const command: Command = { + data: new SlashCommandBuilder() + .setName('cat') + .setDescription('Postet ein zufälliges Katzenbild oder Katzen-GIF.') + .addStringOption(option => + option.setName('typ') + .setDescription('Wähle zwischen Bild oder GIF') + .setRequired(false) + .addChoices( + { name: 'Bild', value: 'image' }, + { name: 'GIF', value: 'gif' } + )) + .setDMPermission(true), + category: 'Public', + async execute(interaction: any) { + const type = (interaction.options.getString('typ') as 'image' | 'gif') || 'image'; + await interaction.deferReply(); + + const imageUrl = await CatManager.fetchCatImage(type); + if (!imageUrl) { + await interaction.editReply('❌ Konnte kein Katzenbild laden. Bitte versuche es später erneut.'); + return; + } + + const embed = new EmbedBuilder() + .setTitle(type === 'gif' ? '🐱 Zufälliges Katzen-GIF' : '🐱 Zufälliges Katzenbild') + .setURL(imageUrl) + .setImage(imageUrl) + .setColor(0xf39c12) + .setTimestamp(); + + await interaction.editReply({ embeds: [embed] }); + }, +}; + +export default command; diff --git a/src/commands/utility/catgif.ts b/src/commands/utility/catgif.ts new file mode 100644 index 0000000..b87cc2f --- /dev/null +++ b/src/commands/utility/catgif.ts @@ -0,0 +1,31 @@ +import { SlashCommandBuilder, EmbedBuilder } from 'discord.js'; +import { Command } from '../../structures/Command.js'; +import { CatManager } from '../../structures/CatManager.js'; + +const command: Command = { + data: new SlashCommandBuilder() + .setName('catgif') + .setDescription('Postet direkt ein zufälliges Katzen-GIF.') + .setDMPermission(true), + category: 'Public', + async execute(interaction: any) { + await interaction.deferReply(); + + const imageUrl = await CatManager.fetchCatImage('gif'); + if (!imageUrl) { + await interaction.editReply('❌ Konnte kein Katzen-GIF laden. Bitte versuche es später erneut.'); + return; + } + + const embed = new EmbedBuilder() + .setTitle('🐱 Zufälliges Katzen-GIF') + .setURL(imageUrl) + .setImage(imageUrl) + .setColor(0xf39c12) + .setTimestamp(); + + await interaction.editReply({ embeds: [embed] }); + }, +}; + +export default command; diff --git a/src/structures/CatManager.ts b/src/structures/CatManager.ts new file mode 100644 index 0000000..7705dac --- /dev/null +++ b/src/structures/CatManager.ts @@ -0,0 +1,26 @@ +export class CatManager { + static async fetchCatImage(type: 'image' | 'gif' = 'image'): Promise { + const apiKey = process.env.THECATAPI_KEY || ''; + const mimeTypes = type === 'gif' ? 'gif' : 'jpg,png'; + const url = `https://api.thecatapi.com/v1/images/search?mime_types=${mimeTypes}`; + + try { + const headers: Record = {}; + if (apiKey) { + headers['x-api-key'] = apiKey; + } + + const response = await fetch(url, { headers }); + if (!response.ok) return null; + + const data: any = await response.json(); + if (Array.isArray(data) && data.length > 0 && data[0].url) { + return data[0].url; + } + return null; + } catch (error) { + console.error('[CAT] Error fetching cat image:', error); + return null; + } + } +}