diff --git a/api/exchange-rate/exchange-rate.js b/api/exchange-rate/exchange-rate.js new file mode 100644 index 0000000..cde9aa9 --- /dev/null +++ b/api/exchange-rate/exchange-rate.js @@ -0,0 +1,351 @@ +const express = require('express'); +const axios = require('axios'); +const fs = require('fs'); +const path = require('path'); +const router = express.Router(); + +// Get API key from environment variables +const API_KEY = process.env.EXCHANGE_RATE_API_KEY; +if (!API_KEY) { + console.error('WARNING: EXCHANGE_RATE_API_KEY environment variable is not set!'); + console.error('Exchange rate functionality will not work correctly.'); +} + +const BASE_URL = 'https://v6.exchangerate-api.com/v6'; + +// We'll use USD as our single base currency +const BASE_CURRENCY = 'USD'; + +// Path to the cache file +const CACHE_FILE_PATH = path.join(__dirname, 'exchange-rates-cache.json'); + +// In-memory storage for cached exchange rates +let exchangeRatesCache = { + USD: { + lastUpdated: null, + rates: {}, + nextUpdateTime: null + } +}; + +// one day in milliseconds +const UPDATE_INTERVAL = 1 * 24 * 60 * 60 * 1000; + +// Load cached exchange rates from file +function loadCachedRates() { + try { + if (fs.existsSync(CACHE_FILE_PATH)) { + const data = fs.readFileSync(CACHE_FILE_PATH, 'utf8'); + const parsedData = JSON.parse(data); + + // Convert string dates back to Date objects + if (parsedData.USD) { + if (parsedData.USD.lastUpdated) { + parsedData.USD.lastUpdated = new Date(parsedData.USD.lastUpdated); + } + if (parsedData.USD.nextUpdateTime) { + parsedData.USD.nextUpdateTime = new Date(parsedData.USD.nextUpdateTime); + } + } + + exchangeRatesCache = parsedData; + console.log('Loaded exchange rates from cache file'); + } else { + console.log('No cache file found, will create one when rates are fetched'); + } + } catch (error) { + console.error('Error loading cached exchange rates:', error.message); + // Continue with default cache if file can't be loaded + } +} + +// Save exchange rates to cache file +function saveCachedRates() { + try { + fs.writeFileSync(CACHE_FILE_PATH, JSON.stringify(exchangeRatesCache, null, 2)); + console.log('Exchange rates saved to cache file'); + } catch (error) { + console.error('Error saving exchange rates to cache file:', error.message); + } +} + +// Function to fetch and update exchange rates using USD as base +async function updateExchangeRates() { + if (!API_KEY) { + console.error('Cannot update exchange rates: API key is not set'); + return false; + } + + try { + console.log(`Fetching latest exchange rates using ${BASE_CURRENCY} as base...`); + const response = await axios.get(`${BASE_URL}/${API_KEY}/latest/${BASE_CURRENCY}`); + + if (response.data && response.data.result === 'success') { + exchangeRatesCache.USD = { + lastUpdated: new Date(), + rates: response.data.conversion_rates, + nextUpdateTime: new Date(Date.now() + UPDATE_INTERVAL) + }; + + // Save to file after updating + saveCachedRates(); + + console.log('Exchange rates updated successfully'); + return true; + } + return false; + } catch (error) { + console.error('Failed to update exchange rates:', error.message); + return false; + } +} + +// Check if rates need updating and update if necessary +async function ensureRatesUpdated() { + if (!exchangeRatesCache.USD.lastUpdated || + Date.now() > exchangeRatesCache.USD.nextUpdateTime.getTime()) { + return await updateExchangeRates(); + } + console.log(`Using cached rates, next update: ${exchangeRatesCache.USD.nextUpdateTime}`); + return true; +} + +// Calculate conversion rate between any two currencies using USD as base +function calculateRate(from, to) { + const rates = exchangeRatesCache.USD.rates; + + // If either currency is USD, we can use the rate directly + if (from === 'USD') return rates[to]; + if (to === 'USD') return 1 / rates[from]; + + // Otherwise, calculate cross rate: from -> USD -> to + return rates[to] / rates[from]; +} + +// Load cached rates when the module is loaded +loadCachedRates(); + +// Initialize rates if needed +ensureRatesUpdated(); + +// Root endpoint +router.get('/', (req, res) => { + const availableCurrencies = exchangeRatesCache.USD.rates ? + Object.keys(exchangeRatesCache.USD.rates) : []; + + res.json({ + message: 'Exchange Rate API is running', + baseCurrency: BASE_CURRENCY, + availableCurrencies, + lastUpdated: exchangeRatesCache.USD.lastUpdated, + nextUpdate: exchangeRatesCache.USD.nextUpdateTime, + updateInterval: '3 days', + endpoints: { + latest: '/latest', + convert: '/convert/:from/:to/:amount', + currencies: '/currencies' + } + }); +}); + +// Get all cached exchange rates +router.get('/latest', async (req, res) => { + await ensureRatesUpdated(); + + if (!exchangeRatesCache.USD.rates) { + return res.status(503).json({ error: 'Exchange rate data not yet available' }); + } + + res.json({ + result: 'success', + base: BASE_CURRENCY, + lastUpdated: exchangeRatesCache.USD.lastUpdated, + nextUpdate: exchangeRatesCache.USD.nextUpdateTime, + rates: exchangeRatesCache.USD.rates + }); +}); + +// Get rates for a specific currency as base +router.get('/latest/:currency', async (req, res) => { + const { currency } = req.params; + const currencyCode = currency.toUpperCase(); + + await ensureRatesUpdated(); + + if (!exchangeRatesCache.USD.rates) { + return res.status(503).json({ error: 'Exchange rate data not yet available' }); + } + + // Check if the currency is supported + if (!exchangeRatesCache.USD.rates[currencyCode] && currencyCode !== 'USD') { + return res.status(400).json({ error: `Currency '${currencyCode}' not supported` }); + } + + // Calculate rates with the requested currency as base + const rates = {}; + const usdRates = exchangeRatesCache.USD.rates; + + // If the requested base is USD, return rates directly + if (currencyCode === 'USD') { + res.json({ + result: 'success', + base: currencyCode, + lastUpdated: exchangeRatesCache.USD.lastUpdated, + nextUpdate: exchangeRatesCache.USD.nextUpdateTime, + rates: usdRates + }); + return; + } + + // Otherwise, calculate rates for all currencies with the requested currency as base + const baseRate = usdRates[currencyCode]; // Rate of 1 USD in the requested currency + + // Add USD rate + rates['USD'] = 1 / baseRate; + + // Add rates for all other currencies + for (const toCurrency in usdRates) { + if (toCurrency !== currencyCode) { + // Convert through USD: from -> USD -> to + rates[toCurrency] = usdRates[toCurrency] / baseRate; + } + } + + // Add rate for the base currency itself + rates[currencyCode] = 1; + + res.json({ + result: 'success', + base: currencyCode, + lastUpdated: exchangeRatesCache.USD.lastUpdated, + nextUpdate: exchangeRatesCache.USD.nextUpdateTime, + rates: rates + }); +}); + +// Get list of available currencies +router.get('/currencies', async (req, res) => { + await ensureRatesUpdated(); + + const availableCurrencies = exchangeRatesCache.USD.rates ? + Object.keys(exchangeRatesCache.USD.rates) : []; + + res.json({ + result: 'success', + baseCurrency: BASE_CURRENCY, + availableCurrencies, + lastUpdated: exchangeRatesCache.USD.lastUpdated, + nextUpdate: exchangeRatesCache.USD.nextUpdateTime + }); +}); + +// Convert between currencies using cached rates +router.get('/convert/:from/:to/:amount', async (req, res) => { + const { from, to, amount } = req.params; + const fromCurrency = from.toUpperCase(); + const toCurrency = to.toUpperCase(); + + await ensureRatesUpdated(); + + if (!exchangeRatesCache.USD.rates) { + return res.status(503).json({ error: 'Exchange rate data not yet available' }); + } + + // Check if currencies are supported + if (fromCurrency !== 'USD' && !exchangeRatesCache.USD.rates[fromCurrency]) { + return res.status(400).json({ error: `Currency '${fromCurrency}' not supported` }); + } + + if (toCurrency !== 'USD' && !exchangeRatesCache.USD.rates[toCurrency]) { + return res.status(400).json({ error: `Currency '${toCurrency}' not supported` }); + } + + try { + const numericAmount = parseFloat(amount); + + if (isNaN(numericAmount)) { + return res.status(400).json({ error: 'Invalid amount' }); + } + + // Calculate conversion rate + const rate = calculateRate(fromCurrency, toCurrency); + const convertedAmount = numericAmount * rate; + + res.json({ + result: 'success', + from: fromCurrency, + to: toCurrency, + amount: numericAmount, + rate, + convertedAmount: parseFloat(convertedAmount.toFixed(4)), + lastUpdated: exchangeRatesCache.USD.lastUpdated, + nextUpdate: exchangeRatesCache.USD.nextUpdateTime + }); + } catch (error) { + console.error('Conversion error:', error); + res.status(500).json({ error: 'Failed to convert currency' }); + } +}); + +// Direct pair conversion (fallback to API if needed) +router.get('/pair/:from/:to/:amount', async (req, res) => { + const { from, to, amount } = req.params; + const fromCurrency = from.toUpperCase(); + const toCurrency = to.toUpperCase(); + + // First try to use our cached rates + await ensureRatesUpdated(); + + if (exchangeRatesCache.USD.rates && + (fromCurrency === 'USD' || exchangeRatesCache.USD.rates[fromCurrency]) && + (toCurrency === 'USD' || exchangeRatesCache.USD.rates[toCurrency])) { + + try { + const numericAmount = parseFloat(amount); + + if (isNaN(numericAmount)) { + return res.status(400).json({ error: 'Invalid amount' }); + } + + // Calculate conversion rate + const rate = calculateRate(fromCurrency, toCurrency); + const convertedAmount = numericAmount * rate; + + res.json({ + result: 'success', + from: fromCurrency, + to: toCurrency, + amount: numericAmount, + rate, + convertedAmount: parseFloat(convertedAmount.toFixed(4)), + lastUpdated: exchangeRatesCache.USD.lastUpdated, + source: 'cache' + }); + return; + } catch (error) { + console.error('Error using cached rates:', error); + // Fall through to API call + } + } + + // If we can't use cached rates, call the API directly + if (!API_KEY) { + return res.status(503).json({ error: 'Exchange rate API key is not configured' }); + } + + try { + const response = await axios.get(`${BASE_URL}/${API_KEY}/pair/${fromCurrency}/${toCurrency}/${amount}`); + + // Update our cache with the latest USD rates if it's time + ensureRatesUpdated(); + + res.json({ + ...response.data, + source: 'api' + }); + } catch (error) { + res.status(500).json({ error: 'Failed to convert currency' }); + } +}); + +module.exports = router; diff --git a/api/exchange-rate/exchange-rates-cache.json b/api/exchange-rate/exchange-rates-cache.json new file mode 100644 index 0000000..40a5a8b --- /dev/null +++ b/api/exchange-rate/exchange-rates-cache.json @@ -0,0 +1,171 @@ +{ + "USD": { + "lastUpdated": "2025-04-07T18:49:41.384Z", + "rates": { + "USD": 1, + "AED": 3.6725, + "AFN": 71.3562, + "ALL": 90.1377, + "AMD": 391.1786, + "ANG": 1.79, + "AOA": 918.5748, + "ARS": 1075.88, + "AUD": 1.662, + "AWG": 1.79, + "AZN": 1.6999, + "BAM": 1.7805, + "BBD": 2, + "BDT": 121.3674, + "BGN": 1.7804, + "BHD": 0.376, + "BIF": 2960.3407, + "BMD": 1, + "BND": 1.3404, + "BOB": 6.8885, + "BRL": 5.7194, + "BSD": 1, + "BTN": 85.5708, + "BWP": 13.8733, + "BYN": 3.1354, + "BZD": 2, + "CAD": 1.423, + "CDF": 2899.0345, + "CHF": 0.8524, + "CLP": 960.6602, + "CNY": 7.2857, + "COP": 4203.747, + "CRC": 502.4294, + "CUP": 24, + "CVE": 100.3829, + "CZK": 22.9524, + "DJF": 177.721, + "DKK": 6.7905, + "DOP": 62.8304, + "DZD": 132.995, + "EGP": 50.8068, + "ERN": 15, + "ETB": 131.8684, + "EUR": 0.9102, + "FJD": 2.3154, + "FKP": 0.7757, + "FOK": 6.7935, + "GBP": 0.7751, + "GEL": 2.7559, + "GGP": 0.7757, + "GHS": 15.5067, + "GIP": 0.7757, + "GMD": 72.6441, + "GNF": 8570.968, + "GTQ": 7.6902, + "GYD": 209.8017, + "HKD": 7.7734, + "HNL": 25.5147, + "HRK": 6.8592, + "HTG": 130.7844, + "HUF": 369.6088, + "IDR": 16757.5574, + "ILS": 3.7448, + "IMP": 0.7757, + "INR": 85.5703, + "IQD": 1309.7144, + "IRR": 42008.9149, + "ISK": 131.125, + "JEP": 0.7757, + "JMD": 157.719, + "JOD": 0.709, + "JPY": 145.2473, + "KES": 129.2338, + "KGS": 86.8492, + "KHR": 3997.3556, + "KID": 1.6621, + "KMF": 447.8769, + "KRW": 1457.9608, + "KWD": 0.307, + "KYD": 0.8333, + "KZT": 510.3326, + "LAK": 21751.6772, + "LBP": 89500, + "LKR": 295.3817, + "LRD": 199.3433, + "LSL": 19.1915, + "LYD": 4.8358, + "MAD": 9.5166, + "MDL": 17.6443, + "MGA": 4654.3433, + "MKD": 55.9208, + "MMK": 2090.2388, + "MNT": 3479.6583, + "MOP": 8.0068, + "MRU": 39.8843, + "MUR": 44.5309, + "MVR": 15.4592, + "MWK": 1740.2553, + "MXN": 20.5515, + "MYR": 4.437, + "MZN": 63.6781, + "NAD": 19.1915, + "NGN": 1529.0612, + "NIO": 36.6793, + "NOK": 10.7699, + "NPR": 136.9132, + "NZD": 1.7956, + "OMR": 0.3845, + "PAB": 1, + "PEN": 3.6809, + "PGK": 4.0959, + "PHP": 57.3644, + "PKR": 280.7358, + "PLN": 3.8787, + "PYG": 8001.2022, + "QAR": 3.64, + "RON": 4.5185, + "RSD": 106.3911, + "RUB": 84.4536, + "RWF": 1422.8596, + "SAR": 3.75, + "SBD": 8.3385, + "SCR": 14.8196, + "SDG": 458.3047, + "SEK": 10.0072, + "SGD": 1.3405, + "SHP": 0.7757, + "SLE": 22.7181, + "SLL": 22718.051, + "SOS": 571.0444, + "SRD": 36.8241, + "SSP": 4519.748, + "STN": 22.3043, + "SYP": 12873.9497, + "SZL": 19.1915, + "THB": 34.3823, + "TJS": 10.9221, + "TMT": 3.4983, + "TND": 3.0571, + "TOP": 2.3833, + "TRY": 38.0295, + "TTD": 6.7342, + "TVD": 1.6621, + "TWD": 33.1309, + "TZS": 2647.2453, + "UAH": 41.1747, + "UGX": 3662.2001, + "UYU": 42.042, + "UZS": 12937.493, + "VES": 72.1856, + "VND": 25642.7185, + "VUV": 121.865, + "WST": 2.8015, + "XAF": 597.1692, + "XCD": 2.7, + "XCG": 1.79, + "XDR": 0.751, + "XOF": 597.1692, + "XPF": 108.6373, + "YER": 244.8828, + "ZAR": 19.2142, + "ZMW": 27.9801, + "ZWL": 6.7864 + }, + "nextUpdateTime": "2025-04-10T18:49:41.384Z" + } +} \ No newline at end of file diff --git a/api/server.js b/api/server.js index d21a51a..4c2c45e 100644 --- a/api/server.js +++ b/api/server.js @@ -3,18 +3,24 @@ const cors = require("cors"); const fs = require("fs"); const https = require("https"); const http = require("http"); -const status = require("./status/server") +const path = require("path"); +// load environment variables from .env file +require('dotenv').config({ path: path.join(__dirname, '../.env') }); + +const status = require("./status/status"); +const exchangeRate = require("./exchange-rate/exchange-rate"); const app = express(); -const PORT = 2589; +const PORT = process.env.PORT || 2589; -const key = "/etc/letsencrypt/live/blahaj.tr/privkey.pem" -const cert = "/etc/letsencrypt/live/blahaj.tr/fullchain.pem" +const key = process.env.SSL_KEY_PATH || "/etc/letsencrypt/live/blahaj.tr/privkey.pem"; +const cert = process.env.SSL_CERT_PATH || "/etc/letsencrypt/live/blahaj.tr/fullchain.pem"; app.use(cors()); app.use("/status", status); +app.use("/exchange-rate", exchangeRate); -// Try to load certificates +// try to load certificates try { const sslOptions = { key: fs.readFileSync(key), @@ -33,7 +39,7 @@ try { console.log("Starting server without SSL..."); - // Start http server as fallback + // start http server as fallback http.createServer(app).listen(PORT, () => { console.log(`API running at http://localhost:${PORT}`); }); diff --git a/api/status/server.js b/api/status/status.js similarity index 100% rename from api/status/server.js rename to api/status/status.js diff --git a/discord/index.js b/discord/index.js deleted file mode 100644 index 9e9dec6..0000000 --- a/discord/index.js +++ /dev/null @@ -1,1801 +0,0 @@ -require("dotenv").config(); -const { - Client, - GatewayIntentBits, - REST, - Routes, - ApplicationCommandOptionType, - ApplicationCommandType, - ChannelType -} = require("discord.js"); -const axios = require("axios"); -const ping = require("ping"); -const whois = require('whois-json'); -const fs = require('fs'); -const path = require('path'); - -const tempDir = path.join(__dirname, '../temp'); -if (fs.existsSync(tempDir)) { - console.log("Cleaning up temp directory..."); - const files = fs.readdirSync(tempDir); - for (const file of files) { - fs.unlinkSync(path.join(tempDir, file)); - } -} else { - fs.mkdirSync(tempDir, { recursive: true }); -} - -const client = new Client({ - 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 - }); - -// Commands that should work in DMs (registered globally) -const globalCommands = [ - { - name: "fetch_data", - description: "Fetches data from an API", - type: ApplicationCommandType.ChatInput, - dm_permission: true, - options: [ - { - name: "url", - description: "The URL to fetch data from", - type: ApplicationCommandOptionType.String, - required: true, - }, - ], - }, - { - name: "ping", - description: "Pings a remote server.", - type: ApplicationCommandType.ChatInput, - dm_permission: true, - options: [ - { - name: "ip", - description: "The IP Adress to ping.", - type: ApplicationCommandOptionType.String, - required: true, - }, - ], - }, - { - name: "server_status", - description: "Fetches data from an API", - type: ApplicationCommandType.ChatInput, - dm_permission: true, - options: [ - { - name: "raw", - description: "Display raw JSON data", - type: ApplicationCommandOptionType.Boolean, - required: false, - }, - ], - }, - { - name: "cody", - description: "Ask Cody (Sourcegraph AI) a coding question", - type: ApplicationCommandType.ChatInput, - dm_permission: true, - options: [ - { - name: "question", - description: "Your coding question", - type: ApplicationCommandOptionType.String, - required: true, - }, - ], - }, - { - name: "weather", - description: "Get current weather for a location", - type: ApplicationCommandType.ChatInput, - dm_permission: true, - options: [ - { - name: "location", - description: "City name or postal code", - type: ApplicationCommandOptionType.String, - required: true, - }, - ], - }, - { - 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" } - ] - }, - ], - }, - { - 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" } - ] - }, - ], - }, - { - name: "checkdns", - description: "Check if a domain is blocked by running it through a DNS server", - type: ApplicationCommandType.ChatInput, - dm_permission: true, - options: [ - { - name: "domain", - description: "Domain name to check (e.g. example.com)", - type: ApplicationCommandOptionType.String, - required: true, - }, - { - name: "provider", - description: "DNS provider to use", - type: ApplicationCommandOptionType.String, - required: false, - choices: [ - { name: "Cloudflare (1.1.1.1)", value: "1.1.1.1" }, - { name: "Google (8.8.8.8)", value: "8.8.8.8" }, - { name: "OpenDNS", value: "208.67.222.222" }, - { name: "Quad9", value: "9.9.9.9" }, - { name: "AdGuard", value: "94.140.14.14" }, - { name: "Turknet", value: "193.192.98.8" }, - { name: "TTnet", value: "195.175.39.49" }, - { name: "Turkcell", value: "195.175.39.49" }, - { name: "Superonline", value: "195.114.66.100" } - ] - } - ] - }, - { - name: "traceroute", - description: "Show network path to a destination", - type: ApplicationCommandType.ChatInput, - dm_permission: true, - options: [ - { - name: "target", - description: "IP address or domain to trace (might take a long time to complete)", - type: ApplicationCommandOptionType.String, - required: true, - }, - { - name: "hops", - description: "Maximum number of hops (default: 30)", - type: ApplicationCommandOptionType.Integer, - required: false, - min_value: 1, - max_value: 32 - } - ] - }, - { - name: "whois", - description: "Get domain registration information", - type: ApplicationCommandType.ChatInput, - dm_permission: true, - options: [ - { - name: "domain", - description: "Domain to lookup (e.g. example.com)", - type: ApplicationCommandOptionType.String, - required: true, - } - ] - }, - { - name: "stats", - description: "Show bot and server statistics", - type: ApplicationCommandType.ChatInput, - dm_permission: true - }, - { - name: "checkport", - description: "Check if specific ports are open on a domain", - type: ApplicationCommandType.ChatInput, - dm_permission: true, - options: [ - { - name: "target", - description: "Domain or IP to scan", - type: ApplicationCommandOptionType.String, - required: true, - }, - { - name: "ports", - description: "Ports to scan (comma separated, e.g. 80,443,3306)", - type: ApplicationCommandOptionType.String, - required: true, - } - ] - }, - { - name: "wikipedia", - description: "Get a summary of a Wikipedia article", - type: ApplicationCommandType.ChatInput, - dm_permission: true, - options: [ - { - name: "query", - description: "The topic to search for on Wikipedia", - type: ApplicationCommandOptionType.String, - required: true, - }, - { - name: "language", - description: "Wikipedia language (default: en)", - type: ApplicationCommandOptionType.String, - required: false, - choices: [ - { name: "English", value: "en" }, - { name: "Spanish", value: "es" }, - { name: "French", value: "fr" }, - { name: "German", value: "de" }, - { name: "Russian", value: "ru" }, - { name: "Japanese", value: "ja" }, - { name: "Chinese", value: "zh" }, - { name: "Turkish", value: "tr" } - ] - } - ], - }, - { - name: "urban", - description: "Look up a term on Urban Dictionary", - type: ApplicationCommandType.ChatInput, - dm_permission: true, - options: [ - { - name: "term", - description: "The slang term to look up", - type: ApplicationCommandOptionType.String, - required: true, - }, - { - name: "random", - description: "Get a random definition instead", - type: ApplicationCommandOptionType.Boolean, - required: false - } - ], - }, - { - name: "currency", - description: "Convert between currencies using real-time exchange rates", - type: ApplicationCommandType.ChatInput, - dm_permission: true, - options: [ - { - name: "amount", - description: "Amount to convert", - type: ApplicationCommandOptionType.Number, - required: true, - }, - { - name: "from", - description: "Source currency code (e.g., USD)", - type: ApplicationCommandOptionType.String, - required: true, - }, - { - name: "to", - description: "Target currency code (e.g., EUR)", - type: ApplicationCommandOptionType.String, - required: true, - } - ], - }, - { - name: "hash", - description: "Generate hash of text or file (up to 500MB)", - type: ApplicationCommandType.ChatInput, - dm_permission: true, - options: [ - { - name: "algorithm", - description: "Hash algorithm to use", - type: ApplicationCommandOptionType.String, - required: true, - choices: [ - { name: "MD5", value: "md5" }, - { name: "SHA-1", value: "sha1" }, - { name: "SHA-256", value: "sha256" }, - { name: "SHA-512", value: "sha512" }, - { name: "SHA3-256", value: "sha3-256" }, - { name: "SHA3-512", value: "sha3-512" } - ] - }, - { - name: "text", - description: "Text to hash (if not uploading a file)", - type: ApplicationCommandOptionType.String, - required: false, - }, - { - name: "file", - description: "File to hash (up to 500MB)", - type: ApplicationCommandOptionType.Attachment, - required: false, - } - ], - } -]; - -// Commands that only make sense in a guild context -const guildCommands = [ - // Add guild-specific commands here if needed -]; - -// User context menu commands (should also be registered globally) -const userCommands = [ - { - name: "User Info", - type: ApplicationCommandType.User, - }, -]; - -// Function to get existing commands without deleting them -async function getExistingCommands(rest, route) { - try { - return await rest.get(route); - } catch (error) { - console.error(`Error fetching commands from ${route}:`, error); - return []; - } -} - -// New function to safely update commands -async function updateCommandsSafely() { - if (!process.env.DISCORD_TOKEN || !process.env.CLIENT_ID) { - console.error("Missing required environment variables: DISCORD_TOKEN or CLIENT_ID"); - return; - } - - const rest = new REST({ version: "10" }).setToken(process.env.DISCORD_TOKEN); - - try { - console.log("Starting command registration..."); - - // First, get all existing global commands to check for entry point commands - const existingGlobalCommands = await getExistingCommands( - rest, - Routes.applicationCommands(process.env.CLIENT_ID) - ); - - // Find any entry point or special commands we need to preserve - const entryPointCommands = existingGlobalCommands.filter( - cmd => cmd.integration_types && cmd.integration_types.includes(1) - ); - - // Create a map to track command names we've already added - const commandNameMap = new Map(); - - // Create a filtered array of commands without duplicates - const allGlobalCommands = []; - - // First add global commands - for (const cmd of globalCommands) { - if (!commandNameMap.has(cmd.name)) { - commandNameMap.set(cmd.name, true); - allGlobalCommands.push(cmd); - } else { - console.warn(`Skipping duplicate global command: ${cmd.name}`); - } - } - - // Then add user commands - for (const cmd of userCommands) { - if (!commandNameMap.has(cmd.name)) { - commandNameMap.set(cmd.name, true); - allGlobalCommands.push(cmd); - } else { - console.warn(`Skipping duplicate user command: ${cmd.name}`); - } - } - - // Finally, add entry point commands that don't duplicate existing names - for (const cmd of entryPointCommands) { - if (!commandNameMap.has(cmd.name)) { - commandNameMap.set(cmd.name, true); - allGlobalCommands.push(cmd); - } else { - console.log(`Entry point command "${cmd.name}" already exists, keeping existing definition`); - } - } - - console.log(`Registering ${allGlobalCommands.length} unique global commands...`); - - // Update global commands (including DM-compatible commands) - await rest.put( - Routes.applicationCommands(process.env.CLIENT_ID), - { body: allGlobalCommands } - ); - console.log(`Successfully registered ${allGlobalCommands.length} global commands`); - - // If we have guild-specific commands, register them for each guild - if (guildCommands.length > 0) { - // Wait for client to be ready to access guilds - if (!client.isReady()) { - await new Promise(resolve => { - client.once('ready', resolve); - }); - } - - // Register guild commands for each guild the bot is in - for (const guild of client.guilds.cache.values()) { - console.log(`Registering guild commands for ${guild.name} (${guild.id})...`); - await rest.put( - Routes.applicationGuildCommands(process.env.CLIENT_ID, guild.id), - { body: guildCommands } - ); - } - console.log(`Successfully registered ${guildCommands.length} guild commands per server`); - } - - console.log("All commands registered successfully!"); - } catch (error) { - console.error("Error updating commands:", error); - if (error.code === 50240) { - console.error("This error suggests you need to include all Entry Point commands in your update."); - console.error("Try using the updateCommandsSafely function which preserves Entry Point commands."); - } - } -} - - -// Function to ask Cody a question and parse the streaming response -async function askCody(question) { - // [Function implementation remains the same] - if (!process.env.SOURCEGRAPH_API_KEY) { - 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', - url: 'https://sourcegraph.com/.api/completions/stream', - data: { - messages: [ - { - speaker: "human", - text: question - } - ], - temperature: 0.3, - maxTokensToSample: 2000, - topK: 50, - topP: 0.95 - }, - headers: { - 'Content-Type': 'application/json', - 'Authorization': `token ${process.env.SOURCEGRAPH_API_KEY}` - }, - responseType: 'text' - }); - - const events = response.data.split('\n\n').filter(line => line.trim() !== ''); - - let fullCompletion = ''; - - for (const event of events) { - const lines = event.split('\n'); - const eventType = lines[0].replace('event: ', ''); - - if (eventType === 'completion') { - const dataLine = lines[1]; - if (dataLine && dataLine.startsWith('data: ')) { - try { - const jsonData = JSON.parse(dataLine.substring(6)); - if (jsonData.completion) { - fullCompletion = jsonData.completion; - } - } catch (e) { - console.error('Error parsing JSON from Cody response:', e); - } - } - } - } - - return fullCompletion; - } catch (error) { - console.error('Error calling Cody API:', error.message); - if (error.response) { - console.error('Response data:', error.response.data); - console.error('Response status:', error.response.status); - } - throw error; - } -} - -client.once("ready", async () => { - console.log(`Logged in as ${client.user.tag}`); - - // Register commands after the bot is ready - await updateCommandsSafely(); - - // Send startup notification - if (process.env.OWNER_ID) { - try { - const ownerId = process.env.OWNER_ID; - const owner = await client.users.fetch(ownerId); - const startupEmbed = { - title: "Bot Status Update", - description: `Bot started successfully at `, - color: 0x00ff00, - fields: [ - { - name: "Bot Name", - value: client.user.tag, - inline: true - }, - { - name: "Relative Time", - value: ``, - inline: true - } - ], - footer: { - text: "blahaj.tr" - } - }; - - await owner.send({ embeds: [startupEmbed] }); - } catch (error) { - console.error("Failed to send startup notification:", error); - } - } -}); - -client.on("interactionCreate", async (interaction) => { - // Add debug logging to help diagnose DM issues - console.log(`Received interaction: ${interaction.commandName} | Channel type: ${interaction.channel.type} | DM: ${interaction.channel.type === ChannelType.DM}`); - - // Handle slash commands - if (interaction.isChatInputCommand()) { - try { - switch (interaction.commandName) { - case "fetch_data": - try { - const url = interaction.options.getString("url"); - if (!url.startsWith("http://") && !url.startsWith("https://")) { - await interaction.reply({ - content: "Please provide a valid URL starting with http:// or https://", - ephemeral: true, - }); - return; - } - const response = await axios.get(url); - await interaction.reply({ - content: `\`\`\`json\n${JSON.stringify(response.data, null, 2)}\n\`\`\``, - }); - } catch (error) { - console.error(error); - await interaction.reply({ content: "Failed to fetch data.", ephemeral: true }); - } - break; - - case "ping": - try { - await interaction.deferReply(); - const ip = interaction.options.getString("ip"); - const pingResult = await ping.promise.probe(ip); - if (pingResult.time == "unknown") { - await interaction.editReply({ - content: "Unable to ping the IP address.", - ephemeral: true, - }); - return; - } - const pingEmbed = { - title: "Ping Results", - description: `Results for IP: ${ip}`, - color: 0x00ff00, - fields: [ - { - name: "Response Time", - value: `${pingResult.time}ms`, - inline: true - } - ], - timestamp: new Date(), - footer: { - text: "Ping Command" - } - }; - await interaction.editReply({ embeds: [pingEmbed] }); - } catch (error) { - console.error(error); - await interaction.editReply({ content: "Failed to ping.", ephemeral: true }); - } - break; - - case "server_status": - try { - const response = await axios.get("https://blahaj.tr:2589/status"); - const isRaw = interaction.options.getBoolean("raw") ?? false; - - if (isRaw) { - await interaction.reply({ - content: `\`\`\`json\n${JSON.stringify(response.data, null, 2)}\n\`\`\``, - }); - } else { - let formattedResponse = ""; - const fields = []; - for (const [server, data] of Object.entries(response.data)) { - const status = data.online ? "🟢 Online" : "🔴 Offline"; - const responseTime = data.responseTime.toFixed(2); - fields.push({ - name: server, - value: `Status: ${status}\nResponse Time: ${responseTime}ms`, - inline: true - }); - } - - const statusEmbed = { - title: "Server Status", - color: 0x00ff00, - fields: fields, - timestamp: new Date(), - footer: { - text: "Server Status Command" - } - }; - - await interaction.reply({ embeds: [statusEmbed] }); - } - } catch (error) { - console.error(error); - await interaction.reply({ content: "Failed to get status.", ephemeral: true }); - } - break; - - case "cody": - try { - await interaction.deferReply(); - const question = interaction.options.getString("question"); - - if (!process.env.SOURCEGRAPH_API_KEY) { - await interaction.editReply({ - content: "Sourcegraph API key not configured. Please add SOURCEGRAPH_API_KEY to your environment variables.", - ephemeral: true - }); - return; - } - - console.log(`Asking Cody: ${question}`); - - // Call Cody API - const codyResponse = await askCody(question); - - console.log(`Cody response received: ${codyResponse ? 'yes' : 'no'}`); - - // 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 (fullResponse.length > 1900) { - formattedResponse = formattedResponse.substring(0, 1900 - question.length - 50) + "...\n(Response truncated due to Discord's character limit)"; - } - - await interaction.editReply({ - content: `**Question:** ${question}\n\n**Cody's Answer:**\n${formattedResponse}` - }); - } catch (error) { - console.error("Cody API error:", error); - await interaction.editReply({ - content: "Sorry, I couldn't get an answer from Cody. Please try again later.", - ephemeral: true - }); - } - break; - - case "weather": - try { - await interaction.deferReply(); - const location = interaction.options.getString("location"); - - // Make sure you have WEATHER_API_KEY in your .env file - if (!process.env.WEATHER_API_KEY) { - await interaction.editReply({ - content: "Weather API key not configured. Please add WEATHER_API_KEY to your environment variables.", - ephemeral: true - }); - return; - } - - 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 temp_c = data.current.temp_c; - const temp_f = data.current.temp_f; - const condition = data.current.condition.text; - const humidity = data.current.humidity; - const wind_kph = data.current.wind_kph; - const wind_mph = data.current.wind_mph; - const feelslike_c = data.current.feelslike_c; - const feelslike_f = data.current.feelslike_f; - - const weatherEmbed = { - title: `Weather for ${location_name}, ${region}, ${country}`, - 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 } - ], - thumbnail: { url: `https:${data.current.condition.icon}` }, - timestamp: new Date(), - footer: { text: 'Powered by WeatherAPI.com' } - }; - - 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; - - // Handle all the other commands the same way as in the original code - case "mcstatus": - try { - await interaction.deferReply(); - const serverAddress = interaction.options.getString("server"); - const isBedrock = interaction.options.getBoolean("bedrock") ?? false; - - 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; - } - - const serverEmbed = { - title: `Minecraft Server Status: ${serverAddress}`, - color: 0x44FF44, - 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() - }; - - if (data.motd && data.motd.clean && data.motd.clean.length > 0) { - serverEmbed.description = `**MOTD:**\n${data.motd.clean.join('\n')}`; - } - - if (data.players && data.players.list && data.players.list.length > 0) { - 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"); - - 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 type = interaction.options.getString("type"); - - let apiUrl; - let isQuote = false; - - if (type === "quote") { - apiUrl = "https://some-random-api.ml/animu/quote"; - isQuote = true; - } else { - apiUrl = `https://some-random-api.ml/animu/${type}`; - } - - const response = await axios.get(apiUrl); - - if (isQuote) { - 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 { - const gifUrl = response.data.link; - - const actionTitle = type.charAt(0).toUpperCase() + type.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; - - case "checkdns": - try { - await interaction.deferReply(); - const domain = interaction.options.getString("domain"); - const provider = interaction.options.getString("provider") || "1.1.1.1"; - - const dns = require('dns'); - const resolver = new dns.Resolver(); - resolver.setServers([provider]); - - resolver.resolve4(domain, async (err, addresses) => { - if (err) { - const dnsEmbed = { - title: "DNS Lookup Result", - description: `Domain: ${domain}\nProvider: ${provider}`, - color: 0xFF0000, - fields: [ - { - name: "Status", - value: "❌ Domain is blocked or unreachable", - inline: true - }, - { - name: "Error", - value: err.code, - inline: true - } - ], - timestamp: new Date(), - footer: { text: "DNS Check Service" } - }; - await interaction.editReply({ embeds: [dnsEmbed] }); - } else { - const dnsEmbed = { - title: "DNS Lookup Result", - description: `Domain: ${domain}\nProvider: ${provider}`, - color: 0x00FF00, - fields: [ - { - name: "Status", - value: "✅ Domain is accessible", - inline: true - }, - { - name: "IP Addresses", - value: addresses.join('\n'), - inline: true - } - ], - timestamp: new Date(), - footer: { text: "DNS Check Service" } - }; - await interaction.editReply({ embeds: [dnsEmbed] }); - } - }); - } catch (error) { - console.error(error); - await interaction.editReply({ - content: "Failed to perform DNS lookup. Please check the domain name and try again.", - ephemeral: true - }); - } - break; - - case "traceroute": - try { - await interaction.deferReply(); - const target = interaction.options.getString("target"); - const maxHops = interaction.options.getInteger("hops") || 16; - - const { spawn } = require('child_process'); - // Use shell option to properly handle the pipe - const tracepath = spawn('traceroute -q 1 -d -m ' + maxHops + ' ' + target + ' | awk \'{print $1, $2, $3}\'', { - shell: true - }); - - let output = ''; - - tracepath.stdout.on('data', async (data) => { - const newData = data.toString() - .split('\n') - .map(line => line.trim()) - .join('\n'); - - output += newData; - const traceEmbed = { - title: `Path to ${target}`, - description: `\`\`\`\n${output}\`\`\``, - color: 0x3498db, - timestamp: new Date(), - footer: { text: "Tracing..." } - }; - - await interaction.editReply({ embeds: [traceEmbed] }); - }); - - tracepath.stderr.on('data', (data) => { - console.log(`stderr: ${data}`); - }); - - tracepath.on('close', async () => { - const finalEmbed = { - title: `Path to ${target} - Complete`, - description: `\`\`\`\n${output}\`\`\``, - color: 0x00ff00, - timestamp: new Date(), - footer: { text: "✅ Trace complete" } - }; - - await interaction.editReply({ embeds: [finalEmbed] }); - }); - } catch (error) { - console.error(error); - await interaction.editReply({ - content: "Failed to trace path. Please check the target and try again.", - ephemeral: true - }); - } - break; - - case "wikipedia": - try { - await interaction.deferReply(); - const query = interaction.options.getString("query"); - const language = interaction.options.getString("language") || "en"; - - // Import the wikipedia package - const wikipedia = require('wikipedia'); - - // Set the language - wikipedia.setLang(language); - - // Search for the query - const searchResults = await wikipedia.search(query); - - if (!searchResults.results || searchResults.results.length === 0) { - await interaction.editReply({ - content: `No results found for "${query}" on Wikipedia.`, - ephemeral: true - }); - return; - } - - // Get the first result - const page = await wikipedia.page(searchResults.results[0].title); - - // Get summary and basic info - const summary = await page.summary(); - - // Create a rich embed - const wikiEmbed = { - title: summary.title, - url: summary.content_urls.desktop.page, - description: summary.extract.length > 1000 - ? summary.extract.substring(0, 1000) + "..." - : summary.extract, - color: 0x0099ff, - thumbnail: summary.thumbnail - ? { url: summary.thumbnail.source } - : null, - fields: [ - { - name: "Page ID", - value: summary.pageid.toString(), - inline: true - }, - { - name: "Language", - value: language.toUpperCase(), - inline: true - } - ], - footer: { - text: "Powered by Wikipedia", - icon_url: "https://upload.wikimedia.org/wikipedia/commons/thumb/8/80/Wikipedia-logo-v2.svg/103px-Wikipedia-logo-v2.svg.png" - }, - timestamp: new Date() - }; - - // Add a related articles field if we have other search results - if (searchResults.results.length > 1) { - const relatedArticles = searchResults.results - .slice(1, 4) // Get 3 related articles - .map(result => `[${result.title}](https://${language}.wikipedia.org/wiki/${encodeURIComponent(result.title.replace(/ /g, '_'))})`) - .join('\n'); - - wikiEmbed.fields.push({ - name: "Related Articles", - value: relatedArticles - }); - } - - await interaction.editReply({ embeds: [wikiEmbed] }); - } catch (error) { - console.error(error); - - // Handle specific Wikipedia errors - if (error.message.includes("No article found")) { - await interaction.editReply({ - content: "Couldn't find a specific Wikipedia article with that title. Try a different search term.", - ephemeral: true - }); - } else { - await interaction.editReply({ - content: "Error fetching Wikipedia data. Please try again later.", - ephemeral: true - }); - } - } - break; - - case "whois": - try { - await interaction.deferReply(); - const domain = interaction.options.getString("domain"); - - const result = await whois(domain); - - const whoisEmbed = { - title: `WHOIS Lookup: ${domain}`, - color: 0x2ecc71, - fields: [ - { - name: "Registrar", - value: result.registrar || "Not available", - inline: true - }, - { - name: "Creation Date", - value: result.creationDate ? new Date(result.creationDate).toLocaleDateString() : "Not available", - inline: true - }, - { - name: "Expiration Date", - value: result.expirationDate ? new Date(result.expirationDate).toLocaleDateString() : "Not available", - inline: true - }, - { - name: "Name Servers", - value: Array.isArray(result.nameServers) ? result.nameServers.join('\n') : "Not available" - }, - { - name: "Status", - value: Array.isArray(result.status) ? result.status.join('\n') : (result.status || "Not available") - } - ], - timestamp: new Date(), - footer: { text: "Domain Information Service" } - }; - - await interaction.editReply({ embeds: [whoisEmbed] }); - } catch (error) { - console.error(error); - await interaction.editReply({ - content: "Failed to fetch WHOIS information. Please check the domain name and try again.", - ephemeral: true - }); - } - break; - - case "stats": - try { - await interaction.deferReply(); - const os = require('os'); - - // Calculate uptime - const uptime = process.uptime(); - const days = Math.floor(uptime / 86400); - const hours = Math.floor((uptime % 86400) / 3600); - const minutes = Math.floor((uptime % 3600) / 60); - - // Get system info - const memUsage = process.memoryUsage(); - const cpuLoad = os.loadavg(); - - const statsEmbed = { - title: "Bot Statistics", - color: 0x7289DA, - fields: [ - { - name: "Bot Info", - value: [ - `**Servers:** ${client.guilds.cache.size}`, - `**Users:** ${client.users.cache.size}`, - `**Channels:** ${client.channels.cache.size}`, - `**Commands:** ${globalCommands.length}` - ].join('\n'), - inline: true - }, - { - name: "System Info", - value: [ - `**Platform:** ${os.platform()}`, - `**Memory Usage:** ${Math.round(memUsage.heapUsed / 1024 / 1024)}MB`, - `**CPU Load:** ${cpuLoad[0].toFixed(2)}%`, - `**Node.js:** ${process.version}` - ].join('\n'), - inline: true - }, - { - name: "Uptime", - value: `${days}d ${hours}h ${minutes}m`, - inline: true - } - ], - timestamp: new Date(), - footer: { text: "blahaj-srv" } - }; - - await interaction.editReply({ embeds: [statsEmbed] }); - } catch (error) { - console.error(error); - await interaction.editReply({ - content: "Failed to fetch statistics.", - ephemeral: true - }); - } - break; - - case "checkport": - try { - await interaction.deferReply(); - const target = interaction.options.getString("target"); - const ports = interaction.options.getString("ports").split(",").map(p => parseInt(p.trim())); - const net = require('net'); - - const checkPort = (port) => { - return new Promise((resolve) => { - const socket = new net.Socket(); - socket.setTimeout(2000); // 2 second timeout - - socket.on('connect', () => { - socket.destroy(); - resolve({ port, status: 'open' }); - }); - - socket.on('timeout', () => { - socket.destroy(); - resolve({ port, status: 'closed' }); - }); - - socket.on('error', () => { - socket.destroy(); - resolve({ port, status: 'closed' }); - }); - - socket.connect(port, target); - }); - }; - - const results = await Promise.all(ports.map(port => checkPort(port))); - - const scanEmbed = { - title: `Port Scan Results for ${target}`, - color: 0x00ff00, - fields: results.map(result => ({ - name: `Port ${result.port}`, - value: result.status === 'open' ? '🟢 Open' : '🔴 Closed', - inline: true - })), - timestamp: new Date(), - footer: { text: "Port Check" } - }; - - await interaction.editReply({ embeds: [scanEmbed] }); - } catch (error) { - console.error(error); - await interaction.editReply({ - content: "Failed to perform port scan. Please check the target and port numbers.", - ephemeral: true - }); - } - break; - - case "urban": - try { - await interaction.deferReply(); - const term = interaction.options.getString("term"); - const isRandom = interaction.options.getBoolean("random") || false; - - // API endpoint - const endpoint = isRandom - ? "https://api.urbandictionary.com/v0/random" - : `https://api.urbandictionary.com/v0/define?term=${encodeURIComponent(term)}`; - - const response = await axios.get(endpoint); - - if (!response.data.list || response.data.list.length === 0) { - await interaction.editReply({ - content: `No definitions found for "${term}" on Urban Dictionary.`, - ephemeral: true - }); - return; - } - - // Sort by thumbs up count if there are multiple definitions - const definitions = response.data.list.sort((a, b) => b.thumbs_up - a.thumbs_up); - const definition = definitions[0]; - - // Clean up the text by replacing square brackets with formatted links - let cleanDefinition = definition.definition.replace(/\[([^\]]+)\]/g, '**$1**'); - let cleanExample = definition.example.replace(/\[([^\]]+)\]/g, '**$1**'); - - // Truncate if too long - if (cleanDefinition.length > 1024) { - cleanDefinition = cleanDefinition.substring(0, 1021) + '...'; - } - - if (cleanExample.length > 1024) { - cleanExample = cleanExample.substring(0, 1021) + '...'; - } - - // Create a rich embed - const urbanEmbed = { - title: isRandom ? definition.word : term, - url: definition.permalink, - color: 0xEFFF00, // Urban Dictionary yellow - fields: [ - { - name: "Definition", - value: cleanDefinition || "No definition provided" - } - ], - footer: { - text: `👍 ${definition.thumbs_up} | 👎 ${definition.thumbs_down} | Written by ${definition.author}`, - icon_url: "https://i.imgur.com/VFXr0ID.jpg" - }, - timestamp: new Date(definition.written_on) - }; - - // Add example if it exists - if (cleanExample && cleanExample.trim().length > 0) { - urbanEmbed.fields.push({ - name: "Example", - value: cleanExample - }); - } - - // Add related definitions if there are more - if (definitions.length > 1) { - const relatedCount = Math.min(definitions.length - 1, 3); - urbanEmbed.fields.push({ - name: `${relatedCount} More Definition${relatedCount > 1 ? 's' : ''}`, - value: `This term has ${definitions.length} definitions. Use the link above to see them all.` - }); - } - - // Add a warning that content might be offensive - const warningMessage = "⚠️ **Note:** Urban Dictionary contains user-submitted content that may be offensive or inappropriate."; - - await interaction.editReply({ - content: warningMessage, - embeds: [urbanEmbed] - }); - } catch (error) { - console.error(error); - await interaction.editReply({ - content: "Error fetching Urban Dictionary definition. Please try again later.", - ephemeral: true - }); - } - break; - case "currency": - try { - await interaction.deferReply(); - const amount = interaction.options.getNumber("amount"); - const fromCurrency = interaction.options.getString("from").toUpperCase(); - const toCurrency = interaction.options.getString("to").toUpperCase(); - - // Check if API key is configured - if (!process.env.EXCHANGE_RATE_API_KEY) { - await interaction.editReply({ - content: "Exchange Rate API key not configured. Please add EXCHANGE_RATE_API_KEY to your environment variables.", - ephemeral: true - }); - return; - } - - // Validate amount - if (amount <= 0) { - await interaction.editReply({ - content: "Please provide a positive amount to convert.", - ephemeral: true - }); - return; - } - - // Fetch exchange rates - const apiUrl = `https://v6.exchangerate-api.com/v6/${process.env.EXCHANGE_RATE_API_KEY}/latest/${fromCurrency}`; - const response = await axios.get(apiUrl); - - // Check if the source currency is valid - if (response.data.result === "error") { - await interaction.editReply({ - content: `Error: ${response.data.error-type || "Invalid request"}. Please check your currency codes.`, - ephemeral: true - }); - return; - } - - // Check if target currency exists in the response - if (!response.data.conversion_rates[toCurrency]) { - await interaction.editReply({ - content: `Could not find exchange rate for ${toCurrency}. Please check your currency code.`, - ephemeral: true - }); - return; - } - - // Calculate the converted amount - const rate = response.data.conversion_rates[toCurrency]; - const convertedAmount = amount * rate; - - // Format numbers with proper separators and decimals - const formatNumber = (num) => { - return new Intl.NumberFormat('en-US', { - minimumFractionDigits: 2, - maximumFractionDigits: 4 - }).format(num); - }; - - // Get currency information to display symbols - const currencyInfo = { - USD: { symbol: '$', name: 'US Dollar' }, - EUR: { symbol: '€', name: 'Euro' }, - GBP: { symbol: '£', name: 'British Pound' }, - JPY: { symbol: '¥', name: 'Japanese Yen' }, - TRY: { symbol: '₺', name: 'Turkish Lira' }, - // Add more currencies as needed - }; - - const fromCurrencyInfo = currencyInfo[fromCurrency] || { symbol: '', name: fromCurrency }; - const toCurrencyInfo = currencyInfo[toCurrency] || { symbol: '', name: toCurrency }; - - // Create a rich embed - const conversionEmbed = { - title: "Currency Conversion", - color: 0x4CAF50, // Green - fields: [ - { - name: "From", - value: `${fromCurrencyInfo.symbol} ${formatNumber(amount)} ${fromCurrency} (${fromCurrencyInfo.name})`, - inline: false - }, - { - name: "To", - value: `${toCurrencyInfo.symbol} ${formatNumber(convertedAmount)} ${toCurrency} (${toCurrencyInfo.name})`, - inline: false - }, - { - name: "Exchange Rate", - value: `1 ${fromCurrency} = ${formatNumber(rate)} ${toCurrency}`, - inline: true - }, - { - name: "Last Updated", - value: new Date(response.data.time_last_update_unix * 1000).toLocaleString(), - inline: true - } - ], - footer: { - text: "Powered by ExchangeRate-API" - }, - timestamp: new Date() - }; - - await interaction.editReply({ embeds: [conversionEmbed] }); - } catch (error) { - console.error(error); - const errorMessage = error.response?.data?.error || "Error fetching exchange rates. Please try again later."; - await interaction.editReply({ - content: errorMessage, - ephemeral: true - }); - } - break; - case "hash": - try { - await interaction.deferReply(); - const algorithm = interaction.options.getString("algorithm"); - const text = interaction.options.getString("text"); - const file = interaction.options.getAttachment("file"); - - // Validate that either text or file is provided - if (!text && !file) { - await interaction.editReply({ - content: "Please provide either text or a file to hash.", - ephemeral: true - }); - return; - } - - // If both are provided, prioritize the file - if (text && file) { - await interaction.followUp({ - content: "Both text and file were provided. Processing the file and ignoring the text.", - ephemeral: true - }); - } - - // For text input, generate hash directly - if (text && !file) { - const crypto = require('crypto'); - const hash = crypto.createHash(algorithm).update(text).digest('hex'); - - const hashEmbed = { - title: `${algorithm.toUpperCase()} Hash`, - description: "Text hash generated successfully", - color: 0x3498db, - fields: [ - { - name: "Input Text", - value: text.length > 1024 ? text.substring(0, 1021) + "..." : text - }, - { - name: "Hash", - value: "```\n" + hash + "\n```" - } - ], - timestamp: new Date(), - footer: { text: `Algorithm: ${algorithm.toUpperCase()}` } - }; - - await interaction.editReply({ embeds: [hashEmbed] }); - return; - } - - // For file input, download and hash the file - if (file) { - // Check file size (500MB limit) - const maxSize = 500 * 1024 * 1024; // 500MB in bytes - if (file.size > maxSize) { - await interaction.editReply({ - content: `File is too large. Maximum size is 500MB. Your file is ${(file.size / (1024 * 1024)).toFixed(2)}MB.`, - ephemeral: true - }); - return; - } - - // If file is larger than 25MB, warn the user it might take a while - if (file.size > 25 * 1024 * 1024) { - await interaction.editReply({ - content: `Processing a ${(file.size / (1024 * 1024)).toFixed(2)}MB file. This might take a while...` - }); - } - - // Process the file using streams for efficiency - const fs = require('fs'); - const path = require('path'); - const crypto = require('crypto'); - const stream = require('stream'); - const { promisify } = require('util'); - const pipeline = promisify(stream.pipeline); - const axios = require('axios'); - - // Create a temporary file path - const tempDir = path.join(__dirname, '../temp'); - if (!fs.existsSync(tempDir)) { - fs.mkdirSync(tempDir, { recursive: true }); - } - - const tempFile = path.join(tempDir, `${Date.now()}_${file.name.replace(/[^a-zA-Z0-9.]/g, '_')}`); - - try { - // Download the file - const writer = fs.createWriteStream(tempFile); - const response = await axios({ - method: 'GET', - url: file.url, - responseType: 'stream' - }); - - await pipeline(response.data, writer); - - // After download completes, hash the file with progress updates - const fileSize = fs.statSync(tempFile).size; - const hash = crypto.createHash(algorithm); - const input = fs.createReadStream(tempFile); - - let processedBytes = 0; - let lastProgressUpdate = Date.now(); - - input.on('data', (chunk) => { - hash.update(chunk); - processedBytes += chunk.length; - - // Update progress every 3 seconds for files larger than 50MB - const now = Date.now(); - if (fileSize > 50 * 1024 * 1024 && now - lastProgressUpdate > 3000) { - const progress = (processedBytes / fileSize * 100).toFixed(2); - interaction.editReply({ - content: `Processing file: ${progress}% complete...` - }).catch(console.error); - lastProgressUpdate = now; - } - }); - - // Wait for the hash to complete - const hashHex = await new Promise((resolve, reject) => { - input.on('end', () => resolve(hash.digest('hex'))); - input.on('error', reject); - }); - - // Clean up the temp file - fs.unlinkSync(tempFile); - - // Create the response embed - const fileExtension = path.extname(file.name).toLowerCase(); - const hashEmbed = { - title: `${algorithm.toUpperCase()} Hash Generated`, - description: "File hash calculated successfully", - color: 0x00ff00, - fields: [ - { - name: "File", - value: `${file.name} (${(file.size / (1024 * 1024)).toFixed(2)} MB)` - }, - { - name: "Hash", - value: "```\n" + hashHex + "\n```" - } - ], - timestamp: new Date(), - footer: { text: `Algorithm: ${algorithm.toUpperCase()}` } - }; - - await interaction.editReply({ embeds: [hashEmbed] }); - } catch (fileError) { - console.error("File processing error:", fileError); - - // Clean up temp file if it exists - if (fs.existsSync(tempFile)) { - fs.unlinkSync(tempFile); - } - - await interaction.editReply({ - content: "Error processing file. The file might be inaccessible or corrupted.", - ephemeral: true - }); - } - } - } catch (error) { - console.error("Hash command error:", error); - await interaction.editReply({ - content: "Error generating hash. Please try again with a smaller file or different input.", - ephemeral: true - }); - } - break; - default: - await interaction.reply({ - content: `Command '${interaction.commandName}' not implemented yet.`, - ephemeral: true - }); - } - } catch (error) { - console.error(`Error executing command ${interaction.commandName}:`, error); - - // Try to respond if we haven't already - try { - const replyMethod = interaction.deferred ? interaction.editReply : interaction.reply; - await replyMethod.call(interaction, { - content: "An error occurred while executing this command.", - ephemeral: true - }); - } catch (e) { - console.error("Could not send error response:", e); - } - } - } else if (interaction.isUserContextMenuCommand()) { - // Handle user context menu commands - if (interaction.commandName === "User Info") { - const user = interaction.targetUser; - - const userInfoEmbed = { - title: "User Information", - color: 0x9B59B6, - thumbnail: { url: user.displayAvatarURL({ dynamic: true }) }, - fields: [ - { name: "Username", value: user.username, inline: true }, - { name: "User ID", value: user.id, inline: true }, - { name: "Account Created", value: ``, inline: true } - ], - footer: { text: "User Information" }, - timestamp: new Date() - }; - - await interaction.reply({ embeds: [userInfoEmbed], ephemeral: true }); - } - } -}); - -// Handle guild join events to register guild-specific commands if needed -client.on("guildCreate", async (guild) => { - console.log(`Joined new guild: ${guild.name} (${guild.id})`); - - if (guildCommands.length > 0) { - try { - console.log(`Registering guild commands for ${guild.name}...`); - const rest = new REST({ version: "10" }).setToken(process.env.DISCORD_TOKEN); - - await rest.put( - Routes.applicationGuildCommands(process.env.CLIENT_ID, guild.id), - { body: guildCommands } - ); - console.log(`Successfully registered guild commands for ${guild.name}`); - } catch (error) { - console.error(`Error registering guild commands for ${guild.name}:`, error); - } - } - - // Notify owner if configured - if (process.env.OWNER_ID) { - try { - const owner = await client.users.fetch(process.env.OWNER_ID); - const guildJoinEmbed = { - title: "New Guild Joined", - color: 0x00ff00, - fields: [ - { name: "Guild Name", value: guild.name, inline: true }, - { name: "Guild ID", value: guild.id, inline: true }, - { name: "Member Count", value: guild.memberCount.toString(), inline: true } - ], - timestamp: new Date(), - footer: { text: "Guild Join Event" } - }; - - await owner.send({ embeds: [guildJoinEmbed] }); - } catch (error) { - console.error("Failed to notify owner of guild join:", error); - } - } -}); - -// Error handling -process.on('unhandledRejection', error => { - console.error('Unhandled promise rejection:', error); -}); - -// Start the bot -client.login(process.env.DISCORD_TOKEN).catch(error => { - console.error("Failed to login:", error); - process.exit(1); -}); diff --git a/node b/node deleted file mode 100644 index e69de29..0000000 diff --git a/package-lock.json b/package-lock.json index 0bacc8c..16ee5a2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,6 @@ "dependencies": { "axios": "^1.8.4", "cors": "^2.8.5", - "discord.js": "^14.18.0", "dotenv": "^16.4.7", "express": "^4.21.2", <<<<<<< HEAD @@ -20,6 +19,7 @@ ======= "nodejs": "^0.0.0", "ping": "^0.4.4", +<<<<<<< HEAD "whois-json": "^2.0.4", "wikipedia": "^2.1.2" >>>>>>> 8979cf12f0836cc64935d71ef45678c40ec8197e @@ -211,6 +211,9 @@ "engines": { "node": ">=v14.0.0", "npm": ">=7.0.0" +======= + "whois-json": "^2.0.4" +>>>>>>> 7cc5ea85e0d343fedf6e39033928da2dc42e5a83 } }, "node_modules/accepts": { @@ -459,6 +462,7 @@ "version": "2.8.5", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "license": "MIT", "dependencies": { "object-assign": "^4", "vary": "^1" @@ -515,38 +519,6 @@ "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/discord-api-types": { - "version": "0.37.119", - "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.119.tgz", - "integrity": "sha512-WasbGFXEB+VQWXlo6IpW3oUv73Yuau1Ig4AZF/m13tXcTKnMpc/mHjpztIlz4+BM9FG9BHQkEXiPto3bKduQUg==", - "license": "MIT" - }, - "node_modules/discord.js": { - "version": "14.18.0", - "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-14.18.0.tgz", - "integrity": "sha512-SvU5kVUvwunQhN2/+0t55QW/1EHfB1lp0TtLZUSXVHDmyHTrdOj5LRKdR0zLcybaA15F+NtdWuWmGOX9lE+CAw==", - "license": "Apache-2.0", - "dependencies": { - "@discordjs/builders": "^1.10.1", - "@discordjs/collection": "1.5.3", - "@discordjs/formatters": "^0.6.0", - "@discordjs/rest": "^2.4.3", - "@discordjs/util": "^1.1.1", - "@discordjs/ws": "^1.2.1", - "@sapphire/snowflake": "3.5.3", - "discord-api-types": "^0.37.119", - "fast-deep-equal": "3.1.3", - "lodash.snakecase": "4.1.1", - "tslib": "^2.6.3", - "undici": "6.21.1" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/discordjs/discord.js?sponsor" - } - }, "node_modules/dot-case": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-2.1.1.tgz", @@ -658,6 +630,7 @@ "version": "4.21.2", "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "license": "MIT", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", @@ -699,12 +672,6 @@ "url": "https://opencollective.com/express" } }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "license": "MIT" - }, "node_modules/finalhandler": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", @@ -925,28 +892,6 @@ "node": ">=0.10.0" } }, - "node_modules/infobox-parser": { - "version": "3.6.4", - "resolved": "https://registry.npmjs.org/infobox-parser/-/infobox-parser-3.6.4.tgz", - "integrity": "sha512-d2lTlxKZX7WsYxk9/UPt51nkmZv5tbC75SSw4hfHqZ3LpRAn6ug0oru9xI2X+S78va3aUAze3xl/UqMuwLmJUw==", - "license": "MIT", - "dependencies": { - "camelcase": "^4.1.0" - }, - "funding": { - "type": "individual", - "url": "https://www.buymeacoffee.com/2tmRKi9" - } - }, - "node_modules/infobox-parser/node_modules/camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha512-FxAv7HpHrXbh3aPo4o2qxHay2lkLY3x5Mw3KeE4KQE8ysVfziWeRZDwcjauvwBSGEC/nXUPzZy8zeh4HokqOnw==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -1018,18 +963,6 @@ "node": ">=8" } }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "license": "MIT" - }, - "node_modules/lodash.snakecase": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", - "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==", - "license": "MIT" - }, "node_modules/lower-case": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", @@ -1045,12 +978,6 @@ "lower-case": "^1.1.2" } }, - "node_modules/magic-bytes.js": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/magic-bytes.js/-/magic-bytes.js-1.10.0.tgz", - "integrity": "sha512-/k20Lg2q8LE5xiaaSkMXk4sfvI+9EGEykFS4b0CHHGWqDYU0bGUFSwchNOMA56D7TCs9GwVTkqe9als1/ns8UQ==", - "license": "MIT" - }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -1264,6 +1191,7 @@ "version": "0.4.4", "resolved": "https://registry.npmjs.org/ping/-/ping-0.4.4.tgz", "integrity": "sha512-56ZMC0j7SCsMMLdOoUg12VZCfj/+ZO+yfOSjaNCRrmZZr6GLbN2X/Ui56T15dI8NhiHckaw5X2pvyfAomanwqQ==", + "license": "MIT", "engines": { "node": ">=4.0.0" } @@ -1609,18 +1537,6 @@ "node": ">=0.6" } }, - "node_modules/ts-mixer": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.4.tgz", - "integrity": "sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA==", - "license": "MIT" - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -1639,21 +1555,6 @@ "integrity": "sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g==", "license": "MIT" }, - "node_modules/undici": { - "version": "6.21.1", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.1.tgz", - "integrity": "sha512-q/1rj5D0/zayJB2FraXdaWxbhWiNKDvu8naDT2dl1yTlvJp4BLtOcp2a5BvgGNQpYYJzau7tf1WgKv3b+7mqpQ==", - "license": "MIT", - "engines": { - "node": ">=18.17" - } - }, - "node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "license": "MIT" - }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -1726,19 +1627,6 @@ "whois": "^2.6.0" } }, - "node_modules/wikipedia": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/wikipedia/-/wikipedia-2.1.2.tgz", - "integrity": "sha512-RAYaMpXC9/E873RaSEtlEa8dXK4e0p5k98GKOd210MtkE5emm6fcnwD+N6ZA4cuffjDWagvhaQKtp/mGp2BOVQ==", - "license": "MIT", - "dependencies": { - "axios": "^1.4.0", - "infobox-parser": "^3.6.2" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", @@ -1753,27 +1641,6 @@ "node": ">=8" } }, - "node_modules/ws": { - "version": "8.18.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz", - "integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, "node_modules/y18n": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", diff --git a/package.json b/package.json index 27ed117..4263284 100644 --- a/package.json +++ b/package.json @@ -22,12 +22,10 @@ "dependencies": { "axios": "^1.8.4", "cors": "^2.8.5", - "discord.js": "^14.18.0", "dotenv": "^16.4.7", "express": "^4.21.2", "nodejs": "^0.0.0", "ping": "^0.4.4", - "whois-json": "^2.0.4", - "wikipedia": "^2.1.2" + "whois-json": "^2.0.4" } }