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. 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. 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. 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. The chatterbot and streamer always are invuln and cannot be stripped of this status.
Moderators can add and remove invulns. Moderators can add and remove invulns.
On your first message in chat you will recieve 10 minutes of invuln status. 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
Commands are functions that are triggered by typing an instruction in the chat. 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: `testcheer {amount} [args]`|Create a fake cheering event|streamer/chatterbot|`testcheer`|:x:
`addinvuln {target}`|Adds an invuln user|moderator|`addinvuln`|:x: `addinvuln {target}`|Adds an invuln user|moderator|`addinvuln`|:x:
`removeinvuln {target}`|Removes an invuln user|moderator|`removeinvuln`|: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: `addadmin {target}`|Adds an admin|streamer/chatterbot|`addadmin`|:x:
`removeadmin {target}`|Removes an admin|streamer/chatterbot|`removeadmin`|: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) // 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 // 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) { 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`); 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 { sendMessage } from "commands";
import { getUserRecord } from "db/dbUser"; import { getUserRecord } from "db/dbUser";
import { changeItemCount } from "items"; import { changeItemCount } from "items";
@@ -6,10 +5,8 @@ import { eventSub, streamerApi, streamerId } from "main";
import User from "user"; import User from "user";
eventSub.onChannelRaidTo(streamerId, async msg => { 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 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 raider = await User.initUsername(msg.raidingBroadcasterName);
const result = await changeItemCount(raider!, await getUserRecord(raider!), 'tnt', 3); const result = await changeItemCount(raider!, await getUserRecord(raider!), 'tnt', 3);
if (!result) await sendMessage("oopsies, no tnt for you!"); 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 User from "user";
import { connectionCheck } from "connectionCheck"; import { connectionCheck } from "connectionCheck";
await connectionCheck() await connectionCheck();
const CHATTERINTENTS = ["user:read:chat", "user:write:chat", "user:bot", "user:manage:whispers"]; 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"]; 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 ?? "!"; export const commandPrefix = process.env.COMMAND_PREFIX ?? "!";
if (!singleUserMode) await redis.set(`user:${chatterId}:bot`, '1');
export const streamerUsers = [chatterId, streamerId]; export const streamerUsers = [chatterId, streamerId];
streamerUsers.forEach(async id => await Promise.all([addAdmin(id), addInvuln(id), redis.set(`user:${id}:mod`, '1')])); 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 reason - reason for timeout/ban
* @param duration - duration of timeout. don't specifiy for ban */ * @param duration - duration of timeout. don't specifiy for ban */
export const timeout = async (user: User, reason: string, duration?: number): Promise<TimeoutResult> => { 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 // Check if user already has a timeout and handle stacking
const banStatus = await timeoutDuration(user); const banStatus = await timeoutDuration(user);