diff options
61 files changed, 5171 insertions, 0 deletions
diff --git a/.env-example b/.env-example new file mode 100644 index 0000000..8909c1e --- /dev/null +++ b/.env-example @@ -0,0 +1,4 @@ +TOKEN= +YOUTUBE_API_KEY= +WEEBY_TOKEN= +WEBUI_KEY=
\ No newline at end of file diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..ba32633 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,13 @@ +{ + "env": { + "browser": true, + "commonjs": true, + "es2021": true + }, + "extends": "eslint:recommended", + "parserOptions": { + "ecmaVersion": "latest" + }, + "rules": { + } +} diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..dfe0770 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f2d781a --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +/config.json +/env +/.idea +/.vscode +/package-lock.json + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + + +#ignore configs +events/*.json +commands/*/*.json + +#ignore database (duh) +db/* + +#aaaaand any client secrets +.env +log
\ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..d411adf --- /dev/null +++ b/.prettierrc @@ -0,0 +1,6 @@ +{ + "printWidth": 110, + "tabWidth": 2, + "useTabs": false, + "trailingComma": "none" +} @@ -0,0 +1,2 @@ +language = "nodejs" +run = "npm start"
\ No newline at end of file diff --git a/app.json b/app.json new file mode 100644 index 0000000..7e1617f --- /dev/null +++ b/app.json @@ -0,0 +1,7 @@ +{ + "name": "SNS Chan", + "description": "Supernoobs Custom Bot", + "repository": "https://github.com/ryzyx/sns-chan", + "logo": "https://i.imgur.com/JFxgbWH.png", + "keywords": ["discord", "discordjs", "music", "bot"] +} diff --git a/buildconfig.sh b/buildconfig.sh new file mode 100644 index 0000000..6d48fa3 --- /dev/null +++ b/buildconfig.sh @@ -0,0 +1,9 @@ +for file in events/defaults/*; do + cp "$file" events/ +done + +for folder in commands/*; do + for file in "$folder"/defaults/*; do + cp "$file" $folder/ + done +done
\ No newline at end of file diff --git a/commands/games/battleship.js b/commands/games/battleship.js new file mode 100644 index 0000000..3c32cd5 --- /dev/null +++ b/commands/games/battleship.js @@ -0,0 +1,200 @@ +const fs = require('fs') +const path = require("path"); +let config_loc = __filename+".json" +const { PermissionsBitField, EmbedBuilder, AttachmentBuilder,ActionRowBuilder,ButtonBuilder,ButtonStyle } = require('discord.js'); +let config = JSON.parse(fs.readFileSync(config_loc)) +const util = require("../../src/util.js") +const sharp = require("sharp"); +const db = require("../../src/db.js") +module.exports = { + name : "battleship", + command: ["battleship"], + mod_only: true, + config:config, + config_loc:config_loc, + async main (client,Discord,message,args){ + let uid2 = args[1]; + if(uid2[0]=='<') uid2 = (args[1].substring(2,args[1].length-1)) + this.exec(client,{action:args[0],message:message,user2:uid2}) + }, + async board_prev(args){ + const move_y = 82 + const move_x = 66 + let pos = {x:0,rx:32,y:0,ry:14,rotated:false} + let max = {x:8,y:7} + let embed = new EmbedBuilder() + .setTitle("battleship") + .setImage("attachment://battleship-board.png") + + const left = new ButtonBuilder() + .setCustomId('left') + .setEmoji('⬅️') + .setStyle(ButtonStyle.Primary); + const up = new ButtonBuilder() + .setCustomId('up') + .setEmoji('⬆️') + .setStyle(ButtonStyle.Primary); + const down = new ButtonBuilder() + .setCustomId('down') + .setEmoji('⬇️') + .setStyle(ButtonStyle.Primary); + const right = new ButtonBuilder() + .setCustomId('right') + .setEmoji('➡️') + .setStyle(ButtonStyle.Primary); + const turn = new ButtonBuilder() + .setCustomId('turn') + .setEmoji('🔄') + .setStyle(ButtonStyle.Primary); + const row1 = new ActionRowBuilder() + .addComponents(left,up,down,right,turn); + + const shoot = new ButtonBuilder() + .setCustomId('shoot') + .setEmoji('🎯') + .setStyle(ButtonStyle.Primary); + const row2 = new ActionRowBuilder() + .addComponents(shoot); + //! TODO: randomize file name + let to_add = [{ + input: "./img/battleship-board-sel.png", + top: pos.rx, + left: pos.ry, + },] + if(args.len!=null){ + for(let i = 1; i<args.len; i++){ + to_add.push({ + input: "./img/battleship-board-sel.png", + top: pos.rotated?pos.rx:pos.rx+(move_x*i), + left: !pos.rotated?pos.ry:pos.ry+(move_y*i), + }) + } + } + args.len ??= 0; + await sharp("./img/battleship-board.png") + .composite(to_add).toFile("/tmp/battleship-board.png"); + const file = new AttachmentBuilder('/tmp/battleship-board.png'); + let mess = args.mm; + if(args.mm==null) + mess = await args.message.reply({embeds:[embed],files:[file],components:[row1,row2]}); + else mess = await args.mm.edit({embeds:[embed],files:[file],components:[row1,row2]}); + args.mm = mess; + async function rec_edit(mess){ + const collectorFilter = i => i.user.id === args.message.author.id; + try { + const confirmation = await mess.awaitMessageComponent({ filter: collectorFilter, time: 60000000 }); + let oldpos = util.deepCopy(pos); + if(confirmation.customId == "down"){ + pos.x--; + pos.rx+=move_x; + } else if(confirmation.customId == "up"){ + pos.x++; + pos.rx-=move_x; + } else if(confirmation.customId == "right"){ + pos.y++; + pos.ry+=move_y; + } else if(confirmation.customId == "left"){ + pos.y--; + pos.ry-=move_y; + + } else if(confirmation.customId == "shoot"){ + //await mess.delete() + pos.mm = mess; + await confirmation.deferUpdate() + return pos; + } else if(confirmation.customId == "turn"){ + pos.rotated = !pos.rotated + } + + let outofbounds = false + if(!pos.rotated||args.len==0){ + outofbounds ||= (args.len!=0&&-pos.x+args.len>max.x); + outofbounds ||= (args.len==0&&-pos.x>=max.x); + outofbounds ||= (pos.y>=max.y); + } else { + outofbounds ||= (args.len!=0&&pos.y+args.len>max.y); + //outofbounds ||= (args.len==0&&pos.y>max.y); + outofbounds ||= (-pos.x>=max.x); + } + outofbounds ||= (pos.x==1||pos.y==-1); + + if(outofbounds){ + pos = oldpos; + await confirmation.deferUpdate() + return rec_edit(mess) + } + let to_add = [{ + input: "./img/battleship-board-sel.png", + top: pos.rx, + left: pos.ry, + }, + ] + if(args.len!=null&&args.len!=0){ + for(let i = 1; i<args.len; i++){ + to_add.push({ + input: "./img/battleship-board-sel.png", + top: pos.rotated?pos.rx:pos.rx+(move_x*i), + left: !pos.rotated?pos.ry:pos.ry+(move_y*i), + }) + } + } + await sharp("./img/battleship-board.png") + .composite(to_add).toFile("/tmp/battleship-board.png"); + const file = new AttachmentBuilder('/tmp/battleship-board.png'); + await mess.edit({embeds:[embed],files:[file], components: [row1,row2]}); + await confirmation.deferUpdate() + return rec_edit(mess) + + } catch (e) { + console.log(e) + } + } + return await rec_edit(mess) + }, + async exec(client,args){ + switch(args.action){ + case 'aim': + + break; + case 'create': + let planned = [4,3,2,1] + let placements = [] + let newboard = [] + for(let i = 0; i!=8; i++){ + newboard.push([]) + for(let z = 0; z!=7; z++) + newboard[i].push('.'); + } + + args.rotated = false; + let temp; + for(let l of planned){ + args.len = l + temp = await this.board_prev(args) + //console.log(temp) + temp.len = l; + placements.push(temp) + } + await temp.mm.edit({embeds:[],files:[],components:[],content:"wowa"}) + for(let p of placements){ + if(p.rotated){ + for(let i = 0; i!=p.len; i++) + newboard[-p.x][p.y+i] = 'b' + } else { + for(let i = 0; i!=p.len; i++) + newboard[-p.x+i][p.y] = 'b' + } + } + + db.BattleShip.create({ + turn:0, + p1_id:args.message.author.id, + p2_id:args.user2, + p1_board:JSON.stringify(newboard), + p2_board:'null', + }) + + break; + } + } +}
\ No newline at end of file diff --git a/commands/games/defaults/battleship.js.json b/commands/games/defaults/battleship.js.json new file mode 100644 index 0000000..fdf0fb6 --- /dev/null +++ b/commands/games/defaults/battleship.js.json @@ -0,0 +1,2 @@ +{"cooldown":3,"desc":"Play battleship!","restrict":[],"restricted":[], +"usage":"{command}"}
\ No newline at end of file diff --git a/commands/mod/autoreact.js b/commands/mod/autoreact.js new file mode 100644 index 0000000..229116b --- /dev/null +++ b/commands/mod/autoreact.js @@ -0,0 +1,57 @@ +const fs = require('fs') +const path = require("path"); +const db = require("../../src/db") +let config_loc = __filename+".json" +let config = JSON.parse(fs.readFileSync(config_loc)) +const {upload_limit} = require("../../src/util") +module.exports = { + name : "autoreact", + command: ["react"], + mod_only: true, + config:config, + config_loc:config_loc, + async main (client,Discord,message,args){ + switch(args[0]){ + case 'add': + case 'a': + if(args.length<3){ + message.reply("not enough parameters, try `sns help react`") + break; + } + db.Auto_React.create({channel:args[1],emote:args[2]}); + message.react("✅") + break; + case 'rem': + case 'remove': + case 'r': + if(args.length<2){ + message.reply("not enough parameters, try `sns help react`") + break; + } + let initial = await db.Auto_React.count() + await db.Auto_React.destroy({ + where: { + channel: args[1] + } + }); + message.reply("removed "+(initial-(await db.Auto_React.count()))+" item(s)") + break; + case 'dump': + case 'list': + let list = await db.Auto_React.findAll() + let flist = "channel : emote" + for(let l of list) + flist+=l.channel+" : "+l.emote+"\n" + let filename = "/tmp/autoreact.json" + fs.writeFileSync(filename,flist) + let stats = fs.statSync(filename) + if(stats.size / (1024*1024) > upload_limit(message.guild)) + return message.reply("file too large:( file is "+stats.size / (1024*1024)+"mb") + message.reply({files:[filename]}) + break; + default: + message.reply("unknown action, try `sns help react`") + } + + }, +}
\ No newline at end of file diff --git a/commands/mod/defaults/autoreact.js.json b/commands/mod/defaults/autoreact.js.json new file mode 100644 index 0000000..7f976dc --- /dev/null +++ b/commands/mod/defaults/autoreact.js.json @@ -0,0 +1,2 @@ +{"cooldown":-1,"desc":"React to every new post in channel:3","restrict":[],"restricted":[], +"usage":"{command} {add {channel} {emote}|rem {channel}|dump}"}
\ No newline at end of file diff --git a/commands/mod/defaults/log.js.json b/commands/mod/defaults/log.js.json new file mode 100644 index 0000000..a5edd7d --- /dev/null +++ b/commands/mod/defaults/log.js.json @@ -0,0 +1,2 @@ +{"cooldown":-1,"desc":"Print logs","restrict":[],"restricted":[], +"usage":"{command}"}
\ No newline at end of file diff --git a/commands/mod/defaults/motw.js.json b/commands/mod/defaults/motw.js.json new file mode 100644 index 0000000..9412b0b --- /dev/null +++ b/commands/mod/defaults/motw.js.json @@ -0,0 +1,2 @@ +{"cooldown":-1,"desc":"Announce the Member of the Week","restrict":[],"restricted":[], +"usage":"{command} {user} {image}"}
\ No newline at end of file diff --git a/commands/mod/defaults/search.js.json b/commands/mod/defaults/search.js.json new file mode 100644 index 0000000..0ef94c9 --- /dev/null +++ b/commands/mod/defaults/search.js.json @@ -0,0 +1,2 @@ +{"cooldown":-1,"desc":"Search for a username/nickname","restrict":[],"restricted":[], +"usage":"{command} {name} [percent (end with %)]"}
\ No newline at end of file diff --git a/commands/mod/defaults/sticky.js.json b/commands/mod/defaults/sticky.js.json new file mode 100644 index 0000000..4717847 --- /dev/null +++ b/commands/mod/defaults/sticky.js.json @@ -0,0 +1,2 @@ +{"cooldown":-1,"desc":"Make a message sticky (i douse it in honey)! Message will always stay at the bottom of the feed","restrict":[],"restricted":[], +"usage":"{command} {add {channel} {message}|rem {channel}|dump} [embed]","embed_color":"#fcc356"}
\ No newline at end of file diff --git a/commands/mod/defaults/ticket.js.json b/commands/mod/defaults/ticket.js.json new file mode 100644 index 0000000..095cdef --- /dev/null +++ b/commands/mod/defaults/ticket.js.json @@ -0,0 +1,2 @@ +{"cooldown":-1,"desc":"Interact with the tickets database, and respond to them.","restrict":[],"restricted":[], +"usage":"{command} {reply {ticket} [message] [attachments]|close {ticket}|dump {ticket}"}
\ No newline at end of file diff --git a/commands/mod/defaults/timed-message.js.json b/commands/mod/defaults/timed-message.js.json new file mode 100644 index 0000000..a330425 --- /dev/null +++ b/commands/mod/defaults/timed-message.js.json @@ -0,0 +1,2 @@ +{"cooldown":-1,"desc":"Send a message per interval:)","restrict":[],"restricted":[], +"usage":"{command} {add {channel} {message} {delay (in s,m,h,d)} |rem {channel}|dump} [embed]","embed_color":"#fcc356"}
\ No newline at end of file diff --git a/commands/mod/defaults/whois.js.json b/commands/mod/defaults/whois.js.json new file mode 100644 index 0000000..ef027ae --- /dev/null +++ b/commands/mod/defaults/whois.js.json @@ -0,0 +1,2 @@ +{"cooldown":-1,"desc":"Get info on user or role","restrict":[],"restricted":[], +"usage":"{command} {userid|roleid|mention}"}
\ No newline at end of file diff --git a/commands/mod/log.js b/commands/mod/log.js new file mode 100644 index 0000000..170f48e --- /dev/null +++ b/commands/mod/log.js @@ -0,0 +1,27 @@ +const Discord = require("discord.js") +const settings = require("../../src/settings") +const fs = require('fs') +const {upload_limit} = require("../../src/util") +const path = require('path') +let config_loc = __filename+".json" +let config = JSON.parse(fs.readFileSync(config_loc)) +module.exports = { + name: "log", + command: ["log"], + mod_only:true, + config:config, + config_loc:config_loc, + main(client,Discord,message,args) { + this.exec(message) + }, + s_main(client,Discord,interaction){ + this.exec(interaction) + }, + exec(message){ + let filename = path.join(__dirname+"../../../log") + let stats = fs.statSync(filename) + if(stats.size / (1024*1024) > upload_limit(message.guild)) + return message.reply("file too large:( file is "+stats.size / (1024*1024)+"mb") + message.reply({files:[filename]}) + } +};
\ No newline at end of file diff --git a/commands/mod/motw.js b/commands/mod/motw.js new file mode 100644 index 0000000..a5984c4 --- /dev/null +++ b/commands/mod/motw.js @@ -0,0 +1,34 @@ +const Discord = require("discord.js") +const { EmbedBuilder } = require("discord.js"); +const { PermissionsBitField } = require('discord.js'); +const settings = require("../../src/settings") +const fs = require('fs') +let config_loc = __filename+".json" +let config = JSON.parse(fs.readFileSync(config_loc)) +module.exports = { + name: "motw", + command: ["motw"], + mod_only:true, + config:config, + config_loc:config_loc, + main(client,Discord,message,args) { + var cmd = message.content.slice(9).split(/ +/g); + var name = cmd.shift(); + var image = cmd.join(" "); + this.exec(client,{message:message,name:name,image:image}) + }, + s_options:[{type:"user",name:"user",desc:"member of the week!",required:true,autocomplete:false}, + {type:"string",name:"image",desc:"your fancy motw image:3",required:true,autocomplete:false}], + s_main(client,Discord,interaction){ + this.exec(client,{name:"<@!"+interaction.options.getUser("user").id+">",image:interaction.options.getString("image"),message:interaction}) + interaction.reply({content:"all done:3",ephemeral: true}) + }, + exec(client,param){ + const motw = new EmbedBuilder() + .setTitle("Member of the Week") + .setColor(settings.defaultColor) + .setDescription(param.name) + .setImage(param.image); + param.message.channel.send({ embeds: [motw] }); + } +};
\ No newline at end of file diff --git a/commands/mod/search.js b/commands/mod/search.js new file mode 100644 index 0000000..e81c3c8 --- /dev/null +++ b/commands/mod/search.js @@ -0,0 +1,70 @@ +const Discord = require("discord.js") +const { EmbedBuilder } = require("discord.js"); +const { PermissionsBitField } = require('discord.js'); +const settings = require("../../src/settings") +const {similarity} = require("../../src/util") +const fs = require('fs') +let config_loc = __filename+".json" +let config = JSON.parse(fs.readFileSync(config_loc)) +module.exports = { + name: "search", + command: ["search"], + mod_only:true, + config:config, + config_loc:config_loc, + async main(client,Discord,message,args) { + let per = 60; + for(let a of args){ + if(a.length>=2&&a[a.length-1]=="%"&&a[a.length-2]!="%"){ + a[a.length-1]="" + per = parseInt(a) + args.splice(args.indexOf(a), 1); + } + } + let dec = per/100 + let mem = Object.fromEntries(await message.guild.members.fetch()) + let found = [] + let count = 0 + for(let k of Object.keys(mem)){ + let s = similarity(mem[k].user.username.toLowerCase(),args[0].toLowerCase()) + if(mem[k].nickname!=null){ + s=Math.max(s,similarity(mem[k].nickname.toLowerCase(),args[0].toLowerCase())) + } + if(s>dec){ + found.push({sim:s,id:mem[k].id}) + count++; + } + } + if(count==0) + return message.reply("no usernames/nicknames found (within a "+per+"% similarity)") + for(let i = 0; i<count-1; i++){ + if(i<0)i=0 + if(found[i+1].sim>found[i].sim){ + let t = found[i+1] + found[i+1] = found[i] + found[i] = t + i-=2; + } + } + let p_found = "" + for(let i = 0; i!=count; i++){ + let new_m = "(<@"+found[i].id+"> @ " + Math.floor(found[i].sim * 100) + "%) " + if((p_found+new_m).length>1024-3){ + p_found+="..." + break; + } + p_found+=new_m + } + message.channel.send("loading ids").then(async(m)=>{ + await m.edit(p_found) + m.delete() + }) + let embed = new EmbedBuilder() + .setTitle("Usernames close to '"+args[0]+"' (≥"+per+"%)") + .setDescription(p_found) + .setColor(settings.defaultColor) + .setFooter({text:count+' user(s) found • Up to '+Math.floor(found[0].sim * 100) + '% similarity'}) + message.channel.send({embeds:[embed]}) + + } +};
\ No newline at end of file diff --git a/commands/mod/sticky.js b/commands/mod/sticky.js new file mode 100644 index 0000000..b3dc055 --- /dev/null +++ b/commands/mod/sticky.js @@ -0,0 +1,57 @@ +const fs = require('fs') +const path = require("path"); +const db = require("../../src/db") +let config_loc = __filename+".json" +let config = JSON.parse(fs.readFileSync(config_loc)) +const {upload_limit} = require("../../src/util") +module.exports = { + name : "sticky", + command: ["sticky"], + mod_only: true, + config:config, + config_loc:config_loc, + async main (client,Discord,message,args){ + switch(args[0]){ + case 'add': + case 'a': + if(args.length<3){ + message.reply("not enough parameters, try `sns help sticky`") + break; + } + db.Sticky.create({channel:args[1],message:args[2],last_message:'null',embed:args.includes("embed"),embed_color:config.embed_color}); + message.react("✅") + break; + case 'rem': + case 'remove': + case 'r': + if(args.length<2){ + message.reply("not enough parameters, try `sns help sticky`") + break; + } + let initial = await db.Sticky.count() + await db.Sticky.destroy({ + where: { + channel: args[1] + } + }); + message.reply("removed "+(initial-(await db.Sticky.count()))+" item(s)") + break; + case 'dump': + case 'list': + let list = await db.Sticky.findAll() + let flist = "channelid : message : last message\n" + for(let l of list) + flist+=l.channel+" : "+l.message+" : "+l.last_message+"\n" + let filename = "/tmp/sticky.json" + fs.writeFileSync(filename,flist) + let stats = fs.statSync(filename) + if(stats.size / (1024*1024) > upload_limit(message.guild)) + return message.reply("file too large:( file is "+stats.size / (1024*1024)+"mb") + message.reply({files:[filename]}) + break; + default: + message.reply("unknown action, try `sns help sticky`") + } + + }, +}
\ No newline at end of file diff --git a/commands/mod/ticket.js b/commands/mod/ticket.js new file mode 100644 index 0000000..5e69ed7 --- /dev/null +++ b/commands/mod/ticket.js @@ -0,0 +1,96 @@ +const Discord = require("discord.js") +const { EmbedBuilder } = require("discord.js"); +const { PermissionsBitField, MessageAttachment } = require('discord.js'); +const {upload_limit} = require("../../src/util") +const settings = require("../../src/settings") +const fs = require('fs'); +let db = require("../../src/db") +let config_loc = __filename+".json" +let config = JSON.parse(fs.readFileSync(config_loc)) +module.exports = { + name: "ticket", + command: ["ticket"], + mod_only:true, + config:config, + config_loc:config_loc, + async main(client,Discord,message,args) { + let matt = Array.from(message.attachments, ([name, value]) => ({ name, value })) + + let action = args[0]; + let ticket_id = args[1]; + args.shift();args.shift(); + let r_message = args.join(' ') + + this.exec(client,{att:matt,action:action,ticket_id:ticket_id,r_message:r_message,message:message}) + }, + s_options:[{type:"string",name:"ticket",desc:"ticket id",required:true,autocomplete: async function(){ let tik = await db.Tickets.findAll({attributes:['ticket'],where:{status:'open'}}); let out = []; for(let t of tik) out.push(t.ticket); return out; }}, + {type:"string",name:"action",desc:"operation to perform",required:true,autocomplete:["reply","close","dump"]}, + {type:"string",name:"message",desc:"message to reply with",required:false,autocomplete:false}, + {type:"attachment",name:"attachment",desc:"attachment to reply with",required:false,autocomplete:false}], + async s_main(client,Discord,interaction){ + this.exec(client,{message:interaction,att:[interaction.options.getAttachment("attachment")] ?? [],action:interaction.options.getString("action"), + ticket_id:interaction.options.getString("ticket"),r_message:interaction.options.getString("message")}) + }, + async exec(client,param){ + let tickets; + let date = new Date().toLocaleString() + switch(param.action){ + case 'reply': + case 'respond': + case 'r': + if(param.r_message==null&¶m.att==null) + return param.message.reply("you must provide a message or attachment") + tickets = await db.Tickets.findAll({where:{status:'open',ticket:param.ticket_id}}) + if(tickets.length==0){ + tickets = await db.Tickets.findAll({where:{ticket:param.ticket_id}}) + if(tickets.length==0) + return param.message.reply("ticket not found:c") + return param.message.reply("ticket is not open") + } + let ticket = tickets[0] + let messages = JSON.parse(ticket.messages) + messages.push({message:param.r_message,attachments:param.att,mod:true}) + db.Tickets.update({'messages':JSON.stringify(messages)},{where:{id:ticket.id}}) + + const embed = new EmbedBuilder() + .setTitle("Updated Ticket | "+ticket.ticket) + .setColor(settings.defaultColor) + .setDescription("Message: '"+param.r_message+"'") + .setFooter({text:"Created: "+date}) + let smatt = [] + for(let att of param.att){ + if(att!=null) + smatt.push({attachment:att?.value?.url ?? att?.url}) + } + param.message.client.users.cache.get(ticket.author).send({embeds:[embed],files:smatt}) + param.message.reply("updated ticket "+ticket.ticket); + break; + case 'close': + tickets = await db.Tickets.findAll({where:{status:'open',ticket:param.ticket_id}}) + if(tickets.length==0){ + tickets = await db.Tickets.findAll({where:{ticket:param.ticket_id}}) + if(ticket.length==0) + return param.message.reply("ticket not found:c") + return param.message.reply("ticket is already closed") + } + db.Tickets.update({'status':'closed'},{where:{id:tickets[0].id}}) + param.message.reply("closed ticket "+tickets[0].ticket); + break; + case 'dump': + case 'list': + tickets = await db.Tickets.findAll({where:{ticket:param.ticket_id}}) + if(tickets.length==0){ + return param.message.reply("ticket not found:c") + } + let filename = "/tmp/"+tickets[0].ticket+".json" + fs.writeFileSync(filename,tickets[0].messages) + let stats = fs.statSync(filename) + if(stats.size / (1024*1024) > upload_limit(param.message.guild)) + return param.message.reply("file too large:( file is "+stats.size / (1024*1024)+"mb") + param.message.reply({files:[filename]}) + break; + default: + param.message.reply("unknown action, try `sns help ticket`") + } + } +};
\ No newline at end of file diff --git a/commands/mod/timed-message.js b/commands/mod/timed-message.js new file mode 100644 index 0000000..35789da --- /dev/null +++ b/commands/mod/timed-message.js @@ -0,0 +1,76 @@ +const fs = require('fs') +const path = require("path"); +const db = require("../../src/db") +let config_loc = __filename+".json" +let config = JSON.parse(fs.readFileSync(config_loc)) +const {upload_limit} = require("../../src/util") +let valid_times = {"s":1000,"m":60000,"h":3.6e+6,"d":8.64e+7} +module.exports = { + name : "timed message", + command: ["timed"], + mod_only: true, + config:config, + config_loc:config_loc, + async main (client,Discord,message,args){ + switch(args[0]){ + case 'add': + case 'a': + if(args.length<3){ + message.reply("not enough parameters, try `sns help timed`") + break; + } + let del_ind = args.indexOf("delay") + if(del_ind==-1||del_ind==args.length-1){ + message.reply("invalid usage! i need a delay") + break; + } + if(!Object.keys(valid_times).includes(args[del_ind+1][args[del_ind+1].length-1])){ + message.reply("please end your delay with either s,m,h, or d") + break; + } + let time = args[del_ind+1] + let ind = time[time.length-1] + time[time.length-1] = ''; + time = valid_times[ind] * parseInt(time) + if(time==NaN){ + message.reply("invalid delay") + break; + } + db.Timed_Message.create({channel:args[1],message:args[2],last_message:'null',last_message_time:'null',embed:args.includes("embed"),embed_color:config.embed_color,delay:time,guild:message.guild.id}) + //db.Sticky.create({channel:args[1],message:args[2],last_message:'null',embed:args.includes("embed"),embed_color:config.embed_color}); + message.react("✅") + break; + case 'rem': + case 'remove': + case 'r': + if(args.length<2){ + message.reply("not enough parameters, try `sns help timed`") + break; + } + let initial = await db.Timed_Message.count() + await db.Timed_Message.destroy({ + where: { + channel: args[1] + } + }); + message.reply("removed "+(initial-(await db.Timed_Message.count()))+" item(s)") + break; + case 'dump': + case 'list': + let list = await db.Timed_Message.findAll() + let flist = "channelid : message : last message : last message time : delay : color\n" + for(let l of list) + flist+=l.channel+" : "+l.message+" : "+l.last_message+" : "+l.last_message_time+" : " + l.delay + " : " + l.embed_color + "\n" + let filename = "/tmp/timed.json" + fs.writeFileSync(filename,flist) + let stats = fs.statSync(filename) + if(stats.size / (1024*1024) > upload_limit(message.guild)) + return message.reply("file too large:( file is "+stats.size / (1024*1024)+"mb") + message.reply({files:[filename]}) + break; + default: + message.reply("unknown action, try `sns help timed`") + } + + }, +}
\ No newline at end of file diff --git a/commands/mod/whois.js b/commands/mod/whois.js new file mode 100644 index 0000000..56d2645 --- /dev/null +++ b/commands/mod/whois.js @@ -0,0 +1,120 @@ +const Discord = require("discord.js") +const settings = require("../../src/settings") +const { EmbedBuilder, PermissionsBitField } = require("discord.js"); +const fs = require('fs') +const {upload_limit} = require("../../src/util") +const path = require('path') +let config_loc = __filename+".json" +let config = JSON.parse(fs.readFileSync(config_loc)) +module.exports = { + name: "whois", + command: ["whois","getuser","getrole"], + mod_only:true, + config:config, + config_loc:config_loc, + async main(client,Discord,message,args) { + if(args.length==0) + return message.reply("bad usage, try `sns help whois`") + await message.guild.members.fetch() + + let error = "" + let users_m = Object.fromEntries(message.mentions.users) + for(let k of Object.keys(users_m)){ + let user = message.guild.members.cache.get(users_m[k].id) + if(user==null) + error+=("user "+users_m[k].id+" not found\n") + else + this.p_user(client,Discord,message,user) + } + let roles_m = Object.fromEntries(message.mentions.roles) + for(let k of Object.keys(roles_m)){ + let role = message.guild.roles.cache.get(roles_m[k].id) + if(role==null) + error+=("role "+roles_m[k].id+" not found\n") + else + this.p_role(client,Discord,message,role) + } + for(let a of args){ + if(a[0]!='<'){ + let role = message.guild.roles.cache.get(a) + let user = message.guild.members.cache.get(a) + if(role!=null) + this.p_role(client,Discord,message,role) + else if(user!=null) + this.p_user(client,Discord,message,user) + else + error+=("user or role "+a+" not found\n") + } + } + if(error!="") + message.reply(error) + //this.p_user(client,Discord,message,user) + }, + s_options:[{type:"role",name:"role",desc:"roles to get info on",required:false,autocomplete:false}, + {type:"user",name:"user",desc:"users to get info on",required:false,autocomplete:false}], + async s_main(client,Discord,interaction){ + let role = interaction.options.getRole("role") + let user = interaction.options.getUser("user") + if(role==null&&user==null) + return interaction.reply({content:"select atleast one option",ephemeral: true}) + if(role!=null) + this.p_role(client,Discord,interaction,role) + if(user!=null){ + await interaction.guild.members.fetch() + user = interaction.guild.members.cache.get(user.id) + this.p_user(client,Discord,interaction,user) + } + }, + p_user(client,Discord,message,user){ + let join = new Date(parseInt(user.joinedTimestamp / 1000, 10)*1000).toLocaleDateString(undefined,{ weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }); + let created = new Date(parseInt(user.user.createdAt / 1000, 10)*1000).toLocaleDateString(undefined,{ weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }); + let mod = user.permissions!=null&&user.permissions?.has(PermissionsBitField.Flags.KickMembers) + let roles = "" + //console.log(message.mentions.users) + message.channel.send("loading ids").then(async(m)=>{ + await m.edit("<@!"+user.id+">") + m.delete() + }) + for(let r of user._roles) + roles+="<@&"+r+"> " + const embed = new EmbedBuilder() + .setAuthor({name:user.user.username,iconURL:user.displayAvatarURL()}) + .setThumbnail(user.displayAvatarURL()) + .setDescription("<@!"+user.id+">"+(mod?" +able to use mod commands":"")) + .addFields([{ name: 'Joined', value:join , inline: true }, + { name: 'Created', value:created , inline: true }, + { name: 'Roles ['+user._roles.length+']', value:roles }]) + .setFooter({text:"id:"+user.id}) + .setColor(settings.defaultColor) + message.reply({embeds:[embed]}) + }, + async p_role(client,Discord,message,role){ + let members = " " + //console.log(message.guild.members) + let members_m = Object.fromEntries(message.guild.roles.cache.get(role.id).members) + let members_a = [] + for(let k of Object.keys(members_m)){ + let new_m = "<@"+members_m[k].id+"> " + if((members+new_m).length>1024-3){ + members+="..." + break; + } + members+=new_m + } + if(members!=" "){ + message.channel.send("loading ids").then(async(m)=>{ + await m.edit(members) + m.delete() + }) + } + let mod = role.permissions!=null&&role.permissions?.has(PermissionsBitField.Flags.KickMembers) + const embed = new EmbedBuilder() + .setTitle(role.name) + .setDescription("<@&"+role.id+">"+(mod?" +able to use mod commands":"")) + .addFields([{ name: 'Mentionable', value:(role.mentionable?"true":"false"), inline: true }, + {name: 'Users ['+role.members.size+"]",value:members}]) + .setFooter({text:"id:"+role.id}) + .setColor(settings.defaultColor) + message.reply({embeds:[embed]}) + } +}; diff --git a/commands/util/defaults/emote.js.json b/commands/util/defaults/emote.js.json new file mode 100644 index 0000000..9dd461b --- /dev/null +++ b/commands/util/defaults/emote.js.json @@ -0,0 +1,45 @@ +{"avaliable_multi":{"value":{ + "hug":"hugged", + "bite":"bit", + "boop":"booped", + "bonk":"bonked", + "cuddle":"cuddled", + "dance":"danced with", + "feed":"fed", + "handhold":"held hands with", + "highfive":"highfived", + "kiss":"kissed", + "lick":"licked", + "love":"loved", + "nom":"nommed", + "nuzzle":"nuzzled", + "pat":"pat", + "poke":"poked", + "punch":"punched", + "slap":"slapped", + "stare":"stared down", + "throw":"threw", + "tickle":"tickled" +}},"avaliable_solo":{"value":{ + "bath":"bathed", + "boom":"exploded", + "blush":"blushed", + "clap":"clapped", + "cookie":"had a cookie", + "cringe":"cringed", + "cry":"cried", + "dab":"dabbed", + "facepalm":"facepalmed", + "grin":"grinned", + "laugh":"laughed", + "lurk":"lurked", + "panic":"panicked", + "pout":"pouted", + "sip":"sipped", + "think":"thought", + "run":"ran", + "triggered":"triggered", + "wag":"wagged", + "wink":"winked" +}},"cooldown":3600,"desc":"Sends a gif","restrict":[],"restricted":[], +"usage":"{command} [user]"}
\ No newline at end of file diff --git a/commands/util/defaults/help.js.json b/commands/util/defaults/help.js.json new file mode 100644 index 0000000..8908911 --- /dev/null +++ b/commands/util/defaults/help.js.json @@ -0,0 +1,2 @@ +{"cooldown":3,"desc":"Lists commands and info on them!","restrict":["695872127487967233"],"restricted":[], +"usage":"{command} [command]"}
\ No newline at end of file diff --git a/commands/util/defaults/poll.js.json b/commands/util/defaults/poll.js.json new file mode 100644 index 0000000..b45d084 --- /dev/null +++ b/commands/util/defaults/poll.js.json @@ -0,0 +1,2 @@ +{"cooldown":3,"desc":"Create a poll","restrict":[],"restricted":[], +"usage":"{command} {title} [option,.. (up to 10)]"}
\ No newline at end of file diff --git a/commands/util/defaults/say.js.json b/commands/util/defaults/say.js.json new file mode 100644 index 0000000..4247fd2 --- /dev/null +++ b/commands/util/defaults/say.js.json @@ -0,0 +1,2 @@ +{"cooldown":-1,"desc":"Make me say something","restrict":[],"restricted":[], +"usage":"{command} [channel] {message}"}
\ No newline at end of file diff --git a/commands/util/defaults/status.js.json b/commands/util/defaults/status.js.json new file mode 100644 index 0000000..53080f3 --- /dev/null +++ b/commands/util/defaults/status.js.json @@ -0,0 +1,2 @@ +{"cooldown":3,"desc":"Lists server status for snschan","restrict":[],"restricted":[], +"usage":"{command}"}
\ No newline at end of file diff --git a/commands/util/emote.js b/commands/util/emote.js new file mode 100644 index 0000000..b06bbef --- /dev/null +++ b/commands/util/emote.js @@ -0,0 +1,47 @@ +const fs = require('fs') +const path = require("path"); +const WeebyAPI = require("weeby-js"); +let settings = require("../../src/settings") +const { EmbedBuilder } = require("discord.js"); +let config_loc = __filename+".json" +let config = JSON.parse(fs.readFileSync(config_loc)) +let subs = [...Object.keys(config.avaliable_solo.value),...Object.keys(config.avaliable_multi.value)] +module.exports = { + name : "emote", + command: subs, + mod_only: false, + config:config, + config_loc:config_loc, + async main (client,Discord,message,args,call){ + let mentioned = message.mentions.users.first(); + this.exec(client,{message:message,emote:call,mentioned:mentioned}) + }, + s_options:[{type:"string",name:"emote",desc:"emote to send",required:true,autocomplete:subs}, + {type:"user",name:"user",desc:"user to emote to (may be optional)",required:false,autocomplete:false}], + async s_main (client,Discord,interaction){ + this.exec(client,{message:interaction,emote:interaction.options.getString("emote"),mentioned:interaction.options.getUser("user")}) + }, + async exec(client,param){ + let msg = ""; + if(Object.keys(config.avaliable_multi.value).includes(param.emote)){ + //let user = await client.users.fetch(userc.id).catch(() => null); + if(!param.mentioned) + return param.message.reply({content:"please mention someone", ephemeral: true}) + if(param.mentioned.id==param.message.author.id) + msg="<@"+param.message.author.id+"> "+config.avaliable_multi.value[param.emote]+" themselves" + else if(param.mentioned.id=="762561860150362142") + msg="<@"+param.message.author.id+"> "+config.avaliable_multi.value[param.emote]+" me!" + else + msg="<@"+param.message.author.id+"> "+config.avaliable_multi.value[param.emote]+" <@"+param.mentioned+">" + } else { + msg="<@"+param.message.author.id+"> "+config.avaliable_solo.value[param.emote] + } + const weeby = new WeebyAPI(client.env.WEEBY_TOKEN); + let gif = await weeby.gif.fetch(param.emote); + let emoteembed = new EmbedBuilder() + .setDescription(msg) + .setImage(gif) + .setColor(settings.defaultColor) + param.message.reply({embeds:[emoteembed]}) + } +}
\ No newline at end of file diff --git a/commands/util/help.js b/commands/util/help.js new file mode 100644 index 0000000..b52ee12 --- /dev/null +++ b/commands/util/help.js @@ -0,0 +1,140 @@ +const fs = require('fs') +const path = require("path"); +const { EmbedBuilder,ActionRowBuilder,ButtonBuilder,ButtonStyle } = require("discord.js"); +let config_loc = __filename+".json" +let settings = require("../../src/settings") +let config = JSON.parse(fs.readFileSync(config_loc)) + +//get command names +let command_names = ["help"] +fs.readdirSync(path.join(__dirname+"/..")).forEach(folder => { + fs.readdirSync(path.join(__dirname+"/../"+folder)).forEach(file => { + if(path.extname(file)==".js"){ + if(path.join(__dirname+"/../"+folder+"/"+file)!=__filename){ + let info = require("../"+folder+"/"+file) + //let commands = info.command.length>3?info.command.slice(0,3).join(",")+"...":info.command.join(",") + command_names.push(info.name) + } + + } + }) +}) +// + +module.exports = { + name : "help", + command: ["help"], + mod_only: false, + config:config, + config_loc:config_loc, + async main (client,Discord,message,args){ + this.exec(client,{message:message,specify:args[0]}) + }, + s_options:[{type:"string",name:"command",desc:"command to be specified",required:false,autocomplete:command_names}], + async s_main(client,Discord,interaction){ + this.exec(client,{message:interaction,specify:interaction.options.getString("command")}) + }, + async exec(client,param){ + if(param.specify!=null){ + let info = null + fs.readdirSync(path.join(__dirname+"/..")).forEach(folder => { + fs.readdirSync(path.join(__dirname+"/../"+folder)).forEach(file => { + if(path.extname(file)==".js"){ + let tinfo = require("../"+folder+"/"+file) + if(tinfo.name==param.specify||tinfo.command.includes(param.specify)) + info=tinfo + } + }) + }) + if(info==null) + return param.message.reply({content:"command not found:c",ephemeral: true}) + let restricted = info.config.restricted.length==0?"undefined":info.config.restricted.join(",") + let restrict = info.config.restrict.length==0?"undefined":info.config.restrict.join(",") + const embed = new EmbedBuilder() + .setTitle(info.name) + .setColor(settings.defaultColor) + .addFields([{ + name:"commands/subcommands", + value:info.command.join(",") + },{ + name:"description", + value: info.config.desc == undefined ? "undefined" : info.config.desc + },{ + name:"usage", + value: info.config.usage == undefined ? "undefined" : info.config.usage + },{ + name:"misc", + value:"cooldown: "+info.config.cooldown+", restricted channels: ["+restricted+"], restrict to channels: ["+restrict+"], "+(info.s_main!=null?"supports slash commands":"does not support slash commands") + }]) + param.message.reply({embeds:[embed]}) + return + } + //message.reply("not done yet:( give ans-chan some hugs and headpats for faster development") + let pages = [] + let page = 0 + fs.readdirSync(path.join(__dirname+"/..")).forEach(folder => { + const embed = new EmbedBuilder() + .setTitle(folder) + .setColor(settings.defaultColor) + let count = 0; + fs.readdirSync(path.join(__dirname+"/../"+folder)).forEach(file => { + if(path.extname(file)==".js"){ + count++; + let info = require("../"+folder+"/"+file) + let commands = info.command.length>3?info.command.slice(0,3).join(",")+"...":info.command.join(",") + embed.addFields([{ + name:info.name + " [" + commands + "]", + value:info.config.desc + }]) + /*page.push({ + name:info.name, + subcommands:info.command, + desc:info.desc, + cooldown:cooldown + })*/ + + } + }) + embed.setFooter({text:"use `sns help {command}` to get more info • "+count+" items"}) + pages.push(embed) + }) + const last = new ButtonBuilder() + .setCustomId('last') + .setEmoji('⬅️') + .setStyle(ButtonStyle.Primary) + const next = new ButtonBuilder() + .setCustomId('next') + .setEmoji('➡️') + .setStyle(ButtonStyle.Primary) + const row = new ActionRowBuilder() + .addComponents(last,next); + let mess = await param.message.reply({embeds:[pages[page]],components:[row]}) + async function rec_edit(mess,page){ + const collectorFilter = i => i.user.id === param.message.author.id; + try { + const confirmation = await mess.awaitMessageComponent({ filter: collectorFilter, time: 60000000 }); + if(confirmation.customId == "next"){ + if(page+1<pages.length) + page++; + else + page=0; + mess.edit({embeds:[pages[page]]}) + rec_edit(mess,page) + confirmation.deferUpdate() + } + if(confirmation.customId == "last"){ + if(page-1>=0) + page--; + else + page=pages.length-1; + mess.edit({embeds:[pages[page]]}) + rec_edit(mess,page) + confirmation.deferUpdate() + } + } catch (e) { + + } + } + await rec_edit(mess,page) + } +}
\ No newline at end of file diff --git a/commands/util/poll.js b/commands/util/poll.js new file mode 100644 index 0000000..39d4e92 --- /dev/null +++ b/commands/util/poll.js @@ -0,0 +1,61 @@ +const fs = require('fs') +const path = require("path"); +let config_loc = __filename+".json" +const { PermissionsBitField, EmbedBuilder } = require('discord.js'); +let config = JSON.parse(fs.readFileSync(config_loc)) +const numbers = ["1️⃣" ,"2️⃣", "3️⃣" ,"4️⃣" ,"5️⃣", "6️⃣", "7️⃣", "8️⃣" ,"9️⃣" ,"🔟"] +let settings = require("../../src/settings") +module.exports = { + name : "poll", + command: ["poll"], + mod_only: true, + config:config, + config_loc:config_loc, + async main (client,Discord,message,args){ + let title = args[0]; + args.shift(); + if(args.length>10)return message.reply("too many options") + this.exec(client,{message:message,title:title,options:args}) + }, + s_options:[{type:"string",name:"title",desc:"title of poll",required:true,autocomplete:false}, + {type:"string",name:"option-1",desc:"poll option",required:true,autocomplete:false}, + {type:"string",name:"option-2",desc:"poll option",required:false,autocomplete:false}, + {type:"string",name:"option-3",desc:"poll option",required:false,autocomplete:false}, + {type:"string",name:"option-4",desc:"poll option",required:false,autocomplete:false}, + {type:"string",name:"option-5",desc:"poll option",required:false,autocomplete:false}, + {type:"string",name:"option-6",desc:"poll option",required:false,autocomplete:false}, + {type:"string",name:"option-7",desc:"poll option",required:false,autocomplete:false}, + {type:"string",name:"option-8",desc:"poll option",required:false,autocomplete:false}, + {type:"string",name:"option-9",desc:"poll option",required:false,autocomplete:false}, + {type:"string",name:"option-10",desc:"poll option",required:false,autocomplete:false},], + async s_main (client,Discord,interaction){ + this.exec(client, + {message:interaction,options:[ + interaction.options.getString("option-1"), + interaction.options.getString("option-2") , + interaction.options.getString("option-3") , + interaction.options.getString("option-4") , + interaction.options.getString("option-5") , + interaction.options.getString("option-6") , + interaction.options.getString("option-7") , + interaction.options.getString("option-8") , + interaction.options.getString("option-9") , + interaction.options.getString("option-10") , + ].filter(n => n),title:interaction.options.getString("title")}) + }, + async exec(client,info){ + let embed = new EmbedBuilder() + .setTitle(info.title) + let desc = ""; + for(let a in info.options){ + desc+=numbers[a]+" "+info.options[a]+"\n"; + } + embed.setDescription(desc) + .setColor(settings.defaultColor) + .setFooter({text: 'poll:'+info.options.length+' | '+info.message.author.username}) + let m = await info.message.reply({embeds:[embed],fetchReply: true }) + for(let a in info.options){ + m.react(numbers[a]) + } + } +}
\ No newline at end of file diff --git a/commands/util/say.js b/commands/util/say.js new file mode 100644 index 0000000..17af382 --- /dev/null +++ b/commands/util/say.js @@ -0,0 +1,36 @@ +const fs = require('fs') +const path = require("path"); +let config_loc = __filename+".json" +const { PermissionsBitField } = require('discord.js'); +let config = JSON.parse(fs.readFileSync(config_loc)) +module.exports = { + name : "say", + command: ["say"], + mod_only: true, + config:config, + config_loc:config_loc, + async main (client,Discord,message,args){ + let id = message.mentions.channels.first() + let echo = message.content.slice(8) + if(id){ + echo = echo.split(/ +/g); + echo.shift(); + echo = echo.join(' ') + } else { + id = message.channel + } + this.exec(client,{id:id,echo:echo}) + }, + s_options:[{type:"string",name:"echo",desc:"message to be said",required:true,autocomplete:false}, + {type:"channel",name:"channel",desc:"channel to be sent to",required:false,autocomplete:false}], + async s_main (client,Discord,interaction){ + this.exec(client, + {echo:interaction.options.getString("echo"), + id:interaction.options.getChannel("channel") ?? interaction.channel}) + await interaction.reply({ content:'sent', ephemeral: true }) + interaction.deleteReply() + }, + async exec(client,info){ + return info.id.send(info.echo) + } +}
\ No newline at end of file diff --git a/commands/util/status.js b/commands/util/status.js new file mode 100644 index 0000000..27f1e03 --- /dev/null +++ b/commands/util/status.js @@ -0,0 +1,63 @@ +const fs = require('fs') +const path = require("path"); +let settings = require("../../src/settings") +let {limit_exp} = require("../../src/util") +const { EmbedBuilder,ActionRowBuilder,ButtonBuilder,ButtonStyle } = require("discord.js"); +const {getLastCommit} = require("git-last-commit") +var os = require('os'); +let config_loc = __filename+".json" +let config = JSON.parse(fs.readFileSync(config_loc)) +module.exports = { + name : "status", + command: ["status","server","uptime","vote","twitter","𝕏"], + mod_only: false, + config:config, + config_loc:config_loc, + async main (client,Discord,message,args){ + this.exec(client,message) + }, + async s_main (client,Discord,interaction){ + this.exec(client,interaction) + }, + async exec(client,message){ + getLastCommit((err,commit)=>{ + let seconds = Math.floor(message.client.uptime / 1000); + let minutes = Math.floor(seconds / 60); + let hours = Math.floor(minutes / 60); + let days = Math.floor(hours / 24); + let uptime = "online for: "; + if(days>0)uptime+=days+" days" + else if(hours>0)uptime+=hours+"h" + else if(minutes>0)uptime+=minutes+"m" + else uptime+=seconds+"s" + uptime+=" | "+limit_exp((message.client.uptime*1000).toExponential(),2)+"μs" + const vote = new ButtonBuilder() + .setLabel('Vote') + .setStyle(ButtonStyle.Link) + .setURL('https://top.gg/servers/486957006628847626/vote') + /*const twitter = new ButtonBuilder() //0% chance i will call this 𝕏 + .setLabel('Twitter') + .setStyle(ButtonStyle.Link) + .setURL('https://twitter.com/sns_chan')*/ + const discord = new ButtonBuilder() + .setLabel('Discord') + .setStyle(ButtonStyle.Link) + .setURL('https://discord.gg/supernoobs') + const github = new ButtonBuilder() + .setLabel('Github') + .setStyle(ButtonStyle.Link) + .setURL('https://github.com/iamAGFX/snschan-bot') + const row = new ActionRowBuilder() + .addComponents(vote,discord,github); + let conv = 1024 * 1024 * 1024 + let sys = "system: cpus: "+os.cpus().length+", ram (free): "+Math.floor(os.freemem()/conv)+"/"+Math.floor(os.totalmem()/conv)+"gb\nkernel: "+os.release() + let emoteembed = new EmbedBuilder() + .setThumbnail(client.user.displayAvatarURL()) + .setTitle("Server info") + .setDescription(uptime+"\n"+sys) + .setColor(settings.defaultColor) + .setFooter({text:"running "+commit.shortHash+" ("+commit.branch+")"}) + message.reply({embeds:[emoteembed],components:[row]}) + }) + } +}
\ No newline at end of file diff --git a/events/defaults/dm.js.json b/events/defaults/dm.js.json new file mode 100644 index 0000000..c11cbb9 --- /dev/null +++ b/events/defaults/dm.js.json @@ -0,0 +1,5 @@ +{ + "ticket-id-chars":{"value":"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"}, + "ticket-id-length":{"value":7}, + "ticket-id-split":{"value":3} +}
\ No newline at end of file diff --git a/events/defaults/interaction.js.json b/events/defaults/interaction.js.json new file mode 100644 index 0000000..544b7b4 --- /dev/null +++ b/events/defaults/interaction.js.json @@ -0,0 +1,3 @@ +{ + +}
\ No newline at end of file diff --git a/events/defaults/message.js.json b/events/defaults/message.js.json new file mode 100644 index 0000000..4e863a9 --- /dev/null +++ b/events/defaults/message.js.json @@ -0,0 +1,6 @@ +{ +"restrict-channels":{"value":["825023574221783102","754582601766928456","545076257369358338"]}, +"error-message":{"value":"failed to execute command:("},"error-timeout":{"value":10000}, +"error-message-auth":{"value":"you are not authorized to use this:(("}, +"error-message-not-found":{"value":"i couldnt find that command:( were you talking to me?"}, +"error-message-not-here":{"value":"cant use that here!"}}
\ No newline at end of file diff --git a/events/defaults/reaction-remove.js.json b/events/defaults/reaction-remove.js.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/events/defaults/reaction-remove.js.json @@ -0,0 +1 @@ +{}
\ No newline at end of file diff --git a/events/defaults/ready.js.json b/events/defaults/ready.js.json new file mode 100644 index 0000000..4ee756c --- /dev/null +++ b/events/defaults/ready.js.json @@ -0,0 +1,5 @@ +{ + "status":{"value":"dm to report an issue"}, + "type":{"value":"Playing","options":["Listening","Playing","Streaming","Watching"]}, + "timed_interval":{"value":5000} +}
\ No newline at end of file diff --git a/events/dm.js b/events/dm.js new file mode 100644 index 0000000..dfb7f51 --- /dev/null +++ b/events/dm.js @@ -0,0 +1,104 @@ +const { ActivityType, ButtonBuilder, ButtonStyle, ActionRowBuilder} = require("discord.js"); +const { EmbedBuilder } = require("discord.js"); +const fs = require('fs') +const path = require("path"); +let db = require("../src/db") +let settings = require("../src/settings") +let config_loc = __filename+".json" +module.exports = { + name : "dm", + config_loc : config_loc, + async main (client,Discord,message){ + if(message==null||message.author.bot)return; + let config = JSON.parse(fs.readFileSync(config_loc)) + //start tickets + let date = new Date().toLocaleString() + let tickets = await db.Tickets.findAll({where:{status:'open',author:message.author.id}}) + let matt = Array.from(message.attachments, ([name, value]) => ({ name, value })) + let smatt = [] + for(let att of matt){ + smatt.push({attachment:att.value.url}) + } + if(tickets.length==0){ + let nticket = this.generate_id() + const embed = new EmbedBuilder() + .setTitle("New Ticket | "+nticket) + .setAuthor({name: message.author.tag, iconURL: message.author.displayAvatarURL()}) + .setColor(settings.defaultColor) + .setDescription("Message: "+message.content) + .setFooter({text:"Created by: "+message.author.id+" | Created: "+date}) + + const confirm = new ButtonBuilder() + .setCustomId('confirm') + .setLabel('Confirm') + .setStyle(ButtonStyle.Primary); + const row = new ActionRowBuilder() + .addComponents(confirm); + + let mess = await message.channel.send({embeds:[embed],components:[row],files:smatt}); + + const collectorFilter = i => true; + + try { + const confirmation = await mess.awaitMessageComponent({ filter: collectorFilter, time: 60000000 }); + if(confirmation.customId == "confirm"){ + + let nt = await db.Tickets.create({ + ticket:nticket, + message: message.content, + attachments: JSON.stringify(matt), + status: "open", + author: message.author.id, + name: message.author.tag, + created: date, + messages: JSON.stringify([]), + pfp: message.author.displayAvatarURL() + }) + + const aembed = new EmbedBuilder() + .setTitle("New Ticket | "+nticket) + .setAuthor({name: message.author.tag, iconURL: message.author.displayAvatarURL()}) + .setColor(settings.defaultColor) + .setDescription("Message: '"+message.content+"'\n\nUse `/ticket` or `sns ticket reply "+nticket+" {message}`") + .setFooter({text:"Created by: "+message.author.id+" | Created: "+date}) + + global.channels["admin-chan"].send({embeds:[aembed],files:smatt}) + confirmation.deferUpdate() + } + } catch (e) { + const confirm_timeout = new ButtonBuilder() + .setCustomId('confirm') + .setLabel('Confirm') + .setStyle(ButtonStyle.Primary) + .setDisabled(true); + const row_timeout = new ActionRowBuilder() + .addComponents(confirm_timeout); + await mess.edit({ components: [row_timeout] }); + } + } else { + let ticket = tickets[0] + let messages = JSON.parse(ticket.messages) + messages.push({message:message.content,attachments:matt}) + db.Tickets.update({'messages':JSON.stringify(messages)},{where:{id:ticket.id}}) + const aembed = new EmbedBuilder() + .setTitle("Updated Ticket | "+ticket.ticket) + .setAuthor({name: message.author.tag, iconURL: message.author.displayAvatarURL()}) + .setColor(settings.defaultColor) + .setDescription("Message: '"+message.content+"'\n\nReply with `sns ticket reply "+ticket.ticket+" {message}`") + .setFooter({text:"Created by: "+message.author.id+" | Created: "+date}) + + global.channels["admin-chan"].send({embeds:[aembed],files:smatt}) + } + //done w/ tickets + }, + generate_id(){ + let config = JSON.parse(fs.readFileSync(config_loc)) + var ticket = ""; + var characters = config["ticket-id-chars"].value; + for (var i = 0; i < config["ticket-id-length"].value; i++) { + ticket += characters.charAt(Math.floor(Math.random() * characters.length)); + if (i == config["ticket-id-split"].value) ticket += "-"; + } + return ticket + } +}
\ No newline at end of file diff --git a/events/interaction.js b/events/interaction.js new file mode 100644 index 0000000..3a3b32a --- /dev/null +++ b/events/interaction.js @@ -0,0 +1,57 @@ +const { ActivityType , PermissionsBitField} = require("discord.js"); +const fs = require('fs') +const path = require("path"); +const { EmbedBuilder } = require("discord.js"); +let db = require("../src/db") +let settings = require('../src/settings') +let config_loc = __filename+".json" +module.exports = { + name : "interactionCreate", + config_loc : config_loc, + async main (client,Discord){ + await db._raw.sync() + let config = JSON.parse(fs.readFileSync(config_loc)) + client.on("interactionCreate", async(interaction) => { + if(interaction.guild==null)return + if(!settings["allowed-servers"].includes(interaction.guild.id)) + return console.log("denied interaction from (guild)"+interaction.guild.id) + let date = new Date() + if(interaction.isChatInputCommand()){ + + await interaction.guild.members.fetch() + interaction.user = interaction.guild.members.cache.get(interaction.user.id) + let mod = interaction.user.permissions!=null&&interaction.user.permissions?.has(PermissionsBitField.Flags.KickMembers) + let command = global.s_commands.find(o => o.name === interaction.commandName) + if(!((!command.command.config.restrict||command.command.config.restrict.includes(interaction.channel.id))&& + (!command.command.config.restricted||!command.command.config.restricted.includes(interaction.channel.id)))&&!mod) + return interaction.reply({content:"you cannot send this here! try `sns help` for more info",ephemeral:true}) + if(command==null) + return; + interaction.author = interaction.user + let uid = interaction.user.id; + + if(!mod&&command.command.last_command[uid]!=null&&(date.getTime()-command.command.last_command[uid].getTime())/1000<command.command.config.cooldown) + return interaction.reply({content:"this command is on cooldown for "+ + ((date.getTime()-command.command.last_command[uid].getTime())/1000).toFixed(2)+"/"+command.command.config.cooldown+"s",ephemeral:true}) + + command.command.last_command[uid] = new Date(); + await command.command.s_main(client,Discord,interaction); + + } else if (interaction.isAutocomplete()){ + + const focused = interaction.options.getFocused(true); + let command = global.s_commands.find(o => o.name === interaction.commandName) + let subcommand = command.opt.find(o => o.name === focused.name) + let autocomplete = (typeof subcommand.autocomplete === 'function' ? await subcommand.autocomplete() : subcommand.autocomplete); + const filtered = autocomplete.filter(choice => choice.startsWith(focused.value)); + if(filtered.length>25) + filtered.length=25 + await interaction.respond( + filtered.map(choice => ({ name: choice, value: choice })), + ); + + } + }) + + }, +}
\ No newline at end of file diff --git a/events/message.js b/events/message.js new file mode 100644 index 0000000..817a592 --- /dev/null +++ b/events/message.js @@ -0,0 +1,100 @@ +const fs = require('fs') +const path = require("path"); +let db = require("../src/db"); +const { parse_inp } = require("../src/util") +const { channel } = require('diagnostics_channel'); +const { PermissionsBitField } = require('discord.js'); +const { EmbedBuilder } = require("discord.js"); +let settings = require('../src/settings') +let config_loc = __filename+".json" +module.exports = { + name : "messageCreate", + config_loc : config_loc, + async main (client,Discord){ + let config = JSON.parse(fs.readFileSync(config_loc)) + client.on("messageCreate", async (message) => { + if(message.guild==null) + return require("./dm").main(client,Discord,message) + if(!settings["allowed-servers"].includes(message.guild.id)||message.author.bot||message.member==null) + return; + //handle sticky messages + let stickies = await db.Sticky.findAll(); + for(let s of stickies){ + if(message.channel.id==s.channel && message.author.id!="762561860150362142"){ + let m; + if(s.embed){ + let embed = new EmbedBuilder() + .setDescription(s.message) + .setColor(s.embed_color) + m = await message.channel.send({embeds:[embed]}) + } else { + m = await message.channel.send(s.message) + } + if(s.last_message!='null'){ + try{ + let msg = await message.channel.messages.fetch(s.last_message) + msg.delete() + + } catch(e){} + } + db.Sticky.update({last_message: m.id},{ where: {id: s.id}}) + } + + } + //done w/ sticky + + //handle auto reactions + let a_react = await db.Auto_React.findAll(); + for(let a of a_react){ + if(message.channel.id==a.channel && message.author.id!="762561860150362142"){ + try{ + let m = await message.react(a.emote) + } catch(e){} + } + + } + //done w/ auto reactions + + //deal with commands + let remove = function(msg) {setTimeout(async()=>{try{await msg.delete()}catch(e){}},config["error-timeout"].value)} + let date = new Date() + let uid = message.member.id; + let mod = message.member.permissions!=null&&message.member.permissions?.has(PermissionsBitField.Flags.KickMembers) + if(message.content.startsWith("sns ")&&(!config["restrict-channels"].value.includes(message.channel.id)||(mod))){ + let com_string = message.content.split(" ") + com_string.shift() + let found = false; + let com_call = com_string[0].trim() + //keep this as a for loop incase we want to do sequential commands + for(let com of global.commands){ + if(com.command.includes(com_call)){ + found = true; + if(com.mod_only&&!mod){ + message.reply(config["error-message-auth"].value).then((msg)=>{remove(msg)}) + break; + } + com_string.shift() + com_string = parse_inp(com_string.join(" ")) + try{ + if(((!com.config.restrict||com.config.restrict.includes(message.channel))&& + (!com.config.restricted||!com.config.restricted.includes(message.channel)))||mod) + if(mod||com.config.cooldown==null||com.last_command[uid]==null||(date.getTime()-com.last_command[uid].getTime())/1000>com.config.cooldown){ + await com.main(client,Discord,message,com_string,com_call) + com.last_command[uid] = new Date() + } else message.reply("this command is on cooldown for "+((date.getTime()-com.last_command[uid].getTime())/1000).toFixed(2)+"/"+com.config.cooldown+"s").then((msg)=>{remove(msg)}) + else + message.reply(config["error-message-not-here"].value).then((msg)=>{remove(msg)}) + } catch(e) { + message.reply(config["error-message"].value + "\n```"+e.stack+"```").then((msg)=>{remove(msg)}) + console.log(e.stack) + } + break; + } + } + if(!found) + message.reply(config["error-message-not-found"].value).then((msg)=>{remove(msg)}) + } + //done w/ commands + }) + }, +}
\ No newline at end of file diff --git a/events/reaction-remove.js b/events/reaction-remove.js new file mode 100644 index 0000000..b46595b --- /dev/null +++ b/events/reaction-remove.js @@ -0,0 +1,20 @@ +const { ActivityType , PermissionsBitField} = require("discord.js"); +const fs = require('fs') +const path = require("path"); +const { EmbedBuilder } = require("discord.js"); +let db = require("../src/db") +let settings = require("../src/settings") +let config_loc = __filename+".json" +module.exports = { + name : "messageReactionRemove", + config_loc : config_loc, + async main (client,Discord){ + client.on("messageReactionRemoveEmoji",(m)=>{ + global.channels.logging.send({embeds:[new EmbedBuilder().setTitle("Emoji removed").setDescription((m.count??1)+" "+m.emoji.name+"(s) have been removed from "+m.message.url).setTimestamp(Date.now()).setColor(settings.defaultColor)]}) + }) + client.on("messageReactionRemoveAll",(m)=>{ + global.channels.logging.send({embeds:[new EmbedBuilder().setTitle("Bulk emoji remove").setDescription("All emojis have been removed from "+m.url).setTimestamp(Date.now()).setColor(settings.defaultColor)]}) + }) + + }, +}
\ No newline at end of file diff --git a/events/ready.js b/events/ready.js new file mode 100644 index 0000000..c694c00 --- /dev/null +++ b/events/ready.js @@ -0,0 +1,87 @@ +const { ActivityType } = require("discord.js"); +const fs = require('fs') +const path = require("path"); +const { EmbedBuilder } = require("discord.js"); +let db = require("../src/db") +let settings = require('../src/settings') +let config_loc = __filename+".json" +module.exports = { + name : "ready", + config_loc : config_loc, + async main (client,Discord){ + await db._raw.sync() + let config = JSON.parse(fs.readFileSync(config_loc)) + client.once("ready", () => { + //preload + global.channels = {} + for(let guild of Object.keys(settings.preloads)){ + + for(let chan of Object.keys(settings.preloads[guild])){ + let t_add = client.guilds.cache.get(guild).channels.cache.get(chan) + if(t_add==null) + console.log("failed to load "+chan+" from "+guild+", skipping"); + else + global.channels[settings.preloads[guild][chan].name] = t_add + } + } + //end + + //register slash commands + let passed = 0; + let failed = 0; + client.guilds.cache.forEach((g)=>{ + if(settings["allowed-servers"].includes(g.id)){ + g.commands.set(global.s_commands).catch((e)=>{ + console.log("unable to load commands for " + g.id + "\n\n****\n") + console.log(e) + }) + passed++; + } else { + failed++; + } + }) + console.log("loaded "+global.s_commands.length+" slash commands for "+passed+" guilds, and denied "+failed+" guilds") + //done w/ slash commands + + console.log("online!") + function set_pres() { + client.user.setPresence({ + activities: [{ name: config.status.value, type: ActivityType[config.type.value] }] + }); + } + set_pres() + setInterval(set_pres,36000) + setInterval(async()=>{ + //timed messages + let timed = await db.Timed_Message.findAll(); + let cur = new Date() + for(let t of timed){ + let delay = parseInt(t.delay) + if(t.last_message_time!='null') + t.last_message_time = parseInt(t.last_message_time) + //console.log(new Date(parseInt(t.last_message_time)),(new Date(t.last_message_time)).getTime(),delay) + if(t.last_message_time=='null'||cur.getTime()-(new Date(t.last_message_time)).getTime()>delay){ + + let gu = await client.guilds.fetch(t.guild) + let channel = await gu.channels.fetch(t.channel) + if(t.embed){ + let embed = new EmbedBuilder() + .setDescription(t.message) + .setColor(t.embed_color) + channel.send({embeds:[embed]}) + } else { + channel.send(t.message) + } + //console.log(new Date(t.last_message_time).getTime(),(new Date(t.last_message_time)).getTime()+delay) + if(t.last_message_time=='null') + db.Timed_Message.update({last_message_time : cur.getTime().toString()},{ where: {id: t.id}}) + else + db.Timed_Message.update({last_message_time : (new Date((new Date(t.last_message_time)).getTime()+delay)).getTime().toString()},{ where: {id: t.id}}) + } + } + //done w/ timed messages + },config.timed_interval.value) + }) + + }, +}
\ No newline at end of file diff --git a/html/settings.html b/html/settings.html new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/html/settings.html diff --git a/html/tickets.html b/html/tickets.html new file mode 100644 index 0000000..925b66d --- /dev/null +++ b/html/tickets.html @@ -0,0 +1,541 @@ +<!DOCTYPE html> +<!--for embeds--> +<meta property="og:type" content="website"> +<meta property="og:title" content="sns-chan ticket moderation:3"> +<meta property="og:description" content="verify with a key for details"> +<meta property="og:image" content="https://i.pinimg.com/originals/8f/52/1b/8f521bf9a52f7f9d3da912e4abf07979.png"> +<!--not ugly mobile view:p --> +<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no"> +<html> + +<head> + <title>sns-chan</title> + <style> + + + .bar{ + background: rgb(0,0,0,0.8); + background: linear-gradient(90deg, rgba(22,22,22,0.8) 0%, rgba(0,0,0,0.8) 35%, rgba(19,19,19,0.8) 100%); + backdrop-filter: blur(3px); + -webkit-backdrop-filter: blur(5px); + } + #top-bar{ + width:100%; + position: sticky; + top: 0; + padding: 0px; + display:inline-block; + + } + html, + body { + height: 100%; + margin: 0; + padding: 0; + background-color: #f1f1f1; + font-family: Arial, monospace; + overflow-x: hidden; + overscroll-behavior: none; + } + + #msg-root { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + + } + + .container { + max-width: 600px; + width: 90%; + background-color: #fff; + border-radius: 5px; + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2); + overflow: hidden; + } + + .header { + background-color: #555; + color: #fff; + padding: 10px; + text-align: center; + } + + .flex-container { + display: flex; + flex-wrap: wrap; + } + + .ticket-item { + margin: 10px; + padding: 5px; + cursor: pointer; + background-color: #f9f9f9; + border-radius: 3px; + } + + .ticket-sel { + background-color: #ffcba4; + } + + .ticket-unsel { + background-color: #fff; + } + + .messages-container { + + overflow-y: scroll; + padding: 10px; + padding-bottom:70px; + display:flex; + flex-direction: column; + width: 100%; + } + .msg { + display:flex; + padding: 5px; + flex-flow: wrap; + } + .user-msg { + background-color: #f9f9f9; + justify-content: left; + } + + .mod-msg { + color: red; + justify-content: right; + } + + .bottom-bar { + padding: 10px; + display: flex; + justify-content: center; + position: fixed; + bottom: 0; + left: 0; + width: 100%; + overflow: hidden; + gap: 10px; + } + textarea{ + height:25px; + flex:.8; + } + + .bottom-bar input[type="text"] { + flex: 1; + padding: 5px; + border: 1px solid #ccc; + border-radius: 3px; + } + + .bottom-bar button { + margin-left: 10px; + padding: 5px 10px; + border: none; + border-radius: 3px; + background-color: #555; + color: #fff; + cursor: pointer; + } + #menu-sym { + display:block; + top:0; + left:0; + z-index:5; + cursor:pointer; + height: 29px; + width: 32px; + position: relative; + margin: 12px; + + } + #side-menu { + + width:300px; + height: 100%; + background-color: rgba(150, 150, 150, 1); + position:absolute; + top:0; + left:0; + overflow:scroll; + z-index:4; + } + .open-link::after { + content: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAQElEQVR42qXKwQkAIAxDUUdxtO6/RBQkQZvSi8I/pL4BoGw/XPkh4XigPmsUgh0626AjRsgxHTkUThsG2T/sIlzdTsp52kSS1wAAAABJRU5ErkJggg==); + margin: 0 3px 0 5px; + } + .open-link{ + display:block; + } + @keyframes lost-hover-select { + from {background-color: rgba(25,25,25,0.4);} + to {background-color: rgba(25,25,25,0.2);} + } + .ticket-list-elem{ + user-select: none; + position: relative; + padding:5px; + margin:3px; + border-radius: 3px; + /* + animation-name: lost-hover-select; + animation-duration: .6s; + animation-fill-mode: forwards;*/ + animation: lost-hover-select .6s forwards; + } + @keyframes lost-hover-select-new { + from {background-color: rgba(255,0,0,0.4);} + to {background-color: rgba(25,25,25,0.2);} + } + @keyframes flashing-color { + 0%{ + background-color: rgba(25,25,25,0.2); + } + 35%{ + background-color: rgba(150,25,25,.2); + } + 65%{ + background-color: rgba(150,25,25,.2); + } + 100%{ + background-color: rgba(25,25,25,.2); + } + } + .ticket-list-elem.new-ticket{ + animation: lost-hover-select-new 1.4s forwards, flashing-color 2s infinite; + } + @keyframes hover-select { + from {background-color: rgba(25,25,25,0.2);} + to {background-color: rgba(25,25,25,0.4);} + } + .ticket-list-elem:hover{ + animation-name: hover-select; + animation-duration: 1s; + animation-fill-mode: forwards; + } + @keyframes hover-select-new { + from {background-color: rgba(25,25,25,0.2);} + to {background-color: rgba(255,0,0,0.4);} + } + .ticket-list-elem.new-ticket:hover{ + animation-name: hover-select-new; + } + .ticket-selected { + box-shadow: inset 0 0 0 2px rgba(0, 0, 0, 0.2); + } + .menu-icon-bar{ + background: black; + height: 5px; + margin: 5px; + } + .ticket-sel-pfp{ + position:absolute; + top:50%; + transform: translateY(-50%); + width:22px; + border-radius: 20px; + margin-right:6px; + } + .ticket-sel-text{ + margin-left:28px; + } + .ticket-list-elem-item{ + padding-left: 35px; + } + pre{ + display:inline-block; + margin:0; + } + .arrow-close { + cursor: pointer; + height: 29px; + width: 32px; + position:relative; + margin: 12px; + } + .arrow-part { + width: 8px; + left:9px; + height: 18px; + background: red; + transform: rotate(-37deg); + border-radius: 2px; + position: absolute; + } + .arrow-part-2 { + transform: rotate(-37deg); + top: 10px; +} +.arrow-part-1 { + + transform: rotate(37deg); + top: 0; +} +.farrow-part-2 { + transform:scaleX(-1) rotate(-37deg); + top: 10px; +} +.farrow-part-1 { + + transform:scaleX(-1) rotate(37deg); + top: 0; +} + </style> +</head> + + <script> + //cookie code taken from https://github.com/squiresgrant/kanna-site/blob/main/html/index.html + //which i probably stole from stackoverflow + function setCookie(name, value, days) { + var expires = ""; + if (days) { + var date = new Date(); + date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); + expires = "; expires=" + date.toUTCString(); + } + document.cookie = name + "=" + (value || "") + expires + "; path=/"; + } + function getCookie(name) { + var nameEQ = name + "="; + var ca = document.cookie.split(';'); + for (var i = 0; i < ca.length; i++) { + var c = ca[i]; + while (c.charAt(0) == ' ') c = c.substring(1, c.length); + if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length); + } + return null; + } + function eraseCookie(name) { + document.cookie = name + '=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT;'; + } + function send(location, content) { + return new Promise((res, rej) => { + var xhr = new XMLHttpRequest(); + let st = 'http://' + window.location.host + window.location.pathname + "/" + location + xhr.open("POST", st, true); + xhr.setRequestHeader('Content-Type', 'application/json'); + xhr.onreadystatechange = function () { + if (xhr.readyState == XMLHttpRequest.DONE) { + console.log('['+xhr.status+']'+' '+st) + res(xhr.responseText); + } + } + let out = {} + Object.assign(out, { json: true, enc: false }, content) + xhr.send(JSON.stringify(out)) + }) + } + let tickets; + //let tickets = [{ "id": 1, "ticket": "7twX-hTE", "message": "intro test", "status": "open", "author": "534554607330787381", "name": "nekomusu#0", "created": "6/22/2023, 2:16:46 AM", "messages": "[{\"msg\":\"<img src=\'hehe\' onerror='alert(`hi`)'>\",\"author\":\"534554607330787381\",\"attachments\":\"\",\"mod\":false},{\"msg\":\" sample reply\",\"author\":\"534554607330787381\",\"mod\":true}]", "attachments": null, "pfp": "https://cdn.discordapp.com/avatars/534554607330787381/f72aa9936aaf85e4a5787910ff28c136.webp", "createdAt": "2023-06-22T07:16:46.096Z", "updatedAt": "2023-06-22T07:17:06.667Z" }, + //{ "id": 2, "ticket": "7ewX-hTE", "message": "intro test2", "status": "open", "author": "534554607330787381", "name": "nekomusu#0", "created": "6/22/2023, 2:16:46 AM", "messages": "[{\"msg\":\"hi:)\",\"author\":\"534554607330787381\",\"attachments\":\"https://media.discordapp.net/attachments/848349994082893884/1031317201732522046/unknown.png?width=708&height=398 https://media.discordapp.net/attachments/545076257369358338/1121494934130794586/IMG_9731.jpg?width=796&height=398\",\"mod\":false},{\"msg\":\" sample reply\",\"author\":\"534554607330787381\",\"mod\":true}]", "attachments": null, "pfp": "https://cdn.discordapp.com/avatars/534554607330787381/f72aa9936aaf85e4a5787910ff28c136.webp", "createdAt": "2023-06-22T07:16:46.096Z", "updatedAt": "2023-06-22T07:17:06.667Z" }, + //{ "id": 3, "ticket": "7ywX-hTE", "message": "intro test2", "status": "open", "author": "534554607330787381", "name": "nekomusu#0", "created": "6/22/2023, 2:16:46 AM", "messages": "[]", "attachments": null, "pfp": "https://cdn.discordapp.com/avatars/534554607330787381/f72aa9936aaf85e4a5787910ff28c136.webp", "createdAt": "2023-06-22T07:16:46.096Z", "updatedAt": "2023-06-22T07:17:06.667Z" }]; + let selected = -1; + function fetch() { + return new Promise(async (res, rej) => { + if (getCookie('main_key') == null) { + setCookie('main_key', window.prompt('you do not have a valid key, contact ans-chan (@nekomusu) for it'), 90) + } + + let data = await send('data', { key: getCookie('main_key') }) + if (data == 'failed') { + alert('invalid key, clearing cookies') + eraseCookie('main_key') + fetch() + return + } + tickets = JSON.parse(data) + res() + }) + } + + async function sendmsg(ticket, msg) { + if (await send('edit', { key: getCookie('main_key'), ticket: ticket, msg: msg, function: 'add' }) == 'failed') { + alert("invalid key, refetching") + console.log('[401] bad key') + fetch() + } + console.log('[200] accepted update (appended)') + } + async function closetkt(ticket) { + if (await send('edit', { key: getCookie('main_key'), ticket: ticket, function: 'close' }) == 'failed') { + alert("invalid key, refetching") + console.log('[401] bad key') + fetch() + } + console.log('[200] accepted update (closed)') + } + function sendmsg_wd(ticket, div) { + sendmsg(ticket, document.getElementById(div).value) + } + //https://github.com/squiresgrant/personal-site/blob/main/src/common.js + function appendHtml(el, str) { + var div = document.createElement('div'); + div.innerHTML = str; + while (div.children.length > 0) { + el.appendChild(div.children[0]); + } + } + let menu_tickets_main_open = true; + let menu_tickets_open_open = true; + let menu_tickets_closed_open=false; + let side_bar_pers = 'none'; + async function load() { + let ta = '<div id="top-bar" class="bar">'; + ta += '<div id="menu-sym"><div class="arrow-part farrow-part-1"></div><div class="arrow-part farrow-part-2"></div></div>' + ta += '</div>' + //let ta = '<div class="flex-container">' + ta += '<div style="display:'+side_bar_pers+';" id="side-menu">' + ta += '<div class="arrow-close" id="arrow-close-menu"><div class="arrow-part arrow-part-1"></div><div class="arrow-part arrow-part-2"></div></div>' + if(menu_tickets_main_open){ + ta+='<div id="toggle-all" class="ticket-list-elem" ><pre>-</pre> all tickets</div>' + if(menu_tickets_open_open){ + ta+='<div id="toggle-open" class="ticket-list-elem"> <pre>-</pre> open tickets</div>' + //for(let i = 0; i!=22;i++){ + for(let ticket of tickets){ + if(ticket.status=='open'){ + let msgss = JSON.parse(ticket.messages); + let replied = false; + for(let msg of msgss){ + if(msg.mod){ + replied=true; + break; + } + } + ta+='<a href="'+window.location.protocol+'//'+ window.location.host + window.location.pathname+'?ticket='+ticket.id+'" class="open-link ticket-list-elem ticket-list-elem-item '+(replied?'':'new-ticket')+' '+(selected==ticket.id?'ticket-selected':'')+'">' + ta+='<img src="'+ticket.pfp+'" class="ticket-sel-pfp"><span class="ticket-sel-text">'+ticket.ticket + ta+='</span></a>' + } + //} + } + } else { + ta+='<div id="toggle-open" class="ticket-list-elem"> <pre>+</pre> open tickets</div>' + } + if(menu_tickets_closed_open){ + ta+='<div id="toggle-closed" class="ticket-list-elem"> <pre>-</pre> closed tickets</div>' + for(let ticket of tickets){ + if(ticket.status!='open'){ + ta+='<a href="'+window.location.protocol+'//'+window.location.host + window.location.pathname+'?ticket='+ticket.id+'" class="open-link ticket-list-elem ticket-list-elem-item '+(selected==ticket.id?'ticket-selected':'')+'">' + ta+='<img src="'+ticket.pfp+'" class="ticket-sel-pfp"><span class="ticket-sel-text">'+ticket.ticket + ta+='</span></a>' + } + } + } else { + ta+='<div id="toggle-closed" class="ticket-list-elem"> <pre>+</pre> closed tickets</div>' + } + } else { + ta+='<div id="toggle-all" class="ticket-list-elem"><pre>+</pre> all tickets</div>' + } + ta += '</div>' + ta += '<div id="msg-root">' + /* + ta+='<select id="ticket-dropdown">' + //for(let i = 0; i!= 999; i++){ + for (let ticket of tickets) { + //ta += '<div id="ticket-'+ticket.id+'" class="'+(selected==ticket.id?'ticket-sel':'ticket-unsel')+' ticket-item"">'+ticket.ticket+"</div>"; + let msgss = JSON.parse(ticket.messages); + let replied = false; + for(let msg of msgss){ + if(msg.mod){ + replied=true; + break; + } + } + ta += '<option value="' + ticket.id + '" ' + (selected == ticket.id ? 'selected' : '') + '>' + (!replied?'(NEW)':'') + ticket.ticket + '</option>' + } + //} + ta += '</select>' + */ + //document.body.innerHTML = ta + let valid_sel = false + + for (let ticket of tickets) { + + + if (ticket.id == selected) { + valid_sel = ticket; + ta += '<div class="messages-container"> <div class="user-msg msg" >' + ticket.message.replace('<','<').replace('</','</')+ '</div>'; + let msgs = JSON.parse(ticket.messages); + for (let i = 0; i != 10; i++) { + for (let msg of msgs) { + + ta += '<div class="' + (msg.mod ? 'mod-msg' : 'user-msg') + ' msg">' + msg.message.replace('<','<').replace('</','</') + if(msg.attachments&&msg.attachments.length>0){ + for(let i of msg.attachments.split(' ')){ + ta += '</br><a href="'+i+'"> (image)</a>'; + } + } + ta += '</div>' + } + } + ta += '</div>' + + + break; + } + } + + + if (valid_sel != false) { + ta += '<div class="bottom-bar bar"><textarea id="send-box"></textarea><button type="button" onclick="sendmsg_wd(`' + valid_sel.ticket + '`,`send-box`)">send</button><button type="button" onclick="closetkt(`' + valid_sel.ticket + '`)">close ticket</button></div>' + //appendHtml(document.body,bottombar); + //return; + } + + //appendHtml(document.body,ta) + ta += '</div>' + //appendHtml(document.body, ta) + document.body.innerHTML = ta; + /* + + let ticket_elem = document.getElementById('ticket-dropdown'); + ticket_elem.onchange = (() => { + selected = ticket_elem.value; + document.location.search = '?ticket=' + selected + load(); + }) + */ + [document.getElementById('arrow-close-menu'),document.getElementById('menu-sym')].forEach((el)=>{ + el.onclick = (()=>{ + let menu = document.getElementById('side-menu') + if(side_bar_pers=='none') + side_bar_pers = 'block'; + else + side_bar_pers = 'none'; + menu.style.display = side_bar_pers + }) + }) + document.getElementById('toggle-all').onclick = (()=>{ + menu_tickets_main_open=!menu_tickets_main_open + load() + }) + if(menu_tickets_main_open){ + document.getElementById('toggle-closed').onclick = (()=>{ + menu_tickets_closed_open=!menu_tickets_closed_open + load() + }) + document.getElementById('toggle-open').onclick = (()=>{ + menu_tickets_open_open=!menu_tickets_open_open + load() + }) + } + } + async function init() { + const queryString = window.location.search; + const urlParams = new URLSearchParams(queryString); + selected = urlParams.get('ticket') + console.log('https://http.cat/') + await fetch(); + load() + + //! hey:) would be best to put any js here + } + </script> + +<body onload="init()"> + + +</body> + +</html> diff --git a/img/battleship-board-sel.png b/img/battleship-board-sel.png Binary files differnew file mode 100644 index 0000000..ba1f56d --- /dev/null +++ b/img/battleship-board-sel.png diff --git a/img/battleship-board.png b/img/battleship-board.png Binary files differnew file mode 100644 index 0000000..4b2fe24 --- /dev/null +++ b/img/battleship-board.png diff --git a/img/fire.png b/img/fire.png Binary files differnew file mode 100644 index 0000000..fe6863c --- /dev/null +++ b/img/fire.png diff --git a/index.js b/index.js new file mode 100644 index 0000000..be5ab38 --- /dev/null +++ b/index.js @@ -0,0 +1,128 @@ +//process mods redirection +const fs = require("fs"); +var access = fs.createWriteStream('./log'); +process.stdout.write = process.stderr.write = (function(write) { + return function(string, encoding, fd) { + write.apply(process.stdout, arguments) + access.write(string) + } +})(process.stdout.write) +process.title = "sns-chan" +/** + * Module Imports + */ +const { Client, Collection, EmbedBuilder, ActivityType, GatewayIntentBits, Partials, Events, SlashCommandBuilder, PermissionsBitField} = require("discord.js"); +const Discord = require('discord.js'); +const dotenv = require("dotenv").config(); +const TOKEN = process.env.TOKEN; +const path = require("path"); +let db = require("./src/db"); + +const client = new Client({ + intents: [ + GatewayIntentBits.Guilds, + GatewayIntentBits.GuildMessages, + GatewayIntentBits.DirectMessages, + GatewayIntentBits.Guilds, + GatewayIntentBits.GuildBans, + GatewayIntentBits.GuildMessages, + GatewayIntentBits.MessageContent, + GatewayIntentBits.GuildVoiceStates, + GatewayIntentBits.GuildMessageReactions, + GatewayIntentBits.DirectMessages, + GatewayIntentBits.GuildMembers, + GatewayIntentBits.DirectMessageReactions, + GatewayIntentBits.DirectMessageTyping, + //GatewayIntentBits.GuildPresences + ], + partials: [Partials.Message, Partials.Channel, Partials.Reaction, Partials.User] +}); + +client.login(TOKEN); + +let commands = [] +let s_commands = [] +fs.readdirSync("./commands/").forEach(folder => { + fs.readdirSync("./commands/"+folder).forEach(file => { + if(path.extname(file)==".js"){ + try{ + let com = require("./commands/"+folder+"/"+file) + com.last_command = {}; + commands.push(com) + if(com.s_main!=null){ + let scom = new SlashCommandBuilder() + .setName(com.name.replace(/ /g,'-')) + .setDescription(com.config.desc) + if(com.mod_only) + scom.setDefaultMemberPermissions(PermissionsBitField.Flags.KickMembers) + if(com.s_options!=null){ + for(let opt of com.s_options){ + switch(opt.type){ + case 'string': + scom.addStringOption(option => + option.setName(opt.name) + .setDescription(opt.desc) + .setRequired(opt.required) + .setAutocomplete(opt.autocomplete!=null&&opt.autocomplete!=false)) + break; + case 'channel': + scom.addChannelOption(option => + option.setName(opt.name) + .setDescription(opt.desc) + .setRequired(opt.required)) + break; + case 'user': + scom.addUserOption(option => + option.setName(opt.name) + .setDescription(opt.desc) + .setRequired(opt.required)) + break; + case 'role': + scom.addRoleOption(option => + option.setName(opt.name) + .setDescription(opt.desc) + .setRequired(opt.required)) + break; + case 'attachment': + scom.addAttachmentOption(option => + option.setName(opt.name) + .setDescription(opt.desc) + .setRequired(opt.required)) + break; + } + + } + scom.opt = com.s_options + } + scom = scom.toJSON() + scom.command = com + s_commands.push(scom) + } + } catch (e) { + if(e.code=="ENOENT"){ + console.log("[ENOENT] missing some config files:( run 'sh buildconfig.sh' to get them\nexiting~") + process.exit(e.errno) + } + console.log("["+e.code+"]"+" unexpected error:( something is wrong with the ./commands/*/* files\n****\n") + console.log(e) + + process.exit(e.errno) + } + } + }) +}) + +client.env = process.env +global.commands = commands; +global.s_commands = s_commands; + +fs.readdirSync("./events/").forEach(file => { + if(path.extname(file)==".js") + require("./events/"+file).main(client,Discord) +}) + +try{ + require("./src/webui") +} catch(e) { + console.log("failed loading webui:c") +} diff --git a/license.md b/license.md new file mode 100644 index 0000000..fd5dea9 --- /dev/null +++ b/license.md @@ -0,0 +1,11 @@ +Copyright <2023> <[email protected]> + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/package.json b/package.json new file mode 100644 index 0000000..140e8bf --- /dev/null +++ b/package.json @@ -0,0 +1,37 @@ +{ + "name": "snschan", + "version": "0.10.0", + "description": "Discord music bot built with discord.js", + "main": "index.js", + "author": "Ryzyx", + "private": true, + "homepage": "https://github.com/ryzyx/sns-chan", + "repository": "github:ryzyx/sns-chan", + "bugs": "https://github.com/ryzyx/sns-chan/issues", + "engines": { + "node": "=16.6.1" + }, + "dependencies": { + "@discordjs/opus": "^0.5.0", + "@discordjs/voice": "^0.11.0", + "discord-embedbuilder": "^4.0.0", + "discord-together": "^1.3.3", + "discord.js": "^14.8.0", + "dotenv": "^9.0.2", + "express": "^4.17.1", + "ffmpeg-static": "^4.2.3", + "git-last-commit": "^1.0.1", + "os-utils": "^0.0.14", + "sequelize": "^6.32.1", + "sharp": "^0.32.6", + "sqlite3": "^5.1.6", + "weeby-js": "^1.0.3" + }, + "scripts": { + "start": "node index.js" + }, + "devDependencies": { + "@types/node": "^18.7.19", + "eslint": "^8.47.0" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..fdbd78d --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,2532 @@ +lockfileVersion: '6.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +dependencies: + '@discordjs/opus': + specifier: ^0.5.0 + version: 0.5.3 + '@discordjs/voice': + specifier: ^0.11.0 + version: 0.11.0(@discordjs/[email protected])([email protected]) + discord-embedbuilder: + specifier: ^4.0.0 + version: 4.0.0 + discord-together: + specifier: ^1.3.3 + version: 1.3.31 + discord.js: + specifier: ^14.8.0 + version: 14.13.0 + dotenv: + specifier: ^9.0.2 + version: 9.0.2 + express: + specifier: ^4.17.1 + version: 4.18.2 + ffmpeg-static: + specifier: ^4.2.3 + version: 4.4.1 + git-last-commit: + specifier: ^1.0.1 + version: 1.0.1 + os-utils: + specifier: ^0.0.14 + version: 0.0.14 + sequelize: + specifier: ^6.32.1 + version: 6.33.0([email protected]) + sharp: + specifier: ^0.32.6 + version: 0.32.6 + sqlite3: + specifier: ^5.1.6 + version: 5.1.6 + weeby-js: + specifier: ^1.0.3 + version: 1.3.0 + +devDependencies: + '@types/node': + specifier: ^18.7.19 + version: 18.17.17 + eslint: + specifier: ^8.47.0 + version: 8.49.0 + +packages: + + /@aashutoshrathi/[email protected]: + resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==} + engines: {node: '>=0.10.0'} + dev: true + + /@derhuerst/[email protected]: + resolution: {integrity: sha512-F9rL9k9Xjf5blCz8HsJRO4diy111cayL2vkY2XE4r4t3n0yPXVYy3KD3nJ1qbrSn9743UWSXH4IwuCa/HWlGFw==} + engines: {node: '>=6.0.0'} + dependencies: + caseless: 0.12.0 + concat-stream: 2.0.0 + http-response-object: 3.0.2 + parse-cache-control: 1.0.1 + dev: false + + /@discordjs/[email protected]: + resolution: {integrity: sha512-SdweyCs/+mHj+PNhGLLle7RrRFX9ZAhzynHahMCLqp5Zeq7np7XC6/mgzHc79QoVlQ1zZtOkTTiJpOZu5V8Ufg==} + engines: {node: '>=16.11.0'} + dependencies: + '@discordjs/formatters': 0.3.2 + '@discordjs/util': 1.0.1 + '@sapphire/shapeshift': 3.9.2 + discord-api-types: 0.37.50 + fast-deep-equal: 3.1.3 + ts-mixer: 6.0.3 + tslib: 2.6.2 + dev: false + + /@discordjs/[email protected]: + resolution: {integrity: sha512-SVb428OMd3WO1paV3rm6tSjM4wC+Kecaa1EUGX7vc6/fddvw/6lg90z4QtCqm21zvVe92vMMDt9+DkIvjXImQQ==} + engines: {node: '>=16.11.0'} + dev: false + + /@discordjs/[email protected]: + resolution: {integrity: sha512-lE++JZK8LSSDRM5nLjhuvWhGuKiXqu+JZ/DsOR89DVVia3z9fdCJVcHF2W/1Zxgq0re7kCzmAJlCMMX3tetKpA==} + engines: {node: '>=16.11.0'} + dependencies: + discord-api-types: 0.37.50 + dev: false + + /@discordjs/[email protected]: + resolution: {integrity: sha512-YJOVVZ545x24mHzANfYoy0BJX5PDyeZlpiJjDkUBM/V/Ao7TFX9lcUvCN4nr0tbr5ubeaXxtEBILUrHtTphVeQ==} + hasBin: true + dependencies: + detect-libc: 2.0.2 + https-proxy-agent: 5.0.1 + make-dir: 3.1.0 + node-fetch: 2.7.0 + nopt: 5.0.0 + npmlog: 5.0.1 + rimraf: 3.0.2 + semver: 7.5.4 + tar: 6.2.0 + transitivePeerDependencies: + - encoding + - supports-color + dev: false + + /@discordjs/[email protected]: + resolution: {integrity: sha512-IQhCwCy2WKXLe+qkOkwO1Wjgk20uqeAbqM62tCbzIqbTsXX4YAge8Me9RFnI77Lx+UTkgm4rSVM3VPVdS/GsUw==} + engines: {node: '>=12.0.0'} + deprecated: no longer supported + requiresBuild: true + dependencies: + '@discordjs/node-pre-gyp': 0.4.5 + node-addon-api: 3.2.1 + transitivePeerDependencies: + - encoding + - supports-color + dev: false + + /@discordjs/[email protected]: + resolution: {integrity: sha512-/eWAdDRvwX/rIE2tuQUmKaxmWeHmGealttIzGzlYfI4+a7y9b6ZoMp8BG/jaohs8D8iEnCNYaZiOFLVFLQb8Zg==} + engines: {node: '>=16.11.0'} + dependencies: + '@discordjs/collection': 1.5.3 + '@discordjs/util': 1.0.1 + '@sapphire/async-queue': 1.5.0 + '@sapphire/snowflake': 3.5.1 + '@vladfrangu/async_event_emitter': 2.2.2 + discord-api-types: 0.37.50 + magic-bytes.js: 1.0.17 + tslib: 2.6.2 + undici: 5.22.1 + dev: false + + /@discordjs/[email protected]: + resolution: {integrity: sha512-d0N2yCxB8r4bn00/hvFZwM7goDcUhtViC5un4hPj73Ba4yrChLSJD8fy7Ps5jpTLg1fE9n4K0xBLc1y9WGwSsA==} + engines: {node: '>=16.11.0'} + dev: false + + resolution: {integrity: sha512-6+9cj1dxzBJm7WJ9qyG2XZZQ8rcLl6x2caW0C0OxuTtMLAaEDntpb6lqMTFiBg/rDc4Rd59g1w0gJmib33CuHw==} + engines: {node: '>=16.9.0'} + dependencies: + '@types/ws': 8.5.5 + discord-api-types: 0.36.3 + prism-media: 1.3.5(@discordjs/[email protected])([email protected]) + tslib: 2.6.2 + ws: 8.14.2 + transitivePeerDependencies: + - '@discordjs/opus' + - bufferutil + - ffmpeg-static + - node-opus + - opusscript + - utf-8-validate + dev: false + + /@discordjs/[email protected]: + resolution: {integrity: sha512-avvAolBqN3yrSvdBPcJ/0j2g42ABzrv3PEL76e3YTp2WYMGH7cuspkjfSyNWaqYl1J+669dlLp+YFMxSVQyS5g==} + engines: {node: '>=16.11.0'} + dependencies: + '@discordjs/collection': 1.5.3 + '@discordjs/rest': 2.0.1 + '@discordjs/util': 1.0.1 + '@sapphire/async-queue': 1.5.0 + '@types/ws': 8.5.5 + '@vladfrangu/async_event_emitter': 2.2.2 + discord-api-types: 0.37.50 + tslib: 2.6.2 + ws: 8.14.2 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + dev: false + + /@eslint-community/[email protected]([email protected]): + resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + dependencies: + eslint: 8.49.0 + eslint-visitor-keys: 3.4.3 + dev: true + + /@eslint-community/[email protected]: + resolution: {integrity: sha512-PWiOzLIUAjN/w5K17PoF4n6sKBw0gqLHPhywmYHP4t1VFQQVYeb1yWsJwnMVEMl3tUHME7X/SJPZLmtG7XBDxQ==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + dev: true + + /@eslint/[email protected]: + resolution: {integrity: sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + ajv: 6.12.6 + debug: 4.3.4 + espree: 9.6.1 + globals: 13.21.0 + ignore: 5.2.4 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@eslint/[email protected]: + resolution: {integrity: sha512-1S8uAY/MTJqVx0SC4epBq+N2yhuwtNwLbJYNZyhL2pO1ZVKn5HFXav5T41Ryzy9K9V7ZId2JB2oy/W4aCd9/2w==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /@gar/[email protected]: + resolution: {integrity: sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==} + requiresBuild: true + dev: false + optional: true + + /@humanwhocodes/[email protected]: + resolution: {integrity: sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==} + engines: {node: '>=10.10.0'} + dependencies: + '@humanwhocodes/object-schema': 1.2.1 + debug: 4.3.4 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@humanwhocodes/[email protected]: + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + dev: true + + /@humanwhocodes/[email protected]: + resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} + dev: true + + /@mapbox/[email protected]: + resolution: {integrity: sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==} + hasBin: true + dependencies: + detect-libc: 2.0.2 + https-proxy-agent: 5.0.1 + make-dir: 3.1.0 + node-fetch: 2.7.0 + nopt: 5.0.0 + npmlog: 5.0.1 + rimraf: 3.0.2 + semver: 7.5.4 + tar: 6.2.0 + transitivePeerDependencies: + - encoding + - supports-color + dev: false + + /@nodelib/[email protected]: + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + dev: true + + /@nodelib/[email protected]: + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + dev: true + + /@nodelib/[email protected]: + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.15.0 + dev: true + + /@npmcli/[email protected]: + resolution: {integrity: sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==} + requiresBuild: true + dependencies: + '@gar/promisify': 1.1.3 + semver: 7.5.4 + dev: false + optional: true + + /@npmcli/[email protected]: + resolution: {integrity: sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==} + engines: {node: '>=10'} + deprecated: This functionality has been moved to @npmcli/fs + requiresBuild: true + dependencies: + mkdirp: 1.0.4 + rimraf: 3.0.2 + dev: false + optional: true + + /@sapphire/[email protected]: + resolution: {integrity: sha512-JkLdIsP8fPAdh9ZZjrbHWR/+mZj0wvKS5ICibcLrRI1j84UmLMshx5n9QmL8b95d4onJ2xxiyugTgSAX7AalmA==} + engines: {node: '>=v14.0.0', npm: '>=7.0.0'} + dev: false + + /@sapphire/[email protected]: + resolution: {integrity: sha512-YRbCXWy969oGIdqR/wha62eX8GNHsvyYi0Rfd4rNW6tSVVa8p0ELiMEuOH/k8rgtvRoM+EMV7Csqz77YdwiDpA==} + engines: {node: '>=v14.0.0', npm: '>=7.0.0'} + dependencies: + fast-deep-equal: 3.1.3 + lodash: 4.17.21 + dev: false + + /@sapphire/[email protected]: + resolution: {integrity: sha512-BxcYGzgEsdlG0dKAyOm0ehLGm2CafIrfQTZGWgkfKYbj+pNNsorZ7EotuZukc2MT70E0UbppVbtpBrqpzVzjNA==} + engines: {node: '>=v14.0.0', npm: '>=7.0.0'} + dev: false + + /@tootallnate/[email protected]: + resolution: {integrity: sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==} + engines: {node: '>= 6'} + requiresBuild: true + dev: false + optional: true + + /@types/[email protected]: + resolution: {integrity: sha512-/vPO1EPOs306Cvhwv7KfVfYvOJqA/S/AXjaHQiJboCZzcNDb+TIJFN9/2C9DZ//ijSKWioNyUxD792QmDJ+HKQ==} + dependencies: + '@types/ms': 0.7.31 + dev: false + + /@types/[email protected]: + resolution: {integrity: sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==} + dev: false + + /@types/[email protected]: + resolution: {integrity: sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==} + dev: false + + /@types/[email protected]: + resolution: {integrity: sha512-cOxcXsQ2sxiwkykdJqvyFS+MLQPLvIdwh5l6gNg8qF6s+C7XSkEWOZjK+XhUZd+mYvHV/180g2cnCcIl4l06Pw==} + + /@types/[email protected]: + resolution: {integrity: sha512-d/MUkJYdOeKycmm75Arql4M5+UuXmf4cHdHKsyw1GcvnNgL6s77UkgSgJ8TE/rI5PYsnwYq5jkcWBLuN/MpQ1A==} + dev: false + + /@types/[email protected]: + resolution: {integrity: sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg==} + dependencies: + '@types/node': 18.17.17 + dev: false + + /@vladfrangu/[email protected]: + resolution: {integrity: sha512-HIzRG7sy88UZjBJamssEczH5q7t5+axva19UbZLO6u0ySbYPrwzWiXBcC0WuHyhKKoeCyneH+FvYzKQq/zTtkQ==} + engines: {node: '>=v14.0.0', npm: '>=7.0.0'} + dev: false + + resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} + dev: false + + resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} + engines: {node: '>= 0.6'} + dependencies: + mime-types: 2.1.35 + negotiator: 0.6.3 + dev: false + + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + acorn: 8.10.0 + dev: true + + resolution: {integrity: sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true + + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} + dependencies: + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + dev: false + + resolution: {integrity: sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==} + engines: {node: '>= 8.0.0'} + requiresBuild: true + dependencies: + humanize-ms: 1.2.1 + dev: false + optional: true + + resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} + engines: {node: '>=8'} + requiresBuild: true + dependencies: + clean-stack: 2.2.0 + indent-string: 4.0.0 + dev: false + optional: true + + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + dev: true + + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + dependencies: + color-convert: 2.0.1 + + resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==} + dev: false + + resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==} + engines: {node: '>=10'} + dependencies: + delegates: 1.0.0 + readable-stream: 3.6.2 + dev: false + + resolution: {integrity: sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + requiresBuild: true + dependencies: + delegates: 1.0.0 + readable-stream: 3.6.2 + dev: false + optional: true + + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + dev: true + + resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} + dev: false + + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + dev: false + + resolution: {integrity: sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw==} + dev: false + + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + dev: false + + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + dependencies: + buffer: 5.7.1 + inherits: 2.0.4 + readable-stream: 3.6.2 + dev: false + + resolution: {integrity: sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + on-finished: 2.4.1 + qs: 6.11.0 + raw-body: 2.5.1 + type-is: 1.6.18 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + dev: false + + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + dev: false + + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + dev: false + + resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} + engines: {node: '>=10.16.0'} + dependencies: + streamsearch: 1.1.0 + dev: false + + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + dev: false + + resolution: {integrity: sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==} + engines: {node: '>= 10'} + requiresBuild: true + dependencies: + '@npmcli/fs': 1.1.1 + '@npmcli/move-file': 1.1.2 + chownr: 2.0.0 + fs-minipass: 2.1.0 + glob: 7.2.3 + infer-owner: 1.0.4 + lru-cache: 6.0.0 + minipass: 3.3.6 + minipass-collect: 1.0.2 + minipass-flush: 1.0.5 + minipass-pipeline: 1.2.4 + mkdirp: 1.0.4 + p-map: 4.0.0 + promise-inflight: 1.0.1 + rimraf: 3.0.2 + ssri: 8.0.1 + tar: 6.2.0 + unique-filename: 1.1.1 + transitivePeerDependencies: + - bluebird + dev: false + optional: true + + resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} + dependencies: + function-bind: 1.1.1 + get-intrinsic: 1.2.1 + dev: false + + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + dev: true + + resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==} + dev: false + + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} + dev: false + + resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} + engines: {node: '>=10'} + dev: false + + resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} + engines: {node: '>=6'} + requiresBuild: true + dev: false + optional: true + + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + dependencies: + color-name: 1.1.4 + + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} + dependencies: + color-name: 1.1.4 + simple-swizzle: 0.2.2 + dev: false + + resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} + hasBin: true + dev: false + + resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} + engines: {node: '>=12.5.0'} + dependencies: + color-convert: 2.0.1 + color-string: 1.9.1 + dev: false + + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + dependencies: + delayed-stream: 1.0.0 + dev: false + + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + resolution: {integrity: sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==} + engines: {'0': node >= 6.0} + dependencies: + buffer-from: 1.1.2 + inherits: 2.0.4 + readable-stream: 3.6.2 + typedarray: 0.0.6 + dev: false + + resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} + dev: false + + resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} + engines: {node: '>= 0.6'} + dependencies: + safe-buffer: 5.2.1 + dev: false + + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + dev: false + + resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} + dev: false + + resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} + engines: {node: '>= 0.6'} + dev: false + + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + dev: true + + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.0.0 + dev: false + + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.2 + + resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} + engines: {node: '>=10'} + dependencies: + mimic-response: 3.1.0 + dev: false + + resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} + engines: {node: '>=4.0.0'} + dev: false + + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + dev: true + + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + dev: false + + resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} + dev: false + + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + dev: false + + resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + dev: false + + resolution: {integrity: sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==} + engines: {node: '>=8'} + dev: false + + resolution: {integrity: sha512-bz/NDyG0KBo/tY14vSkrwQ/n3HKPf87a0WFW/1M9+tXYK+vp5Z5EksawfCWo2zkAc6o7CClc0eff1Pjrqznlwg==} + dev: false + + resolution: {integrity: sha512-X4CDiMnDbA3s3RaUXWXmgAIbY1uxab3fqe3qwzg5XutR3wjqi7M3IkgQbsIBzpqBN2YWr/Qdv7JrFRqSgb4TFg==} + dev: false + + resolution: {integrity: sha512-ZOiEEFUO7AJglSdAvVB6A61A14wx87vf63d7K/jBzR/bJAUdYS5RwqZZ7JW9UHkH/insSSczQTzE68Ej2HmCrQ==} + dependencies: + discord.js: 14.13.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + dev: false + + resolution: {integrity: sha512-BOHLmSkMw376qTvmDdKfgPQ83CYV9UR1YNRPVX9Cq57fu0PDCl9eGx/NfilThzJx7DdojRiliVyp17PlfKlR3w==} + dependencies: + discord.js: 14.13.0 + node-fetch: 2.7.0 + transitivePeerDependencies: + - bufferutil + - encoding + - utf-8-validate + dev: false + + resolution: {integrity: sha512-Kufdvg7fpyTEwANGy9x7i4od4yu5c6gVddGi5CKm4Y5a6sF0VBODObI3o0Bh7TGCj0LfNT8Qp8z04wnLFzgnbA==} + engines: {node: '>=16.11.0'} + dependencies: + '@discordjs/builders': 1.6.5 + '@discordjs/collection': 1.5.3 + '@discordjs/formatters': 0.3.2 + '@discordjs/rest': 2.0.1 + '@discordjs/util': 1.0.1 + '@discordjs/ws': 1.0.1 + '@sapphire/snowflake': 3.5.1 + '@types/ws': 8.5.5 + discord-api-types: 0.37.50 + fast-deep-equal: 3.1.3 + lodash.snakecase: 4.1.1 + tslib: 2.6.2 + undici: 5.22.1 + ws: 8.14.2 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + dev: false + + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + dependencies: + esutils: 2.0.3 + dev: true + + resolution: {integrity: sha512-I9OvvrHp4pIARv4+x9iuewrWycX6CcZtoAu1XrzPxc5UygMJXJZYmBsynku8IkrJwgypE5DGNjDPmPRhDCptUg==} + engines: {node: '>=10'} + dev: false + + resolution: {integrity: sha512-iGCHkfUc5kFekGiqhe8B/mdaurD+lakO9txNnTvKtA6PISrw86LgqHvRzWYPyoE2Ph5aMIrCw9/uko6XHTKCwA==} + dev: false + + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + dev: false + + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + dev: false + + resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} + engines: {node: '>= 0.8'} + dev: false + + resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} + requiresBuild: true + dependencies: + iconv-lite: 0.6.3 + dev: false + optional: true + + resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} + dependencies: + once: 1.4.0 + dev: false + + resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} + engines: {node: '>=6'} + dev: false + + resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} + requiresBuild: true + dev: false + optional: true + + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + dev: false + + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + dev: true + + resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + dev: true + + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + resolution: {integrity: sha512-jw03ENfm6VJI0jA9U+8H5zfl5b+FvuU3YYvZRdZHOlU2ggJkxrlkJH4HcDrZpj6YwD8kuYqvQM8LyesoazrSOQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + hasBin: true + dependencies: + '@eslint-community/eslint-utils': 4.4.0([email protected]) + '@eslint-community/regexpp': 4.8.1 + '@eslint/eslintrc': 2.1.2 + '@eslint/js': 8.49.0 + '@humanwhocodes/config-array': 0.11.11 + '@humanwhocodes/module-importer': 1.0.1 + '@nodelib/fs.walk': 1.2.8 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.3 + debug: 4.3.4 + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esquery: 1.5.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + glob-parent: 6.0.2 + globals: 13.21.0 + graphemer: 1.4.0 + ignore: 5.2.4 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + js-yaml: 4.1.0 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.3 + strip-ansi: 6.0.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + dev: true + + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + acorn: 8.10.0 + acorn-jsx: 5.3.2([email protected]) + eslint-visitor-keys: 3.4.3 + dev: true + + resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} + engines: {node: '>=0.10'} + dependencies: + estraverse: 5.3.0 + dev: true + + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + dependencies: + estraverse: 5.3.0 + dev: true + + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + dev: true + + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + dev: true + + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + dev: false + + resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} + engines: {node: '>=6'} + dev: false + + resolution: {integrity: sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==} + engines: {node: '>= 0.10.0'} + dependencies: + accepts: 1.3.8 + array-flatten: 1.1.1 + body-parser: 1.20.1 + content-disposition: 0.5.4 + content-type: 1.0.5 + cookie: 0.5.0 + cookie-signature: 1.0.6 + debug: 2.6.9 + depd: 2.0.0 + encodeurl: 1.0.2 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 1.2.0 + fresh: 0.5.2 + http-errors: 2.0.0 + merge-descriptors: 1.0.1 + methods: 1.1.2 + on-finished: 2.4.1 + parseurl: 1.3.3 + path-to-regexp: 0.1.7 + proxy-addr: 2.0.7 + qs: 6.11.0 + range-parser: 1.2.1 + safe-buffer: 5.2.1 + send: 0.18.0 + serve-static: 1.15.0 + setprototypeof: 1.2.0 + statuses: 2.0.1 + type-is: 1.6.18 + utils-merge: 1.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + dev: false + + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} + dev: false + + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + dev: true + + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + dev: true + + resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} + dependencies: + reusify: 1.0.4 + dev: true + + resolution: {integrity: sha512-gyGTIf5kgmLDmH7Rwj8vMmNK46bjXKKofHS2gY+LUqoTe/iybVuTuvnhJQR2tZHlKlDG7A/BIH7cRa2jWDKgWw==} + engines: {node: '>=10'} + requiresBuild: true + dependencies: + '@derhuerst/http-basic': 8.2.4 + env-paths: 2.2.1 + https-proxy-agent: 5.0.1 + progress: 2.0.3 + transitivePeerDependencies: + - supports-color + dev: false + + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + flat-cache: 3.1.0 + dev: true + + resolution: {integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==} + engines: {node: '>= 0.8'} + dependencies: + debug: 2.6.9 + encodeurl: 1.0.2 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.1 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + dev: false + + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + dev: true + + resolution: {integrity: sha512-OHx4Qwrrt0E4jEIcI5/Xb+f+QmJYNj2rrK8wiIdQOIrB9WrrJL8cjZvXdXuBTkkEwEqLycb5BeZDV1o2i9bTew==} + engines: {node: '>=12.0.0'} + dependencies: + flatted: 3.2.9 + keyv: 4.5.3 + rimraf: 3.0.2 + dev: true + + resolution: {integrity: sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==} + dev: true + + resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} + engines: {node: '>= 6'} + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.35 + dev: false + + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + dev: false + + resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} + engines: {node: '>= 0.6'} + dev: false + + resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} + dev: false + + resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} + engines: {node: '>= 8'} + dependencies: + minipass: 3.3.6 + dev: false + + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + requiresBuild: true + + resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} + dev: false + + resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==} + engines: {node: '>=10'} + dependencies: + aproba: 2.0.0 + color-support: 1.1.3 + console-control-strings: 1.1.0 + has-unicode: 2.0.1 + object-assign: 4.1.1 + signal-exit: 3.0.7 + string-width: 4.2.3 + strip-ansi: 6.0.1 + wide-align: 1.1.5 + dev: false + + resolution: {integrity: sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + requiresBuild: true + dependencies: + aproba: 2.0.0 + color-support: 1.1.3 + console-control-strings: 1.1.0 + has-unicode: 2.0.1 + signal-exit: 3.0.7 + string-width: 4.2.3 + strip-ansi: 6.0.1 + wide-align: 1.1.5 + dev: false + optional: true + + resolution: {integrity: sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==} + dependencies: + function-bind: 1.1.1 + has: 1.0.3 + has-proto: 1.0.1 + has-symbols: 1.0.3 + dev: false + + resolution: {integrity: sha512-FDSgeMqa7GnJDxt/q0AbrxbfeTyxp4ImxEw1e4nw6NUHA5FMhFUq33dTXI4Xdgcj1VQ1q5QLWF6WxFrJ8KCBOg==} + dev: false + + resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} + dev: false + + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + dependencies: + is-glob: 4.0.3 + dev: true + + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + + resolution: {integrity: sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==} + engines: {node: '>=8'} + dependencies: + type-fest: 0.20.2 + dev: true + + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + requiresBuild: true + dev: false + optional: true + + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + dev: true + + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==} + engines: {node: '>= 0.4'} + dev: false + + resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + engines: {node: '>= 0.4'} + dev: false + + resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} + dev: false + + resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} + engines: {node: '>= 0.4.0'} + dependencies: + function-bind: 1.1.1 + dev: false + + resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} + requiresBuild: true + dev: false + optional: true + + resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} + engines: {node: '>= 0.8'} + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.1 + toidentifier: 1.0.1 + dev: false + + resolution: {integrity: sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==} + engines: {node: '>= 6'} + requiresBuild: true + dependencies: + '@tootallnate/once': 1.1.2 + agent-base: 6.0.2 + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + dev: false + optional: true + + resolution: {integrity: sha512-bqX0XTF6fnXSQcEJ2Iuyr75yVakyjIDCqroJQ/aHfSdlM743Cwqoi2nDYMzLGWUcuTWGWy8AAvOKXTfiv6q9RA==} + dependencies: + '@types/node': 10.17.60 + dev: false + + resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} + engines: {node: '>= 6'} + dependencies: + agent-base: 6.0.2 + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + dev: false + + resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} + requiresBuild: true + dependencies: + ms: 2.1.3 + dev: false + optional: true + + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + dependencies: + safer-buffer: 2.1.2 + dev: false + + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + requiresBuild: true + dependencies: + safer-buffer: 2.1.2 + dev: false + optional: true + + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + dev: false + + resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} + engines: {node: '>= 4'} + dev: true + + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + dev: true + + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} + requiresBuild: true + dev: false + optional: true + + resolution: {integrity: sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==} + requiresBuild: true + dev: false + optional: true + + resolution: {integrity: sha512-6I/HUDeYFfuNCVS3td055BaXBwKYuzw7K3ExVMStBowKo9oOAMJIXIHvdyR3iboTCp1b+1i5DSkIZTcwIktuDw==} + engines: {'0': node >= 0.4.0} + dev: false + + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + requiresBuild: true + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + dev: false + + resolution: {integrity: sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==} + requiresBuild: true + dev: false + optional: true + + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + dev: false + + resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} + dev: false + + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + dev: true + + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + dev: false + + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + dependencies: + is-extglob: 2.1.1 + dev: true + + resolution: {integrity: sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==} + requiresBuild: true + dev: false + optional: true + + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + dev: true + + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + dependencies: + argparse: 2.0.1 + dev: true + + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + dev: true + + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + dev: true + + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + dev: true + + resolution: {integrity: sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==} + dependencies: + json-buffer: 3.0.1 + dev: true + + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + dev: true + + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + dependencies: + p-locate: 5.0.0 + dev: true + + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + dev: true + + resolution: {integrity: sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==} + dev: false + + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + dev: false + + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + dependencies: + yallist: 4.0.0 + dev: false + + resolution: {integrity: sha512-PEDpPzHpKe5AxkVmQrNPHFRvPN2ELkkj3eIg4IZO9JdhBiAY3aU53lgYXs9j8B7lpza+QiW0UA4QHCH7EskSeg==} + dev: false + + resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} + engines: {node: '>=8'} + dependencies: + semver: 6.3.1 + dev: false + + resolution: {integrity: sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==} + engines: {node: '>= 10'} + requiresBuild: true + dependencies: + agentkeepalive: 4.5.0 + cacache: 15.3.0 + http-cache-semantics: 4.1.1 + http-proxy-agent: 4.0.1 + https-proxy-agent: 5.0.1 + is-lambda: 1.0.1 + lru-cache: 6.0.0 + minipass: 3.3.6 + minipass-collect: 1.0.2 + minipass-fetch: 1.4.1 + minipass-flush: 1.0.5 + minipass-pipeline: 1.2.4 + negotiator: 0.6.3 + promise-retry: 2.0.1 + socks-proxy-agent: 6.2.1 + ssri: 8.0.1 + transitivePeerDependencies: + - bluebird + - supports-color + dev: false + optional: true + + resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} + engines: {node: '>= 0.6'} + dev: false + + resolution: {integrity: sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==} + dev: false + + resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} + engines: {node: '>= 0.6'} + dev: false + + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + dev: false + + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + dependencies: + mime-db: 1.52.0 + dev: false + + resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} + engines: {node: '>=4'} + hasBin: true + dev: false + + resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} + engines: {node: '>=10'} + dev: false + + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + dependencies: + brace-expansion: 1.1.11 + + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + dev: false + + resolution: {integrity: sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==} + engines: {node: '>= 8'} + requiresBuild: true + dependencies: + minipass: 3.3.6 + dev: false + optional: true + + resolution: {integrity: sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==} + engines: {node: '>=8'} + requiresBuild: true + dependencies: + minipass: 3.3.6 + minipass-sized: 1.0.3 + minizlib: 2.1.2 + optionalDependencies: + encoding: 0.1.13 + dev: false + optional: true + + resolution: {integrity: sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==} + engines: {node: '>= 8'} + requiresBuild: true + dependencies: + minipass: 3.3.6 + dev: false + optional: true + + resolution: {integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==} + engines: {node: '>=8'} + requiresBuild: true + dependencies: + minipass: 3.3.6 + dev: false + optional: true + + resolution: {integrity: sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==} + engines: {node: '>=8'} + requiresBuild: true + dependencies: + minipass: 3.3.6 + dev: false + optional: true + + resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} + engines: {node: '>=8'} + dependencies: + yallist: 4.0.0 + dev: false + + resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} + engines: {node: '>=8'} + dev: false + + resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} + engines: {node: '>= 8'} + dependencies: + minipass: 3.3.6 + yallist: 4.0.0 + dev: false + + resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} + dev: false + + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + engines: {node: '>=10'} + hasBin: true + dev: false + + resolution: {integrity: sha512-72j3aNyuIsDxdF1i7CEgV2FfxM1r6aaqJyLB2vwb33mXYyoyLly+F1zbWqhA3/bVIoJ4szlUoMbUnVdid32NUQ==} + dependencies: + moment: 2.29.4 + dev: false + + resolution: {integrity: sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==} + dev: false + + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + dev: false + + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + dev: false + + resolution: {integrity: sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==} + dev: false + + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + dev: true + + resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} + engines: {node: '>= 0.6'} + dev: false + + resolution: {integrity: sha512-2s6B2CWZM//kPgwnuI0KrYwNjfdByE25zvAaEpq9IH4zcNsarH8Ihu/UuX6XMPEogDAxkuUFeZn60pXNHAqn3A==} + engines: {node: '>=10'} + dependencies: + semver: 7.5.4 + dev: false + + resolution: {integrity: sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==} + dev: false + + resolution: {integrity: sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==} + dev: false + + resolution: {integrity: sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==} + dev: false + + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + dependencies: + whatwg-url: 5.0.0 + dev: false + + resolution: {integrity: sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==} + engines: {node: '>= 10.12.0'} + hasBin: true + requiresBuild: true + dependencies: + env-paths: 2.2.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + make-fetch-happen: 9.1.0 + nopt: 5.0.0 + npmlog: 6.0.2 + rimraf: 3.0.2 + semver: 7.5.4 + tar: 6.2.0 + which: 2.0.2 + transitivePeerDependencies: + - bluebird + - supports-color + dev: false + optional: true + + resolution: {integrity: sha512-xOqifvw/3N+tIeeC80/TNJXkoxDZm8JWc7/0VBkJ86ttQTlJvoicuVaeJlKKHYRlmC2O6ygNIhNPwLYX4bxqdA==} + engines: {node: '>=8'} + dependencies: + form-data: 4.0.0 + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + dev: false + + resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==} + engines: {node: '>=6'} + hasBin: true + dependencies: + abbrev: 1.1.1 + dev: false + + resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==} + dependencies: + are-we-there-yet: 2.0.0 + console-control-strings: 1.1.0 + gauge: 3.0.2 + set-blocking: 2.0.0 + dev: false + + resolution: {integrity: sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + requiresBuild: true + dependencies: + are-we-there-yet: 3.0.1 + console-control-strings: 1.1.0 + gauge: 4.0.4 + set-blocking: 2.0.0 + dev: false + optional: true + + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + dev: false + + resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==} + dev: false + + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + dependencies: + ee-first: 1.1.1 + dev: false + + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + dependencies: + wrappy: 1.0.2 + + resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} + engines: {node: '>= 0.8.0'} + dependencies: + '@aashutoshrathi/word-wrap': 1.2.6 + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + dev: true + + resolution: {integrity: sha512-ajB8csaHLBvJOYsHJkp8YdO2FvlBbf/ZxaYQwXXRDyQ84UoE+uTuLXxqd0shekXMX6Qr/pt/DDyLMRAMsgfWzg==} + dev: false + + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + dependencies: + yocto-queue: 0.1.0 + dev: true + + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + dependencies: + p-limit: 3.1.0 + dev: true + + resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==} + engines: {node: '>=10'} + requiresBuild: true + dependencies: + aggregate-error: 3.1.0 + dev: false + optional: true + + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + dependencies: + callsites: 3.1.0 + dev: true + + resolution: {integrity: sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg==} + dev: false + + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + dev: false + + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + dev: true + + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + requiresBuild: true + + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + dev: true + + resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==} + dev: false + + resolution: {integrity: sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==} + dev: false + + resolution: {integrity: sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==} + engines: {node: '>=10'} + hasBin: true + dependencies: + detect-libc: 2.0.2 + expand-template: 2.0.3 + github-from-package: 0.0.0 + minimist: 1.2.8 + mkdirp-classic: 0.5.3 + napi-build-utils: 1.0.2 + node-abi: 3.47.0 + pump: 3.0.0 + rc: 1.2.8 + simple-get: 4.0.1 + tar-fs: 2.1.1 + tunnel-agent: 0.6.0 + dev: false + + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + dev: true + + resolution: {integrity: sha512-IQdl0Q01m4LrkN1EGIE9lphov5Hy7WWlH6ulf5QdGePLlPas9p2mhgddTEHrlaXYjjFToM1/rWuwF37VF4taaA==} + peerDependencies: + '@discordjs/opus': '>=0.8.0 <1.0.0' + ffmpeg-static: ^5.0.2 || ^4.2.7 || ^3.0.0 || ^2.4.0 + node-opus: ^0.3.3 + opusscript: ^0.0.8 + peerDependenciesMeta: + '@discordjs/opus': + optional: true + ffmpeg-static: + optional: true + node-opus: + optional: true + opusscript: + optional: true + dependencies: + '@discordjs/opus': 0.5.3 + ffmpeg-static: 4.4.1 + dev: false + + resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} + engines: {node: '>=0.4.0'} + dev: false + + resolution: {integrity: sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==} + requiresBuild: true + peerDependencies: + bluebird: '*' + peerDependenciesMeta: + bluebird: + optional: true + dev: false + optional: true + + resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==} + engines: {node: '>=10'} + requiresBuild: true + dependencies: + err-code: 2.0.3 + retry: 0.12.0 + dev: false + optional: true + + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + dev: false + + resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} + dependencies: + end-of-stream: 1.4.4 + once: 1.4.0 + dev: false + + resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} + engines: {node: '>=6'} + dev: true + + resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==} + engines: {node: '>=0.6'} + dependencies: + side-channel: 1.0.4 + dev: false + + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + dev: true + + resolution: {integrity: sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==} + dev: false + + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + dev: false + + resolution: {integrity: sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==} + engines: {node: '>= 0.8'} + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + unpipe: 1.0.0 + dev: false + + resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} + hasBin: true + dependencies: + deep-extend: 0.6.0 + ini: 1.3.8 + minimist: 1.2.8 + strip-json-comments: 2.0.1 + dev: false + + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + dev: false + + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + dev: true + + resolution: {integrity: sha512-XgmCoxKWkDofwH8WddD0w85ZfqYz+ZHlr5yo+3YUCfycWawU56T5ckWXsScsj5B8tqUcIG67DxXByo3VUgiAdA==} + dev: false + + resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} + engines: {node: '>= 4'} + requiresBuild: true + dev: false + optional: true + + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + dev: true + + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + hasBin: true + dependencies: + glob: 7.2.3 + + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + dependencies: + queue-microtask: 1.2.3 + dev: true + + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + dev: false + + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + dev: false + + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + dev: false + + resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} + engines: {node: '>=10'} + hasBin: true + dependencies: + lru-cache: 6.0.0 + dev: false + + resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} + engines: {node: '>= 0.8.0'} + dependencies: + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + encodeurl: 1.0.2 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 0.5.2 + http-errors: 2.0.0 + mime: 1.6.0 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.1 + transitivePeerDependencies: + - supports-color + dev: false + + resolution: {integrity: sha512-G9c0qlIWQSK29pR/5U2JF5dDQeqqHRragoyahj/Nx4KOOQ3CPPfzxnfqFPCSB7x5UgjOgnZ61nSxz+fjDpRlJg==} + engines: {node: '>= 10.0.0'} + dev: false + + resolution: {integrity: sha512-GkeCbqgaIcpyZ1EyXrDNIwktbfMldHAGOVXHGM4x8bxGSRAOql5htDWofPvwpfL/FoZ59CaFmfO3Mosv1lDbQw==} + engines: {node: '>=10.0.0'} + peerDependencies: + ibm_db: '*' + mariadb: '*' + mysql2: '*' + oracledb: '*' + pg: '*' + pg-hstore: '*' + snowflake-sdk: '*' + sqlite3: '*' + tedious: '*' + peerDependenciesMeta: + ibm_db: + optional: true + mariadb: + optional: true + mysql2: + optional: true + oracledb: + optional: true + pg: + optional: true + pg-hstore: + optional: true + snowflake-sdk: + optional: true + sqlite3: + optional: true + tedious: + optional: true + dependencies: + '@types/debug': 4.1.8 + '@types/validator': 13.11.1 + debug: 4.3.4 + dottie: 2.0.6 + inflection: 1.13.4 + lodash: 4.17.21 + moment: 2.29.4 + moment-timezone: 0.5.43 + pg-connection-string: 2.6.2 + retry-as-promised: 7.0.4 + semver: 7.5.4 + sequelize-pool: 7.1.0 + sqlite3: 5.1.6 + toposort-class: 1.0.1 + uuid: 8.3.2 + validator: 13.11.0 + wkx: 0.5.0 + transitivePeerDependencies: + - supports-color + dev: false + + resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==} + engines: {node: '>= 0.8.0'} + dependencies: + encodeurl: 1.0.2 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 0.18.0 + transitivePeerDependencies: + - supports-color + dev: false + + resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + dev: false + + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + dev: false + + resolution: {integrity: sha512-KyLTWwgcR9Oe4d9HwCwNM2l7+J0dUQwn/yf7S0EnTtb0eVS4RxO0eUSvxPtzT4F3SY+C4K6fqdv/DO27sJ/v/w==} + engines: {node: '>=14.15.0'} + requiresBuild: true + dependencies: + color: 4.2.3 + detect-libc: 2.0.2 + node-addon-api: 6.1.0 + prebuild-install: 7.1.1 + semver: 7.5.4 + simple-get: 4.0.1 + tar-fs: 3.0.4 + tunnel-agent: 0.6.0 + dev: false + + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + dependencies: + shebang-regex: 3.0.0 + dev: true + + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + dev: true + + resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.1 + object-inspect: 1.12.3 + dev: false + + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + dev: false + + resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} + dev: false + + resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==} + dependencies: + decompress-response: 6.0.0 + once: 1.4.0 + simple-concat: 1.0.1 + dev: false + + resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} + dependencies: + is-arrayish: 0.3.2 + dev: false + + resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} + engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} + requiresBuild: true + dev: false + optional: true + + resolution: {integrity: sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==} + engines: {node: '>= 10'} + requiresBuild: true + dependencies: + agent-base: 6.0.2 + debug: 4.3.4 + socks: 2.7.1 + transitivePeerDependencies: + - supports-color + dev: false + optional: true + + resolution: {integrity: sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==} + engines: {node: '>= 10.13.0', npm: '>= 3.0.0'} + requiresBuild: true + dependencies: + ip: 2.0.0 + smart-buffer: 4.2.0 + dev: false + optional: true + + resolution: {integrity: sha512-olYkWoKFVNSSSQNvxVUfjiVbz3YtBwTJj+mfV5zpHmqW3sELx2Cf4QCdirMelhM5Zh+KDVaKgQHqCxrqiWHybw==} + requiresBuild: true + peerDependenciesMeta: + node-gyp: + optional: true + dependencies: + '@mapbox/node-pre-gyp': 1.0.11 + node-addon-api: 4.3.0 + tar: 6.2.0 + optionalDependencies: + node-gyp: 8.4.1 + transitivePeerDependencies: + - bluebird + - encoding + - supports-color + dev: false + + resolution: {integrity: sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==} + engines: {node: '>= 8'} + requiresBuild: true + dependencies: + minipass: 3.3.6 + dev: false + optional: true + + resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} + engines: {node: '>= 0.8'} + dev: false + + resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} + engines: {node: '>=10.0.0'} + dev: false + + resolution: {integrity: sha512-fQMzy2O/Q47rgwErk/eGeLu/roaFWV0jVsogDmrszM9uIw8L5OA+t+V93MgYlufNptfjmYR1tOMWhei/Eh7TQA==} + dependencies: + fast-fifo: 1.3.2 + queue-tick: 1.0.1 + dev: false + + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + dev: false + + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + dependencies: + safe-buffer: 5.2.1 + dev: false + + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + dependencies: + ansi-regex: 5.0.1 + + resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} + engines: {node: '>=0.10.0'} + dev: false + + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + dev: true + + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + dependencies: + has-flag: 4.0.0 + + resolution: {integrity: sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==} + dependencies: + chownr: 1.1.4 + mkdirp-classic: 0.5.3 + pump: 3.0.0 + tar-stream: 2.2.0 + dev: false + + resolution: {integrity: sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==} + dependencies: + mkdirp-classic: 0.5.3 + pump: 3.0.0 + tar-stream: 3.1.6 + dev: false + + resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} + engines: {node: '>=6'} + dependencies: + bl: 4.1.0 + end-of-stream: 1.4.4 + fs-constants: 1.0.0 + inherits: 2.0.4 + readable-stream: 3.6.2 + dev: false + + resolution: {integrity: sha512-B/UyjYwPpMBv+PaFSWAmtYjwdrlEaZQEhMIBFNC5oEG8lpiW8XjcSdmEaClj28ArfKScKHs2nshz3k2le6crsg==} + dependencies: + b4a: 1.6.4 + fast-fifo: 1.3.2 + streamx: 2.15.1 + dev: false + + resolution: {integrity: sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==} + engines: {node: '>=10'} + dependencies: + chownr: 2.0.0 + fs-minipass: 2.1.0 + minipass: 5.0.0 + minizlib: 2.1.2 + mkdirp: 1.0.4 + yallist: 4.0.0 + dev: false + + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + dev: true + + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + dev: false + + resolution: {integrity: sha512-OsLcGGbYF3rMjPUf8oKktyvCiUxSbqMMS39m33MAjLTC1DVIH6x3WSt63/M77ihI09+Sdfk1AXvfhCEeUmC7mg==} + dev: false + + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + dev: false + + resolution: {integrity: sha512-k43M7uCG1AkTyxgnmI5MPwKoUvS/bRvLvUb7+Pgpdlmok8AoqmUaZxUUw8zKM5B1lqZrt41GjYgnvAi0fppqgQ==} + dev: false + + resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} + dev: false + + resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} + dependencies: + safe-buffer: 5.2.1 + dev: false + + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + dev: true + + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + dev: true + + resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} + engines: {node: '>= 0.6'} + dependencies: + media-typer: 0.3.0 + mime-types: 2.1.35 + dev: false + + resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} + dev: false + + resolution: {integrity: sha512-Ji2IJhFXZY0x/0tVBXeQwgPlLWw13GVzpsWPQ3rV50IFMMof2I55PZZxtm4P6iNq+L5znYN9nSTAq0ZyE6lSJw==} + engines: {node: '>=14.0'} + dependencies: + busboy: 1.6.0 + dev: false + + resolution: {integrity: sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==} + requiresBuild: true + dependencies: + unique-slug: 2.0.2 + dev: false + optional: true + + resolution: {integrity: sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==} + requiresBuild: true + dependencies: + imurmurhash: 0.1.4 + dev: false + optional: true + + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + dev: false + + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + dependencies: + punycode: 2.3.0 + dev: true + + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + dev: false + + resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} + engines: {node: '>= 0.4.0'} + dev: false + + resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + hasBin: true + dev: false + + resolution: {integrity: sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==} + engines: {node: '>= 0.10'} + dev: false + + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + dev: false + + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + dev: false + + resolution: {integrity: sha512-yCajTSN8KYz3Ek+qtVThqHICrQ/8f6OAh0qrmUKcnXbO/8u4S6STUfi+LEVMAq1reiuL9qHmKL3YsIkU5eDIiw==} + dependencies: + chalk: 4.1.2 + node-superfetch: 0.2.3 + transitivePeerDependencies: + - encoding + dev: false + + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + dev: false + + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + dependencies: + isexe: 2.0.0 + + resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} + dependencies: + string-width: 4.2.3 + dev: false + + resolution: {integrity: sha512-Xng/d4Ichh8uN4l0FToV/258EjMGU9MGcA0HV2d9B/ZpZB3lqQm7nkOdZdm5GhKtLLhAE7PiVQwN4eN+2YJJUg==} + dependencies: + '@types/node': 18.17.17 + dev: false + + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + resolution: {integrity: sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==} + 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 + dev: false + + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + dev: false + + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + dev: true diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..36bed50 --- /dev/null +++ b/readme.md @@ -0,0 +1,26 @@ +# SNS Chan +Official Custom Discord Bot for Supernoobs Discord Server + + +## running +--- +made for linux or unix systems, running elsewhere isnt supported but should probably work:3 +(for windows you can probably use msys or git bash) + +- `npm i` +- put tokens and api keys in .env-example, and move it to .env +- `sh buildconfig.sh` +- `node .` or `sh snschan.sh` (to restart on error) + + +## todo ~ +--- +- [x] settings for each command +- [x] restricted channels +- [x] restrict to a channel +- [x] cooldown +- [ ] test commands +- [ ] reset/refresh command +- [ ] settings webui +- [ ] settings via discord commands +- [ ] slash commands diff --git a/snschan.sh b/snschan.sh new file mode 100644 index 0000000..a0b60ad --- /dev/null +++ b/snschan.sh @@ -0,0 +1,7 @@ +echo Starting.. +while true +do +node index.js +wait +sleep 10 +done diff --git a/src/db.js b/src/db.js new file mode 100644 index 0000000..aba84f0 --- /dev/null +++ b/src/db.js @@ -0,0 +1,69 @@ +const path = require('path'); +const { Sequelize, DataTypes } = require('sequelize'); + +const _db_raw = new Sequelize({ + dialect: 'sqlite', + storage: path.join(__dirname, '../db/db.sqlite'), + logging:false +}); +let db = {_raw:_db_raw} + +db.Tickets = _db_raw.define('Tickets', { + ticket: DataTypes.TEXT, + message: DataTypes.TEXT, + status: DataTypes.TEXT, + author: DataTypes.TEXT, + name: DataTypes.TEXT, + created: DataTypes.TEXT, + messages: DataTypes.STRING, + attachments: DataTypes.STRING, + pfp: DataTypes.STRING, +}, { +}); + +db.Sticky = _db_raw.define('Sticky', { + embed: DataTypes.BOOLEAN, + embed_color: DataTypes.TEXT, + channel: DataTypes.TEXT, + message: DataTypes.TEXT, + last_message: DataTypes.TEXT, +}, { +}); + +db.Auto_React = _db_raw.define('Auto_React', { + channel: DataTypes.TEXT, + emote: DataTypes.TEXT, +}, { +}); + +db.Timed_Message = _db_raw.define('Timed_Message', { + embed: DataTypes.BOOLEAN, + embed_color: DataTypes.TEXT, + guild: DataTypes.TEXT, + channel: DataTypes.TEXT, + message: DataTypes.TEXT, + last_message: DataTypes.TEXT, + last_message_time: DataTypes.TEXT, + delay: DataTypes.TEXT, +}, { +}); + +db.BattleShip = _db_raw.define('BattleShip', { + turn: DataTypes.INTEGER, + p1_id: DataTypes.TEXT, + p2_id: DataTypes.TEXT, + p1_board: DataTypes.TEXT, + p2_board: DataTypes.TEXT, +}, { +}); + +try { + db._raw.authenticate(); + console.log('db connected'); +} catch (error) { + console.error('Unable to connect to the database:', error); +} + +_db_raw.sync() + +module.exports = db
\ No newline at end of file diff --git a/src/settings.js b/src/settings.js new file mode 100644 index 0000000..87ba87b --- /dev/null +++ b/src/settings.js @@ -0,0 +1,17 @@ +let debug = 0; +let server = debug?"545076257369358336":"486957006628847626"; +let admin_c = debug?"545076257369358338":"753145819820982282"; +module.exports = { + "defaultColor": [43,45,49], + "allowed-servers":["486957006628847626","545076257369358336"], + "preloads": { + [server]: { + [admin_c]:{ + name: "admin-chan", + }, + "748790869938929737":{ + name: "logging", + } + } + } +}
\ No newline at end of file diff --git a/src/util.js b/src/util.js new file mode 100644 index 0000000..ef1cf50 --- /dev/null +++ b/src/util.js @@ -0,0 +1,104 @@ +const rem_emp = function (e) {return e !== "";} + +module.exports = { + upload_limit(guild){ + //https://stackoverflow.com/questions/66396146/get-channels-filesize-limit + switch (guild.premiumTier) { + case 'TIER_3': return 100 + case 'TIER_2': return 50 + default: return 8 + } + }, + limit_exp(exp,length){ + exp = exp.split('e') + for(let i = 0; i!=length; i++) + exp[0]+="0" + exp[0] = exp[0].substring(0,length+2) + return exp.join('e') + }, + rem_emp, + parse_inp(str){ + let open = false; + let out = [] + let cur = "" + for(let c of str){ + if(c=='"'){ + if(open){ + out.push(cur) + cur="" + } + open=!open; + } + else { + if(!open){ + if(c==' '){ + out.push(cur) + cur="" + } else { + cur+=c + } + } else { + cur+=c + } + + } + } + out.push(cur) + return out.filter(rem_emp) + }, + similarity(s1, s2) { + //https://stackoverflow.com/questions/10473745/compare-strings-javascript-return-of-likely + function editDistance(s1, s2) { + s1 = s1.toLowerCase(); + s2 = s2.toLowerCase(); + + var costs = new Array(); + for (var i = 0; i <= s1.length; i++) { + var lastValue = i; + for (var j = 0; j <= s2.length; j++) { + if (i == 0) + costs[j] = j; + else { + if (j > 0) { + var newValue = costs[j - 1]; + if (s1.charAt(i - 1) != s2.charAt(j - 1)) + newValue = Math.min(Math.min(newValue, lastValue), + costs[j]) + 1; + costs[j - 1] = lastValue; + lastValue = newValue; + } + } + } + if (i > 0) + costs[s2.length] = lastValue; + } + return costs[s2.length]; + } + var longer = s1; + var shorter = s2; + if (s1.length < s2.length) { + longer = s2; + shorter = s1; + } + var longerLength = longer.length; + if (longerLength == 0) { + return 1.0; + } + return (longerLength - editDistance(longer, shorter)) / parseFloat(longerLength); + }, + + deepCopy(src) { + //https://stackoverflow.com/questions/122102/what-is-the-most-efficient-way-to-deep-clone-an-object-in-javascript/49497485 + let target = Array.isArray(src) ? [] : {}; + for (let prop in src) { + let value = src[prop]; + if(value && typeof value === 'object') { + target[prop] = deepCopy(value); + } else { + target[prop] = value; + } + } + return target; + } + +}
\ No newline at end of file diff --git a/src/webui.js b/src/webui.js new file mode 100644 index 0000000..8a9fa56 --- /dev/null +++ b/src/webui.js @@ -0,0 +1,40 @@ + +const key = process.env.WEBUI_KEY +const bodyParser = require("body-parser"); +const express = require('express'); +let db = require("../src/db"); +const path = require("path") +const Tickets = db.Tickets + +const app = express(); +app.use(bodyParser.urlencoded({ extended: false })); +app.use(bodyParser.json()); +const port = 7001; +app.get('/tickets', async (req, res) => { + res.sendFile(path.join(__dirname+'/../html/tickets.html')) +}) +app.post('/tickets/data', async (req, res) => { + if(req.body.key == key) + res.send(await Tickets.findAll()) + else + res.send('failed') +}) +app.post('/tickets/edit', async (req, res) => { + if(req.body.key == key){ + console.log('pass') + res.send('pass') + } + else + res.send('failed') +}) + +app.get('/settings', async (req, res) => { + res.sendFile(path.join(__dirname+'/../html/settings.html')) +}) +app.post('/settings/data', async (req, res) => { + if(req.body.key == key) + res.send('todo:P') + else + res.send('failed') +}) +app.listen(port, () => console.log(`listening at http://localhost:${port}`)); |