Files
qweribot/src/index.ts

218 lines
5.2 KiB
TypeScript

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");