mirror of
https://github.com/nx-bat/discordbot-ng.git
synced 2026-09-22 10:29:10 -04:00
Fix line endings
This commit is contained in:
@@ -1,94 +1,94 @@
|
||||
import { APIEmbedField, APIRole, AuditLogEvent, EmbedBuilder, Guild, GuildAuditLogsEntry, RoleFlags, SnowflakeUtil } from 'discord.js';
|
||||
import { Database } from '../shared/Database';
|
||||
import { formatChanges, formatExtras, formatSnowflake, getTargetType } from '../utils';
|
||||
|
||||
const IGNORED_ACTIONS = [
|
||||
AuditLogEvent.MemberMove,
|
||||
// Handled by automod.
|
||||
AuditLogEvent.AutoModerationFlagToChannel
|
||||
];
|
||||
|
||||
export async function handleAuditLogCreate(entry: GuildAuditLogsEntry, guild: Guild) {
|
||||
if (!await shouldLog(entry, guild)) return;
|
||||
const settings = await Database.getGuildSettings(guild.id);
|
||||
|
||||
if (!settings || !settings.audit_logs_channel_id) return;
|
||||
|
||||
const channel = await guild.channels.fetch(settings.audit_logs_channel_id);
|
||||
|
||||
if (!channel || !channel.isSendable()) return;
|
||||
|
||||
const fields: APIEmbedField[] = [
|
||||
{
|
||||
name: 'Actor',
|
||||
value: `<@${entry.executorId}>`,
|
||||
inline: true
|
||||
}
|
||||
];
|
||||
|
||||
if (entry.targetId) {
|
||||
const targetType = getTargetType(entry.action);
|
||||
fields.push({
|
||||
name: 'Target',
|
||||
value: formatSnowflake(entry.targetId, targetType),
|
||||
inline: true
|
||||
});
|
||||
}
|
||||
|
||||
if (entry.reason) {
|
||||
fields.push({
|
||||
name: 'Reason',
|
||||
value: entry.reason,
|
||||
inline: true
|
||||
});
|
||||
}
|
||||
|
||||
if (entry.changes && entry.changes.length > 0) {
|
||||
fields.push({
|
||||
name: 'Changes',
|
||||
value: formatChanges(entry),
|
||||
inline: false
|
||||
});
|
||||
}
|
||||
|
||||
if (entry.extra) {
|
||||
fields.push({
|
||||
name: 'Options',
|
||||
value: formatExtras(entry, guild),
|
||||
inline: false
|
||||
});
|
||||
}
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setTitle(Object.keys(AuditLogEvent)[Object.values(AuditLogEvent).indexOf(entry.action)])
|
||||
.setTimestamp(Number(SnowflakeUtil.decode(entry.id).timestamp))
|
||||
.addFields(...fields);
|
||||
|
||||
channel.send({ embeds: [embed] });
|
||||
}
|
||||
|
||||
async function shouldLog(entry: GuildAuditLogsEntry, guild: Guild): Promise<boolean> {
|
||||
if (!entry.executorId) return true;
|
||||
|
||||
if (IGNORED_ACTIONS.some(a => entry.action == a)) return false;
|
||||
|
||||
if (entry.action == AuditLogEvent.MemberRoleUpdate) {
|
||||
return await shouldLogRoleChanges(entry as GuildAuditLogsEntry<AuditLogEvent.MemberRoleUpdate>, guild);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async function shouldLogRoleChanges(entry: GuildAuditLogsEntry<AuditLogEvent.MemberRoleUpdate>, guild: Guild): Promise<boolean> {
|
||||
for (const change of entry.changes) {
|
||||
// Get role changes from the log.
|
||||
const roles = (await Promise.all((change.new! as Pick<APIRole, 'id' | 'name'>[]).map(c => guild.roles.fetch(c.id))));
|
||||
|
||||
// Check if role is part of onboarding.
|
||||
for (const role of roles) {
|
||||
if (role && !role.flags.has(RoleFlags.InPrompt)) return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
import { APIEmbedField, APIRole, AuditLogEvent, EmbedBuilder, Guild, GuildAuditLogsEntry, RoleFlags, SnowflakeUtil } from 'discord.js';
|
||||
import { Database } from '../shared/Database';
|
||||
import { formatChanges, formatExtras, formatSnowflake, getTargetType } from '../utils';
|
||||
|
||||
const IGNORED_ACTIONS = [
|
||||
AuditLogEvent.MemberMove,
|
||||
// Handled by automod.
|
||||
AuditLogEvent.AutoModerationFlagToChannel
|
||||
];
|
||||
|
||||
export async function handleAuditLogCreate(entry: GuildAuditLogsEntry, guild: Guild) {
|
||||
if (!await shouldLog(entry, guild)) return;
|
||||
const settings = await Database.getGuildSettings(guild.id);
|
||||
|
||||
if (!settings || !settings.audit_logs_channel_id) return;
|
||||
|
||||
const channel = await guild.channels.fetch(settings.audit_logs_channel_id);
|
||||
|
||||
if (!channel || !channel.isSendable()) return;
|
||||
|
||||
const fields: APIEmbedField[] = [
|
||||
{
|
||||
name: 'Actor',
|
||||
value: `<@${entry.executorId}>`,
|
||||
inline: true
|
||||
}
|
||||
];
|
||||
|
||||
if (entry.targetId) {
|
||||
const targetType = getTargetType(entry.action);
|
||||
fields.push({
|
||||
name: 'Target',
|
||||
value: formatSnowflake(entry.targetId, targetType),
|
||||
inline: true
|
||||
});
|
||||
}
|
||||
|
||||
if (entry.reason) {
|
||||
fields.push({
|
||||
name: 'Reason',
|
||||
value: entry.reason,
|
||||
inline: true
|
||||
});
|
||||
}
|
||||
|
||||
if (entry.changes && entry.changes.length > 0) {
|
||||
fields.push({
|
||||
name: 'Changes',
|
||||
value: formatChanges(entry),
|
||||
inline: false
|
||||
});
|
||||
}
|
||||
|
||||
if (entry.extra) {
|
||||
fields.push({
|
||||
name: 'Options',
|
||||
value: formatExtras(entry, guild),
|
||||
inline: false
|
||||
});
|
||||
}
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setTitle(Object.keys(AuditLogEvent)[Object.values(AuditLogEvent).indexOf(entry.action)])
|
||||
.setTimestamp(Number(SnowflakeUtil.decode(entry.id).timestamp))
|
||||
.addFields(...fields);
|
||||
|
||||
channel.send({ embeds: [embed] });
|
||||
}
|
||||
|
||||
async function shouldLog(entry: GuildAuditLogsEntry, guild: Guild): Promise<boolean> {
|
||||
if (!entry.executorId) return true;
|
||||
|
||||
if (IGNORED_ACTIONS.some(a => entry.action == a)) return false;
|
||||
|
||||
if (entry.action == AuditLogEvent.MemberRoleUpdate) {
|
||||
return await shouldLogRoleChanges(entry as GuildAuditLogsEntry<AuditLogEvent.MemberRoleUpdate>, guild);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async function shouldLogRoleChanges(entry: GuildAuditLogsEntry<AuditLogEvent.MemberRoleUpdate>, guild: Guild): Promise<boolean> {
|
||||
for (const change of entry.changes) {
|
||||
// Get role changes from the log.
|
||||
const roles = (await Promise.all((change.new! as Pick<APIRole, 'id' | 'name'>[]).map(c => guild.roles.fetch(c.id))));
|
||||
|
||||
// Check if role is part of onboarding.
|
||||
for (const role of roles) {
|
||||
if (role && !role.flags.has(RoleFlags.InPrompt)) return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { GuildBan } from 'discord.js';
|
||||
import { Database } from '../shared/Database';
|
||||
|
||||
export async function handleBanRemove(ban: GuildBan) {
|
||||
await Database.removeBan(ban.user.id);
|
||||
import { GuildBan } from 'discord.js';
|
||||
import { Database } from '../shared/Database';
|
||||
|
||||
export async function handleBanRemove(ban: GuildBan) {
|
||||
await Database.removeBan(ban.user.id);
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Guild } from 'discord.js';
|
||||
import { Database } from '../shared/Database';
|
||||
|
||||
export async function handleGuildCreate(guild: Guild) {
|
||||
try {
|
||||
if (!await Database.getGuildSettings(guild.id)) await Database.putGuild(guild.id);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
import { Guild } from 'discord.js';
|
||||
import { Database } from '../shared/Database';
|
||||
|
||||
export async function handleGuildCreate(guild: Guild) {
|
||||
try {
|
||||
if (!await Database.getGuildSettings(guild.id)) await Database.putGuild(guild.id);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,23 @@
|
||||
import { GuildMember, GuildTextBasedChannel } from 'discord.js';
|
||||
import { Database } from '../shared/Database';
|
||||
import { getE621Alts } from '../utils';
|
||||
|
||||
export async function handleMemberJoin(member: GuildMember) {
|
||||
const guildSettings = await Database.getGuildSettings(member.guild.id);
|
||||
|
||||
if (guildSettings?.new_member_channel_id) {
|
||||
const channel = await member.guild.channels.fetch(guildSettings.new_member_channel_id) as GuildTextBasedChannel;
|
||||
|
||||
if (channel) {
|
||||
const content = `${member.toString()}'s (${member.id}) e621 and discord account(s):\n${await getE621Alts(member.id, member.guild)}`;
|
||||
|
||||
channel.send(content).catch(console.error);
|
||||
|
||||
if (guildSettings.moderator_channel_id && content.includes('[BANNED]')) {
|
||||
const modChannel = await member.guild.channels.fetch(guildSettings.moderator_channel_id) as GuildTextBasedChannel;
|
||||
|
||||
if (modChannel) modChannel.send(`Member joined with banned alts:\n${content}`).catch(console.error);
|
||||
}
|
||||
}
|
||||
}
|
||||
import { GuildMember, GuildTextBasedChannel } from 'discord.js';
|
||||
import { Database } from '../shared/Database';
|
||||
import { getE621Alts } from '../utils';
|
||||
|
||||
export async function handleMemberJoin(member: GuildMember) {
|
||||
const guildSettings = await Database.getGuildSettings(member.guild.id);
|
||||
|
||||
if (guildSettings?.new_member_channel_id) {
|
||||
const channel = await member.guild.channels.fetch(guildSettings.new_member_channel_id) as GuildTextBasedChannel;
|
||||
|
||||
if (channel) {
|
||||
const content = `${member.toString()}'s (${member.id}) e621 and discord account(s):\n${await getE621Alts(member.id, member.guild)}`;
|
||||
|
||||
channel.send(content).catch(console.error);
|
||||
|
||||
if (guildSettings.moderator_channel_id && content.includes('[BANNED]')) {
|
||||
const modChannel = await member.guild.channels.fetch(guildSettings.moderator_channel_id) as GuildTextBasedChannel;
|
||||
|
||||
if (modChannel) modChannel.send(`Member joined with banned alts:\n${content}`).catch(console.error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+384
-384
@@ -1,385 +1,385 @@
|
||||
import { Message as DiscordMessage, GuildBasedChannel, GuildTextBasedChannel, OmitPartialGroupDMChannel, PartialMessage, ReadonlyCollection, spoiler } from 'discord.js';
|
||||
import { config } from '../config';
|
||||
import { Database } from '../shared/Database';
|
||||
import { E621Post } from '../types';
|
||||
import { ALLOWED_MIMETYPES, artistIDRegex, blipIDRegex, calculateMD5FromURL, channelIgnoresLinks, channelIsInStaffCategory, channelIsSafe, commentIDRegex, forumTopicIDRegex, getE621Post, getE621PostByMd5, getPostUrl, isEdited, isInSpoilerTags, issueRegex, logDeletion, logEdit, poolIDRegex, PostAction, postIDRegex, prRegex, recordIDRegex, searchLinkRegex, setIDRegex, spoilerOrBlacklist, takedownIDRegex, ticketIDRegex, userIDRegex, wikiLinkRegex } from '../utils';
|
||||
|
||||
export type Message<InGuild extends boolean = boolean> = OmitPartialGroupDMChannel<DiscordMessage<InGuild>>;
|
||||
export type Partial = OmitPartialGroupDMChannel<PartialMessage>;
|
||||
|
||||
// TODO: I don't know of any good way to not hardcode this regex for e621 links. So I've provided two that may need to have the port altered.
|
||||
const postRegex = new RegExp('!?https?://(?:.*@)?(?:e621|e926)\\.net/+posts/+([0-9]+)', 'gi');
|
||||
const postShareRegex = new RegExp('!?https?://(?:.*@)?(?:e621|e926)\\.net/+p/+([a-z0-9]+)', 'gi');
|
||||
const imageRegex = new RegExp('!?https?://(?:.*@)?static[0-9]*\\.(?:e621|e926)\\.net/+data/+(?:sample/+|preview/+|)[\\da-f]{2}/+[\\da-f]{2}/+([\\da-f]{32})\\.[\\da-z]+', 'gi');
|
||||
|
||||
const postRegex_DEV = new RegExp('!?https?://(?:.*@)?localhost:3000/+posts/+([0-9]+)', 'gi');
|
||||
const imageRegex_DEV = new RegExp('!?https?://(?:.*@)?localhost:3000/+data/+(?:sample/+|preview/+|)[\\da-f]{2}/+[\\da-f]{2}/+([\\da-f]{32})\\.[\\da-z]+', 'gi');
|
||||
|
||||
const md5Regex = new RegExp('^([a-f0-9]{32}).(?:png|apng|jpg|jpeg|gif|webm|mp4)$', 'gi');
|
||||
|
||||
const regexTesters = [
|
||||
{ runInDev: false, regex: postRegex, handler: postHandler.bind(null, null) },
|
||||
{
|
||||
runInDev: false, regex: postShareRegex, handler: postHandler.bind(null, (idString: string) => {
|
||||
return parseInt(idString, 32);
|
||||
})
|
||||
},
|
||||
{ runInDev: false, regex: imageRegex, handler: imageHandler },
|
||||
{ runInDev: true, regex: postRegex_DEV, handler: postHandler.bind(null, null) },
|
||||
{ runInDev: true, regex: imageRegex_DEV, handler: imageHandler },
|
||||
{ runInDev: true, regex: postIDRegex, handler: postIdHandler },
|
||||
{ runInDev: true, regex: userIDRegex, handler: idHandler.bind(null, 'users') },
|
||||
{ runInDev: true, regex: forumTopicIDRegex, handler: idHandler.bind(null, 'forum_topics') },
|
||||
{ runInDev: true, regex: commentIDRegex, handler: idHandler.bind(null, 'comments') },
|
||||
{ runInDev: true, regex: blipIDRegex, handler: idHandler.bind(null, 'blips') },
|
||||
{ runInDev: true, regex: poolIDRegex, handler: idHandler.bind(null, 'pools') },
|
||||
{ runInDev: true, regex: setIDRegex, handler: idHandler.bind(null, 'post_sets') },
|
||||
{ runInDev: true, regex: takedownIDRegex, handler: idHandler.bind(null, 'takedowns') },
|
||||
{ runInDev: true, regex: recordIDRegex, handler: idHandler.bind(null, 'user_feedbacks') },
|
||||
{ runInDev: true, regex: ticketIDRegex, handler: idHandler.bind(null, 'tickets') },
|
||||
{ runInDev: true, regex: artistIDRegex, handler: idHandler.bind(null, 'artists') },
|
||||
{ runInDev: true, regex: wikiLinkRegex, handler: wikiPageHandler },
|
||||
{ runInDev: true, regex: searchLinkRegex, handler: searchHandler },
|
||||
|
||||
{ runInDev: true, regex: prRegex, handler: githubPullRequestHandler },
|
||||
{ runInDev: true, regex: issueRegex, handler: githubIssueHandler },
|
||||
];
|
||||
|
||||
const uniqueRegexMatches = (g, i, a) => a.findIndex(v => v[1] == g[1]) == i;
|
||||
|
||||
export async function handleMessageCreate(message: Message) {
|
||||
if (message.author.bot) return;
|
||||
if (message.inGuild()) await Database.putMessage(message);
|
||||
|
||||
const responses: string[] = [];
|
||||
|
||||
for (const test of regexTesters) {
|
||||
if (config.DEV_MODE && !test.runInDev) continue;
|
||||
|
||||
const hasMatches = test.regex.test(message.content);
|
||||
test.regex.lastIndex = 0;
|
||||
|
||||
if (hasMatches) {
|
||||
const matches: RegExpExecArray[] = [];
|
||||
let match: RegExpExecArray | null;
|
||||
|
||||
while ((match = test.regex.exec(message.content)) != null) {
|
||||
matches.push(match);
|
||||
}
|
||||
test.regex.lastIndex = 0;
|
||||
|
||||
const response = await test.handler(message, matches.filter(uniqueRegexMatches));
|
||||
|
||||
if (response === false) return;
|
||||
|
||||
if (response !== true) responses.push(response as string);
|
||||
}
|
||||
}
|
||||
|
||||
for (const attachment of message.attachments.values()) {
|
||||
const match = md5Regex.exec(attachment.name);
|
||||
md5Regex.lastIndex = 0;
|
||||
|
||||
const md5s: string[] = [];
|
||||
|
||||
if (match) md5s.push(match[1]);
|
||||
else if (ALLOWED_MIMETYPES.includes(attachment.contentType!)) {
|
||||
const md5Data = await calculateMD5FromURL(attachment.url);
|
||||
if (!md5Data) continue;
|
||||
md5s.push(md5Data.correctedFileMD5, md5Data.originalFileMD5);
|
||||
}
|
||||
|
||||
if (md5s.length == 0) continue;
|
||||
|
||||
for (const md5 of md5s) {
|
||||
const post = await getE621PostByMd5(md5);
|
||||
|
||||
if (post) {
|
||||
if (await blacklistIfNecessary(message, [post])) return;
|
||||
|
||||
responses.push(`<${getPostUrl(post)}>`);
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (responses.length > 0) {
|
||||
await message.reply(responses.join('\n'));
|
||||
}
|
||||
}
|
||||
|
||||
export async function handleMessageUpdate(oldMessage: Message | PartialMessage, newMessage: Message) {
|
||||
if (newMessage.author.bot) return;
|
||||
|
||||
const loggedMessage = await Database.getMessageWithRetry(newMessage.id);
|
||||
|
||||
if (!loggedMessage) {
|
||||
if (newMessage.inGuild()) await Database.putMessage(newMessage);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (newMessage.inGuild() && isEdited(loggedMessage, newMessage)) {
|
||||
await Database.putMessage(newMessage);
|
||||
await logEdit(loggedMessage, newMessage);
|
||||
}
|
||||
|
||||
if (loggedMessage.content == newMessage.content) return;
|
||||
|
||||
const responses: string[] = [];
|
||||
|
||||
for (const test of regexTesters) {
|
||||
if (config.DEV_MODE && !test.runInDev) continue;
|
||||
|
||||
const hasMatches = test.regex.test(newMessage.content);
|
||||
test.regex.lastIndex = 0;
|
||||
|
||||
if (hasMatches) {
|
||||
const oldMatches: RegExpExecArray[] = [];
|
||||
const newMatches: RegExpExecArray[] = [];
|
||||
let match: RegExpExecArray | null;
|
||||
|
||||
while ((match = test.regex.exec(newMessage.content)) != null) {
|
||||
newMatches.push(match);
|
||||
}
|
||||
test.regex.lastIndex = 0;
|
||||
|
||||
while ((match = test.regex.exec(loggedMessage.content)) != null) {
|
||||
oldMatches.push(match);
|
||||
}
|
||||
test.regex.lastIndex = 0;
|
||||
|
||||
const properMatches: RegExpExecArray[] = [];
|
||||
|
||||
for (const newMatch of newMatches) {
|
||||
if (!oldMatches.find(m => m[1] == newMatch[1])) properMatches.push(newMatch);
|
||||
}
|
||||
|
||||
if (properMatches.length == 0) continue;
|
||||
|
||||
const response = await test.handler(newMessage, properMatches.filter(uniqueRegexMatches));
|
||||
|
||||
if (response === false) return;
|
||||
|
||||
if (response !== true) responses.push(response as string);
|
||||
}
|
||||
}
|
||||
|
||||
if (responses.length > 0) {
|
||||
await newMessage.reply(responses.join('\n'));
|
||||
}
|
||||
}
|
||||
|
||||
export async function handleMessageDelete(message: Message | PartialMessage) {
|
||||
const loggedMessage = await Database.getMessageWithRetry(message.id);
|
||||
|
||||
if (!loggedMessage) return;
|
||||
|
||||
if (message.inGuild()) await logDeletion(loggedMessage, message);
|
||||
}
|
||||
|
||||
export async function handleBulkMessageDelete(messages: ReadonlyCollection<string, Message | Partial>, channel: GuildTextBasedChannel) {
|
||||
for (const message of messages.values()) {
|
||||
await handleMessageDelete(message);
|
||||
}
|
||||
}
|
||||
|
||||
async function searchHandler(message: Message, matchedGroups: RegExpExecArray[]): Promise<string | boolean> {
|
||||
const skip = await channelIgnoresLinks(message.channel as GuildBasedChannel);
|
||||
|
||||
if (skip) return true;
|
||||
|
||||
let content = '';
|
||||
|
||||
for (const group of matchedGroups) {
|
||||
content += `<${config.E621_BASE_URL}/posts?tags=${encodeURIComponent(group[1])}>\n`;
|
||||
}
|
||||
|
||||
if (content.trim().length > 0) return content.trim();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async function wikiPageHandler(message: Message, matchedGroups: RegExpExecArray[]): Promise<string | boolean> {
|
||||
const skip = await channelIgnoresLinks(message.channel as GuildBasedChannel);
|
||||
|
||||
if (skip) return true;
|
||||
|
||||
let content = '';
|
||||
|
||||
for (const group of matchedGroups) {
|
||||
content += `<${config.E621_BASE_URL}/wiki_pages/${group[1].split('#').map(t => encodeURIComponent(t)).join('#')}>\n`;
|
||||
}
|
||||
|
||||
if (content.trim().length > 0) return content.trim();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async function blacklistIfNecessary(message: Message, posts: E621Post[]): Promise<boolean> {
|
||||
const blacklistedIds: number[] = [];
|
||||
|
||||
const channel = await message.channel.fetch() as GuildTextBasedChannel;
|
||||
|
||||
const isStaffChannel = await channelIsInStaffCategory(channel);
|
||||
|
||||
for (const post of posts) {
|
||||
if (spoilerOrBlacklist(post).action == PostAction.Blacklist) {
|
||||
blacklistedIds.push(post.id);
|
||||
}
|
||||
}
|
||||
|
||||
if (blacklistedIds.length == 0) return false;
|
||||
|
||||
await message.delete();
|
||||
|
||||
if (channel.parentId && isStaffChannel) {
|
||||
await message.channel.send({
|
||||
content: `_sucks message into the void._ ${message.author.toString()} nono, don't post links to ${blacklistedIds.length == 1 ? `post ${blacklistedIds[0]}` : `posts \`${blacklistedIds.join('`, `')}\``}. See rule #5.b for more details.`,
|
||||
allowedMentions: {
|
||||
users: [message.author.id]
|
||||
}
|
||||
});
|
||||
} else {
|
||||
await message.channel.send({
|
||||
content: `_sucks message into the void._ ${message.author.toString()} nono, don't post links to young/cub content. See rule #5.b for more details.`,
|
||||
allowedMentions: {
|
||||
users: [message.author.id]
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async function postIdHandler(message: Message, matchedGroups: RegExpExecArray[]): Promise<string | boolean> {
|
||||
if (!message.guildId) return true;
|
||||
|
||||
const posts: { post: E621Post, spoilered: boolean }[] = [];
|
||||
|
||||
for (const match of matchedGroups) {
|
||||
try {
|
||||
const post = await getE621Post(match[1]);
|
||||
if (post) posts.push({
|
||||
spoilered: isInSpoilerTags(message.content, match.index),
|
||||
post
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (await blacklistIfNecessary(message, posts.map(p => p.post))) return false;
|
||||
|
||||
const skip = await channelIgnoresLinks(message.channel as GuildBasedChannel);
|
||||
|
||||
if (skip) return true;
|
||||
|
||||
const sfw = await channelIsSafe(message.channel as GuildBasedChannel);
|
||||
|
||||
const content = posts.map((postData) => {
|
||||
if (sfw && postData.post.rating != 's') return ` [NSFW] <${getPostUrl(postData.post)}>`;
|
||||
|
||||
const shouldSpoiler = spoilerOrBlacklist(postData.post);
|
||||
if (shouldSpoiler.action == PostAction.Spoiler) return `${spoiler(getPostUrl(postData.post))} (${shouldSpoiler.tag})`;
|
||||
|
||||
return postData.spoilered ? spoiler(getPostUrl(postData.post)) : getPostUrl(postData.post);
|
||||
}).join('\n');
|
||||
|
||||
if (content.trim().length > 0) return content.trim();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async function idHandler(path: string, message: Message, matchedGroups: RegExpExecArray[]): Promise<string | boolean> {
|
||||
if (!message.guildId) return true;
|
||||
|
||||
const skip = await channelIgnoresLinks(message.channel as GuildBasedChannel);
|
||||
|
||||
if (skip) return true;
|
||||
|
||||
const content = matchedGroups.map(m => `${config.E621_BASE_URL}/${path}/${m[1]}`).join('\n');
|
||||
|
||||
if (content.trim().length > 0) return content.trim();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async function postHandler(transform: ((idString: string) => number) | null, message: Message, matchedGroups: RegExpExecArray[]): Promise<string | boolean> {
|
||||
if (!message.guildId) return true;
|
||||
|
||||
const posts: E621Post[] = [];
|
||||
|
||||
for (const match of matchedGroups) {
|
||||
try {
|
||||
const post = await getE621Post(transform ? transform(match[1]) : match[1]);
|
||||
if (post) posts.push(post);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (await blacklistIfNecessary(message, posts)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async function imageHandler(message: Message, matchedGroups: RegExpExecArray[]): Promise<string | boolean> {
|
||||
if (!message.guildId) return true;
|
||||
|
||||
const posts: E621Post[] = [];
|
||||
|
||||
for (const match of matchedGroups) {
|
||||
try {
|
||||
const post = await getE621PostByMd5(match[1]);
|
||||
if (post) posts.push(post);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (await blacklistIfNecessary(message, posts)) return false;
|
||||
|
||||
const skip = await channelIgnoresLinks(message.channel as GuildBasedChannel);
|
||||
|
||||
if (skip) return true;
|
||||
|
||||
const content = posts.map(post => `<${getPostUrl(post)}>`).join('\n');
|
||||
|
||||
if (content.trim().length > 0) return content.trim();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async function githubPullRequestHandler(message: Message, matchedGroups: RegExpExecArray[]): Promise<string | boolean> {
|
||||
const skip = await channelIgnoresLinks(message.channel as GuildBasedChannel);
|
||||
|
||||
if (skip) return true;
|
||||
|
||||
let content = '';
|
||||
|
||||
for (const group of matchedGroups) {
|
||||
content += `${config.GIT_REPO_BASE_URL}/pull/${group[1]}\n`;
|
||||
}
|
||||
|
||||
if (content.trim().length > 0) return content.trim();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async function githubIssueHandler(message: Message, matchedGroups: RegExpExecArray[]): Promise<string | boolean> {
|
||||
const skip = await channelIgnoresLinks(message.channel as GuildBasedChannel);
|
||||
|
||||
if (skip) return true;
|
||||
|
||||
let content = '';
|
||||
|
||||
for (const group of matchedGroups) {
|
||||
content += `${config.GIT_REPO_BASE_URL}/issues/${group[1]}\n`;
|
||||
}
|
||||
|
||||
if (content.trim().length > 0) return content.trim();
|
||||
|
||||
return true;
|
||||
import { Message as DiscordMessage, GuildBasedChannel, GuildTextBasedChannel, OmitPartialGroupDMChannel, PartialMessage, ReadonlyCollection, spoiler } from 'discord.js';
|
||||
import { config } from '../config';
|
||||
import { Database } from '../shared/Database';
|
||||
import { E621Post } from '../types';
|
||||
import { ALLOWED_MIMETYPES, artistIDRegex, blipIDRegex, calculateMD5FromURL, channelIgnoresLinks, channelIsInStaffCategory, channelIsSafe, commentIDRegex, forumTopicIDRegex, getE621Post, getE621PostByMd5, getPostUrl, isEdited, isInSpoilerTags, issueRegex, logDeletion, logEdit, poolIDRegex, PostAction, postIDRegex, prRegex, recordIDRegex, searchLinkRegex, setIDRegex, spoilerOrBlacklist, takedownIDRegex, ticketIDRegex, userIDRegex, wikiLinkRegex } from '../utils';
|
||||
|
||||
export type Message<InGuild extends boolean = boolean> = OmitPartialGroupDMChannel<DiscordMessage<InGuild>>;
|
||||
export type Partial = OmitPartialGroupDMChannel<PartialMessage>;
|
||||
|
||||
// TODO: I don't know of any good way to not hardcode this regex for e621 links. So I've provided two that may need to have the port altered.
|
||||
const postRegex = new RegExp('!?https?://(?:.*@)?(?:e621|e926)\\.net/+posts/+([0-9]+)', 'gi');
|
||||
const postShareRegex = new RegExp('!?https?://(?:.*@)?(?:e621|e926)\\.net/+p/+([a-z0-9]+)', 'gi');
|
||||
const imageRegex = new RegExp('!?https?://(?:.*@)?static[0-9]*\\.(?:e621|e926)\\.net/+data/+(?:sample/+|preview/+|)[\\da-f]{2}/+[\\da-f]{2}/+([\\da-f]{32})\\.[\\da-z]+', 'gi');
|
||||
|
||||
const postRegex_DEV = new RegExp('!?https?://(?:.*@)?localhost:3000/+posts/+([0-9]+)', 'gi');
|
||||
const imageRegex_DEV = new RegExp('!?https?://(?:.*@)?localhost:3000/+data/+(?:sample/+|preview/+|)[\\da-f]{2}/+[\\da-f]{2}/+([\\da-f]{32})\\.[\\da-z]+', 'gi');
|
||||
|
||||
const md5Regex = new RegExp('^([a-f0-9]{32}).(?:png|apng|jpg|jpeg|gif|webm|mp4)$', 'gi');
|
||||
|
||||
const regexTesters = [
|
||||
{ runInDev: false, regex: postRegex, handler: postHandler.bind(null, null) },
|
||||
{
|
||||
runInDev: false, regex: postShareRegex, handler: postHandler.bind(null, (idString: string) => {
|
||||
return parseInt(idString, 32);
|
||||
})
|
||||
},
|
||||
{ runInDev: false, regex: imageRegex, handler: imageHandler },
|
||||
{ runInDev: true, regex: postRegex_DEV, handler: postHandler.bind(null, null) },
|
||||
{ runInDev: true, regex: imageRegex_DEV, handler: imageHandler },
|
||||
{ runInDev: true, regex: postIDRegex, handler: postIdHandler },
|
||||
{ runInDev: true, regex: userIDRegex, handler: idHandler.bind(null, 'users') },
|
||||
{ runInDev: true, regex: forumTopicIDRegex, handler: idHandler.bind(null, 'forum_topics') },
|
||||
{ runInDev: true, regex: commentIDRegex, handler: idHandler.bind(null, 'comments') },
|
||||
{ runInDev: true, regex: blipIDRegex, handler: idHandler.bind(null, 'blips') },
|
||||
{ runInDev: true, regex: poolIDRegex, handler: idHandler.bind(null, 'pools') },
|
||||
{ runInDev: true, regex: setIDRegex, handler: idHandler.bind(null, 'post_sets') },
|
||||
{ runInDev: true, regex: takedownIDRegex, handler: idHandler.bind(null, 'takedowns') },
|
||||
{ runInDev: true, regex: recordIDRegex, handler: idHandler.bind(null, 'user_feedbacks') },
|
||||
{ runInDev: true, regex: ticketIDRegex, handler: idHandler.bind(null, 'tickets') },
|
||||
{ runInDev: true, regex: artistIDRegex, handler: idHandler.bind(null, 'artists') },
|
||||
{ runInDev: true, regex: wikiLinkRegex, handler: wikiPageHandler },
|
||||
{ runInDev: true, regex: searchLinkRegex, handler: searchHandler },
|
||||
|
||||
{ runInDev: true, regex: prRegex, handler: githubPullRequestHandler },
|
||||
{ runInDev: true, regex: issueRegex, handler: githubIssueHandler },
|
||||
];
|
||||
|
||||
const uniqueRegexMatches = (g, i, a) => a.findIndex(v => v[1] == g[1]) == i;
|
||||
|
||||
export async function handleMessageCreate(message: Message) {
|
||||
if (message.author.bot) return;
|
||||
if (message.inGuild()) await Database.putMessage(message);
|
||||
|
||||
const responses: string[] = [];
|
||||
|
||||
for (const test of regexTesters) {
|
||||
if (config.DEV_MODE && !test.runInDev) continue;
|
||||
|
||||
const hasMatches = test.regex.test(message.content);
|
||||
test.regex.lastIndex = 0;
|
||||
|
||||
if (hasMatches) {
|
||||
const matches: RegExpExecArray[] = [];
|
||||
let match: RegExpExecArray | null;
|
||||
|
||||
while ((match = test.regex.exec(message.content)) != null) {
|
||||
matches.push(match);
|
||||
}
|
||||
test.regex.lastIndex = 0;
|
||||
|
||||
const response = await test.handler(message, matches.filter(uniqueRegexMatches));
|
||||
|
||||
if (response === false) return;
|
||||
|
||||
if (response !== true) responses.push(response as string);
|
||||
}
|
||||
}
|
||||
|
||||
for (const attachment of message.attachments.values()) {
|
||||
const match = md5Regex.exec(attachment.name);
|
||||
md5Regex.lastIndex = 0;
|
||||
|
||||
const md5s: string[] = [];
|
||||
|
||||
if (match) md5s.push(match[1]);
|
||||
else if (ALLOWED_MIMETYPES.includes(attachment.contentType!)) {
|
||||
const md5Data = await calculateMD5FromURL(attachment.url);
|
||||
if (!md5Data) continue;
|
||||
md5s.push(md5Data.correctedFileMD5, md5Data.originalFileMD5);
|
||||
}
|
||||
|
||||
if (md5s.length == 0) continue;
|
||||
|
||||
for (const md5 of md5s) {
|
||||
const post = await getE621PostByMd5(md5);
|
||||
|
||||
if (post) {
|
||||
if (await blacklistIfNecessary(message, [post])) return;
|
||||
|
||||
responses.push(`<${getPostUrl(post)}>`);
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (responses.length > 0) {
|
||||
await message.reply(responses.join('\n'));
|
||||
}
|
||||
}
|
||||
|
||||
export async function handleMessageUpdate(oldMessage: Message | PartialMessage, newMessage: Message) {
|
||||
if (newMessage.author.bot) return;
|
||||
|
||||
const loggedMessage = await Database.getMessageWithRetry(newMessage.id);
|
||||
|
||||
if (!loggedMessage) {
|
||||
if (newMessage.inGuild()) await Database.putMessage(newMessage);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (newMessage.inGuild() && isEdited(loggedMessage, newMessage)) {
|
||||
await Database.putMessage(newMessage);
|
||||
await logEdit(loggedMessage, newMessage);
|
||||
}
|
||||
|
||||
if (loggedMessage.content == newMessage.content) return;
|
||||
|
||||
const responses: string[] = [];
|
||||
|
||||
for (const test of regexTesters) {
|
||||
if (config.DEV_MODE && !test.runInDev) continue;
|
||||
|
||||
const hasMatches = test.regex.test(newMessage.content);
|
||||
test.regex.lastIndex = 0;
|
||||
|
||||
if (hasMatches) {
|
||||
const oldMatches: RegExpExecArray[] = [];
|
||||
const newMatches: RegExpExecArray[] = [];
|
||||
let match: RegExpExecArray | null;
|
||||
|
||||
while ((match = test.regex.exec(newMessage.content)) != null) {
|
||||
newMatches.push(match);
|
||||
}
|
||||
test.regex.lastIndex = 0;
|
||||
|
||||
while ((match = test.regex.exec(loggedMessage.content)) != null) {
|
||||
oldMatches.push(match);
|
||||
}
|
||||
test.regex.lastIndex = 0;
|
||||
|
||||
const properMatches: RegExpExecArray[] = [];
|
||||
|
||||
for (const newMatch of newMatches) {
|
||||
if (!oldMatches.find(m => m[1] == newMatch[1])) properMatches.push(newMatch);
|
||||
}
|
||||
|
||||
if (properMatches.length == 0) continue;
|
||||
|
||||
const response = await test.handler(newMessage, properMatches.filter(uniqueRegexMatches));
|
||||
|
||||
if (response === false) return;
|
||||
|
||||
if (response !== true) responses.push(response as string);
|
||||
}
|
||||
}
|
||||
|
||||
if (responses.length > 0) {
|
||||
await newMessage.reply(responses.join('\n'));
|
||||
}
|
||||
}
|
||||
|
||||
export async function handleMessageDelete(message: Message | PartialMessage) {
|
||||
const loggedMessage = await Database.getMessageWithRetry(message.id);
|
||||
|
||||
if (!loggedMessage) return;
|
||||
|
||||
if (message.inGuild()) await logDeletion(loggedMessage, message);
|
||||
}
|
||||
|
||||
export async function handleBulkMessageDelete(messages: ReadonlyCollection<string, Message | Partial>, channel: GuildTextBasedChannel) {
|
||||
for (const message of messages.values()) {
|
||||
await handleMessageDelete(message);
|
||||
}
|
||||
}
|
||||
|
||||
async function searchHandler(message: Message, matchedGroups: RegExpExecArray[]): Promise<string | boolean> {
|
||||
const skip = await channelIgnoresLinks(message.channel as GuildBasedChannel);
|
||||
|
||||
if (skip) return true;
|
||||
|
||||
let content = '';
|
||||
|
||||
for (const group of matchedGroups) {
|
||||
content += `<${config.E621_BASE_URL}/posts?tags=${encodeURIComponent(group[1])}>\n`;
|
||||
}
|
||||
|
||||
if (content.trim().length > 0) return content.trim();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async function wikiPageHandler(message: Message, matchedGroups: RegExpExecArray[]): Promise<string | boolean> {
|
||||
const skip = await channelIgnoresLinks(message.channel as GuildBasedChannel);
|
||||
|
||||
if (skip) return true;
|
||||
|
||||
let content = '';
|
||||
|
||||
for (const group of matchedGroups) {
|
||||
content += `<${config.E621_BASE_URL}/wiki_pages/${group[1].split('#').map(t => encodeURIComponent(t)).join('#')}>\n`;
|
||||
}
|
||||
|
||||
if (content.trim().length > 0) return content.trim();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async function blacklistIfNecessary(message: Message, posts: E621Post[]): Promise<boolean> {
|
||||
const blacklistedIds: number[] = [];
|
||||
|
||||
const channel = await message.channel.fetch() as GuildTextBasedChannel;
|
||||
|
||||
const isStaffChannel = await channelIsInStaffCategory(channel);
|
||||
|
||||
for (const post of posts) {
|
||||
if (spoilerOrBlacklist(post).action == PostAction.Blacklist) {
|
||||
blacklistedIds.push(post.id);
|
||||
}
|
||||
}
|
||||
|
||||
if (blacklistedIds.length == 0) return false;
|
||||
|
||||
await message.delete();
|
||||
|
||||
if (channel.parentId && isStaffChannel) {
|
||||
await message.channel.send({
|
||||
content: `_sucks message into the void._ ${message.author.toString()} nono, don't post links to ${blacklistedIds.length == 1 ? `post ${blacklistedIds[0]}` : `posts \`${blacklistedIds.join('`, `')}\``}. See rule #5.b for more details.`,
|
||||
allowedMentions: {
|
||||
users: [message.author.id]
|
||||
}
|
||||
});
|
||||
} else {
|
||||
await message.channel.send({
|
||||
content: `_sucks message into the void._ ${message.author.toString()} nono, don't post links to young/cub content. See rule #5.b for more details.`,
|
||||
allowedMentions: {
|
||||
users: [message.author.id]
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async function postIdHandler(message: Message, matchedGroups: RegExpExecArray[]): Promise<string | boolean> {
|
||||
if (!message.guildId) return true;
|
||||
|
||||
const posts: { post: E621Post, spoilered: boolean }[] = [];
|
||||
|
||||
for (const match of matchedGroups) {
|
||||
try {
|
||||
const post = await getE621Post(match[1]);
|
||||
if (post) posts.push({
|
||||
spoilered: isInSpoilerTags(message.content, match.index),
|
||||
post
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (await blacklistIfNecessary(message, posts.map(p => p.post))) return false;
|
||||
|
||||
const skip = await channelIgnoresLinks(message.channel as GuildBasedChannel);
|
||||
|
||||
if (skip) return true;
|
||||
|
||||
const sfw = await channelIsSafe(message.channel as GuildBasedChannel);
|
||||
|
||||
const content = posts.map((postData) => {
|
||||
if (sfw && postData.post.rating != 's') return ` [NSFW] <${getPostUrl(postData.post)}>`;
|
||||
|
||||
const shouldSpoiler = spoilerOrBlacklist(postData.post);
|
||||
if (shouldSpoiler.action == PostAction.Spoiler) return `${spoiler(getPostUrl(postData.post))} (${shouldSpoiler.tag})`;
|
||||
|
||||
return postData.spoilered ? spoiler(getPostUrl(postData.post)) : getPostUrl(postData.post);
|
||||
}).join('\n');
|
||||
|
||||
if (content.trim().length > 0) return content.trim();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async function idHandler(path: string, message: Message, matchedGroups: RegExpExecArray[]): Promise<string | boolean> {
|
||||
if (!message.guildId) return true;
|
||||
|
||||
const skip = await channelIgnoresLinks(message.channel as GuildBasedChannel);
|
||||
|
||||
if (skip) return true;
|
||||
|
||||
const content = matchedGroups.map(m => `${config.E621_BASE_URL}/${path}/${m[1]}`).join('\n');
|
||||
|
||||
if (content.trim().length > 0) return content.trim();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async function postHandler(transform: ((idString: string) => number) | null, message: Message, matchedGroups: RegExpExecArray[]): Promise<string | boolean> {
|
||||
if (!message.guildId) return true;
|
||||
|
||||
const posts: E621Post[] = [];
|
||||
|
||||
for (const match of matchedGroups) {
|
||||
try {
|
||||
const post = await getE621Post(transform ? transform(match[1]) : match[1]);
|
||||
if (post) posts.push(post);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (await blacklistIfNecessary(message, posts)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async function imageHandler(message: Message, matchedGroups: RegExpExecArray[]): Promise<string | boolean> {
|
||||
if (!message.guildId) return true;
|
||||
|
||||
const posts: E621Post[] = [];
|
||||
|
||||
for (const match of matchedGroups) {
|
||||
try {
|
||||
const post = await getE621PostByMd5(match[1]);
|
||||
if (post) posts.push(post);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (await blacklistIfNecessary(message, posts)) return false;
|
||||
|
||||
const skip = await channelIgnoresLinks(message.channel as GuildBasedChannel);
|
||||
|
||||
if (skip) return true;
|
||||
|
||||
const content = posts.map(post => `<${getPostUrl(post)}>`).join('\n');
|
||||
|
||||
if (content.trim().length > 0) return content.trim();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async function githubPullRequestHandler(message: Message, matchedGroups: RegExpExecArray[]): Promise<string | boolean> {
|
||||
const skip = await channelIgnoresLinks(message.channel as GuildBasedChannel);
|
||||
|
||||
if (skip) return true;
|
||||
|
||||
let content = '';
|
||||
|
||||
for (const group of matchedGroups) {
|
||||
content += `${config.GIT_REPO_BASE_URL}/pull/${group[1]}\n`;
|
||||
}
|
||||
|
||||
if (content.trim().length > 0) return content.trim();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async function githubIssueHandler(message: Message, matchedGroups: RegExpExecArray[]): Promise<string | boolean> {
|
||||
const skip = await channelIgnoresLinks(message.channel as GuildBasedChannel);
|
||||
|
||||
if (skip) return true;
|
||||
|
||||
let content = '';
|
||||
|
||||
for (const group of matchedGroups) {
|
||||
content += `${config.GIT_REPO_BASE_URL}/issues/${group[1]}\n`;
|
||||
}
|
||||
|
||||
if (content.trim().length > 0) return content.trim();
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
import { AnyThreadChannel } from 'discord.js';
|
||||
|
||||
export async function handleThreadCreate(thread: AnyThreadChannel, newlyCreated: boolean) {
|
||||
try {
|
||||
await thread.join();
|
||||
} catch (e) {
|
||||
console.error('Failed to join thread:');
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
import { AnyThreadChannel } from 'discord.js';
|
||||
|
||||
export async function handleThreadCreate(thread: AnyThreadChannel, newlyCreated: boolean) {
|
||||
try {
|
||||
await thread.join();
|
||||
} catch (e) {
|
||||
console.error('Failed to join thread:');
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,46 +1,46 @@
|
||||
import { Guild, GuildMember, GuildTextBasedChannel, time, VoiceBasedChannel, VoiceState } from 'discord.js';
|
||||
import { Database } from '../shared/Database';
|
||||
|
||||
export async function handleVoiceStateUpdate(oldState: VoiceState, newState: VoiceState) {
|
||||
// The logChannel declaration being inside is purposeful, as this event is fired a lot for users talking.
|
||||
if (newState.channelId != null && oldState.channelId != null && newState.channelId != oldState.channelId) {
|
||||
const logChannel = await getVoiceLogsChannel(newState.guild);
|
||||
if (!logChannel) return;
|
||||
|
||||
await sendMovedMessage(logChannel, newState.member!, oldState.channel!, newState.channel!);
|
||||
} else if (oldState.channelId == null && newState.channelId != null) {
|
||||
const logChannel = await getVoiceLogsChannel(newState.guild);
|
||||
if (!logChannel) return;
|
||||
|
||||
await sendJoinMessage(logChannel, newState.member!, newState.channel!);
|
||||
} else if (newState.channelId == null && oldState.channelId != null) {
|
||||
const logChannel = await getVoiceLogsChannel(newState.guild);
|
||||
if (!logChannel) return;
|
||||
|
||||
await sendLeftMessage(logChannel, newState.member!, oldState.channel!);
|
||||
}
|
||||
}
|
||||
|
||||
async function sendJoinMessage(channel: GuildTextBasedChannel, member: GuildMember, voiceChannel: VoiceBasedChannel) {
|
||||
await channel.send(`${member} joined ${voiceChannel} at ${time()}`);
|
||||
}
|
||||
|
||||
async function sendLeftMessage(channel: GuildTextBasedChannel, member: GuildMember, voiceChannel: VoiceBasedChannel) {
|
||||
await channel.send(`${member} left ${voiceChannel} at ${time()}`);
|
||||
}
|
||||
|
||||
async function sendMovedMessage(channel: GuildTextBasedChannel, member: GuildMember, oldVoiceChannel: VoiceBasedChannel, newVoiceChannel: VoiceBasedChannel) {
|
||||
await channel.send(`${member} moved from ${oldVoiceChannel} to ${newVoiceChannel} at ${time()}`);
|
||||
}
|
||||
|
||||
async function getVoiceLogsChannel(guild: Guild): Promise<GuildTextBasedChannel | undefined> {
|
||||
const settings = await Database.getGuildSettings(guild.id);
|
||||
|
||||
if (!settings || !settings.voice_logs_channel_id) return;
|
||||
|
||||
const channel = await guild.channels.fetch(settings.voice_logs_channel_id);
|
||||
|
||||
if (!channel || !channel.isSendable()) return;
|
||||
|
||||
return channel as GuildTextBasedChannel;
|
||||
import { Guild, GuildMember, GuildTextBasedChannel, time, VoiceBasedChannel, VoiceState } from 'discord.js';
|
||||
import { Database } from '../shared/Database';
|
||||
|
||||
export async function handleVoiceStateUpdate(oldState: VoiceState, newState: VoiceState) {
|
||||
// The logChannel declaration being inside is purposeful, as this event is fired a lot for users talking.
|
||||
if (newState.channelId != null && oldState.channelId != null && newState.channelId != oldState.channelId) {
|
||||
const logChannel = await getVoiceLogsChannel(newState.guild);
|
||||
if (!logChannel) return;
|
||||
|
||||
await sendMovedMessage(logChannel, newState.member!, oldState.channel!, newState.channel!);
|
||||
} else if (oldState.channelId == null && newState.channelId != null) {
|
||||
const logChannel = await getVoiceLogsChannel(newState.guild);
|
||||
if (!logChannel) return;
|
||||
|
||||
await sendJoinMessage(logChannel, newState.member!, newState.channel!);
|
||||
} else if (newState.channelId == null && oldState.channelId != null) {
|
||||
const logChannel = await getVoiceLogsChannel(newState.guild);
|
||||
if (!logChannel) return;
|
||||
|
||||
await sendLeftMessage(logChannel, newState.member!, oldState.channel!);
|
||||
}
|
||||
}
|
||||
|
||||
async function sendJoinMessage(channel: GuildTextBasedChannel, member: GuildMember, voiceChannel: VoiceBasedChannel) {
|
||||
await channel.send(`${member} joined ${voiceChannel} at ${time()}`);
|
||||
}
|
||||
|
||||
async function sendLeftMessage(channel: GuildTextBasedChannel, member: GuildMember, voiceChannel: VoiceBasedChannel) {
|
||||
await channel.send(`${member} left ${voiceChannel} at ${time()}`);
|
||||
}
|
||||
|
||||
async function sendMovedMessage(channel: GuildTextBasedChannel, member: GuildMember, oldVoiceChannel: VoiceBasedChannel, newVoiceChannel: VoiceBasedChannel) {
|
||||
await channel.send(`${member} moved from ${oldVoiceChannel} to ${newVoiceChannel} at ${time()}`);
|
||||
}
|
||||
|
||||
async function getVoiceLogsChannel(guild: Guild): Promise<GuildTextBasedChannel | undefined> {
|
||||
const settings = await Database.getGuildSettings(guild.id);
|
||||
|
||||
if (!settings || !settings.voice_logs_channel_id) return;
|
||||
|
||||
const channel = await guild.channels.fetch(settings.voice_logs_channel_id);
|
||||
|
||||
if (!channel || !channel.isSendable()) return;
|
||||
|
||||
return channel as GuildTextBasedChannel;
|
||||
}
|
||||
+7
-7
@@ -1,7 +1,7 @@
|
||||
export * from './handle-audit-log-create';
|
||||
export * from './handle-ban-remove';
|
||||
export * from './handle-guild-create';
|
||||
export * from './handle-member-join';
|
||||
export * from './handle-message';
|
||||
export * from './handle-thread-create';
|
||||
export * from './handle-voice-state-update';
|
||||
export * from './handle-audit-log-create';
|
||||
export * from './handle-ban-remove';
|
||||
export * from './handle-guild-create';
|
||||
export * from './handle-member-join';
|
||||
export * from './handle-message';
|
||||
export * from './handle-thread-create';
|
||||
export * from './handle-voice-state-update';
|
||||
|
||||
Reference in New Issue
Block a user