21 Commits

Author SHA1 Message Date
ION606 df4d21f487 Update README.md 2022-12-27 11:12:51 -08:00
ION606 697b1c0c76 Removed member message from the level-up card 2022-12-26 15:22:50 -08:00
ION606 914dc7ece4 Fixed a bug arising from new debug definitions 2022-12-26 14:49:25 -08:00
ION606 ebb34c9f89 Added cards to the leveling system, and fixed some bugs. Note that node-fetch has been bumped DOWN to v2 to avoid EMS format 2022-12-26 14:40:50 -08:00
ION606 5292bd8a09 I'm dumb yea 2022-12-19 20:49:46 -05:00
ION606 9f5f94d49e Fixed an error and added a warning message when the bot crashes 2022-12-19 20:48:01 -05:00
ION606 a41e602036 Added the 'reactionrole' command and removed some redundancies in code 2022-12-19 17:20:04 -05:00
ION606 7b247e462f Fixed a typo 2022-12-04 14:18:26 -05:00
ION606 6ae725d211 Added the framework for the message-leveling system 2022-11-18 11:01:15 -05:00
ION606 1b1932015e Transitioned all working games to Slash Commands (not Battle) 2022-11-16 16:53:24 -05:00
ION606 1324c3db8f Added Slash Command registration timing 2022-10-24 20:49:42 -04:00
ION606 b75cb7b495 Remove the 'meme' command 2022-10-24 20:44:34 -04:00
ION606 df7d79e69a Added the ability to add custom loading banners and started transitioning away from the Canvas module 2022-10-24 20:13:15 -04:00
ION606 2ff7a84bee Ignore this 2022-10-24 11:03:40 -04:00
ION606 8a47943764 Ignore this 2022-10-24 11:01:24 -04:00
ION606 47dc8e37cd Fixed a bug 2022-10-23 18:47:01 -04:00
ION606 c85bb3ef45 Added the "roles" section to the database 2022-10-23 18:38:07 -04:00
ION606 4043038162 Updated the way the verification system works and added custom admin role integration 2022-10-23 18:23:11 -04:00
ION606 18894961bd Added the 'import_ics' command 2022-10-11 16:29:49 -04:00
ION606 d69e7e0479 Fixed some remaining changes from the transition to Slash commands 2022-10-06 12:41:15 -04:00
ION606 b30fdc9e7f Added presence-changing commands to make dynamic bot moddification easier 2022-09-30 22:21:44 -04:00
41 changed files with 3092 additions and 1337 deletions
+3
View File
@@ -7,3 +7,6 @@ backup.json
!spec/LevelsXP.txt
*.sqlite
temp.js
easySetup.js
meme.js
+2
View File
@@ -1,3 +1,5 @@
# THIS REPOSITORY HAS BEEN MIRRORED TO THE SELMER BOT ORGANIZATION AND IS NOW READ-ONLY
# selmerBot
Authors: ION606, MajorDrools
+41
View File
@@ -0,0 +1,41 @@
const { addxp, BASE } = require('../db/econ.js');
async function setCard(bot, interaction) {
bot.mongoconnection.then(async (client) => {
const dbo = client.db(interaction.guildId).collection('SETUP');
dbo.findOne({_id: "LEVELING"}).then((doc) => {
const bkBuffer = Buffer.from(doc.card, 'base64');
});
});
}
function textToLevels(bot, message, xp_list) {
if (!bot.inDebugMode && message.guild.id == bot.home_server) { return; }
const author = message.author;
// doc.xp + (BASE.XP * doc.rank)
bot.mongoconnection.then((client) => {
const serverOpts = client.db(message.guild.id).collection('SETUP');
serverOpts.findOne({_id: "LEVELING"}).then((doc) => {
if (!doc) {
serverOpts.insertOne({_id: "LEVELING", enabled: false, card: undefined, text: undefined, col: "#FFFFFF"});
const server = bot.guilds.cache.get(message.guild.id);
server.members.fetch(message.guild.ownerId).then(function(owner) {
// Implement `setup leveling enable`
owner.send("Interactive Leveling has been added to your server!\nTo enable it, use `/setup leveling enable`");
});
} else if (doc.enabled) {
const dbo = client.db(message.guild.id).collection(author.id);
dbo.findOne({"balance": {$exists: true}}).then((doc) => {
const newXp = doc.xp + Math.ceil((BASE.XP * doc.rank) / 4);
addxp(bot, message, dbo, newXp, xp_list, true);
});
}
});
});
}
module.exports = {textToLevels};
+54
View File
@@ -0,0 +1,54 @@
const Discord = require('discord.js');
/**
* @description a fun little easter egg mention function
* @param {Discord.Message} message
*/
function replies(bot, message) {
const c = message.content.replace(`<@${bot.user.id}>`, "").toLowerCase().trim();
var s = "";
switch (c) {
case "hi":
case "hello":
case "hya":
s = "Hi there! :wave:";
break;
case "i love you":
s = "seek help :smile:";
break;
case "chicken nuggets":
s = "nom nom nom";
break;
case "chimkin nungits":
s = "marry me :heart_eyes:";
break;
case "😉":
s = ":wink:";
break;
case "😜":
s = "😜";
break;
case "🍆":
case "🍑":
case "🍌":
s = "❌";
break;
case "💩":
s = "🤮";
break;
default: s = "I'm not sure what that means! Please use `/help` for a comprehensive list of commands!\n\n_PS - If you want to make full use of the bot's AI capabilities, consider Selmer Bot Premium. See more at https://selmerbot.com/premium _";
}
message.reply(s).catch(() => { message.channel.send(s); });
}
module.exports = replies;
+4 -3
View File
@@ -5,7 +5,7 @@ module.exports = {
description: 'See where Selmer bot\'s code is stored!',
execute(interaction, Discord, Client, bot) {
const embd = new MessageEmbed()
.setAuthor({ name: "Selmer Bot", url: bot.inviteLink, iconURL: bot.user.displayAvatarURL() })
.setAuthor({ name: "Selmer Bot", url: bot.user.inviteLink, iconURL: bot.user.displayAvatarURL() })
.setThumbnail("https://github.com/ION606/selmer-bot-website/blob/main/assets/Selmer-icon.png?raw=true") // .setThumbnail('https://repository-images.githubusercontent.com/460670550/43932b23-d795-4334-838f-f33ee8f795c4')
.setDescription("Selmer Bot was created by ION606");
@@ -18,7 +18,7 @@ module.exports = {
new MessageButton()
.setStyle("LINK")
.setURL("https://www.selmerbot.com/")
.setURL("https://selmerbot.com/")
.setLabel("Website"),
new MessageButton()
@@ -28,5 +28,6 @@ module.exports = {
]);
interaction.reply({ embeds: [embd], components: [row] });
}, options: []
}, options: [],
isDm: true
}
+5 -5
View File
@@ -7,12 +7,12 @@ const tutoText = [
"__**SETUP AND LOGGING**__\nSet up your server to take full advantage of Selmer Bot's features, this includes moderation logging, custom welcome messages, calendar event pings and more\n_Note: Most of these commands are only available to the server owner_\n\n__***COMMANDS***__\nsetup",
"__**ECONOMY**__\nThese commands have to do with the inventory and currency system Selmer Bot uses, although I should note that as of now Selmer Coin holds no IRL value ;-;\n\n__***COMMANDS***__\ninventory, buy, sell, shop, work, rank, balance",
"__**MODERATION**__\nI mean....\n\n***__COMMANDS__***\nhelp admin, warn, mute, unmute, kick, ban, unban, lock, unlock, serverlock\n\n__***NOTE:***__\nThe user needs to have either _kick_ or _ban_ permissions to use these",
"__**AMIME AND MANGA**__\nGet info on your favorite Anime or Manga as a stat-sheet, a fancy embed, or have Selmer Bot describe it to you\n__***COMMANDS***__\nasearch, msearch",
"__**AMIME AND MANGA**__\nGet info on your favorite Anime or Manga as a stat-sheet, a fancy embed, or have Selmer Bot describe it to you\n\n__***COMMANDS***__\nasearch, msearch",
"__**GAMES**__\nAt the moment Selmer Bot offers three games: Trivia, Tic Tac Toe, and Minesweeper. Both Trivia and Tic Tac Toe can be played with other people. Trivia and Minesweeper can also be played solo. Selmer Bot also has a battle game where you can use weapons, potions, attack and defend, but this is still in beta\n\n__***COMMANDS***__\nhelp game, game battle game tictactoe, game trivia, game equip, game status, game hp, game classes, game quit\n\n__**NOTE**__\nDue to how complicated this feature is, it will not be migrated to slash commands for now",
"__**SELMER SPECIFIC**__\nThese commands will probably be found nowhere else\nThese include quotes (For legal reasons I have to state they aren't real quotes, mostly), as well as varius other things I based on good old Selmer\n\n__***COMMANDS***__\narrow, extracredit, tuto, profile, quotes",
"__**MISCELLANEOUS**__\nThese are the commands that are not really in any of the other categories. Don't be fooled, these are actually some of the most useful commands Selmer Bot has to offer. From playing music to web scraping to memes, I'm sure Selmer Bot has what you're looking for\n\n__***COMMANDS***__\nhelp, kareoke, link, meme, pickupline, audio, react, scrape, stocks, crypto",
"__**DM COMMANDS**__\nThese commands will only work in DM's. All these commands will only work with Selmer Bot Premium (it's on the next page).\nThese features include Reminders (AKA a calendar) and Selmer Bot's own chat AI\n\n__***COMMANDS***__\nchat, startconvo, endconvo, premium",
"__**SELMER BOT PREMIUM**__\nUse an AI chat, complete with semi-accurate IRL data, have Selmer Bot remind you of events with an easy-to-use interface and even a clickable calendar on the Selmer Bot website (_www.selmerbot.com_)\n\n__***COMMANDS***__\npremium, premium buy, premium manage, reminders",
"__**SELMER BOT PREMIUM**__\nUse an AI chat, complete with semi-accurate IRL data, have Selmer Bot remind you of events with an easy-to-use interface and even a clickable calendar on the Selmer Bot website (_ https://selmerbot.com _)\n\n__***COMMANDS***__\npremium, premium buy, premium manage, reminders",
"__**Thank you for completing the Selmer Bot Tutorial**__\n\nTry out Selmer Bot's features, play the games and most importantly, have fun\n\n-The Selmer Bot Team AKA ION606"
];
@@ -29,7 +29,7 @@ function postEmbd(bot, interaction, page, refered) {
te.setAuthor(author)
.setTitle("Selmer Bot Tutorial")
.setDescription(tutoText[page])
.setURL('https://www.selmerbot.com/')
.setURL('https://selmerbot.com/')
.setFooter({ text: `Page ${page + 1}` });
@@ -66,9 +66,9 @@ function postEmbd(bot, interaction, page, refered) {
row.addComponents(prevbtn, nextbtn);
if (page > 0 || refered) {
interaction.update({ content: '_Note: To see a full list of reminder stats visit www.selmerbot.com _', embeds: [te], components: [row] });
interaction.update({ content: '_Note: To see a full list of reminder stats visit selmerbot.com _', embeds: [te], components: [row] });
} else {
interaction.reply({ content: '_Note: To see a full list of reminder stats visit www.selmerbot.com _', embeds: [te], components: [row] });
interaction.channel.send({ content: '_Note: To see a full list of reminder stats visit selmerbot.com _', embeds: [te], components: [row] });
}
}
+17 -15
View File
@@ -8,24 +8,26 @@ module.exports = {
const arg = interaction.options.data[0];
const guild = bot.guilds.cache.get(interaction.guildId);
if (!checkRole(bot, guild, interaction.user.id)) { return interaction.reply('Insufficient Permissions!'); }
checkRole(bot, guild, interaction.user.id).then((isAllowed) => {
if (isAllowed) { return interaction.reply('Insufficient Permissions!'); }
var channel;
if (arg) {
channel = arg.channel;
} else {
channel = interaction.channel;
}
var channel;
if (arg) {
channel = arg.channel;
} else {
channel = interaction.channel;
}
let role = interaction.guild.roles.cache.find(r => r.name === "@everyone");
channel.permissionOverwrites.edit(role.id, {
VIEW_CHANNEL: true,
SEND_MESSAGES: false,
READ_MESSAGE_HISTORY: true,
ATTACH_FILES: false
let role = interaction.guild.roles.cache.find(r => r.name === "@everyone");
channel.permissionOverwrites.edit(role.id, {
VIEW_CHANNEL: true,
SEND_MESSAGES: false,
READ_MESSAGE_HISTORY: true,
ATTACH_FILES: false
});
interaction.reply(`${channel} has been locked!`);
});
interaction.reply(`${channel} has been locked!`);
},
options: [{name: 'channel', description: 'The channel to lock (defaults to current channel)', type: Constants.ApplicationCommandOptionTypes.CHANNEL, required: false}]
}
+34 -32
View File
@@ -99,51 +99,53 @@ function moderation_handler(bot, interaction, command) {
const guild = interaction.guild;
//Verify
if (!checkRole(bot, guild, interaction.user.id)) { return interaction.reply('Insufficient Permission!'); }
checkRole(bot, guild, interaction.user.id).then((isAllowed) => {
if (!isAllowed) { return interaction.reply('Insufficient Permission!'); }
const mentioned = interaction.options.data.filter((arg) => { return (arg.name == 'user'); })[0].user;
if (mentioned && mentioned.id == interaction.user.id) { return interaction.reply(`You can't ${command} yourself!`); }
const mentioned = interaction.options.data.filter((arg) => { return (arg.name == 'user'); })[0].user;
if (mentioned && mentioned.id == interaction.user.id) { return interaction.reply(`You can't ${command} yourself!`); }
const reasonInit = interaction.options.data.filter((arg) => { return (arg.name == 'reason'); })[0];
const reason = (reasonInit) ? reasonInit.value : "None";
const reasonInit = interaction.options.data.filter((arg) => { return (arg.name == 'reason'); })[0];
const reason = (reasonInit) ? reasonInit.value : "None";
const user = guild.members.resolve(mentioned.id);
const user = guild.members.resolve(mentioned.id);
if (user && (user.roles.highest.position > guild.members.resolve(bot.user).roles.highest.position)) {
return interaction.reply("I'm not high enough in the role hierarchy to do that!\n_To raise my place, go to **Server Settings -> Roles** then drag me up!_");
}
if (user && (user.roles.highest.position > guild.members.resolve(bot.user).roles.highest.position)) {
return interaction.reply("I'm not high enough in the role hierarchy to do that!\n_To raise my place, go to **Server Settings -> Roles** then drag me up!_");
}
// if (command != 'unban' && !mentioned || !reason) { return message.channel.send(`Please use the following format: _!<command> <user> <reason>`); }
// if (command == 'unban' && !args[0] && !reason) { return message.channel.send("Please use the following format: _!unban <user_tag>#<user_discriminator> <reason>\nExample: _!unban John#1122_"); }
// if (command == 'ban' && guild.members.cache.get(mentioned.id).bannable) { message.reply("This user is not bannable!"); } //Broken
// if (command == 'ban' && !message.guild.members.cache.get(mentioned.id)) { message.reply("This user is not in the server"); }
// if (command != 'unban' && !mentioned || !reason) { return message.channel.send(`Please use the following format: _!<command> <user> <reason>`); }
// if (command == 'unban' && !args[0] && !reason) { return message.channel.send("Please use the following format: _!unban <user_tag>#<user_discriminator> <reason>\nExample: _!unban John#1122_"); }
// if (command == 'ban' && guild.members.cache.get(mentioned.id).bannable) { message.reply("This user is not bannable!"); } //Broken
// if (command == 'ban' && !message.guild.members.cache.get(mentioned.id)) { message.reply("This user is not in the server"); }
switch (command) {
case 'kick': kick(guild, mentioned);
log(bot, interaction, command, mentioned, reason, SEVCODES.medium);
break;
switch (command) {
case 'kick': kick(guild, mentioned);
log(bot, interaction, command, mentioned, reason, SEVCODES.medium);
break;
case 'ban': toggle_ban(guild, interaction, mentioned, true, reason);
log(bot, interaction, command, mentioned, reason, SEVCODES.high);
break;
case 'ban': toggle_ban(guild, interaction, mentioned, true, reason);
log(bot, interaction, command, mentioned, reason, SEVCODES.high);
break;
//Leave the then() catch() thing, it needs to be async
case 'unban': toggle_ban(guild, interaction, false, reason).then((user) => { log(bot, interaction, command, user, reason, SEVCODES.none)}).catch((note) => { interaction.reply(note); });
break;
//Leave the then() catch() thing, it needs to be async
case 'unban': toggle_ban(guild, interaction, false, reason).then((user) => { log(bot, interaction, command, user, reason, SEVCODES.none)}).catch((note) => { interaction.reply(note); });
break;
case 'mute': toggle_mute(bot, guild, command, interaction, mentioned, reason, true);
break;
case 'mute': toggle_mute(bot, guild, command, interaction, mentioned, reason, true);
break;
case 'unmute': toggle_mute(bot, guild, command, interaction, mentioned, reason, true);
break;
case 'unmute': toggle_mute(bot, guild, command, interaction, mentioned, reason, true);
break;
// case 'timeout': timeOut(bot, mentioned, message, args, command, reason);
// shouldIlog = false;
// break;
// case 'timeout': timeOut(bot, mentioned, message, args, command, reason);
// shouldIlog = false;
// break;
default: console.log(`ERROR! Moderation Command "${command}" has somehow been used!`);
}
default: console.log(`ERROR! Moderation Command "${command}" has somehow been used!`);
}
});
}
module.exports = {
+182
View File
@@ -0,0 +1,182 @@
const {Interaction, Constants, Modal, TextInputComponent, MessageActionRow, MessageEmbed, MessageButton, Message} = require('discord.js');
const { checkRole } = require('./verify.js');
const { isValidUrl } = require('../dev only/setPresence.js');
const embdList = [{name: 'Title'}, {name: 'Image', desc: 'The image url'}, {name: 'Thumbnail', desc: 'The thumbnail url'}, {name: 'URL'}, {name: 'Description', desc: 'Use {ROLE} to insert the role'}];
async function postForm(interaction, role, useEmoji, txt) {
// Create the modal
const modal = new Modal();
modal.setTitle('Create a new reaction role!')
.setCustomId(`reactionModal|${role.id}|${useEmoji}|${txt}`);
for (let i = 0; i < 5; i++) {
const tempInp = new TextInputComponent()
.setCustomId(embdList[i].name.toLowerCase())
.setLabel(embdList[i].name);
if (embdList[i].name != 'Description') {
tempInp.setStyle('SHORT');
} else {
tempInp.setStyle('PARAGRAPH');
}
if (embdList[i].desc) {
tempInp.setPlaceholder(embdList[i].desc);
}
modal.addComponents(new MessageActionRow().addComponents(tempInp));
}
// Show the modal to the user
interaction.showModal(modal);
}
/**
* @param {Interaction} interaction
*/
function postNotif(interaction, roleId, guild, user) {
const role = guild.roles.cache.get(roleId);
const embd = new MessageEmbed()
.setTitle("You got a new role!")
.setDescription(`You were given the *${role.name}* role in **${guild}**\n_You did this by reacting to [this message](${interaction.message.url})_`)
.setTimestamp()
.setThumbnail(guild.iconURL());
user.send({embeds: [embd]});
}
/**
* @param {Interaction} interaction
*/
function processForm(interaction, bot) {
const roleId = interaction.customId.split('|')[1];
const title = interaction.fields.getTextInputValue('title');
const img = interaction.fields.getTextInputValue('image');
const thumb = interaction.fields.getTextInputValue('thumbnail');
const url = interaction.fields.getTextInputValue('url');
var desc = interaction.fields.getTextInputValue('description');
const useEmoji = (interaction.customId.split('|')[2] == 'true');
var txt = interaction.customId.split('|')[3];
var emoji = undefined;
//Emoji?
if (Number.isInteger(Number(txt))) {
emoji = interaction.guild.emojis.cache.get(txt);
}
if (desc.indexOf("{ROLE}") != -1) {
desc = desc.replaceAll("{ROLE}", `<@&${roleId}>`);
}
const embd = new MessageEmbed()
.setTitle(title)
.setDescription(desc || "");
if (img && isValidUrl(img)) embd.setImage(img);
if (thumb && isValidUrl(thumb)) embd.setThumbnail(thumb);
if (url && isValidUrl(url)) embd.setURL(url);
if (useEmoji) {
interaction.channel.send({embeds: [embd]}).then((msg) => {
try {
msg.react(emoji || txt);
const filter = (reaction, user) => {
return (!user.bot && (reaction.emoji.id == txt || reaction.emoji.name == txt));
};
const collector = msg.createReactionCollector({ filter });
collector.on('collect', (reaction, user) => {
const guildUser = reaction.message.guild.members.cache.get(user.id);
if (guildUser.roles.cache.has(roleId)) {
return guildUser.send("You already have this role!");
}
guildUser.roles.add(roleId);
postNotif(reaction, roleId, reaction.message.guild, guildUser);
});
} catch (err) {
console.error(err);
}
});
} else {
const btn = new MessageButton()
.setCustomId(`addRole|${roleId}`)
.setStyle('PRIMARY');
if (emoji) btn.setEmoji(emoji);
else btn.setLabel(txt);
const row = new MessageActionRow()
.addComponents([btn]);
interaction.channel.send({embeds: [embd], components: [row]})
}
interaction.reply({content: "Reaction role added!", ephemeral: true});
}
/**
* @param {Interaction} interaction
* @returns
*/
function handleBtn(interaction) {
const roleId = interaction.customId.split('|')[1];
const user = interaction.guild.members.cache.get(interaction.user.id);
if (user.roles.cache.has(roleId)) {
return interaction.reply({content: "You already have this role!", ephemeral: true});
}
user.roles.add(roleId);
const role = interaction.guild.roles.cache.get(roleId);
postNotif(interaction, role.id, interaction.guild, interaction.user);
}
module.exports = {
name: "reactionrole",
description: "Give someone a role when they react to a message",
/**
* @param {Interaction} interaction
*/
execute(interaction, Discord, Client, bot) {
const user = interaction.user;
checkRole(bot, interaction.guild, user.id).then((isAdmin) => {
if (isAdmin) {
const role = interaction.options.data.filter((opt) => { return(opt.name == "role"); })[0].role;
const useEmoji = interaction.options.data.filter((opt) => { return(opt.name == "useemoji"); })[0].value;
var txt = interaction.options.data.filter((opt) => { return(opt.name == "text"); })[0].value;
if (txt.startsWith('<:') && txt.endsWith(">")) {
const emoji = interaction.guild.emojis.cache.get(txt.split(":")[2].split(">")[0]);
if (!emoji) { return interaction.reply("Please use a valid emoji"); }
txt = txt.split(":")[2].split(">")[0];
}
if (role.position >= interaction.guild.me.roles.highest.position ||!interaction.guild.me.permissions.has("MANAGE_ROLES")) {
return interaction.reply({
content: "I'm not high enough in the role hierarchy to do that!\n_To raise my place, go to **Server Settings -> Roles** then drag me up!_",
ephemeral: true
});
}
postForm(interaction, role, useEmoji, txt);
}
});
}, processForm, handleBtn,
options: [
{name: "role", description: "The role to assign", type: Constants.ApplicationCommandOptionTypes.ROLE, required: true},
{name: "useemoji", description: 'React using a button or emoji', type: Constants.ApplicationCommandOptionTypes.BOOLEAN, required: true},
{name: 'text', description: 'The reaction emoji or button text', type: Constants.ApplicationCommandOptionTypes.STRING, required: true}
]
}
+97 -29
View File
@@ -2,13 +2,9 @@
const { MongoClient, ServerApiVersion } = require('mongodb');
const { Constants } = require('discord.js');
const { CreateNewCollection } = require("../db/econ");
async function setWelcomeChannel(dbo, message, channelname) {
const channel = message.guild.channels.cache.find(ch => ch.name === channelname);
dbo.insertOne({welcomechannel: channel});
}
const { checkRole } = require('./verify.js');
const fetch = require('node-fetch');
const help = require('../misc/help.js');
async function execute(interaction, Discord, Client, bot) {
@@ -33,38 +29,33 @@ async function execute(interaction, Discord, Client, bot) {
try {
const command = args[i].name;
// if (!command) {
// interaction.reply('Please use the following format _!setup help <welcome, logs>_');
// } else
if (command == 'welcome_channel') {
// if (args.length != 2) { return interaction.reply('The command format is _!setup welcome_channel <channel name>_'); }
// setWelcomeChannel(dbo, message, args[1]);
const channel = args[i].channel;
dbo.updateOne({welcomechannel: {$exists: true}}, {$set: {welcomechannel: `${channel.id}`}});
interaction.reply({content: `Set ${channel} as the new welcome channel`, ephemeral: true})
} else if (command == 'welcome_message') {
// if (args.length < 2) { return interaction.reply('The command format is _!setup welcome\\_message_\nUse _{sn}_ to insert the server name, _{un}_ to insert the user name, and _{ut}_ to insert the user tag\nExample: _!setup welcome\\_message Welcome to {sn} Sir {un}#{ut}_'); }
}
else if (command == 'welcome_message') {
const msg = args[i].value;
if (msg.length > 30 || msg.length < 1) { return interaction.reply({content: 'Please specify a welcome message between 0 and 30 characters!', ephemeral: true}); }
dbo.updateOne({welcomemessage: {$exists: true}}, {$set: {welcomemessage: msg}})
} else if (command == 'keep_logs') {
}
else if (command == 'keep_logs') {
let keeplogs = args[i].value;
dbo.updateOne({ _id: 'LOG'}, {$set: {keepLogs: keeplogs}});
interaction.reply({content: `Toggled log keeping to ${keeplogs}. Please use _!setup log_channel_ to choose the log channel`, ephemeral: true});
} else if (command == 'log_channel') {
// if (args.length != 2) { return message.reply('Please specify a parameter\nExample: _!setup log\\_channel true_'); }
}
else if (command == 'log_channel') {
const channel = args[i].channel;
if (!channel) { return interaction.reply({content: 'The specified channel does not exist!', ephemeral: true}); }
dbo.updateOne({_id: 'LOG'}, {$set: {logchannel: `${channel.id}`}});
interaction.reply({content: `Made ${channel} the new Selmer Bot Logs channel!`, ephemeral: true});
} else if (command == 'log_severity') {
}
else if (command == 'log_severity') {
const tier = args[i].value;
const l = ['none', 'low', 'medium', 'high'];
if (!l.includes(tier)) { return interaction.reply({content: "Please select an existing tier ('none', 'low', 'medium', 'high')", ephemeral: true}); }
@@ -72,7 +63,8 @@ async function execute(interaction, Discord, Client, bot) {
dbo.updateOne({_id: 'LOG'}, {$set: {severity: tier}})
interaction.reply({content: `Severity updated to ${tier}`, ephemeral: true});
} else if (command == 'announcement_role') {
}
else if (command == 'ping_role') {
const role = args[i].value;
// if (message.mentions.roles.first() == undefined) {
// return message.reply("Please mention a role (_!setup announcement\\_role **@role**_)\n_Note: Selmer Bot does NOT ping the @everyone role_");
@@ -81,12 +73,82 @@ async function execute(interaction, Discord, Client, bot) {
dbo.updateOne({_id: 'announcement'}, { $set: { 'role': role.id } });
interaction.reply({content: `Role updated to ${role}`, ephemeral: true});
} else if (command == "announcement_channel") {
}
else if (command == "ping_channel") {
const channel = args[i].channel;
if (!channel) { return interaction.reply({content: 'The specified channel does not exist!', ephemeral: true}); }
dbo.updateOne({_id: 'announcement'}, { $set: { 'channel': channel.id } });
} else {
interaction.reply({content: `Channel set to ${channel}`, ephemeral: true});
}
else if (command == "add_mod_role") {
dbo.findOne({_id: "roles"}).then((doc) => {
const role = args[i].value;
if (!doc.commands.includes(role)) {
dbo.updateOne({_id: "roles"}, { $push: { commands: role } });
interaction.reply({ content: "Role added!", ephemeral: true });
} else {
interaction.reply({ content: "This role is already a command role!", ephemeral: true });
}
});
}
else if (command == "remove_mod_role") {
dbo.updateOne({_id: "roles"}, { $pull: { commands: { $in: [ args[i].value ] }} });
interaction.reply({ content: "Role removed!", ephemeral: true });
}
else if (command == "welcome_banner") {
const attachement_url = interaction.options.data[0].attachment.attachment;
const response = await fetch(attachement_url);
const arrayBuffer = await response.arrayBuffer();
const imgbfr = Buffer.from(arrayBuffer);
dbo.updateOne({_id: 'WELCOME'}, {$set: {welcomebanner: imgbfr.toString('base64')}});
interaction.reply({ content: `Banner updated to ${attachement_url}`, ephemeral: true});
}
else if (command == "welcome_text_color") {
const reg = /^#[0-9A-F]{6}$/i;
const newCol = interaction.options.data[0].value;
if (reg.test(newCol)) {
dbo.updateOne({_id: 'WELCOME'}, {$set: {welcometextcolor: newCol}});
interaction.reply({content: `Color updated to ${newCol} (https://www.color-hex.com/color/${newCol.substring(1)})`, ephemeral: true});
} else {
interaction.reply("Please chose a valid hex color\nYou can find colors here: https://www.color-hex.com/");
}
}
else if (command == "toggle_leveling") {
const tog = interaction.options.data[0].value;
dbo.updateOne({_id: 'LEVELING'}, {$set: {enabled: tog}});
interaction.reply({content: "Turned leveling " + (tog) ? "ON" : "OFF", ephemeral: true});
}
else if (command == "leveling_banner") {
const level_banner = interaction.options.data[0].attachment.attachment;
const response = await fetch(level_banner);
const arrayBuffer = await response.arrayBuffer();
const imgbfr = Buffer.from(arrayBuffer);
dbo.updateOne({_id: 'LEVELING'}, {$set: {card: imgbfr.toString('base64')}});
interaction.reply({content: `Updated leveling banner to ${level_banner}`, ephemeral: true});
}
else if (command == "leveling_text") {
dbo.updateOne({_id: 'LEVELING'}, {$set: {text: interaction.options.data[0].value}});
interaction.reply({content: `Updated leveling text to ${interaction.options.data[0].value}`, ephemeral: true});
}
else if (command == "leveling_color") {
const reg = /^#[0-9A-F]{6}$/i;
const newCol = interaction.options.data[0].value;
if (reg.test(newCol)) {
dbo.updateOne({_id: 'LEVELING'}, {$set: {col: newCol}});
interaction.reply({content: `Color updated to ${newCol} (https://www.color-hex.com/color/${newCol.substring(1)})`, ephemeral: true});
} else {
interaction.reply("Please chose a valid hex color\nYou can find colors here: https://www.color-hex.com/");
}
}
else if (command == "help") {
if (interaction.options.data[0].value) {
help.execute(interaction, Discord, Client, bot);
} else {
interaction.reply({content: 'https://docs.selmerbot.com/setup', ephemeral: true});
}
}
else {
interaction.reply({content: "Please chose a valid option", ephemeral: true});
}
/* Made obsolete by the change to Slash Commands
@@ -115,20 +177,26 @@ async function execute(interaction, Discord, Client, bot) {
module.exports = {
name: 'setup',
description: 'Set up server features',
execute,
options: [
{name: 'welcome_channel', description: 'Sets the channel for welcome messages', type: Constants.ApplicationCommandOptionTypes.CHANNEL },
{name: 'welcome_message', description: 'Sets the welcome message, Use {un} for username, {ut} for user tag and {sn} for server name', type: Constants.ApplicationCommandOptionTypes.STRING },
{name: 'welcome_message', description: 'Use {un}, {ud}, {ut}, and {sn} for username, user descriminator, user tag, and server name', type: Constants.ApplicationCommandOptionTypes.STRING },
{name: 'welcome_banner', description: 'Sets the welcome banner', type: Constants.ApplicationCommandOptionTypes.ATTACHMENT},
{name: 'welcome_text_color', description: 'Sets the welcome banner text color', type: Constants.ApplicationCommandOptionTypes.STRING},
{name: 'keep_logs', description: 'Toggles logging', type: Constants.ApplicationCommandOptionTypes.BOOLEAN },
{name: 'log_channel', description: 'Sets the logging channel', type: Constants.ApplicationCommandOptionTypes.CHANNEL },
{name: 'log_severity', description: 'Sets the logging Severity (logs this/lower tiers)', type: Constants.ApplicationCommandOptionTypes.STRING, choices: [{name: 'none', value: 'none'}, {name: 'low', value: 'low'}, {name: 'medium', value: 'medium'}, {name: 'high', value: 'high'}] },
{name: 'announcement_role', description: 'Sets the role to be pinged for reminders', type: Constants.ApplicationCommandOptionTypes.ROLE},
{name: 'announcement_channel', description: 'Sets the channel for reminders', type: Constants.ApplicationCommandOptionTypes.CHANNEL}
// {name: 'help', description: 'gets help with setup commands', type: Constants.ApplicationCommandOptionTypes.STRING, choices: [{name: 'welcome', value: 'welcome'}, {name: 'logs', value: 'logs'}, {name: 'announcement', value: 'announcement'}]}
{name: 'ping_role', description: 'Sets the role to be pinged for reminders', type: Constants.ApplicationCommandOptionTypes.ROLE},
{name: 'ping_channel', description: 'Sets the channel for reminders', type: Constants.ApplicationCommandOptionTypes.CHANNEL},
{name: 'add_mod_role', description: 'Make a role into an admin role for Selmer Bot, able to execute ALL Selmer Bot commands', type: Constants.ApplicationCommandOptionTypes.ROLE},
{name: 'remove_mod_role', description: 'Remove a Selmer Bot moderation role', type: Constants.ApplicationCommandOptionTypes.ROLE},
{name: 'toggle_leveling', description: 'Enable or Disable the leveling system', type: Constants.ApplicationCommandOptionTypes.BOOLEAN},
{name: 'leveling_banner', description: 'Set the card background for the leveling system', type: Constants.ApplicationCommandOptionTypes.ATTACHMENT},
{name: 'leveling_text', description: 'Use {un}, {ud}, {ut}, {sn}, and {r} for username, descriminator, user tag, server name, and rank', type: Constants.ApplicationCommandOptionTypes.STRING},
{name: 'leveling_color', description: 'Set the card text color for the leveling system', type: Constants.ApplicationCommandOptionTypes.STRING},
{name: 'help', description: 'in-app?', type: Constants.ApplicationCommandOptionTypes.BOOLEAN}
]
}
+35
View File
@@ -0,0 +1,35 @@
const { checkRole } = require('./verify.js');
const { Constants } = require('discord.js');
module.exports = {
name: 'unlock',
description: 'Unlock a channel',
execute(interaction, Discord, Client, bot) {
const arg = interaction.options.data[0];
const guild = bot.guilds.cache.get(interaction.guildId);
checkRole(bot, guild, interaction.user.id).then((isAllowed) => {
if (!isAllowed) { return message.reply('Insufficient Permissions!'); }
var channel;
if (arg) {
channel = arg.channel;
} else {
channel = interaction.channel;
}
let role = interaction.guild.roles.cache.find(r => r.name === "@everyone");
channel.permissionOverwrites.edit(role.id, {
VIEW_CHANNEL: true,
SEND_MESSAGES: true,
READ_MESSAGE_HISTORY: true,
ATTACH_FILES: true
});
interaction.reply(`${channel} has been unlocked!`);
});
},
options: [{name: 'channel', description: 'The channel to unlock (defaults to current channel)', type: Constants.ApplicationCommandOptionTypes.CHANNEL, required: false}]
}
-33
View File
@@ -1,33 +0,0 @@
const { checkRole } = require('./verify.js');
const { Constants } = require('discord.js');
module.exports = {
name: 'unlock',
description: 'Unlock a channel',
execute(interaction, Discord, Client, bot) {
const arg = interaction.options.data[0];
const guild = bot.guilds.cache.get(interaction.guildId);
if (!checkRole(bot, guild, interaction.user.id)) { return message.reply('Insufficient Permissions!'); }
var channel;
if (arg) {
channel = arg.channel;
} else {
channel = interaction.channel;
}
let role = interaction.guild.roles.cache.find(r => r.name === "@everyone");
channel.permissionOverwrites.edit(role.id, {
VIEW_CHANNEL: true,
SEND_MESSAGES: true,
READ_MESSAGE_HISTORY: true,
ATTACH_FILES: true
});
interaction.reply(`${channel} has been unlocked!`);
},
options: [{name: 'channel', description: 'The channel to unlock (defaults to current channel)', type: Constants.ApplicationCommandOptionTypes.CHANNEL, required: false}]
}
+31 -24
View File
@@ -1,32 +1,39 @@
function checkRole(bot, guild, userId, cal = false) {
var roleName;
const Discord = require('discord.js');
if (cal) {
roleName = "Selmer Bot Calendar";
} else {
roleName = "Selmer Bot Commands";
}
/**
* @param {Discord.Guild} guild
* @returns {Promise<Boolean>}
*/
function checkRole(bot, guild, userId) {
return new Promise((resolve, reject) => {
const user = guild.members.cache.get(userId);
const role = guild.roles.cache.find((role) => { return (role.name == roleName); })
const user = guild.members.cache.get(userId);
// return (role != undefined && user.roles.cache.has(role.id)); // || user.id == guild.ownerId || bot.inDebugMode
return (role != undefined && user.roles.cache.has(role.id)); // || user.id == guild.ownerId || bot.inDebugMode
// Maybe implement this later, useless for now
bot.mongoconnection.then((client) => {
// const role = client.db(message.guild.id).collection("admin-roles");
const a = new Array();
client.db(guild.id).collection("SETUP").findOne({_id: "roles"}).then((doc) => {
const comRoles = doc.commands;
const role = guild.roles.cache.find((role) => { return (role.name == "Selmer Bot Commands"); });
const hasPreAdminRole = (role != undefined && user.roles.cache.has(role.id) || user.id == guild.ownerId);
/*Maybe implement this later, useless for now
const client = new MongoClient(mongouri, { useNewUrlParser: true, useUnifiedTopology: true, serverApi: ServerApiVersion.v1 });
client.connect(err => {
const role = client.db(message.guild.id).collection("admin-roles");
shop.find().toArray(function(err, itemstemp) {
if (err) throw err;
items = [...itemstemp];
client.close();
if (!comRoles) {
resolve(hasPreAdminRole);
} else {
const hasRoles = [];
Promise.all(comRoles.map((val) => {
if (user.roles.cache.has(val)) {
hasRoles.push(true);
}
})).then(() => {
resolve(hasRoles.length > 0 || hasPreAdminRole);
});
}
});
});
});*/
});
}
+107 -87
View File
@@ -1,105 +1,125 @@
const { MessageAttachment } = require('discord.js');
// const { readFile } = require('fs/promises');
const fs = require("fs");
const sharp = require('sharp');
const fetch = require('node-fetch');
const arrayBufferToBuffer = require('arraybuffer-to-buffer');
const { request } = require('undici');
const CanvasImport = require('@napi-rs/canvas');
const canvas = CanvasImport.createCanvas(700, 250)
const context = canvas.getContext('2d')
//https://some-random-api.ml/welcome
async function welcome(member, welcomechannel, welcomemessage = null, welcomebanner = null) {
//Draw Stuff
const context = canvas.getContext('2d');
var bkimgsrc;
let bkurl = 'https://github.com/ION606/selmerBot/blob/main/commands/admin/wallpaper.jpg';
const response = await fetch(bkurl);
response.arrayBuffer().then(async (data) => {
// const background = new CanvasImport.Image();
// background.src = arrayBufferToBuffer(data);
const { GuildMember } = require('discord.js');
// This uses the canvas dimensions to stretch the image onto the entire canvas
// context.drawImage(background, 0, 0, canvas.width, canvas.height);
context.fillStyle = 'rgba(0,0,0,1)';
context.fillRect(0,0, canvas.width, canvas.height);
function formatMessage(member, welcomemessage, isLvl, rank = null) {
return new Promise((resolve, reject) => {
let text;
if (!isLvl) {
text = `Welcome to ${member.guild.name} ${member.user.tag}!`;
if (welcomemessage != null) {
text = welcomemessage;
text = text.replace('{sn}', member.guild.name);
text = text.replace('{un}', member.user.username);
text = text.replace('{ud}', member.user.discriminator);
text = text.replace('{ut}', member.user.tag);
}
} else {
text = `Congradulations ${member.user.tag} for reaching rank ${rank}!`;
//Draw the Border
context.strokeStyle = '#0099ff';
context.strokeRect(0, 0, canvas.width, canvas.height);
//Add Text
//have the function here, because returns are whack
const applyText = (canvas, text) => {
const context = canvas.getContext('2d');
// Declare a base size of the font
let fontSize = 70;
let i = 0;
do {
// Assign the font to the context and decrement it so it can be measured again
context.font = `italic ${fontSize -= 10}px sans-serif`;
// Compare pixel width of the text to the canvas minus the approximate avatar size
i ++;
} while (context.measureText(text).width > canvas.width - 100);
// Return the result to use in the actual canvas
return context.font;
};
//message.author.username == interaction.member.displayName
//message.guild.name == interaction.member.guild.name
let text = `Welcome to ${member.guild.name} ${member.user.username}#${member.user.discriminator}!`;
if(welcomemessage != null) {
text = welcomemessage;
text = text.replace('{sn}', member.guild.name);
text = text.replace('{un}', member.user.username);
text = text.replace('{ut}', member.user.discriminator);
if (welcomemessage != null) {
text = welcomemessage;
text = text.replace('{sn}', member.guild.name);
text = text.replace('{un}', member.user.username);
text = text.replace('{ud}', member.user.discriminator);
text = text.replace('{ut}', member.user.tag);
text = text.replace('{r}', rank);
}
}
context.font= applyText(canvas, text);
context.fillStyle = '#ffffff';
context.fillText(text, (canvas.width/2) - (context.measureText(text).width)/2, canvas.height - 15);
resolve(text);
});
}
//Draw a white circle
context.beginPath();
context.arc((canvas.width/2), 90, 85, 0, 2 * Math.PI, false);
context.fillStyle = 'white';
context.fill();
context.closePath();
/**
* @param {GuildMember} member
* @param {*} welcomeChannel
*/
async function welcome(member, welcomeChannel, welcomemessage, welcomebanner, welcomeTextCol, isLvl = false, rank = null) {
formatMessage(member, welcomemessage, isLvl, rank).then(async (wmsg) => {
const width = 1024;
const height = 500;
const usernameText = `${wmsg}`;
const memberCountText = (!isLvl) ? `You are member ${member.guild.memberCount}` : "";
//ANYTHING DRAWN AFTER THIS WILL BE CLIPPED!!!
//Make whatever image will be draw (the user's avatar) into a circular format
context.beginPath();
context.arc((canvas.width/2), 90, 80, 0, Math.PI * 2, true);
context.closePath();
var uSize = 55 - Math.round(wmsg.length/2);
// Clip off the region you just drew (enforce template?)
context.clip();
if (uSize < 5) { uSize = 5; }
const username = `
<svg width="${width}" height="${height}">
<style>
.username { fill: ${welcomeTextCol}; font-size: ${uSize}px; font-weight: bold;}
</style>
<text x="50%" y="50%" text-anchor="middle" class="username" font-family='Didot'>${usernameText}</text>
</svg>
`;
const memberCount = `
<svg width="${width}" height="${height}">
<style>
.memberCount { fill: ${welcomeTextCol}; font-size: 40px; font-weight: bold;}
</style>
<text x="50%" y="50%" text-anchor="middle" class="memberCount" font-family='Didot'>${memberCountText}</text>
</svg>
`;
//Add the user's profile pic (message.author == interaction.user)
const { body } = await request(member.displayAvatarURL({ format: 'jpg' }));
const avatar = new CanvasImport.Image();
avatar.src = Buffer.from(await body.arrayBuffer());
context.drawImage(avatar, (canvas.width/2) - 80, 10, 160, 160);
const r = 100;
const circleShape = Buffer.from(`<svg><circle cx="${r}" cy="${r}" r="${r}" /></svg>`);
var response, arrayBuffer;
const usernameBuffer = Buffer.from(username);
const memberCountBuffer = Buffer.from(memberCount);
response = await fetch(member.displayAvatarURL());
arrayBuffer = await response.arrayBuffer();
const iconBuffer = Buffer.from(arrayBuffer);
// Use the helpful Attachment class structure to process the file for you
const attachment = new MessageAttachment(canvas.toBuffer('image/png'), 'profile-image.png');
var bkBuffer;
welcomechannel.send({ files: [attachment] });
if (!welcomebanner) {
response = await fetch('https://wallpapercave.com/wp/wp3258574.png');
arrayBuffer = await response.arrayBuffer();
bkBuffer = Buffer.from(arrayBuffer);
} else {
// return console.log(welcomebanner);
bkBuffer = Buffer.from(welcomebanner, 'base64');
}
})
sharp(iconBuffer)
.resize(300, 300)
.composite([{
input: circleShape,
blend: 'dest-in'
}])
.toBuffer().then((iconBufferNew) => {
sharp(bkBuffer)
.resize(1024, 500)
.composite([
{
input: usernameBuffer,
top: 80,
left: -10,
},
{
input: memberCountBuffer,
top: 130,
left: -10,
},
{
input: iconBufferNew,
top: 10,
left: 1024/2 - 300/2,
},
]).toBuffer((err, buffer) => {
if (err) { return console.error(err); }
// return console.log(buffer.byteLength * 0.000001);
welcomeChannel.send({
files: [buffer],
});
});
});
});
}
module.exports = { welcome }
+48 -25
View File
@@ -3,6 +3,7 @@ const { MongoClient, ServerApiVersion } = require('mongodb');
const { Collection, Client, Formatters, Intents, Interaction } = require('discord.js');
const { CLIENT_ODBC } = require('mysql/lib/protocol/constants/client');
const { time } = require('@discordjs/builders');
const { welcome } = require('../admin/welcome.js');
let currencySymbol = '$';
@@ -46,7 +47,12 @@ function CreateNewCollection(interaction, client, server, id, opponent = null, g
}
function addxp(interaction, dbo, amt, xp_list) {
/**
* @param {Interaction} interaction
* @returns
*/
function addxp(bot, interaction, dbo, amt, xp_list, noPing = false) {
if (!isNum(amt)) { return console.log("This isn't a number...."); }
dbo.find({"balance": {$exists: true}}).toArray(function(err, doc) {
@@ -55,6 +61,7 @@ function addxp(interaction, dbo, amt, xp_list) {
temp = doc[0];
let rank = temp.rank + 1; //The table starts at rank 0, the user starts at rank 1
const txp = amt; /*temp.xp + amt; // This part was used before the xp check was made in the 'work' function */
//If the rank is less than 100, you can still advance
if (rank < 101) {
let needed = xp_list.get(rank);
@@ -75,13 +82,38 @@ function addxp(interaction, dbo, amt, xp_list) {
let newmp = temp.mp + 5;
dbo.updateOne({balance: temp.balance, rank: temp.rank, lastdayworked: temp.lastdayworked}, { $set: { rank: rank, hpmp: {maxhp: newhp, maxmp: newmp} }});
interaction.channel.send('Congradulations <@' + interaction.user.id + '> for reaching rank ' + String(rank) + '!');
dbo.updateOne({balance: temp.balance, rank: temp.rank, lastdayworked: temp.lastdayworked}, { $set: { rank: rank, hpmp: {maxhp: newhp, maxmp: newmp}, xp: txp }});
var user;
if (interaction.user) {
user = interaction.user;
} else {
// This is a message
user = interaction.author;
}
if (bot) {
bot.mongoconnection.then((client) => {
const sbo = client.db(interaction.guildId).collection('SETUP');
sbo.findOne({_id: 'LEVELING'}).then((doc) => {
if (!doc || !doc.card) {
return interaction.channel.send('Congradulations <@' + user.id + '> for reaching rank ' + String(rank) + '!');
}
const member = interaction.guild.members.cache.get((interaction.user) ? interaction.user.id : interaction.member.id);
welcome(member, interaction.channel, doc.text, doc.card, doc.col, true, String(rank));
});
});
} else {
interaction.channel.send('Congradulations <@' + user.id + '> for reaching rank ' + String(rank) + '!');
}
}
} else {
interaction.reply("You've already reached max level!").catch((err) => {
interaction.channel.send("You've already reached max level!");
});
if (!noPing) {
interaction.reply("You've already reached max level!").catch((err) => {
interaction.channel.send("You've already reached max level!");
});
}
}
dbo.updateOne({balance: temp.balance}, { $set: { xp: txp}});
@@ -164,7 +196,7 @@ function buy(id, interaction, dbo, shop, xp_list) {
var newObj = { name: item.name, cost: item.cost, icon: item.icon, sect: item.sect};
addxp(interaction, dbo, Math.ceil(item.cost * 1.2), xp_list);
addxp(bot, interaction, dbo, Math.ceil(item.cost * 1.2), xp_list);
dbo.find(newObj, {$exists: true}).toArray(function(err, doc) {
if(String(doc)) {
@@ -182,7 +214,7 @@ function buy(id, interaction, dbo, shop, xp_list) {
//FIXME
function sell(id, interaction, dbo, shop, xp_list) {
function sell(bot, id, interaction, dbo, shop, xp_list) {
const args = interaction.options.data;
const query = args.filter((arg) => { return (arg.name == 'item'); })[0].value;
var num = args.filter((arg) => { return (arg.name == 'amount'); })[0].value;
@@ -218,7 +250,7 @@ function sell(id, interaction, dbo, shop, xp_list) {
dbo.updateOne({"balance": {$exists: true}}, { $set: { balance: currentBal + amountSoldFor }});
});
addxp(interaction, dbo, Math.ceil(functional_item.cost * 1.2), xp_list);
addxp(bot, interaction, dbo, Math.ceil(functional_item.cost * 1.2), xp_list);
interaction.reply(`You've sold ${num} ${String(functional_item.name)} for ${currencySymbol}${amountSoldFor}`)
.catch((err) => {
@@ -233,7 +265,7 @@ function sell(id, interaction, dbo, shop, xp_list) {
}
function work(dbo, interaction, xp_list) {
function work(bot, dbo, interaction, xp_list) {
let fulldate = new Date();
let date = fulldate.getDate();
dbo.find({"lastdayworked": {$exists: true}}).toArray(function(err, doc) {
@@ -254,7 +286,8 @@ function work(dbo, interaction, xp_list) {
//Update the amount to the new TOTAL balance
dbo.updateOne({"balance": {$exists: true}}, { $set: { balance: doc[0].balance + amt, lastdayworked: date }});
addxp(interaction, dbo, xp_earned, xp_list);
addxp(bot, interaction, dbo, xp_earned, xp_list);
interaction.reply(`<@${interaction.user.id}> worked and earned ${currencySymbol}${amt} and ${xp_earned} xp!`).catch((err) => {
interaction.channel.send(`<@${interaction.user.id}> worked and earned ${currencySymbol}${amt} and ${xp_earned} xp!`)
});
@@ -349,27 +382,17 @@ module.exports = {
currencySymbol = bot.currencysymbolmmain;
/*/test area
if (command == 'xp' || command == 'adbal') {
//Selmer Dev only command
if (message.member.roles.cache.has('944048889038774302')) {
if (command == 'xp') {
return addxp(message, dbo, Number(args[0]), xp_list);
}
}
}*/
//Command Area
if(command == 'init') {
//Add security check here
// init.execute(bot, message, args, command, dbo, Discord, connect);
return;
} else if (command == 'buy') {
buy(id, interaction, dbo, items, xp_list);
buy(bot, id, interaction, dbo, items, xp_list);
} else if (command == 'shop') {
getShop(interaction, items, bot);
} else if (command == 'work') {
work(dbo, interaction, xp_list);
work(bot, dbo, interaction, xp_list);
} else if (command == 'rank') {
rank(dbo, interaction, xp_list);
} else if (command == 'inventory') {
@@ -377,7 +400,7 @@ module.exports = {
} else if (command == 'balance') {
getBalance(dbo, interaction);
} else if (command == 'sell') {
sell(id, interaction, dbo, items, xp_list);
sell(bot, id, interaction, dbo, items, xp_list);
} else {
interaction.reply(`${command} is not a command`).catch((err) => {
interaction.channel.send(`${command} is not a command`);
@@ -388,6 +411,6 @@ module.exports = {
},
//Battle Updating stuff
addxp, checkAndUpdateBal, CreateNewCollection, econHelp, addxp, BASE, STATE,
addxp, checkAndUpdateBal, CreateNewCollection, econHelp, BASE, STATE,
options: []
}
@@ -1,5 +1,5 @@
const fs = require('fs');
const {Buffer} = require('buffer');
const { Client } = require('discord.js');
function mapToObj(map){
const obj = {}
@@ -43,15 +43,20 @@ async function backupLists(bot, IDM) {
}
}
/**
* @param {Client} bot
* @param {Boolean} IDM
*/
async function loadBotBackups(bot, IDM) {
try {
if (IDM) {
const botBackups = require('./backup.json').backups;
if (!process.env.backupLists) {
const botBackups = require('../admin/backup.json').backups;
bot.lockedChannels = objToMap(botBackups.locked);
} else {
bot.lockedChannels = objToMap(JSON.parse(botBackups.locked));
bot.lockedChannels = objToMap(JSON.parse(process.env.backupLists.locked));
}
// bot.user.setActivity("RESTARTING BOT, PLEASE STAND BY.mp3", { type: "LISTENING" }); // User is....undefined????
} catch (err) {
console.error(err);
bot.lockedChannels = new Map();
+84
View File
@@ -0,0 +1,84 @@
const Discord = require('discord.js')
const axios = require('axios')
const cheerio = require('cheerio');
const { URL } = require("url");
function isValidUrl(s) {
try {
new URL(s);
return true;
} catch (err) {
return false;
}
};
function handleStreamInp(bot, url, customTitle) {
return new Promise((resolve, reject) => {
if (!isValidUrl(url)) {
return reject(false);
}
axios.get(url).then(async response => {
var title;
if (customTitle) {
title = customTitle;
} else {
const html = response.data;
const $ = cheerio.load(html);
title = $('meta[name="description"]').attr("content");
}
bot.user.setActivity({name: title, type: "STREAMING", url: url});
resolve(true);
});
});
}
//Have this only visible to you.
/**
* @param {Discord.Client} bot
* @param {Discord.Interaction} interaction
*/
async function setPresence(bot, interaction) {
const command = interaction.options.data[0];
if (command.name == "setpresence") {
const txt = command.options.filter((arg) => { return(arg.name == 'pres_text'); })[0].value;
const type = command.options.filter((arg) => { return(arg.name == 'type'); })[0].value;
var sep = " ";
if (type == "LISTENING" || type == "WATCHING") {
sep = " to ";
} else if (type == "COMPETING") {
sep = " in ";
}
//Check if it's me
if (interaction.user.id == bot.guilds.cache.get(bot.home_server).ownerId) {
if (type == "STREAMING") {
const t = (command.options.length > 2) ? command.options.filter((arg) => { return(arg.name == "display_name"); })[0].value : null;
handleStreamInp(bot, txt, t, interaction).then(() => {
interaction.reply(`Set bot presence to _${type + sep + txt}_`);
}).catch(() => {
interaction.reply("Invalid URL").catch((err) => {
interaction.channel.send("Invalid URL");
});
});
} else {
bot.user.setActivity(txt, { type: type });
interaction.reply(`Set bot presence to _${type + sep + txt}_`);
}
}
} else if (command.name == "setactivity") {
const stat = command.options[0];
bot.user.setStatus(stat.value);
interaction.reply(`Set bot status to ${stat.value}`);
}
}
module.exports = { setPresence, isValidUrl }
+4 -2
View File
@@ -27,10 +27,12 @@ function handle_dm(message, bot) {
handleInp(bot, message);
} else if (message.content.indexOf('!reminders') != -1) {
reminders.execute(message, null, null, null, bot);
} else if (message.content.indexOf('!repo') != -1 || message.content.indexOf('!code') != -1) {
repo.execute(message, null, null, null, bot);
}
// else if (message.content.indexOf('!repo') != -1 || message.content.indexOf('!code') != -1) {
// repo.execute(message, null, null, null, bot);
// }
else {
return message.reply('UNUSABLE DM COMMAND DETECTED');
}
+17 -14
View File
@@ -5,16 +5,16 @@ const { addComplaintButton } = require('../dev only/submitcomplaint');
//#region game lose/win
function loseGame(user_dbo, xp_collection, message, bot = null) {
function loseGame(user_dbo, xp_collection, interaction, bot = null) {
return new Promise(function(resolve, reject) {
user_dbo.find({"game": {$exists: true}}).toArray(function(err, docs){
user_dbo.find({"game": {$exists: true}}).toArray(function(err, docs) {
const doc = docs[0];
if (doc == undefined) {
message.reply("Oops! There's been an error, click the ✅ to report this!");
addComplaintButton(bot, message);
interaction.reply("Oops! There's been an error, click the ✅ to report this!");
addComplaintButton(bot, interaction);
return;
}
if (doc.game == null) { return message.reply("You're not even in a game and you're trying to quit! Sad..."); }
if (doc.game == null) { return interaction.reply("You're not even in a game and you're trying to quit! Sad..."); }
var addbal;
//If this function was called from "winGame", return
@@ -25,10 +25,13 @@ function loseGame(user_dbo, xp_collection, message, bot = null) {
if (doc.balance > 5) {
user_dbo.updateOne(doc, { $set: { balance: doc.balance - addbal}});
}
} else { message.channel.delete(); }
} else {
//Check if the channel is a thread
interaction.channel.delete();
}
//Update the player's xp
addxp(message, user_dbo, Math.ceil((BASE.XP * doc.rank)/2),xp_collection)
addxp(bot, interaction, user_dbo, Math.ceil((BASE.XP * doc.rank)/2),xp_collection);
user_dbo.updateOne({"game": {$exists: true}}, { $set: { game: null, opponent: null, state: STATE.IDLE, 'hpmp.hp': doc.hpmp.maxhp, 'hpmp.mp': doc.hpmp.maxmp }});
resolve(addbal);
@@ -38,14 +41,14 @@ function loseGame(user_dbo, xp_collection, message, bot = null) {
}
function winGame(client, bot, db, user_dbo, xp_collection, message, singlePlayer = false) {
function winGame(client, bot, db, user_dbo, xp_collection, interaction, singlePlayer = false) {
user_dbo.find({"game": {$exists: true}}).toArray(function(err, docs){
const doc = docs[0];
//Check for an opponent
if (doc.opponent != null) {
let other = db.collection(doc.opponent);
let promise_temp = loseGame(other, xp_collection, message);
let promise_temp = loseGame(other, xp_collection, interaction);
promise_temp.then(function(result) {
var amt_taken = result;
@@ -55,7 +58,7 @@ function winGame(client, bot, db, user_dbo, xp_collection, message, singlePlayer
//Delete the bot's record of the game
if (!singlePlayer) {
client.db('B|S' + bot.user.id).collection(message.guild.id).drop();
client.db('B|S' + bot.user.id).collection(interaction.guildId).drop();
}
@@ -63,16 +66,16 @@ function winGame(client, bot, db, user_dbo, xp_collection, message, singlePlayer
user_dbo.updateOne({"game": {$exists: true}}, { $set: { game: null, opponent: null, state: STATE.IDLE, xp: doc.xp + (BASE.XP * doc.rank), 'hpmp.hp': doc.hpmp.maxhp, 'hpmp.mp': doc.hpmp.maxmp }});
if (!singlePlayer) {
const channel = bot.channels.cache.get(message.channel.parentId);
const channel = bot.channels.cache.get(interaction.channel.parentId);
channel.send(`<@${user_dbo.s.namespace.collection}> just won a game of "${docs[0].game}"!`);
}
message.channel.delete();
interaction.channel.delete();
});
}
function equipItem(client, bot, db, dbo, message) {
function equipItem(client, bot, db, dbo, interaction) {
return interaction.reply("This command is not implemented yet!");
if (!bot.inDebugMode) { return; }
let items = [
{ name: 'HP Potion', cost: 20, icon: 'CUSTOM|healing_potion', sect: 'HP', num: 2 },
+110 -94
View File
@@ -1,5 +1,7 @@
// // @ts-check //Disabled
// Maybe have the interaction type be "user" https://canary.discord.com/developers/docs/interactions/application-commands#application-command-object-application-command-types
const { MessageActionRow, MessageButton, Message } = require('discord.js');
const { MongoClient, ServerApiVersion } = require('mongodb');
let ecoimport = require("../db/econ.js");
@@ -20,7 +22,7 @@ const { chooseClass, presentClasses } = require('./game_classes.js');
//Has a list of all games (used to change player state)
const allGames = ['battle', 'Tic Tac Toe'];
// const { NULL } = require('mysql/lib/protocol/constants/types');
//#region functions (NOT GAME SPECIFIC)
@@ -69,18 +71,18 @@ async function Initialize(bot, user_dbo, command, message, first, second, other_
//#endregion
//replies to the message with current game specifics
function getGame(message, args, db) {
function getGame(interaction, args, db) {
let id;
var temp;
if (args.length == 1 && String(args[0]).startsWith('<')) { id = args[0].substr(2, args[0].length - 3)}
else { id = message.author.id; }
var user_dbo = db.collection(message.author.id);
else { id = interaction.user.id; }
var user_dbo = db.collection(interaction.user.id);
user_dbo.find({"game": {$exists: true}}).toArray(function(err, docs){
const doc = docs[0];
if (doc.game == null) {
return message.reply(`<@${id}> is not currently playing a game!`);
return interaction.reply(`<@${id}> is not currently playing a game!`);
}
temp = `<@${id}> is currently playing "${doc.game}"`;
@@ -89,58 +91,64 @@ function getGame(message, args, db) {
temp += ` with <@${doc.opponent}>`
}
message.reply(temp);
interaction.reply(temp);
});
}
function acceptIsValid(bot, other_discord, message, msg, tag_len) {
function acceptIsValid(bot, other_discord, interaction, invUserId, message) {
if (other_discord == undefined) {
message.reply("This is not a valid invite!");
interaction.reply("This is not a valid invite!");
return false;
}
//Make sure the bot was the one creating the invite
let check0 = msg.author.bot;
let check0 = message.author.bot;
//Author
let tag = msg.content.substr(2, tag_len);
let check1 = Number(tag) == Number(message.author.id);
let check1 = Number(other_discord.id) == Number(invUserId);
//Time (within the last 5 min)
let prev = snowflake.convertSnowflakeToDate(msg.id);
let now = snowflake.convertSnowflakeToDate(message.id);
let prev = snowflake.convertSnowflakeToDate(message.id);
let now = snowflake.convertSnowflakeToDate(interaction.id);
// @ts-ignore
let diff = now - prev;
var minutes = Math.floor((diff/1000)/60);
let check2 = minutes <= 5 || bot.inDebugMode;
if (!check0) { message.reply("really?"); }
else if (!check1 && check2) { message.reply("_INVALID USER_"); }
else if (check1 && !check2) { message.reply("_THIS INVITE EXPIRED!_"); }
else if (!check1 && !check2) { message.reply("_THIS MESSAGE HAS AN INVALID USER AND HAS EXPIRED_")}
if (!check0) { interaction.reply("really?"); }
else if (!check1 && check2) { interaction.reply("_INVALID USER_"); }
else if (check1 && !check2) { interaction.reply("_THIS INVITE EXPIRED!_"); }
else if (!check1 && !check2) { interaction.reply("_THIS MESSAGE HAS AN INVALID USER AND HAS EXPIRED_")}
return (check0 && check1 && check2);
}
function hpmp(message, command, dbo) {
function hpmp(interaction, command, dbo) {
// throw 'THIS HAS NOT BEEN UPDATED WITH THE MOST RECENT VERSION OF THE MONGODB STRUCTURE!';
dbo.find({"hpmp": {$exists: true}}).toArray(function(err, doc) {
interaction.reply(`You have ${String(doc[0].hpmp.hp)} hp and ${String(doc[0].hpmp.mp)} mp left!`);
});
/*
if (command == 'hp') {
dbo.find({"hpmp": {$exists: true}}).toArray(function(err, doc) {
return message.reply(`You have ${String(doc[0].hpmp.hp)} hp left!`);
return interaction.reply(`You have ${String(doc[0].hpmp.hp)} hp left!`);
});
} else if (command == 'mp') {
dbo.find({"hpmp": {$exists: true}}).toArray(function(err, doc) {
return message.reply(`You have ${String(doc[0].hpmp.hp)} mp left!`);
return interaction.reply(`You have ${String(doc[0].hpmp.MP)} mp left!`);
});
}
*/
}
function equip(message, args, command, dbo, bot, shop) {
const inp = args[1];
if (!inp) { return message.reply("Please provide input (either a weapon for main or shield for secondary)")}
function equip(interaction, inp, command, dbo, bot, shop) {
// const inp = args[1];
if (!inp) { return interaction.reply("Please provide input (either a weapon for main or shield for secondary)"); }
//Check if the user is already in a game
dbo.find({'game': {$exists: true}}).toArray(function(err, docs) {
@@ -149,7 +157,7 @@ function equip(message, args, command, dbo, bot, shop) {
if (doc.game != null) {
ret = true;
// console.log(doc.game);
return message.reply('You can\'t equip while in a game!');
return interaction.reply('You can\'t equip while in a game!');
}
//If the thing is a shield, add it to secondary
@@ -158,7 +166,7 @@ function equip(message, args, command, dbo, bot, shop) {
if (docs[0] != undefined) {
dbo.updateOne({}, {$set: {'equipped.weapons.secondary': docs[0]}});
} else {
message.reply("You don't own a shield!");
interaction.reply("You don't own a shield!");
}
});
@@ -170,7 +178,7 @@ function equip(message, args, command, dbo, bot, shop) {
//Equip the weapon
dbo.updateOne({}, {$set: {'equipped.weapons.main': docs[0]}});
} else {
message.reply(`You don't own any ${inp}s!`);
interaction.reply(`You don't own any ${inp}s!`);
}
});
}
@@ -213,12 +221,11 @@ function in_game_redirector(bot, interaction, threadname, doc, client, mongouri,
module.exports ={
name: "game",
description: "Play a game using Selmer Bot!",
async execute(bot, message, args, command, Discord, mongouri, items, xp_collection) {
async execute(bot, interaction, command, Discord, mongouri, items, xp_collection) {
//#region Setup
const id = message.author.id;
const server = message.guild.id;
const id = interaction.user.id;
const server = interaction.guildId;
// // @ts-ignore
// const client = new MongoClient(mongouri, { useNewUrlParser: true, useUnifiedTopology: true, serverApi: ServerApiVersion.v1 });
@@ -235,12 +242,12 @@ module.exports ={
const serverinbotdb = botdb.collection(server);
//Initialize if necessary
ecoimport.CreateNewCollection(message, client, server, id);
command = args[0];
ecoimport.CreateNewCollection(interaction, client, server, id);
var commandName = command.name;
//Check for a second person and create a second database entry if neccessary
if (message.mentions.users.first() != undefined) {
ecoimport.CreateNewCollection(message, client, server, message.mentions.users.first().id);
if (command.options && command.options.length > 0 && command.options[0].type == "USER") {
ecoimport.CreateNewCollection(interaction, client, server, command.options[0].value);
}
//#endregion
@@ -258,41 +265,30 @@ module.exports ={
//#region non-game-specific commands
//For TWO+ PLAYER games only!!!
if (command == 'accept') {
//Handle the messages
if (message.reference == null) { return message.reply("Please reply to a valid battle request message!"); }
let mid = message.reference.messageId;
let msg = await message.channel.messages.fetch(mid);
//Check if the person actually challenged you
//Get the length of any user tag
let mentioned = msg.mentions.users.keys();
let tag_len = String(mentioned.next().value).length;
//<@tage_len>, <@ --2+tag_len+2+3 = 7+tag_len
let other_tag = msg.content.substr(7+tag_len, tag_len);
const other_discord = msg.mentions.users.get(other_tag);
if (commandName == 'accept') {
const args = interaction.customId.split('|');
// console.log(interaction.message.interaction);
// console.log(interaction.user);
// return console.log(args);
//Should also check if the player is already playing a game!!!
if (!acceptIsValid(bot, other_discord, message, msg, tag_len)) { return; }
if (!acceptIsValid(bot, interaction.user, interaction, args[1], interaction.message)) { return; }
//Get the opponent
const other = db.collection(other_discord.id);
let startPos = msg.content.indexOf('"') + 1;
let newCommand = msg.content.substr(startPos, msg.content.lastIndexOf('"') - startPos);
const other_id = interaction.message.interaction.user.id;
const other = db.collection(other_id);
// return console.log(args, interaction.message.interaction);
let newCommand = interaction.message.interaction.commandName.split(" ")[1];
//#region BOT SECTION
//Store both IDs in the database (for turns)
let name_first = await bot.users.cache.get(id);
let name_second = await bot.users.cache.get(other_discord.id);
let name_second = await bot.users.cache.get(other_id);
// message.reply(`${first} [${name_first}], ${second} [${name_second}]`); throw 'ERR';
const threadname = `${name_first.username} VS ${name_second.username} [${newCommand.toUpperCase()}]`;
var newObj = {0: id, 1: other_discord.id, turn: 0, thread: threadname};
var newObj = {0: id, 1: other_id, turn: 0, thread: threadname};
if (newCommand.replaceAll(" ", "").toLowerCase() == 'tictactoe') { newCommand = 'Tic Tac Toe'; }
@@ -316,90 +312,110 @@ module.exports ={
//#endregion
const remAccptBtn = (msgToDel) => {
try {
msgToDel.edit({components: []});
} catch(err) {
console.error(err);
}
}
//Need this for all 2 player games
const result = Initialize(bot, dbo, newCommand, msg, id, other_discord.id, other);
const result = Initialize(bot, dbo, newCommand, interaction.message, id, other_id, other);
if (newCommand == 'battle') {
result.then(function (thread) {
battle.handle(client, dbo, other, bot, thread, 'initalize', mongouri, items, null, xp_collection);
remAccptBtn(interaction.message);
});
} else if (newCommand == 'Tic Tac Toe') {
result.then(function (thread) {
ttt.handle(client, db, dbo, other, bot, thread, 'initalize', mongouri, null, xp_collection);
remAccptBtn(interaction.message);
});
}
} else if (command == 'quit') {
} else if (commandName == 'quit') {
const channel = bot.channels.cache.get(interaction.channel.parentId);
const channel = bot.channels.cache.get(message.channel.parentId);
//Remove the turn counter from the bot's database
serverinbotdb.deleteOne({0: id} || {1: id});
if (doc.opponent != null) {
// let other = message.guild.members.cache.get(doc.opponent);
let other = db.collection(doc.opponent);
channel.send(`<@${message.author.id}> has quit a game of "${game}" with <@${doc.opponent}>!`);
winGame(client, bot, db, other, xp_collection, message);
channel.send(`<@${interaction.user.id}> has quit a game of "${game}" with <@${doc.opponent}>!`);
winGame(client, bot, db, other, xp_collection, interaction);
} else {
loseGame(dbo, xp_collection, message, bot);
channel.send(`<@${message.author.id}> has quit a game of "${game}"!`);
loseGame(dbo, xp_collection, interaction, bot);
channel.send(`<@${interaction.user.id}> has quit a game of "${game}"!`);
}
}
else if (command == 'status') {
getGame(message, args, db);
} else if (command == 'hp' || command == 'mp') {
hpmp(message, command, dbo);
else if (commandName == 'status') {
getGame(interaction, args, db);
} else if (commandName == 'hpmp') {
hpmp(interaction, commandName, dbo);
} else if (command == 'equip') {
// equipItem(client, bot, db, dbo, message);
equip(message, args, command, dbo, bot, items);
// This does not work
equip(interaction, args, command, dbo, bot, items);
} else if (command == 'classes') {
// This does not work
presentClasses(message, args[1]);
}
//#endregion
//#region game-specific commands
else {
if (command == undefined) { return message.reply("Please specify a game or use _!game help_"); }
if (commandName == undefined) { return interaction.reply("Please specify a game or use _!game help_"); }
//Make change to new name if necessary
if (command.replaceAll(" ", "").toLowerCase() == 'tictactoe') { command = 'Tic Tac Toe'; }
if (commandName.replaceAll(" ", "").toLowerCase() == 'tictactoe') { commandName = 'Tic Tac Toe'; }
if (game == 'battle' || command == 'battle') {
if (!bot.inDebugMode) { return message.reply("This command is currently in development!"); }
//RETURN TO THIS LATER
if (game == 'battle' || commandName == 'battle') {
if (!bot.inDebugMode) { return interaction.reply("This command is currently in development!"); }
//Handle sending the request and making sure the user exists here
let other_discord = message.mentions.users.first();
if (other_discord == undefined) {
return message.reply(`"${args[1]}" is not a valid user (use _!game battle @user_)`);
}
const row = new MessageActionRow()
.addComponents(
new MessageButton()
.setCustomId(`gameaccept|${command.options[0].value}|${interaction.user.id}`)
.setLabel('Accept Invite')
.setStyle('SUCCESS')
);
message.channel.send(`${other_discord}, <@${message.author.id}> has invited you to play _"battle"_. To accept, please reply to this message with _!game accept_`);
} else if (game == 'Tic Tac Toe' || command == 'Tic Tac Toe') {
let other_discord = message.mentions.users.first();
if (other_discord == undefined) {
return message.reply(`"${args[1]}" is not a valid user (use _!game tictactoe @user_)`);
}
const content = {content: `${command.options[0].user}, ${interaction.user} has invited you to play _"Tic Tac Toe"_. Click the button to accept the invitation!`, components: [row]};
interaction.reply(content).catch((err) => interaction.channel.send(content));
message.channel.send(`${other_discord}, <@${message.author.id}> has invited you to play _"Tic Tac Toe"_. To accept, please reply to this message with _!game accept_`);
} else if (game == 'trivia' || command == 'trivia') {
trivia.execute(message, args, Discord, client, bot);
} else if (game == "minesweeper" || command == 'minesweeper') {
if (game == "minesweeper" && command == 'minesweeper') {
} else if (game == 'Tic Tac Toe' || commandName == 'Tic Tac Toe') {
const row = new MessageActionRow()
.addComponents(
new MessageButton()
.setCustomId(`gameaccept|${command.options[0].value}|${interaction.user.id}`)
.setLabel('Accept Invite')
.setStyle('SUCCESS')
);
const content = {content: `${command.options[0].user}, ${interaction.user} has invited you to play _"Tic Tac Toe"_. Click the button to accept the invitation!`, components: [row]};
interaction.reply(content).catch((err) => interaction.channel.send(content));
} else if (game == 'trivia' || commandName == 'trivia') {
trivia.execute(interaction, Discord, client, bot);
} else if (game == "minesweeper" || commandName == 'minesweeper') {
if (game == "minesweeper" && commandName == 'minesweeper') {
return message.reply("You're already in a game!");
}
const threadname = `${message.author.username} has started a solo game of Minesweeper`;
const thread = await message.channel.threads.create({
const threadname = `${interaction.user.username} is playing Minesweeper`;
const thread = await interaction.channel.threads.create({
name: threadname,
// type: 'GUILD_PRIVATE_THREAD',
autoArchiveDuration: 60,
reason: `N/A`,
});
mnswpr.handle(bot, null, thread, message, args);
mnswpr.handle(bot, interaction, thread);
}
//Catch statement (invalid command)
else {
message.reply(`'${bot.prefix}game ${command}' is not a command!`);
interaction.reply(`'/game ${commandName}' is not a command!`);
}
}
//#endregion
+61
View File
@@ -0,0 +1,61 @@
const { Constants } = require('discord.js');
const { trivia_categories } = require('./trivia_categories.json');
module.exports = [
{
name: "trivia",
description: 'Start a game of Trivia',
type: Constants.ApplicationCommandOptionTypes.SUB_COMMAND,
options: [
{name: 'difficulty', description: 'The question difficulty OR "help"', type: Constants.ApplicationCommandOptionTypes.STRING, required: true, choices: [{name: 'easy', value: 'easy'}, {name: 'medium', value: 'medium'}, {name: 'hard', value: 'hard'}]},
{name: 'category', description: 'The trivia Category', type: Constants.ApplicationCommandOptionTypes.INTEGER, required: false, choices: trivia_categories},
{name: 'time', description: 'Set the round length (in seconds)', type: Constants.ApplicationCommandOptionTypes.INTEGER, required: false},
]},
{
name: "battle",
description: 'Start a game of Batte',
type: Constants.ApplicationCommandOptionTypes.SUB_COMMAND,
options: [
{name: 'opponent', description: 'Who do you want to battle against?', type: Constants.ApplicationCommandOptionTypes.USER, required: true},
]},
{
name: "minesweeper",
description: "Start a game of Minesweeper",
type: Constants.ApplicationCommandOptionTypes.SUB_COMMAND,
options: [
{name: 'difficulty', description: 'Set the diffficulty', type: Constants.ApplicationCommandOptionTypes.STRING, required: true, choices: [{name: 'easy', value: 'easy'}, {name: 'medium', value: 'medium'}, {name: 'hard', value: 'hard'}]},
// {name: 'opponent', description: 'Play a game against someone else', type: Constants.ApplicationCommandOptionTypes.USER, required: false}
]
},
{
name: "tictactoe",
description: 'Start a game of TicTacToe',
type: Constants.ApplicationCommandOptionTypes.SUB_COMMAND,
options: [
{name: 'opponent', description: 'Who do you want to play against?', type: Constants.ApplicationCommandOptionTypes.USER, required: true},
]},
{
name: "quit",
description: "Quit your current game",
type: Constants.ApplicationCommandOptionTypes.SUB_COMMAND,
options: []
},
{
name: "status",
description: "Check your current game status",
type: Constants.ApplicationCommandOptionTypes.SUB_COMMAND,
options: []
},
{
name: "hpmp",
description: "Check your current game status",
type: Constants.ApplicationCommandOptionTypes.SUB_COMMAND,
options: []
},
]
+24 -17
View File
@@ -3,23 +3,24 @@ const { winGame, loseGame, equipItem } = require('./external_game_functions.js')
const wait = require('node:timers/promises').setTimeout;
const { STATE } = require('../db/econ.js')
function startGame(bot, channel, message, args) {
function startGame(bot, channel, interaction) {
const args = interaction.options.data;
let componentlist = [];
var diff;
if (args.length < 1 || args[0] == 'easy') {
if (args.length < 1 || args[0].value == 'easy') {
diff = 0;
} else if (args[0] == 'medium') {
} else if (args[0].value == 'medium') {
diff = 0.1;
} else if (args[0] == 'hard') {
} else if (args[0].value == 'hard') {
diff = 0.2;
} else {
diff = 0;
}
let user = '';
if (args.length < 2 || args[1] == 'solo') {
user = message.author.id;
if (args.length < 2) {
user = interaction.user.id;
}
for (let i = 0; i < 5; i ++) {
@@ -45,6 +46,7 @@ function startGame(bot, channel, message, args) {
componentlist.push(row);
}
interaction.reply(`${interaction.user} has started a solo game of Minesweeper!`);
channel.send({ content: `SCORE: \`0\`\nTILES LEFT: \`25\``, components: componentlist });
}
@@ -90,7 +92,7 @@ async function changeBoard(bot, interaction, xp_collection) {
const user = id[4];
if (user && user != '') {
if (interaction.user.id != user) {
interaction.user.send(`Message from a Minesweeper game in <#${interaction.channel.id}>: ***It's not your turn!***`);
interaction.user.send(`Message from a Minesweeper game in <#${interaction.channel.id}>: ***This is a solo game!***`);
return; // interaction.reply({ content: "It's not your turn!", ephemeral: true }); //Can only reply once
}
}
@@ -103,7 +105,7 @@ async function changeBoard(bot, interaction, xp_collection) {
bot.mongoconnection.then((client) => { client.db(interaction.guildId).collection(interaction.user.id).updateOne({ game: {$exists: true} }, { $set: { game: null } }); });
const channel = bot.channels.cache.get(interaction.message.channel.parentId);
channel.send(`${interaction.user} found a bomb in Minesweeper!`);
interaction.channel.send(`\`Thread closing\` <t:${Math.floor((new Date()).getTime()/1000) + 8}:R>`);
interaction.channel.send(`\`Thread closing\` <t:${Math.floor((new Date()).getTime()/1000) + 10}:R>`);
await wait(7000);
interaction.channel.delete();
@@ -140,29 +142,34 @@ async function changeBoard(bot, interaction, xp_collection) {
}
function checkAndStartGame(bot, message, channel, args) {
function checkAndStartGame(bot, interaction, channel) {
bot.mongoconnection.then(client => {
const db = client.db(message.guild.id);
const dbo = db.collection(message.author.id);
const db = client.db(interaction.guildId);
const dbo = db.collection(interaction.user.id);
dbo.findOne({game: {$exists: true}}).then((doc) => {
try {
if (doc.game != null) { return message.reply("You're already in a game!"); }
if (doc.game != null) {
console.log(doc);
return interaction.reply("You're already in a game!").catch((err) => {
interaction.channel.send("You're already in a game!");
});
}
dbo.updateOne({ "game": {$exists: true} }, { $set: { game: "minesweeper", state: STATE.FIGHTING }});
startGame(bot, channel, message, args);
startGame(bot, channel, interaction);
} catch (err) {
console.log(err);
const { addComplaintButton } = require('../dev only/submitcomplaint.js');
addComplaintButton(bot, message);
addComplaintButton(bot, interaction.message);
}
});
});
}
function handle(bot, interaction, channel = null, message = null, args = null, xp_collection = null) {
if (channel != null && args != null) {
checkAndStartGame(bot, message, channel, args);
function handle(bot, interaction, channel = null, isStart = true, xp_collection = null) {
if (isStart) {
checkAndStartGame(bot, interaction, channel);
} else {
//Maybe add player checking later?
changeBoard(bot, interaction, xp_collection);
+2 -1
View File
@@ -124,7 +124,6 @@ async function handle(client, db, dbo, other, bot, thread, command, doc, interac
let board = ["", "", "", "", "", "", "", "", ""];
postActionBar(thread, dbo, board, false,true);
} else {
//Change the board
let square = Number(interaction.customId.split('|')[1]);
let symbol = doc.symbols[doc.turn];
@@ -143,6 +142,8 @@ async function handle(client, db, dbo, other, bot, thread, command, doc, interac
changeTurn(client, bot, interaction);
} else {
postActionBar(interaction, dbo, board, won);
//Maybe add a "close board" button instead of this
await wait(7000);
winGame(client, bot, db, dbo, xp_collection, interaction.message);
}
+34 -23
View File
@@ -3,11 +3,12 @@ const fetch = require('node-fetch');
const categoriesJSON = require('./trivia_categories.json').trivia_categories;
const { decode } = require('html-entities');
const { MongoClient, ServerApiVersion } = require('mongodb');
const { Interaction } = require('discord.js');
const categories = new Map();
for (i in categoriesJSON) {
categories.set(categoriesJSON[i].name, categoriesJSON[i].id);
categories.set(categoriesJSON[i].name, categoriesJSON[i].value);
}
// const { jsonToMapRecursive, mapToTableRecursive } = require('../utils/jsonFormatters.js');
@@ -41,11 +42,11 @@ function changeDB(bot, message, m) {
/**
* @param {*} message
* @param {Interaction} interaction
* @param {Map<string, object>} m
* @param {int} time
*/
function startTrivia(message, m, time, bot) {
function startTrivia(interaction, m, time, bot) {
var iter = m.values().next();
var obj = iter.value;
@@ -63,8 +64,10 @@ function startTrivia(message, m, time, bot) {
return (response.content.toLowerCase() == answer.toLowerCase());
};
message.reply({ content: `${question}\n(Type your answers below!)`, fetchReply: true })
.then(() => {
interaction.channel.send({ content: `${question}\n(Type your answers below!)`, fetchReply: true })
.then((message) => {
interaction.reply({content: `Trivia started by ${interaction.user}`});
const timeList = ['🔟', '9️⃣', '8️⃣', '7️⃣', '6️⃣', '5️⃣', '4️⃣', '3️⃣', '2️⃣', '1️⃣', '0️⃣' ];
var i = 0;
const intId = setInterval(() => { if (i < timeList.length) { message.react(timeList[i]); i++ } }, Math.round(time/11));
@@ -72,9 +75,9 @@ function startTrivia(message, m, time, bot) {
message.channel.awaitMessages({ filter, max: 10, time: time }) // , errors: ['time']
.then((collected) => {
if (collected.size > 0) {
message.reply(`${collected.first().author} got the correct answer (${answer})!`);
message.reply(`${collected.first().author} got the correct answer (||${answer}||) first!`);
} else {
message.reply('Tsk Tsk, looks like nobody got the answer this time.');
message.reply(`Tsk Tsk, looks like nobody got the answer (||${answer}||) this time.`);
}
// changeDB(bot, message, null);
@@ -82,7 +85,7 @@ function startTrivia(message, m, time, bot) {
})
.catch((collected) => {
console.log(collected);
message.reply('Tsk Tsk, looks like nobody got the answer this time.');
message.reply(`Tsk Tsk, looks like nobody got the answer (||${answer}||) this time.`);
// changeDB(bot, message, null);
clearInterval(intId);
});
@@ -94,25 +97,31 @@ function startTrivia(message, m, time, bot) {
module.exports = {
name: 'trivia',
async execute(message, args, Discord, Client, bot) {
const difficult = ['easy', 'medium', 'hard'];
async execute(interaction, Discord, Client, bot) {
const opts = interaction.options.data[0].options;
const args = [];
args[0] = opts.filter((o) => { return (o.name == 'difficulty'); })[0];
args[1] = opts.filter((o) => { return (o.name == 'category'); })[0];
args[2] = opts.filter((o) => { return (o.name == 'time'); })[0];
const difficult = ['hard', 'medium', 'easy'];
let inputs = ['easy', ''];
if (args[0] && difficult.includes(args[0].toLowerCase())) {
inputs[0] = args[0].toLowerCase();
} else if (args[0] == 'help') {
let temp = `Use ${bot.prefix}trivia [difficulty (easy, medium, hard)] [topic] [time]\n`;
if (args[0] && difficult.includes(args[0].value.toLowerCase())) {
inputs[0] = args[0].value.toLowerCase();
} else if (args[0].value == 'help') {
let temp = `Use /trivia [difficulty (easy, medium, hard)] [category] [time]\n`;
temp += '**__Trivia Categories__**\n';
categories.forEach((val, key) => {
temp += `_${key}_\n`;
})
});
temp += '_Please copy and paste the FULL NAME if you want to use a category';
return message.reply(temp);
return interaction.reply(temp);
}
if (args[1] && Array.from(categories.keys()).includes(args[1])) {
inputs[1] = categories.get(args[1]);
if (args[1] && Array.from(categories.keys()).includes(args[1].value)) {
inputs[1] = categories.get(args[1].value);
}
// Get all categories mapped to their ids
@@ -142,9 +151,11 @@ module.exports = {
// console.log(decode(query));
query = decode(query);
//Get the answer (may have "" in it)
const question = query.substring(query.indexOf('question":') + 10, query.indexOf('","correct_answer'));
// console.log(`Q: ${question}\n\nActual: ${query}\n---------------------------------------`);
if (bot.inDebugMode) {
//Get the answer (may have "" in it)
// const question = query.substring(query.indexOf('question":') + 10, query.indexOf('","correct_answer'));
// console.log(`Q: ${question}\n\nActual: ${query}\n---------------------------------------`);
}
let q = query.split('","');
// queries[ind] = q;
@@ -156,11 +167,11 @@ module.exports = {
i ++;
});
const time = args[2] || (difficult.indexOf(inputs[0]) + 1) * 10000;
const time = (args[2]) ? args[2].value : (difficult.indexOf(inputs[0]) + 1) * 10000;
// console.log(m, time);
// changeDB(bot, message, m);
startTrivia(message, m, time, bot);
startTrivia(interaction, m, time, bot);
}
});
}
+24 -24
View File
@@ -1,28 +1,28 @@
{
"trivia_categories": [
{ "id": 9, "name": "General Knowledge" },
{ "id": 10, "name": "Entertainment: Books" },
{ "id": 11, "name": "Entertainment: Film" },
{ "id": 12, "name": "Entertainment: Music" },
{ "id": 13, "name": "Entertainment: Musicals & Theatres" },
{ "id": 14, "name": "Entertainment: Television" },
{ "id": 15, "name": "Entertainment: Video Games" },
{ "id": 16, "name": "Entertainment: Board Games" },
{ "id": 17, "name": "Science & Nature" },
{ "id": 18, "name": "Science: Computers" },
{ "id": 19, "name": "Science: Mathematics" },
{ "id": 20, "name": "Mythology" },
{ "id": 21, "name": "Sports" },
{ "id": 22, "name": "Geography" },
{ "id": 23, "name": "History" },
{ "id": 24, "name": "Politics" },
{ "id": 25, "name": "Art" },
{ "id": 26, "name": "Celebrities" },
{ "id": 27, "name": "Animals" },
{ "id": 28, "name": "Vehicles" },
{ "id": 29, "name": "Entertainment: Comics" },
{ "id": 30, "name": "Science: Gadgets" },
{ "id": 31, "name": "Entertainment: Japanese Anime & Manga" },
{ "id": 32, "name": "Entertainment: Cartoon & Animations" }
{ "value": 9, "name": "General Knowledge" },
{ "value": 10, "name": "Entertainment: Books" },
{ "value": 11, "name": "Entertainment: Film" },
{ "value": 12, "name": "Entertainment: Music" },
{ "value": 13, "name": "Entertainment: Musicals & Theatres" },
{ "value": 14, "name": "Entertainment: Television" },
{ "value": 15, "name": "Entertainment: Video Games" },
{ "value": 16, "name": "Entertainment: Board Games" },
{ "value": 17, "name": "Science & Nature" },
{ "value": 18, "name": "Science: Computers" },
{ "value": 19, "name": "Science: Mathematics" },
{ "value": 20, "name": "Mythology" },
{ "value": 21, "name": "Sports" },
{ "value": 22, "name": "Geography" },
{ "value": 23, "name": "History" },
{ "value": 24, "name": "Politics" },
{ "value": 25, "name": "Art" },
{ "value": 26, "name": "Celebrities" },
{ "value": 27, "name": "Animals" },
{ "value": 28, "name": "Vehicles" },
{ "value": 29, "name": "Entertainment: Comics" },
{ "value": 30, "name": "Science: Gadgets" },
{ "value": 31, "name": "Entertainment: Japanese Anime & Manga" },
{ "value": 32, "name": "Entertainment: Cartoon & Animations" }
]
}
+29 -6
View File
@@ -1,13 +1,16 @@
const { MongoClient, ServerApiVersion } = require('mongodb');
const { createSubscriptionManual } = require('./premium/stripe.js');
const { pause_start_stop, playNext, showQueue } = require('./audio/audioMain.js');
const { resolveComplaint } = require('./dev only/submitcomplaint.js');
const { RSSInteractionHandler } = require('../side projects/RSSHandlers/rssFeed.js');
const reminders = require('./premium/reminders.js');
const reactionrole = require('./admin/reactionrole.js');
const tuto = require('./Selmer Specific/tuto');
const mswpr = require('./games/minesweeper.js');
const giveaway = require('./misc/giveaway.js');
const setup = require('./admin/easySetup.js');
// const { RSSInteractionHandler } = require('./premium/rssFeed.js');
const { Interaction } = require('discord.js')
const { Interaction, Client } = require('discord.js');
const Discord = require('discord.js');
/**
*
@@ -88,10 +91,16 @@ async function handle_interaction(interaction, mongouri, turnManager, bot, STATE
const page = Number(interaction.customId.split('|')[1]);
tuto.postEmbd(bot, interaction, page, true);
} else if (interaction.customId.indexOf("mswpr|") != -1) {
mswpr.handle(bot, interaction, interaction.channel, interaction.message, null, xp_collection);
mswpr.handle(bot, interaction, interaction.channel, false, xp_collection);
} else if (interaction.customId.indexOf("sbtutorial") != -1) {
interaction.deferUpdate();
tuto.execute(interaction.message, null, null, null, bot);
tuto.execute(interaction, null, null, bot);
} else if (interaction.customId.indexOf("gameaccept") != -1) {
bot.commands.get('game').execute(bot, interaction, {name: 'accept'}, Discord, mongouri, items, xp_collection);
} else if (interaction.customId.indexOf("setupBtn") != -1) {
setup.handle(bot, interaction);
} else if (interaction.customId.indexOf('addRole') != -1) {
reactionrole.handleBtn(interaction);
} //Button else ifs here
});
}
@@ -157,12 +166,26 @@ async function handle_interaction(interaction, mongouri, turnManager, bot, STATE
//Forms
else if (interaction.isModalSubmit()) {
reminders.modalHandle(bot, interaction);
if (interaction.customId.indexOf('newEventModal') != -1) {
reminders.modalHandle(bot, interaction);
} else if (interaction.customId.indexOf('giveawayModal') != -1) {
giveaway.processForm(interaction, bot);
} else if (interaction.customId.indexOf('reactionModal') != -1) {
reactionrole.processForm(interaction, bot);
}
} //other selection types here
}
/**
* @param {Client} bot
* @param {Interaction} interaction
*/
async function handleContext(bot, interaction) {
const { targetId, commandName, user } = interaction;
console.log(interaction);
}
module.exports = { handle_interaction }
module.exports = { handle_interaction, handleContext }
//values: [ 'price_1LI5pzFtuywsbrwdlY1gWMkV' ]
//values: [ 'price_1LIpROFtuywsbrwdmxOb8Baj' ]
+11 -10
View File
@@ -18,10 +18,11 @@ getAllCoins();
module.exports = {
name: 'crypto',
description: 'Get the prices for most cryptocurrencies!',
async execute(message, args, Discord, Client, bot) {
if (args.length < 1 || args[0] == 'help') {
return message.reply("Please specify at least one cryptocurrency (_ex: !crypto BTC_) or list all currencies (_!crypto list_)");
} else if (args[0] == 'list') {
async execute(interaction, Discord, Client, bot) {
const args = interaction.options.data;
if (args.length < 1 || args[0].value == 'help') {
return interaction.reply("Please specify at least one cryptocurrency (_ex: !crypto BTC_) or list all currencies (_!crypto list_)");
} else if (args[0].value == 'list') {
try {
return new Promise((resolve, reject) => {
let temp = "```Name --> Symbol\n\n";
@@ -32,11 +33,11 @@ module.exports = {
resolve(temp);
}).then((temp) => {
message.reply(temp);
interaction.reply(temp);
})
} catch (err) {
console.error(err);
return message.reply("Uh Oh! There's been an error!");
return interaction.reply("Uh Oh! There's been an error!");
}
}
@@ -47,7 +48,7 @@ module.exports = {
var datacc = data.data.tickers.filter(t => t.target == 'USD');
const temp = datacc.filter(t => t.base == args[0]);
const temp = datacc.filter(t => t.base == args[0].value);
const res = temp.length == 0 ? [] : temp[0];
//price: res.last, symbol: base, trust score: trust_score
@@ -67,11 +68,11 @@ module.exports = {
.setTimestamp()
.setFooter({ text: 'Selmer Bot uses CoinGecko for cryptocurrency information'});
message.reply({ embeds: [embd] });
interaction.reply({ embeds: [embd] });
} catch (err) {
console.error(err);
return message.reply("Uh Oh! There's been an error!");
return interaction.reply("Uh Oh! There's been an error!");
}
},
options: [{name: 'qeury', description: 'Name or List', type: Constants.ApplicationCommandOptionTypes.STRING, required: true}]
options: [{name: 'query', description: 'Name or List', type: Constants.ApplicationCommandOptionTypes.STRING, required: true}]
}
+182
View File
@@ -0,0 +1,182 @@
const { Interaction, MessageActionRow, MessageButton, MessageEmbed, Modal, TextInputComponent } = require('discord.js');
const { checkRole } = require('../admin/verify.js');
async function postForm(interaction) {
// Create the modal
const modal = new Modal();
modal.setTitle('Creating a new giveaway')
.setCustomId('giveawayModal');
// Add components to modal
// Create the text input components
// The label is the prompt the user sees for this input
// Short means only a single line of text
// Paragraph means multiple lines of text
const durationInp = new TextInputComponent()
.setCustomId('duration')
.setLabel("How long should the givaway last?")
.setPlaceholder('10S or 10M or 10H or 10D between 1 minute and 30 days')
.setStyle('SHORT');
const titleInp = new TextInputComponent()
.setCustomId('title')
.setLabel("What should the giveaway be called?")
.setStyle('SHORT');
const descriptionInp = new TextInputComponent()
.setCustomId('description')
.setLabel("Describe the giveaway")
.setPlaceholder('A fun giveaway!')
.setStyle('PARAGRAPH');
const prizeInp = new TextInputComponent()
.setCustomId('prize')
.setLabel("What does the winner....win?")
.setPlaceholder('Discord Nitro')
.setStyle('SHORT');
const winnersInp = new TextInputComponent()
.setCustomId('winners')
.setLabel("Number of winners")
.setPlaceholder('Between 1 and 99 winners')
.setMinLength(1)
.setMaxLength(2)
.setStyle('SHORT');
// An action row only holds one text input,
// so you need one action row per text input.
const title = new MessageActionRow().addComponents(titleInp);
const desc = new MessageActionRow().addComponents(descriptionInp);
const dur = new MessageActionRow().addComponents(durationInp);
const prize = new MessageActionRow().addComponents(prizeInp);
const winners = new MessageActionRow().addComponents(winnersInp);
// Add inputs to the modal
modal.addComponents(title, desc, dur, prize, winners);
// Show the modal to the user
interaction.showModal(modal);
}
/**
* @param {Interaction} interaction
*/
function processForm(interaction, bot) {
const title = interaction.fields.getTextInputValue('title');
const desc = interaction.fields.getTextInputValue('description');
const dur = interaction.fields.getTextInputValue('duration');
const prize = interaction.fields.getTextInputValue('prize');
const winners = interaction.fields.getTextInputValue('winners');
postGiveawayMessage(bot, interaction, title, desc, dur, prize, winners);
}
/**
* @param {Interaction} interaction
*/
function postGiveawayMessage(bot, interaction, title, desc, dur, prize, winners) {
const author = {
name: "Selmer Bot",
url: "",
iconURL: bot.user.displayAvatarURL()
};
var timeAdjuster = 1;
var time;
if (dur.indexOf('M') != -1) {
timeAdjuster = 60;
time = dur.replace("M", "");
} else if (dur.indexOf('H') != -1) {
timeAdjuster = 3600;
time = dur.replace("H", "");
} else if (dur.indexOf('D') != -1) {
timeAdjuster = 86400;
time = dur.replace("D", "");
}
if (!Number.isInteger(Number(time))) {
return interaction.reply("Please enter a valid time in the following format: 10[S, M, H, D]");
} else if (!Number.isInteger(Number(winners))) {
return interaction.reply("Please enter a valid NUMBER of winners");
}
const embd = new MessageEmbed()
.setTitle(title)
.setAuthor(author)
.setDescription(desc)
.addFields([
{name: 'Prize', value: prize},
{name: 'Ends in', value: `<t:${Math.floor((new Date()).getTime()/1000) + (Number(time) * timeAdjuster)}:R>`},
{name: 'Host', value: `${interaction.user}`},
{name: 'Number of Contestants', value: '0'},
{name: 'Number of Winners', value: winners}
]);
const msgPromise = interaction.channel.send({ embeds: [embd] });
msgPromise.then((msg) => {
msg.react('🎉');
var nonbots = [];
const filter = (reaction, user) => {
if (!user.bot) {
nonbots.push(user);
}
return (reaction.emoji.name === '🎉');
};
const collector = msg.createReactionCollector({ filter, time: 5000 });
// collector.on('collect', (reaction, user) => {
// console.log(`Collected ${reaction.emoji.name} from ${user.tag}`);
// });
collector.on('end', collected => {
const finalists = [];
if (winners >= nonbots.length) {
for (let i = 0; i < nonbots.length; i++) {
finalists.push(`<@${nonbots[i].id}>`);
}
} else {
for (let i = 0; i < winners; i++) {
const ind = Math.floor(nonbots.length * Math.random());
finalists.push(`<@${nonbots[ind].id}>`);
delete nonbots[ind];
nonbots = nonbots.filter((val) => { return (val != null); });
}
}
let embd = msg.embeds[0];
if (winners > 1) {
msg.reply(`And the winners are:\n${finalists.join("\n")}`);
embd.fields.push({name: 'winners:', value: `${finalists.join("\n")}`, inline: true});
} else {
msg.reply(`And the winner is:\n${finalists.join("\n")}`);
embd.fields.push({name: 'winner:', value: `${finalists.join("\n")}`, inline: true});
}
msg.edit({ embeds: [embd] });
});
interaction.reply({ content: 'Giveaway posted!', ephemeral: true });
});
}
module.exports = {
name: 'giveaway',
description: 'Create a giveaway',
execute(interaction, Discord, Client, bot) {
postForm(interaction);
},
processForm,
options: []
}
+12 -9
View File
@@ -17,14 +17,15 @@ module.exports ={
if (spec == 'econ') {
let temp = "***Selmer Bot Commands (Econ):***\n";
temp += bot.commands.get('econ').econHelp();
temp += `\n\n(remember to use _'${bot.prefix}'_ before the command!)`;
temp += `\n\n(remember to use \`/\` before the command!)`;
return interaction.reply({ content: temp, ephemeral: true });
}
else if (spec == 'game') {
let temp = "***Selmer Bot Commands (Games):***\n";
temp += bot.commands.get('game').allGames.join(", ");
temp += `\n\n_Note: due to how complicated this feature is, it will not be migrated to slash commands for now_`;
// temp += `\n\n_Note: due to how complicated this feature is, it will not be migrated to slash commands for now_`;
temp += `\n\n(remember to use \'/\' before the command!)`;
return interaction.reply({ content: temp, ephemeral: true });
}
@@ -39,8 +40,8 @@ module.exports ={
temp += modHelp();
//Uses a different format, only the server owner can use it
temp += '\n_setup_ - ***SERVER OWNER ONLY*** - use _!setup help_\n';
temp += `\n\n(remember to use _'${bot.prefix}'_ before the command!)`;
temp += '\n_setup_ - ***SERVER OWNER ONLY*** - use \`setup help\`\n';
temp += `\n\n(remember to use \`/\` before the command!)`;
return interaction.reply({ content: temp, ephemeral: true });
}
@@ -55,10 +56,10 @@ module.exports ={
bot.commands.forEach((comm) => {
if (comm.name != 'verify') {
if (comm.name == 'econ') {
temp += `**econ** - use _!help econ_\n`;
temp += `**econ** - use \`/help econ\`\n`;
}
else if (comm.name == 'game') {
temp += `**games** - use _!help game_\n`;
temp += `**games** - use \`/help game\`\n`;
}
else {
if (comm.name && comm.description && !noPostList.includes(comm.name)) {
@@ -68,16 +69,18 @@ module.exports ={
}
});
temp += '**admin/moderation commands** - use !help admin\n';
temp += '**admin/moderation commands** - use `/help admin`\n';
//Selmer Specific
temp += '\n__**Selmer\'s \\*Special\\* Commands**__\n'
sList.forEach((commName) => {
const comm = bot.commands.get(commName);
temp += `${comm.name.toLowerCase()} - _${comm.description}_\n`;
if (comm && comm.name && comm.description) {
temp += `${comm.name.toLowerCase()} - _${comm.description}_\n`;
}
})
temp += `\n_(remember to use '${bot.prefix}' before the command!)_`;
temp += `\n_(remember to use \`/\` before the command!)_`;
interaction.reply({ content: temp, ephemeral: true });
},
-40
View File
@@ -1,40 +0,0 @@
const memes = require("random-memes");
const { randomHexColor } = require('../admin/colorgen.js');
module.exports = {
name: 'meme',
description: 'Selmer Bot will post a random meme from reddit',
async execute(interaction, Discord, Client, bot) {
interaction.deferReply();
memes.random().then(meme => {
const newEmbed = new Discord.MessageEmbed()
.setColor(randomHexColor())
.setTitle(meme.caption)
// .setURL(meme.image)
.setDescription(`category: ${meme.category}`)
.setImage(meme.image);
interaction.editReply({ embeds: [newEmbed] });
}).catch(async err => {
console.log(err);
//Try a different way
const fetch = require('node-fetch');
const response = await fetch('https://some-random-api.ml/meme');
const data = await response.json().catch(err => {
console.log(err);
return interaction.reply("_Uh oh, something's gone wrong!_");
});
const newEmbed = new Discord.MessageEmbed()
.setColor(randomHexColor())
.setTitle(data.caption)
// .setURL(data.image)
.setDescription(`category: ${data.category}`)
.setImage(data.image);
interaction.editReply({ embeds: [newEmbed] });
});
}, options: []
}
-9
View File
@@ -1,9 +0,0 @@
module.exports = {
name: 'reactionrole',
description: 'Creates an embed that will give a role when reacted to',
async execute(message, args, Discord, bot) {
if (bot.commands.get('verify').checkRole(message, args)) {
console.log("IS ADMNIN");
}
}
}
+11 -1
View File
@@ -1,14 +1,24 @@
const hastebin = require("hastebin-gen");
const { addComplaintButton } = require('../dev only/submitcomplaint');
const { Constants } = require('discord.js');
const { URL } = require("url");
const axios = require('axios');
const { isValidUrl } = require('../dev only/setPresence.js');
module.exports ={
name: "scrape",
description: "Scrapes a website, then puts the result into a hastebin",
async execute(interaction, Discord, Client, bot) {
const axios = require('axios');
// const cheerio = require('cheerio');
const url = interaction.options.data[0].value;
if (!isValidUrl(url)) {
return interaction.reply("Please enter a valid url").catch((err) => {
interaction.channel.send("Please enter a valid url");
});
}
axios(url)
.then(async response => {
const html = response.data;
+11 -1
View File
@@ -24,7 +24,8 @@ async function messageExists(message) {
async function getResponse(convo, bot) {
const response = await bot.openai.createCompletion({
try {
const response = await bot.openai.createCompletion({
model: "text-davinci-002",
prompt: convo,
temperature: 0.9,
@@ -36,6 +37,11 @@ async function getResponse(convo, bot) {
});
return response;
} catch (err) {
console.error(err);
return false;
}
}
async function convoManager(clientinp, bot, message) {
@@ -78,6 +84,10 @@ async function convoManager(clientinp, bot, message) {
//Get the response
const r = await getResponse(convo, bot);
if (!r) {
return message.channel.send("Uh oh! There's been an error! Please contact support \:[");
}
let response = r.data.choices[0].text;
convo += (response + '\n');
+241
View File
@@ -0,0 +1,241 @@
const { ICalParser } = require('cozy-ical');
const { URL } = require('url');
const { Constants } = require('discord.js');
const request = require('request');
const { verPremium } = require('../premium/verifyPremium.js');
const { isValidUrl } = require('../dev only/setPresence.js');
//#region SET REMINDERS
function addReminder(interaction, bot, obj, Id) {
return new Promise((resolve, reject) => {
try {
bot.mongoconnection.then((client) => {
// Update the Key object first to check if the time is already there
const kbo = client.db('main').collection('reminderKeys');
kbo.findOne(({ 'userId': Id })).then((doc) => {
const t = obj.time.toString();
try {
if (doc.times.indexOf(obj.time) == -1) {
kbo.updateOne({ 'userId': Id }, { $push: { times: t } })
} else {
//Event already exists at this time
reject("An event already exists at this time!");
return; // interaction.channel.send("An event already exists at this time!");
}
//Update the Time object
const dbo = client.db('main').collection('reminders');
dbo.findOne({ time: t }).then((doc) => {
let n = 0;
if (doc) {
n = doc.amt;
doc.amt ++;
doc[`${n}`] = obj.event;
dbo.findOneAndReplace({ time: t }, doc);
} else {
const d = new Date(Number(obj.time));
doc = { "0": obj.event, "time": t, "month": d.getMonth(), "amt": 1 }; //Month used for clearing when the calendar month begins (maybe modify the garbage collection with an `else if (day == 1? clear last month)` )
dbo.insertOne(doc);
}
//Reply with the reminder in correct format
// interaction.reply({ content: "REMINDER SAVED!", embeds: [embd], ephemeral: true });
resolve(true);
}).catch((err) => {
console.error(err);
// interaction.reply("Uh Oh! An error has occured!");
});
} catch (err) {
console.error(err); //interaction.reply("Uh Oh! An error has occured!");
reject(err);
}
}).catch((err) => {
console.log("ERR");
console.error(err);
reject(false);
// interaction.reply("Uh Oh! An error has occured!");
});
});
} catch (err) {
console.error(err);
// return interaction.reply("Uh Oh! An error has occured!"); // Gets "acknowledged" too many times
}
});
}
//#endregion
//#region SETUP AND PARSING
class calClass {
constructor(vevent) {
const model = vevent.model;
const alarms = vevent.subComponents;
if (alarms && alarms[0] && alarms[0].model) {
let temp = alarms[0].model.trigger;
temp = temp.split('DT')[1];
temp = temp.split("H");
let hours = (temp[0] && temp[0] != '0') ? Number(temp[0]) : 0;
let minutesStr = temp[1].split('M')[0];
let minutes = (minutesStr && !isNaN(minutesStr)) ? hours + Number(minutesStr) : hours;
this.offset = minutes;
} else {
this.offset = 0;
}
this.name = model.summary;
this.description = (model.description) ? model.description : "N/A";
const dateTime = new Date(model.startDate);
this.time = dateTime.getTime()/1000;
if (isValidUrl(model.location)) {
this.url = model.location || undefined;
this.loc = undefined;
} else {
this.url = undefined;
this.loc = model.location || undefined;
}
}
isValidUrl(s) {
try {
new URL(s);
return true;
} catch (err) {
return false;
}
}
exportAsObj(gid, uid) {
const obj = { time: this.time, event: { guildId: gid, userId: uid, name: this.name, description: this.description, offset: this.offset, link: this.url, location: this.loc } }
return obj;
}
}
function parseData(calStr, gid, uid) {
return new Promise((resolve, reject) => {
const parser = new ICalParser();
parser.parseString(calStr, function(err, cal) {
try {
const arr = [];
for (i in cal.subComponents) {
const a = new calClass(cal.subComponents[i]);
arr.push(a.exportAsObj(gid, uid));
}
resolve(arr);
} catch (err) {
console.log(err);
reject(err);
}
});
});
}
/**
* @param {String} fileName
*/
function readFileAndParse(url, bot, interaction, gid, uid) {
request.get(url, function (error, response, body) {
try {
if (!error && response.statusCode == 200) {
const isGuild = (gid && !uid) || false;
const notAdded = [];
const Id = (isGuild) ? gid : uid;
parseData(body, gid, uid).then((arr) => {
new Promise((resolve, reject) => {
// If the key does not exist, create it
if (arr.length > 0) {
bot.mongoconnection.then((client) => {
const kbo = client.db('main').collection('reminderKeys');
kbo.findOne(({ 'userId': Id })).then((doc) => {
if (!doc) {
doc = { userId: Id, times: [] }
kbo.insertOne(doc);
reject("New user created, please try again!");
} else {
resolve(true);
}
});
});
} else {
reject("No data...");
}
// const m = new Map(arr.map((obj, ind) => { return [ind, obj]; }));
}).then(() => {
Promise.all(arr.map((obj, ind) => {
addReminder(interaction, bot, obj, Id).then(() => {})
.catch((err) => {
notAdded.push(err);
});
})).then((t) => {
const r1 = { content: `ITEMS NOT ADDED:\n${notAdded.join("\n")}`, ephemeral: true }
if (r1.content != "ITEMS NOT ADDED:\n") {
interaction.reply(r1).catch((err) => { interaction.channel.send(r1); });
} else {
const r2 = `All ${arr.length} items added to calendar!`;
interaction.reply(r2).catch((err) => { interaction.channel.send(r2); });
}
});
}).catch((err) => {
interaction.reply(err);
})
}).catch((err) => {
console.log(err);
});
}
} catch (err) {
console.log(err);
interaction.reply({ content: "Uh oh, there's been an error!", ephemeral: true});
}
});
}
//#endregion
module.exports = {
name: 'import_ics',
description: 'Import events using a calendar',
async execute(interaction, Discord, Client, bot) {
//Check if the user has premium
await verPremium(bot, interaction.user.id).then(() => {
const url = interaction.options.data[0].attachment.attachment;
if (!url.endsWith(".ics")) {
return interaction.reply("Please use a valid ***.ics*** file!")
}
let uid, gid;
if (interaction.channel.type == 'DM') {
uid = interaction.user.id;
} else {
gid = interaction.guildId;
}
readFileAndParse(url, bot, interaction, gid, uid);
}).catch(() => {
interaction.reply("You have to be a premium subscriber to use this feature!");
});
},
options: [
{name: 'ics_file', description: 'The ics file to input', type: Constants.ApplicationCommandOptionTypes.ATTACHMENT, required: true},
],
isDm: true
}
+69 -49
View File
@@ -1,5 +1,4 @@
const { Modal, TextInputComponent, MessageActionRow, MessageButton, MessageEmbed, Interaction } = require('discord.js');
const { checkRole } = require('../admin/verify');
/**
@@ -51,9 +50,9 @@ function postEmbd(bot, desc, interaction, page, isGuild, id, refered) {
row.addComponents(prevbtn, nextbtn);
if (page > 0 || refered) {
interaction.update({ content: '_Note: To see a full list of reminder stats visit www.selmerbot.com _', embeds: [newEmbed], components: [row] });
interaction.update({ content: '_Note: To see a full list of reminder stats visit https://selmerbot.com _', embeds: [newEmbed], components: [row] });
} else {
interaction.reply({ content: '_Note: To see a full list of reminder stats visit www.selmerbot.com _', embeds: [newEmbed], components: [row] });
interaction.reply({ content: '_Note: To see a full list of reminder stats visit https://selmerbot.com _', embeds: [newEmbed], components: [row] });
}
} catch (err) {
console.log(err);
@@ -190,57 +189,78 @@ function addEvent(obj, connection, interaction, embd) {
}
}
/**
* @returns { Promise<[]> | Promise<[false, []] | [true, String]>} (all events) || (custom err) ? [true, err] : [false, err]
*/
function getEvents(bot, interaction, id, jpage = 0, isGuild = false, refered = false, isExport = false) {
return new Promise((resolve, reject) => {
var userId = false;
var guildId = false;
const numperpage = 5;
function getEvents(bot, interaction, id, jpage = 0, isGuild = false, refered = false) {
var userId = false;
var guildId = false;
const numperpage = 5;
if (isGuild) {
guildId = id;
} else {
userId = id;
}
if (isGuild) {
guildId = id;
} else {
userId = id;
}
bot.mongoconnection.then((client) => {
try {
var times;
const dbo = client.db('main').collection('reminderKeys');
bot.mongoconnection.then((client) => {
try {
var times;
const dbo = client.db('main').collection('reminderKeys');
//ReminderKeys are all stored as userId, the reminders themselves are not
dbo.findOne({$or: [ {userId: userId}, {userId: guildId} ]}).then((doc) => {
if (!doc) { return interaction.reply("No events exist!"); }
times = doc.times;
const tbo = client.db('main').collection('reminders');
tbo.find({time: {$in: times}}).toArray((err, docs) => {
//There's gotta be a better way
var temp = [""];
var page = 0;
for (let i = 0; i < docs.length; i ++) {
if (i != 0 && i % numperpage == 0) {
page ++;
temp[page] = '';
}
// temp += `__***Events On ${new Date(Number(docs[i].time))}***__\n\n`;
for (let j in docs[i]) {
if (!isNaN(j) && (docs[i][j].userId == userId || docs[i][j].guildId == guildId)) {
const obj = docs[i][j];
temp[page] += `Name: ${obj.name}\nDescription: ${obj.description}\nDate/Time: ${new Date(Number(docs[i].time))}\nOffset: ${obj.offset}\nLink: ${obj.link}\nLocation: ${obj.location}\n------------------------------\n`
}
//ReminderKeys are all stored as userId, the reminders themselves are not
dbo.findOne({$or: [ {userId: userId}, {userId: guildId} ]}).then((doc) => {
if (!doc) {
if (isExport) {
return reject([true, "No events exist!"]);
}
return interaction.reply("No events exist!");
}
//Create the embed
postEmbd(bot, temp, interaction, jpage, isGuild, id, refered);
times = doc.times;
const tbo = client.db('main').collection('reminders');
tbo.find({time: {$in: times}}).toArray((err, docs) => {
if (isExport) {
return resolve(docs);
}
//There's gotta be a better way
var temp = [""];
var page = 0;
for (let i = 0; i < docs.length; i ++) {
if (i != 0 && i % numperpage == 0) {
page ++;
temp[page] = '';
}
// temp += `__***Events On ${new Date(Number(docs[i].time))}***__\n\n`;
for (let j in docs[i]) {
if (!isNaN(j) && (docs[i][j].userId == userId || docs[i][j].guildId == guildId)) {
const obj = docs[i][j];
temp[page] += `Name: ${obj.name}\nDescription: ${obj.description}\nDate/Time: ${new Date(Number(docs[i].time))}\nOffset: ${obj.offset}\nLink: ${obj.link}\nLocation: ${obj.location}\n------------------------------\n`
}
}
}
//Create the embed
postEmbd(bot, temp, interaction, jpage, isGuild, id, refered);
resolve(true);
});
});
});
} catch (err) {
console.log(err);
return interaction.reply("Uh Oh! There's been an error!");
}
});
} catch (err) {
console.log(err);
if (isExport) {
return reject([false, err]);
}
return interaction.reply("Uh Oh! There's been an error!");
}
});
});
}
//#endregion
@@ -404,12 +424,12 @@ module.exports = {
);
}
return interaction.reply({ content: 'Please select an action\n_Notes: Adding offset to an event is only supported on the website and personal reminders can be viewed in DM\'s_', components: [row] });
return interaction.reply({ content: 'Please select an action\n_Notes: Adding offset to an event is only supported on the website and personal reminders can be viewed in DM\'s_', components: [row], ephemeral: true });
} else {
interaction.reply("You have to be a premium subscriber to use this feature!\n_support coming soon_");
}
});
});
}, modalHandle, turnPage,
}, modalHandle, turnPage, addEvent, getEvents,
options: []
}
+9 -7
View File
@@ -180,11 +180,13 @@ function handleInp(bot, interaction) {
module.exports = {
name: 'premium',
description: 'everything payment',
execute(interaction, Discord, Client, bot) {
handleInp(bot, interaction);
}, handleInp, createSubscriptionManual,
options: [{name: 'input', description: 'What do you want to do?', type: Constants.ApplicationCommandOptionTypes.STRING, required: true, choices: [{name: 'help', value: 'help'}, {name: 'buy', value: 'buy'}, {name: 'manage', value: 'manage'}]}],
isDM: true
name: 'premium',
description: 'everything payment',
execute(interaction, Discord, Client, bot) {
handleInp(bot, interaction);
}, handleInp, createSubscriptionManual,
options: [
{name: 'option', description: 'What do you want to do?', type: Constants.ApplicationCommandOptionTypes.STRING, required: true, choices: [{name: 'help', value: 'help'}, {name: 'buy', value: 'buy'}, {name: 'manage', value: 'manage'}]}
],
isDm: true
}
+1 -1
View File
@@ -34,7 +34,7 @@
<body>
<div class="row">
<div style="text-align: center;">
<button onclick="window.open('https://www.selmerbot.com/', target='_blank')" class="webBtn">selmerbot.com</button>
<button onclick="window.open('https://selmerbot.com/', target='_blank')" class="webBtn">selmerbot.com</button>
<script type="text/javascript" src="https://cdnjs.buymeacoffee.com/1.0.0/button.prod.min.js" data-name="bmc-button" data-slug="ion606" data-color="#FFDD00" data-emoji="" data-font="Cookie" data-text="Buy me a coffee" data-outline-color="#000000" data-font-color="#000000" data-coffee-color="#ffffff"></script>
</div>
</div>
+137 -57
View File
@@ -1,7 +1,7 @@
//#region imports
const { Client, Intents } = require('discord.js');
const Discord = require('discord.js');
const { MongoClient, ServerApiVersion } = require('mongodb');
const { MongoClient, ServerApiVersion, GridFSBucket } = require('mongodb');
const fs = require('fs');
// const OpenAI = require('openai-api')
const { Configuration, OpenAIApi } = require("openai");
@@ -9,13 +9,15 @@ const Stripe = require('stripe');
const turnManager = require('./commands/turnManager.js');
const { welcome } = require('./commands/admin/welcome.js');
const { handle_interaction } = require('./commands/interactionhandler.js');
const { handle_interaction, handleContext } = require('./commands/interactionhandler.js');
const { handle_dm } = require('./commands/dm_handler');
const { devCheck } = require('./commands/dev only/devcheck.js');
const { moderation_handler } = require('./commands/admin/moderation.js');
const { registerCommands } = require('./registerCommands.js');
const { backupLists, loadBotBackups } = require('./commands/admin/backupBot.js');
const { backupLists, loadBotBackups } = require('./commands/dev only/backupBot.js');
const { setPresence } = require('./commands/dev only/setPresence.js');
const { exit } = require('process');
const {textToLevels} = require('./commands/Selmer Specific/msgLevels.js');
//#endregion
const BASE_LVL_XP = 20;
@@ -50,7 +52,7 @@ if (process.env.token != undefined) {
StripeAPIKey = require('./config.json').StripeAPIKey;
youtubeAPIKey = require('./config.json').youtubeAPIKey;
IDM = true;
IDM = token.startsWith("OTI2NT");
}
//#endregion
@@ -105,6 +107,11 @@ bot.mongouri = mongouri;
const client = new MongoClient(mongouri, { useNewUrlParser: true, useUnifiedTopology: true, serverApi: ServerApiVersion.v1 });
bot.mongoconnection = client.connect();
//Error stuff
var preverr = "";
var errmsg;
var errTimes = 1;
//#endregion MongoDB Integration end
@@ -114,12 +121,45 @@ process.on("SIGTERM", (signal) => {
console.log(`Process ${process.pid} received a SIGTERM signal`);
backupLists(bot, IDM);
// process.exit(0);
bot.user.setStatus('invisible');
});
process.on("SIGINT", (signal) => {
console.log(`Process ${process.pid} has been interrupted`);
backupLists(bot, IDM);
// process.exit(0);
bot.user.setStatus('invisible');
});
process.on('uncaughtException', (signal) => {
//Check if this was the last err and if so, ignore
if (preverr == signal.stack.toString()) {
var tempmsg = errmsg.content;
tempmsg.replaceAll(`{${errTimes}}`, `{${errTimes + 1}}`);
errTimes++;
errmsg.edit(tempmsg);
return;
}
console.log(signal);
if (bot.inDebugMode) { return; }
const guild = bot.guilds.cache.get(bot.home_server);
const owner = guild.members.cache.get(guild.ownerId);
preverr = signal.stack.toString();
owner.send(`${owner} SELMER BOT IS DOWN!!!`).then(() => {
guild.channels.cache.get("1054550753982828624").send(`<@&944048889038774302> Selmer Bot is down!\n***ERROR STACK:***\n`).then(() => {
guild.channels.cache.get("1054550753982828624").send(`\`\`\`${preverr}\`\`\`\nTHIS ERROR HAS OCCURED {1} TIMES IN A ROW`).then((msg) => {
errmsg = msg;
preverr = signal.stack.toString();
// exit(12);
bot.user.setStatus('dnd');
});
});
});
});
//#endregion
@@ -150,6 +190,7 @@ fs.readdirSync('./commands')
//Set these two manually because all the seperate games can't be included in the command list (all managed by the 'game' file)
let temp_command = require("./commands/db/econ.js");
const { STATE } = require('./commands/db/econ.js');
const replies = require('./commands/Selmer Specific/replies.js');
bot.commands.set('econ', temp_command);
temp_command = require('./commands/games/game.js');
bot.commands.set('game', temp_command);
@@ -165,70 +206,96 @@ bot.commands.set('game', temp_command);
//#endregion
//#region bot.[anything] section
//XP Table section
let xp_collection = new Map();
let items;
var botIsReady = bot.inDebugMode;
bot.on('ready', async () => {
registerCommands(bot);
const startTime = new Date().getTime();
bot.user.setPresence({ activities: [{ name: 'Booting up, please hold!', type: "PLAYING" }], status: 'idle' });
//Make then copy the shop
bot.mongoconnection.then(client => {
const shop = client.db("main").collection("shop");
shop.find().toArray(function(err, itemstemp) {
if (err) throw err;
registerCommands(bot).then(() => {
//Make then copy the shop
bot.mongoconnection.then(client => {
const shop = client.db("main").collection("shop");
shop.find().toArray(function(err, itemstemp) {
if (err) throw err;
items = [...itemstemp];
items = [...itemstemp];
});
});
//Srt status and Activity (idle and listening to !help)
bot.user.setActivity(`${bot.prefix}help`, { type: "LISTENING" });
// bot.user.setStatus('idle');
//Note the xp numbers are a little wonky on levels 6, 8 and 13 (why though?)
//See https://stackoverflow.com/questions/72212928/why-are-the-differences-between-my-numbers-inconsistent-sort-of-compund-interes
for (let i = 1; i < 101; i ++) {
// xp_collection.set(i, BASE_LVL_XP * .1);
let amount = BASE_LVL_XP * (Math.ceil(Math.pow((1.1), (2 * i))) + i);
xp_collection.set(i+1, amount);
}
bot.user.setPresence({ activities: [{ name: '/help', type: 'PLAYING' }], status: 'online' });
if (!bot.inDebugMode) {
console.log('SLEEMER BOT ONLINE!!!!! OH MY GOD OH MY GOD!!!');
} else {
console.log("Testing testing 1 2 5...");
}
//Add the money symbol
let srv = bot.guilds.cache.get(bot.home_server).emojis.cache;
emj = srv.find((g) => { return g.name == 'selmer_coin' });
bot.currencysymbolmmain = `${emj}`;
}).catch((err) => {
console.log(err);
}).finally(() => {
botIsReady = true;
console.log(`Setting up Slash Commands took ${(new Date().getTime() - startTime) / 1000} seconds to complete!`);
});
//Note the xp numbers are a little wonky on levels 6, 8 and 13 (why though?)
//See https://stackoverflow.com/questions/72212928/why-are-the-differences-between-my-numbers-inconsistent-sort-of-compund-interes
for (let i = 1; i < 101; i ++) {
// xp_collection.set(i, BASE_LVL_XP * .1);
let amount = BASE_LVL_XP * (Math.ceil(Math.pow((1.1), (2 * i))) + i);
xp_collection.set(i+1, amount);
}
//Reaction map area
if (!bot.inDebugMode) {
console.log('SLEEMER BOT ONLINE!!!!! OH MY GOD OH MY GOD!!!');
} else {
console.log("Testing testing 1 2 5...");
}
//Add the money symbol
let srv = bot.guilds.cache.get(bot.home_server).emojis.cache;
emj = srv.find((g) => { return g.name == 'selmer_coin' });
bot.currencysymbolmmain = `${emj}`;
});
//Button Section
bot.on('interactionCreate', async interaction => {
const { commandName } = interaction;
bot.lockedChannels.set(interaction.guildId, ["NUMBERS HERE"]);
if (!botIsReady) {
return interaction.reply("The bot is still warming up. This is process can take up to 5 minutes. Please try again in a bit! \:(");
}
// console.log(bot.lockedChannels);
//Slash commands
if (interaction.isApplicationCommand()) {
if (interaction.isUserContextMenu()) {
return handleContext(bot, interaction.options.data[0]);
}
const logable = ['kick', 'ban', 'unban', 'mute', 'unmute', 'timeout'];
const econList = ["buy", 'shop', 'work', 'rank', 'inventory', 'balance', 'sell'];
const adminList = ["setpresence", "setactivity"];
if (logable.includes(commandName)) {
if (commandName == "admin" && adminList.includes(interaction.options.data[0].name)) {
if (interaction.user.id == bot.guilds.cache.get(bot.home_server).ownerId) {
setPresence(bot, interaction);
} else {
return interaction.reply({ content: "HAHAHAHAHAHAHAHAHAHAHA\n\nno.", ephemeral: true }).catch((err) => {
interaction.channel.send({ content: "HAHAHAHAHAHAHAHAHAHAHA\n\nno.", ephemeral: true });
});
}
} else if (logable.includes(commandName)) {
moderation_handler(bot, interaction, commandName);
} else if (econList.includes(commandName)) {
bot.commands.get('econ').execute(bot, interaction, Discord, mongouri, items, xp_collection);
}
else if (bot.commands.has(commandName)) {
} else if (commandName == 'game') {
if (!bot.inDebugMode) { interaction.reply("This command is still in development, use normal text\nEx: _!game tictactoe @opponent_"); }
const command = interaction.options.data[0];
bot.commands.get('game').execute(bot, interaction, command, Discord, mongouri, items, xp_collection);
} else if (commandName == 'setup_embed') {
const {generateMsg} = require('./commands/admin/easySetup.js')
generateMsg(bot, interaction);
} else if (bot.commands.has(commandName)) {
bot.commands.get(commandName).execute(interaction, Discord, Client, bot);
} else {
interaction.reply("Unknown command detected!");
@@ -253,16 +320,17 @@ bot.on("guildCreate", guild => {
//const role = guild.roles.cache.find((role) => role.name === 'Selmer Bot Mod'); // member.roles.cache.has('role-id-here');
const server = bot.guilds.cache.get(guild.id);
const owner = server.members.fetch(guild.ownerId).then(function(owner) {
owner.send('Thank you for adding Selmer Bot to your server!\nPlease give people you want to have access to Selmer Bot\'s restricted commands the "_Selmer Bot Commands_" role and people you want to access set the calendar the "Selmer Bot Calendar" role');
owner.send('To help set up Selmer Bot to work better with your server, use _!setup help_ in a channel Selmer Bot is in!');
server.members.fetch(guild.ownerId).then(function(owner) {
owner.send('Thank you for adding Selmer Bot to your server!\nPlease give people you want to have access to Selmer Bot\'s restricted commands the "_Selmer Bot Commands_" role and people you want to access set the calendar the "_Selmer Bot Calendar_" role');
owner.send('To help set up Selmer Bot to work better with your server, use _/setup help_ in a channel Selmer Bot is in!');
});
//Set up the server
bot.mongoconnection.then(client => {
const dbo = client.db(guild.id).collection('SETUP');
dbo.insertMany([{_id: 'WELCOME', 'welcomechannel': null, 'welcomemessage': null, 'welcomebanner': null}, {_id: 'LOG', 'keepLogs': false, 'logchannel': null, 'severity': 0}, {_id: 'announcement', channel: null, role: null}]);
dbo.insertMany([{_id: 'WELCOME', 'welcomechannel': null, 'welcomemessage': null, 'welcomebanner': null, 'col': "#FFFFFF"}, {_id: 'LOG', 'keepLogs': false, 'logchannel': null, 'severity': 0},
{_id: 'announcement', channel: null, role: null}, {_id: 'roles', commands: ["Selmer Bot Commands"], announcements: "Selmer Bot Calendar"}]);
});
});
@@ -322,29 +390,29 @@ bot.on('guildMemberAdd', async (member) => {
if (member.guild.id == bot.home_server && !bot.inDebugMode) { return; }
//Check for impartial data
if(member.partial) await member.fetch();
if (member.partial) { member = await member.fetch(); }
const guild = bot.guilds.cache.get(member.guild.id);
bot.mongoconnection.then(client => {
const dbo = client.db(member.guild.id).collection('SETUP');
dbo.find({_id: 'WELCOME'}).toArray(async (err, docs) => {
if (!docs) { return; }
dbo.findOne({_id: 'WELCOME'}).then(async (doc) => {
if (!doc) { return; }
var welcomechannel;
if (docs[0].welcomechannel == null) {
if (doc.welcomechannel == null) {
welcomechannel = guild.channels.cache.find(channel => channel.name.toLowerCase() === 'welcome');
} else {
welcomechannel = guild.channels.cache.get(docs[0].welcomechannel)
welcomechannel = guild.channels.cache.get(doc.welcomechannel)
}
if (welcomechannel == null) {
return console.log('No welcome channel detected');
return; // console.log('No welcome channel detected');
}
await welcome(member, welcomechannel, docs[0].welcomemessage);
})
await welcome(member, welcomechannel, doc.welcomemessage, doc.welcomebanner, (doc.welcometextcolor) ? doc.welcometextcolor : "#FFFFFF");
});
})
});
@@ -366,11 +434,18 @@ bot.on('messageCreate', (message) => {
//Special case, testing server (still need the emojis and error logging)
if (!bot.inDebugMode && message.guild.id == bot.home_server) { return; }
if (message.mentions.has(bot.user.id)) {
if (message.content == `<@${bot.user.id}>`) {
return message.reply("What?");
} else {
return replies(bot, message);
}
}
//Check if the prefix exists
if (!message.content.startsWith(prefix) || message.author.bot) {
//Use for the leveling-by-interaction system
return;
} else {
if (message.author.bot) { return; }
if (message.content.startsWith(prefix)) {
//Game section (too complicated to move to Slash Commands)
//Note: Slash commands do not register as valid replies
const args = message.content.slice(prefix.length).split(' ');
@@ -380,7 +455,12 @@ bot.on('messageCreate', (message) => {
} else if (command == 'rss' && bot.inDebugMode) {
const rss = require('./side projects/RSSHandlers/rssFeed.js');
rss.execute(message, args, Discord, client, bot);
} else {
textToLevels(bot, message, xp_collection);
}
} else {
//Use for the leveling-by-interaction system
textToLevels(bot, message, xp_collection);
}
});
+1196 -631
View File
File diff suppressed because it is too large Load Diff
+7 -4
View File
@@ -4,15 +4,14 @@
"@discordjs/opus": "github:discordjs/opus",
"@discordjs/rest": "^1.1.0",
"@discordjs/voice": "^0.8.0",
"@napi-rs/canvas": "^0.1.22",
"apt": "^0.0.2",
"arraybuffer-to-buffer": "^0.0.7",
"axios": "^0.27.2",
"body-parser": "^1.20.0",
"canvas": "^2.9.3",
"cheerio": "^1.0.0-rc.10",
"coingecko-api": "^1.0.10",
"cors": "^2.8.5",
"cozy-ical": "^1.1.22",
"date-fns": "^2.29.2",
"date-fns-timezone": "^0.1.4",
"discord-reply": "^0.1.2",
@@ -29,16 +28,17 @@
"mal-scraper": "^2.11.4",
"mongoose": "^6.3.2",
"mysql": "^2.18.1",
"node-fetch": "^2.6.7",
"node.js": "^0.0.1-security",
"npm": "^8.13.2",
"openai": "^3.0.0",
"play-dl": "^1.9.4",
"pusher": "^5.1.1-beta",
"random-memes": "^3.1.0",
"request": "^2.88.2",
"robinhood": "^1.8.0",
"rss-parser": "^3.12.0",
"sequelize": "^6.19.0",
"sharp": "^0.31.1",
"sqlite3": "^5.0.3",
"stripe": "^9.11.0",
"sudo": "^1.0.3",
@@ -63,5 +63,8 @@
"bugs": {
"url": "https://github.com/ION606/selmerBot/issues"
},
"homepage": "https://github.com/ION606/selmerBot#readme"
"homepage": "https://github.com/ION606/selmerBot#readme",
"devDependencies": {
"@types/node-fetch": "^2.6.2"
}
}
+129 -62
View File
@@ -5,77 +5,144 @@ const {Client, Constants} = require('discord.js');
* @param {Client} bot
*/
function registerCommands(bot) {
const commands = bot.application.commands;
return new Promise((resolve, reject) => {
const commands = bot.application.commands;
/*
val: {
name: 'code',
description: "See where Selmer bot's code is stored! (you can also use _!repo_)",
execute: [Function: execute]
},
/*
val: {
name: 'code',
description: "See where Selmer bot's code is stored! (you can also use _!repo_)",
execute: [Function: execute]
},
key: code
*/
bot.commands.forEach((val, key) => {
key: code
*/
if (val.options && val.name != 'econ') {
if (val.isDM) {
commands.create({
name: val.name,
description: val.description,
options: val.options,
dm_permission: true,
});
//#region Slash Commands
bot.commands.forEach((val, key) => {
if ((val.options && val.name != 'econ') || val.isDm) {
if (val.isDm) {
commands.create({
name: val.name,
description: val.description,
options: val.options,
dm_permission: true,
});
} else {
commands.create({
name: val.name,
description: val.description,
options: val.options,
dm_permission: false,
});
}
} else {
commands.create({
name: val.name,
description: val.description,
options: val.options,
dm_permission: false,
});
// console.log(val, key);
console.log(key);
}
} else {
// console.log(val, key);
console.log(key);
});
//Create the "econ" commands
const econList = ["buy", 'shop', 'work', 'rank', 'inventory', 'balance', 'sell'];
const econMain = require('./commands/db/econSlashOptions.js');
econList.forEach((commandName) => {
const command = econMain[`${commandName}`];
commands.create({
name: commandName,
description: command.description,
options: command.options || [],
dm_permission: false,
});
});
//Create the moderation commands
//NOTE: The user needs to have kicking or banning permissions to use these
const modList = ['lock', 'unlock', 'kick', 'ban', 'unban', 'mute', 'unmute'];
for (let i = 0; i < modList.length; i++) {
const opts = [
{name: "user", description: `The user to ${modList[i]}`, type: Constants.ApplicationCommandOptionTypes.USER, required: true},
{name: "reason", description: "Why?", type: Constants.ApplicationCommandOptionTypes.STRING, required: false}
];
commands.create({
name: modList[i],
description: `${modList[i]} a user`,
options: opts,
dm_permission: false,
default_member_permissions: 6,
});
// .then((comm) => {
// comm.setDefaultMemberPermissions(Discord.PermissionFlagsBits.KickMembers | Discord.PermissionFlagsBits.BanMembers);
// });
}
});
//Create the "econ" commands
const econList = ["buy", 'shop', 'work', 'rank', 'inventory', 'balance', 'sell'];
const econMain = require('./commands/db/econSlashOptions.js');
econList.forEach((commandName) => {
const command = econMain[`${commandName}`];
commands.create({
name: commandName,
description: command.description,
options: command.options || [],
dm_permission: false,
});
});
//Create the moderation commands
//NOTE: The user needs to have kicking or banning permissions to use these
const modList = ['lock', 'unlock', 'kick', 'ban', 'unban', 'mute', 'unmute'];
for (let i = 0; i < modList.length; i++) {
const opts = [
{name: "user", description: `The user to ${modList[i]}`, type: Constants.ApplicationCommandOptionTypes.USER, required: true},
{name: "reason", description: "Why?", type: Constants.ApplicationCommandOptionTypes.STRING, required: false}
];
commands.create({
name: modList[i],
description: `${modList[i]} a user`,
options: opts,
dm_permission: false,
default_member_permissions: 6,
//Admin commands (Home Server only)
const guild = bot.guilds.cache.get(bot.home_server);
guild.commands.create({
name: "admin",
description: "admin commands",
// type: Constants.ApplicationCommandOptionTypes.SUB_COMMAND_GROUP,
options: [
{
name: "setpresence",
description: "Change the bot's presence",
type: Constants.ApplicationCommandOptionTypes.SUB_COMMAND,
options: [
{name: "pres_text", description: "The new presence text", type: Constants.ApplicationCommandOptionTypes.STRING, required: true },
{name: "type", description: "The new presence text", type: Constants.ApplicationCommandOptionTypes.STRING, required: true, choices: [
{name: "LISTENING", value: "LISTENING"}, {name: "WATCHING", value: "WATCHING"}, {name: "COMPETING", value: "COMPETING"}, {name: "PLAYING", value: "PLAYING"}, { name: "STREAMING", value: "STREAMING"}
]},
{name: 'display_name', description: "What to display instead of the stream's title", type: Constants.ApplicationCommandOptionTypes.STRING, required: false}
],
dm_permission: false
},
{
name: "setactivity",
description: "Change the bot's activity",
type: Constants.ApplicationCommandOptionTypes.SUB_COMMAND,
options: [
{name: "type", description: "The new presence text", type: Constants.ApplicationCommandOptionTypes.STRING, required: true, choices: [
{name: "Do Not Disturb", value: "dnd"}, {name: "Idle", value: "idle"}, {name: "invisible", value: "invisible"}, {name: "online", value: "online"}
]},
],
dm_permission: false
},
]
});
// .then((comm) => {
// comm.setDefaultMemberPermissions(Discord.PermissionFlagsBits.KickMembers | Discord.PermissionFlagsBits.BanMembers);
// });
}
//Takes much longer, so it'll be the benchmark for when the Promise resolves
//#region GAMES
const gameOpts = require('./commands/games/gameCommandOptions.js');
commands.create({
name: 'game',
description: 'Play one of Selmer Bot\'s games!', //NOT APPLICABLE USING SUB COMMAND GROUPS???
// type: Constants.ApplicationCommandOptionTypes.SUB_COMMAND_GROUP,
options: gameOpts,
dm_permission: false
}).then(() => {
if (!bot.inDebugMode) { return resolve(true); }
commands.create({
name: 'setup_embed',
description: 'Create a row of buttons for easier setup',
options: []
});
//#region Context Menus
commands.create({
name: "Temp",
type: 'USER'
}).then(() => { resolve(true); });
//#endregion
}).catch((err) => { reject(err); });
//#endregion
//#endregion
});
}