UNPKG

iobroker.synochat

Version:

This adapter provides an interface of Synology Chat and ioBroker.

209 lines (182 loc) 6.52 kB
const https = require("https"); const axios = require("axios"); const synoChatRequestHelper = require("./synoChatRequestHelper.js"); const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); class SynoChatRequests { constructor(adapterInstance, synoBaseUrl, certCheck) { this.adapterInstance = adapterInstance; this.synoBaseUrl = synoBaseUrl; this.certCheck = certCheck; } async sendBaseRequest(requestProperties, checkCert = null) { if (checkCert == null) { checkCert = this.certCheck; } let request = null; if (checkCert) { request = axios.create(); } else { request = axios.create({ httpsAgent: new https.Agent({ rejectUnauthorized: false, }), timeout: 15000, }); } let requestResponse = null; await request(requestProperties).then((res) => { if (res.status == 200) { this.adapterInstance.log.debug( `${requestProperties["url"]}: ${res.status} ${res.statusText}\n'${JSON.stringify(res.data)}'`, ); requestResponse = res; } else { this.adapterInstance.log.error( `Unable to get valid response from Synology Chat REST API ${requestProperties["url"]} > ${JSON.stringify(res.statusText)} ${JSON.stringify(res.status)}\n'${JSON.stringify(res.data)}'`, ); } }); return requestResponse; } async initialConnectivityCheck() { this.adapterInstance.log.info( `Checking general availability of the Synology Chat REST API...`, ); let synoChatEndpointUrl = `${this.synoBaseUrl}/webapi/entry.cgi`; this.adapterInstance.log.debug( `Preparing REST API call for endpoint '${synoChatEndpointUrl}'...`, ); let requestProperties = { method: "get", url: synoChatEndpointUrl, params: { api: "SYNO.Chat.External", method: "kuchen", version: "2", }, }; try { let response = await this.sendBaseRequest(requestProperties); if ( JSON.parse(JSON.stringify(response.data))["success"] == false && JSON.parse(JSON.stringify(response.data))["error"]["code"] == 103 ) { this.adapterInstance.log.info( `Initial connectivity check of Synology Chat REST API successfully passed!`, ); return true; } this.adapterInstance.log.error( `Unable to get valid response of Synology Chat REST API ${synoChatEndpointUrl} '${JSON.stringify(response.data)}'`, ); } catch (err) { this.adapterInstance.log.error( `Unable to send message to Synology Chat REST API ${synoChatEndpointUrl}\n'${err}'`, ); } this.adapterInstance.log.error(`Adapter instance not in a usable state!`); return false; } async sendMessage( channelToken, synoSendMethod, channelContentCertCheck, message, msgUuid, callIteration = 1, ) { let synoChatEndpointUrl = `${this.synoBaseUrl}/webapi/entry.cgi`; this.adapterInstance.log.debug( `Preparing REST API call for message with ID '${msgUuid}' to endpoint '${synoChatEndpointUrl}'...`, ); let data = await this.preparePayloadData(channelContentCertCheck, message); let requestProperties = { method: "post", url: synoChatEndpointUrl, data: data, params: { api: "SYNO.Chat.External", method: synoSendMethod, version: "2", token: channelToken, }, }; try { let response = await this.sendBaseRequest(requestProperties); if (JSON.parse(JSON.stringify(response.data))["success"] == true) { this.adapterInstance.log.debug( `Successfully sent message '${msgUuid}' to ${synoChatEndpointUrl}.`, ); return true; } // Adding retry operation in case multiple messages were send in a too short time if (JSON.parse(JSON.stringify(response.data))["error"]["code"] == "411") { this.adapterInstance.log.warn( `Attempt to send message '${msgUuid}' failed due to too fast sending sequence: '${JSON.parse(JSON.stringify(response.data))["error"]["errors"]}' > Delaying the message for ${callIteration} second/s...`, ); if (callIteration >= 30) { this.adapterInstance.log.error( `The limit for attempting to send a message has been reached. Message '${msgUuid}' will be discarded!`, ); return false; } // Math.floor(Math.random() * (max - min + 1) + min) await sleep( Math.floor(Math.random() * (1450 - 890 + 1) + 890) * callIteration, ); this.adapterInstance.log.debug( `Start a new attempt to send the message '${msgUuid}'...`, ); return await this.sendMessage( channelToken, synoSendMethod, channelContentCertCheck, message, msgUuid, callIteration + 1, ); } } catch (err) { this.adapterInstance.log.error( `Unable to send message '${msgUuid}' to Synology Chat REST API ${synoChatEndpointUrl}\n'${err}'`, ); return false; } this.adapterInstance.log.error( `Unable to send message '${msgUuid}' to Synology Chat REST API ${synoChatEndpointUrl}!`, ); return false; } async preparePayloadData(channelContentCertCheck, message) { if (synoChatRequestHelper.isValidHttpUrl(this.adapterInstance, message)) { this.adapterInstance.log.debug( `Message seems to be an URL. Checking content...`, ); try { let requestProperties = { method: "get", url: message, }; const res = await this.sendBaseRequest( requestProperties, channelContentCertCheck, ); // See content types https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types if (res.headers["content-type"].includes("image/")) { return `payload={"file_url": "${message}"}`; } this.adapterInstance.log.debug( `Content type is not an image! > Skipping file send preparation.`, ); } catch { this.adapterInstance.log.debug( `Unable check content type of URL. > Skipping file send preparation.`, ); } } return `payload={"text": "${synoChatRequestHelper.prepareTextForSendingMessage(this.adapterInstance, message)}"}`; } } module.exports = { SynoChatRequests, };