diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index 72186f8..0000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,7 +0,0 @@ -version: 2 -updates: - - package-ecosystem: "npm" # See documentation for possible values - directory: "/" # Location of package manifests - schedule: - interval: "daily" - diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index be6c62d..a3d94a3 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -2,9 +2,6 @@ name: CI on: [push] -permissions: - contents: read - jobs: CI: runs-on: node @@ -16,6 +13,6 @@ jobs: with: node-version: 'latest' - name: install dependencies - run: npm ci + run: npm install - name: run tests - run: npm test + run: npm test \ No newline at end of file diff --git a/README.md b/README.md index 1b33dd4..a3ad0fe 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,5 @@ # discord_bot -![CI tests](https://github.com/SileNce5k/discord_bot/actions/workflows/CI.yml/badge.svg) - - A modular discord bot written in javascript, using the [discord.js](https://discord.js.org) library. This is my second attempt at making a discord bot. @@ -27,9 +24,11 @@ You can also change the global prefix. You should enter you discord user id, so you can use the admin commands. Every time you want to change something in this file, you have to restart the bot. -If the config is ever changed, you need to either delete it and repeat the steps above or take a look at the util/createInitialConfig.js file. +If the config is ever changed, you need to either delete it and repeat the steps above or take a look at the util/createInitialConfig.js file +Netload is a feature that enables the ability to upload .js files via discord to the bot. +If you want this feature, you have to enable it in ./data/config.json. ## Known issues -See [issues](https://github.com/SileNce5k/discord_bot/issues?q=is%3Aopen+is%3Aissue+label%3Abug) +None at the moment. diff --git a/commands/.example b/commands/.example index 3fa113b..4086437 100644 --- a/commands/.example +++ b/commands/.example @@ -1,7 +1,7 @@ module.exports = { name: 'exampleName', // Keep it to one word description: 'example description', - execute({message}) { + execute({message}) { //parameters you can use for netload: message, args, client, prefix //code } }; \ No newline at end of file diff --git a/commands/admin/reload.js b/commands/admin/reload.js index 5afb02e..c93aab0 100644 --- a/commands/admin/reload.js +++ b/commands/admin/reload.js @@ -1,4 +1,5 @@ const calculateReloaded = require("../../util/calculateReloaded.js"); +const reloadNetModules = require("../../util/reloadNetModules.js"); module.exports = { name: 'reload', @@ -8,6 +9,7 @@ module.exports = { let reloadCommands = require("../../util/reloadCommands.js") let beforeSize = client.commands.size; + reloadNetModules(client) reloadCommands(client) let sendText = calculateReloaded(beforeSize, client) message.channel.send(sendText) diff --git a/commands/admin/setPresence.js b/commands/admin/setPresence.js index 5dbfd51..bd6fe5f 100644 --- a/commands/admin/setPresence.js +++ b/commands/admin/setPresence.js @@ -1,4 +1,3 @@ -const savePresence = require("../../util/savePresence"); const setPresence = require("../../util/setPresence"); module.exports = { @@ -11,20 +10,12 @@ module.exports = { ,"Updates once a minute if custom variables are used." ,"" ,"Custom Variables:" - ,"${guilds},${prefix},${uptime},{members}"], + ,"${guilds},${prefix},${uptime}"], admin: true, - execute({message, client, args, prefix}) { - let forceUpdate = false; - if(args.length > 1 && args[0] === "force") { - forceUpdate = true; - args.shift(); - } - if(args.length < 1){ - message.channel.send(`You need at least two arguments for this command, see \`${prefix}help setpresence\``) - return; - } + execute({message, client, args, globalPrefix}) { + const savePresence = require("../../util/savePresence"); let presenceType = args[0].toLocaleUpperCase(); - let sendText = "Presence has been set."; + let sendText = "Updated presence"; switch (presenceType) { case "PLAY": @@ -55,11 +46,8 @@ module.exports = { const firstArg = args[0].length + 1; let temp = args.join(" "); let presenceText = temp.slice(firstArg, temp.length) + setPresence({presenceText: presenceText,presenceType: presenceType, client: client, globalPrefix: globalPrefix}); savePresence(presenceType, presenceText, client); - if(forceUpdate) - setPresence({presenceText, presenceType, client}) - else - sendText = `${sendText} It will update ` } message.channel.send(sendText); diff --git a/commands/admin/update.js b/commands/admin/update.js index 7c9a75f..7de9db0 100644 --- a/commands/admin/update.js +++ b/commands/admin/update.js @@ -1,5 +1,4 @@ const calculateReloaded = require("../../util/calculateReloaded"); -const executeCommand = require("../../util/executeCommand"); const reloadCommands = require("../../util/reloadCommands"); module.exports = { @@ -28,12 +27,10 @@ module.exports = { let commitCount = stdout.split(/\r\n|\r|\n/).length - 1 sendText = `${sendText}\n\nLatest commits (${commitCount}):\n${stdout}` if(sendText.length >= 2000){ - sendText = sendText.slice(1955) + sendText.slice(1955) sendText = `${sendText}\n... Message is too long to show everything` } message.channel.send(sendText) - const githash = executeCommand(`git`, ["rev-parse", "--short", "HEAD"]); - client.githash = githash.error ? "N/A" : githash.output; if (err) console.log(stderr) }) } diff --git a/commands/admin/whitelist.js b/commands/admin/whitelist.js deleted file mode 100644 index 327e3de..0000000 --- a/commands/admin/whitelist.js +++ /dev/null @@ -1,107 +0,0 @@ -const sqlite3 = require('sqlite3').verbose(); - -module.exports = { - name: 'whitelist', - description: 'Whitelist a command in a specific server, or for a user.', - admin: true, - async execute({message, args, prefix, client}) { - if(args.length < 3){ - message.channel.send(`You need to supply the command and guild that you want to whitelist\n\`${prefix}whitelist \``); - return; - } - let option = args[0]; - let command = args[1]; - let guildOrUser = args[2]; - - if(option === "guild" || option === "server"){ - if(guildOrUser === "this") guildOrUser = message.guild.id; - const whitelistedCommands = client.whitelist.guild.get(guildOrUser); - if(whitelistedCommands && whitelistedCommands.includes(command)){ - message.channel.send("Command is already whitelisted in that server.") - return; - } - // TODO: First check if the bot has access to that guild before whitelisting. - const databasePath = 'data/database.db' - const db = new sqlite3.Database(databasePath) - let err = false; - await new Promise((resolve, reject)=>{ - db.run(`INSERT INTO guild_whitelist ( - guildId, - command, - dateAdded - ) VALUES (?, ?, ?)`, - [ - guildOrUser, - command, - new Date().getTime() - ], function (error) { - if (error) { - console.error(error); - err = true; - resolve(); - }else{ - resolve(); - } - }); - }) - db.close(); - if(!err){ - if(whitelistedCommands){ - whitelistedCommands.push(command); - client.whitelist.guild.set(guildOrUser, whitelistedCommands); - }else { - client.whitelist.guild.set(guildOrUser, command); - } - message.channel.send(`"${command}" has been whitelisted in that server.`) - - } else message.channel.send("Could not whitelist the server with that command. Check the logs.") - return; - }else if (option === "user"){ - const whitelistedCommands = client.whitelist.user.get(guildOrUser); - if(whitelistedCommands && whitelistedCommands.includes(command)){ - message.channel.send("Command is already whitelisted for that user.") - return; - } - const databasePath = 'data/database.db' - const db = new sqlite3.Database(databasePath) - let err = false; - await new Promise((resolve, reject)=>{ - db.run(`INSERT INTO user_whitelist ( - userId, - command, - dateAdded - ) VALUES (?, ?, ?)`, - [ - guildOrUser, - command, - new Date().getTime() - ], function (error) { - if (error) { - console.error(error); - err = true; - resolve(); - }else{ - resolve(); - } - }); - }) - db.close(); - if(!err){ - if(whitelistedCommands){ - whitelistedCommands.push(command); - client.whitelist.user.set(guildOrUser, whitelistedCommands); - }else { - client.whitelist.user.set(guildOrUser, command); - } - message.channel.send(`"${command}" has been whitelisted for ${guildOrUser}.`) - - } else message.channel.send("Could not whitelist the server with that command. Check the logs.") - return; - } - - - - // TODO: Add ability to remove server from whitelist. - } - -}; \ No newline at end of file diff --git a/commands/info/botinfo.js b/commands/info/botinfo.js index b806459..df700bf 100644 --- a/commands/info/botinfo.js +++ b/commands/info/botinfo.js @@ -1,6 +1,6 @@ const {EmbedBuilder} = require('discord.js'); const getCreationDate = require('../../util/getCreationDate'); -const getGuildInfo = require('../../util/getGuildInfo'); +const getGuildCount = require('../../util/getGuildCount'); @@ -8,28 +8,15 @@ module.exports = { name: 'botinfo', description: 'Shows information about the bot', execute({message, client, prefix}) { - let guildInfo = getGuildInfo(client) - let descriptionArr = [`Name: ${client.user.username}`, - `Prefix: ${prefix}`, - `Total Servers: ${guildInfo.guildCount}`, - `Total Members: ${guildInfo.totalMembers} (${guildInfo.uniqueMemberCount} unique)`, - `Total Commands: ${client.commands.size}`, - `Creation Date: ${getCreationDate(client)}`, - `Source: [Click Here](https://github.com/SileNce5k/discord_bot)`, - `Current Version: ${client.githash}` - ] - - let description = ""; - descriptionArr.forEach(desc => { - description += `${desc}\n`; - }) - + let guildCount = getGuildCount(client) const embed = new EmbedBuilder() .setColor(15780145) .setTitle("Information about bot") .setTimestamp() .setAuthor({name: client.user.username, iconURL: client.user.avatarURL({ format: 'png', dynamic: true, size: 2048 })}) - .setDescription(description) + .addFields({ + name: "General info", value: `Name: ${client.user.username}\nPrefix: ${prefix}\nTotal Servers: ${guildCount}\nTotal Commands: ${client.commands.size}\nCreation Date: ${getCreationDate(client)}\nSource: [Click Here](https://github.com/SileNce5k/discord_bot)`, + },) message.channel.send({embeds :[embed]}) diff --git a/commands/info/guilds.js b/commands/info/guilds.js index 480fdda..c4eb45e 100644 --- a/commands/info/guilds.js +++ b/commands/info/guilds.js @@ -3,10 +3,10 @@ module.exports = { description: 'Returns guild names', admin: true, execute({message, client}) { - let guildNames = client.guilds.cache - .sort((a, b) => b.memberCount - a.memberCount) - .map(guild => `${guild.name} (${guild.memberCount} members)`) - .join("\n"); + let guildNames = ""; + client.guilds.cache.each(guild => { + guildNames = guildNames + guild.name + "\n"; + }); message.channel.send(guildNames) } }; \ No newline at end of file diff --git a/commands/info/help.js b/commands/info/help.js index da05e34..993cf59 100644 --- a/commands/info/help.js +++ b/commands/info/help.js @@ -8,19 +8,26 @@ module.exports = { description: 'List all available commands.', moreHelp: ["Examples:","`help [optional_page]` will return help with a small description for each command", "`help ` will return help with a more descriptive description", - "The descriptive description isn't available on all commands" + "The descriptive description isn't available on all commands", + "`help netmodules` to display help for netmodules" ], execute({ message, args, prefix, client }) { let commands = "" let commandFiles = getSubdirFiles('commands/') + let x = false; let fieldName = `Page [[page]]/${Math.round(client.commands.size / 10)}`; let iteration = 0; let num_in_args = false; let added_commands = 0; let page = -1; - + if (args[0] == "netmodules") { + commandFiles = fs.readdirSync('../../netload').filter(file => file.endsWith('.js')); + if (commandFiles.length == 0) { + message.channel.send("There are no netmodules currently loaded.") + x = true; + } - if(!isNaN(parseInt(args[0])) && parseInt(args[0])){ + }else if(!isNaN(parseInt(args[0])) && parseInt(args[0])){ num_in_args = true; iteration = ( parseInt(args[0]) - 1) * 10; page = Math.round(parseInt(args[0])) @@ -31,6 +38,7 @@ module.exports = { fieldName = fieldName.replace("[[page]]", page); const max_commands = iteration + 10; + if (x) return; const embed = new EmbedBuilder() .setColor(15780145) @@ -58,7 +66,6 @@ module.exports = { start++; } }else if(args[0] === command.name){ - fieldName = "Description"; commands = commands + `${prefix}${command.name}\n` embed.setTitle(command.name.charAt(0).toUpperCase() + command.name.slice(1)) if(command.moreHelp){ @@ -66,6 +73,7 @@ module.exports = { commands = commands + `${element}\n` }); } else { + fieldName = "Description"; commands = commands + `${command.description}`; } break; diff --git a/commands/info/mc.js b/commands/info/mc.js deleted file mode 100644 index dac751b..0000000 --- a/commands/info/mc.js +++ /dev/null @@ -1,103 +0,0 @@ -// Code is taken from https://github.com/stphnduvall/mcstatus/blob/master/src/index.ts -// and converted to pure js. - -const net = require('net') -const { EmbedBuilder } = require('discord.js'); - -module.exports = { - name: 'mc', - description: 'get minecraft server information', - hidden: true, - needsWhitelist: true, - async execute({ message, args }) { - let host = ""; - let port = 25565; - if (args[0]) host = args[0]; - - if (host.includes(":")) { - port = host.replace(/.+(?:\:)/g, ""); - host = host.match(/.+(?:\:)/g, "")[0].replace(":", ""); - } - if(host === "") return message.channel.send("No host provided") - let info = await getMinecraftServerInfo(host, port); - if (info) { - const embed = new EmbedBuilder() - embed.setColor("#ee7939") - embed.setTimestamp() - embed.addFields( - { name: "ping", value: info.ping.toString(), inline: false }, - { name: "Player Count", value: info.playercount.toString(), inline: false }, - { name: "Max Players", value: info.maxPlayers.toString(), inline: false }, - { name: "MOTD", value: info.motd, inline: false }, - ) - - message.channel.send({ embeds: [embed] }); - } else { - message.channel.send("Something went wrong\nThe minecraft server is likely not reachable from the discord bot") - } - - } -}; - - - -async function getMinecraftServerInfo(host, port = 25565) { - let serverInfo = { - ping: undefined, - maxPlayers: undefined, - version: undefined, - playercount: undefined, - motd: undefined - } - - let startTime = new Date(); - let data; - let ping; - serverInfo = await new Promise((resolve) => { - const client = net.connect({ host, port }, () => { - ping = Math.round(new Date().getMilliseconds() - startTime.getMilliseconds()); - - let buff = Buffer.from([0xFE, 0x01]); - client.write(buff); - - }) - let error = false; - client.on('data', (d) => { - data = d.toString() - client.destroy(); - - }) - client.once('error', (error) => { - console.error(error) - error = true; - }) - client.once('connectionAttemptFailed', (ip) => { - console.error("in attempt failed") - error = true; - }) - - client.once('connectionAttemptTimeout', (ip) => { - console.error("in attempt timeout") - error = true; - }) - client.on('close', () => { - if (!error) { - let _serverInfo = data?.split('\x00\x00\x00'); - - if (!_serverInfo) { - console.log("Something went wrong.") - resolve(serverInfo) - } - serverInfo.version = _serverInfo[2].replace(/\u0000/g, '') - serverInfo.motd = _serverInfo[3].replace(/\u0000/g, '') - serverInfo.playercount = Number(_serverInfo[4].replace(/\u0000/g, '')) - serverInfo.maxPlayers = Number(_serverInfo[5].replace(/\u0000/g, '')) - serverInfo.ping = Number(ping) - resolve(serverInfo) - } else { - resolve(0) - } - }) - }) - return serverInfo -} diff --git a/commands/info/pfp.js b/commands/info/pfp.js index dd76507..68a4640 100644 --- a/commands/info/pfp.js +++ b/commands/info/pfp.js @@ -16,9 +16,9 @@ module.exports = { let user = message.guild.members.cache.get(info); let guildPfp = user.avatarURL({format: 'png', dynamic: true, size: 4096}); let globalPfp = user.user.avatarURL({format: 'png', dynamic: true, size: 4096}); - let sendText = `${globalPfp}`; + let sendText = `Global pfp:\n${globalPfp}`; if(guildPfp != null){ - sendText = `Global pfp${sendText}\nGuild pfp:\n${guildPfp}`; + sendText = `${sendText}\nGuild pfp:\n${guildPfp}`; } message.channel.send(sendText) diff --git a/commands/info/serverinfo.js b/commands/info/serverinfo.js index 1f329de..1526c32 100644 --- a/commands/info/serverinfo.js +++ b/commands/info/serverinfo.js @@ -15,7 +15,6 @@ module.exports = { {name: "Server Name", value: message.guild.name, inline: false}, {name: "Created", value: convertDateToISOString(message.guild.createdAt), inline: false}, {name: "Members", value: message.guild.memberCount.toString(), inline: false}, - {name: "Channels", value: message.guild.channels.channelCountWithoutThreads.toString(), inline: false}, ]) diff --git a/commands/info/uptime.js b/commands/info/uptime.js index 30bef15..c89c07c 100644 --- a/commands/info/uptime.js +++ b/commands/info/uptime.js @@ -1,4 +1,4 @@ -const parseMS = require('../../util/parseMS') +const parseMS = require('parse-ms') module.exports = { name: 'uptime', diff --git a/commands/misc/dl.js b/commands/misc/dl.js deleted file mode 100644 index 13d95df..0000000 --- a/commands/misc/dl.js +++ /dev/null @@ -1,71 +0,0 @@ -const path = require('path'); -const fs = require('fs') - -const executeCommand = require('../../util/executeCommand'); -module.exports = { - name: 'dl', - description: 'Download a video', - moreHelp: [ - "Usage: dl " - ], - async execute({message, args}) { - const downloadsDir = path.resolve(process.cwd(), 'data', 'downloads', Date.now().toString()); - const cookieFilepath = path.resolve(process.cwd(), 'data', 'cookies.txt') - if(!fs.existsSync(cookieFilepath)) { - message.channel.send("Some dependencies are needed for the command to work properly. Please let the bot's owner know.") - return; - } - fs.mkdirSync(downloadsDir, {recursive: true}); - - let url; - - if(args.length > 0){ - if(args[0].charAt(0) === '<' && args[0].charAt(args[0].length - 1) === '>'){ - args[0] = args[0].slice(1, args[0].length - 1) - } - try { - url = new URL(args[0]); - url = url.href; - } catch (error) { - this.cleanUp(downloadsDir); - message.channel.send("Could not parse the provided argument as a URL."); - return; - } - } else { - this.cleanUp(downloadsDir); - return message.channel.send("You have to provide a URL in an argument.") - } - - const originalMessage = await message.channel.send("Downloading video...") - - if(executeCommand("yt-dlp", [url, "-P", downloadsDir, "--cookies", cookieFilepath]).error){ - originalMessage.edit("An error occurred when downloading the video."); - this.cleanUp(downloadsDir); - return; - } - - - let files = fs.readdirSync(downloadsDir); - if(files.length < 1) { - this.cleanUp(downloadsDir); - originalMessage.edit("Something went wrong when downloading the video.") - return; - } - const filename = files[0]; - - await originalMessage.edit({ - content: null, - files: [{ - attachment: path.resolve(downloadsDir, filename) - }]}) - - this.cleanUp(downloadsDir); - - - }, - - cleanUp(downloadsDir){ - fs.rmSync(downloadsDir, {force: true, recursive: true}); - }, - -} diff --git a/commands/misc/fm.js b/commands/misc/fm.js index e259f4c..615630c 100644 --- a/commands/misc/fm.js +++ b/commands/misc/fm.js @@ -4,12 +4,6 @@ const getCurrentCover = require("../../util/lastfm/getCurrentCover"); const getTopTracks = require("../../util/lastfm/getTopTracks"); const help = require("../info/help"); const parseMention = require("../../util/parseMention"); -const roast = require("../../util/lastfm/roast"); -const getTopArtists = require("../../util/lastfm/getTopArtists"); -const getTopAlbums = require("../../util/lastfm/getTopAlbums"); -const {EmbedBuilder} = require('discord.js'); -const getNickname = require('../../util/getNickname'); -const isWhitelisted = require("../../util/isWhitelisted"); module.exports = { name: 'fm', description: 'Last fm commands. See `help fm` for more info.', @@ -17,11 +11,8 @@ module.exports = { "They behave the same (for example: `fmtt` and `fm tt`)", "Set username: `fmset `", "Get current scrobble: `fm`", - "Get top artists `fmas`", - "Get top albums: `fmabl`", "Get top tracks: `fmtt`", "Get album cover for current scrobble: `fmcover`", - "Get a roast from an LLM using your top artists and albums: `fmroast`" ], async execute({ message, args, prefix, client }) { let sendText = {text: "Something went wrong.", embed: null}; @@ -37,46 +28,9 @@ module.exports = { args.shift(); sendText = await getTopTracks(message.author.id, args, message.guild); break; - case "topalbums": - case "topalbum": - case "abl": - args.shift(); - sendText = await getTopAlbums(message.author.id, args, message.guild) - break; - case "topartists": - case "topartist": - case "ta": - case "as": - args.shift(); - sendText = await getTopArtists(message.author.id, args, message.guild); - break; case "cover": sendText = await getCurrentCover(message.author.id, message.guild); break; - case "roast": - const commandName = "fmroast" - if(!isWhitelisted.isWhitelisted(commandName, client.whitelist, message.guild.id, message.author.id, isWhitelisted.whitelistTypes.EITHER)) { - message.channel.send("fmroast has not been whitelisted for you or this server.") - return; - } - let topArtists = await getTopArtists(message.author.id, ["yearly"], message.guild, true); - let topAlbums = await getTopAlbums(message.author.id, ["yearly"], message.guild, true); - message.channel.sendTyping(); - let result = await roast(topArtists, topAlbums); - - let parse = parseMention(message.author.id, message.guild) - let user = message.guild.members.cache.get(parse); - let nickname = getNickname(user, message.guild) - if (nickname == null) { - nickname = user.user.username; - } - const possesive = nickname.at(nickname.length - 1) === "s" ? "'" : "'s" - const embed = new EmbedBuilder() - .setAuthor({name: `${nickname}${possesive} AI roast`, iconURL: user.user.avatarURL({ dynamic: true, size: 4096 })}) - .setColor(15780145) - .setDescription(result); - sendText.embed = embed; - break; default: sendText.text = `${args[0]} is not a valid subcommand.\nSee \`${prefix}help fm\` for more info.`; break; @@ -87,16 +41,14 @@ module.exports = { if(sendText.embed != null){ let parse = parseMention(message.author.id, message.guild) let user = message.guild.members.cache.get(parse); - if(!sendText.embed.data.color){ - let roleColor = 15788778; - if (user.roles.color) { - roleColor = user.roles.color.color; - } - sendText.embed.setColor(roleColor); + let roleColor = 15788778; + if (user.roles.color) { + roleColor = user.roles.color.color; } + sendText.embed.setColor(roleColor); message.channel.send({embeds :[sendText.embed]}) }else{ message.channel.send(sendText.text.replaceAll("", prefix)); } } -}; \ No newline at end of file +}; \ No newline at end of file diff --git a/commands/misc/ig.js b/commands/misc/ig.js deleted file mode 100644 index 21268b7..0000000 --- a/commands/misc/ig.js +++ /dev/null @@ -1,28 +0,0 @@ - -module.exports = { - name: 'ig', - description: 'Replaces Instagram links with kkinstagram and deletes the original message', - execute({message, args}) { - const noUrlErr = "You need to provide an Instagram link to use this command." - if(args.length < 1) { - message.channel.send(noUrlErr); - return; - } - let replacedLink = ""; - const regex = /(?<=\/)(instagram)\.com/g - if(args[0].startsWith("https://") || args[0].startsWith("http://")){ - replacedLink = args[0].replace(regex, "kkinstagram.com"); - } - if(replacedLink.length === 0 || args[0] === replacedLink){ - message.channel.send(noUrlErr); - return; - } - message.channel.send(replacedLink); - try{ - message.delete() - }catch(err){ - console.error(`${this.name}: An error occurred while trying to delete the original message.`) - console.error(err); - } - } -}; \ No newline at end of file diff --git a/commands/misc/netload.js b/commands/misc/netload.js new file mode 100644 index 0000000..0420f63 --- /dev/null +++ b/commands/misc/netload.js @@ -0,0 +1,77 @@ +const https = require('https'); +const fs = require('fs') +const netloadDir = "../../netload" +const validUrl = require('valid-url'); + +module.exports = { + name: 'netload', + description: 'Load a module from the internet', + moreHelp: ["Examples:", + "Either provide a link to the module, or upload it", + "To get an example of how your module should look like, do:", + "netload example", + "You have to be whitelisted to use this command.", + "The bot operator also has to have this enabled in the config." + ], + execute({ message, args, prefix, client, owners }) { + if (!fs.existsSync('./data/netmoduleWhitelist.json')) { + fs.writeFileSync('./data/netmoduleWhitelist.json', "[]") + } + let json = fs.readFileSync('./data/netmoduleWhitelist.json', 'utf8'); + let whitelist = JSON.parse(json) + if (json.indexOf(message.author.id.toString()) == -1) { + message.channel.send("You do not have permissions to use this command."); + return; + } + if (args[0] == "whitelist" && owners.indexOf(message.author.id.toString()) >= 0) { + whitelist.push(args[1]) + fs.writeFileSync("./data/netmoduleWhitelist.json", JSON.stringify(whitelist, null, 4)) + return; + } + if (!args[0] && message.attachments.size == 0) { + message.channel.send(`You have to either specify a url or upload a file via the command.\nTo get an example file, execute \`${prefix}netload example\``) + return; + + } if (args[0] == "example") { + + let example = fs.readFileSync('commands/.example') + + message.channel.send(`\`\`\`js\n${example}\n\`\`\``) + return; + } + + + let url, fileName; + if (message.attachments.size > 0) { + url = message.attachments.first().url + fileName = message.attachments.first().name + } else { + url = args[0] + if (!validUrl.isUri(url)) { + message.channel.send("This does not look like a valid url") + return; + } + fileName = args[0].split("/")[args[0].split("/").length - 1] + } + if (fs.existsSync(`${netloadDir}/${fileName}`)) { + message.channel.send(`A module with this filename(${fileName}) already exists.`) + return; + } + https.get(url, (res) => { + res.on('data', (d) => { + fs.writeFileSync(`${netloadDir}/${fileName}`, d); + reloadNetModules(client); + }); + + }).on('error', (e) => { + message.channel.send("Error download file."); + console.log(e) + }); + + let reloadNetModules = require('../util/reloadNetModules'); + reloadNetModules(client) + + + } +}; + diff --git a/commands/misc/ping.js b/commands/misc/ping.js index fe9fb09..6e23a0c 100644 --- a/commands/misc/ping.js +++ b/commands/misc/ping.js @@ -1,11 +1,7 @@ module.exports = { name: 'ping', description: 'Just ping.', - async execute({message, client}) { - let apiLatency = client.ws.ping; - const time = process.hrtime.bigint(); - let initialMessage = await message.channel.send(`Pong.`) - const diff = Number(process.hrtime.bigint() - time) * 1e-6; - initialMessage.edit(`Pong.\nAPI latency: ${apiLatency} ms\nMessage latency: ${diff.toFixed(0)} ms.`); + execute({message, client}) { + message.channel.send(`Pong.\n${client.ws.ping}ms`) } }; \ No newline at end of file diff --git a/commands/misc/prefix.js b/commands/misc/prefix.js index 954c834..19ec7d1 100644 --- a/commands/misc/prefix.js +++ b/commands/misc/prefix.js @@ -1,11 +1,10 @@ const setServerPrefix = require("../../util/setServerPrefix"); -const { PermissionsBitField } = require('discord.js'); module.exports = { name: 'prefix', description: 'Change the prefix of the bot in this server.', execute({ message, client, args, prefix }) { - if (!message.member.permissions.has(PermissionsBitField.Flags.ManageGuild)) { + if (!message.member.permissions.has('MANAGE_GUILD')) { message.channel.send("You do not have sufficient permissions(MANAGE_GUILD) to change the prefix of this server.") return; } diff --git a/commands/misc/say.js b/commands/misc/say.js index 2995f6a..e8ca4e6 100644 --- a/commands/misc/say.js +++ b/commands/misc/say.js @@ -1,7 +1,6 @@ module.exports = { name: 'say', description: 'Repeats arguments', - admin: true, execute({message, args}) { if(args.length == 0) diff --git a/commands/misc/tdoss.js b/commands/misc/tdoss.js deleted file mode 100644 index f5430fe..0000000 --- a/commands/misc/tdoss.js +++ /dev/null @@ -1,100 +0,0 @@ -const { execSync } = require('child_process'); -const fs = require('fs'); -const path = require('path'); -const { writeFile } = require('node:fs/promises') -const { Readable } = require('node:stream') -const executeCommand = require('../../util/executeCommand'); - -module.exports = { - name: 'tdoss', - description: 'Combine picture with tdoss album cover template', - async execute({ message, args }) { - - let tdossDir = path.resolve(process.cwd(), 'data', 'tdoss'); - const tdossTemplate = path.resolve(process.cwd(), 'resources', 'tdoss_template.png'); - - const directory = path.resolve(tdossDir, Math.floor(new Date).toString()) - fs.mkdirSync(directory, {recursive: true}) - - let url = ""; - if(message.attachments.size > 0){ - url = message.attachments.first().url - - - } else if(args[0] && args[0].startsWith("https://") ){ - url = args[0]; - }else if(message.reference){ - let referencedMessage = await message.fetchReference(); - if(referencedMessage.attachments.size > 0){ - url = await referencedMessage.attachments.first().url; - } - else { - message.channel.send("The message you replied to doesn't have any attachments."); - return; - } - } - else { - let lastMessages = await message.channel.messages.fetch({limit: 20, cache: false}); - let lastMessageWithAttachment = lastMessages.find(m => m.attachments.size > 0); - if(!lastMessageWithAttachment){ - message.channel.send("Couldn't find an image to use this command on.") - fs.rmSync(`${directory}`, {recursive: true}) - return - } - url = lastMessageWithAttachment.attachments.first().url; - - } - // TODO: Download with correct extension. - message.channel.sendTyping(); - let downloadResult = await this.downloadImage(url, path.resolve(directory, "input.png")); - if(downloadResult.value != this.ERROR_CODES.SUCCESS){ - if(downloadResult.value === this.ERROR_CODES.FETCH_ERROR){ - message.channel.send(`Failed to download the provided image, got error '${downloadResult.errorMessage}'`); - }else if (downloadResult.value === this.ERROR_CODES.HTTP_ERROR){ - message.channel.send(`Failed to download the provided image, got response status '${downloadResult.errorMessage}'`); - }else if(downloadResult.value === this.ERROR_CODES.NOT_IMAGE){ - message.channel.send(`The provided url was not an image.`) - } - fs.rmSync(`${directory}`, {recursive: true}) - return; - } - - - const commandArgs = [tdossTemplate, "(", `${directory}/input.png`, "-resize", "800x800^", "-gravity", "center", "-extent", "1000x1000", ")", "-compose", "dst-over", "-composite", `${directory}/tdoss_result.png`] - if (executeCommand("magick", commandArgs).error === true) { - message.channel.send("Something went wrong during image manipulation.\nTry again and if it keeps happening, contact the owner of the bot.") - fs.rmSync(`${directory}`, {recursive: true}) - return - } - - let final_image = { - attachment: path.resolve(`${directory}/tdoss_result.png`), - name: "tdoss_result.png" - } - - await message.channel.send({files: [final_image]}) - fs.rmSync(`${directory}`, {recursive: true}) - }, - // https://stackoverflow.com/a/77210219 - async downloadImage(url, path) { - let res; - try { - res = await fetch(url); - } catch (error) { - return {value: this.ERROR_CODES.FETCH_ERROR, errorMessage: error.cause?.message || error.message}; - } - if(!res.ok) return {value: this.ERROR_CODES.HTTP_ERROR, errorMessage: res.status.toString()}; - const contentType = res.headers.get('content-type'); - if(!contentType || !contentType.startsWith("image")) return {value: this.ERROR_CODES.NOT_IMAGE, errorMessage: contentType || "No content-type header"}; - const stream = Readable.fromWeb(res.body) - await writeFile(path, stream); - return {value: this.ERROR_CODES.SUCCESS, errorMessage: ""}; - }, - ERROR_CODES: { - SUCCESS: 0, - HTTP_ERROR: 1, - NOT_IMAGE: 2, - FETCH_ERROR: 3 - } - -} diff --git a/commands/misc/timer.js b/commands/misc/timer.js index 522a9aa..746689b 100644 --- a/commands/misc/timer.js +++ b/commands/misc/timer.js @@ -4,11 +4,11 @@ const parseTime = require('../../util/timer/parseTime'); const showTimer = require('../../util/timer/showTimer'); module.exports = { name: "timer", - description: "Set a timer for a date or time duration.", + description: "Set a timer for a time in minutes.", moreHelp: ["Usage:" ,"`timer [add|create] `" ,"`timer