Last active
August 24, 2018 19:44
-
-
Save taf2/cdcbd168e751b25965771251f1d202e4 to your computer and use it in GitHub Desktop.
panoply.io Integration to send call data to panoply.io
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| panoply.io send data about phone calls, text messages, web forms and more to panoply.io. | |
| USAGE: | |
| Step 1: | |
| Add to https://app.calltrackingmetrics.com/lambdas/new | |
| Name your function panoply.io | |
| Step 2: | |
| In panoply.io here: https://platform.panoply.io/#/sources | |
| Add a new source Panoply SDK. You'll have a API Key and API Secret, configure these as Variables in | |
| the lambda function you created in step 1. | |
| Step 3: | |
| Add a trigger https://app.calltrackingmetrics.com/trigger/new | |
| You may want to configure the trigger to run at the end of a call, when paid attribution data is received | |
| or after every manual update. You may add conditions to your trigger to have it only run when specific | |
| conditions are true. The important thing is to add an action that calls your lambda function. | |
| Keep in mind data you add will by default include all attributes of a phone call, text message or web form. | |
| You may want to modify the code below to adjust how and what data we send. | |
| */ | |
| exports.handler = function(event, context, callback) { | |
| var activity = event.activity; | |
| var conn = new panoply(process.env.apikey, process.env.apisec); | |
| conn.on("flush", function() { | |
| context.done(); | |
| }).on("error", function(error) { | |
| console.log("error:", error) | |
| }).on("warn", function(warn){ | |
| console.log("warn:", warn); | |
| }); | |
| conn.write("calls", [activity]); | |
| conn.flush(); | |
| } | |
| // see: https://github.com/panoplyio/color-sdk-nodejs | |
| const panoply = (function() { | |
| const url = require( "url" ); | |
| const util = require( "util" ); | |
| const http = require( "http" ); | |
| const https = require( "https" ); | |
| const events = require( "events" ); | |
| const qs = require( "querystring" ); | |
| const pkg = require( "./package.json" ); | |
| const MAXSIZE = 1024 * 250; // 250kib | |
| const FLUSH_TIMEOUT = 2 * 1000; // 2 seconds | |
| module.exports.SDK = SDK; | |
| util.inherits( SDK, events.EventEmitter ); | |
| function SDK ( apikey, apisecret ) { | |
| events.EventEmitter.call( this ); | |
| // api-key: ACCOUNT/RAND1 | |
| // api-secret: BASE64( RAND2/UUID/AWSACCOUNT/REGION ) | |
| // queue: sdk-ACCOUNT-RAND2 | |
| var account = apikey.split( "/" )[ 0 ]; | |
| var decoded = new Buffer( apisecret, "base64" ).toString().split( "/" ); | |
| var rand = decoded[ 0 ]; | |
| var awsaccount = decoded[ 2 ]; | |
| var region = decoded[ 3 ]; | |
| this.apikey = apikey; | |
| this.apisecret = apisecret; | |
| this.logger = console.log; | |
| this._buffer = ""; | |
| this._timeout = null; | |
| this.qurl = [ | |
| "https://sqs." + region + ".amazonaws.com", | |
| awsaccount, | |
| "sdk-" + account + "-" + rand | |
| ].join( "/" ); | |
| this.flushcnt = 0; | |
| this.flushid = 0; | |
| this.eventscnt = 0; | |
| this._log( "Created. Version: " + pkg.version ); | |
| this._log( "Queue URL: ", this.qurl ); | |
| this.on( "error", function ( err ) { | |
| this._log( "ERROR #" + err.id + ":", err ); | |
| }) | |
| this.on( "warn", function ( err ) { | |
| this._log( "WARN #" + err.id + ":", err ); | |
| }) | |
| this.on( "send", function ( data ) { | |
| this._log( "Sending #" + data.id + ":", "tries: " + data.tries + " ,", | |
| data.count, "events", | |
| "(" + data.size + " bytes)", | |
| this.flushcnt, "flushes remaining" | |
| ); | |
| }) | |
| this.on( "flush", function ( data ) { | |
| this._log( "Sent Successfuly #" + data.id + ":", | |
| data.count, "events", | |
| "(" + data.size + " bytes)", | |
| "in", data.t + "s", | |
| this.flushcnt, "flushes remaining" | |
| ); | |
| }) | |
| this.on( "empty", function () { | |
| this.flushcnt = 0; | |
| this._log( "Empty" ); | |
| }) | |
| } | |
| SDK.prototype.write = function ( table, data ) { | |
| data = copy( data ); | |
| data.__table = table; | |
| this._buffer += encodeURIComponent( JSON.stringify( data ) + "\n" ); | |
| this.eventscnt += 1; | |
| if ( this._buffer.length > MAXSIZE ) { | |
| this.flush(); // max size is exceeded, flush immediately | |
| } else if ( !this._timeout ) { | |
| // flush something after the flush timeout | |
| this._timeout = setTimeout( this.flush.bind( this ), FLUSH_TIMEOUT ); | |
| } | |
| return this; | |
| } | |
| SDK.prototype._log = function () { | |
| if ( !this.logger ) { | |
| return; | |
| } | |
| var args = [].slice.call( arguments ); | |
| args = [ new Date().toISOString(), "COLOR-SDK" ].concat( args ); | |
| this.logger.apply( null, args ); | |
| } | |
| SDK.prototype.flush = function () { | |
| clearTimeout( this._timeout ); | |
| this._timeout = null; | |
| if ( !this._buffer.length ) { | |
| if ( !this.flushcnt ) { | |
| this.emit( "empty" ) | |
| } | |
| return | |
| } | |
| var that = this; | |
| var done = false; | |
| var retries = 3; | |
| var count = this.eventscnt; | |
| var buffer = this._buffer; | |
| var flushid = ++this.flushid; | |
| var size = buffer.length; | |
| this.eventscnt = 0; | |
| this.flushcnt += 1; | |
| this._buffer = ""; | |
| var time = new Date().toISOString(); | |
| var body = [ | |
| "Action=SendMessage", | |
| "MessageBody=" + buffer, | |
| "MessageAttribute.1.Name=key", | |
| "MessageAttribute.1.Value.DataType=String", | |
| "MessageAttribute.1.Value.StringValue=" + this.apikey, | |
| "MessageAttribute.2.Name=secret", | |
| "MessageAttribute.2.Value.DataType=String", | |
| "MessageAttribute.2.Value.StringValue=" + this.apisecret, | |
| "MessageAttribute.3.Name=sdk", | |
| "MessageAttribute.3.Value.DataType=String", | |
| "MessageAttribute.3.Value.StringValue=" + pkg.name + "-" + pkg.version, | |
| ].join( "&" ); | |
| var parsedurl = url.parse( this.qurl ) | |
| var options = { | |
| port: 443, | |
| method: "POST", | |
| path: parsedurl.path, | |
| hostname: parsedurl.hostname, | |
| headers: { | |
| "Content-Length": body.length, | |
| "Content-Type": "application/x-www-form-urlencoded" | |
| } | |
| } | |
| var t; | |
| var tries = 0; | |
| request(); | |
| function request() { | |
| t = new Date().getTime(); | |
| that.emit( "send", { | |
| id: flushid, | |
| count: count, | |
| size: size, | |
| fullsize: body.length, | |
| tries: ++tries | |
| }); | |
| var req = https.request( options, function ( res ) { | |
| var code = res.statusCode; | |
| var body = ""; | |
| res | |
| .on( "error", onerror ) | |
| .on( "data", function ( d ) { body += d.toString(); } ) | |
| .on( "end", function () { | |
| if ( code < 200 || code > 300 ) { | |
| var err = [ | |
| code, ":", http.STATUS_CODES[ code ], body | |
| ].join( " " ); | |
| onerror( new Error( err ) ); | |
| } else { | |
| onend(); | |
| } | |
| }) | |
| }) | |
| .on( "error", onerror ) | |
| req.setTimeout( 60 * 1000 ); | |
| req.end( body ); | |
| } | |
| function onerror ( err ) { | |
| err.id = flushid; | |
| that.emit( "warn", err ); | |
| if ( done ) return; | |
| if ( retries-- > 0 ) { | |
| that._log( "Retrying after error, in 2s. Remaining: ", retries ); | |
| return setTimeout( request, 2000 ); | |
| } | |
| that.emit( "error", err ); | |
| if ( --that.flushcnt <= 0 && !that._buffer ) { | |
| that.emit( "empty" ) | |
| } | |
| done = true; | |
| } | |
| function onend() { | |
| t = ( new Date().getTime() - t ) / 1000; | |
| that.emit( "flush", { | |
| id: flushid, | |
| count: count, | |
| size: size, | |
| fullsize: body.length, | |
| t: t | |
| }); | |
| if ( done ) return; | |
| if ( --that.flushcnt <= 0 && !that._buffer ) { | |
| that.emit( "empty" ) | |
| } | |
| done = true; | |
| } | |
| return this; | |
| } | |
| function copy ( obj ) { | |
| return JSON.parse( JSON.stringify( obj ) ); | |
| } | |
| return SDK; | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment