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:
commit
3f06cab4a4
41 changed files with 287 additions and 62 deletions
11
.clabot
11
.clabot
|
@ -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!"
|
||||
}
|
||||
|
|
|
@ -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`
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
|
|
96
backend/src/plugins/Automod/actions/changePerms.ts
Normal file
96
backend/src/plugins/Automod/actions/changePerms.ts
Normal 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);
|
||||
},
|
||||
});
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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!),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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!),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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!),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
);
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
);
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
);
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
);
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
);
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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}\`\`\``);
|
||||
},
|
||||
});
|
||||
|
|
6
backend/src/utils/isDmChannel.ts
Normal file
6
backend/src/utils/isDmChannel.ts
Normal 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;
|
||||
}
|
5
backend/src/utils/isGuildChannel.ts
Normal file
5
backend/src/utils/isGuildChannel.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
import { Channel, GuildChannel } from "discord.js";
|
||||
|
||||
export function isGuildChannel(channel: Channel): channel is GuildChannel {
|
||||
return channel.type.startsWith("GUILD_");
|
||||
}
|
10
backend/src/utils/isThreadChannel.ts
Normal file
10
backend/src/utils/isThreadChannel.ts
Normal 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
|
||||
);
|
||||
}
|
53
backend/src/utils/resolveChannelIds.ts
Normal file
53
backend/src/utils/resolveChannelIds.ts
Normal 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,
|
||||
};
|
||||
}
|
|
@ -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: |-
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue