How to intercept bot messages in SDK4 - node.js

I have an SDK 4 bot, logging user interactions to Blob storage. I would also like to log bot responses also. In SDK 3 I did this with something like ...
bot.use({
// Code for handling message receives
receive: function (session, event, next) {
var userId = session.address.user.id;
logger.logUserConversation(session.text, userId, session.address.conversation.id, userId);
next();
},
// Code for handling message sends
send: function (event, next) {
var text = event.text;
if(!event.text) {
text = "Attachments sent";
}
logger.logUserConversation(text, 'bot', event.address.conversation.id, event.address.user.id);
next();
}
});
In SDK 4, I am able to configure middleware which intercepts user activity, but I cannot seem to intercept bot activity. I can't seem to find anything in the documentation, but I am new to SDK 4 and might be missing something.
Anybody know how I can intercept both user and bot events, so that I can log?

There are already 2 samples in Node.js in the official samples repository:
1 called "Logger": https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/javascript_nodejs/25.logger
1 called "transcript Logger": https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/javascript_nodejs/26.transcript-logger
I gave a try to the 1st one and can confirm that it logs both user input and bot replies. It is logging every activity, even ConversationUpdate.
See example of transcript generated below:
Activity Received: { type: 'conversationUpdate',
membersAdded: [ { id: '1', name: 'Bot' } ],
channelId: 'emulator',
conversation: { id: '36e25420-ec19-11e8-8040-2ba105e71021|livechat' },
id: '370f7ea0-ec19-11e8-9ee4-fb60855d29c5',
localTimestamp: 2018-11-19T16:36:07.000Z,
recipient: { id: '1', name: 'Bot', role: 'bot' },
timestamp: 2018-11-19T16:36:07.689Z,
from:
{ id: 'fd3fd64d-6297-4e36-98c5-ee398857f2b6',
name: 'User',
role: 'user' },
locale: '',
serviceUrl: 'http://localhost:58083' }
Activity Received: { type: 'conversationUpdate',
membersAdded:
[ { id: 'fd3fd64d-6297-4e36-98c5-ee398857f2b6', name: 'User' } ],
channelId: 'emulator',
conversation: { id: '36e25420-ec19-11e8-8040-2ba105e71021|livechat' },
id: '3711c890-ec19-11e8-9ee4-fb60855d29c5',
localTimestamp: 2018-11-19T16:36:07.000Z,
recipient: { id: '1', name: 'Bot', role: 'bot' },
timestamp: 2018-11-19T16:36:07.705Z,
from:
{ id: 'fd3fd64d-6297-4e36-98c5-ee398857f2b6',
name: 'User',
role: 'user' },
locale: '',
serviceUrl: 'http://localhost:58083' }
Activity Received: { text:
'I am a bot that demonstrates custom logging. We will have a short conversation where I ask a few questions to collect your name and age, then store those values in UserState for later use. after this you will be able to find a log of the conversation in the folder set by the transcriptsPath environment variable Say anything to continue.',
inputHint: 'acceptingInput',
channelId: 'emulator',
serviceUrl: 'http://localhost:58083',
conversation: { id: '36e25420-ec19-11e8-8040-2ba105e71021|livechat' },
from: { id: '1', name: 'Bot', role: 'bot' },
recipient:
{ id: 'fd3fd64d-6297-4e36-98c5-ee398857f2b6',
name: 'User',
role: 'user' },
replyToId: '3711c890-ec19-11e8-9ee4-fb60855d29c5',
type: 'message',
timestamp: 2018-11-19T16:36:08.408Z }
Activity Received: { type: 'message',
text: 'test',
from:
{ id: 'fd3fd64d-6297-4e36-98c5-ee398857f2b6',
name: 'User',
role: 'user' },
locale: '',
textFormat: 'plain',
timestamp: 2018-11-19T16:36:23.421Z,
channelData: { clientActivityId: '1542645367574.7109285295569892.0' },
entities:
[ { type: 'ClientCapabilities',
requiresBotState: true,
supportsTts: true,
supportsListening: true } ],
channelId: 'emulator',
conversation: { id: '36e25420-ec19-11e8-8040-2ba105e71021|livechat' },
id: '406fdad0-ec19-11e8-9ee4-fb60855d29c5',
localTimestamp: 2018-11-19T16:36:23.000Z,
recipient: { id: '1', name: 'Bot', role: 'bot' },
serviceUrl: 'http://localhost:58083' }
Activity Received: { text: 'What is your name, human?',
inputHint: 'expectingInput',
channelId: 'emulator',
serviceUrl: 'http://localhost:58083',
conversation: { id: '36e25420-ec19-11e8-8040-2ba105e71021|livechat' },
from: { id: '1', name: 'Bot', role: 'bot' },
recipient:
{ id: 'fd3fd64d-6297-4e36-98c5-ee398857f2b6',
name: 'User',
role: 'user' },
replyToId: '406fdad0-ec19-11e8-9ee4-fb60855d29c5',
type: 'message',
timestamp: 2018-11-19T16:36:23.443Z }
More details about the code that generated that is available on the project, here. If you look at it, the main point is:
if (activity.value === 'endOfInput') {
console.log(this.conversations[id]);
var transcriptfileName = util.format('%s/log_%s.transcript', process.env.transcriptsPath, id);
fs.writeFile(transcriptfileName, JSON.stringify(this.conversations[id], null, 3), function(err) {
if (err) throw err;
});
delete this.conversations[id];
}

Related

Why does the child's resolve function not contain the result of the parent's resolve function?

I've tried to isolate this example and I hope it's ok. I know, this isn't great code, but I hope you get the drift.
For the time being the resolvers return a static result object.
Here's my problem:
The result of the company resolve function should be passed on the user's resolve function. But that ain't happenin' and I wonder what I am missing.
const GraphQL = require('graphql');
const UserType = new GraphQL.GraphQLObjectType({
name: 'User',
fields: {
givenName: { type: GraphQL.GraphQLString },
familyName: { type: GraphQL.GraphQLString },
city: { type: GraphQL.GraphQLString },
},
});
const CompanyType = new GraphQL.GraphQLObjectType({
name: 'Company',
fields: {
legalName: { type: GraphQL.GraphQLString },
city: { type: GraphQL.GraphQLString },
employees: { type: new GraphQL.GraphQLList(UserType) },
},
});
const queryDef = new GraphQL.GraphQLObjectType({
name: 'Query',
fields: {
user: {
type: UserType,
args: {
id: { type: GraphQL.GraphQLID },
givenName: { type: GraphQL.GraphQLString },
familyName: { type: GraphQL.GraphQLString },
city: { type: GraphQL.GraphQLString },
},
resolve: (parent, args, context, info) => {
console.log('parent should provide company object', parent);
// currentyl parent is undefined
return {
id: 10,
givenName: 'test',
};
},
},
company: {
type: CompanyType,
args: {
id: { type: GraphQL.GraphQLID },
},
resolve: (parent, args, context, info) => {
return {
id: 3,
legalName: 'legal test name',
city: 'company location',
};
},
},
},
});
const schema = new GraphQL.GraphQLSchema({ query: queryDef });
const companyQuery = `
{
company(id: 1) {
city
employees {
familyName
}
}
}`;
GraphQL.graphql(schema, companyQuery).then( (companyResult) => {
console.log(companyResult);
} ).catch( (err) => {
console.error(err);
});

Nodejs verify array strings

I have array of strings and I need to validate if incoming text id is included any of string in my array or not, how can I do that in node? (in php we usually do in_array()) but in node I don't have much experience.
sample code
var text = msg;
var myArray = [
"123",
"456"
];
if(text.id == myArray) {
// true
} else {
// false
}
PS: I am aware that if(text.id == myArray) is wrong, I just wrote that so you can get the idea of what I'm looking for.
Update
My current code
var myArray = [
"182700619",
"1566587158"
];
if (myArray.includes(msg.forward_from.id)) {
console.log("msg2:", true);
} else {
console.log("msg2:", false);
}
my incoming message structure
msg = {
message_id: 221,
from: {
id: 1566587158,
is_bot: false,
first_name: 'Test User',
username: 'testuser',
language_code: 'en'
},
chat: {
id: 1566587158,
first_name: 'Test User',
username: 'testuser',
type: 'private'
},
date: 1619746446,
forward_from: {
id: 182700619,
is_bot: false,
first_name: 'John',
last_name: 'Doe',
username: 'johndoe'
},
forward_date: 1619694506,
text: 'tesing 01!'
}
You can do it with below code, by using toString() method to convert the int to strings and then compare it.
msg = {
message_id: 221,
from: {
id: 1566587158,
is_bot: false,
first_name: 'Test User',
username: 'testuser',
language_code: 'en'
},
chat: {
id: 1566587158,
first_name: 'Test User',
username: 'testuser',
type: 'private'
},
date: 1619746446,
forward_from: {
id: 182700619,
is_bot: false,
first_name: 'John',
last_name: 'Doe',
username: 'johndoe'
},
forward_date: 1619694506,
text: 'tesing 01!'
}
var myArray = [
"182700619",
"1566587158"
];
console.log(typeof msg.forward_from.id);
if (myArray.includes(msg.forward_from.id.toString())) {
console.log("msg2:", true);
} else {
console.log("msg2:", false);
}
Output of the above code is below
$node main.js
number
msg2: true

Node.js - Swagger - Unable to render this definition

I am following this toutorial: https://github.com/codeBelt/open-api-documentation/blob/master/src/openApiDocumentation.js
Can I validate my openApiDocumentation.js file somewhow? I get:
Unable to render this definition
The provided definition does not specify a valid version field.
Please indicate a valid Swagger or OpenAPI version field. Supported version fields are swagger: "2.0" and those that match openapi: 3.0.n (for example, openapi: 3.0.0).
I am attaching mi .js file. Maybe you guys will see a typo here. Thanks in advance.
.js file:
const USER_TYPES = {
EXCHANGE: 'xxx',
GIVEAWAY: 'xxx'
}
const openApiDocumentation = {
openapi: '3.0.1',
info: {
version: '1.3.0',
title: 'xxx',
description: 'xxx',
contact: {
name: 'xxx',
email: 'xxx',
}
},
license: {
name: 'Apache 2.0',
url: 'https://www.apache.org/licenses/LICENSE-2.0.html',
},
servers: [
{
url: 'http://localhost:4000/',
description: 'Local server',
},
],
tags: [
{
name: 'Books CRUD operations',
},
],
paths: {
'/findAllBooks': {
get: {
tags: ['CRUD operations'],
description: 'Get all Book offers',
operationId: 'getUsers',
parameters: [
{
name: 'page',
in: 'query',
schema: {
type: 'integer',
default: 1,
},
required: true,
description: 'Page numer used pagination.',
},
],
responses: {
'200': {
description: 'Books were obtained',
content: {
'application/json': {
schema: {
$ref: '#/components/schemas/Books',
},
},
},
},
'500': {
description: 'Missing parameters',
content: {
'application/json': {
schema: {
$ref: '#/components/schemas/Error',
},
example: {
message: 'page qyery parameter is missing',
internal_code: 'missing_parameters',
},
},
},
},
},
},
},
},
components: {
schemas: {
coverImg: {
type: 'string',
example: 'http:',
},
image: {
type: 'string',
example: 'http',
},
category: {
type: 'string',
example: 'Crafts & Hobbies',
},
linkTypes: {
type: 'object',
properties: {
coverImg: {
$ref: '#/components/schemas/coverImg',
},
images: {
type: 'array',
items: {
$ref: '#/components/schemas/image',
},
}
}
},
offerID: {
type: 'string',
example: '27301927',
},
userID: {
type: 'string',
example: 'efdc5192',
},
title: {
type: 'string',
example: 'Quilting For Dummies',
},
description: {
type: 'string',
example: 'You ',
},
categories: {
type: 'array',
items: {
$ref: '#/components/schemas/category',
},
},
links: {
type: 'object',
items: {
$ref: '#/components/schemas/linkTypes',
},
},
offerType: {
type: 'string',
enum: USER_TYPES,
default: USER_TYPES.EXCHANGE,
},
Book: {
type: 'object',
properties: {
offerID: {
$ref: '#/components/schemas/offerID',
},
userID: {
$ref: '#/components/schemas/userID',
},
title: {
$ref: '#/components/schemas/title',
},
description: {
$ref: '#/components/schemas/description',
},
categories: {
$ref: '#/components/schemas/categories',
},
imageLinks: {
$ref: '#/components/schemas/links',
},
offerType: {
$ref: '#/components/schemas/offerType',
},
},
},
Books: {
type: 'object',
properties: {
users: {
type: 'array',
items: {
$ref: '#/components/schemas/Book',
},
},
},
},
Error: {
type: 'object',
properties: {
message: {
type: 'string',
},
internal_code: {
type: 'string',
},
},
},
},
},
};
There are few mistakes,
license must be inside the info object
info: {
version: '1.3.0',
title: 'xxx',
description: 'xxx',
contact: {
name: 'xxx',
email: 'xxx' // make sure you have used valid email address!
},
license: {
name: 'Apache 2.0',
url: 'https://www.apache.org/licenses/LICENSE-2.0.html'
}
}
enum will allow only array not an object and here you have passed object USER_TYPES, corrected below:
const USER_TYPES = {
EXCHANGE: 'xxx',
GIVEAWAY: 'xxx'
};
const USER_TYPES_ENUM = [
USER_TYPES.EXCHANGE,
USER_TYPES.GIVEAWAY
];
offerType: {
type: 'string',
enum: USER_TYPES_ENUM,
default: USER_TYPES.EXCHANGE,
},
For best practice use https://editor.swagger.io/ (also they have provided a option to convert json to yaml under Edit > Convert to YAML)!

How to filter nested objects in array of a document in mongoose?

My data stored for user is in the following format :
{
name: 'XYZ',
email: 'abc#gmail.com',
password: 'encrypted',
categories: [
{
name: 'category1',
type: 'type1',
},
{
name: 'category2',
type: 'type2',
}
],
transactions: [
{
date: 2020-04-09T06:00:30.000Z,
type: 'type1',
category: 'category1',
description: 'some desc',
},
{
date: 2020-04-08T06:00:30.000Z,
type: 'type2',
category: 'category1',
description: 'some desc',
},
{
date: 2020-04-07T06:00:30.000Z,
type: 'type3',
category: 'category1',
description: 'some desc',
}
]
}
I want to get the data of particular user after providing unique email but along with that I want transactions filtered between 2 dates.
Example I want data of user with email = abc#gmail.com and transactions of dates between 2020-04-08 and 2020-04-09.
Sample output looks like this :
{
name: 'XYZ',
email: 'abc#gmail.com',
categories: [...],
transactions: [
{
date: 2020-04-09T06:00:30.000Z,
type: 'type1',
category: 'category1',
description: 'some desc',
},
{
date: 2020-04-08T06:00:30.000Z,
type: 'type2',
category: 'category1',
description: 'some desc',
}]
}
Or only transactions between the given dates for the given user email is also fine.
Tried using this query
User.aggregate([
{$match: {email: email}},
{$unwind: '$transactions'},
{$match: {'transactions.date': {$gt: startDate, $lt: endDate}}},
{
$group: {
_id: '$_id',
transactions: {
$type: "$transactions.type",
$category: "$transactions.category",
$date: "$transactions.date",
$description: "$transactions.decription"
}
}
}
]).exec((err, data) => {
if (err) throw err;
console.log(data);
})
Getting the error as follows The field 'transactions' must specify one accumulator

How to use arrays Schema Types for GraphQL

Trying out GraphQL for the first time, and I'd like for one Schema Type to have access to an array of objects within another Schema Type. I'm using local data and lodash to test this before I wire it up to MongoDB. I'm getting a few errors regarding my attempts at it. I know I'm close. Any assistance would be appreciated.
The following was my latest attempt. I accessed the GraphQLList using express-graphql and used GraphQLID to access the second schema.
schema.js
var projects = [
{
name: "Title 1",
subtitle: "Subtitle 1",
summary: "Lorem ipsum....",
languageId: ["4", "2"],
id: "1"
},
...
var languages = [
{ name: "Javascript", id: "1" },
{ name: "HTML", id: "2" },
{ name: "CSS", id: "3" },
{ name: "Python", id: "4" },
]
...
const ProjectType = new GraphQLObjectType({
name: 'Project',
fields: () => ({
id: { type: GraphQLID },
name: { type: GraphQLString },
subtitle: { type: GraphQLString },
summary: { type: GraphQLString },
languages: {
type: new GraphQLList(GraphQLID),
resolve(parent, args) {
console.log(parent)
return _.find(languages, { id: parent.languageId })
}
}
})
});
const LanguageType = new GraphQLObjectType({
name: 'Language',
fields: () => ({
id: { type: GraphQLID },
name: { type: GraphQLString },
})
});
//Root Queries
const RootQuery = new GraphQLObjectType({
name: 'RootQueryType',
fields: {
project: {
type: ProjectType,
args: { id: { type: GraphQLID } },
resolve(parent, args) {
return _.find(projects, { id: args.id });
}
},
language: {
type: LanguageType,
args: { id: { type: GraphQLID } },
resolve(parent, args) {
return _.find(languages, { id: args.id })
}
}
}
});
The intention was to bring up a list of languages used in that project in Graphiql that would look something like this...
{
"data": {
"project": {
"name": "Project 1"
languages{
names{
"Python"
"HTML"
}
}
}
}
}
Instead I'm getting the following...
{
"errors": [
{
"message": "Syntax Error: Expected Name, found }",
"locations": [
{
"line": 7,
"column": 7
}
]
}
]
}
I reformatted the resolve to hard code an array of languages, and that works, but it's not functional. Any suggestions would be greatly appreciated. Thank you all for your time.
I'm not sure what query you're trying to make.
But in order to return Languages nested in Projects, you should change the return type of the languages field.
Something like:
const ProjectType = new GraphQLObjectType({
name: 'Project',
fields: () => ({
id: {type: GraphQLID},
name: {type: GraphQLString},
subtitle: {type: GraphQLString},
summary: {type: GraphQLString},
languages: {
// change this type to LanguageType
type: new GraphQLList(LanguageType),
resolve(parent, args) {
// No need to lodash here
return languages.filter((language) => {
return parent.languageId.includes(language.id)
})
}
}
})
});
I had a problem with an old project for my tags. in the end, I found it by try and catch u can use this
tags: {type: GraphQLList(GraphQLString)},

Resources