Files
legica-bot/src/app.ts
2024-08-16 23:00:39 +02:00

270 lines
4.3 KiB
TypeScript

import { Client } from "discord.js";
import { config } from "@constants";
import { sendDiscordMessage, sendNextMessage } from "@common";
import { Elysia, t } from "elysia";
import { swagger } from "@elysiajs/swagger";
import { basicAuth, BasicAuthError } from "@core";
import pino from "pino";
import cron from "@elysiajs/cron";
const client: Client = new Client();
const fileTransport = pino.transport({
target: "pino/file",
options: { destination: `app.log` },
});
const logger = pino(
{
level: "error",
},
fileTransport
);
async function jobRunner() {
try {
await sendNextMessage(client);
} catch (err) {
logger.error(err);
}
}
const botPlugin = new Elysia({ prefix: "/bot" })
.use(
basicAuth({
users: [
{
username: "admin",
password: config.PASSWORD,
},
],
errorMessage: "Unauthorized",
})
)
.get(
"/",
() => ({
uptime: client.uptime,
readyAt: client.readyAt,
readyTimestamp: client.readyTimestamp,
}),
{
detail: {
summary: "Get BOT status",
},
}
)
.post(
"/",
() => {
client.login(config.TOKEN);
return "Bot logged in started";
},
{
detail: {
summary: "Start BOT if it is not running",
},
}
)
.delete(
"/",
() => {
client.destroy();
return "Bot logged out";
},
{
detail: {
summary: "Stops the BOT.",
},
}
);
const taskPlugin = new Elysia({ prefix: "/job" })
.use(
cron({
name: "job",
pattern: config.CRON_LEGICA,
run: jobRunner,
paused: true,
timezone: config.TIMEZONE,
})
)
.onStart(
({
store: {
cron: { job },
},
}) => {
client.on("ready", (): void => {
job.resume();
});
}
)
.onBeforeHandle(
({
store: {
cron: { job },
},
set,
}) => {
if (job.isStopped()) {
set.status = 400;
return "Job is not running.";
}
}
)
.use(
basicAuth({
users: [
{
username: "admin",
password: config.PASSWORD,
},
],
errorMessage: "Unauthorized",
})
)
.get(
"/",
({
store: {
cron: { job },
},
}) => ({
running: job.isRunning() ?? false,
stopped: job.isStopped() ?? false,
next: job.nextRun()?.toISOString(),
}),
{
detail: {
summary: "Get CRON job status",
},
}
)
.post(
"/",
({
store: {
cron: { job },
},
set,
}) => {
if (job.isRunning()) {
set.status = 400;
return "Job already running";
}
job.resume();
return "Job started";
},
{
detail: {
summary: "Start CRON job if it is not running",
},
}
)
.delete(
"/",
({
store: {
cron: { job },
},
set,
}) => {
if (!job.isRunning()) {
set.status = 400;
return "Job already paused";
}
job.pause();
return "Job paused";
},
{
detail: {
summary: "Pause CRON job if it is not paused",
},
}
)
.post(
"/send",
async ({ set, body }) => {
try {
const url = body?.url;
if (url) {
await sendDiscordMessage(client, url);
} else {
await sendNextMessage(client);
}
return true;
} catch (err) {
set.status = 400;
logger.error(err);
return false;
}
},
{
body: t.Optional(
t.Object({
url: t.Optional(t.String()),
})
),
detail: {
summary: "Send legica-dana post to discord channels",
},
}
)
.get("/log", () => Bun.file("app.log"), {
detail: {
summary: "Get the error log",
},
});
const app = new Elysia()
.error({ BASIC_AUTH_ERROR: BasicAuthError })
.onError(({ error, code }) => {
switch (code) {
case "BASIC_AUTH_ERROR":
return new Response(error.message, {
status: 401,
headers: {
"WWW-Authenticate": `Basic${
config.realm ? ` realm="${config.realm}"` : ""
}`,
},
});
case "NOT_FOUND":
return new Response(error.message, {
status: 404,
});
default:
logger.error(error);
}
})
.get("/", () => config.APP_VERSION, {
detail: {
summary: "Get current API version",
},
})
.use(
swagger({
documentation: {
info: {
title: "Legica Bot",
version: config.APP_VERSION,
},
security: [
{
type: ["basic"],
},
],
},
swaggerOptions: {
withCredentials: true,
},
})
)
.use(taskPlugin)
.use(botPlugin)
.listen(config.PORT);
client.login(config.TOKEN);
console.log(
`🦊 Elysia is running at http://${app.server?.hostname}:${app.server?.port}`
);