Newsletter does this. It's not too complicated really. You have to add the html block to the custom settings page filter, add a default value to the user settings object, and save the data back to the user's db object.
https://github.com/yariplus/nodebb-plugin-newsletter/blob/master/plugin.json#L33-L35
https://github.com/yariplus/nodebb-plugin-newsletter/blob/master/library.js#L213-L239
Newsletter.filterUserCustomSettings = function (data, next) {
// data = {settings: {}, customSettings: [], uid: req.uid}
// data.settings is the user's db object. I check if my custom setting is defined and parse it into a boolean. (When it's saved to the database, it is a '1' or '0' for checkboxes.)
data.settings.pluginNewsletterSub = data.settings.pluginNewsletterSub !== void 0 ? parseInt(data.settings.pluginNewsletterSub, 10) === 1 : true;
// This adds an HTML block to the user's settings page.
// 'data-property' is what it will be saved as when sent to 'actionSaveSettings' below.
data.customSettings.push({
title: "[[newsletter:sub-setting]]",
content: '\
<div class="checkbox">\
<label>\
<input type="checkbox" data-property="pluginNewsletterSub"' + (data.settings.pluginNewsletterSub ? ' checked' : '') + '> <strong>[[newsletter:sub]]</strong>\
</label>\
<a name="newsletter"></a>\
</div>'
});
next(null, data);
};
// Here I just make sure the setting is always defined when I get a user's settings.
Newsletter.filterUserGetSettings = function (data, next) {
if (data.settings.pluginNewsletterSub === void 0) data.settings.pluginNewsletterSub = '1';
next(null, data);
};
// Here, I am saving the custom settings to the user's setting object. data.settings is populated with my custom settings from the 'data-property' above.
// Here is also where I could check if the setting has changed, and take appropriate action.
Newsletter.actionSaveSettings = function (data, next) {
db.setObjectField('user:' + data.uid + ':settings', 'pluginNewsletterSub', data.settings.pluginNewsletterSub);
};