aboutsummaryrefslogtreecommitdiff
path: root/commands
diff options
context:
space:
mode:
authorame <[email protected]>2023-09-26 23:12:35 -0500
committerame <[email protected]>2023-09-26 23:12:35 -0500
commitadc05627661e4b229037dc1e43410846078f4996 (patch)
tree8bbd7d34085007e36207f8e8c5976b7de64a7b3b /commands
init
Diffstat (limited to 'commands')
-rw-r--r--commands/games/battleship.js200
-rw-r--r--commands/games/defaults/battleship.js.json2
-rw-r--r--commands/mod/autoreact.js57
-rw-r--r--commands/mod/defaults/autoreact.js.json2
-rw-r--r--commands/mod/defaults/log.js.json2
-rw-r--r--commands/mod/defaults/motw.js.json2
-rw-r--r--commands/mod/defaults/search.js.json2
-rw-r--r--commands/mod/defaults/sticky.js.json2
-rw-r--r--commands/mod/defaults/ticket.js.json2
-rw-r--r--commands/mod/defaults/timed-message.js.json2
-rw-r--r--commands/mod/defaults/whois.js.json2
-rw-r--r--commands/mod/log.js27
-rw-r--r--commands/mod/motw.js34
-rw-r--r--commands/mod/search.js70
-rw-r--r--commands/mod/sticky.js57
-rw-r--r--commands/mod/ticket.js96
-rw-r--r--commands/mod/timed-message.js76
-rw-r--r--commands/mod/whois.js120
-rw-r--r--commands/util/defaults/emote.js.json45
-rw-r--r--commands/util/defaults/help.js.json2
-rw-r--r--commands/util/defaults/poll.js.json2
-rw-r--r--commands/util/defaults/say.js.json2
-rw-r--r--commands/util/defaults/status.js.json2
-rw-r--r--commands/util/emote.js47
-rw-r--r--commands/util/help.js140
-rw-r--r--commands/util/poll.js61
-rw-r--r--commands/util/say.js36
-rw-r--r--commands/util/status.js63
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&&param.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