From c960ae49bffd29fa85b3d4bd51d6bde2a29a00d9 Mon Sep 17 00:00:00 2001 From: qwerinope Date: Fri, 27 Jun 2025 14:31:20 +0200 Subject: [PATCH] add admin powers --- README.md | 43 ++++++++++++++++++++++++------------- bot/commands/addadmin.ts | 16 ++++++++++++++ bot/commands/admingive.ts | 4 ++-- bot/commands/getadmins.ts | 13 +++++++++++ bot/commands/removeadmin.ts | 17 +++++++++++++++ bot/index.ts | 2 ++ bot/lib/admins.ts | 14 ++++++++++++ 7 files changed, 92 insertions(+), 17 deletions(-) create mode 100644 bot/commands/addadmin.ts create mode 100644 bot/commands/getadmins.ts create mode 100644 bot/commands/removeadmin.ts create mode 100644 bot/lib/admins.ts diff --git a/README.md b/README.md index b0c740a..41cd395 100644 --- a/README.md +++ b/README.md @@ -2,34 +2,47 @@ ## Commands +### About commands + All of these command need a prefix. By default this is `!` Arguments like `[this]` are optional. Arguments like `{this}` are required. +Commands and items can be disabled and enabled by admins with the [`enable` and `disable` commands](#administrative-commands). +Not all Commands can be disabled, the `DISABLEABLE` field shows if they can or can't. Items can always be disabled. + +Admins are defined by the streamer and can use special administrative command on the bot. +Admins don't need to have moderator status in the channel. +The chatterbot and streamer always have admin status and cannot be stripped of admin powers. +Only the streamer and chatterbot have the power to add and remove admins. + ### Fun commands -COMMAND|FUNCTION|USER|ALIASES --|-|-|- -`ping`|Testing command|anyone|`ping` -`yabai`|Random number|anyone|`yabai` `goon` -`seiso`|Random number|anyone|`seiso` +COMMAND|FUNCTION|USER|ALIASES|DISABLEABLE +-|-|-|-|- +`ping`|Testing command|anyone|`ping`|:white_check_mark: +`yabai`|Random number|anyone|`yabai` `goon`|:white_check_mark: +`seiso`|Random number|anyone|`seiso`|:white_check_mark: ### Item commands -COMMAND|FUNCTION|USER|ALIASES --|-|-|- -`iteminfo {item}`|Get item function and aliases|anyone|`iteminfo` `itemhelp` `info` -`inventory [target]`|Get inventory contents of target or self|anyone|`inventory` `inv` -`give {target} {item} {amount}`|Give targeted user amount of items|anyone|`give` -`use {item} ...`|Use item. More info at [The items section](#items)|anyone|`use` -`admingive {target} {item} {amount}`|Give targeted user amount of new items|admins|`admingive` +COMMAND|FUNCTION|USER|ALIASES|DISABLEABLE +-|-|-|-|- +`iteminfo {item}`|Get item function and aliases|anyone|`iteminfo` `itemhelp` `info`|:white_check_mark: +`inventory [target]`|Get inventory contents of target or self|anyone|`inventory` `inv`|:white_check_mark: +`give {target} {item} {amount}`|Give targeted user amount of items|anyone|`give`|:white_check_mark: +`use {item} ...`|Use item. More info at [The items section](#items)|anyone|`use`|:white_check_mark: +`admingive {target} {item} {amount}`|Give targeted user amount of new items|admins|`admingive`|:x: ### Administrative commands -COMMAND|FUNCTION|USER|ALIASES --|-|-|- -`vulnchatters`|Get amount of chatters vulnerable to explosives|anyone|`vulnchatters` `vulnc` +COMMAND|FUNCTION|USER|ALIASES|DISABLEABLE +-|-|-|-|- +`vulnchatters`|Get amount of chatters vulnerable to explosives|anyone|`vulnchatters` `vulnc`|:white_check_mark: +`getadmins`|Get a list of every admin in the channel|anyone|`getadmins`|:x: +`addadmin {target}`|Adds an admin|streamer/botchatter|`addadmin`|:x: +`removeadmin {target}`|Removes an admin|streamer/botchatter|`removeadmin`|:x: ## Items diff --git a/bot/commands/addadmin.ts b/bot/commands/addadmin.ts new file mode 100644 index 0000000..85c3a21 --- /dev/null +++ b/bot/commands/addadmin.ts @@ -0,0 +1,16 @@ +import { Command, sendMessage } from "."; +import { addAdmin } from "../lib/admins"; +import parseCommandArgs from "../lib/parseCommandArgs"; +import { User } from "../user"; +import { unbannableUsers } from ".."; + +export default new Command('addadmin', ['addadmin'], [], async msg => { + if (!unbannableUsers.includes(msg.chatterId)) return; + const args = parseCommandArgs(msg.messageText); + if (!args[0]) { await sendMessage('Please specify a target', msg.messageId); return; }; + const target = await User.initUsername(args[0].toLowerCase()); + if (!target) { await sendMessage(`Chatter ${args[0]} doesn't exist`, msg.messageId); return; }; + const data = await addAdmin(target.id); + if (data === 1) await sendMessage(`${target.displayName} is now an admin`, msg.messageId); + else await sendMessage(`${target.displayName} is already an admin`, msg.messageId); +}, false); diff --git a/bot/commands/admingive.ts b/bot/commands/admingive.ts index 7920cdc..d7dfec0 100644 --- a/bot/commands/admingive.ts +++ b/bot/commands/admingive.ts @@ -1,12 +1,12 @@ import { Command, sendMessage } from "."; -import { unbannableUsers } from ".."; import { getUserRecord } from "../db/dbUser"; import items, { changeItemCount } from "../items"; +import { isAdmin } from "../lib/admins"; import parseCommandArgs from "../lib/parseCommandArgs"; import { User } from "../user"; export default new Command('admingive', ['admingive'], [], async msg => { - if (!unbannableUsers.includes(msg.chatterId)) { await sendMessage('nah', msg.messageId); return; }; + if (!await isAdmin(msg.chatterId)) return; const args = parseCommandArgs(msg.messageText); if (!args[0]) { await sendMessage('Please specify a user', msg.messageId); return; }; const target = await User.initUsername(args[0].toLowerCase()); diff --git a/bot/commands/getadmins.ts b/bot/commands/getadmins.ts new file mode 100644 index 0000000..35ae4cc --- /dev/null +++ b/bot/commands/getadmins.ts @@ -0,0 +1,13 @@ +import { Command, sendMessage } from "."; +import { getAdmins } from "../lib/admins"; +import { User } from "../user"; + +export default new Command('getadmins', ['getadmins'], [], async msg => { + const admins = await getAdmins() + const adminnames: string[] = []; + for (const id of admins) { + const admin = await User.initUserId(id); + adminnames.push(admin?.displayName!); + }; + await sendMessage(`Current admins: ${adminnames.join(', ')}`, msg.messageId); +}, false); diff --git a/bot/commands/removeadmin.ts b/bot/commands/removeadmin.ts new file mode 100644 index 0000000..3924783 --- /dev/null +++ b/bot/commands/removeadmin.ts @@ -0,0 +1,17 @@ +import { Command, sendMessage } from "."; +import { unbannableUsers } from ".."; +import { removeAdmin } from "../lib/admins"; +import parseCommandArgs from "../lib/parseCommandArgs"; +import { User } from "../user"; + +export default new Command('removeadmin', ['removeadmin'], [], async msg => { + if (!unbannableUsers.includes(msg.chatterId)) return; + const args = parseCommandArgs(msg.messageText); + if (!args[0]) { await sendMessage('Please specify a target', msg.messageId); return; }; + const target = await User.initUsername(args[0].toLowerCase()); + if (!target) { await sendMessage(`Chatter ${args[0]} doesn't exist`, msg.messageId); return; }; + if (unbannableUsers.includes(target.id)) { await sendMessage(`Can't remove admin ${target.displayName} as they are managed by the bot program`, msg.messageId); return; }; + const data = await removeAdmin(target.id); + if (data === 1) await sendMessage(`${target.displayName} is no longer an admin`, msg.messageId); + else await sendMessage(`${target.displayName} isn't an admin`, msg.messageId); +}, false); diff --git a/bot/index.ts b/bot/index.ts index 0e5e81e..72d0b6c 100644 --- a/bot/index.ts +++ b/bot/index.ts @@ -3,6 +3,7 @@ import { ApiClient } from "@twurple/api"; import { EventSubHttpListener, ReverseProxyAdapter } from "@twurple/eventsub-http"; import { commandintents } from "./commands"; import { itemintents } from "./items"; +import { addAdmin } from "./lib/admins"; const CHATTERBASEINTENTS = ["user:read:chat", "user:write:chat", "user:bot"]; const STREAMERBASEINTENTS = ["user:read:chat", "moderation:read", "channel:manage:moderators"]; @@ -40,5 +41,6 @@ export const eventSub = new EventSubHttpListener({ export const commandPrefix = process.env.COMMAND_PREFIX ?? "!"; export const unbannableUsers = [chatterId, streamerId]; +unbannableUsers.forEach(async id => await addAdmin(id)); await import("./events"); diff --git a/bot/lib/admins.ts b/bot/lib/admins.ts new file mode 100644 index 0000000..3af7f4e --- /dev/null +++ b/bot/lib/admins.ts @@ -0,0 +1,14 @@ +import { redis } from "bun"; + +export async function getAdmins() { + return await redis.smembers('admins'); +}; +export async function isAdmin(userid: string) { + return await redis.sismember('admins', userid); +}; +export async function addAdmin(userid: string) { + return await redis.sadd('admins', userid); +}; +export async function removeAdmin(userid: string) { + return await redis.srem('admins', userid); +};