Wait for main and inbox guilds to become available before initializing the bot
parent
eac3e9e0a8
commit
fcd48d6420
385
src/main.js
385
src/main.js
|
@ -29,211 +29,242 @@ const alert = require('./modules/alert');
|
||||||
|
|
||||||
const {ACCIDENTAL_THREAD_MESSAGES} = require('./data/constants');
|
const {ACCIDENTAL_THREAD_MESSAGES} = require('./data/constants');
|
||||||
|
|
||||||
// Once the bot has connected, set the status/"playing" message
|
module.exports = {
|
||||||
bot.on('ready', () => {
|
async start() {
|
||||||
bot.editStatus(null, {name: config.status});
|
console.log('Connecting to Discord...');
|
||||||
console.log('Connected! Now listening to DMs.');
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
bot.once('ready', async () => {
|
||||||
* When a moderator posts in a modmail thread...
|
console.log('Connected, waiting for guilds to become available');
|
||||||
* 1) If alwaysReply is enabled, reply to the user
|
await Promise.all([
|
||||||
* 2) If alwaysReply is disabled, save that message as a chat message in the thread
|
...config.mainGuildId.map(id => waitForGuild(id)),
|
||||||
*/
|
waitForGuild(config.mailGuildId)
|
||||||
bot.on('messageCreate', async msg => {
|
]);
|
||||||
if (! utils.messageIsOnInboxServer(msg)) return;
|
|
||||||
if (msg.author.bot) return;
|
|
||||||
|
|
||||||
const thread = await threads.findByChannelId(msg.channel.id);
|
console.log('Initializing');
|
||||||
if (! thread) return;
|
initStatus();
|
||||||
|
initBaseMessageHandlers();
|
||||||
|
initPlugins();
|
||||||
|
|
||||||
if (msg.content.startsWith(config.prefix) || msg.content.startsWith(config.snippetPrefix)) {
|
console.log('Done! Now listening to DMs.');
|
||||||
// Save commands as "command messages"
|
});
|
||||||
if (msg.content.startsWith(config.snippetPrefix)) return; // Ignore snippets
|
|
||||||
thread.saveCommandMessage(msg);
|
|
||||||
} else if (config.alwaysReply) {
|
|
||||||
// AUTO-REPLY: If config.alwaysReply is enabled, send all chat messages in thread channels as replies
|
|
||||||
if (! utils.isStaff(msg.member)) return; // Only staff are allowed to reply
|
|
||||||
|
|
||||||
const replied = await thread.replyToUser(msg.member, msg.content.trim(), msg.attachments, config.alwaysReplyAnon || false);
|
bot.connect();
|
||||||
if (replied) msg.delete();
|
|
||||||
} else {
|
|
||||||
// Otherwise just save the messages as "chat" in the logs
|
|
||||||
thread.saveChatMessage(msg);
|
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
|
||||||
/**
|
function waitForGuild(guildId) {
|
||||||
* When we get a private message...
|
if (bot.guilds.has(guildId)) {
|
||||||
* 1) Find the open modmail thread for this user, or create a new one
|
return Promise.resolve();
|
||||||
* 2) Post the message as a user reply in the thread
|
}
|
||||||
*/
|
|
||||||
bot.on('messageCreate', async msg => {
|
|
||||||
if (! (msg.channel instanceof Eris.PrivateChannel)) return;
|
|
||||||
if (msg.author.bot) return;
|
|
||||||
if (msg.type !== 0) return; // Ignore pins etc.
|
|
||||||
|
|
||||||
if (await blocked.isBlocked(msg.author.id)) return;
|
return new Promise(resolve => {
|
||||||
|
bot.on('guildAvailable', guild => {
|
||||||
// Private message handling is queued so e.g. multiple message in quick succession don't result in multiple channels being created
|
if (guild.id === guildId) {
|
||||||
messageQueue.add(async () => {
|
resolve();
|
||||||
let thread = await threads.findOpenThreadByUserId(msg.author.id);
|
}
|
||||||
|
});
|
||||||
// New thread
|
|
||||||
if (! thread) {
|
|
||||||
// Ignore messages that shouldn't usually open new threads, such as "ok", "thanks", etc.
|
|
||||||
if (config.ignoreAccidentalThreads && msg.content && ACCIDENTAL_THREAD_MESSAGES.includes(msg.content.trim().toLowerCase())) return;
|
|
||||||
|
|
||||||
thread = await threads.createNewThreadForUser(msg.author);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (thread) await thread.receiveUserReply(msg);
|
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
|
||||||
/**
|
function initStatus() {
|
||||||
* When a message is edited...
|
bot.editStatus(null, {name: config.status});
|
||||||
* 1) If that message was in DMs, and we have a thread open with that user, post the edit as a system message in the thread
|
}
|
||||||
* 2) If that message was moderator chatter in the thread, update the corresponding chat message in the DB
|
|
||||||
*/
|
|
||||||
bot.on('messageUpdate', async (msg, oldMessage) => {
|
|
||||||
if (! msg || ! msg.author) return;
|
|
||||||
if (msg.author.bot) return;
|
|
||||||
if (await blocked.isBlocked(msg.author.id)) return;
|
|
||||||
|
|
||||||
// Old message content doesn't persist between bot restarts
|
function initBaseMessageHandlers() {
|
||||||
const oldContent = oldMessage && oldMessage.content || '*Unavailable due to bot restart*';
|
/**
|
||||||
const newContent = msg.content;
|
* When a moderator posts in a modmail thread...
|
||||||
|
* 1) If alwaysReply is enabled, reply to the user
|
||||||
|
* 2) If alwaysReply is disabled, save that message as a chat message in the thread
|
||||||
|
*/
|
||||||
|
bot.on('messageCreate', async msg => {
|
||||||
|
if (! utils.messageIsOnInboxServer(msg)) return;
|
||||||
|
if (msg.author.bot) return;
|
||||||
|
|
||||||
// Ignore bogus edit events with no changes
|
const thread = await threads.findByChannelId(msg.channel.id);
|
||||||
if (newContent.trim() === oldContent.trim()) return;
|
|
||||||
|
|
||||||
// 1) Edit in DMs
|
|
||||||
if (msg.channel instanceof Eris.PrivateChannel) {
|
|
||||||
const thread = await threads.findOpenThreadByUserId(msg.author.id);
|
|
||||||
if (! thread) return;
|
if (! thread) return;
|
||||||
|
|
||||||
const editMessage = utils.disableLinkPreviews(`**The user edited their message:**\n\`B:\` ${oldContent}\n\`A:\` ${newContent}`);
|
if (msg.content.startsWith(config.prefix) || msg.content.startsWith(config.snippetPrefix)) {
|
||||||
thread.postSystemMessage(editMessage);
|
// Save commands as "command messages"
|
||||||
}
|
if (msg.content.startsWith(config.snippetPrefix)) return; // Ignore snippets
|
||||||
|
thread.saveCommandMessage(msg);
|
||||||
|
} else if (config.alwaysReply) {
|
||||||
|
// AUTO-REPLY: If config.alwaysReply is enabled, send all chat messages in thread channels as replies
|
||||||
|
if (! utils.isStaff(msg.member)) return; // Only staff are allowed to reply
|
||||||
|
|
||||||
|
const replied = await thread.replyToUser(msg.member, msg.content.trim(), msg.attachments, config.alwaysReplyAnon || false);
|
||||||
|
if (replied) msg.delete();
|
||||||
|
} else {
|
||||||
|
// Otherwise just save the messages as "chat" in the logs
|
||||||
|
thread.saveChatMessage(msg);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When we get a private message...
|
||||||
|
* 1) Find the open modmail thread for this user, or create a new one
|
||||||
|
* 2) Post the message as a user reply in the thread
|
||||||
|
*/
|
||||||
|
bot.on('messageCreate', async msg => {
|
||||||
|
if (! (msg.channel instanceof Eris.PrivateChannel)) return;
|
||||||
|
if (msg.author.bot) return;
|
||||||
|
if (msg.type !== 0) return; // Ignore pins etc.
|
||||||
|
|
||||||
|
if (await blocked.isBlocked(msg.author.id)) return;
|
||||||
|
|
||||||
|
// Private message handling is queued so e.g. multiple message in quick succession don't result in multiple channels being created
|
||||||
|
messageQueue.add(async () => {
|
||||||
|
let thread = await threads.findOpenThreadByUserId(msg.author.id);
|
||||||
|
|
||||||
|
// New thread
|
||||||
|
if (! thread) {
|
||||||
|
// Ignore messages that shouldn't usually open new threads, such as "ok", "thanks", etc.
|
||||||
|
if (config.ignoreAccidentalThreads && msg.content && ACCIDENTAL_THREAD_MESSAGES.includes(msg.content.trim().toLowerCase())) return;
|
||||||
|
|
||||||
|
thread = await threads.createNewThreadForUser(msg.author);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (thread) await thread.receiveUserReply(msg);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When a message is edited...
|
||||||
|
* 1) If that message was in DMs, and we have a thread open with that user, post the edit as a system message in the thread
|
||||||
|
* 2) If that message was moderator chatter in the thread, update the corresponding chat message in the DB
|
||||||
|
*/
|
||||||
|
bot.on('messageUpdate', async (msg, oldMessage) => {
|
||||||
|
if (! msg || ! msg.author) return;
|
||||||
|
if (msg.author.bot) return;
|
||||||
|
if (await blocked.isBlocked(msg.author.id)) return;
|
||||||
|
|
||||||
|
// Old message content doesn't persist between bot restarts
|
||||||
|
const oldContent = oldMessage && oldMessage.content || '*Unavailable due to bot restart*';
|
||||||
|
const newContent = msg.content;
|
||||||
|
|
||||||
|
// Ignore bogus edit events with no changes
|
||||||
|
if (newContent.trim() === oldContent.trim()) return;
|
||||||
|
|
||||||
|
// 1) Edit in DMs
|
||||||
|
if (msg.channel instanceof Eris.PrivateChannel) {
|
||||||
|
const thread = await threads.findOpenThreadByUserId(msg.author.id);
|
||||||
|
if (! thread) return;
|
||||||
|
|
||||||
|
const editMessage = utils.disableLinkPreviews(`**The user edited their message:**\n\`B:\` ${oldContent}\n\`A:\` ${newContent}`);
|
||||||
|
thread.postSystemMessage(editMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) Edit in the thread
|
||||||
|
else if (utils.messageIsOnInboxServer(msg) && utils.isStaff(msg.member)) {
|
||||||
|
const thread = await threads.findOpenThreadByChannelId(msg.channel.id);
|
||||||
|
if (! thread) return;
|
||||||
|
|
||||||
|
thread.updateChatMessage(msg);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When a staff message is deleted in a modmail thread, delete it from the database as well
|
||||||
|
*/
|
||||||
|
bot.on('messageDelete', async msg => {
|
||||||
|
if (! msg.author) return;
|
||||||
|
if (msg.author.bot) return;
|
||||||
|
if (! utils.messageIsOnInboxServer(msg)) return;
|
||||||
|
if (! utils.isStaff(msg.member)) return;
|
||||||
|
|
||||||
// 2) Edit in the thread
|
|
||||||
else if (utils.messageIsOnInboxServer(msg) && utils.isStaff(msg.member)) {
|
|
||||||
const thread = await threads.findOpenThreadByChannelId(msg.channel.id);
|
const thread = await threads.findOpenThreadByChannelId(msg.channel.id);
|
||||||
if (! thread) return;
|
if (! thread) return;
|
||||||
|
|
||||||
thread.updateChatMessage(msg);
|
thread.deleteChatMessage(msg.id);
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* When a staff message is deleted in a modmail thread, delete it from the database as well
|
|
||||||
*/
|
|
||||||
bot.on('messageDelete', async msg => {
|
|
||||||
if (! msg.author) return;
|
|
||||||
if (msg.author.bot) return;
|
|
||||||
if (! utils.messageIsOnInboxServer(msg)) return;
|
|
||||||
if (! utils.isStaff(msg.member)) return;
|
|
||||||
|
|
||||||
const thread = await threads.findOpenThreadByChannelId(msg.channel.id);
|
|
||||||
if (! thread) return;
|
|
||||||
|
|
||||||
thread.deleteChatMessage(msg.id);
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* When the bot is mentioned on the main server, ping staff in the log channel about it
|
|
||||||
*/
|
|
||||||
bot.on('messageCreate', async msg => {
|
|
||||||
if (! utils.messageIsOnMainServer(msg)) return;
|
|
||||||
if (! msg.mentions.some(user => user.id === bot.user.id)) return;
|
|
||||||
if (msg.author.bot) return;
|
|
||||||
|
|
||||||
if (utils.messageIsOnInboxServer(msg)) {
|
|
||||||
// For same server setups, check if the person who pinged modmail is staff. If so, ignore the ping.
|
|
||||||
if (utils.isStaff(msg.member)) return;
|
|
||||||
} else {
|
|
||||||
// For separate server setups, check if the member is staff on the modmail server
|
|
||||||
const inboxMember = utils.getInboxGuild().members.get(msg.author.id);
|
|
||||||
if (inboxMember && utils.isStaff(inboxMember)) return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the person who mentioned the bot is blocked, ignore them
|
|
||||||
if (await blocked.isBlocked(msg.author.id)) return;
|
|
||||||
|
|
||||||
let content;
|
|
||||||
const mainGuilds = utils.getMainGuilds();
|
|
||||||
const staffMention = (config.pingOnBotMention ? utils.getInboxMention() : '');
|
|
||||||
|
|
||||||
if (mainGuilds.length === 1) {
|
|
||||||
content = `${staffMention}Bot mentioned in ${msg.channel.mention} by **${msg.author.username}#${msg.author.discriminator}**: "${msg.cleanContent}"`;
|
|
||||||
} else {
|
|
||||||
content = `${staffMention}Bot mentioned in ${msg.channel.mention} (${msg.channel.guild.name}) by **${msg.author.username}#${msg.author.discriminator}**: "${msg.cleanContent}"`;
|
|
||||||
}
|
|
||||||
|
|
||||||
bot.createMessage(utils.getLogChannel().id, {
|
|
||||||
content,
|
|
||||||
disableEveryone: false,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Send an auto-response to the mention, if enabled
|
/**
|
||||||
if (config.botMentionResponse) {
|
* When the bot is mentioned on the main server, ping staff in the log channel about it
|
||||||
bot.createMessage(msg.channel.id, config.botMentionResponse.replace(/{userMention}/g, `<@${msg.author.id}>`));
|
*/
|
||||||
}
|
bot.on('messageCreate', async msg => {
|
||||||
});
|
if (! utils.messageIsOnMainServer(msg)) return;
|
||||||
|
if (! msg.mentions.some(user => user.id === bot.user.id)) return;
|
||||||
|
if (msg.author.bot) return;
|
||||||
|
|
||||||
module.exports = {
|
if (utils.messageIsOnInboxServer(msg)) {
|
||||||
async start() {
|
// For same server setups, check if the person who pinged modmail is staff. If so, ignore the ping.
|
||||||
// Initialize command manager
|
if (utils.isStaff(msg.member)) return;
|
||||||
const commands = createCommandManager(bot);
|
} else {
|
||||||
|
// For separate server setups, check if the member is staff on the modmail server
|
||||||
// Register command aliases
|
const inboxMember = utils.getInboxGuild().members.get(msg.author.id);
|
||||||
if (config.commandAliases) {
|
if (inboxMember && utils.isStaff(inboxMember)) return;
|
||||||
for (const alias in config.commandAliases) {
|
|
||||||
commands.addAlias(config.commandAliases[alias], alias);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load modules
|
// If the person who mentioned the bot is blocked, ignore them
|
||||||
console.log('Loading plugins...');
|
if (await blocked.isBlocked(msg.author.id)) return;
|
||||||
const builtInPlugins = [
|
|
||||||
reply,
|
|
||||||
close,
|
|
||||||
logs,
|
|
||||||
block,
|
|
||||||
move,
|
|
||||||
snippets,
|
|
||||||
suspend,
|
|
||||||
greeting,
|
|
||||||
webserver,
|
|
||||||
typingProxy,
|
|
||||||
version,
|
|
||||||
newthread,
|
|
||||||
idModule,
|
|
||||||
alert
|
|
||||||
];
|
|
||||||
|
|
||||||
const plugins = [...builtInPlugins];
|
let content;
|
||||||
|
const mainGuilds = utils.getMainGuilds();
|
||||||
|
const staffMention = (config.pingOnBotMention ? utils.getInboxMention() : '');
|
||||||
|
|
||||||
if (config.plugins && config.plugins.length) {
|
if (mainGuilds.length === 1) {
|
||||||
for (const plugin of config.plugins) {
|
content = `${staffMention}Bot mentioned in ${msg.channel.mention} by **${msg.author.username}#${msg.author.discriminator}**: "${msg.cleanContent}"`;
|
||||||
const pluginFn = require(`../${plugin}`);
|
} else {
|
||||||
plugins.push(pluginFn);
|
content = `${staffMention}Bot mentioned in ${msg.channel.mention} (${msg.channel.guild.name}) by **${msg.author.username}#${msg.author.discriminator}**: "${msg.cleanContent}"`;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
plugins.forEach(pluginFn => {
|
bot.createMessage(utils.getLogChannel().id, {
|
||||||
pluginFn(bot, knex, config, commands);
|
content,
|
||||||
|
disableEveryone: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(`Loaded ${plugins.length} plugins (${builtInPlugins.length} built-in plugins, ${plugins.length - builtInPlugins.length} external plugins)`);
|
// Send an auto-response to the mention, if enabled
|
||||||
|
if (config.botMentionResponse) {
|
||||||
if (config.updateNotifications) {
|
bot.createMessage(msg.channel.id, config.botMentionResponse.replace(/{userMention}/g, `<@${msg.author.id}>`));
|
||||||
updates.startVersionRefreshLoop();
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Connect to Discord
|
function initPlugins() {
|
||||||
console.log('Connecting to Discord...');
|
// Initialize command manager
|
||||||
await bot.connect();
|
const commands = createCommandManager(bot);
|
||||||
|
|
||||||
|
// Register command aliases
|
||||||
|
if (config.commandAliases) {
|
||||||
|
for (const alias in config.commandAliases) {
|
||||||
|
commands.addAlias(config.commandAliases[alias], alias);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
// Load plugins
|
||||||
|
console.log('Loading plugins');
|
||||||
|
const builtInPlugins = [
|
||||||
|
reply,
|
||||||
|
close,
|
||||||
|
logs,
|
||||||
|
block,
|
||||||
|
move,
|
||||||
|
snippets,
|
||||||
|
suspend,
|
||||||
|
greeting,
|
||||||
|
webserver,
|
||||||
|
typingProxy,
|
||||||
|
version,
|
||||||
|
newthread,
|
||||||
|
idModule,
|
||||||
|
alert
|
||||||
|
];
|
||||||
|
|
||||||
|
const plugins = [...builtInPlugins];
|
||||||
|
|
||||||
|
if (config.plugins && config.plugins.length) {
|
||||||
|
for (const plugin of config.plugins) {
|
||||||
|
const pluginFn = require(`../${plugin}`);
|
||||||
|
plugins.push(pluginFn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
plugins.forEach(pluginFn => {
|
||||||
|
pluginFn(bot, knex, config, commands);
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`Loaded ${plugins.length} plugins (${builtInPlugins.length} built-in plugins, ${plugins.length - builtInPlugins.length} external plugins)`);
|
||||||
|
|
||||||
|
if (config.updateNotifications) {
|
||||||
|
updates.startVersionRefreshLoop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue