/**
* Lists all {@link Event|Events} enabled in the context, toggles the Event provided, or lists all Events that can be enabled
*
* @type {Command}
* @memberof Command
* @name event
* @prop {Object} executeConfig `execution` function configuration object
* @prop {external:Message} executeConfig.message discord.js Message
* @prop {String[]} executeConfig.args Array of space-separated strings following the command
* @prop {Esdi} executeConfig.server Esdi server instance
* @static
*/
module.exports = {
name: 'event',
ownerOnly: true,
cooldown: 1,
usage: '[<Event name>]/[list]',
description: 'Lists all Events enabled here, toggles the Event provided, or lists all Events that can be enabled.',
aliases: ['events'],
async execute ({ message, args, server }) {
const prefix = server.controllers.get('CommandController').determinePrefix(message)
const EVENT_TOGGLE_TXT = `Use \`${prefix}event <Event name>\` to toggle an Event.`
const EVENT_TXT = `Use \`${prefix}event\` to see all Events enabled here. ${EVENT_TOGGLE_TXT}`
const EVENT_LIST_TXT = `Use \`${prefix}event list\` to see the Events you can enable here. ${EVENT_TOGGLE_TXT}`
// helper function that initializes the database document if missing
const initializeIfMissing = async (event, doc) => {
if (doc.status === 404 || !doc.contextTimestampPairs) {
return await server.controllers.get('DatabaseController').updateDoc({
db: 'event',
id: event.name,
payload: {
contextTimestampPairs: []
}
})
} else {
return doc
}
}
// if no arguments provided, list all Events enabled for this context
if (args.length === 0) {
const globalEmbedFieldValues = []
const guildEmbedFieldValues = []
const channelEmbedFieldValues = []
// get all loaded Events and their database documents
for (const e of server.events.values()) {
// only consider interval Events
if (e.type !== 'interval') continue
// fetch this Event's database document
let doc = await server.controllers.get('DatabaseController').fetchDoc({ db: 'event', id: e.name })
// if missing, initialize this Event's database document
doc = await initializeIfMissing(e, doc)
const contexts = doc.contextTimestampPairs.map(p => p[0])
// get all contexts with this Event enabled
for (const c of contexts) {
// keep only enabled contexts
const contextDoc = await server.controllers.get('DatabaseController').fetchDoc({
db: 'event',
id: `${e.name}_${c}`
})
if (!contextDoc.enabled) continue
const args = (contextDoc.args && contextDoc.args.length) ? `- *argument${contextDoc.args.length > 1 ? 's' : ''}:* \`${contextDoc.args.join(' ')}\`\n` : ''
// if found, remember that this Event is enabled globally
if (e.context === 'global' && server.controllers.get('DiscordController').botOwner === message.author.id) {
const timestamp = doc.contextTimestampPairs.filter(p => p[0] === c)[0][1]
server.controllers.get('DiscordController').buildEmbedFieldValues(globalEmbedFieldValues, `\n\`${e.name}\` - ${e.description}\n${args}- *handled:* \`${new Date(timestamp).toLocaleString() + ' PT'}\``)
continue
}
// if found, remember that this Event is enabled for this Guild
if (message.guild.id === c) {
const timestamp = doc.contextTimestampPairs.filter(p => p[0] === c)[0][1]
server.controllers.get('DiscordController').buildEmbedFieldValues(guildEmbedFieldValues, `\n\`${e.name}\` - ${e.description}\n${args}- *handled:* \`${new Date(timestamp).toLocaleString() + ' PT'}\``)
}
// if found, remember that this Event is enabled for this channel
if (message.channel.id === c) {
const timestamp = doc.contextTimestampPairs.filter(p => p[0] === c)[0][1]
server.controllers.get('DiscordController').buildEmbedFieldValues(channelEmbedFieldValues, `\n\`${e.name}\` - ${e.description}\n${args}- *handled:* \`${new Date(timestamp).toLocaleString() + ' PT'}\``)
}
}
}
// if there are enabled Events, announce them
if ([...globalEmbedFieldValues, ...guildEmbedFieldValues, ...channelEmbedFieldValues].length > 0) {
const globalEmbedFields = server.controllers.get('DiscordController').buildEmbedFields('Enabled Global Events', globalEmbedFieldValues)
const guildEmbedFields = server.controllers.get('DiscordController').buildEmbedFields('Enabled Server Events', guildEmbedFieldValues)
const channelEmbedFields = server.controllers.get('DiscordController').buildEmbedFields('Enabled Channel Events', channelEmbedFieldValues)
// build message embed
const embed = server.controllers.get('DiscordController').buildEmbed({
title: EVENT_LIST_TXT,
footerTextType: 'Command',
fields: [...globalEmbedFields, ...guildEmbedFields, ...channelEmbedFields]
})
// send message embed
return message.channel.send(embed)
// otherwise, provide help about enabling Events
} else {
message.channel.send(`There are no Events enabled here. ${EVENT_LIST_TXT}`)
}
// if the argument provided is 'list', show a list of Events that can be enabled for this context
} else if (args[0] === 'list') {
const globalEmbedFieldValues = []
const guildEmbedFieldValues = []
const channelEmbedFieldValues = []
// get all loaded Events and their database documents
for (const e of server.events.values()) {
// only consider interval Events
if (e.type !== 'interval') continue
// fetch this Event's database document
let doc = await server.controllers.get('DatabaseController').fetchDoc({ db: 'event', id: e.name })
// if missing, initialize this Event's database document
doc = await initializeIfMissing(e, doc)
if (e.context === 'guild') {
// determine if context is enabled
const contextDoc = await server.controllers.get('DatabaseController').fetchDoc({
db: 'event',
id: `${e.name}_${message.guild.id}`
})
if (contextDoc.enabled) continue
// if the Guild Event is not already enabled, add it to the embed
server.controllers.get('DiscordController').buildEmbedFieldValues(guildEmbedFieldValues, `\n\`${e.name}\` - ${e.description}`)
}
if (e.context === 'channel') {
// determine if context is enabled
const contextDoc = await server.controllers.get('DatabaseController').fetchDoc({
db: 'event',
id: `${e.name}_${message.channel.id}`
})
if (contextDoc.enabled) continue
// if the channel Event is not already enabled, add it to the embed
server.controllers.get('DiscordController').buildEmbedFieldValues(channelEmbedFieldValues, `\n\`${e.name}\` - ${e.description}`)
}
if (e.context === 'global' && server.controllers.get('DiscordController').botOwner === message.author.id) {
const contextDoc = await server.controllers.get('DatabaseController').fetchDoc({
db: 'event',
id: `${e.name}_global`
})
if (contextDoc.enabled) continue
// if the global Event is not already enabled, add it to the embed
server.controllers.get('DiscordController').buildEmbedFieldValues(globalEmbedFieldValues, `\n\`${e.name}\` - ${e.description}`)
}
}
// if there are Events that can be enabled, announce them
if ([...globalEmbedFieldValues, ...guildEmbedFieldValues, ...channelEmbedFieldValues].length > 0) {
const globalEmbedFields = server.controllers.get('DiscordController').buildEmbedFields('Available Global Events', globalEmbedFieldValues)
const guildEmbedFields = server.controllers.get('DiscordController').buildEmbedFields('Available Server Events', guildEmbedFieldValues)
const channelEmbedFields = server.controllers.get('DiscordController').buildEmbedFields('Available Channel Events', channelEmbedFieldValues)
// build message embed
const embed = server.controllers.get('DiscordController').buildEmbed({
title: EVENT_TXT,
footerTextType: 'Command',
fields: [...globalEmbedFields, ...guildEmbedFields, ...channelEmbedFields]
})
// send message embed
return message.channel.send(embed)
// otherwise, announce none available
} else {
message.channel.send(`There are no Events that can be enabled here. ${EVENT_TXT}`)
}
// otherwise, toggle the provided Event
} else {
const event = Array.from(server.events.values()).filter(e => e.name === args[0])[0]
// stop if Event not found
if (!event) return message.reply('that Event does not exist.')
// stop if Event is not an interval type
if (event.type !== 'interval') return message.reply('that Event cannot be enabled.')
// stop if global interval Event is not being toggled by bot owner
if (server.controllers.get('DiscordController').botOwner !== message.author.id && event.context === 'global') return message.reply('you cannot do that.')
let msg
try {
// fetch this Event's database document
let eventData = await server.controllers.get('DatabaseController').fetchDoc({ db: 'event', id: event.name })
// if missing, initialize this Event's database document
eventData = await initializeIfMissing(event, eventData)
// determine this Event's context
const fetchType = (event.context === 'guild' ? message.guild.id : message.channel.id)
const context = {
type: event.context,
id: (event.context === 'global' ? 'global' : fetchType)
}
// if missing, initialize the context's database document for this Event
const contextDoc = await server.controllers.get('DatabaseController').fetchDoc({ db: 'event', id: `${event.name}_${context.id}` })
if (contextDoc.status === 404) {
await server.controllers.get('DatabaseController').updateDoc({
db: 'event',
id: `${event.name}_${context.id}`
})
}
// if Event is enabled for this context, disable it
if (contextDoc.enabled) {
// do any Event-specific cleanup before disabling the Event for this context
if (context.type === 'guild') {
await event.disable({
server,
context: await server.controllers.get('DiscordController').client.guilds.fetch(context.id),
args: args.slice(1),
message
})
} else if (context.type === 'channel') {
await event.disable({
server,
context: await server.controllers.get('DiscordController').client.channels.fetch(context.id),
args: args.slice(1),
message
})
} else if (context.type === 'global') {
await event.disable({
server,
context: 'global',
args: args.slice(1),
message
})
}
// update locally to avoid database polling every loop
server.controllers.get('EventController').updateLocalEventContexts({
event,
filterFunc: c => !context.id
})
// save the Event as disabled in the context's database document
await server.controllers.get('DatabaseController').updateDoc({
db: 'event',
id: `${event.name}_${context.id}`,
payload: {
enabled: false,
args: undefined
}
})
// announce successfully disabling
const contextNameFixed = (context.type === 'guild' ? 'server' : 'channel')
msg = `The \`${event.name}\` Event is now **disabled** ${context.type === 'global' ? 'globally' : 'for this ' + contextNameFixed}.`
message.channel.send(msg)
msg = `${event.name} Event is now disabled ${context.type === 'global' ? 'globally' : `for ${server.utils.capitalize(context.type)}<${context.id}>`} @ ${new Date().toLocaleString()} PT`
console.log(msg)
// otherwise, enable the Event for this context
} else {
// do any Event-specific setup before enabling the Event for this context
if (context.type === 'guild') {
await event.enable({
server,
context: await server.controllers.get('DiscordController').client.guilds.fetch(context.id),
args: args.slice(1),
message
})
} else if (context.type === 'channel') {
await event.enable({
server,
context: await server.controllers.get('DiscordController').client.channels.fetch(context.id),
args: args.slice(1),
message
})
} else if (context.type === 'global') {
await event.enable({
server,
context: 'global',
args: args.slice(1),
message
})
}
// update locally to avoid database polling every loop
server.controllers.get('EventController').updateLocalEventContexts({
event,
filterFunc: c => !context.id,
newContext: context.id
})
const local = server.controllers.get('EventController').intervalEvents.get(event.name)
server.controllers.get('EventController').intervalEvents.set(event.name, [...local.filter(c => c !== context.id), context.id])
// if this context does not have a timestamp, create one and fire Event
if (!eventData.contextTimestampPairs.find(p => p[0] === context.id)) {
const payload = eventData.contextTimestampPairs
server.controllers.get('DatabaseController').updateDoc({
db: 'event',
id: event.name,
payload: {
contextTimestampPairs: [...payload, [context.id, Date.now()]]
}
})
// check if bot can see context
try {
// determine this Event's context
const fetchedContext = await server.controllers.get('EventController').fetchEventContext({ context, event })
event.handler({ server, context: fetchedContext })
} catch (e) {
// update locally to avoid database polling every loop
server.controllers.get('EventController').updateLocalEventContexts({
event,
filterFunc: c => !context.id,
newContext: context.id
})
// save the Event as disabled in the context's database document
await server.controllers.get('DatabaseController').updateDoc({
db: 'event',
id: `${event.name}_${context.id}`,
payload: {
enabled: false,
args: undefined
}
})
// determine the capitalized name of this context
const contextName = (this.context === 'guild' ? 'Guild' : 'Channel')
return console.log(`Bot doesn't know about ${contextName}<${context.id}> for ${event.name} Event @ ${new Date().toLocaleString()} PT:`, e)
}
}
// save the Event as enabled in the context's database document
await server.controllers.get('DatabaseController').updateDoc({
db: 'event',
id: `${event.name}_${context.id}`,
payload: {
enabled: true,
args: args.slice(1)
}
})
// announce successfully enabling
const contextNameFixed = (context.type === 'guild' ? 'server' : 'channel')
msg = `The \`${event.name}\` Event is now **enabled** ${context.type === 'global' ? 'globally' : 'for this ' + contextNameFixed}.`
message.channel.send(msg)
msg = `${event.name} Event is now enabled ${context.type === 'global' ? 'globally' : `for ${server.utils.capitalize(context.type)}<${context.id}>`} @ ${new Date().toLocaleString()} PT`
console.log(msg)
}
} catch (e) {
msg = `An error occurred while toggling \`${event.name}\` Event.`
message.channel.send(msg)
msg = `An error occurred in Channel<${message.channel.id}> while toggling ${event.name} @ ${new Date().toLocaleString()} PT`
console.log(msg, e)
}
}
}
}