@rithien/discord_bridge 0.3.3 → 0.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## v0.3.4 - rithien fork
4
+
5
+ - `discordMessage` no longer assumes `message.member` is present (it can be `null` in discord.js v14 — author left the guild, partial/cache miss, gateway didn't deliver the member); falls back to the always-present `message.author`, so Discord→Factorio chat is no longer silently dropped with a `TypeError` (FIX31 #31).
6
+ - `fetchChannels` handles channel errors per-channel (log + continue) instead of re-throwing any `code !== 10003`; a single inaccessible channel (Missing Access/Permissions, network) no longer aborts plugin `init()` and leaves the bridge half-built (FIX31 #32).
7
+ - Added `allowedMentions: { parse: [] }` to instance status (`onInstanceStatusChanged`) and host connection (`onHostConnectionEvent`) notifications — previously only player-action messages set it. An instance/host name containing `@everyone`/`@here`/a role mention no longer triggers a real ping (FIX31 #33).
8
+ - Bounded the offline `messageQueue` (cap 1000, FIFO drop-oldest with a one-time warn) so a long controller disconnect during active play cannot grow it without limit nor flood Discord with the whole backlog on reconnect (FIX31 #30).
9
+
10
+ ## v0.3.3 - rithien fork
11
+
12
+ - Added `allowedMentions: { parse: [] }` to player-action `channel.send` calls so chat relayed from Factorio containing `@everyone`/`@here`/role mentions does not actually ping on Discord (security fix K3).
13
+
3
14
  ## v0.3.2 - rithien fork
4
15
 
5
16
  - Switched `handleDiscordChatEvent` from `/sc game.print('${text}')` to `/fp ${text}` (custom command exposed by the `factorio-polska` scenario in `commands/fp.lua`). Avoids the persistent "Cheats have been enabled" flag set by `/silent-command`, which permanently disables achievements on the save. Pair with `@rithien/factorio-polska` scenario v0.1.0+ (defines `/fp`). Without that scenario, the chat bridge will fail silently (RCON returns "Unknown command: fp"). To keep working on a vanilla scenario, pin to v0.3.1.
package/controller.js CHANGED
@@ -98,10 +98,15 @@ class ControllerPlugin extends BaseControllerPlugin {
98
98
  this.logger.error(`Channel ID ${id} was not found`);
99
99
  }
100
100
  } catch (err) {
101
- if (err.code !== 10003) { // Unknown channel
102
- throw err;
101
+ // FIX31 #32: odporność per-kanał. Re-throw wywracał fetchChannels() poza connect()/init()
102
+ // (fetchChannels jest wołane poza try/catch logowania), zostawiając most w stanie
103
+ // częściowo zbudowanym. Błędy inne niż 10003 to m.in. 50001 (Missing Access),
104
+ // 50013 (Missing Permissions) i sieciowe — logujemy i kontynuujemy pętlę.
105
+ if (err.code === 10003) { // Unknown channel
106
+ this.logger.error(`Channel ID ${id} was not found`);
107
+ } else {
108
+ this.logger.error(`Failed to fetch channel ID ${id}, skipping:\n${err.stack}`);
103
109
  }
104
- this.logger.error(`Channel ID ${id} was not found`);
105
110
  }
106
111
  }
107
112
 
@@ -157,10 +162,15 @@ class ControllerPlugin extends BaseControllerPlugin {
157
162
  return;
158
163
  }
159
164
 
165
+ // FIX31 #31: message.member bywa null (autor nie jest już członkiem gildii, partial/cache
166
+ // miss, gateway nie dostarczył member) — sięgamy po zawsze obecny message.author.
167
+ const displayName = message.member?.displayName ?? message.author.displayName ?? message.author.username;
168
+ const tag = message.author.tag ?? message.author.username;
169
+
160
170
  const template = this.controller.config.get("discord_bridge.discord_template");
161
171
  const content = template.replace(/(__display_name__|__username__|__content__)/g, (sub) => ({
162
- "__display_name__": message.member.displayName,
163
- "__username__": message.member.user.tag,
172
+ "__display_name__": displayName,
173
+ "__username__": tag,
164
174
  "__content__": message.cleanContent,
165
175
  }[sub]));
166
176
 
@@ -204,7 +214,12 @@ class ControllerPlugin extends BaseControllerPlugin {
204
214
  return;
205
215
  }
206
216
 
207
- await channel.send(this.formatMessage(template, hostName, instanceName, ""));
217
+ // allowedMentions: nazwa instancji/hosta trafia do szablonu — bez tego @everyone/@here w nazwie
218
+ // realnie pinguje (FIX31 #33, spójnie z akcjami graczy).
219
+ await channel.send({
220
+ content: this.formatMessage(template, hostName, instanceName, ""),
221
+ allowedMentions: { parse: [] },
222
+ });
208
223
  }
209
224
 
210
225
  onHostConnectionEvent(hostConnection, event) {
@@ -218,7 +233,8 @@ class ControllerPlugin extends BaseControllerPlugin {
218
233
  return;
219
234
  }
220
235
  const message = this.formatMessage(template, hostName, "", "");
221
- this.fallbackChannel.send(message).catch(
236
+ // allowedMentions: nazwa hosta trafia do szablonu — blokujemy ping z @everyone/@here (FIX31 #33).
237
+ this.fallbackChannel.send({ content: message, allowedMentions: { parse: [] } }).catch(
222
238
  err => { this.logger.error(`Unexpected error:\n${err.stack}`); }
223
239
  );
224
240
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "main.js": "static/main.42d03a9854919d3e8189.js",
3
- "discord_bridge.js": "static/discord_bridge.eb399a233189cc9de727.js",
3
+ "discord_bridge.js": "static/discord_bridge.3b69d59522327ecb3b8a.js",
4
4
  "static/info_js.js": "static/info_js.131defcd9652f0e24d4a.js",
5
- "static/package_json.js": "static/package_json.eb40f2bb2cb67c86f9d0.js"
5
+ "static/package_json.js": "static/package_json.04ae25900d51f777e4ec.js"
6
6
  }
@@ -123,7 +123,7 @@ __webpack_require__.d(exports, {
123
123
  /******/ // This function allow to reference async chunks
124
124
  /******/ __webpack_require__.u = (chunkId) => {
125
125
  /******/ // return url for filenames based on template
126
- /******/ return "static/" + chunkId + "." + {"info_js":"131defcd9652f0e24d4a","package_json":"eb40f2bb2cb67c86f9d0"}[chunkId] + ".js";
126
+ /******/ return "static/" + chunkId + "." + {"info_js":"131defcd9652f0e24d4a","package_json":"04ae25900d51f777e4ec"}[chunkId] + ".js";
127
127
  /******/ };
128
128
  /******/ })();
129
129
  /******/
@@ -15,7 +15,7 @@
15
15
  \**********************/
16
16
  (module) {
17
17
 
18
- module.exports = /*#__PURE__*/JSON.parse('{"name":"@rithien/discord_bridge","version":"0.3.3","description":"Clusterio plugin bridging chat between instances and Discord, with per-instance channel routing","main":"info.js","scripts":{"test":"echo \\"Error: no test specified\\" && exit 1","prepare":"webpack-cli --env production"},"keywords":["clusterio","clusterio-plugin","factorio","discord"],"author":"rithien <jacek@zaluzje.bialystok.pl>","license":"MIT","peerDependencies":{"@clusterio/lib":"^2.0.0-alpha.14"},"devDependencies":{"@clusterio/lib":"^2.0.0-alpha.14","@clusterio/web_ui":"^2.0.0-alpha.14","webpack":"^5.88.2","webpack-cli":"^5.1.4","webpack-merge":"^5.9.0"},"dependencies":{"discord.js":"^14.14.1"},"publishConfig":{"access":"public"}}');
18
+ module.exports = /*#__PURE__*/JSON.parse('{"name":"@rithien/discord_bridge","version":"0.3.4","description":"Clusterio plugin bridging chat between instances and Discord, with per-instance channel routing","main":"info.js","scripts":{"test":"echo \\"Error: no test specified\\" && exit 1","prepare":"webpack-cli --env production"},"keywords":["clusterio","clusterio-plugin","factorio","discord"],"author":"rithien <jacek@zaluzje.bialystok.pl>","license":"MIT","peerDependencies":{"@clusterio/lib":"^2.0.0-alpha.14"},"devDependencies":{"@clusterio/lib":"^2.0.0-alpha.14","@clusterio/web_ui":"^2.0.0-alpha.14","webpack":"^5.88.2","webpack-cli":"^5.1.4","webpack-merge":"^5.9.0"},"dependencies":{"discord.js":"^14.14.1"},"publishConfig":{"access":"public"}}');
19
19
 
20
20
  /***/ }
21
21
 
package/instance.js CHANGED
@@ -3,6 +3,11 @@ const { BaseInstancePlugin } = require("@clusterio/host");
3
3
 
4
4
  const { InstanceActionEvent, DiscordChatEvent } = require("./info.js");
5
5
 
6
+ // Twardy limit bufora akcji na czas rozłączenia z kontrolerem (FIX31 #30). Przy długim rozłączeniu
7
+ // i aktywnej rozgrywce (join/leave/chat) kolejka rosłaby bez ograniczeń (OOM hosta), a po reconnekcie
8
+ // cała zaległość zalałaby kanał Discord naraz. FIFO: po przekroczeniu odrzucamy najstarsze.
9
+ const MAX_MESSAGE_QUEUE = 1000;
10
+
6
11
 
7
12
  /**
8
13
  * Removes gps and train tags from messags
@@ -14,6 +19,7 @@ function removeTags(content) {
14
19
  class InstancePlugin extends BaseInstancePlugin {
15
20
  async init() {
16
21
  this.messageQueue = [];
22
+ this.queueOverflowWarned = false;
17
23
  this.instance.handle(DiscordChatEvent, this.handleDiscordChatEvent.bind(this));
18
24
  }
19
25
 
@@ -23,6 +29,7 @@ class InstancePlugin extends BaseInstancePlugin {
23
29
  this.sendChat(action, content);
24
30
  }
25
31
  this.messageQueue = [];
32
+ this.queueOverflowWarned = false;
26
33
  }
27
34
  }
28
35
 
@@ -53,6 +60,13 @@ class InstancePlugin extends BaseInstancePlugin {
53
60
  this.sendChat(output.action, output.message);
54
61
  } else {
55
62
  this.messageQueue.push([output.action, output.message]);
63
+ if (this.messageQueue.length > MAX_MESSAGE_QUEUE) {
64
+ this.messageQueue.shift(); // odrzuć najstarsze (FIX31 #30)
65
+ if (!this.queueOverflowWarned) {
66
+ this.logger.warn(`discord_bridge: messageQueue przekroczyła ${MAX_MESSAGE_QUEUE} akcji (kontroler rozłączony) — odrzucam najstarsze`);
67
+ this.queueOverflowWarned = true;
68
+ }
69
+ }
56
70
  }
57
71
  }
58
72
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rithien/discord_bridge",
3
- "version": "0.3.3",
3
+ "version": "0.3.4",
4
4
  "description": "Clusterio plugin bridging chat between instances and Discord, with per-instance channel routing",
5
5
  "main": "info.js",
6
6
  "scripts": {