From 974b356d980e6d2a6ba05e8c7947b5b3907581b2 Mon Sep 17 00:00:00 2001 From: Xargana Date: Sat, 29 Mar 2025 22:10:59 +0300 Subject: [PATCH] hopefully fixed the global commands not appearing in dms :pray: :pray: :pray: --- discord/index.js | 306 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 269 insertions(+), 37 deletions(-) diff --git a/discord/index.js b/discord/index.js index 6b1582f..a764964 100644 --- a/discord/index.js +++ b/discord/index.js @@ -11,8 +11,19 @@ const axios = require("axios"); const ping = require("ping"); const client = new Client({ - intents: [GatewayIntentBits.Guilds, GatewayIntentBits.DirectMessages], -}); + intents: [ + GatewayIntentBits.Guilds, + GatewayIntentBits.DirectMessages, + GatewayIntentBits.MessageContent, + GatewayIntentBits.GuildMessages, + GatewayIntentBits.DirectMessageReactions, + GatewayIntentBits.DirectMessageTyping, + GatewayIntentBits.GuildMessageReactions, + GatewayIntentBits.GuildMessageTyping + ], + partials: ['CHANNEL', 'MESSAGE'] // This is important for DM functionality + }); + // Original slash commands const slashCommands = [ @@ -20,6 +31,8 @@ const slashCommands = [ name: "fetch_data", description: "Fetches data from an API", type: ApplicationCommandType.ChatInput, + dm_permission: true, + options: [ { name: "url", @@ -33,6 +46,7 @@ const slashCommands = [ name: "ping", description: "Pings a remote server.", type: ApplicationCommandType.ChatInput, + dm_permission: true, options: [ { name: "ip", @@ -46,6 +60,7 @@ const slashCommands = [ name: "server_status", description: "Fetches data from an API", type: ApplicationCommandType.ChatInput, + dm_permission: true, options: [ { name: "raw", @@ -60,6 +75,7 @@ const slashCommands = [ name: "cody", description: "Ask Cody (Sourcegraph AI) a coding question", type: ApplicationCommandType.ChatInput, + dm_permission: true, options: [ { name: "question", @@ -74,6 +90,7 @@ const slashCommands = [ name: "weather", description: "Get current weather for a location", type: ApplicationCommandType.ChatInput, + dm_permission: true, options: [ { name: "location", @@ -83,6 +100,75 @@ const slashCommands = [ }, ], }, + // Add Minecraft server status command + { + name: "mcstatus", + description: "Check the status of a Minecraft server", + type: ApplicationCommandType.ChatInput, + dm_permission: true, + options: [ + { + name: "server", + description: "Server address (e.g., mc.hypixel.net)", + type: ApplicationCommandOptionType.String, + required: true, + }, + { + name: "bedrock", + description: "Is this a Bedrock server? (Default: false)", + type: ApplicationCommandOptionType.Boolean, + required: false, + }, + ], + }, + { + name: "animal", + description: "Get a random animal image", + type: ApplicationCommandType.ChatInput, + dm_permission: true, + options: [ + { + name: "type", + description: "Type of animal", + type: ApplicationCommandOptionType.String, + required: true, + choices: [ + { name: "Dog", value: "dog" }, + { name: "Cat", value: "cat" }, + { name: "Panda", value: "panda" }, + { name: "Fox", value: "fox" }, + { name: "Bird", value: "bird" }, + { name: "Koala", value: "koala" }, + { name: "Red Panda", value: "red_panda" }, + { name: "Raccoon", value: "raccoon" }, + { name: "Kangaroo", value: "kangaroo" } + ] + }, + ], + }, + // Add Anime commands + { + name: "anime", + description: "Get anime-related content", + type: ApplicationCommandType.ChatInput, + dm_permission: true, + options: [ + { + name: "type", + description: "Type of anime content", + type: ApplicationCommandOptionType.String, + required: true, + choices: [ + { name: "Wink", value: "wink" }, + { name: "Pat", value: "pat" }, + { name: "Hug", value: "hug" }, + { name: "Face Palm", value: "face-palm" }, + { name: "Quote", value: "quote" } + ] + }, + ], + }, + ]; // User context menu commands @@ -128,6 +214,10 @@ async function askCody(question) { throw new Error("SOURCEGRAPH_API_KEY is not set in environment variables"); } + if (question.length > 1999) { + throw new Error("Input question is too long - must be 1999 characters or less"); + } + try { const response = await axios({ method: 'post', @@ -185,13 +275,26 @@ async function askCody(question) { } throw error; } -} - -client.once("ready", async () => { +}client.once("ready", async () => { console.log(`Logged in as ${client.user.tag}`); await updateCommands(); + + const ownerId = process.env.OWNER_ID; + const owner = await client.users.fetch(ownerId); + await owner.send(`Bot started successfully at ${new Date().toLocaleString()}`); }); +client.on("interactionCreate", async (interaction) => { + // Safe channel type logging + console.log(`Interaction received in: ${interaction.channel?.type || 'Unknown channel type'}`); + console.log(`Command name: ${interaction.commandName}`); + console.log(`Channel: ${interaction.channel?.id || 'No channel'}`); + + // Rest of your existing handler code... + }); + + + client.on("interactionCreate", async (interaction) => { // Handle slash commands if (interaction.isChatInputCommand()) { @@ -282,9 +385,12 @@ client.on("interactionCreate", async (interaction) => { // Format the response let formattedResponse = codyResponse || "No response received from Cody."; + // Calculate total length including the question + const fullResponse = `**Question:** ${question}\n\n**Cody's Answer:**\n${formattedResponse}`; + // If the response is too long for Discord (which has a 2000 character limit) - if (formattedResponse.length > 1900) { - formattedResponse = formattedResponse.substring(0, 1900) + "...\n(Response truncated due to Discord's character limit)"; + if (fullResponse.length > 1900) { + formattedResponse = formattedResponse.substring(0, 1900 - question.length - 50) + "...\n(Response truncated due to Discord's character limit)"; } await interaction.editReply({ @@ -296,8 +402,7 @@ client.on("interactionCreate", async (interaction) => { content: "Sorry, I couldn't get an answer from Cody. Please try again later.", ephemeral: true }); - } - break; + } break; case "weather": try { @@ -313,14 +418,13 @@ client.on("interactionCreate", async (interaction) => { return; } - const weatherUrl = `https://api.weatherapi.com/v1/forecast.json?key=${process.env.WEATHER_API_KEY}&q=${encodeURIComponent(location)}&aqi=no&days=1`; + const weatherUrl = `https://api.weatherapi.com/v1/current.json?key=${process.env.WEATHER_API_KEY}&q=${encodeURIComponent(location)}&aqi=no`; const response = await axios.get(weatherUrl); const data = response.data; const location_name = data.location.name; const region = data.location.region; const country = data.location.country; - const localTime = data.location.localtime; const temp_c = data.current.temp_c; const temp_f = data.current.temp_f; const condition = data.current.condition.text; @@ -329,21 +433,15 @@ client.on("interactionCreate", async (interaction) => { const wind_mph = data.current.wind_mph; const feelslike_c = data.current.feelslike_c; const feelslike_f = data.current.feelslike_f; - const maxtemp_c = data.forecast.forecastday[0].day.maxtemp_c; - const maxtemp_f = data.forecast.forecastday[0].day.maxtemp_f; - const mintemp_c = data.forecast.forecastday[0].day.mintemp_c; - const mintemp_f = data.forecast.forecastday[0].day.mintemp_f; const weatherEmbed = { title: `Weather for ${location_name}, ${region}, ${country}`, - description: `Current condition: ${condition}\nLocal time: ${localTime}`, + description: `Current condition: ${condition}`, fields: [ { name: 'Temperature', value: `${temp_c}°C / ${temp_f}°F`, inline: true }, { name: 'Feels Like', value: `${feelslike_c}°C / ${feelslike_f}°F`, inline: true }, { name: 'Humidity', value: `${humidity}%`, inline: true }, - { name: 'Wind Speed', value: `${wind_kph} km/h / ${wind_mph} mph`, inline: true }, - { name: 'Max Temperature', value: `${maxtemp_c}°C / ${maxtemp_f}°F`, inline: true }, - { name: 'Min Temperature', value: `${mintemp_c}°C / ${mintemp_f}°F`, inline: true } + { name: 'Wind Speed', value: `${wind_kph} km/h / ${wind_mph} mph`, inline: true } ], thumbnail: { url: `https:${data.current.condition.icon}` }, timestamp: new Date(), @@ -352,32 +450,166 @@ client.on("interactionCreate", async (interaction) => { await interaction.editReply({ embeds: [weatherEmbed] }); } catch (error) { + console.error(error); await interaction.editReply({ content: "Failed to fetch weather data. Please check the location name and try again.", ephemeral: true }); } break; + +// In the mcstatus command handler, replace the thumbnail section with this: +case "mcstatus": + try { + await interaction.deferReply(); + const serverAddress = interaction.options.getString("server"); + const isBedrock = interaction.options.getBoolean("bedrock") ?? false; + + // Determine which API endpoint to use based on server type + const apiUrl = isBedrock + ? `https://api.mcsrvstat.us/bedrock/2/${encodeURIComponent(serverAddress)}` + : `https://api.mcsrvstat.us/2/${encodeURIComponent(serverAddress)}`; + + const response = await axios.get(apiUrl); + const data = response.data; + + if (!data.online) { + await interaction.editReply({ + content: `📡 **${serverAddress}** is currently offline or could not be reached.` + }); + return; } - } - - // Handle user context menu commands - else if (interaction.isUserContextMenuCommand()) { - switch (interaction.commandName) { - case "User Info": - try { - const user = interaction.targetUser; - await interaction.reply({ - content: `User info:\n• Tag: ${user.tag}\n• ID: ${user.id}`, - ephemeral: true, - }); - } catch (error) { - console.error(error); - await interaction.reply({ content: "Failed to retrieve user info.", ephemeral: true }); - } - break; + + // Create a rich embed for the server status + const serverEmbed = { + title: `Minecraft Server Status: ${serverAddress}`, + color: 0x44FF44, // Green color + // Use a default Minecraft image instead of trying to use the base64 icon + thumbnail: { + url: 'https://www.minecraft.net/content/dam/games/minecraft/key-art/MC_The-Wild-Update_540x300.jpg' + }, + fields: [ + { name: 'Status', value: data.online ? '✅ Online' : '❌ Offline', inline: true }, + { name: 'Players', value: data.players ? `${data.players.online}/${data.players.max}` : 'Unknown', inline: true }, + { name: 'Version', value: data.version || 'Unknown', inline: true } + ], + footer: { text: 'Powered by mcsrvstat.us' }, + timestamp: new Date() + }; + + // Add MOTD if available + if (data.motd && data.motd.clean && data.motd.clean.length > 0) { + serverEmbed.description = `**MOTD:**\n${data.motd.clean.join('\n')}`; } + + // Add player list if available and not empty + if (data.players && data.players.list && data.players.list.length > 0) { + // Limit to first 20 players to avoid hitting Discord's limits + const playerList = data.players.list.slice(0, 20).join(', '); + const hasMore = data.players.list.length > 20; + + serverEmbed.fields.push({ + name: 'Online Players', + value: playerList + (hasMore ? '...' : '') + }); + } + + await interaction.editReply({ embeds: [serverEmbed] }); + } catch (error) { + console.error(error); + await interaction.editReply({ + content: "Failed to fetch Minecraft server status. Please check the server address and try again.", + ephemeral: true + }); } -}); + break; + case "animal": + try { + await interaction.deferReply(); + const animalType = interaction.options.getString("type"); + + // Get animal image + const imageResponse = await axios.get(`https://some-random-api.com/animal/${animalType}`); + const imageUrl = imageResponse.data.image; + + const animalEmbed = { + color: 0x3498db, + image: { url: imageUrl }, + footer: { text: 'Powered by some-random-api.com' }, + timestamp: new Date() + }; + + await interaction.editReply({ embeds: [animalEmbed] }); + } catch (error) { + console.error(error); + await interaction.editReply({ + content: "Failed to fetch animal image. The API might be down or the animal type is not available.", + ephemeral: true + }); + } + break; + +case "anime": + try { + await interaction.deferReply(); + const action = interaction.options.getString("action"); + + let apiUrl; + let isQuote = false; + + if (action === "quote") { + apiUrl = "https://some-random-api.ml/animu/quote"; + isQuote = true; + } else { + apiUrl = `https://some-random-api.ml/animu/${action}`; + } + + const response = await axios.get(apiUrl); + + if (isQuote) { + // Handle quote response + const quote = response.data.sentence; + const character = response.data.character; + const anime = response.data.anime; + + const quoteEmbed = { + title: "Anime Quote", + description: `"${quote}"`, + fields: [ + { name: "Character", value: character, inline: true }, + { name: "Anime", value: anime, inline: true } + ], + color: 0xe74c3c, + footer: { text: 'Powered by some-random-api.ml' }, + timestamp: new Date() + }; + + await interaction.editReply({ embeds: [quoteEmbed] }); + } else { + // Handle GIF response + const gifUrl = response.data.link; + + const actionTitle = action.charAt(0).toUpperCase() + action.slice(1).replace('-', ' '); + + const gifEmbed = { + title: `Anime ${actionTitle}`, + color: 0xe74c3c, + image: { url: gifUrl }, + footer: { text: 'Powered by some-random-api.ml' }, + timestamp: new Date() + }; + + await interaction.editReply({ embeds: [gifEmbed] }); + } + } catch (error) { + console.error(error); + await interaction.editReply({ + content: "Failed to fetch anime content. The API might be down or the requested action is not available.", + ephemeral: true + }); + } + break; + } +}}); client.login(process.env.DISCORD_TOKEN); \ No newline at end of file