diff options
author | ame <[email protected]> | 2023-09-26 23:12:35 -0500 |
---|---|---|
committer | ame <[email protected]> | 2023-09-26 23:12:35 -0500 |
commit | adc05627661e4b229037dc1e43410846078f4996 (patch) | |
tree | 8bbd7d34085007e36207f8e8c5976b7de64a7b3b /commands |
init
Diffstat (limited to 'commands')
28 files changed, 1155 insertions, 0 deletions
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 |