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/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 index 327e3de..d915114 100644 --- a/commands/admin/whitelist.js +++ b/commands/admin/whitelist.js @@ -2,106 +2,41 @@ const sqlite3 = require('sqlite3').verbose(); module.exports = { name: 'whitelist', - description: 'Whitelist a command in a specific server, or for a user.', + description: 'Whitelist different commands that need whitelisting.', 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 \``); + async execute({message, args, prefix}) { + if(args < 2){ + message.channel.send(`You need to supply the command and channel 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, + let command = args[0]; + let guild = args[1]; + // 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 whitelist ( + serverId, + command, + dateAdded + ) VALUES (?, ?, ?)`, + [ + guild, 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); + new Date().getTime() + ], function (error) { + if (error) { + console.error(error); + err = true; + resolve(); + }else{ + resolve(); } - 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. + }); + }) + if(!err) + message.channel.send("Command has been whitelisted in this server.") + else message.channel.send("Could not whitelist the server with that command. Check the logs.") } - }; \ 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..36ed658 100644 --- a/commands/info/help.js +++ b/commands/info/help.js @@ -13,6 +13,7 @@ module.exports = { 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; @@ -31,6 +32,7 @@ module.exports = { fieldName = fieldName.replace("[[page]]", page); const max_commands = iteration + 10; + if (x) return; const embed = new EmbedBuilder() .setColor(15780145) 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/misc/chat.js b/commands/misc/chat.js new file mode 100644 index 0000000..215890f --- /dev/null +++ b/commands/misc/chat.js @@ -0,0 +1,72 @@ +require("dotenv").config(); +const sqlite3 = require('sqlite3').verbose(); + +module.exports = { + name: 'chat', + description: 'A chat command that uses an LLM to answer your prompts (server must be whitelisted)', + async execute({ message, args }) { + // TODO: Externalize the whitelist checking into a the message function with a variable export. And cache it. + // Have a global collection that gets created on launch from the database, the collection uses the guild ID as a key and has an array of commands that are whitelisted. + // Just write to both the database and this collection when a new command gets whitelisted. + const db = new sqlite3.Database('data/database.db'); + let isWhitelisted = false; + await new Promise((resolve, reject) => { + db.get(`SELECT * FROM whitelist WHERE serverId = ? AND command = ?`, [message.guild.id, this.name], + function (error, row){ + if(error){ + console.error(error); + resolve(""); + }else{ + if(row === undefined){ + resolve(); + }else { + isWhitelisted = true; + resolve(); + } + } + }) + }) + + if(!isWhitelisted){ + message.channel.send("This server is not whitelisted. The bot admin needs to whitelist the server for this command to work."); + return; + } + if(args.length === 0){ + message.channel.send("You have to set your prompt in the arguments"); + return; + } + const prompt = args.join(" "); + let answer = ""; + const initialMessage = await message.channel.send("Generating response... This may take a moment.") + message.channel.sendTyping(); + await fetch(`https://openrouter.ai/api/v1/chat/completions`, { + method: `POST`, + headers: { + "Authorization": `Bearer ${process.env.OPENROUTER_API_KEY}`, + "Content-Type": `application/json` + }, + body: JSON.stringify({ + "model": `deepseek/deepseek-chat-v3-0324:free`, + "messages": [ + { + "role": `user`, + "content": prompt + } + ], + "max_tokens": 100 + }) + }).then(response => response.json()).then(data => { + answer = data.choices[0].message.content; + }).catch(error => { + console.error(error); + }); + if(answer.length > 0){ + if(answer.length > 1999){ + initialMessage.edit("Unfortunately the length of the message was longer than what discord allows.") + } + initialMessage.edit(answer) + }else { + initialMessage.edit("Something went wrong. The owner should check the console.") + } + } +}; \ No newline at end of file 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/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 index f5430fe..2c44fa5 100644 --- a/commands/misc/tdoss.js +++ b/commands/misc/tdoss.js @@ -3,18 +3,16 @@ 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 dataDir = path.resolve(__dirname, '..', '..', 'data'); + const directory = path.resolve(dataDir, Math.floor(new Date).toString()) + fs.mkdirSync(directory) let url = ""; if(message.attachments.size > 0){ @@ -46,22 +44,15 @@ module.exports = { } // 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.`) - } + if(await this.downloadImage(url, path.resolve(directory, "input.png")) != 0){ + message.channel.send("Something went wrong during the download.\nThe link might be unreachable for the bot or it's 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) { + const command = `magick ${dataDir}/tdoss_template.png \\( ${directory}/input.png -resize 800x800^ -gravity center -extent 1000x1000 \\) -compose dst-over -composite ${directory}/tdoss_result.png`; + if (this.executeCommand(command).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 @@ -75,26 +66,30 @@ module.exports = { await message.channel.send({files: [final_image]}) fs.rmSync(`${directory}`, {recursive: true}) }, + + + executeCommand(command) { + console.log("Executing:", command) + try { + const output = execSync(command, { encoding: 'utf-8' }) + if (output.length != 0) + console.log(output) + } catch (error) { + console.error(`Error executing ${command.split(" ")[0]} command:`, error); + return { error: true }; + } + return { error: false }; + }, // 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 res = await fetch(url); + if(!res.ok) return 1; + if(!res.headers.get('content-type').startsWith("image")) 2; 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 + return 0; } } + + 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