Skip to content

Commit

Permalink
Add bot.dmCard method
Browse files Browse the repository at this point in the history
  • Loading branch information
jpjpjp committed Oct 1, 2020
1 parent fe95a15 commit 44f2f0a
Show file tree
Hide file tree
Showing 7 changed files with 196 additions and 19 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -17,6 +17,7 @@ node_modules
# Private environments
.env
ToDo.private
local-tests

# Test version of sample and private tests
example1.js
Expand Down
36 changes: 36 additions & 0 deletions README.md
Expand Up @@ -753,6 +753,7 @@ before emitting and event
* [.reply(replyTo, message, [format])](#Bot+reply) ⇒ <code>Promise.&lt;Message&gt;</code>
* [.dm(person, [format], message)](#Bot+dm) ⇒ <code>Promise.&lt;Message&gt;</code>
* [.sendCard(cardJson, fallbackText)](#Bot+sendCard) ⇒ <code>Promise.&lt;Message&gt;</code>
* [.dmCard(person, cardJson, fallbackText)](#Bot+dmCard) ⇒ <code>Promise.&lt;Message&gt;</code>
* [.uploadStream(filename, stream)](#Bot+uploadStream) ⇒ <code>Promise.&lt;Message&gt;</code>
* [.censor(messageId)](#Bot+censor) ⇒ <code>Promise.&lt;Message&gt;</code>
* [.roomRename(title)](#Bot+roomRename) ⇒ <code>Promise.&lt;Room&gt;</code>
Expand Down Expand Up @@ -1233,6 +1234,41 @@ framework.hears('card please', function(bot, trigger) {
"This is the fallback text if the client can't render this card");
});
```
<a name="Bot+dmCard"></a>
### bot.dmCard(person, cardJson, fallbackText) ⇒ <code>Promise.&lt;Message&gt;</code>
Send a Card to a 1-1 space.
**Kind**: instance method of [<code>Bot</code>](#Bot)
| Param | Type | Description |
| --- | --- | --- |
| person | <code>String</code> | Email or ID of the user to 1-1 message. |
| cardJson | <code>Object</code> | The card JSON to render. This can come from the Webex Buttons and Cards Designer. |
| fallbackText | <code>String</code> | Message to be displayed on client's that can't render cards. |
**Example**
```js
// Simple example
framework.hears('card for joe please', function(bot, trigger) {
bot.dmCard(
'joe@email.com',
{
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"type": "AdaptiveCard",
"version": "1.0",
"body": [
{
"type": "TextBlock",
"text": "Joe, here is your card!",
"weight": "Bolder",
"size": "Medium"
}
]
},
"This is the fallback text if the client can't render this card");
});
```
<a name="Bot+uploadStream"></a>
### bot.uploadStream(filename, stream) ⇒ <code>Promise.&lt;Message&gt;</code>
Expand Down
7 changes: 6 additions & 1 deletion docs/version-history.md
@@ -1,8 +1,13 @@
# Version History

## v 2.3.0

* Added a new [bot.dmCard](../README.md#Bot+dmCard) method to facilitate sending a card to a user in a 1-1 space
* Fixed a bug in the startup logic introduced in the v2.2.2 change to better handle spawning in 1-1 spaces where the other participant no longer has a valid webex account

## v 2.2.2

* Finally merged an awesome PR from @zapdos26, which cleaned up a lot of embarrasing spelling and syntax errors in my [Migrating from flint guide](./migrate-from-node-flint.md)
* Finally merged an awesome PR from @zapdos26, which cleaned up a lot of embarrassing spelling and syntax errors in my [Migrating from flint guide](./migrate-from-node-flint.md)
* Better handling during startup of 1-1 spaces where the other participant no longer has a valid Webex account

## v 2.2.1
Expand Down
88 changes: 73 additions & 15 deletions lib/bot.js
Expand Up @@ -1269,26 +1269,58 @@ Bot.prototype.sendCard = function (cardJson, fallbackText) {
return when.reject(new Error(msg));
}

if (!fallbackText) {
fallbackText = 'This message contains a card that this client cannot render';
}

// use the default format type for the fallback text
var format = this.framework.messageFormat;

// construct message object with attachments
var messageObj = {};
messageObj[format] = fallbackText;
messageObj.attachments = [{
contentType: "application/vnd.microsoft.card.adaptive",
content: cardJson
}];

let messageObj = buildCardMsgObj(cardJson,
this.framework.messageFormat, fallbackText);
// send constructed message object to room
messageObj.roomId = this.room.id;
return this.framework.webex.messages.create(messageObj);
};

/**
* Send a Card to a 1-1 space.
*
* @function
* @param {String} person - Email or ID of the user to 1-1 message.
* @param {Object} cardJson - The card JSON to render. This can come from the Webex Buttons and Cards Designer.
* @param {String} fallbackText - Message to be displayed on client's that can't render cards.
* @returns {Promise.<Message>}
*
* @example
* // Simple example
* framework.hears('card for joe please', function(bot, trigger) {
* bot.dmCard(
* 'joe@email.com',
* {
* "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
* "type": "AdaptiveCard",
* "version": "1.0",
* "body": [
* {
* "type": "TextBlock",
* "text": "Joe, here is your card!",
* "weight": "Bolder",
* "size": "Medium"
* }
* ]
* },
* "This is the fallback text if the client can't render this card");
* });
*
*/
Bot.prototype.dmCard = function (person, cardJson, fallbackText) {
if (!this.active) {
let msg = 'bot.dmCard() failed, bot is not in active state';
if ((this.framework) && ("membershipRules" in this.framework)) {
msg += ' due to membership rules. Use bot.webex.message.create() to override';
}
return when.reject(new Error(msg));
}

let messageObj = buildCardMsgObj(cardJson,
this.framework.messageFormat, fallbackText);
return this.dm(person, messageObj);
};

/**
* Upload a file to a room using a Readable Stream
*
Expand Down Expand Up @@ -1409,3 +1441,29 @@ Bot.prototype.recall = null;
Bot.prototype.forget = null;

module.exports = Bot;

/**
* Build a Webex message request object from a card JSON design object and fallback text.
*
* @function
* @private
* @param {Object} cardJson - The card JSON to render. This can come from the Webex Buttons and Cards Designer.
* @param {string} fallbackFormat - The message format for the fallback text.
* @param {String} fallbackText - Message to be displayed on client's that can't render cards.
* @returns {Object}
*/
function buildCardMsgObj(cardJson, fallbackFormat, fallbackText) {
if (!fallbackText) {
fallbackText = 'A card was sent with no fallback text specified';
}

// construct message object with attachments
var messageObj = {};
messageObj[fallbackFormat] = fallbackText;
messageObj.attachments = [{
contentType: "application/vnd.microsoft.card.adaptive",
content: cardJson
}];

return messageObj;
}
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
@@ -1,6 +1,6 @@
{
"name": "webex-node-bot-framework",
"version": "2.2.2",
"version": "2.3.0",
"description": "Webex Teams Bot Framework for Node JS",
"main": "index.js",
"scripts": {
Expand Down
79 changes: 78 additions & 1 deletion test/common/bot-direct-message-tests.js
Expand Up @@ -14,6 +14,11 @@ describe('Bot interacts with user in 1-1 space', () => {
let message;
let eventsData = {};
let trigger = {};

// Let's use markdown by default for these test
let origFormat = framework.messageFormat;
framework.messageFormat = 'markdown';

let messageCreatedEvent, frameworkMessageEvent, botMessageEvent;
// Setup the promises for the events that come from user input that mentions a bot
beforeEach(() => {
Expand All @@ -36,7 +41,10 @@ describe('Bot interacts with user in 1-1 space', () => {
});
});


after(() =>
// restore the default framework message format
framework.messageFormat = origFormat
);

it('checks for persistent storage from previous tests', () => {
let bot = common.botForUser1on1Space;
Expand Down Expand Up @@ -157,6 +165,75 @@ describe('Bot interacts with user in 1-1 space', () => {
});
});

it('bot sends a plain text message in the 1-1 space', () => {
testName = 'bot sends a plain text message in the 1-1 space';
if (!common.botForUser1on1Space) {
return when(true);
}
// send the bots response
let msg = 'This is a **plain text** message.';
let personId = common.userInfo.id;
return common.botForUser1on1Space.dm(personId, 'text', msg)
.then((m) => {
message = m;
// messages.push(m);
assert(validator.isMessage(message),
'create message did not return a valid message');
// Wait for all the event handlers and the heard handler to fire
return when(messageCreatedEvent);
})
.catch((e) => {
console.error(`${testName} failed: ${e.message}`);
return Promise.reject(e);
});
});

it('bot sends a plain text message using a message object in the 1-1 space', () => {
testName = 'bot sends a plain text message using a message object in the 1-1 space';
if (!common.botForUser1on1Space) {
return when(true);
}
// send the bots response
let msg = 'This is a **plain text** message sent in the markdown fields of a message request.';
let personId = common.userInfo.id;
return common.botForUser1on1Space.dm(personId, 'text', {markdown: msg})
.then((m) => {
message = m;
// messages.push(m);
assert(validator.isMessage(message),
'create message did not return a valid message');
// Wait for all the event handlers and the heard handler to fire
return when(messageCreatedEvent);
})
.catch((e) => {
console.error(`${testName} failed: ${e.message}`);
return Promise.reject(e);
});
});

it('bot sends a card in the 1-1 space', () => {
testName = 'bot sends a card in the 1-1 space';
if (!common.botForUser1on1Space) {
return when(true);
}
// send the bots response
let cardJson = require('../common/input-card.json');
let personEmail = common.userInfo.emails[0];
return common.botForUser1on1Space.dmCard(personEmail, cardJson, 'What is your name')
.then((m) => {
message = m;
// messages.push(m);
assert(validator.isMessage(message),
'create message did not return a valid message');
// Wait for all the event handlers and the heard handler to fire
return when(messageCreatedEvent);
})
.catch((e) => {
console.error(`${testName} failed: ${e.message}`);
return Promise.reject(e);
});
});

it('updates persistent storage for the next tests', () => {
let bot = common.botForUser1on1Space;
if (process.env.MONGO_USER) {
Expand Down

0 comments on commit 44f2f0a

Please sign in to comment.