nodebb-plugin-discord-onekite 1.1.10 → 1.1.11

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,5 +1,9 @@
1
- # nodebb-plugin-discord-onekite v1.1.7
1
+ # nodebb-plugin-discord-onekite v1.1.4.2
2
2
 
3
- Fixes webpack template build errors by removing `<!-- IMPORT admin/partials/save_button.tpl -->` and using an inline save button.
3
+ Based on v1.1.4.
4
4
 
5
- Everything else unchanged from v1.1.6.
5
+ Change: Discord notification format is now:
6
+ - Single clickable link line: 🆕 Nouveau sujet : [Titre](URL) (or reply variant)
7
+ - Excerpt below
8
+
9
+ ACP behavior unchanged from v1.1.4.
package/library.js CHANGED
@@ -8,6 +8,7 @@ const middleware = require.main.require('./src/middleware');
8
8
 
9
9
  const topics = require.main.require('./src/topics');
10
10
  const posts = require.main.require('./src/posts');
11
+ const user = require.main.require('./src/user');
11
12
 
12
13
  const controllers = require('./lib/controllers');
13
14
 
@@ -37,6 +38,15 @@ function ensureAbsoluteUrl(baseUrl, maybeUrl) {
37
38
  return s;
38
39
  }
39
40
 
41
+ function isValidHttpUrl(s) {
42
+ try {
43
+ const u = new URL(s);
44
+ return u.protocol === 'http:' || u.protocol === 'https:';
45
+ } catch {
46
+ return false;
47
+ }
48
+ }
49
+
40
50
  function stripHtml(html) {
41
51
  if (!html) return '';
42
52
  return String(html)
@@ -89,15 +99,15 @@ async function postToDiscord(webhookUrl, payload) {
89
99
  }
90
100
  }
91
101
 
92
- async function sendDiscord(webhookUrl, payload) {
102
+ async function sendDiscord(webhookUrl, payload, fallbackContent) {
93
103
  if (!webhookUrl) return;
104
+
94
105
  try {
95
106
  await postToDiscord(webhookUrl, payload);
96
107
  } catch (e) {
97
- // If Discord rejects embeds, retry with plain content only.
98
- if (e && e.statusCode === 400 && payload && payload.content) {
108
+ if (e && e.statusCode === 400 && fallbackContent) {
99
109
  try {
100
- await postToDiscord(webhookUrl, { content: payload.content });
110
+ await postToDiscord(webhookUrl, { content: fallbackContent });
101
111
  return;
102
112
  } catch (e2) {
103
113
  console.error(e2);
@@ -121,25 +131,40 @@ async function getPostExcerpt(pid) {
121
131
  async function buildPayload({ tid, pid, isReply }) {
122
132
  const baseUrl = normalizeBaseUrl(meta.config.url);
123
133
 
124
- const topicData = await topics.getTopicFields(tid, ['tid', 'cid', 'title', 'slug', 'mainPid']);
134
+ const topicData = await topics.getTopicFields(tid, ['tid', 'uid', 'cid', 'title', 'slug', 'mainPid']);
125
135
  if (!topicData) return null;
126
136
 
137
+ // Always build the forum link like the example: https://www.onekite.com/topic/<tid or slug>
127
138
  const topicUrl = ensureAbsoluteUrl(baseUrl, `/topic/${topicData.slug || topicData.tid}`);
128
- const targetUrl = (isReply && pid) ? `${topicUrl}/${pid}` : topicUrl;
129
139
 
140
+ // For replies, link directly to the post if possible
141
+ const postUrl = (isReply && pid) ? `${topicUrl}/${pid}` : topicUrl;
142
+
143
+ const u = await user.getUserFields(topicData.uid, ['username']);
130
144
  const title = (topicData.title || (isReply ? 'Nouvelle réponse' : 'Nouveau sujet')).toString().slice(0, 256);
145
+ const authorName = (u && u.username ? String(u.username) : 'Utilisateur').slice(0, 256);
146
+
131
147
  const excerpt = await getPostExcerpt(isReply ? pid : topicData.mainPid);
148
+ const description = truncate(excerpt || (isReply ? `Réponse de ${authorName}` : `Sujet créé par ${authorName}`), 500);
149
+
150
+ // Minimal embed but with URL so the title is clickable
151
+ const embed = {
152
+ title,
153
+ description,
154
+ };
155
+ if (isValidHttpUrl(postUrl)) embed.url = postUrl;
132
156
 
133
- // Single clickable link line + excerpt below
157
+ // Message format:
158
+ // 1) A single clickable link line: "🆕 Nouveau sujet : [Titre](URL)" (or reply variant)
159
+ // 2) Excerpt below
134
160
  const linkLine = isReply
135
- ? `🗨️ Nouvelle réponse : [${title}](${targetUrl})`
161
+ ? `🗨️ Nouvelle réponse : [${title}](${postUrl})`
136
162
  : `🆕 Nouveau sujet : [${title}](${topicUrl})`;
137
163
 
138
- const content = [linkLine, excerpt].filter(Boolean).join('\n');
139
-
140
- const embed = { title, description: excerpt || '' , url: targetUrl };
164
+ const content = [linkLine, description].filter(Boolean).join('
165
+ ');
141
166
 
142
- return { topicData, content, embed };
167
+ return { topicData, embed, content };
143
168
  }
144
169
 
145
170
  function extractTidPid(data) {
@@ -158,8 +183,8 @@ Plugin.init = async ({ router }) => {
158
183
  controllers.renderAdminPage
159
184
  );
160
185
 
161
- // AJAX save endpoint (used by admin.js via api.post)
162
- router.post('/admin/plugins/discord-onekite/save',
186
+ // AJAX save endpoint for ACP (used by admin.js)
187
+ router.post('/api/admin/plugins/discord-onekite/save',
163
188
  middleware.admin.checkPrivileges,
164
189
  async (req, res) => {
165
190
  try {
@@ -196,9 +221,11 @@ Plugin.onTopicPost = async (data) => {
196
221
 
197
222
  const built = await buildPayload({ tid, isReply: false });
198
223
  if (!built) return;
224
+
199
225
  if (!cidAllowed(built.topicData.cid, settings.cids)) return;
200
226
 
201
- await sendDiscord(settings.webhookUrl, { content: built.content, embeds: [built.embed] });
227
+ // Send content + embed (content ensures clickable link always)
228
+ await sendDiscord(settings.webhookUrl, { content: built.content, embeds: [built.embed] }, built.content);
202
229
  };
203
230
 
204
231
  Plugin.onTopicReply = async (data) => {
@@ -211,9 +238,12 @@ Plugin.onTopicReply = async (data) => {
211
238
 
212
239
  const built = await buildPayload({ tid, pid, isReply: true });
213
240
  if (!built) return;
241
+
242
+ if (built.topicData?.mainPid && String(built.topicData.mainPid) === String(pid)) return;
243
+
214
244
  if (!cidAllowed(built.topicData.cid, settings.cids)) return;
215
245
 
216
- await sendDiscord(settings.webhookUrl, { content: built.content, embeds: [built.embed] });
246
+ await sendDiscord(settings.webhookUrl, { content: built.content, embeds: [built.embed] }, built.content);
217
247
  };
218
248
 
219
249
  module.exports = Plugin;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-discord-onekite",
3
- "version": "1.1.10",
3
+ "version": "1.1.11",
4
4
  "description": "Discord webhook notifier for Onekite (NodeBB v4.x only)",
5
5
  "main": "library.js",
6
6
  "license": "MIT",
@@ -21,7 +21,6 @@ define('admin/plugins/discord-onekite', ['api'], function (api) {
21
21
  cids: getMultiSelectValues('#cids'),
22
22
  };
23
23
 
24
- // api.post prefixes /api automatically, so server route must be /admin/...
25
24
  api.post('/admin/plugins/discord-onekite/save', payload).then(function () {
26
25
  if (window.app && typeof app.alertSuccess === 'function') {
27
26
  app.alertSuccess('Paramètres enregistrés !');
@@ -1,13 +1,8 @@
1
1
  <div class="acp-page-container">
2
- <div class="d-flex justify-content-between align-items-start mb-3">
3
- <div>
4
- <h4 class="mb-1">Discord Onekite</h4>
5
- <p class="text-muted mb-0">Notifications Discord via webhook.</p>
6
- </div>
7
- <button id="save" class="btn btn-primary">
8
- <i class="fa fa-save"></i> Enregistrer
9
- </button>
10
- </div>
2
+ <h4>Discord Onekite</h4>
3
+ <p class="text-muted">
4
+ Notifications Discord via webhook.
5
+ </p>
11
6
 
12
7
  <form role="form" class="discord-onekite-settings">
13
8
  <div class="mb-3">
@@ -33,5 +28,7 @@
33
28
  Si aucune catégorie n’est sélectionnée : <strong>toutes les catégories</strong> seront notifiées.
34
29
  </p>
35
30
  </div>
31
+
32
+ <!-- IMPORT admin/partials/save_button.tpl -->
36
33
  </form>
37
34
  </div>