mirror of
https://github.com/ZeppelinBot/Zeppelin.git
synced 2025-05-10 12:25:02 +00:00
feat: add internal role manager plugin; add role buttons plugin
This commit is contained in:
parent
9314d57645
commit
3fe71b3e27
23 changed files with 732 additions and 1 deletions
39
backend/src/plugins/RoleManager/RoleManagerPlugin.ts
Normal file
39
backend/src/plugins/RoleManager/RoleManagerPlugin.ts
Normal file
|
@ -0,0 +1,39 @@
|
|||
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
|
||||
import { ConfigSchema, RoleManagerPluginType } from "./types";
|
||||
import { GuildRoleQueue } from "../../data/GuildRoleQueue";
|
||||
import { mapToPublicFn } from "../../pluginUtils";
|
||||
import { addRole } from "./functions/addRole";
|
||||
import { removeRole } from "./functions/removeRole";
|
||||
import { addPriorityRole } from "./functions/addPriorityRole";
|
||||
import { removePriorityRole } from "./functions/removePriorityRole";
|
||||
import { runRoleAssignmentLoop } from "./functions/runRoleAssignmentLoop";
|
||||
import { LogsPlugin } from "../Logs/LogsPlugin";
|
||||
|
||||
export const RoleManagerPlugin = zeppelinGuildPlugin<RoleManagerPluginType>()({
|
||||
name: "role_manager",
|
||||
configSchema: ConfigSchema,
|
||||
showInDocs: false,
|
||||
|
||||
dependencies: () => [LogsPlugin],
|
||||
|
||||
public: {
|
||||
addRole: mapToPublicFn(addRole),
|
||||
removeRole: mapToPublicFn(removeRole),
|
||||
addPriorityRole: mapToPublicFn(addPriorityRole),
|
||||
removePriorityRole: mapToPublicFn(removePriorityRole),
|
||||
},
|
||||
|
||||
beforeLoad(pluginData) {
|
||||
pluginData.state.roleQueue = GuildRoleQueue.getGuildInstance(pluginData.guild.id);
|
||||
pluginData.state.pendingRoleAssignmentPromise = Promise.resolve();
|
||||
},
|
||||
|
||||
afterLoad(pluginData) {
|
||||
runRoleAssignmentLoop(pluginData);
|
||||
},
|
||||
|
||||
async afterUnload(pluginData) {
|
||||
pluginData.state.abortRoleAssignmentLoop = true;
|
||||
await pluginData.state.pendingRoleAssignmentPromise;
|
||||
},
|
||||
});
|
1
backend/src/plugins/RoleManager/constants.ts
Normal file
1
backend/src/plugins/RoleManager/constants.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export const PRIORITY_ROLE_PRIORITY = 10;
|
13
backend/src/plugins/RoleManager/functions/addPriorityRole.ts
Normal file
13
backend/src/plugins/RoleManager/functions/addPriorityRole.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
import { GuildPluginData } from "knub";
|
||||
import { RoleManagerPluginType } from "../types";
|
||||
import { PRIORITY_ROLE_PRIORITY } from "../constants";
|
||||
import { runRoleAssignmentLoop } from "./runRoleAssignmentLoop";
|
||||
|
||||
export async function addPriorityRole(
|
||||
pluginData: GuildPluginData<RoleManagerPluginType>,
|
||||
userId: string,
|
||||
roleId: string,
|
||||
) {
|
||||
await pluginData.state.roleQueue.addQueueItem(userId, roleId, true, PRIORITY_ROLE_PRIORITY);
|
||||
runRoleAssignmentLoop(pluginData);
|
||||
}
|
8
backend/src/plugins/RoleManager/functions/addRole.ts
Normal file
8
backend/src/plugins/RoleManager/functions/addRole.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
import { GuildPluginData } from "knub";
|
||||
import { RoleManagerPluginType } from "../types";
|
||||
import { runRoleAssignmentLoop } from "./runRoleAssignmentLoop";
|
||||
|
||||
export async function addRole(pluginData: GuildPluginData<RoleManagerPluginType>, userId: string, roleId: string) {
|
||||
await pluginData.state.roleQueue.addQueueItem(userId, roleId, true);
|
||||
runRoleAssignmentLoop(pluginData);
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
import { GuildPluginData } from "knub";
|
||||
import { RoleManagerPluginType } from "../types";
|
||||
import { PRIORITY_ROLE_PRIORITY } from "../constants";
|
||||
import { runRoleAssignmentLoop } from "./runRoleAssignmentLoop";
|
||||
|
||||
export async function removePriorityRole(
|
||||
pluginData: GuildPluginData<RoleManagerPluginType>,
|
||||
userId: string,
|
||||
roleId: string,
|
||||
) {
|
||||
await pluginData.state.roleQueue.addQueueItem(userId, roleId, false, PRIORITY_ROLE_PRIORITY);
|
||||
runRoleAssignmentLoop(pluginData);
|
||||
}
|
8
backend/src/plugins/RoleManager/functions/removeRole.ts
Normal file
8
backend/src/plugins/RoleManager/functions/removeRole.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
import { GuildPluginData } from "knub";
|
||||
import { RoleManagerPluginType } from "../types";
|
||||
import { runRoleAssignmentLoop } from "./runRoleAssignmentLoop";
|
||||
|
||||
export async function removeRole(pluginData: GuildPluginData<RoleManagerPluginType>, userId: string, roleId: string) {
|
||||
await pluginData.state.roleQueue.addQueueItem(userId, roleId, false);
|
||||
runRoleAssignmentLoop(pluginData);
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
import { GuildPluginData } from "knub";
|
||||
import { RoleManagerPluginType } from "../types";
|
||||
import { LogsPlugin } from "../../Logs/LogsPlugin";
|
||||
import { logger } from "../../../logger";
|
||||
import { RoleQueueItem } from "../../../data/entities/RoleQueueItem";
|
||||
|
||||
const ROLE_ASSIGNMENTS_PER_BATCH = 20;
|
||||
|
||||
export async function runRoleAssignmentLoop(pluginData: GuildPluginData<RoleManagerPluginType>) {
|
||||
if (pluginData.state.roleAssignmentLoopRunning || pluginData.state.abortRoleAssignmentLoop) {
|
||||
return;
|
||||
}
|
||||
pluginData.state.roleAssignmentLoopRunning = true;
|
||||
|
||||
while (true) {
|
||||
// Abort on unload
|
||||
if (pluginData.state.abortRoleAssignmentLoop) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!pluginData.state.roleAssignmentLoopRunning) {
|
||||
break;
|
||||
}
|
||||
|
||||
await (pluginData.state.pendingRoleAssignmentPromise = (async () => {
|
||||
// Process assignments in batches, stopping once the queue's exhausted
|
||||
const nextAssignments = await pluginData.state.roleQueue.consumeNextRoleAssignments(ROLE_ASSIGNMENTS_PER_BATCH);
|
||||
if (nextAssignments.length === 0) {
|
||||
pluginData.state.roleAssignmentLoopRunning = false;
|
||||
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;
|
||||
}
|
||||
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);
|
||||
|
||||
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}\`)`,
|
||||
});
|
||||
});
|
||||
}),
|
||||
);
|
||||
})());
|
||||
}
|
||||
}
|
17
backend/src/plugins/RoleManager/types.ts
Normal file
17
backend/src/plugins/RoleManager/types.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
import * as t from "io-ts";
|
||||
import { BasePluginType, typedGuildCommand } from "knub";
|
||||
import { GuildLogs } from "../../data/GuildLogs";
|
||||
import { GuildRoleQueue } from "../../data/GuildRoleQueue";
|
||||
|
||||
export const ConfigSchema = t.type({});
|
||||
export type TConfigSchema = t.TypeOf<typeof ConfigSchema>;
|
||||
|
||||
export interface RoleManagerPluginType extends BasePluginType {
|
||||
config: TConfigSchema;
|
||||
state: {
|
||||
roleQueue: GuildRoleQueue;
|
||||
roleAssignmentLoopRunning: boolean;
|
||||
abortRoleAssignmentLoop: boolean;
|
||||
pendingRoleAssignmentPromise: Promise<unknown>;
|
||||
};
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue