Node sendgrid ICAS/ICS invitation - node.js

I am using the npm sendgrid Node SendGrid GitHub
With this I have created the following module:
var path = require('path'),
emailTemplates = require('email-templates'),
async = require("async"),
mailConfig = require('../config/email.json'),
templates = require('../config/emailTemplates.json'),
_ = require('lodash'),
sendgrid = require('sendgrid')(mailConfig.sendGridApiKey),
fs = require("fs");
var mymailer = {};
/**
* Sends an email to either one or multiple users
* #param template_id (The id key of the template. Can be found in emailTemplates.json
* #param to String or Array
* #param from String
* #param subject String
* #param keyReplacer Array of objects for keys to replace in the template
* #param files Array of file objects
*/
mymailer.sendTemplate = function (template_id, to, from, subject, keyReplacer, section, text, files) {
var email = new sendgrid.Email(), templateKey = templates[template_id];
if (templateKey) {
email.setSmtpapiTos(to);
email.subject = subject;
email.from = from;
email.text = text;
email.html = 'Gief HTML NU:D';
email.setFilters({
"templates": {
"settings": {
"enable": 1,
"template_id": templateKey
}
}
});
email.smtpapi.header.sub = prepareSub(keyReplacer, section);
email.smtpapi.header.section = prepareSection(section);
email.files = prepareAttachement(files);
sendgrid.send(email);
} else {
console.log('incorrect key');
}
};
Now for some of my mails I wish to send an invitation that can be accepted in your calendar. However I have no idea how to do this and I can't seem to find any information on the subject.
Has anyone tried sending this using sendgrid? And if so can you point me in the right direction?

If you are willing to upgrade to the newest version of the Node.js SendGrid Client library, you may find that the v3 /mail/send endpoint makes sending calendar invitations much easier.
Here is an example of adding a content type of "text/calendar": https://github.com/sendgrid/sendgrid-nodejs/blob/master/examples/helpers/mail/example.js#L57
Here is some documentation on the new v3 /mail/send endpoint if you would rather use it directly:
https://sendgrid.com/docs/Classroom/Send/v3_Mail_Send/index.html
https://sendgrid.com/docs/API_Reference/Web_API_v3/Mail/index.html

Related

Unable to retrieve Analytics data for branded accounts using YouTube Analytics API

I am trying to get analytics data for a brand account that I am the administrator of.
I am going to use Google Apps Script and YouTube Analytics API to retrieve the data.
I have done the following.
Creating Google Apps Script
Enabling YouTube Analytics API
Write the following code (This is a slightly modified version of the sample code in the following link. https://developers.google.com/apps-script/advanced/youtube-analytics I put the ID of the brand account in the variable channelId because, as in the sample, the ID of my account channel, not the brand account channel, goes into the variable channelId.)
/**
* Creates a spreadsheet containing daily view counts, watch-time metrics,
* and new-subscriber counts for a channel's videos.
*/
function createReport() {
// ========== Comment out from here ==========
// // Retrieve info about the user's YouTube channel.
// const channels = YouTube.Channels.list('id,contentDetails', {
// mine: true
// });
// const channelId = channels.items[0].id;
// ========== Comment out so far ==========
// ========== Added from here ==========
const channelId = 'xxxxxxxxxxxxxxxxxxxx' // Fill in the channel id of the brand account here
// ========== Added so far ==========
// Retrieve analytics report for the channel.
const oneMonthInMillis = 1000 * 60 * 60 * 24 * 30;
const today = new Date();
const lastMonth = new Date(today.getTime() - oneMonthInMillis);
const metrics = [
'views',
'estimatedMinutesWatched',
'averageViewDuration',
'subscribersGained'
];
const result = YouTubeAnalytics.Reports.query({
ids: 'channel==' + channelId,
startDate: formatDateString(lastMonth),
endDate: formatDateString(today),
metrics: metrics.join(','),
dimensions: 'day',
sort: 'day'
});
if (!result.rows) {
Logger.log('No rows returned.');
return;
}
const spreadsheet = SpreadsheetApp.create('YouTube Analytics Report');
const sheet = spreadsheet.getActiveSheet();
// Append the headers.
const headers = result.columnHeaders.map((columnHeader)=> {
return formatColumnName(columnHeader.name);
});
sheet.appendRow(headers);
// Append the results.
sheet.getRange(2, 1, result.rows.length, headers.length).setValues(result.rows);
Logger.log('Report spreadsheet created: %s', spreadsheet.getUrl());
}
/**
* Converts a Date object into a YYYY-MM-DD string.
* #param {Date} date The date to convert to a string.
* #return {string} The formatted date.
*/
function formatDateString(date) {
return Utilities.formatDate(date, Session.getScriptTimeZone(), 'yyyy-MM-dd');
}
/**
* Formats a column name into a more human-friendly name.
* #param {string} columnName The unprocessed name of the column.
* #return {string} The formatted column name.
* #example "averageViewPercentage" becomes "Average View Percentage".
*/
function formatColumnName(columnName) {
let name = columnName.replace(/([a-z])([A-Z])/g, '$1 $2');
name = name.slice(0, 1).toUpperCase() + name.slice(1);
return name;
}
I tried to run the script in this state, but got the following error.
GoogleJsonResponseException: API call to youtubeAnalytics.reports.query failed with error: Forbidden
createReport # youtube_analytics_api.gs:25
What can I do to resolve this error and get the API to run successfully?

Unable to get a Stripe Checkout session object

Stripe.Net v34.16.0 bounces my code on the creation of the checkout session object responding with:
StripeException: No such plan: plan_myPlanId; a similar object exists in live mode, but a test mode key was used to make this request.
I do not see a means in the Stripe Dashboard to designate a given plan as a test plan .I also do not see
anything resembling a mode property.. my code
public async Task<IActionResult> Index()
{
//var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
//user = await _userManager.FindByIdAsync(userId);
StripeConfiguration.ApiKey = "sk_test_mytestkey";
var options = new Stripe.Checkout.SessionCreateOptions
{
PaymentMethodTypes = new List<string> {
"card",
},
SubscriptionData = new Stripe.Checkout.SessionSubscriptionDataOptions
{
Items = new List<SessionSubscriptionDataItemOptions> {
new SessionSubscriptionDataItemOptions {
Plan = "plan_myplanid",
},
},
},
//to do
SuccessUrl = "localhost://home",
CancelUrl = "localhost://home",
//CancelUrl = "https://example.com/cancel",
};
var service = new Stripe.Checkout.SessionService();
Stripe.Checkout.Session session = service.Create(options); //error out here
StripeCheckoutSessionId stripeCheckoutSessionId = new StripeCheckoutSessionId();
stripeCheckoutSessionId.StripeSessionID = session.Id;
return View(stripeCheckoutSessionId);
}
I am referring to Stripe sample code in the .Net tab here: https://stripe.com/docs/payments/checkout/subscriptions/starting
I appreciate your guidance in correcting my errors.

How to create OutPutContext via V2 client library for node js

I am working on entity and intents creation in my agent using v2 client library for node.js . And for that i am going through this sample which is on git. And it says something related to session id and context id. Can anyone explain me what is sessionId and contextId. And also provide me link where i can read those thing in details.
I am unable to create context by following those example. How can i create context while creating intent at the same time.
The following is code to create a context. You cannot create a context and an intent in a single API call, you first need to create the context and then create the intent that uses the context. The response to the create context API call will return a context ID you can use in your intent.
const dialogflow = require('dialogflow');
// Instantiates clients
const entityTypesClient = new dialogflow.EntityTypesClient();
// The path to the agent the created entity type belongs to.
const agentPath = entityTypesClient.projectAgentPath(projectId);
const createEntityTypeRequest = {
parent: agentPath,
entityType: {
displayName: displayName,
kind: kind,
},
};
entityTypesClient
.createEntityType(createEntityTypeRequest)
.then(responses => {
console.log(`Created ${responses[0].name} entity type`);
})
.catch(err => {
console.error('Failed to create size entity type:', err);
});
Source: https://github.com/googleapis/nodejs-dialogflow/blob/master/samples/resource.js
Contexts are very closely associated with SessionID. Say for eg, you have a chatbot that gets spun up on two computers serving two different user's. Each user will have a respective session_id (If you're coding in NODE, when a new user fires the chatbot, you need to ensure he/she will get a unique session_id).
Now, every unique session id will have unique contexts. From above example, let's say user 1 will initialize an intent that has input context named 'abc' with lifespan of 2 and user 2 will initialize another intent that has input context named 'xyz' with lifespan of 5, these respective contexts gets recorded against each of these user's individual session id's. You can programatically control (edit) contexts and its lifecycle. This is the biggest advantage of code facilitated Dialogflow as opposed to using GUI. Using services like Firebase, you can also preserve session id's and its associated contexts so, next time same user sign's in again, they can start from where they had last left.
I can share a snippet from one of my previous projects where I was managing contexts programatically. Initialization script is as follows:
/**
* #author Pruthvi Kumar
* #email pruthvikumar.123#gmail.com
* #create date 2018-08-15 04:42:22
* #modify date 2018-08-15 04:42:22
* #desc Dialogflow config for chatbot.
*/
const dialogflow_config = {
projectId: 'xxx',
sessionId: 'chatbot-session-id', //This is default assignment. This will hve to be overridden by sessionId as obtained from client in order to main context per sessionId.
languageCode: 'en-US'
};
exports.configStoreSingleton = (function () {
let instanceStacks;
let instanceSessionId;
let contextStack = {};
let intentsStack = {};
let successfulIntentResponseStack = {};
function init() {
contextStack[dialogflow_config['sessionId']] = [];
intentsStack[dialogflow_config['sessionId']] = [];
successfulIntentResponseStack[dialogflow_config['sessionId']] = [];
return {
contextStack: contextStack,
intentsStack: intentsStack,
successfulIntentResponseStack: successfulIntentResponseStack
};
}
return {
init: function () {
if (!instanceStacks || (instanceSessionId !== dialogflow_config['sessionId'] && (!intentsStack[dialogflow_config['sessionId']]))) {
console.log('[dialogflow_config]: Singleton is not instantiated previously or New userSession is triggered! Fresh instance stack will be provisioned');
instanceStacks = init();
instanceSessionId = dialogflow_config['sessionId'];
}
return instanceStacks;
}
};
})();
exports.updateSessionIdOfDialogflowConfig = function (sessionId) {
if (typeof (sessionId) === 'string') {
dialogflow_config['sessionId'] = sessionId;
return true;
} else {
console.warn('[dialogflow_config]: SessionId must be of type STRING!');
return;
}
};
exports.getDialogflowConfig = function () {
return dialogflow_config;
};
And then, to programmatically manage contexts:
/**
* #author Pruthvi Kumar
* #email pruthvikumar.123#gmail.com
* #create date 2018-08-15 04:37:15
* #modify date 2018-08-15 04:37:15
* #desc Operate on Dialogflow Contexts
*/
const dialogflow = require('dialogflow');
const dialogflowConfig = require('../modules/dialogflow_config');
const structjson = require('./dialogflow_structjson');
const util = require('util');
const contextsClient = new dialogflow.ContextsClient();
exports.setContextHistory = function (sessionId, intent_name, context_payload, preservedContext=false) {
/* maintain context stack per session */
/* context_payload = {input_contexts: [], output_contexts = []}
*/
const contextStack = dialogflowConfig.configStoreSingleton.init().contextStack;
if (intent_name) {
contextStack[sessionId].push({
intent: intent_name,
contexts: context_payload,
preserveContext: preservedContext
});
} else {
console.warn('[dialogflow_contexts]: Intent name is not provided OR Nothing in context_payload to add to history!');
}
};
exports.getContextHistory = function () {
const contextStack = dialogflowConfig.configStoreSingleton.init().contextStack;
return contextStack;
}
exports.preserveContext = function () {
const contextStack = dialogflowConfig.configStoreSingleton.init().contextStack;
//Traverse contextStack, get the last contexts.
let context_to_be_preserved = contextStack[dialogflowConfig.getDialogflowConfig()['sessionId']][contextStack[dialogflowConfig.getDialogflowConfig()['sessionId']].length - 1];
//console.log(`context to be preserved is: ${util.inspect(context_to_be_preserved)}`);
return context_to_be_preserved['contexts'].map((context, index) => {
let context_id = exports.getContextId(context);
return exports.updateContext(context_id, true)
});
}
From here, you can reference this github resource to build your own contexts - https://github.com/googleapis/nodejs-dialogflow/blob/master/samples/resource.js
Happy creating digital souls!

SuiteScript hmac sha256

I'm actually working on a new project based on netsuite product.
I'm trying to encrypt a message using hmac sha256.
What's is the simple way to do it considering that I have the stringToEncrypt and a key.
I've read the documentation in Netsuite but I'm still stucked...
There is my function
function toHmacSHA256Base64(toCrypt, key) {
var inputString = toCrypt;
var myGuid = key;
var sKey = crypto.createSecretKey({
guid: myGuid,
encoding: encode.Encoding.UTF_8
});
var hmacSHA256 = crypto.createHmac({
algorithm: 'SHA256',
key: sKey
});
hmacSHA256.update({
input: inputString,
inputEncoding: encode.Encoding.BASE_64
});
var digestSHA256 = hmacSHA256.digest({
outputEncoding: encode.Encoding.HEX
});
return digestSHA256;
};
of course behind the word crypto I use the module 'N/crypto' and encode 'N/encode'.
Thx a lot.
That's roughly correct and looks exactly like the sample from the NS help. If you have a string then you probably want inputEncoding:encode.Encoding.UTF_8 for the update call.
What's missing is how to generate the guid of the secret key.
For that you use a suitelet. Note the addSecretKeyField not the addCredentialField of the NS help:
/**
*#NApiVersion 2.x
*#NScriptType Suitelet
*/
define(['N/ui/serverWidget', './config.js'],
function(serverWidget, config) {
function onRequest(context) {
if (context.request.method === 'GET') {
var form = serverWidget.createForm({
title: 'SFTP Password'
});
form.addSecretKeyField({
id : 'username',
label : 'Pwd',
restrictToScriptIds : config.targetScript,
restrictToCurrentUser : false
});
form.addSubmitButton({
label: 'Submit Button'
});
context.response.writePage(form);
} else {
var textField = context.request.parameters.username;
context.response.write('You have entered: ' + textField);
}
}
return {
onRequest: onRequest
};
});
FWIW encrypt is the wrong term here. You are creating a hash of the data which will be used for ensuring data integrity. You cannot decrypt a hash.
Once a GUID for a key has been generated I just store it in a config file (same one as is used for the script list above.
in TypeScript it looks like:
/**
* config.js
* #NApiVersion 2.x
*/
export var config = {
'host': '162.242.144.xxx',
'userName': 'unsername',
'targetScript': ['customscript_transmit_dsv_943', 'customscript_transmit_dsv_940', 'customscript_retrieve_dsv_944'],
'hostKey': 'AAAAB3Nza...Wz'
};
Then everything except the config.xs files can be stored in version control. Audience needs to be set appropriately on the files used in the script.

Ensuring a unique socket.id over multiple app instances using socket.io

I am running a Node.js + Socket.io app over multiple instances with a Redis cache layer. I am wondering if the socket.id field for each connection would be unique over all instances. Is there a possibility that a socket.id on one instance can be the same as socket.id on another instance?
As far as I can see, the socket.id is generated using the following code:
/**
* Interface to a `Client` for a given `Namespace`.
*
* #param {Namespace} nsp
* #param {Client} client
* #api public
*/
function Socket(nsp, client, query){
this.nsp = nsp;
this.server = nsp.server;
this.adapter = this.nsp.adapter;
this.id = nsp.name !== '/' ? nsp.name + '#' + client.id : client.id;
this.client = client;
this.conn = client.conn;
this.rooms = {};
this.acks = {};
this.connected = true;
this.disconnected = false;
this.handshake = this.buildHandshake(query);
this.fns = [];
}
I am unsure about what it is actually doing when creating the id field.
If you trace the flow further, you will realize the socket.io module depends on engine.io (https://github.com/socketio/engine.io). It uses the id generated by engine.io.
1.engine.io emits connection to socket.io server as per the code below:
Server.prototype.onconnection = function(conn){
debug('incoming connection with id %s', conn.id);
var client = new Client(this, conn);
conn.id has the unique id.
2.socket.io client code stores this id in its pointer
function Client(server, conn){
this.server = server;
this.conn = conn;
this.encoder = new parser.Encoder();
this.decoder = new parser.Decoder();
this.id = conn.id;
3.Then when socket is created, same id is used
this.id = nsp.name !== '/' ? nsp.name + '#' + client.id : client.id;
Now back to the original question. The id is generated by engine.io using the following method:
/**
* generate a socket id.
* Overwrite this method to generate your custom socket id
*
* #param {Object} request object
* #api public
*/
Server.prototype.generateId = function (req) {
return base64id.generateId();
};
Here base64id and generateId are part of npm package https://www.npmjs.com/package/base64id. This package is supposed to generate a random base 64 id. I hope this helps you to understand that there is a very less probability of having the same id for 2 connections. You do have an option to override this method if you don't prefer this algorithm
The id will always be unique .this is due to fact that the id is based from the socket it was connected . Every socket is different.So every id is differet.
socket()
|
bind()
|
recvfrom()
|
(wait for a sendto request from some client)
|
(process the sendto request)
|
sendto (in reply to the request from the client...for example, send an HTML file)

Resources