mirror of
https://github.com/ZeppelinBot/Zeppelin.git
synced 2025-05-10 12:25:02 +00:00
feat: add member cache; handle all role changes with RoleManagerPlugin; exit gracefully
This commit is contained in:
parent
fd60a09947
commit
fa50110766
48 changed files with 755 additions and 264 deletions
|
@ -15,6 +15,7 @@ import { LogsPlugin } from "../Logs/LogsPlugin";
|
|||
import { ModActionsPlugin } from "../ModActions/ModActionsPlugin";
|
||||
import { MutesPlugin } from "../Mutes/MutesPlugin";
|
||||
import { PhishermanPlugin } from "../Phisherman/PhishermanPlugin";
|
||||
import { RoleManagerPlugin } from "../RoleManager/RoleManagerPlugin";
|
||||
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
|
||||
import { availableActions } from "./actions/availableActions";
|
||||
import { AntiraidClearCmd } from "./commands/AntiraidClearCmd";
|
||||
|
@ -196,6 +197,7 @@ export const AutomodPlugin = zeppelinGuildPlugin<AutomodPluginType>()({
|
|||
CountersPlugin,
|
||||
PhishermanPlugin,
|
||||
InternalPosterPlugin,
|
||||
RoleManagerPlugin,
|
||||
],
|
||||
|
||||
defaultOptions,
|
||||
|
|
|
@ -3,9 +3,9 @@ import * as t from "io-ts";
|
|||
import { nonNullish, unique } from "../../../utils";
|
||||
import { canAssignRole } from "../../../utils/canAssignRole";
|
||||
import { getMissingPermissions } from "../../../utils/getMissingPermissions";
|
||||
import { memberRolesLock } from "../../../utils/lockNameHelpers";
|
||||
import { missingPermissionError } from "../../../utils/missingPermissionError";
|
||||
import { LogsPlugin } from "../../Logs/LogsPlugin";
|
||||
import { RoleManagerPlugin } from "../../RoleManager/RoleManagerPlugin";
|
||||
import { ignoreRoleChange } from "../functions/ignoredRoleChanges";
|
||||
import { automodAction } from "../helpers";
|
||||
|
||||
|
@ -52,25 +52,14 @@ export const AddRolesAction = automodAction({
|
|||
|
||||
await Promise.all(
|
||||
members.map(async (member) => {
|
||||
const memberRoles = new Set(member.roles.cache.keys());
|
||||
const currentMemberRoles = new Set(member.roles.cache.keys());
|
||||
for (const roleId of rolesToAssign) {
|
||||
memberRoles.add(roleId as Snowflake);
|
||||
ignoreRoleChange(pluginData, member.id, roleId);
|
||||
if (!currentMemberRoles.has(roleId)) {
|
||||
pluginData.getPlugin(RoleManagerPlugin).addRole(member.id, roleId);
|
||||
// TODO: Remove this and just ignore bot changes in general?
|
||||
ignoreRoleChange(pluginData, member.id, roleId);
|
||||
}
|
||||
}
|
||||
|
||||
if (memberRoles.size === member.roles.cache.size) {
|
||||
// No role changes
|
||||
return;
|
||||
}
|
||||
|
||||
const memberRoleLock = await pluginData.locks.acquire(memberRolesLock(member));
|
||||
|
||||
const rolesArr = Array.from(memberRoles.values());
|
||||
await member.edit({
|
||||
roles: rolesArr,
|
||||
});
|
||||
|
||||
memberRoleLock.unlock();
|
||||
}),
|
||||
);
|
||||
},
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
import * as t from "io-ts";
|
||||
import { GuildMemberCache } from "../../data/GuildMemberCache";
|
||||
import { makeIoTsConfigParser, mapToPublicFn } from "../../pluginUtils";
|
||||
import { SECONDS } from "../../utils";
|
||||
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
|
||||
import { cancelDeletionOnMemberJoin } from "./events/cancelDeletionOnMemberJoin";
|
||||
import { removeMemberCacheOnMemberLeave } from "./events/removeMemberCacheOnMemberLeave";
|
||||
import { updateMemberCacheOnMemberUpdate } from "./events/updateMemberCacheOnMemberUpdate";
|
||||
import { updateMemberCacheOnMessage } from "./events/updateMemberCacheOnMessage";
|
||||
import { updateMemberCacheOnRoleChange } from "./events/updateMemberCacheOnRoleChange";
|
||||
import { updateMemberCacheOnVoiceStateUpdate } from "./events/updateMemberCacheOnVoiceStateUpdate";
|
||||
import { getCachedMemberData } from "./functions/getCachedMemberData";
|
||||
import { GuildMemberCachePluginType } from "./types";
|
||||
|
||||
const PENDING_SAVE_INTERVAL = 30 * SECONDS;
|
||||
|
||||
export const GuildMemberCachePlugin = zeppelinGuildPlugin<GuildMemberCachePluginType>()({
|
||||
name: "guild_member_cache",
|
||||
showInDocs: false,
|
||||
|
||||
configParser: makeIoTsConfigParser(t.type({})),
|
||||
|
||||
events: [
|
||||
updateMemberCacheOnMemberUpdate,
|
||||
updateMemberCacheOnMessage,
|
||||
updateMemberCacheOnVoiceStateUpdate,
|
||||
updateMemberCacheOnRoleChange,
|
||||
removeMemberCacheOnMemberLeave,
|
||||
cancelDeletionOnMemberJoin,
|
||||
],
|
||||
|
||||
public: {
|
||||
getCachedMemberData: mapToPublicFn(getCachedMemberData),
|
||||
},
|
||||
|
||||
beforeLoad(pluginData) {
|
||||
pluginData.state.memberCache = GuildMemberCache.getGuildInstance(pluginData.guild.id);
|
||||
// This won't leak memory... too much #trust
|
||||
pluginData.state.initialUpdatedMembers = new Set();
|
||||
},
|
||||
|
||||
afterLoad(pluginData) {
|
||||
pluginData.state.saveInterval = setInterval(
|
||||
() => pluginData.state.memberCache.savePendingUpdates(),
|
||||
PENDING_SAVE_INTERVAL,
|
||||
);
|
||||
},
|
||||
|
||||
async beforeUnload(pluginData) {
|
||||
clearInterval(pluginData.state.saveInterval);
|
||||
await pluginData.state.memberCache.savePendingUpdates();
|
||||
},
|
||||
});
|
|
@ -0,0 +1,9 @@
|
|||
import { guildPluginEventListener } from "knub";
|
||||
import { GuildMemberCachePluginType } from "../types";
|
||||
|
||||
export const cancelDeletionOnMemberJoin = guildPluginEventListener<GuildMemberCachePluginType>()({
|
||||
event: "guildMemberAdd",
|
||||
async listener({ pluginData, args: { member } }) {
|
||||
pluginData.state.memberCache.unmarkMemberForDeletion(member.id);
|
||||
},
|
||||
});
|
|
@ -0,0 +1,12 @@
|
|||
import { guildPluginEventListener } from "knub";
|
||||
import { MINUTES } from "../../../utils";
|
||||
import { GuildMemberCachePluginType } from "../types";
|
||||
|
||||
const DELETION_DELAY = 2 * MINUTES;
|
||||
|
||||
export const removeMemberCacheOnMemberLeave = guildPluginEventListener<GuildMemberCachePluginType>()({
|
||||
event: "guildMemberRemove",
|
||||
async listener({ pluginData, args: { member } }) {
|
||||
pluginData.state.memberCache.markMemberForDeletion(member.id);
|
||||
},
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
import { AuditLogEvent } from "discord.js";
|
||||
import { guildPluginEventListener } from "knub";
|
||||
import { updateMemberCacheForMember } from "../functions/updateMemberCacheForMember";
|
||||
import { GuildMemberCachePluginType } from "../types";
|
||||
|
||||
export const updateMemberCacheOnMemberUpdate = guildPluginEventListener<GuildMemberCachePluginType>()({
|
||||
event: "guildAuditLogEntryCreate",
|
||||
async listener({ pluginData, args: { auditLogEntry } }) {
|
||||
if (auditLogEntry.action !== AuditLogEvent.MemberUpdate) {
|
||||
return;
|
||||
}
|
||||
|
||||
updateMemberCacheForMember(pluginData, auditLogEntry.targetId!);
|
||||
},
|
||||
});
|
|
@ -0,0 +1,16 @@
|
|||
import { guildPluginEventListener } from "knub";
|
||||
import { updateMemberCacheForMember } from "../functions/updateMemberCacheForMember";
|
||||
import { GuildMemberCachePluginType } from "../types";
|
||||
|
||||
export const updateMemberCacheOnMessage = guildPluginEventListener<GuildMemberCachePluginType>()({
|
||||
event: "messageCreate",
|
||||
listener({ pluginData, args }) {
|
||||
// Update each member once per guild load when we see a message from them
|
||||
const key = `${pluginData.guild.id}-${args.message.author.id}`;
|
||||
if (pluginData.state.initialUpdatedMembers.has(key)) {
|
||||
return;
|
||||
}
|
||||
updateMemberCacheForMember(pluginData, args.message.author.id);
|
||||
pluginData.state.initialUpdatedMembers.add(key);
|
||||
},
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
import { AuditLogEvent } from "discord.js";
|
||||
import { guildPluginEventListener } from "knub";
|
||||
import { updateMemberCacheForMember } from "../functions/updateMemberCacheForMember";
|
||||
import { GuildMemberCachePluginType } from "../types";
|
||||
|
||||
export const updateMemberCacheOnRoleChange = guildPluginEventListener<GuildMemberCachePluginType>()({
|
||||
event: "guildAuditLogEntryCreate",
|
||||
async listener({ pluginData, args: { auditLogEntry } }) {
|
||||
if (auditLogEntry.action !== AuditLogEvent.MemberRoleUpdate) {
|
||||
return;
|
||||
}
|
||||
|
||||
updateMemberCacheForMember(pluginData, auditLogEntry.targetId!);
|
||||
},
|
||||
});
|
|
@ -0,0 +1,20 @@
|
|||
import { guildPluginEventListener } from "knub";
|
||||
import { updateMemberCacheForMember } from "../functions/updateMemberCacheForMember";
|
||||
import { GuildMemberCachePluginType } from "../types";
|
||||
|
||||
export const updateMemberCacheOnVoiceStateUpdate = guildPluginEventListener<GuildMemberCachePluginType>()({
|
||||
event: "voiceStateUpdate",
|
||||
listener({ pluginData, args }) {
|
||||
const memberId = args.newState.member?.id;
|
||||
if (!memberId) {
|
||||
return;
|
||||
}
|
||||
// Update each member once per guild load when we see a message from them
|
||||
const key = `${pluginData.guild.id}-${memberId}`;
|
||||
if (pluginData.state.initialUpdatedMembers.has(key)) {
|
||||
return;
|
||||
}
|
||||
updateMemberCacheForMember(pluginData, memberId);
|
||||
pluginData.state.initialUpdatedMembers.add(key);
|
||||
},
|
||||
});
|
|
@ -0,0 +1,10 @@
|
|||
import { GuildPluginData } from "knub";
|
||||
import { MemberCacheItem } from "../../../data/entities/MemberCacheItem";
|
||||
import { GuildMemberCachePluginType } from "../types";
|
||||
|
||||
export function getCachedMemberData(
|
||||
pluginData: GuildPluginData<GuildMemberCachePluginType>,
|
||||
userId: string,
|
||||
): Promise<MemberCacheItem | null> {
|
||||
return pluginData.state.memberCache.getCachedMemberData(userId);
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
import { GuildPluginData } from "knub";
|
||||
import { GuildMemberCachePluginType } from "../types";
|
||||
|
||||
export async function updateMemberCacheForMember(
|
||||
pluginData: GuildPluginData<GuildMemberCachePluginType>,
|
||||
userId: string,
|
||||
) {
|
||||
const upToDateMember = await pluginData.guild.members.fetch(userId);
|
||||
const roles = Array.from(upToDateMember.roles.cache.keys())
|
||||
// Filter out @everyone role
|
||||
.filter((roleId) => roleId !== pluginData.guild.id);
|
||||
pluginData.state.memberCache.setCachedMemberData(upToDateMember.id, {
|
||||
username: upToDateMember.user.username,
|
||||
nickname: upToDateMember.nickname,
|
||||
roles,
|
||||
});
|
||||
}
|
10
backend/src/plugins/GuildMemberCache/types.ts
Normal file
10
backend/src/plugins/GuildMemberCache/types.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
import { BasePluginType } from "knub";
|
||||
import { GuildMemberCache } from "../../data/GuildMemberCache";
|
||||
|
||||
export interface GuildMemberCachePluginType extends BasePluginType {
|
||||
state: {
|
||||
memberCache: GuildMemberCache;
|
||||
saveInterval: NodeJS.Timeout;
|
||||
initialUpdatedMembers: Set<string>;
|
||||
};
|
||||
}
|
|
@ -8,7 +8,7 @@ import { LogType } from "../../data/LogType";
|
|||
import { logger } from "../../logger";
|
||||
import { makeIoTsConfigParser, mapToPublicFn } from "../../pluginUtils";
|
||||
import { discardRegExpRunner, getRegExpRunner } from "../../regExpRunners";
|
||||
import { createTypedTemplateSafeValueContainer, TypedTemplateSafeValueContainer } from "../../templateFormatter";
|
||||
import { TypedTemplateSafeValueContainer, createTypedTemplateSafeValueContainer } from "../../templateFormatter";
|
||||
import { TimeAndDatePlugin } from "../TimeAndDate/TimeAndDatePlugin";
|
||||
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
|
||||
import { LogsChannelCreateEvt, LogsChannelDeleteEvt, LogsChannelUpdateEvt } from "./events/LogsChannelModifyEvts";
|
||||
|
@ -40,6 +40,7 @@ import { onMessageUpdate } from "./util/onMessageUpdate";
|
|||
|
||||
import { escapeCodeBlock } from "discord.js";
|
||||
import { InternalPosterPlugin } from "../InternalPoster/InternalPosterPlugin";
|
||||
import { LogsGuildMemberRoleChangeEvt } from "./events/LogsGuildMemberRoleChangeEvt";
|
||||
import { logAutomodAction } from "./logFunctions/logAutomodAction";
|
||||
import { logBotAlert } from "./logFunctions/logBotAlert";
|
||||
import { logCaseCreate } from "./logFunctions/logCaseCreate";
|
||||
|
@ -173,6 +174,7 @@ export const LogsPlugin = zeppelinGuildPlugin<LogsPluginType>()({
|
|||
LogsStickerCreateEvt,
|
||||
LogsStickerDeleteEvt,
|
||||
LogsStickerUpdateEvt,
|
||||
LogsGuildMemberRoleChangeEvt,
|
||||
],
|
||||
|
||||
public: {
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
import { APIRole, AuditLogChange, AuditLogEvent } from "discord.js";
|
||||
import { guildPluginEventListener } from "knub";
|
||||
import { resolveRole } from "../../../utils";
|
||||
import { logMemberRoleAdd } from "../logFunctions/logMemberRoleAdd";
|
||||
import { logMemberRoleRemove } from "../logFunctions/logMemberRoleRemove";
|
||||
import { LogsPluginType } from "../types";
|
||||
|
||||
type RoleAddChange = AuditLogChange & {
|
||||
key: "$add";
|
||||
new: Array<Pick<APIRole, "id" | "name">>;
|
||||
};
|
||||
|
||||
function isRoleAddChange(change: AuditLogChange): change is RoleAddChange {
|
||||
return change.key === "$add";
|
||||
}
|
||||
|
||||
type RoleRemoveChange = AuditLogChange & {
|
||||
key: "$remove";
|
||||
new: Array<Pick<APIRole, "id" | "name">>;
|
||||
};
|
||||
|
||||
function isRoleRemoveChange(change: AuditLogChange): change is RoleRemoveChange {
|
||||
return change.key === "$remove";
|
||||
}
|
||||
|
||||
export const LogsGuildMemberRoleChangeEvt = guildPluginEventListener<LogsPluginType>()({
|
||||
event: "guildAuditLogEntryCreate",
|
||||
async listener({ pluginData, args: { auditLogEntry } }) {
|
||||
// Ignore the bot's own audit log events
|
||||
if (auditLogEntry.executorId === pluginData.client.user?.id) {
|
||||
return;
|
||||
}
|
||||
if (auditLogEntry.action !== AuditLogEvent.MemberRoleUpdate) {
|
||||
return;
|
||||
}
|
||||
|
||||
const member = await pluginData.guild.members.fetch(auditLogEntry.targetId!);
|
||||
const mod = await pluginData.client.users.fetch(auditLogEntry.executorId!);
|
||||
for (const change of auditLogEntry.changes) {
|
||||
if (isRoleAddChange(change)) {
|
||||
const addedRoles = change.new.map((r) => resolveRole(pluginData.guild, r.id));
|
||||
logMemberRoleAdd(pluginData, {
|
||||
member,
|
||||
mod,
|
||||
roles: addedRoles,
|
||||
});
|
||||
}
|
||||
if (isRoleRemoveChange(change)) {
|
||||
const removedRoles = change.new.map((r) => resolveRole(pluginData.guild, r.id));
|
||||
logMemberRoleRemove(pluginData, {
|
||||
member,
|
||||
mod,
|
||||
roles: removedRoles,
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
|
@ -1,10 +1,4 @@
|
|||
import diff from "lodash.difference";
|
||||
import isEqual from "lodash.isequal";
|
||||
import { LogType } from "../../../data/LogType";
|
||||
import { logMemberNickChange } from "../logFunctions/logMemberNickChange";
|
||||
import { logMemberRoleAdd } from "../logFunctions/logMemberRoleAdd";
|
||||
import { logMemberRoleChanges } from "../logFunctions/logMemberRoleChanges";
|
||||
import { logMemberRoleRemove } from "../logFunctions/logMemberRoleRemove";
|
||||
import { logsEvt } from "../types";
|
||||
|
||||
export const LogsGuildMemberUpdateEvt = logsEvt({
|
||||
|
@ -14,8 +8,6 @@ export const LogsGuildMemberUpdateEvt = logsEvt({
|
|||
const pluginData = meta.pluginData;
|
||||
const oldMember = meta.args.oldMember;
|
||||
const member = meta.args.newMember;
|
||||
const oldRoles = [...oldMember.roles.cache.keys()];
|
||||
const currentRoles = [...member.roles.cache.keys()];
|
||||
|
||||
if (!oldMember || oldMember.partial) {
|
||||
return;
|
||||
|
@ -28,62 +20,5 @@ export const LogsGuildMemberUpdateEvt = logsEvt({
|
|||
newNick: member.nickname != null ? member.nickname : "<none>",
|
||||
});
|
||||
}
|
||||
|
||||
if (!isEqual(oldRoles, currentRoles)) {
|
||||
const addedRoles = diff(currentRoles, oldRoles);
|
||||
const removedRoles = diff(oldRoles, currentRoles);
|
||||
let skip = false;
|
||||
|
||||
if (
|
||||
addedRoles.length &&
|
||||
removedRoles.length &&
|
||||
pluginData.state.guildLogs.isLogIgnored(LogType.MEMBER_ROLE_CHANGES, member.id)
|
||||
) {
|
||||
skip = true;
|
||||
} else if (addedRoles.length && pluginData.state.guildLogs.isLogIgnored(LogType.MEMBER_ROLE_ADD, member.id)) {
|
||||
skip = true;
|
||||
} else if (
|
||||
removedRoles.length &&
|
||||
pluginData.state.guildLogs.isLogIgnored(LogType.MEMBER_ROLE_REMOVE, member.id)
|
||||
) {
|
||||
skip = true;
|
||||
}
|
||||
|
||||
if (!skip) {
|
||||
if (addedRoles.length && removedRoles.length) {
|
||||
// Roles added *and* removed
|
||||
logMemberRoleChanges(pluginData, {
|
||||
member,
|
||||
addedRoles: addedRoles.map(
|
||||
(roleId) => pluginData.guild.roles.cache.get(roleId) ?? { id: roleId, name: `Unknown (${roleId})` },
|
||||
),
|
||||
removedRoles: removedRoles.map(
|
||||
(roleId) => pluginData.guild.roles.cache.get(roleId) ?? { id: roleId, name: `Unknown (${roleId})` },
|
||||
),
|
||||
mod: null,
|
||||
});
|
||||
} else if (addedRoles.length) {
|
||||
// Roles added
|
||||
logMemberRoleAdd(pluginData, {
|
||||
member,
|
||||
roles: addedRoles.map(
|
||||
(roleId) => pluginData.guild.roles.cache.get(roleId) ?? { id: roleId, name: `Unknown (${roleId})` },
|
||||
),
|
||||
mod: null,
|
||||
});
|
||||
} else if (removedRoles.length && !addedRoles.length) {
|
||||
// Roles removed
|
||||
logMemberRoleRemove(pluginData, {
|
||||
member,
|
||||
roles: removedRoles.map(
|
||||
(roleId) => pluginData.guild.roles.cache.get(roleId) ?? { id: roleId, name: `Unknown (${roleId})` },
|
||||
),
|
||||
mod: null,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// TODO: Reimplement USERNAME_CHANGE
|
||||
|
|
|
@ -2,6 +2,7 @@ import { GuildMember, Role, User } from "discord.js";
|
|||
import { GuildPluginData } from "knub";
|
||||
import { LogType } from "../../../data/LogType";
|
||||
import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter";
|
||||
import { UnknownRole } from "../../../utils";
|
||||
import { memberToTemplateSafeMember, userToTemplateSafeUser } from "../../../utils/templateSafeObjects";
|
||||
import { LogsPluginType } from "../types";
|
||||
import { log } from "../util/log";
|
||||
|
@ -9,7 +10,7 @@ import { log } from "../util/log";
|
|||
interface LogMemberRoleAddData {
|
||||
mod: User | null;
|
||||
member: GuildMember;
|
||||
roles: Role[];
|
||||
roles: Array<Role | UnknownRole>;
|
||||
}
|
||||
|
||||
export function logMemberRoleAdd(pluginData: GuildPluginData<LogsPluginType>, data: LogMemberRoleAddData) {
|
||||
|
|
|
@ -14,6 +14,9 @@ interface LogMemberRoleChangesData {
|
|||
removedRoles: Role[];
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use logMemberRoleAdd() and logMemberRoleRemove() instead
|
||||
*/
|
||||
export function logMemberRoleChanges(pluginData: GuildPluginData<LogsPluginType>, data: LogMemberRoleChangesData) {
|
||||
return log(
|
||||
pluginData,
|
||||
|
|
|
@ -2,6 +2,7 @@ import { GuildMember, Role, User } from "discord.js";
|
|||
import { GuildPluginData } from "knub";
|
||||
import { LogType } from "../../../data/LogType";
|
||||
import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter";
|
||||
import { UnknownRole } from "../../../utils";
|
||||
import { memberToTemplateSafeMember, userToTemplateSafeUser } from "../../../utils/templateSafeObjects";
|
||||
import { LogsPluginType } from "../types";
|
||||
import { log } from "../util/log";
|
||||
|
@ -9,7 +10,7 @@ import { log } from "../util/log";
|
|||
interface LogMemberRoleRemoveData {
|
||||
mod: User | null;
|
||||
member: GuildMember;
|
||||
roles: Role[];
|
||||
roles: Array<Role | UnknownRole>;
|
||||
}
|
||||
|
||||
export function logMemberRoleRemove(pluginData: GuildPluginData<LogsPluginType>, data: LogMemberRoleRemoveData) {
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import { Snowflake } from "discord.js";
|
||||
import moment from "moment-timezone";
|
||||
import { MuteTypes } from "../../../data/MuteTypes";
|
||||
import { memberRolesLock } from "../../../utils/lockNameHelpers";
|
||||
import { LogsPlugin } from "../../Logs/LogsPlugin";
|
||||
import { RoleManagerPlugin } from "../../RoleManager/RoleManagerPlugin";
|
||||
import { getTimeoutExpiryTime } from "../functions/getTimeoutExpiryTime";
|
||||
import { mutesEvt } from "../types";
|
||||
|
||||
|
@ -18,15 +17,9 @@ export const ReapplyActiveMuteOnJoinEvt = mutesEvt({
|
|||
}
|
||||
|
||||
if (mute.type === MuteTypes.Role) {
|
||||
const muteRole = pluginData.config.get().mute_role;
|
||||
|
||||
if (muteRole) {
|
||||
const memberRoleLock = await pluginData.locks.acquire(memberRolesLock(member));
|
||||
try {
|
||||
await member.roles.add(muteRole as Snowflake);
|
||||
} finally {
|
||||
memberRoleLock.unlock();
|
||||
}
|
||||
const muteRoleId = pluginData.config.get().mute_role;
|
||||
if (muteRoleId) {
|
||||
pluginData.getPlugin(RoleManagerPlugin).addPriorityRole(member.id, muteRoleId);
|
||||
}
|
||||
} else {
|
||||
if (!member.isCommunicationDisabled()) {
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
import { GuildMember } from "discord.js";
|
||||
import { GuildPluginData } from "knub";
|
||||
import { MuteTypes } from "../../../data/MuteTypes";
|
||||
import { Mute } from "../../../data/entities/Mute";
|
||||
import { clearExpiringMute } from "../../../data/loops/expiringMutesLoop";
|
||||
import { MuteTypes } from "../../../data/MuteTypes";
|
||||
import { resolveMember, verboseUserMention } from "../../../utils";
|
||||
import { memberRolesLock } from "../../../utils/lockNameHelpers";
|
||||
import { LogsPlugin } from "../../Logs/LogsPlugin";
|
||||
import { RoleManagerPlugin } from "../../RoleManager/RoleManagerPlugin";
|
||||
import { MutesPluginType } from "../types";
|
||||
|
||||
export async function clearMute(
|
||||
|
@ -23,15 +24,16 @@ export async function clearMute(
|
|||
|
||||
if (member) {
|
||||
const lock = await pluginData.locks.acquire(memberRolesLock(member));
|
||||
const roleManagerPlugin = pluginData.getPlugin(RoleManagerPlugin);
|
||||
|
||||
try {
|
||||
const defaultMuteRole = pluginData.config.get().mute_role;
|
||||
if (mute) {
|
||||
const muteRole = mute.mute_role || pluginData.config.get().mute_role;
|
||||
const muteRoleId = mute.mute_role || pluginData.config.get().mute_role;
|
||||
|
||||
if (mute.type === MuteTypes.Role) {
|
||||
if (muteRole) {
|
||||
await member.roles.remove(muteRole);
|
||||
if (muteRoleId) {
|
||||
roleManagerPlugin.removePriorityRole(member.id, muteRoleId);
|
||||
}
|
||||
} else {
|
||||
await member.timeout(null);
|
||||
|
@ -39,19 +41,18 @@ export async function clearMute(
|
|||
|
||||
if (mute.roles_to_restore) {
|
||||
const guildRoles = pluginData.guild.roles.cache;
|
||||
const newRoles = [...member.roles.cache.keys()].filter((roleId) => roleId !== muteRole);
|
||||
for (const toRestore of mute?.roles_to_restore) {
|
||||
if (guildRoles.has(toRestore) && toRestore !== muteRole && !newRoles.includes(toRestore)) {
|
||||
newRoles.push(toRestore);
|
||||
const newRoles = [...member.roles.cache.keys()].filter((roleId) => roleId !== muteRoleId);
|
||||
for (const roleIdToRestore of mute?.roles_to_restore) {
|
||||
if (guildRoles.has(roleIdToRestore) && roleIdToRestore !== muteRoleId) {
|
||||
roleManagerPlugin.addRole(member.id, roleIdToRestore);
|
||||
}
|
||||
}
|
||||
await member.roles.set(newRoles);
|
||||
}
|
||||
} else {
|
||||
// Unmuting someone without an active mute -> remove timeouts and/or mute role
|
||||
const muteRole = pluginData.config.get().mute_role;
|
||||
if (muteRole && member.roles.cache.has(muteRole)) {
|
||||
await member.roles.remove(muteRole);
|
||||
roleManagerPlugin.removePriorityRole(member.id, muteRole);
|
||||
}
|
||||
if (member.isCommunicationDisabled()) {
|
||||
await member.timeout(null);
|
||||
|
|
|
@ -1,26 +1,27 @@
|
|||
import { Snowflake } from "discord.js";
|
||||
import humanizeDuration from "humanize-duration";
|
||||
import { GuildPluginData } from "knub";
|
||||
import { ERRORS, RecoverablePluginError } from "../../../RecoverablePluginError";
|
||||
import { CaseTypes } from "../../../data/CaseTypes";
|
||||
import { AddMuteParams } from "../../../data/GuildMutes";
|
||||
import { MuteTypes } from "../../../data/MuteTypes";
|
||||
import { Case } from "../../../data/entities/Case";
|
||||
import { Mute } from "../../../data/entities/Mute";
|
||||
import { AddMuteParams } from "../../../data/GuildMutes";
|
||||
import { registerExpiringMute } from "../../../data/loops/expiringMutesLoop";
|
||||
import { MuteTypes } from "../../../data/MuteTypes";
|
||||
import { LogsPlugin } from "../../../plugins/Logs/LogsPlugin";
|
||||
import { ERRORS, RecoverablePluginError } from "../../../RecoverablePluginError";
|
||||
import { renderTemplate, TemplateSafeValueContainer } from "../../../templateFormatter";
|
||||
import { TemplateSafeValueContainer, renderTemplate } from "../../../templateFormatter";
|
||||
import {
|
||||
UserNotificationMethod,
|
||||
UserNotificationResult,
|
||||
notifyUser,
|
||||
resolveMember,
|
||||
resolveUser,
|
||||
ucfirst,
|
||||
UserNotificationMethod,
|
||||
UserNotificationResult,
|
||||
} from "../../../utils";
|
||||
import { muteLock } from "../../../utils/lockNameHelpers";
|
||||
import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects";
|
||||
import { CasesPlugin } from "../../Cases/CasesPlugin";
|
||||
import { RoleManagerPlugin } from "../../RoleManager/RoleManagerPlugin";
|
||||
import { MuteOptions, MutesPluginType } from "../types";
|
||||
import { getDefaultMuteType } from "./getDefaultMuteType";
|
||||
import { getTimeoutExpiryTime } from "./getTimeoutExpiryTime";
|
||||
|
@ -91,38 +92,29 @@ export async function muteUser(
|
|||
}
|
||||
|
||||
if (muteType === MuteTypes.Role) {
|
||||
// Apply mute role if it's missing
|
||||
if (!currentUserRoles.includes(muteRole!)) {
|
||||
try {
|
||||
await member.roles.add(muteRole!);
|
||||
} catch (e) {
|
||||
const actualMuteRole = pluginData.guild.roles.cache.get(muteRole!);
|
||||
if (!actualMuteRole) {
|
||||
lock.unlock();
|
||||
logs.logBotAlert({
|
||||
body: `Cannot mute users, specified mute role Id is invalid`,
|
||||
});
|
||||
throw new RecoverablePluginError(ERRORS.INVALID_MUTE_ROLE_ID);
|
||||
}
|
||||
// Verify the configured mute role is valid
|
||||
const actualMuteRole = pluginData.guild.roles.cache.get(muteRole!);
|
||||
if (!actualMuteRole) {
|
||||
lock.unlock();
|
||||
logs.logBotAlert({
|
||||
body: `Cannot mute users, specified mute role Id is invalid`,
|
||||
});
|
||||
throw new RecoverablePluginError(ERRORS.INVALID_MUTE_ROLE_ID);
|
||||
}
|
||||
|
||||
const zep = await resolveMember(pluginData.client, pluginData.guild, pluginData.client.user!.id);
|
||||
const zepRoles = pluginData.guild.roles.cache.filter((x) => zep!.roles.cache.has(x.id));
|
||||
// If we have roles and one of them is above the muted role, throw generic error
|
||||
if (zepRoles.size >= 0 && zepRoles.some((zepRole) => zepRole.position > actualMuteRole.position)) {
|
||||
lock.unlock();
|
||||
logs.logBotAlert({
|
||||
body: `Cannot mute user ${member.id}: ${e}`,
|
||||
});
|
||||
throw e;
|
||||
} else {
|
||||
// Otherwise, throw error that mute role is above zeps roles
|
||||
lock.unlock();
|
||||
logs.logBotAlert({
|
||||
body: `Cannot mute users, specified mute role is above Zeppelin in the role hierarchy`,
|
||||
});
|
||||
throw new RecoverablePluginError(ERRORS.MUTE_ROLE_ABOVE_ZEP, pluginData.guild);
|
||||
}
|
||||
}
|
||||
// Verify the mute role is not above Zep's roles
|
||||
const zep = await pluginData.guild.members.fetchMe();
|
||||
const zepRoles = pluginData.guild.roles.cache.filter((x) => zep.roles.cache.has(x.id));
|
||||
if (zepRoles.size === 0 || !zepRoles.some((zepRole) => zepRole.position > actualMuteRole.position)) {
|
||||
lock.unlock();
|
||||
logs.logBotAlert({
|
||||
body: `Cannot mute users, specified mute role is above Zeppelin in the role hierarchy`,
|
||||
});
|
||||
throw new RecoverablePluginError(ERRORS.MUTE_ROLE_ABOVE_ZEP, pluginData.guild);
|
||||
}
|
||||
|
||||
if (!currentUserRoles.includes(muteRole!)) {
|
||||
pluginData.getPlugin(RoleManagerPlugin).addPriorityRole(member.id, muteRole!);
|
||||
}
|
||||
} else {
|
||||
await member.disableCommunicationUntil(timeoutUntil);
|
||||
|
|
|
@ -3,7 +3,9 @@ import { GuildLogs } from "../../data/GuildLogs";
|
|||
import { GuildPersistedData } from "../../data/GuildPersistedData";
|
||||
import { makeIoTsConfigParser } from "../../pluginUtils";
|
||||
import { trimPluginDescription } from "../../utils";
|
||||
import { GuildMemberCachePlugin } from "../GuildMemberCache/GuildMemberCachePlugin";
|
||||
import { LogsPlugin } from "../Logs/LogsPlugin";
|
||||
import { RoleManagerPlugin } from "../RoleManager/RoleManagerPlugin";
|
||||
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
|
||||
import { LoadDataEvt } from "./events/LoadDataEvt";
|
||||
import { StoreDataEvt } from "./events/StoreDataEvt";
|
||||
|
@ -29,7 +31,7 @@ export const PersistPlugin = zeppelinGuildPlugin<PersistPluginType>()({
|
|||
configSchema: ConfigSchema,
|
||||
},
|
||||
|
||||
dependencies: () => [LogsPlugin],
|
||||
dependencies: () => [LogsPlugin, RoleManagerPlugin, GuildMemberCachePlugin],
|
||||
configParser: makeIoTsConfigParser(ConfigSchema),
|
||||
defaultOptions,
|
||||
|
||||
|
|
|
@ -2,9 +2,9 @@ import { GuildMemberEditOptions, PermissionFlagsBits } from "discord.js";
|
|||
import intersection from "lodash.intersection";
|
||||
import { canAssignRole } from "../../../utils/canAssignRole";
|
||||
import { getMissingPermissions } from "../../../utils/getMissingPermissions";
|
||||
import { memberRolesLock } from "../../../utils/lockNameHelpers";
|
||||
import { missingPermissionError } from "../../../utils/missingPermissionError";
|
||||
import { LogsPlugin } from "../../Logs/LogsPlugin";
|
||||
import { RoleManagerPlugin } from "../../RoleManager/RoleManagerPlugin";
|
||||
import { persistEvt } from "../types";
|
||||
|
||||
const p = PermissionFlagsBits;
|
||||
|
@ -16,13 +16,11 @@ export const LoadDataEvt = persistEvt({
|
|||
const member = meta.args.member;
|
||||
const pluginData = meta.pluginData;
|
||||
|
||||
const memberRoleLock = await pluginData.locks.acquire(memberRolesLock(member));
|
||||
|
||||
const persistedData = await pluginData.state.persistedData.find(member.id);
|
||||
if (!persistedData) {
|
||||
memberRoleLock.unlock();
|
||||
return;
|
||||
}
|
||||
await pluginData.state.persistedData.clear(member.id);
|
||||
|
||||
const toRestore: GuildMemberEditOptions = {
|
||||
reason: "Restored upon rejoin",
|
||||
|
@ -59,29 +57,29 @@ export const LoadDataEvt = persistEvt({
|
|||
|
||||
const persistedRoles = config.persisted_roles;
|
||||
if (persistedRoles.length) {
|
||||
const roleManager = pluginData.getPlugin(RoleManagerPlugin);
|
||||
const rolesToRestore = intersection(persistedRoles, persistedData.roles, guildRoles);
|
||||
|
||||
if (rolesToRestore.length) {
|
||||
restoredData.push("roles");
|
||||
toRestore.roles = Array.from(new Set([...rolesToRestore, ...member.roles.cache.keys()]));
|
||||
for (const roleId of rolesToRestore) {
|
||||
roleManager.addRole(member.id, roleId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (config.persist_nicknames && persistedData.nickname) {
|
||||
restoredData.push("nickname");
|
||||
toRestore.nick = persistedData.nickname;
|
||||
await member.edit({
|
||||
nick: persistedData.nickname,
|
||||
});
|
||||
}
|
||||
|
||||
if (restoredData.length) {
|
||||
await member.edit(toRestore);
|
||||
await pluginData.state.persistedData.clear(member.id);
|
||||
|
||||
pluginData.getPlugin(LogsPlugin).logMemberRestore({
|
||||
member,
|
||||
restoredData: restoredData.join(", "),
|
||||
});
|
||||
}
|
||||
|
||||
memberRoleLock.unlock();
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,34 +1,41 @@
|
|||
import { GuildMember } from "discord.js";
|
||||
import intersection from "lodash.intersection";
|
||||
import { IPartialPersistData } from "../../../data/GuildPersistedData";
|
||||
import { PersistedData } from "../../../data/entities/PersistedData";
|
||||
import { GuildMemberCachePlugin } from "../../GuildMemberCache/GuildMemberCachePlugin";
|
||||
import { persistEvt } from "../types";
|
||||
|
||||
export const StoreDataEvt = persistEvt({
|
||||
event: "guildMemberRemove",
|
||||
|
||||
async listener(meta) {
|
||||
const member = meta.args.member as GuildMember;
|
||||
const pluginData = meta.pluginData;
|
||||
|
||||
let persist = false;
|
||||
const persistData: IPartialPersistData = {};
|
||||
async listener({ pluginData, args: { member } }) {
|
||||
const config = await pluginData.config.getForUser(member.user);
|
||||
const persistData: Partial<PersistedData> = {};
|
||||
|
||||
const persistedRoles = config.persisted_roles;
|
||||
if (persistedRoles.length && member.roles) {
|
||||
const rolesToPersist = intersection(persistedRoles, [...member.roles.cache.keys()]);
|
||||
if (member.partial) {
|
||||
// Djs hasn't cached member data => use db cache
|
||||
const data = await pluginData.getPlugin(GuildMemberCachePlugin).getCachedMemberData(member.id);
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
const rolesToPersist = config.persisted_roles.filter((roleId) => data.roles.includes(roleId));
|
||||
if (rolesToPersist.length) {
|
||||
persist = true;
|
||||
persistData.roles = rolesToPersist;
|
||||
}
|
||||
if (config.persist_nicknames && data.nickname) {
|
||||
persistData.nickname = data.nickname;
|
||||
}
|
||||
} else {
|
||||
// Djs has cached member data => use that
|
||||
const memberRoles = Array.from(member.roles.cache.keys());
|
||||
const rolesToPersist = config.persisted_roles.filter((roleId) => memberRoles.includes(roleId));
|
||||
if (rolesToPersist.length) {
|
||||
persistData.roles = rolesToPersist;
|
||||
}
|
||||
if (config.persist_nicknames && member.nickname) {
|
||||
persistData.nickname = member.nickname as any;
|
||||
}
|
||||
}
|
||||
|
||||
if (config.persist_nicknames && member.nickname) {
|
||||
persist = true;
|
||||
persistData.nickname = member.nickname;
|
||||
}
|
||||
|
||||
if (persist) {
|
||||
if (Object.keys(persistData).length) {
|
||||
pluginData.state.persistedData.set(member.id, persistData);
|
||||
}
|
||||
},
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
import { GuildMember } from "discord.js";
|
||||
import { guildPluginEventListener } from "knub";
|
||||
import { SECONDS } from "../../../utils";
|
||||
import { parseCustomId } from "../../../utils/parseCustomId";
|
||||
import { RoleManagerPlugin } from "../../RoleManager/RoleManagerPlugin";
|
||||
import { getAllRolesInButtons } from "../functions/getAllRolesInButtons";
|
||||
import { RoleButtonsPluginType, TRoleButtonOption } from "../types";
|
||||
|
||||
const ROLE_BUTTON_CD = 5 * SECONDS;
|
||||
|
||||
export const onButtonInteraction = guildPluginEventListener<RoleButtonsPluginType>()({
|
||||
event: "interactionCreate",
|
||||
async listener({ pluginData, args }) {
|
||||
|
@ -33,6 +36,16 @@ export const onButtonInteraction = guildPluginEventListener<RoleButtonsPluginTyp
|
|||
return;
|
||||
}
|
||||
|
||||
const cdIdentifier = `${args.interaction.user.id}-${optionIndex}`;
|
||||
if (pluginData.cooldowns.isOnCooldown(cdIdentifier)) {
|
||||
args.interaction.reply({
|
||||
ephemeral: true,
|
||||
content: "Please wait before clicking the button again",
|
||||
});
|
||||
return;
|
||||
}
|
||||
pluginData.cooldowns.setCooldown(cdIdentifier, ROLE_BUTTON_CD);
|
||||
|
||||
const member = args.interaction.member as GuildMember;
|
||||
const role = pluginData.guild.roles.cache.get(option.role_id);
|
||||
const roleName = role?.name || option.role_id;
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
import { GuildPluginData } from "knub";
|
||||
import { RoleQueueItem } from "../../../data/entities/RoleQueueItem";
|
||||
import { logger } from "../../../logger";
|
||||
import { LogsPlugin } from "../../Logs/LogsPlugin";
|
||||
import { RoleManagerPluginType } from "../types";
|
||||
|
||||
const ROLE_ASSIGNMENTS_PER_BATCH = 20;
|
||||
const ROLE_ASSIGNMENTS_PER_BATCH = 10;
|
||||
|
||||
export async function runRoleAssignmentLoop(pluginData: GuildPluginData<RoleManagerPluginType>) {
|
||||
if (pluginData.state.roleAssignmentLoopRunning || pluginData.state.abortRoleAssignmentLoop) {
|
||||
|
@ -30,40 +29,25 @@ export async function runRoleAssignmentLoop(pluginData: GuildPluginData<RoleMana
|
|||
return;
|
||||
}
|
||||
|
||||
// Remove assignments that cancel each other out (e.g. from spam-clicking a role button)
|
||||
const validAssignments = new Map<string, RoleQueueItem>();
|
||||
for (const assignment of nextAssignments) {
|
||||
const key = `${assignment.should_add ? 1 : 0}|${assignment.user_id}|${assignment.role_id}`;
|
||||
const oppositeKey = `${assignment.should_add ? 0 : 1}|${assignment.user_id}|${assignment.role_id}`;
|
||||
if (validAssignments.has(oppositeKey)) {
|
||||
validAssignments.delete(oppositeKey);
|
||||
continue;
|
||||
const member = await pluginData.guild.members.fetch(assignment.user_id).catch(() => null);
|
||||
if (!member) {
|
||||
return;
|
||||
}
|
||||
validAssignments.set(key, assignment);
|
||||
}
|
||||
|
||||
// Apply batch in parallel
|
||||
await Promise.all(
|
||||
Array.from(validAssignments.values()).map(async (assignment) => {
|
||||
const member = await pluginData.guild.members.fetch(assignment.user_id).catch(() => null);
|
||||
if (!member) {
|
||||
return;
|
||||
}
|
||||
const operation = assignment.should_add
|
||||
? member.roles.add(assignment.role_id)
|
||||
: member.roles.remove(assignment.role_id);
|
||||
|
||||
const operation = assignment.should_add
|
||||
? member.roles.add(assignment.role_id)
|
||||
: member.roles.remove(assignment.role_id);
|
||||
|
||||
await operation.catch((err) => {
|
||||
logger.warn(err);
|
||||
pluginData.getPlugin(LogsPlugin).logBotAlert({
|
||||
body: `Could not ${assignment.should_add ? "assign" : "remove"} role <@&${assignment.role_id}> (\`${
|
||||
assignment.role_id
|
||||
}\`) ${assignment.should_add ? "to" : "from"} <@!${assignment.user_id}> (\`${assignment.user_id}\`)`,
|
||||
});
|
||||
await operation.catch((err) => {
|
||||
logger.warn(err);
|
||||
pluginData.getPlugin(LogsPlugin).logBotAlert({
|
||||
body: `Could not ${assignment.should_add ? "assign" : "remove"} role <@&${assignment.role_id}> (\`${
|
||||
assignment.role_id
|
||||
}\`) ${assignment.should_add ? "to" : "from"} <@!${assignment.user_id}> (\`${assignment.user_id}\`)`,
|
||||
});
|
||||
}),
|
||||
);
|
||||
});
|
||||
}
|
||||
})());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ import { GuildLogs } from "../../data/GuildLogs";
|
|||
import { makeIoTsConfigParser } from "../../pluginUtils";
|
||||
import { trimPluginDescription } from "../../utils";
|
||||
import { LogsPlugin } from "../Logs/LogsPlugin";
|
||||
import { RoleManagerPlugin } from "../RoleManager/RoleManagerPlugin";
|
||||
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
|
||||
import { AddRoleCmd } from "./commands/AddRoleCmd";
|
||||
import { MassAddRoleCmd } from "./commands/MassAddRoleCmd";
|
||||
|
@ -43,7 +44,7 @@ export const RolesPlugin = zeppelinGuildPlugin<RolesPluginType>()({
|
|||
configSchema: ConfigSchema,
|
||||
},
|
||||
|
||||
dependencies: () => [LogsPlugin],
|
||||
dependencies: () => [LogsPlugin, RoleManagerPlugin],
|
||||
configParser: makeIoTsConfigParser(ConfigSchema),
|
||||
defaultOptions,
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { GuildChannel } from "discord.js";
|
||||
import { commandTypeHelpers as ct } from "../../../commandTypes";
|
||||
import { LogType } from "../../../data/LogType";
|
||||
import { canActOn, sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils";
|
||||
import { resolveRoleId, verboseUserMention } from "../../../utils";
|
||||
import { LogsPlugin } from "../../Logs/LogsPlugin";
|
||||
import { RoleManagerPlugin } from "../../RoleManager/RoleManagerPlugin";
|
||||
import { rolesCmd } from "../types";
|
||||
|
||||
export const AddRoleCmd = rolesCmd({
|
||||
|
@ -49,9 +49,7 @@ export const AddRoleCmd = rolesCmd({
|
|||
return;
|
||||
}
|
||||
|
||||
pluginData.state.logs.ignoreLog(LogType.MEMBER_ROLE_ADD, args.member.id);
|
||||
|
||||
await args.member.roles.add(roleId);
|
||||
pluginData.getPlugin(RoleManagerPlugin).addRole(args.member.id, roleId);
|
||||
|
||||
pluginData.getPlugin(LogsPlugin).logMemberRoleAdd({
|
||||
mod: msg.author,
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { GuildMember } from "discord.js";
|
||||
import { commandTypeHelpers as ct } from "../../../commandTypes";
|
||||
import { LogType } from "../../../data/LogType";
|
||||
import { logger } from "../../../logger";
|
||||
import { canActOn, sendErrorMessage } from "../../../pluginUtils";
|
||||
import { resolveMember, resolveRoleId, successMessage } from "../../../utils";
|
||||
import { LogsPlugin } from "../../Logs/LogsPlugin";
|
||||
import { RoleManagerPlugin } from "../../RoleManager/RoleManagerPlugin";
|
||||
import { rolesCmd } from "../types";
|
||||
|
||||
export const MassAddRoleCmd = rolesCmd({
|
||||
|
@ -72,8 +72,7 @@ export const MassAddRoleCmd = rolesCmd({
|
|||
|
||||
for (const member of membersWithoutTheRole) {
|
||||
try {
|
||||
pluginData.state.logs.ignoreLog(LogType.MEMBER_ROLE_ADD, member.id);
|
||||
await member.roles.add(roleId);
|
||||
pluginData.getPlugin(RoleManagerPlugin).addRole(member.id, roleId);
|
||||
pluginData.getPlugin(LogsPlugin).logMemberRoleAdd({
|
||||
member,
|
||||
roles: [role],
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
import { GuildMember } from "discord.js";
|
||||
import { commandTypeHelpers as ct } from "../../../commandTypes";
|
||||
import { LogType } from "../../../data/LogType";
|
||||
import { logger } from "../../../logger";
|
||||
import { canActOn, sendErrorMessage } from "../../../pluginUtils";
|
||||
import { resolveMember, resolveRoleId, successMessage } from "../../../utils";
|
||||
import { LogsPlugin } from "../../Logs/LogsPlugin";
|
||||
import { RoleManagerPlugin } from "../../RoleManager/RoleManagerPlugin";
|
||||
import { rolesCmd } from "../types";
|
||||
|
||||
export const MassRemoveRoleCmd = rolesCmd({
|
||||
|
@ -71,19 +70,13 @@ export const MassRemoveRoleCmd = rolesCmd({
|
|||
);
|
||||
|
||||
for (const member of membersWithTheRole) {
|
||||
try {
|
||||
pluginData.state.logs.ignoreLog(LogType.MEMBER_ROLE_REMOVE, member.id);
|
||||
await member.roles.remove(roleId);
|
||||
pluginData.getPlugin(LogsPlugin).logMemberRoleRemove({
|
||||
member,
|
||||
roles: [role],
|
||||
mod: msg.author,
|
||||
});
|
||||
assigned++;
|
||||
} catch (e) {
|
||||
logger.warn(`Error when removing role via !massremoverole: ${e.message}`);
|
||||
failed.push(member.id);
|
||||
}
|
||||
pluginData.getPlugin(RoleManagerPlugin).removeRole(member.id, roleId);
|
||||
pluginData.getPlugin(LogsPlugin).logMemberRoleRemove({
|
||||
member,
|
||||
roles: [role],
|
||||
mod: msg.author,
|
||||
});
|
||||
assigned++;
|
||||
}
|
||||
|
||||
let resultMessage = `Removed role **${role.name}** from ${assigned} ${assigned === 1 ? "member" : "members"}!`;
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { GuildChannel } from "discord.js";
|
||||
import { commandTypeHelpers as ct } from "../../../commandTypes";
|
||||
import { LogType } from "../../../data/LogType";
|
||||
import { canActOn, sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils";
|
||||
import { resolveRoleId, verboseUserMention } from "../../../utils";
|
||||
import { LogsPlugin } from "../../Logs/LogsPlugin";
|
||||
import { RoleManagerPlugin } from "../../RoleManager/RoleManagerPlugin";
|
||||
import { rolesCmd } from "../types";
|
||||
|
||||
export const RemoveRoleCmd = rolesCmd({
|
||||
|
@ -49,10 +49,7 @@ export const RemoveRoleCmd = rolesCmd({
|
|||
return;
|
||||
}
|
||||
|
||||
pluginData.state.logs.ignoreLog(LogType.MEMBER_ROLE_REMOVE, args.member.id);
|
||||
|
||||
await args.member.roles.remove(roleId);
|
||||
|
||||
pluginData.getPlugin(RoleManagerPlugin).removeRole(args.member.id, roleId);
|
||||
pluginData.getPlugin(LogsPlugin).logMemberRoleRemove({
|
||||
mod: msg.author,
|
||||
member: args.member,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { AutoDeletePlugin } from "./AutoDelete/AutoDeletePlugin";
|
||||
import { AutomodPlugin } from "./Automod/AutomodPlugin";
|
||||
import { AutoReactionsPlugin } from "./AutoReactions/AutoReactionsPlugin";
|
||||
import { AutomodPlugin } from "./Automod/AutomodPlugin";
|
||||
import { BotControlPlugin } from "./BotControl/BotControlPlugin";
|
||||
import { CasesPlugin } from "./Cases/CasesPlugin";
|
||||
import { CensorPlugin } from "./Censor/CensorPlugin";
|
||||
|
@ -12,6 +12,7 @@ import { CustomEventsPlugin } from "./CustomEvents/CustomEventsPlugin";
|
|||
import { GuildAccessMonitorPlugin } from "./GuildAccessMonitor/GuildAccessMonitorPlugin";
|
||||
import { GuildConfigReloaderPlugin } from "./GuildConfigReloader/GuildConfigReloaderPlugin";
|
||||
import { GuildInfoSaverPlugin } from "./GuildInfoSaver/GuildInfoSaverPlugin";
|
||||
import { GuildMemberCachePlugin } from "./GuildMemberCache/GuildMemberCachePlugin";
|
||||
import { InternalPosterPlugin } from "./InternalPoster/InternalPosterPlugin";
|
||||
import { LocateUserPlugin } from "./LocateUser/LocateUserPlugin";
|
||||
import { LogsPlugin } from "./Logs/LogsPlugin";
|
||||
|
@ -53,6 +54,7 @@ export const guildPlugins: Array<ZeppelinGuildPluginBlueprint<any>> = [
|
|||
PostPlugin,
|
||||
ReactionRolesPlugin,
|
||||
MessageSaverPlugin,
|
||||
GuildMemberCachePlugin,
|
||||
ModActionsPlugin,
|
||||
NameHistoryPlugin,
|
||||
RemindersPlugin,
|
||||
|
@ -91,6 +93,7 @@ export const baseGuildPlugins: Array<ZeppelinGuildPluginBlueprint<any>> = [
|
|||
GuildInfoSaverPlugin,
|
||||
MessageSaverPlugin,
|
||||
NameHistoryPlugin,
|
||||
GuildMemberCachePlugin,
|
||||
CasesPlugin,
|
||||
MutesPlugin,
|
||||
TimeAndDatePlugin,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue