nodebb-plugin-discord-onekite 1.0.0 → 1.0.2

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/README.md CHANGED
@@ -1,20 +1,7 @@
1
- # nodebb-plugin-discord-onekite (NodeBB v4.x)
1
+ # nodebb-plugin-discord-onekite (NodeBB v4.x) — v1.0.1
2
2
 
3
- Plugin NodeBB qui notifie un salon Discord via **webhook** lors :
4
- - des **nouveaux sujets**
5
- - et optionnellement des **réponses**
3
+ Fix: template build errors by removing inline <script> from the .tpl and using acpScripts properly.
6
4
 
7
- ## Installation (local)
8
- Depuis le dossier NodeBB :
9
-
10
- ```bash
11
- npm install /chemin/vers/nodebb-plugin-discord-onekite
12
- ./nodebb build
13
- ./nodebb restart
14
- ```
15
-
16
- ## Configuration
17
- ACP → Plugins → **Discord Onekite**
18
- - Webhook URL
19
- - (option) Notifier les réponses
20
- - Catégories à notifier (si aucune sélection : toutes)
5
+ - Categories list loads in ACP
6
+ - Settings save works
7
+ - No categories selected => notify all
package/library.js CHANGED
@@ -10,19 +10,13 @@ const user = require.main.require('./src/user');
10
10
 
11
11
  const controllers = require('./lib/controllers');
12
12
 
13
- // Settings are stored under this key in NodeBB's settings system
14
13
  const SETTINGS_KEY = 'discord-onekite';
15
14
 
16
15
  function normalizeCids(value) {
17
16
  if (!value) return [];
18
17
  if (Array.isArray(value)) return value.map(v => String(v)).filter(Boolean);
19
-
20
- // Sometimes saved as "1,2,3"
21
18
  if (typeof value === 'string') {
22
- return value
23
- .split(',')
24
- .map(s => s.trim())
25
- .filter(Boolean);
19
+ return value.split(',').map(s => s.trim()).filter(Boolean);
26
20
  }
27
21
  return [];
28
22
  }
@@ -37,7 +31,7 @@ async function getSettings() {
37
31
  }
38
32
 
39
33
  function cidAllowed(topicCid, allowedCids) {
40
- // IMPORTANT (per request): if no categories are selected => notify ALL categories.
34
+ // none selected => ALL
41
35
  if (!allowedCids || allowedCids.length === 0) return true;
42
36
  return allowedCids.includes(String(topicCid));
43
37
  }
@@ -51,7 +45,6 @@ async function postToDiscord(webhookUrl, payload) {
51
45
  body: JSON.stringify(payload),
52
46
  });
53
47
 
54
- // Discord webhook success is commonly 204 No Content
55
48
  if (res.statusCode < 200 || res.statusCode >= 300) {
56
49
  const text = await res.body.text().catch(() => '');
57
50
  throw new Error(`[discord-onekite] Discord webhook HTTP ${res.statusCode}: ${text}`);
@@ -61,13 +54,10 @@ async function postToDiscord(webhookUrl, payload) {
61
54
  async function buildTopicEmbed({ tid, pid, type }) {
62
55
  const baseUrl = meta.config.url;
63
56
 
64
- // For v4.x, these fields are stable and enough for URL + title
65
57
  const topicData = await topics.getTopicFields(tid, ['tid', 'uid', 'cid', 'title', 'slug', 'timestamp', 'mainPid']);
66
58
  if (!topicData) return null;
67
59
 
68
60
  const topicUrl = `${baseUrl}/topic/${topicData.slug || topicData.tid}`;
69
-
70
- // Author (topic author). If you prefer reply author, adjust to read post author.
71
61
  const u = await user.getUserFields(topicData.uid, ['username', 'picture']);
72
62
 
73
63
  const isReply = type === 'reply';
@@ -88,7 +78,6 @@ async function buildTopicEmbed({ tid, pid, type }) {
88
78
 
89
79
  const Plugin = {};
90
80
 
91
- // Mount ACP page route (NodeBB v4.x)
92
81
  Plugin.init = async ({ router }) => {
93
82
  routeHelpers.setupAdminPageRoute(
94
83
  router,
@@ -98,7 +87,6 @@ Plugin.init = async ({ router }) => {
98
87
  );
99
88
  };
100
89
 
101
- // Add entry under ACP -> Plugins
102
90
  Plugin.addAdminNavigation = (header) => {
103
91
  header.plugins.push({
104
92
  route: '/plugins/discord-onekite',
@@ -108,16 +96,14 @@ Plugin.addAdminNavigation = (header) => {
108
96
  return header;
109
97
  };
110
98
 
111
- // NEW TOPICS ONLY (unless notifyReplies enabled for posts)
112
99
  Plugin.onTopicSave = async (data) => {
113
100
  try {
114
- const { webhookUrl, cids } = await getSettings();
115
- if (!webhookUrl) return;
101
+ const settings = await getSettings();
102
+ if (!settings.webhookUrl) return;
116
103
 
117
104
  const tid = data?.tid || data?.topic?.tid;
118
105
  if (!tid) return;
119
106
 
120
- // In v4.x, new topics often expose isNew; as a fallback, we detect postcount=1 if present.
121
107
  const isNew =
122
108
  data?.isNew === true ||
123
109
  data?.topic?.isNew === true ||
@@ -128,38 +114,35 @@ Plugin.onTopicSave = async (data) => {
128
114
  const built = await buildTopicEmbed({ tid, type: 'topic' });
129
115
  if (!built) return;
130
116
 
131
- if (!cidAllowed(built.topicData.cid, cids)) return;
117
+ if (!cidAllowed(built.topicData.cid, settings.cids)) return;
132
118
 
133
- await postToDiscord(webhookUrl, { embeds: [built.embed] });
119
+ await postToDiscord(settings.webhookUrl, { embeds: [built.embed] });
134
120
  } catch (err) {
135
121
  // eslint-disable-next-line no-console
136
122
  console.error(err);
137
123
  }
138
124
  };
139
125
 
140
- // REPLIES (optional)
141
126
  Plugin.onPostSave = async (data) => {
142
127
  try {
143
- const { webhookUrl, notifyReplies, cids } = await getSettings();
144
- if (!webhookUrl || !notifyReplies) return;
128
+ const settings = await getSettings();
129
+ if (!settings.notifyReplies) return;
130
+ if (!settings.webhookUrl) return;
145
131
 
146
132
  const pid = data?.pid || data?.post?.pid;
147
133
  const tid = data?.tid || data?.post?.tid;
148
134
  if (!pid || !tid) return;
149
135
 
150
- // Only new posts (avoid edits). v4.x commonly provides isNew for creates.
151
136
  const isNew = data?.isNew === true || data?.post?.isNew === true;
152
137
  if (!isNew) return;
153
138
 
154
139
  const built = await buildTopicEmbed({ tid, pid, type: 'reply' });
155
140
  if (!built) return;
156
141
 
157
- // Avoid notifying the main post (already covered by topic.save)
158
142
  if (built.topicData?.mainPid && String(built.topicData.mainPid) === String(pid)) return;
143
+ if (!cidAllowed(built.topicData.cid, settings.cids)) return;
159
144
 
160
- if (!cidAllowed(built.topicData.cid, cids)) return;
161
-
162
- await postToDiscord(webhookUrl, { embeds: [built.embed] });
145
+ await postToDiscord(settings.webhookUrl, { embeds: [built.embed] });
163
146
  } catch (err) {
164
147
  // eslint-disable-next-line no-console
165
148
  console.error(err);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-discord-onekite",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Discord webhook notifier for Onekite (NodeBB v4.x only)",
5
5
  "main": "library.js",
6
6
  "license": "MIT",
@@ -1,41 +1,63 @@
1
1
  'use strict';
2
-
3
2
  /* global $, app */
4
3
 
5
4
  define('admin/plugins/discord-onekite', ['settings', 'api'], function (settings, api) {
6
5
  const ACP = {};
7
6
 
7
+ function normalizeCids(v) {
8
+ if (!v) return [];
9
+ if (Array.isArray(v)) return v.map(String).filter(Boolean);
10
+ if (typeof v === 'string') {
11
+ return v.split(',').map(s => s.trim()).filter(Boolean);
12
+ }
13
+ return [];
14
+ }
15
+
8
16
  ACP.init = async function () {
9
- // Charge settings existants (webhookUrl, notifyReplies, cids)
10
- settings.load('discord-onekite', $('.discord-onekite-settings'));
17
+ const $form = $('.discord-onekite-settings');
18
+ const $select = $('#cids');
19
+
20
+ // Load current saved settings (including cids) BEFORE fetching categories,
21
+ // so we can apply selection after options are created.
22
+ let saved = {};
23
+ try {
24
+ saved = await new Promise((resolve) => {
25
+ settings.get('discord-onekite', (data) => resolve(data || {}));
26
+ });
27
+ } catch (e) {
28
+ saved = {};
29
+ }
30
+
31
+ // Populate simple fields (webhookUrl, notifyReplies, etc.)
32
+ settings.load('discord-onekite', $form);
11
33
 
12
- // Charge les catégories via l'API NodeBB (client api module ajoute /api)
34
+ const savedCids = normalizeCids(saved.cids);
35
+
36
+ // Fetch categories list and populate select
13
37
  try {
14
38
  const res = await api.get('/categories');
15
39
  const categories = (res && res.categories) ? res.categories : [];
16
40
 
17
- const $select = $('#cids');
18
41
  $select.empty();
19
-
20
42
  categories
21
43
  .filter(c => c && typeof c.cid !== 'undefined' && c.name)
22
44
  .forEach(c => {
23
- $('<option />')
24
- .val(String(c.cid))
25
- .text(c.name)
26
- .appendTo($select);
45
+ const cid = String(c.cid);
46
+ const $opt = $('<option />').val(cid).text(c.name);
47
+ if (savedCids.includes(cid)) {
48
+ $opt.prop('selected', true);
49
+ }
50
+ $opt.appendTo($select);
27
51
  });
28
-
29
- // Recharge settings pour re-sélectionner les catégories déjà sauvegardées
30
- settings.load('discord-onekite', $('.discord-onekite-settings'));
31
52
  } catch (e) {
32
53
  // eslint-disable-next-line no-console
33
54
  console.error('[discord-onekite] Could not load categories', e);
34
55
  app.alertError('Impossible de charger la liste des catégories.');
35
56
  }
36
57
 
37
- $('#save').on('click', function () {
38
- settings.save('discord-onekite', $('.discord-onekite-settings'), function () {
58
+ // Save button
59
+ $('#save').off('click.discordOnekite').on('click.discordOnekite', function () {
60
+ settings.save('discord-onekite', $form, function () {
39
61
  app.alertSuccess('Paramètres enregistrés !');
40
62
  });
41
63
  });