nodebb-plugin-discord-onekite 1.0.5 → 1.0.7
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 +4 -6
- package/lib/controllers.js +40 -1
- package/library.js +19 -0
- package/package.json +9 -3
- package/plugin.json +19 -8
- package/templates/admin/plugins/discord-onekite.tpl +12 -6
- package/static/lib/admin.js +0 -67
package/README.md
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
# nodebb-plugin-discord-onekite
|
|
1
|
+
# nodebb-plugin-discord-onekite — v1.0.6
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
-
|
|
6
|
-
- ACP script is loaded via `acpScripts` and runs globally on `action:ajaxify.end`
|
|
7
|
-
- No reliance on `modules` name matching
|
|
3
|
+
Fixes:
|
|
4
|
+
- Form action/redirect no longer uses `{config.relative_path}` (was rendering as `undefined` on your install).
|
|
5
|
+
- Categories are fetched server-side via `categories.getCategoriesByPrivilege('categories:cid', uid, 'read', ...)` (returns category objects). citeturn0search10
|
package/lib/controllers.js
CHANGED
|
@@ -1,9 +1,48 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const meta = require.main.require('./src/meta');
|
|
4
|
+
const categories = require.main.require('./src/categories');
|
|
5
|
+
|
|
6
|
+
const SETTINGS_KEY = 'discord-onekite';
|
|
7
|
+
|
|
8
|
+
function normalizeCids(v) {
|
|
9
|
+
if (!v) return [];
|
|
10
|
+
if (Array.isArray(v)) return v.map(String).filter(Boolean);
|
|
11
|
+
if (typeof v === 'string') return v.split(',').map(s => s.trim()).filter(Boolean);
|
|
12
|
+
return [];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async function getReadableCategories(uid) {
|
|
16
|
+
// NodeBB core uses this helper to get categories for a user by privilege.
|
|
17
|
+
// It returns an array of category objects (cid, name, slug, ...). citeturn0search10
|
|
18
|
+
return await new Promise((resolve) => {
|
|
19
|
+
categories.getCategoriesByPrivilege('categories:cid', uid || 0, 'read', (err, categoriesData) => {
|
|
20
|
+
if (err || !Array.isArray(categoriesData)) return resolve([]);
|
|
21
|
+
resolve(categoriesData.filter(Boolean));
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
3
26
|
const controllers = {};
|
|
4
27
|
|
|
5
28
|
controllers.renderAdminPage = async function (req, res) {
|
|
6
|
-
|
|
29
|
+
const settings = await meta.settings.get(SETTINGS_KEY);
|
|
30
|
+
const savedCids = normalizeCids(settings && settings.cids);
|
|
31
|
+
|
|
32
|
+
const cats = await getReadableCategories(req.uid);
|
|
33
|
+
const categoriesForTpl = (cats || [])
|
|
34
|
+
.filter(c => c && typeof c.cid !== 'undefined' && c.name)
|
|
35
|
+
.map(c => ({
|
|
36
|
+
cid: String(c.cid),
|
|
37
|
+
name: c.name,
|
|
38
|
+
selected: savedCids.includes(String(c.cid)),
|
|
39
|
+
}))
|
|
40
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
41
|
+
|
|
42
|
+
res.render('admin/plugins/discord-onekite', {
|
|
43
|
+
settings: settings || {},
|
|
44
|
+
categories: categoriesForTpl,
|
|
45
|
+
});
|
|
7
46
|
};
|
|
8
47
|
|
|
9
48
|
module.exports = controllers;
|
package/library.js
CHANGED
|
@@ -4,6 +4,7 @@ const { request } = require('undici');
|
|
|
4
4
|
|
|
5
5
|
const routeHelpers = require.main.require('./src/routes/helpers');
|
|
6
6
|
const meta = require.main.require('./src/meta');
|
|
7
|
+
const middleware = require.main.require('./src/middleware');
|
|
7
8
|
|
|
8
9
|
const topics = require.main.require('./src/topics');
|
|
9
10
|
const user = require.main.require('./src/user');
|
|
@@ -80,6 +81,24 @@ Plugin.init = async ({ router }) => {
|
|
|
80
81
|
[],
|
|
81
82
|
controllers.renderAdminPage
|
|
82
83
|
);
|
|
84
|
+
|
|
85
|
+
// POST save - keep it very simple and redirect to the canonical path
|
|
86
|
+
router.post('/admin/plugins/discord-onekite/save',
|
|
87
|
+
middleware.admin.checkPrivileges,
|
|
88
|
+
async (req, res) => {
|
|
89
|
+
try {
|
|
90
|
+
const payload = {
|
|
91
|
+
webhookUrl: req.body.webhookUrl || '',
|
|
92
|
+
notifyReplies: req.body.notifyReplies ? 'on' : '',
|
|
93
|
+
cids: req.body.cids || '',
|
|
94
|
+
};
|
|
95
|
+
await meta.settings.set(SETTINGS_KEY, payload);
|
|
96
|
+
} catch (e) {
|
|
97
|
+
console.error('[discord-onekite] save failed', e);
|
|
98
|
+
}
|
|
99
|
+
res.redirect('/admin/plugins/discord-onekite');
|
|
100
|
+
}
|
|
101
|
+
);
|
|
83
102
|
};
|
|
84
103
|
|
|
85
104
|
Plugin.addAdminNavigation = (header) => {
|
package/package.json
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nodebb-plugin-discord-onekite",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.7",
|
|
4
4
|
"description": "Discord webhook notifier for Onekite (NodeBB v4.x only)",
|
|
5
5
|
"main": "library.js",
|
|
6
6
|
"license": "MIT",
|
|
7
|
-
"keywords": [
|
|
7
|
+
"keywords": [
|
|
8
|
+
"nodebb",
|
|
9
|
+
"plugin",
|
|
10
|
+
"discord",
|
|
11
|
+
"webhook",
|
|
12
|
+
"notifications"
|
|
13
|
+
],
|
|
8
14
|
"dependencies": {
|
|
9
15
|
"undici": "^6.0.0"
|
|
10
16
|
},
|
|
11
17
|
"nbbpm": {
|
|
12
18
|
"compatibility": "^4.0.0"
|
|
13
19
|
}
|
|
14
|
-
}
|
|
20
|
+
}
|
package/plugin.json
CHANGED
|
@@ -1,14 +1,25 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "nodebb-plugin-discord-onekite",
|
|
3
3
|
"name": "Discord Onekite Notifier",
|
|
4
|
-
"description": "Notifie Discord via webhook pour nouveaux sujets et/ou
|
|
4
|
+
"description": "Notifie Discord via webhook pour nouveaux sujets et/ou r\u00e9ponses, filtrable par cat\u00e9gories (NodeBB v4.x uniquement).",
|
|
5
5
|
"library": "./library.js",
|
|
6
6
|
"hooks": [
|
|
7
|
-
{
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
{
|
|
8
|
+
"hook": "static:app.load",
|
|
9
|
+
"method": "init"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"hook": "filter:admin.header.build",
|
|
13
|
+
"method": "addAdminNavigation"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"hook": "action:topic.save",
|
|
17
|
+
"method": "onTopicSave"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"hook": "action:post.save",
|
|
21
|
+
"method": "onPostSave"
|
|
22
|
+
}
|
|
11
23
|
],
|
|
12
|
-
"templates": "templates"
|
|
13
|
-
|
|
14
|
-
}
|
|
24
|
+
"templates": "templates"
|
|
25
|
+
}
|
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
<div class="acp-page-container">
|
|
2
2
|
<h4>Discord Onekite</h4>
|
|
3
3
|
<p class="text-muted">
|
|
4
|
-
Notifications Discord via webhook.
|
|
4
|
+
Notifications Discord via webhook (page ACP en rendu serveur).
|
|
5
5
|
</p>
|
|
6
6
|
|
|
7
|
-
<form role="form"
|
|
7
|
+
<form role="form" method="post" action="/admin/plugins/discord-onekite/save">
|
|
8
|
+
<input type="hidden" name="_csrf" value="{config.csrf_token}" />
|
|
9
|
+
|
|
8
10
|
<div class="mb-3">
|
|
9
11
|
<label class="form-label" for="webhookUrl">Discord Webhook URL</label>
|
|
10
|
-
<input type="text" class="form-control" id="webhookUrl" name="webhookUrl" placeholder="https://discord.com/api/webhooks/..." />
|
|
12
|
+
<input type="text" class="form-control" id="webhookUrl" name="webhookUrl" value="{settings.webhookUrl}" placeholder="https://discord.com/api/webhooks/..." />
|
|
11
13
|
</div>
|
|
12
14
|
|
|
13
15
|
<div class="form-check mb-3">
|
|
14
|
-
<input class="form-check-input" type="checkbox" id="notifyReplies" name="notifyReplies"
|
|
16
|
+
<input class="form-check-input" type="checkbox" id="notifyReplies" name="notifyReplies" <!-- IF settings.notifyReplies -->checked<!-- ENDIF settings.notifyReplies -->>
|
|
15
17
|
<label class="form-check-label" for="notifyReplies">
|
|
16
18
|
Notifier aussi les réponses (si décoché : uniquement les nouveaux sujets)
|
|
17
19
|
</label>
|
|
@@ -19,12 +21,16 @@
|
|
|
19
21
|
|
|
20
22
|
<div class="mb-3">
|
|
21
23
|
<label class="form-label" for="cids">Catégories à notifier</label>
|
|
22
|
-
<select class="form-select" id="cids" name="cids" multiple size="12"
|
|
24
|
+
<select class="form-select" id="cids" name="cids" multiple size="12">
|
|
25
|
+
<!-- BEGIN categories -->
|
|
26
|
+
<option value="{categories.cid}" <!-- IF categories.selected -->selected<!-- ENDIF categories.selected -->>{categories.name}</option>
|
|
27
|
+
<!-- END categories -->
|
|
28
|
+
</select>
|
|
23
29
|
<p class="form-text text-muted">
|
|
24
30
|
Si aucune catégorie n’est sélectionnée : <strong>toutes les catégories</strong> seront notifiées.
|
|
25
31
|
</p>
|
|
26
32
|
</div>
|
|
27
33
|
|
|
28
|
-
|
|
34
|
+
<button type="submit" class="btn btn-primary">Enregistrer</button>
|
|
29
35
|
</form>
|
|
30
36
|
</div>
|
package/static/lib/admin.js
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
/* global $, app, ajaxify */
|
|
3
|
-
|
|
4
|
-
(function () {
|
|
5
|
-
function initOnekiteACP() {
|
|
6
|
-
if (!ajaxify || !ajaxify.data || ajaxify.data.template !== 'admin/plugins/discord-onekite') {
|
|
7
|
-
return;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
// Use NodeBB's AMD loader to access core helpers
|
|
11
|
-
require(['settings', 'api'], function (settings, api) {
|
|
12
|
-
const $form = $('.discord-onekite-settings');
|
|
13
|
-
if (!$form.length) return;
|
|
14
|
-
|
|
15
|
-
// Avoid double-binding
|
|
16
|
-
if ($form.data('onekite-initialized')) return;
|
|
17
|
-
$form.data('onekite-initialized', true);
|
|
18
|
-
|
|
19
|
-
function normalizeCids(v) {
|
|
20
|
-
if (!v) return [];
|
|
21
|
-
if (Array.isArray(v)) return v.map(String).filter(Boolean);
|
|
22
|
-
if (typeof v === 'string') return v.split(',').map(s => s.trim()).filter(Boolean);
|
|
23
|
-
return [];
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// Load saved settings into the form
|
|
27
|
-
settings.load('discord-onekite', $form);
|
|
28
|
-
|
|
29
|
-
// Get raw settings so we can pre-select categories
|
|
30
|
-
settings.get('discord-onekite', function (saved) {
|
|
31
|
-
saved = saved || {};
|
|
32
|
-
const savedCids = normalizeCids(saved.cids);
|
|
33
|
-
|
|
34
|
-
// Load categories (admin should have access)
|
|
35
|
-
api.get('/categories').then(function (res) {
|
|
36
|
-
const categories = (res && res.categories) ? res.categories : [];
|
|
37
|
-
const $select = $('#cids');
|
|
38
|
-
$select.empty();
|
|
39
|
-
|
|
40
|
-
categories
|
|
41
|
-
.filter(c => c && typeof c.cid !== 'undefined' && c.name)
|
|
42
|
-
.forEach(c => {
|
|
43
|
-
const cid = String(c.cid);
|
|
44
|
-
const $opt = $('<option/>').val(cid).text(c.name);
|
|
45
|
-
if (savedCids.includes(cid)) $opt.prop('selected', true);
|
|
46
|
-
$opt.appendTo($select);
|
|
47
|
-
});
|
|
48
|
-
}).catch(function (e) {
|
|
49
|
-
// eslint-disable-next-line no-console
|
|
50
|
-
console.error('[discord-onekite] GET /api/categories failed', e);
|
|
51
|
-
if (app && app.alertError) app.alertError('Impossible de charger les catégories (GET /api/categories).');
|
|
52
|
-
});
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
// Save
|
|
56
|
-
$('#save').off('click.discordOnekite').on('click.discordOnekite', function () {
|
|
57
|
-
settings.save('discord-onekite', $form, function () {
|
|
58
|
-
if (app && app.alertSuccess) app.alertSuccess('Paramètres enregistrés !');
|
|
59
|
-
});
|
|
60
|
-
});
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// Run on initial load and after ajax navigation in ACP
|
|
65
|
-
$(window).on('action:ajaxify.end', initOnekiteACP);
|
|
66
|
-
$(initOnekiteACP);
|
|
67
|
-
})();
|