51 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
ION606 e1002d748d Transitioned all Misc, audio, Selmer Specific, admin, anime/manga, and inventory commands to Slash Command format. The RSS and reactionrole commands are still broken and all game commands are still in message format due to compications 2022-09-27 16:45:50 -04:00
ION606 a190a250a6 Merge branch 'main' of https://github.com/ION606/selmerBot 2022-09-16 10:53:08 -04:00
ION606 fcd96c930a Removed some node.js vulnerabilities (last commit before slash command transition) 2022-09-16 10:52:47 -04:00
ION606 52d1119842 Update README.md 2022-09-15 10:05:25 -04:00
ION606 08a49e6149 Fixed the 'setup announcement_channel command' 2022-09-13 12:00:28 -04:00
ION606 d3f2085813 Fixed the 'setup announcement_role' command and added the 'twitchnotifs.js' file 2022-09-13 10:57:53 -04:00
ION606 27fecfffcb Added the 'poll' and 'code/repo' commands, as well as added single/multiplayer functionality to 'Minesweeper' 2022-09-06 15:29:48 -04:00
ION606 af8f9f69ae Fixed an issue with the 'guildDelete' event 2022-09-03 20:27:09 -04:00
ION606 07f41f5ece Added the redirect HTML file and the 'game minesweeper' command 2022-09-02 15:27:54 -04:00
ION606 1cd968b9e2 Updated several npm packages to fix some vulnerabilities 2022-09-01 09:01:30 -04:00
ION606 3f2e9c6c53 Added the 'crypto' command 2022-08-18 16:29:47 -07:00
ION606 62ea3ecec8 Added the 'stocks' command 2022-08-18 14:25:25 -07:00
ION606 9de7eecdd9 Added the 'tuto' command and fixed some bugs 2022-08-17 20:21:19 -07:00
ION606 b7d51c2d14 Added full support for server reminders and added the ability to view reminders in Discord 2022-08-17 12:09:40 -07:00
ION606 d8d4bbea56 Removed some useless code 2022-07-31 10:17:12 +03:00
ION606 a596d18eeb Merge branch 'main' of https://github.com/ION606/selmerBot 2022-07-31 10:12:32 +03:00
ION606 14dd4e002a Removed the excessive connecting and closing of the MongoDb databases 2022-07-31 10:11:55 +03:00
ION606 6d8aa991b6 Fixed an issue with the moderation and logging 2022-07-24 15:25:51 +03:00
ION606 b55e669df2 Created the LISCENCE.md
I modified it and it didn't display on Github so...
2022-07-23 02:09:23 -04:00
ION606 23f91591cd Delete LICENSE.md 2022-07-23 02:05:39 -04:00
ION606 fb87d33bd9 Added the circle outline PNG 2022-07-21 14:22:31 +03:00
ION606 0b13f6db29 Added error reporting functionality, fixed bugs in the various game's code and tidied up a bit 2022-07-19 15:41:49 +03:00
ION606 4e1214a312 Added the 'trivia' commands 2022-07-18 20:44:38 +03:00
ION606 03d0b136e1 Update notes.txt 2022-07-17 03:49:39 -04:00
ION606 34bbd823ab Mostly cleaning and added the JSON to Map to Print function 2022-07-16 19:26:06 +03:00
ION606 b9c4caa608 Fixed a bug with the audio player and added the 'youtubeNotifs file 2022-07-12 21:15:51 +03:00
ION606 5a17e3811f Added more moderation commands and upgraded the 'audio' command 2022-07-12 20:10:35 +03:00
ION606 6ceb8cb381 Added button integration to the audio command and removed the YouTube.data file 2022-07-10 21:22:07 +03:00
ION606 373d57de2e Added moderation commands 2022-07-09 20:30:31 +03:00
ION606 919a7f62dc Added the ability to collect spam with a ommand, cleaned up the code and modified the LISCENCE.md file 2022-07-08 17:09:36 +03:00
77 changed files with 10190 additions and 2323 deletions
-12
View File
@@ -1,12 +0,0 @@
{
"cookie": {
"GPS": "1",
"Domain": ".youtube.com",
"Expires": "Thu, 27-Oct-2022 17:51:25 GMT",
"Path": "/",
"YSC": "AlW2Krsd2Ek",
"SameSite": "none",
"VISITOR_INFO1_LIVE": "_eP8T2zceRQ"
},
"file": true
}
+5 -1
View File
@@ -1,8 +1,12 @@
node_modules
config.json
backup.json
*.env
*.sqlite
*.txt
!spec/LevelsXP.txt
database.sqlite
*.sqlite
temp.js
easySetup.js
meme.js
+9 -2
View File
@@ -1,6 +1,13 @@
# THIS REPOSITORY HAS BEEN MIRRORED TO THE SELMER BOT ORGANIZATION AND IS NOW READ-ONLY
# selmerBot
Authors: ION606, MajorDrools
Description:
This is a bot modeled after Selmer Bringsjord of RPI and all the wonderful things he says
### Node Packages Installation Instructions
Download the _package.json_ file from the repo
`npm install`
### Run Selmer Bot Locally
Set the entry point to `main.js`, then run using `node .`

Before

Width:  |  Height:  |  Size: 862 KiB

After

Width:  |  Height:  |  Size: 862 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

+7 -4
View File
@@ -1,7 +1,7 @@
module.exports = {
name: 'extracredit',
description: "Selmer Bot Dm's you for some *AHEM* extra credit",
execute(message, args, Discord, Client, bot) {
execute(interaction, Discord, Client, bot) {
let dm;
let num = Math.floor(Math.random() * 10);
@@ -41,7 +41,10 @@ module.exports = {
break;
}
let ID = message.member.id;
message.client.users.fetch(ID).then(user => user.send(dm));
}
// let ID = interaction.user.id;
// message.client.users.fetch(ID).then(user => user.send(dm));
interaction.user.send(dm);
interaction.reply({content: "Extra Credit sent 😉", ephemeral: true});
},
options: []
}
+9 -18
View File
@@ -1,24 +1,15 @@
module.exports = {
name: 'arrow',
description: 'Engage in a trademarked activity and throw an arrow at a trash can',
async execute(message, args, Discord, Client, bot) {
let counter = 0;
async execute(interaction, Discord, Client, bot) {
arrow = '>';
while (true) {
arrow = '-' + arrow;
message.channel.send(arrow);
await message.channel.messages.fetch({limit: 1}).then(messages => {
message.channel.bulkDelete(messages);
});
counter ++;
if (counter >= 5) {
message.channel.messages.fetch({limit: 1}).then(messages => {
message.channel.bulkDelete(messages);
});
arrow = arrow + '🗑️';
message.channel.send(arrow);
break;
}
await interaction.reply(arrow);
for (let i = 0; i < 5; i++) {
arrow = '-' + arrow
await interaction.editReply(arrow);
}
}
arrow = arrow + '🗑️';
await interaction.editReply(arrow);
}, options: []
}
+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};
+4 -4
View File
@@ -1,19 +1,19 @@
module.exports = {
name: "profile",
description: "Posts a description of Monsieur Sleemer himself",
execute(message, args, Discord, Client, bot) {
execute(interaction, Discord, Client, bot) {
const newEmbed = new Discord.MessageEmbed()
.setColor('#002eff')
.setTitle('My professional resume')
//.setURL('https://discordjs.guide/popular-topics/embeds.html#embed-preview')
//.setDescription('My professional resume')
.setImage('https://github.com/ION606/selmerBot/blob/main/Sleemer_Bringsjorgend.png?raw=true')
.setImage('https://github.com/ION606/selmerBot/blob/main/assets/Sleemer_Bringsjorgend.png?raw=true')
.addFields(
{name: 'My Epithets:', value: "~~Pearls of Wisdom~~"},
{name: '\t__Epithet 1__', value: "_Negative money is the best money_"},
{name: '\t__Epithet 2__', value: "_There is no god, only logic_"}
);
message.channel.send({ embeds: [newEmbed] });
}
interaction.reply({ embeds: [newEmbed] });
}, options: []
}
+4 -3
View File
@@ -1,7 +1,7 @@
module.exports = {
name: 'quotes',
description: "A public version of Extra Credit",
execute(message, args, Discord, Client, bot) {
execute(interaction, Discord, Client, bot) {
let dm;
let num = Math.floor(Math.random() * 10);
@@ -41,6 +41,7 @@ module.exports = {
break;
}
message.channel.send(dm);
}
interaction.reply(dm);
},
options: []
}
+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;
+33
View File
@@ -0,0 +1,33 @@
const { MessageEmbed, MessageActionRow, MessageButton, Interaction } = require('discord.js');
module.exports = {
name: 'repo',
description: 'See where Selmer bot\'s code is stored!',
execute(interaction, Discord, Client, bot) {
const embd = new MessageEmbed()
.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");
const row = new MessageActionRow()
.addComponents([
new MessageButton()
.setStyle("LINK")
.setURL("https://github.com/ION606/selmerBot")
.setLabel("Github Repo"),
new MessageButton()
.setStyle("LINK")
.setURL("https://selmerbot.com/")
.setLabel("Website"),
new MessageButton()
.setStyle("PRIMARY")
.setLabel("Tutorial")
.setCustomId("sbtutorial")
]);
interaction.reply({ embeds: [embd], components: [row] });
}, options: [],
isDm: true
}
+83
View File
@@ -0,0 +1,83 @@
// @ts-check
const { MessageActionRow, MessageButton, MessageEmbed, Interaction } = require('discord.js');
//Intro, setup/logging, Econ, Moderation, anime/manga, games, Selmer Specific, Misc, DMS/Premium
const tutoText = [
"__**Hello, and welcome to the Selmer Bot tutorial**__\nIn this tutorial, I will walk you through the various commands and features of Selmer Bot\n\nTo progress to the next page, click the right arrow at the bottom of this message.\nTo go back to the previous page, click the left arrow",
"__**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\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 (_ 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"
];
//If the page number == 0 and refered == false, then interaction will be a Message
function postEmbd(bot, interaction, page, refered) {
const author = {
name: "Selmer Bot",
url: "",
iconURL: bot.user.displayAvatarURL()
};
//Tutorial Embed
const te = new MessageEmbed();
te.setAuthor(author)
.setTitle("Selmer Bot Tutorial")
.setDescription(tutoText[page])
.setURL('https://selmerbot.com/')
.setFooter({ text: `Page ${page + 1}` });
if (tutoText[page].indexOf('Thank you for completing the Selmer Bot Tutorial') != -1) {
te.setImage('https://github.com/ION606/selmerBot/blob/main/assets/Sleemer_Bringsjorgend.png?raw=true');
}
const row = new MessageActionRow();
//Make sure the page is never < 1
const prevbtn = new MessageButton()
.setCustomId(`tutoQueue|`)
.setLabel('⬅️')
.setStyle('SECONDARY');
if (page <= 0) {
prevbtn.customId += `0`;
prevbtn.setDisabled(true);
} else {
prevbtn.customId += `${page - 1}`;
}
const nextbtn = new MessageButton()
.setCustomId(`tutoQueue|`)
.setLabel('➡️')
.setStyle('SECONDARY');
if ((page + 1) >= tutoText.length) {
nextbtn.customId += `${tutoText.length}`;
nextbtn.setDisabled(true);
} else {
nextbtn.customId += `${page + 1}`;
}
row.addComponents(prevbtn, nextbtn);
if (page > 0 || refered) {
interaction.update({ content: '_Note: To see a full list of reminder stats visit selmerbot.com _', embeds: [te], components: [row] });
} else {
interaction.channel.send({ content: '_Note: To see a full list of reminder stats visit selmerbot.com _', embeds: [te], components: [row] });
}
}
module.exports = {
name: 'tuto',
description: 'An introduction command to Selmer Bot',
async execute(interaction, Discord, Client, bot) {
postEmbd(bot, interaction, 0, false);
}, postEmbd,
options: []
}
+33
View File
@@ -0,0 +1,33 @@
const { checkRole } = require('./verify.js');
const { Constants } = require('discord.js');
module.exports = {
name: 'lock',
description: 'Lock 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 interaction.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: false,
READ_MESSAGE_HISTORY: true,
ATTACH_FILES: false
});
interaction.reply(`${channel} has been locked!`);
});
},
options: [{name: 'channel', description: 'The channel to lock (defaults to current channel)', type: Constants.ApplicationCommandOptionTypes.CHANNEL, required: false}]
}
+154
View File
@@ -0,0 +1,154 @@
//@ts-check
const { log, SEVCODES } = require('../log.js');
const { checkRole } = require('./verify.js');
const { Constants } = require('discord.js');
function modHelp() {
const l = ['lock', 'unlock', 'kick', 'ban', 'unban', 'mute', 'unmute'];
return l.join(", ");
}
function kick(guild, user) {
guild.members.kick(user);
}
async function toggle_ban(guild, interaction, args, ban, reason) {
if (ban) {
guild.members.ban(args);
} else {
var user = args[0];
let i = 0;
while (user.indexOf('#') == -1) {
user += args[i];
i++
}
return new Promise((resolve, reject) => {
interaction.guild.bans.fetch().then((users) => {
const userObj = users.filter((u) => {
return (`${u.user.username}#${u.user.discriminator}` == user);
}).first();
if (userObj && userObj.user) {
guild.members.unban(userObj.user.id, reason).then(() => {
resolve(userObj.user);
});
} else {
reject("This user is not in the server!");
}
})
});
// //Check if the user is banned or not
// message.guild.invites.fetch().then((invites) => {
// const u = guild.members.cache.get(id);
// u.send(`You have been unbanned from ${guild.name}, you can rejoin using the following link!\nhttps://discord.gg/${invites.first().code}`);
// });
}
}
function toggle_mute(bot, guild, command, interaction, user, reason, mute) {
const mutedRole = guild.roles.cache.find((role) => role.name.toLowerCase() === 'muted');
const guser = guild.members.cache.get(user.id);
// if there is no `Muted` role, send an error
if (!mutedRole) { return interaction.reply('There is no "muted" role on this server. Please create one then try again'); }
if (mute) {
if (guser.roles.cache.get(mutedRole.id) == undefined) {
guser.roles.add(mutedRole);
log(bot, interaction, command, user, reason, SEVCODES.low);
} else { interaction.reply("This user is already muted!"); }
} else {
if (guser.roles.cache.get(mutedRole.id) != undefined) {
guser.roles.remove(mutedRole);
log(bot, interaction, command, user, reason, SEVCODES.none);
} else { interaction.reply("This user is not muted!"); }
}
/*
NOTE: use the following function for a "time out" type thing?
setTimeout(() => {
target.roles.remove(mutedRole); // remove the role
}, <time>)
*/
}
function timeOut(bot, user, message, args, command, reason) {
let num = Number(args[1]);
if (!args[1] && !Number.isSafeInteger(num)) { return message.reply(`Please use the following format ${bot.repfix}timeout <user> <amount of time> [*hours* **or** *minutes (default)*]`)}
let ms = num * 60 * 1000;
let timeAsSt = '';
if (args[2] == 'hours') { ms *= 60; timeAsSt = `${args[1]} hours`; }
else { timeAsSt = `${args[2]} minutes`; }
user.timeout(ms, reason);
log(bot, message, command, user, reason, timeAsSt);
}
function moderation_handler(bot, interaction, command) {
const guild = interaction.guild;
//Verify
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 reasonInit = interaction.options.data.filter((arg) => { return (arg.name == 'reason'); })[0];
const reason = (reasonInit) ? reasonInit.value : "None";
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 (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;
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;
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 'timeout': timeOut(bot, mentioned, message, args, command, reason);
// shouldIlog = false;
// break;
default: console.log(`ERROR! Moderation Command "${command}" has somehow been used!`);
}
});
}
module.exports = {
name: 'moderation',
moderation_handler, modHelp,
}
+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}
]
}
+36
View File
@@ -0,0 +1,36 @@
module.exports = {
name: 'serverlock',
description: 'Lock ALL CHANNELS for everyone with the "everyone" role - SERVER OWNER ONLY!',
execute(interaction, Discord, Client, bot) {
if (interaction.guild.ownerId != interaction.user.id) { return interaction.reply('Insufficient Permissions!'); }
const role = interaction.guild.roles.cache.find(r => r.name === "@everyone");
const arr = [];
interaction.guild.channels.cache.forEach(channel => {
if (channel.permissionsFor(role).has("SEND_MESSAGES")) {
channel.permissionOverwrites.edit(role.id, {
VIEW_CHANNEL: true,
SEND_MESSAGES: false,
READ_MESSAGE_HISTORY: true,
ATTACH_FILES: false
});
//Maybe add the message to the array to be edited/deleted after unlock
if (channel.type == 'GUILD_TEXT') {
channel.send(`***CHANNEL LOCKED BY ${interaction.user}***`);
}
arr.push(channel.id);
}
});
bot.lockedChannels.set(interaction.guildId, arr);
interaction.reply(`***SERVER LOCKED BY ${interaction.user}***`);
}, options: []
}
// interaction.reply(```diff
// - SERVER LOCKED
// ```);
+32
View File
@@ -0,0 +1,32 @@
// const { checkRole } = require('./verify.js');
//Maybe add a list to selmer Bot that adds the channels when locking
module.exports = {
name: 'serverunlock',
description: 'unlocks the channels locked using /serverlock',
execute(interaction, Discord, Client, bot) {
const guild = bot.guilds.cache.get(interaction.guildId);
const role = interaction.guild.roles.cache.find(r => r.name === "@everyone");
// if (!checkRole(bot, guild, message.author.id)) { return message.reply('Insufficient Permissions!'); }
if (interaction.guild.ownerId != interaction.user.id) { return interaction.reply('Insufficient Permissions!'); }
const channelIds = bot.lockedChannels.get(interaction.guildId);
if (!channelIds) { return interaction.reply("No channels to unlock..."); }
channelIds.forEach(id => {
const channel = guild.channels.cache.get(id);
channel.permissionOverwrites.edit(role.id, {
VIEW_CHANNEL: true,
SEND_MESSAGES: true,
READ_MESSAGE_HISTORY: true,
ATTACH_FILES: true
});
});
bot.lockedChannels.set(interaction.guildId, []);
interaction.reply(`Channels unlocked by ${interaction.user}`);
},
options: []
}
+179 -45
View File
@@ -1,68 +1,202 @@
//@ts-check
const { MongoClient, ServerApiVersion } = require('mongodb');
const { Constants } = require('discord.js');
const { CreateNewCollection } = require("../db/econ");
const { checkRole } = require('./verify.js');
const fetch = require('node-fetch');
const help = require('../misc/help.js');
async function setWelcomeChannel(dbo, message, channelname) {
const channel = message.guild.channels.cache.find(ch => ch.name === channelname);
dbo.insertOne({welcomechannel: channel});
}
async function execute(interaction, Discord, Client, bot) {
const server = interaction.guildId;
const owner = interaction.guild.members.cache.get(interaction.guild.ownerId);
const args = interaction.options.data;
async function execute(bot, message, args, command, Discord, mongouri, items, xp_collection) {
const server = message.guild.id;
const owner = message.guild.members.cache.get(message.guild.ownerId);
if (message.author.id != message.guild.ownerId) {
return message.reply('Only the server owner can do this!')
if (interaction.user.id != interaction.guild.ownerId) {
return interaction.reply({content: 'Only the server owner can do this!', ephemeral: true});
}
// @ts-ignore
const client = new MongoClient(mongouri, { useNewUrlParser: true, useUnifiedTopology: true, serverApi: ServerApiVersion.v1 });
if (client.writeConcern || client.writeConcern) {
client.close();
return message.reply("Something went wrong with the database, please try again later and contact support if this problem persists!");
}
//Initialize
CreateNewCollection(message, client, server, owner.user.id);
client.connect(err => {
if (err) { return console.log(err); }
bot.mongoconnection.then(async (client) => {
//Initialize
CreateNewCollection(interaction, client, server, owner.user.id);
const db = client.db(server);
const dbo = db.collection('SETUP');
//Chose the appropriate command
command = args[0];
if (args.length < 1) { return interaction.reply({content: "Please chose a valid option", ephemeral: true}); }
if (command == 'welcome_channel') {
if (args.length != 2) { return message.reply('The command format is _!setup welcome_channel <channel name>_'); }
// setWelcomeChannel(dbo, message, args[1]);
const channel = message.guild.channels.cache.find(ch => ch.name === args[1]);
dbo.updateOne({welcomechannel: {$exists: true}}, {$set: {welcomechannel: `${channel.id}`}});
} else if (command == 'welcome_message') {
if (args.length < 2) { return message.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}_'); }
let msg = "";
for (let i = 1; i < args.length; i ++ ) {
msg += args[i] + ' ';
for (let i = 0; i < args.length; i++) {
try {
const command = args[i].name;
if (command == 'welcome_channel') {
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') {
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') {
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') {
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') {
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}); }
dbo.updateOne({_id: 'LOG'}, {$set: {severity: tier}})
interaction.reply({content: `Severity updated to ${tier}`, ephemeral: true});
}
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_");
// }
// const role = message.mentions.roles.first().id;
dbo.updateOne({_id: 'announcement'}, { $set: { 'role': role.id } });
interaction.reply({content: `Role updated to ${role}`, ephemeral: true});
}
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 } });
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
else if (command == 'help') {
let temp;
const subcat = args[i].value;
if (args[1] == 'welcome') {
temp = 'Use _/setup welcome\\_channel [channel name]_ to set the welcome channel and _/setup welcome\\_message [message]_ to set a welcome message/\n';
} else if (args[1] == 'logs') {
temp = 'To enable logging, use the command _/setup keep\\_logs true_ and _/setup log\\_channel_ [channel name] to set the logging channel/\n';
temp += 'Use _/setup keep\\_logs false_ to disable logging and _/setup log\\_severity [none, low, medium, high]_ to set the threshold\n';
temp += '__Severities:__\n*none* - unmute, unban\n*low* - mute\n*medium* - kick\n*high* - ban\nEvery tier also includes all notifs for ***higher*** tiers (AKA _/setup log\\_severity none_ will log everything from every severity)\n';
} else if (args[1] == 'announcement') {
temp = "To pick the announcement channel, use _/setup announcement\\_channel_\nTo pick the announcement role, use _/setup announcement\\_role_";
} else { temp = 'Use _/setup Please use the following format: _/setup help [welcome, logs, announcement]_\nExample: _/setup help welcome_'; }
interaction.reply({content: temp, ephemeral: true});
}*/
} catch (err) {
console.error(err);
}
if (msg.length > 30) { return message.reply('Please specify a welcome message under 30 characters!'); }
dbo.updateOne({welcomemessage: {$exists: true}}, {$set: {welcomemessage: msg}})
}
});
client.close();
}
module.exports = {
name: 'setup',
description: 'N/A',
execute
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: '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: '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}]
}
+32 -14
View File
@@ -1,23 +1,41 @@
function checkRole(message, args) {
let role = args[0];
if (message.member.hasPermission('ADMINISTRATOR')) { return true; }
const Discord = require('discord.js');
/*Maybe implement this later, useless for now
const client = new MongoClient(mongouri, { useNewUrlParser: true, useUnifiedTopology: true, serverApi: ServerApiVersion.v1 });
client.connect(err => {
/**
* @param {Discord.Guild} guild
* @returns {Promise<Boolean>}
*/
function checkRole(bot, guild, userId) {
return new Promise((resolve, reject) => {
const user = guild.members.cache.get(userId);
// return (role != undefined && user.roles.cache.has(role.id)); // || user.id == guild.ownerId || bot.inDebugMode
const role = client.db(message.guild.id).collection("admin-roles");
shop.find().toArray(function(err, itemstemp) {
if (err) throw err;
// 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);
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);
});
}
});
});
});*/
});
}
module.exports = {name: 'verify', checkRole}
module.exports = { checkRole }
+107 -86
View File
@@ -1,104 +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);
// 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);
//Draw the Border
context.strokeStyle = '#0099ff';
context.strokeRect(0, 0, canvas.width, canvas.height);
const { GuildMember } = require('discord.js');
//Add Text
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}!`;
//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 }
+63 -37
View File
@@ -1,51 +1,77 @@
const { Constants } = require('discord.js');
const scraper = require('mal-scraper');
module.exports = {
name: 'asearch',
description: 'Selmer bot gives you info on an anime',
async execute(message, args, Discord, Client, bot) {
if (args.length < 1) { return message.reply("Please specify an anime!"); }
let name = "";
async execute(interaction, Discord, Client, bot) {
const args = interaction.options.data;
const name = args.filter((arg) => { return (arg.name == 'anime'); })[0].value;
var style;
if (args.length > 1) {
let i = 0;
while (i < args.length && args[i] != '~fancy' && args[i] != '~summary' && args[i] != '~stats') {
name += args[i] + " ";
i++;
}
style = args.filter((arg) => { return (arg.name == 'style'); })[0].value;
}
else { style = "stats"; }
scraper.getInfoFromName(name).then((data) => {
if (args[args.length - 1] == '~stats') {
const newEmbed = new Discord.MessageEmbed()
.setColor('#002eff')
.setTitle(data.title)
//.setURL('https://discordjs.guide/popular-topics/embeds.html#embed-preview')
//.setDescription('My professional resume')
.setImage(data.picture)
.addFields(
{name: 'Genres:', value: data.genres.join(", ")},
{name: 'Score:', value: data.score},
{name: 'Episode:', value: data.episodes}
).setURL(data.trailer);
//Maybe change it to "this anime movie" if there is only 1 episode?
message.channel.send({ embeds: [newEmbed] });
} else if (args[args.length - 1] == '~fancy') {
let temp = `The ${data.genres.join(", ")} anime _${data.title}_ first aired on ${data.premiered}`;
if (data.aired) { temp += `. This anime ran for ${data.aired} for a total of ${data.episodes} episodes.`}
else { temp += ` and is still airing with ${data.episodes} so far!`}
//When set to true, getInfoFromName.getBestMatch did not, in fact, return the best results
scraper.getInfoFromName(name, false).then((data) => {
try { console.log(data);
if (style == 'stats') {
const newEmbed = new Discord.MessageEmbed()
.setColor('#002eff')
.setTitle(data.title)
//.setURL('https://discordjs.guide/popular-topics/embeds.html#embed-preview')
//.setDescription('My professional resume')
.setImage(data.picture)
.addFields(
{name: 'Genres:', value: data.genres.join(", ")},
{name: 'Score:', value: data.score},
{name: 'Episode:', value: data.episodes},
{name: "Date Aired/Premiered", value: data.premiered || data.aired}
).setURL(data.trailer);
temp += ` This anime has a score of ${data.score} and is ${data.popularity} on MyAnimeList!\n`;
temp += `You can see a trailer for ${data.title} here: ${data.trailer}`;
temp += `\n\n(to see a summary of the anime, use '${bot.prefix}asearch <anime name> ~summary')`;
interaction.reply({ embeds: [newEmbed] });
} else if (style == 'fancy') {
let temp = `The ${data.genres.join(", ")} anime _${data.title}_ first aired on ${data.premiered || data.aired}`;
if (data.aired) { temp += `. This anime ran for ${data.aired} for a total of ${data.episodes} episodes.`}
else { temp += ` and is still airing with ${data.episodes} so far!`}
message.channel.send({ embeds: [new Discord.MessageEmbed().setImage(data.picture)]});
message.channel.send(temp);
} else if (args[args.length - 1] == '~summary') {
let temp = data.synopsis;
message.channel.send(temp);
} else {
message.reply(`Unknown command, try using the format '${bot.prefix}asearch <anime name> [~stats or ~fancy or ~summary]`);
temp += ` This anime has a score of ${data.score} and is ${data.popularity} on MyAnimeList!\n`;
temp += `You can see a trailer for ${data.title} ***[here](${data.trailer})***`;
// temp += `\n\n(to see a summary of the anime, use '${bot.prefix}asearch <anime name> summary')`;
interaction.reply({ embeds: [new Discord.MessageEmbed().setImage(data.picture).setDescription(temp)] });
// message.channel.send(temp);
} else if (style == 'summary') {
let temp = data.synopsis;
interaction.reply(temp);
} else {
interaction.reply(`Unknown command, try using the format '/asearch <anime name> [stats or fancy or summary]`);
}
} catch (err) {
if (err.message.indexOf('MessageEmbed field values must be non-empty strings') != -1) {
interaction.reply(`Insufficient information on website!\nThe page can be found here: ${data.url}`);
} else {
const m = interaction.reply("Uh oh, an unknown error occured, click the ✅ to report this!");
const { addComplaintButton } = require('../dev only/submitcomplaint');
m.then((msg) => {
addComplaintButton(bot, msg);
});
}
console.log(err);
}
});
}
},
options: [
{name: 'anime', description: 'The name of the anime', type: Constants.ApplicationCommandOptionTypes.STRING, required: true},
{name: 'style', description: 'stats or fancy or summary (defaults to stats)', type: Constants.ApplicationCommandOptionTypes.STRING, required: false, choices: [ { name: 'stats', value: 'stats' }, { name: 'fancy', value: 'fancy' }, {name: 'summary', value: 'summary'} ] }
]
}
+59 -39
View File
@@ -1,52 +1,72 @@
const { Constants } = require('discord.js');
const scraper = require('mal-scraper');
const search = scraper.search;
const type = "manga";
module.exports = {
name: 'msearch',
description: 'Selmer bot gives you info on a manga',
async execute(message, args, Discord, Client, bot) {
if (args.length < 1) { return message.reply("Please specify a manga!"); }
let name = "";
async execute(interaction, Discord, Client, bot) {
const args = interaction.options.data;
const name = args.filter((arg) => { return (arg.name == 'manga'); })[0].value;
var style;
if (args.length > 1) {
let i = 0;
while (i < args.length && args[i] != '~fancy' && args[i] != '~summary' && args[i] != '~stats') {
name += args[i] + " ";
i++;
}
style = args.filter((arg) => { return (arg.name == 'style'); })[0].value;
}
else { style = "stats"; }
let cmd = args[args.length - 1];
search.search(type, {
maxResults: 1,
term: name
}).then((data1) => {
let data = data1[0];
if (cmd == "~stats") {
const newEmbed = new Discord.MessageEmbed()
.setColor('#ff9900')
.setTitle(data.title)
.setURL(data.url)
.setImage(data.thumbnail)
//.setDescription('My professional resume')
.addFields(
{name: 'Type:', value: data.type},
{name: 'Score:', value: data.score},
{name: 'Volumes:', value: data.vols}
);
try {
search.search(type, {
maxResults: 1,
term: name
}).then((data1) => {
let data = data1[0];
if (style == "stats") {
const newEmbed = new Discord.MessageEmbed()
.setColor('#ff9900')
.setTitle(data.title)
.setURL(data.url)
.setImage(data.thumbnail)
//.setDescription('My professional resume')
.addFields(
{name: 'Type:', value: data.type},
{name: 'Score:', value: data.score},
{name: 'Volumes:', value: data.vols}
);
message.channel.send({ embeds: [newEmbed] });
} else if (cmd == "~fancy") {
let temp = `The ${data.type} _${data.title}_ currently has ${data.vols} volumes with ${data.nbChapters} chapters, `;
temp += `running from _${data.startDate.replace(/-/g, "/")}_ to _${data.endDate.replace(/-/g, "/")}_, and has a score of ${data.score} on MyAnimeList!\n`;
temp += `You can read more about _${data.title}_ at ${data.url}`;
interaction.reply({ embeds: [newEmbed] });
} else if (style == "fancy") {
let temp = `The ${data.type} _${data.title}_ currently has ${data.vols} volumes with ${data.nbChapters} chapters, `;
temp += `running from _${data.startDate.replace(/-/g, "/")}_ to _${data.endDate.replace(/-/g, "/")}_, and has a score of ${data.score} on MyAnimeList!\n`;
temp += `You can read more about _${data.title}_ at ${data.url}`;
message.channel.send(temp);
} else if (cmd == "~summary") {
//Remove the "read more." at the end
let temp = data.shortDescription.slice(0, -10);
temp += ` _read more at_ ${data.url}`;
return message.channel.send(temp);
interaction.reply(temp);
} else if (style == "summary") {
//Remove the "read more." at the end
let temp = data.shortDescription.slice(0, -10);
temp += ` _read more at_ ${data.url}`;
return interaction.reply(temp);
} else {
interaction.reply(`Unknown command, try using the format '${bot.prefix}msearch <manga name> [stats or fancy or summary]`);
}
});
} catch (err) {
if (err.message.indexOf('MessageEmbed field values must be non-empty strings') != -1) {
interaction.reply(`Insufficient information on website!\nThe page can be found here: ${data.url}`);
} else {
const m = interaction.reply("Uh oh, an unknown error occured, click the ✅ to report this!");
const { addComplaintButton } = require('../dev only/submitcomplaint');
m.then((msg) => {
addComplaintButton(bot, msg);
});
}
});
}
console.log(err);
}
},
options: [{name: 'manga', description: 'The name of the manga', type: Constants.ApplicationCommandOptionTypes.STRING, required: true}, {name: 'style', description: 'stats or fancy or summary (defaults to stats)', type: Constants.ApplicationCommandOptionTypes.STRING, required: false, choices: [ { name: 'stats', value: 'stats' }, { name: 'fancy', value: 'fancy' }, {name: 'summary', value: 'summary'} ] }]
}
+43
View File
@@ -0,0 +1,43 @@
const axios = require('axios');
/**
* @param {String} purl
* @returns {Promise<Array<String>>}
*
* @example
* const purls = getPlaylistUrls(url);
* purls.then((urls) => { console.log(urls); });
*/
async function getPlaylistUrls(bot, purl, isPremium) {
const gApiKey = bot.youtubeAPIKey;
const numSongs = (isPremium) ? 20 : 10;
return new Promise(async (resolve, reject) => {
try {
const pid = (purl.split("list=")[1]).replace("&feature=share", "");
await axios.get(`https://www.googleapis.com/youtube/v3/playlistItems`, {
params: {
part: 'id,snippet',
maxResults: numSongs,
playlistId: pid,
key: gApiKey
}
})
.then((result) => {
const l = [];
result.data.items.forEach((vid, ind) => {
const vurl = `https://www.youtube.com/watch?v=${vid.snippet.resourceId.videoId}`
const pvurl = `https://www.youtube.com/watch?v=${vid.snippet.resourceId.videoId}&list=${pid}&index=${ind+1}`
l.push({video_url: vurl, in_playlist_url: pvurl});
});
resolve(l);
}).catch((err) => { reject(err.message); });
} catch (err) {
reject(err.message);
}
});
}
module.exports = { getPlaylistUrls }
+600
View File
@@ -0,0 +1,600 @@
// const { joinVoiceChannel, createAudioResource } = require('@discordjs/voice');
const { VoiceConnectionStatus, AudioPlayerStatus, createAudioPlayer, StreamType, joinVoiceChannel, createAudioResource, getVoiceConnection } = require('@discordjs/voice');
const { MessageActionRow, MessageButton, MessageEmbed, Constants } = require('discord.js');
const play = require('play-dl');
const { getPlaylistUrls } = require('./addPlaylist.js');
const { verPremium } = require('../premium/verifyPremium.js');
// Note: Unsure of what this does , but may be related to the play-dl lib (my notes are inconsistent)
// play.authorization();
async function playMusic(bot, interaction, channelId, url, isPlaylist) {
return new Promise(async (resolve, reject) => {
const channel = bot.channels.cache.get(channelId);
const connection = joinVoiceChannel({
channelId: channel.id,
guildId: channel.guild.id,
adapterCreator: channel.guild.voiceAdapterCreator,
});
connection.on(VoiceConnectionStatus.Ready, () => {
// console.log('Connected to the voice channel!');
});
try {
let stream;
let yt_info;
if (url.startsWith("https://")) {
if (!url.startsWith("https://www.youtube.com/") &&
!url.startsWith("https://music.youtube.com/") && !url.startsWith("https://youtu.be/")) {
if (!isPlaylist) {
interaction.reply("This is not a valid YouTube URL").catch((err) => {
console.log(err.message);
interaction.reply("Uh oh, an error has occured!");
});
reject();
return;
}
}
yt_info = await play.video_info(url);
// let stream = await play.stream_from_info(yt_info)
stream = await play.stream(url);
// console.log("Playing from a URL!");
} else {
yt_info = await play.search(url, {
limit: 1
});
stream = await play.stream(yt_info[0].url);
yt_info = await play.video_info(yt_info[0].url);
}
let resource = createAudioResource(stream.stream, {
inputType: stream.type
})
// let audio = "em.mp3";
// let resource = createAudioResource(join(__dirname, audio));
const data = bot.audioData.get(channel.guild.id);
if (data && data[1]) {
//[player, [queue Array]]
data[1].push({yt_info: yt_info, resource: resource});
bot.audioData.set(interaction.guildId, data);
if (!isPlaylist) {
interaction.reply(`_"${yt_info.video_details.title}" added to queue!_`).catch((err) => {
channel.send("Uh oh, there's been a Discord API error!");
console.log(err);
reject();
});
}
} else {
const player = createAudioPlayer();
connection.subscribe(player);
bot.audioData.set(interaction.guildId, [player, new Array(), null]);
player.play(resource);
player.on(AudioPlayerStatus.Playing, () => {
//Check maybe?
});
player.on(AudioPlayerStatus.Idle, () => {
//TODO find away to trigger the "stop" event here
// playNext(interaction, bot);
// pause_start_stop(interaction, bot);
});
playStopEmbed(bot, interaction, yt_info, false, true);
}
resolve(true);
} catch (err) {
if (!isPlaylist) {
console.log(err);
interaction.reply("Uh Oh, there's been an error!").catch((err) => { console.log(err); })
}
reject();
}
});
}
async function playStopEmbed(bot, interaction, yt_info, stopped, message = null) {
if (stopped) {
var em = interaction.message.embeds[0];
rows = [];
em.description = new String;
em.description = 'IS NOW STOPPED';
interaction.update({embeds: [em], components: rows});
} else {
const author = {
name: "Selmer Bot",
url: "",
iconURL: bot.user.displayAvatarURL()
}
const newEmbed = new MessageEmbed()
.setColor('#0F00F0')
.setTitle(`${yt_info.video_details.title}`)
.setAuthor(author)
.setDescription('IS NOW PLAYING')
.setURL(yt_info.video_details.url)
.setThumbnail(yt_info.video_details.thumbnails[0].url);
const row = new MessageActionRow()
.addComponents(
new MessageButton()
.setCustomId('PAUSE')
.setLabel('⏸️')
.setStyle('SECONDARY'),
new MessageButton()
.setCustomId('STOP')
.setLabel('⏹️')
.setStyle('SECONDARY'),
new MessageButton()
.setCustomId('SKIP')
.setLabel('⏭️')
.setStyle('SECONDARY')
);
if (message) {
if (interaction) {
const m = interaction.channel.send({ embeds: [newEmbed], components: [row] });
m.then((msg) => {
const data = bot.audioData.get(interaction.guildId);
data[2] = msg.id;
bot.audioData.set(interaction.guildId, data);
});
} else {
const m = message.reply({ embeds: [newEmbed], components: [row] });
m.then((msg) => {
const data = bot.audioData.get(message.guild.id);
data[2] = msg.id;
bot.audioData.set(message.guild.id, data);
});
}
} else {
interaction.update({embeds: [newEmbed], components: [row]});
}
}
}
function pause_start_stop(interaction, bot, message = null, command = null) {
try {
var player, em, guildId;
if (interaction) { guildId = interaction.guildId }
else { guildId = message.guild.id; }
const data = bot.audioData.get(guildId);
if (!data) {
var em = interaction.message.embeds[0];
em.description = new String;
em.description = 'IS NOW STOPPED';
return interaction.message.edit({ components: [], embeds: [em]});
}
if (interaction) {
player = data[0];
command = interaction.customId.toLowerCase();
em = interaction.message.embeds[0];
} else {
player = data[0];
em = message.embeds[0];
}
var rows = [new MessageActionRow()];
if (command == "pause") {
rows[0].addComponents(
new MessageButton()
.setCustomId('RESUME')
.setLabel('▶️')
.setStyle('SECONDARY'),
new MessageButton()
.setCustomId('STOP')
.setLabel('⏹️')
.setStyle('SECONDARY'),
new MessageButton()
.setCustomId('SKIP')
.setLabel('⏭️')
.setStyle('SECONDARY')
);
em.description = 'IS NOW PAUSED';
player.pause();
} else if (command == "resume") {
rows[0].addComponents(
new MessageButton()
.setCustomId('PAUSE')
.setLabel('⏸️')
.setStyle('SECONDARY'),
new MessageButton()
.setCustomId('STOP')
.setLabel('⏹️')
.setStyle('SECONDARY'),
new MessageButton()
.setCustomId('SKIP')
.setLabel('⏭️')
.setStyle('SECONDARY')
);
em.description = 'IS NOW PLAYING';
player.unpause();
} else if (command == "stop") {
playStopEmbed(bot, interaction, null, true);
const connection = getVoiceConnection(interaction.guild.id);
player.stop();
//Remove everything from queue
bot.audioData.delete(interaction.guildId);
if (connection) { connection.destroy(); }
return;
}
if (interaction) { interaction.update({embeds: [em], components: rows}); }
else {
const data = bot.audioData.get(guildId);
// var msg = message.channel.messages.cache.get(data[2]);
const newEmbed = message.embeds[0];
newEmbed.description = "Has been deferred";
message.edit({ embeds: [ newEmbed ], components: []});
const m = message.reply({embeds: [em], components: rows});
m.then((msg) => {
const data = bot.audioData.get(message.guild.id);
data[2] = msg.id;
bot.audioData.set(message.guild.id, data);
});
}
} catch (e) {
console.log(e);
rows = [];
em.description = new String('IS NOW STOPPED');
interaction.update({embeds: [em], components: rows});
}
}
function playNext(interaction, bot, message = null) {
// https://discordjs.guide/voice/audio-player.html#taking-action-within-the-error-handler
//Setup data[1] = {info: yt_info, resource: resource}
var guildId;
if (message != null) { guildId = message.guild.id; }
else { guildId = interaction.guildId; }
let data = bot.audioData.get(guildId);
if (!data) { return interaction.followUp("Audio queue empty!"); }
const player = data[0];
//Check if the queue is empty
if (data[1].length <= 0) {
player.stop();
bot.audioData.delete(guildId);
if (message) { return true; }
else { return playStopEmbed(bot, interaction, null, true); }
}
const resource = data[1][0].resource;
const yt_info = data[1][0].yt_info;
player.stop();
//Play the thing
player.play(resource);
//remove the song from queue
delete data[1][0];
data[1] = data[1].filter(n => n);
bot.audioData.set(guildId, data);
//Add the embed
var msg = message;
if (!message) {
msg = interaction.message;
interaction.update({ embeds: [ new MessageEmbed(interaction.message.embeds[0]).setDescription("IS NOW STOPPED") ], components: []});
}
playStopEmbed(bot, interaction, yt_info, false, msg);
return false;
}
function fromMessage(bot, command, interaction) {
//Setup data[1] = {info: yt_info, resource: resource}
const guildId = interaction.guildId;
let data = bot.audioData.get(guildId);
if (!data) { return interaction.reply("No music is currently playing!"); }
const player = data[0];
const message = interaction.channel.messages.cache.get(data[2]);
// console.log(message);
var em;
if (message.embeds) { em = message.embeds[0]; }
var rows;
if (command == 'stop') {
em = message.embeds[0];
rows = [];
em.description = new String;
em.description = 'IS NOW STOPPED';
player.stop();
const connection = getVoiceConnection(guildId);
if (connection) { connection.destroy(); }
bot.audioData.delete(guildId);
interaction.reply("Audio stopped!");
} else if (command == 'skip') {
if (playNext(null, bot, message)) {
rows = [];
em = message.embeds[0];
em.description = new String;
em.description = 'IS NOW STOPPED';
interaction.reply("Audio stopped!");
}
} else if (command == 'pause' || command == 'resume') {
interaction.deferReply();
pause_start_stop(null, bot, message, command);
interaction.deleteReply();
}
message.edit({embeds: [em], components: rows});
}
function showQueue(bot, isUpdate, interaction = null, page = 0) {
const guild = interaction.guildId;
const data = bot.audioData.get(guild);
if (!data) { return interaction.reply("The audio queue is empty!"); }
const rawQueue = data[1];
if (!rawQueue || rawQueue.length <= 0) { return interaction.reply("The audio queue is empty!"); }
const songList = [];
var tenSongs = '';
let i = 0;
rawQueue.forEach(function (rawSong) {
const songDetails = rawSong.yt_info.video_details;
tenSongs += `${i + 1}. ${songDetails.title}\n`;
i++;
//Split the songs into pages of 10
if (i % 10 == 0) { songList.push(tenSongs); tenSongs = ''; }
});
//If there's still some left over songs, add that
if (i % 10 != 0) {
songList.push(tenSongs);
}
if (page >= songList.length) { page = songList.length - 1 }
if (page < 0) { page = 0; } //LEAVE AS TWO IF's AS THE LENGTH MIGHT BE 0
if (songList.length == 0) { songList.push(tenSongs); }
//Create the embed
const author = {
name: "Selmer Bot",
url: "",
iconURL: bot.user.displayAvatarURL()
}
const newEmbed = new MessageEmbed()
.setTitle("SONG QUEUE")
.setAuthor(author)
.setDescription(songList[page])
.setFooter({ text: `Page ${page + 1}` })
const row = new MessageActionRow()
.addComponents(
new MessageButton()
.setCustomId(`audioQueue|${page - 1}`)
.setLabel('⬅️')
.setStyle('SECONDARY'),
new MessageButton()
.setCustomId(`audioQueue|${page + 1}`)
.setLabel('➡️')
.setStyle('SECONDARY'),
)
if (isUpdate) {
interaction.update({embeds: [newEmbed], components: [row]});
} else {
interaction.reply({ embeds: [newEmbed], components: [row] }).catch((err) => {
console.log(err);
interaction.channel.send({ embeds: [newEmbed], components: [row] });
});
}
}
function removeFromQueue(bot, interaction, posStr) {
const guildId = interaction.guildId;
let data = bot.audioData.get(guildId);
if (!data) { return interaction.reply("The audio queue is empty!"); }
const rawQueue = data[1];
if (!rawQueue || rawQueue.length <= 0) { return interaction.reply("The audio queue is empty!"); }
else if (isNaN(posStr) || Number(posStr) > rawQueue.length) { return interaction.reply("Please specify a number within queue bounds!"); }
const pos = Number(posStr) - 1;
const details = rawQueue[pos].yt_info.video_details;
delete data[1][pos];
data[1] = data[1].filter(n => n);
bot.audioData.set(guildId, data);
const newEmbed = new MessageEmbed()
.setColor('#0F00F0')
.setTitle(`${details.title}`)
.setAuthor({ name: "Selmer Bot", url: "", iconURL: bot.user.displayAvatarURL() })
.setDescription( `has been removed from position ${pos + 1} in queue!`)
.setThumbnail(details.thumbnails[0].url);
interaction.reply({ embeds: [newEmbed] }).catch((err) => {
interaction.channel.send({ embeds: [newEmbed] });
console.log(err);
})
}
function shuffleQueue(bot, interaction) {
const guildId = interaction.guildId;
let data = bot.audioData.get(guildId);
if (!data) { return interaction.reply("The audio queue is empty!"); }
let rawQueue = data[1];
if (!rawQueue || rawQueue.length <= 0) { return interaction.reply("The audio queue is empty!"); }
//Shuffle the queue
rawQueue = rawQueue.sort(() => Math.random()-0.5);
data[1] = rawQueue;
bot.audioData.set(guildId, data);
interaction.reply("The queue has been shuffled!\nThe new queue is:").catch((err) => {
console.log(err);
interaction.channel.send("The queue has been shuffled!\nThe new queue is:");
});
showQueue(bot, false, interaction);
}
//[ { name: 'play', type: 'SUB_COMMAND', options: [ [Object] ] } ]
module.exports = {
name: "audio",
description: 'Play a song from YouTube, add free!',
async execute(interaction, Discord, Client, bot) {
const commandList = ['stop', 'skip', 'pause', 'resume'];
const command = interaction.options.data[0];
if (!command) {
return interaction.reply("Please specify a song or playlist!").chatch(err => {
console.log(err);
interaction.channel.send("Uh oh, there's been an error!");
});
}
// if (args.length < 1) {
// message.reply("Please use the following format _!audio [song name or URL]_ **or** _!audio queue_");
// return;
// } else
if (command.name == 'queue') {
return showQueue(bot, false, interaction);
} else if (commandList.indexOf(command.name) != -1) {
return fromMessage(bot, command.name, interaction);
} else if (command.name == 'remove') {
if (args.length < 2) { return interaction.reply("Please specify a position in queue!"); }
return removeFromQueue(bot, interaction, args[1].value);
} else if (command.name == 'shuffle') {
return shuffleQueue(bot, interaction);
}
/*
Re-introduce once the issue with ydtl-core is resolved (see
https://github.com/porridgewithraisins/jam-bot#known-bugs)
const stream = await ytdl(url, { filter: 'audioonly' });
*/
const channelId = interaction.guild.members.cache.get(interaction.user.id).voice.channelId;
if (!channelId) {
interaction.reply("Please join a voice channel before you try this!");
return;
}
const subCommand = command.options[0];
if (!subCommand) { return; }
interaction.deferReply();
if (subCommand.name == 'playlist') {
var isPremium;
await verPremium(bot, interaction.user.id).then(() => { isPremium = true; }).catch(() => { isPremium = false; });
const urls_promise = getPlaylistUrls(bot, subCommand.value, isPremium);
urls_promise.then(async (urls) => {
for (let i = 0; i < urls.length; i++) {
try {
const url = urls[i].video_url;
await playMusic(bot, interaction, channelId, url, true);
const msg = (i > 0) ? `Added ${i+1}/${urls.length} songs to queue` : `Added ${i+1}/${urls.length} song to queue`;
interaction.editReply(msg).catch((err) => { interaction.channel.send(msg); });
} catch(err) {
console.log(err);
}
}
}).catch(err => {
const msg = (err == "Request failed with status code 400") ? "Invalid playlist URL" : "uh oh, there's been an error";
console.log(err);
interaction.reply(msg).catch((err) => {
interaction.channel.send(msg);
});
});
} else {
const url = subCommand.value;
playMusic(bot, interaction, channelId, url);
interaction.deleteReply();
}
}, pause_start_stop, playNext, showQueue,
options: [
{name: 'play', description: 'play a song', type: Constants.ApplicationCommandOptionTypes.SUB_COMMAND, options: [
{name: 'video', description: 'The song URL/search term(s)', type: Constants.ApplicationCommandOptionTypes.STRING, required: false},
{name: 'playlist', description: 'The playlist URL', type: Constants.ApplicationCommandOptionTypes.STRING, required: false}
]},
{name: 'pause', description: 'Pause the currently playing song', type: Constants.ApplicationCommandOptionTypes.SUB_COMMAND},
{name: 'queue', description: 'Show the song queue', type: Constants.ApplicationCommandOptionTypes.SUB_COMMAND},
{name: 'remove', description: 'Remove a song from the queue', type: Constants.ApplicationCommandOptionTypes.SUB_COMMAND, options: [
{name: 'position', description: 'The song\'s position in queue', type: Constants.ApplicationCommandOptionTypes.INTEGER, required: true}
]},
{name: 'resume', description: 'Resume playing the current song', type: Constants.ApplicationCommandOptionTypes.SUB_COMMAND},
{name: 'shuffle', description: 'Shuffle the song queue', type: Constants.ApplicationCommandOptionTypes.SUB_COMMAND},
{name: 'skip', description: 'skip the current song', type: Constants.ApplicationCommandOptionTypes.SUB_COMMAND},
{name: 'stop', description: 'stop the music and clear the queue', type: Constants.ApplicationCommandOptionTypes.SUB_COMMAND},
//Actions left: remove, shuffle,
]
}
View File
+24 -20
View File
@@ -12,27 +12,31 @@ function convertSnowflakeToDate(snowflake, epoch = DISCORD_EPOCH) {
// Validates a snowflake ID string and returns a JS Date object if valid
function validateSnowflake(snowflake, epoch) {
if (!Number.isInteger(+snowflake)) {
throw new Error(
"That doesn't look like a snowflake. Snowflakes contain only numbers."
)
try {
if (!Number.isInteger(+snowflake)) {
throw new Error(
"That doesn't look like a snowflake. Snowflakes contain only numbers."
)
}
if (snowflake < 4194304) {
throw new Error(
"That doesn't look like a snowflake. Snowflakes are much larger numbers."
)
}
const timestamp = convertSnowflakeToDate(snowflake, epoch)
if (Number.isNaN(timestamp.getTime())) {
throw new Error(
"That doesn't look like a snowflake. Snowflakes have fewer digits."
)
}
return timestamp
} catch(err) {
console.log(err);
}
if (snowflake < 4194304) {
throw new Error(
"That doesn't look like a snowflake. Snowflakes are much larger numbers."
)
}
const timestamp = convertSnowflakeToDate(snowflake, epoch)
if (Number.isNaN(timestamp.getTime())) {
throw new Error(
"That doesn't look like a snowflake. Snowflakes have fewer digits."
)
}
return timestamp
}
module.exports = { convertSnowflakeToDate, validateSnowflake }
+160 -117
View File
@@ -1,8 +1,9 @@
const { MongoClient, ServerApiVersion } = require('mongodb');
// const { update } = require('apt');
const { Collection, Client, Formatters, Intents } = require('discord.js');
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 = '$';
@@ -31,27 +32,27 @@ function isNum(arg) {
};
function CreateNewCollection(message, client, server, id, opponent = null, game = null) {
client.connect(err => {
const db = client.db(String(server) + "[ECON]");
const dbo = db.collection(id);
if (err) { return console.log(err); }
db.listCollections({name: id})
.next(function(err, collinfo) {
if (err) { return console.log(err); }
if (!collinfo) {
message.reply("You didn't have a place in my databases, so I created one for you!\nPlease try your command again!")
let hp_mp = {maxhp: BASE.HP, hp: BASE.HP, maxmp: BASE.MP, mp: BASE.MP}
dbo.insertOne({balance: 10, rank: 1, lastdayworked: 0, xp: 0, hpmp: hp_mp, game: game, gamesettings: {battle: {class: 'none', ultimate: true}}, opponent: opponent, state: STATE.IDLE, equipped: { weapons: {main: null, secondary: null}, items: {}}});
}
});
});
function CreateNewCollection(interaction, client, server, id, opponent = null, game = null) {
const db = client.db(String(server));
const dbo = db.collection(id);
client.close();
db.listCollections({name: id})
.next(function(err, collinfo) {
if (!collinfo) {
interaction.reply("You didn't have a place in my databases, so I created one for you!\nPlease try your command again!")
let hp_mp = {maxhp: BASE.HP, hp: BASE.HP, maxmp: BASE.MP, mp: BASE.MP}
dbo.insertOne({balance: 10, rank: 1, lastdayworked: 0, xp: 0, hpmp: hp_mp, game: game, gamesettings: {battle: {class: 'none', ultimate: true}}, opponent: opponent, state: STATE.IDLE, equipped: { weapons: {main: null, secondary: null}, items: {}}});
}
});
}
function addxp(message, 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) {
@@ -60,6 +61,7 @@ function addxp(message, 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);
@@ -80,11 +82,38 @@ function addxp(message, 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} }});
message.channel.send('Congradulations <@' + message.author.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 {
message.reply("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}});
@@ -92,25 +121,28 @@ function addxp(message, dbo, amt, xp_list) {
}
function getBalance(dbo, message) {
function getBalance(dbo, interaction) {
dbo.find({"balance": {$exists: true}}).toArray(function(err, doc) {
let bal = 0;
if (doc[0] && doc[0].balance) {
bal = doc[0].balance;
}
return message.reply(`<@${message.author.id}>, your current balance is ${currencySymbol}${bal}`);
return interaction.reply(`<@${interaction.user.id}>, your current balance is ${currencySymbol}${bal}`)
.catch((err) => {
interaction.channel.send(`<@${interaction.user.id}>, your current balance is ${currencySymbol}${bal}`);
});
});
}
function rank(dbo, message, xp_list) {
function rank(dbo, interaction, xp_list) {
dbo.find({"balance": {$exists: true}}).toArray(function(err, doc) {
if (!String(doc)) { return console.log("ERROR!\nThis account does not exist!"); }
let next = doc[0].rank + 1;
let needed = xp_list.get(next);
message.channel.send('<@' + message.author.id + '> you are currently at rank ' + String(next-1) + ' and have ' + String(doc[0].xp) + 'xp. You need ' + String(needed - doc[0].xp) + ' more xp to get to rank ' + String(next));
interaction.channel.send(`<@${interaction.user.id}'> you are currently at rank ${next-1} and have ${doc[0].xp}xp. You need ${needed - doc[0].xp} more xp to get to rank ${next}`);
});
}
@@ -120,22 +152,27 @@ function convertCurrency(id, amt, dbo) {
}
function checkAndUpdateBal(dbo, item, message, args) {
function checkAndUpdateBal(dbo, item, interaction, amt) {
return new Promise(function(resolve, reject) {
dbo.find({"balance": {$exists: true}}).toArray(b = function(err, doc) {
if (!String(doc)) {
message.reply("Your account doesn't exist, please contact the mods for support");
interaction.reply("Your account doesn't exist, please contact the mods for support").catch(() => {
interaction.channel.send("Your account doesn't exist, please contact the mods for support");
});
return false;
}
const icost = args[0] * item.cost;
const icost = amt * item.cost;
if (doc[0].balance < icost) {
message.reply("Insufficient funds!");
interaction.reply("Insufficient funds!").catch(() => { interaction.channel.send("Insufficient funds!"); });
resolve(false);
} else {
let temp = doc[0];
dbo.updateOne({balance: temp.balance, rank: temp.rank, lastdayworked: temp.lastdayworked}, { $set: { balance: doc[0].balance -= icost }});
message.reply(`You have bought ${item.name} for ${currencySymbol}${icost}!`);
interaction.reply(`You have bought ${item.name} for ${currencySymbol}${icost}!`).catch(() => {
interaction.channel.send(`You have bought ${item.name} for ${currencySymbol}${icost}!`);
});
resolve(true);
}
});
@@ -143,46 +180,51 @@ function checkAndUpdateBal(dbo, item, message, args) {
}
function buy(id, message, args, dbo, shop, xp_list) {
if (args.length < 2) { return; }
if (!isNum(args[0])) { return message.reply("Please enter a number for query 2"); }
function buy(id, interaction, dbo, shop, xp_list) {
const args = interaction.options.data;
let query = args[1];
//REAPPLY THIS TO OTHER FUNCTIONS
let query = args.filter((arg) => { return (arg.name == 'item'); })[0].value;
let amt = args.filter((arg) => { return (arg.name == 'amount'); })[0].value;
let item = shop.filter(function (item) { return item.name.toLowerCase() == query.toLowerCase(); })[0];
if (!String(item)) { return message.reply("This item does not exist!"); }
if (!String(item)) { return interaction.reply("This item does not exist!").catch(() => { interaction.channel.send("This item does not exist!"); }); }
// let success = Boolean(checkAndUpdateBal(dbo, item, message, args));
checkAndUpdateBal(dbo, item, message, args).then((success) => {
if (!success) { return } //The message is handled in the CheckAndUpdateBal() function
checkAndUpdateBal(dbo, item, interaction, amt).then((success) => {
//The message is handled in the CheckAndUpdateBal() function
if (!success) { return }
var newObj = { name: item.name, cost: item.cost, icon: item.icon, sect: item.sect};
addxp(message, 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)) {
let newnum = doc[0].num + Number(args[0]);
let newnum = doc[0].num + amt;
dbo.updateOne({ name: item.name }, {$set: {num: newnum}});
} else {
item.num = amt;
// dbo.insertOne({ name: item.name, cost: item.cost, icon: item.icon, sect: item.sect, num: Number(args[0])}); //Causes "cyclic dependancy"
dbo.insertOne(item);
dbo.updateOne(item, { $set: {num: Number(args[0]) }});
dbo.updateOne(item, { $set: {num: amt }});
}
});
})
};
function sell(id, message, args, dbo, shop, xp_list) {
if (args.length < 2) { return; }
if (!isNum(args[0])) { return message.reply("Please enter a number for query 1"); }
let query = args[1];
var newObj = { name: query };
//FIXME
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;
let item = shop.filter(function (titem) { return titem.name.toLowerCase() == query.toLowerCase(); });
if (!String(item)) { return message.reply("This item does not exist!"); }
if (!String(item)) {
return interaction.reply("This item does not exist!").catch((err) => {
interaction.channel.send("This item does not exist!");
});
}
item[0] = {name: item[0].name, cost: item[0].cost, icon: item[0].icon, sect: item[0].sect};
@@ -192,7 +234,6 @@ function sell(id, message, args, dbo, shop, xp_list) {
if(String(doc)) {
//Make sure you don't sell more than you have
let num = Number(args[0]);
if (num < doc[0].num) {
let newNum = doc[0].num - num;
dbo.updateOne({ name: item[0].name }, {$set: {num: newNum}});
@@ -209,23 +250,34 @@ function sell(id, message, args, dbo, shop, xp_list) {
dbo.updateOne({"balance": {$exists: true}}, { $set: { balance: currentBal + amountSoldFor }});
});
addxp(message, dbo, Math.ceil(functional_item.cost * 1.2), xp_list);
addxp(bot, interaction, dbo, Math.ceil(functional_item.cost * 1.2), xp_list);
message.reply(`You've sold ${num} ${String(functional_item.name)} for ${currencySymbol}${amountSoldFor}`);
interaction.reply(`You've sold ${num} ${String(functional_item.name)} for ${currencySymbol}${amountSoldFor}`)
.catch((err) => {
interaction.channel.send(`You've sold ${num} ${String(functional_item.name)} for ${currencySymbol}${amountSoldFor}`);
});
} else {
message.reply("You don't own this item!");
interaction.reply("You don't own this item!").catch((err) => {
interaction.channel.send(`You've sold ${num} ${String(functional_item.name)} for ${currencySymbol}${amountSoldFor}`);
});
}
});
}
function work(dbo, message, 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) {
if (!String(doc)) { return message.reply("Your account doesn't exist, please contact the mods for support"); }
if (!String(doc)) {
return interaction.reply("Your account doesn't exist, please contact the mods for support").catch((err) => {
interaction.channel.send("Your account doesn't exist, please contact the mods for support");
});
}
if (doc[0].lastdayworked == date) {//date
message.reply("You've already worked today, try again tomorrow!");
interaction.reply("You've already worked today, try again tomorrow!").catch((err) => {
interaction.channel.send("You've already worked today, try again tomorrow!");
});
} else {
//Amount to be paid
let amt = 0;
@@ -234,14 +286,17 @@ function work(dbo, message, xp_list) {
//Update the amount to the new TOTAL balance
dbo.updateOne({"balance": {$exists: true}}, { $set: { balance: doc[0].balance + amt, lastdayworked: date }});
addxp(message, dbo, xp_earned, xp_list);
message.channel.send(`<@${message.author.id}> worked and earned ${currencySymbol}${amt} and ${xp_earned} xp!`);
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!`)
});
}
});
}
function printInventory(dbo, message) {
function printInventory(dbo, interaction) {
let tempstring = "";
dbo.find().toArray(function(err, docs){
docs.forEach(val => {
@@ -251,44 +306,50 @@ function printInventory(dbo, message) {
});
if (tempstring == "") { tempstring += "You have nothing in your inventory!"; }
message.reply(tempstring);
interaction.reply(tempstring).catch((err) => {
interaction.channel.send(tempstring);
});
});
}
function getShop(message, args, items, bot) {
if (args.length == 0) {
let temp = Formatters.codeBlock(items.map(i => `${i.sect}`).join(' '));
temp = [...new Set(temp.split(' '))];
function getShop(interaction, items, bot) {
const args = interaction.options.data;
const type = args.filter((arg) => { return (arg.name == 'type'); })[0].value.toLowerCase();
return message.reply(`Please use the format ${bot.prefix}shop [type] [page number]\nTypes are: ${temp}`);
}
// if (args.length == 0) {
// let temp = Formatters.codeBlock(items.map(i => `${i.sect}`).join(' '));
// temp = [...new Set(temp.split(' '))];
// return message.reply(`Please use the format ${bot.prefix}shop [type] [page number]\nTypes are: ${temp}`);
// }
let ind = 1;
let noinp = false;
if (args.length > 1) {
if (args[1] < (items.length / 9)) {
ind = Number(args[1]);
const amt = args.filter((arg) => { return (arg.name == 'page'); })[0].value;
if (amt.value < (items.length / 9)) {
ind = Number(amt);
} else {
return message.reply("That number is too large");
return interaction.reply("That number is too large").catch(() => { interaction.channel.send("That number is too large"); });
}
} else {
noinp = true;
}
const items2 = items.filter(function(f) { return (f.sect.toLowerCase() == args[0].toLowerCase()) }).slice((ind - 1)*10, (ind - 1)*10+10);
const items2 = items.filter(function(f) { return (f.sect.toLowerCase() == type) }).slice((ind - 1)*10, (ind - 1)*10+10);
newText = Formatters.codeBlock(items2.map(i => `${i.icon} (${i.name}): $${i.cost}`).join('\n')); //${currencySymbol} doesn't owrk for some reason
if (noinp) {
newText += `(Use ${bot.prefix}shop [type] [page number] to access other pages)`;
}
return message.reply(newText);
return interaction.reply(newText).catch(() => { interaction.channel.send(newtext); });
}
function econHelp() {
let l = ["buy", 'shop', 'work', 'rank', 'inventory', 'balance', 'sell']
let l = ["buy", 'shop', 'work', 'rank', 'inventory', 'balance', 'sell'];
return l.join(", ");
}
@@ -298,76 +359,58 @@ function econHelp() {
module.exports = {
name: 'econ',
description: 'ECON',
async execute(bot, message, args, command, Discord, mongouri, items, xp_list) {
async execute(bot, interaction, Discord, mongouri, items, xp_list) {
//Set Discord vars
const id = message.author.id;
const server = message.guild.id;
const id = interaction.user.id;
const server = interaction.guildId;
const command = interaction.commandName;
const client = new MongoClient(mongouri, { useNewUrlParser: true, useUnifiedTopology: true, serverApi: ServerApiVersion.v1 });
if (client.writeConcern || client.writeConcern) {
client.close();
return message.reply("Something went wrong with the database, please try again later and contact support if this problem persists!");
}
//Initialize if necessary
CreateNewCollection(message, client, server, id);
// const client = new MongoClient(mongouri, { useNewUrlParser: true, useUnifiedTopology: true, serverApi: ServerApiVersion.v1 });
// if (client.writeConcern || client.writeConcern) {
// client.close();
// return message.reply("Something went wrong with the database, please try again later and contact support if this problem persists!");
// }
client.connect(err => {
const db = client.db(String(server) + "[ECON]");
bot.mongoconnection.then(async (client) => {
//Initialize if necessary
CreateNewCollection(interaction, client, server, id);
const db = client.db(String(server));
const dbo = db.collection(id);
if (err) { return console.log(err); }
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, message, args, dbo, items, xp_list);
buy(bot, id, interaction, dbo, items, xp_list);
} else if (command == 'shop') {
getShop(message, args, items, bot);
getShop(interaction, items, bot);
} else if (command == 'work') {
work(dbo, message, xp_list);
work(bot, dbo, interaction, xp_list);
} else if (command == 'rank') {
rank(dbo, message, xp_list);
rank(dbo, interaction, xp_list);
} else if (command == 'inventory') {
printInventory(dbo, message);
printInventory(dbo, interaction);
} else if (command == 'balance') {
getBalance(dbo, message);
getBalance(dbo, interaction);
} else if (command == 'sell') {
sell(id, message, args, dbo, items, xp_list);
sell(bot, id, interaction, dbo, items, xp_list);
} else {
message.channel.send("'" + message.content + "' is not a command!");
interaction.reply(`${command} is not a command`).catch((err) => {
interaction.channel.send(`${command} is not a command`);
});
}
});
//Close the database
client.close();
},
//Battle Updating stuff
addxp, checkAndUpdateBal, CreateNewCollection, econHelp, addxp, BASE, STATE
addxp, checkAndUpdateBal, CreateNewCollection, econHelp, BASE, STATE,
options: []
}
/*
?????????????? What did I need this for?
else if (command == 'checkinv') {
const req = dbo.findOne({ id: message.guild.id });
if (!req) { return message.reply("Doc doesn't exist!"); }
}
*/
+31
View File
@@ -0,0 +1,31 @@
const { Constants } = require('discord.js');
module.exports = {
//Add the items on boot-up
"buy": {
description: 'Buy an item from the shop',
options: [
{name: 'item', description: 'the item you want to buy', type: Constants.ApplicationCommandOptionTypes.STRING, required: true, choices: []},
{name: 'amount', description: 'item amount', type: Constants.ApplicationCommandOptionTypes.INTEGER, required: true, choices: []},
]},
'shop': {
description: 'Displays the shop',
options: [
{name: 'type', description: 'the type of item', type: Constants.ApplicationCommandOptionTypes.STRING, required: true, choices: [{name: 'Food', value: 'Food'}, {name: 'Weapons', value: 'Weapons'}, {name: 'HP', value: 'HP'}, {name: 'MP', value: 'MP'}]},
{name: 'page', description: 'the shop page you want to go to', type: Constants.ApplicationCommandOptionTypes.INTEGER, required: false},
]
}, 'work': {
description: 'Work and earn money and xp',
options: []
},
'rank': { description: 'See your current rank' },
'inventory': { description: 'Check what\'s in your inventory' },
'balance': { description: 'Check your current balance' },
'sell': {
description: 'Sell an item from your inventory',
options: [
{name: 'item', description: 'the item you want to buy', type: Constants.ApplicationCommandOptionTypes.STRING, required: true},
{name: 'amount', description: 'the item you want to buy', type: Constants.ApplicationCommandOptionTypes.INTEGER, required: true}
]
}
}
+68
View File
@@ -0,0 +1,68 @@
const fs = require('fs');
const { Client } = require('discord.js');
function mapToObj(map){
const obj = {}
for (let [k,v] of map) {
obj[k] = v
}
return obj
}
function objToMap(obj) {
const m = new Map();
for (i in obj) {
m.set(i, obj[i]);
}
return m;
}
async function backupLists(bot, IDM) {
try {
var backups = {}
backups.locked = mapToObj(bot.lockedChannels);
const bts = JSON.stringify({ "backups": backups });
if (IDM) {
fs.writeFile('commands/admin/backup.json', bts, 'utf8', (err) => {
// error checking
if(err) throw err;
console.log("New data added: " + bts);
process.exit(0);
});
} else {
process.env.backupLists = bts;
process.exit(0);
}
} catch (err) {
console.error(err);
exit(-1);
}
}
/**
* @param {Client} bot
* @param {Boolean} IDM
*/
async function loadBotBackups(bot, IDM) {
try {
if (!process.env.backupLists) {
const botBackups = require('../admin/backup.json').backups;
bot.lockedChannels = objToMap(botBackups.locked);
} else {
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();
const a = new Map();
}
}
module.exports = { backupLists, loadBotBackups }
+17
View File
@@ -0,0 +1,17 @@
const { cleardb } = require('./spam_collection.js');
function devCheck(message, bot) {
const command = message.content.split(' ')[0].slice(1);
const args = message.content.split(' ')[1];
const member = bot.guilds.cache.get(bot.home_server).members.cache.get(message.author.id);
//Check if they have the "Selmer Dev" role
if (member.roles.cache.has('944048889038774302')) {
switch (command) {
case 'spam_collection': if (args[0] != undefined) { cleardb(args[0]); }
break;
}
}
}
module.exports = { devCheck }
+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 }
+105
View File
@@ -0,0 +1,105 @@
const { MessageActionRow, MessageButton, MessageEmbed, DiscordAPIError, Message } = require('discord.js');
const complaintRow = new MessageActionRow();
const green = '#00f035';
const red = '#f30000';
complaintRow.setComponents(
new MessageButton()
.setCustomId('SUBMITCOMPLAINT')
.setLabel('Submit Complaint')
.setStyle('DANGER') //Maybe change this to 'PRIMARY'
);
function submitComplaint(message, bot) {
const complaint = message.content;
const channel = bot.guilds.cache.get(bot.home_server).channels.cache.get('998899306671124501');
const author = {
name: "Selmer Bot",
url: "",
iconURL: bot.user.displayAvatarURL()
}
const newEmbed = new MessageEmbed()
.setColor(red)
.setTitle(`Submitted by _${message.author.username}#${message.author.discriminator} ${message.author}_ in *${message.guild}* (OPEN)`)
.setAuthor(author)
.setDescription(`Content: ${complaint}`)
.setTimestamp();
const row = new MessageActionRow()
.addComponents(
new MessageButton()
.setCustomId('DEBUGDONE')
.setLabel('Done')
.setStyle('SUCCESS'),
new MessageButton()
.setCustomId('DEBUGURGENT')
.setLabel('Mark as Urgent')
.setStyle('DANGER'),
);
channel.send({ embeds: [newEmbed], components: [row] });
}
function resolveComplaint(interaction) {
if (interaction.customId == 'DEBUGDONE') {
var embd = new MessageEmbed(interaction.message.embeds[0]);
embd.setColor(green);
embd.title = embd.title.replace('(OPEN)', '(CLOSED)').replace('(URGENT)', '(CLOSED)');
interaction.update({ embeds: [embd], components: [] });
interaction.message.unpin();
} else {
var embd = new MessageEmbed(interaction.message.embeds[0]);
const row = new MessageActionRow();
row.addComponents(
new MessageButton()
.setCustomId('DEBUGDONE')
.setLabel('Done')
.setStyle('SUCCESS'),
);
embd.title = embd.title.replace('(OPEN)', '(URGENT)');
interaction.update({ embeds: [embd], components: [row] });
const m = interaction.message.pin();
// m.then((msg) => {
// msg.delete();
// });
}
}
module.exports = {
name: 'Complaints',
/**
* @param {Message} message
*/
async addComplaintButton(bot, message) {
try {
function filter(reaction) {
return (reaction.emoji.name == '✅');
}
message.react('✅').then(() => {
message.awaitReactions({ filter, max: 1, time: 60000, errors: ['time'] })
.then(collected => {
const reaction = collected.first();
submitComplaint(message, bot);
})
.catch(collected => { message.reactions.cache.get('✅').remove(); });
});
} catch (err) {
console.error(err);
}
}, submitComplaint, resolveComplaint
}
/*
const { addComplaintButton } = require('../dev only/submitcomplaint');
addComplaintButton(bot, message);
*/
+15 -9
View File
@@ -1,5 +1,7 @@
const { convoManager } = require('./API/chat.js');
const { handleInp } = require('./API/stripe');
const { convoManager } = require('./premium/chat.js');
const { handleInp } = require('./premium/stripe');
const reminders = require('./premium/reminders.js');
const repo = require('./Selmer Specific/repo.js');
const { MongoClient, ServerApiVersion, ConnectionClosedEvent } = require('mongodb');
function handle_dm(message, bot) {
@@ -8,12 +10,9 @@ function handle_dm(message, bot) {
if (!message.content.startsWith('!') || message.content.split(' ')[0] == '!startconvo' || message.content.split(' ')[0] == '!endconvo') {
const member = bot.guilds.cache.get(bot.home_server).members.cache.get(message.author.id);
const client = new MongoClient(bot.mongouri, { useNewUrlParser: true, useUnifiedTopology: true, serverApi: ServerApiVersion.v1 });
client.connect(async (err) => {
if (err) { return console.log(err); }
bot.mongoconnection.then(async (client) => {
const dbo = client.db('main').collection('authorized');
dbo.find({id: message.author}).toArray((err, docs) => {
dbo.find({ discordID: message.author.id }).toArray((err, docs) => {
//Only available to Selmer Bot devs, testers and "authorized" users
if (docs[0] != undefined || member.roles.cache.has('944048889038774302') || member.roles.cache.has('946610800418762792')) {
@@ -24,10 +23,17 @@ function handle_dm(message, bot) {
});
});
client.close();
} else if (message.content.indexOf('!premium') != -1) {
handleInp(bot, message);
} else {
} 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 {
return message.reply('UNUSABLE DM COMMAND DETECTED');
}
+26 -14
View File
@@ -1,15 +1,20 @@
//@ts-check
const { addxp, STATE, BASE } = require("../db/econ");
const turnManger = require('../turnManager.js');
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) { return message.reply("Oops! There's been an error! Please contact support if this problem persists!"); }
if (doc.game == null) { return message.reply("You're not even in a game and you're trying to quit! Sad..."); }
if (doc == undefined) {
interaction.reply("Oops! There's been an error, click the ✅ to report this!");
addComplaintButton(bot, interaction);
return;
}
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
@@ -20,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);
@@ -33,14 +41,14 @@ function loseGame(user_dbo, xp_collection, message, bot = null) {
}
function winGame(client, bot, db, user_dbo, xp_collection, message) {
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;
@@ -49,21 +57,25 @@ function winGame(client, bot, db, user_dbo, xp_collection, message) {
}
//Delete the bot's record of the game
client.db('B|S' + bot.user.id).collection(user_dbo.s.namespace.db.substr(0, user_dbo.s.namespace.db.length - 6)).drop();
if (!singlePlayer) {
client.db('B|S' + bot.user.id).collection(interaction.guildId).drop();
}
//Update the player with xp
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 }});
const channel = bot.channels.cache.get(message.channel.parentId);
channel.send(`<@${user_dbo.s.namespace.collection}> just won a game of "${docs[0].game}"!`);
message.channel.delete();
if (!singlePlayer) {
const channel = bot.channels.cache.get(interaction.channel.parentId);
channel.send(`<@${user_dbo.s.namespace.collection}> just won a game of "${docs[0].game}"!`);
}
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 },
+197 -167
View File
@@ -1,11 +1,15 @@
// // @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");
//#region Game Imports
const battle = require("./battle.js");
const ttt = require('./tictactoe.js');
const trivia = require('./trivia.js');
const mnswpr = require('./minesweeper.js');
//#endregion
@@ -18,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)
@@ -28,9 +32,7 @@ const allGames = ['battle', 'Tic Tac Toe'];
*/
async function Initialize(bot, user_dbo, command, message, first, second, other_dbo = null) {
return new Promise(async function(resolve, reject) {
user_dbo.find({"game": {$exists: true}}).toArray(function(err, docs){
let doc = docs[0];
console.log(command);
user_dbo.findOne({"game": {$exists: true}}).then(function(doc){
if (allGames.indexOf(command) != -1) {
if (other_dbo != null) {
user_dbo.updateOne( { "game": {$exists: true} }, { $set: { game: command, opponent: other_dbo.s.namespace.collection, state: STATE.FIGHTING }});
@@ -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,57 +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];
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) {
@@ -147,8 +156,8 @@ 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!');
// console.log(doc.game);
return interaction.reply('You can\'t equip while in a game!');
}
//If the thing is a shield, add it to secondary
@@ -157,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!");
}
});
@@ -169,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!`);
}
});
}
@@ -190,7 +199,7 @@ function in_game_redirector(bot, interaction, threadname, doc, client, mongouri,
let turn = doc.turn;
const user1 = doc[turn];
const user2 = doc[Number(!turn)];
const db = client.db(interaction.guildId + "[ECON]");
const db = client.db(interaction.guildId);
const dbo = db.collection(user1);
const other = db.collection(user2);
const thread = interaction.channel;
@@ -212,185 +221,206 @@ 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 });
if (client.writeConcern || client.writeConcern) {
client.close();
return message.reply("Something went wrong with the database, please try again later and contact support if this problem persists!");
}
// // @ts-ignore
// const client = new MongoClient(mongouri, { useNewUrlParser: true, useUnifiedTopology: true, serverApi: ServerApiVersion.v1 });
// if (client.writeConcern || client.writeConcern) {
// client.close();
// return message.reply("Something went wrong with the database, please try again later and contact support if this problem persists!");
// }
var client;
await bot.mongoconnection.then((client1) => {
client = client1;
});
const botdb = client.db('B|S' + bot.user.id);
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
const db = client.db(String(server));
const dbo = db.collection(id);
client.connect(err => {
const db = client.db(String(server) + "[ECON]");
const dbo = db.collection(id);
if (err) { return console.log(err); }
//Check if the client is currently in a game and act accordingly
//Check if the client is currently in a game and act accordingly
//#region Check Game
dbo.find({"game": {$exists: true}}).toArray(async function(err, docs){
if (err) { return console.log(err); }
let doc = docs[0];
let game = null;
if (doc) { game = doc.game; }
dbo.find({"game": {$exists: true}}).toArray(async function(err, docs) {
if (err) { return console.log(err); }
let doc = docs[0];
let game = null;
if (doc) { game = doc.game; }
//#endregion
//#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);
//For TWO+ PLAYER games only!!!
if (commandName == 'accept') {
const args = interaction.customId.split('|');
// console.log(interaction.message.interaction);
// console.log(interaction.user);
// return console.log(args);
//Check if the person actually challenged you
//Get the length of any user tag
let mentioned = msg.mentions.users.keys();
//Should also check if the player is already playing a game!!!
if (!acceptIsValid(bot, interaction.user, interaction, args[1], interaction.message)) { return; }
let tag_len = String(mentioned.next().value).length;
//Get the opponent
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];
//<@tage_len>, <@ --2+tag_len+2+3 = 7+tag_len
let other_tag = msg.content.substr(7+tag_len, tag_len);
//#region BOT SECTION
const other_discord = msg.mentions.users.get(other_tag);
//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_id);
//Should also check if the player is already playing a game!!!
if (!acceptIsValid(bot, other_discord, message, msg, tag_len)) { return; }
// 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_id, turn: 0, thread: threadname};
//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);
if (newCommand.replaceAll(" ", "").toLowerCase() == 'tictactoe') { newCommand = 'Tic Tac Toe'; }
if (newCommand === 'Tic Tac Toe') {
//Create the new board
let newboard = ["", "", "", "", "", "", "", "", ""];
newObj.board = newboard;
let symbols;
//#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);
// 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};
if (newCommand.replaceAll(" ", "").toLowerCase() == 'tictactoe') { newCommand = 'Tic Tac Toe'; }
if (newCommand === 'Tic Tac Toe') {
//Create the new board
let newboard = ["", "", "", "", "", "", "", "", ""];
newObj.board = newboard;
let symbols;
/*DOES NOT WORK
if (msg.content.lastIndexOf('>') == msg.content.lenth) {
symbols = ['X', 'O'];
} else {
symbols = msg.content.substring(msg.content.lastIndexOf('>') + 2).split(' ');
}
*/
newObj.symbols = ['X', 'O'];
}
serverinbotdb.insertOne(newObj);
//#endregion
//Need this for all 2 player games
const result = Initialize(bot, dbo, newCommand, msg, id, other_discord.id, other);
if (newCommand == 'battle') {
result.then(function (thread) {
battle.handle(client, dbo, other, bot, thread, 'initalize', mongouri, items, null, xp_collection);
});
} else if (newCommand == 'Tic Tac Toe') {
result.then(function (thread) {
ttt.handle(client, db, dbo, other, bot, thread, 'initalize', mongouri, null, xp_collection);
});
}
} else if (command == 'quit') {
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);
/*DOES NOT WORK
if (msg.content.lastIndexOf('>') == msg.content.lenth) {
symbols = ['X', 'O'];
} else {
loseGame(dbo, xp_collection, message, bot);
channel.send(`<@${message.author.id}> has quit a game of "${game}"!`);
symbols = msg.content.substring(msg.content.lastIndexOf('>') + 2).split(' ');
}
*/
newObj.symbols = ['X', 'O'];
}
serverinbotdb.insertOne(newObj);
//#endregion
const remAccptBtn = (msgToDel) => {
try {
msgToDel.edit({components: []});
} catch(err) {
console.error(err);
}
}
else if (command == 'status') {
getGame(message, args, db);
} else if (command == 'hp' || command == 'mp') {
hpmp(message, command, dbo);
} else if (command == 'equip') {
// equipItem(client, bot, db, dbo, message);
equip(message, args, command, dbo, bot, items);
} else if (command == 'classes') {
presentClasses(message, args[1]);
//Need this for all 2 player games
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 (commandName == 'quit') {
const channel = bot.channels.cache.get(interaction.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(`<@${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, interaction, bot);
channel.send(`<@${interaction.user.id}> has quit a game of "${game}"!`);
}
}
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);
// 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 {
//Make change to new name if necessary
if (command.replaceAll(" ", "").toLowerCase() == 'tictactoe') { command = 'Tic Tac Toe'; }
else {
if (commandName == undefined) { return interaction.reply("Please specify a game or use _!game help_"); }
if (game == 'battle' || command == 'battle') {
if (!bot.inDebugMode) { return message.reply("This command is currently in development!"); }
//Make change to new name if necessary
if (commandName.replaceAll(" ", "").toLowerCase() == 'tictactoe') { commandName = 'Tic Tac Toe'; }
//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!`);
}
//RETURN TO THIS LATER
if (game == 'battle' || commandName == 'battle') {
if (!bot.inDebugMode) { return interaction.reply("This command is currently in development!"); }
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!`);
}
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 _"Tic Tac Toe"_. To accept, please reply to this message with _!game accept_`);
}
//Catch statement (invalid command)
else {
if (command == undefined) { message.reply("Please specify a game or use _!game help_"); }
else { message.reply(`'!game ${command}' is not a command!`); }
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 == '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 = `${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, interaction, thread);
}
//Catch statement (invalid command)
else {
interaction.reply(`'/game ${commandName}' is not a command!`);
}
}
//#endregion
});
});
client.close();
}, allGames, in_game_redirector
}
+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: []
},
]
+180
View File
@@ -0,0 +1,180 @@
const { MessageActionRow, MessageButton, Interaction } = require('discord.js');
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, interaction) {
const args = interaction.options.data;
let componentlist = [];
var diff;
if (args.length < 1 || args[0].value == 'easy') {
diff = 0;
} else if (args[0].value == 'medium') {
diff = 0.1;
} else if (args[0].value == 'hard') {
diff = 0.2;
} else {
diff = 0;
}
let user = '';
if (args.length < 2) {
user = interaction.user.id;
}
for (let i = 0; i < 5; i ++) {
const row = new MessageActionRow();
for (let j = 0; j < 5; j ++) {
//customId = (spot in row)|(spot in column)
const btn = new MessageButton();
const isbmb = (Math.random() > (0.70 - diff));
if (isbmb) {
btn.setCustomId(`mswpr|${i}|${j}|t|${user}`);
} else {
btn.setCustomId(`mswpr|${i}|${j}|f|${user}`);
}
btn.setLabel('?')
.setStyle('SECONDARY')
row.addComponents(btn);
}
//Add the row to the list of rows
componentlist.push(row);
}
interaction.reply(`${interaction.user} has started a solo game of Minesweeper!`);
channel.send({ content: `SCORE: \`0\`\nTILES LEFT: \`25\``, components: componentlist });
}
function gameOver(interaction, won = false) {
var components = interaction.message.components;
return new Promise((resolve, reject) => {
for (i in components) {
for (j in components[i].components) {
if (components[i].components[j].customId.split("|")[3] === 't') {
components[i].components[j].label = "💣";
components[i].components[j].style = "DANGER";
} else {
components[i].components[j].style = "SUCCESS";
components[i].components[j].label = "5";
}
components[i].components[j].setDisabled(true);
}
}
if (won) {
resolve(components);
} else {
interaction.message.edit({ components: components });
}
});
}
/**
* @param {Interaction} interaction
*/
async function changeBoard(bot, interaction, xp_collection) {
interaction.deferUpdate();
const id = interaction.customId.split('|');
//"mswpr|y|x|<t/f>|[user]"
const col = id[1];
const row = id[2];
const isbmb = (id[3] === 't');
const user = id[4];
if (user && user != '') {
if (interaction.user.id != user) {
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
}
}
var components = interaction.message.components;
var btn = components[col].components[row];
if (isbmb) {
gameOver(interaction);
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) + 10}:R>`);
await wait(7000);
interaction.channel.delete();
} else {
btn.setDisabled(true);
btn.label = "1";
btn.style = "SUCCESS";
components[col].components[row] = btn;
let content = interaction.message.content;
let score = Number(content.split('`')[1]);
let tLeft = Number(content.split('`')[3]);
//Win the game (just clicked the last tile)
if (tLeft <= 1) {
gameOver(interaction, true).then(async (newComp) => {
interaction.message.edit({ content: `GAME WON!!!\nSCORE: \`${score + 1}\``, components: newComp });
const channel = bot.channels.cache.get(interaction.message.channel.parentId);
channel.send(`${interaction.user} won a game of Minesweeper with a score of ${score + 1}!`);
interaction.channel.send(`\`Thread closing\` <t:${Math.floor((new Date()).getTime()/1000) + 8}:R>`);
await wait(7000);
// interaction.channel.delete();
bot.mongoconnection.then(client => {
const db = client.db(interaction.guildId);
const dbo = db.collection(interaction.user.id);
winGame(client, bot, db, dbo, xp_collection, interaction.message, true);
});
});
} else {
interaction.message.edit({ content: `SCORE: \`${score + 1}\`\nTILES LEFT: \`${tLeft - 1}\``, components: components });
}
}
}
function checkAndStartGame(bot, interaction, channel) {
bot.mongoconnection.then(client => {
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) {
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, interaction);
} catch (err) {
console.log(err);
const { addComplaintButton } = require('../dev only/submitcomplaint.js');
addComplaintButton(bot, interaction.message);
}
});
});
}
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);
}
}
module.exports = { handle }
+6 -2
View File
@@ -108,7 +108,7 @@ function postActionBar(interaction, user_dbo, board, won, initial = false) {
}
}
console.log(componentlist);
// console.log(componentlist);
if (initial) {
interaction.send({ content: `Your turn <@${user_dbo.s.namespace.collection}>!`, components: componentlist });
@@ -129,7 +129,9 @@ async function handle(client, db, dbo, other, bot, thread, command, doc, interac
let symbol = doc.symbols[doc.turn];
let board = doc.board;
board[square] = symbol;
client.db('B|S' + bot.user.id).collection(dbo.s.namespace.db.substr(0, dbo.s.namespace.db.length - 6)).updateOne({'board': {$exists: true}}, {$set: {board: board}});
const gamedbo = client.db('B|S' + bot.user.id).collection(interaction.guildId);
gamedbo.updateOne({$or: [ {0: interaction.user.id}, {1: interaction.user.id} ], 'board': {$exists: true}}, {$set: {board: board}});
//Check if the game is over
let won = isTerminal(board);
@@ -140,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);
}
+178
View File
@@ -0,0 +1,178 @@
const request = require('request');
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].value);
}
// const { jsonToMapRecursive, mapToTableRecursive } = require('../utils/jsonFormatters.js');
function changeDB(bot, message, m) {
try {
bot.mongoconnection.then(client => {
const dbo = client.db(message.guild.id).collection('trivia');
//Game Over
if (m == null) {
return dbo.deleteOne({ channel: message.channel.id });
}
dbo.findOne({ channel: message.channel.id }).then((doc) => {
if (doc) {
dbo.updateOne({ channel: message.channel.id }, {$set: { m: Object.fromEntries(m) }});
} else{
dbo.insertOne({ channel: message.channel.id, m: Object.fromEntries(m) });
}
});
});
} catch (err) {
console.log(err);
}
}
/**
* @param {Interaction} interaction
* @param {Map<string, object>} m
* @param {int} time
*/
function startTrivia(interaction, m, time, bot) {
var iter = m.values().next();
var obj = iter.value;
//Get rid of the "answers required" ones
while (obj.question.toLowerCase().indexOf('which of these') != -1 && obj.question.toLowerCase().indexOf('which of the following') != -1) {
iter = iter.next();
obj = iter.value;
}
const question = obj.question;
const answer = obj.answer;
const filter = (response) => {
// return item.answers.some(answer => answer.toLowerCase() === response.content.toLowerCase());
return (response.content.toLowerCase() == answer.toLowerCase());
};
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));
//time: 1000 = 1 second
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}||) first!`);
} else {
message.reply(`Tsk Tsk, looks like nobody got the answer (||${answer}||) this time.`);
}
// changeDB(bot, message, null);
clearInterval(intId);
})
.catch((collected) => {
console.log(collected);
message.reply(`Tsk Tsk, looks like nobody got the answer (||${answer}||) this time.`);
// changeDB(bot, message, null);
clearInterval(intId);
});
});
}
//Add shuffle button?
module.exports = {
name: 'trivia',
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].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 interaction.reply(temp);
}
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
// const a = await fetch('https://opentdb.com/api_category.php');
// const json = await a.json();
// console.log(json);
var url = `https://opentdb.com/api.php?amount=${3}&difficulty=${inputs[0]}&type=multiple`;
if (inputs[1] != '') {
url += `&category=${inputs[1]}`;
}
request(url, function (error, response, body) {
if (!error && response.statusCode == 200) {
// const m = new Map(body);
let s = body.replace('{"response_code":0,"results":[', '');
s = s.substring(0, s.length - 2);
let queries = s.split('},');
const m = new Map();
let i = 0;
queries.forEach((query, ind) => {
query = query.substring(1, s.length - 2);
if (query.endsWith('}')) { query = query.substring(0, s.length - 2); }
// console.log(decode(query));
query = decode(query);
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;
q[5] = q[5].split(':[')[1];
let obj = { question: q[3].split(':"')[1], answer: q[4].split(':"')[1], incorrect: [ q[5].replaceAll('"', ''), q[6].replaceAll('"', ''), q[7].replaceAll(']}', '').replaceAll(']', '').replaceAll('"', '') ] }
m.set(i, obj);
i ++;
});
const time = (args[2]) ? args[2].value : (difficult.indexOf(inputs[0]) + 1) * 10000;
// console.log(m, time);
// changeDB(bot, message, m);
startTrivia(interaction, m, time, bot);
}
});
}
}
+28
View File
@@ -0,0 +1,28 @@
{
"trivia_categories": [
{ "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" }
]
}
+78 -17
View File
@@ -1,14 +1,28 @@
const { MongoClient, ServerApiVersion } = require('mongodb');
const { createSubscriptionManual } = require('./API/stripe.js');
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, Client } = require('discord.js');
const Discord = require('discord.js');
/**
*
* @param {Interaction} interaction
*/
async function handle_interaction(interaction, mongouri, turnManager, bot, STATE, items, xp_collection) {
if (interaction.isButton()) {
const battlecommandlist = ['ATTACK', 'HEAL', 'DEFEND', 'ITEMS', 'ULTIMATE'];
const singleCommandGames = ['ttt'];
const singleCommandGames = ['ttt']; // Use when you have more single-player games
const musicCommandList = ['PLAY', 'PAUSE', 'RESUME', 'STOP', 'SKIP'];
const client = new MongoClient(mongouri, { useNewUrlParser: true, useUnifiedTopology: true, serverApi: ServerApiVersion.v1 });
client.connect(async (err) => {
bot.mongoconnection.then(async (client) => {
if (battlecommandlist.indexOf(interaction.customId) != -1) {
await interaction.deferReply();
@@ -19,7 +33,7 @@ async function handle_interaction(interaction, mongouri, turnManager, bot, STATE
const id = result[0];
const doc = result[1];
const threadname = doc.thread;
const dbo = client.db(interaction.guildId + '[ECON]').collection(id);
const dbo = client.db(interaction.guildId).collection(id);
dbo.find({ 'state': {$exists: true} }).toArray(async function (err, docs) {
if (interaction.user.id == id) {
@@ -57,10 +71,38 @@ async function handle_interaction(interaction, mongouri, turnManager, bot, STATE
console.log("It's not your turn!");
}
});
} //else ifs here
});
} else if (musicCommandList.indexOf(interaction.customId) != -1 || interaction.customId.indexOf('audioQueue|') != -1) {
if (interaction.customId == 'SKIP') {
playNext(interaction, bot);
} else if (interaction.customId.indexOf('audioQueue|') != -1) {
const page = Number(interaction.customId.split('|')[1]);
showQueue(bot, true, interaction, page);
} else {
pause_start_stop(interaction, bot);
}
client.close();
} else if (interaction.customId == 'DEBUGURGENT' || interaction.customId == 'DEBUGDONE') {
resolveComplaint(interaction);
} else if (interaction.customId.indexOf('newEvent') != -1 || interaction.customId.indexOf('getEvents') != -1) {
reminders.modalHandle(bot, interaction);
} else if (interaction.customId.indexOf('reminderQueue') != -1) {
reminders.turnPage(bot, interaction);
} else if (interaction.customId.indexOf("tutoQueue") != -1){
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, false, xp_collection);
} else if (interaction.customId.indexOf("sbtutorial") != -1) {
interaction.deferUpdate();
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
});
}
//Menu Selection
@@ -69,16 +111,14 @@ async function handle_interaction(interaction, mongouri, turnManager, bot, STATE
// const command = interaction.customId.substring(interaction.customId.indexOf('|'), interaction.customId.length - interaction.customId.indexOf('|'))
if (interaction.customId.toLowerCase().indexOf('|heal') != -1) {
const client = new MongoClient(mongouri, { useNewUrlParser: true, useUnifiedTopology: true, serverApi: ServerApiVersion.v1 });
client.connect(err => {
console.log(id);
bot.mongoconnection.then(client => {
if (id != interaction.user.id) { return; }
let current_user = turnManager.getTurn(client, bot, interaction);
current_user.then(function(result) {
const doc = result[1];
const threadname = doc.thread;
const dbo = client.db(interaction.guildId + '[ECON]').collection(id);
const dbo = client.db(interaction.guildId).collection(id);
dbo.find({ 'state': {$exists: true} }).toArray(async function (err, docs) {
if (interaction.user.id == id) {
@@ -117,14 +157,35 @@ async function handle_interaction(interaction, mongouri, turnManager, bot, STATE
//Handle the interaction here
}
}
} else if (interaction.customId.indexOf('RSS') != -1) {
if (bot.inDebugMode) {
RSSInteractionHandler(bot, interaction);
}
} //menu else ifs here
}
//menu else ifs here
//Forms
else if (interaction.isModalSubmit()) {
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' ]
+44
View File
@@ -0,0 +1,44 @@
const Discord = require('discord.js');
const SEVCODES = {
none: 0,
low: 1,
medium: 2,
high: 3
}
const col_list = {0: '0ed300', 1: 'f6ff00', 2: 'ffa100', 3: 'FF0000'}
/**
* @param {Discord.Client} bot
* @param {Discord.Interaction} interaction the message the mod sent (AKA a DISCORD MESSAGE OBJECT)
*/
function log(bot, interaction, command, mentioned, reason, severity) {
bot.mongoconnection.then(client => {
// if (err) { return console.log(err); }
client.db(interaction.guildId).collection('SETUP').findOne({_id: 'LOG'}).then((doc) => {
if (!doc) { return interaction.reply("Server logs not set up yet!"); }
const channel = interaction.guild.channels.cache.get(doc.logchannel);
if (!channel) { return console.log("There is no specified log channel!"); }
//Check severity threshold
if (SEVCODES[doc.severity] < severity) { return; }
let action;
if (command.endsWith('e')) { action = (command + 'd'); }
else if (command.endsWith('n')) { action = (command + 'ned'); }
else { action = (command + 'ed'); }
const newEmbed = new Discord.MessageEmbed()
.setColor(col_list[severity])
.setTitle(`User ${mentioned.username}#${mentioned.discriminator} has been ${action}`)
//.setURL('https://discordjs.guide/popular-topics/embeds.html#embed-preview')
.setDescription(`Reason: ${reason}\n Responsible Mod: ${interaction.user.username}#${interaction.user.discriminator} (${interaction.user})`)
.setThumbnail(mentioned.displayAvatarURL())
.setTimestamp();
channel.send({ embeds: [newEmbed] });
});
});
}
module.exports = { log, SEVCODES }
+78
View File
@@ -0,0 +1,78 @@
const { Formatters, MessageEmbed, Constants } = require('discord.js');
const CoinGecko = require('coingecko-api');
const CoinGeckoClient = new CoinGecko();
//#region Runs at the start to store all coins
const coinlist = new Map();
async function getAllCoins() {
const coinlistraw = await CoinGeckoClient.coins.all();
coinlistraw.data.forEach((coin) => {
const obj = { id: coin.id, symbol: coin.symbol, name: coin.name, img: coin.image.small };
coinlist.set(coin.symbol.toLowerCase(), obj);
});
}
getAllCoins();
//#endregion
// Formatters.codeBlock
module.exports = {
name: 'crypto',
description: 'Get the prices for most cryptocurrencies!',
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";
coinlist.forEach((val, key) => {
temp += `${val.name} --> ${key.toUpperCase()}\n`;
});
temp += "```";
resolve(temp);
}).then((temp) => {
interaction.reply(temp);
})
} catch (err) {
console.error(err);
return interaction.reply("Uh Oh! There's been an error!");
}
}
try {
let data = await CoinGeckoClient.exchanges.fetchTickers('bitfinex', {
coin_ids: ['bitcoin', 'ethereum', 'ripple', 'litecoin', 'stellar']
});
var datacc = data.data.tickers.filter(t => t.target == 'USD');
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
var obj = coinlist.get(res.base.toLowerCase());
obj.price = res.last;
obj.score = res.trust_score || "N/A";
const embd = new MessageEmbed()
.setTitle(obj.name)
.setAuthor({ name: "Selmer Bot", url: "", iconURL: bot.user.displayAvatarURL() })
.setFields(
{name: "Price", value: `$${obj.price}`},
{name: "Symbol", value: `${obj.symbol}`},
{name: "Trust Score", value: `${obj.score}`},
)
.setThumbnail(obj.img)
.setTimestamp()
.setFooter({ text: 'Selmer Bot uses CoinGecko for cryptocurrency information'});
interaction.reply({ embeds: [embd] });
} catch (err) {
console.error(err);
return interaction.reply("Uh Oh! There's been an error!");
}
},
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: []
}
+4 -3
View File
@@ -1,7 +1,8 @@
module.exports = {
name: "test",
description: "HI SELMER",
execute(message, args, Discord, Client, bot) {
message.channel.send("HI SELMER!!!");
}
execute(interaction, Discord, Client, bot) {
interaction.reply("HI SELMER!!!");
},
options: []
}
+60 -16
View File
@@ -1,44 +1,88 @@
const { modHelp } = require('../admin/moderation.js');
const { Constants } = require('discord.js');
//CHANGE THIS TO FORMS?
module.exports ={
name: "help",
description: "Gets help for all of Selmer Bot's commands",
execute(message, args, Discord, Client, bot) {
execute(interaction, Discord, Client, bot) {
if (args[0] == 'econ') {
const groups = new Map([['SBspec', ['arrow', 'extracredit', 'profile', 'quotes', 'code']], ['adminCommands', [ 'setup', 'lock', 'unlock', 'serverlock' ]]]);
var spec = "";
if (interaction.options.data[0]) {
spec = interaction.options.data[0].value;
}
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!)`;
return message.channel.send(temp);
temp += `\n\n(remember to use \`/\` before the command!)`;
return interaction.reply({ content: temp, ephemeral: true });
}
else if (args[0] == 'game') {
else if (spec == 'game') {
let temp = "***Selmer Bot Commands (Games):***\n";
temp += bot.commands.get('game').allGames.join(", ");
temp += `\n\n(remember to use _'${bot.prefix}game'_ before the command!)`;
return message.channel.send(temp);
// 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 });
}
else if (spec == 'admin') {
let temp = `__**Selmer Bot Admin Commands**__\n`
Array.from(groups.get('adminCommands')).forEach(commName => {
let comm = bot.commands.get(commName);
temp += `${comm.name.toLowerCase()} - _${comm.description}_\n`;
});
temp += `__**Selmer Bot Moderation Commands**__\n`
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 \`/\` before the command!)`;
return interaction.reply({ content: temp, ephemeral: true });
}
let temp = "***Selmer Bot Commands:***\n";
bot.commands.sort((a, b) => {if (a.name && b.name) { return a.name[0] < b.name[0]} else {return false;} });
const noPostList = Array.from(groups.values()).flat();
const sList = groups.get('SBspec');
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 += `game - use _!help game_\n`;
} /* else if (comm.name == 'setup') {
temp += `setup - use _!setup_\n`;
}*/
temp += `**games** - use \`/help game\`\n`;
}
else {
if (comm.name && comm.description) {
if (comm.name && comm.description && !noPostList.includes(comm.name)) {
temp += `${comm.name.toLowerCase()} - _${comm.description}_\n`;
}
}
}
});
temp += `\n_(remember to use '${bot.prefix}' before the command!)_`;
message.channel.send(temp);
}
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);
if (comm && comm.name && comm.description) {
temp += `${comm.name.toLowerCase()} - _${comm.description}_\n`;
}
})
temp += `\n_(remember to use \`/\` before the command!)_`;
interaction.reply({ content: temp, ephemeral: true });
},
options: [{name: 'command', description: 'econ, game, or admin', type: Constants.ApplicationCommandOptionTypes.STRING, required: false, choices: [ { name: 'econ', value: 'econ' }, { name: 'game', value: 'game' }, {name: 'admin', value: 'admin'} ]}]
}
+23 -17
View File
@@ -1,26 +1,32 @@
const { Constants } = require('discord.js');
module.exports ={
name: 'kareoke',
description: 'Sing your least-favorite song with your favorite person, me!',
execute(message, args, Discord, Client, bot) {
execute(interaction, Discord, Client, bot) {
const arg = interaction.options.data[0].value;
if (arg == "help") { return interaction.reply({content: "Please pick out a song at https://www.karaoke-lyrics.net/\nThe command should look like\n/kareoke [link_here]"}); }
const axios = require('axios');
const cheerio = require('cheerio')
const url = args[0];
if (args[0] == undefined) {
message.channel.send("Please pick out a song at https://www.karaoke-lyrics.net/\nThe command should look like\n/kareoke [link_here]");
} else {
axios(url)
.then(response => {
const html = response.data;
const $ = cheerio.load(html);
lyrics = $('.para_row').text();
breakbar = "---------------------------------------------";
const url = interaction.options.data[0].value;
message.channel.send(breakbar + "\n" + lyrics + "\n" + breakbar);
//console.log(lyrics);
})
.catch(console.error);
}
}
axios(url)
.then(response => {
const html = response.data;
const $ = cheerio.load(html);
lyrics = $('.para_row').text();
breakbar = "---------------------------------------------";
message.channel.send(breakbar + "\n" + lyrics + "\n" + breakbar);
//console.log(lyrics);
})
.catch((err) => {
console.log(err);
interaction.reply("Please provide a valid url from https://www.karaoke-lyrics.net/");
});
},
options: [{name: 'url', description: 'the url of the song or "help"', type: Constants.ApplicationCommandOptionTypes.STRING, required: true}]
}
//TEST: https://www.karaoketexty.cz/texty-pisni/zoegirl/plain-170199
+4 -4
View File
@@ -1,15 +1,15 @@
module.exports = {
name: "links",
description: "A helpful list of links to all of Selmer's wonderful websites",
execute(message, args, Discord, Client, bot) {
execute(interaction, Discord, Client, bot) {
const newEmbed = new Discord.MessageEmbed()
.setColor('#002eff')
.setTitle("Selmer's Links")
.addFields(
{name: 'HyperGrader', value: "https://rpi.logicamodernapproach.com/"},
{name: 'Personal Website', value: "http://www.logicamodernapproach.com/rpi/intlogs22.bringsjord/#sec-3"}
{name: 'Personal Website', value: "http://www.logicamodernapproach.com/"}
);
message.channel.send({ embeds: [newEmbed] });
}
interaction.reply({ embeds: [newEmbed] });
}, options: []
}
-39
View File
@@ -1,39 +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(message, args, Discord, Client, bot) {
memes.random().then(meme => {
const newEmbed = new Discord.MessageEmbed()
.setColor(randomHexColor())
.setTitle(meme.caption)
// .setURL(meme.image)
.setDescription(`category: ${meme.category}`)
.setImage(meme.image);
message.channel.send({ 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 message.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);
message.channel.send({ embeds: [newEmbed] });
});
}
}
+313
View File
@@ -0,0 +1,313 @@
// https://ponly.com/200-pick-up-lines/
const { Constants } = require('discord.js');
const cheesy = [
'If I said you had a good body would you hold it against me?',
'Do you believe in love at first sight, or should I walk past again?',
'Are you a magician? Because you just cast a spell on me.',
'Youre so sweet you must be made out of chocolate.',
'Did you hurt yourself when you fell from Heaven?',
'Are you a cat? Because you look purrrfect!',
'Are you tired? Because youve been running through my mind all day.',
'Hold out hand: “Hey Im going for a walk. Will you hold this for me?”',
'Im not a photographer, but I can picture me and you together.”',
'Are you a cake, “Because I want a piece of that.”',
'Are you a bank loan? Well, youve certainly got my interest. ',
'If you were a triangle, youd be acute one!',
'Your hand looks heavy, let me hold it for you.',
'I think you are suffering from a lack of Vitamin Me.',
'On a scale of 1 to 10, youre a 9 and Im the 1 you lack.',
'Do you like Harry Potter? Because I adumbledore you.',
'Was your dad a boxer? Because damn, youre a knockout!',
'Your lips look lonely. Would they like to meet mine?',
'I was wondering if you had an extra heart. Mine was just stolen.',
'Your phone has GPS, right? Because Im totally going to get lost in those *insert color* eyes.',
'Would you grab my arm, so I can tell my friends Ive been touched by an angel?',
'Can I have your picture so I can show Santa what I want for Christmas?',
'Your body is 65% water and Im thirsty.',
'Excuse me, do you have a band-aid? Cause I scraped my knee falling for you.',
'Do I know you? Cause you look a lot like my next girlfriend/boyfriend.',
'Do you know what my shirt is made of? Boyfriend/girlfriend material?',
'They say Disneyland is the happiest place on earth. Well apparently, no one has ever been standing next to you.',
'You look cold. Want to use me as a blanket?',
'Are you an alien? Because you just abducted my heart.',
'Did your license get suspended for driving all these guys crazy?',
'For some reason, I was feeling a little off today. But when you came along, you definitely turned me on.',
'Can I borrow your phone? I need to call God and tell him Ive found his missing angel.',
'Hey, youre pretty and Im cute. Together wed be Pretty Cute.',
'Can I follow you home? Cause my parents always told me to follow my dreams.',
'Whats a smart, attractive man like myself doing without your phone number?',
'I seem to have lost my phone number. Can I have yours?',
'Im lost. Can you give me directions to your heart?',
'I would say God bless you, but it looks like he already did.',
'Are you a parking ticket? Cause youve got fine written all over you.',
'Is your name Google? Because you got everything I am searching for.',
'Are you sure youre not tired? Youve been running through my mind all day.',
'Did I tell you Im writing a book? Its a phone book and its missing your number.',
'Is there an airport nearby or is it my heart taking off?',
'Life without you would be like a broken pencil… pointless.',
'Are you from Tennessee? Because youre the only ten I see!',
'I must be in a museum because you truly are a work of art.',
'Im not stalking you, Im doing research!',
'If I could rearrange the alphabet, Id put U and I together.',
'Im no mathematician, but Im pretty good with numbers. Tell you what, give me yours and watch what I can do with it.',
'Aside from being sexy, what do you do for a living?'
];
const funny = [
'Are you wi-fi? Cause Im totally feeling a connection.',
'If I had a nickel for every time I saw someone as beautiful as you, Id have five cents.',
'Id like to take you to the movies, but they dont let you bring in your own snacks.',
'Are you Australian? Because you meet all of my koalafications.',
'Know whats on the menu? Me-N-U.',
'Your middle name must be Gillette. Because youre the best a man can get!',
'You look so familiar. Didnt we take a class together? I couldve sworn we had chemistry.',
'You and I are like nachos with jalapeños. Im super cheesy, youre super hot, and we belong together.',
'Knock-knock. (Whos there?) When where? (When where who?) Tomorrow night, my house, you.',
'Do you like Star Wars? Cause Yoda only one for me.',
'Go ahead, feel my shirt. Its made of boyfriend material!',
'If you were a Transformer youd be Optimus Fine!',
'Do you believe in love at first sight, or should I walk past you again?',
'Im learning about important dates in history. Wanna be one of them?',
'I seem to have lost my phone number. Can I have yours?',
'Are you a parking ticket? Cause youve got fine written all over you!',
'Did you invent the airplane? Because you seem just Wright for me!',
'I was wondering if you had an extra heart…because mine was just stolen.',
'Are you Siri? Because you autocomplete me!',
'I hope you know CPR, because you are taking my breath away!',
'If I had four quarters to give to the four prettiest women in the world, you would have a dollar!',
'Let me guess, your middle name is Gillette, right? Because youre the best a man can get!',
'Your eyes are bluer than the Atlantic Ocean, and I dont mind being lost at sea.',
'If you were a burger at McDonalds, youd be the McGorgeous.',
'Are you a camera? Because every time I look at you, I smile.',
'Is there an airport nearby, or was that just my heart taking off?',
'Are you a loan? Cause youve got my interest!',
'Im in the mood for pizza. A pizza you, that is!',
'Are you a 45-degree angle? Because youre a-cutie!',
'Youre so sweet, you could put Hersheys out of business!',
'Im good at algebra; I can replace your X and you wouldnt need to figure out Y.',
'Im really glad I just bought life insurance, because when I saw you, my heart stopped.',
'If I had to rate you from 1 to 10, Id give you a 9, because Im the 1 youre missing.',
'You must be jelly, cause jam dont shake like that.',
'You must be a bank loan, cause youve got my interest.',
'Ive got 1-ply, Ive got 2-ply, but all I really want is your re-ply.',
'If nothing lasts forever, will you be my nothing?',
'If you were a phaser on Star Trek, youd be set to stun!',
'Is your name Google? Because you have everything Ive been searching for.',
'Have you been covered in bees recently? I just assumed, because you look sweeter than honey.',
'There must be something wrong with my eyes. I cant take them off you.',
'Are you from Tennessee? Because youre the only Ten I See.',
'You must be a campfire. Because youre super hot and I want smore.',
'My buddies bet me that I wouldnt be able to start a conversation with the most beautiful person here. How should we spend their money?',
'Well, here I am. What are your other two wishes?',
'Remember me? Oh, thats right, Ive only met you in my dreams.',
'You must be made of cheese. Because youre looking Gouda tonight!',
'Im glad I remembered to bring my library card. Cause I am totally checking you out!',
'If you were a vegetable, you would be a cute-cumber!',
'Im no mathematician, but Im pretty good with numbers. Tell you what, give me yours and watch what I can do with it.'
];
const smooth = [
'Girl are those space pants? Because your butt is out of this world!',
'I think youre suffering from a lack of vitamin me.',
'Kiss me if Im wrong, but dinosaurs still exist, right?',
'Excuse me, is your name Earl Grey? Because you look like a hot-tea!',
'Can I borrow a kiss? I swear Ill give it back!',
'If you were a vegetable youd be a cute cumber.',
'Is summer over? Because Im about to “fall” for you!',
'Theres a massive clothes sale in my bedroom everything is 100% off',
'I lost my number…can I have yours?',
'Are you a baker? Cause those buns look TASTY.',
'Im not a hoarder but I really want to keep you forever.',
'Is your name google? Because youre everything Ive been searching for.',
'Are you an onion cos I want to remove your layers.',
'Even if there wasnt gravity on earth, Id still fall for you.',
'Im glad I brought my library card because Im checking you out.',
'You dont need keys to drive me crazy.',
'Do you know what my shirt is made of? Girlfriend material?',
'Do you smoke pot? Because weed be cute together.',
'Are those mirrors in your pants? Because I can see myself in them!',
'I was wondering if you had an extra heart? Mine was just stolen.',
'Are those space pants? Because your butt looks out of this world.',
'Is your name Chapstick? Because youre da-balm.',
'Do you have a map? Because Im getting lost in your eyes.',
'Do you have a bandaid? Cause I hurt my knee falling for you!',
'Are you my phone charger? Because without you, Id die.',
'Like a broken pencil, life without you is pointless.',
'Were not socks. But I think wed make a great pair.',
'This may be cheesy, but I think youre grate.',
'Did it hurt? When you fell from heaven.',
'I wish I could select all of your clothes and press delete.',
'If you were a booger Id pick you first ',
'Did you sit in sugar? Because you have a sweet ass.',
'Were you born a mermaid, because you were a mermaid for me.',
'Your hand looks heavy; can I hold it for you?',
'Is your name honey? Cuz Id love to drizzle you on my bland day.',
'Of all the beautiful curves on your body, your smile is my favorite.',
'Im finding it really hard to breathe. U just keep on taking my breath away.',
'Have you got the time… Ive got the time if youve got the place!',
'Are you glitter because you add sparkle to my life?',
'Are you sitting on the F5 key? Cause your ass is refreshing!',
'Lets commit the perfect crime- Ill steal your heart, you steal mine.',
'Do you wanna grab a coffee because I like you a latte?',
'Hello! I guess you are looking for Mr. Right. Well, thats me!',
'My mom said she found a beautiful and intelligent girl for me. Is that you?',
'How does it feel to be so gorgeous?',
'What does it feel like to be the most beautiful girl in the room?',
'If you were a transformer, youd be Optimus Fine.',
' Do you know whats on todays menu? Its Me n U.',
'Are you a doctor? Because my heart beats faster when I see you.',
'Are you Australian? Because you meet all of my koalafications.'
]
const best = [
'Let me tie your shoes, cause I dont want you falling for anyone else.',
' Are you an omelette? Because youre making me egg-cited!',
'Did you sit in a pile of sugar? Cause you have a pretty sweet butt.',
'Do you have a pencil? Cause I want to erase your past and write our future.',
'Are you my Appendix? Because I have a funny feeling in my stomach that makes me feel like I should take you out.',
'Are you a florist? Cause ever since I met you, my life has been Rosey.',
'I wanna live in your socks so I can be with you every step of the way.',
'If God made anything more beautiful than you, Im sure hed keep it for himself.',
'Do you have a map? Im getting lost in your eyes.',
'I dont have a library card, but do you mind if I check you out?',
'Are you an orphanage? Cause I wanna give you kids.',
'Do you have a sunburn, or are you always this hot?',
'I was feeling a little off today, but you definitely turned me on.',
'Are you a fruit, because Honeydew you know how fine you look right now?',
'Do you live in a corn field, cause Im stalking you.',
'Sorry, but you owe me a drink. [Why?] Because when I looked at you, I dropped mine.',
'Excuse me, is your name Earl Grey? Because you look like a hot-tea!',
'Im not a hoarder but I really want to keep you forever.',
'Do you have a Band-Aid? Because I just scraped my knee falling for you.',
'Are you a parking ticket? Cause youve got fine written all over you.',
'Are you mexican? Because youre my juan and only!',
'Do you drink Pepsi? Because youre so-da-licious!',
'Do I know you? Cause you look exactly like my next girlfriend.',
'Im no organ donor but Id be happy to give you my heart.',
'I seem to have lost my phone number. Can I have yours?',
'Is your nickname Chapstick? Because youre da balm!',
'Im not staring at your b00bs. Im staring at your heart.',
'Can I take your picture to prove to all my friends that angels do exist?',
'Ill give up my morning cereal to spoon you instead.',
'Do you want to see a picture of a beautiful person? (hold up a mirror)',
'Im not drunk, Im just intoxicated by YOU.',
'I was blinded by your beauty… Im going to need your name and number for insurance purposes.',
'Is there an airport nearby or is that just my heart taking off?',
'There must be a lightswitch on my forehead because everytime I see you, you turn me on!',
'Hi, Im writing a term paper on the finer things in life, and I was wondering if I could interview you?',
'Have you been to the doctor lately? Cause I think youre lacking some Vitamin Me.',
'Can I follow you home? Cause my parents always told me to follow my dreams.',
'You look so familiar… didnt we take a class together? I couldve sworn we had chemistry.',
'Hi, Im Mr. Right. Someone said you were looking for me?',
'Do you like Nintendo? Because Wii would look good together.',
'If you were a flower youd be a damnnn-delion',
'If you were ground coffee, youd be Espresso cause youre so fine.',
'If I had a star for every time you brightened my day, Id have a galaxy in my hand.',
'Damn, if being sexy was a crime, youd be guilty as charged!',
'I was wondering if you had an extra heart? Mine seems to have been stolen',
'Do you smoke pot? Because weed be cute together.',
'I thought happiness started with an H. Why does mine start with U?',
'Are you a campfire? Cause you are hot and I want smore.',
'If you were a tropical fruit, youd be a Fine-apple!',
'Are you a banana? Because I find you a-peeling'
]
const anime = [
'Hey darling, I must be in Infinite Tsukuyomi, cause youre like a dream come true. (Naruto)',
'Do you have a pen? So I can write your virginity in my death note. (Death Note)',
'Our love is over 9000. Know what I am Saiyan? (Dragon Ball)',
'Do you believe in fate? How about you stay the night? (Fate)',
'Are you a trap card? Because Ive Fallen for you. (Yu-Gi-Oh)',
'Baby, you can call me Luffy, because Ill bend anyway you want me to. (One Piece)',
'I dont need 99 souls. All I need is yours. (Soul Eater)',
'Did you like Ghostory? How about making our story? (Ghostory)',
'Youre hotter than the Amaterasu. (Naruto)',
'I know a mystery even Detective Conan cant solve: The mystery of how you got so damn beautiful! (Case Closed)',
'Girl have you mastered the Rasengan? Because every time your hand touches me you make me dizzy. (Naruto)',
'Zoro has the three sword style. But Im really good with my one sword. (One Piece)',
'You must be better than Kuuhaku. Because when I first saw you, you already won my heart. (No Game No Life)',
'If I just had a Geass, Id command you to be mine. (Code Geass)',
'Call me All Might cause Im just looking to Texas Smash. (My Hero Academia)',
'Something is rising and it isnt the shield hero. (The Rising of the Shield Hero)',
'I wish I was a demon because I really want you to slay me tonight. (Demon Slayer)',
'Are you a Wing Spiker? Cause you make my pulse spike too! (Haikyuu)',
'Are you a part of the phantom thieves? Because you just stole my heart. (Persona 5)',
'Is your name Erza? Cause my cheeks go Scarlet when I think of you. (Fairy Tale)',
'Do you have the Byakugaan, because it feels like you can see right through my Heart. (Naruto)',
'Is your name Levi? Because I love you GAJEEL-ION times more than Jet and Droy. (Fairy Tale)',
'Jeagar on the streets, Titan in the bed. (Attack on Titan )',
'Youre the hospital bed for my Deku. (My Hero Academia)',
'I was just checking you out from across the room with my Sharingan. (Naruto)',
'Are you Chisaki? Because I wanna Detroit smash you. (My Hero Academia)',
'Are you Nezuko? Because I want to be in your box. (Demon Slayer)',
'Just say yes and Ill give you more than seven eurekas. (Eureka Seven)',
'You remind me of Menma. Because even when I cant see you, I still feel you inside my heart. (Anohana)',
'Are you Gaara? Because love is written all over your face. (Naruto)',
'Are you Karasunos Captain? Because youre my number one. (Haikyuu)',
'Orochimaru: Be my vessel, I want to be inside you. (Naruto)',
'Are you Kikyo? Because I think you shot an arrow through my heart. (Inuyasha)',
'Are you from dragon ball, because I wanna blow my Picciload in you. (Dragon Ball)',
'Are you Makise Kurisu? Because I will travel world lines to be with you. (Steins;Gate)',
'Is your name Cana? Um, Cana call you mine? (Fairy Tail Guild)',
'Lets have a future diary. A diary of the future of you and me. (Future Diary)',
'Youre so sexy and thats a deadly sin! (The Seven Deadly Sins)',
'Dont need to be a phantom thief to steal your heart.',
'Show me your Bankai, big boy. (Bleach)',
'Did Buu attack you? Because youre as sweet as candy. (Dragon Ball)',
'Lelouch Vi Britannia commands you to give me your number. (Code Geass)',
'Are you a tuner monster? Because youre powering up my Syncro. (Yu-Gi-Oh!)',
'Damn girl, Are you Kira Yoshikage? Cause you blow me away. (JoJos Bizarre Adventure)',
'Id trade an arm and a leg to get a piece of your philosophers stone. (Fullmetal Alchemist)',
'Are you a pokemon? Because I wanna Peak-at-you? (Pokemon)',
'Roses are red, Light is dead, will you be the butter to my bread? (Death Note)',
'Hey Girl, you Bulma mind. (Dragon Ball)',
'Are you Saitama? Because you got me down in one move. (One-Punch Man)',
'Hey! Are you the railgun? Because I can feel a spark. (A Certain Scientific Railgun)',
'Are you a loli? Because youre worth going to jail for. (A lolicon)',
'Are you from Howls Moving Castle? Because you take my spirit away. (Howls Moving Castle)',
'I can be the Aizawa to your sleeping bag. (My Hero Academia)',
'Equivalent exchange. Ill give you half of my life, so give me half of yours! (Fullmetal Alchemist)',
'Even if it means risking my existence, Ill cross different world lines just to find you. (Steins;Gate)',
'Can I touch your Pikacheeks? (Pokemon)',
'Better wear my sunglasses because you Meiko my day so bright. (Prison School)',
'Are you Hiro? Because I want you to be my darling. (Darling in the Franxx)',
'Are you a death note? Because you make my heart stop. (Death Note)',
'Ill love you longer than all the One Piece episodes. (One Piece)'
]
module.exports = {
name: 'pickupline',
description: 'Get a pickup line from our selection of 200 lines!',
execute(interaction, Discord, Client, bot) {
const nameList = new Map([['cheesy', cheesy], ['funny', funny], ['smooth', smooth], ['best', best], ['anime', anime]]);
const type = interaction.options.data[0].value;
if (type == 'sources') {
message.reply("The normal lines are from https://ponly.com/200-pick-up-lines/\nThe anime lines are from https://thoughtcatalog.com/january-nelson/2021/06/anime-pick-up-lines/")
.then((msg) => { msg.suppressEmbeds(true); })
return;
}
var key;
if (type == 'random') {
let keyInd = Math.floor(Math.random() * nameList.size);
key = Array.from(nameList.keys())[keyInd];
} else {
key = type;
}
const arr = nameList.get(key);
keyInd = Math.floor(Math.random() * arr.length);
try {
interaction.reply(arr[keyInd]);
} catch {
interaction.channel.send(arr[keyInd]);
}
},
options: [{name: 'type', description: 'Pick the genre of line or take a chance with random!', type: Constants.ApplicationCommandOptionTypes.STRING, required: true, choices: [{name: "cheesy", value: "cheesy"}, {name: "funny", value: "funny"}, {name: "smooth", value: "smooth"}, {name: "best", value: "best"}, {name: "anime", value: "anime"}, {name: "random", value: "random"}, {name: "sources", value: "sources"} ] }]
}
-104
View File
@@ -1,104 +0,0 @@
const pathToFfmpeg = require('ffmpeg-static');
const { joinVoiceChannel, createAudioResource } = require('@discordjs/voice');
const { generateDependencyReport } = require('@discordjs/voice');
const { VoiceConnectionStatus, AudioPlayerStatus, createAudioPlayer, StreamType } = require('@discordjs/voice');
const play = require('play-dl');
// Leave here to be initialized at the program's start
const player = createAudioPlayer();
// Note: Unsure of what this does , but may be related to the play-dl lib (my notes are inconsistent)
// play.authorization();
module.exports = {
name: "audio",
async execute(message, args, Discord, Client, bot) {
// message.channel.send("This command has not been set up yet\nSorry!");
// return;
if (args[0] == "play") {
if (args.length < 1) {
message.reply("Please specify a function (play, pause, unpause or stop)");
return;
} else if (args.length < 2) {
message.reply("Please provide a song url");
return;
}
}
/*
Re-introduce once the issue with ydtl-core is resolved (see
https://github.com/porridgewithraisins/jam-bot#known-bugs)
const stream = await ytdl(url, { filter: 'audioonly' });
*/
if (!message.member.voice.channel) {
message.reply("Please join a voice channel before you try this!");
return;
}
//Test 930148609406685227
const channel = bot.channels.cache.get(message.member.voice.channel.id);
console.log(message.member.voice.channel.id);
const connection = joinVoiceChannel({
channelId: channel.id,
guildId: channel.guild.id,
adapterCreator: channel.guild.voiceAdapterCreator,
});
connection.on(VoiceConnectionStatus.Ready, () => {
console.log('Connected to the voice channel!');
});
if (args[0] == "play") {
let stream;
let info = "Playing __***";
let yt_info;
if (args[1].startsWith("https://")) {
if (!args[1].startsWith("https://www.youtube.com/") &&
!args[1].startsWith("https://music.youtube.com/")) {
message.reply("This is not a valid YouTube URL");
return;
}
yt_info = await play.video_info(args[1]);
// let stream = await play.stream_from_info(yt_info)
stream = await play.stream(args[1]);
console.log("Playing from a URL!");
} else {
yt_info = await play.search(args.slice(1).join(' '), {
limit: 1
});
stream = await play.stream(yt_info[0].url);
yt_info = await play.video_info(yt_info[0].url);
}
//Add the video info to the return message
info += yt_info.video_details.title + "***__\n";
info += "Check it out at " + yt_info.video_details.url + "\n";
let resource = createAudioResource(stream.stream, {
inputType: stream.type
})
connection.subscribe(player);
let audio = "em.mp3";
// let resource = createAudioResource(join(__dirname, audio));
player.play(resource);
player.on(AudioPlayerStatus.Playing, () => {
console.log('The audio player has started playing!');
});
message.reply(info);
} else if (args[0] == "pause") {
player.pause();
} else if (args[0] == "unpause") {
player.unpause();
} else if (args[0] == "stop") {
player.stop();
connection.destroy();
}
}
}
+106
View File
@@ -0,0 +1,106 @@
const { MessageEmbed, Constants } = require('discord.js');
//!poll <name> <option 1, option 2> [option 3...option 10]
module.exports = {
name: "poll",
description: "Create a cool poll embed (with time up to 1 hour!)",
async execute(interaction, Discord, Client, bot) {
const args = interaction.options.data;
const timeList = [ '1️⃣', '2️⃣', '3️⃣', '4️⃣', '5️⃣', '6️⃣', '7️⃣', '8️⃣', '9️⃣', '🔟' ];
const author = {
name: "Selmer Bot",
url: "",
iconURL: bot.user.displayAvatarURL()
}
const name = args.filter((arg) => { return (arg.name == 'question'); })[0].value;
const time = interaction.options.data.filter((arg) => { return (arg.name == 'time'); })[0].value;
var temp;
// var isTimed = !Number.isNaN(Number(args[1].split(":")[0]));
if (time != 0) {
temp = `This poll was created by ${interaction.user} and has no time limit!\n`;
} else {
time += time * 60; // (Number(args[1].split(':')[0]) * 60) + Number(args[1].split(':')[1]);
temp = `This poll was created by ${interaction.user} and ends <t:${Math.floor((new Date()).getTime()/1000) + time}:R>!\n`;
}
//args[0] is the poll name
for(let i = 0; i < args.length; i ++) {
if (args[i].name.indexOf('option') == -1) { continue; }
// complist.push({ name: `${timeList[i - 1]}: ${args[i]}`, value: "" });
temp += `\n${timeList[i - 2]}: ${args[i].value}\n`;
}
const embd = new MessageEmbed()
.setTimestamp()
.setTitle(`${name}`)
.setDescription(temp)
.setAuthor(author)
const m = interaction.channel.send({ embeds: [embd] });
m.then((msg) => {
interaction.reply("Poll Posted!");
for(let i = 0; i < args.length - 2; i ++) {
msg.react(timeList[i]);
}
if (!isTimed) {
return;
}
const filter = (reaction, user) => {
return timeList.includes(reaction.emoji.name);
};
let embd = msg.embeds[0];
//Replace the "and ends in <t:timestamp:R>" part with "has ended"
const collector = msg.createReactionCollector({ filter, time: time * 1000 });
collector.on('end', collected => {
let winnerC = 0;
let winners = [];
const col = Array.from(collected);
for (let i = 0; i < col.length; i++) {
const key = col[i][0];
const val = col[i][1];
if (val.count > winnerC) {
winners = [key];
winnerC = val.count;
} else if (val.count == winnerC) {
winners.push(key);
}
}
let temp;
if (winners.length > 1) {
temp = `The winners are: \`${winners.join(", ")}\` with \`${winnerC}\` votes each!`;
} else {
temp = `The winner is: \`${winners.join(", ")}\` with \`${winnerC}\` votes!`;
}
embd.description = embd.description.substr(0, 50) + ` has ended!\n${temp}` + embd.description.substr(embd.description.indexOf("!") + 1);
msg.edit({ embeds: [embd] });
msg.reply(temp);
});
});
},
options: [
{name: 'question', description: 'The poll question...', type: Constants.ApplicationCommandOptionTypes.STRING, required: true},
{name: 'time', description: 'the time the poll is open for in minutes (for no limit input 0)', type: Constants.ApplicationCommandOptionTypes.INTEGER, required: true},
{name: 'option1', description: 'A poll option', type: Constants.ApplicationCommandOptionTypes.STRING, required: true},
{name: 'option2', description: 'A poll option', type: Constants.ApplicationCommandOptionTypes.STRING, required: false},
{name: 'option3', description: 'A poll option', type: Constants.ApplicationCommandOptionTypes.STRING, required: false},
{name: 'option4', description: 'A poll option', type: Constants.ApplicationCommandOptionTypes.STRING, required: false},
{name: 'option5', description: 'A poll option', type: Constants.ApplicationCommandOptionTypes.STRING, required: false},
{name: 'option6', description: 'A poll option', type: Constants.ApplicationCommandOptionTypes.STRING, required: false},
{name: 'option7', description: 'A poll option', type: Constants.ApplicationCommandOptionTypes.STRING, required: false},
{name: 'option8', description: 'A poll option', type: Constants.ApplicationCommandOptionTypes.STRING, required: false},
{name: 'option9', description: 'A poll option', type: Constants.ApplicationCommandOptionTypes.STRING, required: false},
{name: 'option10', description: 'A poll option', type: Constants.ApplicationCommandOptionTypes.STRING, required: false},
]
}
+25 -20
View File
@@ -1,3 +1,5 @@
//NOTE: THIS FUNCTION REQUIRES REPLIES, AND I CAN'T FIGURE OUT HOW TO LINK THEM TO SLASH COMMANDS
module.exports = {
name: 'react',
description: "Reacts with a phrase or single emoji",
@@ -8,31 +10,34 @@ module.exports = {
if (message.reference) {
msg = await message.channel.messages.fetch(message.reference.messageId);
} else { msg = message; }
//Get rid of any custom emojis
console.log("IMPLEMENT THIS");
let emoji = [...new Set(args[0])];
if (emoji.length > 15 /*|| message.IndexOf(":") != -1*/) { return message.reply("Please enter less than 15 emojis"); }
let notused = new Array(15);
let counter = 0;
try {
let emoji = [...new Set(args[0])];
if (emoji.length > 15 /*|| message.IndexOf(":") != -1*/) { return message.reply("Please enter less than 15 emojis"); }
let notused = new Array(15);
let counter = 0;
for (let i = 0; i < emoji.length; i ++) {
try {
await msg.react(emoji[i]);
} catch(err) {
//The emoji wasn't a valid one
notused[counter] = emoji[i];
counter ++;
for (let i = 0; i < emoji.length; i ++) {
try {
await msg.react(emoji[i]);
} catch(err) {
//The emoji wasn't a valid one
notused[counter] = emoji[i];
counter ++;
}
}
}
if (notused.length > 0) {
notused = notused.filter(element => element !== undefined);
if (notused.length > 1) {
message.reply("These are not valid reaction emoji(s): " + notused.toString());
} else {
message.reply(notused.toString() + " is not a valid reaction emoji");
if (notused.length > 0) {
notused = notused.filter(element => element !== undefined);
if (notused.length > 1) {
message.reply("These are not valid reaction emoji(s): " + notused.toString());
} else {
message.reply(notused.toString() + " is not a valid reaction emoji");
}
}
} catch (err) {
console.log(err);
return message.reply("Uh oh, there's been an error");
}
}
}
-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");
}
}
}
+42 -22
View File
@@ -1,31 +1,51 @@
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: ".....",
async execute(message, args, Discord, Client, bot) {
const axios = require('axios');
const cheerio = require('cheerio');
const url = args[0];
axios(url)
.then(async response => {
const html = response.data;
const $ = cheerio.load(html);
//lyrics = $('.para_row').text();
const haste = await hastebin(html, { extension: "txt" });
message.channel.send(haste);
// console.log(lyrics);
})
.catch(function(err) {
if (err.message.indexOf('The "url" argument must be of type string') != -1) {
message.reply("The URL should be a string!");
} else {
message.reply("Oops! There's been an error");
}
description: "Scrapes a website, then puts the result into a hastebin",
async execute(interaction, Discord, Client, bot) {
// const cheerio = require('cheerio');
const url = interaction.options.data[0].value;
console.log(err);
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;
// const $ = cheerio.load(html);
//lyrics = $('.para_row').text();
const haste = await hastebin(html, { extension: "txt" });
interaction.reply(haste);
// console.log(lyrics);
})
.catch(function(err) {
if (err.message.indexOf('The "url" argument must be of type string') != -1) {
interaction.reply("The URL should be a string!");
} else if (err.code == 'ERR_BAD_REQUEST') {
interaction.reply("404 link not valid!")
} else {
const m = interaction.reply("Oops! There's been an error, click the ✅ to report this!");
m.then((msg) => {
addComplaintButton(bot, msg);
});
}
console.log(err);
}
});
},
options: [{name: 'url', description: 'The website URL', type: Constants.ApplicationCommandOptionTypes.STRING, required: true }]
}
//TEST: https://www.karaoketexty.cz/texty-pisni/zoegirl/plain-170199
+121
View File
@@ -0,0 +1,121 @@
const { Constants, MessageEmbed, Interaction } = require('discord.js');
const dateFns = require('date-fns');
const { formatToTimeZone } = require('date-fns-timezone');
const Alpaca = require('@alpacahq/alpaca-trade-api');
const apiKeyId = process.env.alpKey || require('../../config.json').alpKey;
const secretKey = process.env.alpSec || require('../../config.json').alpSec;
const alpaca = new Alpaca({
keyId: apiKeyId,
secretKey: secretKey,
paper: true,
usePolygon: false
});
//This is the same as making the following request: https://data.alpaca.markets/v2/stocks/snapshots?symbols={stock_symbols_here}
async function getStockData(bot, interaction, stock, type, after) {
try {
const snapshotPromise = alpaca.getSnapshot(stock);
snapshotPromise.then(snapshot => {
const embd = new MessageEmbed()
.setAuthor({ name: "Selmer Bot", url: "", iconURL: bot.user.displayAvatarURL() })
.setFooter({ text: 'Selmer Bot uses Alpaca for stock information'})
if (type) {
const lt = snapshot.LatestTrade;
embd.setTitle(`${stock} Latest Trade`)
.setTimestamp(lt.Timestamp)
.addFields(
{name: 'Price', value: `${lt.Price}`},
{name: 'Size', value: `${lt.Size}`},
//This will always be IEX, as it is the only exchange the free version offers
{name: 'Exchange', value: `IEX (${lt.Exchange})`},
)
} else if (type) {
if (after) {
return interaction.reply("Due to the markets not being open, there is no quote data available!");
}
const lq = snapshot.LatestQuote;
embd.setTitle(`${stock} Latest Quote`)
.setTimestamp(lq.Timestamp)
.addFields(
{name: 'Ask Price', value: `${lq.AskPrice}`},
{name: 'Ask Size', value: `${lq.AskSize}`},
{name: 'Bid price', value: `${lq.BidPrice}`},
{name: 'Bid Size', value: `${lq.BidSize}`},
//This will always be IEX, as it is the only exchange the free version offers
{name: 'Exchange', value: `IEX (${lq.Exchange})`},
)
} else if (type) {
const mb = snapshot.MinuteBar;
const db = snapshot.DailyBar;
const pdb = snapshot.PrevDailyBar;
embd.setTitle(`${stock} Bars`)
.setTimestamp(mb.Timestamp)
.addFields(
{name: 'Minute Bar', value: `Open Price: ${mb.OpenPrice},\nClose Price: ${mb.ClosePrice},\nHigh Price: ${mb.HighPrice},\nLow Price: ${mb.LowPrice},\nVolume: ${mb.Volume},\nCount: ${mb.TradeCount},\nVWAP: ${mb.VWAP}`},
{name: 'Day Bar (Today)', value: `Open Price: ${db.OpenPrice},\nClose Price: ${db.ClosePrice},\nHigh Price: ${db.HighPrice},\nLow Price: ${db.LowPrice},\nVolume: ${db.Volume},\nCount: ${db.TradeCount},\nVWAP: ${db.VWAP}`},
{name: 'Day Bar (Yesterday)', value: `Open Price: ${pdb.OpenPrice},\nClose Price: ${pdb.ClosePrice},\nHigh Price: ${pdb.HighPrice},\nLow Price: ${pdb.LowPrice},\nVolume: ${pdb.Volume},\nCount: ${pdb.TradeCount},\nVWAP: ${pdb.VWAP}`},
)
} else {
return interaction.reply("The command format is: _/stocks <stock_name, 'hours'> <trade, quote, bars> [after]_");
}
interaction.reply({embeds: [embd]});
})
} catch(err) {
console.error(err);
interaction.reply("Uh Oh, there's been an error!");
}
}
function getData(bot, interaction) {
const args = interaction.options.data;
const stock = args.filter((arg) => { return (arg.name == 'name')})[0].value;
const type = args.filter((arg) => { return (arg.name == 'type')})[0].value;
const after = (args.length > 2 && args.filter((arg) => { return (arg.name == 'after')})[0].value);
const format = `yyyy-MM-dd HH:mm:ss`;
const date = dateFns.format(new Date(), format);
alpaca.getClock().then((clock) => {
// var txt = `The market is currently ${clock.is_open ? 'open.' : 'closed.'}`;
alpaca.getCalendar({
start: date,
end: date
}).then((calendars) => {
let temp;
if (clock.is_open || after) {
if (stock == 'hours') {
temp = `The markets opened at ${calendars[0].open} and will close at ${calendars[0].close} on ${date}.`;
return interaction.reply(temp);
}
getStockData(bot, interaction, stock, type, after);
} else {
// `The market is currently ${clock.is_open ? 'open.' : 'closed.'}`
//May be innacurate?
temp = `_The markets closed at \`${calendars[0].close}\` and will open again at \`${calendars[0].open}\` on \`${dateFns.format((new Date()).setDate(new Date().getDate() + 1), 'yyyy-MM-dd')}\`.\nTo get the last snapshot before market closure, add the \`after\` keyword to the end of your command (trade and bars ONLY), ex: /stocks GOOG bars after_`;
return interaction.reply(temp);
}
});
})
}
//!stocks <stock_name, "hours"> <trade, quote, bars> [after]
module.exports = {
name: 'stocks',
description: "Have Selmer Bot give you \"current\" stock prices",
async execute(interaction, Discord, Client, bot) {
getData(bot, interaction);
},
options: [
{name: 'name', description: 'the stock name or "hours" for market hours', type: Constants.ApplicationCommandOptionTypes.STRING, required: true},
{name: 'type', description: 'The type of data to present', type: Constants.ApplicationCommandOptionTypes.STRING, required: true, choices: [ { name: 'trade', value: 'trade' }, { name: 'quote', value: 'quote' }, {name: 'bars', value: 'bars'}]},
{name: 'after', description: 'If the markets are closed, get the last entry', type: Constants.ApplicationCommandOptionTypes.BOOLEAN, required: false},
]
}
@@ -3,7 +3,7 @@ const { exit } = require('process');
const { checkResponses } = require('./wordlist.js');
//Error checking function (message deleted error fix)
//Error checking function (message deleted error fix, workaround already applied but...)
//message.channel.send("Oops, there's been an error, please contact support!");
async function messageExists(message) {
return new Promise((resolve, reject) => {
@@ -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,9 +37,14 @@ async function getResponse(convo, bot) {
});
return response;
} catch (err) {
console.error(err);
return false;
}
}
async function convoManager(clientinp, bot, message) {
async function convoManager(clientinp, bot, message) {
//Just in case, make sure it can't be changed
const client = clientinp;
@@ -78,6 +84,10 @@ async function getResponse(convo, bot) {
//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');
@@ -103,7 +113,7 @@ module.exports = {
name: 'chat',
description: 'chat',
convoManager,
execute(message, args, Discord, Client, bot) {
message.reply("Please DM Selmer bot to use this command!");
execute(interaction, args, Discord, Client, bot) {
interaction.reply("Please DM Selmer bot to use this command!");
}
}
+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
}
+435
View File
@@ -0,0 +1,435 @@
const { Modal, TextInputComponent, MessageActionRow, MessageButton, MessageEmbed, Interaction } = require('discord.js');
/**
* @param {Interaction} interaction
*/
function postEmbd(bot, desc, interaction, page, isGuild, id, refered) {
try {
const author = {
name: "Selmer Bot",
url: "",
iconURL: bot.user.displayAvatarURL()
};
const newEmbed = new MessageEmbed()
.setTitle("REMINDERS")
.setAuthor(author)
.setDescription(desc[page])
.setFooter({ text: `Page ${page + 1}` });
const row = new MessageActionRow();
//Make sure the page is never < 1
const prevbtn = new MessageButton()
.setCustomId(`reminderQueue|${isGuild}-${id}|`)
.setLabel('⬅️')
.setStyle('SECONDARY')
if (page <= 0) {
prevbtn.customId += `0`;
// prevbtn.setCustomId(`reminderQueue|${isGuild}-${id}|0`);
prevbtn.setDisabled(true);
} else {
prevbtn.customId += `${page - 1}`;
}
const nextbtn = new MessageButton()
.setCustomId(`reminderQueue|${isGuild}-${id}|`)
.setLabel('➡️')
.setStyle('SECONDARY');
if ((page + 1) >= desc.length) {
nextbtn.customId += `${desc.length}`;
// nextbtn.setCustomId(`reminderQueue|`);
nextbtn.setDisabled(true);
} else {
nextbtn.customId += `${page + 1}`;
}
row.addComponents(prevbtn, nextbtn);
if (page > 0 || refered) {
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 https://selmerbot.com _', embeds: [newEmbed], components: [row] });
}
} catch (err) {
console.log(err);
return interaction.reply("Uh Oh! There's been an error!");
}
}
async function postForm(interaction, isGuild = false) {
// Create the modal
const modal = new Modal();
if (!isGuild) {
modal.setTitle('Creating a New Personal Reminder')
.setCustomId('newEventModal|user');
} else {
modal.setTitle('Creating a New Guild Reminder')
.setCustomId('newEventModal|guild');
}
// 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 nameInp = new TextInputComponent()
.setCustomId('name')
.setLabel("What is the Event's name?")
.setStyle('SHORT');
const descInp = new TextInputComponent()
.setCustomId('description')
.setLabel("What's the event's description?")
.setStyle('PARAGRAPH');
const dateInp = new TextInputComponent()
.setCustomId('date')
.setLabel("What's the event's date?")
.setPlaceholder('1/1/2020')
.setStyle('SHORT');
const timeInp = new TextInputComponent()
.setCustomId('time')
.setLabel("What's the event's time?")
.setPlaceholder("2:00 PM or 14:00")
.setStyle('SHORT');
const locurlinp = new TextInputComponent()
.setCustomId('locationwurl')
.setLabel("Where is the event happening?")
.setPlaceholder('To add a URL, simply use location;url (the seperator is a semi-colon)')
.setStyle('SHORT');
// An action row only holds one text input,
// so you need one action row per text input.
const name = new MessageActionRow().addComponents(nameInp);
const desc = new MessageActionRow().addComponents(descInp);
const date = new MessageActionRow().addComponents(dateInp);
const time = new MessageActionRow().addComponents(timeInp);
const offset = new MessageActionRow().addComponents(locurlinp);
// Add inputs to the modal
modal.addComponents(name, desc, date, time, offset);
// Show the modal to the user
interaction.showModal(modal);
}
//#region DATABASE PROCESSING
function addEvent(obj, connection, interaction, embd) {
try {
var Id;
if (obj.event.userId != null) { Id = obj.event.userId }
else { Id = obj.event.guildId; }
connection.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) {
if (doc.times.indexOf(obj.time) == -1) {
kbo.updateOne({ 'userId': Id }, { $push: { times: t } })
} else {
//Event already exists at this time
return interaction.reply("An event already exists at this time!");
}
} else {
doc = { userId: Id, times: [t] }
kbo.insertOne(doc);
}
//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 });
}).catch((err) => {
console.log("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!");
}
}).catch((err) => {
console.log("ERR");
console.error(err);
interaction.reply("Uh Oh! An error has occured!");
});
});
} catch (err) {
console.error(err);
return interaction.reply("Uh Oh! An error has occured!");
}
}
/**
* @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;
if (isGuild) {
guildId = id;
} else {
userId = id;
}
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) {
if (isExport) {
return reject([true, "No events exist!"]);
}
return interaction.reply("No events exist!");
}
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);
if (isExport) {
return reject([false, err]);
}
return interaction.reply("Uh Oh! There's been an error!");
}
});
});
}
//#endregion
//fields: [<name>, <description>, <date>, <time>, [offset], [url], [location]]
function processForm(bot, interaction) {
try {
var guildId = null;
var userId = null;
var isGuild = false;
if (interaction.customId.toLowerCase().indexOf('user') != -1) {
userId = interaction.user.id;
} else {
guildId = interaction.guildId;
isGuild = true;
}
//Get the values
const name = interaction.fields.getTextInputValue('name');
const desc = interaction.fields.getTextInputValue('description');
const date = new Date(interaction.fields.getTextInputValue('date'));
const timeTemp = interaction.fields.getTextInputValue('time');
const locurl = interaction.fields.getTextInputValue('locationwurl');
var loc = "N/A";
var url = "N/A";
if (locurl.indexOf(';') != -1) {
let temp = locurl.split(';');
loc = temp[0];
url = temp[1];
} else if (locurl.indexOf('http') != -1) {
//Set the URL
url = locurl;
} else if (locurl != "") {
//Set the location
loc = locurl;
}
//Process time
var timesplit = timeTemp.split(' ').filter((inp) => { return(inp.indexOf(':') != -1); });
if (timesplit.length == 0) { return interaction.reply("Please enter a date in one of the following formats: _2:00 PM or 14:00_"); }
timesplit = timesplit[0].split(":");
timesplit[0] = Number(timesplit[0]);
timesplit[1] = Number(timesplit[1]);
if (timeTemp.toLowerCase().indexOf('pm') != -1) { timesplit[0] += 12; }
//else if (timeTemp.toLowerCase().indexOf('am') != -1) {
date.setHours(timesplit[0]);
date.setMinutes(timesplit[1]);
const timeUTC = date.getTime();
//Make sure the reminder is at least 5 minutes into the future
var currentDate = new Date();
currentDate.setMinutes(currentDate.getMinutes() + 5);
if (currentDate.getTime() >= (timeUTC)) {
return interaction.reply("Please enter a date at least 5 minutes in the future!");
}
temp = `***${name}*** is coming up in <t:${timeUTC/1000}:R> on <t:${timeUTC/1000}:F>`;
const embd = new MessageEmbed()
.setAuthor({ name: "Selmer Bot", url: "", iconURL: bot.user.displayAvatarURL() })
.setTitle(temp)
.setDescription(`Description: ${desc}`)
.addFields(
{ name: 'Time', value: `<t:${timeUTC/1000}:F>` },
{ name: 'Location', value: `${loc}` },
{ name: 'Link', value: `${url}` },
{ name: 'Offset', value: `Available on Website` }
);
const obj = { time: timeUTC, event: { guildId: guildId, userId: userId, name: name, description: desc, offset: 0, link: url, location: loc } }
addEvent(obj, bot.mongoconnection, interaction, embd, isGuild);
} catch (err) {
console.error(err);
}
}
function modalHandle(bot, interaction) {
try {
//If the person selected "add" post the form
if (interaction.customId.indexOf('newEvent|User') != -1) {
if (interaction.user.id == interaction.customId.split('|')[2]) {
postForm(interaction);
}
} else if (interaction.customId == 'newEvent|Guild') {
postForm(interaction, true);
} else if (interaction.isModalSubmit()) {
//The user is submitting a form
processForm(bot, interaction);
} else if (interaction.customId == 'getEvents') {
if (interaction.channel.type === "DM") {
getEvents(bot, interaction, interaction.user.id, 0, false);
} else {
getEvents(bot, interaction, interaction.guildId, 0, true);
}
}
} catch (err) {
console.error(err);
}
}
// reminderQueue
function turnPage(bot, interaction) {
const isplit = interaction.customId.split('|');
const isGuild = (isplit[1].split('-')[0] === 'true');
const id = isplit[1].split('-')[1];
const page = Number(isplit[2]);
getEvents(bot, interaction, id, page, isGuild, true);
}
module.exports = {
name: "reminders",
description: "Have Selmer Bot remind you - premium feature",
execute(interaction, Discord, Client, bot) {
//Check if the user has premium
bot.mongoconnection.then(async (client) => {
const dbo = client.db('main').collection('authorized');
dbo.find({ discordID: interaction.user.id }).toArray((err, docs) => {
//Only available to Selmer Bot devs, testers and "authorized" users
if (docs[0] != undefined) {
//Execute the command
const row = new MessageActionRow()
if (interaction.channel.type == 'DM') {
row.addComponents(
new MessageButton()
.setCustomId(`newEvent|User|${interaction.user.id}`)
.setLabel('New Personal Reminder')
.setStyle('SUCCESS'),
new MessageButton()
.setCustomId('getEvents')
.setLabel('See Personal Reminders')
.setStyle('PRIMARY'),
);
} else {
row.addComponents(
new MessageButton()
.setCustomId(`newEvent|User|${interaction.user.id}`)
.setLabel('New Personal Reminder')
.setStyle('SUCCESS'),
new MessageButton()
.setCustomId('newEvent|Guild')
.setLabel('New Guild Reminder')
.setStyle('SUCCESS'),
new MessageButton()
.setCustomId('getEvents')
.setLabel('See Guild Reminders')
.setStyle('PRIMARY'),
);
}
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, addEvent, getEvents,
options: []
}
@@ -1,11 +1,13 @@
/*
-----WEBHOOKS ARE RECIEVED AND MONITORED HERE-----
https://glitch.com/edit/#!/selmer-bot-listener
-----WEBHOOKS ARE MONITORED AND PROCESSED HERE-----
https://selmer-bot-listener.ion606.repl.co
--------------------------------------------------
*/
//@ts-check
const { MongoClient, ServerApiVersion } = require('mongodb');
const { MessageActionRow, MessageSelectMenu } = require('discord.js');
const { MessageActionRow, MessageSelectMenu, Constants } = require('discord.js');
const { addComplaintButton } = require('../dev only/submitcomplaint');
//Called from the dropdown menu
@@ -13,20 +15,20 @@ async function createSubscriptionManual(bot, interaction, id, priceID) {
const stripe = bot.stripe;
const mongouri = bot.mongouri;
//Start Error Checking
//Error Checking (unlikely, but just in case)
if (!id) { console.log('....What? How?'); return interaction.editReply("Uh oh, something happened with the Stripe Discord ID check, please contact support!"); }
const client = new MongoClient(mongouri, { useNewUrlParser: true, useUnifiedTopology: true, serverApi: ServerApiVersion.v1 });
// const client = new MongoClient(mongouri, { useNewUrlParser: true, useUnifiedTopology: true, serverApi: ServerApiVersion.v1 });
new Promise(async function(resolve, reject) {
client.connect(async (err) => {
if (err) { return console.log(err); }
bot.mongoconnection.then(async (client) => {
// if (err) { return console.log(err); }
const dbo = client.db('main').collection('authorized');
await dbo.findOne({'discordID': id}).then(async (doc) => {
var userID;
if (doc != undefined) {
client.close();
// client.close();
reject(`An account with the tag <@${id}> already exists!`);
} else {
@@ -36,7 +38,7 @@ async function createSubscriptionManual(bot, interaction, id, priceID) {
userID = stripeUser.id;
//Add to the database (I have to wait for the insertion)
await dbo.insertOne({stripeID: userID, discordID: id, paid: false, startDateUTC: null, tier: 0}).then(() => { client.close(); resolve(userID); });
await dbo.insertOne({stripeID: userID, discordID: id, paid: false, startDateUTC: null, tier: 0}).then(() => { /*client.close();*/ resolve(userID); });
}
});
});
@@ -66,22 +68,30 @@ async function createSubscriptionManual(bot, interaction, id, priceID) {
});
interaction.editReply(session.url);
}).catch((err) => { interaction.editReply(err); })
}).catch((err) => {
if (String(typeof(err)) == 'string') {
interaction.editReply(err);
} else {
console.log(err);
interaction.editReply("A Stripe error occured! Please click the ✅ to report this ASAP!");
addComplaintButton(bot, interaction.message);
}
});
}
async function changeSubscriptionManual(bot, message) {
async function changeSubscriptionManual(bot, interaction) {
const stripe = bot.stripe;
const mongouri = bot.mongouri;
const id = message.author.id;
const id = interaction.user.id;
//Start Error Checking
//Just in case
if (!id) { return console.log('....What? How?'); }
const client = new MongoClient(mongouri, { useNewUrlParser: true, useUnifiedTopology: true, serverApi: ServerApiVersion.v1 });
// const client = new MongoClient(mongouri, { useNewUrlParser: true, useUnifiedTopology: true, serverApi: ServerApiVersion.v1 });
new Promise(async function(resolve, reject) {
client.connect(async (err) => {
if (err) { return console.log(err); }
bot.mongoconnection.then(async (client) => {
// if (err) { return console.log(err); }
const dbo = client.db('main').collection('authorized');
await dbo.findOne({'discordID': id}).then(async (doc) => {
@@ -89,33 +99,36 @@ async function changeSubscriptionManual(bot, message) {
if (doc != undefined) {
userID = doc.stripeID;
client.close();
// client.close();
resolve(userID);
} else {
client.close();
reject(`No user with the ID of <@${message.author.id}>`);
// client.close();
reject(`No user with the ID of <@${interaction.user.id}>`);
}
});
});
}).then(async (userID) => {
const session = await stripe.billingPortal.sessions.create({
await stripe.billingPortal.sessions.create({
customer: userID,
return_url: "https://linktr.ee/selmerbot",
});
message.reply(session.url);
// console.log(session.url);
}).then((session) => {
interaction.reply(session.url);
})
}).catch((err) => {
message.reply(err);
// console.log(err);
if (String(typeof(err)) == 'string') {
interaction.reply(err);
} else {
console.log(err);
interaction.reply("A Stripe error occured! Please click the ✅ to report this ASAP!");
addComplaintButton(bot, interaction.message); //?????????
}
});
}
function createDropDown(bot, message) {
// const stripe = bot.stripe;
function createDropDown(bot, interaction) {
const stripe = bot.stripe;
const pl = [];
@@ -143,33 +156,37 @@ function createDropDown(bot, message) {
const row = new MessageActionRow()
.addComponents(
new MessageSelectMenu()
.setCustomId(`${message.author.id}|premium`)
.setCustomId(`${interaction.user.id}|premium`)
.setPlaceholder('Nothing selected')
.addOptions(vl)
);
message.channel.send({ content: `Please choose a tier`, components: [row] });
interaction.reply({ content: `Please choose a tier`, components: [row], ephemeral: true });
});
});
}
function handleInp(bot, message) {
if (message.content == '!premium help') {
message.reply('Use _!premium buy_ to get premium or use _!premium manage_ to change or cancel your subscription\n_Disclaimer: Selmer Bot uses Stripe to manage payments. Read more at *https://stripe.com/ *_');
} else if (message.content == '!premium buy') {
createDropDown(bot, message);
} else if (message.content == '!premium manage') {
changeSubscriptionManual(bot, message)
function handleInp(bot, interaction) {
const inp = interaction.options.data[0];
if (!inp || inp.value == 'help') {
interaction.reply({content: 'Use _!premium buy_ to get premium or use _!premium manage_ to change or cancel your subscription\n\n_Disclaimer: Selmer Bot uses Stripe to manage payments. Read more at *https://stripe.com/ *_', ephemeral: true});
} else if (inp.value == 'buy') {
createDropDown(bot, interaction);
} else if (inp.value == 'manage') {
changeSubscriptionManual(bot, interaction);
}
}
module.exports = {
name: 'premium',
description: 'everything payment',
execute(message, args, Discord, Client, bot) {
message.reply("Please DM Selmer bot to use this command!");
}, handleInp, createSubscriptionManual
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
}
+4
View File
@@ -0,0 +1,4 @@
/* TODO
1) Save by streamer id, with the users to ping inside
2) Check the streamers every 5 min or so
*/
+26
View File
@@ -0,0 +1,26 @@
/**
* Check if the user has a premium subscription
* @param {*} bot
* @param {String} userId
* @returns {Promise<Boolean>}
*/
function verPremium(bot, userId) {
return new Promise((resolve, reject) => {
const member = bot.guilds.cache.get(bot.home_server).members.cache.get(userId);
bot.mongoconnection.then(async (client) => {
const dbo = client.db('main').collection('authorized');
dbo.findOne({ discordID: userId }).then((doc) => {
//Only available to Selmer Bot devs, testers and "authorized" users
if (doc != undefined || member && (member.roles.cache.has('944048889038774302') || member.roles.cache.has('946610800418762792'))) {
resolve(true);
} else {
reject("You have to be a premium subscriber to use this feature!\n_support coming soon_");
}
});
});
});
}
module.exports = { verPremium }
@@ -17,15 +17,12 @@ function checkResponses(convoOG, answer) {
if (b === 'pay') {
//Exctract the number
var amt = convoOG.match(/(\d+)/)[0];
// var amt = convoOG.match(/(\d+)/)[0];
// currency = convoOG[convoOG.indexOf(amt) - 1];
if (matches) {
currency = convoOG[convoOG.indexOf(amt) - 1];
//Do something with pay API to get the amount here
}
return ('Use _!premium_ to get Selmer Bot Premium now!');
} else if (b == 'name') {
return 'My name is Selmer Bot!';
return ('My name is Selmer Bot!');
} else { return null; }
return b;
+1
View File
@@ -0,0 +1 @@
//TODO -- Maybe use https://console.cloud.google.com/apis/api/youtube.googleapis.com
+1 -1
View File
@@ -56,7 +56,7 @@ function changeTurn(client, bot, interaction) {
}
const other_dbo = client.db(interaction.member.guild.id + '[ECON]').collection(id);
const other_dbo = client.db(interaction.member.guild.id).collection(id);
other_dbo.find({'state': {$exists: true}}).toArray((err, docs) => {
//If the person was prone, skip their turn
+75
View File
@@ -0,0 +1,75 @@
/**
* @param {JSON} inp
* @returns {Map<String, Map>}
*/
function jsonToMapRecursive(inp) {
if (typeof(inp) != 'object') {
return inp;
}
let m2 = new Map();
Object.entries(inp).forEach((key) => { m2.set(key[0], jsonToMapRecursive(inp[key[0]])); });
return m2;
}
/**
*
* @param {Map} inp
* @param {int} layer
* @returns The map in string format
*
* @example
* {"key1": "val1", "key2": "key3": {"key4": "Val4"}} ==>>
* `
* |-- key1
* | |-- val1
* |
* |-- key2
* | |-- val3
* | | |-- key4
* | | | |--val4
* `
*/
function mapToTableRecursive(inp, layer = 1) {
var temp = '';
if (typeof(inp) != 'object') {
// return `?[${inp}]`;
return '';
}
Array.from(inp.keys()).forEach((key) => {
var keyTemp = ('| ').repeat(layer);
temp += `${keyTemp}- - ${key}\n`.replaceAll(' - -', '- -');
temp += mapToTable(inp.get(key), layer + 1);
});
temp += ('| ').repeat(layer - 1) + '\n';
if (layer == 1) {
var links = new Array();
//Post-processing
var l = temp.split('\n')
l = l.filter((entry, ind) => {
return entry.trim() == '|' || !((/[^A-Za-z0-9 ]+$/).test(entry.trim()) && (/[^A-Za-z0-9 ]+$/).test(l[ind + 1].trim()));
});
temp = l.join('\n')
//Get the links
Array.from(inp.keys()).forEach((key) => {
links.push(key);
});
return [temp, links];
}
return temp;
}
module.exports = { jsonToMapRecursive, mapToTableRecursive }
+44
View File
@@ -0,0 +1,44 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
<title>Selmer Bot Directory</title>
<style>
body {
background-color: black;
}
.webBtn {
margin: 50px;
font-family:'Comic Sans MS';
font-size:20px;
line-height:20px;
color:#ffffff;
background-color:#4d4d4d;
padding:20px;
border-radius:10px;
}
.webBtn:hover {
color: #000000;
background-color: #9b9999;
}
</style>
</head>
<body>
<div class="row">
<div style="text-align: center;">
<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>
<div style="text-align: center; position: absolute; bottom: 5px; color: white;">Copyright @ION606 2022</div>
</body>
</html>
+290 -93
View File
@@ -1,6 +1,7 @@
const { Client, Intents, MessageActionRow, MessageButton, MessageSelectMenu } = require('discord.js');
//#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");
@@ -8,9 +9,17 @@ 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/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;
@@ -20,22 +29,30 @@ const BASE_LVL_XP = 20;
let token;
let IDM = false;
let home_server;
let debug_channel;
let MLAIKEY;
let StripeAPIKey;
let youtubeAPIKey;
if (process.env.token != undefined) {
//Use "setx NAME VALUE" in the local powershell terminal to set
token = process.env.token;
home_server = process.env.home_server;
debug_channel = process.env.debug_channel;
MLAIKEY = process.env.MLAIKEY;
StripeAPIKey = process.env.StripeAPIKey;
youtubeAPIKey = process.env.youtubeAPIKey;
} else {
token = require('./config.json').token;
home_server = require('./config.json').home_server;
IDM = true;
debug_channel = require('./config.json').debug_channel;
MLAIKEY = require('./config.json').MLAIKEY;
StripeAPIKey = require('./config.json').StripeAPIKey;
youtubeAPIKey = require('./config.json').youtubeAPIKey;
IDM = token.startsWith("OTI2NT");
}
//#endregion
@@ -61,6 +78,9 @@ bot.prefix = new String;
bot.prefix = prefix;
bot.inDebugMode = IDM;
bot.home_server = home_server;
bot.debug_channel = debug_channel;
bot.inviteLink = 'https://discord.com/oauth2/authorize?client_id=944046902415093760&scope=applications.commands+bot&permissions=549755289087';
bot.youtubeAPIKey = youtubeAPIKey;
const configuration = new Configuration({
apiKey: MLAIKEY,
@@ -69,6 +89,10 @@ bot.openai = new OpenAIApi(configuration);
bot.temptext = '';
bot.stripe = Stripe(StripeAPIKey);
//The first thing will be an audioPlayer(), the second a queue
bot.audioData = new Map();
bot.lockedChannels = new Map();
//#region MongoDB integration
//Development support
@@ -76,21 +100,77 @@ let mongouritemp;
if (process.env.MONGODB_URI) {
mongouritemp = process.env.MONGODB_URI;
} else {
mongouritemp = require('./config.json');
mongouritemp = require('./config.json').mongooseURI;
}
const mongouri = mongouritemp;
bot.mongouri = mongouri;
const { connect } = require('mongoose');
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
//#region PROCESS STUFF
loadBotBackups(bot, IDM);
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
//#region set up bot commands
// const commandFiles = fs.readdirSync('./commands/').filter(file => file.endsWith('.js')); // Obsolete?
bot.commands = new Discord.Collection();
const forbiddenFolders = ['db', 'API', 'dev only'];
const forbiddenFolders = ['db', 'dev only']; //premium,
fs.readdirSync('./commands')
.forEach(dir => {
@@ -99,7 +179,9 @@ fs.readdirSync('./commands')
.filter(file => file.endsWith('.js'))
.forEach(file => {
const command = require(`./commands/${dir}/${file}`);
bot.commands.set(command.name, command);
if (command.name && command.description) {
bot.commands.set(command.name.toLowerCase(), command);
}
});
}
});
@@ -108,164 +190,279 @@ 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);
//Everything in the API should be handled by specific handler functions
const chat = require('./commands/API/chat.js');
bot.commands.set('chat', chat);
const stripeCommands = require('./commands/API/stripe.js');
bot.commands.set('premium', stripeCommands);
// const chat = require('./commands/premium/chat.js');
// bot.commands.set('chat', chat);
// const stripeCommands = require('./commands/premium/stripe.js');
// bot.commands.set('premium', stripeCommands);
// const
// bot.commands.set('RSS', )
//#endregion
//#region bot.[anything] section
//XP Table section
let xp_collection = new Map();
let items;
var botIsReady = bot.inDebugMode;
bot.on('ready', async () => {
//Make then copy the shop
const client = new MongoClient(mongouri, { useNewUrlParser: true, useUnifiedTopology: true, serverApi: ServerApiVersion.v1 });
client.connect(err => {
const shop = client.db("main").collection("shop");
shop.find().toArray(function(err, itemstemp) {
if (err) throw err;
const startTime = new Date().getTime();
bot.user.setPresence({ activities: [{ name: 'Booting up, please hold!', type: "PLAYING" }], status: 'idle' });
items = [...itemstemp];
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;
client.close();
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 => {
handle_interaction(interaction, mongouri, turnManager, bot, STATE, items, xp_collection);
const { commandName } = interaction;
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 (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 (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!");
}
} else {
handle_interaction(interaction, mongouri, turnManager, bot, STATE, items, xp_collection);
}
});
//Add the bot to a server setup
bot.on("guildCreate", guild => {
guild.roles.create({ name: 'Selmer Bot Mod' });
if (guild.roles.cache.find((role) => { return (role.name == 'Selmer Bot Commands'); }) == undefined) {
guild.roles.create({ name: 'Selmer Bot Commands' });
}
if (guild.roles.cache.find((role) => { return (role.name == 'Selmer Bot Calendar'); }) == undefined) {
guild.roles.create({ name: 'Selmer Bot Calendar' });
}
//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 Mod_" 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
const client = new MongoClient(mongouri, { useNewUrlParser: true, useUnifiedTopology: true, serverApi: ServerApiVersion.v1 });
client.connect(err => {
if (err) { return console.log(err); }
bot.mongoconnection.then(client => {
const dbo = client.db(guild.id).collection('SETUP');
dbo.insertMany([{_id: 'WELCOME', 'welcomechannel': null, 'welcomemessage': null, 'welcomebanner': 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"}]);
});
});
client.close();
bot.on("guildDelete", guild => {
bot.mongoconnection.then((client) => {
//Insufficient Permission????
// db.dropDatabase();
try {
const db = client.db(guild.id);
db.listCollections().forEach(function(x) { db.collection(x.name).drop(); });
var times;
const dbo = client.db('main').collection('reminderKeys');
//ReminderKeys are all stored as userId, the reminders themselves are not
dbo.findOne({userId: guild.id}).then((doc) => {
if (!doc || !doc.times) { return; }
times = doc.times;
const tbo = client.db('main').collection('reminders');
tbo.find({time: {$in: times}}).toArray((err, docs) => {
try {
for (let i = 0; i < docs.length; i ++) {
for (let j in docs[i]) {
if (!isNaN(j) && (docs[i][j].guildId == guild.id)) {
delete docs[i][j];
docs[i].amt --;
}
}
if (docs.amt > 0) {
tbo.replaceOne({ time: docs[i].time }, docs[i]);
} else {
tbo.deleteOne({ time: docs[i].time });
}
}
} catch (err) {
console.error(err);
}
});
});
dbo.deleteOne({ userId: guild.id });
} catch (err) {
console.error(err);
}
})
});
//Welcome new members
bot.on('guildMemberAdd', async (member) => {
//Check for impartial data
if(member.partial) await member.fetch();
if (member.guild.id == bot.home_server && !bot.inDebugMode) { return; }
const client = new MongoClient(mongouri, { useNewUrlParser: true, useUnifiedTopology: true, serverApi: ServerApiVersion.v1 });
//Check for impartial data
if (member.partial) { member = await member.fetch(); }
const guild = bot.guilds.cache.get(member.guild.id);
client.connect(err => {
bot.mongoconnection.then(client => {
const dbo = client.db(member.guild.id).collection('SETUP');
dbo.find({_id: 'WELCOME'}).toArray(async (err, docs) => {
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");
});
})
});
bot.on('messageCreate', (message) => {
//DM SECTION
if (message.channel.type === "DM") {
return handle_dm(message, bot);
}
//Special case, testing server (still need the emojis)
if (!bot.inDebugMode && message.guild.id == bot.home_server) { return; }
//COMMAND AREA
//Check if the prefix exists
if (!message.content.startsWith(prefix) || message.author.bot) return;
const args = message.content.slice(prefix.length).split(' ');
const command = args.shift().toLowerCase();
//Performes the command
//Admin section
if (command == 'reactionrole') { bot.commands.get(command).execute(message, args, Discord, bot); }
else if(bot.commands.has(command) && command != 'ECON') {
//Database access is required, change the inputs
if (command == 'game' || command == 'accept' || command == 'setup') {
bot.commands.get(command).execute(bot, message, args, command, Discord, mongouri, items, xp_collection);
} else {
bot.commands.get(command).execute(message, args, Discord, Client, bot);
} else if (message.content.indexOf('!spam_collection') != -1) {
//Handle spam collection/Dev commands
return devCheck(message, bot);
} else if (message.type === "CHANNEL_PINNED_MESSAGE") {
//Debug log stuff
if (message.guild.id == bot.home_server && message.channel.id == bot.debug_channel) {
message.delete();
}
}
//Econ and also the catch statement
else { bot.commands.get('econ').execute(bot, message, args, command, Discord, mongouri, items, xp_collection); }
})
//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.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(' ');
const command = args.shift().toLowerCase();
if (command == 'game' || command == 'accept') {
bot.commands.get(command).execute(bot, message, args, command, Discord, mongouri, items, xp_collection);
} 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);
}
});
//#endregion
+2
View File
@@ -1 +1,3 @@
VS stuff: https://dbotmaker.io/forums/threads/make-bot-join-vc.95/
selmer-bot-listener watcher https://uptimerobot.com/dashboard
+4365 -1304
View File
File diff suppressed because it is too large Load Diff
+23 -4
View File
@@ -1,28 +1,44 @@
{
"dependencies": {
"@alpacahq/alpaca-trade-api": "^2.16.1",
"@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",
"canvas": "^2.9.1",
"body-parser": "^1.20.0",
"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",
"discord.js": "^13.6.0",
"dotenv": "^16.0.1",
"express": "^4.18.1",
"feedparser": "^2.2.10",
"ffmpeg": "^0.0.4",
"ffmpeg-static": "^5.0.0",
"googleapis": "^107.0.0",
"hastebin-gen": "^2.0.5",
"html-entities": "^2.3.3",
"libsodium-wrappers": "^0.7.10",
"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",
"random-memes": "^3.1.0",
"pusher": "^5.1.1-beta",
"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",
@@ -47,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"
}
}
+150
View File
@@ -0,0 +1,150 @@
const {Client, Constants} = require('discord.js');
/**
* Registers all slash commands
* @param {Client} bot
*/
function registerCommands(bot) {
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]
},
key: code
*/
//#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 {
// 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);
// });
}
//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
},
]
});
//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
});
}
module.exports = { registerCommands }
+24
View File
@@ -0,0 +1,24 @@
{
"News": {
"NYTimes": {
"world": "https://rss.nytimes.com/services/xml/rss/nyt/World.xml",
"US": "https://rss.nytimes.com/services/xml/rss/nyt/HomePage.xml",
"econ": "https://rss.nytimes.com/services/xml/rss/nyt/Economy.xml",
"business": "https://rss.nytimes.com/services/xml/rss/nyt/Business.xml",
"tech": "https://rss.nytimes.com/services/xml/rss/nyt/Technology.xml",
"obituaries": "https://rss.nytimes.com/services/xml/rss/nyt/Obituaries.xml"
},
"Fox": {
"LatestHeadlines": "https://moxie.foxnews.com/feedburner/latest.xml",
"World": "https://moxie.foxnews.com/feedburner/world.xml",
"US": "https://moxie.foxnews.com/feedburner/national.xml",
"Tech": "https://moxie.foxnews.com/feedburner/scitech.xml",
"Politics": "https://moxie.foxnews.com/feedburner/politics.xml"
},
"NPR": "https://feeds.npr.org/1001/rss.xml"
},
"Crime": {
"DateLine_NBC": "https://podcastfeeds.nbcnews.com/HL4TzgYC",
"CrimeJunkie": "https://feeds.simplecast.com/qm_9xx0g"
}
}
+322
View File
@@ -0,0 +1,322 @@
const { MessageActionRow, MessageButton, MessageEmbed, MessageSelectMenu, CommandInteractionOptionResolver } = require('discord.js');
const axios = require('axios');
const cheerio = require('cheerio');
var FeedParser = require('feedparser');
const fetch = require('node-fetch');
const { VoiceConnectionStatus, AudioPlayerStatus, createAudioPlayer, StreamType, joinVoiceChannel, createAudioResource, getVoiceConnection } = require('@discordjs/voice');
const play = require('play-dl');
const { addComplaintButton } = require('../../commands/dev only/spam_collection.js');
const hastebin = require("hastebin-gen");
const { simpleCast } = require('./simplecast.js')
let Parser = require('rss-parser');
let parser = new Parser();
const allFeedsJSON = require('./feeds.json');
/**
* @param {JSON} inp
* @returns {Map<String, Map>}
*/
function jsonToMapRecursive(inp) {
if (typeof(inp) != 'object') {
return inp;
}
let m2 = new Map();
Object.entries(inp).forEach((key) => { m2.set(key[0], jsonToMapRecursive(inp[key[0]])); });
return m2;
}
const allFeeds = jsonToMapRecursive(allFeedsJSON);
function mapToTable(inp, layer) {
var temp = '';
if (typeof(inp) != 'object') {
// return `?[${inp}]`;
return '';
}
Array.from(inp.keys()).forEach((key) => {
var keyTemp = ('| ').repeat(layer);
temp += `${keyTemp}- - ${key}\n`.replaceAll(' - -', '- -');
temp += mapToTable(inp.get(key), layer + 1);
});
temp += ('| ').repeat(layer - 1) + '\n';
if (layer == 1) {
var links = new Array();
//Post-processing
var l = temp.split('\n')
l = l.filter((entry, ind) => {
return entry.trim() == '|' || !((/[^A-Za-z0-9 ]+$/).test(entry.trim()) && (/[^A-Za-z0-9 ]+$/).test(l[ind + 1].trim()));
});
temp = l.join('\n')
//Get the links
Array.from(inp.keys()).forEach((key) => {
links.push(key);
});
return [temp, links];
}
return temp;
}
/**
*
* @param {simpleCast} obj
* @returns
*/
function playAudio(bot, message, user, obj) {
const member = message.guild.members.cache.get(user.id);
if (!member.voice.channel) {
message.reply("Please join a voice channel before you try this!");
return;
}
const channel = bot.channels.cache.get(member.voice.channel.id);
const connection = joinVoiceChannel({
channelId: channel.id,
guildId: channel.guild.id,
adapterCreator: channel.guild.voiceAdapterCreator,
});
const resource = createAudioResource(obj.audioLink /*, { inlineVolume: true }*/ );
const data = bot.audioData.get(message.channel.guild.id);
if (data && data[1]) {
return message.reply("No podcast queue support yet!");
}
const player = createAudioPlayer();
connection.subscribe(player);
bot.audioData.set(message.guild.id, [player, new Array(), null]);
player.play(resource);
//Create the embed
const newEmbed = new MessageEmbed()
.setColor('#0F00F0')
.setTitle(`${obj.title}`)
.setAuthor({ name: "Selmer Bot", url: "", iconURL: bot.user.displayAvatarURL() })
.setDescription('IS NOW PLAYING')
.setURL(obj.url)
.setThumbnail(obj.thumbnal);
const row = new MessageActionRow()
.addComponents(
new MessageButton()
.setCustomId('PAUSE')
.setLabel('⏸️')
.setStyle('SECONDARY'),
new MessageButton()
.setCustomId('STOP')
.setLabel('⏹️')
.setStyle('SECONDARY'),
// new MessageButton()
// .setCustomId('SKIP')
// .setLabel('⏭️')
// .setStyle('SECONDARY')
);
console.log(obj.audioLink);
const m = message.reply({ embeds: [newEmbed], components: [row] });
}
async function getAndFormatRSS(bot, message, user, inp) {
message.reply(inp).catch((err) => { message.channel.send(inp); console.error(err); });
var req = fetch(inp)
const feedparser = new FeedParser();
req.then(function (res) {
if (res.status !== 200) {
throw new Error('Bad status code');
}
else {
// The response `body` -- res.body -- is a stream
res.body.pipe(feedparser);
}
}, function (err) {
// handle any request errors
});
feedparser.on('error', function (error) {
// always handle errors
addComplaintButton(bot, message);
console.log(error);
});
const items = new Array();
feedparser.on('readable', async function () {
// This is where the action is!
var stream = this; // `this` is `feedparser`, which is a stream
var meta = this.meta; // **NOTE** the "meta" is always available in the context of the feedparser instance
var item;
let i = 0;
while (item = stream.read()) {
items.push(item);
i ++;
if (i >= 100) { break; }
}
});
feedparser.addListener('end', () => {
const item = items[Math.round(Math.random() * items.length)];
// return console.log(item);
if (inp.indexOf('simplecast') != -1) {
var s = new simpleCast(item, inp);
// s.audioLink = 'https://download.samplelib.com/mp3/sample-15s.mp3';
playAudio(bot, message, user, s);
}
});
return;
//Get the feed
const feed = await parser.parseURL(inp);
// const items = feed.items;
const item = items[Math.round(Math.random() * items.length)];
var url;
try {
url = item.link || item.guid;
axios(url).then(async response => {
const html = response.data;
const $ = cheerio.load(html);
// console.log(html);
const haste = await hastebin(html, { extension: "txt" });
console.log(url, '\n', haste);
})
} catch (err) {
console.log(err);
return console.log(item);
}
// const newEmbed = new MessageEmbed()
// .setTitle(feed.title)
// .setAuthor({ name: "Selmer Bot", url: "", iconURL: bot.user.displayAvatarURL() })
// .setTimestamp()
// console.log(feed.items);
/*
creator?: 'Ben Casselman and Jeanna Smialek',
title: 'Income and Spending Rose Less Than Prices in May',
link: 'https://www.nytimes.com/2022/06/30/business/economy/income-spending-may.html',
*/
}
function presentFeeds(bot, message, commands, interaction) {
var r;
var url;
if (commands[0] == 'all') {
r = mapToTable(allFeeds, 1);
} else {
var r2 = allFeeds.get(commands[0]);
// commands = commands.slice(1);
commands.slice(1).forEach((key) => {
r2 = r2.get(key);
});
r = mapToTable(r2, 1);
if(!r[1]) { url = r2; }
//Array.from(r2.keys())
}
//Check if we have a feed (no more paths)
if (!r[1]) {
// console.log("Commands:", commands, "\nR: ", r); throw 1;
let path = ''
commands.forEach((com) => { path += `${com} --> `});
path = path.slice(0, path.length - 5);
interaction.update({ content: `You have chosen ${path}!`, components: []});
return getAndFormatRSS(bot, interaction.message, interaction.user, url);
}
var keyList = new Array();
r[1].forEach((key) => {
const listEntry = {
label: `${key}`,
description: `Choose a feed from this category!`,
value: `${key}`,
}
keyList.push(listEntry);
});
const row = new MessageActionRow()
.setComponents(
new MessageSelectMenu()
.setCustomId(`RSS|${commands.join('|')}`)
.setPlaceholder('Nothing selected')
.addOptions(keyList)
)
if (interaction) { return interaction.update({content: r[0], components: [ row ]}); }
message.reply({ content: r[0], components: [ row ] });
/*else if (commands[0].length = 1) {
const data = allFeeds.get(commands[0]).get(commands[1]).get(commands[2]);
interaction.update({ content: `You have chosen ${commands[1]} from the ${commands[0]} section!`, components: []});
return getAndFormatRSS(bot, interaction.message, data);
}*/
}
function RSSInteractionHandler(bot, interaction) {
let commands = interaction.values[0].split('|');
let temp = interaction.customId.split('|').slice(1);
if (temp[0] != 'all') { commands = temp.concat(commands); }
// console.log(interaction.customId, interaction.values);
presentFeeds(bot, null, commands, interaction);
}
// getAndFormatRSS(null, null, imp[0]);
// presentFeeds(null, ['all']);
module.exports = {
name: 'RSS',
description: 'Selmer Bot will present a list of RSS feeds to read from *EXPERAMENTAL*',
execute(message, args, Discord, Client, bot) {
if (!bot.inDebugMode) { return message.reply('Command under development!'); }
if (!args[0]) {
presentFeeds(bot, message, [ 'all' ], null);
}
},
RSSInteractionHandler
}
/*
REMOVED
"ABC": "https://abcnews.go.com/abcnews/topstories"
"FBI": "https://www.fbi.gov/feeds/national-press-releases/rss.xml" (Uhhhh......maybe I should't use this one.....)
*/
+23
View File
@@ -0,0 +1,23 @@
class simpleCast {
constructor(data, url) {
this.title = data.title;
if (data['itunes:summary']) {
this.description = data['itunes:summary']['#'];
} else {
//<p>......<p>
this.description = data.description.substring(3, s.indexOf('<p>', 4));
}
var audio = data.enclosures.filter((entry) => { return (entry.type.indexOf('audio') != -1) });
if (audio.length > 0) {
this.audioLink = audio[0].url;
} else { console.log("What?"); }
this.url = url;
this.thumbnal = data.meta.image.url;
}
}
module.exports = { simpleCast }