diff --git a/.gitea/workflows/cd.yaml b/.gitea/workflows/cd.yaml index c1dffe9..748a557 100644 --- a/.gitea/workflows/cd.yaml +++ b/.gitea/workflows/cd.yaml @@ -68,7 +68,7 @@ jobs: uses: actions/checkout@v4 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 - name: Login to Registry uses: docker/login-action@v2 diff --git a/Dockerfile b/Dockerfile index a26af85..0dccc70 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,4 +20,4 @@ FROM base AS release COPY --from=install /temp/prod/node_modules node_modules COPY --from=build /app/dist . -CMD [ "bun", "/app/app.js"] \ No newline at end of file +CMD [ "bun", "/app/main.js"] \ No newline at end of file diff --git a/package.json b/package.json index 868d630..fe696b8 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,9 @@ { "scripts": { - "build": "bun build --minify --target bun --outdir dist --sourcemap src/app.ts", + "build": "bun build --minify --target bun --outdir dist --sourcemap src/main.ts", "check:code": "eslint src/ --ext .ts", "check:spell": "cspell --config cspell.code.json **/*.ts", - "start": "bun run src/app.ts" + "start": "bun run src/main.ts" }, "devDependencies": { "@types/ts3-nodejs-library": "^2.0.1", @@ -22,6 +22,6 @@ "ts3-nodejs-library": "^3.4.1" }, "name": "ts3gotify", - "module": "src/app.ts", + "module": "src/main.ts", "type": "module" } diff --git a/src/app.ts b/src/app.ts deleted file mode 100644 index 897ab56..0000000 --- a/src/app.ts +++ /dev/null @@ -1,141 +0,0 @@ -import { Gotify } from "gotify"; -import { - QueryProtocol, - TeamSpeak, - TextMessageTargetMode, -} from "ts3-nodejs-library"; - -import { pino } from "pino"; - -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 = pino({ - level: LOG_LEVEL, - name: "ts3gotify", -}); - -const gotify = new Gotify({ - server: GOTIFY_URL, -}); - -const gotifyConfig = { - app: GOTIFY_TOKEN, - title: GOTIFY_TITLE, -}; - -function getModes(): { - [key in Mode]: boolean; -} { - const modes = MODE.map((mode) => { - return { [mode]: true }; - }); - - return { - 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}`); - }); -} - -function resolveMessageTarget(target: TextMessageTargetMode): string { - if (target === 1) { - return "Client"; - } else if (target === 2) { - return "Channel"; - } else { - return "Server"; - } -} - -function handleMessage(message: string) { - logger.debug(message); - sendNotification(message); -} - -TeamSpeak.connect({ - 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(); - - logger.info( - `connected to TS3 in modes: ${Object.entries(mode) - .filter(([_, value]) => value) - .map(([key]) => key) - .join(", ")}` - ); - - logger.info("connected to TS3"); - - 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.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}` - ); - }); - } - - 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); - }); -}); diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..fa1cf39 --- /dev/null +++ b/src/main.ts @@ -0,0 +1,64 @@ +import { Gotify } from "gotify"; +import { QueryProtocol, TeamSpeak } from "ts3-nodejs-library"; + +import { pino } from "pino"; + +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"; +import { getModes, ts3gotifyFactory } from "./ts3gotify"; + +async function main() { + const logger = pino({ + level: LOG_LEVEL, + name: "ts3gotify", + }); + + const gotify = new Gotify({ + server: GOTIFY_URL, + }); + + const teamspeak = await TeamSpeak.connect({ + host: TS3_HOST, + queryport: TS3_QUERY_PORT, + serverport: TS3_SERVER_PORT, + protocol: QueryProtocol.RAW, + username: TS3_USERNAME, + password: TS3_PASSWORD, + nickname: TS3_NICKNAME, + }); + logger.info("connected to TS3"); + + const modeList = getModes(MODE); + const enabledModeNames = Object.entries(modeList) + .filter(([, value]) => value) + .map(([key]) => key); + + logger.info(`connected to TS3 in modes: ${enabledModeNames.join(", ")}`); + + const ts3gotify = ts3gotifyFactory( + teamspeak, + gotify, + { + app: GOTIFY_TOKEN, + title: GOTIFY_TITLE, + }, + logger + ); + + for (const mode of enabledModeNames) + ts3gotify.registerEventListenerForMode(mode as Mode); +} + +main(); diff --git a/src/ts3gotify.ts b/src/ts3gotify.ts new file mode 100644 index 0000000..0426a80 --- /dev/null +++ b/src/ts3gotify.ts @@ -0,0 +1,106 @@ +import type { Gotify } from "gotify"; +import type { Logger } from "pino"; +import type { TeamSpeak, TextMessageTargetMode } from "ts3-nodejs-library"; +import type { GotifyConfig, Mode } from "./types"; +import { + ClientConnect, + ClientDisconnect, + ClientMoved, + TextMessage, +} from "ts3-nodejs-library/lib/types/Events"; + +function resolveMessageTarget(target: TextMessageTargetMode): string { + if (target === 1) { + return "Client"; + } else if (target === 2) { + return "Channel"; + } else { + return "Server"; + } +} + +export function getModes(mode: Mode[]): { + [key in Mode]: boolean; +} { + const modes = mode + .map((mode) => { + return { [mode]: true }; + }) + .reduce((acc, cur) => { + return { ...acc, ...cur }; + }); + + return { + connect: false, + disconnect: false, + moved: false, + message: false, + ...modes, + }; +} + +export function ts3gotifyFactory( + ts3Client: TeamSpeak, + gotifyClient: Gotify, + gotifyConfig: GotifyConfig, + logger: Logger +) { + function sendNotification(message: string) { + gotifyClient + .send({ + ...gotifyConfig, + message: message, + }) + .catch((error: Error) => { + logger.error(`Error sending message to gotify: ${error.message}`); + }); + } + + function registerEventListenerForMode(mode: Mode) { + switch (mode) { + case "connect": + ts3Client.on("clientconnect", (event: ClientConnect) => + sendNotification(`${event.client.nickname} connected`) + ); + break; + case "disconnect": + ts3Client.on("clientdisconnect", (event: ClientDisconnect) => + sendNotification(`${event.client?.nickname} disconnected`) + ); + break; + case "moved": + ts3Client.on("clientmoved", (event: ClientMoved) => + sendNotification( + `${event.client.nickname} got moved to ${event.channel.name}` + ) + ); + break; + case "message": + ts3Client.on("textmessage", (event: TextMessage) => + sendNotification( + `${event.invoker.nickname} wrote ${ + event.msg + } to a ${resolveMessageTarget(event.targetmode)}` + ) + ); + break; + } + } + + ts3Client.on("close", async () => { + logger.info("disconnected, trying to reconnect..."); + await ts3Client.reconnect(5, 1000); + logger.info("reconnected!"); + }); + + ts3Client.on("error", (error: Error) => { + logger.error(`Error connecting to TS3 server: ${error.message}`); + process.exit(1); + }); + + return { + registerEventListenerForMode, + }; +} + +export type Ts3Gotify = ReturnType; diff --git a/src/types.ts b/src/types.ts index b372a3b..53a97eb 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,3 +1,8 @@ export type Mode = "connect" | "disconnect" | "moved" | "message"; export type LogLevel = "error" | "info" | "debug"; + +export type GotifyConfig = { + app: string; + title: string; +};