import { connectionCheck } from "connectionCheck"; import { ApiClient } from "@twurple/api"; import { type ConnectionAdapter, EventSubHttpListener, ReverseProxyAdapter, } from "@twurple/eventsub-http"; import { NgrokAdapter } from "@twurple/eventsub-ngrok"; import { type authProviderInstructions, createAuthProvider } from "auth"; import { database, host, password, user } from "db/connection"; import logger from "lib/logger"; import { redis } from "lib/redis"; import { chatterId, singleUserMode, streamerId, streamerUsers } from "main"; if (chatterId === "") { logger.enverr("CHATTER_ID"); process.exit(1); } if (streamerId === "") { logger.enverr("STREAMER_ID"); process.exit(1); } if (!user) { logger.enverr("POSTGRES_USER"); process.exit(1); } if (!password) { logger.enverr("POSTGRES_USER"); process.exit(1); } if (!database) { logger.enverr("POSTGRES_DB"); process.exit(1); } if (!host) { logger.enverr("POSTGRES_HOST"); process.exit(1); } const eventSubHostName = process.env.EVENTSUB_HOSTNAME ?? (() => { logger.enverr("EVENTSUB_HOSTNAME"); process.exit(1); })(); const eventSubPort = process.env.EVENTSUB_PORT ?? (() => { logger.enverr("EVENTSUB_PORT"); process.exit(1); })(); const eventSubSecret = process.env.EVENTSUB_SECRET ?? (() => { logger.enverr("EVENTSUB_SECRET"); process.exit(1); })(); const eventSubPath = process.env.EVENTSUB_PATH; await connectionCheck(); const CHATTERINTENTS = [ "user:read:chat", "user:write:chat", "user:bot", "user:manage:whispers", ]; const STREAMERINTENTS = [ "channel:bot", "user:read:chat", "moderation:read", "moderator:read:chatters", "channel:manage:moderators", "moderator:manage:chat_messages", "moderator:manage:banned_users", "bits:read", "channel:moderate", "moderator:manage:shoutouts", "channel:read:subscriptions", "channel:manage:redemptions", ]; const users: authProviderInstructions[] = [ { userId: streamerId, intents: singleUserMode ? CHATTERINTENTS.concat(STREAMERINTENTS) : STREAMERINTENTS, streamer: true, }, ]; if (!singleUserMode) users.push({ userId: chatterId, intents: CHATTERINTENTS, streamer: false, }); const adapter: ConnectionAdapter = process.env.NODE_ENV === "development" ? new NgrokAdapter({ ngrokConfig: { authtoken: process.env.EVENTSUB_NGROK_TOKEN ?? (() => { logger.enverr("EVENTSUB_NGROK_TOKEN"); process.exit(1); })(), }, }) : new ReverseProxyAdapter({ pathPrefix: eventSubPath, hostName: eventSubHostName, port: parseInt(eventSubPort, 10), }); const authProvider = await createAuthProvider(users); export const api = new ApiClient({ authProvider }); export const eventSub = new EventSubHttpListener({ apiClient: api, secret: eventSubSecret, adapter, }); if (!singleUserMode) await redis.set(`user:${chatterId}:bot`, "1"); import { addAdmin } from "lib/admins"; import { addInvuln } from "lib/invuln"; import { remodMod, timeoutDuration } from "lib/timeout"; import User from "user"; streamerUsers.forEach( async (id) => await Promise.all([ addAdmin(id), addInvuln(id), redis.set(`user:${id}:mod`, "1"), ]), ); // Deleting all timeouts to prevent ghosts while bot was off await redis .keys("user:*:timeout") .then(async (a) => a.map(async (b) => await redis.del(b))); const banned = await api.moderation .getBannedUsers(streamerId) .then((a) => a.data); for (const ban of banned) { await redis.set(`user:${ban.userId}:timeout`, "1"); const banlength = ban.expiryDate; if (banlength) { redis.expire( `user:${ban.userId}:timeout`, Math.floor((ban.expiryDate.getTime() - Date.now()) / 1000) + 1, ); logger.info( `Set the timeout of \x1b[3;4;1;95m${ban.userDisplayName}\x1b[0;97m in the Redis/Valkey database.`, ); } } const mods = await api.moderation.getModerators(streamerId).then((a) => a.data); for (const mod of mods) { await redis.set(`user:${mod.userId}:mod`, "1"); logger.info( `Set the mod status of \x1b[3;4;1;95m${mod.userDisplayName}\x1b[0;97m in the Redis/Valkey database.`, ); } const bannedmods = await redis .keys("user:*:remod") .then((a) => Array.from(a).map((b) => b.slice(5, -6))); for (const remod of bannedmods) { const target = await User.initUserId(remod); const durationdata = await timeoutDuration(target!); let duration = 0; if (durationdata) duration = Math.floor((durationdata * 1000 - Date.now()) / 1000); remodMod(target!, duration); logger.info( `Set the remod timer for \x1b[3;4;1;95m${target?.displayName}\x1b[0;97m to \x1b[3;4;1;95m${duration}\x1b[0;97m seconds.`, ); } const subs = await api.subscriptions .getSubscriptions(streamerId) .then((a) => a.data); const redisSubs = await redis .keys("user:*:subbed") .then((a) => a.map((b) => b.slice(5, -7))); for (const sub of subs) { if (redisSubs.includes(sub.userId)) { const index = redisSubs.indexOf(sub.userId); redisSubs.splice(index, 1); continue; } await redis.set(`user:${sub.userId}:subbed`, sub.tier.slice(0, 1)); } redisSubs.map(async (a) => await redis.del(`user:${a}:subbed`)); const streamdata = await api.streams.getStreamByUserId(streamerId); if (streamdata) await redis.set("streamIsLive", "1"); else await redis.del("streamIsLive"); await import("./events"); await import("./pointRedeems"); await import("./web");