From 990e7b1dbe3171cc806c4d838fbe2ae0d422f869 Mon Sep 17 00:00:00 2001 From: Timo Behrendt Date: Tue, 7 Jan 2025 19:46:32 +0100 Subject: [PATCH] refactor: better env handling (#5) Reviewed-on: https://gitea.t000-n.de/t.behrendt/ts3gotify/pulls/5 Co-authored-by: Timo Behrendt Co-committed-by: Timo Behrendt --- bun.lockb | Bin 125280 -> 125622 bytes package.json | 1 + src/app.ts | 179 +++++++++++++++++++++++++------------------ src/env.ts | 53 +++++++++++++ src/environment.d.ts | 19 ----- src/types.ts | 3 + 6 files changed, 162 insertions(+), 93 deletions(-) create mode 100644 src/env.ts delete mode 100644 src/environment.d.ts create mode 100644 src/types.ts diff --git a/bun.lockb b/bun.lockb index 991cd094444c4426ec71d901b3e5c45bb5df4e6d..0776f04756a61a907301c535e205ff1cfc11fb6b 100755 GIT binary patch delta 17893 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 delta 17841 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 PpvIKha(1pdoBY23!iyg{ diff --git a/package.json b/package.json index c72ef9c..39921c4 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "@types/bun": "latest" }, "dependencies": { + "env-var": "^7.5.0", "gotify": "^1.1.0", "ts3-nodejs-library": "^3.4.1", "winston": "^3.8.2" diff --git a/src/app.ts b/src/app.ts index f985a2c..81739ee 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,105 +1,136 @@ import { Gotify } from "gotify"; -import { QueryProtocol, TeamSpeak, TextMessageTargetMode } from "ts3-nodejs-library" +import { + QueryProtocol, + TeamSpeak, + TextMessageTargetMode, +} from "ts3-nodejs-library"; import { createLogger, transports, format } from "winston"; +import { + GOTIFY_TITLE, + GOTIFY_TOKEN, + GOTIFY_URL, + LOG_LEVEL, + MODE, + TS3_HOST, + TS3_NICKNAME, + TS3_PASSWORD, + TS3_QUERY_PORT, + TS3_SERVER_PORT, + TS3_USERNAME, +} from "./env"; +import type { Mode } from "./types"; + const logger = createLogger({ - level: process.env.LOG_LEVEL, - transports: [new transports.Console()], - format: format.combine( - format.colorize(), - format.timestamp(), - ), -}) + level: LOG_LEVEL, + transports: [new transports.Console()], + format: format.combine(format.colorize(), format.timestamp()), +}); const gotify = new Gotify({ - server: process.env.GOTIFY_URL, -}) + server: GOTIFY_URL, +}); const gotifyConfig = { - app: process.env.GOTIFY_TOKEN, - title: process.env.GOTIFY_TITLE || "ts3gotify" -} + app: GOTIFY_TOKEN, + title: GOTIFY_TITLE, +}; -function getModes() { - const modeIsProvided = process.env.MODE != undefined +function getModes(): { + [key in Mode]: boolean; +} { + const modes = MODE.map((mode) => { + return { [mode]: true }; + }); - return { - connect: modeIsProvided ? process.env.MODE?.includes("connect") || false : true, - disconnect: process.env.MODE?.includes("disconnect") || false, - moved: process.env.MODE?.includes("moved") || false, - message: process.env.MODE?.includes("message") || false - } + return Object.assign( + { + connect: false, + disconnect: false, + moved: false, + message: false, + }, + ...modes + ); } function sendNotification(message: string) { - gotify.send({ - ...gotifyConfig, - message: message, - }).catch((error: Error) => { - logger.error(`Error sending message to gotify: ${error.message}`) + gotify + .send({ + ...gotifyConfig, + message: message, }) + .catch((error: Error) => { + logger.error(`Error sending message to gotify: ${error.message}`); + }); } function resolveMessageTarget(target: TextMessageTargetMode): string { - if (target === 1) { - return "Client" - } else if (target === 2) { - return "Channel" - } else { - return "Server" - } + if (target === 1) { + return "Client"; + } else if (target === 2) { + return "Channel"; + } else { + return "Server"; + } } function handleMessage(message: string) { - logger.debug(message) - sendNotification(message) + logger.debug(message); + sendNotification(message); } TeamSpeak.connect({ - host: process.env.TS3_HOST || "info", - queryport: process.env.TS3_QUERY_PORT || 10011, - serverport: process.env.TS3_SERVER_PORT || 9987, - protocol: QueryProtocol.RAW, - username: process.env.TS3_USERNAME, - password: process.env.TS3_PASSWORD, - nickname: process.env.TS3_NICKNAME || "ts3gotify", + host: TS3_HOST, + queryport: TS3_QUERY_PORT, + serverport: TS3_SERVER_PORT, + protocol: QueryProtocol.RAW, + username: TS3_USERNAME, + password: TS3_PASSWORD, + nickname: TS3_NICKNAME, }).then((teamspeak) => { - const mode = getModes() + const mode = getModes(); - logger.info("connected to TS3") + logger.info("connected to TS3"); - if (mode.connect) { - teamspeak.on("clientconnect", (event) => { - handleMessage(`${event.client.nickname} connected`) - }) - } + if (mode.connect) { + teamspeak.on("clientconnect", (event) => { + handleMessage(`${event.client.nickname} connected`); + }); + } - if (mode.disconnect) { - teamspeak.on("clientdisconnect", (event) => { - handleMessage(`${event.client?.nickname} disconnected`) - }) - } + if (mode.disconnect) { + teamspeak.on("clientdisconnect", (event) => { + handleMessage(`${event.client?.nickname} disconnected`); + }); + } - if (mode.message) { - teamspeak.on("textmessage", (event) => { - handleMessage(`${event.invoker.nickname} wrote ${event.msg} to a ${resolveMessageTarget(event.targetmode)}`) - }) - } + if (mode.message) { + teamspeak.on("textmessage", (event) => { + handleMessage( + `${event.invoker.nickname} wrote ${ + event.msg + } to a ${resolveMessageTarget(event.targetmode)}` + ); + }); + } - if (mode.moved) { - teamspeak.on("clientmoved", (event) => { - handleMessage(`${event.client.nickname} got moved to ${event.channel.name}`) - }) - } + if (mode.moved) { + teamspeak.on("clientmoved", (event) => { + handleMessage( + `${event.client.nickname} got moved to ${event.channel.name}` + ); + }); + } - teamspeak.on("close", async () => { - logger.debug("disconnected, trying to reconnect...") - await teamspeak.reconnect(5, 1000) - logger.info("reconnected!") - }) + teamspeak.on("close", async () => { + logger.debug("disconnected, trying to reconnect..."); + await teamspeak.reconnect(5, 1000); + logger.info("reconnected!"); + }); - teamspeak.on("error", (error: Error) => { - logger.error(`Error connecting to TS3 server: ${error.message}`) - process.exit(1) - }) -}) + teamspeak.on("error", (error: Error) => { + logger.error(`Error connecting to TS3 server: ${error.message}`); + process.exit(1); + }); +}); diff --git a/src/env.ts b/src/env.ts new file mode 100644 index 0000000..6277f55 --- /dev/null +++ b/src/env.ts @@ -0,0 +1,53 @@ +import { from } from "env-var"; + +import type { LogLevel, Mode } from "./types"; + +const envVar = from(process.env, { + asLogLevel: (value): LogLevel => { + const logLevels = ["error", "info", "debug"]; + if (logLevels.includes(value)) { + return value as LogLevel; + } else { + throw new Error("Invalid log level"); + } + }, + asTs3GotifyMode: (value): Mode => { + const modes = ["connect", "disconnect", "moved", "message"]; + if (modes.includes(value)) { + return value as Mode; + } else { + throw new Error("Invalid mode"); + } + }, +}); + +export const LOG_LEVEL = envVar.get("LOG_LEVEL").default("info").asLogLevel(); + +export const TS3_HOST = envVar.get("TS3_HOST").required().asString(); +export const TS3_QUERY_PORT = envVar + .get("TS3_QUERY_PORT") + .default(10011) + .asPortNumber(); +export const TS3_SERVER_PORT = envVar + .get("TS3_SERVER_PORT") + .default(9987) + .asPortNumber(); +export const TS3_USERNAME = envVar.get("TS3_USERNAME").required().asString(); +export const TS3_PASSWORD = envVar.get("TS3_PASSWORD").required().asString(); +export const TS3_NICKNAME = envVar + .get("TS3_NICKNAME") + .default("ts3gotify") + .asString(); + +export const GOTIFY_URL = envVar.get("GOTIFY_URL").required().asUrlString(); +export const GOTIFY_TOKEN = envVar.get("GOTIFY_TOKEN").required().asString(); +export const GOTIFY_TITLE = envVar + .get("GOTIFY_TITLE") + .default("ts3gotify") + .asString(); + +export const MODE = envVar + .get("MODE") + .default("['connect']") + .asJsonArray() + .map((value) => value.asTs3GotifyMode()); diff --git a/src/environment.d.ts b/src/environment.d.ts deleted file mode 100644 index ecf206c..0000000 --- a/src/environment.d.ts +++ /dev/null @@ -1,19 +0,0 @@ -export { }; - -declare global { - namespace NodeJS { - interface ProcessEnv { - LOG_LEVEL?: "error" | "info" | "debug" - TS3_HOST: string; - TS3_QUERY_PORT?: number; - TS3_SERVER_PORT?: number; - TS3_USERNAME: string; - TS3_PASSWORD: string; - TS3_NICKNAME?: string; - GOTIFY_URL: string; - GOTIFY_TOKEN: string; - GOTIFY_TITLE?: string; - MODE?: Array<"connect" | "disconnect" | "moved" | "message"> - } - } -} diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..b372a3b --- /dev/null +++ b/src/types.ts @@ -0,0 +1,3 @@ +export type Mode = "connect" | "disconnect" | "moved" | "message"; + +export type LogLevel = "error" | "info" | "debug";