mirror of
https://github.com/qwerinope/qweribot.git
synced 2025-12-18 21:11:39 +01:00
add pretty console formatting
This commit is contained in:
25
bot/auth.ts
25
bot/auth.ts
@@ -1,6 +1,9 @@
|
||||
import { RefreshingAuthProvider, exchangeCode, type AccessToken } from "@twurple/auth";
|
||||
import { createAuthRecord, deleteAuthRecord, getAuthRecord, updateAuthRecord } from "./db/dbAuth";
|
||||
|
||||
import { logger } from ".";
|
||||
import kleur from "kleur";
|
||||
|
||||
async function initAuth(userId: string, clientId: string, clientSecret: string, requestedIntents: string[], streamer: boolean): Promise<AccessToken> {
|
||||
const port = process.env.REDIRECT_PORT ?? 3456
|
||||
const redirectURL = process.env.REDIRECT_URL ?? `http://localhost:${port}`;
|
||||
@@ -9,9 +12,9 @@ async function initAuth(userId: string, clientId: string, clientSecret: string,
|
||||
const state = Bun.randomUUIDv7().replace(/-/g, "").slice(0, 32).toUpperCase();
|
||||
// Generate random state variable to prevent cross-site-scripting attacks
|
||||
|
||||
const instruction = `Visit this URL as ${streamer ? 'the streamer' : 'the chatter'} to authenticate the bot.`
|
||||
console.info(instruction);
|
||||
console.info(`https://id.twitch.tv/oauth2/authorize?client_id=${clientId}&redirect_uri=${redirectURL}&response_type=code&scope=${requestedIntents.join('+')}&state=${state}`);
|
||||
const instruction = `Visit this URL as ${kleur.red().underline().italic(streamer ? 'the streamer' : 'the chatter')} to authenticate the bot.`
|
||||
logger.info(instruction);
|
||||
logger.info(`https://id.twitch.tv/oauth2/authorize?client_id=${clientId}&redirect_uri=${redirectURL}&response_type=code&scope=${requestedIntents.join('+')}&state=${state}`);
|
||||
|
||||
const createCodePromise = () => {
|
||||
let resolver: (code: string) => void;
|
||||
@@ -39,20 +42,20 @@ async function initAuth(userId: string, clientId: string, clientSecret: string,
|
||||
|
||||
const code = await codepromise;
|
||||
server.stop(false);
|
||||
console.info(`Authentication code received.`);
|
||||
logger.info(`Authentication code received.`);
|
||||
const tokenData = await exchangeCode(clientId, clientSecret, code, redirectURL);
|
||||
console.info(`Successfully authenticated code.`);
|
||||
logger.info(`Successfully authenticated code.`);
|
||||
await createAuthRecord(tokenData, userId);
|
||||
console.info(`Successfully saved auth data in the database.`);
|
||||
logger.ok(`Successfully saved auth data in the database.`);
|
||||
return tokenData;
|
||||
};
|
||||
|
||||
export async function createAuthProvider(user: string, intents: string[], streamer = false): Promise<RefreshingAuthProvider> {
|
||||
const clientId = process.env.CLIENT_ID;
|
||||
if (!clientId) { console.error("Please provide a CLIENT_ID in .env."); process.exit(1); };
|
||||
if (!clientId) { logger.enverr("CLIENT_ID"); process.exit(1); };
|
||||
|
||||
const clientSecret = process.env.CLIENT_SECRET;
|
||||
if (!clientSecret) { console.error("Please provide a CLIENT_SECRET in .env."); process.exit(1); };
|
||||
if (!clientSecret) { logger.enverr("CLIENT_SECRET"); process.exit(1); };
|
||||
|
||||
const authRecord = await getAuthRecord(user, intents);
|
||||
|
||||
@@ -65,18 +68,18 @@ export async function createAuthProvider(user: string, intents: string[], stream
|
||||
await authData.addUserForToken(token, intents);
|
||||
|
||||
authData.onRefresh(async (user, token) => {
|
||||
console.info(`Successfully refreshed auth for user ${user}`);
|
||||
logger.ok(`Successfully refreshed auth for user ${user}`);
|
||||
await updateAuthRecord(user, token);
|
||||
});
|
||||
|
||||
authData.onRefreshFailure((user, err) => {
|
||||
console.error(`ERROR: Failed to refresh auth for user ${user}: ${err.name} ${err.message}`);
|
||||
logger.err(`Failed to refresh auth for user ${user}: ${err.name} ${err.message}`);
|
||||
});
|
||||
|
||||
try {
|
||||
await authData.refreshAccessTokenForUser(user);
|
||||
} catch (err) {
|
||||
console.error(`Failed to refresh user ${user}. Please restart the bot and re-authenticate it. Make sure the user that auths the bot and the user that's defined in .env are the same.`);
|
||||
logger.err(`Failed to refresh user ${user}. Please restart the bot and re-authenticate it. Make sure the user that auths the bot and the user that's defined in .env are the same.`);
|
||||
await deleteAuthRecord(user);
|
||||
};
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import { getUserRecord } from "../db/dbUser";
|
||||
import parseCommandArgs from "../lib/parseCommandArgs";
|
||||
import { changeBalance } from "../lib/changeBalance";
|
||||
import { User } from "../user";
|
||||
import { logger } from "..";
|
||||
|
||||
export default new Command('donate', ['donate'], 'chatter', async (msg, user) => {
|
||||
const args = parseCommandArgs(msg.messageText);
|
||||
@@ -37,7 +38,7 @@ export default new Command('donate', ['donate'], 'chatter', async (msg, user) =>
|
||||
} else {
|
||||
// TODO: Rewrite this section
|
||||
await sendMessage(`Failed to give ${target.displayName} ${amount} qbuck${(amount === 1 ? '' : 's')}`, msg.messageId);
|
||||
console.error(`WARNING: Qweribucks donation failed: target success: ${data[0] !== false}, donator success: ${data[1] !== false}`);
|
||||
logger.err(`WARNING: Qweribucks donation failed: target success: ${data[0] !== false}, donator success: ${data[1] !== false}`);
|
||||
};
|
||||
|
||||
await Promise.all([
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Command, sendMessage } from ".";
|
||||
import { logger } from "..";
|
||||
import type { userRecord } from "../db/connection";
|
||||
import { getUserRecord } from "../db/dbUser";
|
||||
import items, { changeItemCount } from "../items";
|
||||
@@ -41,7 +42,7 @@ export default new Command('give', ['give'], 'chatter', async (msg, user) => {
|
||||
} else {
|
||||
// TODO: Rewrite this section
|
||||
await sendMessage(`Failed to give ${target.displayName} ${amount} ${item.prettyName + (amount === 1 ? '' : item.plural)}`, msg.messageId);
|
||||
console.error(`WARNING: Item donation failed: target success: ${data[0] !== false}, donator success: ${data[1] !== false}`);
|
||||
logger.warn(`WARNING: Item donation failed: target success: ${data[0] !== false}, donator success: ${data[1] !== false}`);
|
||||
};
|
||||
await user.clearLock();
|
||||
await target.clearLock();
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import type { AccessToken } from "@twurple/auth";
|
||||
import PocketBase, { RecordService } from "pocketbase";
|
||||
import type { inventory } from "../items";
|
||||
import { logger } from "..";
|
||||
|
||||
const pocketbaseurl = process.env.POCKETBASE_URL ?? "localhost:8090";
|
||||
if (pocketbaseurl === "") { console.error("Please provide a POCKETBASE_URL in .env."); process.exit(1); };
|
||||
if (pocketbaseurl === "") { logger.enverr("POCKETBASE_URL"); process.exit(1); };
|
||||
|
||||
export type authRecord = {
|
||||
id: string;
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import pocketbase from "./connection";
|
||||
import { User } from "../user";
|
||||
import { logger } from "..";
|
||||
const pb = pocketbase.collection('timeouts');
|
||||
|
||||
export async function createTimeoutRecord(user: User, target: User, item: string): Promise<void> {
|
||||
try {
|
||||
await pb.create({ user: user.id, target: target.id, item });
|
||||
} catch (err) {
|
||||
console.error(`Failed to create timeout record in database: user: ${user.id}, target: ${target.id}, item: ${item}`);
|
||||
console.error(err);
|
||||
logger.err(`Failed to create timeout record in database: user: ${user.id}, target: ${target.id}, item: ${item}`);
|
||||
logger.err(err as string);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import pocketbase from "./connection";
|
||||
import { User } from "../user";
|
||||
import { logger } from "..";
|
||||
const pb = pocketbase.collection('usedItems');
|
||||
|
||||
export async function createUsedItemRecord(user: User, item: string): Promise<void> {
|
||||
try {
|
||||
await pb.create({ user: user.id, item });
|
||||
} catch (err) {
|
||||
console.error(`Failed to create usedItem record in database: user: ${user.id}, item: ${item}`);
|
||||
console.error(err);
|
||||
logger.err(`Failed to create usedItem record in database: user: ${user.id}, item: ${item}`);
|
||||
logger.err(err as string);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import pocketbase, { type userRecord } from "./connection";
|
||||
import { emptyInventory, itemarray } from "../items";
|
||||
import type { User } from "../user";
|
||||
import { logger } from "..";
|
||||
const pb = pocketbase.collection('users');
|
||||
|
||||
/** Use this function to both ensure existance and to retreive data */
|
||||
@@ -38,7 +39,7 @@ export async function updateUserRecord(user: User, newData: userRecord): Promise
|
||||
await pb.update(user.id, newData);
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
logger.err(err as string);
|
||||
return false;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,24 +1,25 @@
|
||||
import { eventSub, streamerApi, streamerId } from "..";
|
||||
import kleur from "kleur";
|
||||
import { eventSub, streamerApi, streamerId, logger } from "..";
|
||||
|
||||
eventSub.onRevoke(event => {
|
||||
console.info(`Successfully revoked EventSub subscription: ${event.id}`);
|
||||
logger.ok(`Successfully revoked EventSub subscription: ${event.id}`);
|
||||
});
|
||||
|
||||
eventSub.onSubscriptionCreateSuccess(event => {
|
||||
console.info(`Successfully created EventSub subscription: ${event.id}`);
|
||||
logger.ok(`Successfully created EventSub subscription: ${kleur.underline(event.id)}`);
|
||||
deleteDuplicateSubscriptions.refresh();
|
||||
});
|
||||
|
||||
eventSub.onSubscriptionCreateFailure(event => {
|
||||
console.error(`Failed to create EventSub subscription: ${event.id}`);
|
||||
logger.err(`Failed to create EventSub subscription: ${event.id}`);
|
||||
});
|
||||
|
||||
eventSub.onSubscriptionDeleteSuccess(event => {
|
||||
console.info(`Successfully deleted EventSub subscription: ${event.id}`);
|
||||
logger.ok(`Successfully deleted EventSub subscription: ${event.id}`);
|
||||
});
|
||||
|
||||
eventSub.onSubscriptionDeleteFailure(event => {
|
||||
console.error(`Failed to delete EventSub subscription: ${event.id}`);
|
||||
logger.err(`Failed to delete EventSub subscription: ${event.id}`);
|
||||
});
|
||||
|
||||
import { readdir } from 'node:fs/promises';
|
||||
@@ -36,12 +37,12 @@ import { StaticAuthProvider } from "@twurple/auth";
|
||||
import { ApiClient, HelixEventSubSubscription } from "@twurple/api";
|
||||
|
||||
const deleteDuplicateSubscriptions = setTimeout(async () => {
|
||||
console.info('Deleting all double subscriptions');
|
||||
logger.info('Deleting all double subscriptions');
|
||||
const tokendata = await streamerApi.getTokenInfo();
|
||||
const authdata = await getAuthRecord(streamerId, []);
|
||||
const tempauth = new StaticAuthProvider(tokendata.clientId, authdata?.accesstoken.accessToken!);
|
||||
const tempapi: ApiClient = new ApiClient({ authProvider: tempauth });
|
||||
console.info('Created the temporary API client');
|
||||
logger.info('Created the temporary API client');
|
||||
const subs = await tempapi.eventSub.getSubscriptionsForStatus('enabled');
|
||||
const seen = new Map();
|
||||
const duplicates: HelixEventSubSubscription[] = [];
|
||||
@@ -58,8 +59,8 @@ const deleteDuplicateSubscriptions = setTimeout(async () => {
|
||||
|
||||
for (const sub of duplicates) {
|
||||
await tempapi.eventSub.deleteSubscription(sub.id);
|
||||
console.info(`Deleted sub: id: ${sub.id}, type: ${sub.type}`);
|
||||
logger.ok(`Deleted sub: id: ${sub.id}, type: ${sub.type}`);
|
||||
};
|
||||
if (duplicates.length === 0) console.info('No duplicate subscriptions found');
|
||||
console.info('Removed temporary API client');
|
||||
if (duplicates.length === 0) logger.ok('No duplicate subscriptions found');
|
||||
else logger.ok('Deleted all duplicate EventSub subscriptions');
|
||||
}, 5000);
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { chatterId, streamerId, eventSub, commandPrefix, singleUserMode, streamerUsers } from "..";
|
||||
import { chatterId, streamerId, eventSub, commandPrefix, singleUserMode, streamerUsers, logger } from "..";
|
||||
import { User } from "../user";
|
||||
import commands, { sendMessage } from "../commands";
|
||||
import { redis } from "bun";
|
||||
import { isAdmin } from "../lib/admins";
|
||||
|
||||
console.info(`Loaded the following commands: ${commands.keys().toArray().join(', ')}`);
|
||||
logger.info(`Loaded the following commands: ${commands.keys().toArray().join(', ')}`);
|
||||
|
||||
eventSub.onChannelChatMessage(streamerId, streamerId, async msg => {
|
||||
// return if double user mode is on and the chatter says something, we don't need them
|
||||
@@ -43,7 +43,7 @@ eventSub.onChannelChatMessage(streamerId, streamerId, async msg => {
|
||||
|
||||
try { await selected.execute(msg, user!); }
|
||||
catch (err) {
|
||||
console.error(err);
|
||||
logger.err(err as string);
|
||||
await sendMessage('ERROR: Something went wrong', msg.messageId);
|
||||
await user?.clearLock();
|
||||
};
|
||||
|
||||
13
bot/index.ts
13
bot/index.ts
@@ -2,15 +2,24 @@ import { createAuthProvider } from "./auth";
|
||||
import { ApiClient } from "@twurple/api";
|
||||
import { EventSubWsListener } from "@twurple/eventsub-ws";
|
||||
import { addAdmin } from "./lib/admins";
|
||||
import kleur from "kleur";
|
||||
|
||||
const CHATTERINTENTS = ["user:read:chat", "user:write:chat", "user:bot"];
|
||||
const STREAMERINTENTS = ["user:read:chat", "moderation:read", "channel:manage:moderators", "moderator:manage:banned_users"];
|
||||
|
||||
export const logger = {
|
||||
err: (arg: string) => console.error(kleur.red().bold().italic('[ERROR] ') + kleur.red().bold(arg)),
|
||||
warn: (arg: string) => console.warn(kleur.yellow().bold().italic('[WARN] ') + kleur.yellow().bold(arg)),
|
||||
info: (arg: string) => console.info(kleur.white().bold().italic('[INFO] ') + kleur.white(arg)),
|
||||
ok: (arg: string) => console.info(kleur.green().bold(arg)),
|
||||
enverr: (arg: string) => logger.err(`Please provide a ${arg} in the .env`)
|
||||
};
|
||||
|
||||
export const singleUserMode = process.env.CHATTER_IS_STREAMER === 'true';
|
||||
export const chatterId = process.env.CHATTER_ID ?? "";
|
||||
if (chatterId === "") { console.error('Please set a CHATTER_ID in the .env'); process.exit(1); };
|
||||
if (chatterId === "") { logger.enverr('CHATTER_ID'); process.exit(1); };
|
||||
export const streamerId = process.env.STREAMER_ID ?? "";
|
||||
if (streamerId === "") { console.error('Please set a STREAMER_ID in the .env'); process.exit(1); };
|
||||
if (streamerId === "") { logger.enverr('STREAMER_ID'); process.exit(1); };
|
||||
|
||||
export const chatterAuthProvider = await createAuthProvider(chatterId, singleUserMode ? CHATTERINTENTS.concat(STREAMERINTENTS) : CHATTERINTENTS);
|
||||
export const streamerAuthProvider = singleUserMode ? undefined : await createAuthProvider(streamerId, STREAMERINTENTS, true);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { streamerApi, streamerId, streamerUsers } from "..";
|
||||
import { logger, streamerApi, streamerId, streamerUsers } from "..";
|
||||
import { User } from "../user";
|
||||
|
||||
type SuccessfulTimeout = { status: true };
|
||||
@@ -25,7 +25,7 @@ export const timeout = async (user: User, reason: string, duration?: number): Pr
|
||||
try {
|
||||
await streamerApi.moderation.banUser(streamerId, { user: user.id, reason, duration });
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
logger.err(err as string);
|
||||
return { status: false, reason: 'unknown' }
|
||||
};
|
||||
|
||||
|
||||
3
bun.lock
3
bun.lock
@@ -6,6 +6,7 @@
|
||||
"dependencies": {
|
||||
"@twurple/auth": "^7.3.0",
|
||||
"@twurple/eventsub-ws": "^7.3.0",
|
||||
"kleur": "^4.1.5",
|
||||
"pocketbase": "^0.26.1",
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -57,6 +58,8 @@
|
||||
|
||||
"bun-types": ["bun-types@1.2.17", "", { "dependencies": { "@types/node": "*" } }, "sha512-ElC7ItwT3SCQwYZDYoAH+q6KT4Fxjl8DtZ6qDulUFBmXA8YB4xo+l54J9ZJN+k2pphfn9vk7kfubeSd5QfTVJQ=="],
|
||||
|
||||
"kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="],
|
||||
|
||||
"klona": ["klona@2.0.6", "", {}, "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA=="],
|
||||
|
||||
"node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="],
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
"dependencies": {
|
||||
"@twurple/auth": "^7.3.0",
|
||||
"@twurple/eventsub-ws": "^7.3.0",
|
||||
"kleur": "^4.1.5",
|
||||
"pocketbase": "^0.26.1"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user