add vulnchatters command and add comments

This commit is contained in:
2025-06-24 20:14:45 +02:00
parent dcfb013447
commit 5728440fcd
6 changed files with 44 additions and 8 deletions

View File

@@ -38,12 +38,12 @@ async function initAuth(userId: string, clientId: string, clientSecret: string,
await deleteAuthRecord(userId); await deleteAuthRecord(userId);
const code = await codepromise; const code = await codepromise;
await server.stop(false); await server.stop(true);
console.info(`Authentication code received.`); console.info(`Authentication code received.`);
const tokenData = await exchangeCode(clientId, clientSecret, code, redirectURL); const tokenData = await exchangeCode(clientId, clientSecret, code, redirectURL);
console.info(`Successfully authenticated code.`); console.info(`Successfully authenticated code.`);
await createAuthRecord(tokenData, userId); await createAuthRecord(tokenData, userId);
console.info(`Successfully saved auth data in the database.`) console.info(`Successfully saved auth data in the database.`);
return tokenData; return tokenData;
}; };

View File

@@ -0,0 +1,12 @@
import { redis } from "bun";
import { Command, sendMessage } from ".";
export default new Command('vulnchatters',
['vulnchatters', 'vulnc'],
[],
async msg => {
const data = await redis.keys('vulnchatters:*');
const one = data.length === 1;
await sendMessage(`There ${one ? 'is' : 'are'} ${data.length} vulnerable chatter${one ? '' : 's'}`, msg.messageId);
}
);

View File

@@ -13,8 +13,6 @@ eventSub.onSubscriptionCreateSuccess(event => {
eventSub.onSubscriptionCreateFailure(event => { eventSub.onSubscriptionCreateFailure(event => {
console.error(`Failed to create EventSub subscription: ${event.id}`); console.error(`Failed to create EventSub subscription: ${event.id}`);
event.stop()
event.start()
}); });
eventSub.onSubscriptionDeleteSuccess(event => { eventSub.onSubscriptionDeleteSuccess(event => {

View File

@@ -1,4 +1,4 @@
import { chatterId, streamerId, eventSub, commandPrefix, singleUserMode } from ".."; import { chatterId, streamerId, eventSub, commandPrefix, singleUserMode, unbannableUsers } from "..";
import { User } from "../user"; import { User } from "../user";
import commands, { sendMessage } from "../commands"; import commands, { sendMessage } from "../commands";
@@ -6,11 +6,18 @@ console.info(`Loaded the following commands: ${commands.keys().toArray().join(',
eventSub.onChannelChatMessage(streamerId, streamerId, async msg => { eventSub.onChannelChatMessage(streamerId, streamerId, async msg => {
// return if double user mode is on and the chatter says something, we don't need them // return if double user mode is on and the chatter says something, we don't need them
if (!singleUserMode && msg.chatterId === chatterId) return if (!singleUserMode && msg.chatterId === chatterId) return;
// Get user from cache or place user in cache // Get user from cache or place user in cache
// Given the fact that this is the user that chats, this user object always exists and cannot be null
//
// One of the flaws with the user object is solved by creating the object with the name.
// This way, if a user changes their name, the original name stays in the cache for at least 1 hour (extendable by using that name to timeout)
// and both are usable to target the same user (id is the same)
const user = await User.initUsername(msg.chatterName); const user = await User.initUsername(msg.chatterName);
if (!unbannableUsers.includes(msg.chatterId)) user?.makeVulnerable(); // Make the user vulnerable to explosions
// Parse commands: // Parse commands:
if (msg.messageText.startsWith(commandPrefix)) { if (msg.messageText.startsWith(commandPrefix)) {
const commandSelection = msg.messageText.slice(commandPrefix.length).split(' ')[0]!; const commandSelection = msg.messageText.slice(commandPrefix.length).split(' ')[0]!;

View File

@@ -21,7 +21,7 @@ export const timeout = async (target: string | User, reason: string, duration?:
if (banStatus[0]) return { status: false, reason: 'banned' }; if (banStatus[0]) return { status: false, reason: 'banned' };
if (await streamerApi.moderation.checkUserMod(streamerId, user.id!)) { if (await streamerApi.moderation.checkUserMod(streamerId, user.id!)) {
if (!duration) duration = 60; if (!duration) duration = 60; // make sure that mods don't get perma-banned
remodMod(user, duration); remodMod(user, duration);
await streamerApi.moderation.removeModerator(streamerId, user.id!); await streamerApi.moderation.removeModerator(streamerId, user.id!);
}; };

View File

@@ -2,7 +2,17 @@ import { redis } from "bun";
import { chatterApi } from "."; import { chatterApi } from ".";
import { HelixUser } from "@twurple/api" import { HelixUser } from "@twurple/api"
const EXPIRETIME = 60 * 30 // 30 minutes const EXPIRETIME = 60 * 60 // 60 minutes
// The objective of this class is to:
// store displayname, username and id to reduce api calls
// keep track of temporary user specific flags (vulnerable to explosives, locked from using items)
//
// The userlookup key is used to find id's based on the username.
//
// The vulnchatters and userlookup look similar, but they're not the same
// userlookup expiration gets set when user chats or is targeted by another user
// vulnchatters only gets set when user chats
export class User { export class User {
public username!: string; public username!: string;
@@ -79,4 +89,13 @@ export class User {
public async clearLock(): Promise<void> { public async clearLock(): Promise<void> {
await redis.set(`user:${this.id}:itemlock`, '0'); await redis.set(`user:${this.id}:itemlock`, '0');
}; };
public async makeVulnerable(): Promise<void> {
await redis.set(`vulnchatters:${this.id}`, this.displayName);
await redis.expire(`vulnchatters:${this.id}`, Math.floor(EXPIRETIME / 2)); // Vulnerable chatter gets removed from the pool after 30 minutes
};
public async makeInvulnerable(): Promise<void> {
await redis.del(`vulnchatters:${this.id}`);
};
}; };