tested & working basic bot

This commit is contained in:
Darius
2025-09-27 16:12:50 +02:00
parent 44f3f7af0b
commit 10eee0c0fd
16 changed files with 161 additions and 127 deletions

View File

@@ -1,3 +1,4 @@
import * as crypto from "node:crypto";
import {
createServer,
type IncomingMessage,
@@ -5,6 +6,7 @@ import {
} from "node:http";
import { URL } from "node:url";
import { Config } from "../config/config.ts";
import { logError, logInfo } from "./logger.ts";
export class TwitchAuth {
private readonly redirectUri: string;
@@ -13,20 +15,29 @@ export class TwitchAuth {
this.redirectUri = redirectUri;
}
getAuthorizationUrl(scopes: string[]): string {
getAuthorizationUrl(scopes: string[], state: string): string {
const baseUrl = "https://id.twitch.tv/oauth2/authorize";
const params = new URLSearchParams({
response_type: "code",
client_id: Config.client_id,
redirect_uri: this.redirectUri,
scope: scopes.join(" "),
state: state,
});
return `${baseUrl}?${params.toString()}`;
}
startCallbackServer(port: number, timeoutS: number = 120): Promise<string> {
return new Promise((resolve, reject) => {
generateState(): string {
return crypto.randomBytes(20).toString("hex");
}
startCallbackServer(
port: number,
timeoutS: number = 120,
state: string,
): Promise<string> {
return new Promise((resolve) => {
const server = createServer(
(req: IncomingMessage, res: ServerResponse) => {
if (!req.url) {
@@ -38,8 +49,15 @@ export class TwitchAuth {
const url = new URL(req.url, `http://localhost:${port}`);
const code = url.searchParams.get("code");
const error = url.searchParams.get("error");
const responseState = url.searchParams.get("state");
const errorDescription = url.searchParams.get("error_description");
if (state !== responseState) {
res.writeHead(400, { "Content-Type": "text/html" });
res.end("<h1>Wrong state</h1><p>Invalid state param</p>");
return;
}
if (error) {
res.writeHead(400, { "Content-Type": "text/html" });
res.end(`
@@ -49,10 +67,8 @@ export class TwitchAuth {
<p>You can close this window.</p>
`);
server.close();
reject(
new Error(`Authorization failed: ${error} - ${errorDescription}`),
);
return;
logError(`authorization failed: ${error} - ${errorDescription}`);
throw new Error();
}
if (code) {
@@ -73,18 +89,20 @@ export class TwitchAuth {
);
server.listen(port, "localhost", () => {
console.log(
`Waiting for authorization callback on http://localhost:${port}`,
logInfo(
`waiting for authorization callback on http://localhost:${port}`,
);
});
server.on("error", (err) => {
reject(new Error(`Server error: ${err.message}`));
logError(`server error: ${err.message}`);
throw new Error();
});
const timeout = setTimeout(() => {
server.close();
reject(new Error("Authorization timeout. Please try again."));
logError("authorization timeout");
throw new Error();
}, timeoutS * 1000);
server.on("close", () => {

View File

@@ -1,14 +1,14 @@
import * as readline from "node:readline";
import { apiClient } from "../core/api-client.ts";
import { logError, logInfo } from "./logger.ts";
import { logSuccess, logWarning } from "./logger.ts";
export async function getUserId(username: string): Promise<string | null> {
const user = await apiClient.users.getUserByName(username);
if (user) {
logInfo(`${user.name} => ${user.id}`);
if (user?.id) {
logSuccess(`${user.name} => ${user.id}`);
return user.id;
}
logError(`no user with name ${username} found`);
logWarning(`no user with name ${username} found`);
return null;
}

View File

@@ -1,7 +1,5 @@
import chalk from "chalk";
const verbose = process.argv.includes("--verbose");
export function logError(...args: unknown[]) {
console.log(chalk.red(args));
}
@@ -15,7 +13,5 @@ export function logSuccess(...args: unknown[]) {
}
export function logInfo(...args: unknown[]) {
if (verbose) {
console.log(chalk.cyan(args));
}
console.log(chalk.cyan(args));
}