add bot status and minor changes

This commit is contained in:
2025-09-21 17:21:50 +02:00
parent 86b2a30f5f
commit 85b584c87e
7 changed files with 60 additions and 8 deletions

View File

@@ -16,12 +16,18 @@ Admins have the power to destroy the item economy. Be very careful with admin po
Invulns, or invulnerable chatters cannot be shot with items and cannot get hit by explosives.
When an invuln uses an item or a lootbox they lose their invuln status.
The intended use for invulns is for when you need to talk to a chatter, or for using other bots.
The intended use for invulns is for when a chatter doesn't want to participate in pvp or for when you need to talk to someone.
Invulns don't need moderator or vip status in the channel.
The chatterbot and streamer always are invuln and cannot be stripped of this status.
Moderators can add and remove invulns.
On your first message in chat you will recieve 10 minutes of invuln status.
### Bots
Bots are ignored by the program.
Bots cannot be timed out.
Bots don't need moderator or vip status.
### Commands
Commands are functions that are triggered by typing an instruction in the chat.
@@ -147,6 +153,8 @@ COMMAND|FUNCTION|USER|ALIASES|DISABLEABLE
`testcheer {amount} [args]`|Create a fake cheering event|streamer/chatterbot|`testcheer`|:x:
`addinvuln {target}`|Adds an invuln user|moderator|`addinvuln`|:x:
`removeinvuln {target}`|Removes an invuln user|moderator|`removeinvuln`|:x:
`addbot {target}`|Adds bot status to a specific chatter|streamer/chatterbot|`addbot`|:x:
`removebot {target}`|Removes bot status from a specific chatter|streamer/chatterbot|`removebot`|:x:
`addadmin {target}`|Adds an admin|streamer/chatterbot|`addadmin`|:x:
`removeadmin {target}`|Removes an admin|streamer/chatterbot|`removeadmin`|:x:

23
src/commands/addbot.ts Normal file
View File

@@ -0,0 +1,23 @@
import { redis } from "bun";
import { Command, sendMessage } from "commands";
import parseCommandArgs from "lib/parseCommandArgs";
import { streamerUsers } from "main";
import User from "user";
export default new Command({
name: 'addbot',
aliases: ['addbot'],
usertype: 'streamer',
disableable: false,
execution: async msg => {
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 (streamerUsers.includes(target.id)) { await sendMessage(`Cannot change bot status of qweribot managed user`, msg.messageId); return; };
const data = await redis.set(`user:${target.id}:bot`, '1');
await target.clearVulnerable();
if (data === "OK") await sendMessage(`${target.displayName} is now a bot`, msg.messageId);
else await sendMessage(`${target.displayName} is already a bot`, msg.messageId);
}
});

22
src/commands/removebot.ts Normal file
View File

@@ -0,0 +1,22 @@
import { Command, sendMessage } from "commands";
import { streamerUsers } from "main";
import { redis } from "bun";
import parseCommandArgs from "lib/parseCommandArgs";
import User from "user";
export default new Command({
name: 'removebot',
aliases: ['removebot'],
usertype: 'streamer',
disableable: false,
execution: async msg => {
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 (streamerUsers.includes(target.id)) { await sendMessage(`Cannot change bot status of qweribot managed user`, msg.messageId); return; };
const data = await redis.del(`user:${target.id}:bot`);
if (data === 1) await sendMessage(`${target.displayName} is no longer a bot`, msg.messageId);
else await sendMessage(`${target.displayName} isn't a bot`, msg.messageId);
}
});

View File

@@ -28,7 +28,7 @@ async function parseChatMessage(msg: EventSubChannelChatMessageEvent) {
// and both are usable to target the same user (id is the same)
// The only problem would be if a user changed their name and someone else took their name right after
if (msg.chatterId === chatterId && chatterId !== streamerId) return;
if (await redis.exists(`user:${user?.id}:bot`)) return; // Ignore all bot commands
if (!await redis.exists(`user:${user?.id}:haschatted`) && !msg.sourceMessageId) {
const message = await sendMessage(`Welcome ${user?.displayName}. Please note: This chat has PvP, if you get timed out that's part of the qwerinope experience. You have 10 minutes of invincibility. A full list of commands and items can be found here: https://github.com/qwerinope/qweribot/#qweribot`);

View File

@@ -1,4 +1,3 @@
import { redis } from "bun";
import { sendMessage } from "commands";
import { getUserRecord } from "db/dbUser";
import { changeItemCount } from "items";
@@ -6,10 +5,8 @@ import { eventSub, streamerApi, streamerId } from "main";
import User from "user";
eventSub.onChannelRaidTo(streamerId, async msg => {
await sendMessage(`Ty for raiding ${msg.raidingBroadcasterDisplayName}. You get 10 minutes of invulnerability and 3 pieces of TNT. Enjoy!`);
await sendMessage(`Ty for raiding ${msg.raidingBroadcasterDisplayName}. You get 3 pieces of TNT. Enjoy!`);
await streamerApi.chat.shoutoutUser(streamerId, msg.raidingBroadcasterId);
await redis.set(`user:${msg.raidingBroadcasterId}:invuln`, '1');
await redis.expire(`user:${msg.raidingBroadcasterId}:invuln`, 600);
const raider = await User.initUsername(msg.raidingBroadcasterName);
const result = await changeItemCount(raider!, await getUserRecord(raider!), 'tnt', 3);
if (!result) await sendMessage("oopsies, no tnt for you!");

View File

@@ -9,7 +9,7 @@ import { remodMod, timeoutDuration } from "lib/timeout";
import User from "user";
import { connectionCheck } from "connectionCheck";
await connectionCheck()
await connectionCheck();
const CHATTERINTENTS = ["user:read:chat", "user:write:chat", "user:bot", "user:manage:whispers"];
const STREAMERINTENTS = ["channel:bot", "user:read:chat", "moderation:read", "channel:manage:moderators", "moderator:manage:chat_messages", "moderator:manage:banned_users", "bits:read", "channel:moderate", "moderator:manage:shoutouts"];
@@ -36,6 +36,8 @@ export const chatterEventSub = singleUserMode ? eventSub : new EventSubWsListene
export const commandPrefix = process.env.COMMAND_PREFIX ?? "!";
if (!singleUserMode) await redis.set(`user:${chatterId}:bot`, '1');
export const streamerUsers = [chatterId, streamerId];
streamerUsers.forEach(async id => await Promise.all([addAdmin(id), addInvuln(id), redis.set(`user:${id}:mod`, '1')]));

View File

@@ -13,7 +13,7 @@ type TimeoutResult = SuccessfulTimeout | UnSuccessfulTimeout;
* @param reason - reason for timeout/ban
* @param duration - duration of timeout. don't specifiy for ban */
export const timeout = async (user: User, reason: string, duration?: number): Promise<TimeoutResult> => {
if (await isInvuln(user.id) && duration) return { status: false, reason: 'illegal' }; // Don't timeout invulnerable chatters
if (await isInvuln(user.id) && duration || await redis.exists(`user:${user.id}:bot`)) return { status: false, reason: 'illegal' }; // Don't timeout invulnerable chatters and bots
// Check if user already has a timeout and handle stacking
const banStatus = await timeoutDuration(user);