nodebb-plugin-discord-onekite 1.1.9 → 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 +8 -4
- package/lib/controllers.js +0 -1
- package/library.js +48 -14
- package/package.json +1 -1
- package/plugin.json +4 -1
- package/static/lib/admin.js +39 -0
- package/templates/admin/plugins/discord-onekite.tpl +5 -9
package/README.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
# nodebb-plugin-discord-onekite v1.1.
|
|
1
|
+
# nodebb-plugin-discord-onekite v1.1.4.2
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
Discord
|
|
3
|
+
Based on v1.1.4.
|
|
4
|
+
|
|
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/lib/controllers.js
CHANGED
|
@@ -40,7 +40,6 @@ controllers.renderAdminPage = async function (req, res) {
|
|
|
40
40
|
res.render('admin/plugins/discord-onekite', {
|
|
41
41
|
settings: settings || {},
|
|
42
42
|
categories: categoriesForTpl,
|
|
43
|
-
saved: req.query && (req.query.saved === '1' || req.query.saved === 'true'),
|
|
44
43
|
});
|
|
45
44
|
};
|
|
46
45
|
|
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,14 +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 (e && e.statusCode === 400 &&
|
|
108
|
+
if (e && e.statusCode === 400 && fallbackContent) {
|
|
98
109
|
try {
|
|
99
|
-
await postToDiscord(webhookUrl, { content:
|
|
110
|
+
await postToDiscord(webhookUrl, { content: fallbackContent });
|
|
100
111
|
return;
|
|
101
112
|
} catch (e2) {
|
|
102
113
|
console.error(e2);
|
|
@@ -120,24 +131,40 @@ async function getPostExcerpt(pid) {
|
|
|
120
131
|
async function buildPayload({ tid, pid, isReply }) {
|
|
121
132
|
const baseUrl = normalizeBaseUrl(meta.config.url);
|
|
122
133
|
|
|
123
|
-
const topicData = await topics.getTopicFields(tid, ['tid', 'cid', 'title', 'slug', 'mainPid']);
|
|
134
|
+
const topicData = await topics.getTopicFields(tid, ['tid', 'uid', 'cid', 'title', 'slug', 'mainPid']);
|
|
124
135
|
if (!topicData) return null;
|
|
125
136
|
|
|
137
|
+
// Always build the forum link like the example: https://www.onekite.com/topic/<tid or slug>
|
|
126
138
|
const topicUrl = ensureAbsoluteUrl(baseUrl, `/topic/${topicData.slug || topicData.tid}`);
|
|
127
|
-
const targetUrl = (isReply && pid) ? `${topicUrl}/${pid}` : topicUrl;
|
|
128
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']);
|
|
129
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
|
+
|
|
130
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;
|
|
131
156
|
|
|
157
|
+
// Message format:
|
|
158
|
+
// 1) A single clickable link line: "🆕 Nouveau sujet : [Titre](URL)" (or reply variant)
|
|
159
|
+
// 2) Excerpt below
|
|
132
160
|
const linkLine = isReply
|
|
133
|
-
? `🗨️ Nouvelle réponse : [${title}](${
|
|
161
|
+
? `🗨️ Nouvelle réponse : [${title}](${postUrl})`
|
|
134
162
|
: `🆕 Nouveau sujet : [${title}](${topicUrl})`;
|
|
135
163
|
|
|
136
|
-
const content = [linkLine,
|
|
137
|
-
|
|
138
|
-
const embed = { title, description: excerpt || '', url: targetUrl };
|
|
164
|
+
const content = [linkLine, description].filter(Boolean).join('
|
|
165
|
+
');
|
|
139
166
|
|
|
140
|
-
return { topicData,
|
|
167
|
+
return { topicData, embed, content };
|
|
141
168
|
}
|
|
142
169
|
|
|
143
170
|
function extractTidPid(data) {
|
|
@@ -156,7 +183,8 @@ Plugin.init = async ({ router }) => {
|
|
|
156
183
|
controllers.renderAdminPage
|
|
157
184
|
);
|
|
158
185
|
|
|
159
|
-
|
|
186
|
+
// AJAX save endpoint for ACP (used by admin.js)
|
|
187
|
+
router.post('/api/admin/plugins/discord-onekite/save',
|
|
160
188
|
middleware.admin.checkPrivileges,
|
|
161
189
|
async (req, res) => {
|
|
162
190
|
try {
|
|
@@ -166,10 +194,11 @@ Plugin.init = async ({ router }) => {
|
|
|
166
194
|
cids: req.body.cids || '',
|
|
167
195
|
};
|
|
168
196
|
await meta.settings.set(SETTINGS_KEY, payload);
|
|
197
|
+
res.json({ ok: true });
|
|
169
198
|
} catch (e) {
|
|
170
199
|
console.error('[discord-onekite] save failed', e);
|
|
200
|
+
res.status(500).json({ ok: false });
|
|
171
201
|
}
|
|
172
|
-
res.redirect('/admin/plugins/discord-onekite?saved=1');
|
|
173
202
|
}
|
|
174
203
|
);
|
|
175
204
|
};
|
|
@@ -192,9 +221,11 @@ Plugin.onTopicPost = async (data) => {
|
|
|
192
221
|
|
|
193
222
|
const built = await buildPayload({ tid, isReply: false });
|
|
194
223
|
if (!built) return;
|
|
224
|
+
|
|
195
225
|
if (!cidAllowed(built.topicData.cid, settings.cids)) return;
|
|
196
226
|
|
|
197
|
-
|
|
227
|
+
// Send content + embed (content ensures clickable link always)
|
|
228
|
+
await sendDiscord(settings.webhookUrl, { content: built.content, embeds: [built.embed] }, built.content);
|
|
198
229
|
};
|
|
199
230
|
|
|
200
231
|
Plugin.onTopicReply = async (data) => {
|
|
@@ -207,9 +238,12 @@ Plugin.onTopicReply = async (data) => {
|
|
|
207
238
|
|
|
208
239
|
const built = await buildPayload({ tid, pid, isReply: true });
|
|
209
240
|
if (!built) return;
|
|
241
|
+
|
|
242
|
+
if (built.topicData?.mainPid && String(built.topicData.mainPid) === String(pid)) return;
|
|
243
|
+
|
|
210
244
|
if (!cidAllowed(built.topicData.cid, settings.cids)) return;
|
|
211
245
|
|
|
212
|
-
await sendDiscord(settings.webhookUrl, { content: built.content, embeds: [built.embed] });
|
|
246
|
+
await sendDiscord(settings.webhookUrl, { content: built.content, embeds: [built.embed] }, built.content);
|
|
213
247
|
};
|
|
214
248
|
|
|
215
249
|
module.exports = Plugin;
|
package/package.json
CHANGED
package/plugin.json
CHANGED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
/* global $, app */
|
|
3
|
+
|
|
4
|
+
define('admin/plugins/discord-onekite', ['api'], function (api) {
|
|
5
|
+
const ACP = {};
|
|
6
|
+
|
|
7
|
+
function getMultiSelectValues(selector) {
|
|
8
|
+
const el = document.querySelector(selector);
|
|
9
|
+
if (!el) return [];
|
|
10
|
+
return Array.from(el.selectedOptions || []).map(o => o.value);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
ACP.init = function () {
|
|
14
|
+
const $form = $('.discord-onekite-settings');
|
|
15
|
+
if (!$form.length) return;
|
|
16
|
+
|
|
17
|
+
$('#save').off('click.discordOnekite').on('click.discordOnekite', function () {
|
|
18
|
+
const payload = {
|
|
19
|
+
webhookUrl: String($('#webhookUrl').val() || '').trim(),
|
|
20
|
+
notifyReplies: $('#notifyReplies').is(':checked'),
|
|
21
|
+
cids: getMultiSelectValues('#cids'),
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
api.post('/admin/plugins/discord-onekite/save', payload).then(function () {
|
|
25
|
+
if (window.app && typeof app.alertSuccess === 'function') {
|
|
26
|
+
app.alertSuccess('Paramètres enregistrés !');
|
|
27
|
+
}
|
|
28
|
+
}).catch(function (err) {
|
|
29
|
+
// eslint-disable-next-line no-console
|
|
30
|
+
console.error(err);
|
|
31
|
+
if (window.app && typeof app.alertError === 'function') {
|
|
32
|
+
app.alertError('Erreur lors de l’enregistrement');
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
return ACP;
|
|
39
|
+
});
|
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
<div class="acp-page-container">
|
|
2
2
|
<h4>Discord Onekite</h4>
|
|
3
|
-
<p class="text-muted">
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
<div class="alert alert-success" role="alert">Paramètres enregistrés !</div>
|
|
7
|
-
<!-- ENDIF saved -->
|
|
8
|
-
|
|
9
|
-
<form role="form" method="post" action="/admin/plugins/discord-onekite/save">
|
|
10
|
-
<input type="hidden" name="_csrf" value="{config.csrf_token}" />
|
|
3
|
+
<p class="text-muted">
|
|
4
|
+
Notifications Discord via webhook.
|
|
5
|
+
</p>
|
|
11
6
|
|
|
7
|
+
<form role="form" class="discord-onekite-settings">
|
|
12
8
|
<div class="mb-3">
|
|
13
9
|
<label class="form-label" for="webhookUrl">Discord Webhook URL</label>
|
|
14
10
|
<input type="text" class="form-control" id="webhookUrl" name="webhookUrl" value="{settings.webhookUrl}" placeholder="https://discord.com/api/webhooks/..." />
|
|
@@ -33,6 +29,6 @@
|
|
|
33
29
|
</p>
|
|
34
30
|
</div>
|
|
35
31
|
|
|
36
|
-
|
|
32
|
+
<!-- IMPORT admin/partials/save_button.tpl -->
|
|
37
33
|
</form>
|
|
38
34
|
</div>
|