rework auth (i'm an idiot), add whisper commands, change whispercmds, back to webhook

This commit is contained in:
2025-11-20 20:23:24 +01:00
parent 977082f38e
commit 34fa80e292
20 changed files with 263 additions and 200 deletions

View File

@@ -3,28 +3,10 @@ import { ApiClient } from "@twurple/api";
import { connectionCheck } from "connectionCheck";
import logger from "lib/logger";
import { redis } from "lib/redis";
import { createAuthProvider } from "auth";
import { EventSubWsListener } from "@twurple/eventsub-ws";
import { createAuthProvider, type authProviderInstructions } from "auth";
import { user, password, database, host } from "db/connection";
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", "channel:read:subscriptions", "channel:manage:redemptions"];
export const chatterAuthProvider = await createAuthProvider(chatterId, singleUserMode ? CHATTERINTENTS.concat(STREAMERINTENTS) : CHATTERINTENTS);
export const streamerAuthProvider = singleUserMode ? undefined : await createAuthProvider(streamerId, STREAMERINTENTS, true);
/** chatterApi should be used for sending messages, retrieving user data, etc */
export const chatterApi = new ApiClient({ authProvider: chatterAuthProvider });
/** streamerApi should be used for: adding/removing mods, managing timeouts, etc. */
export const streamerApi = streamerAuthProvider ? new ApiClient({ authProvider: streamerAuthProvider }) : chatterApi; // if there is no streamer user, use the chatter user
/** As the streamerApi has either the streamer or the chatter if the chatter IS the streamer this has streamer permissions */
export const eventSub = new EventSubWsListener({ apiClient: streamerApi });
export const chatterEventSub = singleUserMode ? eventSub : new EventSubWsListener({ apiClient: chatterApi });
import { ConnectionAdapter, EventSubHttpListener, ReverseProxyAdapter } from "@twurple/eventsub-http";
import { NgrokAdapter } from "@twurple/eventsub-ngrok";
if (chatterId === "") { logger.enverr('CHATTER_ID'); process.exit(1); };
if (streamerId === "") { logger.enverr('STREAMER_ID'); process.exit(1); };
@@ -33,6 +15,59 @@ 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", "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)
});
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";
@@ -42,7 +77,7 @@ import { remodMod, timeoutDuration } from "lib/timeout";
streamerUsers.forEach(async id => await Promise.all([addAdmin(id), addInvuln(id), redis.set(`user:${id}:mod`, '1')]));
const banned = await streamerApi.moderation.getBannedUsers(streamerId).then(a => a.data);
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;
@@ -52,7 +87,7 @@ for (const ban of banned) {
};
};
const mods = await streamerApi.moderation.getModerators(streamerId).then(a => a.data);
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 ${mod.userDisplayName} in the Redis/Valkey database.`);
@@ -68,7 +103,7 @@ for (const remod of bannedmods) {
logger.info(`Set the remod timer for ${target?.displayName} to ${duration} seconds.`);
};
const subs = await streamerApi.subscriptions.getSubscriptions(streamerId).then(a => a.data);
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)) {
@@ -81,7 +116,7 @@ for (const sub of subs) {
redisSubs.map(async a => await redis.del(`user:${a}:subbed`));
const streamdata = await streamerApi.streams.getStreamByUserId(streamerId);
const streamdata = await api.streams.getStreamByUserId(streamerId);
if (streamdata) await redis.set('streamIsLive', '1');
await import("./events");