3
0
Fork 0
mirror of https://github.com/ZeppelinBot/Zeppelin.git synced 2025-05-25 10:25:01 +00:00

Merge branch 'ZeppelinBot:master' into master

This commit is contained in:
Code 2022-08-15 01:04:11 -04:00 committed by GitHub
commit 3f06cab4a4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
41 changed files with 287 additions and 62 deletions

11
.clabot
View file

@ -1,19 +1,22 @@
{
"contributors": [
"dependabot",
"BanTheNons",
"CleverSource",
"DarkView",
"DenverCoder1",
"Jernik",
"WeebHiroyuki",
"Rstar284",
"almeidx",
"axisiscool",
"dexbiobot",
"greenbigfrog",
"k200-1",
"metal0",
"paolojpa",
"roflmaoqwerty",
"thewilloftheshadow",
"usoka",
"vcokltfre",
"Rstar284"
"vcokltfre"
],
"message": "Thank you for contributing to Zeppelin! We require contributors to sign our Contributor License Agreement (CLA). To let us review and merge your code, please visit https://github.com/ZeppelinBot/CLA to sign the CLA!"
}

View file

@ -31,4 +31,4 @@ Only edit files in `/backend/src`, `/shared/src`, and `/dashboard/src`.
Make sure to revert any hotfixes before updating the bot normally.
## View logs
To view real-time logs, run `docker compose -f docker-compose.production.yml -t -f logs`
To view real-time logs, run `docker compose -f docker-compose.production.yml logs -t -f`

View file

@ -80,7 +80,8 @@ export class ApiPermissionAssignments extends BaseRepository {
.createQueryBuilder()
.where("expires_at IS NOT NULL")
.andWhere("expires_at <= NOW()")
.delete();
.delete()
.execute();
}
async applyOwnerChange(guildId: string, newOwnerId: string) {

View file

@ -6,6 +6,7 @@ import { AlertAction } from "./alert";
import { ArchiveThreadAction } from "./archiveThread";
import { BanAction } from "./ban";
import { ChangeNicknameAction } from "./changeNickname";
import { ChangePermsAction } from "./changePerms";
import { CleanAction } from "./clean";
import { KickAction } from "./kick";
import { LogAction } from "./log";
@ -36,6 +37,7 @@ export const availableActions: Record<string, AutomodActionBlueprint<any>> = {
set_slowmode: SetSlowmodeAction,
start_thread: StartThreadAction,
archive_thread: ArchiveThreadAction,
change_perms: ChangePermsAction,
};
export const AvailableActions = t.type({
@ -56,4 +58,5 @@ export const AvailableActions = t.type({
set_slowmode: SetSlowmodeAction.configType,
start_thread: StartThreadAction.configType,
archive_thread: ArchiveThreadAction.configType,
change_perms: ChangePermsAction.configType,
});

View file

@ -0,0 +1,96 @@
import { Permissions, PermissionString } from "discord.js";
import * as t from "io-ts";
import { automodAction } from "../helpers";
import { tNullable, isValidSnowflake, tPartialDictionary } from "../../../utils";
import { noop } from "knub/dist/utils";
import { renderTemplate, TemplateSafeValueContainer } from "../../../templateFormatter";
import {
guildToTemplateSafeGuild,
savedMessageToTemplateSafeSavedMessage,
userToTemplateSafeUser,
} from "../../../utils/templateSafeObjects";
export const ChangePermsAction = automodAction({
configType: t.type({
target: t.string,
channel: tNullable(t.string),
perms: tPartialDictionary(t.keyof(Permissions.FLAGS), tNullable(t.boolean)),
}),
defaultConfig: {},
async apply({ pluginData, contexts, actionConfig, ruleName }) {
const user = contexts.find((c) => c.user)?.user;
const message = contexts.find((c) => c.message)?.message;
const renderTarget = async (str: string) =>
renderTemplate(
str,
new TemplateSafeValueContainer({
user: user ? userToTemplateSafeUser(user) : null,
guild: guildToTemplateSafeGuild(pluginData.guild),
message: message ? savedMessageToTemplateSafeSavedMessage(message) : null,
}),
);
const renderChannel = async (str: string) =>
renderTemplate(
str,
new TemplateSafeValueContainer({
user: user ? userToTemplateSafeUser(user) : null,
guild: guildToTemplateSafeGuild(pluginData.guild),
message: message ? savedMessageToTemplateSafeSavedMessage(message) : null,
}),
);
const target = await renderTarget(actionConfig.target);
const channelId = actionConfig.channel ? await renderChannel(actionConfig.channel) : null;
const role = pluginData.guild.roles.resolve(target);
if (!role) {
const member = await pluginData.guild.members.fetch(target).catch(noop);
if (!member) return;
}
if (channelId && isValidSnowflake(channelId)) {
const channel = pluginData.guild.channels.resolve(channelId);
if (!channel || channel.isThread()) return;
const overwrite = channel.permissionOverwrites.cache.find((pw) => pw.id === target);
const allow = new Permissions(overwrite?.allow ?? 0n).serialize();
const deny = new Permissions(overwrite?.deny ?? 0n).serialize();
const newPerms: Partial<Record<PermissionString, boolean | null>> = {};
for (const key in allow) {
if (typeof actionConfig.perms[key] !== "undefined") {
newPerms[key] = actionConfig.perms[key];
continue;
}
if (allow[key]) {
newPerms[key] = true;
} else if (deny[key]) {
newPerms[key] = false;
}
}
// takes more code lines but looks cleaner imo
let hasPerms = false;
for (const key in newPerms) {
if (typeof newPerms[key] === "boolean") {
hasPerms = true;
break;
}
}
if (overwrite && !hasPerms) {
await channel.permissionOverwrites.delete(target).catch(noop);
return;
}
await channel.permissionOverwrites.edit(target, newPerms).catch(noop);
return;
}
if (!role) return;
const perms = new Permissions(role.permissions).serialize();
for (const key in actionConfig.perms) {
perms[key] = actionConfig.perms[key];
}
const permsArray = <PermissionString[]>Object.keys(perms).filter((key) => perms[key]);
await role.setPermissions(new Permissions(permsArray)).catch(noop);
},
});

View file

@ -1,4 +1,4 @@
import { MessageEditOptions, MessageOptions } from "discord.js";
import { MessageEditOptions, MessageOptions, Util } from "discord.js";
import { GuildPluginData } from "knub";
import moment from "moment-timezone";
import { CaseTypes } from "../../../data/CaseTypes";
@ -67,7 +67,7 @@ export async function getCaseEmbed(
if (theCase.notes.length) {
for (const note of theCase.notes) {
const noteDate = moment.utc(note.created_at);
let noteBody = note.body.trim();
let noteBody = Util.escapeCodeBlock(note.body.trim());
if (noteBody === "") {
noteBody = emptyEmbedValue;
}

View file

@ -12,6 +12,7 @@ import {
import { SavedMessage } from "../../../data/entities/SavedMessage";
import { UnknownUser } from "../../../utils";
import { deactivateMentions, disableCodeBlocks } from "knub/dist/helpers";
import { resolveChannelIds } from "src/utils/resolveChannelIds";
interface LogCensorData {
user: User | UnknownUser;
@ -33,9 +34,8 @@ export function logCensor(pluginData: GuildPluginData<LogsPluginType>, data: Log
}),
{
userId: data.user.id,
channel: data.channel.id,
category: data.channel.parentId,
bot: data.user instanceof User ? data.user.bot : false,
...resolveChannelIds(data.channel),
},
);
}

View file

@ -5,6 +5,7 @@ import { log } from "../util/log";
import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter";
import { GuildChannel, NewsChannel } from "discord.js";
import { channelToTemplateSafeChannel } from "../../../utils/templateSafeObjects";
import { resolveChannelIds } from "../../../utils/resolveChannelIds";
interface LogChannelCreateData {
channel: GuildChannel | NewsChannel;
@ -18,8 +19,7 @@ export function logChannelCreate(pluginData: GuildPluginData<LogsPluginType>, da
channel: channelToTemplateSafeChannel(data.channel),
}),
{
channel: data.channel.id,
category: data.channel.parentId,
...resolveChannelIds(data.channel),
},
);
}

View file

@ -5,6 +5,7 @@ import { log } from "../util/log";
import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter";
import { GuildChannel, NewsChannel } from "discord.js";
import { channelToTemplateSafeChannel } from "../../../utils/templateSafeObjects";
import { resolveChannelIds } from "../../../utils/resolveChannelIds";
interface LogChannelDeleteData {
channel: GuildChannel | NewsChannel;
@ -18,8 +19,7 @@ export function logChannelDelete(pluginData: GuildPluginData<LogsPluginType>, da
channel: channelToTemplateSafeChannel(data.channel),
}),
{
channel: data.channel.id,
category: data.channel.parentId,
...resolveChannelIds(data.channel),
},
);
}

View file

@ -5,6 +5,7 @@ import { log } from "../util/log";
import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter";
import { GuildChannel, NewsChannel } from "discord.js";
import { channelToTemplateSafeChannel } from "../../../utils/templateSafeObjects";
import { resolveChannelIds } from "../../../utils/resolveChannelIds";
interface LogChannelUpdateData {
oldChannel: GuildChannel | NewsChannel;
@ -22,8 +23,7 @@ export function logChannelUpdate(pluginData: GuildPluginData<LogsPluginType>, da
differenceString: data.differenceString,
}),
{
channel: data.newChannel.id,
category: data.newChannel.parentId,
...resolveChannelIds(data.newChannel),
},
);
}

View file

@ -5,6 +5,7 @@ import { log } from "../util/log";
import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter";
import { BaseGuildTextChannel, User } from "discord.js";
import { channelToTemplateSafeChannel, userToTemplateSafeUser } from "../../../utils/templateSafeObjects";
import { resolveChannelIds } from "../../../utils/resolveChannelIds";
interface LogCleanData {
mod: User;
@ -24,8 +25,7 @@ export function logClean(pluginData: GuildPluginData<LogsPluginType>, data: LogC
archiveUrl: data.archiveUrl,
}),
{
channel: data.channel.id,
category: data.channel.parentId,
...resolveChannelIds(data.channel),
},
);
}

View file

@ -13,6 +13,7 @@ import moment from "moment-timezone";
import { ISavedMessageAttachmentData, SavedMessage } from "../../../data/entities/SavedMessage";
import { TimeAndDatePlugin } from "../../TimeAndDate/TimeAndDatePlugin";
import { UnknownUser, useMediaUrls } from "../../../utils";
import { resolveChannelIds } from "../../../utils/resolveChannelIds";
interface LogMessageDeleteData {
user: User | UnknownUser;
@ -47,10 +48,9 @@ export function logMessageDelete(pluginData: GuildPluginData<LogsPluginType>, da
}),
{
userId: data.user.id,
channel: data.channel.id,
category: data.channel.parentId,
messageTextContent: data.message.data.content,
bot: data.user instanceof User ? data.user.bot : false,
...resolveChannelIds(data.channel),
},
);
}

View file

@ -11,6 +11,7 @@ import {
} from "../../../utils/templateSafeObjects";
import { SavedMessage } from "../../../data/entities/SavedMessage";
import { UnknownUser } from "../../../utils";
import { resolveChannelIds } from "../../../utils/resolveChannelIds";
interface LogMessageDeleteAutoData {
message: SavedMessage;
@ -32,8 +33,7 @@ export function logMessageDeleteAuto(pluginData: GuildPluginData<LogsPluginType>
{
userId: data.user.id,
bot: data.user instanceof User ? data.user.bot : false,
channel: data.channel.id,
category: data.channel.parentId,
...resolveChannelIds(data.channel),
},
);
}

View file

@ -5,6 +5,7 @@ import { log } from "../util/log";
import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter";
import { BaseGuildTextChannel, GuildTextBasedChannel, ThreadChannel } from "discord.js";
import { channelToTemplateSafeChannel } from "../../../utils/templateSafeObjects";
import { resolveChannelIds } from "../../../utils/resolveChannelIds";
interface LogMessageDeleteBareData {
messageId: string;
@ -20,8 +21,7 @@ export function logMessageDeleteBare(pluginData: GuildPluginData<LogsPluginType>
channel: channelToTemplateSafeChannel(data.channel),
}),
{
channel: data.channel.id,
category: data.channel.parentId,
...resolveChannelIds(data.channel),
},
);
}

View file

@ -5,6 +5,7 @@ import { log } from "../util/log";
import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter";
import { BaseGuildTextChannel, GuildTextBasedChannel, ThreadChannel } from "discord.js";
import { channelToTemplateSafeChannel } from "../../../utils/templateSafeObjects";
import { resolveChannelIds } from "../../../utils/resolveChannelIds";
interface LogMessageDeleteBulkData {
count: number;
@ -24,8 +25,7 @@ export function logMessageDeleteBulk(pluginData: GuildPluginData<LogsPluginType>
archiveUrl: data.archiveUrl,
}),
{
channel: data.channel.id,
category: data.channel.parentId,
...resolveChannelIds(data.channel),
},
);
}

View file

@ -11,6 +11,7 @@ import {
} from "../../../utils/templateSafeObjects";
import { SavedMessage } from "../../../data/entities/SavedMessage";
import { UnknownUser } from "../../../utils";
import { resolveChannelIds } from "../../../utils/resolveChannelIds";
interface LogMessageEditData {
user: User | UnknownUser;
@ -31,9 +32,9 @@ export function logMessageEdit(pluginData: GuildPluginData<LogsPluginType>, data
}),
{
userId: data.user.id,
channel: data.channel.id,
messageTextContent: data.after.data.content,
bot: data.user instanceof User ? data.user.bot : false,
...resolveChannelIds(data.channel),
},
);
}

View file

@ -5,6 +5,7 @@ import { log } from "../util/log";
import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter";
import { BaseGuildTextChannel, GuildChannel, GuildMember, ThreadChannel } from "discord.js";
import { channelToTemplateSafeChannel, memberToTemplateSafeMember } from "../../../utils/templateSafeObjects";
import { resolveChannelIds } from "../../../utils/resolveChannelIds";
interface LogMessageSpamDetectedData {
member: GuildMember;
@ -30,9 +31,8 @@ export function logMessageSpamDetected(pluginData: GuildPluginData<LogsPluginTyp
{
userId: data.member.id,
roles: Array.from(data.member.roles.cache.keys()),
channel: data.channel.id,
category: data.channel.parentId,
bot: data.member.user.bot,
...resolveChannelIds(data.channel),
},
);
}

View file

@ -5,6 +5,7 @@ import { log } from "../util/log";
import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter";
import { BaseGuildTextChannel, GuildTextBasedChannel, ThreadChannel, User } from "discord.js";
import { channelToTemplateSafeChannel, userToTemplateSafeUser } from "../../../utils/templateSafeObjects";
import { resolveChannelIds } from "../../../utils/resolveChannelIds";
interface LogPostedScheduledMessageData {
author: User;
@ -27,8 +28,7 @@ export function logPostedScheduledMessage(
{
userId: data.author.id,
bot: data.author.bot,
channel: data.channel.id,
category: data.channel.parentId,
...resolveChannelIds(data.channel),
},
);
}

View file

@ -5,6 +5,7 @@ import { log } from "../util/log";
import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter";
import { BaseGuildTextChannel, GuildTextBasedChannel, ThreadChannel, User } from "discord.js";
import { channelToTemplateSafeChannel, userToTemplateSafeUser } from "../../../utils/templateSafeObjects";
import { resolveChannelIds } from "../../../utils/resolveChannelIds";
interface LogRepeatedMessageData {
author: User;
@ -32,8 +33,7 @@ export function logRepeatedMessage(pluginData: GuildPluginData<LogsPluginType>,
{
userId: data.author.id,
bot: data.author.bot,
channel: data.channel.id,
category: data.channel.parentId,
...resolveChannelIds(data.channel),
},
);
}

View file

@ -5,6 +5,7 @@ import { log } from "../util/log";
import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter";
import { BaseGuildTextChannel, GuildTextBasedChannel, ThreadChannel, User } from "discord.js";
import { channelToTemplateSafeChannel, userToTemplateSafeUser } from "../../../utils/templateSafeObjects";
import { resolveChannelIds } from "../../../utils/resolveChannelIds";
interface LogScheduledMessageData {
author: User;
@ -28,8 +29,7 @@ export function logScheduledMessage(pluginData: GuildPluginData<LogsPluginType>,
{
userId: data.author.id,
bot: data.author.bot,
channel: data.channel.id,
category: data.channel.parentId,
...resolveChannelIds(data.channel),
},
);
}

View file

@ -5,6 +5,7 @@ import { log } from "../util/log";
import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter";
import { BaseGuildTextChannel, GuildTextBasedChannel, ThreadChannel, User } from "discord.js";
import { channelToTemplateSafeChannel, userToTemplateSafeUser } from "../../../utils/templateSafeObjects";
import { resolveChannelIds } from "../../../utils/resolveChannelIds";
interface LogScheduledRepeatedMessageData {
author: User;
@ -35,8 +36,7 @@ export function logScheduledRepeatedMessage(
{
userId: data.author.id,
bot: data.author.bot,
channel: data.channel.id,
category: data.channel.parentId,
...resolveChannelIds(data.channel),
},
);
}

View file

@ -5,6 +5,7 @@ import { log } from "../util/log";
import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter";
import { StageChannel, StageInstance } from "discord.js";
import { channelToTemplateSafeChannel, stageToTemplateSafeStage } from "../../../utils/templateSafeObjects";
import { resolveChannelIds } from "../../../utils/resolveChannelIds";
interface LogStageInstanceCreateData {
stageInstance: StageInstance;
@ -20,8 +21,7 @@ export function logStageInstanceCreate(pluginData: GuildPluginData<LogsPluginTyp
stageChannel: channelToTemplateSafeChannel(data.stageChannel),
}),
{
channel: data.stageInstance.channel!.id,
category: data.stageInstance.channel!.parentId,
...resolveChannelIds(data.stageInstance.channel!),
},
);
}

View file

@ -5,6 +5,7 @@ import { log } from "../util/log";
import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter";
import { StageChannel, StageInstance } from "discord.js";
import { channelToTemplateSafeChannel, stageToTemplateSafeStage } from "../../../utils/templateSafeObjects";
import { resolveChannelIds } from "../../../utils/resolveChannelIds";
interface LogStageInstanceDeleteData {
stageInstance: StageInstance;
@ -20,8 +21,7 @@ export function logStageInstanceDelete(pluginData: GuildPluginData<LogsPluginTyp
stageChannel: channelToTemplateSafeChannel(data.stageChannel),
}),
{
channel: data.stageInstance.channel!.id,
category: data.stageInstance.channel!.parentId,
...resolveChannelIds(data.stageInstance.channel!),
},
);
}

View file

@ -5,6 +5,7 @@ import { log } from "../util/log";
import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter";
import { StageChannel, StageInstance } from "discord.js";
import { channelToTemplateSafeChannel, stageToTemplateSafeStage } from "../../../utils/templateSafeObjects";
import { resolveChannelIds } from "../../../utils/resolveChannelIds";
interface LogStageInstanceUpdateData {
oldStageInstance: StageInstance;
@ -24,8 +25,7 @@ export function logStageInstanceUpdate(pluginData: GuildPluginData<LogsPluginTyp
differenceString: data.differenceString,
}),
{
channel: data.newStageInstance.channel!.id,
category: data.newStageInstance.channel!.parentId,
...resolveChannelIds(data.newStageInstance.channel!),
},
);
}

View file

@ -5,6 +5,7 @@ import { log } from "../util/log";
import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter";
import { ThreadChannel } from "discord.js";
import { channelToTemplateSafeChannel } from "../../../utils/templateSafeObjects";
import { resolveChannelIds } from "../../../utils/resolveChannelIds";
interface LogThreadCreateData {
thread: ThreadChannel;
@ -18,8 +19,7 @@ export function logThreadCreate(pluginData: GuildPluginData<LogsPluginType>, dat
thread: channelToTemplateSafeChannel(data.thread),
}),
{
channel: data.thread.parentId,
category: data.thread.parent?.parentId,
...resolveChannelIds(data.thread),
},
);
}

View file

@ -5,6 +5,7 @@ import { log } from "../util/log";
import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter";
import { ThreadChannel } from "discord.js";
import { channelToTemplateSafeChannel } from "../../../utils/templateSafeObjects";
import { resolveChannelIds } from "../../../utils/resolveChannelIds";
interface LogThreadDeleteData {
thread: ThreadChannel;
@ -18,8 +19,7 @@ export function logThreadDelete(pluginData: GuildPluginData<LogsPluginType>, dat
thread: channelToTemplateSafeChannel(data.thread),
}),
{
channel: data.thread.parentId,
category: data.thread.parent?.parentId,
...resolveChannelIds(data.thread),
},
);
}

View file

@ -5,6 +5,7 @@ import { log } from "../util/log";
import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter";
import { ThreadChannel } from "discord.js";
import { channelToTemplateSafeChannel } from "../../../utils/templateSafeObjects";
import { resolveChannelIds } from "../../../utils/resolveChannelIds";
interface LogThreadUpdateData {
oldThread: ThreadChannel;
@ -22,8 +23,7 @@ export function logThreadUpdate(pluginData: GuildPluginData<LogsPluginType>, dat
differenceString: data.differenceString,
}),
{
channel: data.newThread.parentId,
category: data.newThread.parent?.parentId,
...resolveChannelIds(data.newThread),
},
);
}

View file

@ -9,6 +9,7 @@ import {
memberToTemplateSafeMember,
userToTemplateSafeUser,
} from "../../../utils/templateSafeObjects";
import { resolveChannelIds } from "../../../utils/resolveChannelIds";
interface LogVoiceChannelForceDisconnectData {
mod: User;
@ -31,8 +32,7 @@ export function logVoiceChannelForceDisconnect(
{
userId: data.member.id,
roles: Array.from(data.member.roles.cache.keys()),
channel: data.oldChannel.id,
category: data.oldChannel.parentId,
...resolveChannelIds(data.oldChannel),
bot: data.member.user.bot,
},
);

View file

@ -9,6 +9,7 @@ import {
memberToTemplateSafeMember,
userToTemplateSafeUser,
} from "../../../utils/templateSafeObjects";
import { resolveChannelIds } from "../../../utils/resolveChannelIds";
interface LogVoiceChannelForceMoveData {
mod: User;
@ -33,8 +34,7 @@ export function logVoiceChannelForceMove(
{
userId: data.member.id,
roles: Array.from(data.member.roles.cache.keys()),
channel: data.newChannel.id,
category: data.newChannel.parentId,
...resolveChannelIds(data.newChannel),
bot: data.member.user.bot,
},
);

View file

@ -5,6 +5,7 @@ import { log } from "../util/log";
import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter";
import { BaseGuildVoiceChannel, GuildMember } from "discord.js";
import { channelToTemplateSafeChannel, memberToTemplateSafeMember } from "../../../utils/templateSafeObjects";
import { resolveChannelIds } from "../../../utils/resolveChannelIds";
interface LogVoiceChannelJoinData {
member: GuildMember;
@ -22,8 +23,7 @@ export function logVoiceChannelJoin(pluginData: GuildPluginData<LogsPluginType>,
{
userId: data.member.id,
roles: Array.from(data.member.roles.cache.keys()),
channel: data.channel.id,
category: data.channel.parentId,
...resolveChannelIds(data.channel),
bot: data.member.user.bot,
},
);

View file

@ -5,6 +5,7 @@ import { log } from "../util/log";
import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter";
import { BaseGuildVoiceChannel, GuildMember } from "discord.js";
import { channelToTemplateSafeChannel, memberToTemplateSafeMember } from "../../../utils/templateSafeObjects";
import { resolveChannelIds } from "../../../utils/resolveChannelIds";
interface LogVoiceChannelLeaveData {
member: GuildMember;
@ -22,8 +23,7 @@ export function logVoiceChannelLeave(pluginData: GuildPluginData<LogsPluginType>
{
userId: data.member.id,
roles: Array.from(data.member.roles.cache.keys()),
channel: data.channel.id,
category: data.channel.parentId,
...resolveChannelIds(data.channel),
bot: data.member.user.bot,
},
);

View file

@ -5,6 +5,7 @@ import { log } from "../util/log";
import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter";
import { BaseGuildVoiceChannel, GuildMember } from "discord.js";
import { channelToTemplateSafeChannel, memberToTemplateSafeMember } from "../../../utils/templateSafeObjects";
import { resolveChannelIds } from "../../../utils/resolveChannelIds";
interface LogVoiceChannelMoveData {
member: GuildMember;
@ -24,8 +25,7 @@ export function logVoiceChannelMove(pluginData: GuildPluginData<LogsPluginType>,
{
userId: data.member.id,
roles: Array.from(data.member.roles.cache.keys()),
channel: data.newChannel.id,
category: data.newChannel.parentId,
...resolveChannelIds(data.newChannel),
bot: data.member.user.bot,
},
);

View file

@ -42,6 +42,7 @@ const LogChannel = t.partial({
excluded_message_regexes: t.array(TRegex),
excluded_channels: t.array(t.string),
excluded_categories: t.array(t.string),
excluded_threads: t.array(t.string),
exclude_bots: t.boolean,
excluded_roles: t.array(t.string),
format: tNullable(tLogFormats),

View file

@ -22,6 +22,7 @@ interface ExclusionData {
roles?: Snowflake[] | null;
channel?: Snowflake | null;
category?: Snowflake | null;
thread?: Snowflake | null;
messageTextContent?: string | null;
}
@ -58,6 +59,10 @@ async function shouldExclude(
return true;
}
if (opts.excluded_threads && exclusionData.thread && opts.excluded_threads.includes(exclusionData.thread)) {
return true;
}
if (opts.excluded_message_regexes && exclusionData.messageTextContent) {
for (const regex of opts.excluded_message_regexes) {
const matches = await pluginData.state.regexRunner

View file

@ -67,6 +67,12 @@ export const CasesUserCmd = modActionsCmd({
msg.channel.send(`No cases found for **${userName}**`);
} else {
const casesToDisplay = args.hidden ? cases : normalCases;
if (!casesToDisplay.length) {
msg.channel.send(
`No normal cases found for **${userName}**. Use "-hidden" to show ${cases.length} hidden cases.`,
);
return;
}
if (args.expand) {
if (casesToDisplay.length > 8) {

View file

@ -1,11 +1,17 @@
import { commandTypeHelpers as ct } from "../../../commandTypes";
import { createChunkedMessage } from "../../../utils";
import { tagsCmd } from "../types";
import escapeStringRegexp from "escape-string-regexp";
export const TagListCmd = tagsCmd({
trigger: ["tag list", "tags", "taglist"],
permission: "can_list",
async run({ message: msg, pluginData }) {
signature: {
search: ct.string({ required: false }),
},
async run({ message: msg, args, pluginData }) {
const tags = await pluginData.state.tags.all();
if (tags.length === 0) {
msg.channel.send(`No tags created yet! Use \`tag create\` command to create one.`);
@ -14,7 +20,30 @@ export const TagListCmd = tagsCmd({
const prefix = (await pluginData.config.getForMessage(msg)).prefix;
const tagNames = tags.map((tag) => tag.tag).sort();
const searchRegex = args.search ? new RegExp([...args.search].map((s) => escapeStringRegexp(s)).join(".*")) : null;
createChunkedMessage(msg.channel, `Available tags (use with ${prefix}tag): \`\`\`${tagNames.join(", ")}\`\`\``);
const filteredTags = args.search ? tagNames.filter((tag) => searchRegex!.test(tag)) : tagNames;
if (filteredTags.length === 0) {
msg.channel.send("No tags matched the filter");
return;
}
const tagGroups = filteredTags.reduce((obj, tag) => {
const tagUpper = tag.toUpperCase();
const key = /[A-Z]/.test(tagUpper[0]) ? tagUpper[0] : "#";
if (!(key in obj)) {
obj[key] = [];
}
obj[key].push(tag);
return obj;
}, {});
const tagList = Object.keys(tagGroups)
.sort()
.map((key) => `[${key}] ${tagGroups[key].join(", ")}`)
.join("\n");
createChunkedMessage(msg.channel, `Available tags (use with ${prefix}tag): \`\`\`${tagList}\`\`\``);
},
});

View file

@ -0,0 +1,6 @@
import { Channel, DMChannel } from "discord.js";
import { ChannelTypeStrings } from "src/types";
export function isDmChannel(channel: Channel): channel is DMChannel {
return channel.type === ChannelTypeStrings.DM || channel.type === ChannelTypeStrings.GROUP;
}

View file

@ -0,0 +1,5 @@
import { Channel, GuildChannel } from "discord.js";
export function isGuildChannel(channel: Channel): channel is GuildChannel {
return channel.type.startsWith("GUILD_");
}

View file

@ -0,0 +1,10 @@
import { Channel, ThreadChannel } from "discord.js";
import { ChannelTypeStrings } from "src/types";
export function isThreadChannel(channel: Channel): channel is ThreadChannel {
return (
channel.type === ChannelTypeStrings.NEWS_THREAD ||
channel.type === ChannelTypeStrings.PUBLIC_THREAD ||
channel.type === ChannelTypeStrings.PRIVATE_THREAD
);
}

View file

@ -0,0 +1,53 @@
import { ChannelType } from "discord-api-types/v9";
import { CategoryChannel, Channel } from "discord.js";
import { ChannelTypes } from "discord.js/typings/enums";
import { ChannelTypeStrings } from "src/types";
import { isDmChannel } from "./isDmChannel";
import { isGuildChannel } from "./isGuildChannel";
import { isThreadChannel } from "./isThreadChannel";
type ResolvedChannelIds = {
category: string | null;
channel: string | null;
thread: string | null;
};
export function resolveChannelIds(channel: Channel): ResolvedChannelIds {
if (isDmChannel(channel)) {
return {
category: null,
channel: channel.id,
thread: null,
};
}
if (isThreadChannel(channel)) {
return {
category: channel.parent?.parentId || null,
channel: channel.parentId,
thread: channel.id,
};
}
if (channel instanceof CategoryChannel) {
return {
category: channel.id,
channel: null,
thread: null,
};
}
if (isGuildChannel(channel)) {
return {
category: channel.parentId,
channel: channel.id,
thread: null,
};
}
return {
category: null,
channel: channel.id,
thread: null,
};
}

View file

@ -24,6 +24,11 @@ services:
volumes:
- ./docker/production/data/mysql:/var/lib/mysql
command: --authentication-policy=mysql_native_password
healthcheck:
test: "/usr/bin/mysql --user=root --password=\"${DOCKER_PROD_MYSQL_ROOT_PASSWORD}\" --execute \"SHOW DATABASES;\""
interval: 5s
timeout: 300s
retries: 60
prepare_backend:
build:
@ -32,7 +37,8 @@ services:
DOCKER_USER_UID: ${DOCKER_USER_UID:?Missing DOCKER_USER_UID}
DOCKER_USER_GID: ${DOCKER_USER_GID:?Missing DOCKER_USER_GID}
depends_on:
- mysql
mysql:
condition: service_healthy
volumes:
- ./:/zeppelin
command: |-