mirror of
https://github.com/ZeppelinBot/Zeppelin.git
synced 2025-07-13 21:57:18 +00:00
Merge branch 'master' into fr_vckick
This commit is contained in:
commit
93bc15a7de
12 changed files with 265 additions and 15 deletions
|
@ -19,7 +19,7 @@ export const BanAction = automodAction({
|
|||
|
||||
async apply({ pluginData, contexts, actionConfig, matchResult }) {
|
||||
const reason = actionConfig.reason || "Kicked automatically";
|
||||
const contactMethods = resolveActionContactMethods(pluginData, actionConfig);
|
||||
const contactMethods = actionConfig.notify ? resolveActionContactMethods(pluginData, actionConfig) : undefined;
|
||||
const deleteMessageDays = actionConfig.deleteMessageDays || undefined;
|
||||
|
||||
const caseArgs = {
|
||||
|
|
|
@ -18,7 +18,7 @@ export const KickAction = automodAction({
|
|||
|
||||
async apply({ pluginData, contexts, actionConfig, matchResult }) {
|
||||
const reason = actionConfig.reason || "Kicked automatically";
|
||||
const contactMethods = resolveActionContactMethods(pluginData, actionConfig);
|
||||
const contactMethods = actionConfig.notify ? resolveActionContactMethods(pluginData, actionConfig) : undefined;
|
||||
|
||||
const caseArgs = {
|
||||
modId: pluginData.client.user.id,
|
||||
|
|
|
@ -31,7 +31,7 @@ export const MuteAction = automodAction({
|
|||
async apply({ pluginData, contexts, actionConfig, ruleName, matchResult }) {
|
||||
const duration = actionConfig.duration ? convertDelayStringToMS(actionConfig.duration)! : undefined;
|
||||
const reason = actionConfig.reason || "Muted automatically";
|
||||
const contactMethods = resolveActionContactMethods(pluginData, actionConfig);
|
||||
const contactMethods = actionConfig.notify ? resolveActionContactMethods(pluginData, actionConfig) : undefined;
|
||||
|
||||
const caseArgs = {
|
||||
modId: pluginData.client.user.id,
|
||||
|
|
|
@ -18,7 +18,7 @@ export const WarnAction = automodAction({
|
|||
|
||||
async apply({ pluginData, contexts, actionConfig, matchResult }) {
|
||||
const reason = actionConfig.reason || "Warned automatically";
|
||||
const contactMethods = resolveActionContactMethods(pluginData, actionConfig);
|
||||
const contactMethods = actionConfig.notify ? resolveActionContactMethods(pluginData, actionConfig) : undefined;
|
||||
|
||||
const caseArgs = {
|
||||
modId: pluginData.client.user.id,
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
import { GuildPluginData } from "knub";
|
||||
import { CustomEventsPluginType, TCustomEvent } from "../types";
|
||||
import * as t from "io-ts";
|
||||
import { convertDelayStringToMS, noop, tDelayString } from "../../../utils";
|
||||
import { ActionError } from "../ActionError";
|
||||
|
||||
export const MakeRoleMentionableAction = t.type({
|
||||
type: t.literal("make_role_mentionable"),
|
||||
role: t.string,
|
||||
timeout: tDelayString,
|
||||
});
|
||||
export type TMakeRoleMentionableAction = t.TypeOf<typeof MakeRoleMentionableAction>;
|
||||
|
||||
export async function makeRoleMentionableAction(
|
||||
pluginData: GuildPluginData<CustomEventsPluginType>,
|
||||
action: TMakeRoleMentionableAction,
|
||||
values: any,
|
||||
event: TCustomEvent,
|
||||
eventData: any,
|
||||
) {
|
||||
const role = pluginData.guild.roles.get(action.role);
|
||||
if (!role) {
|
||||
throw new ActionError(`Unknown role: ${role}`);
|
||||
}
|
||||
|
||||
await role.edit(
|
||||
{
|
||||
mentionable: true,
|
||||
},
|
||||
`Custom event: ${event.name}`,
|
||||
);
|
||||
|
||||
const timeout = convertDelayStringToMS(action.timeout)!;
|
||||
setTimeout(() => {
|
||||
role
|
||||
.edit(
|
||||
{
|
||||
mentionable: false,
|
||||
},
|
||||
`Custom event: ${event.name}`,
|
||||
)
|
||||
.catch(noop);
|
||||
}, timeout);
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
import { GuildPluginData } from "knub";
|
||||
import { CustomEventsPluginType, TCustomEvent } from "../types";
|
||||
import * as t from "io-ts";
|
||||
import { ActionError } from "../ActionError";
|
||||
|
||||
export const MakeRoleUnmentionableAction = t.type({
|
||||
type: t.literal("make_role_unmentionable"),
|
||||
role: t.string,
|
||||
});
|
||||
export type TMakeRoleUnmentionableAction = t.TypeOf<typeof MakeRoleUnmentionableAction>;
|
||||
|
||||
export async function makeRoleUnmentionableAction(
|
||||
pluginData: GuildPluginData<CustomEventsPluginType>,
|
||||
action: TMakeRoleUnmentionableAction,
|
||||
values: any,
|
||||
event: TCustomEvent,
|
||||
eventData: any,
|
||||
) {
|
||||
const role = pluginData.guild.roles.get(action.role);
|
||||
if (!role) {
|
||||
throw new ActionError(`Unknown role: ${role}`);
|
||||
}
|
||||
|
||||
await role.edit(
|
||||
{
|
||||
mentionable: false,
|
||||
},
|
||||
`Custom event: ${event.name}`,
|
||||
);
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
import { GuildPluginData } from "knub";
|
||||
import { CustomEventsPluginType, TCustomEvent } from "../types";
|
||||
import * as t from "io-ts";
|
||||
import { ActionError } from "../ActionError";
|
||||
|
||||
export const SetChannelPermissionOverridesAction = t.type({
|
||||
type: t.literal("set_channel_permission_overrides"),
|
||||
channel: t.string,
|
||||
overrides: t.array(
|
||||
t.type({
|
||||
type: t.union([t.literal("member"), t.literal("role")]),
|
||||
id: t.string,
|
||||
allow: t.number,
|
||||
deny: t.number,
|
||||
}),
|
||||
),
|
||||
});
|
||||
export type TSetChannelPermissionOverridesAction = t.TypeOf<typeof SetChannelPermissionOverridesAction>;
|
||||
|
||||
export async function setChannelPermissionOverridesAction(
|
||||
pluginData: GuildPluginData<CustomEventsPluginType>,
|
||||
action: TSetChannelPermissionOverridesAction,
|
||||
values: any,
|
||||
event: TCustomEvent,
|
||||
eventData: any,
|
||||
) {
|
||||
const channel = pluginData.guild.channels.get(action.channel);
|
||||
if (!channel) {
|
||||
throw new ActionError(`Unknown channel: ${action.channel}`);
|
||||
}
|
||||
|
||||
for (const override of action.overrides) {
|
||||
await channel.editPermission(
|
||||
override.id,
|
||||
override.allow,
|
||||
override.deny,
|
||||
override.type,
|
||||
`Custom event: ${event.name}`,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -7,6 +7,9 @@ import { addRoleAction } from "../actions/addRoleAction";
|
|||
import { createCaseAction } from "../actions/createCaseAction";
|
||||
import { moveToVoiceChannelAction } from "../actions/moveToVoiceChannelAction";
|
||||
import { messageAction } from "../actions/messageAction";
|
||||
import { makeRoleMentionableAction } from "../actions/makeRoleMentionableAction";
|
||||
import { makeRoleUnmentionableAction } from "../actions/makeRoleUnmentionableAction";
|
||||
import { setChannelPermissionOverridesAction } from "../actions/setChannelPermissionOverrides";
|
||||
|
||||
export async function runEvent(
|
||||
pluginData: GuildPluginData<CustomEventsPluginType>,
|
||||
|
@ -24,6 +27,12 @@ export async function runEvent(
|
|||
await moveToVoiceChannelAction(pluginData, action, values, event, eventData);
|
||||
} else if (action.type === "message") {
|
||||
await messageAction(pluginData, action, values);
|
||||
} else if (action.type === "make_role_mentionable") {
|
||||
await makeRoleMentionableAction(pluginData, action, values, event, eventData);
|
||||
} else if (action.type === "make_role_unmentionable") {
|
||||
await makeRoleUnmentionableAction(pluginData, action, values, event, eventData);
|
||||
} else if (action.type === "set_channel_permission_overrides") {
|
||||
await setChannelPermissionOverridesAction(pluginData, action, values, event, eventData);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
|
|
|
@ -4,6 +4,9 @@ import { AddRoleAction } from "./actions/addRoleAction";
|
|||
import { CreateCaseAction } from "./actions/createCaseAction";
|
||||
import { MoveToVoiceChannelAction } from "./actions/moveToVoiceChannelAction";
|
||||
import { MessageAction } from "./actions/messageAction";
|
||||
import { MakeRoleMentionableAction } from "./actions/makeRoleMentionableAction";
|
||||
import { MakeRoleUnmentionableAction } from "./actions/makeRoleUnmentionableAction";
|
||||
import { SetChannelPermissionOverridesAction } from "./actions/setChannelPermissionOverrides";
|
||||
|
||||
// Triggers
|
||||
const CommandTrigger = t.type({
|
||||
|
@ -17,7 +20,15 @@ type TCommandTrigger = t.TypeOf<typeof CommandTrigger>;
|
|||
const AnyTrigger = CommandTrigger; // TODO: Make into a union once we have more triggers
|
||||
type TAnyTrigger = t.TypeOf<typeof AnyTrigger>;
|
||||
|
||||
const AnyAction = t.union([AddRoleAction, CreateCaseAction, MoveToVoiceChannelAction, MessageAction]);
|
||||
const AnyAction = t.union([
|
||||
AddRoleAction,
|
||||
CreateCaseAction,
|
||||
MoveToVoiceChannelAction,
|
||||
MessageAction,
|
||||
MakeRoleMentionableAction,
|
||||
MakeRoleUnmentionableAction,
|
||||
SetChannelPermissionOverridesAction,
|
||||
]);
|
||||
type TAnyAction = t.TypeOf<typeof AnyAction>;
|
||||
|
||||
export const CustomEvent = t.type({
|
||||
|
|
|
@ -8,6 +8,7 @@ import { formatReasonWithAttachments } from "../functions/formatReasonWithAttach
|
|||
import { banUserId } from "../functions/banUserId";
|
||||
import { ignoreEvent } from "../functions/ignoreEvent";
|
||||
import { LogType } from "../../../data/LogType";
|
||||
import { waitForReaction } from "knub/dist/helpers";
|
||||
|
||||
const opts = {
|
||||
mod: ct.member({ option: true }),
|
||||
|
@ -36,19 +37,30 @@ export const BanCmd = modActionsCmd({
|
|||
|
||||
const memberToBan = await resolveMember(pluginData.client, pluginData.guild, user.id);
|
||||
|
||||
let forceban = false;
|
||||
if (!memberToBan) {
|
||||
const banned = await isBanned(pluginData, user.id);
|
||||
|
||||
if (banned) {
|
||||
sendErrorMessage(pluginData, msg.channel, `User is already banned`);
|
||||
return;
|
||||
} else {
|
||||
sendErrorMessage(pluginData, msg.channel, `User not found on the server`);
|
||||
}
|
||||
// Ask the mod if we should upgrade to a forceban as the user is not on the server
|
||||
const notOnServerMsg = await msg.channel.createMessage("User not found on the server, forceban instead?");
|
||||
const reply = await waitForReaction(pluginData.client, notOnServerMsg, ["✅", "❌"], msg.author.id);
|
||||
|
||||
return;
|
||||
notOnServerMsg.delete();
|
||||
if (!reply || reply.name === "❌") {
|
||||
sendErrorMessage(pluginData, msg.channel, "User not on server, ban cancelled by moderator");
|
||||
return;
|
||||
} else {
|
||||
forceban = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure we're allowed to ban this member
|
||||
if (!canActOn(pluginData, msg.member, memberToBan)) {
|
||||
// Make sure we're allowed to ban this member if they are on the server
|
||||
if (!forceban && !canActOn(pluginData, msg.member, memberToBan!)) {
|
||||
sendErrorMessage(pluginData, msg.channel, "Cannot ban: insufficient permissions");
|
||||
return;
|
||||
}
|
||||
|
@ -74,7 +86,7 @@ export const BanCmd = modActionsCmd({
|
|||
|
||||
const deleteMessageDays = args["delete-days"] ?? pluginData.config.getForMessage(msg).ban_delete_message_days;
|
||||
const reason = formatReasonWithAttachments(args.reason, msg.attachments);
|
||||
const banResult = await banUserId(pluginData, memberToBan.id, reason, {
|
||||
const banResult = await banUserId(pluginData, user.id, reason, {
|
||||
contactMethods,
|
||||
caseArgs: {
|
||||
modId: mod.id,
|
||||
|
@ -89,9 +101,14 @@ export const BanCmd = modActionsCmd({
|
|||
}
|
||||
|
||||
// Confirm the action to the moderator
|
||||
let response = `Banned **${memberToBan.user.username}#${memberToBan.user.discriminator}** (Case #${banResult.case.case_number})`;
|
||||
let response = "";
|
||||
if (!forceban) {
|
||||
response = `Banned **${user.username}#${user.discriminator}** (Case #${banResult.case.case_number})`;
|
||||
if (banResult.notifyResult.text) response += ` (${banResult.notifyResult.text})`;
|
||||
} else {
|
||||
response = `Member forcebanned (Case #${banResult.case.case_number})`;
|
||||
}
|
||||
|
||||
if (banResult.notifyResult.text) response += ` (${banResult.notifyResult.text})`;
|
||||
sendSuccessMessage(pluginData, msg.channel, response);
|
||||
},
|
||||
});
|
||||
|
|
|
@ -16,7 +16,7 @@ import { NicknameCmd } from "./commands/NicknameCmd";
|
|||
import { PingCmd } from "./commands/PingCmd";
|
||||
import { SourceCmd } from "./commands/SourceCmd";
|
||||
import { ContextCmd } from "./commands/ContextCmd";
|
||||
import { VcmoveCmd } from "./commands/VcmoveCmd";
|
||||
import { VcmoveAllCmd, VcmoveCmd } from "./commands/VcmoveCmd";
|
||||
import { HelpCmd } from "./commands/HelpCmd";
|
||||
import { AboutCmd } from "./commands/AboutCmd";
|
||||
import { PluginOptions } from "knub";
|
||||
|
@ -124,6 +124,7 @@ export const UtilityPlugin = zeppelinGuildPlugin<UtilityPluginType>()("utility",
|
|||
ContextCmd,
|
||||
VcmoveCmd,
|
||||
VcdisconnectCmd,
|
||||
VcmoveAllCmd,
|
||||
HelpCmd,
|
||||
AboutCmd,
|
||||
ReloadGuildCmd,
|
||||
|
|
|
@ -4,11 +4,12 @@ import {
|
|||
channelMentionRegex,
|
||||
errorMessage,
|
||||
isSnowflake,
|
||||
resolveMember,
|
||||
simpleClosestStringMatch,
|
||||
stripObjectToScalars,
|
||||
} from "../../../utils";
|
||||
import { sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils";
|
||||
import { VoiceChannel } from "eris";
|
||||
import { Member, VoiceChannel } from "eris";
|
||||
import { LogType } from "../../../data/LogType";
|
||||
|
||||
export const VcmoveCmd = utilityCmd({
|
||||
|
@ -93,3 +94,99 @@ export const VcmoveCmd = utilityCmd({
|
|||
);
|
||||
},
|
||||
});
|
||||
|
||||
export const VcmoveAllCmd = utilityCmd({
|
||||
trigger: "vcmoveall",
|
||||
description: "Move all members of a voice channel to another voice channel",
|
||||
usage: "!vcmoveall 551767166395875334 767497573560352798",
|
||||
permission: "can_vcmove",
|
||||
|
||||
signature: {
|
||||
oldChannel: ct.voiceChannel(),
|
||||
channel: ct.string({ catchAll: true }),
|
||||
},
|
||||
|
||||
async run({ message: msg, args, pluginData }) {
|
||||
let channel: VoiceChannel;
|
||||
|
||||
if (isSnowflake(args.channel)) {
|
||||
// Snowflake -> resolve channel directly
|
||||
const potentialChannel = pluginData.guild.channels.get(args.channel);
|
||||
if (!potentialChannel || !(potentialChannel instanceof VoiceChannel)) {
|
||||
sendErrorMessage(pluginData, msg.channel, "Unknown or non-voice channel");
|
||||
return;
|
||||
}
|
||||
|
||||
channel = potentialChannel;
|
||||
} else if (channelMentionRegex.test(args.channel)) {
|
||||
// Channel mention -> parse channel id and resolve channel from that
|
||||
const channelId = args.channel.match(channelMentionRegex)![1];
|
||||
const potentialChannel = pluginData.guild.channels.get(channelId);
|
||||
if (!potentialChannel || !(potentialChannel instanceof VoiceChannel)) {
|
||||
sendErrorMessage(pluginData, msg.channel, "Unknown or non-voice channel");
|
||||
return;
|
||||
}
|
||||
|
||||
channel = potentialChannel;
|
||||
} else {
|
||||
// Search string -> find closest matching voice channel name
|
||||
const voiceChannels = pluginData.guild.channels.filter(theChannel => {
|
||||
return theChannel instanceof VoiceChannel;
|
||||
}) as VoiceChannel[];
|
||||
const closestMatch = simpleClosestStringMatch(args.channel, voiceChannels, ch => ch.name);
|
||||
if (!closestMatch) {
|
||||
sendErrorMessage(pluginData, msg.channel, "No matching voice channels");
|
||||
return;
|
||||
}
|
||||
|
||||
channel = closestMatch;
|
||||
}
|
||||
|
||||
if (args.oldChannel.voiceMembers.size === 0) {
|
||||
sendErrorMessage(pluginData, msg.channel, "Voice channel is empty");
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.oldChannel.id === channel.id) {
|
||||
sendErrorMessage(pluginData, msg.channel, "Cant move from and to the same channel!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Cant leave null, otherwise we get an assignment error in the catch
|
||||
let currMember = msg.member;
|
||||
const moveAmt = args.oldChannel.voiceMembers.size;
|
||||
try {
|
||||
for (const memberWithId of args.oldChannel.voiceMembers) {
|
||||
currMember = memberWithId[1];
|
||||
|
||||
currMember.edit({
|
||||
channelID: channel.id,
|
||||
});
|
||||
|
||||
pluginData.state.logs.log(LogType.VOICE_CHANNEL_FORCE_MOVE, {
|
||||
mod: stripObjectToScalars(msg.author),
|
||||
member: stripObjectToScalars(currMember, ["user", "roles"]),
|
||||
oldChannel: stripObjectToScalars(args.oldChannel),
|
||||
newChannel: stripObjectToScalars(channel),
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
if (msg.member.id === currMember.id) {
|
||||
sendErrorMessage(pluginData, msg.channel, "Unknown error when trying to move members");
|
||||
return;
|
||||
}
|
||||
sendErrorMessage(
|
||||
pluginData,
|
||||
msg.channel,
|
||||
`Failed to move ${currMember.username}#${currMember.discriminator} (${currMember.id})`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
sendSuccessMessage(
|
||||
pluginData,
|
||||
msg.channel,
|
||||
`All ${moveAmt} members from **${args.oldChannel.name}** moved to **${channel.name}**`,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue