import joplin from 'api'; import { SettingItemType, ModelType, ToastType } from 'api/types'; enum ItemChangeEventType { create = 1, update = 2, delete = 3 } const settingObjs = { 'HooksEnabled': { value: false, type: SettingItemType.Bool, section: 'WebAngling', public: true, label: 'Enable Webhooks. Must also enable Note Changes or Resource Changes.' }, "URL": { value: null, type: SettingItemType.String, section: 'WebAngling', public: true, label: 'Webhook URL.' }, "Method": { value: false, type: SettingItemType.String, section: 'WebAngling', public: true, label: 'HTTP Method to use sending webhook (e.g., POST, GET).', isEnum: true, options: { 'POST': 'POST', 'GET': 'GET', }, }, "NoteChangesEnabled": { value: false, type: SettingItemType.Bool, section: 'WebAngling', public: true, label: 'Send Webhooks on note changes.' }, "ResourceChangesEnabled": { value: false, type: SettingItemType.Bool, section: 'WebAngling', public: true, label: 'Send Webhooks on resource changes.' }, "IncludeTitle": { value: true, type: SettingItemType.Bool, section: 'WebAngling', public: true, label: 'Include Note/Resource title in webhook payload.' } } async function registerSettings(){ await joplin.settings.registerSection('WebAngling', { label: 'Web Angling', iconName: 'fas fa-fish', }); await joplin.settings.registerSettings(settingObjs) } async function onEvent( event_type: string, id: string, change_type="Update", ){ const anglerSettings = await joplin.settings.values(Object.keys(settingObjs)); if(!anglerSettings["HooksEnabled"]){ return } const obj_type = ModelType[await joplin.data.itemType(id)] let title = undefined; if(obj_type == "Note"){ if(!anglerSettings["NoteChangesEnabled"]){ return } if(anglerSettings["IncludeTitle"]){ const note = await joplin.data.get(['notes', id], { fields: ['title'] }); title = note.title } } else { if(!anglerSettings["ResourceChangesEnabled"]){ return } } const payload = { object_type: obj_type, object_id: id, change_type: change_type, title: title } try { await doFetch( anglerSettings["URL"], payload, anglerSettings["Method"] as string, {}, "application/json", ) } catch (error) { await joplin.views.dialogs.showToast( { message: `Webhook error ${error.message}`, type: ToastType.Error, duration: 5000, } ); } } async function doFetch(url, payload, method = 'POST', headers = {}, contentType = 'application/json') { const finalHeaders = { 'Content-Type': contentType, ...headers, }; try { const response = await fetch(url, { method: method, headers: finalHeaders, body: contentType === 'application/json' ? JSON.stringify(payload) : payload, }); if (!response.ok) { throw new Error(`Webhook request failed with status ${response.status}`); } return response; } catch (error) { console.error("Webhook request error:", error); throw error; } } joplin.plugins.register({ onStart: async function() { await registerSettings() await joplin.workspace.onNoteChange(async (e) => { await onEvent("note", e.id, ItemChangeEventType[e.event]) }); await joplin.workspace.onResourceChange(async (e) => { await onEvent("resource", e.id, "update") }); }, });