{"_id":"moxtra-bot-sdk","_rev":"54-777af3c2a889e8a697a9031627cd395a","name":"moxtra-bot-sdk","dist-tags":{"latest":"2.0.0"},"versions":{"1.0.0":{"name":"moxtra-bot-sdk","version":"1.0.0","keywords":["bot","chatbot","chat","sdk","moxtra"],"_id":"moxtra-bot-sdk@1.0.0","maintainers":[{"name":"moxtradeveloper","email":"jonathan@moxtra.com"}],"homepage":"https://github.com/Moxtra/moxtra-bot-sdk-nodejs#readme","bugs":{"url":"https://github.com/Moxtra/moxtra-bot-sdk-nodejs/issues"},"dist":{"shasum":"1a2481b3d7af07db3473a37707eab801c1dce9af","tarball":"https://registry.npmjs.org/moxtra-bot-sdk/-/moxtra-bot-sdk-1.0.0.tgz","integrity":"sha512-+oU6bqvWY+KIaXxNoV7WgZoRnJJq7ZaniQ3J+ObAU6/87dNuVFXCCAjIdYBdo2xYqx1vvEIYDvzfH8OSKHkxmw==","signatures":[{"sig":"MEYCIQCIzfC/xraIQSq+8qtsBU96Yxeb4yy1I+L3ec07SUK+NgIhAOzU1O77bc9a40jX4TsdNG1cdagCxecdLJvA36TvRVQC","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}]},"main":"index.js","_from":".","_shasum":"1a2481b3d7af07db3473a37707eab801c1dce9af","gitHead":"b53093ebd319dc9b784c0d01badd10a2b4a05a7c","scripts":{"test":"mocha test"},"_npmUser":{"name":"moxtradeveloper","email":"jonathan@moxtra.com"},"repository":{"url":"git+https://github.com/Moxtra/moxtra-bot-sdk-nodejs.git","type":"git"},"_npmVersion":"2.15.5","description":"Moxtra Bot SDK","directories":{},"_nodeVersion":"4.4.5","dependencies":{"crypto":"0.0.3","express":"^4.14.0","node-fetch":"^1.5.3","body-parser":"^1.15.2","jsonwebtoken":"^7.3.0","cookie-parser":"^1.4.3","eventemitter3":"^1.2.0","simple-oauth2":"^1.1.0"},"devDependencies":{"chai":"^3.5.0","mocha":"^2.5.3","sinon":"^1.17.4"},"_npmOperationalInternal":{"tmp":"tmp/moxtra-bot-sdk-1.0.0.tgz_1491869607263_0.3009540250059217","host":"packages-18-east.internal.npmjs.com"}},"1.0.1":{"name":"moxtra-bot-sdk","version":"1.0.1","keywords":["bot","chatbot","chat","sdk","moxtra"],"_id":"moxtra-bot-sdk@1.0.1","maintainers":[{"name":"moxtradeveloper","email":"jonathan@moxtra.com"}],"homepage":"https://github.com/Moxtra/moxtra-bot-sdk-nodejs#readme","bugs":{"url":"https://github.com/Moxtra/moxtra-bot-sdk-nodejs/issues"},"dist":{"shasum":"ad4be3b606ad1ca7bec9945773a4958b32c28916","tarball":"https://registry.npmjs.org/moxtra-bot-sdk/-/moxtra-bot-sdk-1.0.1.tgz","integrity":"sha512-FV4uNdwbnF9kX/82GeDgPIsgsStakb5DOA2dIqj3jZ1LA21x5uJ77N9WQPhsNEUTDYhIZ8vR0lrWiuJSjteguA==","signatures":[{"sig":"MEQCIAQaab9wg6cuR93u5UGCu+XtJGzCzmlyErDMHQg2KY0kAiBVUgTN9sSrixN4GEydg6otB++atPAZf/jXMESQJZzJdA==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}]},"main":"index.js","_from":".","_shasum":"ad4be3b606ad1ca7bec9945773a4958b32c28916","gitHead":"6f702d3204ebec3fac8646b7bb10f63593965c10","scripts":{"test":"mocha test"},"_npmUser":{"name":"moxtradeveloper","email":"developer@moxtra.com"},"repository":{"url":"git+https://github.com/Moxtra/moxtra-bot-sdk-nodejs.git","type":"git"},"_npmVersion":"2.15.5","description":"Moxtra Bot SDK","directories":{},"_nodeVersion":"4.4.5","dependencies":{"crypto":"0.0.3","express":"^4.14.0","node-fetch":"^1.5.3","body-parser":"^1.15.2","jsonwebtoken":"^7.3.0","cookie-parser":"^1.4.3","eventemitter3":"^1.2.0","simple-oauth2":"^1.1.0"},"devDependencies":{"chai":"^3.5.0","mocha":"^2.5.3","sinon":"^1.17.4"},"_npmOperationalInternal":{"tmp":"tmp/moxtra-bot-sdk-1.0.1.tgz_1491960922032_0.3781591404695064","host":"packages-18-east.internal.npmjs.com"}},"1.0.2":{"name":"moxtra-bot-sdk","version":"1.0.2","keywords":["bot","chatbot","chat","sdk","moxtra"],"_id":"moxtra-bot-sdk@1.0.2","maintainers":[{"name":"moxtradeveloper","email":"jonathan@moxtra.com"}],"homepage":"https://github.com/Moxtra/moxtra-bot-sdk-nodejs#readme","bugs":{"url":"https://github.com/Moxtra/moxtra-bot-sdk-nodejs/issues"},"dist":{"shasum":"5d0935cdeba6312645d73203ab4ef45eb398fa9e","tarball":"https://registry.npmjs.org/moxtra-bot-sdk/-/moxtra-bot-sdk-1.0.2.tgz","integrity":"sha512-T02mQdJys6ehZdCUbbtvOmdgM3SpxJMVx0pmpgsLxIxQ6bhTU5ZdHQ7Oe1aVLhJ2igmDKqK/5PlbnYssVOUCuA==","signatures":[{"sig":"MEYCIQCo36Y3RQlRm4ZR2qbB5wZCM80hD4cRTK/N5ERSDrRr3gIhAND7I0aALfJM9ARrArxkcmxUSIn41TsjrxmF+Xgt7s1y","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}]},"main":"index.js","_from":".","_shasum":"5d0935cdeba6312645d73203ab4ef45eb398fa9e","scripts":{"test":"mocha test"},"_npmUser":{"name":"moxtradeveloper","email":"developer@moxtra.com"},"repository":{"url":"git+https://github.com/Moxtra/moxtra-bot-sdk-nodejs.git","type":"git"},"_npmVersion":"2.15.5","description":"Moxtra Bot SDK","directories":{},"_nodeVersion":"4.4.5","dependencies":{"crypto":"0.0.3","express":"^4.14.0","node-fetch":"^1.5.3","body-parser":"^1.15.2","jsonwebtoken":"^7.3.0","cookie-parser":"^1.4.3","eventemitter3":"^1.2.0","simple-oauth2":"^1.1.0"},"devDependencies":{"chai":"^3.5.0","mocha":"^2.5.3","sinon":"^1.17.4"},"_npmOperationalInternal":{"tmp":"tmp/moxtra-bot-sdk-1.0.2.tgz_1493665590168_0.6215000762604177","host":"packages-12-west.internal.npmjs.com"}},"1.0.3":{"name":"moxtra-bot-sdk","version":"1.0.3","keywords":["bot","chatbot","chat","sdk","moxtra"],"_id":"moxtra-bot-sdk@1.0.3","maintainers":[{"name":"moxtradeveloper","email":"jonathan@moxtra.com"}],"homepage":"https://github.com/Moxtra/moxtra-bot-sdk-nodejs#readme","bugs":{"url":"https://github.com/Moxtra/moxtra-bot-sdk-nodejs/issues"},"dist":{"shasum":"89d4f6068f8f4ab6376e807b7664af825574527c","tarball":"https://registry.npmjs.org/moxtra-bot-sdk/-/moxtra-bot-sdk-1.0.3.tgz","integrity":"sha512-bUuSAOs8HZJQ2BIQpYnu0XXc6mXPl3njby0T1MhlOh5qWJWRnygURgRjGkwdDr2G6fdiInVuznpwr03dggyOPQ==","signatures":[{"sig":"MEUCIB3u2zyigeey7T0+hFcYITgnGJYPaT3uacL+nliKNVmqAiEA1Gq6/+lfKPs/5KpuGDa/ndf099839f3Hzn4x+t7HfEY=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}]},"main":"index.js","_from":".","_shasum":"89d4f6068f8f4ab6376e807b7664af825574527c","scripts":{"test":"mocha test"},"_npmUser":{"name":"moxtradeveloper","email":"developer@moxtra.com"},"repository":{"url":"git+https://github.com/Moxtra/moxtra-bot-sdk-nodejs.git","type":"git"},"_npmVersion":"2.15.5","description":"Moxtra Bot SDK","directories":{},"_nodeVersion":"4.4.5","dependencies":{"crypto":"0.0.3","express":"^4.14.0","node-fetch":"^1.5.3","body-parser":"^1.15.2","jsonwebtoken":"^7.3.0","cookie-parser":"^1.4.3","eventemitter3":"^1.2.0","simple-oauth2":"^1.1.0"},"devDependencies":{"chai":"^3.5.0","mocha":"^2.5.3","sinon":"^1.17.4"},"_npmOperationalInternal":{"tmp":"tmp/moxtra-bot-sdk-1.0.3.tgz_1493680879606_0.7817843162920326","host":"packages-12-west.internal.npmjs.com"}},"1.0.4":{"name":"moxtra-bot-sdk","version":"1.0.4","keywords":["bot","chatbot","chat","sdk","moxtra"],"_id":"moxtra-bot-sdk@1.0.4","maintainers":[{"name":"moxtradeveloper","email":"jonathan@moxtra.com"}],"homepage":"https://github.com/Moxtra/moxtra-bot-sdk-nodejs#readme","bugs":{"url":"https://github.com/Moxtra/moxtra-bot-sdk-nodejs/issues"},"dist":{"shasum":"a6992d715c7af1de8367c8f7032638aeda84d0bc","tarball":"https://registry.npmjs.org/moxtra-bot-sdk/-/moxtra-bot-sdk-1.0.4.tgz","integrity":"sha512-hGhAUTolna+NlDYeLBlGuJkcsOI6teU7X4j+YhuVI/jw3CS2vv+24srh364+cL/4LdlKK6FtUJYDvqjchmCbFg==","signatures":[{"sig":"MEUCIBOHfFDYU71Xi5FdR08UOKTXP1dsz057zIU0Kfe0ZKJjAiEAqsgXis7qQmWfiHESia4A3INP5+9pXgOmLhGq64HmHjI=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}]},"main":"index.js","_from":".","_shasum":"a6992d715c7af1de8367c8f7032638aeda84d0bc","scripts":{"test":"mocha test"},"_npmUser":{"name":"moxtradeveloper","email":"developer@moxtra.com"},"repository":{"url":"git+https://github.com/Moxtra/moxtra-bot-sdk-nodejs.git","type":"git"},"_npmVersion":"2.15.5","description":"Moxtra Bot SDK","directories":{},"_nodeVersion":"4.4.5","dependencies":{"crypto":"0.0.3","express":"^4.14.0","node-fetch":"^1.5.3","body-parser":"^1.15.2","jsonwebtoken":"^7.3.0","cookie-parser":"^1.4.3","eventemitter3":"^1.2.0","simple-oauth2":"^1.1.0"},"devDependencies":{"chai":"^3.5.0","mocha":"^2.5.3","sinon":"^1.17.4"},"_npmOperationalInternal":{"tmp":"tmp/moxtra-bot-sdk-1.0.4.tgz_1493750586432_0.01358456490561366","host":"packages-12-west.internal.npmjs.com"}},"1.0.5":{"name":"moxtra-bot-sdk","version":"1.0.5","keywords":["bot","chatbot","chat","sdk","moxtra"],"_id":"moxtra-bot-sdk@1.0.5","maintainers":[{"name":"moxtradeveloper","email":"jonathan@moxtra.com"}],"homepage":"https://github.com/Moxtra/moxtra-bot-sdk-nodejs#readme","bugs":{"url":"https://github.com/Moxtra/moxtra-bot-sdk-nodejs/issues"},"dist":{"shasum":"c9a28c249db08f8f4121d917de939adc1e3f9605","tarball":"https://registry.npmjs.org/moxtra-bot-sdk/-/moxtra-bot-sdk-1.0.5.tgz","integrity":"sha512-1/hqKaRbvI/H+EDa8RgPXi5IcjRQuY6qPfGjjqM4kZ9L2v9M7E1ANuWW4RpgCZL9vq7LC44Nx7Ubpyr/xU0dAA==","signatures":[{"sig":"MEQCIG9NVkrKH2bmSfUc/W0Rk8cb7iDJgJhj23d/Ow10PhrFAiBhFg1nIc50q/Rtga5gRHGdevPGQDyTl1g4eZ+RMAZlLg==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}]},"main":"index.js","_from":".","_shasum":"c9a28c249db08f8f4121d917de939adc1e3f9605","scripts":{"test":"mocha test"},"_npmUser":{"name":"moxtradeveloper","email":"developer@moxtra.com"},"repository":{"url":"git+https://github.com/Moxtra/moxtra-bot-sdk-nodejs.git","type":"git"},"_npmVersion":"2.15.5","description":"Moxtra Bot SDK","directories":{},"_nodeVersion":"4.4.5","dependencies":{"crypto":"0.0.3","express":"^4.14.0","node-fetch":"^1.5.3","body-parser":"^1.15.2","jsonwebtoken":"^7.3.0","cookie-parser":"^1.4.3","eventemitter3":"^1.2.0","simple-oauth2":"^1.1.0"},"devDependencies":{"chai":"^3.5.0","mocha":"^2.5.3","sinon":"^1.17.4"},"_npmOperationalInternal":{"tmp":"tmp/moxtra-bot-sdk-1.0.5.tgz_1493774273783_0.7602304634638131","host":"packages-18-east.internal.npmjs.com"}},"1.1.0":{"name":"moxtra-bot-sdk","version":"1.1.0","keywords":["bot","chatbot","chat","sdk","moxtra"],"_id":"moxtra-bot-sdk@1.1.0","maintainers":[{"name":"moxtradeveloper","email":"jonathan@moxtra.com"}],"homepage":"https://github.com/Moxtra/moxtra-bot-sdk-nodejs#readme","bugs":{"url":"https://github.com/Moxtra/moxtra-bot-sdk-nodejs/issues"},"dist":{"shasum":"bec3f979e75093826af2cc7f0a08416291338a3c","tarball":"https://registry.npmjs.org/moxtra-bot-sdk/-/moxtra-bot-sdk-1.1.0.tgz","integrity":"sha512-z7mW8iTDimqxxXYb0Lh5ji2oLC3BnZqfYp8oq3d706COkjwyxRG7cBFdZ/98xQFGmIGJuJBYPV6KNixxNZpBLA==","signatures":[{"sig":"MEQCIHMA9WGUdjYGAhrwkYbkdh5dy/BeZ89spmTap2Wzp0jgAiBAmZSgy7QJg24hRHP3DNFiluV4oeOwgg/FAcnxHuixKw==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}]},"main":"index.js","_from":".","_shasum":"bec3f979e75093826af2cc7f0a08416291338a3c","scripts":{"test":"mocha test"},"_npmUser":{"name":"moxtradeveloper","email":"developer@moxtra.com"},"repository":{"url":"git+https://github.com/Moxtra/moxtra-bot-sdk-nodejs.git","type":"git"},"_npmVersion":"2.15.5","description":"Moxtra Bot SDK","directories":{},"_nodeVersion":"4.4.5","dependencies":{"crypto":"0.0.3","express":"^4.14.0","form-data":"^2.2.0","node-fetch":"^1.5.3","body-parser":"^1.15.2","jsonwebtoken":"^7.3.0","cookie-parser":"^1.4.3","eventemitter3":"^1.2.0","simple-oauth2":"^1.1.0"},"devDependencies":{"chai":"^3.5.0","mocha":"^2.5.3","sinon":"^1.17.4"},"_npmOperationalInternal":{"tmp":"tmp/moxtra-bot-sdk-1.1.0.tgz_1497576464585_0.31387005583383143","host":"s3://npm-registry-packages"}},"2.0.0":{"name":"moxtra-bot-sdk","version":"2.0.0","keywords":["bot","chatbot","chat","sdk","moxtra"],"_id":"moxtra-bot-sdk@2.0.0","maintainers":[{"name":"moxtradeveloper","email":"jonathan@moxtra.com"}],"homepage":"https://github.com/Moxtra/moxtra-bot-sdk-nodejs#readme","bugs":{"url":"https://github.com/Moxtra/moxtra-bot-sdk-nodejs/issues"},"dist":{"shasum":"4d5e26314404535291267531c59e4ad6916003e7","tarball":"https://registry.npmjs.org/moxtra-bot-sdk/-/moxtra-bot-sdk-2.0.0.tgz","integrity":"sha512-chlAFyW1cpFrqfBAxsOJ7Tndem5pFxAmOU2hJy5UdnIrUWXyQ9ive2SGehQ+cnA/J0b9IbxSbQZT1e0r58USPw==","signatures":[{"sig":"MEQCIGnjX6l7WL4kzz4t2on4T84k3cNzKvgqo+BB+6RIdJYQAiBuPnhxentDqLUMHFa+y7Mndn2t1yeWGdkNKcDQhaN4ZA==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh2zsNCRA9TVsSAnZWagAArpMP/3VOE2TpJbRdRGrqy2gB\nMuNrxe0hqDY2eqJmZuznQ9a2Uc8P17TfuUqqbY/6pJswjiO8mvkcEjVNejCH\nIacN2Jcb399nlhLlbheS5jcIzlBxAbXQ4mGTQfiORk9/GRLHOQe/rVALYCMK\nDeh0jVZ7XHuYve18lsGAmZPR5B/1atKPNw+fU5WxkGanI+xeqbq8C1HfE9o5\nhJDeyvktgQ6PVLM/cJ0bZ+X38fFAulXvL3350VAZvdHGthqD5ESz7BC18b1K\nWUEg+8pXgoKVZd8oxhMK4Yec+Tbt4dJ+e9oJj9Bj9Sn75x4pYeIKFWTwYXEt\nB81jEngiaqPtrgAxjcG/PFu90zK2M0VOO5JGRhq4IWhn30I2J2BMiNApE8/w\n5iFwrP9lqBBInbdtObtFbw9Zbx/E1aYFJtGOysB6zHg06G04+wyvVP4lR31w\nGmVF+LOqwv73UdZyefWbJlGHg0He8ycSxPYy7ET0LjWCWT8ti/D8EPghV8Kf\nSd9zoDo+bSE9XRvTUtVciHH9NJ01AV3MNkRwRsXVN9rERWH37FCsa2/pA6uD\nGMD3aUiUcZ8jyfvUV8dPEViUVVGAHUSsGWhBSFeSQNW9RbyR/ic690/Nzj4B\naEfIvWh3BhRlOlxPO99inry/N4RceyD9vGKUo2U/uuS9nRCETM4nEAwoNmLC\nMX0S\r\n=7POb\r\n-----END PGP SIGNATURE-----\r\n"},"main":"index.js","_from":".","_shasum":"4d5e26314404535291267531c59e4ad6916003e7","gitHead":"4d4d0caa86df4e7fb84789038868e89940b1b81b","scripts":{"test":"mocha test"},"_npmUser":{"name":"moxtradeveloper","email":"developer@moxtra.com"},"repository":{"url":"git+https://github.com/Moxtra/moxtra-bot-sdk-nodejs.git","type":"git"},"_npmVersion":"2.15.5","description":"Moxtra Bot SDK","directories":{},"_nodeVersion":"4.4.5","dependencies":{"crypto":"0.0.3","express":"^4.14.0","form-data":"^2.2.0","node-fetch":"^1.5.3","body-parser":"^1.15.2","jsonwebtoken":"^8.0.1","cookie-parser":"^1.4.3","eventemitter3":"^1.2.0","simple-oauth2":"^1.1.0","urlsafe-base64":"^1.0.0"},"devDependencies":{"chai":"^3.5.0","mocha":"^2.5.3","sinon":"^1.17.4"},"_npmOperationalInternal":{"tmp":"tmp/moxtra-bot-sdk-2.0.0.tgz_1508433824593_0.44663294684141874","host":"s3://npm-registry-packages"}}},"time":{"created":"2017-04-11T00:13:29.095Z","modified":"2026-06-25T23:15:24.505Z","1.0.0":"2017-04-11T00:13:29.095Z","1.0.1":"2017-04-12T01:35:23.819Z","1.0.2":"2017-05-01T19:06:30.452Z","1.0.3":"2017-05-01T23:21:19.854Z","1.0.4":"2017-05-02T18:43:06.666Z","1.0.5":"2017-05-03T01:17:55.533Z","1.1.0":"2017-06-16T01:27:44.732Z","2.0.0":"2017-10-19T17:23:45.218Z"},"bugs":{"url":"https://github.com/Moxtra/moxtra-bot-sdk-nodejs/issues"},"homepage":"https://github.com/Moxtra/moxtra-bot-sdk-nodejs#readme","keywords":["bot","chatbot","chat","sdk","moxtra"],"repository":{"url":"git+https://github.com/Moxtra/moxtra-bot-sdk-nodejs.git","type":"git"},"description":"Moxtra Bot SDK","maintainers":[{"email":"developer@moxtra.com","name":"moxtradev"},{"email":"nitin.nigam@moxo.com","name":"nigam.nitin"},{"email":"punith.bandodekar@moxo.com","name":"punithbandodekar"}],"readme":"# Moxtra Bot SDK\r\n\r\n[![npm](https://img.shields.io/badge/npm-2.0.0-orange.svg)](https://www.npmjs.com/package/moxtra-bot-sdk)\r\n[![David](https://img.shields.io/david/strongloop/express.svg)](https://github.com/Moxtra/moxtra-bot-sdk-nodejs.git)\r\n[![Packagist](https://img.shields.io/packagist/l/doctrine/orm.svg)](https://spdx.org/licenses/MIT)\r\n\r\nMoxtra Bot SDK will ease and streamline the Bot development for Moxtra's business collaboration platform. The design allows developers to focus on application logic instead of APIs for sending and receiving data payload.\r\n\r\n```js\r\nconst MoxtraBot = require('moxtra-bot-sdk');\r\n\r\nconst bot = new MoxtraBot({\r\n  client_id: 'YOUR_CLIENT_ID',\r\n  client_secret: 'YOUR_CLIENT_SECRET',\r\n  api_endpoint: 'https://apisandbox.moxtra.com/v1'\r\n});\r\n\r\nbot.on('message', (chat) => {\r\n  const username = chat.username;\r\n  const text = chat.comment.text;\r\n  \r\n  // obtain access_token    \r\n  bot.getAccessToken(chat.client_id, chat.org_id, function(error, token) {\r\n\r\n    if (error) {\r\n      // error happens\r\n    \r\n    } else {\r\n      chat.setAccessToken(token.access_token);\r\n  \r\n      chat.sendText(`Echo: @${username} ${text}`);\r\n    }\r\n  });  \r\n});\r\n```\r\n\r\n| [Core Concepts][] | [Installation][] | [Getting Started][] | [Account Linking][] | [Documentation][] | [Examples][] | [License][] |\r\n|---|---|---|---|---|---|---|\r\n\r\n---\r\n\r\n## Core Concepts\r\n\r\n- Definitions:\r\n>* **Bot application** (the 3rd party Bot) has a corresponding **bot app** configuration in Moxtra. \r\n>* Each **bot app** is identified by *client_id* in the message event. \r\n>* `Bot app` becomes a **bot user** (an org user) once the `bot app` is enabled in an org. This **bot user** is identified by *org_id* in the message event. \r\n\r\n- Bot lifecycle: \r\n>* Partner Admin creates a **bot app** configuration via Partner Admin Console\r\n>* An Org Admin enables this `bot app` via Org Admin Console or API, this `bot app` becomes a **bot user** inside this org. A *bot_enabled* event gets generated.\r\n>* When the Org Admin disabled this `bot app` via Org Admin Console or API, a *bot_disabled* event gets generated.    \r\n>* Binder users of the same org as the binder owner can then add this **bot user** into the binder. A *bot_installed* event gets generated.\r\n>* When the **bot user** leaves the binder, a *bot_uninstalled* event gets generated.\r\n\r\n- Each received message event has the corresponding *binder_id*, *client_id*, and *org_id*, which are encapsulated in the `Chat` object. `Bot application` can use *client_id*, *org_id*, and *timestamp* to generate a *signature* signed with *client_secret* to create a `bot user` **access_token**.  \r\n\r\n- Each `POST` message event from Moxtra has `x-moxtra-signature` header set as HMA-SHA1 hash of the message content signed with `client_secret` \r\n\r\n- Different message event has the corresponding object in the event; however, the basic message structure remains the same. Below shows a `Comment` message event format:\r\n```js\r\n{\r\n  message_id: 'MESSAGE_ID',\r\n  message_type: 'comment_posted',\r\n  binder_id: 'BINDER_ID',\r\n  client_id: 'CLIENT_ID',\r\n  org_id: 'ORG_ID',\r\n  event: {\r\n    timestamp: 'TIMESTAMP',\r\n    user: {\r\n      id: 'USER_ID',\r\n      name: 'USERNAME'\r\n    },\r\n    comment: {\r\n      id: 'COMMENT_ID',\r\n      text: 'TEXT MESSAGE',\r\n      audio: 'AUDIO MESSAGE'    \r\n    },\r\n    target: {\r\n      id: 'BINDER_ID',\r\n      object_type: 'binder'\r\n    }\r\n  }\r\n}\r\n```\r\n\r\n## Installation\r\n\r\n```\r\n$ npm install moxtra-bot-sdk --save\r\n```\r\n\r\nYou can also check out Moxtra Bot SDK directly from Git.\r\nIf you want to use the example code and included bots, it may be preferable to use Github over NPM.\r\n\r\n```bash\r\ngit clone https://github.com/Moxtra/moxtra-bot-sdk-nodejs.git\r\n```\r\n\r\n## Getting Started\r\n\r\n- After MoxtraBot is installed via NPM, create a new bot instance using your `client_id` and `client_secret` obtained from your [Manage Bots in Partner Admin Console](https://admin.moxtra.com) in the `bot app` creation.\r\n\r\n- Set correct API endpoint for different environment:\r\n>[Sandbox] api_endpoint:`'https://apisandbox.moxtra.com/v1'`     \r\n>[Production] api_endpoint:`'https://api.moxtra.com/v1'`\r\n\r\n```js\r\nconst MoxtraBot = require('moxtra-bot-sdk');\r\n\r\nconst bot = new MoxtraBot({\r\n  client_id: 'YOUR_CLIENT_ID',\r\n  client_secret: 'YOUR_CLIENT_SECRET',\r\n  api_endpoint: 'https://apisandbox.moxtra.com/v1'\r\n});\r\n```\r\n\r\n- Subscribe to messages with the `bot.on()` method for various events: *message*, *bot_enabled*, *bot_disabled*, *bot_installed*, *bot_uninstalled*, *postback*,\r\nand *account_link*. \r\n\r\n```js\r\nbot.on('message', (chat) => {\r\n  const username = chat.username;\r\n  const text = chat.comment.text;\r\n  console.log(`@${username} says ${text}`);\r\n});\r\n\r\nbot.on('bot_enabled', (chat) => {\r\n\r\n  const bot_name = chat.bot.name;\r\n  const org_id = chat.org_id;\r\n  \r\n  console.log(`Bot ${bot_name} enabled on ${org_id}`);  \r\n});\r\n\r\nbot.on('bot_disabled', (chat) => {\r\n\r\n  const bot_name = chat.bot.name;\r\n  const org_id = chat.org_id;\r\n  \r\n  console.log(`Bot ${bot_name} disabled on ${org_id}`);\r\n});\r\n\r\nbot.on('bot_installed', (chat) => {\r\n\r\n  const username = chat.username;\r\n  const bot_name = chat.bot.name;\r\n  \r\n  // obtain access_token    \r\n  bot.getAccessToken(chat.client_id, chat.org_id, function(error, token) {\r\n\r\n    if (error) {\r\n      // error happens\r\n    \r\n    } else {\r\n      chat.setAccessToken(token.access_token);\r\n  \r\n      chat.sendText(`@${username} Welcome to ${bot_name}!!`);\r\n    }\r\n  });\r\n  \r\n});\r\n\r\nbot.on('bot_uninstalled', (chat) => {\r\n\r\n  const bot_name = chat.bot.name;\r\n  const binder_id = chat.binder_id;\r\n  \r\n  console.log(`Bot ${bot_name} uninstalled on ${binder_id}`);\r\n});\r\n```\r\nOther message events are *page_created*, *file_uploaded*, *page_annotated*, *todo_created*, *todo_completed* and *meet_recording_ready*.\r\n\r\n- Subscribe to messages with the `bot.hears()` method using regular expression or exact keyword match:\r\n\r\n```js\r\nbot.hears([/(schedule|plan|have)? meet/i, 'meeting together'], (chat) => {\r\n  const username = chat.username;\r\n  console.log('@${username} said \"schedule... meet\", \"plan... meet\", \"have... meet\", or \"meeting together\"');\r\n});\r\n```\r\n\r\n- Reply to messages using the `chat` object:\r\n\r\n```js\r\nbot.hears([/(schedule|plan|have)? meet/i, 'meeting together'], (chat) => {\r\n  const username = chat.username;\r\n\r\n  // obtain access_token    \r\n  bot.getAccessToken(chat.client_id, chat.org_id, function(error, token) {\r\n\r\n    if (error) {\r\n      // error happens\r\n    \r\n    } else {\r\n      chat.setAccessToken(token.access_token);\r\n  \r\n      chat.sendText(`@${username} do you need to schedule a meet?`);\r\n    }\r\n  });\r\n});\r\n```\r\n>- Obtain access_token\r\n>```js\r\n>bot.getAccessToken(chat.client_id, chat.org_id, function(error, token) {\r\n>});\r\n>```\r\n>- Set access_token before sending message\r\n>```js\r\n>chat.setAccessToken(token.access_token);\r\n>```\r\n>- Send Text  \r\n>```js\r\n>chat.sendText(`@${username} do you need to schedule a meet?`);\r\n>```\r\n>\r\n>- Send RichText (in [BBCode](https://www.bbcode.org) style)  \r\n>```js\r\n>chat.sendRichText(`@[b]${username}[/b] [i][color=Blue]do you need to schedule a meet?[/color][/i]`); \r\n>```\r\n>\r\n>- Send JSON (in key-value style along with a pre-configured or an on-demand template)  \r\n>```js\r\n>const fields = {  \r\n>   title: 'BBCode Info',  \r\n>   from: username,  \r\n>   info: text,  \r\n>   image_url: 'https://www.bbcode.org/images/lubeck_small.jpg'  \r\n>};  \r\n>chat.sendJSON(fields);  \r\n>```\r\n>\r\n>- Upload File or Add Audio Comment for audio file (audio/x-m4a, audio/3gpp)\r\n>```js\r\n>var options = {};\r\n>options.file_path = `${__dirname}/examples/Upload/start.png`;\r\n>options.audio_path = `${__dirname}/examples/Upload/test_comment.3gpp`;\r\n>  \r\n>chat.sendText(`@${username} upload files`, null, options);\r\n>```\r\n\r\n- Matching keywords for more than once:\r\n\r\nIf there are more than one keyword matches in `bot.hears()`, the same handler as well as *bot.on('message')* event handler would get invoked. By checking `chat.condition` to determine whether to handle in this situation. You can turn off the generic handler in case there are keywords matches via **bot.setGenericHandling(false);**\r\n\r\n`chat.condition.match` - the word that matches the regular expression or the whole keyword  \r\n`chat.condition.primatches` - the number of times that match happened before \r\n\r\n\r\n```js\r\nbot.on('message', (chat) => {\r\n  const username = chat.username;\r\n  const text = chat.comment.text;\r\n\r\n  const condition = chat.condition;\r\n\r\n  if (condition && condition.primatches > 0) {\r\n    // don't handle message again\r\n    console.log(`message has been handled: @${username} ${text} for ${condition.primatches} times`);\r\n    return;\r\n  }\r\n  \r\n  // obtain access_token    \r\n  bot.getAccessToken(chat.client_id, chat.org_id, function(error, token) {\r\n\r\n    if (error) {\r\n      // error happens\r\n    \r\n    } else {\r\n      chat.setAccessToken(token.access_token);\r\n  \r\n      chat.sendText(`Echo: @${username} ${text}`);\r\n    }\r\n  });\r\n};  \r\n\r\nbot.hears([/(schedule|plan|have)? meet/i, 'meeting together'], (chat) => {\r\n  const username = chat.username;\r\n  const text = chat.comment.text;\r\n\r\n  const condition = chat.condition;\r\n\r\n  if (condition) {  \r\n    if (condition.primatches > 0) {\r\n      // handle message again\r\n      console.log(`message has been handled: @${username} ${text} on ${condition.match} for ${condition.primatches} times`);\r\n      return;\r\n    } else {\r\n      console.log(`message for: @${username} ${text} on ${condition.match}`);    \r\n    }\r\n  }\r\n\r\n  // obtain access_token    \r\n  bot.getAccessToken(chat.client_id, chat.org_id, function(error, token) {\r\n\r\n    if (error) {\r\n      // error happens\r\n    \r\n    } else {\r\n      chat.setAccessToken(token.access_token);\r\n  \r\n      chat.sendText(`@${username} do you need to schedule a meet?`);\r\n    }\r\n  });\r\n});\r\n```\r\n\r\n- Add `POSTBACK` button to the reply: \r\n\r\n>- `buttons` array  \r\n>Setting desired buttons in an array which would form buttons in a single column layout \r\n>- `POSTBACK` object   \r\n>&nbsp;type - \"postpack\"  \r\n>&nbsp;text - required text shown on the button   \r\n>&nbsp;payload - optional info to carry back; if not specified, it's \"MOXTRABOT_text in uppercase\" \r\n>\r\n>- A single string can also turn into a button object; for example 'Not Sure?' becomes  \r\n>{  \r\n>&nbsp;type: 'postpack',  \r\n>&nbsp;text: 'Not Sure?',   \r\n>&nbsp;payload: 'MOXTRABOT_NOT SURE?\"   \r\n>}\r\n\r\n```js\r\n  var buttons = [{\r\n    type: 'postback',\r\n    text: 'Sure!'\r\n  }, 'Not Sure?'];\r\n  \r\n  chat.sendText(`@${username} do you need to schedule a meet?`, buttons);\r\n```\r\nor\r\n```js\r\n  chat.sendRichText(`${richtext}`, buttons);    \r\n```\r\nor\r\n```js\r\n  chat.sendJSON(fields, buttons);\r\n```\r\n\r\n- Handle *postback* event:  \r\n\r\nThe *postback* event gets triggered when the `POSTBACK` button is tapped. The corresponding *postback:TEXT* gets triggered as well.  \r\n\r\n```js\r\nbot.on('postback:Not Sure?', (chat) => {\r\n  \r\n  const username = chat.username;\r\n  const text = chat.postback.text;\r\n  const payload = chat.postback.payload;\r\n\r\n  // obtain access_token    \r\n  bot.getAccessToken(chat.client_id, chat.org_id, function(error, token) {\r\n\r\n    if (error) {\r\n      // error happens\r\n    \r\n    } else {\r\n      chat.setAccessToken(token.access_token);\r\n  \r\n      chat.sendText(`@${username} specific postback ${text} ${payload}`);\r\n    }\r\n  });\r\n});\r\n\r\nbot.on('postback', (chat) => {\r\n  \r\n  const username = chat.username;\r\n  const text = chat.postback.text;\r\n  const payload = chat.postback.payload;\r\n\r\n  // obtain access_token    \r\n  bot.getAccessToken(chat.client_id, chat.org_id, function(error, token) {\r\n\r\n    if (error) {\r\n      // error happens\r\n    \r\n    } else {\r\n      chat.setAccessToken(token.access_token);\r\n  \r\n      chat.sendText(`@${username} generic postback ${text} ${payload}`);\r\n    }\r\n  });\r\n});\r\n```\r\n\r\n- Set up Express server:\r\n\r\nYou can setup your own preferred web application framework as well as endpoints for handling `GET` and `POST` methods. In the example, we use `Express` to showcase how it is to be done and using `/webhooks` as the endpoints.  \r\n\r\n```js\r\nconst express = require('express');\r\nconst bodyParser = require('body-parser');\r\n\r\nconst app = express();\r\n\r\nconst MoxtraBot = require('moxtra-bot-sdk');\r\n\r\nconst bot = new MoxtraBot({\r\n  client_id: 'YOUR_CLIENT_ID',\r\n  client_secret: 'YOUR_CLIENT_SECRET',\r\n  api_endpoint: 'https://apisandbox.moxtra.com/v1'\r\n});\r\n\r\napp.use(bodyParser.urlencoded({ extended: false }));\r\napp.use(bodyParser.json( { verify: bot.verifyRequestSignature.bind(bot) }));\r\n\r\n// handle account_link\r\napp.get('/webhooks', (req, res, next) => {\r\n  bot.handleGetRequest(req, res, next);\r\n});\r\n\r\n// handle message events\r\napp.post('/webhooks', (req, res, next) => {\r\n  bot.handlePostRequest(req, res, next);\r\n});  \r\n```\r\n\r\n## Account Linking\r\n\r\n- Link Moxtra account with the 3rd party service through Account Linking flow:\r\n>1. User sends a request to Bot (**Bot application**), which requires access to a 3rd party service that needs user's authorization. Bot does not have prior user account linking info with the 3rd party service.\r\n>2. Bot sends `ACCOUNT_LINK` button back to Moxtra chat.\r\n>3. User clicks the button and a [JSON web token](https://en.wikipedia.org/wiki/JSON_Web_Token) sends back to Bot via the `GET` method.\r\n>4. Bot verifies the token using `client_secret` as the key and decodes the token; Bot obtains *user_id*, *username*, *binder_id*, *client_id*, and *org_id* via handling the *bot.on('account_link')* event.\r\n>5. Bot needs to check whether the *user_id* having the corresponding *access_token* from the 3rd party service in case `ACCOUNT_LINK` button might be tapped more than once or by different users in a group chat. If no, next OAuth2 authorization flow would then follows.\r\n>6. After obtaining the *access_token* from the 3rd party service, Bot needs to complete the original request.\r\n\r\n- Add `ACCOUNT_LINK` button:\r\n```js\r\n  var buttons = [{\r\n    type: 'account_link',\r\n    text: 'Sign In'\r\n  }];\r\n  \r\n  chat.sendText(`@${username} do you need to schedule a meet?`, buttons);\r\n```\r\n- Handle *account_link* event:\r\n```js \r\nbot.on('account_link', (req, res, data) => {\r\n  \r\n  const user_id = data.user_id;\r\n  const username = data.username;\r\n  const binder_id = data.binder_id;\r\n  const client_id = data.client_id;\r\n  const org_id = data.org_id;\r\n  \r\n  // obtain from pendingResponse\r\n  var chat = _pendingResponse[ binder_id + user_id ];\r\n  \r\n  if (chat) {\r\n\r\n    // obtain access_token    \r\n    bot.getAccessToken(chat.client_id, chat.org_id, function(error, token) {\r\n\r\n      if (error) {\r\n        // error happens\r\n    \r\n      } else {\r\n        chat.setAccessToken(token.access_token);\r\n  \r\n        chat.sendText(`@${username} performs an account_link for user_id: ${user_id} on binder_id: ${binder_id}`);\r\n      }\r\n    });    \r\n\r\n  } else {\r\n    chat = _pendingOAuth[ user_id ];  \r\n  }\r\n  \r\n  // saved linked accessToken from the 3rd party service\r\n  const accessToken = _accountLinked[ user_id ];\r\n  \r\n  if (accessToken) {  \r\n    \r\n    if (!chat) {\r\n      console.log('Unable to get pending request!');\r\n    \r\n      // obtain access_token    \r\n      bot.getAccessToken(client_id, org_id, function(error, token) {\r\n\r\n        if (error) {\r\n          // error happens\r\n    \r\n        } else {        \r\n          // create a new Chat\r\n          chat = new Chat(bot);\r\n        \r\n          chat.setAccessToken(token.access_token);\r\n  \r\n          chat.sendText(`@${username} has already obtained access_token from the 3rd party service!`);\r\n        }\r\n      });        \r\n      \r\n    } else {\r\n      // obtain access_token    \r\n      bot.getAccessToken(chat.client_id, chat.org_id, function(error, token) {\r\n\r\n        if (error) {\r\n          // error happens\r\n    \r\n        } else {        \r\n          chat.setAccessToken(token.access_token);\r\n  \r\n          chat.sendText(`@${username} has already obtained access_token from the 3rd party service!`);\r\n        }\r\n      });            \r\n\r\n    }\r\n\r\n    // close window    \r\n    res.send('<html><head></head><body onload=\"javascript:window.close();\"></body></html>');\r\n    \r\n  }  else { \r\n\r\n    delete _pendingResponse[ binder_id + user_id ];    \r\n\r\n    // save chat to the pendingOAuth \r\n    _pendingOAuth[ user_id ] = chat;\r\n\r\n    // redirect if needed  \r\n    res.cookie('user_id', user_id);\r\n    \r\n    res.redirect('/auth');    \r\n  }  \r\n  \r\n});\r\n```\r\n- Setup OAuth2 configuration and handle OAuth2 flow:\r\n\r\n```js\r\nconst OAuth2 = require('./lib/oauth2');\r\n\r\nconst config = {\r\n  client_id: \"YOUR_CLIENT_ID\",\r\n  client_secret: \"YOUR_CLIENT_SECRET\",\r\n  api_endpoint: \"https://apisandbox.moxtra.com/v1\",\r\n  oauth2_client_id: \"SERVICE_OAUTH2_CLIENT_ID\",\r\n  oauth2_client_secret: \"SERVICE_OAUTH2_CLIENT_SECRET\",\r\n  oauth2_endpoint: \"SERVICE_OAUTH2_ENDPOINT\",\r\n  oauth2_auth_path: \"SERVICE_OAUTH2_AUTH_PATH\",\r\n  oauth2_token_path: \"SERVICE_OAUTH2_TOKEN_PATH\",\r\n  oauth2_redirect_uri: \"SERVICE_OAUTH2_DIRECT_URI\"\r\n};\r\n\r\n...\r\nconst oauth2 = new OAuth2(bot, config);\r\n\r\n// OAuth2 request flow\r\napp.get('/auth', (req, res, next) => { \r\n  oauth2.auth(req, res, next);\r\n});\r\n\r\n// OAuth2 callback to obtain access_token\r\napp.get('/callback', (req, res, next) => {\r\n  oauth2.callback(req, res, next);\r\n});\r\n```\r\n\r\n- Obtain `access_token` via catching *access_token* event \r\n\r\n```js\r\n// after doing OAuth2 against the 3rd party service to obtain a user level access_token\r\nbot.on('access_token', (accessToken, req) => {\r\n\r\n  const user_id = req.cookies.user_id;\r\n  var chat;\r\n  \r\n  if (user_id) {\r\n    chat = _pendingOAuth[ user_id ];    \r\n    \r\n    // save linked accessToken\r\n    _accountLinked[ user_id ] = accessToken;\r\n  }\r\n  \r\n  if (chat) {\r\n    \r\n    delete _pendingOAuth[ user_id ];\r\n\r\n    // complete the pending request\r\n    const username = chat.username;\r\n    const text = chat.comment.text;\r\n    \r\n    // obtain access_token    \r\n    bot.getAccessToken(chat.client_id, chat.org_id, function(error, token) {\r\n\r\n      if (error) {\r\n        // error happens\r\n    \r\n      } else {\r\n        chat.setAccessToken(token.access_token);\r\n  \r\n        chat.sendText(`@${username} after account linked, bot will complete your request: ${text}`);\r\n      }\r\n    });\r\n  }\r\n\r\n  console.log(`Obtained access_token: ${accessToken.token.access_token} for user ${user_id}`);\r\n\r\n});\r\n```\r\n\r\n## Documentation\r\n\r\n### MoxtraBot Class\r\n\r\n#### `new MoxtraBot(configuration)`\r\n\r\n| `configuration` key | Type | Required |\r\n|:--------------|:-----|:---------|\r\n| `client_id` | string | `Y` |\r\n| `client_secret` | string | `Y` |\r\n| `api_endpoint` | string | `N` |\r\n\r\nCreates a new `MoxtraBot` instance. \r\n\r\n---\r\n\r\n### MoxtraBot API\r\n\r\n\r\n#### `.on(event, callback (parameters) )`\r\n\r\nSubscribe to the message event emitted by the bot, and a callback gets invoked with the parameter pertaining to the event type. Available events are:\r\n\r\n| Event | Callback Parameters | Description |\r\n|:------|:-----|:-----|\r\n| *bot_enabled* | Chat object - chat.bot | Org Admin enabled the `bot app` in one org |\r\n| *bot_disabled* | Chat object - chat.bot | Org Admin disabled the `bot app` from the org |\r\n| *bot_installed* | Chat object - chat.bot | A binder user added the `bot user` in the binder |\r\n| *bot_uninstalled* | Chat object - chat.bot | A binder user removed the `bot user` from the binder |\r\n| *message* | Chat object - chat.comment | The `Bot application` received a text message from the user by commenting in a binder |\r\n| *postback* | Chat object - chat.postback | The `Bot application` received a postback call from the user after  clicking a `POSTBACK` button |\r\n| *account_link* | Request, Response, JSON Web Token content | The `Bot application` received an account_link call from the user after clicking an `ACCOUNT_LINK` button |\r\n| *page_created* | Chat object - chat.page | The `Bot application` received a message that a page was created in the binder |\r\n| *file_uploaded* | Chat object - chat.file | The `Bot application` received a message that a file was uploaded in the binder |\r\n| *page_annotated* | Chat object - chat.annotate | The `Bot application` received a message that an annotation was created on a page in the binder |\r\n| *todo_created* | Chat object - chat.todo | The `Bot application` received a message that a todo item was created in the binder |\r\n| *todo_completed* | Chat object - chat.todo | The `Bot application` received a message that a todo item was completed in the binder |\r\n| *meet_recording_ready* | Chat object - chat.meet | The `Bot application` received a message that a meet recording was ready in the binder |\r\n\r\n##### Example:\r\n\r\n```js\r\nbot.on('message', (chat) => {\r\n  const text = chat.comment.text; \r\n  console.log(`message ${text} was received!`);\r\n});\r\n\r\nbot.on('postback', (chat) => {\r\n  const text = chat.postback.text;\r\n  const payload = chat.postback.payload;\r\n  console.log(`Postback ${text} with payload: ${payload} was received!`);\r\n});\r\n\r\nbot.on('file_uploaded', (chat) => {\r\n  const name = chat.file.name;\r\n  const file_id = chat.file.id;\r\n  console.log(`File:${file_id} ${name} was uploaded!`);\r\n});\r\n```\r\n\r\n#### `.hears(patterns, callback (chat) )`\r\n\r\nUsing pattern matching mechanism to handle desired message. The `patterns` param can be a string, a regex or an array of both strings and regexs that find matching against the received message. If a match was found, the callback gets invoked. At the same time, `message` event also gets fired.  `chat.condition` needs to be checked in such case.\r\n\r\n| Param | Type | Required |\r\n|:------|:-----|:---------|\r\n| `patterns` | string, regex or mixed array | `Y` |\r\n| `callback (chat)` | function | `Y` |\r\n\r\n##### Example:\r\n\r\n```js\r\nbot.hears([/(schedule|plan|have)? meet/i, 'meeting together'], (chat) => {\r\n\r\n  const username = chat.username;\r\n  const text = chat.comment.text;\r\n\r\n  const condition = chat.condition;\r\n\r\n  if (condition) {  \r\n    if (condition.primatches > 0) {\r\n      // handle message again\r\n      console.log(`message has been handled: @${username} ${text} on ${condition.match} for ${condition.primatches} times`);\r\n      return;\r\n    } else {\r\n      console.log(`message for: @${username} ${text} on ${condition.match}`);    \r\n    }\r\n  }\r\n\r\n  // obtain access_token    \r\n  bot.getAccessToken(chat.client_id, chat.org_id, function(error, token) {\r\n\r\n    if (error) {\r\n      // error happens\r\n   \r\n    } else {\r\n      chat.setAccessToken(token.access_token);\r\n  \r\n      chat.sendText(`@${username} do you need to schedule a meet?`);\r\n    }\r\n  });       \r\n});\r\n```\r\n---\r\n\r\n### Chat Class and API\r\n\r\n#### `new Chat(bot)`\r\n\r\nChat instance is created in the callback for each type of message event except *account_link*. Therefore, chat.binder_id, chat.user_id, chat.username, chat.client_id, and chat.org_id as well as the corresponding event object are pre-populated.  \r\n\r\nA typical `Chat` instance has the following structure:\r\n\r\n```js\r\n{\r\n  moxtrabot: {MoxtraBot instance},\r\n  data: {raw message event data},\r\n  binder_id: 'BINDER_ID',\r\n  client_id: 'CLIENT_ID',\r\n  org_id: 'ORG_ID',\r\n  access_token: {access_token for sending message},\r\n  condition: {\r\n    match: 'MATCHED_KEYWORD',\r\n    primatches: NUMBER_OF_MATCHES_BEFORE\r\n  },\r\n  user_id: 'USER_ID',\r\n  username: 'USERNAME',\r\n  // for bot_installed event\r\n  bot: {   \r\n    id: 'BOT_ID',\r\n    name: 'BOT_NAME'\r\n  },\r\n  // for message event\r\n  comment: {\r\n    id: 'COMMENT_ID',\r\n    text: 'TEXT MESSAGE',\r\n    audio: 'AUDIO MESSAGE'    \r\n  },\r\n  // for postback event\r\n  postback: {\r\n    text: 'POSTBACK_TEXT',\r\n    payload: 'POSTBACK_PAYLOAD'\r\n  },\r\n  // for page_created and page_annotated event\r\n  page: {\r\n    id: 'PAGE_ID',\r\n    type: 'PAGE_TYPE'\r\n  },  \r\n  // for file_uploaded event\r\n  file: {\r\n    id: 'FILE_ID',\r\n    name: 'FILE_NAME'\r\n  },\r\n  // for todo_created and todo_completed event\r\n  todo: {\r\n    id: 'TODO_ID',\r\n    name: 'TODO_ITEM_NAME'\r\n  },\r\n  // for meet_recording_ready event\r\n  meet: {\r\n    id: 'MEET_ID',\r\n    topic: 'MEET_TOPIC',\r\n    start_time: 'MEET_START_TIME',\r\n    end_time: 'MEET_END_TIME',\r\n    recording_url: 'MEET_RECORDING_URL'\r\n  },\r\n  // sendText API\r\n  sendText: function (text, buttons, options) {    \r\n  },\r\n  // sendRichText API\r\n  sendRichText: function (richtext, buttons, text, options) {    \r\n  },\r\n  // sendJSON API\r\n  sendJSON: function (fields, buttons, options) {    \r\n  },\r\n  // send API\r\n  send: function (message, options) {    \r\n  },\r\n  // sendRequest API\r\n  sendRequest: function (body, path, method) {    \r\n  },\r\n  // uploadRequest API\r\n  uploadRequest: function (body, file_path, audio_path) {    \r\n  },\r\n  // getBinderInfo API\r\n  getBinderInfo: function (callback) {    \r\n  }\r\n}\r\n```\r\n\r\n#### `buttons`\r\n\r\n`buttons` is a string, button, or mixed array. A typical `button` object structure is as follows:\r\n\r\n```js\r\n{\r\n  type: 'postback | account_link',\r\n  text: 'BUTTON_TEXT',\r\n  payload: 'BUTTON_PAYLOAD'\r\n}  \r\n```\r\n\r\n#### `options`\r\n\r\n`options` is an object that specify on-demand action type, fields_template array - which is used in `sendJSON()`, file_path for uploading a file attachment, and audio_path for adding audio comment for audio file (audio/x-m4a, audio/3gpp). A typical `options` has the following structure:\r\n\r\n```js\r\n{\r\n  action: 'chat | page | todo',\r\n  fields_template: [\r\n    {\r\n      template_type: 'text | richtext | page',\r\n      template: 'TEMPLATE'\r\n    }\r\n  ],\r\n  file_path: 'UPLOAD FILE PATH',\r\n  audio_path: 'AUDIO COMMENT FILE PATH'\r\n}  \r\n```\r\n##### Example:\r\n\r\n```js\r\nconst fields = {\r\n  title: 'BBCode Info',\r\n  from: 'USERNAME',\r\n  info: 'TEXT',\r\n  image_url: 'https://www.bbcode.org/images/lubeck_small.jpg'\r\n};\r\n  \r\nconst options = {\r\n  action: 'page',\r\n  fields_template: [\r\n    {\r\n       template_type: 'page',\r\n       template: '<p><image src=\"{{image_url}}\" height=\"36\" width=\"36\"/><br/>' +\r\n        '<div>Title: {{title}}<br/>From: {{from}}<br/>Message: {{info}}</div></p>'        \r\n    }\r\n  ]  \r\n};  \r\n```\r\n\r\n#### `.sendText(text, buttons, options)`\r\n\r\nSend a text message to Binder along with optional `buttons` and `options`.\r\n\r\n| Param | Type | Required |\r\n|:------|:-----|:---------|\r\n| `text` | string | `Y` |\r\n| `buttons` | string, button or mixed array | `N` |\r\n| `options` | options object | `N` |\r\n\r\n##### Example:\r\n\r\n```js\r\nconst username = chat.username;\r\n\r\nvar buttons = [{\r\n  type: 'postback',\r\n  text: 'Sure!'\r\n}, 'Not Sure?'];\r\n\r\nchat.sendText(`@${username} do you need to schedule a meet?`, buttons);\r\n```\r\n\r\n#### `.sendRichText(richtext, buttons, text, options)`\r\n\r\nSend a richtext (in [BBCode](https://www.bbcode.org) style) message along with optional `buttons`, `text` and `options`.   \r\nIf `action` is *todo*, `text` is set as todo item name and `richtext` message as todo comment.   \r\n\r\n| Param | Type | Required |\r\n|:------|:-----|:---------|\r\n| `richtext` | string | `Y` |\r\n| `buttons` | string, button or mixed array | `N` |\r\n| `text` | string | `N`, `Y` for action is `todo` |\r\n| `options` | options object | `N` |\r\n\r\n##### Example:\r\n\r\n```js\r\nconst username = chat.username;\r\nconst text = chat.comment.text;\r\n\r\nconst richtext = '[table][tr][th][center]BBCode Info[/center][/th][/tr]' +\r\n  '[tr][td][img=50x25]https://www.bbcode.org/images/lubeck_small.jpg[/img][/td][/tr]' +\r\n  '[tr][td]From: [i]' + username + '[/i][/td][/tr]' +\r\n  '[tr][td][color=Red]' + text + '[/color][/td][/tr][/table]';     \r\n\r\nchat.sendRichText(`${richtext}`);\r\n```\r\n\r\n#### `.sendJSON(fields, buttons, options)`\r\n\r\nSend a JSON object `fields` (in key-value style along with a pre-configured or an on-demand template) message along with optional `buttons` and `options`. \r\n\r\n| Param | Type | Required |\r\n|:------|:-----|:---------|\r\n| `fields` | fields object | `Y` |\r\n| `buttons` | string, button or mixed array | `N` |\r\n| `options` | options object | `N` |\r\n\r\n##### Example:\r\n\r\n```js\r\nconst username = chat.username;\r\nconst text = chat.comment.text;\r\n\r\nconst fields = {\r\n  title: 'BBCode Info',\r\n  from: username,\r\n  info: text,\r\n  image_url: 'https://www.bbcode.org/images/lubeck_small.jpg'\r\n};\r\nconst options = {\r\n  action: 'page',\r\n  fields_template: [\r\n    {\r\n       template_type: 'page',\r\n       template: '<p><image src=\"{{image_url}}\" height=\"36\" width=\"36\"/><br/>' +\r\n        '<div>Title: {{title}}<br/>From: {{from}}<br/>Message: {{info}}</div></p>'        \r\n    }\r\n  ]  \r\n};  \r\n\r\nchat.sendJSON(fields, null, options);\r\n```\r\n\r\n#### `.send(message, options)`\r\n\r\nSend a complete message along with `options`. The message structure is shown as follows:\r\n\r\n```js\r\n{\r\n  message: {\r\n    text: 'TEXT',\r\n    richtext: 'RICHTEXT',\r\n    fields: {FIELDS},\r\n    action: 'chat | page | todo',\r\n    buttons: [\r\n      {\r\n        type: 'postback | account_link',\r\n        text: 'TEXT',\r\n        payload: 'PAYLOAD'\r\n      }\r\n    ]\r\n  }\r\n}  \r\n```\r\n\r\n#### `.sendRequest(body, path, method)`\r\n\r\nThis is the root API for sending API request to Moxtra. `access_token` in the `chat` has to present to send the request. \r\n\r\n| Param | Type | Required |\r\n|:------|:-----|:---------|\r\n| `body` | string | `Y` |\r\n| `path` | string | `N`, default is '/messages' |\r\n| `method` | string | `N`, default is 'POST' |\r\n\r\n#### `.uploadRequest(body, file_path, audio_path)`\r\n\r\nThis is the API for uploading file and adding audio comment to Moxtra. `access_token` in the `chat` has to present to send the request. \r\n\r\n| Param | Type | Required |\r\n|:------|:-----|:---------|\r\n| `body` | string | `N` |\r\n| `file_path` | string | `N`, file path |\r\n| `audio_path` | string | `N`, audio file path for audio file (audio/x-m4a, audio/3gpp) |\r\n\r\n#### `.getBinderInfo()`\r\n\r\nThis API is to get a particular binder information. `binder_id` has to be set prior to making the api call.  \r\n\r\n##### Example:\r\n\r\n```js\r\nvar chat = new Chat(bot);\r\n\r\n// obtain access_token    \r\nbot.getAccessToken('CLIENT_ID', 'ORG_ID', function(error, token) {\r\n\r\n  if (error) {\r\n    // error happens\r\n\r\n  } else {\r\n    chat.setAccessToken(token.access_token);\r\n\r\n    chat.binder_id='BINDER_ID'; \r\n    chat.getBinderInfo(function(error, ret) {\r\n      if (!error) {\r\n        console.log('binder : ' + ret);\r\n      }      \r\n    });\r\n  }\r\n});\r\n\r\n\r\n```\r\n##### Result:\r\n\r\n```js\r\n{\r\n  \"code\": \"RESPONSE_SUCCESS\",\r\n  \"data\": {\r\n    \"id\": \"BiHGjPE2ZbsHyhVujuU4TUL\",\r\n    \"name\": \"test bot\",\r\n    \"created_time\": 1487787567445,\r\n    \"updated_time\": 1491598936463,\r\n    \"total_comments\": 0,\r\n    \"total_members\": 8,\r\n    \"total_pages\": 0,\r\n    \"total_todos\": 0,\r\n    \"revision\": 884,\r\n    \"thumbnail_uri\": \"https://www.moxtra.com/board/BiHGjPE2ZbsHyhVujuU4TUL/4\",\r\n    \"conversation\": false,\r\n    \"users\": [],\r\n    \"restricted\": false,\r\n    \"team\": false,\r\n    \"description\": \"\",\r\n    \"feeds_timestamp\": 1491598936463,\r\n    \"status\": \"BOARD_MEMBER\",\r\n    \"last_feed\": null,\r\n    \"binder_email\": \"b2f583d00339e44d0b2d02f9d50f352fa\",\r\n    \"tags\": null,\r\n    \"unread_feeds\": 0,\r\n    \"pages\": []\r\n  }\r\n}\r\n```\r\n\r\n---\r\n\r\n## Examples\r\n\r\nCheck the `examples` directory to see more samples of:\r\n\r\n- An echo bot\r\n- A bot using regular expression to capture text message\r\n- A bot sending RichText message\r\n- A bot sending Fields message\r\n- A bot uploading file and adding audio comment\r\n- A bot handling Account Link with OAuth2 \r\n\r\nTo run the examples, make sure to complete the bot creation on [MoxtraBot configuration](https://developer.moxtra.com/nextbots) and setup required configurations. For example, to run Echo example using the following command:\r\n\r\n```\r\n$ node examples/Echo\r\n```\r\n\r\n## License\r\n\r\nMIT\r\n\r\n[Core Concepts]:#core-concepts\r\n[Installation]:#installation\r\n[Getting Started]:#getting-started\r\n[Account Linking]:#account-linking\r\n[Documentation]:#documentation\r\n[Examples]:#examples\r\n[License]:#license\r\n","readmeFilename":"README.md"}