diff --git a/.envdev b/.envdev index ca72adb..2690805 100644 --- a/.envdev +++ b/.envdev @@ -30,12 +30,19 @@ PREFIX="!" # Replace them with your own on a sandbox server! SANCTUM_ID="454425466821017611" +# Discord setup (Configred based on the server) +ADMIN_ROLE="Overseer" + # Role IDs (A = Obsidian, B = Genesis, C = Hand) # Replace them with your own on a sandbox server! GROUP_A_ROLE="470656524042371072" GROUP_B_ROLE="470668082441093121" GROUP_C_ROLE="470656609295794199" +GROUP_A_NAME="Obsidian Technologies" +GROUP_B_NAME="Genesis Command" +GROUP_C_NAME="The Hand" + # Channel IDs # Replace them with your own on a sandbox server! CODEX_CHANNEL_ID="457580831284789249" diff --git a/Shared/factions.js b/Shared/factions.js new file mode 100644 index 0000000..e7a5730 --- /dev/null +++ b/Shared/factions.js @@ -0,0 +1,100 @@ +//initialize the exports +exports = module.exports = {}; + +//GetFactionName +//factionID - the discord role ID of the faction +exports.GetFactionName = function(factionID) { + switch(factionID) { + case process.env.GROUP_A_ROLE: + return process.env.GROUP_A_NAME; + case process.env.GROUP_B_ROLE: + return process.env.GROUP_B_NAME; + case process.env.GROUP_C_ROLE: + return process.env.GROUP_C_NAME; + default: + return "Unknown"; + } +} + +//GetFactionChannel +//user - discord.js user +exports.GetFactionChannel = function(user) { + if (user.roles.has(process.env.GROUP_A_ROLE)) { + return process.env.GROUP_A_BOT_ID; + } + if (user.roles.has(process.env.GROUP_B_ROLE)) { + return process.env.GROUP_B_BOT_ID; + } + if (user.roles.has(process.env.GROUP_C_ROLE)) { + return process.env.GROUP_C_BOT_ID; + } + return null; +} + +/* +// Change Faction +exports.ChangeFaction = async function(factionID, channelID, userID, member, botChannelID) { + if (member.roles.has(factionID)) { + if (factionID === process.env.GROUP_A_ROLE) + sendMessage(channelID, dialog.getDialog("orderAlreadyJoined", userID)); + else if (factionID === process.env.GROUP_B_ROLE) + sendMessage(channelID, dialog.getDialog("anarchyAlreadyJoined", userID)); + else if (factionID === process.env.GROUP_C_ROLE) + sendMessage(channelID, dialog.getDialog("religionAlreadyJoined", userID)); + } else { + if (dataRequest.loadServerData("hasConvertedToday", userID) == 1) { + sendMessage(channelID, dialog.getDialog("alreadyConvertedToday", userID)); + } else { + // Creates new user + var response = String(dataRequest.sendServerData("newUser", "New user.", userID)); + + //var response = "createdUser" + // Obsidian Tech. + if (factionID === process.env.GROUP_A_ROLE) { + await member.removeRole(process.env.GROUP_B_ROLE); + await member.removeRole(process.env.GROUP_C_ROLE); + await member.addRole(process.env.GROUP_A_ROLE); + + dataRequest.sendServerData("conversion", "Converted to The Order.", userID); + + if (response == "createdUser") { + client.users.get(userID).send(dialog.getDialog("newUserPM", userID, getFactionName(factionID))); + sendMessage(botChannelID, dialog.getDialog("newUserWelcome", userID, `<#${getFactionName(factionID)}>`)); + } else { + sendMessage(channelID, dialog.getDialog("orderJoin", userID)); + } + + // Genesis Command + } else if (factionID === process.env.GROUP_B_ROLE) { + await member.removeRole(process.env.GROUP_C_ROLE); + await member.removeRole(process.env.GROUP_A_ROLE); + await member.addRole(process.env.GROUP_B_ROLE); + + dataRequest.sendServerData("conversion", "Converted to the Anarchy.", userID); + + if (response == "createdUser") { + client.users.get(userID).send(dialog.getDialog("newUserPM", userID, getFactionName(factionID))); + sendMessage(botChannelID, dialog.getDialog("newUserWelcome", userID, `<#${getFactionName(factionID)}>`)); + } else { + sendMessage(channelID, dialog.getDialog("anarchyJoin", userID)); + } + + // The Hand + } else if (factionID === process.env.GROUP_C_ROLE) { + await member.removeRole(process.env.GROUP_A_ROLE); + await member.removeRole(process.env.GROUP_B_ROLE); + await member.addRole(process.env.GROUP_C_ROLE); + + dataRequest.sendServerData("conversion", "Converted to The Religion.", userID); + + if (response == "createdUser") { + client.users.get(userID).send(dialog.getDialog("newUserPM", userID, getFactionName(factionID))); + sendMessage(botChannelID, dialog.getDialog("newUserWelcome", userID, `<#${getFactionName(factionID)}>`)); + } else { + sendMessage(channelID, dialog.getDialog("religionJoin", userID)); + } + } + } + } +} +*/ \ No newline at end of file diff --git a/Shared/inventory.js b/Shared/inventory.js new file mode 100644 index 0000000..4be4426 --- /dev/null +++ b/Shared/inventory.js @@ -0,0 +1,211 @@ +//initialize the exports +exports = module.exports = {}; + +/* +// Adds item to inventory +exports.AddItem = function(userID, item, amount) { + // If item exists in inventory + console.log("[Add Item] " + amount); + var i = userItems.findIndex(i => i.name === item.name); + console.log("[Item Amount Item Exists] i Equals " + i); + + // If there is an item that exists in inventory + if (i !== -1) { + console.log("[Item Amount Start] " + userItems[i].amount); + userItems[i].amount += amount; // Increments amount value + console.log("[Item Amount Finish] " + userItems[i].amount); + // Disallows adding objects such as crystals + } else { + console.log("[Item Amount Wait] Created item for the first time."); + var clonedItem = JSON.parse(JSON.stringify(item)); // Clones item in order to not actually increment the rooms.js item JSON data + userItems.push(clonedItem); + addItem(userID, item, amount - 1); + } +} + +// Sends inventory into chat +async function sendInventory(message, pageNum, newMessage) { + var items = ""; + var groupedArr = createGroupedArray(userItems, 5); + + // Sets a default page num, or makes it human readable + if (pageNum === undefined) pageNum = 1; else { + if (pageNum < 1) pageNum = 1; + if (groupedArr.length < pageNum) pageNum = groupedArr.length; + } + + // Checks if page number is valid + if (pageNum > groupedArr.length) { + // If it's longer than actual length, but isn't just an empty inventory + if (!groupedArr.length === 0) return; + } + + //console.log(pageNum); + + // Grabs item in loop, parses it, then adds it to "items" variable + if (groupedArr[pageNum - 1]) { + for (let index = 0; index < groupedArr[pageNum - 1].length; index++) { + // Grabs an item, from a page index + const element = groupedArr[pageNum - 1][index]; + + // Makes if there is an emote, it'll add an extra space + var emoteText = ""; + if (element.emote) emoteText = element.emote + " "; + + // Adds it in + items += `> ${emoteText}**${element.name}** [${element.amount}x] ${element.info}\n`; + } + } + + // No items message to fill field + if (items === "") items = "There are no items in the party's inventory."; + + // To make the message of "Page 1/0" with no items in !inventory not happen + var moddedLength = groupedArr.length; + if (moddedLength < 1) moddedLength = 1; + var moddedPageNum = pageNum; + if (moddedPageNum < 1) moddedPageNum = 1; + + const footer = getFooterCommands("!inventory"); + + // Creates embed & sends it + const inv = new Discord.RichEmbed() + .setAuthor(`${message.member.displayName}`, message.author.avatarURL) + .setColor(message.member.displayColor) + .setDescription("I can appraise your items with `!item [ITEM NAME]`, traveler.") + .addField(`Items (Page ${moddedPageNum}/${moddedLength})`, items) + .setFooter(`Commands: ${footer}`) + + var invMessage; + if (!newMessage) + invMessage = await message.channel.send({embed: inv}); + else { + invMessage = newMessage; + await invMessage.edit({embed: inv}); + } + + // Collector for emotes + const emotes = ['⬅', '❌', '➡']; + const collector = invMessage.createReactionCollector( + (reaction, user) => emotes.includes(reaction.emoji.name) && user.id !== client.user.id & message.author.id === user.id, { time: 15 * 1000 }); + var react = ""; + var endedOnReact; + + // Sends reactions + if (!newMessage) { + for (let i = 0; i < emotes.length; i++) { + const element = emotes[i]; + console.log(element); + await invMessage.react(element); + } + } + + // Collects reactions + collector.on("collect", async reaction => { + var user = reaction.users.last(); + react = reaction.emoji.name; + if (react !== '❌') { reaction.remove(user); } + endedOnReact = true; + collector.stop(); + }); + + // Clears reactions + collector.once("end", async collecter => { + console.log("[Reaction Options] Ended collector, clearing emotes and sending timing out message if needed."); + + if (!endedOnReact) { + invMessage.clearReactions(); + return message.channel.send(":x: **Timed Out**: The emote reaction request timed out after 15 seconds."); + } + if (react === '❌') { + invMessage.clearReactions(); + return invMessage.edit(invMessage.content); + } + + var pageNumModifier = 0; + if (react === emotes[0]) pageNumModifier -= 1; + if (react === emotes[2]) pageNumModifier += 1; + console.log(pageNum + " | " + pageNumModifier); + sendInventory(message, pageNum + pageNumModifier, invMessage); + }); +} + +// Appraise an item +async function appraiseItem(message) { + const itemName = message.content.replace("!item", "").trim().toLowerCase(); + console.log("[Item Appraisal] " + `<< ${itemName} >>`); + + // Show if no parameter is given + if (itemName === "" || !itemName) { + message.channel.send(`:x: ${message.author} Please tell me the item name from your inventory, or I won't know which item you want me to look at.`); + return; + } + + var i = userItems.findIndex(i => i.name.toLowerCase() === itemName); + console.log("[Item Amount Item Exists] i Equals " + i); + + // If there is an item that exists in inventory + if (i !== -1) { + console.log(`[Item Appraisal] Found item in inventory!`); + const embed = new Discord.RichEmbed() + .setAuthor(`${message.member.displayName}`, message.author.avatarURL) + .setColor(message.member.displayColor) + .setTitle(userItems[i].name) + .setDescription(userItems[i].info) + + if (userItems[i].type.subtype) + embed.addField("Type", `${userItems[i].type.type}\n> ${userItems[i].type.subtype}`, true) + else + embed.addField("Type", `${userItems[i].type.type}`, true) + + if (userItems[i].type.stats) { + console.log(JSON.stringify(userItems[i].type.stats, null, 4)) + var itemStats = "There are no stats avaliable."; + var resetItemStats = false; + userItems[i].type.stats.forEach(element => { + if (element) { + if (!resetItemStats) { + resetItemStats = true; + itemStats = ""; + } + const types = [ + "attack", + "defence", + "speed", + "healing" + ]; + + types.forEach(type => { + if (element[type]) { + switch (type) { + case "attack": + itemStats += `ATK: ${element.attack}\n`; + break; + case "defence": + itemStats += `DEF: ${element.defence}\n`; + break; + case "speed": + itemStats += `SPD: ${element.speed}\n`; + break; + case "healing": + itemStats += `Healing: ${element.healing} HP\n`; + break; + } + } + }); + } + }); + } + + embed.addField("Stats", `${itemStats}`, true); + + message.channel.send(embed); + + // Disallows adding objects such as crystals + } else { + console.log("[Item Appraisal] Couldn't find item in inventory."); + message.channel.send(`:x: ${message.author} I'm unable to find "${itemName}" in your inventory.`); + } +} + +*/ \ No newline at end of file diff --git a/Shared/messaging.js b/Shared/messaging.js new file mode 100644 index 0000000..114a512 --- /dev/null +++ b/Shared/messaging.js @@ -0,0 +1,56 @@ +//initialize the exports +exports = module.exports = {}; + +//SendPublicMessage +//client - discord.js client +//user (optional) - discord.js user OR username +//channel - discord.js channel OR channel name +//message - message to send +//typingDelay (optional) - delay in milliseconds for the message, while typing is shown +exports.SendPublicMessage = function(client, user, channel, message, typingDelay = 0) { + //Handle optional second argument (so much for default arugments in node) + if (message === undefined) { + message = channel; + channel = user; + userID = null; + } + + //handle user strings + if (typeof(user) == "string") { + user = client.users.find(item => item.username === user); + } + + //handle channel strings + if (typeof(channel) == "string") { + channel = client.channels.find(item => item.name === channel); + } + + //Utility trick: @user + if (user !== null) { + message = "<@" + user.id + "> " + message; + } + + //Sends message + if (typingDelay !== 0) { + channel.startTyping(); + setTimeout(() => { + channel.send(message); + channel.stopTyping(true); + }, typingDelay); + } else { + channel.send(message); + } +} + +//SendPrivateMessage +//client - discord.js client +//user - discord.js user OR username +//message - message to send +exports.SendPrivateMessage = function(client, user, message) { + //handle user strings + if (typeof(user) == "string") { + user = client.users.find(item => item.username === user); + } + + user.send(message); +} diff --git a/Shared/progression.js b/Shared/progression.js new file mode 100644 index 0000000..0582ca5 --- /dev/null +++ b/Shared/progression.js @@ -0,0 +1,92 @@ +exports = module.exports = {}; + +/* +function addXP(userID, amount) { + var response = String(dataRequest.sendServerData("addXP", amount, userID)); +} + +function getLevelUp(userID) { + const server = client.guilds.get(process.env.SANCTUM_ID); + const member = server.members.get(userID); + if (client.user.username == "Kamala, Obsidian Vice President" && !member.roles.has(process.env.GROUP_A_ROLE)) return; + if (client.user.username == "Captain Montgomery" && !member.roles.has(process.env.GROUP_B_ROLE)) return; + if (client.user.username == "Dairo, High Prophet of The Hand" && !member.roles.has(process.env.GROUP_C_ROLE)) return; + + //const user = server.members.get(userID); + var response = String(dataRequest.sendServerData("getLevelUp", 0, userID)); + var responseMessage = String(response.split(",")[0]); + var lvl = Math.floor(parseFloat(response.split(",")[1])); + var statPoints = parseFloat(response.split(",")[2]); + + var attacker = String(dataRequest.loadServerData("userStats", userID)); + var chests = parseFloat(attacker.split(",")[11]); + + console.log(response.split(",")); + + majorLevelUp(lvl, server, userID); + + if (responseMessage == "levelup") { + //if (true) { + console.log("Chests: " + chests) + checkinLevelUp(userID, lvl, statPoints, chests); + } +} + +async function majorLevelUp(level, server, userID) { + const user = server.members.get(userID); + + var newChannel = ""; + + var levels = [ + // Role, Level + [server.roles.find(role => role.name === "LVL 1+"), 1, process.env.CRYSTAL_SHORES_CHANNEL_ID], + [server.roles.find(role => role.name === "LVL 15+"), 15, process.env.SEA_OF_FOG_CHANNEL_ID], + [server.roles.find(role => role.name === "LVL 30+"), 30, process.env.DEADLANDS_CHANNEL_ID] + ] + + // Shrinking level + if (level < 30) { + level = 15; + } else if (level < 15) { + level = 1; + } + + // Rank Level Up + var levelRole = server.roles.find(role => role.name === `LVL ${level}+`); + if (levelRole) { + var memberRole = user.roles.has(levelRole.id); + if (!memberRole) { + user.addRole(levelRole).catch(console.error); + for (let i = 0; i < levels.length; i++) { + const element = levels[i]; + if (element[1] !== level) { + await user.removeRole(element[0]); + } + } + if (user !== undefined) {// && newChannel !== undefined) { + var levelMarks = [1, 15, 30]; + + for (let i = 0; i < levelMarks.length; i++) { + const element = levelMarks[i]; + // Gets channel of array + newChannel = client.channels.get(levels[levels.findIndex(level => level[1] === element)][2]); + if (level === element) + newChannel.send(dialog.getDialog("level" + level, user, newChannel)); + } + } + } + } +} + +function checkinLevelUp(userID, lvl, statPoints, chests) { + const guild = client.guilds.get(process.env.SANCTUM_ID); + if (lvl === 30) { + //Post level cap level up! + sendMessage(userID, getFactionChannel(guild.members.get(userID)), dialog.getDialog("levelUpLevelCap", userID, lvl, chests)); + } else { + //regular level up + sendMessage(userID, getFactionChannel(guild.members.get(userID)), dialog.getDialog("levelUp", userID, lvl, statPoints)); + } + //sendMessage(testChannelID, dialog.getDialog("levelUp", userID, lvl, statPoints)); +} +*/ \ No newline at end of file diff --git a/Shared/shared.js b/Shared/shared.js new file mode 100644 index 0000000..d035ed7 --- /dev/null +++ b/Shared/shared.js @@ -0,0 +1,9 @@ +//initialize the exports +exports = module.exports = {}; + +exports = Object.assign(exports, require("./factions.js")); +exports = Object.assign(exports, require("./inventory.js")); +exports = Object.assign(exports, require("./messaging.js")); +exports = Object.assign(exports, require("./progression.js")); +exports = Object.assign(exports, require("./utility.js")); + diff --git a/Shared/testbot.js b/Shared/testbot.js new file mode 100644 index 0000000..e433c34 --- /dev/null +++ b/Shared/testbot.js @@ -0,0 +1,42 @@ +// .env Variables +require('dotenv').config({path: '../.env'}); + +let shared = require("./shared.js"); + +let Discord = require("discord.js"); +let client = new Discord.Client(); + +//message handler +client.on("ready", function() { + console.log("Logged in as " + client.user.tag); +}); + +client.on("reconnecting", function() { + console.log("Reconnecting..."); +}); + +client.on("disconnect", function() { + console.log("Disconnected"); +}); + +client.on("message", function(msg) { + //only react to commands + if (msg.content.slice(0, 1) != "!") { + return; + } + + //get the command + let command = msg.content.slice(1).split(" ")[0]; + let args = msg.content.slice(1 + command.length).trim(); + + //handle command + switch(command) { + //used for debugging + case "ping": + shared.SendPublicMessage(client, "Ratstail91", "bot-spam", "pong", 3000); + break; + } +}); + +//actually log in +client.login("TOKEN"); \ No newline at end of file diff --git a/Shared/utility.js b/Shared/utility.js new file mode 100644 index 0000000..529a3cb --- /dev/null +++ b/Shared/utility.js @@ -0,0 +1,77 @@ +//initialize the exports +exports = module.exports = {}; + +//GetFooterCommands - Gets footer commands for botspam channel commands +//commandArray - the array of possible commands to use +//excludeCommand (optional) - the command to filter out +exports.GetFooterCommands = function(commandArray, excludeCommand = null) { + let filteredCommandList = commandArray.filter(command => command !== excludeCommand); + + let returnText = ""; + filteredCommandList.forEach(command => { + if (returnText.length !== 0) { //if this isn't the first command, prepend the separator to this command + returnText += " | "; + } + returnText += command; + }); + + return returnText; +} + +//IsAdmin +//client - discord.js client +//user - discord.js user OR username +exports.IsAdmin = function(client, user) { + //handle user strings + if (typeof(user) === "string") { + user = client.users.find(item => item.username === user); + } + + let guild = client.guilds.get(process.env.SANCTUM_ID); + + return guild.members.get(user.id).roles.find(role => role.name === process.env.ADMIN_ROLE) != null; +} + +//SplitArray +//arr - 1 dimensional array to split into chunks +//chunkSize - the size of the chunks in the resulting array +exports.SplitArray = function(arr, chunkSize) { + // http://www.frontcoded.com/splitting-javascript-array-into-chunks.html + let groups = []; + for (let i = 0; i < arr.length; i += chunkSize) { + groups.push(arr.slice(i, i + chunkSize)); + } + return groups; +} + +/* +// See if the bot should display its message +function checkValidDisplay(member, channelID, checkRole) { + if (client.user.username == "Kamala, Obsidian Vice President" && channelID === process.env.GROUP_A_BOT_ID) { + if (checkRole) { if (member.roles.has(process.env.GROUP_A_ROLE)) return true; } else true; + } + else if (client.user.username == "Captain Montgomery" && channelID === process.env.GROUP_B_BOT_ID) { + if (checkRole) { if (member.roles.has(process.env.GROUP_B_ROLE)) return true; } else true; + } + else if (client.user.username == "Dairo, High Prophet of The Hand" && channelID === process.env.GROUP_C_BOT_ID) { + if (checkRole) { if (member.roles.has(process.env.GROUP_C_ROLE)) return true; } else true; + } + else if (client.user.username == "Ghost 5.0.1") { + // JSON + const rooms = require('../TextAdv/rooms.json'); + var roomExists = false; + + // Loops for all rooms + rooms.rooms.forEach(element => { + if (channelID === rooms[element].channel) roomExists = true; + }); + + if (!roomExists) { + if (channelID === process.env.TEST_CHANNEL_ID) return true; + } else return true; + + } + + return false; +} +*/ \ No newline at end of file