From fd420fa59ac912d93938a807234de27868864883 Mon Sep 17 00:00:00 2001 From: Darius Date: Thu, 2 Oct 2025 23:04:31 +0200 Subject: [PATCH] rework auth a little --- package.json | 4 +- src/bot.ts | 2 +- src/commands/base-command.ts | 2 +- src/core/{chat-client.ts => client.ts} | 11 ++- src/events/base-event.ts | 2 +- src/events/impl/message.ts | 2 +- src/events/registry.ts | 2 +- src/setup-auth.ts | 22 ----- src/setup/setup-auth.ts | 97 +++++++++++++++++++ .../api-client.ts => setup/setup-client.ts} | 2 +- src/{ => setup}/setup-env.ts | 19 +++- src/util/api-functions.ts | 4 +- src/util/general.ts | 2 +- 13 files changed, 130 insertions(+), 41 deletions(-) rename src/core/{chat-client.ts => client.ts} (62%) delete mode 100644 src/setup-auth.ts create mode 100644 src/setup/setup-auth.ts rename src/{core/api-client.ts => setup/setup-client.ts} (79%) rename src/{ => setup}/setup-env.ts (54%) diff --git a/package.json b/package.json index 24f4389..e318384 100644 --- a/package.json +++ b/package.json @@ -8,8 +8,8 @@ "build": "npm run clean && tsc -p .", "start": "node dist/bot.js", "dev": "nodemon src/bot.ts", - "setup:env": "nodemon src/setup-env.ts", - "setup:auth": "nodemon src/setup-auth.ts" + "setup:env": "node src/setup/setup-env.ts", + "setup:auth": "node src/setup/setup-auth.ts" }, "keywords": [], "author": "", diff --git a/src/bot.ts b/src/bot.ts index eb1dea1..e9044f0 100644 --- a/src/bot.ts +++ b/src/bot.ts @@ -1,4 +1,4 @@ -import { chatClient } from "./core/chat-client.ts"; +import { chatClient } from "./core/client.ts"; import { registerAllEvents } from "./events/registry.ts"; registerAllEvents(); diff --git a/src/commands/base-command.ts b/src/commands/base-command.ts index ffed26a..ba56002 100644 --- a/src/commands/base-command.ts +++ b/src/commands/base-command.ts @@ -1,6 +1,6 @@ import type { ChatUser } from "@twurple/chat"; import { Config } from "../config/config.ts"; -import { chatClient } from "../core/chat-client.ts"; +import { chatClient } from "../core/client.ts"; import type { ICommand, ICommandRequirements } from "./interface.ts"; export abstract class BaseCommand implements ICommand { diff --git a/src/core/chat-client.ts b/src/core/client.ts similarity index 62% rename from src/core/chat-client.ts rename to src/core/client.ts index ab254ef..3958dd4 100644 --- a/src/core/chat-client.ts +++ b/src/core/client.ts @@ -1,3 +1,4 @@ +import { ApiClient } from "@twurple/api"; import { type AccessToken, RefreshingAuthProvider } from "@twurple/auth"; import { ChatClient } from "@twurple/chat"; import { Config } from "../config/config.ts"; @@ -12,18 +13,20 @@ if (!tokenData) { throw new Error(); } -const authProviderPrivate = new RefreshingAuthProvider({ +export const authProviderUserToken = new RefreshingAuthProvider({ clientId: Config.client_id, clientSecret: Config.client_secret, }); -authProviderPrivate.onRefresh(async (_userId, newTokenData: AccessToken) => +authProviderUserToken.onRefresh(async (_userId, newTokenData: AccessToken) => tokenManager.createTokenFile(newTokenData), ); -authProviderPrivate.addUser(Config.bot_user_id, tokenData, ["chat"]); +authProviderUserToken.addUser(Config.bot_user_id, tokenData, ["chat"]); export const chatClient = new ChatClient({ - authProvider: authProviderPrivate, + authProvider: authProviderUserToken, channels: Config.channels, }); + +export const apiClient = new ApiClient({ authProvider: authProviderUserToken }); diff --git a/src/events/base-event.ts b/src/events/base-event.ts index 5b28168..a8242e8 100644 --- a/src/events/base-event.ts +++ b/src/events/base-event.ts @@ -1,4 +1,4 @@ -import { chatClient } from "../core/chat-client.ts"; +import { chatClient } from "../core/client.ts"; import type { IEvent } from "./interface.ts"; import type { EventName } from "./registry.ts"; diff --git a/src/events/impl/message.ts b/src/events/impl/message.ts index 639ae85..e3822a9 100644 --- a/src/events/impl/message.ts +++ b/src/events/impl/message.ts @@ -27,7 +27,7 @@ async function checkMessage( text: string, msg: ChatMessage, ) { - // logInfo(`message seen: ${channel} - ${user} - ${text}`); + //logInfo(`message seen: ${channel} - ${user} - ${text}`); const prefix = Config.prefix; if (!text.startsWith(prefix)) return; diff --git a/src/events/registry.ts b/src/events/registry.ts index 8118591..17610c2 100644 --- a/src/events/registry.ts +++ b/src/events/registry.ts @@ -1,4 +1,4 @@ -import { chatClient } from "../core/chat-client.ts"; +import { chatClient } from "../core/client.ts"; import { logInfo } from "../util/logger.ts"; import ConnectedEvent from "./impl/connected.ts"; import MessageEvent from "./impl/message.ts"; diff --git a/src/setup-auth.ts b/src/setup-auth.ts deleted file mode 100644 index 92b9fed..0000000 --- a/src/setup-auth.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { exchangeCode } from "@twurple/auth"; -import { Config } from "./config/config.ts"; -import { TokenManager } from "./core/token-manager.ts"; -import { TwitchAuth } from "./util/auth.ts"; -import { logInfo } from "./util/logger.ts"; - -const port = 3000; -const redirectUri = `http://localhost:${port}`; -const scopes = ["chat:read", "chat:edit", "channel:moderate"]; - -const auth = new TwitchAuth(redirectUri); -const state = auth.generateState(); -const authUrl = auth.getAuthorizationUrl(scopes, state); -logInfo("To authorize your Twitch bot, visit this URL:"); -logInfo(authUrl); -const code = await auth.startCallbackServer(port, 120, state); - -const tokenManager = new TokenManager(Config.bot_user_id); - -tokenManager.createTokenFile( - await exchangeCode(Config.client_id, Config.client_secret, code, redirectUri), -); diff --git a/src/setup/setup-auth.ts b/src/setup/setup-auth.ts new file mode 100644 index 0000000..51febac --- /dev/null +++ b/src/setup/setup-auth.ts @@ -0,0 +1,97 @@ +import { exchangeCode } from "@twurple/auth"; +import { Config } from "../config/config.ts"; +import { TokenManager } from "../core/token-manager.ts"; +import { TwitchAuth } from "../util/auth.ts"; +import { logInfo } from "../util/logger.ts"; + +const port = 3000; +const redirectUri = `http://localhost:${port}`; +const scopes = [ + "analytics:read:extensions", + "analytics:read:games", + "bits:read", + "channel:manage:ads", + "channel:read:ads", + "channel:manage:broadcast", + "channel:read:charity", + "channel:edit:commercial", + "channel:read:editors", + "channel:manage:extensions", + "channel:read:goals", + "channel:read:guest_star", + "channel:manage:guest_star", + "channel:read:hype_train", + "channel:manage:moderators", + "channel:read:polls", + "channel:manage:polls", + "channel:read:predictions", + "channel:manage:predictions", + "channel:manage:raids", + "channel:read:redemptions", + "channel:manage:redemptions", + "channel:manage:schedule", + "channel:read:stream_key", + "channel:read:subscriptions", + "channel:manage:videos", + "channel:read:vips", + "channel:manage:vips", + "clips:edit", + "moderation:read", + "moderator:manage:announcements", + "moderator:manage:automod", + "moderator:read:automod_settings", + "moderator:manage:automod_settings", + "moderator:manage:banned_users", + "moderator:read:blocked_terms", + "moderator:manage:blocked_terms", + "moderator:manage:chat_messages", + "moderator:read:chat_settings", + "moderator:manage:chat_settings", + "moderator:read:chatters", + "moderator:read:followers", + "moderator:read:guest_star", + "moderator:manage:guest_star", + "moderator:read:shield_mode", + "moderator:manage:shield_mode", + "moderator:read:shoutouts", + "moderator:manage:shoutouts", + "moderator:read:unban_requests", + "moderator:manage:unban_requests", + "user:edit", + "user:edit:follows", + "user:read:blocked_users", + "user:manage:blocked_users", + "user:read:broadcast", + "user:manage:chat_color", + "user:read:email", + "user:read:emotes", + "user:read:follows", + "user:read:moderated_channels", + "user:read:subscriptions", + "user:manage:whispers", + "channel:bot", + "channel:moderate", + "chat:edit", + "chat:read", + "user:bot", + "user:read:chat", + "user:write:chat", + "whispers:read", + "whispers:edit", + "moderator:manage:warnings", +]; + +const auth = new TwitchAuth(redirectUri); +const state = auth.generateState(); +const authUrl = auth.getAuthorizationUrl(scopes, state); +logInfo("To authorize your Twitch bot, visit this URL:"); +logInfo(authUrl); +const code = await auth.startCallbackServer(port, 120, state); + +const tokenManager = new TokenManager(Config.bot_user_id); + +tokenManager.createTokenFile( + await exchangeCode(Config.client_id, Config.client_secret, code, redirectUri), +); + +logInfo("Token file created"); diff --git a/src/core/api-client.ts b/src/setup/setup-client.ts similarity index 79% rename from src/core/api-client.ts rename to src/setup/setup-client.ts index c46c107..5a830f7 100644 --- a/src/core/api-client.ts +++ b/src/setup/setup-client.ts @@ -7,4 +7,4 @@ const authProvider = new AppTokenAuthProvider( Config.client_secret, ); -export const apiClient = new ApiClient({ authProvider }); +export const setupClient = new ApiClient({ authProvider }); diff --git a/src/setup-env.ts b/src/setup/setup-env.ts similarity index 54% rename from src/setup-env.ts rename to src/setup/setup-env.ts index c3574ab..edce10b 100644 --- a/src/setup-env.ts +++ b/src/setup/setup-env.ts @@ -1,12 +1,23 @@ -import { getUserId, promptForInput } from "./util/general.ts"; -import { logError, logSuccess, logWarning } from "./util/logger.ts"; +import { promptForInput } from "../util/general.ts"; +import { logError, logSuccess, logWarning } from "../util/logger.ts"; +import { setupClient } from "./setup-client.ts"; const botname = await promptForInput("enter bot username: "); const developers = ( await promptForInput("enter developer usernames (,separated): ") ).split(","); -const botId = await getUserId(botname); +const getUid = async (username: string) => { + const user = await setupClient.users.getUserByName(username); + if (user?.id) { + logSuccess(`${user.name} => ${user.id}`); + return user.id; + } + logWarning(`no user with name ${username} found`); + return ""; +}; + +const botId = await getUid(botname); if (!botId) { logError("bot not found. please check the configuration"); throw new Error(); @@ -14,7 +25,7 @@ if (!botId) { const developerIds = []; for (const dev of developers) { - const devId = await getUserId(dev); + const devId = await getUid(dev); if (devId) { developerIds.push(devId); } else { diff --git a/src/util/api-functions.ts b/src/util/api-functions.ts index 87d867e..2960860 100644 --- a/src/util/api-functions.ts +++ b/src/util/api-functions.ts @@ -1,6 +1,6 @@ -import type { UserIdResolvable } from "@twurple/api"; +import { HellFreezesOverError, type UserIdResolvable } from "@twurple/api"; import { Config } from "../config/config.ts"; -import { apiClient } from "../core/api-client.ts"; +import { apiClient } from "../core/client.ts"; import { getUserId } from "./general.ts"; import { logError } from "./logger.ts"; diff --git a/src/util/general.ts b/src/util/general.ts index 8fd5431..5cba28d 100644 --- a/src/util/general.ts +++ b/src/util/general.ts @@ -1,6 +1,6 @@ import * as readline from "node:readline"; import type { UserIdResolvable } from "@twurple/api"; -import { apiClient } from "../core/api-client.ts"; +import { apiClient } from "../core/client.ts"; import { logSuccess, logWarning } from "./logger.ts"; export async function getUserId(username: string): Promise {