invite-logger/index.js

290 lines
8.9 KiB
JavaScript

const fs = require('fs');
const dotenv = require('dotenv');
dotenv.config();
const colors = require('colors');
const Discord = require('discord.js');
const client = new Discord.Client({
intents: [
"Guilds",
"GuildMembers",
"GuildInvites"
]
});
const {
REST,
Routes
} = require('discord.js');
const rest = new REST({
version: '10'
}).setToken(process.env.BOT_TOKEN);
const sqlite3 = require('sqlite3').verbose();
const db = new sqlite3.Database(process.env.DB_PATH);
// DB migrations from files in ./migrations
const migrations = fs.readdirSync('./migrations').sort();
migrations.forEach((migration) => {
const sql = fs.readFileSync(`./migrations/${migration}`).toString();
// get first line for comment to log
const comment = sql.split('\n')[0];
console.log(`${colors.cyan('INFO')} Running migration: ${colors.yellow(comment)}`);
db.run(sql);
});
// Handle bot ready
client.on('ready', async () => {
console.log(`${colors.cyan('INFO')} Logged in as ${colors.green(client.user.displayName)}!`);
// Get all invites
client.invites = [];
// Update Invites
console.log(`${colors.cyan("[INFO]")} Fetching Invites...`);
const fetchInvites = async () => {
await client.guilds.cache.forEach(guild => { //on bot start, fetch all guilds and fetch all invites to store
thisGuild = []
guild.invites.fetch().then(guildInvites => {
guildInvites.each(guildInvite => {
client.invites[guildInvite.code] = guildInvite.uses
thisGuild.push(guildInvite.code)
})
}).then(() => {
console.log(`${colors.cyan("[INFO]")} Fetched ${thisGuild.length} Invites for ${colors.green(guild.name)}`);
})
guild.fetchVanityData().then(vanityData => {
client.invites[vanityData.code] = vanityData.uses
}).catch(err => {
// Do nothing
})
})
}
fetchInvites();
// Set interval to update invites every 5 minutes, just in case something desyncs
setInterval(fetchInvites, 300000);
// Register commands
const commands = [
{
name: "logchannel",
description: "Set the channel to log invites",
options: [
{
name: "channel",
description: "The channel to log invites to",
type: 7,
required: true
},
{
name: "log_create",
description: "Log invite creation",
type: 5,
required: false
}
],
default_member_permissions: 32
}
]
await (async () => {
try {
console.log(`${colors.cyan("[INFO]")} Registering Commands...`)
let start = Date.now()
//Global
await rest.put(Routes.applicationCommands(client.user.id), { body: commands })
console.log(`${colors.cyan("[INFO]")} Successfully registered commands. Took ${colors.green((Date.now() - start) / 1000)} seconds.`);
} catch (error) {
console.error(error);
}
})();
})
// Handle invite creation
client.on('inviteCreate', (invite) => { //if someone creates an invite while bot is running, update store
client.invites[invite.code] = invite.uses
// Check the db for the channel to log to, guild id is column `id` in table `guilds`, channel id is column `channel`
db.get(`SELECT * FROM guilds WHERE id = ?`, [invite.guild.id], async (err, row) => {
if (err) {
console.error(err);
return;
}
if (!row) return;
if(!row.log_create) return;
client.channels.fetch(row.channel).then(channel => {
if (!channel) return; // They probably set perms wrong
channel.send({
embeds: [{
color: 0x00ffff,
fields: [
{
name: "New Invite",
// inline check, if expiry is in over 100 years, then it's never, otherwise it's the date
// ${invite.expiresTimestamp > 95617584000 ? "Never" : `<t:${invite.expiresTimestamp}>`
value: `Code: ${invite.code}\nMax Uses: ${invite.maxUses || "∞"}\nExpires: ${invite.expiresAt <= 0 ? "Never" : `<t:${Math.floor(new Date(invite.expiresAt) / 1000)}:R>`}\nCreated at: <t:${Math.floor(new Date(invite.createdAt) / 1000)}>`
},
{
name: "Guild",
value: `${invite.guild.name}`
},
{
name: "Channel",
value: `${invite.channel.name} <#${invite.channel.id}>`
},
{
name: "Inviter",
value: `${invite.inviter?.id ?`${invite.inviter} (${invite.inviter.displayName})` : "Custom Invite URL (Through Bot/Embed)"}`
}
]
}]
});
}).catch(err => {
console.log(`${colors.red('ERROR')} ${err.stack}`)
})
})
});
// Handle new member
client.on('guildMemberAdd', async (member) => { // We're just gonna always send invite logs, even if we're not monitoring them
let guild = member.guild
db.get(`SELECT * FROM guilds WHERE id = ?`, [guild.id], (err, row) => {
if (err) {
console.error(err);
return;
}
if (!row) return;
client.channels.fetch(row.channel.toString()).then(channel => {
if (!channel) return; // They probably set perms wrong
member.guild.invites.fetch().then(async guildInvites => { //get all guild invites
sent = false;
guildInvites.forEach(invite => { //basically a for loop over the invites
if (invite.uses != client.invites[invite.code]) { //if it doesn't match what we stored:
if (sent) return console.log(`${colors.red("[ERROR]")} Something VERY bad has happened, and we seem to have lost track of invites.\nCurrent Invite Table:\n${JSON.stringify(client.invites, null, 2)}`); //if we already sent a message, this is a fail safe to prevent spam
channel.send({
embeds: [{
color: 0x00ff00,
fields: [
{
name: "New Member",
value: `${member} (${member.user.displayName})\nJoined at: <t:${Math.floor(new Date(member.joinedAt) / 1000)}>\nAccount Created: <t:${Math.floor(new Date(member.user.createdTimestamp) / 1000)}>`
},
{
name: "Invite",
value: `Inviter: ${invite.inviter?.id ? `${invite.inviter} (${invite.inviter.displayName})` : "Custom Invite URL (Through Bot/Embed)"}\nCode: ${invite.code}\nUses: ${invite.uses}/${invite.maxUses ||"∞"}\nExpires: ${invite.expiresAt <= 0 ? "Never" : `<t:${Math.floor(new Date(invite.expiresAt) / 1000)}:R>`}\nCreated at: <t:${Math.floor(new Date(invite.createdAt) / 1000)}>`
},
{
name: "Guild",
value: `${guild.name}`
}
]
}]
});
client.invites[invite.code] = invite.uses
}
})
})
// Handle vanity URLs
member.guild.fetchVanityData().then(vanityData => {
if (vanityData.uses != client.invites[vanityData.code]) { // They used the vanity URL
channel.send({
embeds: [{
color: 0x00ff00,
fields: [
{
name: "New Member",
value: `${member} (${member.user.displayName})\nJoined at: <t:${Math.floor(new Date(member.joinedAt) / 1000)}>\nAccount Created: <t:${Math.floor(new Date(member.user.createdTimestamp) / 1000)}>`
},
{
name: "Invite",
value: `Vanity Code: ${vanityData.code}\nUses: ${vanityData.uses}`
},
{
name: "Guild",
value: `${guild.name}`
}
]
}]
});
}
}).catch(err => {
// Do nothing
})
})
});
})
// Guild create
client.on('guildCreate', async (guild) => {
// Get invites
guild.invites.fetch().then(guildInvites => {
guildInvites.each(invite => {
client.invites[invite.code] = invite.uses
})
})
guild.fetchVanityData().then(vanityData => {
client.invites[vanityData.code] = vanityData.uses
}).catch(err => {
// Do nothing
})
});
// Guild delete
client.on('guildDelete', async (guild) => {
// Remove invites
guild.invites.fetch().then(guildInvites => {
guildInvites.each(invite => {
delete client.invites[invite.code];
})
})
guild.fetchVanityData().then(vanityData => {
delete client.invites[vanityData.code];
}).catch(err => {
// Do nothing
})
});
// Handle Commands
client.on('interactionCreate', async interaction => {
if (!interaction.isCommand()) return;
if (interaction.commandName === 'logchannel') {
if (!interaction.member.permissions.has('MANAGE_GUILD')) {
interaction.reply({
content: "You do not have permission to use this command.",
ephemeral: true
});
return;
}
log_create = interaction.options.getBoolean('log_create');
if (log_create == null) log_create = true;
const channel = interaction.options.getChannel('channel').id;
db.run(`INSERT OR REPLACE INTO guilds (id, channel, log_create) VALUES (?, ?, ?)`, [interaction.guild.id.toString(), channel.toString(), log_create], (err) => {
if (err) {
console.error(err);
interaction.reply({
content: "An error occurred while setting the log channel.",
ephemeral: true
});
return;
}
interaction.reply({
content: `The log channel has been set to <#${channel}>.`,
ephemeral: true
});
});
}
});
// Handle unhandled rejections and exceptions
process.on('unhandledRejection', error => {
console.error(`${colors.red('ERROR')} Unhandled promise rejection: ${error.stack}`);
});
process.on('uncaughtException', error => {
console.error(`${colors.red('ERROR')} Uncaught exception: ${error.stack}`);
});
client.login(process.env.BOT_TOKEN);