nodebb-plugin-discord-onekite 1.0.13 → 1.0.15
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 +6 -6
- package/lib/controllers.js +0 -1
- package/library.js +56 -16
- package/package.json +1 -1
- package/static/lib/admin.js +28 -17
- package/templates/admin/plugins/discord-onekite.tpl +2 -10
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
# nodebb-plugin-discord-onekite v1.1.
|
|
1
|
+
# nodebb-plugin-discord-onekite v1.1.4
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
-
|
|
5
|
-
|
|
3
|
+
Discord:
|
|
4
|
+
- Always sends the forum topic link (example: https://www.onekite.com/topic/16024/test-discord)
|
|
5
|
+
- First line is the raw URL, then a Markdown clickable link `[title](url)`
|
|
6
|
+
- Still sends an embed (title clickable via embed.url) + fallback to plain content if embeds are rejected
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
- Discord notifications: minimal embed + fallback to plain content if Discord rejects embed.
|
|
8
|
+
ACP remains with the "disk" save button and NodeBB toast.
|
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
|
@@ -7,6 +7,7 @@ const meta = require.main.require('./src/meta');
|
|
|
7
7
|
const middleware = require.main.require('./src/middleware');
|
|
8
8
|
|
|
9
9
|
const topics = require.main.require('./src/topics');
|
|
10
|
+
const posts = require.main.require('./src/posts');
|
|
10
11
|
const user = require.main.require('./src/user');
|
|
11
12
|
|
|
12
13
|
const controllers = require('./lib/controllers');
|
|
@@ -46,6 +47,27 @@ function isValidHttpUrl(s) {
|
|
|
46
47
|
}
|
|
47
48
|
}
|
|
48
49
|
|
|
50
|
+
function stripHtml(html) {
|
|
51
|
+
if (!html) return '';
|
|
52
|
+
return String(html)
|
|
53
|
+
.replace(/<br\s*\/?>/gi, '\n')
|
|
54
|
+
.replace(/<\/p>/gi, '\n')
|
|
55
|
+
.replace(/<[^>]*>/g, '')
|
|
56
|
+
.replace(/ /g, ' ')
|
|
57
|
+
.replace(/&/g, '&')
|
|
58
|
+
.replace(/</g, '<')
|
|
59
|
+
.replace(/>/g, '>')
|
|
60
|
+
.replace(/'/g, "'")
|
|
61
|
+
.replace(/"/g, '"')
|
|
62
|
+
.trim();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function truncate(s, n) {
|
|
66
|
+
const str = String(s || '');
|
|
67
|
+
if (str.length <= n) return str;
|
|
68
|
+
return str.slice(0, n - 1) + '…';
|
|
69
|
+
}
|
|
70
|
+
|
|
49
71
|
async function getSettings() {
|
|
50
72
|
const s = await meta.settings.get(SETTINGS_KEY);
|
|
51
73
|
return {
|
|
@@ -88,40 +110,56 @@ async function sendDiscord(webhookUrl, payload, fallbackContent) {
|
|
|
88
110
|
await postToDiscord(webhookUrl, { content: fallbackContent });
|
|
89
111
|
return;
|
|
90
112
|
} catch (e2) {
|
|
91
|
-
// eslint-disable-next-line no-console
|
|
92
113
|
console.error(e2);
|
|
93
114
|
}
|
|
94
115
|
}
|
|
95
|
-
// eslint-disable-next-line no-console
|
|
96
116
|
console.error(e);
|
|
97
117
|
}
|
|
98
118
|
}
|
|
99
119
|
|
|
120
|
+
async function getPostExcerpt(pid) {
|
|
121
|
+
if (!pid) return '';
|
|
122
|
+
try {
|
|
123
|
+
const p = await posts.getPostFields(pid, ['content']);
|
|
124
|
+
const text = stripHtml(p && p.content);
|
|
125
|
+
return truncate(text, 500);
|
|
126
|
+
} catch {
|
|
127
|
+
return '';
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
100
131
|
async function buildPayload({ tid, pid, isReply }) {
|
|
101
132
|
const baseUrl = normalizeBaseUrl(meta.config.url);
|
|
102
133
|
|
|
103
134
|
const topicData = await topics.getTopicFields(tid, ['tid', 'uid', 'cid', 'title', 'slug', 'mainPid']);
|
|
104
135
|
if (!topicData) return null;
|
|
105
136
|
|
|
137
|
+
// Always build the forum link like the example: https://www.onekite.com/topic/<tid or slug>
|
|
106
138
|
const topicUrl = ensureAbsoluteUrl(baseUrl, `/topic/${topicData.slug || topicData.tid}`);
|
|
107
|
-
|
|
139
|
+
|
|
140
|
+
// For replies, link directly to the post if possible
|
|
141
|
+
const postUrl = (isReply && pid) ? `${topicUrl}/${pid}` : topicUrl;
|
|
108
142
|
|
|
109
143
|
const u = await user.getUserFields(topicData.uid, ['username']);
|
|
110
144
|
const title = (topicData.title || (isReply ? 'Nouvelle réponse' : 'Nouveau sujet')).toString().slice(0, 256);
|
|
111
145
|
const authorName = (u && u.username ? String(u.username) : 'Utilisateur').slice(0, 256);
|
|
112
146
|
|
|
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
|
|
113
151
|
const embed = {
|
|
114
152
|
title,
|
|
115
|
-
description
|
|
153
|
+
description,
|
|
116
154
|
};
|
|
155
|
+
if (isValidHttpUrl(postUrl)) embed.url = postUrl;
|
|
117
156
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
: `🆕 Nouveau sujet : ${title}\n${url}`;
|
|
157
|
+
// Ensure the message is clickable: markdown link + also raw URL in first line
|
|
158
|
+
const content = [
|
|
159
|
+
postUrl, // raw URL first line
|
|
160
|
+
isReply ? `🗨️ Nouvelle réponse : [${title}](${postUrl})` : `🆕 Nouveau sujet : [${title}](${topicUrl})`,
|
|
161
|
+
description ? description : '',
|
|
162
|
+
].filter(Boolean).join('\n');
|
|
125
163
|
|
|
126
164
|
return { topicData, embed, content };
|
|
127
165
|
}
|
|
@@ -142,7 +180,8 @@ Plugin.init = async ({ router }) => {
|
|
|
142
180
|
controllers.renderAdminPage
|
|
143
181
|
);
|
|
144
182
|
|
|
145
|
-
|
|
183
|
+
// AJAX save endpoint for ACP (used by admin.js)
|
|
184
|
+
router.post('/api/admin/plugins/discord-onekite/save',
|
|
146
185
|
middleware.admin.checkPrivileges,
|
|
147
186
|
async (req, res) => {
|
|
148
187
|
try {
|
|
@@ -152,11 +191,11 @@ Plugin.init = async ({ router }) => {
|
|
|
152
191
|
cids: req.body.cids || '',
|
|
153
192
|
};
|
|
154
193
|
await meta.settings.set(SETTINGS_KEY, payload);
|
|
194
|
+
res.json({ ok: true });
|
|
155
195
|
} catch (e) {
|
|
156
|
-
// eslint-disable-next-line no-console
|
|
157
196
|
console.error('[discord-onekite] save failed', e);
|
|
197
|
+
res.status(500).json({ ok: false });
|
|
158
198
|
}
|
|
159
|
-
res.redirect('/admin/plugins/discord-onekite?saved=1');
|
|
160
199
|
}
|
|
161
200
|
);
|
|
162
201
|
};
|
|
@@ -182,7 +221,8 @@ Plugin.onTopicPost = async (data) => {
|
|
|
182
221
|
|
|
183
222
|
if (!cidAllowed(built.topicData.cid, settings.cids)) return;
|
|
184
223
|
|
|
185
|
-
|
|
224
|
+
// Send content + embed (content ensures clickable link always)
|
|
225
|
+
await sendDiscord(settings.webhookUrl, { content: built.content, embeds: [built.embed] }, built.content);
|
|
186
226
|
};
|
|
187
227
|
|
|
188
228
|
Plugin.onTopicReply = async (data) => {
|
|
@@ -200,7 +240,7 @@ Plugin.onTopicReply = async (data) => {
|
|
|
200
240
|
|
|
201
241
|
if (!cidAllowed(built.topicData.cid, settings.cids)) return;
|
|
202
242
|
|
|
203
|
-
await sendDiscord(settings.webhookUrl, { embeds: [built.embed] }, built.content);
|
|
243
|
+
await sendDiscord(settings.webhookUrl, { content: built.content, embeds: [built.embed] }, built.content);
|
|
204
244
|
};
|
|
205
245
|
|
|
206
246
|
module.exports = Plugin;
|
package/package.json
CHANGED
package/static/lib/admin.js
CHANGED
|
@@ -1,27 +1,38 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
-
/* global app */
|
|
2
|
+
/* global $, app */
|
|
3
3
|
|
|
4
|
-
define('admin/plugins/discord-onekite', [], function () {
|
|
4
|
+
define('admin/plugins/discord-onekite', ['api'], function (api) {
|
|
5
5
|
const ACP = {};
|
|
6
6
|
|
|
7
|
-
function
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
if (saved === '1' || saved === 'true') {
|
|
12
|
-
if (window.app && typeof app.alertSuccess === 'function') {
|
|
13
|
-
app.alertSuccess('Paramètres enregistrés !');
|
|
14
|
-
}
|
|
15
|
-
url.searchParams.delete('saved');
|
|
16
|
-
window.history.replaceState({}, document.title, url.toString());
|
|
17
|
-
}
|
|
18
|
-
} catch (e) {
|
|
19
|
-
// ignore
|
|
20
|
-
}
|
|
7
|
+
function getMultiSelectValues(selector) {
|
|
8
|
+
const el = document.querySelector(selector);
|
|
9
|
+
if (!el) return [];
|
|
10
|
+
return Array.from(el.selectedOptions || []).map(o => o.value);
|
|
21
11
|
}
|
|
22
12
|
|
|
23
13
|
ACP.init = function () {
|
|
24
|
-
|
|
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
|
+
});
|
|
25
36
|
};
|
|
26
37
|
|
|
27
38
|
return ACP;
|
|
@@ -4,9 +4,7 @@
|
|
|
4
4
|
Notifications Discord via webhook.
|
|
5
5
|
</p>
|
|
6
6
|
|
|
7
|
-
<form role="form"
|
|
8
|
-
<input type="hidden" name="_csrf" value="{config.csrf_token}" />
|
|
9
|
-
|
|
7
|
+
<form role="form" class="discord-onekite-settings">
|
|
10
8
|
<div class="mb-3">
|
|
11
9
|
<label class="form-label" for="webhookUrl">Discord Webhook URL</label>
|
|
12
10
|
<input type="text" class="form-control" id="webhookUrl" name="webhookUrl" value="{settings.webhookUrl}" placeholder="https://discord.com/api/webhooks/..." />
|
|
@@ -31,12 +29,6 @@
|
|
|
31
29
|
</p>
|
|
32
30
|
</div>
|
|
33
31
|
|
|
34
|
-
|
|
32
|
+
<!-- IMPORT admin/partials/save_button.tpl -->
|
|
35
33
|
</form>
|
|
36
|
-
|
|
37
|
-
<noscript>
|
|
38
|
-
<!-- IF saved -->
|
|
39
|
-
<div class="alert alert-success mt-3" role="alert">Paramètres enregistrés !</div>
|
|
40
|
-
<!-- ENDIF saved -->
|
|
41
|
-
</noscript>
|
|
42
34
|
</div>
|