From 97069a73d215262569f319399d0c052f4f7bdff8 Mon Sep 17 00:00:00 2001 From: Sophia Atkinson Date: Tue, 29 Apr 2025 22:11:52 -0700 Subject: [PATCH] add beter configuration, & added readme with guide --- README.md | 78 +++++++++++++++++++++++++++++++++++++++++++++ config.json.example | 4 +++ index.js | 34 ++++++++++++-------- package.json | 2 +- 4 files changed, 104 insertions(+), 14 deletions(-) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..6e92251 --- /dev/null +++ b/README.md @@ -0,0 +1,78 @@ +# Hedgedoc to Discord + +## What does it do? + +- Monitors Markdown content from multiple configurable URLs such as HedgeDoc, or any markdown source (designed and tested for HedgeDoc). +- Pushes polled markdown content to a Discord webhook. +- Checks for changes by comparing content against a local copy of the last known version (stored in /data/state.json). +- Automatically updates the Discord message when the Markdown changes. +- Logs markdown source errors (timeouts, HTTP errors, etc.) in the specified timezone (stored in /data/error.log). +- Truncates Markdown content to 2000 characters to comply with Discord limits. +- Recreates manually deleted messages on the next content change. + +## Why use this? + +- Collaborate with your staff to manage rules for your Discord server. +- Ensure your community always sees the most up-to-date information. +- It's not a bot, it uses webhooks, so it’s lightweight and easy to deploy. + +## Requirments + +- NodeJS 20+ +- NPM + +## How to set up + +Start by downloading the code. You can clone the repository by running: + +`git clone https://github.com/SophiaAtkinson/discord-hedgedoc-md.git` + +Or download the ZIP directly from [GitHub](https://github.com/SophiaAtkinson/discord-hedgedoc-md/archive/refs/heads/main.zip). + +Once downloaded, navigate into the directory: + +`cd discord-hedgedoc-md` + +Next, install the dependencies: + +`npm install` + +Copy the example config to get started: + +`cp config.json.example config.json` + +Then open config.json in your preferred text editor. + +### Config options + +- timezone: Set your preferred timezone. If left blank, it defaults to `UTC`. +- pollingRate: Recommended to keep at the default `30000` (30 seconds). You can increase this, but don’t go lower. +- webhooks: For each webhook, you’ll need: + - name: A name to identify it. + - markdownURL: The URL to fetch markdown from (e.g., https://example.com/s/example/download). + - webhookURL: Your Discord webhook URL (found under Channel Settings → Integrations → Webhooks). + +If you only want one webhook, just include one in the array. If you want more, add additional entries. + +Once configured, start the tool: + +`npm run` + +Or: + +`node index.js` + +That’s it! The messages should now appear in Discord. If you run into issues, feel free to open an issue on GitHub. + +### Run it 24/7 + +You can use **[pm2](https://pm2.io/)** or **[Docker](https://www.docker.com/)** to keep the service running continuously. + +## Notes + +- Timezones must be in [TZ format](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) +- Markdown must be supported by [Discord’s Markdown guide](https://support.discord.com/hc/en-us/articles/210298617-Markdown-Text-101-Chat-Formatting-Bold-Italic-Underline) +- Your markdown source should not have aggressive rate limiting. +- Do not set the polling rate below 30 seconds (30000ms), as too many requests might be considered API abuse. +- With the recommended max of 20 webhook sources, the tool should not exceed 128MB of RAM. If it does, please open an issue. +- If you are using Hedgedoc, you must provide `/download` to the end of you published content! \ No newline at end of file diff --git a/config.json.example b/config.json.example index 51162a9..99ba1e2 100644 --- a/config.json.example +++ b/config.json.example @@ -1,4 +1,8 @@ [ + { + "timezone": "America/Los_Angeles", + "pollingRateMS": "30000" + }, { "name": "first", "markdownURL": "https://example.com/s/example/download", diff --git a/index.js b/index.js index c01036f..0d4e26b 100644 --- a/index.js +++ b/index.js @@ -3,32 +3,40 @@ const fs = require('fs'); const path = require('path'); const dns = require('dns'); -// --- CONFIG --- -const pollIntervalMs = 30 * 1000; // 30 seconds const configPath = path.join(__dirname, 'config.json'); const statePath = path.join(__dirname, './data/state.json'); const errorLogPath = path.join(__dirname, './data/error.log'); +// Load configs +let rawConfigs; +try { + rawConfigs = JSON.parse(fs.readFileSync(configPath, 'utf8')); +} catch (error) { + console.error('❗ Failed to load config.json: ' + error.message); + process.exit(1); +} + +const globalSettings = rawConfigs[0] || {}; +const configs = rawConfigs.slice(1); + +const globalTimezone = globalSettings.timezone || 'UTC'; +const pollIntervalMs = parseInt(globalSettings.pollingRateMS || '30000'); + +if (configs.length === 0) { + console.error('❗ No configs found in config.json.'); + process.exit(1); +} + // --- ERROR LOGGER --- function logError(message) { const timestamp = new Date().toLocaleString('en-US', { - timeZone: 'America/Los_Angeles' + timeZone: globalTimezone }); const line = `[${timestamp}] ${message}\n`; fs.appendFileSync(errorLogPath, line); console.error(message); } - -// Load configs -let configs; -try { - configs = JSON.parse(fs.readFileSync(configPath, 'utf8')); -} catch (error) { - logError('❗ Failed to load config.json: ' + error.message); - process.exit(1); -} - // Load or initialize global state let state = {}; if (fs.existsSync(statePath)) { diff --git a/package.json b/package.json index 2b60b89..ab15609 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "discord-hedgedoc-md", - "version": "1.0.5", + "version": "1.1", "main": "index.js", "scripts": { "test": "node index.js",