From 23052af1887d7257d5bd1d70a6f6e7a64c7211cc Mon Sep 17 00:00:00 2001 From: Dylan Stauch <50031006+sjkd23@users.noreply.github.com> Date: Sat, 2 Sep 2023 05:46:54 -0400 Subject: [PATCH 01/17] spin command --- JeffreyDB | Bin 36864 -> 36864 bytes JeffreyDB.db | Bin 0 -> 36864 bytes src/DBUtils.ts | 106 +++++++++++++++++++++++----------- src/JeffreyDB.ts | 96 ++++++++++++++++++++++++------- src/JeffreyGacha.ts | 122 +++++++++++++++++++++++++++++++++++++++- src/commands/balance.ts | 79 +++++++++++--------------- src/commands/poll.ts | 35 +++++++----- src/commands/roll.ts | 41 +++++++------- src/commands/spin.ts | 58 +++++++++++++++++++ src/index.ts | 34 +++++++++-- 10 files changed, 429 insertions(+), 142 deletions(-) create mode 100644 JeffreyDB.db create mode 100644 src/commands/spin.ts diff --git a/JeffreyDB b/JeffreyDB index 6405b9fd5d40df1571dd41bf0fe2fd179f21afa3..c950338d3a72e98971eebb51136def80d3632b96 100644 GIT binary patch delta 845 zcmZozz|^pSX@ayM8v_FaClJE`%S0VxSvCf}w3od6KNvW;-ZSt^@t@^A%J-VLn8%3g zJ@@a8jm2DcO`PoP;>ya5&5yB$N^M6vrDPaX6do z*x1G8fSL(KDiW)9q(kNQ}uqDrQrjAj-1Z=m-XmjteKc>@TXL9M2u^dZUwb5|sB=bBNY(H7weV={1tG8Y+ z<@uS0T^Ba@XtQPu3ik%b^W0~Gz;WDTxDUbIy~W`{pZf&<@;#5k9zN#8&0mL;e{+d* zJKV^#XWtg z?OI{CWpy76ocaX+I@XhxV+lfSQQGziaBX62y+XC+1$diaMN5Ca*LHx zp$KoBHH(!`gw;cAbQyH{lee^9r1J%PB6r(R1QrsUj2?XJ)`&YuYbL*z@{2t=H`OMh>jb z87qZZQ|K`t?r(&e$M5TX1F2Mse^6=@YZtx!tKUn??-RR_z1%vac5BUgxJ#!g`gngL z^=XRd8oL|yW9wiCvdr7HLwm3N{C2y*w@Ld?VlJLYUAe+9w|(p!YSj-8>$~f9-_t&i z-#+7I$D2>!o#Xen`GM5f82?48%dJ}5*{R!>qu6;E^PH<47ahx~<8856m|rx3@mg)C z23`aM10wE5ZgHj}NO06?IBT&)sxbC%oI6gP(C$2Oa`{20vl)gvpM1)}58jXf5rN<76l}d*FQjA4~qiCI5n7ctZk600|%gB!C2v01`j~NB{{S0VIF~ zUQGhSvG^$8{U*)W2p=CC>iH7H$@>2%T=L1Q=^mCE2_OL^fCP{L5v-=F*G%!SzMkaD(f zaC+cb|JU&!6892`fwlgN$wz%^;?Hxd$@hky@W1mdKE{1{ddT$B07oW=SQ8!Fw!Lp< zuU%_2w+^YezSq2Fua(u!+KjlryVacmarZbNifG81e+GnUr5{JoJl?L}Div}}O-YAY z7bQ&7W3>cFlYcosmq|n3nw@hrV_rv<7UQYOq(Vu-Tp7RefkAZj0P-6^SMhNrTYg({UbnJEfWUaBOj#BezxRqxz@q{(dI2jH16{Sb_Fp+Npfo~VAXU{%D6j<&<`BZmu#gIwfxe(1Kkk~o>djo zGP}hd2#ykMKVwboF&$N*Mzv5! zJyz@O2#y}l9~{x@1j|vV(X^;aZlDC%D`_Z_Yo+f-a5Psg)n;lLrlU@a=yo_k&0Z@# zA3;-9f*Q1kF~f zx}xYRLlZoKel;D^0s5@;tq7W>yW6DF$TKx%7+#Ff!1Y+^n-Mf~jaG9-s<1VQsH^PJ z1?m%aFM46M&N`YeIgV~qVjrj5Y=a%W1}#)^n?oRQ2{a|c?dWMTf}{DHo25ycalzn> zXsW1pH)0BE_FCyj5j2%rHGgnW55iTXtkdizb`bfkbU1?MthH&c6+`?aqGY&ZbI{LT zt2GdzLT@za-8b(_^IK!xf(*8@REd#&_L z1kFZyYTYVr1S{0JLNUZlCZx6ataK=XW^?|!dOSthny|l^9@1D&b|33n>4yw_}(D$TQUD<^+dCn|F`!S z?`HVkAaZ6TdS?eP|7Y|>nEykz-J9VDV#s5~{GSm+r_cY}{~h4u`~Q!)Oxm6&?R96z$xH*$)TEKxeJ#M}JKy>4<2wsD!_M78 z%d_czx8Jrrx=h|797jH(lo0ZQJq7kWS)Q`MwUZ6D<|5&7!51X6|HpLFBoo4OGWmD% zm#M8Aza?$q`SedWejS$y@eqIj1Rwwb2tWV=QGuk%8H10$O^CCz+><+=b*RtJAO_h9y{sUN_jT?plsxdu~+@1J|b>kqN(m z@1>)JD2m+Ra_|xxs?reuXwT?*W;A}K$ID;DHT{SeKNQEcG^l)44pwpY0ph0L z%7O4zLY$l9emFiEd8_3d?KW-OaU;RRaEl`;m)D~!Me}aO7~ioOoZD-4Sf4!^J!iV5 zcEN6`J>*4mj`0nI$3Qv0(xkQTLX?rCC(;$Fa%5v-F!yyroSEUC+zdufIJm;qwc-90 z#$H`t`i~dMdzNFdE7CbVPP$-^n`R@BOZ`mP0}BKo009U<00Izz00bZa0SG_<0#`3k z=dR0(rn$1Rx~|EpqOMBn+Nz>z(wFxBe&0S~U!S^`(`wj?q%1E=>x+^?B_*q_WhFJE zDjHpoq^u;JpE}9f#ijlv>=C>ACgO+?fB*y_009U<00Izz00bZa0SLUJz4IG)JdsM`Vbku&`3 zqvrpYvyz;Z*D}lNYIt`*Wd1+;+y2X|H%fv41Rwwb2tWV=5P$##AOHafK;Q}m&YS;# zN>cv6`(c3q1Rwwb2tWV=5P$##AOHafK;X3t^tl9)H-u4h{7uj6x!I+qR{P+vpQ(4- zOTDhL_QiHL)9V~W=l@S;_*Hdf#P9#lruz5)vp@aE0s#m>00Izz00bZa0SG_<0uX?} zTP)z;{~vz-PrSv=MAIMu0SG_<0uX=z1Rwwb2tWV=|7!t%{y&!bouvL^FIXS|0SG_< r0uX=z1Rwwb2tWV=5O{+Hreg6~VZ*97Ewl5$o#f&(!mi~ww&(r>=RHx^ literal 0 HcmV?d00001 diff --git a/src/DBUtils.ts b/src/DBUtils.ts index ee1a120..55f828b 100644 --- a/src/DBUtils.ts +++ b/src/DBUtils.ts @@ -1,13 +1,13 @@ -import { Users, UserWallets, GachaInvs } from "./JeffreyDB" +import { User, Wallet, GachaInv, DailyWheel } from "./JeffreyDB" //check if 'userid' is in the Users table export async function checkUsers(userID: string): Promise { try { - const user = await Users.findOne({ where: { userid: userID } }); + const user = await User.findOne({ where: { id: userID } }); if (user) { - console.log(`user ${user.userid} found in Users`); + console.log(`user ${user.id} found in Users`); return true; } else { @@ -18,31 +18,34 @@ export async function checkUsers(userID: string): Promise { console.log(`ERROR: could not check for ${userID} in Users table `); } return null; -} +}; // adds 'userid' and 'username' to the Users table export async function addUsers(userID: string, username: string): Promise { + try { - const user = await Users.create({ userid: userID, username: username }); + const user = await User.create({ id: userID, name: username }); console.log(`added ${userID} to Users`); } catch { console.log(`ERROR: could not add ${userID} to Users`); } -} +}; //checks balance for 'userid' in UserWallets export async function checkBalance(userID: string): Promise { + try { - const userBalance = await UserWallets.findOne({ where: { userid: userID } }); + const userBalance = await Wallet.findOne({ where: { userid: userID } }); if (userBalance) { console.log(`Balance for ${userID} is ${userBalance.balance}`); return userBalance.balance; } - } catch { - console.log(`ERROR: Could not check balance for ${userID}`); + return null; + } catch(err) { + console.error(err); + return null; } - return null; -} +}; /** * change 'userid' balance by 'add' (can be positive or negative) @@ -50,18 +53,20 @@ export async function checkBalance(userID: string): Promise { * @param add */ export async function changeBalance(userID: string, add: number): Promise { + try { - const addBalance = await UserWallets.increment({ balance: add }, { where: { userid: userID } }); + const addBalance = await Wallet.increment({ balance: add }, { where: { userid: userID } }); console.log(`${userID}'s wallet has been changed by: ${add}`); } catch { console.log(`ERROR: could not increment ${userID}`); } -} +}; //checks if 'userid' is in UserWallets export async function checkUserWalletsUser(userID: string): Promise { + try { - const user = await UserWallets.findOne({ where: { userid: userID } }); + const user = await Wallet.findOne({ where: { userid: userID } }); if (user) { console.log(`${userID} found in UserWallets`); return true; @@ -73,22 +78,24 @@ export async function checkUserWalletsUser(userID: string): Promise { try { - const user = await UserWallets.create({ where: { userid: userID } }); + const user = await Wallet.create({ userid: userID, balance: 0 }); console.log(`${userID} added to UserWallets`); - } catch { - console.log(`ERROR: Could not add ${userID} to UserWallets`); + } catch (err) { + console.error(err); } -} +}; + //checks if 'userid' has the given 'gachas' (gachaURL) yet. export async function checkGachaInv(userID: string, gachaURL: string): Promise { + try { - const user = await GachaInvs.findOne({ where: { userid: userID, gachas: gachaURL } }); + const user = await GachaInv.findOne({ where: { userid: userID, gachas: gachaURL } }); if (user) { console.log(`found ${userID} in GachaInvs`); return true; @@ -100,19 +107,20 @@ export async function checkGachaInv(userID: string, gachaURL: string): Promise { + try { console.log(`${userID} and ${gachaURL}`); - const user = await GachaInvs.create({ userid: userID, gachas: gachaURL, amt: 1 }); + const user = await GachaInv.create({ userid: userID, gachas: gachaURL, amt: 1 }); console.log(`${userID} added to GachaInvs`); - } catch (err){ + } catch (err) { console.error(`ERROR: Could not add ${userID} to GachaInvs`); throw err; } -} +}; /** * @@ -121,13 +129,14 @@ export async function addGacha(userID: string, gachaURL: string): Promise * increases 'amt' by 1 (level it up). */ export async function gachaLvlUp(userID: string, gachaURL: string): Promise { + try { - const gacha = await GachaInvs.increment({ amt: 1 }, { where: { userid: userID, gachas: gachaURL } }); + const gacha = await GachaInv.increment({ amt: 1 }, { where: { userid: userID, gachas: gachaURL } }); console.log(`increased ${gachaURL}'s level by 1 for ${userID}!`); } catch { console.log(`ERROR: could not increase ${gachaURL}'s level by 1 for ${userID}.`); } -} +}; /** * @@ -135,16 +144,45 @@ export async function gachaLvlUp(userID: string, gachaURL: string): Promise{ - try{ - const level = await GachaInvs.findOne({where: {userid: userID, gachas: gachaURL}}); - if(level){ - return level.amt; - }else{ +export async function checkGachaLevel(userID: string, gachaURL: string): Promise { + + try { + const level = await GachaInv.findOne({ where: { userid: userID, gachas: gachaURL } }); + if (level) { + return level.amt; + } else { return null; } - }catch{ + } catch { console.log(`ERROR: could not check ${userID}'s GachaInv for ${gachaURL}'s level.`); } return null; -} \ No newline at end of file +}; + +export async function findOrCreateDailyWheel(userID: string): Promise { + + try { + const user = await DailyWheel.findOrCreate({ where: { userid: userID } }); + console.log(`${userID} found or created in DailyWheel`); + } catch (err) { + console.error(err); + } +}; + +export async function checkSpins(userID: string): Promise<[number, Date] | null> { + + try { + const wheel = await DailyWheel.findOne({ where: { userid: userID } }); + + if (!wheel) return null; + + const endTime = new Date(wheel.createdAt); + endTime.setHours(endTime.getHours() + 24); + console.log(`${userID} has to wait until ${endTime} before they can spin again!`); + + return [wheel.spins, endTime]; + } catch (err) { + console.error(err); + } + return null; +}; \ No newline at end of file diff --git a/src/JeffreyDB.ts b/src/JeffreyDB.ts index f884996..a74561e 100644 --- a/src/JeffreyDB.ts +++ b/src/JeffreyDB.ts @@ -1,4 +1,4 @@ -import { Sequelize, DataTypes, Model } from 'sequelize'; +import { Sequelize, DataTypes, Model, IntegerDataType } from 'sequelize'; export const sequelize = new Sequelize('database', 'user', 'password', { host: 'localhost', @@ -7,33 +7,41 @@ export const sequelize = new Sequelize('database', 'user', 'password', { logging: false, }); -export class Users extends Model { - declare userid: string; - declare username: string; +export class User extends Model { + declare id: string; + declare name: string; } -Users.init({ - userid: { +User.init({ + id: { type: DataTypes.STRING, allowNull: false, primaryKey: true, }, - username: { + name: { type: DataTypes.STRING, allowNull: false, }, }, { sequelize }); -export class UserWallets extends Model { +export class Wallet extends Model { + declare id: string; declare userid: string; declare balance: number; } -UserWallets.init({ +Wallet.init({ + id: { + type: DataTypes.INTEGER, + primaryKey: true, + allowNull: false, + autoIncrement: true, + }, userid: { type: DataTypes.STRING, allowNull: false, + unique: true, references: { - model: Users, - key: 'userid', + model: User, + key: 'id', }, }, balance: { @@ -43,20 +51,29 @@ UserWallets.init({ } }, { sequelize } ); -Users.hasOne(UserWallets, { foreignKey: 'userid', sourceKey: 'userid' }); +User.hasOne(Wallet, { foreignKey: 'userid', sourceKey: 'id' }); -export class GachaInvs extends Model { +export class GachaInv extends Model { + declare id: string; declare userid: string; declare gachas: string; declare amt: number; } -GachaInvs.init({ +GachaInv.init({ + id: { + type: DataTypes.INTEGER, + primaryKey: true, + allowNull: false, + autoIncrement: true, + }, userid: { type: DataTypes.STRING, + unique: false, + allowNull: false, references: { - model: Users, - key: 'userid', + model: User, + key: 'id', }, }, gachas: { @@ -70,7 +87,43 @@ GachaInvs.init({ }, }, { sequelize } ); -Users.hasMany(GachaInvs, { foreignKey: 'userid', sourceKey: 'userid' }); +User.hasMany(GachaInv, { foreignKey: 'userid', sourceKey: 'id' }); + +export class DailyWheel extends Model { + declare id: string; + declare userid: string; + declare spins: number; + declare createdAt: string; +} + +DailyWheel.init({ + id: { + type: DataTypes.INTEGER, + primaryKey: true, + allowNull: false, + autoIncrement: true, + defaultValue: 1, + }, + userid: { + type: DataTypes.STRING, + unique: true, + allowNull: false, + references: { + model: User, + key: 'id', + }, + }, + spins: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: 5, + }, + createdAt: { + type: DataTypes.DATE, + allowNull: false, + }, +}, { sequelize } +); export const DB = { test: async (): Promise => { @@ -83,14 +136,17 @@ export const DB = { }, sync: async (): Promise => { try { - await Users.sync(); + await User.sync(); console.log(`UsersDB synced`); - await UserWallets.sync(); + await Wallet.sync(); console.log(`UserWallets synced`); - await GachaInvs.sync(); + await GachaInv.sync(); console.log(`GachaInvs synced`); + + await DailyWheel.sync(); + console.log(`DailyWheel synced`); } catch { console.log('Failed to sync table(s)'); } diff --git a/src/JeffreyGacha.ts b/src/JeffreyGacha.ts index 8139608..9ea53ed 100644 --- a/src/JeffreyGacha.ts +++ b/src/JeffreyGacha.ts @@ -1,3 +1,6 @@ +import { DailyWheel } from './JeffreyDB'; +import { rng } from './utils'; + //Object containing the 4 rarities, which are arrays of all the gacha URL pictures from imgur. export const JeffreyGachaURLs = { Common: [ @@ -94,8 +97,8 @@ export const JeffreyGachaURLs = { { link: 'https://imgur.com/eTGaGLM.png', name: 'LapCatJeffrey' }, { link: 'https://imgur.com/7YdKWjS.png', name: 'GateKeeperJeffrey' } ] - } + //Object containing the Names and Descriptions of all the legendary gachas const LegendaryInfo: { [key: string]: { Name: string; Description: string } } = { RadiantJeffrey: { @@ -148,6 +151,50 @@ const LegendaryInfo: { [key: string]: { Name: string; Description: string } } = } } +export const WheelResults: { [key: string]: { Name: string; Description: string } } = { + OneCoin: { + Name: '1 Coin...', + Description: 'Wow thats lame...' + }, + TenCoins: { + Name: '10 Coins', + Description: 'Hey, at least its something.' + }, + TwentyFiveCoins: { + Name: '25 coins', + Description: 'Could be more, but not terrible!' + }, + FiftyCoins: { + Name: '50 Coins', + Description: 'Solid spin!' + }, + SixtyNineCoins: { + Name: '69 coins', + Description: 'Nice' + }, + OneHundredCoins: { + Name: '100 coins!', + Description: 'One hundred? Thats TEN rolls if you did them individually! I wouldn\'t recommend that though.' + }, + OneFiftyCoins: { + Name: '150 coins!', + Description: 'Awesome! Perfect for TEN rolls (if you do them five at a time)!' + }, + FourTwentyCoins: { + Name: '420 coins', + Description: 'Blaze it' + }, + Legendary: { + Name: 'LEGENDARY!?', + Description: 'You got a legendary from the wheel? I didn\'t even know that was possible!' + }, + Jackpot: { + Name: ' A JACKPOT!!!!!!!!!', + Description: 'OMG. Ok im starting to think this creator sucks at balancing.' + } + +} + /** * * @param gacha - takes a legendary gacha image URL @@ -163,11 +210,82 @@ export async function displayLegendary(gacha: string): Promise `${legendaryName}`, `${legendaryDescription}` ]; - console.log(gachaInfo); return gachaInfo; } } return null; } +export async function spinWheel(userID: string): Promise<[string[], number] | null> { + const rndm = await rng(0, 1000); + let reward: string[] = []; + let coins: number = 0; + + try{ + const spin = await DailyWheel.increment({ spins: -1 }, {where: { userid: userID } }); + console.log(`${userID} has used one of their spins!`); + }catch(err){ + console.error(err); + } + + if(rndm < 20){ + reward = [ + WheelResults.OneCoin.Name, + WheelResults.OneCoin.Description + ]; + coins = 1; + }else if(rndm >= 20 && rndm < 150 ){ + reward = [ + WheelResults.TenCoins.Name, + WheelResults.TenCoins.Description + ]; + coins = 10; + }else if(rndm >= 150 && rndm < 400){ + reward = [ + WheelResults.TwentyFiveCoins.Name, + WheelResults.TwentyFiveCoins.Description + ]; + coins = 25; + }else if(rndm >= 400 && rndm < 650 ){ + reward = [ + WheelResults.FiftyCoins.Name, + WheelResults.FiftyCoins.Description + ]; + coins = 50; + }else if(rndm >= 650 && rndm < 750 ){ + reward = [ + WheelResults.SixtyNineCoins.Name, + WheelResults.SixtyNineCoins.Description + ]; + coins = 69; + }else if(rndm >= 750 && rndm < 870){ + reward = [ + WheelResults.OneHundredCoins.Name, + WheelResults.OneHundredCoins.Description + ]; + coins = 100; + }else if(rndm >= 870 && rndm < 970 ){ + reward = [ + WheelResults.OneFiftyCoins.Name, + WheelResults.OneFiftyCoins.Description + ]; + coins = 150; + }else if(rndm >= 970 && rndm < 990 ){ + reward = [ + WheelResults.Legendary.Name, + WheelResults.Legendary.Description + ]; + coins = -1; + }else if(rndm >= 990 && rndm <= 1000){ + reward = [ + WheelResults.Jackpot.Name, + WheelResults.Jackpot.Description + ]; + coins = 1000; + }else{ + console.log(`ERROR: Failed to choose WheelResult`); + return null; + } + return [reward, coins]; +}; diff --git a/src/commands/balance.ts b/src/commands/balance.ts index 4ad47a3..5765998 100644 --- a/src/commands/balance.ts +++ b/src/commands/balance.ts @@ -1,4 +1,4 @@ -import { CommandInteraction, SlashCommandBuilder, EmbedBuilder } from 'discord.js'; +import { SlashCommandBuilder, EmbedBuilder, ChatInputCommandInteraction } from 'discord.js'; import { checkUsers, checkBalance, changeBalance, addUsers, checkUserWalletsUser, addUserWalletsUser } from '../DBUtils'; import { replyWithEmbed } from '../utils'; @@ -15,7 +15,7 @@ export const Balance = { .setDescription('Add to balance, negative number to subtract. (officer+ only)') .setRequired(false)), - run: async (interaction: CommandInteraction): Promise => { + run: async (interaction: ChatInputCommandInteraction): Promise => { if (!interaction.guild) { await interaction.reply('This command can only be done in a server.'); return; @@ -23,18 +23,16 @@ export const Balance = { const userID = interaction.user.id; const member = interaction.options.getUser('member'); const getRole = interaction.guild.roles.cache; - const add = (interaction.options as any).getInteger('add'); - + const add = interaction.options.getInteger('add'); + let target; let higherUp = false; - let targetBalance; - let startingBalance; //need at least one of these roles to use the 'add' option const role = [ getRole.find(role => role.name.toLowerCase() === 'moderator'), - getRole.find(role => role.name.toLocaleLowerCase() === 'officer'), - getRole.find(role => role.name.toLocaleLowerCase() === 'head raid leader') + getRole.find(role => role.name.toLowerCase() === 'officer'), + getRole.find(role => role.name.toLowerCase() === 'head raid leader') ]; const length = role.length; @@ -66,38 +64,29 @@ export const Balance = { //checks for target in Users, otherwise adds them //checks for target in UserWallets, otherwise adds them - try { - const userInUsers = await checkUsers(target.id); - if (!userInUsers) { - await addUsers(target.id, target.username); - } - const userInWallet = await checkUserWalletsUser(target.id); - if (!userInWallet) { - await addUserWalletsUser(target.id); - } - } catch { - console.log(`ERROR: Could not run checkUsers on ${target.id}`) + const userInUsers = await checkUsers(target.id); + if (!userInUsers) { + await addUsers(target.id, target.username); + } + const userInWallet = await checkUserWalletsUser(target.id); + if (!userInWallet) { + await addUserWalletsUser(target.id); } + let startingBalance; //checks targets current balance, then adds 'add' to it (can be negative) if (add !== null) { - try { - startingBalance = await checkBalance(target.id); - await changeBalance(target.id, add); - } catch { - console.log(`ERROR: Could not run changeBalance command for ${target.id} + (${add})`) + startingBalance = await checkBalance(target.id); + if (!startingBalance && startingBalance !== 0) { + startingBalance = 0; } + await changeBalance(target.id, add); } //checks targets balance - try { - targetBalance = await checkBalance(target.id); - if (!targetBalance) { - console.log(`ERROR: ${target.id}'s balance is NULL or undefined.`); - return; - } - } catch { - console.log('error checking balance'); + const targetBalance = await checkBalance(target.id); + if (!targetBalance && targetBalance !== 0) { + console.log(`ERROR: ${target.id}'s balance is NULL or undefined.`); return; } @@ -105,22 +94,20 @@ export const Balance = { .setAuthor({ name: `${target.displayName}'s Wallet`, iconURL: target.displayAvatarURL() }) .addFields({ name: ' ', value: `Balance: ${targetBalance}` }); - //if no 'add' was specified, reply with embed of balance - //otherwise, reply with the balance change, then send embed - try { - if (!add) { - await replyWithEmbed(embed, interaction); - } - if (add >= 0) { - await interaction.reply(`Added ${add} to ${target}'s wallet! (${startingBalance} + ${add} = ${targetBalance})`); - } - if (add < 0) { - await interaction.reply(`Removed ${add * -1} from ${target}'s wallet! (${startingBalance} - ${add * -1} = ${targetBalance})`); - } + //if no 'add' was specified, reply with embed of balance + //otherwise, reply with the balance change, then send embed + if (!add) { + await replyWithEmbed(embed, interaction); + } else if (add > 0) { + await interaction.reply(`Added ${add} to ${target}'s wallet! (${startingBalance!} + ${add} = ${targetBalance})`); await interaction.channel!.send({ embeds: [embed] }); - } catch { - console.log(`ERROR: Failed to send reply and/or embed in ${interaction.channelId}`); + } else if (add < 0) { + await interaction.reply(`Removed ${add * -1} from ${target}'s wallet! (${startingBalance!} - ${add * -1} = ${targetBalance})`); + await interaction.channel!.send({ embeds: [embed] }); + } else { + console.log(`ERROR: Could not send msg embed in ${interaction.channelId}`); return; } + } }; \ No newline at end of file diff --git a/src/commands/poll.ts b/src/commands/poll.ts index 9f3a808..df75c0d 100644 --- a/src/commands/poll.ts +++ b/src/commands/poll.ts @@ -1,4 +1,4 @@ -import { CommandInteraction, EmbedBuilder, SlashCommandBuilder } from 'discord.js'; +import { ChatInputCommandInteraction, EmbedBuilder, SlashCommandBuilder } from 'discord.js'; import { numberEmojis } from '../utils'; const MAX_CHOICES = 5; @@ -20,27 +20,32 @@ export const Poll = { .setRequired(false)); } }, - run: async (interaction: CommandInteraction): Promise => { - if (!interaction.guild) { //Checks if the command is NOT done in a server + run: async (interaction: ChatInputCommandInteraction): Promise => { + + //Checks if the command is NOT done in a server + if (!interaction.guild) { await interaction.reply('This command can only be done in a server.'); return; } - const questionString = interaction.options.get('question')?.value; + const questionString = interaction.options.getString('member'); - if (!questionString || typeof questionString !== 'string') return; + if (!questionString) return; - if (questionString.length > 256) {//char limit - interaction.reply(`Question is too long. (max 256 char.)`); + //char limit + if (questionString.length > 256) { + await interaction.reply(`Question is too long. (max 256 char.)`); } const choices = []; - for (let i = 1; i <= 5; i++) { + for (let i = 1; i <= MAX_CHOICES; i++) { const choice = interaction.options.get(`choice${i}`)?.value; if (choice) { choices.push(choice); } } - if (choices.length === 0) {//No choices by user = Yes and No + + //No choices by user = Yes and No + if (choices.length === 0) { choices.push('Yes'); choices.push('No'); } @@ -51,22 +56,26 @@ export const Poll = { name: interaction.user.displayName, iconURL: interaction.user.displayAvatarURL() }) + + //Discord embed spacing to look nicer .addFields( - { name: ' ', value: '** **' }//Discord embed spacing to look nicer + { name: ' ', value: '** **' } ); for (let i = 0; i < choices.length; i++) { embed.addFields( { name: ' ', value: `${numberEmojis[i]} ${choices[i]}` }, ) if (i + 1 < choices.length) { + //more spacing embed.addFields( - { name: ' ', value: '** **' },//more spacing + { name: ' ', value: '** **' }, ) } }; try { - const embedMessage = await interaction.channel!.send({ embeds: [embed] }); //send created embed + //send created embed + const embedMessage = await interaction.channel!.send({ embeds: [embed] }); interaction.reply({ content: 'Poll created!', ephemeral: true }); // React to the embed with emojis @@ -78,7 +87,7 @@ export const Poll = { } } } catch { - console.log(`ERROR: Could not send embed in ${interaction.channelId}`); + console.log(`ERROR: Could not send embed in ${interaction.channelId})`); } } diff --git a/src/commands/roll.ts b/src/commands/roll.ts index a9b05c9..99000df 100644 --- a/src/commands/roll.ts +++ b/src/commands/roll.ts @@ -1,5 +1,5 @@ import { JeffreyGachaURLs, displayLegendary } from "../JeffreyGacha"; -import { SlashCommandBuilder, CommandInteraction, EmbedBuilder, InviteTargetType } from "discord.js"; +import { SlashCommandBuilder, CommandInteraction, EmbedBuilder } from "discord.js"; import { replyWithEmbed, rng } from '../utils'; import { addGacha, @@ -9,7 +9,9 @@ import { checkGachaInv, checkUserWalletsUser, gachaLvlUp, - checkGachaLevel + checkGachaLevel, + addUsers, + checkUsers } from "../DBUtils"; export const Roll = { @@ -23,24 +25,25 @@ export const Roll = { return; } let embed; - let currentBalance; const userID = interaction.user.id; - try { - const user = await checkUserWalletsUser(userID); - if (!user) { - await addUserWalletsUser(userID); - } - } catch { - console.log("could not findorcreate target"); + let currentBalance; + + + + const userInUsers = await checkUsers(userID); + if (!userInUsers) { + await addUsers(userID, interaction.user.username); } - try { - currentBalance = await checkBalance(userID); - } catch { - console.log(`ERROR: Could not chekc balance for ${userID}`); + const user = await checkUserWalletsUser(userID); + if (!user) { + await addUserWalletsUser(userID); } - if (!currentBalance) { + currentBalance = await checkBalance(userID); + console.log(currentBalance); + + if (!currentBalance && currentBalance !== 0) { console.log(`${userID}'s currentBalance is NULL or undefined`); return; } @@ -50,7 +53,6 @@ export const Roll = { return; } else { await changeBalance(userID, -5); - currentBalance -= 5; } const rndm = await rng(0, 100); @@ -117,10 +119,7 @@ export const Roll = { starArray += '⭐'; } embed.setDescription(starArray); - try { - await replyWithEmbed(embed, interaction); - } catch { - console.log(`ERROR: could not reply with embed in ${interaction.channelId}`); - } + + await replyWithEmbed(embed, interaction); } }; \ No newline at end of file diff --git a/src/commands/spin.ts b/src/commands/spin.ts new file mode 100644 index 0000000..ef84109 --- /dev/null +++ b/src/commands/spin.ts @@ -0,0 +1,58 @@ +import { CommandInteraction, SlashCommandBuilder, EmbedBuilder } from 'discord.js'; +import { checkSpins, findOrCreateDailyWheel, changeBalance } from '../DBUtils'; +import { spinWheel, JeffreyGachaURLs, displayLegendary } from '../JeffreyGacha'; +import { replyWithEmbed, rng } from '../utils'; + +export const Spin = { + info: new SlashCommandBuilder() + .setName('spin') + .setDescription('Spin the daily wheel! (up to 5 times per day)'), + + run: async (interaction: CommandInteraction): Promise => { + if (!interaction.guild) return; + const userID = interaction.user.id; + + await findOrCreateDailyWheel(userID); + + //check how many spins user has left today and when the spins reset + let spinInfo = await checkSpins(userID); + + if(!spinInfo) { + console.log(`ERROR: check spins is NULL for ${userID}`) + return; + } + if(spinInfo[0]< 1){ + await interaction.reply(`You've exhausted your 5 spins today! Your spins refresh at ${spinInfo[1]}`); + return; + } + //if NULL spins then add user to DailyWheel + const result = await spinWheel(userID); + if (!result) return; + + const [reward, coins] = result; + + if (coins !== -1) { + await changeBalance(userID, coins); + await interaction.reply(`** **\nYou got ${reward[0]}\n\n${reward[1]}`); + } else if (coins === -1) { + + const legendary = JeffreyGachaURLs['Legendary']; + const chooseGacha = await rng(0, legendary.length - 1); + const gachaArray = legendary[chooseGacha]; + const gacha = (typeof gachaArray === 'string' ? gachaArray : gachaArray.link); + + const legendaryInfo = await displayLegendary(gacha); + + if (!legendaryInfo) return; + + const embed = new EmbedBuilder() + .setTitle(`You got a ${reward[0]}`) + .setDescription(reward[1]) + .setAuthor({ name: `${interaction.user.displayName}` }) + .setImage(gacha) + .addFields({ name: `${legendaryInfo[0]}`, value: `${legendaryInfo[1]}` }); + + await replyWithEmbed(embed, interaction); + } + } +}; diff --git a/src/index.ts b/src/index.ts index f5bab73..2c8d469 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,7 +5,10 @@ import { ClientOptions, REST, Routes, - GatewayIntentBits + GatewayIntentBits, + SlashCommandBuilder, + SlashCommandAssertions, + ChatInputCommandInteraction } from 'discord.js'; import { Ping } from './commands/ping'; import { Mock } from './commands/mock'; @@ -14,6 +17,7 @@ import { Balance } from './commands/balance'; import { Poll } from './commands/poll'; import { Roll } from './commands/roll'; import { DB } from './JeffreyDB'; +import { Spin } from './commands/spin'; config(); @@ -60,7 +64,8 @@ async function main() { Cat.info.toJSON(), Poll.info.toJSON(), Balance.info.toJSON(), - Roll.info.toJSON() + Roll.info.toJSON(), + Spin.info.toJSON() ] } ); @@ -83,7 +88,7 @@ client.on('interactionCreate', async (interaction) => { } const cooldownMap = cooldown.get(commandName)!; - if (cooldownMap.has(userID) && cooldownMap.get(userID)! > Date.now() && interaction.user.id !== '218823980524634112') { + if (cooldownMap.has(userID) && cooldownMap.get(userID)! > Date.now()) { const cooldownRemaining = (cooldownMap.get(userID)! - Date.now()) / 1000; await interaction.reply(`Please wait ${cooldownRemaining.toFixed(1)} seconds.`); return; @@ -105,13 +110,16 @@ client.on('interactionCreate', async (interaction) => { await Cat.run(interaction); } if (commandName === 'poll') { - Poll.run(interaction); + await Poll.run(interaction as ChatInputCommandInteraction); } if (commandName === 'balance') { - await Balance.run(interaction); + await Balance.run(interaction as ChatInputCommandInteraction); } if (commandName === 'roll') { - await Roll.run(interaction) + await Roll.run(interaction); + } + if (commandName === 'spin') { + await Spin.run(interaction); } }); @@ -123,6 +131,20 @@ client.on('messageCreate', async (message) => { await Mock.effect(message); } }); + export const mockTargets = new Set(); +export async function fishGame(): Promise { + +const fisharr = [ "🐸", "🐢", "🦎", "🐬", "🦀", "🦑", "🐈", "🍱","🐙","🐚"] +const rodarr = ['rare', 'uncommon'] +const rare = 10 +const uncommon = 15 + +const rndm = Math.floor(Math.random() * fisharr.length); +const emoji = fisharr[rndm]; +console.log(emoji); + +} + main(); \ No newline at end of file From 9f6e9b8c2ec8e96c7a325b1df117ac9f39ee0129 Mon Sep 17 00:00:00 2001 From: Dylan Stauch <50031006+sjkd23@users.noreply.github.com> Date: Mon, 4 Sep 2023 00:53:49 -0400 Subject: [PATCH 02/17] User, Balance, Inventory, DailyWheel, Gacha. - Tables --- src/DBUtils.ts | 24 ++++---- src/JeffreyDB.ts | 127 +++++++++++++++++++++++++++------------- src/commands/balance.ts | 6 +- src/commands/roll.ts | 4 +- 4 files changed, 102 insertions(+), 59 deletions(-) diff --git a/src/DBUtils.ts b/src/DBUtils.ts index 962174d..971321e 100644 --- a/src/DBUtils.ts +++ b/src/DBUtils.ts @@ -1,4 +1,4 @@ -import { User, Wallet, GachaInv, DailyWheel } from "./JeffreyDB" +import { User, Balance, Gacha, DailyWheel } from "./JeffreyDB" /** * Checks if the given userID is in the "User" table. @@ -33,47 +33,47 @@ export async function addUser(userID: string, username: string): Promise { /** * Searches for the 'balance' associated the given users Discord ID. * - * If no balance was found, it calls another function to create a new row in "Wallet"- + * If no balance was found, it calls another function to create a new row in "Balance"- * -which contains the users Discord ID, and 'balance' defaults to 0. * * (balance is used in a monetary context $$$) * * @param {string} userID - Users Discord ID - * @returns {Promise} - Returns the users current balance in "Wallet" table. + * @returns {Promise} - Returns the users current balance in "Balance" table. * Returns 0 if no number was found (default value). */ export async function checkBalance(userID: string): Promise { - const userBalance = await Wallet.findOne({ where: { userid: userID } }); + const userBalance = await Balance.findOne({ where: { userid: userID } }); if (userBalance) { console.log(`${userID} has a balance of ${userBalance.balance}`); return userBalance.balance; } else { - await findOrAddUserWallet(userID); + await findOrAddUserBalance(userID); return 0; } }; /** * Add or Subtract the 'balance' associated with the users Discord ID- - * -in the "Wallet" table by 'amount'. + * -in the "Balance" table by 'amount'. * * @param {string}userID - Users Discord ID * @param {number}amount - Positive or Negative integer to Add or Subtract 'balance' by */ export async function addOrSubtractBalance(userID: string, amount: number): Promise { - await Wallet.increment({ balance: amount }, { where: { userid: userID } }); + await Balance.increment({ balance: amount }, { where: { userid: userID } }); console.log(`${userID}'s wallet has been changed by: ${amount}`); } /** - * Checks if there is a row in the "Wallet" table that contains the users Discord ID- + * Checks if there is a row in the "Balance" table that contains the users Discord ID- * -creates one if it is not found. * * @param {string} userID - Users Discord ID */ -export async function findOrAddUserWallet(userID: string): Promise { - await Wallet.findOrCreate({ where: { userid: userID } }); - console.log(`Found or created Wallet for ${userID}`); +export async function findOrAddUserBalance(userID: string): Promise { + await Balance.findOrCreate({ where: { userid: userID } }); + console.log(`Found or created Balance for ${userID}`); } /** @@ -112,7 +112,7 @@ export async function addNewGacha(userID: string, gachaURL: string): Promise => { try { await User.sync(); - console.log(`UsersDB synced`); + console.log('UsersDB synced'); - await Wallet.sync(); - console.log(`UserWallets synced`); + await Balance.sync(); + console.log('UserBalances synced'); - await GachaInv.sync(); - console.log(`GachaInvs synced`); + await Gacha.sync(); + console.log('GachaInvs synced'); + await Inventory.sync(); + console.log('Inventory synced'); await DailyWheel.sync(); - console.log(`DailyWheel synced`); + console.log('DailyWheel synced'); } catch { console.log('Failed to sync table(s)'); } diff --git a/src/commands/balance.ts b/src/commands/balance.ts index 3370932..d2a75f0 100644 --- a/src/commands/balance.ts +++ b/src/commands/balance.ts @@ -1,5 +1,5 @@ import { SlashCommandBuilder, EmbedBuilder, ChatInputCommandInteraction, User } from 'discord.js'; -import { checkUser, checkBalance, addOrSubtractBalance, addUser, findOrAddUserWallet } from '../DBUtils'; +import { checkUser, checkBalance, addOrSubtractBalance, addUser, findOrAddUserBalance } from '../DBUtils'; import { replyWithEmbed } from '../utils'; export const Balance = { @@ -72,7 +72,7 @@ export const Balance = { await addUser(target.id, target.username); } - await findOrAddUserWallet(target.id); + await findOrAddUserBalance(target.id); //checks targets current balance, then adds 'add' to it (can be negative) if (add !== null) { @@ -88,7 +88,7 @@ export const Balance = { } const embed = new EmbedBuilder() - .setAuthor({ name: `${target.displayName}'s Wallet`, iconURL: target.displayAvatarURL() }) + .setAuthor({ name: `${target.displayName}'s Balance`, iconURL: target.displayAvatarURL() }) .addFields({ name: ' ', value: `Balance: ${targetBalance}` }); //if no 'add' was specified, reply with embed of balance diff --git a/src/commands/roll.ts b/src/commands/roll.ts index 97bbaee..72b4a7f 100644 --- a/src/commands/roll.ts +++ b/src/commands/roll.ts @@ -3,7 +3,7 @@ import { SlashCommandBuilder, CommandInteraction, EmbedBuilder } from "discord.j import { replyWithEmbed, rng } from '../utils'; import { addNewGacha, - findOrAddUserWallet, + findOrAddUserBalance, addOrSubtractBalance, checkBalance, gachaLvlUp, @@ -24,7 +24,7 @@ export const Roll = { const userID = interaction.user.id; - await findOrAddUserWallet(userID); + await findOrAddUserBalance(userID); let currentBalance = await checkBalance(userID); From 71278a01cc6ed7d0fc77a895b60c30fedbcebce6 Mon Sep 17 00:00:00 2001 From: Dylan Stauch <50031006+sjkd23@users.noreply.github.com> Date: Mon, 11 Sep 2023 06:02:43 -0400 Subject: [PATCH 03/17] roll command, multiple rolls at once, cycle through pictures you got, roll again with button. | database commands | Schema re-org | formatting | --- .gitignore | 4 +- src/DBMain.ts | 140 +++++++++++++++++ src/DBUtils.ts | 186 +++++------------------ src/JeffreyDB.ts | 105 +++++-------- src/JeffreyGacha.ts | 322 +++++++++++----------------------------- src/commands/balance.ts | 46 +++--- src/commands/poll.ts | 6 +- src/commands/roll.ts | 270 +++++++++++++++++++++++---------- src/index.ts | 46 ++---- src/utils.ts | 47 +++++- 10 files changed, 572 insertions(+), 600 deletions(-) create mode 100644 src/DBMain.ts diff --git a/.gitignore b/.gitignore index 85e6ccf..c48a3c4 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,6 @@ node_modules .env test.ts .DS_Store -*.db \ No newline at end of file +*.db +*.sql +settings.json \ No newline at end of file diff --git a/src/DBMain.ts b/src/DBMain.ts new file mode 100644 index 0000000..4f167ae --- /dev/null +++ b/src/DBMain.ts @@ -0,0 +1,140 @@ +import { User, Wallet, Gacha, Collection } from "./JeffreyDB"; + +/** + * Checks if the given userID is in the "User" table, if not add it. + * + * @param {string} userID - Discord ID + * @param {string} username - Discord Username + * @param {string} displayName - Discord users Display Name in that server + * @returns {Promise} - Returns 'User' object which contains all information from the found/created row in table. + */ +export async function findOrAddToUser(userID: string, username: string, displayName: string): Promise { + + const [user, created] = await User.findOrCreate({ where: { id: userID, name: username, display_name: displayName } }); + if (created) { + console.log(`Created new "User" row for - User: ${userID}`); + } else { + console.log(`User already in database "User" - User: ${userID}`); + } + return user; +} + +/** + * Searches for the 'balance' associated the given user ID. + * If none was found, creates one. + * The user ID is the same for that user in every database table that contains a user_id. + * + * @param {string} userID - user ID that is consistent across all database tables + * @returns {Promise} - Returns the users current balance in "Wallet" table. + */ +export async function checkOrStartWallet(userID: string): Promise { + const [userWallet, created] = await Wallet.findOrCreate({ where: { user_id: userID } }); + + if (created) { + console.log(`Created new "Wallet" row for - User: ${userID}`); + return 0; + } else { + console.log(`User already in database "Wallet" - User: ${userID}`); + } + + console.log(`User: ${userID} has a balance of ${userWallet.balance}`); + return userWallet.balance; +} + +/** + * Add or Subtract the 'balance' associated with the user ID in the "Wallet" table by 'amount'. + * Function should only be used if you know that the row with 'user_id' exists (ie: call the checkOrStartWallet) + * + * @param {string} userID - Users Discord ID + * @param {number} amount - Positive or Negative integer to Add or Subtract 'balance' by + */ +export async function addOrSubtractWallet(userID: string, amount: number): Promise { + + await Wallet.increment({ balance: amount }, { where: { user_id: userID } }); + + console.log(`${userID}'s wallet has been changed by: ${amount}`); +} + +/** + * Checks for a row in GachaInv that contains both 'userID' and 'gachaID'. + * Looking for if the user has previously aquired this gacha.(ie: if its a duplicate) + * If its a duplicate, increase its level by 1 (up to 5); + * + * @param {string} userID - Users Discord ID + * @param {number} gachaID - ID for the gacha they have aquired + * @returns {Promise} - 'Collection' object contains all information in affected row. + */ +export async function levelUpOrAddGachaToCollection(userID: string, gachaID: number): Promise { + const [gacha, created] = await Collection.findOrCreate({ where: { user_id: userID, gacha_id: gachaID } }); + if (!created) { + if (gacha.level < 5) { + const [leveledGacha] = await Collection.increment({ level: 1 }, { where: { user_id: userID, gacha_id: gachaID } }); + gacha.level += 1; + return gacha; + } else { + return gacha; + } + } else { + console.log(`${gachaID} added to Collection for - User: ${userID}`); + return gacha; + } +} + +/** + * Checks the 'level' column that is in the same row as userID and gachaID + * Should only be used when you know that gachaID exists in the same row as userID + * + * @param {string} userID - Users Discord ID + * @param {number} gachaID - ID associated with specific gacha + * @returns {Promise} - The 'amt' column in the row containing 'userID' and 'gachaID' + */ +export async function checkGachaLevel(userID: string, gachaID: number): Promise { + const lvl = await Collection.findOne({ where: { user_id: userID, gacha_id: gachaID } }); + if (lvl) { + return lvl.level; + } else { + return 0; + } +}; +/** + * Get the link, name, description and rarity from a specific Gacha. + * + * @param {number} gachaID - Gacha ID associated with a specifc Gacha. + * @returns {Promise} - returns the 'Gacha' object which contains information about a specifc gacha + */ +export async function gachaInfo(gachaID: number): Promise { + try{ + const gachaInfo = await Gacha.findOne({ where: { id: gachaID } }); + return gachaInfo; + }catch(e){ + console.error(e); + return null; + } + +} +/** + * + * + * @param {string} userID - Users Discord ID + * @param gachaID - ID associated with a specific Gacha + * @returns {Promise} - 'Collection' object containing information on specified row in table. + * - Also returns true if Gacha is max level. + */ +export async function addToCollection(userID: string, gachaID: number): Promise { + const gacha = await Gacha.findOne({ where: { id: gachaID } }) + const [userGacha, created] = await Collection.findOrCreate({ where: { user_id: userID, gacha_id: gacha!.id } }); + + if (!created) { + if (userGacha.level < 5) { + await userGacha.increment({ level: 1 }); + await userGacha.reload(); + console.log(`Increased level of - GachaID: ${gachaID} for - User: ${userID} `); + return userGacha; + } else { + return [userGacha, true]; + } + } else { + console.log(`GachaID: ${gachaID} - has been added to the Collection of - User: ${userID}`); + return userGacha + } +} diff --git a/src/DBUtils.ts b/src/DBUtils.ts index 971321e..a901552 100644 --- a/src/DBUtils.ts +++ b/src/DBUtils.ts @@ -1,170 +1,64 @@ -import { User, Balance, Gacha, DailyWheel } from "./JeffreyDB" -/** - * Checks if the given userID is in the "User" table. - * - * @param {string} userID - Users Discord ID - * @returns {Promise}- True or False (whether or not their userID is in "User" table) - */ -export async function checkUser(userID: string): Promise { +import { Gacha } from './JeffreyDB'; +import { rng } from "./utils"; - const user = await User.findOne({ where: { userid: userID } }); - if (user) { - console.log(`user ${user.id} found in Users`); - return true; - } else { - console.log(`user ${userID} NOT found in Users`); - return false; - } -} /** - * Adds a row in the "User" table, which contains a users discord ID and discord Username. + * Takes a rarity, randomly picks one gacha among that rarity, returns it * - * @param {string} userID - Users Discord ID - * @param {string} username - Users Discord Username(not displayname) + * @param {string} rarity - Which rarity it should choose from + * @returns {Promise} - random 'rarity' gacha */ -export async function addUser(userID: string, username: string): Promise { +export async function getRandomGacha(rarity: string): Promise { - await User.create({ userid: userID, username: username }); - console.log(`added ${userID} to Users`); -} + const allGachaOfRarity = await Gacha.findAll({ where: { rarity: rarity } }); -/** - * Searches for the 'balance' associated the given users Discord ID. - * - * If no balance was found, it calls another function to create a new row in "Balance"- - * -which contains the users Discord ID, and 'balance' defaults to 0. - * - * (balance is used in a monetary context $$$) - * - * @param {string} userID - Users Discord ID - * @returns {Promise} - Returns the users current balance in "Balance" table. - * Returns 0 if no number was found (default value). - */ -export async function checkBalance(userID: string): Promise { - const userBalance = await Balance.findOne({ where: { userid: userID } }); - if (userBalance) { - console.log(`${userID} has a balance of ${userBalance.balance}`); - return userBalance.balance; - } else { - await findOrAddUserBalance(userID); - return 0; - } -}; + const randomGacha = Math.floor(Math.random() * Math.floor(allGachaOfRarity.length)); -/** - * Add or Subtract the 'balance' associated with the users Discord ID- - * -in the "Balance" table by 'amount'. - * - * @param {string}userID - Users Discord ID - * @param {number}amount - Positive or Negative integer to Add or Subtract 'balance' by - */ -export async function addOrSubtractBalance(userID: string, amount: number): Promise { - await Balance.increment({ balance: amount }, { where: { userid: userID } }); - console.log(`${userID}'s wallet has been changed by: ${amount}`); -} + const gacha = allGachaOfRarity[randomGacha]; -/** - * Checks if there is a row in the "Balance" table that contains the users Discord ID- - * -creates one if it is not found. - * - * @param {string} userID - Users Discord ID - */ -export async function findOrAddUserBalance(userID: string): Promise { - await Balance.findOrCreate({ where: { userid: userID } }); - console.log(`Found or created Balance for ${userID}`); + return gacha; } /** - * Checks for a row in GachaInv that contains both 'userID' and 'gachaURL'. - * Looking for if the user has previously aquired this gacha.(ie: if its a duplicate) + * Calls getRandomGacha and specifies that it wants a 'legendary' gacha * - * @param {string} userID - Users Discord ID - * @param {string} gachaURL - URL associated with a specific gacha item (picture). - * @returns {Promise} - True or False (whether or not userID is in the same row as gachaURL in "GachaInv" table) + * @returns {Promise} - 'Gacha' object containing table information about a specific Gacha */ -export async function checkIfUserHasGachaInv(userID: string, gachaURL: string): Promise { - const user = await GachaInv.findOne({ where: { userid: userID, gachas: gachaURL } }); - if (user) { - console.log(`found ${userID} in GachaInvs`); - return true; - } else { - console.log(`did NOT find ${userID} in GachaInvs`); - return false; - } -} - -/** - * Adds a row in "GachaInv" table containing the users Disord ID, their new gacha picture, and setting amt to 1. - * This function must be called only when we know this row does not already exist (ie: call the checkGachaInv function before this). - * - * @param {string} userID - Users Discord ID - * @param {string} achaURL - URL associated with a specific gacha item (picture). - */ -export async function addNewGacha(userID: string, gachaURL: string): Promise { - await GachaInv.create({ userid: userID, gachas: gachaURL, amt: 1 }); - console.log(`${userID} added to GachaInvs with their new ${gachaURL}`); -} +export async function getRandomLegendary(): Promise { -/** - * Increases the amount ('amt') column in the row that contains both userID and gachaURL. - * Function should only be called when it is confirmed that a row exists with 'userID' and 'gachaURL' (ie: the 'checkGachaInv' function). - * - * @example - * (Balance table) - * table starts with: | userID: 8743284 | gachas: https:/sajdjioafh.png | amt: 1 | - * table becomes: | userID: 8743284 | gachas: https:/sajdjioafh.png | amt: 2 | - * - * @param userID - Users Discord ID - * @param gachaURL - URL associated with a specific gacha item (picture). - */ -export async function gachaLvlUp(userID: string, gachaURL: string): Promise { - await GachaInv.increment({ amt: 1 }, { where: { userid: userID, gachas: gachaURL } }); - console.log(`increased ${gachaURL}'s level by 1 for ${userID}!`); + const legendaryGacha = await getRandomGacha('legendary'); + + return legendaryGacha; } /** - * Checks the 'amt' number column that is in the row of the users Discord ID and Gacha URL/ID. + * Randomly selects rarity, then calls getRandomGacha to choose a random gacha of that rarity * - * @param {string} userID - Users Discord ID - * @param {string} gachaURL - URL associated with a specific gacha item (picture). - * @returns {Promise} - The 'amt' column in the row containing 'userID' and 'gachaURL' - * Returns 0 if no 'amt' was found.(default value is 1, so 0 is impossible for 'amt') + * @param {number} rolls - The number of gacha that should be returned + * @returns {Promise} - 'Gacha' object containing table information about a random gacha of random rarity. */ -export async function checkGachaLevel(userID: string, gachaURL: string): Promise { - const level = await GachaInv.findOne({ where: { userid: userID, gachas: gachaURL } }); - if (level) { - return level.amt; - } else { - return 0; +export async function rollForGacha(rolls: number): Promise { + const gacha: Gacha[] = []; + + for(let i = 0; i < rolls; i++){ + + const rndm = await rng(0, 100); + let rarity: string; + + if (rndm <= 65) { + rarity = 'common'; + } else if (rndm > 65 && rndm <= 90) { + rarity = 'uncommon'; + } else if (rndm > 90 && rndm < 99) { + rarity = 'rare'; + } else if (rndm >= 100) { + rarity = 'legendary'; } -}; -/** - * Checks if the users Discord ID is in the DailyWheel table, - * if not, create a new row with the userID - * - * @param {string} userID - Users Discord ID - */ -export async function findOrCreateDailyWheel(userID: string): Promise { - - await DailyWheel.findOrCreate({ where: { userid: userID } }); - console.log(`${userID} found or created in DailyWheel`); -}; -/** - * Checks how many spins are left for given userID (Discord ID), - * and when the wheel resets (every 24 hours) - * - * @param {string} userID - Users Discord ID - * @returns {[number, Date]} - How many spins left, and when the users wheel will regain its available spins. - */ -export async function checkSpins(userID: string): Promise<[number, Date]> { - - const wheel = await DailyWheel.findOne({ where: { userid: userID } }); - const endTime = new Date(wheel!.createdAt); - - endTime.setHours(endTime.getHours() + 24); + const randomGacha = await getRandomGacha(rarity!); + gacha.push(randomGacha); +} - return [wheel!.spins, endTime]; -}; \ No newline at end of file + return gacha; +} \ No newline at end of file diff --git a/src/JeffreyDB.ts b/src/JeffreyDB.ts index dfa8a4c..5e69edc 100644 --- a/src/JeffreyDB.ts +++ b/src/JeffreyDB.ts @@ -6,26 +6,20 @@ export const sequelize = new Sequelize({ logging: false, }); - // Creates the "User" table with: // @example (id is Discord user ID, username is Discord Username, display_name is their nickname in that server) // // | id:'667951424704872450' | username: 'jeffreyhassalide' | display_name: Jeffrey | export class User extends Model { - declare id: number; - declare discord_id: string; + declare id: string; declare name: string; declare display_name: string; } User.init({ id: { - type: DataTypes.INTEGER, - allowNull: false, - primaryKey: true, - }, - discord_id: { type: DataTypes.STRING, allowNull: false, + primaryKey: true, unique: true, }, name: { @@ -39,16 +33,16 @@ User.init({ }, }, { sequelize }); -// Creates the "Balance" table with +// Creates the "Wallet" table with // @example(id is a unique ID for each row, userid is users Discord ID, balance is how much money they have) // // | id: 5 | userid: 667951424704872450 | balance: 10 | -export class Balance extends Model { +export class Wallet extends Model { declare id: number; declare user_id: string; declare balance: number; } -Balance.init({ +Wallet.init({ id: { type: DataTypes.INTEGER, primaryKey: true, @@ -71,7 +65,7 @@ Balance.init({ defaultValue: 0, } }, { sequelize }); -User.hasOne(Balance, { foreignKey: 'user_id', sourceKey: 'id' }); +User.hasOne(Wallet, { foreignKey: 'user_id', sourceKey: 'id' }); // Creates the "Gacha" table with // @example @@ -81,6 +75,7 @@ export class Gacha extends Model { declare link: string; declare name: string; declare description: string; + declare rarity: string; } Gacha.init({ @@ -88,90 +83,70 @@ Gacha.init({ type: DataTypes.INTEGER, primaryKey: true, allowNull: false, + unique: true, autoIncrement: true, }, link: { type: DataTypes.STRING, - unique: true, allowNull: false, + unique: true, }, name: { type: DataTypes.STRING, allowNull: false, - unique: true, }, description: { type: DataTypes.STRING, allowNull: false, - unique: true, }, -}, { sequelize }); - -export class Inventory extends Model { - declare id: number; - declare user_id: number; - declare gacha_id: number; - declare balance_id: number; -} -Inventory.init({ - id: { - type: DataTypes.INTEGER, - primaryKey: true, - allowNull: false, - unique: false, - }, - user_id: { - type: DataTypes.INTEGER, + rarity: { + type: DataTypes.STRING, allowNull: false, unique: false, }, - gacha_id: { - type: DataTypes.INTEGER, - unique: false, - }, - balance_id: { - type: DataTypes.INTEGER, - unique: false, - allowNull: false, - }, }, { sequelize }); -User.hasOne(Inventory, {foreignKey: 'user_id', sourceKey: 'id'}); -Balance.hasOne(Inventory, {foreignKey: 'balance_id', sourceKey: 'id'}); -Gacha.hasMany(Inventory, {foreignKey: 'gacha_id', sourceKey: 'id'}) -export class DailyWheel extends Model { + +export class Collection extends Model{ declare id: number; - declare userid: number; - declare spins: number; - declare createdAt: string; + declare user_id: string; + declare gacha_id: number; + declare level: number; } - -DailyWheel.init({ +Collection.init({ id: { type: DataTypes.INTEGER, primaryKey: true, allowNull: false, + unique: true, autoIncrement: true, - defaultValue: 1, }, - userid: { + user_id: { type: DataTypes.STRING, - unique: true, allowNull: false, + unique: false, references: { model: User, key: 'id', }, }, - spins: { + gacha_id: { type: DataTypes.INTEGER, allowNull: false, - defaultValue: 5, + unique: false, + references: { + model: Gacha, + key: 'id', + }, }, - createdAt: { - type: DataTypes.DATE, + level: { + type: DataTypes.INTEGER, allowNull: false, + unique: false, + defaultValue: 1, }, }, { sequelize }); +User.hasMany(Collection, {foreignKey: 'user_id', sourceKey: 'id'}); +Gacha.hasMany(Collection, {foreignKey: 'gacha_id', sourceKey: 'id'}); // Contains'test' and 'sync' export const DB = { @@ -197,18 +172,18 @@ export const DB = { await User.sync(); console.log('UsersDB synced'); - await Balance.sync(); - console.log('UserBalances synced'); - + await Wallet.sync(); + console.log('UserWallets synced'); + await Gacha.sync(); console.log('GachaInvs synced'); - await Inventory.sync(); - console.log('Inventory synced'); - await DailyWheel.sync(); - console.log('DailyWheel synced'); - } catch { + await Collection.sync() + console.log ('Collection sunced'); + + }catch(e){ console.log('Failed to sync table(s)'); + console.error(e); } } }; \ No newline at end of file diff --git a/src/JeffreyGacha.ts b/src/JeffreyGacha.ts index 1d6f8b6..cefc21b 100644 --- a/src/JeffreyGacha.ts +++ b/src/JeffreyGacha.ts @@ -1,249 +1,93 @@ -import { DailyWheel } from './JeffreyDB'; import { rng } from './utils'; -type GachaURLType = { - link: string; - name: string; - description: string; -}; //Object containing the 4 rarities, 'Common', 'Uncommon', 'Rare' and 'Legendary'. //Each rarity is an array of gacha objects, containing the link(URL), name and description of all gachas. -export const JeffreyGachaURLs: { [key: string]: GachaURLType[] } = { - Common: [ - { link: 'https://imgur.com/iSHcsOd.png', name: '', description: '' }, - { link: 'https://imgur.com/Oyh1TiC.png', name: '', description: '' }, - { link: 'https://imgur.com/hr1tx64.png', name: '', description: '' }, - { link: 'https://imgur.com/xQqwrWD.png', name: '', description: '' }, - { link: 'https://imgur.com/4mrEWuV.png', name: '', description: '' }, - { link: 'https://imgur.com/cSprAX4.png', name: '', description: '' }, - { link: 'https://imgur.com/DaCVzq8.png', name: '', description: '' }, - { link: 'https://imgur.com/Z2LaGwY.png', name: '', description: '' }, - { link: 'https://imgur.com/mx70oY2.png', name: '', description: '' }, - { link: 'https://imgur.com/AivmZ0T.png', name: '', description: '' }, - { link: 'https://imgur.com/6dD6wHv.png', name: '', description: '' }, - { link: 'https://imgur.com/22niPvw.png', name: '', description: '' }, - { link: 'https://imgur.com/sqnH5tg.png', name: '', description: '' }, - { link: 'https://imgur.com/nXo9YbZ.png', name: '', description: '' }, - { link: 'https://imgur.com/po28ZPo.png', name: '', description: '' }, - { link: 'https://imgur.com/zEfh9G1.png', name: '', description: '' }, - { link: 'https://imgur.com/MsftbNA.png', name: '', description: '' }, - { link: 'https://imgur.com/GJFRw0N.png', name: '', description: '' }, - { link: 'https://imgur.com/Qgi4yeQ.png', name: '', description: '' }, - { link: 'https://imgur.com/bpVQ6xz.png', name: '', description: '' }, - { link: 'https://imgur.com/DdhzNO2.png', name: '', description: '' }, - { link: 'https://imgur.com/AvqpwfX.png', name: '', description: '' }, - { link: 'https://imgur.com/RuOtxcv.png', name: '', description: '' }, - { link: 'https://imgur.com/6WkntdW.png', name: '', description: '' }, - { link: 'https://imgur.com/40M1KOh.png', name: '', description: '' }, - { link: 'https://imgur.com/ZtPZZV9.png', name: '', description: '' } - ], - - Uncommon: [ - { link: 'https://imgur.com/txgkO4S.png', name: '', description: '' }, - { link: 'https://imgur.com/gqbRfhW.png', name: '', description: '' }, - { link: 'https://imgur.com/qYIxVan.png', name: '', description: '' }, - { link: 'https://imgur.com/KBwG10J.png', name: '', description: '' }, - { link: 'https://imgur.com/dord5jv.png', name: '', description: '' }, - { link: 'https://imgur.com/WGDO515.png', name: '', description: '' }, - { link: 'https://imgur.com/JujU88m.png', name: '', description: '' }, - { link: 'https://imgur.com/6ur3hgK.png', name: '', description: '' }, - { link: 'https://imgur.com/J1Ozvgw.png', name: '', description: '' }, - { link: 'https://imgur.com/DQDZFjA.png', name: '', description: '' }, - { link: 'https://imgur.com/15p7sDo.png', name: '', description: '' }, - { link: 'https://imgur.com/ASOaLa7.png', name: '', description: '' }, - { link: 'https://imgur.com/kSo1xYB.png', name: '', description: '' }, - { link: 'https://imgur.com/vWkhS1P.png', name: '', description: '' }, - { link: 'https://imgur.com/FirGg1T.png', name: '', description: '' }, - { link: 'https://imgur.com/WVAcNOm.png', name: '', description: '' }, - { link: 'https://imgur.com/57yMtrl.png', name: '', description: '' }, - { link: 'https://imgur.com/j00IjET.png', name: '', description: '' }, - { link: 'https://imgur.com/DnWGWJS.png', name: '', description: '' }, - { link: 'https://imgur.com/OqOol5X.png', name: '', description: '' }, - { link: 'https://imgur.com/xryAFJi.png', name: '', description: '' }, - { link: 'https://imgur.com/WYGcMzt.png', name: '', description: '' }, - { link: 'https://imgur.com/jhIC3Js.png', name: '', description: '' }, - { link: 'https://imgur.com/Qh7TDY3.png', name: '', description: '' }, - { link: 'https://imgur.com/NaKE5Y2.png', name: '', description: '' }, - { link: 'https://imgur.com/M2wJBt9.png', name: '', description: '' }, - { link: 'https://imgur.com/voMHJ9n.png', name: '', description: '' }, - { link: 'https://imgur.com/z81zoZ9.png', name: '', description: '' }, - { link: 'https://imgur.com/mQB4xHr.png', name: '', description: '' } - ], - - Rare: [ - { link: 'https://imgur.com/eNx2Ntg.png', name: '', description: '' }, - { link: 'https://imgur.com/moIgaas.png', name: '', description: '' }, - { link: 'https://imgur.com/M2wJBt9.png', name: '', description: '' }, - { link: 'https://imgur.com/NaKE5Y2.png', name: '', description: '' }, - { link: 'https://imgur.com/KBwG10J.png', name: '', description: '' }, - { link: 'https://imgur.com/D1cuR7k.png', name: '', description: '' }, - { link: 'https://imgur.com/rt6lIPt.png', name: '', description: '' }, - { link: 'https://imgur.com/vvWZ384.png', name: '', description: '' }, - { link: 'https://imgur.com/UFNSYrP.png', name: '', description: '' }, - { link: 'https://imgur.com/CKfOGtA.png', name: '', description: '' }, - { link: 'https://imgur.com/dVGsAIo.png', name: '', description: '' }, - { link: 'https://imgur.com/JujU88m.png', name: '', description: '' }, - { link: 'https://imgur.com/KnTrE2e.png', name: '', description: '' }, - { link: 'https://imgur.com/yjh3yZg.png', name: '', description: '' }, - { link: 'https://imgur.com/BbtiZwy.png', name: '', description: '' }, - { link: 'https://imgur.com/LijfPss.png', name: '', description: '' }, - { link: 'https://imgur.com/GnnuMI5.png', name: '', description: '' }, - { link: 'https://imgur.com/eGPRhm5.png', name: '', description: '' }, - { link: 'https://imgur.com/l8uOmAq.png', name: '', description: '' } - ], - - Legendary: [ - { link: 'https://imgur.com/miHJetv.png', name: 'Radiant Jeffrey', description: 'Our Lord and Savior Jeffrey' }, - { link: 'https://imgur.com/tbS4faL.png', name: 'Radiant Jeffrey', description: 'OK FINE ILL FEED YOU NOW!' }, - { link: 'https://imgur.com/5QfM3zQ.png', name: 'Dead Jeffrey', description: 'RIP. Fly high king' }, - { link: 'https://imgur.com/xhWXOIA.png', name: 'Box Jeffrey', description: 'Wow, soooo original Jeffrey. Never seen a cat do THAT before.' }, - { link: 'https://imgur.com/okvejbp.png', name: 'Disturbed Jeffrey', description: 'I don\'t think he\'s a fan...' }, - { link: 'https://imgur.com/sdLYctZ.png', name: 'Cuddle Jeffrey', description: 'Wittle Cutie!' }, - { link: 'https://imgur.com/CYvTDSC.png', name: 'Not Jeffrey', description: 'Hey, that\'s not Jeffrey!' }, - { link: 'https://imgur.com/jmKtrlf.png', name: 'Peaceful Jeffrey', description: 'That\'s about as peacful as its gonna get...' }, - { link: 'https://imgur.com/Y1i2gMP.png', name: 'Attack Jeffrey', description: 'That bite\'s lethal!' }, - { link: 'https://imgur.com/3aWCqqe.png', name: 'Caught Jeffrey', description: 'Hey, what going on over here!' }, - { link: 'https://imgur.com/eTGaGLM.png', name: 'Lap-Cat Jeffrey', description: 'He\'s been know to peruse a lap or two.' }, - { link: 'https://imgur.com/7YdKWjS.png', name: 'GateKeeper Jeffrey', description: 'Come on Jeffrey, that\'s not very nice!' } - ] -} -export const WheelResults: { [key: string]: { Name: string; Description: string } } = { - OneCoin: { - Name: '1 Coin...', - Description: 'Wow thats lame...' - }, - TenCoins: { - Name: '10 Coins', - Description: 'Hey, at least its something.' - }, - TwentyFiveCoins: { - Name: '25 coins', - Description: 'Could be more, but not terrible!' - }, - FiftyCoins: { - Name: '50 Coins', - Description: 'Solid spin!' - }, - SixtyNineCoins: { - Name: '69 coins', - Description: 'Nice' - }, - OneHundredCoins: { - Name: '100 coins!', - Description: 'One hundred? Thats TEN rolls if you did them individually! I wouldn\'t recommend that though.' - }, - OneFiftyCoins: { - Name: '150 coins!', - Description: 'Awesome! Perfect for TEN rolls (if you do them five at a time)!' - }, - FourTwentyCoins: { - Name: '420 coins', - Description: 'Blaze it' - }, - Legendary: { - Name: 'LEGENDARY!?', - Description: 'You got a legendary from the wheel? I didn\'t even know that was possible!' - }, - Jackpot: { - Name: ' A JACKPOT!!!!!!!!!', - Description: 'OMG. Ok im starting to think this creator sucks at balancing.' - } +export const GACHA_URL_LIST = [ + { link: 'https://imgur.com/Oyh1TiC.png', name: 'Prepared', description: 'He\'s comfy, but not *too* comfy...', rarity: 'common' }, + { link: 'https://imgur.com/hr1tx64.png', name: 'Soul Stare', description: 'Those tiny pupil\'s can see your true intentions.', rarity: 'common' }, + { link: 'https://imgur.com/xQqwrWD.png', name: 'Blue Blanket', description: 'He prefers the softer, cozier douvet, but grandma\'s quilt will do.', rarity: 'common' }, + { link: 'https://imgur.com/4mrEWuV.png', name: 'You Can Leave Now', description: '\"Let me get some rest, Human!\"', rarity: 'common' }, + { link: 'https://imgur.com/iSHcsOd.png', name: 'Doesn\'t Notice', description: 'This silly billy doesn\'t even know we\'re here!', rarity: 'common' }, + { link: 'https://imgur.com/cSprAX4.png', name: 'Tail-Pillow', description: 'A multi-purpose apendage! (let\'s pretend he isn\'t using his paws)', rarity: 'common' }, + { link: 'https://imgur.com/DaCVzq8.png', name: 'Skyblock', description: 'He wants to try!', rarity: 'common' }, + { link: 'https://imgur.com/Z2LaGwY.png', name: 'Sweet', description: 'Get it? Cuz of the sugarcane? Yeah, im not getting lazy with these, im just clever.', rarity: 'common' }, + { link: 'https://imgur.com/mx70oY2.png', name: 'Are You Even Listening', description: '\"FOCUS ON ME HUMAN\"', rarity: 'common' }, + { link: 'https://imgur.com/AivmZ0T.png', name: 'Bed-Head', description: 'Man, he looks rough.', rarity: 'common' }, + { link: 'https://imgur.com/6dD6wHv.png', name: 'Daring', description: 'He made the jump (probably)', rarity: 'common' }, + { link: 'https://imgur.com/sqnH5tg.png', name: 'Strategic', description: 'He could build a much better empire.', rarity: 'common' }, + { link: 'https://imgur.com/nXo9YbZ.png', name: 'Living On The Edge', description: 'He does this way too often.', rarity: 'common' }, + { link: 'https://imgur.com/po28ZPo.png', name: 'Attack On', description: 'The green thing is an AOT cloak (it\'s *very* soft).', rarity: 'common' }, + { link: 'https://imgur.com/zEfh9G1.png', name: 'Laundry', description: 'Silly Jeffrey, you aren\'t clothes!', rarity: 'common' }, + { link: 'https://imgur.com/MsftbNA.png', name: 'Red Blanket', description: 'That\'s won tiewd wittuwl meow meow.', rarity: 'common' }, + { link: 'https://imgur.com/GJFRw0N.png', name: 'Spot-Stealing', description: '\"Oh, was someone sitting here?\"', rarity: 'common' }, + { link: 'https://imgur.com/Qgi4yeQ.png', name: 'Desk Bed', description: 'The first picture of Jeffrey on the desk-bed! (at least, while its on the desk)', rarity: 'common' }, + { link: 'https://imgur.com/bpVQ6xz.png', name: 'Coat', description: 'My sister wasn\'t too happy when I showed her this one.', rarity: 'common' }, + { link: 'https://imgur.com/DdhzNO2.png', name: 'TV-Time', description: 'His favourite position for watching TV with us! (The tv is directly behind him...)', rarity: 'common' }, + { link: 'https://imgur.com/AvqpwfX.png', name: 'Severed Head', description: 'At least he still has his best feature\'s!', rarity: 'common' }, + { link: 'https://imgur.com/RuOtxcv.png', name: 'Dog Bed', description: 'Wrong bed silly, that one\'s way too big!', rarity: 'common' }, + { link: 'https://imgur.com/6WkntdW.png', name: 'Morning Selfie', description: 'Not his best look.', rarity: 'common' }, + { link: 'https://imgur.com/40M1KOh.png', name: 'Lean-Back', description: 'What a strange sleeping positon.', rarity: 'common' }, + { link: 'https://imgur.com/ZtPZZV9.png', name: 'Itchy-Ear', description: 'They say this is the only picture in existence of Jeffrey scratching his ear!', rarity: 'common' }, -} + { link: 'https://imgur.com/txgkO4S.png', name: 'Shower', description: 'Would be a shame if I were to...', rarity: 'uncommon' }, + { link: 'https://imgur.com/gqbRfhW.png', name: 'Cats Only', description: '\"No Humans allowed.\"', rarity: 'uncommon' }, + { link: 'https://imgur.com/qYIxVan.png', name: 'Bunker', description: 'With his perfect defense, he\'s ready to attack!', rarity: 'uncommon' }, + { link: 'https://imgur.com/dord5jv.png', name: 'Sunglass', description: '\"Hey Human, can you buy me some sunglasses?\"', rarity: 'uncommon' }, + { link: 'https://imgur.com/WGDO515.png', name: 'Superior', description: '\"I am higher than you, so I am better than you.\"', rarity: 'uncommon' }, + { link: 'https://imgur.com/6ur3hgK.png', name: 'Photo Pose', description: 'Does he think he\'s in a photoshoot or something?', rarity: 'uncommon' }, + { link: 'https://imgur.com/J1Ozvgw.png', name: 'I\'ve Been Waiting', description: '\"What took you so long?\"', rarity: 'uncommon' }, + { link: 'https://imgur.com/DQDZFjA.png', name: 'Oops, You Blinked!', description: 'Come on Jeffrey, you have to keep your eye\'s open!', rarity: 'uncommon' }, + { link: 'https://imgur.com/15p7sDo.png', name: 'Basement Time?', description: '\"Human, we shall explore the basement.\"', rarity: 'uncommon' }, + { link: 'https://imgur.com/ASOaLa7.png', name: 'Scenic View', description: 'Jeffrey thought this would be the perfect place for a picture!', rarity: 'uncommon' }, + { link: 'https://imgur.com/kSo1xYB.png', name: 'On The Freezer', description: 'It turns out freezers can actually emit heat!', rarity: 'uncommon' }, + { link: 'https://imgur.com/vWkhS1P.png', name: 'Ceiling', description: 'This might be the highest he\'s ever been!', rarity: 'uncommon' }, + { link: 'https://imgur.com/FirGg1T.png', name: 'Recon Tower', description: 'He\'ll let us know if he see\'s anything. Probably...', rarity: 'uncommon' }, + { link: 'https://imgur.com/WVAcNOm.png', name: 'Linen Closet', description: 'My mom was not impressed.', rarity: 'uncommon' }, + { link: 'https://imgur.com/57yMtrl.png', name: 'Flower', description: 'Jeffrey\'s the REAL flower!', rarity: 'uncommon' }, + { link: 'https://imgur.com/j00IjET.png', name: 'Blurry Carpet', description: 'He\'ll sleep anywhere, and in any quality!', rarity: 'uncommon' }, + { link: 'https://imgur.com/DnWGWJS.png', name: 'Peak-At', description: 'Look at his cute little face under the monitor!', rarity: 'uncommon' }, + { link: 'https://imgur.com/OqOol5X.png', name: 'Trap', description: 'I wouldn\'t try to pet him right now...', rarity: 'uncommon' }, + { link: 'https://imgur.com/xryAFJi.png', name: 'Heater', description: 'CAT CRAVE HEAT', rarity: 'uncommon' }, + { link: 'https://imgur.com/WYGcMzt.png', name: 'Face-Cover', description: 'A make-shift light blocker!', rarity: 'uncommon' }, + { link: 'https://imgur.com/jhIC3Js.png', name: 'Arching', description: 'Jeffrey knows he\'s not suppose to be on the table, but he\'s playing it cool.', rarity: 'uncommon' }, + { link: 'https://imgur.com/Qh7TDY3.png', name: 'Pixel', description: 'Low quality, but still cute!', rarity: 'uncommon' }, + { link: 'https://imgur.com/voMHJ9n.png', name: 'Pillow', description: 'Kind of a weird spot, but ok Jeffrey.', rarity: 'uncommon' }, + { link: 'https://imgur.com/z81zoZ9.png', name: 'Alert', description: 'A very young and very alert Jeffrey.', rarity: 'uncommon' }, + { link: 'https://imgur.com/mQB4xHr.png', name: 'Cat House', description: 'His first cat house (and my phones lockscreen).', rarity: 'uncommon' }, -/** - * Takes a Legendary gacha URL and returns an array containing the link(url), name and description of that gacha. - * - * @param {string} gacha - A Legendary gacha image URL - * @returns {string[]} - gachaInfo, contains legendary info (link, name and description) - */ -export async function displayLegendary(gacha: string): Promise { - const legendary = JeffreyGachaURLs['Legendary']; - for(let i = 0; i < legendary.length; i++){ - if(legendary[i].link === gacha){ - const gachaInfo = [ - legendary[i].name, - legendary[i].name, - legendary[i].description - ]; - return gachaInfo; - } - } - return null; -} + { link: 'https://imgur.com/eNx2Ntg.png', name: 'Snickering', description: 'I know he\'s actually just sleeping, but I can pretend.', rarity: 'rare' }, + { link: 'https://imgur.com/moIgaas.png', name: 'Calm Collar', description: 'It didn\'t work very well, Jeffrey and Kitty still hated eachother.', rarity: 'rare' }, + { link: 'https://imgur.com/M2wJBt9.png', name: 'Birthday Party', description: 'He loves baloons!', rarity: 'rare' }, + { link: 'https://imgur.com/NaKE5Y2.png', name: 'Weird', description: 'What a strange little creature!', rarity: 'rare' }, + { link: 'https://imgur.com/KBwG10J.png', name: 'Pillowcase', description: 'That pillow fell on the floor overnight.', rarity: 'rare' }, + { link: 'https://imgur.com/D1cuR7k.png', name: 'BOX - Kitty vs', description: 'I wonder if they know...', rarity: 'rare' }, + { link: 'https://imgur.com/rt6lIPt.png', name: 'DOUBLE BED - Kitty vs', description: 'A bed on a bed? Jeffrey wants it!', rarity: 'rare' }, + { link: 'https://imgur.com/vvWZ384.png', name: 'MIRRIORED - Kitty vs', description: '\"She\'s right behind me, isn\'t she?\"', rarity: 'rare' }, + { link: 'https://imgur.com/UFNSYrP.png', name: 'TV - Kitty vs', description: 'They just can\'t agree on what to watch!', rarity: 'rare' }, + { link: 'https://imgur.com/CKfOGtA.png', name: 'STARE - Kitty vs', description: 'Kitty\'s staring daggers at our boy!', rarity: 'rare' }, + { link: 'https://imgur.com/dVGsAIo.png', name: 'WATER BOTTLE - Kitty vs', description: 'So, is the water bottle actually doing anything here?', rarity: 'rare' }, + { link: 'https://imgur.com/JujU88m.png', name: 'Box + Boxes', description: 'Jeffrey does love his boxes!', rarity: 'rare' }, + { link: 'https://imgur.com/KnTrE2e.png', name: 'TOWERS - Kitty vs', description: 'A very common scene, that usually led to the same outcome... destruction.', rarity: 'rare' }, + { link: 'https://imgur.com/yjh3yZg.png', name: 'Box Trap', description: 'He\'s ready for shipment!', rarity: 'rare' }, + { link: 'https://imgur.com/BbtiZwy.png', name: 'From Above', description: 'It\'s over Jeffrey, I have the high ground!', rarity: 'rare' }, + { link: 'https://imgur.com/LijfPss.png', name: 'Back Window', description: 'I feel like this shouldn\'t be named that...', rarity: 'rare' }, + { link: 'https://imgur.com/GnnuMI5.png', name: 'Aww', description: 'This picture speaks for itself!', rarity: 'rare' }, + { link: 'https://imgur.com/eGPRhm5.png', name: 'First Cat Bed', description: 'We didn\'t even tell him it was his yet!', rarity: 'rare' }, + { link: 'https://imgur.com/l8uOmAq.png', name: 'Father', description: 'Jeffrey\'s the new Dad!', rarity: 'rare' }, -export async function spinWheel(userID: string): Promise<[string[], number] | null> { - const rndm = await rng(0, 1000); - let reward: string[] = []; - let coins: number = 0; - - try{ - const spin = await DailyWheel.increment({ spins: -1 }, {where: { userid: userID } }); - console.log(`${userID} has used one of their spins!`); - }catch(err){ - console.error(err); - } - - if(rndm < 20){ - reward = [ - WheelResults.OneCoin.Name, - WheelResults.OneCoin.Description - ]; - coins = 1; - }else if(rndm >= 20 && rndm < 150 ){ - reward = [ - WheelResults.TenCoins.Name, - WheelResults.TenCoins.Description - ]; - coins = 10; - }else if(rndm >= 150 && rndm < 400){ - reward = [ - WheelResults.TwentyFiveCoins.Name, - WheelResults.TwentyFiveCoins.Description - ]; - coins = 25; - }else if(rndm >= 400 && rndm < 650 ){ - reward = [ - WheelResults.FiftyCoins.Name, - WheelResults.FiftyCoins.Description - ]; - coins = 50; - }else if(rndm >= 650 && rndm < 750 ){ - reward = [ - WheelResults.SixtyNineCoins.Name, - WheelResults.SixtyNineCoins.Description - ]; - coins = 69; - }else if(rndm >= 750 && rndm < 870){ - reward = [ - WheelResults.OneHundredCoins.Name, - WheelResults.OneHundredCoins.Description - ]; - coins = 100; - }else if(rndm >= 870 && rndm < 970 ){ - reward = [ - WheelResults.OneFiftyCoins.Name, - WheelResults.OneFiftyCoins.Description - ]; - coins = 150; - }else if(rndm >= 970 && rndm < 990 ){ - reward = [ - WheelResults.Legendary.Name, - WheelResults.Legendary.Description - ]; - coins = -1; - }else if(rndm >= 990 && rndm <= 1000){ - reward = [ - WheelResults.Jackpot.Name, - WheelResults.Jackpot.Description - ]; - coins = 1000; - }else{ - console.log(`ERROR: Failed to choose WheelResult`); - return null; - } - return [reward, coins]; -}; + { link: 'https://imgur.com/miHJetv.png', name: 'Radiant', description: 'Our Lord and Savior Jeffrey', rarity: 'legendary' }, + { link: 'https://imgur.com/tbS4faL.png', name: 'Cannibal', description: 'OK FINE ILL FEED YOU NOW!', rarity: 'legendary' }, + { link: 'https://imgur.com/5QfM3zQ.png', name: 'Dead', description: 'RIP. Fly high king', rarity: 'legendary' }, + { link: 'https://imgur.com/xhWXOIA.png', name: 'Box', description: 'Wow, soooo original Jeffrey. Never seen a cat do THAT before.', rarity: 'legendary' }, + { link: 'https://imgur.com/okvejbp.png', name: 'Disturbed', description: 'I don\'t think he\'s a fan...', rarity: 'legendary' }, + { link: 'https://imgur.com/sdLYctZ.png', name: 'Cuddle', description: 'Wittle Cutie!', rarity: 'legendary' }, + { link: 'https://imgur.com/CYvTDSC.png', name: 'Not', description: 'Hey, that\'s not Jeffrey!', rarity: 'legendary' }, + { link: 'https://imgur.com/jmKtrlf.png', name: 'Peaceful', description: 'That\'s about as peaceful as its gonna get...', rarity: 'legendary' }, + { link: 'https://imgur.com/Y1i2gMP.png', name: 'Attack', description: 'That bite\'s lethal!', rarity: 'legendary' }, + { link: 'https://imgur.com/3aWCqqe.png', name: 'Caught', description: 'Hey, what\'s going on over here!', rarity: 'legendary' }, + { link: 'https://imgur.com/eTGaGLM.png', name: 'Lap-Cat', description: 'He\'s been known to peruse a lap or two.', rarity: 'legendary' }, + { link: 'https://imgur.com/7YdKWjS.png', name: 'GateKeeper', description: 'Come on Jeffrey, that\'s not very nice!', rarity: 'legendary' } +]; diff --git a/src/commands/balance.ts b/src/commands/balance.ts index d2a75f0..cb96287 100644 --- a/src/commands/balance.ts +++ b/src/commands/balance.ts @@ -1,8 +1,8 @@ import { SlashCommandBuilder, EmbedBuilder, ChatInputCommandInteraction, User } from 'discord.js'; -import { checkUser, checkBalance, addOrSubtractBalance, addUser, findOrAddUserBalance } from '../DBUtils'; +import { addOrSubtractWallet, findOrAddToUser, checkOrStartWallet } from '../DBMain'; import { replyWithEmbed } from '../utils'; -export const Balance = { +export const Wallet = { info: new SlashCommandBuilder() .setName('balance') .setDescription('Shows you a users JeffreyCoin balance') @@ -24,11 +24,7 @@ export const Balance = { const member = interaction.options.getUser('member'); const add = interaction.options.getInteger('add'); - let target: User; - let targetBalance: number; - let startingBalance: number; - - let higherUp = false; + //checks if non-higher up attempted to use the 'add' option if (add) { @@ -42,6 +38,7 @@ export const Balance = { const commandUser = await interaction.guild.members.fetch(userID); + let higherUp = false; for (const currentRole of role) { if (!currentRole) { console.log(`ERROR finding higher up role(s) for balance command`); @@ -53,13 +50,14 @@ export const Balance = { } } if(!higherUp){ - interaction.reply('Only officer+ can modify member balances!'); + await interaction.reply('Only officer+ can modify member balances!'); return; } } - //whos(target) balance to check - + // whos balance to check (target) = //'member' option if one is specified. Otherwise, defaults to the command user. + let target: User; if (!member || member.id === userID) { target = interaction.user; } else { @@ -67,29 +65,25 @@ export const Balance = { } //checks for target in Users, otherwise adds them - const userInUsers = await checkUser(target.id); - if (!userInUsers) { - await addUser(target.id, target.username); - } - - await findOrAddUserBalance(target.id); + await findOrAddToUser(target.id, target.username, target.displayName); + const startingBalance = await checkOrStartWallet(target.id); + let displayBalance = startingBalance; //checks targets current balance, then adds 'add' to it (can be negative) if (add !== null) { - startingBalance = await checkBalance(target.id); - await addOrSubtractBalance(target.id, add); + await addOrSubtractWallet(target.id, add); + displayBalance += add; } - //checks targets balance - targetBalance = await checkBalance(target.id); - if (!targetBalance && targetBalance !== 0) { + //checks if balance is valid + if (!startingBalance && startingBalance !== 0) { console.log(`ERROR: ${target.id}'s balance is NULL or undefined.`); await interaction.reply('An error has occured, please contact a developer') } const embed = new EmbedBuilder() - .setAuthor({ name: `${target.displayName}'s Balance`, iconURL: target.displayAvatarURL() }) - .addFields({ name: ' ', value: `Balance: ${targetBalance}` }); + .setAuthor({ name: `${target.displayName}'s Wallet`, iconURL: target.displayAvatarURL() }) + .addFields({ name: ' ', value: `Balance: ${displayBalance}` }); //if no 'add' was specified, reply with embed of balance //otherwise, reply with the balance change, then send embed @@ -98,11 +92,11 @@ export const Balance = { await replyWithEmbed(embed, interaction); return; } - if (add && add > 0) { - await interaction.reply(`Added ${add} to ${target}'s wallet! (${startingBalance!} + ${add} = ${targetBalance})`); + if (add > 0) { + await interaction.reply(`Added ${add} to ${target}'s wallet! (${startingBalance} + ${add} = ${startingBalance + add})`); } - if (add && add < 0) { - await interaction.reply(`Removed ${add * -1} from ${target}'s wallet! (${startingBalance!} - ${add * -1} = ${targetBalance})`); + if (add < 0) { + await interaction.reply(`Removed ${add * -1} from ${target}'s wallet! (${startingBalance} - ${add * -1} = ${startingBalance + add})`); } await interaction.channel!.send({ embeds: [embed] }); } catch { diff --git a/src/commands/poll.ts b/src/commands/poll.ts index 3359592..a6bcf6c 100644 --- a/src/commands/poll.ts +++ b/src/commands/poll.ts @@ -56,8 +56,8 @@ export const Poll = { //Discord embed spacing to look nicer .addFields( - { name: ' ', value: '** **' } - { name: ' ', value: '** **' } + { name: ' ', value: '** **' }, + { name: ' ', value: '** **' }, ); for (let i = 0; i < choices.length; i++) { embed.addFields( @@ -74,8 +74,6 @@ export const Poll = { }; try { - //send created embed - const embedMessage = await interaction.channel!.send({ embeds: [embed] }); //send created embed const embedMessage = await interaction.channel!.send({ embeds: [embed] }); interaction.reply({ content: 'Poll created!', ephemeral: true }); diff --git a/src/commands/roll.ts b/src/commands/roll.ts index 72b4a7f..a9e863f 100644 --- a/src/commands/roll.ts +++ b/src/commands/roll.ts @@ -1,119 +1,225 @@ -import { JeffreyGachaURLs, displayLegendary } from "../JeffreyGacha"; -import { SlashCommandBuilder, CommandInteraction, EmbedBuilder } from "discord.js"; -import { replyWithEmbed, rng } from '../utils'; + +import { + SlashCommandBuilder, + EmbedBuilder, + ChatInputCommandInteraction, + ButtonBuilder, + ButtonStyle, + ActionRowBuilder, + ComponentType, + Message, + ButtonInteraction + } from "discord.js"; import { - addNewGacha, - findOrAddUserBalance, - addOrSubtractBalance, - checkBalance, - gachaLvlUp, + addOrSubtractWallet, + addToCollection, checkGachaLevel, - checkIfUserHasGachaInv -} from "../DBUtils"; + checkOrStartWallet +} from "../DBMain"; +import { rollForGacha } from "../DBUtils"; +import { getEmbedColor } from "../utils"; export const Roll = { info: new SlashCommandBuilder() .setName('roll') - .setDescription('Roll for Jeffrey\'s!'), + .setDescription('Roll for Jeffrey\'s!') + .addIntegerOption(option => + option.setName('number') + .setDescription('How many rolls you want to do (costs 10 coins each!)') + .setRequired(false)), - run: async (interaction: CommandInteraction): Promise => { + run: async (interaction: ChatInputCommandInteraction, rollingAgain?: number): Promise => { if (!interaction.guild) { await interaction.reply('this command can only be done in a server'); return; } - const userID = interaction.user.id; + let rolls: number | null; + + if (rollingAgain) { + rolls = rollingAgain; + } else { + rolls = interaction.options.getInteger('number') - await findOrAddUserBalance(userID); + if (!rolls) { + rolls = 1; + } + } + const userID = interaction.user.id; - let currentBalance = await checkBalance(userID); + const currentWallet = await checkOrStartWallet(userID); - if (!currentBalance && currentBalance !== 0) { - console.log(`${userID}'s currentBalance is NULL or undefined`); + if (!currentWallet && currentWallet !== 0) { + console.log(`${userID}'s currentWallet is NULL or undefined`); interaction.reply('Failed to check balance, please contact a developer'); return; } - const price: number = 5; - - if (currentBalance < price) { + const price = 10; + + if (currentWallet < (price * rolls)) { await interaction.reply('not enough JeffreyCoins!'); return; } else { - await addOrSubtractBalance(userID, -price); - currentBalance -= price; + await addOrSubtractWallet(userID, -price); } - const rndm = await rng(0, 100); - let raritySelect: 'Common' | 'Uncommon' | 'Rare' | 'Legendary'; - - if (rndm <= 65) { - raritySelect = 'Common'; - } else if (rndm > 65 && rndm <= 90) { - raritySelect = 'Uncommon'; - } else if (rndm > 90 && rndm < 99) { - raritySelect = 'Rare'; - } else if (rndm >= 100) { - raritySelect = 'Legendary'; - } else { - console.log('error choosing random rarity for Roll command'); - return; - } + const gacha = await rollForGacha(rolls); - const rarity = JeffreyGachaURLs[raritySelect]; - const chooseGacha = await rng(0, rarity.length - 1); - const gachaObj = rarity[chooseGacha]; - const gacha = gachaObj.link; + let gachaLevel: number[] = []; + let level: string[] = []; + const embeds: EmbedBuilder[] = []; + for (let i = 0; i < rolls; i++) { - const displayRarity = raritySelect.toUpperCase(); + await addToCollection(userID, gacha[i].id); - const gachaInv = await checkIfUserHasGachaInv(userID, gacha); - if (!gachaInv) { - console.log(gacha); - await addNewGacha(userID, gacha); - } else { - await gachaLvlUp(userID, gacha); - } + const getLevel = await checkGachaLevel(userID, gacha[i].id); + gachaLevel.push(getLevel); - let embed: EmbedBuilder; - - if (raritySelect !== 'Legendary') { - embed = new EmbedBuilder() - .setTitle(`You pulled a **${displayRarity}** Jeffrey!`) - .setAuthor({ name: `${interaction.user.displayName}` }) - .setImage(gacha); + let starArray = ''; + for (let j = 0; j < gachaLevel[i]; j++) { + starArray += '⭐'; + } + let lvlUp = 'Level: '; + if (gachaLevel[i] > 1) { + lvlUp = '**Rank Up!** | Level: '; + } + level.push(lvlUp + starArray); - } else { - const legendaryInfo = await displayLegendary(gacha); + const color = await getEmbedColor(gacha[i].rarity); - if (!legendaryInfo || !legendaryInfo[0] || !legendaryInfo[1]) { - console.log(`ERROR: could not find legendaryInfo`); - await interaction.reply('Sorry the command could not be completed, please contact a developer.'); - return; - } else { - embed = new EmbedBuilder() - .setTitle('YOU PULLED A LEGENDARY JEFFREY!!!') - .setAuthor({ name: `${interaction.user.displayName}` }) - .setImage(gacha) - .addFields({ name: `${legendaryInfo[0]}`, value: `${legendaryInfo[1]}` }); - } + const embed = new EmbedBuilder(); + embed.setTitle(`${gacha[i].name} Jeffrey`) + .setDescription(`${gacha[i].description}`) + .setImage(gacha[i].link) + .setFields({ name: ' ', value: `${level[i]}` }) + .setColor(color) + .setFooter({ text: `${0 + i + 1}/${rolls}` }); + + embeds.push(embed); } - const gachaLevel = await checkGachaLevel(userID, gacha); - let starArray = ''; - if (!gachaLevel) { - console.log(`gachaLevel is ${gachaLevel} (NULL)`); - await interaction.reply('Sorry the command could not be completed, please contact a developer.') + const next = new ButtonBuilder() + .setCustomId('next') + .setLabel('Next') + .setStyle(ButtonStyle.Primary); + + const previous = new ButtonBuilder() + .setCustomId('previous') + .setLabel('Previous') + .setStyle(ButtonStyle.Primary) + .setDisabled(true); + + const rollAgain = new ButtonBuilder() + .setCustomId('roll_again') + .setLabel('Roll Again!') + .setStyle(ButtonStyle.Danger); + + if (rolls === 1) { + next.setDisabled(true); } - for (let i = 0; i < gachaLevel; i++) { - starArray += '⭐'; + const row = new ActionRowBuilder() + .addComponents(previous, next, rollAgain); + + let currentGacha = 0; + + if (rollingAgain) { + await interaction.channel?.send(`${interaction.user} rolled for ${rolls} Jeffrey(s)!`); + } else { + await interaction.reply(`${interaction.user} rolled for ${rolls} Jeffrey(s)!`); } - embed.setDescription(starArray); - try { - await replyWithEmbed(embed!, interaction); - } catch { - console.log(`ERROR: could not reply with embed in ${interaction.channelId}`); + const gachaMessage = await interaction.channel?.send({ + content: `**${gacha[currentGacha].rarity.toUpperCase()} JEFFREY**`, + embeds: [embeds[currentGacha]], + components: [row], + }); + + if (!gachaMessage) { + await interaction.reply('could not send embed, please contact a developer'); return; } + + const collector = gachaMessage!.createMessageComponentCollector({ filter: i => i.user.id === interaction.user.id, componentType: ComponentType.Button, time: 1_800_000 }); + + collector.on('collect', async i => { + + let first = true; + let last = false; + if (i.customId === 'next') { + currentGacha += 1; + } + if (i.customId === 'previous') { + currentGacha -= 1; + } + if (rolls! - currentGacha <= 1) { + last = true; + } else { + last = false; + } + if (currentGacha < 1) { + first = true; + } else { + first = false; + } + let msg = `**${gacha[currentGacha].rarity.toUpperCase()} JEFFREY**`; + await updateEmbed(msg, i, embeds[currentGacha], first, last); + + if (i.customId === 'roll_again') { + if (!interaction.channel) return; + + const filter = (m: Message) => m.author.id === interaction.user.id; + await interaction.channel.send(`How many more rolls would you like to do? (Please type a number)`); + const collector = interaction.channel.createMessageCollector({ filter, time: 60000 }); + + const collectListener = async (collected: Message) => { + const content = collected.content; + const parsedNumber = Number(content); + + if (!isNaN(parsedNumber)) { + + collector.off('collect', collectListener); + await Roll.run(interaction, parsedNumber); + } else { + await interaction.channel?.send('Please type a valid number!'); + } + }; + + collector.on('collect', collectListener); + } + + }); + } +}; + +async function updateEmbed(msg: string, interaction: ButtonInteraction, embed: EmbedBuilder, first: boolean, last: boolean): Promise { + + const next = new ButtonBuilder() + .setCustomId('next') + .setLabel('Next') + .setStyle(ButtonStyle.Primary); + + const previous = new ButtonBuilder() + .setCustomId('previous') + .setLabel('Previous') + .setStyle(ButtonStyle.Primary); + + const rollAgain = new ButtonBuilder() + .setCustomId('roll_again') + .setLabel('Roll Again!') + .setStyle(ButtonStyle.Danger); + + const row = new ActionRowBuilder() + + if (first) { + previous.setDisabled(true); + } + if (last) { + next.setDisabled(true); + } + row.setComponents(previous, next, rollAgain); + + if (first) { + row } -}; \ No newline at end of file + await interaction.update({ content: msg, embeds: [embed], components: [row] }); +} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index d6128a9..b9041c2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,11 +11,10 @@ import { import { Ping } from './commands/ping'; import { Mock } from './commands/mock'; import { Cat } from './commands/cat'; -import { Balance } from './commands/balance'; +import { Wallet } from './commands/balance'; import { Poll } from './commands/poll'; import { Roll } from './commands/roll'; import { DB } from './JeffreyDB'; -import { Spin } from './commands/spin'; config(); @@ -30,12 +29,10 @@ const intents = [ GatewayIntentBits.Guilds, GatewayIntentBits.GuildMembers, GatewayIntentBits.GuildMessages, - GatewayIntentBits.MessageContent, -] + GatewayIntentBits.MessageContent +]; -const options: ClientOptions = { - intents: intents, -}; +const options: ClientOptions = { intents: intents }; const client = new Client(options); @@ -61,9 +58,8 @@ async function main() { Mock.info.toJSON(), Cat.info.toJSON(), Poll.info.toJSON(), - Balance.info.toJSON(), - Roll.info.toJSON(), - Spin.info.toJSON() + Wallet.info.toJSON(), + Roll.info.toJSON() ] } ); @@ -86,15 +82,15 @@ client.on('interactionCreate', async (interaction) => { } const cooldownMap = cooldown.get(commandName)!; - if (cooldownMap.has(userID) && cooldownMap.get(userID)! > Date.now()) { + if (cooldownMap.has(userID) && cooldownMap.get(userID)! > Date.now() && interaction.user.id !== '218823980524634112') { const cooldownRemaining = (cooldownMap.get(userID)! - Date.now()) / 1000; await interaction.reply(`Please wait ${cooldownRemaining.toFixed(1)} seconds.`); - return; } cooldownMap.set(userID, Date.now() + cooldownTime); - console.log(`user ${interaction.user.username} (${userID}) ran the '${commandName}' command | Guild: ${interaction.guild} |` - + ` Channel: ${interaction.channel} | Timestamp: ${interaction.createdAt}`); + console.log( + `user ${interaction.user.username} (${userID}) ran the '${commandName}' command | Guild: ${interaction.guild} |` + + ` Channel: ${interaction.channel} | Timestamp: ${interaction.createdAt.getUTCDate()}`); if (commandName === 'ping') { await Ping.run(interaction); @@ -111,14 +107,10 @@ client.on('interactionCreate', async (interaction) => { await Poll.run(interaction as ChatInputCommandInteraction); } if (commandName === 'balance') { - await Balance.run(interaction as ChatInputCommandInteraction); - await Balance.run(interaction as ChatInputCommandInteraction); + await Wallet.run(interaction as ChatInputCommandInteraction); } if (commandName === 'roll') { - await Roll.run(interaction); - } - if (commandName === 'spin') { - await Spin.run(interaction); + await Roll.run(interaction as ChatInputCommandInteraction); } }); @@ -130,20 +122,6 @@ client.on('messageCreate', async (message) => { await Mock.effect(message); } }); - export const mockTargets = new Set(); -export async function fishGame(): Promise { - -const fisharr = [ "🐸", "🐢", "🦎", "🐬", "🦀", "🦑", "🐈", "🍱","🐙","🐚"] -const rodarr = ['rare', 'uncommon'] -const rare = 10 -const uncommon = 15 - -const rndm = Math.floor(Math.random() * fisharr.length); -const emoji = fisharr[rndm]; -console.log(emoji); - -} - main(); \ No newline at end of file diff --git a/src/utils.ts b/src/utils.ts index 388be66..8d30267 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,4 +1,4 @@ -import { CommandInteraction, EmbedBuilder } from 'discord.js'; +import { CommandInteraction, EmbedBuilder, HexColorString } from 'discord.js'; export const NUMBER_EMOJIS: string[] = [ "1⃣", @@ -19,10 +19,11 @@ export const NUMBER_EMOJIS: string[] = [ * @returns - returns the random number */ export async function rng(min: number, max: number): Promise { + const randomDecimal = Math.random(); const range = max - min + 1; - const randomNumber = Math.floor(randomDecimal * range) + min; + const randomNumber = Math.floor(randomDecimal * range + 1) + min; return randomNumber } @@ -38,6 +39,46 @@ export async function replyWithEmbed(embed: EmbedBuilder, interaction: CommandIn console.log(`replied with embed in ${interaction.channelId}`); } catch { console.log(`ERROR: could not send embed in ${interaction.channelId}`); - return; + await interaction.reply(`An error has occured, please contact a developer.`); + } +} + +/** + * Sends an embedded message in the same channel that the user used a slash command. + * Embed will be constructed in command files. + * @param embed - Embed with desired information/fields + * @param interaction - The command interaction to reply to. + */ +export async function sendEmbedMessage(embed: EmbedBuilder, interaction: CommandInteraction): Promise { + try{ + await interaction.channel?.send({embeds: [embed]}); + }catch { + console.log(`Could not send embed in ${interaction.channel}`); + await interaction.reply(`An error has occured, please contact a developer.`); + } +} + +export async function editEmbedReply(embed: EmbedBuilder, interaction: CommandInteraction) { + try { + await interaction.editReply({embeds: [embed]}); + }catch{ + console.log(`Could not send embed in ${interaction.channel}`); + await interaction.reply(`An error has occured, please contact a developer.`); + } +} + +export async function getEmbedColor(rarity: string): Promise { + let color: HexColorString; + if(rarity === 'common') { + color = `#808080`; //grey + }else if(rarity === 'uncommon') { + color = `#00A36C`; //green + }else if (rarity === 'rare') { + color = `#FF69B4`; //pink + }else if (rarity === 'legendary') { + color = `#D4A017`;// orange gold + }else{ + color = `#000000` //black (shouldnt show up) } + return color; } \ No newline at end of file From 5c51e2aea4475b616aa38fd06ce2113241789988 Mon Sep 17 00:00:00 2001 From: Dylan Stauch <50031006+sjkd23@users.noreply.github.com> Date: Mon, 11 Sep 2023 06:23:12 -0400 Subject: [PATCH 04/17] docs --- src/DBMain.ts | 16 ++++++------ src/commands/roll.ts | 8 +++++- src/commands/spin.ts | 58 -------------------------------------------- 3 files changed, 15 insertions(+), 67 deletions(-) delete mode 100644 src/commands/spin.ts diff --git a/src/DBMain.ts b/src/DBMain.ts index 4f167ae..8be5e5e 100644 --- a/src/DBMain.ts +++ b/src/DBMain.ts @@ -24,8 +24,8 @@ export async function findOrAddToUser(userID: string, username: string, displayN * If none was found, creates one. * The user ID is the same for that user in every database table that contains a user_id. * - * @param {string} userID - user ID that is consistent across all database tables - * @returns {Promise} - Returns the users current balance in "Wallet" table. + * @param {string} userID - Discord ID + * @returns {Promise} - Returns the users current balance in "Wallet" table (defaults to 0 if Wallet was created) */ export async function checkOrStartWallet(userID: string): Promise { const [userWallet, created] = await Wallet.findOrCreate({ where: { user_id: userID } }); @@ -45,7 +45,7 @@ export async function checkOrStartWallet(userID: string): Promise { * Add or Subtract the 'balance' associated with the user ID in the "Wallet" table by 'amount'. * Function should only be used if you know that the row with 'user_id' exists (ie: call the checkOrStartWallet) * - * @param {string} userID - Users Discord ID + * @param {string} userID - Discord ID * @param {number} amount - Positive or Negative integer to Add or Subtract 'balance' by */ export async function addOrSubtractWallet(userID: string, amount: number): Promise { @@ -60,8 +60,8 @@ export async function addOrSubtractWallet(userID: string, amount: number): Promi * Looking for if the user has previously aquired this gacha.(ie: if its a duplicate) * If its a duplicate, increase its level by 1 (up to 5); * - * @param {string} userID - Users Discord ID - * @param {number} gachaID - ID for the gacha they have aquired + * @param {string} userID - Discord ID + * @param {number} gachaID - ID associated with specific gacha * @returns {Promise} - 'Collection' object contains all information in affected row. */ export async function levelUpOrAddGachaToCollection(userID: string, gachaID: number): Promise { @@ -81,12 +81,12 @@ export async function levelUpOrAddGachaToCollection(userID: string, gachaID: num } /** - * Checks the 'level' column that is in the same row as userID and gachaID + * Checks the 'level' column that is in the same row as userID and gachaID in 'Collection' table. * Should only be used when you know that gachaID exists in the same row as userID * - * @param {string} userID - Users Discord ID + * @param {string} userID - Discord ID * @param {number} gachaID - ID associated with specific gacha - * @returns {Promise} - The 'amt' column in the row containing 'userID' and 'gachaID' + * @returns {Promise} - The 'level' column in the row containing 'userID' and 'gachaID' */ export async function checkGachaLevel(userID: string, gachaID: number): Promise { const lvl = await Collection.findOne({ where: { user_id: userID, gacha_id: gachaID } }); diff --git a/src/commands/roll.ts b/src/commands/roll.ts index a9e863f..ee58a6f 100644 --- a/src/commands/roll.ts +++ b/src/commands/roll.ts @@ -69,11 +69,17 @@ export const Roll = { let gachaLevel: number[] = []; let level: string[] = []; const embeds: EmbedBuilder[] = []; + + //Cycle through all rolls, creates embed for each. for (let i = 0; i < rolls; i++) { await addToCollection(userID, gacha[i].id); const getLevel = await checkGachaLevel(userID, gacha[i].id); + if(!getLevel){ + console.log(`ERROR: Invalid level for - User: ${userID}, Gacha: ${gacha[i].id}`); + await interaction.reply('An error has occured, please contact a developer'); + } gachaLevel.push(getLevel); let starArray = ''; @@ -135,7 +141,7 @@ export const Roll = { }); if (!gachaMessage) { - await interaction.reply('could not send embed, please contact a developer'); + await interaction.reply('Could not send message embed, please contact a developer'); return; } diff --git a/src/commands/spin.ts b/src/commands/spin.ts deleted file mode 100644 index ef84109..0000000 --- a/src/commands/spin.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { CommandInteraction, SlashCommandBuilder, EmbedBuilder } from 'discord.js'; -import { checkSpins, findOrCreateDailyWheel, changeBalance } from '../DBUtils'; -import { spinWheel, JeffreyGachaURLs, displayLegendary } from '../JeffreyGacha'; -import { replyWithEmbed, rng } from '../utils'; - -export const Spin = { - info: new SlashCommandBuilder() - .setName('spin') - .setDescription('Spin the daily wheel! (up to 5 times per day)'), - - run: async (interaction: CommandInteraction): Promise => { - if (!interaction.guild) return; - const userID = interaction.user.id; - - await findOrCreateDailyWheel(userID); - - //check how many spins user has left today and when the spins reset - let spinInfo = await checkSpins(userID); - - if(!spinInfo) { - console.log(`ERROR: check spins is NULL for ${userID}`) - return; - } - if(spinInfo[0]< 1){ - await interaction.reply(`You've exhausted your 5 spins today! Your spins refresh at ${spinInfo[1]}`); - return; - } - //if NULL spins then add user to DailyWheel - const result = await spinWheel(userID); - if (!result) return; - - const [reward, coins] = result; - - if (coins !== -1) { - await changeBalance(userID, coins); - await interaction.reply(`** **\nYou got ${reward[0]}\n\n${reward[1]}`); - } else if (coins === -1) { - - const legendary = JeffreyGachaURLs['Legendary']; - const chooseGacha = await rng(0, legendary.length - 1); - const gachaArray = legendary[chooseGacha]; - const gacha = (typeof gachaArray === 'string' ? gachaArray : gachaArray.link); - - const legendaryInfo = await displayLegendary(gacha); - - if (!legendaryInfo) return; - - const embed = new EmbedBuilder() - .setTitle(`You got a ${reward[0]}`) - .setDescription(reward[1]) - .setAuthor({ name: `${interaction.user.displayName}` }) - .setImage(gacha) - .addFields({ name: `${legendaryInfo[0]}`, value: `${legendaryInfo[1]}` }); - - await replyWithEmbed(embed, interaction); - } - } -}; From aac06dbfce4642d573c23e5948be6f0f4ab49b1b Mon Sep 17 00:00:00 2001 From: Dylan Stauch <50031006+sjkd23@users.noreply.github.com> Date: Mon, 11 Sep 2023 06:33:57 -0400 Subject: [PATCH 05/17] remove redundant functions, etc. --- src/JeffreyGacha.ts | 2 -- src/commands/balance.ts | 4 +--- src/commands/roll.ts | 37 +++++++++++++++++-------------------- src/utils.ts | 39 --------------------------------------- 4 files changed, 18 insertions(+), 64 deletions(-) diff --git a/src/JeffreyGacha.ts b/src/JeffreyGacha.ts index cefc21b..7aa2671 100644 --- a/src/JeffreyGacha.ts +++ b/src/JeffreyGacha.ts @@ -1,5 +1,3 @@ -import { rng } from './utils'; - //Object containing the 4 rarities, 'Common', 'Uncommon', 'Rare' and 'Legendary'. //Each rarity is an array of gacha objects, containing the link(URL), name and description of all gachas. diff --git a/src/commands/balance.ts b/src/commands/balance.ts index cb96287..1d4c3a9 100644 --- a/src/commands/balance.ts +++ b/src/commands/balance.ts @@ -1,6 +1,5 @@ import { SlashCommandBuilder, EmbedBuilder, ChatInputCommandInteraction, User } from 'discord.js'; import { addOrSubtractWallet, findOrAddToUser, checkOrStartWallet } from '../DBMain'; -import { replyWithEmbed } from '../utils'; export const Wallet = { info: new SlashCommandBuilder() @@ -89,7 +88,7 @@ export const Wallet = { //otherwise, reply with the balance change, then send embed try { if (!add) { - await replyWithEmbed(embed, interaction); + await interaction.reply({embeds: [embed]}); return; } if (add > 0) { @@ -103,6 +102,5 @@ export const Wallet = { console.log(`ERROR: Could not send msg embed in ${interaction.channelId}`); return; } - } }; \ No newline at end of file diff --git a/src/commands/roll.ts b/src/commands/roll.ts index ee58a6f..f9aa240 100644 --- a/src/commands/roll.ts +++ b/src/commands/roll.ts @@ -1,15 +1,15 @@ -import { - SlashCommandBuilder, - EmbedBuilder, - ChatInputCommandInteraction, - ButtonBuilder, - ButtonStyle, - ActionRowBuilder, - ComponentType, - Message, - ButtonInteraction - } from "discord.js"; +import { + SlashCommandBuilder, + EmbedBuilder, + ChatInputCommandInteraction, + ButtonBuilder, + ButtonStyle, + ActionRowBuilder, + ComponentType, + Message, + ButtonInteraction +} from "discord.js"; import { addOrSubtractWallet, addToCollection, @@ -76,7 +76,7 @@ export const Roll = { await addToCollection(userID, gacha[i].id); const getLevel = await checkGachaLevel(userID, gacha[i].id); - if(!getLevel){ + if (!getLevel) { console.log(`ERROR: Invalid level for - User: ${userID}, Gacha: ${gacha[i].id}`); await interaction.reply('An error has occured, please contact a developer'); } @@ -172,17 +172,17 @@ export const Roll = { if (i.customId === 'roll_again') { if (!interaction.channel) return; - + const filter = (m: Message) => m.author.id === interaction.user.id; await interaction.channel.send(`How many more rolls would you like to do? (Please type a number)`); const collector = interaction.channel.createMessageCollector({ filter, time: 60000 }); - + const collectListener = async (collected: Message) => { const content = collected.content; const parsedNumber = Number(content); - + if (!isNaN(parsedNumber)) { - + collector.off('collect', collectListener); await Roll.run(interaction, parsedNumber); } else { @@ -192,7 +192,7 @@ export const Roll = { collector.on('collect', collectListener); } - + }); } }; @@ -224,8 +224,5 @@ async function updateEmbed(msg: string, interaction: ButtonInteraction, embed: E } row.setComponents(previous, next, rollAgain); - if (first) { - row - } await interaction.update({ content: msg, embeds: [embed], components: [row] }); } \ No newline at end of file diff --git a/src/utils.ts b/src/utils.ts index 8d30267..c471d7d 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -27,45 +27,6 @@ export async function rng(min: number, max: number): Promise { return randomNumber } -/** - * Replies to the command user with an embedded message. - * Embed will be constructed in command files. - * @param embed - Embed with desired information/fields - * @param interaction - The command interaction to reply to. - */ -export async function replyWithEmbed(embed: EmbedBuilder, interaction: CommandInteraction): Promise { - try { - await interaction.reply({ embeds: [embed] }); - console.log(`replied with embed in ${interaction.channelId}`); - } catch { - console.log(`ERROR: could not send embed in ${interaction.channelId}`); - await interaction.reply(`An error has occured, please contact a developer.`); - } -} - -/** - * Sends an embedded message in the same channel that the user used a slash command. - * Embed will be constructed in command files. - * @param embed - Embed with desired information/fields - * @param interaction - The command interaction to reply to. - */ -export async function sendEmbedMessage(embed: EmbedBuilder, interaction: CommandInteraction): Promise { - try{ - await interaction.channel?.send({embeds: [embed]}); - }catch { - console.log(`Could not send embed in ${interaction.channel}`); - await interaction.reply(`An error has occured, please contact a developer.`); - } -} - -export async function editEmbedReply(embed: EmbedBuilder, interaction: CommandInteraction) { - try { - await interaction.editReply({embeds: [embed]}); - }catch{ - console.log(`Could not send embed in ${interaction.channel}`); - await interaction.reply(`An error has occured, please contact a developer.`); - } -} export async function getEmbedColor(rarity: string): Promise { let color: HexColorString; From 1c7c8d3bdefc3932085712d42e13bb5f149860ab Mon Sep 17 00:00:00 2001 From: Dylan Stauch <50031006+sjkd23@users.noreply.github.com> Date: Wed, 13 Sep 2023 06:18:18 -0400 Subject: [PATCH 06/17] DM command. --- .gitignore | 3 +- src/commands/dm.ts | 382 +++++++++++++++++++++++++++++++++++++++++++++ src/index.ts | 16 +- src/utils.ts | 26 ++- 4 files changed, 419 insertions(+), 8 deletions(-) create mode 100644 src/commands/dm.ts diff --git a/.gitignore b/.gitignore index 85e6ccf..ac61331 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ node_modules .env test.ts .DS_Store -*.db \ No newline at end of file +*.db +*.sql \ No newline at end of file diff --git a/src/commands/dm.ts b/src/commands/dm.ts new file mode 100644 index 0000000..3a5c15b --- /dev/null +++ b/src/commands/dm.ts @@ -0,0 +1,382 @@ +import { +ChatInputCommandInteraction, +SlashCommandBuilder, +EmbedBuilder, +ActionRowBuilder, +ButtonBuilder, +ButtonStyle, +ComponentType, +MessageCollector, +User +} from 'discord.js'; +import { tryDelete, tryToDMEmbed } from '../utils'; + +const MAX_CHOICES = 15; + +export const DM = { + info: new SlashCommandBuilder() + .setName('dm') + .setDescription('The bot will DM the specified user(s), a specified message.') + .addStringOption(option => + option.setName('message') + .setDescription('Please type the message you would like me to send!')) + .addBooleanOption(option => + option.setName('anon') + .setDescription('True: Name shown to recipients (default) | False: Name NOT shown.') + .setRequired(false)), + + addChoiceOptions: () => { + for (let i = 0; i < MAX_CHOICES; i++) { + DM.info.addUserOption(option => + option.setName(`user${i + 1}`) + .setDescription(`One of the users that will be DM\'d`) + .setRequired(false)); + } + }, + run: async (interaction: ChatInputCommandInteraction): Promise => { + if (!interaction.guild) { + await interaction.reply('This command can only be run in a server!'); + return; + } + const userID = interaction.user.id; + + let higherUp = false; + + const getRole = interaction.guild.roles.cache; + + const role = [ + getRole.find(role => role.name.toLowerCase() === 'moderator'), + getRole.find(role => role.name.toLowerCase() === 'officer'), + getRole.find(role => role.name.toLowerCase() === 'head raid leader') + ]; + + const commandUser = await interaction.guild.members.fetch(userID); + + for (const currentRole of role) { + if (!currentRole) { + console.log(`ERROR finding higher up role(s) for balance command`); + continue; + } + if (commandUser.roles.cache.has(currentRole.id)) { + higherUp = true; + break; + } + } + if (!higherUp) { + await interaction.reply('Only officer+ can use this command!'); + return; + } + + const cancel = new ButtonBuilder() + .setCustomId('cancel') + .setLabel('❌ Cancel') + .setStyle(ButtonStyle.Danger); + + const yes = new ButtonBuilder() + .setCustomId('yes') + .setLabel('📤 Yes, Send!') + .setStyle(ButtonStyle.Success); + + //checks the 'message' string option + const message = interaction.options.getString('message'); + let users: User[] = []; + //checks all 'user' options. + for (let i = 0; i < MAX_CHOICES; i++) { + const getUser = interaction.options.getUser(`user${i + 1}`); + if (getUser) { + users.push(getUser); + } + } + //if command users used the message and user options/shortcuts. + if (message && users.length > 0) { + //confirmation embed + const embed = new EmbedBuilder() + .setDescription(`**Are you sure you want to send this message to these users?**`) + .setFields( + { name: '__Message Recipients:__ ', value: users.join(', ') }, + { name: '__Current Message:__ ', value: message }); + + //confirmation action row with buttons + const row = new ActionRowBuilder() + .setComponents(cancel, yes); + + const embedMessage = await interaction.reply({ content: `Please read over the following:`, embeds: [embed], components: [row] }); + + const collector = embedMessage.createMessageComponentCollector({ filter: i => i.user.id === interaction.user.id, componentType: ComponentType.Button, time: 60_000 }); + + collector.on('collect', async i => { + if (i.customId === 'cancel') { + await i.update({}); + await interaction.editReply({ content: `Process Canceled.`, components: [] }); + return; + } + + else if (i.customId === 'yes') { + await i.update({}); + await sendDMs(message, users); + return; + } + }) + } + //if there is a message and no users, or users and no message - specified options. + else if (message && users.length < 1 || !message && users.length > 0) { + await interaction.reply('If you specify a message, you must also specify at least one user (and vice-versa).'); + return; + } + //if no message or user options were specified + else { + const startEmbed = new EmbedBuilder() + .setTitle('DM User(s)') + .addFields( + { name: 'Please use the buttons below to: ', value: '- Create your desired message.' }, + { name: ' ', value: '- Choose which user(s) you would like the bot to send the message to.' }, + { name: ' ', value: '- Finally, click the "Send" button when you are finished.' } + ); + + const addMsgEmbed = new EmbedBuilder() + .setTitle('Create/Change Message') + .addFields({ name: ' ', value: '*Please type the message you would like me to send for you! Click "Done" when you are satisfied with the message.*' }); + + const addUsersEmbed = new EmbedBuilder() + .setTitle('Add Users') + .setDescription('Please type the names of the users you\'d like me to DM!'); + + const addMessage = new ButtonBuilder() + .setCustomId('add_message') + .setLabel('✉ Add/Change Message') + .setStyle(ButtonStyle.Primary); + const addUsers = new ButtonBuilder() + .setCustomId('add_users') + .setLabel('➕ Add Users') + .setStyle(ButtonStyle.Primary); + + const send = new ButtonBuilder() + .setCustomId('send') + .setLabel('📤 Send') + .setStyle(ButtonStyle.Success); + + const back = new ButtonBuilder() + .setCustomId('back') + .setLabel('⬅ Back') + .setStyle(ButtonStyle.Danger); + + const done = new ButtonBuilder() + .setCustomId('done') + .setLabel('✅ Done') + .setStyle(ButtonStyle.Success); + + const reset = new ButtonBuilder() + .setCustomId('reset') + .setLabel('🔃 Reset') + .setStyle(ButtonStyle.Danger); + + const startRow = new ActionRowBuilder() + .setComponents(cancel, addMessage, addUsers, send); + + const addMsgRow = new ActionRowBuilder() + .setComponents(back, done); + const addUsersRow = new ActionRowBuilder() + .setComponents(back, done, reset); + + const confirmRow = new ActionRowBuilder() + .setComponents(back, yes); + + + const embedMessage = await interaction.reply({ content: 'Please follow the instructions below!', embeds: [startEmbed], components: [startRow] }); + + if (!embedMessage) { + await interaction.reply('An error has occurred, please contact a developer.'); + return; + } + + const buttonCollector = embedMessage.createMessageComponentCollector({ filter: i => i.user.id === interaction.user.id, componentType: ComponentType.Button, time: 60_000 }); + + let msgCollector: MessageCollector; + let userCollector: MessageCollector; + let memberList: User[] = []; + let DM = ''; + + buttonCollector.on('collect', async i => { + + if (i.customId === 'add_message') { + await i.update({ embeds: [addMsgEmbed], components: [addMsgRow] }) + + msgCollector = interaction.channel!.createMessageCollector({ + filter: (m) => m.author.id === userID, + time: 60_000, + }); + + msgCollector.on('collect', async m => { + await tryDelete(m); + if (m.content) { + DM = m.content; + if (DM.length > 1024) { + DM = '**__Message too long! (limit: 1024 characters)__**'; + } + addMsgEmbed.setFields({ name: '__Current Message:__ ', value: DM }); + } else { + await interaction.channel?.send('Could not read message.'); + } + await i.editReply({ embeds: [addMsgEmbed], components: [addMsgRow] }); + }); + } + + else if (i.customId === 'add_users') { + await i.update({ embeds: [addUsersEmbed], components: [addUsersRow] }); + + userCollector = interaction.channel!.createMessageCollector({ + filter: (m) => m.author.id === userID, + time: 60_000, + }); + + userCollector.on('collect', async m => { + await tryDelete(m); + const words = m.content.split(' '); + + for (const currentWord of words) { + + let member = interaction.guild?.members.cache.find((member) => + member.user.username === currentWord || + member.user.displayName === currentWord || + member.user.id === currentWord + ); if (!member) { + const fetchMember = await interaction.guild?.members.fetch({ query: currentWord, limit: 1 }); + if (fetchMember) { + member = fetchMember.first(); + } + } + if (member) { + if (memberList.includes(member.user)) { + continue; + } else { + memberList.push(member.user); + } + } + addUsersEmbed.setFields({ name: '__Message Recipients:__ ', value: memberList.join(', ') }); + } + await interaction.editReply({ embeds: [addUsersEmbed], components: [addUsersRow] }); + }); + } + + else if (i.customId === 'cancel') { + await interaction.editReply({ content: 'Process canceled', components: [] }); + console.log('Stopped all Collectors'); + return; + } + + else if (i.customId === 'done') { + msgCollector?.stop(); + userCollector?.stop(); + + startEmbed.setTitle('DM User(s)') + .setFields( + { name: 'Please use the buttons below to: ', value: '- Create your desired message.' }, + { name: ' ', value: '- Choose which user(s) you would like the bot to send the message to.' }, + { name: ' ', value: '- Finally, click the "Send" button when you are finished.' } + ); + if (memberList.length > 0) { + startEmbed.addFields({ name: '__Message Recipients:__ ', value: memberList.join(', ') }); + } + if (DM.length > 0 && DM !== '**__Message too long! (limit: 4096 characters)__**') { + startEmbed.addFields({ name: '__Current Message:__ ', value: DM }); + } + + await interaction.editReply({ embeds: [startEmbed], components: [startRow] }); + await i.update({}); + } + + else if (i.customId === 'back') { + msgCollector?.stop(); + userCollector?.stop(); + + await i.update({ embeds: [startEmbed], components: [startRow] }); + } + + else if (i.customId === 'reset') { + memberList.length = 0; + addUsersEmbed.setDescription('Please type the names of the user(s) you\'d like me to DM!'); + addUsersEmbed.spliceFields(0, 1); + await i.update({ components: [addUsersRow] }); + await interaction.editReply({ embeds: [addUsersEmbed] }); + } + + else if (i.customId === 'send') { + if (!DM || memberList.length < 1) { + await interaction.channel?.send(`No message OR no user specified!`); + await i.update({}) + } else { + startEmbed.setDescription(`**Are you sure you want to send this message to these users?**`); + startEmbed.setFields( + { name: '__Message Recipients:__ ', value: memberList.join(', ') }, + { name: '__Current Message:__ ', value: DM } + ); + await interaction.editReply({ embeds: [startEmbed], components: [confirmRow] }); + await i.update({ components: [confirmRow] }); + } + } else if (i.customId === 'yes') { + await i.update({}) + await sendDMs(DM, memberList); + } + }); + } + /** + * function specifically for this command. + * Attempts to send specified message to all specified users. + * + * @param {string} m - The message that is to be sent to all users + * @param {User[]}users - Object array of all specified discord users. + */ + async function sendDMs(m: string, users: User[]): Promise { + const DMEmbed = new EmbedBuilder() + + .addFields( + { name: '__Message Content:__ ', value: m }, + { name: ' ', value: '--END--' }, + { + name: ' ', value: `** **\n*__Note:__ This bot cannot recieve DMs. ` + + `For any questions or concerns regarding this message, please contact a higher up in the related Discord server.*\n** **` + }, + ) + .setColor('#8B0000'); + + //tries to get the server icon + const serverIcon = interaction.guild?.iconURL(); + if (serverIcon) { + DMEmbed.setAuthor({ name: interaction.guild!.name, iconURL: serverIcon }) + } else { + DMEmbed.setAuthor({ name: interaction.guild!.name }); + } + //if the command user wants to be anonymous or not (name will be shown if false) + const anon = interaction.options.getBoolean('anon'); + if (!anon) { + const userIcon = interaction.user.avatarURL(); + if (userIcon) { + DMEmbed.setFooter({ text: `Message sent by: ${interaction.user.displayName} (${interaction.user.username})`, iconURL: userIcon }); + } else { + DMEmbed.setFooter({ text: `Message sent by: ${interaction.user.displayName} (${interaction.user.username})` }); + } + } + //stores all users that were successfully DM'd + let sentArr: User[] = []; + //stores all users that could not be DM'd + let notSentArr: User[] = []; + for (const currentMember of users) { + const sent = await tryToDMEmbed(DMEmbed, currentMember); + if (sent) { + sentArr.push(currentMember); + } else if (!sent) { + notSentArr.push(currentMember); + } + } + if (sentArr.length > 0) { + await interaction.channel?.send(`Successfully DM'd: ${sentArr.join(', ')}`); + } + if (notSentArr.length > 0) { + await interaction.channel?.send(`Failed to DM: ${notSentArr.join(', ')}`); + } + await interaction.editReply({ content: 'Sent!', embeds: [DMEmbed], components: [] }); + return; + } + + } +}; diff --git a/src/index.ts b/src/index.ts index 9f56bbf..472f1b3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -14,10 +14,13 @@ import { Cat } from './commands/cat'; import { Balance } from './commands/balance'; import { Poll } from './commands/poll'; import { Roll } from './commands/roll'; +import { DM } from './commands/dm'; import { DB } from './JeffreyDB'; config(); +Poll.addChoiceOptions(); +DM.addChoiceOptions(); const cooldown = new Map>(); const cooldownTime = 5000; @@ -61,7 +64,8 @@ async function main() { Cat.info.toJSON(), Poll.info.toJSON(), Balance.info.toJSON(), - Roll.info.toJSON() + Roll.info.toJSON(), + DM.info.toJSON() ] } ); @@ -71,7 +75,6 @@ async function main() { console.error(error); } } -Poll.addChoiceOptions(); client.on('interactionCreate', async (interaction) => { if (!interaction.isCommand()) return; @@ -91,8 +94,8 @@ client.on('interactionCreate', async (interaction) => { } cooldownMap.set(userID, Date.now() + cooldownTime); - console.log(`user ${interaction.user.username} (${userID}) ran the '${commandName}' command | Guild: ${interaction.guild} |` - + ` Channel: ${interaction.channel} | Timestamp: ${interaction.createdAt}`); + console.log(`User ${interaction.user.username} (${userID}) ran the '${commandName}' command | Guild: ${interaction.guild} |` + + ` Channel: ${interaction.channel} | Timestamp: ${interaction.createdAt.toUTCString()}`); if (commandName === 'ping') { await Ping.run(interaction); @@ -112,7 +115,10 @@ client.on('interactionCreate', async (interaction) => { await Balance.run(interaction as ChatInputCommandInteraction); } if (commandName === 'roll') { - await Roll.run(interaction) + await Roll.run(interaction); + } + if (commandName === 'dm') { + await DM.run(interaction as ChatInputCommandInteraction); } }); diff --git a/src/utils.ts b/src/utils.ts index 388be66..aa48374 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,4 +1,4 @@ -import { CommandInteraction, EmbedBuilder } from 'discord.js'; +import { CommandInteraction, EmbedBuilder, Message, User } from 'discord.js'; export const NUMBER_EMOJIS: string[] = [ "1⃣", @@ -40,4 +40,26 @@ export async function replyWithEmbed(embed: EmbedBuilder, interaction: CommandIn console.log(`ERROR: could not send embed in ${interaction.channelId}`); return; } -} \ No newline at end of file +} + +export async function tryDelete(m: Message): Promise { + try{ + m.delete(); + return true; + }catch(err){ + console.error('could not delete message', err); + return false; + } +} + +export async function tryToDMEmbed(embed: EmbedBuilder, member: User): Promise{ + + try{ + await member.send({embeds: [embed]}); + console.log(`Sent message to ${member.id}`) + return true; + }catch(err){ + console.error(`Could not send message to ${member.id}`, err); + return false; + } +} From ac6bfaac64c8a7a85fd67301590a9792c3819bd5 Mon Sep 17 00:00:00 2001 From: Dylan Stauch <50031006+sjkd23@users.noreply.github.com> Date: Thu, 14 Sep 2023 05:31:56 -0400 Subject: [PATCH 07/17] constants folders and button imports from outside command folder. --- src/commands/dm.ts | 55 +++++++--------------------------------- src/constants/buttons.ts | 44 ++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 46 deletions(-) create mode 100644 src/constants/buttons.ts diff --git a/src/commands/dm.ts b/src/commands/dm.ts index 3a5c15b..4ff325e 100644 --- a/src/commands/dm.ts +++ b/src/commands/dm.ts @@ -10,6 +10,7 @@ MessageCollector, User } from 'discord.js'; import { tryDelete, tryToDMEmbed } from '../utils'; +import { BUTTONS as b} from '../constants/buttons'; const MAX_CHOICES = 15; @@ -67,16 +68,6 @@ export const DM = { return; } - const cancel = new ButtonBuilder() - .setCustomId('cancel') - .setLabel('❌ Cancel') - .setStyle(ButtonStyle.Danger); - - const yes = new ButtonBuilder() - .setCustomId('yes') - .setLabel('📤 Yes, Send!') - .setStyle(ButtonStyle.Success); - //checks the 'message' string option const message = interaction.options.getString('message'); let users: User[] = []; @@ -98,7 +89,7 @@ export const DM = { //confirmation action row with buttons const row = new ActionRowBuilder() - .setComponents(cancel, yes); + .setComponents(b.cancel, b.yes); const embedMessage = await interaction.reply({ content: `Please read over the following:`, embeds: [embed], components: [row] }); @@ -135,51 +126,22 @@ export const DM = { const addMsgEmbed = new EmbedBuilder() .setTitle('Create/Change Message') - .addFields({ name: ' ', value: '*Please type the message you would like me to send for you! Click "Done" when you are satisfied with the message.*' }); + .setDescription('Please type the message you would like me to send for you! Click "Done" when you are satisfied with the message.'); const addUsersEmbed = new EmbedBuilder() .setTitle('Add Users') .setDescription('Please type the names of the users you\'d like me to DM!'); - const addMessage = new ButtonBuilder() - .setCustomId('add_message') - .setLabel('✉ Add/Change Message') - .setStyle(ButtonStyle.Primary); - const addUsers = new ButtonBuilder() - .setCustomId('add_users') - .setLabel('➕ Add Users') - .setStyle(ButtonStyle.Primary); - - const send = new ButtonBuilder() - .setCustomId('send') - .setLabel('📤 Send') - .setStyle(ButtonStyle.Success); - - const back = new ButtonBuilder() - .setCustomId('back') - .setLabel('⬅ Back') - .setStyle(ButtonStyle.Danger); - - const done = new ButtonBuilder() - .setCustomId('done') - .setLabel('✅ Done') - .setStyle(ButtonStyle.Success); - - const reset = new ButtonBuilder() - .setCustomId('reset') - .setLabel('🔃 Reset') - .setStyle(ButtonStyle.Danger); - const startRow = new ActionRowBuilder() - .setComponents(cancel, addMessage, addUsers, send); + .setComponents(b.cancel, b.addMessage, b.addUsers, b.send); const addMsgRow = new ActionRowBuilder() - .setComponents(back, done); + .setComponents(b.back, b.done); const addUsersRow = new ActionRowBuilder() - .setComponents(back, done, reset); + .setComponents(b.back, b.done, b.reset); const confirmRow = new ActionRowBuilder() - .setComponents(back, yes); + .setComponents(b.back, b.yes); const embedMessage = await interaction.reply({ content: 'Please follow the instructions below!', embeds: [startEmbed], components: [startRow] }); @@ -302,7 +264,7 @@ export const DM = { else if (i.customId === 'send') { if (!DM || memberList.length < 1) { - await interaction.channel?.send(`No message OR no user specified!`); + await interaction.channel?.send(`No message/no user specified!`); await i.update({}) } else { startEmbed.setDescription(`**Are you sure you want to send this message to these users?**`); @@ -314,6 +276,7 @@ export const DM = { await i.update({ components: [confirmRow] }); } } else if (i.customId === 'yes') { + buttonCollector.stop(); await i.update({}) await sendDMs(DM, memberList); } diff --git a/src/constants/buttons.ts b/src/constants/buttons.ts new file mode 100644 index 0000000..0a2f44a --- /dev/null +++ b/src/constants/buttons.ts @@ -0,0 +1,44 @@ +import { ButtonBuilder, ButtonStyle } from "discord.js" + +export const BUTTONS = { + addMessage: new ButtonBuilder() + .setCustomId('add_message') + .setLabel('✉ Add/Change Message') + .setStyle(ButtonStyle.Primary), + + addUsers: new ButtonBuilder() + .setCustomId('add_users') + .setLabel('➕ Add Users') + .setStyle(ButtonStyle.Primary), + + send: new ButtonBuilder() + .setCustomId('send') + .setLabel('📤 Send') + .setStyle(ButtonStyle.Success), + + back: new ButtonBuilder() + .setCustomId('back') + .setLabel('⬅ Back') + .setStyle(ButtonStyle.Danger), + + done: new ButtonBuilder() + .setCustomId('done') + .setLabel('✅ Done') + .setStyle(ButtonStyle.Success), + + reset: new ButtonBuilder() + .setCustomId('reset') + .setLabel('🔃 Reset') + .setStyle(ButtonStyle.Danger), + + cancel: new ButtonBuilder() + .setCustomId('cancel') + .setLabel('❌ Cancel') + .setStyle(ButtonStyle.Danger), + + yes: new ButtonBuilder() + .setCustomId('yes') + .setLabel('✅ Yes') + .setStyle(ButtonStyle.Success) +} + From 50ca270e55e8bab8f85b5f103048a3477dbe6df2 Mon Sep 17 00:00:00 2001 From: Dylan Stauch <50031006+sjkd23@users.noreply.github.com> Date: Thu, 14 Sep 2023 07:33:58 -0400 Subject: [PATCH 08/17] not allow multiple instances of roll again from same user. Button constants outside of command files. prettier roll.ts code. --- src/commands/roll.ts | 165 ++++++++++++++++++--------------------- src/constants/buttons.ts | 19 +++++ src/utils.ts | 12 ++- 3 files changed, 104 insertions(+), 92 deletions(-) create mode 100644 src/constants/buttons.ts diff --git a/src/commands/roll.ts b/src/commands/roll.ts index f9aa240..e623131 100644 --- a/src/commands/roll.ts +++ b/src/commands/roll.ts @@ -4,11 +4,9 @@ import { EmbedBuilder, ChatInputCommandInteraction, ButtonBuilder, - ButtonStyle, ActionRowBuilder, ComponentType, - Message, - ButtonInteraction + Message } from "discord.js"; import { addOrSubtractWallet, @@ -17,7 +15,10 @@ import { checkOrStartWallet } from "../DBMain"; import { rollForGacha } from "../DBUtils"; -import { getEmbedColor } from "../utils"; +import { getEmbedColor, tryDelete } from "../utils"; +import { BUTTONS as b } from "../constants/buttons"; + +let rollAgainInUse = false; export const Roll = { info: new SlashCommandBuilder() @@ -34,15 +35,14 @@ export const Roll = { return; } - let rolls: number | null; + let rolls = 1 if (rollingAgain) { rolls = rollingAgain; } else { - rolls = interaction.options.getInteger('number') - - if (!rolls) { - rolls = 1; + const checkIfRolls = interaction.options.getInteger('number') + if (checkIfRolls) { + rolls = checkIfRolls; } } const userID = interaction.user.id; @@ -51,14 +51,18 @@ export const Roll = { if (!currentWallet && currentWallet !== 0) { console.log(`${userID}'s currentWallet is NULL or undefined`); - interaction.reply('Failed to check balance, please contact a developer'); + await interaction.reply('Failed to check balance, please contact a developer'); return; } const price = 10; if (currentWallet < (price * rolls)) { - await interaction.reply('not enough JeffreyCoins!'); + if (rollingAgain) { + await interaction.channel?.send('Not enough JeffreyCoins!'); + return; + } + await interaction.reply('Not enough JeffreyCoins!'); return; } else { await addOrSubtractWallet(userID, -price); @@ -105,27 +109,12 @@ export const Roll = { embeds.push(embed); } - const next = new ButtonBuilder() - .setCustomId('next') - .setLabel('Next') - .setStyle(ButtonStyle.Primary); - - const previous = new ButtonBuilder() - .setCustomId('previous') - .setLabel('Previous') - .setStyle(ButtonStyle.Primary) - .setDisabled(true); - - const rollAgain = new ButtonBuilder() - .setCustomId('roll_again') - .setLabel('Roll Again!') - .setStyle(ButtonStyle.Danger); - if (rolls === 1) { - next.setDisabled(true); + b.next.setDisabled(true); } + //previous starts disabled const row = new ActionRowBuilder() - .addComponents(previous, next, rollAgain); + .addComponents(b.previous, b.next, b.rollAgain); let currentGacha = 0; @@ -145,84 +134,78 @@ export const Roll = { return; } - const collector = gachaMessage!.createMessageComponentCollector({ filter: i => i.user.id === interaction.user.id, componentType: ComponentType.Button, time: 1_800_000 }); + const buttonCollector = gachaMessage.createMessageComponentCollector({ filter: i => i.user.id === interaction.user.id, componentType: ComponentType.Button, time: 1_800_000 }); - collector.on('collect', async i => { + buttonCollector.on('collect', async i => { - let first = true; - let last = false; if (i.customId === 'next') { currentGacha += 1; + const nextRow = await checkIfFirstOrLast(row, rolls, currentGacha); + await i.update({ + content: `**${gacha[currentGacha].rarity.toUpperCase()}**`, + embeds: [embeds[currentGacha]], + components: [nextRow] + }); } if (i.customId === 'previous') { currentGacha -= 1; + const prevRow = await checkIfFirstOrLast(row, rolls, currentGacha); + await i.update({ + content: `**${gacha[currentGacha].rarity.toUpperCase()}**`, + embeds: [embeds[currentGacha]], + components: [prevRow] + }); } - if (rolls! - currentGacha <= 1) { - last = true; - } else { - last = false; - } - if (currentGacha < 1) { - first = true; - } else { - first = false; - } - let msg = `**${gacha[currentGacha].rarity.toUpperCase()} JEFFREY**`; - await updateEmbed(msg, i, embeds[currentGacha], first, last); if (i.customId === 'roll_again') { - if (!interaction.channel) return; - - const filter = (m: Message) => m.author.id === interaction.user.id; - await interaction.channel.send(`How many more rolls would you like to do? (Please type a number)`); - const collector = interaction.channel.createMessageCollector({ filter, time: 60000 }); - - const collectListener = async (collected: Message) => { - const content = collected.content; - const parsedNumber = Number(content); - - if (!isNaN(parsedNumber)) { - - collector.off('collect', collectListener); - await Roll.run(interaction, parsedNumber); - } else { - await interaction.channel?.send('Please type a valid number!'); - } - }; - - collector.on('collect', collectListener); + if (rollAgainInUse) { + await i.update({}) + await interaction.channel?.send('Please complete your previous roll again before using this one!'); + } else { + rollAgainInUse = true; + row.setComponents(b.previous, b.next); + + await i.update({ components: [row] }); + const rollAgainMsg = await interaction.channel?.send(`How many more rolls would you like to do? (Please type a number, type '0' to cancel)`); + + const msgFilter = (m: Message) => m.author.id === interaction.user.id; + const msgCollector = interaction.channel!.createMessageCollector({ filter: msgFilter, time: 60_000 }); + + msgCollector.on('collect', async m => { + await tryDelete(m); + if (m.content === '0') { + await tryDelete(rollAgainMsg!); + msgCollector.stop(); + } + const content = m.content; + const parsedNumber = Number(content); + + if (!isNaN(parsedNumber)) { + msgCollector.stop(); + rollAgainInUse = false; + await Roll.run(interaction, parsedNumber); + return; + } else { + await interaction.channel?.send('Please type a valid number!'); + } + }); + } } - }); } }; -async function updateEmbed(msg: string, interaction: ButtonInteraction, embed: EmbedBuilder, first: boolean, last: boolean): Promise { - - const next = new ButtonBuilder() - .setCustomId('next') - .setLabel('Next') - .setStyle(ButtonStyle.Primary); +async function checkIfFirstOrLast(row: ActionRowBuilder, rolls: number, currentGacha: number): Promise> { - const previous = new ButtonBuilder() - .setCustomId('previous') - .setLabel('Previous') - .setStyle(ButtonStyle.Primary); - - const rollAgain = new ButtonBuilder() - .setCustomId('roll_again') - .setLabel('Roll Again!') - .setStyle(ButtonStyle.Danger); - - const row = new ActionRowBuilder() - - if (first) { - previous.setDisabled(true); + if (rolls - currentGacha <= 1) { + b.next.setDisabled(true); + } else { + b.next.setDisabled(false); } - if (last) { - next.setDisabled(true); + if (currentGacha < 1) { + b.previous.setDisabled(true); + } else { + b.previous.setDisabled(false); } - row.setComponents(previous, next, rollAgain); - - await interaction.update({ content: msg, embeds: [embed], components: [row] }); + return row.setComponents(b.previous, b.next, b.rollAgain); } \ No newline at end of file diff --git a/src/constants/buttons.ts b/src/constants/buttons.ts new file mode 100644 index 0000000..2c8ef17 --- /dev/null +++ b/src/constants/buttons.ts @@ -0,0 +1,19 @@ +import { ButtonBuilder, ButtonStyle } from "discord.js"; + +export const BUTTONS = { + next: new ButtonBuilder() + .setCustomId('next') + .setLabel('Next') + .setStyle(ButtonStyle.Primary), + + previous: new ButtonBuilder() + .setCustomId('previous') + .setLabel('Previous') + .setStyle(ButtonStyle.Primary) + .setDisabled(true), + + rollAgain: new ButtonBuilder() + .setCustomId('roll_again') + .setLabel('Roll Again!') + .setStyle(ButtonStyle.Danger) +} \ No newline at end of file diff --git a/src/utils.ts b/src/utils.ts index c471d7d..49a732c 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,4 +1,4 @@ -import { CommandInteraction, EmbedBuilder, HexColorString } from 'discord.js'; +import { Message, HexColorString } from 'discord.js'; export const NUMBER_EMOJIS: string[] = [ "1⃣", @@ -42,4 +42,14 @@ export async function getEmbedColor(rarity: string): Promise { color = `#000000` //black (shouldnt show up) } return color; +} + +export async function tryDelete(m: Message): Promise { + try{ + m.delete(); + return true; + }catch(err){ + console.error('could not delete message', err); + return false; + } } \ No newline at end of file From a5f1148aa69e94cf3df7159f494a840f7bc7ae35 Mon Sep 17 00:00:00 2001 From: Dylan Stauch <50031006+sjkd23@users.noreply.github.com> Date: Thu, 14 Sep 2023 09:00:21 -0400 Subject: [PATCH 09/17] export firstOrLastFunction in utils --- src/commands/roll.ts | 41 +++++++++++++---------------------------- src/utils.ts | 17 ++++++++++++++++- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/commands/roll.ts b/src/commands/roll.ts index e623131..643236c 100644 --- a/src/commands/roll.ts +++ b/src/commands/roll.ts @@ -15,7 +15,7 @@ import { checkOrStartWallet } from "../DBMain"; import { rollForGacha } from "../DBUtils"; -import { getEmbedColor, tryDelete } from "../utils"; +import { checkIfFirstOrLast, getEmbedColor, tryDelete } from "../utils"; import { BUTTONS as b } from "../constants/buttons"; let rollAgainInUse = false; @@ -37,7 +37,7 @@ export const Roll = { let rolls = 1 - if (rollingAgain) { + if (rollingAgain && rollingAgain > 0) { rolls = rollingAgain; } else { const checkIfRolls = interaction.options.getInteger('number') @@ -56,8 +56,8 @@ export const Roll = { } const price = 10; - - if (currentWallet < (price * rolls)) { + const totalPrice = price * rolls; + if (currentWallet < totalPrice) { if (rollingAgain) { await interaction.channel?.send('Not enough JeffreyCoins!'); return; @@ -65,7 +65,7 @@ export const Roll = { await interaction.reply('Not enough JeffreyCoins!'); return; } else { - await addOrSubtractWallet(userID, -price); + await addOrSubtractWallet(userID, -totalPrice); } const gacha = await rollForGacha(rolls); @@ -114,11 +114,11 @@ export const Roll = { } //previous starts disabled const row = new ActionRowBuilder() - .addComponents(b.previous, b.next, b.rollAgain); + .setComponents(b.previous, b.next, b.rollAgain); let currentGacha = 0; - if (rollingAgain) { + if (rollingAgain && rollingAgain > 0) { await interaction.channel?.send(`${interaction.user} rolled for ${rolls} Jeffrey(s)!`); } else { await interaction.reply(`${interaction.user} rolled for ${rolls} Jeffrey(s)!`); @@ -126,7 +126,7 @@ export const Roll = { const gachaMessage = await interaction.channel?.send({ content: `**${gacha[currentGacha].rarity.toUpperCase()} JEFFREY**`, embeds: [embeds[currentGacha]], - components: [row], + components: [row] }); if (!gachaMessage) { @@ -140,20 +140,20 @@ export const Roll = { if (i.customId === 'next') { currentGacha += 1; - const nextRow = await checkIfFirstOrLast(row, rolls, currentGacha); + await checkIfFirstOrLast(currentGacha, rolls); await i.update({ content: `**${gacha[currentGacha].rarity.toUpperCase()}**`, embeds: [embeds[currentGacha]], - components: [nextRow] + components: [row] }); } if (i.customId === 'previous') { currentGacha -= 1; - const prevRow = await checkIfFirstOrLast(row, rolls, currentGacha); + await checkIfFirstOrLast(currentGacha, rolls); await i.update({ content: `**${gacha[currentGacha].rarity.toUpperCase()}**`, embeds: [embeds[currentGacha]], - components: [prevRow] + components: [row] }); } @@ -193,19 +193,4 @@ export const Roll = { } }); } -}; - -async function checkIfFirstOrLast(row: ActionRowBuilder, rolls: number, currentGacha: number): Promise> { - - if (rolls - currentGacha <= 1) { - b.next.setDisabled(true); - } else { - b.next.setDisabled(false); - } - if (currentGacha < 1) { - b.previous.setDisabled(true); - } else { - b.previous.setDisabled(false); - } - return row.setComponents(b.previous, b.next, b.rollAgain); -} \ No newline at end of file +}; \ No newline at end of file diff --git a/src/utils.ts b/src/utils.ts index 49a732c..582387b 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,4 +1,5 @@ -import { Message, HexColorString } from 'discord.js'; +import { Message, HexColorString, ActionRowBuilder, ButtonBuilder } from 'discord.js'; +import { BUTTONS as b } from './constants/buttons'; export const NUMBER_EMOJIS: string[] = [ "1⃣", @@ -52,4 +53,18 @@ export async function tryDelete(m: Message): Promise { console.error('could not delete message', err); return false; } +} + +export async function checkIfFirstOrLast(currentPos: number, max: number): Promise { + + if (max - currentPos <= 1) { + b.next.setDisabled(true); + } else { + b.next.setDisabled(false); + } + if (currentPos < 1) { + b.previous.setDisabled(true); + } else { + b.previous.setDisabled(false); + } } \ No newline at end of file From 61ae1e7f179a1a12e9a342430220352ea1fcf741 Mon Sep 17 00:00:00 2001 From: Dylan Stauch <50031006+sjkd23@users.noreply.github.com> Date: Thu, 14 Sep 2023 09:15:29 -0400 Subject: [PATCH 10/17] gitigore settings.json --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index ac61331..c48a3c4 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ node_modules test.ts .DS_Store *.db -*.sql \ No newline at end of file +*.sql +settings.json \ No newline at end of file From 2e2df5a6716256bec9887d96e2dc4507d8713846 Mon Sep 17 00:00:00 2001 From: Dylan Stauch <50031006+sjkd23@users.noreply.github.com> Date: Mon, 18 Sep 2023 02:39:52 -0400 Subject: [PATCH 11/17] formatting, docs, no hardcoding or redundancies. --- src/DBMain.ts | 38 ++++++++++-------------- src/DBUtils.ts | 49 ++++++++++++------------------- src/JeffreyDB.ts | 58 ++++++++++++++++++++---------------- src/commands/balance.ts | 8 ++--- src/commands/cat.ts | 4 +-- src/commands/mock.ts | 4 +-- src/commands/ping.ts | 4 +-- src/commands/roll.ts | 34 ++++++++-------------- src/constants/buttons.ts | 29 +++++++++--------- src/index.ts | 15 +++++----- src/utils.ts | 63 ++++++++++++++++++++++++++-------------- 11 files changed, 154 insertions(+), 152 deletions(-) diff --git a/src/DBMain.ts b/src/DBMain.ts index 8be5e5e..410eca3 100644 --- a/src/DBMain.ts +++ b/src/DBMain.ts @@ -1,5 +1,6 @@ import { User, Wallet, Gacha, Collection } from "./JeffreyDB"; +const LEVEL_CAP = 5; /** * Checks if the given userID is in the "User" table, if not add it. * @@ -9,7 +10,6 @@ import { User, Wallet, Gacha, Collection } from "./JeffreyDB"; * @returns {Promise} - Returns 'User' object which contains all information from the found/created row in table. */ export async function findOrAddToUser(userID: string, username: string, displayName: string): Promise { - const [user, created] = await User.findOrCreate({ where: { id: userID, name: username, display_name: displayName } }); if (created) { console.log(`Created new "User" row for - User: ${userID}`); @@ -17,7 +17,7 @@ export async function findOrAddToUser(userID: string, username: string, displayN console.log(`User already in database "User" - User: ${userID}`); } return user; -} +}; /** * Searches for the 'balance' associated the given user ID. @@ -29,17 +29,15 @@ export async function findOrAddToUser(userID: string, username: string, displayN */ export async function checkOrStartWallet(userID: string): Promise { const [userWallet, created] = await Wallet.findOrCreate({ where: { user_id: userID } }); - if (created) { console.log(`Created new "Wallet" row for - User: ${userID}`); return 0; } else { console.log(`User already in database "Wallet" - User: ${userID}`); } - console.log(`User: ${userID} has a balance of ${userWallet.balance}`); return userWallet.balance; -} +}; /** * Add or Subtract the 'balance' associated with the user ID in the "Wallet" table by 'amount'. @@ -49,16 +47,14 @@ export async function checkOrStartWallet(userID: string): Promise { * @param {number} amount - Positive or Negative integer to Add or Subtract 'balance' by */ export async function addOrSubtractWallet(userID: string, amount: number): Promise { - await Wallet.increment({ balance: amount }, { where: { user_id: userID } }); - console.log(`${userID}'s wallet has been changed by: ${amount}`); -} +}; /** * Checks for a row in GachaInv that contains both 'userID' and 'gachaID'. * Looking for if the user has previously aquired this gacha.(ie: if its a duplicate) - * If its a duplicate, increase its level by 1 (up to 5); + * If its a duplicate, increase its level by 1 (up to level cap of 5); * * @param {string} userID - Discord ID * @param {number} gachaID - ID associated with specific gacha @@ -67,7 +63,7 @@ export async function addOrSubtractWallet(userID: string, amount: number): Promi export async function levelUpOrAddGachaToCollection(userID: string, gachaID: number): Promise { const [gacha, created] = await Collection.findOrCreate({ where: { user_id: userID, gacha_id: gachaID } }); if (!created) { - if (gacha.level < 5) { + if (gacha.level < LEVEL_CAP) { const [leveledGacha] = await Collection.increment({ level: 1 }, { where: { user_id: userID, gacha_id: gachaID } }); gacha.level += 1; return gacha; @@ -78,7 +74,7 @@ export async function levelUpOrAddGachaToCollection(userID: string, gachaID: num console.log(`${gachaID} added to Collection for - User: ${userID}`); return gacha; } -} +}; /** * Checks the 'level' column that is in the same row as userID and gachaID in 'Collection' table. @@ -103,29 +99,27 @@ export async function checkGachaLevel(userID: string, gachaID: number): Promise< * @returns {Promise} - returns the 'Gacha' object which contains information about a specifc gacha */ export async function gachaInfo(gachaID: number): Promise { - try{ const gachaInfo = await Gacha.findOne({ where: { id: gachaID } }); - return gachaInfo; - }catch(e){ - console.error(e); + if (gachaInfo) { + return gachaInfo; + } else { return null; } - -} +}; /** - * + * Checks if a user has a specific gacha, if they do, increase its level by one, + * otherwise, create a new row with that gacha. * * @param {string} userID - Users Discord ID - * @param gachaID - ID associated with a specific Gacha + * @param {number} gachaID - ID associated with a specific Gacha * @returns {Promise} - 'Collection' object containing information on specified row in table. * - Also returns true if Gacha is max level. */ export async function addToCollection(userID: string, gachaID: number): Promise { const gacha = await Gacha.findOne({ where: { id: gachaID } }) const [userGacha, created] = await Collection.findOrCreate({ where: { user_id: userID, gacha_id: gacha!.id } }); - if (!created) { - if (userGacha.level < 5) { + if (userGacha.level < LEVEL_CAP) { await userGacha.increment({ level: 1 }); await userGacha.reload(); console.log(`Increased level of - GachaID: ${gachaID} for - User: ${userID} `); @@ -137,4 +131,4 @@ export async function addToCollection(userID: string, gachaID: number): Promise< console.log(`GachaID: ${gachaID} - has been added to the Collection of - User: ${userID}`); return userGacha } -} +}; diff --git a/src/DBUtils.ts b/src/DBUtils.ts index a901552..4bfeaff 100644 --- a/src/DBUtils.ts +++ b/src/DBUtils.ts @@ -9,56 +9,45 @@ import { rng } from "./utils"; * @param {string} rarity - Which rarity it should choose from * @returns {Promise} - random 'rarity' gacha */ -export async function getRandomGacha(rarity: string): Promise { - +export async function getRandomGachaOfRarity(rarity: string): Promise { const allGachaOfRarity = await Gacha.findAll({ where: { rarity: rarity } }); - const randomGacha = Math.floor(Math.random() * Math.floor(allGachaOfRarity.length)); - - const gacha = allGachaOfRarity[randomGacha]; - - return gacha; + return allGachaOfRarity[randomGacha]; } /** - * Calls getRandomGacha and specifies that it wants a 'legendary' gacha + * Calls getRandomGachaOfRarity and specifies that it wants a 'legendary' gacha * * @returns {Promise} - 'Gacha' object containing table information about a specific Gacha */ export async function getRandomLegendary(): Promise { - - const legendaryGacha = await getRandomGacha('legendary'); - + const legendaryGacha = await getRandomGachaOfRarity('legendary'); return legendaryGacha; } /** - * Randomly selects rarity, then calls getRandomGacha to choose a random gacha of that rarity + * Randomly selects rarity, then calls getRandomGachaOfRarity to choose a random gacha of that rarity * * @param {number} rolls - The number of gacha that should be returned * @returns {Promise} - 'Gacha' object containing table information about a random gacha of random rarity. */ export async function rollForGacha(rolls: number): Promise { const gacha: Gacha[] = []; - - for(let i = 0; i < rolls; i++){ - - const rndm = await rng(0, 100); - let rarity: string; - - if (rndm <= 65) { - rarity = 'common'; - } else if (rndm > 65 && rndm <= 90) { - rarity = 'uncommon'; - } else if (rndm > 90 && rndm < 99) { - rarity = 'rare'; - } else if (rndm >= 100) { - rarity = 'legendary'; + for (let i = 0; i < rolls; i++) { + const rndm = await rng(0, 100); + let rarity: string; + if (rndm <= 65) { + rarity = 'common'; + } else if (rndm > 65 && rndm <= 90) { + rarity = 'uncommon'; + } else if (rndm > 90 && rndm <= 99) { + rarity = 'rare'; + } else if (rndm >= 100) { + rarity = 'legendary'; + } + const randomGacha = await getRandomGachaOfRarity(rarity!); + gacha.push(randomGacha); } - const randomGacha = await getRandomGacha(rarity!); - gacha.push(randomGacha); -} - return gacha; } \ No newline at end of file diff --git a/src/JeffreyDB.ts b/src/JeffreyDB.ts index 5e69edc..d215cf7 100644 --- a/src/JeffreyDB.ts +++ b/src/JeffreyDB.ts @@ -6,14 +6,14 @@ export const sequelize = new Sequelize({ logging: false, }); -// Creates the "User" table with: -// @example (id is Discord user ID, username is Discord Username, display_name is their nickname in that server) -// -// | id:'667951424704872450' | username: 'jeffreyhassalide' | display_name: Jeffrey | +/** Creates the "User" table with: +* @example (id is Discord user ID, name is Discord Username, display_name is their nickname in that server) +* | id:'667951424704872450' | name: 'jeffreyhassalide' | display_name: Jeffrey | +*/ export class User extends Model { declare id: string; declare name: string; - declare display_name: string; + declare display_name: string; } User.init({ id: { @@ -33,10 +33,10 @@ User.init({ }, }, { sequelize }); -// Creates the "Wallet" table with -// @example(id is a unique ID for each row, userid is users Discord ID, balance is how much money they have) -// -// | id: 5 | userid: 667951424704872450 | balance: 10 | +/** Creates the "Wallet" table with +* @example(id is a unique ID for each row, user_id is users Discord ID, balance is how much money they have) +* | id: 5 | user_id: 667951424704872450 | balance: 10 | +*/ export class Wallet extends Model { declare id: number; declare user_id: string; @@ -67,9 +67,10 @@ Wallet.init({ }, { sequelize }); User.hasOne(Wallet, { foreignKey: 'user_id', sourceKey: 'id' }); -// Creates the "Gacha" table with -// @example -// | id: 8 | link: https://fakelink.png | name: JeffreyDaKilla | description: He gonna scratch you up! | +/** Creates the "Gacha" table which contains a link, name, description and rarity for each gacha, aswell as a unique number ID for each of them. +* @example +* | id: 8 | link: https://fakelink.png | name: JeffreyDaKilla | description: He gonna scratch you up! | rarity: legendary | +*/ export class Gacha extends Model { declare id: number; declare link: string; @@ -106,7 +107,14 @@ Gacha.init({ }, }, { sequelize }); -export class Collection extends Model{ +/** + * Creats 'Collection' table which creates rows with user_id and gacha_id, to show which gachas each user owns, + * as well as what level each specific gacha is for that user(level = how many copies they have, caps at 5) + * Has a unique number ID for each row. + * @example + * | id: 25 | user_id: 667951424704872450 | gacha_id: 48 | level: 3 | + */ +export class Collection extends Model { declare id: number; declare user_id: string; declare gacha_id: number; @@ -145,8 +153,8 @@ Collection.init({ defaultValue: 1, }, }, { sequelize }); -User.hasMany(Collection, {foreignKey: 'user_id', sourceKey: 'id'}); -Gacha.hasMany(Collection, {foreignKey: 'gacha_id', sourceKey: 'id'}); +User.hasMany(Collection, { foreignKey: 'user_id', sourceKey: 'id' }); +Gacha.hasMany(Collection, { foreignKey: 'gacha_id', sourceKey: 'id' }); // Contains'test' and 'sync' export const DB = { @@ -162,11 +170,11 @@ export const DB = { console.error('Unable to connect to the database:', error); } }, -/** - * sync: - * Tries to sync all database tables with the database file, - * which allows it to build upon the information previously stored in their respective tables after bot resets. - */ + /** + * sync: + * Tries to sync all database tables with the database file, + * which allows it to build upon the information previously stored in their respective tables after bot resets. + */ sync: async (): Promise => { try { await User.sync(); @@ -174,16 +182,16 @@ export const DB = { await Wallet.sync(); console.log('UserWallets synced'); - + await Gacha.sync(); console.log('GachaInvs synced'); await Collection.sync() - console.log ('Collection sunced'); + console.log('Collection sunced'); - }catch(e){ - console.log('Failed to sync table(s)'); - console.error(e); + } catch (err) { + console.log(); + console.error('Failed to sync table(s)', err); } } }; \ No newline at end of file diff --git a/src/commands/balance.ts b/src/commands/balance.ts index 1d4c3a9..b768bd9 100644 --- a/src/commands/balance.ts +++ b/src/commands/balance.ts @@ -1,7 +1,7 @@ import { SlashCommandBuilder, EmbedBuilder, ChatInputCommandInteraction, User } from 'discord.js'; import { addOrSubtractWallet, findOrAddToUser, checkOrStartWallet } from '../DBMain'; -export const Wallet = { +export const Balance = { info: new SlashCommandBuilder() .setName('balance') .setDescription('Shows you a users JeffreyCoin balance') @@ -23,7 +23,7 @@ export const Wallet = { const member = interaction.options.getUser('member'); const add = interaction.options.getInteger('add'); - + //checks if non-higher up attempted to use the 'add' option if (add) { @@ -48,7 +48,7 @@ export const Wallet = { break; } } - if(!higherUp){ + if (!higherUp) { await interaction.reply('Only officer+ can modify member balances!'); return; } @@ -88,7 +88,7 @@ export const Wallet = { //otherwise, reply with the balance change, then send embed try { if (!add) { - await interaction.reply({embeds: [embed]}); + await interaction.reply({ embeds: [embed] }); return; } if (add > 0) { diff --git a/src/commands/cat.ts b/src/commands/cat.ts index 1259785..3b2e455 100644 --- a/src/commands/cat.ts +++ b/src/commands/cat.ts @@ -1,12 +1,12 @@ import axios from 'axios'; -import { CommandInteraction, SlashCommandBuilder, EmbedBuilder } from 'discord.js'; +import { ChatInputCommandInteraction, SlashCommandBuilder, EmbedBuilder } from 'discord.js'; export const Cat = { info: new SlashCommandBuilder() .setName('cat') .setDescription('Sends a picture of a cat!'), - run: async (interaction: CommandInteraction): Promise => { + run: async (interaction: ChatInputCommandInteraction): Promise => { let catUrl: string; try { diff --git a/src/commands/mock.ts b/src/commands/mock.ts index 7a9fdb1..82d4566 100644 --- a/src/commands/mock.ts +++ b/src/commands/mock.ts @@ -1,4 +1,4 @@ -import { CommandInteraction, Message, SlashCommandBuilder } from 'discord.js'; +import { ChatInputCommandInteraction, Message, SlashCommandBuilder } from 'discord.js'; import { mockTargets } from '../index'; export const Mock = { @@ -10,7 +10,7 @@ export const Mock = { .setDescription('Choose which member you want to be mocked') .setRequired(true)), - run: async (interaction: CommandInteraction): Promise => { + run: async (interaction: ChatInputCommandInteraction): Promise => { if (!interaction.guild) { //Checks if the command is NOT done in a server await interaction.reply('This command can only be done in a server.'); return; diff --git a/src/commands/ping.ts b/src/commands/ping.ts index 1832b4e..86f9330 100644 --- a/src/commands/ping.ts +++ b/src/commands/ping.ts @@ -1,11 +1,11 @@ -import { CommandInteraction, SlashCommandBuilder } from 'discord.js'; +import { ChatInputCommandInteraction, SlashCommandBuilder } from 'discord.js'; export const Ping = { info: new SlashCommandBuilder() .setName('ping') .setDescription('Tells you the bot\'s ping (ms)'), - run: async (interaction: CommandInteraction): Promise => { + run: async (interaction: ChatInputCommandInteraction): Promise => { await interaction.reply(`${interaction.client.ws.ping}ms`); } }; diff --git a/src/commands/roll.ts b/src/commands/roll.ts index 643236c..849c755 100644 --- a/src/commands/roll.ts +++ b/src/commands/roll.ts @@ -1,4 +1,3 @@ - import { SlashCommandBuilder, EmbedBuilder, @@ -16,7 +15,7 @@ import { } from "../DBMain"; import { rollForGacha } from "../DBUtils"; import { checkIfFirstOrLast, getEmbedColor, tryDelete } from "../utils"; -import { BUTTONS as b } from "../constants/buttons"; +import { BUTTONS } from "../constants/buttons"; let rollAgainInUse = false; @@ -36,7 +35,6 @@ export const Roll = { } let rolls = 1 - if (rollingAgain && rollingAgain > 0) { rolls = rollingAgain; } else { @@ -45,16 +43,10 @@ export const Roll = { rolls = checkIfRolls; } } - const userID = interaction.user.id; + const userID = interaction.user.id; const currentWallet = await checkOrStartWallet(userID); - if (!currentWallet && currentWallet !== 0) { - console.log(`${userID}'s currentWallet is NULL or undefined`); - await interaction.reply('Failed to check balance, please contact a developer'); - return; - } - const price = 10; const totalPrice = price * rolls; if (currentWallet < totalPrice) { @@ -67,18 +59,15 @@ export const Roll = { } else { await addOrSubtractWallet(userID, -totalPrice); } - const gacha = await rollForGacha(rolls); let gachaLevel: number[] = []; let level: string[] = []; const embeds: EmbedBuilder[] = []; - //Cycle through all rolls, creates embed for each. + //Cycle through all rolls, creates embed for each gacha acquired for (let i = 0; i < rolls; i++) { - await addToCollection(userID, gacha[i].id); - const getLevel = await checkGachaLevel(userID, gacha[i].id); if (!getLevel) { console.log(`ERROR: Invalid level for - User: ${userID}, Gacha: ${gacha[i].id}`); @@ -97,7 +86,6 @@ export const Roll = { level.push(lvlUp + starArray); const color = await getEmbedColor(gacha[i].rarity); - const embed = new EmbedBuilder(); embed.setTitle(`${gacha[i].name} Jeffrey`) .setDescription(`${gacha[i].description}`) @@ -110,11 +98,11 @@ export const Roll = { } if (rolls === 1) { - b.next.setDisabled(true); + BUTTONS.NEXT_BUTTON.setDisabled(true); } //previous starts disabled const row = new ActionRowBuilder() - .setComponents(b.previous, b.next, b.rollAgain); + .setComponents(BUTTONS.PREVIOUS_BUTTON, BUTTONS.NEXT_BUTTON, BUTTONS.ROLL_AGAIN_BUTTON); let currentGacha = 0; @@ -123,6 +111,7 @@ export const Roll = { } else { await interaction.reply(`${interaction.user} rolled for ${rolls} Jeffrey(s)!`); } + const gachaMessage = await interaction.channel?.send({ content: `**${gacha[currentGacha].rarity.toUpperCase()} JEFFREY**`, embeds: [embeds[currentGacha]], @@ -138,7 +127,7 @@ export const Roll = { buttonCollector.on('collect', async i => { - if (i.customId === 'next') { + if (i.customId === BUTTONS.NEXT_ID) { currentGacha += 1; await checkIfFirstOrLast(currentGacha, rolls); await i.update({ @@ -147,7 +136,8 @@ export const Roll = { components: [row] }); } - if (i.customId === 'previous') { + + if (i.customId === BUTTONS.PREVIOUS_ID) { currentGacha -= 1; await checkIfFirstOrLast(currentGacha, rolls); await i.update({ @@ -157,13 +147,13 @@ export const Roll = { }); } - if (i.customId === 'roll_again') { + if (i.customId === BUTTONS.ROLL_AGAIN_ID) { if (rollAgainInUse) { await i.update({}) await interaction.channel?.send('Please complete your previous roll again before using this one!'); } else { rollAgainInUse = true; - row.setComponents(b.previous, b.next); + row.setComponents(BUTTONS.PREVIOUS_BUTTON, BUTTONS.NEXT_BUTTON); await i.update({ components: [row] }); const rollAgainMsg = await interaction.channel?.send(`How many more rolls would you like to do? (Please type a number, type '0' to cancel)`); @@ -177,9 +167,9 @@ export const Roll = { await tryDelete(rollAgainMsg!); msgCollector.stop(); } + const content = m.content; const parsedNumber = Number(content); - if (!isNaN(parsedNumber)) { msgCollector.stop(); rollAgainInUse = false; diff --git a/src/constants/buttons.ts b/src/constants/buttons.ts index 2c8ef17..9fe1c15 100644 --- a/src/constants/buttons.ts +++ b/src/constants/buttons.ts @@ -1,19 +1,22 @@ import { ButtonBuilder, ButtonStyle } from "discord.js"; -export const BUTTONS = { - next: new ButtonBuilder() - .setCustomId('next') - .setLabel('Next') - .setStyle(ButtonStyle.Primary), +export namespace BUTTONS { + export const NEXT_ID: string = 'next'; + export const NEXT_BUTTON = new ButtonBuilder() + .setCustomId(NEXT_ID) + .setLabel('Next ➡') + .setStyle(ButtonStyle.Primary); - previous: new ButtonBuilder() - .setCustomId('previous') - .setLabel('Previous') + export const PREVIOUS_ID: string = 'previous'; + export const PREVIOUS_BUTTON = new ButtonBuilder() + .setCustomId(PREVIOUS_ID) + .setLabel('⬅ Previous') .setStyle(ButtonStyle.Primary) - .setDisabled(true), + .setDisabled(true); - rollAgain: new ButtonBuilder() - .setCustomId('roll_again') - .setLabel('Roll Again!') - .setStyle(ButtonStyle.Danger) + export const ROLL_AGAIN_ID: string = 'roll_again'; + export const ROLL_AGAIN_BUTTON = new ButtonBuilder() + .setCustomId(ROLL_AGAIN_ID) + .setLabel('🔁 Roll Again!') + .setStyle(ButtonStyle.Danger); } \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index b9041c2..3f0e126 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,13 +5,12 @@ import { ClientOptions, REST, Routes, - GatewayIntentBits, - ChatInputCommandInteraction + GatewayIntentBits } from 'discord.js'; import { Ping } from './commands/ping'; import { Mock } from './commands/mock'; import { Cat } from './commands/cat'; -import { Wallet } from './commands/balance'; +import { Balance } from './commands/balance'; import { Poll } from './commands/poll'; import { Roll } from './commands/roll'; import { DB } from './JeffreyDB'; @@ -58,7 +57,7 @@ async function main() { Mock.info.toJSON(), Cat.info.toJSON(), Poll.info.toJSON(), - Wallet.info.toJSON(), + Balance.info.toJSON(), Roll.info.toJSON() ] } @@ -72,7 +71,7 @@ async function main() { Poll.addChoiceOptions(); client.on('interactionCreate', async (interaction) => { - if (!interaction.isCommand()) return; + if (!interaction.isChatInputCommand()) return; const { commandName } = interaction; const userID = interaction.user.id; @@ -104,13 +103,13 @@ client.on('interactionCreate', async (interaction) => { await Cat.run(interaction); } if (commandName === 'poll') { - await Poll.run(interaction as ChatInputCommandInteraction); + await Poll.run(interaction); } if (commandName === 'balance') { - await Wallet.run(interaction as ChatInputCommandInteraction); + await Balance.run(interaction); } if (commandName === 'roll') { - await Roll.run(interaction as ChatInputCommandInteraction); + await Roll.run(interaction); } }); diff --git a/src/utils.ts b/src/utils.ts index 582387b..592a012 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,5 +1,5 @@ -import { Message, HexColorString, ActionRowBuilder, ButtonBuilder } from 'discord.js'; -import { BUTTONS as b } from './constants/buttons'; +import { Message, HexColorString } from 'discord.js'; +import { BUTTONS } from './constants/buttons'; export const NUMBER_EMOJIS: string[] = [ "1⃣", @@ -13,58 +13,77 @@ export const NUMBER_EMOJIS: string[] = [ "9⃣", "🔟" ]; + /** * Chooses a random number between min and max - * @param min - the lowest possible number - * @param max - the highest possible number + * @param {number} min - the lowest possible number + * @param {number} max - the highest possible number * @returns - returns the random number */ export async function rng(min: number, max: number): Promise { - const randomDecimal = Math.random(); - const range = max - min + 1; const randomNumber = Math.floor(randomDecimal * range + 1) + min; - return randomNumber -} +}; +/** + * Function that takes a rarity and returns the color that the embed for that gacha rarity should be. + * + * @param {string} rarity - the rarity of the gacha to be displayed + * @returns {Promise} - the color(hexadecimal color string) associated with the given rarity + */ export async function getEmbedColor(rarity: string): Promise { let color: HexColorString; - if(rarity === 'common') { + if (rarity === 'common') { color = `#808080`; //grey - }else if(rarity === 'uncommon') { + } else if (rarity === 'uncommon') { color = `#00A36C`; //green - }else if (rarity === 'rare') { + } else if (rarity === 'rare') { color = `#FF69B4`; //pink - }else if (rarity === 'legendary') { + } else if (rarity === 'legendary') { color = `#D4A017`;// orange gold - }else{ + } else { color = `#000000` //black (shouldnt show up) } return color; -} +}; +/** + * Attempts to delete specified message. + * + * @param {Message} m - a discord message + * @returns {Promise} - Whether or not the message was successfully deleted + */ export async function tryDelete(m: Message): Promise { - try{ + try { m.delete(); return true; - }catch(err){ + } catch (err) { console.error('could not delete message', err); return false; } -} +}; +/** + * Function used specifically for situations where the user cycles through 'Next' and 'Previous' buttons. + * Checks if next/previous should be disabled or not. + * Previous - disabled if there is no place before it (position 0) + * Next - disabled if there is no place above it (current position === max/highest possible position) + * + * @param {number} currentPos - The current position in an array/list + * @param {number} max - The highest possible position/number + */ export async function checkIfFirstOrLast(currentPos: number, max: number): Promise { if (max - currentPos <= 1) { - b.next.setDisabled(true); + BUTTONS.NEXT_BUTTON.setDisabled(true); } else { - b.next.setDisabled(false); + BUTTONS.NEXT_BUTTON.setDisabled(false); } if (currentPos < 1) { - b.previous.setDisabled(true); + BUTTONS.PREVIOUS_BUTTON.setDisabled(true); } else { - b.previous.setDisabled(false); + BUTTONS.PREVIOUS_BUTTON.setDisabled(false); } -} \ No newline at end of file +}; \ No newline at end of file From 6bdcaf102e340668e0037906418c668cf323ede4 Mon Sep 17 00:00:00 2001 From: Dylan Stauch <50031006+sjkd23@users.noreply.github.com> Date: Mon, 18 Sep 2023 03:18:28 -0400 Subject: [PATCH 12/17] spacing, docs, constants, formatting. --- src/commands/dm.ts | 96 +++++++++++++++++++++------------------- src/commands/poll.ts | 2 +- src/commands/roll.ts | 4 +- src/constants/buttons.ts | 64 +++++++++++++++++++-------- src/constants/misc.ts | 19 ++++++++ src/index.ts | 8 ++-- src/utils.ts | 52 +++++++++++----------- 7 files changed, 147 insertions(+), 98 deletions(-) create mode 100644 src/constants/misc.ts diff --git a/src/commands/dm.ts b/src/commands/dm.ts index 4ff325e..85fc80a 100644 --- a/src/commands/dm.ts +++ b/src/commands/dm.ts @@ -1,18 +1,18 @@ -import { -ChatInputCommandInteraction, -SlashCommandBuilder, -EmbedBuilder, -ActionRowBuilder, -ButtonBuilder, -ButtonStyle, -ComponentType, -MessageCollector, -User +import { + ChatInputCommandInteraction, + SlashCommandBuilder, + EmbedBuilder, + ActionRowBuilder, + ButtonBuilder, + ComponentType, + MessageCollector, + User } from 'discord.js'; import { tryDelete, tryToDMEmbed } from '../utils'; -import { BUTTONS as b} from '../constants/buttons'; +import { BUTTONS } from '../constants/buttons'; +import { TIME_IN_MS } from '../constants/misc'; -const MAX_CHOICES = 15; +const MAX_CHOICES = 5; export const DM = { info: new SlashCommandBuilder() @@ -42,17 +42,13 @@ export const DM = { const userID = interaction.user.id; let higherUp = false; - const getRole = interaction.guild.roles.cache; - const role = [ getRole.find(role => role.name.toLowerCase() === 'moderator'), getRole.find(role => role.name.toLowerCase() === 'officer'), getRole.find(role => role.name.toLowerCase() === 'head raid leader') ]; - const commandUser = await interaction.guild.members.fetch(userID); - for (const currentRole of role) { if (!currentRole) { console.log(`ERROR finding higher up role(s) for balance command`); @@ -85,24 +81,30 @@ export const DM = { .setDescription(`**Are you sure you want to send this message to these users?**`) .setFields( { name: '__Message Recipients:__ ', value: users.join(', ') }, - { name: '__Current Message:__ ', value: message }); + { name: '__Current Message:__ ', value: message } + ); //confirmation action row with buttons const row = new ActionRowBuilder() - .setComponents(b.cancel, b.yes); + .setComponents(BUTTONS.CANCEL_BUTTON, BUTTONS.YES_BUTTON); const embedMessage = await interaction.reply({ content: `Please read over the following:`, embeds: [embed], components: [row] }); - const collector = embedMessage.createMessageComponentCollector({ filter: i => i.user.id === interaction.user.id, componentType: ComponentType.Button, time: 60_000 }); + const collector = embedMessage.createMessageComponentCollector({ + filter: i => i.user.id === interaction.user.id, + componentType: ComponentType.Button, + time: TIME_IN_MS.TEN_MINUTES + }); collector.on('collect', async i => { - if (i.customId === 'cancel') { + + if (i.customId === BUTTONS.CANCEL_ID) { await i.update({}); await interaction.editReply({ content: `Process Canceled.`, components: [] }); return; } - else if (i.customId === 'yes') { + else if (i.customId === BUTTONS.YES_ID) { await i.update({}); await sendDMs(message, users); return; @@ -133,15 +135,16 @@ export const DM = { .setDescription('Please type the names of the users you\'d like me to DM!'); const startRow = new ActionRowBuilder() - .setComponents(b.cancel, b.addMessage, b.addUsers, b.send); + .setComponents(BUTTONS.CANCEL_BUTTON, BUTTONS.ADD_MESSAGE_BUTTON, BUTTONS.ADD_USERS_BUTTON, BUTTONS.SEND_BUTTON); const addMsgRow = new ActionRowBuilder() - .setComponents(b.back, b.done); + .setComponents(BUTTONS.BACK_BUTTON, BUTTONS.DONE_BUTTON); + const addUsersRow = new ActionRowBuilder() - .setComponents(b.back, b.done, b.reset); + .setComponents(BUTTONS.BACK_BUTTON, BUTTONS.DONE_BUTTON, BUTTONS.RESET_BUTTON); const confirmRow = new ActionRowBuilder() - .setComponents(b.back, b.yes); + .setComponents(BUTTONS.BACK_BUTTON, BUTTONS.YES_BUTTON); const embedMessage = await interaction.reply({ content: 'Please follow the instructions below!', embeds: [startEmbed], components: [startRow] }); @@ -151,21 +154,26 @@ export const DM = { return; } - const buttonCollector = embedMessage.createMessageComponentCollector({ filter: i => i.user.id === interaction.user.id, componentType: ComponentType.Button, time: 60_000 }); + const buttonCollector = embedMessage.createMessageComponentCollector({ + filter: i => i.user.id === interaction.user.id, + componentType: ComponentType.Button, + time: TIME_IN_MS.TEN_MINUTES + }); let msgCollector: MessageCollector; let userCollector: MessageCollector; let memberList: User[] = []; let DM = ''; + const msgTooLong = '**__Message too long! (limit: 1024 characters)__**'; buttonCollector.on('collect', async i => { - if (i.customId === 'add_message') { + if (i.customId === BUTTONS.ADD_MESSAGE_ID) { await i.update({ embeds: [addMsgEmbed], components: [addMsgRow] }) msgCollector = interaction.channel!.createMessageCollector({ filter: (m) => m.author.id === userID, - time: 60_000, + time: TIME_IN_MS.THIRTY_MINUTES, }); msgCollector.on('collect', async m => { @@ -173,7 +181,7 @@ export const DM = { if (m.content) { DM = m.content; if (DM.length > 1024) { - DM = '**__Message too long! (limit: 1024 characters)__**'; + DM = msgTooLong; } addMsgEmbed.setFields({ name: '__Current Message:__ ', value: DM }); } else { @@ -183,12 +191,12 @@ export const DM = { }); } - else if (i.customId === 'add_users') { + else if (i.customId === BUTTONS.ADD_USERS_ID) { await i.update({ embeds: [addUsersEmbed], components: [addUsersRow] }); userCollector = interaction.channel!.createMessageCollector({ filter: (m) => m.author.id === userID, - time: 60_000, + time: TIME_IN_MS.TEN_MINUTES, }); userCollector.on('collect', async m => { @@ -220,15 +228,15 @@ export const DM = { }); } - else if (i.customId === 'cancel') { + else if (i.customId === BUTTONS.CANCEL_ID) { await interaction.editReply({ content: 'Process canceled', components: [] }); console.log('Stopped all Collectors'); return; } - else if (i.customId === 'done') { - msgCollector?.stop(); - userCollector?.stop(); + else if (i.customId === BUTTONS.DONE_ID) { + msgCollector.stop(); + userCollector.stop(); startEmbed.setTitle('DM User(s)') .setFields( @@ -239,7 +247,7 @@ export const DM = { if (memberList.length > 0) { startEmbed.addFields({ name: '__Message Recipients:__ ', value: memberList.join(', ') }); } - if (DM.length > 0 && DM !== '**__Message too long! (limit: 4096 characters)__**') { + if (DM.length > 0 && DM !== msgTooLong) { startEmbed.addFields({ name: '__Current Message:__ ', value: DM }); } @@ -247,14 +255,14 @@ export const DM = { await i.update({}); } - else if (i.customId === 'back') { - msgCollector?.stop(); - userCollector?.stop(); + else if (i.customId === BUTTONS.BACK_ID) { + msgCollector.stop(); + userCollector.stop(); await i.update({ embeds: [startEmbed], components: [startRow] }); } - else if (i.customId === 'reset') { + else if (i.customId === BUTTONS.RESET_ID) { memberList.length = 0; addUsersEmbed.setDescription('Please type the names of the user(s) you\'d like me to DM!'); addUsersEmbed.spliceFields(0, 1); @@ -262,7 +270,7 @@ export const DM = { await interaction.editReply({ embeds: [addUsersEmbed] }); } - else if (i.customId === 'send') { + else if (i.customId === BUTTONS.SEND_ID) { if (!DM || memberList.length < 1) { await interaction.channel?.send(`No message/no user specified!`); await i.update({}) @@ -275,7 +283,7 @@ export const DM = { await interaction.editReply({ embeds: [startEmbed], components: [confirmRow] }); await i.update({ components: [confirmRow] }); } - } else if (i.customId === 'yes') { + } else if (i.customId === BUTTONS.YES_ID) { buttonCollector.stop(); await i.update({}) await sendDMs(DM, memberList); @@ -287,11 +295,10 @@ export const DM = { * Attempts to send specified message to all specified users. * * @param {string} m - The message that is to be sent to all users - * @param {User[]}users - Object array of all specified discord users. + * @param {User[]} users - Object array of all specified discord users. */ async function sendDMs(m: string, users: User[]): Promise { const DMEmbed = new EmbedBuilder() - .addFields( { name: '__Message Content:__ ', value: m }, { name: ' ', value: '--END--' }, @@ -301,7 +308,6 @@ export const DM = { }, ) .setColor('#8B0000'); - //tries to get the server icon const serverIcon = interaction.guild?.iconURL(); if (serverIcon) { diff --git a/src/commands/poll.ts b/src/commands/poll.ts index 56dd91c..7a64030 100644 --- a/src/commands/poll.ts +++ b/src/commands/poll.ts @@ -1,5 +1,5 @@ import { ChatInputCommandInteraction, EmbedBuilder, SlashCommandBuilder } from 'discord.js'; -import { NUMBER_EMOJIS } from '../utils'; +import { NUMBER_EMOJIS } from '../constants/misc'; const MAX_CHOICES = 5; diff --git a/src/commands/roll.ts b/src/commands/roll.ts index 97bbaee..4bd5eb3 100644 --- a/src/commands/roll.ts +++ b/src/commands/roll.ts @@ -1,5 +1,5 @@ import { JeffreyGachaURLs, displayLegendary } from "../JeffreyGacha"; -import { SlashCommandBuilder, CommandInteraction, EmbedBuilder } from "discord.js"; +import { SlashCommandBuilder, CommandInteraction, EmbedBuilder, ChatInputCommandInteraction } from "discord.js"; import { replyWithEmbed, rng } from '../utils'; import { addNewGacha, @@ -16,7 +16,7 @@ export const Roll = { .setName('roll') .setDescription('Roll for Jeffrey\'s!'), - run: async (interaction: CommandInteraction): Promise => { + run: async (interaction: ChatInputCommandInteraction): Promise => { if (!interaction.guild) { await interaction.reply('this command can only be done in a server'); return; diff --git a/src/constants/buttons.ts b/src/constants/buttons.ts index 0a2f44a..b6e1817 100644 --- a/src/constants/buttons.ts +++ b/src/constants/buttons.ts @@ -1,44 +1,70 @@ import { ButtonBuilder, ButtonStyle } from "discord.js" -export const BUTTONS = { - addMessage: new ButtonBuilder() +export namespace BUTTONS { + export const NEXT_ID: string = 'next'; + export const NEXT_BUTTON = new ButtonBuilder() + .setCustomId(NEXT_ID) + .setLabel('Next ➡') + .setStyle(ButtonStyle.Primary); + + export const PREVIOUS_ID: string = 'previous'; + export const PREVIOUS_BUTTON = new ButtonBuilder() + .setCustomId(PREVIOUS_ID) + .setLabel('⬅ Previous') + .setStyle(ButtonStyle.Primary) + .setDisabled(true); + + export const ROLL_AGAIN_ID: string = 'roll_again'; + export const ROLL_AGAIN_BUTTON = new ButtonBuilder() + .setCustomId(ROLL_AGAIN_ID) + .setLabel('🔁 Roll Again!') + .setStyle(ButtonStyle.Danger); + + export const ADD_MESSAGE_ID: string = 'add_message'; + export const ADD_MESSAGE_BUTTON = new ButtonBuilder() .setCustomId('add_message') .setLabel('✉ Add/Change Message') - .setStyle(ButtonStyle.Primary), + .setStyle(ButtonStyle.Primary); - addUsers: new ButtonBuilder() + export const ADD_USERS_ID: string = 'add_users'; + export const ADD_USERS_BUTTON = new ButtonBuilder() .setCustomId('add_users') .setLabel('➕ Add Users') - .setStyle(ButtonStyle.Primary), + .setStyle(ButtonStyle.Primary); - send: new ButtonBuilder() + export const SEND_ID: string = 'send'; + export const SEND_BUTTON = new ButtonBuilder() .setCustomId('send') .setLabel('📤 Send') - .setStyle(ButtonStyle.Success), + .setStyle(ButtonStyle.Success); - back: new ButtonBuilder() + export const BACK_ID: string = 'back'; + export const BACK_BUTTON = new ButtonBuilder() .setCustomId('back') .setLabel('⬅ Back') - .setStyle(ButtonStyle.Danger), + .setStyle(ButtonStyle.Danger); - done: new ButtonBuilder() + export const DONE_ID: string = 'done'; + export const DONE_BUTTON = new ButtonBuilder() .setCustomId('done') .setLabel('✅ Done') - .setStyle(ButtonStyle.Success), + .setStyle(ButtonStyle.Success); - reset: new ButtonBuilder() + export const RESET_ID: string = 'reset'; + export const RESET_BUTTON = new ButtonBuilder() .setCustomId('reset') .setLabel('🔃 Reset') - .setStyle(ButtonStyle.Danger), + .setStyle(ButtonStyle.Danger); - cancel: new ButtonBuilder() + export const CANCEL_ID: string = 'cancel'; + export const CANCEL_BUTTON = new ButtonBuilder() .setCustomId('cancel') .setLabel('❌ Cancel') - .setStyle(ButtonStyle.Danger), + .setStyle(ButtonStyle.Danger); - yes: new ButtonBuilder() + export const YES_ID: string = 'yes'; + export const YES_BUTTON = new ButtonBuilder() .setCustomId('yes') .setLabel('✅ Yes') - .setStyle(ButtonStyle.Success) -} - + .setStyle(ButtonStyle.Success); +} \ No newline at end of file diff --git a/src/constants/misc.ts b/src/constants/misc.ts new file mode 100644 index 0000000..584054b --- /dev/null +++ b/src/constants/misc.ts @@ -0,0 +1,19 @@ +export const NUMBER_EMOJIS: string[] = [ + "1⃣", + "2⃣", + "3⃣", + "4⃣", + "5⃣", + "6⃣", + "7⃣", + "8⃣", + "9⃣", + "🔟" +]; + +export const TIME_IN_MS = { + ONE_MINUTE: 60_000, + TEN_MINUTES: 600_000, + THIRTY_MINUTES: 1_800_000, + ONE_HOUR: 3_600_000 +}; \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 472f1b3..8e6b894 100644 --- a/src/index.ts +++ b/src/index.ts @@ -77,7 +77,7 @@ async function main() { } client.on('interactionCreate', async (interaction) => { - if (!interaction.isCommand()) return; + if (!interaction.isChatInputCommand()) return; const { commandName } = interaction; const userID = interaction.user.id; @@ -109,16 +109,16 @@ client.on('interactionCreate', async (interaction) => { await Cat.run(interaction); } if (commandName === 'poll') { - Poll.run(interaction as ChatInputCommandInteraction); + Poll.run(interaction); } if (commandName === 'balance') { - await Balance.run(interaction as ChatInputCommandInteraction); + await Balance.run(interaction); } if (commandName === 'roll') { await Roll.run(interaction); } if (commandName === 'dm') { - await DM.run(interaction as ChatInputCommandInteraction); + await DM.run(interaction); } }); diff --git a/src/utils.ts b/src/utils.ts index aa48374..0539847 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,47 +1,40 @@ -import { CommandInteraction, EmbedBuilder, Message, User } from 'discord.js'; +import { ChatInputCommandInteraction, CommandInteraction, Embed, EmbedBuilder, Message, User } from 'discord.js'; -export const NUMBER_EMOJIS: string[] = [ - "1⃣", - "2⃣", - "3⃣", - "4⃣", - "5⃣", - "6⃣", - "7⃣", - "8⃣", - "9⃣", - "🔟" -]; /** * Chooses a random number between min and max - * @param min - the lowest possible number - * @param max - the highest possible number - * @returns - returns the random number + * @param {number} min - the lowest possible number + * @param {number} max - the highest possible number + * @returns {Promise} - returns the random number */ export async function rng(min: number, max: number): Promise { const randomDecimal = Math.random(); - const range = max - min + 1; const randomNumber = Math.floor(randomDecimal * range) + min; + return randomNumber; +}; - return randomNumber -} /** * Replies to the command user with an embedded message. * Embed will be constructed in command files. - * @param embed - Embed with desired information/fields - * @param interaction - The command interaction to reply to. + * @param {EmbedBuilder} embed - Embed with desired information/fields + * @param {ChatInputCommandInteraction} interaction - The command interaction to reply to. */ -export async function replyWithEmbed(embed: EmbedBuilder, interaction: CommandInteraction): Promise { +export async function replyWithEmbed(embed: EmbedBuilder, interaction: ChatInputCommandInteraction): Promise { try { await interaction.reply({ embeds: [embed] }); - console.log(`replied with embed in ${interaction.channelId}`); + console.log(`Replied with embed in ${interaction.channelId}`); } catch { console.log(`ERROR: could not send embed in ${interaction.channelId}`); return; } -} +}; +/** + * Attempts to delete specified message. + * + * @param {Message} m - a discord message + * @returns {Promise} - Whether or not the message was successfully deleted + */ export async function tryDelete(m: Message): Promise { try{ m.delete(); @@ -50,10 +43,15 @@ export async function tryDelete(m: Message): Promise { console.error('could not delete message', err); return false; } -} +}; +/** + * Function that takes a previously created embed, and attempts to DM it to the specified member + * + * @param {EmbedBuilder} embed - The embed message to send. + * @param {User} member - Which member to dm + */ export async function tryToDMEmbed(embed: EmbedBuilder, member: User): Promise{ - try{ await member.send({embeds: [embed]}); console.log(`Sent message to ${member.id}`) @@ -62,4 +60,4 @@ export async function tryToDMEmbed(embed: EmbedBuilder, member: User): Promise Date: Mon, 18 Sep 2023 03:30:21 -0400 Subject: [PATCH 13/17] more formatting --- src/commands/dm.ts | 4 ++-- src/index.ts | 7 +++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/commands/dm.ts b/src/commands/dm.ts index 85fc80a..c55c83c 100644 --- a/src/commands/dm.ts +++ b/src/commands/dm.ts @@ -99,13 +99,13 @@ export const DM = { collector.on('collect', async i => { if (i.customId === BUTTONS.CANCEL_ID) { - await i.update({}); + await i.update({ components: [] }); await interaction.editReply({ content: `Process Canceled.`, components: [] }); return; } else if (i.customId === BUTTONS.YES_ID) { - await i.update({}); + await i.update({ components: [] }); await sendDMs(message, users); return; } diff --git a/src/index.ts b/src/index.ts index 8e6b894..04cafab 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,8 +5,7 @@ import { ClientOptions, REST, Routes, - GatewayIntentBits, - ChatInputCommandInteraction + GatewayIntentBits } from 'discord.js'; import { Ping } from './commands/ping'; import { Mock } from './commands/mock'; @@ -32,8 +31,8 @@ const intents = [ GatewayIntentBits.Guilds, GatewayIntentBits.GuildMembers, GatewayIntentBits.GuildMessages, - GatewayIntentBits.MessageContent, -] + GatewayIntentBits.MessageContent +]; const options: ClientOptions = { intents: intents, From 72acd91ebaaed3bf7721497865b57d4f688e5b38 Mon Sep 17 00:00:00 2001 From: Dylan Stauch <50031006+sjkd23@users.noreply.github.com> Date: Mon, 18 Sep 2023 09:08:08 -0400 Subject: [PATCH 14/17] file re-organization. index size trimming. --- src/{ => DB}/JeffreyDB.ts | 10 +-- src/{ => DB}/JeffreyGacha.ts | 0 src/commands/balance.ts | 6 +- src/commands/dm.ts | 6 +- src/commands/mock.ts | 3 +- src/commands/poll.ts | 2 +- src/commands/roll.ts | 10 +-- .../{buttons.ts => buttonConstants.ts} | 0 src/constants/{misc.ts => miscConstants.ts} | 2 +- src/index.ts | 84 ++----------------- src/{ => utils}/DBUtils.ts | 2 +- src/utils/cmdUtils.ts | 77 +++++++++++++++++ src/{utils.ts => utils/miscUtils.ts} | 0 13 files changed, 103 insertions(+), 99 deletions(-) rename src/{ => DB}/JeffreyDB.ts (92%) rename src/{ => DB}/JeffreyGacha.ts (100%) rename src/constants/{buttons.ts => buttonConstants.ts} (100%) rename src/constants/{misc.ts => miscConstants.ts} (90%) rename src/{ => utils}/DBUtils.ts (98%) create mode 100644 src/utils/cmdUtils.ts rename src/{utils.ts => utils/miscUtils.ts} (100%) diff --git a/src/JeffreyDB.ts b/src/DB/JeffreyDB.ts similarity index 92% rename from src/JeffreyDB.ts rename to src/DB/JeffreyDB.ts index c27ef60..f03ecc9 100644 --- a/src/JeffreyDB.ts +++ b/src/DB/JeffreyDB.ts @@ -92,11 +92,11 @@ export const DB = { console.error('Unable to connect to the database:', error); } }, -/** - * sync: - * Tries to sync all database tables with the database file, - * which allows it to build upon the information previously stored in their respective tables after bot resets. - */ + /** + * sync: + * Tries to sync all database tables with the database file, + * which allows it to build upon the information previously stored in their respective tables after bot resets. + */ sync: async (): Promise => { try { await User.sync(); diff --git a/src/JeffreyGacha.ts b/src/DB/JeffreyGacha.ts similarity index 100% rename from src/JeffreyGacha.ts rename to src/DB/JeffreyGacha.ts diff --git a/src/commands/balance.ts b/src/commands/balance.ts index 8394f6b..fd2dfe6 100644 --- a/src/commands/balance.ts +++ b/src/commands/balance.ts @@ -1,6 +1,6 @@ import { SlashCommandBuilder, EmbedBuilder, ChatInputCommandInteraction, User } from 'discord.js'; -import { checkUser, checkBalance, addOrSubtractBalance, addUser, findOrAddUserWallet } from '../DBUtils'; -import { replyWithEmbed } from '../utils'; +import { checkUser, checkBalance, addOrSubtractBalance, addUser, findOrAddUserWallet } from '../utils/DBUtils'; +import { replyWithEmbed } from '../utils/miscUtils'; export const Balance = { info: new SlashCommandBuilder() @@ -52,7 +52,7 @@ export const Balance = { break; } } - if(!higherUp){ + if (!higherUp) { interaction.reply('Only officer+ can modify member balances!'); return; } diff --git a/src/commands/dm.ts b/src/commands/dm.ts index c55c83c..7d13765 100644 --- a/src/commands/dm.ts +++ b/src/commands/dm.ts @@ -8,9 +8,9 @@ import { MessageCollector, User } from 'discord.js'; -import { tryDelete, tryToDMEmbed } from '../utils'; -import { BUTTONS } from '../constants/buttons'; -import { TIME_IN_MS } from '../constants/misc'; +import { tryDelete, tryToDMEmbed } from '../utils/miscUtils'; +import { BUTTONS } from '../constants/buttonConstants'; +import { TIME_IN_MS } from '../constants/miscConstants'; const MAX_CHOICES = 5; diff --git a/src/commands/mock.ts b/src/commands/mock.ts index 7a9fdb1..f0e76cd 100644 --- a/src/commands/mock.ts +++ b/src/commands/mock.ts @@ -47,6 +47,7 @@ export const Mock = { } mockTargets.add(target.id); await interaction.reply(`Sure thing, I will begin mocking ${target}!`); + console.log(`Current Mock list: ${mockTargets}`); }, /** * Deletes 'mock' targets message and re-sends it in "sPoNgEbOb" text format @@ -69,7 +70,6 @@ export const Mock = { shouldBeLower = !shouldBeLower; } } - // Attempt to delete the original message try { await message.delete(); @@ -78,7 +78,6 @@ export const Mock = { console.log(`ERROR: could not delete ${message.author.username} (${message.author.id}) message in ${message.channel}`); return; } - // Attempt to send the mocked message try { await message.channel.send(`${message.author} says "${mockMessage}"`); diff --git a/src/commands/poll.ts b/src/commands/poll.ts index 7a64030..a7c0551 100644 --- a/src/commands/poll.ts +++ b/src/commands/poll.ts @@ -1,5 +1,5 @@ import { ChatInputCommandInteraction, EmbedBuilder, SlashCommandBuilder } from 'discord.js'; -import { NUMBER_EMOJIS } from '../constants/misc'; +import { NUMBER_EMOJIS } from '../constants/miscConstants'; const MAX_CHOICES = 5; diff --git a/src/commands/roll.ts b/src/commands/roll.ts index 4bd5eb3..fdd1809 100644 --- a/src/commands/roll.ts +++ b/src/commands/roll.ts @@ -1,6 +1,6 @@ -import { JeffreyGachaURLs, displayLegendary } from "../JeffreyGacha"; +import { JeffreyGachaURLs, displayLegendary } from "../DB/JeffreyGacha"; import { SlashCommandBuilder, CommandInteraction, EmbedBuilder, ChatInputCommandInteraction } from "discord.js"; -import { replyWithEmbed, rng } from '../utils'; +import { replyWithEmbed, rng } from '../utils/miscUtils'; import { addNewGacha, findOrAddUserWallet, @@ -9,7 +9,7 @@ import { gachaLvlUp, checkGachaLevel, checkIfUserHasGachaInv -} from "../DBUtils"; +} from "../utils/DBUtils"; export const Roll = { info: new SlashCommandBuilder() @@ -35,7 +35,7 @@ export const Roll = { } const price: number = 5; - + if (currentBalance < price) { await interaction.reply('not enough JeffreyCoins!'); return; @@ -76,7 +76,7 @@ export const Roll = { } let embed: EmbedBuilder; - + if (raritySelect !== 'Legendary') { embed = new EmbedBuilder() .setTitle(`You pulled a **${displayRarity}** Jeffrey!`) diff --git a/src/constants/buttons.ts b/src/constants/buttonConstants.ts similarity index 100% rename from src/constants/buttons.ts rename to src/constants/buttonConstants.ts diff --git a/src/constants/misc.ts b/src/constants/miscConstants.ts similarity index 90% rename from src/constants/misc.ts rename to src/constants/miscConstants.ts index 584054b..a9e9bb8 100644 --- a/src/constants/misc.ts +++ b/src/constants/miscConstants.ts @@ -11,7 +11,7 @@ export const NUMBER_EMOJIS: string[] = [ "🔟" ]; -export const TIME_IN_MS = { +export const TIME_IN_MS = { ONE_MINUTE: 60_000, TEN_MINUTES: 600_000, THIRTY_MINUTES: 1_800_000, diff --git a/src/index.ts b/src/index.ts index 04cafab..4470dad 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,25 +4,14 @@ import { Client, ClientOptions, REST, - Routes, GatewayIntentBits } from 'discord.js'; -import { Ping } from './commands/ping'; import { Mock } from './commands/mock'; -import { Cat } from './commands/cat'; -import { Balance } from './commands/balance'; -import { Poll } from './commands/poll'; -import { Roll } from './commands/roll'; -import { DM } from './commands/dm'; -import { DB } from './JeffreyDB'; +import { DB } from './DB/JeffreyDB'; +import { checkAndRunCommand, initCmds } from './utils/cmdUtils'; config(); -Poll.addChoiceOptions(); -DM.addChoiceOptions(); -const cooldown = new Map>(); -const cooldownTime = 5000; - const token = process.env.BOT_TOKEN; const clientID = process.env.CLIENT_ID; const guildID = process.env.GUILD_ID; @@ -34,12 +23,9 @@ const intents = [ GatewayIntentBits.MessageContent ]; -const options: ClientOptions = { - intents: intents, -}; +const options: ClientOptions = { intents: intents }; const client = new Client(options); - client.login(token); client.once('ready', () => { @@ -48,77 +34,19 @@ client.once('ready', () => { DB.sync(); }); -const rest = new REST({ version: '10' }).setToken(token!); - async function main() { try { console.log('Started refreshing application (/) commands.'); - - await rest.put( - Routes.applicationGuildCommands(clientID!, guildID!), - { - body: [ - Ping.info.toJSON(), - Mock.info.toJSON(), - Cat.info.toJSON(), - Poll.info.toJSON(), - Balance.info.toJSON(), - Roll.info.toJSON(), - DM.info.toJSON() - ] - } - ); - + await initCmds(token!, clientID!, guildID!); console.log('Successfully reloaded application (/) commands.'); } catch (error) { console.error(error); } -} +}; client.on('interactionCreate', async (interaction) => { if (!interaction.isChatInputCommand()) return; - - const { commandName } = interaction; - const userID = interaction.user.id; - - if (!cooldown.has(commandName)) { - cooldown.set(commandName, new Map()); - } - const cooldownMap = cooldown.get(commandName)!; - - if (cooldownMap.has(userID) && cooldownMap.get(userID)! > Date.now() && interaction.user.id !== '218823980524634112') { - const cooldownRemaining = (cooldownMap.get(userID)! - Date.now()) / 1000; - await interaction.reply(`Please wait ${cooldownRemaining.toFixed(1)} seconds.`); - return; - } - cooldownMap.set(userID, Date.now() + cooldownTime); - - console.log(`User ${interaction.user.username} (${userID}) ran the '${commandName}' command | Guild: ${interaction.guild} |` - + ` Channel: ${interaction.channel} | Timestamp: ${interaction.createdAt.toUTCString()}`); - - if (commandName === 'ping') { - await Ping.run(interaction); - } - if (commandName === 'mock') { - console.log(`${interaction.user} is attempting to use the mock command.`); - await Mock.run(interaction); - console.log(`Current mock list: ${Array.from(mockTargets)}`); - } - if (commandName === 'cat') { - await Cat.run(interaction); - } - if (commandName === 'poll') { - Poll.run(interaction); - } - if (commandName === 'balance') { - await Balance.run(interaction); - } - if (commandName === 'roll') { - await Roll.run(interaction); - } - if (commandName === 'dm') { - await DM.run(interaction); - } + await checkAndRunCommand(interaction); }); client.on('messageCreate', async (message) => { diff --git a/src/DBUtils.ts b/src/utils/DBUtils.ts similarity index 98% rename from src/DBUtils.ts rename to src/utils/DBUtils.ts index 524f9d8..3549a88 100644 --- a/src/DBUtils.ts +++ b/src/utils/DBUtils.ts @@ -1,4 +1,4 @@ -import { User, Wallet, GachaInv } from "./JeffreyDB" +import { User, Wallet, GachaInv } from "../DB/JeffreyDB" /** * Checks if the given userID is in the "User" table. diff --git a/src/utils/cmdUtils.ts b/src/utils/cmdUtils.ts new file mode 100644 index 0000000..b08224e --- /dev/null +++ b/src/utils/cmdUtils.ts @@ -0,0 +1,77 @@ +import { ChatInputCommandInteraction, REST, Routes } from "discord.js"; +import { Balance } from "../commands/balance"; +import { Cat } from "../commands/cat"; +import { DM } from "../commands/dm"; +import { Mock } from "../commands/mock"; +import { Ping } from "../commands/ping"; +import { Poll } from "../commands/poll"; +import { Roll } from "../commands/roll"; + +const cooldown = new Map>(); +const cooldownTime = 5000; + +const CMD_LIST = [ + Ping, + Mock, + Cat, + Poll, + Balance, + Roll, + DM +]; + +export async function initCmds(token: string, clientID: string, guildID: string): Promise { + const rest = new REST({ version: '10' }).setToken(token); + + const cmdInfo = CMD_LIST.map(cmd => cmd.info.toJSON()); + await rest.put( + Routes.applicationGuildCommands(clientID, guildID), + { + body: cmdInfo + } + ); + Poll.addChoiceOptions(); + DM.addChoiceOptions(); +}; + +export async function checkAndRunCommand(interaction: ChatInputCommandInteraction): Promise { + const { commandName } = interaction; + const userID = interaction.user.id; + + if (!cooldown.has(commandName)) { + cooldown.set(commandName, new Map()); + } + const cooldownMap = cooldown.get(commandName)!; + + if (cooldownMap.has(userID) && cooldownMap.get(userID)! > Date.now() && interaction.user.id !== '218823980524634112') { + const cooldownRemaining = (cooldownMap.get(userID)! - Date.now()) / 1000; + await interaction.reply(`Please wait ${cooldownRemaining.toFixed(1)} seconds.`); + return; + } + cooldownMap.set(userID, Date.now() + cooldownTime); + + console.log(`User ${interaction.user.username} (${userID}) ran the '${commandName}' command | Guild: ${interaction.guild} |` + + ` Channel: ${interaction.channel} | Timestamp: ${interaction.createdAt.toUTCString()}`); + + if (commandName === 'ping') { + await Ping.run(interaction); + } + if (commandName === 'mock') { + await Mock.run(interaction); + } + if (commandName === 'cat') { + await Cat.run(interaction); + } + if (commandName === 'poll') { + await Poll.run(interaction); + } + if (commandName === 'balance') { + await Balance.run(interaction); + } + if (commandName === 'roll') { + await Roll.run(interaction); + } + if (commandName === 'dm') { + await DM.run(interaction); + } +} \ No newline at end of file diff --git a/src/utils.ts b/src/utils/miscUtils.ts similarity index 100% rename from src/utils.ts rename to src/utils/miscUtils.ts From 4f586f31b0fccdc5f2a403ff5472f1322e47bf45 Mon Sep 17 00:00:00 2001 From: Dylan Stauch <50031006+sjkd23@users.noreply.github.com> Date: Mon, 18 Sep 2023 09:10:08 -0400 Subject: [PATCH 15/17] addChoiceOptions --- src/index.ts | 1 - src/utils/cmdUtils.ts | 5 +++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/index.ts b/src/index.ts index 4470dad..991f750 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,7 +3,6 @@ import { config } from 'dotenv'; import { Client, ClientOptions, - REST, GatewayIntentBits } from 'discord.js'; import { Mock } from './commands/mock'; diff --git a/src/utils/cmdUtils.ts b/src/utils/cmdUtils.ts index b08224e..1c18b82 100644 --- a/src/utils/cmdUtils.ts +++ b/src/utils/cmdUtils.ts @@ -23,6 +23,9 @@ const CMD_LIST = [ export async function initCmds(token: string, clientID: string, guildID: string): Promise { const rest = new REST({ version: '10' }).setToken(token); + Poll.addChoiceOptions(); + DM.addChoiceOptions(); + const cmdInfo = CMD_LIST.map(cmd => cmd.info.toJSON()); await rest.put( Routes.applicationGuildCommands(clientID, guildID), @@ -30,8 +33,6 @@ export async function initCmds(token: string, clientID: string, guildID: string) body: cmdInfo } ); - Poll.addChoiceOptions(); - DM.addChoiceOptions(); }; export async function checkAndRunCommand(interaction: ChatInputCommandInteraction): Promise { From d7d215223b81fe630fffd16d923d6604e2da41bb Mon Sep 17 00:00:00 2001 From: Dylan Stauch <50031006+sjkd23@users.noreply.github.com> Date: Mon, 18 Sep 2023 09:29:35 -0400 Subject: [PATCH 16/17] re-arrange files --- src/commands/balance.ts | 4 ++-- src/commands/dm.ts | 2 +- src/commands/roll.ts | 6 +++--- src/{utils => databse}/DBUtils.ts | 2 +- src/{DB => databse}/JeffreyDB.ts | 0 src/{DB => databse}/JeffreyGacha.ts | 0 src/index.ts | 4 ++-- src/{utils => utilities}/cmdUtils.ts | 0 src/{utils => utilities}/miscUtils.ts | 0 9 files changed, 9 insertions(+), 9 deletions(-) rename src/{utils => databse}/DBUtils.ts (98%) rename src/{DB => databse}/JeffreyDB.ts (100%) rename src/{DB => databse}/JeffreyGacha.ts (100%) rename src/{utils => utilities}/cmdUtils.ts (100%) rename src/{utils => utilities}/miscUtils.ts (100%) diff --git a/src/commands/balance.ts b/src/commands/balance.ts index fd2dfe6..38ded1b 100644 --- a/src/commands/balance.ts +++ b/src/commands/balance.ts @@ -1,6 +1,6 @@ import { SlashCommandBuilder, EmbedBuilder, ChatInputCommandInteraction, User } from 'discord.js'; -import { checkUser, checkBalance, addOrSubtractBalance, addUser, findOrAddUserWallet } from '../utils/DBUtils'; -import { replyWithEmbed } from '../utils/miscUtils'; +import { checkUser, checkBalance, addOrSubtractBalance, addUser, findOrAddUserWallet } from '../databse/DBUtils'; +import { replyWithEmbed } from '../utilities/miscUtils'; export const Balance = { info: new SlashCommandBuilder() diff --git a/src/commands/dm.ts b/src/commands/dm.ts index 7d13765..fd68321 100644 --- a/src/commands/dm.ts +++ b/src/commands/dm.ts @@ -8,7 +8,7 @@ import { MessageCollector, User } from 'discord.js'; -import { tryDelete, tryToDMEmbed } from '../utils/miscUtils'; +import { tryDelete, tryToDMEmbed } from '../utilities/miscUtils'; import { BUTTONS } from '../constants/buttonConstants'; import { TIME_IN_MS } from '../constants/miscConstants'; diff --git a/src/commands/roll.ts b/src/commands/roll.ts index fdd1809..e3e46a1 100644 --- a/src/commands/roll.ts +++ b/src/commands/roll.ts @@ -1,6 +1,6 @@ -import { JeffreyGachaURLs, displayLegendary } from "../DB/JeffreyGacha"; +import { JeffreyGachaURLs, displayLegendary } from "../databse/JeffreyGacha"; import { SlashCommandBuilder, CommandInteraction, EmbedBuilder, ChatInputCommandInteraction } from "discord.js"; -import { replyWithEmbed, rng } from '../utils/miscUtils'; +import { replyWithEmbed, rng } from '../utilities/miscUtils'; import { addNewGacha, findOrAddUserWallet, @@ -9,7 +9,7 @@ import { gachaLvlUp, checkGachaLevel, checkIfUserHasGachaInv -} from "../utils/DBUtils"; +} from "../databse/DBUtils"; export const Roll = { info: new SlashCommandBuilder() diff --git a/src/utils/DBUtils.ts b/src/databse/DBUtils.ts similarity index 98% rename from src/utils/DBUtils.ts rename to src/databse/DBUtils.ts index 3549a88..524f9d8 100644 --- a/src/utils/DBUtils.ts +++ b/src/databse/DBUtils.ts @@ -1,4 +1,4 @@ -import { User, Wallet, GachaInv } from "../DB/JeffreyDB" +import { User, Wallet, GachaInv } from "./JeffreyDB" /** * Checks if the given userID is in the "User" table. diff --git a/src/DB/JeffreyDB.ts b/src/databse/JeffreyDB.ts similarity index 100% rename from src/DB/JeffreyDB.ts rename to src/databse/JeffreyDB.ts diff --git a/src/DB/JeffreyGacha.ts b/src/databse/JeffreyGacha.ts similarity index 100% rename from src/DB/JeffreyGacha.ts rename to src/databse/JeffreyGacha.ts diff --git a/src/index.ts b/src/index.ts index 991f750..6d51c72 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,8 +6,8 @@ import { GatewayIntentBits } from 'discord.js'; import { Mock } from './commands/mock'; -import { DB } from './DB/JeffreyDB'; -import { checkAndRunCommand, initCmds } from './utils/cmdUtils'; +import { DB } from './databse/JeffreyDB'; +import { checkAndRunCommand, initCmds } from './utilities/cmdUtils'; config(); diff --git a/src/utils/cmdUtils.ts b/src/utilities/cmdUtils.ts similarity index 100% rename from src/utils/cmdUtils.ts rename to src/utilities/cmdUtils.ts diff --git a/src/utils/miscUtils.ts b/src/utilities/miscUtils.ts similarity index 100% rename from src/utils/miscUtils.ts rename to src/utilities/miscUtils.ts From a81d3976e5d03692cbec0c62b1d33aae3a930785 Mon Sep 17 00:00:00 2001 From: Dylan Stauch <50031006+sjkd23@users.noreply.github.com> Date: Mon, 18 Sep 2023 09:47:24 -0400 Subject: [PATCH 17/17] docs --- src/utilities/cmdUtils.ts | 80 +++++++++++++++++++++++---------------- 1 file changed, 48 insertions(+), 32 deletions(-) diff --git a/src/utilities/cmdUtils.ts b/src/utilities/cmdUtils.ts index 1c18b82..49ef708 100644 --- a/src/utilities/cmdUtils.ts +++ b/src/utilities/cmdUtils.ts @@ -7,9 +7,11 @@ import { Ping } from "../commands/ping"; import { Poll } from "../commands/poll"; import { Roll } from "../commands/roll"; +//individual command cooldown/rate limit const cooldown = new Map>(); -const cooldownTime = 5000; +const cooldownTime = 3000; +//List of all commands const CMD_LIST = [ Ping, Mock, @@ -19,7 +21,14 @@ const CMD_LIST = [ Roll, DM ]; - +/** + * Function to initialize all of the bots commands (making them usable/visable to discord users) + * Also uses to add choice options for commands that need it. + * + * @param {string} token - bot token + * @param {string} clientID - bots discord account ID + * @param {string} guildID - discord server ID + */ export async function initCmds(token: string, clientID: string, guildID: string): Promise { const rest = new REST({ version: '10' }).setToken(token); @@ -34,45 +43,52 @@ export async function initCmds(token: string, clientID: string, guildID: string) } ); }; - +/** + * Function to be called on 'interactionCreate' event. + * Checks which command was used, and runs said command. + * + * @param {ChatInputCommandInteraction} interaction - A slash command interaction + */ export async function checkAndRunCommand(interaction: ChatInputCommandInteraction): Promise { const { commandName } = interaction; const userID = interaction.user.id; + const cooldown = await checkCmdCD(commandName, userID); + if (!cooldown) { + console.log( + `User ${interaction.user.username} (${userID}) ran the '${commandName}' command | Guild: ${interaction.guild} |` + + ` Channel: ${interaction.channel} | Timestamp: ${interaction.createdAt.toUTCString()}` + ); + + for (let i = 0; i < CMD_LIST.length; i++) { + if (commandName === CMD_LIST[i].info.name) { + CMD_LIST[i].run(interaction); + } else { + continue; + } + } + } else { + await interaction.reply(`Please wait ${cooldown.toFixed(1)} seconds.`); + } +} +/** + * Function to check if the user is on cooldown for a specific command, when they try and use it. + * + * @param {string} commandName - Name of the command being used + * @param {string} userID - Discord ID of the command user + * @returns {Promise} - The time remaining on the users command cooldown. Returns 0 if it was not on cooldown. + */ +export async function checkCmdCD(commandName: string, userID: string): Promise { if (!cooldown.has(commandName)) { cooldown.set(commandName, new Map()); } const cooldownMap = cooldown.get(commandName)!; - if (cooldownMap.has(userID) && cooldownMap.get(userID)! > Date.now() && interaction.user.id !== '218823980524634112') { + if (cooldownMap.has(userID) && cooldownMap.get(userID)! > Date.now()) { const cooldownRemaining = (cooldownMap.get(userID)! - Date.now()) / 1000; - await interaction.reply(`Please wait ${cooldownRemaining.toFixed(1)} seconds.`); - return; - } - cooldownMap.set(userID, Date.now() + cooldownTime); - - console.log(`User ${interaction.user.username} (${userID}) ran the '${commandName}' command | Guild: ${interaction.guild} |` - + ` Channel: ${interaction.channel} | Timestamp: ${interaction.createdAt.toUTCString()}`); - - if (commandName === 'ping') { - await Ping.run(interaction); - } - if (commandName === 'mock') { - await Mock.run(interaction); - } - if (commandName === 'cat') { - await Cat.run(interaction); - } - if (commandName === 'poll') { - await Poll.run(interaction); - } - if (commandName === 'balance') { - await Balance.run(interaction); - } - if (commandName === 'roll') { - await Roll.run(interaction); - } - if (commandName === 'dm') { - await DM.run(interaction); + return cooldownRemaining; + } else { + cooldownMap.set(userID, Date.now() + cooldownTime); + return 0; } } \ No newline at end of file