2023-01-18 00:35:24 +00:00
/* eslint-disable comma-dangle */
// dotenv for handling environment variables
const dotenv = require ( 'dotenv' ) ;
dotenv . config ( ) ;
const isDev = process . env . isDev ;
2023-01-25 08:07:41 +00:00
const package = require ( '../package.json' ) ;
2023-01-18 00:35:24 +00:00
// filesystem
const fs = require ( 'fs' ) ;
// Discord.js
const Discord = require ( 'discord.js' ) ;
2023-01-19 01:10:05 +00:00
const { EmbedBuilder , ActionRowBuilder , ButtonBuilder , ButtonStyle } = Discord ;
2023-01-18 00:35:24 +00:00
// Various imports from other files
2023-01-19 17:44:49 +00:00
const config = require ( '../data/config.json' ) ;
const strings = require ( '../data/strings.json' ) ;
2023-01-18 00:35:24 +00:00
const slashCommandFiles = fs . readdirSync ( './slash-commands/' ) . filter ( file => file . endsWith ( '.js' ) ) ;
2023-01-25 08:07:41 +00:00
const dbfn = require ( './dbfn.js' ) ;
2023-01-26 04:11:49 +00:00
const { finished } = require ( 'stream' ) ;
2023-01-25 08:07:41 +00:00
dbfn . createGuildTables ( ) . then ( res => {
console . log ( res . status ) ;
} ) . catch ( err => {
console . error ( err ) ;
} ) ;
2023-01-18 00:35:24 +00:00
const functions = {
// Functions for managing and creating Collections
collections : {
// Create the collection of slash commands
slashCommands ( client ) {
if ( ! client . slashCommands ) client . slashCommands = new Discord . Collection ( ) ;
client . slashCommands . clear ( ) ;
for ( const file of slashCommandFiles ) {
2023-01-19 17:44:49 +00:00
const slashCommand = require ( ` ../slash-commands/ ${ file } ` ) ;
2023-01-18 00:35:24 +00:00
if ( slashCommand . data != undefined ) {
client . slashCommands . set ( slashCommand . data . name , slashCommand ) ;
}
}
if ( isDev ) console . log ( 'Slash Commands Collection Built' ) ;
2023-01-19 01:10:05 +00:00
}
} ,
builders : {
2023-01-31 00:28:12 +00:00
actionRows : {
reminderActionRow ( ) {
const deleteButton = new ButtonBuilder ( )
. setCustomId ( 'deleteping' )
. setEmoji ( '♻️' )
. setStyle ( ButtonStyle . Danger ) ;
const actionRow = new ActionRowBuilder ( )
. addComponents ( deleteButton ) ;
return actionRow ;
} ,
comparisonActionRow ( guildInfo ) {
// Create the button to go in the Action Row
const refreshButton = new ButtonBuilder ( )
. setCustomId ( 'refresh' )
. setEmoji ( '🔄' )
. setStyle ( ButtonStyle . Primary ) ;
// Create the Action Row with the Button in it, to be sent with the Embed
let refreshActionRow = new ActionRowBuilder ( )
. addComponents (
refreshButton
) ;
if ( guildInfo . reminderOptIn == 1 && guildInfo . remindedStatus == 1 ) {
const resetPingButton = new ButtonBuilder ( )
. setCustomId ( 'resetping' )
. setLabel ( 'Reset Ping' )
. setStyle ( ButtonStyle . Secondary ) ;
refreshActionRow . addComponents ( resetPingButton ) ;
} else if ( guildInfo . reminderOptIn == 1 && guildInfo . remindedStatus == 0 ) {
const resetPingButton = new ButtonBuilder ( )
. setCustomId ( 'resetping' )
. setLabel ( '[Armed]' )
. setStyle ( ButtonStyle . Secondary ) ;
refreshActionRow . addComponents ( resetPingButton ) ;
}
return refreshActionRow ;
}
} ,
2023-01-27 23:14:01 +00:00
async refreshAction ( guildId ) {
2023-01-19 01:10:05 +00:00
// Create the button to go in the Action Row
const refreshButton = new ButtonBuilder ( )
. setCustomId ( 'refresh' )
. setLabel ( 'Refresh' )
. setStyle ( ButtonStyle . Primary ) ;
2023-01-27 23:14:01 +00:00
const resetPingButton = new ButtonBuilder ( )
. setCustomId ( 'resetping' )
. setLabel ( 'Reset Ping' )
. setStyle ( ButtonStyle . Secondary ) ;
2023-01-19 01:10:05 +00:00
// Create the Action Row with the Button in it, to be sent with the Embed
2023-01-27 23:14:01 +00:00
let refreshActionRow = new ActionRowBuilder ( )
2023-01-19 01:10:05 +00:00
. addComponents (
refreshButton
) ;
2023-01-27 23:14:01 +00:00
const getGuildInfoResponse = await dbfn . getGuildInfo ( guildId ) ;
const guildInfo = getGuildInfoResponse . data ;
if ( guildInfo . reminderMessage != "" && guildInfo . reminderChannelId != "" ) {
refreshActionRow . addComponents ( resetPingButton ) ;
}
2023-01-19 01:10:05 +00:00
return refreshActionRow ;
2023-01-18 00:35:24 +00:00
} ,
2023-01-31 00:28:12 +00:00
comparisonEmbed ( content , guildInfo ) {
2023-01-21 14:58:32 +00:00
// Create the embed using the content passed to this function
const embed = new EmbedBuilder ( )
. setColor ( strings . embeds . color )
2023-01-31 00:28:12 +00:00
. setTitle ( 'Tallest Trees Comparison' )
2023-01-21 14:58:32 +00:00
. setDescription ( content )
2023-01-30 06:12:26 +00:00
. setFooter ( { text : ` v ${ package . version } - ${ strings . embeds . footer } ` } ) ;
2023-01-31 00:28:12 +00:00
const messageContents = { embeds : [ embed ] , components : [ this . actionRows . comparisonActionRow ( guildInfo ) ] } ;
2023-01-21 14:58:32 +00:00
return messageContents ;
} ,
2023-01-27 23:14:01 +00:00
reminderEmbed ( content , guildInfo ) {
// Create the embed using the content passed to this function
const embed = new EmbedBuilder ( )
. setColor ( strings . embeds . color )
. setTitle ( 'Water Reminder' )
2023-01-30 06:12:26 +00:00
. setDescription ( ` [Click here to go to your Tree](https://discord.com/channels/ ${ guildInfo . guildId } / ${ guildInfo . treeChannelId } / ${ guildInfo . treeMessageId } ) ` )
2023-01-31 00:28:12 +00:00
. setFooter ( { text : ` Click ♻️ to delete this message ` } ) ;
const messageContents = { content : content , embeds : [ embed ] , components : [ this . actionRows . reminderActionRow ( ) ] } ;
2023-01-27 23:14:01 +00:00
return messageContents ;
} ,
2023-01-19 01:10:05 +00:00
helpEmbed ( content , private ) {
const embed = new EmbedBuilder ( )
. setColor ( strings . embeds . color )
2023-01-24 02:36:44 +00:00
. setTitle ( strings . help . title )
2023-01-19 01:10:05 +00:00
. setDescription ( content )
2023-01-25 08:07:41 +00:00
. setFooter ( { text : ` v ${ package . version } - ${ strings . embeds . footer } ` } ) ;
2023-01-19 01:10:05 +00:00
const privateBool = private == 'true' ;
const messageContents = { embeds : [ embed ] , ephemeral : privateBool } ;
return messageContents ;
} ,
errorEmbed ( content ) {
const embed = new EmbedBuilder ( )
. setColor ( 0xFF0000 )
. setTitle ( 'Error!' )
2023-01-24 00:37:51 +00:00
. setDescription ( "Error: " + content )
2023-01-25 08:07:41 +00:00
. setFooter ( { text : ` v ${ package . version } - ${ strings . embeds . footer } ` } ) ;
2023-01-19 01:10:05 +00:00
const messageContents = { embeds : [ embed ] , ephemeral : true } ;
return messageContents ;
2023-01-18 00:35:24 +00:00
} ,
2023-01-19 01:10:05 +00:00
embed ( content ) {
const embed = new EmbedBuilder ( )
. setColor ( 0x8888FF )
. setTitle ( 'Information' )
. setDescription ( content )
2023-01-25 08:07:41 +00:00
. setFooter ( { text : ` v ${ package . version } - ${ strings . embeds . footer } ` } ) ;
2023-01-19 01:10:05 +00:00
const messageContents = { embeds : [ embed ] , ephemeral : true } ;
return messageContents ;
}
2023-01-18 00:35:24 +00:00
} ,
2023-01-21 14:58:32 +00:00
rankings : {
2023-01-30 06:12:26 +00:00
parse ( interaction , guildInfo ) {
return new Promise ( ( resolve , reject ) => {
if ( guildInfo . guildId == "" ) {
reject ( strings . error . noGuild ) ;
return ;
}
if ( guildInfo . leaderboardMessageId != undefined ) {
interaction . guild . channels . fetch ( guildInfo . leaderboardChannelId ) . then ( c => {
c . messages . fetch ( guildInfo . leaderboardMessageId ) . then ( leaderboardMessage => {
if ( ( leaderboardMessage . embeds . length == 0 ) || ( leaderboardMessage . embeds [ 0 ] . data . title != 'Tallest Trees' ) ) {
reject ( "This doesn't appear to be a valid ``/top trees`` message." ) ;
return ;
}
let lines = leaderboardMessage . embeds [ 0 ] . data . description . split ( '\n' ) ;
let leaderboard = {
"guildId" : interaction . guildId ,
"entries" : [ ]
} ;
for ( let i = 0 ; i < 10 ; i ++ ) {
// Breakdown each line separating it on each -
let breakdown = lines [ i ] . split ( ' - ' ) ;
// Check if the first part, the ranking, has these emojis to detect first second and third place
if ( breakdown [ 0 ] . includes ( '🥇' ) ) {
breakdown [ 0 ] = '``#1 ``'
} else if ( breakdown [ 0 ] . includes ( '🥈' ) ) {
breakdown [ 0 ] = '``#2 ``'
} else if ( breakdown [ 0 ] . includes ( '🥉' ) ) {
breakdown [ 0 ] = '``#3 ``'
2023-01-25 08:07:41 +00:00
}
2023-01-30 06:12:26 +00:00
// Clean off the excess and get just the number from the rank, make sure it's an int not string
let trimmedRank = parseInt ( breakdown [ 0 ] . slice ( breakdown [ 0 ] . indexOf ( '#' ) + 1 , breakdown [ 0 ] . lastIndexOf ( '``' ) ) ) ;
2023-01-25 08:07:41 +00:00
2023-01-30 06:12:26 +00:00
// Clean off the excess and get just the tree name
let trimmedName = breakdown [ 1 ] . slice ( breakdown [ 1 ] . indexOf ( '``' ) + 2 ) ;
trimmedName = trimmedName . slice ( 0 , trimmedName . indexOf ( '``' ) ) ;
// Clean off the excess and get just the tree height, make sure it's a 1 decimal float
let trimmedHeight = parseFloat ( breakdown [ 2 ] . slice ( 0 , breakdown [ 2 ] . indexOf ( 'ft' ) ) ) . toFixed ( 1 ) ;
let isMyTree = false ;
let isMaybeMyTree = false ;
if ( breakdown [ 2 ] . includes ( '📍' ) ) isMyTree = true ;
if ( breakdown [ 1 ] . includes ( guildInfo . treeName ) ) maybeMyTree = true ;
// "entries": [ { "treeHeight": 12, "treeRank": 34, "treeName": "name" }, ] }
leaderboard . entries . push ( {
treeRank : trimmedRank ,
treeName : trimmedName ,
treeHeight : trimmedHeight ,
hasPin : isMyTree
2023-01-21 14:58:32 +00:00
} ) ;
2023-01-30 06:12:26 +00:00
}
dbfn . uploadLeaderboard ( leaderboard ) . then ( res => {
resolve ( res . status ) ;
} ) . catch ( err => {
console . error ( err ) ;
reject ( err ) ;
return ;
2023-01-25 08:07:41 +00:00
} ) ;
2023-01-30 06:12:26 +00:00
} ) . catch ( err => {
reject ( strings . status . missingLeaderboardMessage ) ;
console . error ( err ) ;
return ;
2023-01-21 14:58:32 +00:00
} ) ;
2023-01-30 06:12:26 +00:00
} ) . catch ( err => {
reject ( strings . status . missingLeaderboardChannel ) ;
console . error ( err ) ;
2023-01-25 08:07:41 +00:00
return ;
2023-01-30 06:12:26 +00:00
} ) ;
} else {
reject ( "The leaderboardMessageId is undefined somehow" ) ;
2023-01-21 14:58:32 +00:00
return ;
2023-01-30 06:12:26 +00:00
}
2023-01-21 14:58:32 +00:00
} ) ;
2023-01-30 06:12:26 +00:00
2023-01-21 14:58:32 +00:00
} ,
2023-01-30 06:12:26 +00:00
async compare ( interaction , guildInfo ) {
2023-01-26 04:11:49 +00:00
try {
const getLeaderboardResponse = await dbfn . getLeaderboard ( interaction . guildId ) ;
const leaderboard = getLeaderboardResponse . data ; // [ { treeName: "Name", treeHeight: 1234.5, treeRank: 67 }, {...}, {...} ]
2023-01-30 06:12:26 +00:00
2023-01-26 04:11:49 +00:00
// Prepare the beginning of the comparison message
let comparisonReplyString = ` Here \' s how your tree compares: \n Current Tree Height: ${ guildInfo . treeHeight } ft \n \n ` ;
// Iterate over the leaderboard entries, backwards
for ( let i = leaderboard . length - 1 ; i >= 0 ; i -- ) {
const leaderboardEntry = leaderboard [ i ] ;
// Setup the status indicator, default to blank, we'll change it later
2023-01-28 01:57:40 +00:00
let statusIndicator = "" ;
if ( ( leaderboardEntry . treeHeight % 1 ) . toFixed ( 1 ) > 0 ) statusIndicator += "``[💧]``" ;
2023-01-30 06:12:26 +00:00
2023-01-26 04:11:49 +00:00
// Get the data for this tree from 24 hours ago
2023-01-28 01:57:40 +00:00
// const get24hTreeResponse = await dbfn.get24hTree(interaction.guildId, leaderboardEntry.treeName);
// const dayAgoTree = get24hTreeResponse.data;
// const hist24hDifference = (leaderboardEntry.treeHeight - dayAgoTree.treeHeight).toFixed(1);
// statusIndicator += `+${hist24hDifference}ft|`
2023-01-26 04:11:49 +00:00
2023-01-27 02:30:41 +00:00
// Get the 24h watering time for this tree
2023-01-28 01:57:40 +00:00
// const totalWaterTime = await functions.timeToHeight(dayAgoTree.treeHeight, leaderboardEntry.treeHeight);
// statusIndicator += `${totalWaterTime}]\`\``;
2023-01-30 06:12:26 +00:00
2023-01-26 04:11:49 +00:00
// Determine if this tree is the guild's tree
if ( leaderboardEntry . hasPin ) {
2023-01-26 04:17:07 +00:00
comparisonReplyString += ` # ${ leaderboardEntry . treeRank } - This is your tree ` ;
2023-01-26 04:11:49 +00:00
} else { // If it's another guild's tree
// Calculate the current height difference
const currentHeightDifference = guildInfo . treeHeight - leaderboardEntry . treeHeight ;
2023-01-30 06:12:26 +00:00
2023-01-26 04:11:49 +00:00
if ( currentHeightDifference > 0 ) { // Guild Tree is taller than the leaderboard tree
2023-01-26 04:14:40 +00:00
comparisonReplyString += ` # ${ leaderboardEntry . treeRank } - ${ currentHeightDifference . toFixed ( 1 ) } ft taller ` ;
2023-01-26 04:11:49 +00:00
} else {
2023-01-26 04:14:40 +00:00
comparisonReplyString += ` # ${ leaderboardEntry . treeRank } - ${ Math . abs ( currentHeightDifference ) . toFixed ( 1 ) } ft shorter ` ;
2023-01-26 04:11:49 +00:00
}
}
2023-01-26 04:17:07 +00:00
// Build a string using the current leaderboard entry and the historic entry from 24 hours ago
2023-01-28 01:57:40 +00:00
comparisonReplyString += ` ${ statusIndicator } \n ` ;
2023-01-27 23:14:01 +00:00
// if (process.env.isDev == 'true') comparisonReplyString += `Current Height: ${leaderboardEntry.treeHeight} 24h Ago Height: ${dayAgoTree.treeHeight}\n`;
2023-01-26 04:11:49 +00:00
}
return comparisonReplyString ;
} catch ( err ) {
2023-01-30 06:12:26 +00:00
throw err ;
2023-01-26 04:11:49 +00:00
}
2023-01-21 14:58:32 +00:00
}
} ,
tree : {
2023-01-30 06:12:26 +00:00
parse ( interaction , guildInfo ) {
2023-01-21 14:58:32 +00:00
let input ;
return new Promise ( ( resolve , reject ) => {
2023-01-30 06:12:26 +00:00
if ( guildInfo == undefined ) {
reject ( ` The guild entry hasn't been created yet. [ ${ interaction . guildId || interaction . commandGuildId } ] ` ) ;
return ;
}
if ( guildInfo . treeMessageId != "Run /setup where your tree is." ) {
interaction . guild . channels . fetch ( guildInfo . treeChannelId ) . then ( c => {
c . messages . fetch ( guildInfo . treeMessageId ) . then ( m => {
if ( ( m . embeds . length == 0 ) || ! ( m . embeds [ 0 ] . data . description . includes ( 'Your tree is' ) ) ) {
reject ( "This doesn't appear to be a valid ``/tree`` message." ) ;
return ;
}
input = m . embeds [ 0 ] . data . description ;
let treeName = m . embeds [ 0 ] . data . title ;
let lines = input . split ( '\n' ) ;
guildInfo . treeHeight = parseFloat ( lines [ 0 ] . slice ( lines [ 0 ] . indexOf ( 'is' ) + 3 , lines [ 0 ] . indexOf ( 'ft' ) ) ) . toFixed ( 1 ) ;
guildInfo . treeName = treeName ;
dbfn . setTreeInfo ( guildInfo ) . then ( res => {
resolve ( "The reference tree message has been saved/updated." ) ;
} ) ;
} ) . catch ( err => {
reject ( strings . status . missingTreeMessage ) ;
console . error ( err ) ;
return ;
} ) ;
} ) . catch ( err => {
reject ( strings . status . missingTreeChannel ) ;
console . error ( err ) ;
2023-01-25 08:07:41 +00:00
return ;
2023-01-30 06:12:26 +00:00
} ) ;
} else {
console . error ( 'treeMessageId undefined' ) ;
reject ( "There was an unknown error while setting the tree message." ) ;
return ;
}
} ) ;
}
} ,
messages : {
async find ( interaction , guildInfo ) {
try {
let response = { status : "Incomplete" , data : undefined , code : 0 } ;
// If the tree channel ID and leaderboard channel ID are both set
if ( guildInfo . treeChannelId != "" || guildInfo . leaderboardChannelId != "" ) {
// If one us unset, we'll set it to the current channel just to check
if ( guildInfo . treeChannelId == "" ) {
guildInfo . treeChannelId = ` ${ guildInfo . leaderboardChannelId } ` ;
} else if ( guildInfo . leaderboardChannelId == "" ) {
guildInfo . leaderboardChannelId = ` ${ guildInfo . treeChannelId } ` ;
2023-01-25 08:07:41 +00:00
}
2023-01-30 06:12:26 +00:00
let treeFound , leaderboardFound = false ;
// If these values have already been set in the database, we don't want to report that they weren't found
// they'll still get updated later if applicable.
treeFound = ( guildInfo . treeMessageId != "" ) ;
leaderboardFound = ( guildInfo . leaderboardMessageId != "" ) ;
// If the Tree and Leaderboard messages are in the same channel
if ( guildInfo . treeChannelId == guildInfo . leaderboardChannelId ) {
// Fetch the tree channel so we can get the most recent messages
const treeChannel = await interaction . guild . channels . fetch ( guildInfo . treeChannelId ) ;
// Fetch the last 20 messages in the channel
const treeChannelMessageCollection = await treeChannel . messages . fetch ( { limit : 20 } ) ;
// Create a basic array of [Message, Message, ...] from the Collection
const treeChannelMessages = Array . from ( treeChannelMessageCollection . values ( ) ) ;
// Iterate over the Messages in reverse order (newest messages first)
for ( let i = treeChannelMessages . length - 1 ; i >= 0 ; i -- ) {
let treeChannelMessage = treeChannelMessages [ i ] ;
if ( this . isTree ( treeChannelMessage ) ) { // This is a tree message
// Set the tree message ID
guildInfo . treeMessageId = treeChannelMessage . id ;
// Parse out the tree name
input = treeChannelMessage . embeds [ 0 ] . data . description ;
let treeName = treeChannelMessage . embeds [ 0 ] . data . title ;
// And tree height
2023-01-25 08:07:41 +00:00
let lines = input . split ( '\n' ) ;
guildInfo . treeHeight = parseFloat ( lines [ 0 ] . slice ( lines [ 0 ] . indexOf ( 'is' ) + 3 , lines [ 0 ] . indexOf ( 'ft' ) ) ) . toFixed ( 1 ) ;
guildInfo . treeName = treeName ;
2023-01-30 06:12:26 +00:00
// Upload the found messages to the database
await dbfn . setTreeInfo ( guildInfo ) ;
// Let the end of the function know we found a tree message and successfully uploaded it
treeFound = true ;
} else if ( this . isLeaderboard ( treeChannelMessage ) ) { // This is a leaderboard message
// Set the leaderboard message ID
guildInfo . leaderboardMessageId = treeChannelMessage . id ;
// Upload it to the database
await dbfn . setLeaderboardInfo ( guildInfo ) ;
// Let the end of the function know we found a leaderboard message and successfully uploaded it
leaderboardFound = true ;
}
}
} else { // If the tree and leaderboard are in different channels
// Fetch the tree channel so we can get the most recent messages
const treeChannel = await interaction . guild . channels . fetch ( guildInfo . treeChannelId ) ;
// Fetch the last 20 messages in the tree channel
const treeChannelMessageCollection = await treeChannel . messages . fetch ( { limit : 20 } ) ;
// Create an Array like [Message, Message, ...] from the Collection
const treeChannelMessages = Array . from ( treeChannelMessageCollection . values ( ) ) ;
// Iterate over the Array of Messages in reverse order (newest messages first)
for ( let i = treeChannelMessages . length - 1 ; i >= 0 ; i -- ) {
let treeChannelMessage = treeChannelMessages [ i ] ;
if ( this . isTree ( treeChannelMessage ) ) { // This is a tree message
// Set the tree message ID
guildInfo . treeMessageId = treeChannelMessage . id ;
// Parse out the tree name
input = treeChannelMessage . embeds [ 0 ] . data . description ;
let treeName = treeChannelMessage . embeds [ 0 ] . data . title ;
// And tree height
let lines = input . split ( '\n' ) ;
guildInfo . treeHeight = parseFloat ( lines [ 0 ] . slice ( lines [ 0 ] . indexOf ( 'is' ) + 3 , lines [ 0 ] . indexOf ( 'ft' ) ) ) . toFixed ( 1 ) ;
guildInfo . treeName = treeName ;
// Upload the found messages to the database
await dbfn . setTreeInfo ( guildInfo ) ;
// Let the end of the function know we found a tree message and successfully uploaded it
treeFound = true ;
}
}
// Fetch the tree channel so we can get the most recent messages
const leaderboardChannel = await interaction . guild . channels . fetch ( guildInfo . leaderboardChannelId ) ;
// Fetch the last 20 messages in the leaderboard channel
const leaderboardChannelMessageCollection = await leaderboardChannel . messages . fetch ( { limit : 20 } ) ;
// Create an Array like [Message, Message, ...] from the Collection
const leaderboardChannelMessages = Array . from ( leaderboardChannelMessageCollection . values ( ) ) ;
// Iterate over the Array of Messages in reverse order (newest messages first)
for ( let i = leaderboardChannelMessages . length - 1 ; i >= 0 ; i -- ) {
let leaderboardChannelMessage = leaderboardChannelMessages [ i ] ;
if ( this . isLeaderboard ( leaderboardChannelMessage ) ) { // This is a leaderboard message
// Set the leaderboard message ID
guildInfo . leaderboardMessageId = leaderboardChannelMessage . id ;
// Upload it to the database
await dbfn . setLeaderboardInfo ( guildInfo ) ;
// Let the end of the function know we've found a leaderboard
leaderboardFound = true ;
}
}
2023-01-25 08:07:41 +00:00
}
2023-01-30 06:12:26 +00:00
// await dbfn.setGuildInfo(guildInfo);
// Bundle guildInfo into the response
const getGuildInfoResponse = await dbfn . getGuildInfo ( guildInfo . guildId ) ;
response . data = getGuildInfoResponse . data ;
// Set the response status, this is only used as a response to /setup
if ( treeFound && leaderboardFound ) { // we found both the tree and leaderboard
response . status = strings . status . treeAndLeaderboard ;
response . code = 1 ;
} else if ( treeFound || leaderboardFound ) { // Only found the tree
response . status = strings . status . missingMessage ;
response . code = 2 ;
} else { // Didn't find any
response . status = strings . status . noneFound ;
response . code = 3 ;
}
return response ;
} else { // This should only ever occur if some weird database stuff happens
response . status = "It looks like this channel doesn't contain both your ``/tree`` and ``/top trees`` messages, please run ``/setup``" ;
return response ;
}
} catch ( err ) {
throw "Problem checking messages: " + err ;
}
} ,
isTree ( message ) {
if ( message . embeds . length > 0 ) {
return message . embeds [ 0 ] . data . description . includes ( "Your tree is" ) ;
}
} ,
isLeaderboard ( message ) {
if ( message . embeds . length > 0 ) {
return message . embeds [ 0 ] . data . title == "Tallest Trees" ;
}
2023-01-21 14:58:32 +00:00
}
} ,
2023-01-30 06:12:26 +00:00
async refresh ( interaction ) {
const getGuildInfoResponse = await dbfn . getGuildInfo ( interaction . guildId ) ;
let guildInfo = getGuildInfoResponse . data ;
const findMessagesResponse = await this . messages . find ( interaction , guildInfo ) ;
if ( findMessagesResponse . code == 1 ) {
guildInfo = findMessagesResponse . data ;
// Parse the tree
await this . tree . parse ( interaction , guildInfo ) ;
// Parse the leaderboard
await this . rankings . parse ( interaction , guildInfo ) ;
// Build the string that shows the comparison // TODO Move the string building section to fn.builders?
const comparedRankings = await this . rankings . compare ( interaction , guildInfo ) ;
2023-01-31 00:28:12 +00:00
const embed = this . builders . comparisonEmbed ( comparedRankings , guildInfo ) ;
2023-01-30 06:12:26 +00:00
await interaction . update ( embed ) ;
} else {
await interaction . update ( this . builders . errorEmbed ( findMessagesResponse . status ) ) ;
}
2023-01-21 14:58:32 +00:00
} ,
reset ( guildId ) {
2023-01-25 08:07:41 +00:00
return new Promise ( ( resolve , reject ) => {
dbfn . deleteGuildInfo ( guildId ) . then ( res => {
resolve ( res ) ;
} ) . catch ( err => {
console . error ( err ) ;
reject ( err ) ;
return ;
} ) ;
} ) ;
2023-01-21 14:58:32 +00:00
} ,
getInfo ( guildId ) {
2023-01-25 08:07:41 +00:00
return new Promise ( ( resolve , reject ) => {
dbfn . getGuildInfo ( guildId ) . then ( res => {
let guildInfo = res . data ;
let guildInfoString = "" ;
guildInfoString += ` Tree Message: https://discord.com/channels/ ${ guildId } / ${ guildInfo . treeChannelId } / ${ guildInfo . treeMessageId } \n ` ;
guildInfoString += ` Rank Message: https://discord.com/channels/ ${ guildId } / ${ guildInfo . leaderboardChannelId } / ${ guildInfo . leaderboardMessageId } \n ` ;
resolve ( ` Here is your servers setup info: \n ${ guildInfoString } ` ) ;
} ) . catch ( err => {
console . error ( err ) ;
reject ( err ) ;
return ;
} )
} ) ;
2023-01-23 23:14:57 +00:00
} ,
getWaterTime ( size ) {
2023-01-27 02:30:41 +00:00
return Math . floor ( Math . pow ( size * 0.07 + 5 , 1.1 ) ) ; // Seconds
2023-01-25 09:53:49 +00:00
} ,
2023-01-27 02:30:41 +00:00
timeToHeight ( beginHeight , destHeight ) {
2023-01-25 09:53:49 +00:00
return new Promise ( ( resolve , reject ) => {
2023-01-27 02:30:41 +00:00
let time = 0 ;
for ( let i = beginHeight ; i < destHeight ; i ++ ) {
const waterTime = parseFloat ( functions . getWaterTime ( i ) ) ;
// console.log("Height: " + i + "Time: " + waterTime);
time += waterTime ;
}
// 60 secs in min
// 3600 secs in hr
// 86400 sec in day
let units = " secs" ;
2023-01-30 06:12:26 +00:00
if ( 60 < time && time <= 3600 ) { // Minutes
2023-01-27 02:30:41 +00:00
time = parseFloat ( time / 60 ) . toFixed ( 1 ) ;
units = " mins" ;
2023-01-30 06:12:26 +00:00
} else if ( 3600 < time && time <= 86400 ) {
2023-01-27 02:30:41 +00:00
time = parseFloat ( time / 3600 ) . toFixed ( 1 ) ;
units = " hrs" ;
2023-01-30 06:12:26 +00:00
} else if ( 86400 < time ) {
2023-01-27 02:30:41 +00:00
time = parseFloat ( time / 86400 ) . toFixed ( 1 ) ;
units = " days" ;
}
resolve ( time + units ) ;
2023-01-25 09:53:49 +00:00
} ) ;
2023-01-27 03:31:48 +00:00
} ,
2023-01-27 23:14:01 +00:00
sleep ( ms ) {
return new Promise ( resolve => setTimeout ( resolve , ms ) ) ;
} ,
async sendReminder ( guildInfo , guild ) {
const { guildId , reminderChannelId , reminderMessage } = guildInfo ;
const reminderChannel = await guild . channels . fetch ( reminderChannelId ) ;
const reminderEmbed = functions . builders . reminderEmbed ( reminderMessage , guildInfo ) ;
reminderChannel . send ( reminderEmbed ) . then ( async m => {
await dbfn . setRemindedStatus ( guildId , 1 ) ;
2023-01-31 00:28:12 +00:00
return 1 ;
2023-01-27 23:14:01 +00:00
} ) . catch ( err => {
console . error ( err ) ;
} ) ;
} ,
async setReminder ( interaction , ms ) {
setTimeout ( this . sendReminder ( interaction ) , ms ) ;
} ,
async checkReady ( client ) { // Check if the guilds trees are ready to water
// TODO This is hard coded for the dev server, need to change it to lookup each server and iterate over them
// Would also be helpful to have an opt-in or opt-out ability for water checks
try {
const getOptedInGuildsResponse = await dbfn . getOptedInGuilds ( ) ;
2023-01-31 02:38:12 +00:00
// console.log(JSON.stringify(getOptedInGuildsResponse));
2023-01-27 23:14:01 +00:00
if ( getOptedInGuildsResponse . status != "No servers have opted in yet" ) {
const guilds = getOptedInGuildsResponse . data ;
2023-01-31 03:03:47 +00:00
for ( let i = 0 ; i < guilds . length ; i ++ ) {
const oldGuildInfo = guilds [ i ] ;
2023-01-31 02:38:12 +00:00
const getGuildInfoResponse = await dbfn . getGuildInfo ( oldGuildInfo . guildId ) ;
const guildInfo = getGuildInfoResponse . data ;
2023-01-27 23:14:01 +00:00
const { guildId , treeChannelId , treeMessageId , remindedStatus } = guildInfo ;
2023-01-30 06:12:26 +00:00
2023-01-27 23:14:01 +00:00
if ( remindedStatus == 0 ) {
const guild = await client . guilds . fetch ( guildId ) ;
const treeChannel = await guild . channels . fetch ( treeChannelId ) ;
const treeMessage = await treeChannel . messages . fetch ( treeMessageId ) ;
2023-01-31 02:38:12 +00:00
const description = treeMessage . embeds [ 0 ] . description ;
2023-01-31 03:03:47 +00:00
let readyToWater = false ;
if ( description . includes ( "Ready to be watered" ) ) {
readyToWater = true ;
} else {
const beginWaterTimestamp = description . indexOf ( "<t:" ) + 3 ;
const endWaterTimestamp = description . indexOf ( ":>" ) ;
const waterTimestamp = parseInt ( description . slice ( beginWaterTimestamp , endWaterTimestamp ) ) ;
const nowTimestamp = ( Date . now ( ) / 1000 ) ;
readyToWater = ( nowTimestamp > waterTimestamp ) ;
}
2023-01-27 23:14:01 +00:00
if ( readyToWater ) {
2023-01-28 01:57:40 +00:00
// console.log("Ready to water");
2023-01-30 06:12:26 +00:00
await this . sendReminder ( guildInfo , guild ) ;
2023-01-31 02:38:12 +00:00
this . sleep ( 5000 ) . then ( async ( ) => {
await this . checkReady ( client ) ;
2023-01-27 23:14:01 +00:00
} ) ;
2023-01-31 02:38:12 +00:00
return ;
2023-01-27 23:14:01 +00:00
} else {
2023-01-31 02:38:12 +00:00
// console.log("Not ready to water\n" + `Water At: ${waterTimestamp} | Now: ${nowTimestamp}`);
this . sleep ( 5000 ) . then ( async ( ) => {
await this . checkReady ( client ) ;
2023-01-27 23:14:01 +00:00
} ) ;
2023-01-31 02:38:12 +00:00
return ;
2023-01-27 23:14:01 +00:00
}
}
2023-01-31 03:03:47 +00:00
}
2023-01-27 23:14:01 +00:00
} else {
// console.log(getOptedInGuildsResponse.status);
2023-01-31 02:38:12 +00:00
this . sleep ( 5000 ) . then ( async ( ) => {
await this . checkReady ( client ) ;
2023-01-27 23:14:01 +00:00
} ) ;
2023-01-31 02:38:12 +00:00
return ;
2023-01-27 23:14:01 +00:00
}
2023-01-30 06:12:26 +00:00
} catch ( err ) {
2023-01-27 23:14:01 +00:00
console . error ( err ) ;
2023-01-31 02:38:12 +00:00
this . sleep ( 5000 ) . then ( async ( ) => {
await this . checkReady ( client ) ;
2023-01-27 03:31:48 +00:00
} ) ;
2023-01-31 02:38:12 +00:00
return ;
2023-01-27 23:14:01 +00:00
}
} ,
2023-01-31 00:28:12 +00:00
async resetPing ( interaction ) {
await dbfn . setRemindedStatus ( interaction . guildId , 0 ) ;
2023-01-19 01:10:05 +00:00
}
2023-01-18 00:35:24 +00:00
} ;
module . exports = functions ;