Source: plugins/outbound/alarmiator/base.js

const EventEmitter = require('events');

const admin = require("firebase-admin");
let serviceAccount = require("./alarmiator-oauth.json");
const FCMToken = require('../../../models/fctmtokens');

/**
 * 
 * @class Sending new alarms to mobile apps via Firebase Cloud Messaging
 * ALARMiator Zusatzalarmierung
 * 
 * @version: 1.0.1
 * @author:  Stefan Bauer 2020
 */

class alarmiatormobile extends EventEmitter {
    /**
     *
     * @constructor
     */
    constructor() {
        super();
        global.plgManager.logger.info('-------- PLUGIN ALARMIATOR MOBILE -----------');
        //this.plgManager = global.plgManager;
        this.registerForEvents();
        this.serviceProcId = null;
        this.loadConfig((success) => {
            
        });    
        
        this.alarmiator = null;
        this.alarmInfo = null;
        this.fcmtokens = new FCMToken(global.plgManager.logger);
    }

    /**
     * Registration of Events at the plugin manager (hook in to events provided by the plugin manager)
     */
    registerForEvents() {
        global.plgManager.logger.info('PLUGIN | ALARMIATORMOBILE | Registering events')

        global.plgManager.on('event_pluginConfig_refreshed', (namespace) => {
            if (namespace === 'alarmiatormobile') {
                // plugins config has changed --> refresh
                this.loadConfig((success) => {
                    if (success) {
                        global.plgManager.logger.info('PLUGIN | ALARMIATORMOBILE | successfully reloaded plugin configuration');
                    } else {
                        global.plgManager.logger.error('PLUGIN | ALARMIATORMOBILE | could not reload plugin config!');
                    }
                });
            }
        })

        global.plgManager.on('event_new_alarm', (alarmInfo) => {
            this.alarmInfo = alarmInfo;
            this.setupInstance();
            this.sendMessage(alarmInfo);
        })

        global.plgManager.on('event_new_test_alarm', (baseDataId, pluginNamespace, alarmInfo) => {
            if (pluginNamespace === "alarmiatormobile"){
                global.plgManager.logger.info('PLUGIN | ALARMIATORMOBILE | EVENT event_new_test_alarm recieved -> processing now');
                this.alarmInfo = alarmInfo;
                this.setupInstance();
                this.sendTestAlarm(baseDataId, alarmInfo);
            }
        })
    }


    /**
     * Plugin-specific Getters and Setters
     *
    */

    set alarmiatorActive(alarmiatorActive) {
        this._alarmiatorActive = alarmiatorActive;
    }
    get alarmiatorActive() {
        return this._alarmiatorActive;
    }

    /**
     * Plugin-specific implementation
     */

    /**
     * Creates ALARMiator mobile Instance 
     * 
     */
    setupInstance() {
        global.plgManager.logger.debug('PLUGIN | ALARMIATORMOBILE | Setup ALARMiator Mobile Instance');

        if (admin.apps.length == 0){
            admin.initializeApp(
                {credential: admin.credential.cert(serviceAccount)}
            )
        }    

    }

    /**
     * Check plugin config, whether sending alarms to ALARMiator is active 
     * 
     * @param {operation} alarmInfo 
     */
    sendMessage(alarmInfo) {
        global.plgManager.logger.debug('PLUGIN | ALARMIATORMOBILE | Send Messages');

        if (this.getConfigValue('alarmiatorActive') == "true"){
            global.plgManager.logger.debug('PLUGIN | ALARMIATORMOBILE | Sending Alarmiator messages');
            this.sendAlarmMessages(alarmInfo);
        }
    }


    /**
     * Sending ALARMiator messages to all users in ALARMiatorReceivers
     * @param {operation} alarmInfo 
     */
    sendAlarmMessages(alarmInfo){
        
        var fcmKeysString = this.getConfigValue('ALARMiatorReceivers');
        var fcmKeys = [];

        // check if string is empty
        if (fcmKeysString){
            fcmKeys = fcmKeysString.split(",").map(x => x.trim()).filter(x => x); // filter removes empty results
        }

        /* Get FCM token from tokenstore*/
        this.fcmtokens.getAllFCMTokensForNamespace("alarmiatormobile", (fcmrows) => {
            if (fcmrows != null){
                fcmrows.forEach((entry)=> {
                    fcmKeys.push(entry.fcmToken);
                })
            }
            var message = this.buildPayloadAlarm(alarmInfo, fcmKeys);
            this.sendFCMMessage(message, fcmKeys);
        }); 

    }

    /**
     * Building alarm payload
     * @param {number} baseDataId 
     * @param {operation} alarmInfo 
     */
    sendTestAlarm(baseDataId, alarmInfo){
        
        var fcmKeys = [];

        /* Get FCM token for User from tokenstore*/
        this.fcmtokens.getAllFCMTokensForBasedataAndNamespace("alarmiatormobile", baseDataId, (fcmrows) => {
            console.log(fcmrows);
            if (fcmrows != null){
                fcmrows.forEach((entry)=> {
                    fcmKeys.push(entry.fcmToken);
                })
            }
            var message = this.buildPayloadAlarm(alarmInfo, fcmKeys);
            this.sendFCMMessage(message, fcmKeys);
        }); 

    }

    /**
     * Building alarm payload
     * @param {operation} alarmInfo 
     * @param {array} fcmKeys 
     */
    buildPayloadAlarm(alarmInfo, fcmKeys){

        var sound = "default";
        if (this.getConfigValue('iosSound') != undefined){
            sound = this.getConfigValue('iosSound');
        }

        var organization = global.serverInstanceName;;

        /* Customize notification here for Android + iOS */
        var notification_title = "EINSATZ " + organization;
        var notification_subtitle = alarmInfo.subject.value +" - "+ alarmInfo.keywordName.value;
        var notification_body = "Einsatzort: " + alarmInfo.locationCalc.value;
        /*************************************************/

        var payload = {  
            /* NEW */
            operation: alarmInfo.getReduced(),
            /* Android Notification */
            notification_payload: {
                title: notification_subtitle,   // switched with subtitle for android!!
                subtitle : notification_title,  // switched with title for android!!
                body: notification_body,
            }
          };
          
          // REFERENCE: https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages#top_of_page
          var message = {
            data: {
              payload: JSON.stringify(payload),
              messageType: "alarm",
              feedback: "true",
              serverUUID: global.serverUUID,
              content_available: "1"
            },
            android: {
                priority: "high",
            },
            apns: {
              payload: {
                aps: {
                  alert : {
                    title : notification_title,
                    subtitle : notification_subtitle,
                    body: notification_body,
                  },
                  sound: {
                    critical: true,
                    name: sound,
                    volume: 1.0
                  },
                  "mutable-content": 1,
                  "content-available": 1
                }
              }
            },
            tokens: fcmKeys
          };

        global.plgManager.logger.debug('PLUGIN | ALARMIATORMOBILE | PAYLOAD for DEBUGGING -------START-----------');
        global.plgManager.logger.debug(JSON.stringify(message));
        global.plgManager.logger.debug('PLUGIN | ALARMIATORMOBILE | PAYLOAD for DEBUGGING -------END-------------');
        
        return message;
    }

    /**
     * Sending message to tokens
     * @param {string} message 
     * @param {array} fcmKeys 
     */
    sendFCMMessage(message,fcmKeys){
        admin.messaging().sendMulticast(message)
            .then((response) => {
                // Response is a message ID string.

                const failedTokens = {};
                const successTokens = [];
                response.responses.forEach((resp, idx) => {
                    if (!resp.success) {
                        failedTokens[fcmKeys[idx]] = resp.error.message;
                    }
                    else {
                        successTokens.push(fcmKeys[idx]);
                    }
                });

                global.plgManager.logger.debug("PLUGIN | ALARMIATORMOBILE | List of successfully sent tokens: " + JSON.stringify(successTokens))
                global.plgManager.logger.debug('PLUGIN | ALARMIATORMOBILE | List of tokens that caused failures: ' + JSON.stringify(failedTokens));
                admin.app().delete();
            })
            .catch((error) => {
                global.plgManager.logger.error('PLUGIN | ALARMIATORMOBILE | Error sending message: ',JSON.stringify(error));
                admin.app().delete();
        });
    }

    /**
     * Loads configuration for KatSys Service
     * TBD: Needs to be pulled from plugin config store
     */
    loadConfig(callback) {
        global.plgManager.loadConfigFromStore('alarmiatormobile', (configRows) => {
            if (configRows !== null) {
                this.config = configRows[0];
                this.config = JSON.parse(this.config.configstore);
                callback(true);
            }
            global.plgManager.logger.info('PLUGIN | ALARMIATORMOBILE | Configuration reloaded from core database');
        })
    }

    /**
     * returns config parameter for given fieldname
     * @param {string} fieldname 
     */
    getConfigValue(fieldname) {
        var retVal = null;
        this.config.fields.forEach((field) => {
            if (field.fieldname === fieldname) {
                retVal = field.value;
            } 
        });
        return retVal;
    }

}

module.exports = alarmiatormobile;