more wip work yippeee
This commit is contained in:
parent
72b835f475
commit
ad48cd516e
|
@ -16,6 +16,7 @@
|
|||
"@discordjs/rest": "^2.2.0",
|
||||
"chalk": "^4.1.2",
|
||||
"d3-array": "^2.12.1",
|
||||
"diff": "^5.2.0",
|
||||
"discord.js": "^14.14.1",
|
||||
"express": "^4.18.3",
|
||||
"express-handlebars": "^7.1.2",
|
||||
|
@ -31,6 +32,7 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@types/d3-array": "^3.2.1",
|
||||
"@types/diff": "^5.0.9",
|
||||
"@types/express": "^4.17.21",
|
||||
"@types/parse-color": "^1.0.3",
|
||||
"@types/tough-cookie": "^4.0.5",
|
||||
|
|
|
@ -17,6 +17,9 @@ dependencies:
|
|||
d3-array:
|
||||
specifier: ^2.12.1
|
||||
version: 2.12.1
|
||||
diff:
|
||||
specifier: ^5.2.0
|
||||
version: 5.2.0
|
||||
discord.js:
|
||||
specifier: ^14.14.1
|
||||
version: 14.14.1
|
||||
|
@ -58,6 +61,9 @@ devDependencies:
|
|||
'@types/d3-array':
|
||||
specifier: ^3.2.1
|
||||
version: 3.2.1
|
||||
'@types/diff':
|
||||
specifier: ^5.0.9
|
||||
version: 5.0.9
|
||||
'@types/express':
|
||||
specifier: ^4.17.21
|
||||
version: 4.17.21
|
||||
|
@ -372,6 +378,10 @@ packages:
|
|||
resolution: {integrity: sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==}
|
||||
dev: true
|
||||
|
||||
/@types/diff@5.0.9:
|
||||
resolution: {integrity: sha512-RWVEhh/zGXpAVF/ZChwNnv7r4rvqzJ7lYNSmZSVTxjV0PBLf6Qu7RNg+SUtkpzxmiNkjCx0Xn2tPp7FIkshJwQ==}
|
||||
dev: true
|
||||
|
||||
/@types/express-serve-static-core@4.17.43:
|
||||
resolution: {integrity: sha512-oaYtiBirUOPQGSWNGPWnzyAFJ0BP3cwvN4oWZQY+zUBwpVIGsKUkpBpSztp74drYcjavs7SKFZ4DX1V2QeN8rg==}
|
||||
dependencies:
|
||||
|
@ -1043,6 +1053,11 @@ packages:
|
|||
engines: {node: '>=8'}
|
||||
dev: false
|
||||
|
||||
/diff@5.2.0:
|
||||
resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==}
|
||||
engines: {node: '>=0.3.1'}
|
||||
dev: false
|
||||
|
||||
/dir-glob@3.0.1:
|
||||
resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==}
|
||||
engines: {node: '>=8'}
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
import { ActionRowBuilder, ButtonBuilder, ButtonStyle, CommandInteraction, ComponentType, SlashCommandBuilder, StringSelectMenuBuilder } from 'discord.js';
|
||||
import { Command } from '../types/index';
|
||||
import { AuditLog, db } from '../lib/db';
|
||||
import { EventType } from '../lib/events';
|
||||
import { chunks } from '../lib/util';
|
||||
|
||||
export default {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('auditlog')
|
||||
.setDescription('[ADMIN] Set up an audit logger, or edit an existing one')
|
||||
.setDefaultMemberPermissions('0'),
|
||||
|
||||
execute: async (interaction: CommandInteraction) => {
|
||||
if (!interaction.isChatInputCommand()) return;
|
||||
|
||||
const channel = interaction.channelId;
|
||||
|
||||
if (!channel) return;
|
||||
|
||||
const log = await db<AuditLog>('auditLogs')
|
||||
.select('eventTypes')
|
||||
.where('channel', channel)
|
||||
.first();
|
||||
|
||||
let types = log ? log.eventTypes.split(',') : [];
|
||||
|
||||
const options = Object.values(EventType)
|
||||
.map(event => ({
|
||||
label: event,
|
||||
value: event,
|
||||
}));
|
||||
|
||||
const chunked = [...chunks(options, 25)];
|
||||
|
||||
const selectRows =
|
||||
chunked.map(
|
||||
(opt, i) => new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(
|
||||
new StringSelectMenuBuilder()
|
||||
.addOptions(
|
||||
...opt
|
||||
)
|
||||
.setMinValues(0)
|
||||
.setMaxValues(opt.length)
|
||||
.setCustomId(`auditlog-select-events-${i}`)
|
||||
)
|
||||
);
|
||||
|
||||
const components = [
|
||||
...selectRows,
|
||||
new ActionRowBuilder<ButtonBuilder>().addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId('auditlog-select-events-done')
|
||||
.setLabel('Done')
|
||||
.setStyle(ButtonStyle.Primary)
|
||||
.setDisabled(true)
|
||||
),
|
||||
];
|
||||
|
||||
const formatContent = () =>
|
||||
`${log ? `Editing audit log in <#${channel}>` : `Creating audit log in <#${channel}>`}\n` +
|
||||
'Select the types of events to be reported on' + ((types.length > 0) ? `\n**Current events**: ${types.map(s => '`' + s + '`').join(', ')}` : '');
|
||||
|
||||
const msg = await interaction.reply({
|
||||
ephemeral: true,
|
||||
content: formatContent(),
|
||||
components,
|
||||
});
|
||||
|
||||
const selectCollector = msg.createMessageComponentCollector({
|
||||
componentType: ComponentType.StringSelect,
|
||||
time: 60_000 * 5,
|
||||
});
|
||||
|
||||
selectCollector.on('collect', async selectInteraction => {
|
||||
const possibleTypes = selectInteraction.component.options.map(opt => opt.value);
|
||||
const selectedTypes = selectInteraction.values;
|
||||
types = types.filter(t => !possibleTypes.includes(t));
|
||||
types = [...types, ...selectedTypes];
|
||||
|
||||
components[components.length - 1].components[0].setDisabled(types.length === 0);
|
||||
|
||||
await selectInteraction.reply({
|
||||
content: 'Hit "Done" when finished',
|
||||
ephemeral: true,
|
||||
});
|
||||
|
||||
await msg.edit({
|
||||
content: formatContent(),
|
||||
components,
|
||||
});
|
||||
});
|
||||
selectCollector.on('end', async () => {
|
||||
await msg.edit({
|
||||
content: formatContent(),
|
||||
components: [],
|
||||
});
|
||||
});
|
||||
|
||||
const buttonInteraction = await msg.awaitMessageComponent({ componentType: ComponentType.Button, time: 60_000 * 5 });
|
||||
selectCollector.stop();
|
||||
|
||||
if (log) {
|
||||
await db<AuditLog>('auditLogs')
|
||||
.where('channel', channel)
|
||||
.update({
|
||||
eventTypes: types.join(','),
|
||||
});
|
||||
} else {
|
||||
await db<AuditLog>('auditLogs')
|
||||
.insert({
|
||||
guild: interaction.guildId!,
|
||||
channel: channel,
|
||||
eventTypes: types.join(','),
|
||||
});
|
||||
}
|
||||
|
||||
await buttonInteraction.reply({
|
||||
content: 'Audit log successfully created.',
|
||||
components: [],
|
||||
ephemeral: true,
|
||||
});
|
||||
}
|
||||
} satisfies Command;
|
|
@ -1,12 +1,10 @@
|
|||
import { Sticker, ThreadChannel, Message, User, Invite, GuildMember, GuildScheduledEvent, GuildEmoji, Channel, Client, Events, GuildAuditLogsEntry, AuditLogEvent } from 'discord.js';
|
||||
import { Sticker, ThreadChannel, Message, User, Invite, GuildMember, GuildScheduledEvent, GuildEmoji, Client, Events, AuditLogEvent, ChannelType, PartialMessage, EmbedBuilder, MessageType } from 'discord.js';
|
||||
import { AuditLog, db } from './db';
|
||||
import * as log from './log';
|
||||
import { formatMessageAsEmbed, getUploadLimitForGuild, shortenStr } from './util';
|
||||
import { Change, diffWords } from 'diff';
|
||||
|
||||
export enum EventType {
|
||||
ChannelCreate = 'CHANNEL_CREATE',
|
||||
ChannelRename = 'CHANNEL_RENAME',
|
||||
ChannelDelete = 'CHANNEL_DELETE',
|
||||
|
||||
EmojiCreate = 'EMOJI_CREATE',
|
||||
EmojiRename = 'EMOJI_RENAME',
|
||||
EmojiDelete = 'EMOJI_DELETE',
|
||||
|
@ -16,7 +14,6 @@ export enum EventType {
|
|||
EventDelete = 'EVENT_DELETE',
|
||||
|
||||
InviteCreate = 'INVITE_CREATE',
|
||||
InviteUpdate = 'INVITE_UPDATE',
|
||||
InviteDelete = 'INVITE_DELETE',
|
||||
|
||||
MemberBan = 'MEMBER_BAN',
|
||||
|
@ -24,7 +21,8 @@ export enum EventType {
|
|||
MemberKick = 'MEMBER_KICK',
|
||||
MemberDisconnect = 'MEMBER_DISCONNECT',
|
||||
MemberNickname = 'MEMBER_NICKNAME',
|
||||
MemberChangeRoles = 'MEMBER_CHANGE_ROLES',
|
||||
MemberJoin = 'MEMBER_JOIN',
|
||||
MemberLeave = 'MEMBER_LEAVE',
|
||||
|
||||
MessageDelete = 'MESSAGE_DELETE',
|
||||
MessageEdit = 'MESSAGE_EDIT',
|
||||
|
@ -39,129 +37,107 @@ export enum EventType {
|
|||
}
|
||||
|
||||
export type Event = {
|
||||
type: EventType.ChannelCreate,
|
||||
causer: User,
|
||||
channel: Channel,
|
||||
} | {
|
||||
type: EventType.ChannelRename,
|
||||
causer: User,
|
||||
channel: Channel,
|
||||
oldName: string, newName: string,
|
||||
} | {
|
||||
type: EventType.ChannelDelete,
|
||||
causer: User,
|
||||
channel: Channel,
|
||||
} | {
|
||||
type: EventType.EmojiCreate,
|
||||
causer: User,
|
||||
causer: User | null,
|
||||
emoji: GuildEmoji,
|
||||
} | {
|
||||
type: EventType.EmojiRename,
|
||||
causer: User,
|
||||
causer: User | null,
|
||||
emoji: GuildEmoji,
|
||||
oldName: string, newName: string,
|
||||
} | {
|
||||
type: EventType.EmojiDelete,
|
||||
causer: User,
|
||||
causer: User | null,
|
||||
emoji: GuildEmoji,
|
||||
} | {
|
||||
type: EventType.EventCreate,
|
||||
causer: User,
|
||||
causer: User | null,
|
||||
event: GuildScheduledEvent,
|
||||
} | {
|
||||
type: EventType.EventEdit,
|
||||
causer: User,
|
||||
causer: User | null,
|
||||
oldEvent: GuildScheduledEvent,
|
||||
event: GuildScheduledEvent,
|
||||
} | {
|
||||
type: EventType.EventDelete,
|
||||
causer: User,
|
||||
causer: User | null,
|
||||
event: GuildScheduledEvent,
|
||||
} | {
|
||||
type: EventType.InviteCreate,
|
||||
causer: User,
|
||||
invite: Invite,
|
||||
} | {
|
||||
type: EventType.InviteUpdate,
|
||||
causer: User,
|
||||
oldInvite: Invite,
|
||||
causer: User | null,
|
||||
invite: Invite,
|
||||
} | {
|
||||
type: EventType.InviteDelete,
|
||||
causer: User,
|
||||
causer: User | null,
|
||||
invite: Invite,
|
||||
} | {
|
||||
type: EventType.MemberBan,
|
||||
causer: User,
|
||||
causer: User | null,
|
||||
member: GuildMember,
|
||||
reason: string,
|
||||
} | {
|
||||
type: EventType.MemberUnban,
|
||||
causer: User,
|
||||
causer: User | null,
|
||||
member: GuildMember,
|
||||
reason: string,
|
||||
} | {
|
||||
type: EventType.MemberKick,
|
||||
causer: User,
|
||||
causer: User | null,
|
||||
member: GuildMember,
|
||||
reason: string,
|
||||
} | {
|
||||
type: EventType.MemberDisconnect,
|
||||
causer: User,
|
||||
causer: User | null,
|
||||
member: GuildMember,
|
||||
} | {
|
||||
type: EventType.MemberNickname,
|
||||
causer: User,
|
||||
causer: User | null,
|
||||
member: GuildMember,
|
||||
oldNickname: string, newNickname: string,
|
||||
} | {
|
||||
type: EventType.MemberChangeRoles,
|
||||
causer: User,
|
||||
member: GuildMember,
|
||||
// TODO: huh
|
||||
} | {
|
||||
type: EventType.MessageDelete,
|
||||
causer: User,
|
||||
message: Message,
|
||||
causer: User | null,
|
||||
message: Message | PartialMessage,
|
||||
} | {
|
||||
type: EventType.MessageEdit,
|
||||
causer: User,
|
||||
causer: User | null,
|
||||
oldMessage: Message,
|
||||
message: Message,
|
||||
} | {
|
||||
type: EventType.StickerCreate,
|
||||
causer: User,
|
||||
causer: User | null,
|
||||
sticker: Sticker,
|
||||
} | {
|
||||
type: EventType.StickerRename,
|
||||
causer: User,
|
||||
causer: User | null,
|
||||
oldName: string, newName: string,
|
||||
} | {
|
||||
type: EventType.StickerDelete,
|
||||
causer: User,
|
||||
causer: User | null,
|
||||
sticker: Sticker,
|
||||
} | {
|
||||
type: EventType.ThreadCreate,
|
||||
causer: User,
|
||||
causer: User | null,
|
||||
thread: ThreadChannel,
|
||||
} | {
|
||||
type: EventType.ThreadEdit,
|
||||
causer: User,
|
||||
causer: User | null,
|
||||
oldThread: ThreadChannel,
|
||||
thread: ThreadChannel,
|
||||
} | {
|
||||
type: EventType.ThreadDelete,
|
||||
causer: User,
|
||||
causer: User | null,
|
||||
thread: ThreadChannel,
|
||||
};
|
||||
|
||||
export async function triggerEvent(bot: Client, event: Event) {
|
||||
export async function triggerEvent(bot: Client, guildId: string, event: Event) {
|
||||
const type = event.type;
|
||||
|
||||
log.info(`Got event ${event.type}: ${JSON.stringify(event)}`);
|
||||
log.info(`got event ${event.type}`);
|
||||
|
||||
const logs = await db<AuditLog>('auditLogs')
|
||||
.select('guild', 'channel')
|
||||
.where('guild', guildId)
|
||||
// @ts-expect-error this LITERALLY works
|
||||
.whereLike(db.raw('UPPER(eventTypes)'), `%${type.toUpperCase()}%`);
|
||||
|
||||
|
@ -173,31 +149,144 @@ export async function triggerEvent(bot: Client, event: Event) {
|
|||
log.warn(err);
|
||||
}
|
||||
|
||||
if (channel && channel.isText()) {
|
||||
channel.send(`${JSON.stringify(event)}`);
|
||||
if (channel && (channel.type === ChannelType.GuildText)) {
|
||||
switch(event.type) {
|
||||
case EventType.MessageDelete: {
|
||||
const limit = getUploadLimitForGuild(channel.guild);
|
||||
const { embed, attach } = formatMessageAsEmbed(event.message, limit);
|
||||
|
||||
const logEmbed = new EmbedBuilder()
|
||||
.setDescription(`Message sent by ${event.message.author} in <#${event.message.channelId}> deleted by **${event.causer}**:`)
|
||||
.setTimestamp();
|
||||
|
||||
channel.send({
|
||||
embeds: [ logEmbed, embed ],
|
||||
files: attach ? [{
|
||||
attachment: attach.url,
|
||||
name: attach.name,
|
||||
description: attach.url,
|
||||
}] : [],
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
case EventType.MessageEdit: {
|
||||
const logEmbed = new EmbedBuilder()
|
||||
.setDescription(`Message sent by ${event.message.author} in <#${event.message.channelId}> edited:`)
|
||||
.setTimestamp();
|
||||
|
||||
const diff = diffWords(event.oldMessage.content.replace(/`/g, ''), event.message.content.replace(/`/g, ''));
|
||||
let output = '';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
diff.forEach((part: Change) => {
|
||||
if (part.added) output += `\x1B[2;32m\x1B[1;32m${part.value}\x1B[0m`;
|
||||
if (part.removed) output += `\x1B[2;41m\x1B[1;2m${part.value}\x1B[0m`;
|
||||
if (!part.added && !part.removed) output += part.value;
|
||||
});
|
||||
|
||||
const diffMsg = `\`\`\`ansi\n${shortenStr(output, 4000)}\`\`\``;
|
||||
|
||||
const editEmbed = new EmbedBuilder()
|
||||
.setAuthor({ name: event.message.author.tag, iconURL: event.message.author.displayAvatarURL() })
|
||||
.setDescription(diffMsg);
|
||||
|
||||
channel.send({
|
||||
embeds: [ logEmbed, editEmbed ],
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
case EventType.InviteCreate: {
|
||||
const logEmbed = new EmbedBuilder()
|
||||
.setDescription(`Invite ${event.invite.code} created by ${event.causer} in <#${event.invite.channelId}>`)
|
||||
.setTimestamp();
|
||||
|
||||
channel.send({
|
||||
embeds: [ logEmbed ],
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
case EventType.InviteDelete: {
|
||||
const logEmbed = new EmbedBuilder()
|
||||
.setDescription(`Invite ${event.invite.code} made by ${event.invite.inviter} in <#${event.invite.channelId}> deleted by ${event.causer}`)
|
||||
.setTimestamp();
|
||||
|
||||
channel.send({
|
||||
embeds: [ logEmbed ],
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.warn(`Channel ${auditLog.channel} from guild ${auditLog.guild} not found! Deleting audit log`);
|
||||
await db<AuditLog>('auditLogs')
|
||||
.where('guild', auditLog.guild)
|
||||
.where('channel', auditLog.channel)
|
||||
.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const DELETE_FETCH_TIMEOUT = 2_000;
|
||||
|
||||
export function setupListeners(bot: Client) {
|
||||
/*bot.on(Events.GuildAuditLogEntryCreate, async (auditLog: GuildAuditLogsEntry) => {
|
||||
const { action, extra: channel, executorId, targetId } = auditLog;
|
||||
bot.on(Events.MessageDelete, async message => {
|
||||
if (!message.guild) return;
|
||||
if (!(message.type === MessageType.Default || message.type === MessageType.Reply || message.type === MessageType.ThreadStarterMessage)) return;
|
||||
|
||||
// Check only for deleted messages.
|
||||
if (action !== AuditLogEvent.MessageDelete) return;
|
||||
let causer: User | null = message.author;
|
||||
|
||||
// Ensure the executor is cached.
|
||||
const executor = await bot.users.fetch(executorId);
|
||||
const audit = await message.guild.fetchAuditLogs({
|
||||
type: AuditLogEvent.MessageDelete,
|
||||
limit: 1,
|
||||
});
|
||||
|
||||
// Ensure the author whose message was deleted is cached.
|
||||
const target = await bot.users.fetch(targetId);
|
||||
const entry = audit.entries.first();
|
||||
|
||||
// Log the output.
|
||||
console.log(`A message by ${target.tag} was deleted by ${executor.tag} in ${channel}.`);
|
||||
});*/
|
||||
if (entry && ((Date.now() - entry.createdTimestamp) < DELETE_FETCH_TIMEOUT || (entry.extra.count > 1)) && entry.targetId === message.author?.id) {
|
||||
causer = entry.executor;
|
||||
}
|
||||
|
||||
triggerEvent(bot, message.guild.id, {
|
||||
type: EventType.MessageDelete,
|
||||
causer, message,
|
||||
});
|
||||
});
|
||||
bot.on(Events.MessageUpdate, async (old, message) => {
|
||||
if (!message.guild) return;
|
||||
if (message.content === old.content) return;
|
||||
if (message.partial || old.partial) return;
|
||||
|
||||
triggerEvent(bot, message.guild.id, {
|
||||
type: EventType.MessageEdit,
|
||||
causer: message.author,
|
||||
oldMessage: old, message: message,
|
||||
});
|
||||
});
|
||||
bot.on(Events.InviteCreate, async invite => {
|
||||
if (!invite.guild || !invite.inviter) return;
|
||||
|
||||
triggerEvent(bot, invite.guild.id, {
|
||||
type: EventType.InviteCreate,
|
||||
causer: invite.inviter, invite,
|
||||
});
|
||||
});
|
||||
|
||||
bot.on(Events.GuildAuditLogEntryCreate, async (entry, guild) => {
|
||||
if (entry.action === AuditLogEvent.InviteDelete) {
|
||||
if (!entry.target) return;
|
||||
|
||||
triggerEvent(bot, guild.id, {
|
||||
type: EventType.InviteDelete,
|
||||
causer: entry.executor,
|
||||
// @ts-expect-error shut up
|
||||
invite: entry.target
|
||||
});
|
||||
} else if (entry.action === AuditLogEvent.EmojiCreate) {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
import { Interaction, Message, TextBasedChannel, User } from 'discord.js';
|
||||
import * as log from '../lib/log';
|
||||
import { chunks } from './util';
|
||||
|
||||
export const RANDOM_WORDS = [
|
||||
'tarsorado', 'aboba', 'radiation', 'extreme', 'glogging', 'glogged', 'penis', 'easy', 'glue', 'contaminated water',
|
||||
|
@ -140,12 +141,6 @@ export async function getTextResponsePrettyPlease(user: User, prompt: string, fi
|
|||
return randomWord();
|
||||
}
|
||||
|
||||
function* chunks(arr: unknown[], n: number) {
|
||||
for (let i = 0; i < arr.length; i += n) {
|
||||
yield arr.slice(i, i + n);
|
||||
}
|
||||
}
|
||||
|
||||
export async function sendSegments(segments: string[], channel: TextBasedChannel) {
|
||||
const content = [];
|
||||
let contentBuffer = '';
|
||||
|
|
|
@ -2,6 +2,7 @@ import * as fsp from 'fs/promises';
|
|||
import { tmpdir } from 'os';
|
||||
import { join } from 'path';
|
||||
import { randomBytes } from 'crypto';
|
||||
import { Attachment, EmbedBuilder, Guild, GuildPremiumTier, Message, PartialMessage } from 'discord.js';
|
||||
|
||||
export async function exists(file: string) {
|
||||
try {
|
||||
|
@ -41,3 +42,91 @@ export class Right<R> {
|
|||
constructor(private readonly value: R) {}
|
||||
public getValue() { return this.value; }
|
||||
}
|
||||
|
||||
export function* chunks<T>(arr: T[], n: number) {
|
||||
for (let i = 0; i < arr.length; i += n) {
|
||||
yield arr.slice(i, i + n);
|
||||
}
|
||||
}
|
||||
|
||||
export function shortenStr(str: string, chars: number) {
|
||||
if (str.length > chars)
|
||||
return str.slice(0, chars - 1) + '…';
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
export const MAX_MESSAGE_SIZE = 2000;
|
||||
|
||||
export const defaultUploadLimit = 25 * 1024 * 1024;
|
||||
|
||||
export const getUploadLimitForGuild = (guild: Guild) => {
|
||||
switch (guild.premiumTier) {
|
||||
case GuildPremiumTier.Tier3: return 100 * 1024 * 1024;
|
||||
case GuildPremiumTier.Tier2: return 50 * 1024 * 1024;
|
||||
default: return defaultUploadLimit;
|
||||
}
|
||||
};
|
||||
|
||||
export function formatMessageAsEmbed(message: Message | PartialMessage, uploadLimit: number): { embed: EmbedBuilder, attach: Attachment | null } {
|
||||
const embed = new EmbedBuilder()
|
||||
.setTimestamp(message.createdTimestamp);
|
||||
|
||||
if (message.author) {
|
||||
embed.setAuthor({
|
||||
name: message.author.tag,
|
||||
iconURL: message.author.displayAvatarURL(),
|
||||
});
|
||||
}
|
||||
|
||||
let attach = null;
|
||||
|
||||
message.embeds.forEach(em => {
|
||||
if (em.image) {
|
||||
embed.setImage(em.image.url);
|
||||
} else if (em.thumbnail) {
|
||||
embed.setImage(em.thumbnail.url);
|
||||
}
|
||||
if (em.provider) {
|
||||
embed.addFields({
|
||||
name: shortenStr(`${em.provider.name}:`, 256),
|
||||
value: shortenStr(`${em.author}: **${em.title}**\n${em.description || ''}`, 2048),
|
||||
});
|
||||
} else if (!em.image) {
|
||||
embed.addFields({
|
||||
name: 'Embed:',
|
||||
value: shortenStr(em.description || em.fields[0]?.value || em.title || 'Unknown content', 2048),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
let msgEnd = null;
|
||||
|
||||
const first = message.attachments.first();
|
||||
|
||||
if (
|
||||
message.attachments.size === 1 && first && first.contentType
|
||||
&& !first.spoiler
|
||||
&& (first.contentType?.startsWith('image/')
|
||||
|| first.contentType?.startsWith('video/'))
|
||||
&& (first.size <= uploadLimit)
|
||||
) {
|
||||
attach = first;
|
||||
} else {
|
||||
for (const attach of message.attachments.values()) {
|
||||
msgEnd = (msgEnd || '\n') + `\n[${attach.name}](${attach.url})`;
|
||||
}
|
||||
}
|
||||
|
||||
if (message.content) {
|
||||
if (!msgEnd) {
|
||||
embed.setDescription(shortenStr(message.content, 4096));
|
||||
} else {
|
||||
embed.setDescription(shortenStr(message.content, 4096 - msgEnd.length) + msgEnd);
|
||||
}
|
||||
} else if (msgEnd) {
|
||||
embed.setDescription('_Content unknown_' + msgEnd);
|
||||
}
|
||||
|
||||
return { embed, attach };
|
||||
}
|
Loading…
Reference in New Issue