import { EventSubChannelChatMessageEvent } from "@twurple/eventsub-base" import { streamerId, eventSub, commandPrefix, streamerUsers, chatterId } from "main"; import User from "user"; import commands, { Command, sendMessage, specialAliasCommands } from "commands"; import { redis } from "bun"; import { isAdmin } from "lib/admins"; import cheers from "cheers"; import logger from "lib/logger"; import { addMessageToChatWidget } from "web/chatWidget/message"; import { isInvuln, setTemporaryInvuln } from "lib/invuln"; import { getUserRecord } from "db/dbUser"; import { createCheerRecord } from "db/dbCheers"; import handleAnivMessage from "lib/handleAnivMessage"; eventSub.onChannelChatMessage(streamerId, streamerId, parseChatMessage); async function parseChatMessage(msg: EventSubChannelChatMessageEvent) { addMessageToChatWidget(msg); const user = await User.initUsername(msg.chatterName); // 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 as target for item) // 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) return; 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`); await redis.set(`user:${user?.id}:haschatted`, "1"); await redis.set(`user:${user?.id}:welcomemessageid`, message.id); await redis.expire(`user:${user?.id}:welcomemessageid`, 600); if (!await isInvuln(msg.chatterId)) await setTemporaryInvuln(user?.id!); // This would set the invuln expiration lmao }; if (!await isInvuln(user?.id!)) user?.setVulnerable(); // Make the user vulnerable to explosions if not marked as invuln if (!msg.isCheer && !msg.isRedemption) await handleChatMessage(msg, user!) else if (msg.isCheer && !msg.isRedemption) await handleCheer(msg, msg.bits, user!); }; async function handleChatMessage(msg: EventSubChannelChatMessageEvent, user: User) { // Aniv message filter handleAnivMessage(msg, user); // Parse commands: const selected = selectCommand(msg.messageText); if (!selected) return; const { cmd: selection, activation } = selected; if (await redis.sismember('disabledcommands', selection.name)) return; switch (selection.usertype) { case "admin": if (!await isAdmin(user.id)) return; break; case "streamer": if (!streamerUsers.includes(msg.chatterId)) return; break; case "moderator": if (!(await redis.exists(`user:${user.id}:mod`) || await isAdmin(user.id))) return; break; }; try { await selection.execute(msg, user, { activation }); } catch (err) { logger.err(err as string); await sendMessage('ERROR: Something went wrong', msg.messageId); await user.clearLock(); }; }; type selectedCommand = { cmd: Command; activation: string; }; function selectCommand(message: string): selectedCommand | false { const specialcmdselector = message.trim().toLowerCase().split(' ')[0]!; const specialcmd = specialAliasCommands.get(specialcmdselector); if (specialcmd) return { cmd: specialcmd, activation: specialcmdselector }; const commandSelector = message.slice(commandPrefix.length).trim().toLowerCase().split(' ')[0]!; const normalcmd = commands.get(commandSelector); if (normalcmd) return { cmd: normalcmd, activation: commandPrefix + commandSelector }; return false; }; export async function handleCheer(msg: EventSubChannelChatMessageEvent, bits: number, user: User) { if (msg.isCheer) { await getUserRecord(user); // ensure they exist in the database await createCheerRecord(user, bits); }; // If this is not triggered it's because of the testcheer command. these fake bits should not be added to the database const selection = cheers.get(bits); if (!selection) return; if (await redis.sismember('disabledcheers', selection.name)) { await sendMessage(`The ${selection.name} cheer is disabled! Sorry!`, msg.messageId); return; }; try { selection.execute(msg, user); } catch (err) { logger.err(err as string); }; };