From 6744a8716c19846eff87e6931122314e199c5b9a Mon Sep 17 00:00:00 2001
From: Marlon <46379950+Reeperk@users.noreply.github.com>
Date: Sun, 1 Mar 2026 07:12:21 +0100
Subject: [PATCH] Added Webhook System
---
webhook/config.json | 7 +++
webhook/functions/plugin_onLoad.mjs | 82 ++++++++++++++++++++++++++
webhook/sockets/webhookSocket.mjs | 71 ++++++++++++++++++++++
webhook/web/main.js | 91 +++++++++++++++++++++++++++++
4 files changed, 251 insertions(+)
create mode 100644 webhook/config.json
create mode 100644 webhook/functions/plugin_onLoad.mjs
create mode 100644 webhook/sockets/webhookSocket.mjs
create mode 100644 webhook/web/main.js
diff --git a/webhook/config.json b/webhook/config.json
new file mode 100644
index 0000000..726c94a
--- /dev/null
+++ b/webhook/config.json
@@ -0,0 +1,7 @@
+{
+ "title": "Webhook System",
+ "author": "Reeperk",
+ "version": 1,
+ "enabled": true,
+ "default_aboutme": "A Webhook System"
+}
\ No newline at end of file
diff --git a/webhook/functions/plugin_onLoad.mjs b/webhook/functions/plugin_onLoad.mjs
new file mode 100644
index 0000000..d16dca4
--- /dev/null
+++ b/webhook/functions/plugin_onLoad.mjs
@@ -0,0 +1,82 @@
+import { app, io, serverconfig } from "../../../index.mjs";
+import { generateId, sanitizeInput, getCastingMemberObject } from "../../../modules/functions/main.mjs";
+import { saveChatMessage } from "../../../modules/functions/io.mjs";
+import express from "express";
+import fs from "fs";
+import path from "path";
+
+const WEBHOOKS_FILE = path.join(process.cwd(), "configs", "webhooks.json");
+
+export function onLoad() {
+ console.log("[Webhook Plugin] Loading Webhook API Routes...");
+
+ if (!fs.existsSync(WEBHOOKS_FILE)) {
+ fs.writeFileSync(WEBHOOKS_FILE, JSON.stringify({}));
+ }
+
+ app.post('/api/webhooks/:token', express.json(), async (req, res) => {
+ try {
+ const token = req.params.token;
+ const webhooks = JSON.parse(fs.readFileSync(WEBHOOKS_FILE, 'utf-8'));
+
+ let webhook = Object.values(webhooks).find(wh => wh.token === token);
+
+ if (!webhook) {
+ return res.status(404).json({ error: "Webhook not found" });
+ }
+
+ const content = req.body.content;
+ if (!content) {
+ return res.status(400).json({ error: "Message content is required" });
+ }
+
+ let messageid = generateId(12);
+ let whId = webhook.id;
+ let authorName = req.body.username || webhook.name || "Webhook";
+ let authorAvatar = req.body.avatar_url || webhook.avatar || "";
+
+ let fakeUserId = `webhook-${whId}`;
+
+ let pluginConfig = JSON.parse(fs.readFileSync(path.join(process.cwd(), "plugins", "webhook", "config.json"), 'utf-8'));
+
+ serverconfig.servermembers[fakeUserId] = {
+ id: fakeUserId,
+ name: authorName,
+ icon: authorAvatar,
+ isOnline: false,
+ status: "",
+ aboutme: pluginConfig.default_aboutme || "A Webhook",
+ joined: Date.now()
+ };
+
+ let member = {
+ group: webhook.group,
+ category: webhook.category,
+ channel: webhook.channel,
+ message: sanitizeInput(content),
+ timestamp: new Date().getTime(),
+ messageId: messageid,
+ reply: { messageId: null },
+ room: `${webhook.group}-${webhook.category}-${webhook.channel}`,
+ author: {
+ id: fakeUserId,
+ name: authorName,
+ icon: authorAvatar,
+ isWebhook: true
+ }
+ };
+
+ member.author = Object.assign(member.author, getCastingMemberObject(serverconfig.servermembers[fakeUserId]));
+ member = Object.assign(member, getCastingMemberObject(member));
+
+ await saveChatMessage(member, null);
+ io.emit("messageCreate", member);
+
+ res.status(204).send();
+ } catch (e) {
+ console.error("[Webhook Plugin] Error in webhook receiver:", e);
+ res.status(500).json({ error: "Internal Server Error" });
+ }
+ });
+
+}
diff --git a/webhook/sockets/webhookSocket.mjs b/webhook/sockets/webhookSocket.mjs
new file mode 100644
index 0000000..71007f3
--- /dev/null
+++ b/webhook/sockets/webhookSocket.mjs
@@ -0,0 +1,71 @@
+import fs from "fs";
+import path from "path";
+import { generateId, validateMemberId } from "../../../modules/functions/main.mjs";
+import { hasPermission } from "../../../modules/functions/chat/main.mjs";
+
+const WEBHOOKS_FILE = path.join(process.cwd(), "configs", "webhooks.json");
+
+export default (socket) => {
+ socket.on('getWebhooks', (data, response) => {
+ if (validateMemberId(data?.id, socket, data?.token) === true) {
+ if (!hasPermission(data.id, "manageChannels", data.channelId)) {
+ return response({ type: 'error', error: "No permission" });
+ }
+
+ const channelId = data.channelId;
+ const webhooks = fs.existsSync(WEBHOOKS_FILE) ? JSON.parse(fs.readFileSync(WEBHOOKS_FILE, 'utf-8')) : {};
+
+ let channelWebhooks = Object.values(webhooks).filter(wh => wh.channel === channelId);
+ response({ type: 'success', data: channelWebhooks });
+ }
+ });
+
+ socket.on('createWebhook', (data, response) => {
+ if (validateMemberId(data?.id, socket, data?.token) === true) {
+ if (!hasPermission(data.id, "manageChannels", data.channelId)) {
+ return response({ type: 'error', error: "No permission" });
+ }
+
+ const group = data.group;
+ const category = data.category;
+ const channelId = data.channelId;
+ const name = data.name || "New Webhook";
+
+ const webhooks = fs.existsSync(WEBHOOKS_FILE) ? JSON.parse(fs.readFileSync(WEBHOOKS_FILE, 'utf-8')) : {};
+ let whId = generateId(8);
+ let whToken = generateId(32);
+
+ let newWh = {
+ id: whId,
+ token: whToken,
+ group: group,
+ category: category,
+ channel: channelId,
+ name: name,
+ avatar: ""
+ };
+
+ webhooks[whId] = newWh;
+ fs.writeFileSync(WEBHOOKS_FILE, JSON.stringify(webhooks, null, 2));
+
+ response({ type: 'success', data: newWh });
+ }
+ });
+
+ socket.on('deleteWebhook', (data, response) => {
+ if (validateMemberId(data?.id, socket, data?.token) === true) {
+ if (!hasPermission(data.id, "manageChannels", data.channelId)) {
+ return response({ type: 'error', error: "No permission" });
+ }
+ const webhooks = fs.existsSync(WEBHOOKS_FILE) ? JSON.parse(fs.readFileSync(WEBHOOKS_FILE, 'utf-8')) : {};
+
+ if (webhooks[data.webhookId]) {
+ delete webhooks[data.webhookId];
+ fs.writeFileSync(WEBHOOKS_FILE, JSON.stringify(webhooks, null, 2));
+ response({ type: 'success' });
+ } else {
+ response({ type: 'error', error: "Not found" });
+ }
+ }
+ });
+};
diff --git a/webhook/web/main.js b/webhook/web/main.js
new file mode 100644
index 0000000..454c7bc
--- /dev/null
+++ b/webhook/web/main.js
@@ -0,0 +1,91 @@
+if (!window.webhookPluginLoaded) {
+ window.webhookPluginLoaded = true;
+
+ document.addEventListener("pagechange", (e) => {
+ if (e.detail.page === "channel-info") {
+ setTimeout(injectWebhookUI, 100);
+ }
+ });
+
+ if (window.location.href.includes("page=channel-info")) {
+ setTimeout(injectWebhookUI, 500);
+ }
+
+ function injectWebhookUI() {
+ let channelSettings = document.getElementById("channel_settings");
+ if (!channelSettings) return;
+
+ if (document.getElementById("channel_webhooks")) return;
+
+ let webhookHtml = `
+
+