mirror of
https://github.com/nx-bat/discordbot-ng.git
synced 2026-09-22 10:29:10 -04:00
Merge branch 'master' into db-rewrite
This commit is contained in:
+140
-140
@@ -1,141 +1,141 @@
|
||||
import { ApplicationIntegrationType, ChatInputCommandInteraction, Client, Guild, GuildMember, InteractionContextType, MessageMentions, PermissionFlagsBits, SlashCommandBuilder, time, TimestampStyles, User } from 'discord.js';
|
||||
import { Database } from '../shared/Database';
|
||||
import { AltData, comprehensiveAltLookupFromDiscord, deferInteraction } from '../utils';
|
||||
|
||||
const mentionRegex = new RegExp(MessageMentions.UsersPattern);
|
||||
|
||||
export default {
|
||||
name: 'ban',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('ban')
|
||||
.setDescription('Bans a user.')
|
||||
.setIntegrationTypes(ApplicationIntegrationType.GuildInstall)
|
||||
.setContexts(InteractionContextType.Guild)
|
||||
.setDefaultMemberPermissions(PermissionFlagsBits.BanMembers)
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('user')
|
||||
.setDescription('The discord user mention, or ID, to ban.')
|
||||
.setRequired(true)
|
||||
)
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('reason')
|
||||
.setDescription('The reason for the ban')
|
||||
.setRequired(false)
|
||||
.setMaxLength(400)
|
||||
)
|
||||
.addNumberOption(option =>
|
||||
option
|
||||
.setName('hours')
|
||||
.setDescription('The duration of the ban, added with other options (0 for permanent).')
|
||||
.setRequired(false)
|
||||
)
|
||||
.addNumberOption(option =>
|
||||
option
|
||||
.setName('minutes')
|
||||
.setDescription('The duration of the ban, added with other options (0 for permanent).')
|
||||
.setRequired(false)
|
||||
)
|
||||
.addNumberOption(option =>
|
||||
option
|
||||
.setName('seconds')
|
||||
.setDescription('The duration of the ban, added with other options (0 for permanent).')
|
||||
.setRequired(false)
|
||||
)
|
||||
.addNumberOption(option =>
|
||||
option
|
||||
.setName('delete-message-days')
|
||||
.setDescription('How far back to delete messages (in days, default: 0 days).')
|
||||
.setRequired(false)
|
||||
.setMinValue(0)
|
||||
.setMaxValue(7)
|
||||
)
|
||||
.addBooleanOption(option =>
|
||||
option
|
||||
.setName('full-ban')
|
||||
.setDescription('Whether or not to prevent the user from joining on known alts (and ban all existing alts).')
|
||||
.setRequired(false)
|
||||
),
|
||||
handler: async function (client: Client, interaction: ChatInputCommandInteraction) {
|
||||
await deferInteraction(interaction);
|
||||
|
||||
if (!interaction.guild) return interaction.editReply('This command must be used in a server');
|
||||
|
||||
if (!interaction.guild.members.me) return interaction.editReply('An error has occurred. Please try again later.');
|
||||
|
||||
const input = interaction.options.getString('user', true);
|
||||
|
||||
const matches = mentionRegex.exec(input);
|
||||
mentionRegex.lastIndex = 0;
|
||||
|
||||
const idToUse = matches ? matches.groups!.id : input;
|
||||
const reason = interaction.options.getString('reason') ?? '';
|
||||
|
||||
const hours = (interaction.options.getNumber('hours') ?? 0) * 3.6e+6;
|
||||
const minutes = (interaction.options.getNumber('minutes') ?? 0) * 60000;
|
||||
const seconds = (interaction.options.getNumber('seconds') ?? 0) * 1000;
|
||||
|
||||
const duration = hours + minutes + seconds;
|
||||
|
||||
const deleteMessageDays = (interaction.options.getNumber('delete-message-days') ?? 0) * 86400;
|
||||
|
||||
const fullBan = interaction.options.getBoolean('full-ban') ?? false;
|
||||
|
||||
let banMember: GuildMember | null = null;
|
||||
|
||||
try {
|
||||
banMember = await interaction.guild.members.fetch(idToUse);
|
||||
} catch (e) {
|
||||
// Member not in server.
|
||||
}
|
||||
|
||||
const member = await interaction.guild.members.fetch(interaction.user.id);
|
||||
|
||||
if (banMember && member.roles.highest.comparePositionTo(banMember.roles.highest) <= 0) {
|
||||
return await interaction.editReply('You do not have permission to ban this user.');
|
||||
}
|
||||
|
||||
if (banMember && !banMember.bannable) {
|
||||
return await interaction.editReply('I do not have permission to ban this user.');
|
||||
}
|
||||
|
||||
const expiresAt = new Date(Date.now() + duration);
|
||||
|
||||
await Database.putBan(idToUse, duration > 0 ? expiresAt : null, fullBan);
|
||||
|
||||
try {
|
||||
await interaction.guild.bans.create(idToUse, {
|
||||
reason: (reason + ` ${fullBan ? 'Full banned' : 'Banned'} by ${interaction.user.username} (${interaction.user.id})${duration > 0 ? `. Expires at: ${time(expiresAt, TimestampStyles.ShortDateTime)}` : ''}`).trim(),
|
||||
deleteMessageSeconds: deleteMessageDays
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return await interaction.editReply("Error banning user (couldn't ban).");
|
||||
}
|
||||
|
||||
if (fullBan) {
|
||||
const alts = await comprehensiveAltLookupFromDiscord(idToUse, interaction.guild);
|
||||
|
||||
await removeAllAlts([alts], interaction.guild, interaction.user, fullBan, reason, deleteMessageDays, duration, expiresAt);
|
||||
}
|
||||
|
||||
await interaction.editReply(`<@${idToUse}> (${idToUse}) has been ${fullBan ? 'full banned' : 'banned'}.`);
|
||||
}
|
||||
};
|
||||
|
||||
async function removeAllAlts(altData: AltData[], guild: Guild, moderator: User, fullBan: boolean, reason: string, deleteMessageDays: number, duration: number, expiresAt: Date) {
|
||||
for (const data of altData) {
|
||||
if (data.type == 'discord') {
|
||||
try {
|
||||
if (!data.banned) {
|
||||
await guild.members.kick(data.thisId as string, (reason + ` ${fullBan ? 'Full banned' : 'Banned'} by ${moderator.username} (${moderator.id})${duration > 0 ? `. Expires at: ${time(expiresAt, TimestampStyles.ShortDateTime)}` : ''}`).trim());
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
await removeAllAlts(data.alts, guild, moderator, fullBan, reason, deleteMessageDays, duration, expiresAt);
|
||||
}
|
||||
import { ApplicationIntegrationType, ChatInputCommandInteraction, Client, Guild, GuildMember, InteractionContextType, MessageMentions, PermissionFlagsBits, SlashCommandBuilder, time, TimestampStyles, User } from 'discord.js';
|
||||
import { Database } from '../shared/Database';
|
||||
import { AltData, comprehensiveAltLookupFromDiscord, deferInteraction } from '../utils';
|
||||
|
||||
const mentionRegex = new RegExp(MessageMentions.UsersPattern);
|
||||
|
||||
export default {
|
||||
name: 'ban',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('ban')
|
||||
.setDescription('Bans a user.')
|
||||
.setIntegrationTypes(ApplicationIntegrationType.GuildInstall)
|
||||
.setContexts(InteractionContextType.Guild)
|
||||
.setDefaultMemberPermissions(PermissionFlagsBits.BanMembers)
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('user')
|
||||
.setDescription('The discord user mention, or ID, to ban.')
|
||||
.setRequired(true)
|
||||
)
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('reason')
|
||||
.setDescription('The reason for the ban')
|
||||
.setRequired(false)
|
||||
.setMaxLength(400)
|
||||
)
|
||||
.addNumberOption(option =>
|
||||
option
|
||||
.setName('hours')
|
||||
.setDescription('The duration of the ban, added with other options (0 for permanent).')
|
||||
.setRequired(false)
|
||||
)
|
||||
.addNumberOption(option =>
|
||||
option
|
||||
.setName('minutes')
|
||||
.setDescription('The duration of the ban, added with other options (0 for permanent).')
|
||||
.setRequired(false)
|
||||
)
|
||||
.addNumberOption(option =>
|
||||
option
|
||||
.setName('seconds')
|
||||
.setDescription('The duration of the ban, added with other options (0 for permanent).')
|
||||
.setRequired(false)
|
||||
)
|
||||
.addNumberOption(option =>
|
||||
option
|
||||
.setName('delete-message-days')
|
||||
.setDescription('How far back to delete messages (in days, default: 0 days).')
|
||||
.setRequired(false)
|
||||
.setMinValue(0)
|
||||
.setMaxValue(7)
|
||||
)
|
||||
.addBooleanOption(option =>
|
||||
option
|
||||
.setName('full-ban')
|
||||
.setDescription('Whether or not to prevent the user from joining on known alts (and ban all existing alts).')
|
||||
.setRequired(false)
|
||||
),
|
||||
handler: async function (client: Client, interaction: ChatInputCommandInteraction) {
|
||||
await deferInteraction(interaction);
|
||||
|
||||
if (!interaction.guild) return interaction.editReply('This command must be used in a server');
|
||||
|
||||
if (!interaction.guild.members.me) return interaction.editReply('An error has occurred. Please try again later.');
|
||||
|
||||
const input = interaction.options.getString('user', true);
|
||||
|
||||
const matches = mentionRegex.exec(input);
|
||||
mentionRegex.lastIndex = 0;
|
||||
|
||||
const idToUse = matches ? matches.groups!.id : input;
|
||||
const reason = interaction.options.getString('reason') ?? '';
|
||||
|
||||
const hours = (interaction.options.getNumber('hours') ?? 0) * 3.6e+6;
|
||||
const minutes = (interaction.options.getNumber('minutes') ?? 0) * 60000;
|
||||
const seconds = (interaction.options.getNumber('seconds') ?? 0) * 1000;
|
||||
|
||||
const duration = hours + minutes + seconds;
|
||||
|
||||
const deleteMessageDays = (interaction.options.getNumber('delete-message-days') ?? 0) * 86400;
|
||||
|
||||
const fullBan = interaction.options.getBoolean('full-ban') ?? false;
|
||||
|
||||
let banMember: GuildMember | null = null;
|
||||
|
||||
try {
|
||||
banMember = await interaction.guild.members.fetch(idToUse);
|
||||
} catch (e) {
|
||||
// Member not in server.
|
||||
}
|
||||
|
||||
const member = await interaction.guild.members.fetch(interaction.user.id);
|
||||
|
||||
if (banMember && member.roles.highest.comparePositionTo(banMember.roles.highest) <= 0) {
|
||||
return await interaction.editReply('You do not have permission to ban this user.');
|
||||
}
|
||||
|
||||
if (banMember && !banMember.bannable) {
|
||||
return await interaction.editReply('I do not have permission to ban this user.');
|
||||
}
|
||||
|
||||
const expiresAt = new Date(Date.now() + duration);
|
||||
|
||||
await Database.putBan(idToUse, duration > 0 ? expiresAt : null, fullBan);
|
||||
|
||||
try {
|
||||
await interaction.guild.bans.create(idToUse, {
|
||||
reason: (reason + ` ${fullBan ? 'Full banned' : 'Banned'} by ${interaction.user.username} (${interaction.user.id})${duration > 0 ? `. Expires at: ${time(expiresAt, TimestampStyles.ShortDateTime)}` : ''}`).trim(),
|
||||
deleteMessageSeconds: deleteMessageDays
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return await interaction.editReply("Error banning user (couldn't ban).");
|
||||
}
|
||||
|
||||
if (fullBan) {
|
||||
const alts = await comprehensiveAltLookupFromDiscord(idToUse, interaction.guild);
|
||||
|
||||
await removeAllAlts([alts], interaction.guild, interaction.user, fullBan, reason, deleteMessageDays, duration, expiresAt);
|
||||
}
|
||||
|
||||
await interaction.editReply(`<@${idToUse}> (${idToUse}) has been ${fullBan ? 'full banned' : 'banned'}.`);
|
||||
}
|
||||
};
|
||||
|
||||
async function removeAllAlts(altData: AltData[], guild: Guild, moderator: User, fullBan: boolean, reason: string, deleteMessageDays: number, duration: number, expiresAt: Date) {
|
||||
for (const data of altData) {
|
||||
if (data.type == 'discord') {
|
||||
try {
|
||||
if (!data.banned) {
|
||||
await guild.members.kick(data.thisId as string, (reason + ` ${fullBan ? 'Full banned' : 'Banned'} by ${moderator.username} (${moderator.id})${duration > 0 ? `. Expires at: ${time(expiresAt, TimestampStyles.ShortDateTime)}` : ''}`).trim());
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
await removeAllAlts(data.alts, guild, moderator, fullBan, reason, deleteMessageDays, duration, expiresAt);
|
||||
}
|
||||
}
|
||||
+47
-47
@@ -1,48 +1,48 @@
|
||||
import { ApplicationIntegrationType, AutocompleteInteraction, ChatInputCommandInteraction, Client, InteractionContextType, MessageFlags, PermissionFlagsBits, SlashCommandBuilder } from 'discord.js';
|
||||
import { Database } from '../shared/Database';
|
||||
import { KnowledgebaseItem } from '../types';
|
||||
|
||||
export default {
|
||||
name: 'cite',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('cite')
|
||||
.setDescription('Cite content from the knowledgebase.')
|
||||
.setIntegrationTypes(ApplicationIntegrationType.GuildInstall)
|
||||
.setContexts(InteractionContextType.Guild)
|
||||
.setDefaultMemberPermissions(PermissionFlagsBits.ManageMessages)
|
||||
.addIntegerOption(option =>
|
||||
option
|
||||
.setName('name')
|
||||
.setDescription('The name of the entry.')
|
||||
.setRequired(true)
|
||||
.setAutocomplete(true)
|
||||
),
|
||||
handler: async function (client: Client, interaction: ChatInputCommandInteraction) {
|
||||
if (!interaction.guild) return interaction.reply({ flags: [MessageFlags.Ephemeral], content: 'Must be ran in guild.' });
|
||||
|
||||
await interaction.deferReply();
|
||||
|
||||
const id = interaction.options.getInteger('name', true);
|
||||
|
||||
const item = await Database.getFromKnowledgebase(id);
|
||||
|
||||
if (!item) return interaction.editReply('Knowledgebase item not found.');
|
||||
|
||||
return interaction.editReply(item.content);
|
||||
},
|
||||
autoComplete: async function (client: Client, interaction: AutocompleteInteraction) {
|
||||
if (!interaction.guild) return interaction.respond([]);
|
||||
|
||||
const items: KnowledgebaseItem[] = await Database.getAllKnowledgebaseItems(interaction.guild.id);
|
||||
|
||||
const value = interaction.options.getFocused();
|
||||
|
||||
const toRespond = items.filter(i => !value ? true : i.content.includes(value));
|
||||
if (toRespond.length > 25) toRespond.length = 25;
|
||||
|
||||
interaction.respond(toRespond.map(p => ({
|
||||
name: p.name,
|
||||
value: p.id
|
||||
})));
|
||||
}
|
||||
import { ApplicationIntegrationType, AutocompleteInteraction, ChatInputCommandInteraction, Client, InteractionContextType, MessageFlags, PermissionFlagsBits, SlashCommandBuilder } from 'discord.js';
|
||||
import { Database } from '../shared/Database';
|
||||
import { KnowledgebaseItem } from '../types';
|
||||
|
||||
export default {
|
||||
name: 'cite',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('cite')
|
||||
.setDescription('Cite content from the knowledgebase.')
|
||||
.setIntegrationTypes(ApplicationIntegrationType.GuildInstall)
|
||||
.setContexts(InteractionContextType.Guild)
|
||||
.setDefaultMemberPermissions(PermissionFlagsBits.ManageMessages)
|
||||
.addIntegerOption(option =>
|
||||
option
|
||||
.setName('name')
|
||||
.setDescription('The name of the entry.')
|
||||
.setRequired(true)
|
||||
.setAutocomplete(true)
|
||||
),
|
||||
handler: async function (client: Client, interaction: ChatInputCommandInteraction) {
|
||||
if (!interaction.guild) return interaction.reply({ flags: [MessageFlags.Ephemeral], content: 'Must be ran in guild.' });
|
||||
|
||||
await interaction.deferReply();
|
||||
|
||||
const id = interaction.options.getInteger('name', true);
|
||||
|
||||
const item = await Database.getFromKnowledgebase(id);
|
||||
|
||||
if (!item) return interaction.editReply('Knowledgebase item not found.');
|
||||
|
||||
return interaction.editReply(item.content);
|
||||
},
|
||||
autoComplete: async function (client: Client, interaction: AutocompleteInteraction) {
|
||||
if (!interaction.guild) return interaction.respond([]);
|
||||
|
||||
const items: KnowledgebaseItem[] = await Database.getAllKnowledgebaseItems(interaction.guild.id);
|
||||
|
||||
const value = interaction.options.getFocused();
|
||||
|
||||
const toRespond = items.filter(i => !value ? true : i.content.includes(value));
|
||||
if (toRespond.length > 25) toRespond.length = 25;
|
||||
|
||||
interaction.respond(toRespond.map(p => ({
|
||||
name: p.name,
|
||||
value: p.id
|
||||
})));
|
||||
}
|
||||
};
|
||||
+41
-41
@@ -1,42 +1,42 @@
|
||||
import { ActionRowBuilder, ApplicationIntegrationType, ButtonBuilder, ButtonStyle, ChatInputCommandInteraction, Client, InteractionContextType, MessageFlags, PermissionFlagsBits, SlashCommandBuilder } from 'discord.js';
|
||||
|
||||
export default {
|
||||
name: 'devwatch',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('devwatch')
|
||||
.setDescription('Sends a dev watch role toggle button.')
|
||||
.setIntegrationTypes(ApplicationIntegrationType.GuildInstall)
|
||||
.setContexts(InteractionContextType.Guild)
|
||||
.setDefaultMemberPermissions(PermissionFlagsBits.ManageGuild)
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('content')
|
||||
.setDescription('The content of the message.')
|
||||
.setRequired(false)
|
||||
)
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('button-label')
|
||||
.setDescription('The button label.')
|
||||
.setRequired(false)
|
||||
),
|
||||
handler: async function (client: Client, interaction: ChatInputCommandInteraction) {
|
||||
if (!interaction.channel || !interaction.channel.isSendable())
|
||||
return interaction.reply({ flags: [MessageFlags.Ephemeral], content: 'Missing permissions to send to channel.' });
|
||||
|
||||
const content = interaction.options.getString('content') ?? '';
|
||||
const label = interaction.options.getString('button-label') ?? 'Toggle DevWatch Role';
|
||||
|
||||
const button = new ButtonBuilder()
|
||||
.setCustomId('dev-watch')
|
||||
.setStyle(ButtonStyle.Primary)
|
||||
.setLabel(label);
|
||||
|
||||
const row = new ActionRowBuilder<ButtonBuilder>()
|
||||
.addComponents(button);
|
||||
|
||||
await interaction.channel.send({ components: [row], content });
|
||||
|
||||
interaction.reply({ flags: [MessageFlags.Ephemeral], content: 'Sent.' });
|
||||
}
|
||||
import { ActionRowBuilder, ApplicationIntegrationType, ButtonBuilder, ButtonStyle, ChatInputCommandInteraction, Client, InteractionContextType, MessageFlags, PermissionFlagsBits, SlashCommandBuilder } from 'discord.js';
|
||||
|
||||
export default {
|
||||
name: 'devwatch',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('devwatch')
|
||||
.setDescription('Sends a dev watch role toggle button.')
|
||||
.setIntegrationTypes(ApplicationIntegrationType.GuildInstall)
|
||||
.setContexts(InteractionContextType.Guild)
|
||||
.setDefaultMemberPermissions(PermissionFlagsBits.ManageGuild)
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('content')
|
||||
.setDescription('The content of the message.')
|
||||
.setRequired(false)
|
||||
)
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('button-label')
|
||||
.setDescription('The button label.')
|
||||
.setRequired(false)
|
||||
),
|
||||
handler: async function (client: Client, interaction: ChatInputCommandInteraction) {
|
||||
if (!interaction.channel || !interaction.channel.isSendable())
|
||||
return interaction.reply({ flags: [MessageFlags.Ephemeral], content: 'Missing permissions to send to channel.' });
|
||||
|
||||
const content = interaction.options.getString('content') ?? '';
|
||||
const label = interaction.options.getString('button-label') ?? 'Toggle DevWatch Role';
|
||||
|
||||
const button = new ButtonBuilder()
|
||||
.setCustomId('dev-watch')
|
||||
.setStyle(ButtonStyle.Primary)
|
||||
.setLabel(label);
|
||||
|
||||
const row = new ActionRowBuilder<ButtonBuilder>()
|
||||
.addComponents(button);
|
||||
|
||||
await interaction.channel.send({ components: [row], content });
|
||||
|
||||
interaction.reply({ flags: [MessageFlags.Ephemeral], content: 'Sent.' });
|
||||
}
|
||||
};
|
||||
+41
-41
@@ -1,42 +1,42 @@
|
||||
import { ApplicationIntegrationType, ChatInputCommandInteraction, Client, InteractionContextType, PermissionFlagsBits, SlashCommandBuilder } from 'discord.js';
|
||||
import { config } from '../config';
|
||||
import { deferInteraction, getDiscordAlts, getE621User } from '../utils';
|
||||
|
||||
export default {
|
||||
name: 'finduser',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('finduser')
|
||||
.setDescription("Find a user's discord account based on their e621 usernamename or id.")
|
||||
.setIntegrationTypes(ApplicationIntegrationType.GuildInstall)
|
||||
.setContexts(InteractionContextType.Guild)
|
||||
.setDefaultMemberPermissions(PermissionFlagsBits.BanMembers)
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('user')
|
||||
.setDescription('The e621 username or e621 id to find the discord user of.')
|
||||
.setRequired(true)
|
||||
),
|
||||
handler: async function (client: Client, interaction: ChatInputCommandInteraction) {
|
||||
await deferInteraction(interaction);
|
||||
|
||||
if (!interaction.guild) return interaction.editReply('This command must be used in a server');
|
||||
|
||||
const user = interaction.options.getString('user', true);
|
||||
|
||||
try {
|
||||
const e621User = await getE621User(user);
|
||||
|
||||
if (!e621User) {
|
||||
return interaction.editReply('I got lost along the way. Who again?');
|
||||
}
|
||||
|
||||
const content = await getDiscordAlts(e621User.id, interaction.guild, 1, [e621User.id]);
|
||||
|
||||
interaction.editReply(`[${e621User.name}](${config.E621_BASE_URL}/users/${e621User.id})<${e621User.id}>'s e621 and discord account(s):\n${content}`);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
|
||||
interaction.editReply('I got lost in the net.');
|
||||
}
|
||||
}
|
||||
import { ApplicationIntegrationType, ChatInputCommandInteraction, Client, InteractionContextType, PermissionFlagsBits, SlashCommandBuilder } from 'discord.js';
|
||||
import { config } from '../config';
|
||||
import { deferInteraction, getDiscordAlts, getE621User } from '../utils';
|
||||
|
||||
export default {
|
||||
name: 'finduser',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('finduser')
|
||||
.setDescription("Find a user's discord account based on their e621 usernamename or id.")
|
||||
.setIntegrationTypes(ApplicationIntegrationType.GuildInstall)
|
||||
.setContexts(InteractionContextType.Guild)
|
||||
.setDefaultMemberPermissions(PermissionFlagsBits.BanMembers)
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('user')
|
||||
.setDescription('The e621 username or e621 id to find the discord user of.')
|
||||
.setRequired(true)
|
||||
),
|
||||
handler: async function (client: Client, interaction: ChatInputCommandInteraction) {
|
||||
await deferInteraction(interaction);
|
||||
|
||||
if (!interaction.guild) return interaction.editReply('This command must be used in a server');
|
||||
|
||||
const user = interaction.options.getString('user', true);
|
||||
|
||||
try {
|
||||
const e621User = await getE621User(user);
|
||||
|
||||
if (!e621User) {
|
||||
return interaction.editReply('I got lost along the way. Who again?');
|
||||
}
|
||||
|
||||
const content = await getDiscordAlts(e621User.id, interaction.guild, 1, [e621User.id]);
|
||||
|
||||
interaction.editReply(`[${e621User.name}](${config.E621_BASE_URL}/users/${e621User.id})<${e621User.id}>'s e621 and discord account(s):\n${content}`);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
|
||||
interaction.editReply('I got lost in the net.');
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1,87 +1,87 @@
|
||||
import { ApplicationIntegrationType, ChatInputCommandInteraction, Client, InteractionContextType, MessageFlags, MessageMentions, PermissionFlagsBits, SlashCommandBuilder } from 'discord.js';
|
||||
import { Database } from '../shared/Database';
|
||||
|
||||
const mentionRegex = new RegExp(MessageMentions.UsersPattern);
|
||||
|
||||
export default {
|
||||
name: 'github-mapping',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('github-mapping')
|
||||
.setDescription('Maps github users to discord ids for releases.')
|
||||
.setIntegrationTypes(ApplicationIntegrationType.GuildInstall)
|
||||
.setContexts(InteractionContextType.Guild)
|
||||
.setDefaultMemberPermissions(PermissionFlagsBits.ManageGuild)
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('add')
|
||||
.setDescription('Add a user mapping.')
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('discord-user')
|
||||
.setDescription('The discord user id, or mention, of the user.')
|
||||
.setRequired(true)
|
||||
)
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('github-name')
|
||||
.setDescription('The github username of the user (case sensitive).')
|
||||
.setRequired(true)
|
||||
)
|
||||
)
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('remove')
|
||||
.setDescription('Remove a user mapping.')
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('discord-user')
|
||||
.setDescription('The discord user id, or mention, of the user.')
|
||||
.setRequired(true)
|
||||
)
|
||||
)
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('list')
|
||||
.setDescription('List all github-discord mappings.')
|
||||
),
|
||||
handler: async function (client: Client, interaction: ChatInputCommandInteraction) {
|
||||
await interaction.deferReply({ flags: [MessageFlags.Ephemeral] });
|
||||
|
||||
const subcommand = await interaction.options.getSubcommand(true);
|
||||
|
||||
if (subcommand == 'add') {
|
||||
const discordUserInput = interaction.options.getString('discord-user', true);
|
||||
|
||||
const matches = mentionRegex.exec(discordUserInput);
|
||||
mentionRegex.lastIndex = 0;
|
||||
|
||||
const idToUse = matches ? matches.groups!.id : discordUserInput;
|
||||
|
||||
const githubName = interaction.options.getString('github-name', true);
|
||||
|
||||
const existingMappingId = await Database.getGithubFromDiscordId(idToUse);
|
||||
const existingMappingName = await Database.getDiscordIdFromGithub(githubName);
|
||||
|
||||
if (existingMappingId) return interaction.editReply(`Discord user id is already mapped to ${existingMappingId}`);
|
||||
if (existingMappingName) return interaction.editReply(`Github username is already mapped to <@${existingMappingName}> (${existingMappingName})`);
|
||||
|
||||
Database.putGithubUserMapping(idToUse, githubName);
|
||||
|
||||
return interaction.editReply('Mapping added.');
|
||||
} else if (subcommand == 'remove') {
|
||||
const discordUserInput = interaction.options.getString('discord-user', true);
|
||||
|
||||
const matches = mentionRegex.exec(discordUserInput);
|
||||
mentionRegex.lastIndex = 0;
|
||||
|
||||
const idToUse = matches ? matches.groups!.id : discordUserInput;
|
||||
|
||||
Database.removeGithubUserMapping(idToUse);
|
||||
return interaction.editReply('Mapping removed.');
|
||||
} else if (subcommand == 'list') {
|
||||
const allMappings = await Database.getAllGithubUserMappings();
|
||||
|
||||
return interaction.editReply(allMappings.map(m => `- <@${m.discord_id}> (${m.discord_id}) - ${m.github_username}`).join('\n'));
|
||||
}
|
||||
}
|
||||
import { ApplicationIntegrationType, ChatInputCommandInteraction, Client, InteractionContextType, MessageFlags, MessageMentions, PermissionFlagsBits, SlashCommandBuilder } from 'discord.js';
|
||||
import { Database } from '../shared/Database';
|
||||
|
||||
const mentionRegex = new RegExp(MessageMentions.UsersPattern);
|
||||
|
||||
export default {
|
||||
name: 'github-mapping',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('github-mapping')
|
||||
.setDescription('Maps github users to discord ids for releases.')
|
||||
.setIntegrationTypes(ApplicationIntegrationType.GuildInstall)
|
||||
.setContexts(InteractionContextType.Guild)
|
||||
.setDefaultMemberPermissions(PermissionFlagsBits.ManageGuild)
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('add')
|
||||
.setDescription('Add a user mapping.')
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('discord-user')
|
||||
.setDescription('The discord user id, or mention, of the user.')
|
||||
.setRequired(true)
|
||||
)
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('github-name')
|
||||
.setDescription('The github username of the user (case sensitive).')
|
||||
.setRequired(true)
|
||||
)
|
||||
)
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('remove')
|
||||
.setDescription('Remove a user mapping.')
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('discord-user')
|
||||
.setDescription('The discord user id, or mention, of the user.')
|
||||
.setRequired(true)
|
||||
)
|
||||
)
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('list')
|
||||
.setDescription('List all github-discord mappings.')
|
||||
),
|
||||
handler: async function (client: Client, interaction: ChatInputCommandInteraction) {
|
||||
await interaction.deferReply({ flags: [MessageFlags.Ephemeral] });
|
||||
|
||||
const subcommand = await interaction.options.getSubcommand(true);
|
||||
|
||||
if (subcommand == 'add') {
|
||||
const discordUserInput = interaction.options.getString('discord-user', true);
|
||||
|
||||
const matches = mentionRegex.exec(discordUserInput);
|
||||
mentionRegex.lastIndex = 0;
|
||||
|
||||
const idToUse = matches ? matches.groups!.id : discordUserInput;
|
||||
|
||||
const githubName = interaction.options.getString('github-name', true);
|
||||
|
||||
const existingMappingId = await Database.getGithubFromDiscordId(idToUse);
|
||||
const existingMappingName = await Database.getDiscordIdFromGithub(githubName);
|
||||
|
||||
if (existingMappingId) return interaction.editReply(`Discord user id is already mapped to ${existingMappingId}`);
|
||||
if (existingMappingName) return interaction.editReply(`Github username is already mapped to <@${existingMappingName}> (${existingMappingName})`);
|
||||
|
||||
Database.putGithubUserMapping(idToUse, githubName);
|
||||
|
||||
return interaction.editReply('Mapping added.');
|
||||
} else if (subcommand == 'remove') {
|
||||
const discordUserInput = interaction.options.getString('discord-user', true);
|
||||
|
||||
const matches = mentionRegex.exec(discordUserInput);
|
||||
mentionRegex.lastIndex = 0;
|
||||
|
||||
const idToUse = matches ? matches.groups!.id : discordUserInput;
|
||||
|
||||
Database.removeGithubUserMapping(idToUse);
|
||||
return interaction.editReply('Mapping removed.');
|
||||
} else if (subcommand == 'list') {
|
||||
const allMappings = await Database.getAllGithubUserMappings();
|
||||
|
||||
return interaction.editReply(allMappings.map(m => `- <@${m.discord_id}> (${m.discord_id}) - ${m.github_username}`).join('\n'));
|
||||
}
|
||||
}
|
||||
};
|
||||
+126
-126
@@ -1,127 +1,127 @@
|
||||
import { ApplicationIntegrationType, AutocompleteInteraction, ChatInputCommandInteraction, Client, InteractionContextType, MessageFlags, ModalBuilder, PermissionFlagsBits, SlashCommandBuilder, TextInputStyle } from 'discord.js';
|
||||
import { Database } from '../shared/Database';
|
||||
import { KnowledgebaseItem } from '../types';
|
||||
import { createTextInput, deferInteraction, logCustomEvent } from '../utils';
|
||||
|
||||
export default {
|
||||
name: 'knowledgebase',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('knowledgebase')
|
||||
.setDescription('Access the compendium of knowledge.')
|
||||
.setIntegrationTypes(ApplicationIntegrationType.GuildInstall)
|
||||
.setContexts(InteractionContextType.Guild)
|
||||
.setDefaultMemberPermissions(PermissionFlagsBits.ManageMessages)
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('add')
|
||||
.setDescription('Add to the knowledgebase.')
|
||||
)
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('remove')
|
||||
.setDescription('Purge knowledge from the universe.')
|
||||
.addIntegerOption(option =>
|
||||
option
|
||||
.setName('name')
|
||||
.setDescription('The name of the entry to remove.')
|
||||
.setRequired(true)
|
||||
.setAutocomplete(true)
|
||||
)
|
||||
)
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('edit')
|
||||
.setDescription('Edit a knowledgebase entry.')
|
||||
.addIntegerOption(option =>
|
||||
option
|
||||
.setName('name')
|
||||
.setDescription('The name of the entry to edit.')
|
||||
.setRequired(true)
|
||||
.setAutocomplete(true)
|
||||
)
|
||||
),
|
||||
handler: async function (client: Client, interaction: ChatInputCommandInteraction) {
|
||||
if (!interaction.guild) return interaction.reply({ flags: [MessageFlags.Ephemeral], content: 'Must be ran in guild.' });
|
||||
|
||||
const subcommand = interaction.options.getSubcommand(true);
|
||||
|
||||
if (subcommand == 'add') {
|
||||
const modal = new ModalBuilder()
|
||||
.setCustomId('add-knowledgebase-item-modal')
|
||||
.setTitle('Add to knowledgebase');
|
||||
|
||||
const nameLabel = createTextInput('name', 'Knowledgebase Item Name', null, true, TextInputStyle.Short, 300, 1);
|
||||
const contentLabel = createTextInput('content', 'Item Content', null, true, TextInputStyle.Paragraph, 2000, 1);
|
||||
|
||||
modal.addLabelComponents(nameLabel, contentLabel);
|
||||
|
||||
return await interaction.showModal(modal);
|
||||
} else if (subcommand == 'remove') {
|
||||
await deferInteraction(interaction);
|
||||
const id = interaction.options.getInteger('name', true);
|
||||
|
||||
const item = await Database.getFromKnowledgebase(id);
|
||||
|
||||
if (!item) return interaction.editReply('Knowledgebase item not found.');
|
||||
|
||||
logCustomEvent(interaction.guild!, {
|
||||
title: 'Knowledgebase Item Removed',
|
||||
description: null,
|
||||
color: 0xFF0000,
|
||||
timestamp: new Date(),
|
||||
fields: [
|
||||
{
|
||||
name: 'User',
|
||||
value: `<@${interaction.user.id}>\n${interaction.user.username}`,
|
||||
inline: true
|
||||
},
|
||||
{
|
||||
name: 'Name',
|
||||
value: item.name,
|
||||
inline: true
|
||||
},
|
||||
{
|
||||
name: 'Content',
|
||||
value: item.content,
|
||||
inline: true
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
await Database.removeFromKnowledgebase(id);
|
||||
|
||||
return interaction.editReply(`Removed knowledgebase entry \`${item.name}\`.`);
|
||||
} else if (subcommand == 'edit') {
|
||||
const id = interaction.options.getInteger('name', true);
|
||||
|
||||
const existingItem = await Database.getFromKnowledgebase(id);
|
||||
|
||||
if (!existingItem) return interaction.editReply('Knowledgebase item not found.');
|
||||
|
||||
const modal = new ModalBuilder()
|
||||
.setCustomId(`edit-knowledgebase-item-modal_${id}`)
|
||||
.setTitle(`Editing knowledgebase item ${existingItem.name.slice(0, 18)}`);
|
||||
|
||||
const contentLabel = createTextInput('content', 'New Content', null, true, TextInputStyle.Paragraph, 2000, 1);
|
||||
|
||||
modal.addLabelComponents(contentLabel);
|
||||
|
||||
return await interaction.showModal(modal);
|
||||
}
|
||||
},
|
||||
autoComplete: async function (client: Client, interaction: AutocompleteInteraction) {
|
||||
if (!interaction.guild) return interaction.respond([]);
|
||||
|
||||
const items: KnowledgebaseItem[] = await Database.getAllKnowledgebaseItems(interaction.guild.id);
|
||||
|
||||
const value = interaction.options.getFocused();
|
||||
|
||||
const toRespond = items.filter(i => !value ? true : i.name.includes(value));
|
||||
if (toRespond.length > 25) toRespond.length = 25;
|
||||
|
||||
interaction.respond(toRespond.map(p => ({
|
||||
name: p.name,
|
||||
value: p.id
|
||||
})));
|
||||
}
|
||||
import { ApplicationIntegrationType, AutocompleteInteraction, ChatInputCommandInteraction, Client, InteractionContextType, MessageFlags, ModalBuilder, PermissionFlagsBits, SlashCommandBuilder, TextInputStyle } from 'discord.js';
|
||||
import { Database } from '../shared/Database';
|
||||
import { KnowledgebaseItem } from '../types';
|
||||
import { createTextInput, deferInteraction, logCustomEvent } from '../utils';
|
||||
|
||||
export default {
|
||||
name: 'knowledgebase',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('knowledgebase')
|
||||
.setDescription('Access the compendium of knowledge.')
|
||||
.setIntegrationTypes(ApplicationIntegrationType.GuildInstall)
|
||||
.setContexts(InteractionContextType.Guild)
|
||||
.setDefaultMemberPermissions(PermissionFlagsBits.ManageMessages)
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('add')
|
||||
.setDescription('Add to the knowledgebase.')
|
||||
)
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('remove')
|
||||
.setDescription('Purge knowledge from the universe.')
|
||||
.addIntegerOption(option =>
|
||||
option
|
||||
.setName('name')
|
||||
.setDescription('The name of the entry to remove.')
|
||||
.setRequired(true)
|
||||
.setAutocomplete(true)
|
||||
)
|
||||
)
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('edit')
|
||||
.setDescription('Edit a knowledgebase entry.')
|
||||
.addIntegerOption(option =>
|
||||
option
|
||||
.setName('name')
|
||||
.setDescription('The name of the entry to edit.')
|
||||
.setRequired(true)
|
||||
.setAutocomplete(true)
|
||||
)
|
||||
),
|
||||
handler: async function (client: Client, interaction: ChatInputCommandInteraction) {
|
||||
if (!interaction.guild) return interaction.reply({ flags: [MessageFlags.Ephemeral], content: 'Must be ran in guild.' });
|
||||
|
||||
const subcommand = interaction.options.getSubcommand(true);
|
||||
|
||||
if (subcommand == 'add') {
|
||||
const modal = new ModalBuilder()
|
||||
.setCustomId('add-knowledgebase-item-modal')
|
||||
.setTitle('Add to knowledgebase');
|
||||
|
||||
const nameLabel = createTextInput('name', 'Knowledgebase Item Name', null, true, TextInputStyle.Short, 300, 1);
|
||||
const contentLabel = createTextInput('content', 'Item Content', null, true, TextInputStyle.Paragraph, 2000, 1);
|
||||
|
||||
modal.addLabelComponents(nameLabel, contentLabel);
|
||||
|
||||
return await interaction.showModal(modal);
|
||||
} else if (subcommand == 'remove') {
|
||||
await deferInteraction(interaction);
|
||||
const id = interaction.options.getInteger('name', true);
|
||||
|
||||
const item = await Database.getFromKnowledgebase(id);
|
||||
|
||||
if (!item) return interaction.editReply('Knowledgebase item not found.');
|
||||
|
||||
logCustomEvent(interaction.guild!, {
|
||||
title: 'Knowledgebase Item Removed',
|
||||
description: null,
|
||||
color: 0xFF0000,
|
||||
timestamp: new Date(),
|
||||
fields: [
|
||||
{
|
||||
name: 'User',
|
||||
value: `<@${interaction.user.id}>\n${interaction.user.username}`,
|
||||
inline: true
|
||||
},
|
||||
{
|
||||
name: 'Name',
|
||||
value: item.name,
|
||||
inline: true
|
||||
},
|
||||
{
|
||||
name: 'Content',
|
||||
value: item.content,
|
||||
inline: true
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
await Database.removeFromKnowledgebase(id);
|
||||
|
||||
return interaction.editReply(`Removed knowledgebase entry \`${item.name}\`.`);
|
||||
} else if (subcommand == 'edit') {
|
||||
const id = interaction.options.getInteger('name', true);
|
||||
|
||||
const existingItem = await Database.getFromKnowledgebase(id);
|
||||
|
||||
if (!existingItem) return interaction.editReply('Knowledgebase item not found.');
|
||||
|
||||
const modal = new ModalBuilder()
|
||||
.setCustomId(`edit-knowledgebase-item-modal_${id}`)
|
||||
.setTitle(`Editing knowledgebase item ${existingItem.name.slice(0, 18)}`);
|
||||
|
||||
const contentLabel = createTextInput('content', 'New Content', null, true, TextInputStyle.Paragraph, 2000, 1);
|
||||
|
||||
modal.addLabelComponents(contentLabel);
|
||||
|
||||
return await interaction.showModal(modal);
|
||||
}
|
||||
},
|
||||
autoComplete: async function (client: Client, interaction: AutocompleteInteraction) {
|
||||
if (!interaction.guild) return interaction.respond([]);
|
||||
|
||||
const items: KnowledgebaseItem[] = await Database.getAllKnowledgebaseItems(interaction.guild.id);
|
||||
|
||||
const value = interaction.options.getFocused();
|
||||
|
||||
const toRespond = items.filter(i => !value ? true : i.name.includes(value));
|
||||
if (toRespond.length > 25) toRespond.length = 25;
|
||||
|
||||
interaction.respond(toRespond.map(p => ({
|
||||
name: p.name,
|
||||
value: p.id
|
||||
})));
|
||||
}
|
||||
};
|
||||
+133
-133
@@ -1,134 +1,134 @@
|
||||
import { ApplicationIntegrationType, ChatInputCommandInteraction, Client, InteractionContextType, MessageFlags, MessageMentions, PermissionFlagsBits, SlashCommandBuilder } from 'discord.js';
|
||||
import { config } from '../config';
|
||||
import { Database } from '../shared/Database';
|
||||
import { logCustomEvent, resolveUser } from '../utils';
|
||||
|
||||
const mentionRegex = new RegExp(MessageMentions.UsersPattern);
|
||||
|
||||
export default {
|
||||
name: 'link',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('link')
|
||||
.setDescription('Manually link discord users and e621 users.')
|
||||
.setIntegrationTypes(ApplicationIntegrationType.GuildInstall)
|
||||
.setContexts(InteractionContextType.Guild)
|
||||
.setDefaultMemberPermissions(PermissionFlagsBits.ManageGuild)
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('create')
|
||||
.setDescription('Create a link.')
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('discord-user')
|
||||
.setDescription('The discord user id, or mention, of the user.')
|
||||
.setRequired(true)
|
||||
)
|
||||
.addIntegerOption(option =>
|
||||
option
|
||||
.setName('e621-id')
|
||||
.setDescription('The id of the e621 user.')
|
||||
.setRequired(true)
|
||||
)
|
||||
)
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('remove')
|
||||
.setDescription('Remove a link.')
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('discord-user')
|
||||
.setDescription('The discord user id, or mention, of the user.')
|
||||
.setRequired(true)
|
||||
)
|
||||
.addIntegerOption(option =>
|
||||
option
|
||||
.setName('e621-id')
|
||||
.setDescription('The id of the e621 user.')
|
||||
.setRequired(true)
|
||||
)
|
||||
),
|
||||
handler: async function (client: Client, interaction: ChatInputCommandInteraction) {
|
||||
await interaction.deferReply({ flags: [MessageFlags.Ephemeral] });
|
||||
|
||||
const subcommand = await interaction.options.getSubcommand(true);
|
||||
|
||||
const discordUserInput = interaction.options.getString('discord-user', true);
|
||||
|
||||
const matches = mentionRegex.exec(discordUserInput);
|
||||
mentionRegex.lastIndex = 0;
|
||||
|
||||
const idToUse = matches ? matches.groups!.id : discordUserInput;
|
||||
|
||||
const user = await resolveUser(client, idToUse, interaction.guild);
|
||||
|
||||
if (!user) return interaction.editReply('User not found.');
|
||||
|
||||
const e621Id = interaction.options.getInteger('e621-id', true);
|
||||
|
||||
if (subcommand == 'create') {
|
||||
const existingLinks = await Database.getDiscordIds(e621Id);
|
||||
|
||||
if (existingLinks.includes(user.id)) return interaction.editReply('Accounts already linked.');
|
||||
|
||||
await Database.putUser(e621Id, user);
|
||||
|
||||
await logCustomEvent(interaction.guild!, {
|
||||
title: 'Account Link Created',
|
||||
description: null,
|
||||
color: 0x00FF00,
|
||||
timestamp: new Date(),
|
||||
fields: [
|
||||
{
|
||||
name: 'Admin',
|
||||
value: `<@${interaction.user.id}>\n${interaction.user.username}`,
|
||||
inline: true
|
||||
},
|
||||
{
|
||||
name: 'Discord User',
|
||||
value: `<@${user.id}>\n${user.username}`,
|
||||
inline: true
|
||||
},
|
||||
{
|
||||
name: 'E621 User',
|
||||
value: `${config.E621_BASE_URL}/users/${e621Id}`,
|
||||
inline: true
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
interaction.editReply('Accounts linked');
|
||||
} else if (subcommand == 'remove') {
|
||||
const existingLinks = await Database.getDiscordIds(e621Id);
|
||||
|
||||
if (!existingLinks.includes(user.id)) return interaction.editReply('Accounts not linked.');
|
||||
|
||||
await Database.removeUser(e621Id, user.id);
|
||||
|
||||
await logCustomEvent(interaction.guild!, {
|
||||
title: 'Account Link Removed',
|
||||
description: null,
|
||||
color: 0x00FF00,
|
||||
timestamp: new Date(),
|
||||
fields: [
|
||||
{
|
||||
name: 'Admin',
|
||||
value: `<@${interaction.user.id}>\n${interaction.user.username}`,
|
||||
inline: true
|
||||
},
|
||||
{
|
||||
name: 'Discord User',
|
||||
value: `<@${user.id}>\n${user.username}`,
|
||||
inline: true
|
||||
},
|
||||
{
|
||||
name: 'E621 User',
|
||||
value: `${config.E621_BASE_URL}/users/${e621Id}`,
|
||||
inline: true
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
interaction.editReply('Accounts unlinked');
|
||||
}
|
||||
}
|
||||
import { ApplicationIntegrationType, ChatInputCommandInteraction, Client, InteractionContextType, MessageFlags, MessageMentions, PermissionFlagsBits, SlashCommandBuilder } from 'discord.js';
|
||||
import { config } from '../config';
|
||||
import { Database } from '../shared/Database';
|
||||
import { logCustomEvent, resolveUser } from '../utils';
|
||||
|
||||
const mentionRegex = new RegExp(MessageMentions.UsersPattern);
|
||||
|
||||
export default {
|
||||
name: 'link',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('link')
|
||||
.setDescription('Manually link discord users and e621 users.')
|
||||
.setIntegrationTypes(ApplicationIntegrationType.GuildInstall)
|
||||
.setContexts(InteractionContextType.Guild)
|
||||
.setDefaultMemberPermissions(PermissionFlagsBits.ManageGuild)
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('create')
|
||||
.setDescription('Create a link.')
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('discord-user')
|
||||
.setDescription('The discord user id, or mention, of the user.')
|
||||
.setRequired(true)
|
||||
)
|
||||
.addIntegerOption(option =>
|
||||
option
|
||||
.setName('e621-id')
|
||||
.setDescription('The id of the e621 user.')
|
||||
.setRequired(true)
|
||||
)
|
||||
)
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('remove')
|
||||
.setDescription('Remove a link.')
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('discord-user')
|
||||
.setDescription('The discord user id, or mention, of the user.')
|
||||
.setRequired(true)
|
||||
)
|
||||
.addIntegerOption(option =>
|
||||
option
|
||||
.setName('e621-id')
|
||||
.setDescription('The id of the e621 user.')
|
||||
.setRequired(true)
|
||||
)
|
||||
),
|
||||
handler: async function (client: Client, interaction: ChatInputCommandInteraction) {
|
||||
await interaction.deferReply({ flags: [MessageFlags.Ephemeral] });
|
||||
|
||||
const subcommand = await interaction.options.getSubcommand(true);
|
||||
|
||||
const discordUserInput = interaction.options.getString('discord-user', true);
|
||||
|
||||
const matches = mentionRegex.exec(discordUserInput);
|
||||
mentionRegex.lastIndex = 0;
|
||||
|
||||
const idToUse = matches ? matches.groups!.id : discordUserInput;
|
||||
|
||||
const user = await resolveUser(client, idToUse, interaction.guild);
|
||||
|
||||
if (!user) return interaction.editReply('User not found.');
|
||||
|
||||
const e621Id = interaction.options.getInteger('e621-id', true);
|
||||
|
||||
if (subcommand == 'create') {
|
||||
const existingLinks = await Database.getDiscordIds(e621Id);
|
||||
|
||||
if (existingLinks.includes(user.id)) return interaction.editReply('Accounts already linked.');
|
||||
|
||||
await Database.putUser(e621Id, user);
|
||||
|
||||
await logCustomEvent(interaction.guild!, {
|
||||
title: 'Account Link Created',
|
||||
description: null,
|
||||
color: 0x00FF00,
|
||||
timestamp: new Date(),
|
||||
fields: [
|
||||
{
|
||||
name: 'Admin',
|
||||
value: `<@${interaction.user.id}>\n${interaction.user.username}`,
|
||||
inline: true
|
||||
},
|
||||
{
|
||||
name: 'Discord User',
|
||||
value: `<@${user.id}>\n${user.username}`,
|
||||
inline: true
|
||||
},
|
||||
{
|
||||
name: 'E621 User',
|
||||
value: `${config.E621_BASE_URL}/users/${e621Id}`,
|
||||
inline: true
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
interaction.editReply('Accounts linked');
|
||||
} else if (subcommand == 'remove') {
|
||||
const existingLinks = await Database.getDiscordIds(e621Id);
|
||||
|
||||
if (!existingLinks.includes(user.id)) return interaction.editReply('Accounts not linked.');
|
||||
|
||||
await Database.removeUser(e621Id, user.id);
|
||||
|
||||
await logCustomEvent(interaction.guild!, {
|
||||
title: 'Account Link Removed',
|
||||
description: null,
|
||||
color: 0x00FF00,
|
||||
timestamp: new Date(),
|
||||
fields: [
|
||||
{
|
||||
name: 'Admin',
|
||||
value: `<@${interaction.user.id}>\n${interaction.user.username}`,
|
||||
inline: true
|
||||
},
|
||||
{
|
||||
name: 'Discord User',
|
||||
value: `<@${user.id}>\n${user.username}`,
|
||||
inline: true
|
||||
},
|
||||
{
|
||||
name: 'E621 User',
|
||||
value: `${config.E621_BASE_URL}/users/${e621Id}`,
|
||||
inline: true
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
interaction.editReply('Accounts unlinked');
|
||||
}
|
||||
}
|
||||
};
|
||||
+27
-27
@@ -1,28 +1,28 @@
|
||||
import { ApplicationIntegrationType, ChatInputCommandInteraction, Client, InteractionContextType, PermissionFlagsBits, SlashCommandBuilder } from 'discord.js';
|
||||
import { openModTicketModal } from '../utils';
|
||||
|
||||
export default {
|
||||
name: 'mod-ticket',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('mod-ticket')
|
||||
.setDescription('Opens a mod private ticket and pulls the user into it.')
|
||||
.setIntegrationTypes(ApplicationIntegrationType.GuildInstall)
|
||||
.setContexts(InteractionContextType.Guild)
|
||||
.setDefaultMemberPermissions(PermissionFlagsBits.KickMembers)
|
||||
.addUserOption(option =>
|
||||
option
|
||||
.setName('user')
|
||||
.setDescription('The user to pull in to the ticket.')
|
||||
.setRequired(true)
|
||||
),
|
||||
handler: async function (client: Client, interaction: ChatInputCommandInteraction) {
|
||||
if (!interaction.guild) return interaction.editReply('This command must be used in a server.');
|
||||
|
||||
const user = interaction.options.getUser('user', true);
|
||||
const member = await interaction.guild.members.fetch(user.id);
|
||||
|
||||
if (!member) return interaction.editReply('Could not find member.');
|
||||
|
||||
openModTicketModal(interaction, member);
|
||||
}
|
||||
import { ApplicationIntegrationType, ChatInputCommandInteraction, Client, InteractionContextType, PermissionFlagsBits, SlashCommandBuilder } from 'discord.js';
|
||||
import { openModTicketModal } from '../utils';
|
||||
|
||||
export default {
|
||||
name: 'mod-ticket',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('mod-ticket')
|
||||
.setDescription('Opens a mod private ticket and pulls the user into it.')
|
||||
.setIntegrationTypes(ApplicationIntegrationType.GuildInstall)
|
||||
.setContexts(InteractionContextType.Guild)
|
||||
.setDefaultMemberPermissions(PermissionFlagsBits.KickMembers)
|
||||
.addUserOption(option =>
|
||||
option
|
||||
.setName('user')
|
||||
.setDescription('The user to pull in to the ticket.')
|
||||
.setRequired(true)
|
||||
),
|
||||
handler: async function (client: Client, interaction: ChatInputCommandInteraction) {
|
||||
if (!interaction.guild) return interaction.editReply('This command must be used in a server.');
|
||||
|
||||
const user = interaction.options.getUser('user', true);
|
||||
const member = await interaction.guild.members.fetch(user.id);
|
||||
|
||||
if (!member) return interaction.editReply('Could not find member.');
|
||||
|
||||
openModTicketModal(interaction, member);
|
||||
}
|
||||
};
|
||||
+35
-35
@@ -1,36 +1,36 @@
|
||||
import { ApplicationIntegrationType, ChatInputCommandInteraction, Client, InteractionContextType, MessageFlags, SlashCommandBuilder } from 'discord.js';
|
||||
import { config } from '../config';
|
||||
import { syncName } from '../utils';
|
||||
|
||||
export default {
|
||||
name: 'name-sync',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('name-sync')
|
||||
.setDescription('Sync your discord nickname to your e621 name.')
|
||||
.setIntegrationTypes(ApplicationIntegrationType.GuildInstall, ApplicationIntegrationType.UserInstall)
|
||||
.setContexts(InteractionContextType.Guild, InteractionContextType.BotDM)
|
||||
.addIntegerOption(option =>
|
||||
option
|
||||
.setName('id')
|
||||
.setDescription('The id of the e621 user to sync your nickname to.')
|
||||
.setRequired(false)
|
||||
),
|
||||
handler: async function (client: Client, interaction: ChatInputCommandInteraction) {
|
||||
await interaction.deferReply({ flags: [MessageFlags.Ephemeral] });
|
||||
const id = interaction.options.getInteger('id');
|
||||
|
||||
const guild = await interaction.client.guilds.fetch(config.DISCORD_GUILD_ID!);
|
||||
|
||||
if (!guild) {
|
||||
return interaction.editReply('An error has occurred. Please try again later.');
|
||||
}
|
||||
|
||||
const member = await guild.members.fetch(interaction.user.id);
|
||||
|
||||
if (!member || !guild.members.me) {
|
||||
return interaction.editReply('An error has occurred. Please try again later.');
|
||||
}
|
||||
|
||||
await syncName(interaction, member, id);
|
||||
}
|
||||
import { ApplicationIntegrationType, ChatInputCommandInteraction, Client, InteractionContextType, MessageFlags, SlashCommandBuilder } from 'discord.js';
|
||||
import { config } from '../config';
|
||||
import { syncName } from '../utils';
|
||||
|
||||
export default {
|
||||
name: 'name-sync',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('name-sync')
|
||||
.setDescription('Sync your discord nickname to your e621 name.')
|
||||
.setIntegrationTypes(ApplicationIntegrationType.GuildInstall, ApplicationIntegrationType.UserInstall)
|
||||
.setContexts(InteractionContextType.Guild, InteractionContextType.BotDM)
|
||||
.addIntegerOption(option =>
|
||||
option
|
||||
.setName('id')
|
||||
.setDescription('The id of the e621 user to sync your nickname to.')
|
||||
.setRequired(false)
|
||||
),
|
||||
handler: async function (client: Client, interaction: ChatInputCommandInteraction) {
|
||||
await interaction.deferReply({ flags: [MessageFlags.Ephemeral] });
|
||||
const id = interaction.options.getInteger('id');
|
||||
|
||||
const guild = await interaction.client.guilds.fetch(config.DISCORD_GUILD_ID!);
|
||||
|
||||
if (!guild) {
|
||||
return interaction.editReply('An error has occurred. Please try again later.');
|
||||
}
|
||||
|
||||
const member = await guild.members.fetch(interaction.user.id);
|
||||
|
||||
if (!member || !guild.members.me) {
|
||||
return interaction.editReply('An error has occurred. Please try again later.');
|
||||
}
|
||||
|
||||
await syncName(interaction, member, id);
|
||||
}
|
||||
};
|
||||
+238
-238
@@ -1,239 +1,239 @@
|
||||
import { ApplicationIntegrationType, AutocompleteInteraction, ChatInputCommandInteraction, Client, InteractionContextType, MessageMentions, PermissionFlagsBits, SlashCommandBuilder } from 'discord.js';
|
||||
import { Database } from '../shared/Database';
|
||||
import { deferInteraction, logCustomEvent, resolveUser } from '../utils';
|
||||
import { getNoteMessage } from '../utils/note-utils';
|
||||
|
||||
const mentionRegex = new RegExp(MessageMentions.UsersPattern);
|
||||
|
||||
export default {
|
||||
name: 'notes',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('notes')
|
||||
.setDescription('Add, view, or remove user notes.')
|
||||
.setIntegrationTypes(ApplicationIntegrationType.GuildInstall)
|
||||
.setContexts(InteractionContextType.Guild)
|
||||
.setDefaultMemberPermissions(PermissionFlagsBits.BanMembers)
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('add')
|
||||
.setDescription('Add notes to a user.')
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('user')
|
||||
.setDescription('The discord user mention, or ID, to add a note to.')
|
||||
.setRequired(true)
|
||||
)
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('reason')
|
||||
.setDescription('The reason for the note.')
|
||||
.setRequired(true)
|
||||
)
|
||||
)
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('edit')
|
||||
.setDescription('Edit notes on a user.')
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('user')
|
||||
.setDescription('The discord user mention, or ID, to edit the notes of.')
|
||||
.setRequired(true)
|
||||
)
|
||||
.addIntegerOption(option =>
|
||||
option
|
||||
.setName('note')
|
||||
.setDescription('The note to edit.')
|
||||
.setRequired(true)
|
||||
.setAutocomplete(true)
|
||||
)
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('new-reason')
|
||||
.setDescription('The new reason for the note.')
|
||||
.setRequired(true)
|
||||
)
|
||||
)
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('remove')
|
||||
.setDescription('Remove notes from a user.')
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('user')
|
||||
.setDescription('The discord user mention, or ID, to remove a note from.')
|
||||
.setRequired(true)
|
||||
)
|
||||
.addIntegerOption(option =>
|
||||
option
|
||||
.setName('note')
|
||||
.setDescription('The note to remove.')
|
||||
.setRequired(true)
|
||||
.setAutocomplete(true)
|
||||
)
|
||||
)
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('list')
|
||||
.setDescription("List a user's notes")
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('user')
|
||||
.setDescription('The discord user mention, or ID, to list the notes of.')
|
||||
.setRequired(true)
|
||||
)
|
||||
),
|
||||
handler: async function (client: Client, interaction: ChatInputCommandInteraction) {
|
||||
const subcommand = interaction.options.getSubcommand(true);
|
||||
|
||||
const input = interaction.options.getString('user', true);
|
||||
|
||||
const matches = mentionRegex.exec(input);
|
||||
mentionRegex.lastIndex = 0;
|
||||
|
||||
const idToUse = matches ? matches.groups!.id : input;
|
||||
|
||||
await deferInteraction(interaction);
|
||||
|
||||
const user = await resolveUser(client, idToUse, interaction.guild);
|
||||
|
||||
if (!user) return interaction.editReply('User not found.');
|
||||
|
||||
if (subcommand == 'add') {
|
||||
const reason = interaction.options.getString('reason', true);
|
||||
|
||||
logCustomEvent(interaction.guild!, {
|
||||
title: 'Note Added',
|
||||
description: null,
|
||||
color: 0x00FF00,
|
||||
timestamp: new Date(),
|
||||
fields: [
|
||||
{
|
||||
name: 'Moderator',
|
||||
value: `<@${interaction.user.id}>\n${interaction.user.username}`,
|
||||
inline: true
|
||||
},
|
||||
{
|
||||
name: 'User',
|
||||
value: `<@${user.id}>\n${user.username}`,
|
||||
inline: true
|
||||
},
|
||||
{
|
||||
name: 'Note',
|
||||
value: reason,
|
||||
inline: true
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
await Database.putNote(user.id, reason, interaction.user.id);
|
||||
|
||||
interaction.editReply(`Note added to <@${user.id}> (\`${user.username}\` | \`${user.id}\`).\n\nReason:\n${reason}`);
|
||||
} else if (subcommand == 'remove') {
|
||||
const noteId = interaction.options.getInteger('note', true);
|
||||
|
||||
const notes = await Database.getNotes(user.id);
|
||||
const note = notes.find(n => n.id == noteId);
|
||||
|
||||
if (!note) return interaction.editReply('Note not found.');
|
||||
|
||||
logCustomEvent(interaction.guild!, {
|
||||
title: 'Note Removed',
|
||||
description: null,
|
||||
color: 0xFF0000,
|
||||
timestamp: new Date(),
|
||||
fields: [
|
||||
{
|
||||
name: 'Moderator',
|
||||
value: `<@${interaction.user.id}>\n${interaction.user.username}`,
|
||||
inline: true
|
||||
},
|
||||
{
|
||||
name: 'User',
|
||||
value: `<@${user.id}>\n${user.username}`,
|
||||
inline: true
|
||||
},
|
||||
{
|
||||
name: 'Note',
|
||||
value: `${note.reason}\nBy: <@${note.mod_id}>`,
|
||||
inline: true
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
await Database.removeNote(noteId);
|
||||
interaction.editReply('Removed note.');
|
||||
} else if (subcommand == 'edit') {
|
||||
const noteId = interaction.options.getInteger('note', true);
|
||||
|
||||
const notes = await Database.getNotes(user.id);
|
||||
const note = notes.find(n => n.id == noteId);
|
||||
|
||||
if (!note) return interaction.editReply('Note not found.');
|
||||
|
||||
const reason = interaction.options.getString('new-reason', true);
|
||||
|
||||
logCustomEvent(interaction.guild!, {
|
||||
title: 'Note Edited',
|
||||
description: null,
|
||||
color: 0x00FF00,
|
||||
timestamp: new Date(),
|
||||
fields: [
|
||||
{
|
||||
name: 'Moderator',
|
||||
value: `<@${interaction.user.id}>\n${interaction.user.username}`,
|
||||
inline: true
|
||||
},
|
||||
{
|
||||
name: 'User',
|
||||
value: `<@${user.id}>\n${user.username}`,
|
||||
inline: true
|
||||
},
|
||||
{
|
||||
name: 'Old reason',
|
||||
value: note.reason
|
||||
},
|
||||
{
|
||||
name: 'New reason',
|
||||
value: reason,
|
||||
inline: true
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
await Database.editNote(noteId, note.reason, reason, interaction.user.id);
|
||||
|
||||
interaction.editReply(`Note on <@${user.id}> (\`${user.username}\` | \`${user.id}\`) edited.\n\nNew reason:\n${reason}`);
|
||||
} else if (subcommand == 'list') {
|
||||
const noteMessage = await getNoteMessage(user.id, 1);
|
||||
|
||||
if (!noteMessage) return interaction.editReply(`No notes found for <@${user.id}> (\`${user.username}\` | \`${user.id}\`)`);
|
||||
|
||||
interaction.editReply(noteMessage);
|
||||
}
|
||||
},
|
||||
autoComplete: async function (client: Client, interaction: AutocompleteInteraction) {
|
||||
const input = interaction.options.getString('user', true);
|
||||
|
||||
const matches = mentionRegex.exec(input);
|
||||
mentionRegex.lastIndex = 0;
|
||||
|
||||
const idToUse = matches ? matches.groups!.id : input;
|
||||
|
||||
if (!idToUse) return interaction.respond([]);
|
||||
|
||||
const value = interaction.options.getFocused().toLowerCase();
|
||||
|
||||
const notes = await Database.getNotes(idToUse);
|
||||
|
||||
const toRespond = notes.filter(w => !value ? true : w.reason.toLowerCase().includes(value));
|
||||
if (toRespond.length > 25) toRespond.length = 25;
|
||||
|
||||
interaction.respond(toRespond.map((w) => {
|
||||
return {
|
||||
name: w.reason.substring(0, 50),
|
||||
value: w.id
|
||||
};
|
||||
}));
|
||||
}
|
||||
import { ApplicationIntegrationType, AutocompleteInteraction, ChatInputCommandInteraction, Client, InteractionContextType, MessageMentions, PermissionFlagsBits, SlashCommandBuilder } from 'discord.js';
|
||||
import { Database } from '../shared/Database';
|
||||
import { deferInteraction, logCustomEvent, resolveUser } from '../utils';
|
||||
import { getNoteMessage } from '../utils/note-utils';
|
||||
|
||||
const mentionRegex = new RegExp(MessageMentions.UsersPattern);
|
||||
|
||||
export default {
|
||||
name: 'notes',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('notes')
|
||||
.setDescription('Add, view, or remove user notes.')
|
||||
.setIntegrationTypes(ApplicationIntegrationType.GuildInstall)
|
||||
.setContexts(InteractionContextType.Guild)
|
||||
.setDefaultMemberPermissions(PermissionFlagsBits.BanMembers)
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('add')
|
||||
.setDescription('Add notes to a user.')
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('user')
|
||||
.setDescription('The discord user mention, or ID, to add a note to.')
|
||||
.setRequired(true)
|
||||
)
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('reason')
|
||||
.setDescription('The reason for the note.')
|
||||
.setRequired(true)
|
||||
)
|
||||
)
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('edit')
|
||||
.setDescription('Edit notes on a user.')
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('user')
|
||||
.setDescription('The discord user mention, or ID, to edit the notes of.')
|
||||
.setRequired(true)
|
||||
)
|
||||
.addIntegerOption(option =>
|
||||
option
|
||||
.setName('note')
|
||||
.setDescription('The note to edit.')
|
||||
.setRequired(true)
|
||||
.setAutocomplete(true)
|
||||
)
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('new-reason')
|
||||
.setDescription('The new reason for the note.')
|
||||
.setRequired(true)
|
||||
)
|
||||
)
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('remove')
|
||||
.setDescription('Remove notes from a user.')
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('user')
|
||||
.setDescription('The discord user mention, or ID, to remove a note from.')
|
||||
.setRequired(true)
|
||||
)
|
||||
.addIntegerOption(option =>
|
||||
option
|
||||
.setName('note')
|
||||
.setDescription('The note to remove.')
|
||||
.setRequired(true)
|
||||
.setAutocomplete(true)
|
||||
)
|
||||
)
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('list')
|
||||
.setDescription("List a user's notes")
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('user')
|
||||
.setDescription('The discord user mention, or ID, to list the notes of.')
|
||||
.setRequired(true)
|
||||
)
|
||||
),
|
||||
handler: async function (client: Client, interaction: ChatInputCommandInteraction) {
|
||||
const subcommand = interaction.options.getSubcommand(true);
|
||||
|
||||
const input = interaction.options.getString('user', true);
|
||||
|
||||
const matches = mentionRegex.exec(input);
|
||||
mentionRegex.lastIndex = 0;
|
||||
|
||||
const idToUse = matches ? matches.groups!.id : input;
|
||||
|
||||
await deferInteraction(interaction);
|
||||
|
||||
const user = await resolveUser(client, idToUse, interaction.guild);
|
||||
|
||||
if (!user) return interaction.editReply('User not found.');
|
||||
|
||||
if (subcommand == 'add') {
|
||||
const reason = interaction.options.getString('reason', true);
|
||||
|
||||
logCustomEvent(interaction.guild!, {
|
||||
title: 'Note Added',
|
||||
description: null,
|
||||
color: 0x00FF00,
|
||||
timestamp: new Date(),
|
||||
fields: [
|
||||
{
|
||||
name: 'Moderator',
|
||||
value: `<@${interaction.user.id}>\n${interaction.user.username}`,
|
||||
inline: true
|
||||
},
|
||||
{
|
||||
name: 'User',
|
||||
value: `<@${user.id}>\n${user.username}`,
|
||||
inline: true
|
||||
},
|
||||
{
|
||||
name: 'Note',
|
||||
value: reason,
|
||||
inline: true
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
await Database.putNote(user.id, reason, interaction.user.id);
|
||||
|
||||
interaction.editReply(`Note added to <@${user.id}> (\`${user.username}\` | \`${user.id}\`).\n\nReason:\n${reason}`);
|
||||
} else if (subcommand == 'remove') {
|
||||
const noteId = interaction.options.getInteger('note', true);
|
||||
|
||||
const notes = await Database.getNotes(user.id);
|
||||
const note = notes.find(n => n.id == noteId);
|
||||
|
||||
if (!note) return interaction.editReply('Note not found.');
|
||||
|
||||
logCustomEvent(interaction.guild!, {
|
||||
title: 'Note Removed',
|
||||
description: null,
|
||||
color: 0xFF0000,
|
||||
timestamp: new Date(),
|
||||
fields: [
|
||||
{
|
||||
name: 'Moderator',
|
||||
value: `<@${interaction.user.id}>\n${interaction.user.username}`,
|
||||
inline: true
|
||||
},
|
||||
{
|
||||
name: 'User',
|
||||
value: `<@${user.id}>\n${user.username}`,
|
||||
inline: true
|
||||
},
|
||||
{
|
||||
name: 'Note',
|
||||
value: `${note.reason}\nBy: <@${note.mod_id}>`,
|
||||
inline: true
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
await Database.removeNote(noteId);
|
||||
interaction.editReply('Removed note.');
|
||||
} else if (subcommand == 'edit') {
|
||||
const noteId = interaction.options.getInteger('note', true);
|
||||
|
||||
const notes = await Database.getNotes(user.id);
|
||||
const note = notes.find(n => n.id == noteId);
|
||||
|
||||
if (!note) return interaction.editReply('Note not found.');
|
||||
|
||||
const reason = interaction.options.getString('new-reason', true);
|
||||
|
||||
logCustomEvent(interaction.guild!, {
|
||||
title: 'Note Edited',
|
||||
description: null,
|
||||
color: 0x00FF00,
|
||||
timestamp: new Date(),
|
||||
fields: [
|
||||
{
|
||||
name: 'Moderator',
|
||||
value: `<@${interaction.user.id}>\n${interaction.user.username}`,
|
||||
inline: true
|
||||
},
|
||||
{
|
||||
name: 'User',
|
||||
value: `<@${user.id}>\n${user.username}`,
|
||||
inline: true
|
||||
},
|
||||
{
|
||||
name: 'Old reason',
|
||||
value: note.reason
|
||||
},
|
||||
{
|
||||
name: 'New reason',
|
||||
value: reason,
|
||||
inline: true
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
await Database.editNote(noteId, note.reason, reason, interaction.user.id);
|
||||
|
||||
interaction.editReply(`Note on <@${user.id}> (\`${user.username}\` | \`${user.id}\`) edited.\n\nNew reason:\n${reason}`);
|
||||
} else if (subcommand == 'list') {
|
||||
const noteMessage = await getNoteMessage(user.id, 1);
|
||||
|
||||
if (!noteMessage) return interaction.editReply(`No notes found for <@${user.id}> (\`${user.username}\` | \`${user.id}\`)`);
|
||||
|
||||
interaction.editReply(noteMessage);
|
||||
}
|
||||
},
|
||||
autoComplete: async function (client: Client, interaction: AutocompleteInteraction) {
|
||||
const input = interaction.options.getString('user', true);
|
||||
|
||||
const matches = mentionRegex.exec(input);
|
||||
mentionRegex.lastIndex = 0;
|
||||
|
||||
const idToUse = matches ? matches.groups!.id : input;
|
||||
|
||||
if (!idToUse) return interaction.respond([]);
|
||||
|
||||
const value = interaction.options.getFocused().toLowerCase();
|
||||
|
||||
const notes = await Database.getNotes(idToUse);
|
||||
|
||||
const toRespond = notes.filter(w => !value ? true : w.reason.toLowerCase().includes(value));
|
||||
if (toRespond.length > 25) toRespond.length = 25;
|
||||
|
||||
interaction.respond(toRespond.map((w) => {
|
||||
return {
|
||||
name: w.reason.substring(0, 50),
|
||||
value: w.id
|
||||
};
|
||||
}));
|
||||
}
|
||||
};
|
||||
+251
-251
@@ -1,252 +1,252 @@
|
||||
import { ApplicationIntegrationType, AutocompleteInteraction, ChatInputCommandInteraction, Client, InteractionContextType, PermissionFlagsBits, SlashCommandBuilder, User } from 'discord.js';
|
||||
import { Database } from '../shared/Database';
|
||||
import { TicketPhrase } from '../types';
|
||||
import { logCustomEvent } from '../utils';
|
||||
|
||||
const MIN_PHRASE_LENGTH = 1;
|
||||
const MAX_PHRASE_LENGTH = 512;
|
||||
|
||||
type SubcommandGroup = 'admin' | 'personal';
|
||||
type Subcommand = 'add' | 'remove' | 'list' | 'dump' | 'purge';
|
||||
|
||||
export default {
|
||||
name: 'phrases',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('phrases')
|
||||
.setDescription('Manage notified phrases.')
|
||||
.setIntegrationTypes(ApplicationIntegrationType.GuildInstall)
|
||||
.setContexts(InteractionContextType.Guild)
|
||||
.setDefaultMemberPermissions(PermissionFlagsBits.BanMembers)
|
||||
.addSubcommandGroup(subcommandGroup =>
|
||||
subcommandGroup
|
||||
.setName('admin')
|
||||
.setDescription('Manage admin notified phrases.')
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('add')
|
||||
.setDescription('Add an admin notification phrase.')
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('phrase')
|
||||
.setDescription('The phrase to add.')
|
||||
.setRequired(true)
|
||||
.setMinLength(MIN_PHRASE_LENGTH)
|
||||
.setMaxLength(MAX_PHRASE_LENGTH)
|
||||
)
|
||||
)
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('remove')
|
||||
.setDescription('Remove an admin notification phrase.')
|
||||
.addNumberOption(option =>
|
||||
option
|
||||
.setName('phrase')
|
||||
.setDescription('The phrase to remove.')
|
||||
.setRequired(true)
|
||||
.setAutocomplete(true)
|
||||
)
|
||||
)
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('list')
|
||||
.setDescription('Get a list of the current admin notification phrases.')
|
||||
)
|
||||
)
|
||||
.addSubcommandGroup(subcommandGroup =>
|
||||
subcommandGroup
|
||||
.setName('personal')
|
||||
.setDescription('Manage personal notified phrases.')
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('add')
|
||||
.setDescription('Add a personal notification phrase.')
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('phrase')
|
||||
.setDescription('The phrase to add.')
|
||||
.setRequired(true)
|
||||
.setMinLength(MIN_PHRASE_LENGTH)
|
||||
.setMaxLength(MAX_PHRASE_LENGTH)
|
||||
)
|
||||
)
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('remove')
|
||||
.setDescription('Remove a personal notification phrase.')
|
||||
.addNumberOption(option =>
|
||||
option
|
||||
.setName('phrase')
|
||||
.setDescription('The phrase to remove.')
|
||||
.setRequired(true)
|
||||
.setAutocomplete(true)
|
||||
)
|
||||
)
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('list')
|
||||
.setDescription('Get a list of the current personal notification phrases.')
|
||||
)
|
||||
)
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('dump')
|
||||
.setDescription('List all notification phrases.')
|
||||
)
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('purge')
|
||||
.setDescription("Purge a user's phrases.")
|
||||
.addUserOption(option =>
|
||||
option
|
||||
.setName('user')
|
||||
.setDescription('The user to purge the phrases of.')
|
||||
.setRequired(true)
|
||||
)
|
||||
),
|
||||
handler: async function (client: Client, interaction: ChatInputCommandInteraction) {
|
||||
const subcommandGroup: SubcommandGroup | null = interaction.options.getSubcommandGroup() as SubcommandGroup;
|
||||
const subcommand: Subcommand | null = interaction.options.getSubcommand() as Subcommand;
|
||||
|
||||
switch (subcommand) {
|
||||
case 'add':
|
||||
return addPhrase(interaction, interaction.options.getString('phrase', true), subcommandGroup!);
|
||||
case 'remove':
|
||||
return removePhrase(interaction, interaction.options.getNumber('phrase', true), subcommandGroup!);
|
||||
case 'list':
|
||||
return listPhrases(interaction, subcommandGroup!);
|
||||
case 'dump':
|
||||
return dumpPhrases(interaction);
|
||||
case 'purge':
|
||||
return purgePhrases(interaction, interaction.options.getUser('user', true));
|
||||
}
|
||||
},
|
||||
autoComplete: async function (client: Client, interaction: AutocompleteInteraction) {
|
||||
const subcommandGroup: SubcommandGroup | null = interaction.options.getSubcommandGroup() as SubcommandGroup;
|
||||
|
||||
if (!subcommandGroup) return interaction.respond([]);
|
||||
|
||||
const value = interaction.options.getFocused();
|
||||
|
||||
const phrases: TicketPhrase[] = await Database.getTicketPhrasesFor(subcommandGroup == 'admin' ? 'admin' : interaction.user.id);
|
||||
|
||||
const toRespond = phrases.filter(p => !value ? true : p.phrase.includes(value));
|
||||
if (toRespond.length > 25) toRespond.length = 25;
|
||||
|
||||
interaction.respond(toRespond.map(p => ({
|
||||
name: p.phrase,
|
||||
value: p.id
|
||||
})));
|
||||
}
|
||||
};
|
||||
|
||||
async function purgePhrases(interaction: ChatInputCommandInteraction, user: User) {
|
||||
const phrases: TicketPhrase[] = await Database.getTicketPhrasesFor(user.id);
|
||||
|
||||
const count = await Database.removeAllTicketPhrasesFor(user.id);
|
||||
|
||||
logCustomEvent(interaction.guild!, {
|
||||
title: 'Ticket Phrases Purged',
|
||||
description: null,
|
||||
color: 0xFF0000,
|
||||
timestamp: new Date(),
|
||||
fields: [
|
||||
{
|
||||
name: 'User',
|
||||
value: `<@${interaction.user.id}>\n${interaction.user.username}`,
|
||||
inline: true
|
||||
},
|
||||
{
|
||||
name: 'Target User',
|
||||
value: `<@${user.id}>\n${user.username}`,
|
||||
inline: true
|
||||
},
|
||||
{
|
||||
name: 'Count',
|
||||
value: count.toString(),
|
||||
inline: true
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
interaction.reply(`Purged the following phrases (${count}):\n${phrases.map(p => `- \`${p.phrase}\``).join('\n')}`);
|
||||
}
|
||||
|
||||
async function dumpPhrases(interaction: ChatInputCommandInteraction) {
|
||||
let content = '';
|
||||
|
||||
const guildSettings = await Database.getGuildSettings(interaction.guildId!);
|
||||
|
||||
await Database.getAllTicketPhrases((phrase: TicketPhrase) => {
|
||||
if (phrase.user_id == 'admin' && (!guildSettings || !guildSettings.admin_role_id)) return;
|
||||
|
||||
const mention = phrase.user_id == 'admin' ? `<@&${guildSettings?.admin_role_id}>` : `<@${phrase.user_id}>`;
|
||||
|
||||
content += `${mention}: \`${phrase.phrase}\`\n`;
|
||||
});
|
||||
|
||||
if (content.length == 0) return interaction.reply('No phrases found.');
|
||||
|
||||
interaction.reply('The following phrases are registered:\n\n' + content);
|
||||
}
|
||||
|
||||
async function addPhrase(interaction: ChatInputCommandInteraction, phrase: string, group: SubcommandGroup) {
|
||||
await Database.putTicketPhrase(group == 'admin' ? 'admin' : interaction.user.id, phrase);
|
||||
|
||||
logCustomEvent(interaction.guild!, {
|
||||
title: `${group == 'admin' ? 'Admin ' : ''}Ticket Phrase Added`,
|
||||
description: null,
|
||||
color: 0x00FF00,
|
||||
timestamp: new Date(),
|
||||
fields: [
|
||||
{
|
||||
name: 'User',
|
||||
value: `<@${interaction.user.id}>\n${interaction.user.username}`,
|
||||
inline: true
|
||||
},
|
||||
{
|
||||
name: 'Phrase',
|
||||
value: phrase,
|
||||
inline: true
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
interaction.reply(`Phrases matching "${phrase}" will now alert ${group == 'admin' ? 'admins' : 'you'}.`);
|
||||
}
|
||||
|
||||
async function removePhrase(interaction: ChatInputCommandInteraction, phraseId: number, group: SubcommandGroup) {
|
||||
const phrase = await Database.getTicketPhrase(phraseId);
|
||||
|
||||
if (!phrase) return interaction.reply('Phrase not found');
|
||||
|
||||
await Database.removeTicketPhrase(phraseId);
|
||||
|
||||
logCustomEvent(interaction.guild!, {
|
||||
title: `${group == 'admin' ? 'Admin ' : ''}Ticket Phrase Removed`,
|
||||
description: null,
|
||||
color: 0xFF0000,
|
||||
timestamp: new Date(),
|
||||
fields: [
|
||||
{
|
||||
name: 'User',
|
||||
value: `<@${interaction.user.id}>\n${interaction.user.username}`,
|
||||
inline: true
|
||||
},
|
||||
{
|
||||
name: 'Phrase',
|
||||
value: phrase.phrase,
|
||||
inline: true
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
interaction.reply(`Phrase will no longer alert ${group == 'admin' ? 'admins' : 'you'}.`);
|
||||
}
|
||||
|
||||
async function listPhrases(interaction: ChatInputCommandInteraction, group: SubcommandGroup) {
|
||||
const phrases = await Database.getTicketPhrasesFor(group == 'admin' ? 'admin' : interaction.user.id);
|
||||
|
||||
if (phrases.length == 0) return interaction.reply('No phrases registered');
|
||||
|
||||
interaction.reply(`The following phrases are registered:\n\n${phrases.map(p => (`- \`${p.phrase}\``)).join('\n')}`);
|
||||
import { ApplicationIntegrationType, AutocompleteInteraction, ChatInputCommandInteraction, Client, InteractionContextType, PermissionFlagsBits, SlashCommandBuilder, User } from 'discord.js';
|
||||
import { Database } from '../shared/Database';
|
||||
import { TicketPhrase } from '../types';
|
||||
import { logCustomEvent } from '../utils';
|
||||
|
||||
const MIN_PHRASE_LENGTH = 1;
|
||||
const MAX_PHRASE_LENGTH = 512;
|
||||
|
||||
type SubcommandGroup = 'admin' | 'personal';
|
||||
type Subcommand = 'add' | 'remove' | 'list' | 'dump' | 'purge';
|
||||
|
||||
export default {
|
||||
name: 'phrases',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('phrases')
|
||||
.setDescription('Manage notified phrases.')
|
||||
.setIntegrationTypes(ApplicationIntegrationType.GuildInstall)
|
||||
.setContexts(InteractionContextType.Guild)
|
||||
.setDefaultMemberPermissions(PermissionFlagsBits.BanMembers)
|
||||
.addSubcommandGroup(subcommandGroup =>
|
||||
subcommandGroup
|
||||
.setName('admin')
|
||||
.setDescription('Manage admin notified phrases.')
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('add')
|
||||
.setDescription('Add an admin notification phrase.')
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('phrase')
|
||||
.setDescription('The phrase to add.')
|
||||
.setRequired(true)
|
||||
.setMinLength(MIN_PHRASE_LENGTH)
|
||||
.setMaxLength(MAX_PHRASE_LENGTH)
|
||||
)
|
||||
)
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('remove')
|
||||
.setDescription('Remove an admin notification phrase.')
|
||||
.addNumberOption(option =>
|
||||
option
|
||||
.setName('phrase')
|
||||
.setDescription('The phrase to remove.')
|
||||
.setRequired(true)
|
||||
.setAutocomplete(true)
|
||||
)
|
||||
)
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('list')
|
||||
.setDescription('Get a list of the current admin notification phrases.')
|
||||
)
|
||||
)
|
||||
.addSubcommandGroup(subcommandGroup =>
|
||||
subcommandGroup
|
||||
.setName('personal')
|
||||
.setDescription('Manage personal notified phrases.')
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('add')
|
||||
.setDescription('Add a personal notification phrase.')
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('phrase')
|
||||
.setDescription('The phrase to add.')
|
||||
.setRequired(true)
|
||||
.setMinLength(MIN_PHRASE_LENGTH)
|
||||
.setMaxLength(MAX_PHRASE_LENGTH)
|
||||
)
|
||||
)
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('remove')
|
||||
.setDescription('Remove a personal notification phrase.')
|
||||
.addNumberOption(option =>
|
||||
option
|
||||
.setName('phrase')
|
||||
.setDescription('The phrase to remove.')
|
||||
.setRequired(true)
|
||||
.setAutocomplete(true)
|
||||
)
|
||||
)
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('list')
|
||||
.setDescription('Get a list of the current personal notification phrases.')
|
||||
)
|
||||
)
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('dump')
|
||||
.setDescription('List all notification phrases.')
|
||||
)
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('purge')
|
||||
.setDescription("Purge a user's phrases.")
|
||||
.addUserOption(option =>
|
||||
option
|
||||
.setName('user')
|
||||
.setDescription('The user to purge the phrases of.')
|
||||
.setRequired(true)
|
||||
)
|
||||
),
|
||||
handler: async function (client: Client, interaction: ChatInputCommandInteraction) {
|
||||
const subcommandGroup: SubcommandGroup | null = interaction.options.getSubcommandGroup() as SubcommandGroup;
|
||||
const subcommand: Subcommand | null = interaction.options.getSubcommand() as Subcommand;
|
||||
|
||||
switch (subcommand) {
|
||||
case 'add':
|
||||
return addPhrase(interaction, interaction.options.getString('phrase', true), subcommandGroup!);
|
||||
case 'remove':
|
||||
return removePhrase(interaction, interaction.options.getNumber('phrase', true), subcommandGroup!);
|
||||
case 'list':
|
||||
return listPhrases(interaction, subcommandGroup!);
|
||||
case 'dump':
|
||||
return dumpPhrases(interaction);
|
||||
case 'purge':
|
||||
return purgePhrases(interaction, interaction.options.getUser('user', true));
|
||||
}
|
||||
},
|
||||
autoComplete: async function (client: Client, interaction: AutocompleteInteraction) {
|
||||
const subcommandGroup: SubcommandGroup | null = interaction.options.getSubcommandGroup() as SubcommandGroup;
|
||||
|
||||
if (!subcommandGroup) return interaction.respond([]);
|
||||
|
||||
const value = interaction.options.getFocused();
|
||||
|
||||
const phrases: TicketPhrase[] = await Database.getTicketPhrasesFor(subcommandGroup == 'admin' ? 'admin' : interaction.user.id);
|
||||
|
||||
const toRespond = phrases.filter(p => !value ? true : p.phrase.includes(value));
|
||||
if (toRespond.length > 25) toRespond.length = 25;
|
||||
|
||||
interaction.respond(toRespond.map(p => ({
|
||||
name: p.phrase,
|
||||
value: p.id
|
||||
})));
|
||||
}
|
||||
};
|
||||
|
||||
async function purgePhrases(interaction: ChatInputCommandInteraction, user: User) {
|
||||
const phrases: TicketPhrase[] = await Database.getTicketPhrasesFor(user.id);
|
||||
|
||||
const count = await Database.removeAllTicketPhrasesFor(user.id);
|
||||
|
||||
logCustomEvent(interaction.guild!, {
|
||||
title: 'Ticket Phrases Purged',
|
||||
description: null,
|
||||
color: 0xFF0000,
|
||||
timestamp: new Date(),
|
||||
fields: [
|
||||
{
|
||||
name: 'User',
|
||||
value: `<@${interaction.user.id}>\n${interaction.user.username}`,
|
||||
inline: true
|
||||
},
|
||||
{
|
||||
name: 'Target User',
|
||||
value: `<@${user.id}>\n${user.username}`,
|
||||
inline: true
|
||||
},
|
||||
{
|
||||
name: 'Count',
|
||||
value: count.toString(),
|
||||
inline: true
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
interaction.reply(`Purged the following phrases (${count}):\n${phrases.map(p => `- \`${p.phrase}\``).join('\n')}`);
|
||||
}
|
||||
|
||||
async function dumpPhrases(interaction: ChatInputCommandInteraction) {
|
||||
let content = '';
|
||||
|
||||
const guildSettings = await Database.getGuildSettings(interaction.guildId!);
|
||||
|
||||
await Database.getAllTicketPhrases((phrase: TicketPhrase) => {
|
||||
if (phrase.user_id == 'admin' && (!guildSettings || !guildSettings.admin_role_id)) return;
|
||||
|
||||
const mention = phrase.user_id == 'admin' ? `<@&${guildSettings?.admin_role_id}>` : `<@${phrase.user_id}>`;
|
||||
|
||||
content += `${mention}: \`${phrase.phrase}\`\n`;
|
||||
});
|
||||
|
||||
if (content.length == 0) return interaction.reply('No phrases found.');
|
||||
|
||||
interaction.reply('The following phrases are registered:\n\n' + content);
|
||||
}
|
||||
|
||||
async function addPhrase(interaction: ChatInputCommandInteraction, phrase: string, group: SubcommandGroup) {
|
||||
await Database.putTicketPhrase(group == 'admin' ? 'admin' : interaction.user.id, phrase);
|
||||
|
||||
logCustomEvent(interaction.guild!, {
|
||||
title: `${group == 'admin' ? 'Admin ' : ''}Ticket Phrase Added`,
|
||||
description: null,
|
||||
color: 0x00FF00,
|
||||
timestamp: new Date(),
|
||||
fields: [
|
||||
{
|
||||
name: 'User',
|
||||
value: `<@${interaction.user.id}>\n${interaction.user.username}`,
|
||||
inline: true
|
||||
},
|
||||
{
|
||||
name: 'Phrase',
|
||||
value: phrase,
|
||||
inline: true
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
interaction.reply(`Phrases matching "${phrase}" will now alert ${group == 'admin' ? 'admins' : 'you'}.`);
|
||||
}
|
||||
|
||||
async function removePhrase(interaction: ChatInputCommandInteraction, phraseId: number, group: SubcommandGroup) {
|
||||
const phrase = await Database.getTicketPhrase(phraseId);
|
||||
|
||||
if (!phrase) return interaction.reply('Phrase not found');
|
||||
|
||||
await Database.removeTicketPhrase(phraseId);
|
||||
|
||||
logCustomEvent(interaction.guild!, {
|
||||
title: `${group == 'admin' ? 'Admin ' : ''}Ticket Phrase Removed`,
|
||||
description: null,
|
||||
color: 0xFF0000,
|
||||
timestamp: new Date(),
|
||||
fields: [
|
||||
{
|
||||
name: 'User',
|
||||
value: `<@${interaction.user.id}>\n${interaction.user.username}`,
|
||||
inline: true
|
||||
},
|
||||
{
|
||||
name: 'Phrase',
|
||||
value: phrase.phrase,
|
||||
inline: true
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
interaction.reply(`Phrase will no longer alert ${group == 'admin' ? 'admins' : 'you'}.`);
|
||||
}
|
||||
|
||||
async function listPhrases(interaction: ChatInputCommandInteraction, group: SubcommandGroup) {
|
||||
const phrases = await Database.getTicketPhrasesFor(group == 'admin' ? 'admin' : interaction.user.id);
|
||||
|
||||
if (phrases.length == 0) return interaction.reply('No phrases registered');
|
||||
|
||||
interaction.reply(`The following phrases are registered:\n\n${phrases.map(p => (`- \`${p.phrase}\``)).join('\n')}`);
|
||||
}
|
||||
+44
-44
@@ -1,45 +1,45 @@
|
||||
import { ApplicationIntegrationType, ChatInputCommandInteraction, Client, InteractionContextType, MessageFlags, PermissionFlagsBits, SlashCommandBuilder } from 'discord.js';
|
||||
import { deferInteraction } from '../utils';
|
||||
import { getRecordMessageFromDiscordId } from '../utils/record-utils';
|
||||
|
||||
export default {
|
||||
name: 'records',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('records')
|
||||
.setDescription("Get a user's on-site records.")
|
||||
.setIntegrationTypes(ApplicationIntegrationType.GuildInstall)
|
||||
.setContexts(InteractionContextType.Guild)
|
||||
.setDefaultMemberPermissions(PermissionFlagsBits.BanMembers)
|
||||
.addUserOption(option =>
|
||||
option
|
||||
.setName('user')
|
||||
.setDescription('The discord user to find the e621 user of.')
|
||||
.setRequired(false)
|
||||
)
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('id')
|
||||
.setDescription('The discord user id to find the e621 user of.')
|
||||
.setRequired(false)
|
||||
),
|
||||
handler: async function (client: Client, interaction: ChatInputCommandInteraction) {
|
||||
await deferInteraction(interaction);
|
||||
|
||||
if (!interaction.guild) return interaction.editReply('This command must be used in a server');
|
||||
|
||||
const user = interaction.options.getUser('user');
|
||||
const id = interaction.options.getString('id');
|
||||
|
||||
if (!user && !id) {
|
||||
return interaction.reply({ content: 'No user or id given.', flags: [MessageFlags.Ephemeral] });
|
||||
}
|
||||
|
||||
const idToUse = (user?.id ?? id) as string;
|
||||
|
||||
const recordMessage = await getRecordMessageFromDiscordId(idToUse, 1, interaction.guild);
|
||||
|
||||
if (!recordMessage) return interaction.editReply('No records found on any linked accounts.');
|
||||
|
||||
interaction.editReply(recordMessage);
|
||||
}
|
||||
import { ApplicationIntegrationType, ChatInputCommandInteraction, Client, InteractionContextType, MessageFlags, PermissionFlagsBits, SlashCommandBuilder } from 'discord.js';
|
||||
import { deferInteraction } from '../utils';
|
||||
import { getRecordMessageFromDiscordId } from '../utils/record-utils';
|
||||
|
||||
export default {
|
||||
name: 'records',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('records')
|
||||
.setDescription("Get a user's on-site records.")
|
||||
.setIntegrationTypes(ApplicationIntegrationType.GuildInstall)
|
||||
.setContexts(InteractionContextType.Guild)
|
||||
.setDefaultMemberPermissions(PermissionFlagsBits.BanMembers)
|
||||
.addUserOption(option =>
|
||||
option
|
||||
.setName('user')
|
||||
.setDescription('The discord user to find the e621 user of.')
|
||||
.setRequired(false)
|
||||
)
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('id')
|
||||
.setDescription('The discord user id to find the e621 user of.')
|
||||
.setRequired(false)
|
||||
),
|
||||
handler: async function (client: Client, interaction: ChatInputCommandInteraction) {
|
||||
await deferInteraction(interaction);
|
||||
|
||||
if (!interaction.guild) return interaction.editReply('This command must be used in a server');
|
||||
|
||||
const user = interaction.options.getUser('user');
|
||||
const id = interaction.options.getString('id');
|
||||
|
||||
if (!user && !id) {
|
||||
return interaction.reply({ content: 'No user or id given.', flags: [MessageFlags.Ephemeral] });
|
||||
}
|
||||
|
||||
const idToUse = (user?.id ?? id) as string;
|
||||
|
||||
const recordMessage = await getRecordMessageFromDiscordId(idToUse, 1, interaction.guild);
|
||||
|
||||
if (!recordMessage) return interaction.editReply('No records found on any linked accounts.');
|
||||
|
||||
interaction.editReply(recordMessage);
|
||||
}
|
||||
};
|
||||
+50
-50
@@ -1,51 +1,51 @@
|
||||
import { ApplicationIntegrationType, ChatInputCommandInteraction, Client, InteractionContextType, PermissionFlagsBits, RateLimitError, SlashCommandBuilder } from 'discord.js';
|
||||
import { msToHuman } from '../utils';
|
||||
import { Database } from '../shared/Database';
|
||||
|
||||
export default {
|
||||
name: 'rename',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('rename')
|
||||
.setDescription('Rename the general channel.')
|
||||
.setIntegrationTypes(ApplicationIntegrationType.GuildInstall)
|
||||
.setContexts(InteractionContextType.Guild)
|
||||
.setDefaultMemberPermissions(PermissionFlagsBits.BanMembers)
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('new-name')
|
||||
.setDescription('The new name of the general channel.')
|
||||
.setRequired(true)
|
||||
),
|
||||
handler: async function (client: Client, interaction: ChatInputCommandInteraction) {
|
||||
const guildSettings = await Database.getGuildSettings(interaction.guildId!);
|
||||
|
||||
if (!guildSettings || !guildSettings.general_chat_id) {
|
||||
return interaction.reply('No general chat id found.');
|
||||
}
|
||||
|
||||
const name = interaction.options.getString('new-name', true);
|
||||
|
||||
if (name.length > 100) {
|
||||
return interaction.reply('Name must be less than 100 characters in length.');
|
||||
}
|
||||
|
||||
const channel = await interaction.guild!.channels.fetch(guildSettings.general_chat_id)!;
|
||||
|
||||
if (!channel) {
|
||||
return interaction.reply('No general chat id found.');
|
||||
}
|
||||
|
||||
try {
|
||||
await channel.setName(name);
|
||||
|
||||
interaction.reply(`Renamed general to ${channel.name}`);
|
||||
} catch (e: any) {
|
||||
if (e instanceof RateLimitError) {
|
||||
return interaction.reply(`Name change limited. Try again in ${msToHuman(e.retryAfter)}`);
|
||||
}
|
||||
|
||||
console.error(e);
|
||||
return interaction.reply('An error has occurred.');
|
||||
}
|
||||
}
|
||||
import { ApplicationIntegrationType, ChatInputCommandInteraction, Client, InteractionContextType, PermissionFlagsBits, RateLimitError, SlashCommandBuilder } from 'discord.js';
|
||||
import { msToHuman } from '../utils';
|
||||
import { Database } from '../shared/Database';
|
||||
|
||||
export default {
|
||||
name: 'rename',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('rename')
|
||||
.setDescription('Rename the general channel.')
|
||||
.setIntegrationTypes(ApplicationIntegrationType.GuildInstall)
|
||||
.setContexts(InteractionContextType.Guild)
|
||||
.setDefaultMemberPermissions(PermissionFlagsBits.BanMembers)
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('new-name')
|
||||
.setDescription('The new name of the general channel.')
|
||||
.setRequired(true)
|
||||
),
|
||||
handler: async function (client: Client, interaction: ChatInputCommandInteraction) {
|
||||
const guildSettings = await Database.getGuildSettings(interaction.guildId!);
|
||||
|
||||
if (!guildSettings || !guildSettings.general_chat_id) {
|
||||
return interaction.reply('No general chat id found.');
|
||||
}
|
||||
|
||||
const name = interaction.options.getString('new-name', true);
|
||||
|
||||
if (name.length > 100) {
|
||||
return interaction.reply('Name must be less than 100 characters in length.');
|
||||
}
|
||||
|
||||
const channel = await interaction.guild!.channels.fetch(guildSettings.general_chat_id)!;
|
||||
|
||||
if (!channel) {
|
||||
return interaction.reply('No general chat id found.');
|
||||
}
|
||||
|
||||
try {
|
||||
await channel.setName(name);
|
||||
|
||||
interaction.reply(`Renamed general to ${channel.name}`);
|
||||
} catch (e: any) {
|
||||
if (e instanceof RateLimitError) {
|
||||
return interaction.reply(`Name change limited. Try again in ${msToHuman(e.retryAfter)}`);
|
||||
}
|
||||
|
||||
console.error(e);
|
||||
return interaction.reply('An error has occurred.');
|
||||
}
|
||||
}
|
||||
};
|
||||
+80
-80
@@ -1,81 +1,81 @@
|
||||
import { ApplicationIntegrationType, ChatInputCommandInteraction, Client, GuildMember, InteractionContextType, PermissionFlagsBits, SlashCommandBuilder } from 'discord.js';
|
||||
import { deferInteraction } from '../utils';
|
||||
|
||||
export default {
|
||||
name: 'softban',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('softban')
|
||||
.setDescription('Bans and immediately unbans a user to purge messages.')
|
||||
.setIntegrationTypes(ApplicationIntegrationType.GuildInstall)
|
||||
.setContexts(InteractionContextType.Guild)
|
||||
.setDefaultMemberPermissions(PermissionFlagsBits.KickMembers)
|
||||
.addUserOption(option =>
|
||||
option
|
||||
.setName('user')
|
||||
.setDescription('The discord user to softban.')
|
||||
.setRequired(true)
|
||||
)
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('reason')
|
||||
.setDescription('The reason for the softban')
|
||||
.setRequired(false)
|
||||
.setMaxLength(400)
|
||||
)
|
||||
.addNumberOption(option =>
|
||||
option
|
||||
.setName('days')
|
||||
.setDescription('How far back to delete messages (in days, default: 7 days).')
|
||||
.setRequired(false)
|
||||
.setMinValue(0)
|
||||
.setMaxValue(7)
|
||||
),
|
||||
handler: async function (client: Client, interaction: ChatInputCommandInteraction) {
|
||||
await deferInteraction(interaction);
|
||||
|
||||
if (!interaction.guild) return interaction.editReply('This command must be used in a server');
|
||||
|
||||
if (!interaction.guild.members.me) return interaction.editReply('An error has occurred. Please try again later.');
|
||||
|
||||
const user = interaction.options.getUser('user', true);
|
||||
const reason = interaction.options.getString('reason') ?? '';
|
||||
const seconds = (interaction.options.getNumber('days') ?? 7) * 86400;
|
||||
|
||||
let banMember: GuildMember | null = null;
|
||||
|
||||
try {
|
||||
banMember = await interaction.guild.members.fetch(user.id);
|
||||
} catch (e) {
|
||||
// Member not in server.
|
||||
}
|
||||
|
||||
const member = await interaction.guild.members.fetch(interaction.user.id);
|
||||
|
||||
if (banMember && member.roles.highest.comparePositionTo(banMember.roles.highest) <= 0) {
|
||||
return await interaction.editReply('You do not have permission to softban this user.');
|
||||
}
|
||||
|
||||
if (banMember && !banMember.bannable) {
|
||||
return await interaction.editReply('I do not have permission to softban this user.');
|
||||
}
|
||||
|
||||
try {
|
||||
await interaction.guild.bans.create(user, {
|
||||
reason: (reason + ` Softban by ${interaction.user.username} (${interaction.user.id})`).trim(),
|
||||
deleteMessageSeconds: seconds
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return await interaction.editReply("Error softbanning user (couldn't ban).");
|
||||
}
|
||||
|
||||
try {
|
||||
await interaction.guild.bans.remove(user);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return await interaction.editReply("Error softbanning user (couldn't remove ban).");
|
||||
}
|
||||
|
||||
await interaction.editReply('Softban successful');
|
||||
}
|
||||
import { ApplicationIntegrationType, ChatInputCommandInteraction, Client, GuildMember, InteractionContextType, PermissionFlagsBits, SlashCommandBuilder } from 'discord.js';
|
||||
import { deferInteraction } from '../utils';
|
||||
|
||||
export default {
|
||||
name: 'softban',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('softban')
|
||||
.setDescription('Bans and immediately unbans a user to purge messages.')
|
||||
.setIntegrationTypes(ApplicationIntegrationType.GuildInstall)
|
||||
.setContexts(InteractionContextType.Guild)
|
||||
.setDefaultMemberPermissions(PermissionFlagsBits.KickMembers)
|
||||
.addUserOption(option =>
|
||||
option
|
||||
.setName('user')
|
||||
.setDescription('The discord user to softban.')
|
||||
.setRequired(true)
|
||||
)
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('reason')
|
||||
.setDescription('The reason for the softban')
|
||||
.setRequired(false)
|
||||
.setMaxLength(400)
|
||||
)
|
||||
.addNumberOption(option =>
|
||||
option
|
||||
.setName('days')
|
||||
.setDescription('How far back to delete messages (in days, default: 7 days).')
|
||||
.setRequired(false)
|
||||
.setMinValue(0)
|
||||
.setMaxValue(7)
|
||||
),
|
||||
handler: async function (client: Client, interaction: ChatInputCommandInteraction) {
|
||||
await deferInteraction(interaction);
|
||||
|
||||
if (!interaction.guild) return interaction.editReply('This command must be used in a server');
|
||||
|
||||
if (!interaction.guild.members.me) return interaction.editReply('An error has occurred. Please try again later.');
|
||||
|
||||
const user = interaction.options.getUser('user', true);
|
||||
const reason = interaction.options.getString('reason') ?? '';
|
||||
const seconds = (interaction.options.getNumber('days') ?? 7) * 86400;
|
||||
|
||||
let banMember: GuildMember | null = null;
|
||||
|
||||
try {
|
||||
banMember = await interaction.guild.members.fetch(user.id);
|
||||
} catch (e) {
|
||||
// Member not in server.
|
||||
}
|
||||
|
||||
const member = await interaction.guild.members.fetch(interaction.user.id);
|
||||
|
||||
if (banMember && member.roles.highest.comparePositionTo(banMember.roles.highest) <= 0) {
|
||||
return await interaction.editReply('You do not have permission to softban this user.');
|
||||
}
|
||||
|
||||
if (banMember && !banMember.bannable) {
|
||||
return await interaction.editReply('I do not have permission to softban this user.');
|
||||
}
|
||||
|
||||
try {
|
||||
await interaction.guild.bans.create(user, {
|
||||
reason: (reason + ` Softban by ${interaction.user.username} (${interaction.user.id})`).trim(),
|
||||
deleteMessageSeconds: seconds
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return await interaction.editReply("Error softbanning user (couldn't ban).");
|
||||
}
|
||||
|
||||
try {
|
||||
await interaction.guild.bans.remove(user);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return await interaction.editReply("Error softbanning user (couldn't remove ban).");
|
||||
}
|
||||
|
||||
await interaction.editReply('Softban successful');
|
||||
}
|
||||
};
|
||||
+29
-29
@@ -1,30 +1,30 @@
|
||||
import { ApplicationIntegrationType, ChatInputCommandInteraction, Client, GuildBasedChannel, InteractionContextType, MessageMentions, PermissionFlagsBits, SlashCommandBuilder } from 'discord.js';
|
||||
import { channelIsInStaffCategory, handleWhoIsInteraction } from '../utils';
|
||||
|
||||
const mentionRegex = new RegExp(MessageMentions.UsersPattern);
|
||||
|
||||
export default {
|
||||
name: 'whois',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('whois')
|
||||
.setDescription("Find a user's e621 account from their discord account, or vice versa.")
|
||||
.setIntegrationTypes(ApplicationIntegrationType.GuildInstall)
|
||||
.setContexts(InteractionContextType.Guild)
|
||||
.setDefaultMemberPermissions(PermissionFlagsBits.BanMembers)
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('user')
|
||||
.setDescription('The discord user mention, or ID, to find the e621 user of.')
|
||||
.setRequired(true)
|
||||
),
|
||||
handler: async function (client: Client, interaction: ChatInputCommandInteraction) {
|
||||
const input = interaction.options.getString('user', true);
|
||||
|
||||
const matches = mentionRegex.exec(input);
|
||||
mentionRegex.lastIndex = 0;
|
||||
|
||||
const valueToUse = matches ? matches.groups!.id : input;
|
||||
|
||||
handleWhoIsInteraction(interaction, valueToUse, !(await channelIsInStaffCategory(interaction.channel as GuildBasedChannel)));
|
||||
}
|
||||
import { ApplicationIntegrationType, ChatInputCommandInteraction, Client, GuildBasedChannel, InteractionContextType, MessageMentions, PermissionFlagsBits, SlashCommandBuilder } from 'discord.js';
|
||||
import { channelIsInStaffCategory, handleWhoIsInteraction } from '../utils';
|
||||
|
||||
const mentionRegex = new RegExp(MessageMentions.UsersPattern);
|
||||
|
||||
export default {
|
||||
name: 'whois',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('whois')
|
||||
.setDescription("Find a user's e621 account from their discord account, or vice versa.")
|
||||
.setIntegrationTypes(ApplicationIntegrationType.GuildInstall)
|
||||
.setContexts(InteractionContextType.Guild)
|
||||
.setDefaultMemberPermissions(PermissionFlagsBits.BanMembers)
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('user')
|
||||
.setDescription('The discord user mention, or ID, to find the e621 user of.')
|
||||
.setRequired(true)
|
||||
),
|
||||
handler: async function (client: Client, interaction: ChatInputCommandInteraction) {
|
||||
const input = interaction.options.getString('user', true);
|
||||
|
||||
const matches = mentionRegex.exec(input);
|
||||
mentionRegex.lastIndex = 0;
|
||||
|
||||
const valueToUse = matches ? matches.groups!.id : input;
|
||||
|
||||
handleWhoIsInteraction(interaction, valueToUse, !(await channelIsInStaffCategory(interaction.channel as GuildBasedChannel)));
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user