Skip to content

Commit

Permalink
Cleanup, Restructure, First steps adding action buttons
Browse files Browse the repository at this point in the history
  • Loading branch information
sestriel committed Apr 14, 2024
1 parent ae36a8d commit dd4735f
Show file tree
Hide file tree
Showing 4 changed files with 297 additions and 99 deletions.
91 changes: 91 additions & 0 deletions lib/actionButton.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
'use strict';

class ActionButton
{
constructor(action, label, clear = false)
{
this.action = action;
this.label = label;
this.clear = clear;
}

isValidUrl(urlString)
{
const urlPattern = new RegExp('^(https?:\\/\\/)?'+ // validate protocol
'((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|'+ // validate domain name
'((\\d{1,3}\\.){3}\\d{1,3}))'+ // validate OR ip (v4) address
'(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*'+ // validate port and path
'(\\?[;&a-z\\d%_.~+=-]*)?'+ // validate query string
'(\\#[-a-z\\d_]*)?$','i'); // validate fragment locator
return !!urlPattern.test(urlString);
}
}

class ActionButtonView extends ActionButton
{
constructor(label, url, clear = false)
{
super('view', label, clear);

this.url = url;
}

getData()
{
return {
action: this.action,
label: this.label,
url: this.url,
clear: this.clear
};
}
}

class ActionButtonHTTP extends ActionButton
{
constructor(label, url, clear = false)
{
super('http', label, clear);

this.url = url;
this.method = 'POST';
this.headers = {};
this.body = '';
}

getData()
{
return {
action: this.action,
label: this.label,
url: this.url,
method: this.method,
headers: this.headers,
boddy: this.body,
clear: this.clear
};
}

setMethod(method)
{
this.method = method;
}

setHeaders(headers)
{
this.headers = headers;
}

setBody(body)
{
this.body = body;
}
}

class ActionButtonAB extends ActionButton
{

}


module.exports = { ActionButton, ActionButtonView, ActionButtonHTTP, ActionButtonAB };
64 changes: 64 additions & 0 deletions lib/message.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
'use strict';


class Message {
constructor(topic, title, message, priority = 3)
{
this.topic = topic;
this.title = title;
this.message = message;
this.priority = priority;

this.delay = 0;
this.tags = {};
this.clickURL = null;

this.attachment = null;
this.actionBtns = {};
}

addDelay()
{
this.delay = 5;
}

addTag(tag)
{
this.tags[this.tags.length + 1] = tag;
}

addClickURL(url)
{
this.clickURL = url;
}

addAttachment(attachFromURL)
{
if (!this.isValidUrl(attachFromURL)) return;

this.attachment = {
attach: attachFromURL,
filename: attachFromURL.substring(attachFromURL.lastIndexOf('/')+1)
};
}

addActionBtn(actionButton)
{
this.actionBtns[this.actionBtns.length + 1] = actionButton;
}



isValidUrl(urlString)
{
const urlPattern = new RegExp('^(https?:\\/\\/)?'+ // validate protocol
'((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|'+ // validate domain name
'((\\d{1,3}\\.){3}\\d{1,3}))'+ // validate OR ip (v4) address
'(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*'+ // validate port and path
'(\\?[;&a-z\\d%_.~+=-]*)?'+ // validate query string
'(\\#[-a-z\\d_]*)?$','i'); // validate fragment locator
return !!urlPattern.test(urlString);
}
}

module.exports = { Message };
108 changes: 108 additions & 0 deletions lib/topic.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
'use strict';

const axios = require('axios');
const EventSource = require('eventsource');

class Topic {
constructor(adapter, name, subscribed, authType = 0, username = '', password = '', accessToken)
{
this.adapter = adapter;

this.name = name;
this.authType = authType;
this.username = username;
this.password = password;
this.accessToken = accessToken;

this.shouldSubcribe = subscribed;
this.subscribed = false;

this.topicKey = 'subscribedTopics.' + this.name;
this.messageKey = 'subscribedTopics.' + this.name + '.lastMessage.';
this.connected = false;
this.eventSource = null;
}

async subscribe()
{
this.adapter.log.debug('subscribe called ' + this.name);

if (this.shouldSubcribe) {
this.adapter.log.debug('shouldSub ' + this.name);

if (this.eventSource !== null && this.eventSource.readyState !== 2) {
return;
}

await this.createSubsrciptionObjects();

this.eventSource = new EventSource(this.adapter.config.serverURL + '/' + this.name + '/sse', this.getHTTPConfig());
this.eventSource.onmessage = (e) => {
this.adapter.log.debug('Received new message from topic ' + this.name);

const msgData = JSON.parse(e.data);

this.adapter.setState(this.topicKey + '.lastMessageRaw', {ack: true, val: e.data});
this.adapter.setState(this.messageKey + 'id', {ack: true, val: msgData.id});
this.adapter.setState(this.messageKey + 'time', {ack: true, val: msgData.time});
this.adapter.setState(this.messageKey + 'tags', {ack: true, val: msgData.tags});
this.adapter.setState(this.messageKey + 'event', {ack: true, val: msgData.event});
this.adapter.setState(this.messageKey + 'title', {ack: true, val: msgData.title});
this.adapter.setState(this.messageKey + 'value', {ack: true, val: msgData.message});
this.adapter.setState(this.messageKey + 'expires', {ack: true, val: msgData.expires});
this.adapter.setState(this.messageKey + 'priority', {ack: true, val: msgData.priority});
};
this.eventSource.onopen = () => {
if (!this.subscribed) {
this.adapter.log.debug('Subscription to topic ' + this.name + ' established.');
this.adapter.setState(this.topicKey + '.connected', {ack: true, val: true});
this.subscribed = true;
}
};
this.eventSource.onerror = (e) => {
this.adapter.log.error('Subscription to topic ' + this.name + ' cant be established or lost. Status code: ' + e.status + ' Error: ' + e.message);
this.adapter.setState(this.topicKey + '.connected', {ack: true, val: false});
};
}
}

async createSubsrciptionObjects()
{
await this.adapter.setObjectNotExists(this.topicKey, { type: 'channel', common: { name: this.adapter.config.serverURL + '/' + this.name, role: 'subscription' } });
await this.adapter.setObjectNotExists(this.topicKey + '.lastMessageRaw', { type: 'state', common: { name: 'Last message RAW', role: 'message', type: 'string' } });
await this.adapter.setObjectNotExists(this.topicKey + '.connected', { type: 'state', common: { name: 'Subscribtion state', role: 'state', type: 'boolean' } });
await this.adapter.setObjectNotExists(this.messageKey + 'id', { type: 'state', common: { name: 'ID', role: '', type: 'string' } });
await this.adapter.setObjectNotExists(this.messageKey + 'tags', { type: 'state', common: { name: 'Tags', role: '', type: 'string' } });
await this.adapter.setObjectNotExists(this.messageKey + 'time', { type: 'state', common: { name: 'Time', role: '', type: 'number' } });
await this.adapter.setObjectNotExists(this.messageKey + 'event', { type: 'state', common: { name: 'Event type', role: '', type: 'string' } });
await this.adapter.setObjectNotExists(this.messageKey + 'title', { type: 'state', common: { name: 'Title', role: '', type: 'string' } });
await this.adapter.setObjectNotExists(this.messageKey + 'value', { type: 'state', common: { name: 'Content', role: '', type: 'string' } });
await this.adapter.setObjectNotExists(this.messageKey + 'expires', { type: 'state', common: { name: 'Expire', role: '', type: 'number' } });
await this.adapter.setObjectNotExists(this.messageKey + 'priority', { type: 'state', common: { name: 'Priority', role: '', type: 'number' } });
}

async sendMessage(message)
{
const data = message.getData();
axios.post(this.adapter.config.serverURL, data, this.getHTTPConfig())
.catch(err => {
this.adapter.log.error(`Ntfy error: ${err}`);
this.adapter.log.error(`Ntfy error: ${JSON.stringify(err.response.data)}`);
this.adapter.log.error(`Ntfy with config: ${JSON.stringify(this.getHTTPConfig())}`);
});
}

getHTTPConfig()
{
switch(this.authType) {
case 1:
return { headers: { 'Authorization': 'Basic ' + Buffer.from(this.username + ':' + this.password).toString('base64') } };
case 2:
return { headers: { 'Authorization': 'Bearer ' + this.accessToken } };
default:
return {};
}
}
}

module.exports = { Topic };
Loading

0 comments on commit dd4735f

Please sign in to comment.