- Fixed bugs in waterfall handling logic.
- Added a slack-todoBotLuis example
This commit is contained in:
Родитель
c3635d7834
Коммит
40b2b9db20
|
@ -1,10 +1,8 @@
|
|||
/*-----------------------------------------------------------------------------
|
||||
A simple "Hello World" bot for slack. SlackBot is powered by Botkit and you can
|
||||
do pretty much anything you can do in Botkit.
|
||||
|
||||
More details about setting up Botkit can be found at:
|
||||
|
||||
http://howdy.ai/botkit
|
||||
A bot for managing a teams to-do list. Created tasks are saved to the sessions
|
||||
channelData object which means that private task lists can be created by sending
|
||||
the bot a Direct Message and group task lists can be managed by @mentioning
|
||||
the bot in a channel.
|
||||
|
||||
|
||||
# RUN THE BOT:
|
||||
|
@ -24,9 +22,12 @@ More details about setting up Botkit can be found at:
|
|||
|
||||
# USE THE BOT:
|
||||
|
||||
Find your bot inside Slack
|
||||
To manage your private tasks find the bot in slack and send it a Direct
|
||||
Message saying "Hello". The bot will reply with instructions for how to
|
||||
manage tasks.
|
||||
|
||||
Say: "hello"
|
||||
To manage group tasks invite the bot to a channel by saying "/invite @bot"
|
||||
then say "@bot hello" for a list of instructions.
|
||||
|
||||
-----------------------------------------------------------------------------*/
|
||||
|
||||
|
@ -36,13 +37,13 @@ var index = require('./dialogs/index')
|
|||
|
||||
var controller = Botkit.slackbot();
|
||||
var bot = controller.spawn({
|
||||
token: process.env.token
|
||||
token: process.env.token || 'xoxb-27353876818-1JaNnDS8yhLSKuzmX9DncWAI'
|
||||
});
|
||||
|
||||
var slackBot = new builder.SlackBot(controller, bot);
|
||||
slackBot.add('/', index);
|
||||
|
||||
slackBot.listen(['direct_message','direct_mention']);
|
||||
slackBot.listenForMentions();
|
||||
|
||||
bot.startRTM(function(err,bot,payload) {
|
||||
if (err) {
|
||||
|
|
|
@ -3,12 +3,10 @@ var prompts = require('../prompts');
|
|||
|
||||
// Export Command Dialog
|
||||
module.exports = new builder.CommandDialog()
|
||||
.matches('^(hello|hi|howdy)', builder.DialogAction.send(prompts.helpMessage))
|
||||
.matches('^(hello|hi|howdy|help)', builder.DialogAction.send(prompts.helpMessage))
|
||||
.matches('^(?:new|save|create|add)(?: (.+))?', saveTask)
|
||||
.matches('^(?:done|delete|finish|remove)(?: (\\d+))?', finishTask)
|
||||
.matches('^(list|show|tasks)', listTasks)
|
||||
.onDefault(builder.DialogAction.send(prompts.notRecognized));
|
||||
|
||||
.matches('^(list|show|tasks)', listTasks);
|
||||
|
||||
function saveTask(session, args) {
|
||||
if (args.matches) {
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
const helpMessage = 'Available commands are:\n\n' +
|
||||
module.exports = {
|
||||
helpMessage: 'Available commands are:\n\n' +
|
||||
'* *list* - show all tasks (also *show*, *tasks*)\n' +
|
||||
'* *new* [your task] - create a new task (also *save*, *create*, *add*)\n' +
|
||||
'* *done* [number of task] - finish a task, you can get the number of a task from *list* (also *delete", *finish*, *remove*)';
|
||||
|
||||
module.exports = {
|
||||
helpMessage: helpMessage,
|
||||
notRecognized: 'I am afraid I don\'t understand. ' + helpMessage,
|
||||
'* *done* [number of task] - finish a task, you can get the number of a task from *list* (also *delete", *finish*, *remove*)',
|
||||
saveTaskCreated: 'Created a new task no. %(index)d: %(task)s',
|
||||
saveTaskMissing: 'You need to tell me what the new task should be. For example: "new Remember the milk"',
|
||||
listTaskList: 'Tasks:\n%s',
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
/*-----------------------------------------------------------------------------
|
||||
A bot for managing a teams to-do list. Created tasks are saved to the sessions
|
||||
channelData object which means that private task lists can be created by sending
|
||||
the bot a Direct Message and group task lists can be managed by @mentioning
|
||||
the bot in a channel.
|
||||
|
||||
|
||||
# RUN THE BOT:
|
||||
|
||||
Get a Bot token from Slack:
|
||||
|
||||
http://my.slack.com/services/new/bot
|
||||
|
||||
Run your bot from the command line:
|
||||
|
||||
token=YOUR_TOKEN node app.js
|
||||
|
||||
Run your bot from the command line (WINDOWS):
|
||||
|
||||
set token=YOUR_TOKEN
|
||||
node app.js
|
||||
|
||||
# USE THE BOT:
|
||||
|
||||
To manage your private tasks find the bot in slack and send it a Direct
|
||||
Message saying "Hello". The bot will reply with instructions for how to
|
||||
manage tasks.
|
||||
|
||||
To manage group tasks invite the bot to a channel by saying "/invite @bot"
|
||||
then say "@bot hello" for a list of instructions.
|
||||
|
||||
-----------------------------------------------------------------------------*/
|
||||
|
||||
var Botkit = require('botkit');
|
||||
var builder = require('../../');
|
||||
var index = require('./dialogs/index')
|
||||
|
||||
var controller = Botkit.slackbot();
|
||||
var bot = controller.spawn({
|
||||
token: process.env.token || 'xoxb-27353876818-1JaNnDS8yhLSKuzmX9DncWAI'
|
||||
});
|
||||
|
||||
var slackBot = new builder.SlackBot(controller, bot);
|
||||
slackBot.add('/', index);
|
||||
|
||||
slackBot.listenForMentions();
|
||||
|
||||
bot.startRTM(function(err,bot,payload) {
|
||||
if (err) {
|
||||
throw new Error('Could not connect to Slack');
|
||||
}
|
||||
});
|
|
@ -0,0 +1,84 @@
|
|||
var builder = require('../../../');
|
||||
var prompts = require('../prompts');
|
||||
|
||||
/** Return a LuisDialog that points at our model and then add intent handlers. */
|
||||
var dialog = new builder.LuisDialog('https://api.projectoxford.ai/luis/v1/application?id=0748d3d3-eb60-4e58-95df-3d5e904c5fdc&subscription-key=fe054e042fd14754a83f0a205f6552a5&q=');
|
||||
module.exports = dialog;
|
||||
|
||||
/** Answer users help requests. We can use a DialogAction to send a static message. */
|
||||
dialog.on('Help', builder.DialogAction.send(prompts.helpMessage));
|
||||
|
||||
/** Prompts a user for the title of the task and saves it. */
|
||||
dialog.on('SaveTask', [
|
||||
function (session, args, next) {
|
||||
// See if got the tasks title from our LUIS model.
|
||||
var title = builder.EntityRecognizer.findEntity(args.entities, 'TaskTitle');
|
||||
if (!title) {
|
||||
// Prompt user to enter title.
|
||||
builder.Prompts.text(session, prompts.saveTaskMissing);
|
||||
} else {
|
||||
// Pass title to next step.
|
||||
next({ response: title.entity });
|
||||
}
|
||||
},
|
||||
function (session, results) {
|
||||
// Save the task
|
||||
if (results.response) {
|
||||
if (!session.channelData.tasks) {
|
||||
session.channelData.tasks = [results.response];
|
||||
} else {
|
||||
session.channelData.tasks.push(results.response);
|
||||
}
|
||||
session.send(prompts.saveTaskCreated, { task: results.response });
|
||||
} else {
|
||||
session.send(prompts.canceled);
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
/** Prompts the user for the task to delete and then removes it. */
|
||||
dialog.on('FinishTask', [
|
||||
function (session, args, next) {
|
||||
// Do we have any tasks?
|
||||
if (session.channelData.tasks && session.channelData.tasks.length > 0) {
|
||||
// See if got the tasks title from our LUIS model.
|
||||
var topTask;
|
||||
var title = builder.EntityRecognizer.findEntity(args.entities, 'TaskTitle');
|
||||
if (title) {
|
||||
// Find it in our list of tasks
|
||||
topTask = builder.EntityRecognizer.findBestMatch(session.channelData.tasks, title.entity);
|
||||
}
|
||||
|
||||
// Prompt user if task missing or not found
|
||||
if (!topTask) {
|
||||
builder.Prompts.choice(session, prompts.finishTaskMissing, session.channelData.tasks);
|
||||
} else {
|
||||
next({ response: topTask });
|
||||
}
|
||||
} else {
|
||||
session.send(prompts.listNoTasks);
|
||||
}
|
||||
},
|
||||
function (session, results) {
|
||||
if (results && results.response) {
|
||||
session.channelData.tasks.splice(results.response.index, 1);
|
||||
session.send(prompts.finishTaskDone, { task: results.response.entity });
|
||||
} else {
|
||||
session.send(prompts.canceled);
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
/** Shows the user a list of tasks. */
|
||||
dialog.on('ListTasks', function (session) {
|
||||
if (session.channelData.tasks && session.channelData.tasks.length > 0) {
|
||||
var list = '';
|
||||
session.channelData.tasks.forEach(function (value, index) {
|
||||
list += session.gettext(prompts.listTaskItem, { index: index + 1, task: value });
|
||||
});
|
||||
session.send(prompts.listTaskList, list);
|
||||
}
|
||||
else {
|
||||
session.send(prompts.listNoTasks);
|
||||
}
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
|
||||
module.exports = {
|
||||
helpMessage: "Here's what I can do:\n\n" +
|
||||
"* Create new tasks by saying something like 'add a new task to go to the gym'\n" +
|
||||
"* List your existing tasks by saying something like 'what do I have to do?'\n" +
|
||||
"* Finish an existing task by saying something like 'remove go to the gym'",
|
||||
canceled: 'Sure... No problem.',
|
||||
saveTaskCreated: "Created a new task called '%(task)s'",
|
||||
saveTaskMissing: 'What would you like to name the task?',
|
||||
listTaskList: 'Tasks\n%s',
|
||||
listTaskItem: '%(index)d. %(task)s\n',
|
||||
listNoTasks: 'You have no tasks.',
|
||||
finishTaskMissing: "Which task would you like to delete?",
|
||||
finishTaskDone: "Removed '%(task)s' from your task list."
|
||||
}
|
|
@ -506,7 +506,7 @@ export interface ISkypeBotOptions {
|
|||
/** Storage system to use for persisting Session.sessionState values. By default the MemoryStorage is used. */
|
||||
sessionStore?: IStorage;
|
||||
|
||||
/** Maximum time since ISessionState.lastAccess before the current session state is discarded. Default is 4 hours. */
|
||||
/** Maximum time (in milliseconds) since ISessionState.lastAccess before the current session state is discarded. Default is 4 hours. */
|
||||
maxSessionAge?: number;
|
||||
|
||||
/** Optional localizer used to localize the bots responses to the user. */
|
||||
|
@ -536,7 +536,7 @@ export interface ISkypeBotOptions {
|
|||
|
||||
/** Options used to configure the SlackBot. */
|
||||
export interface ISlackBotOptions {
|
||||
/** Maximum time since ISessionState.lastAccess before the current session state is discarded. Default is 4 hours. */
|
||||
/** Maximum time (in milliseconds) since ISessionState.lastAccess before the current session state is discarded. Default is 4 hours. */
|
||||
maxSessionAge?: number;
|
||||
|
||||
/** Optional localizer used to localize the bots responses to the user. */
|
||||
|
@ -547,6 +547,9 @@ export interface ISlackBotOptions {
|
|||
|
||||
/** Optional arguments to pass to the initial dialog for a conversation. */
|
||||
defaultDialogArgs?: any;
|
||||
|
||||
/** Maximum time (in milliseconds) that a bot continues to recieve ambient messages after its been @mentioned. Default 5 minutes. */
|
||||
ambientMentionDuration?: number;
|
||||
}
|
||||
|
||||
/** Address info passed to SlackBot.beginDialog() calls. Specifies the address of the user or channel to start a conversation with. */
|
||||
|
@ -572,7 +575,7 @@ export interface ITextBotOptions {
|
|||
/** Storage system to use for persisting Session.sessionState values. By default the MemoryStorage is used. */
|
||||
sessionStore?: IStorage;
|
||||
|
||||
/** Maximum time since ISessionState.lastAccess before the current session state is discarded. Default is 4 hours. */
|
||||
/** Maximum time (in milliseconds) since ISessionState.lastAccess before the current session state is discarded. Default is 4 hours. */
|
||||
maxSessionAge?: number;
|
||||
|
||||
/** Optional localizer used to localize the bots responses to the user. */
|
||||
|
@ -587,8 +590,7 @@ export interface ITextBotOptions {
|
|||
|
||||
/** Signature for function passed as a step to DialogAction.waterfall(). */
|
||||
export interface IDialogWaterfallStep {
|
||||
<T>(session: Session, result?: T, skip?: (count?: number, results?: IDialogResult<any>) => void): any;
|
||||
<T>(session: Session, result?: IDialogResult<T>, skip?: (count?: number, results?: IDialogResult<any>) => void): any;
|
||||
<T>(session: Session, result?: IDialogResult<T>, skip?: (results?: IDialogResult<any>) => void): any;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1559,11 +1561,10 @@ export class SlackBot extends DialogCollection {
|
|||
/**
|
||||
* Creates a new instance of the Slack bot using BotKit.
|
||||
* @param controller Controller created from a call to Botkit.slackbot().
|
||||
* @param bot Optional bot created from a call to controller.spawn(). If you don't pass this in
|
||||
* you won't be able to initiate outgoing conversations using SlackBot.beginDialog().
|
||||
* @param bot The bot created from a call to controller.spawn().
|
||||
* @param options Optional configuration settings for the bot.
|
||||
*/
|
||||
constructor(controller: any, bot?: any, options?: ISlackBotOptions);
|
||||
constructor(controller: any, bot: any, options?: ISlackBotOptions);
|
||||
|
||||
/**
|
||||
* Registers an event listener to get notified of bot related events.
|
||||
|
@ -1572,10 +1573,11 @@ export class SlackBot extends DialogCollection {
|
|||
* - reply: A reply to an existing message was sent. [IBotMessageEvent]
|
||||
* - send: A new message was sent to start a new conversation. [IBotMessageEvent]
|
||||
* - quit: The bot has elected to ended the current conversation. [IBotMessageEvent]
|
||||
* - ambient: An ambient message was received. [IBotMessageEvent]
|
||||
* - direct_mention: The bot was directly mentioned in a message. [IBotMessageEvent]
|
||||
* - mention: The bot was mentioned in a message. [IBotMessageEvent]
|
||||
* - direct_message: The bot has received a direct 1:1 chat message. [IBotMessageEvent]
|
||||
* - message_received: The bot received a message. [IBotMessageEvent]
|
||||
* - bot_channel_join: The bot has joined a channel. [IBotMessageEvent]
|
||||
* - user_channel_join: A user has joined a channel. [IBotMessageEvent]
|
||||
* - bot_group_join: The bot has joined a group. [IBotMessageEvent]
|
||||
* - user_group_join: A user has joined a group. [IBotMessageEvent]
|
||||
* @param event Name of event to listen for.
|
||||
* @param listener Function to invoke.
|
||||
*/
|
||||
|
@ -1593,9 +1595,22 @@ export class SlackBot extends DialogCollection {
|
|||
* - direct_mention: Direct mentions are messages that begin with the bot's name, as in "@bot hello".
|
||||
* - mention: Mentions are messages that contain the bot's name, but not at the beginning, as in "hello @bot".
|
||||
* - direct_message: Direct messages are sent via private 1:1 direct message channels.
|
||||
* @param types The type of events to listen for,
|
||||
* @param dialogId Optional ID of the bots dialog to begin for new conversations.
|
||||
* @param dialogArgs Optional arguments to pass to the dialog.
|
||||
*/
|
||||
listen(types: string[], dialogId?: string, dialogArgs?: any): SlackBot;
|
||||
|
||||
/**
|
||||
* Begins listening for messages sent to the bot. The bot will recieve direct messages,
|
||||
* direct mentions, and mentions. One the bot has been mentioned it will continue to receive
|
||||
* ambient messages from the user that mentioned them for a short period of time. This time
|
||||
* can be configured using ISlackBotOptions.ambientMentionDuration.
|
||||
* @param dialogId Optional ID of the bots dialog to begin for new conversations.
|
||||
* @param dialogArgs Optional arguments to pass to the dialog.
|
||||
*/
|
||||
listenForMentions(dialogId?: string, dialogArgs?: any): SlackBot;
|
||||
|
||||
/**
|
||||
* Starts a new conversation with a user.
|
||||
* @param address Address of the user to begin the conversation with.
|
||||
|
|
|
@ -10,14 +10,21 @@ var utils = require('../utils');
|
|||
var SlackBot = (function (_super) {
|
||||
__extends(SlackBot, _super);
|
||||
function SlackBot(controller, bot, options) {
|
||||
var _this = this;
|
||||
_super.call(this);
|
||||
this.controller = controller;
|
||||
this.bot = bot;
|
||||
this.options = {
|
||||
maxSessionAge: 14400000,
|
||||
defaultDialogId: '/'
|
||||
defaultDialogId: '/',
|
||||
ambientMentionDuration: 300000 // <-- default duration of 5 minutes
|
||||
};
|
||||
this.configure(options);
|
||||
['message_received', 'bot_channel_join', 'user_channel_join', 'bot_group_join', 'user_group_join'].forEach(function (type) {
|
||||
_this.controller.on(type, function (bot, msg) {
|
||||
_this.emit(type, bot, msg);
|
||||
});
|
||||
});
|
||||
}
|
||||
SlackBot.prototype.configure = function (options) {
|
||||
if (options) {
|
||||
|
@ -31,17 +38,56 @@ var SlackBot = (function (_super) {
|
|||
};
|
||||
SlackBot.prototype.listen = function (types, dialogId, dialogArgs) {
|
||||
var _this = this;
|
||||
dialogId = dialogId || this.options.defaultDialogId;
|
||||
dialogArgs = dialogArgs || this.options.defaultDialogArgs;
|
||||
types.forEach(function (type) {
|
||||
_this.controller.on(type, function (bot, msg) {
|
||||
bot.identifyTeam(function (err, teamId) {
|
||||
msg.team = teamId;
|
||||
_this.emit(type, msg);
|
||||
_this.dispatchMessage(bot, msg, dialogId || _this.options.defaultDialogId, dialogArgs || _this.options.defaultDialogArgs);
|
||||
_this.dispatchMessage(bot, msg, dialogId, dialogArgs);
|
||||
});
|
||||
});
|
||||
});
|
||||
return this;
|
||||
};
|
||||
SlackBot.prototype.listenForMentions = function (dialogId, dialogArgs) {
|
||||
var _this = this;
|
||||
var sessions = {};
|
||||
var dispatch = function (bot, msg, ss) {
|
||||
bot.identifyTeam(function (err, teamId) {
|
||||
msg.team = teamId;
|
||||
_this.dispatchMessage(bot, msg, dialogId, dialogArgs, ss);
|
||||
});
|
||||
};
|
||||
dialogId = dialogId || this.options.defaultDialogId;
|
||||
dialogArgs = dialogArgs || this.options.defaultDialogArgs;
|
||||
this.controller.on('direct_message', function (bot, msg) {
|
||||
dispatch(bot, msg);
|
||||
});
|
||||
['direct_mention', 'mention'].forEach(function (type) {
|
||||
_this.controller.on(type, function (bot, msg) {
|
||||
// Create a new session
|
||||
var key = msg.channel + ':' + msg.user;
|
||||
var ss = sessions[key] = { callstack: [], lastAccess: new Date().getTime() };
|
||||
dispatch(bot, msg, ss);
|
||||
});
|
||||
});
|
||||
this.controller.on('ambient', function (bot, msg) {
|
||||
// Conditionally dispatch the message
|
||||
var key = msg.channel + ':' + msg.user;
|
||||
if (sessions.hasOwnProperty(key)) {
|
||||
// Validate session
|
||||
var ss = sessions[key];
|
||||
if (ss.callstack && ss.callstack.length > 0 && (new Date().getTime() - ss.lastAccess) <= _this.options.ambientMentionDuration) {
|
||||
dispatch(bot, msg, ss);
|
||||
}
|
||||
else {
|
||||
delete sessions[key];
|
||||
}
|
||||
}
|
||||
});
|
||||
return this;
|
||||
};
|
||||
SlackBot.prototype.beginDialog = function (address, dialogId, dialogArgs) {
|
||||
// Validate args
|
||||
if (!address.user && !address.channel) {
|
||||
|
@ -54,7 +100,7 @@ var SlackBot = (function (_super) {
|
|||
this.dispatchMessage(null, address, dialogId, dialogArgs);
|
||||
return this;
|
||||
};
|
||||
SlackBot.prototype.dispatchMessage = function (bot, msg, dialogId, dialogArgs) {
|
||||
SlackBot.prototype.dispatchMessage = function (bot, msg, dialogId, dialogArgs, smartState) {
|
||||
var _this = this;
|
||||
var onError = function (err) {
|
||||
_this.emit('error', err, msg);
|
||||
|
@ -71,7 +117,7 @@ var SlackBot = (function (_super) {
|
|||
var teamData = ses.teamData && ses.teamData.id ? utils.clone(ses.teamData) : null;
|
||||
var channelData = ses.channelData && ses.channelData.id ? utils.clone(ses.channelData) : null;
|
||||
var userData = ses.userData && ses.userData.id ? utils.clone(ses.userData) : null;
|
||||
if (channelData) {
|
||||
if (channelData && !smartState) {
|
||||
channelData[consts.Data.SessionState] = ses.sessionState;
|
||||
}
|
||||
// Save data
|
||||
|
@ -120,7 +166,10 @@ var SlackBot = (function (_super) {
|
|||
data.userData = { id: msg.user };
|
||||
}
|
||||
// Unpack session state
|
||||
if (data.channelData && data.channelData.hasOwnProperty(consts.Data.SessionState)) {
|
||||
if (smartState) {
|
||||
sessionState = smartState;
|
||||
}
|
||||
else if (data.channelData && data.channelData.hasOwnProperty(consts.Data.SessionState)) {
|
||||
sessionState = data.channelData[consts.Data.SessionState];
|
||||
delete data.channelData[consts.Data.SessionState];
|
||||
}
|
||||
|
|
|
@ -41,10 +41,11 @@ var DialogAction = (function () {
|
|||
};
|
||||
DialogAction.waterfall = function (steps) {
|
||||
return function waterfallAction(s, r) {
|
||||
var skip = function (count, result) {
|
||||
if (count === void 0) { count = 1; }
|
||||
result = result || { resumed: dialog.ResumeReason.forward };
|
||||
s.dialogData[consts.Data.WaterfallStep] += count;
|
||||
var skip = function (result) {
|
||||
result = result || {};
|
||||
if (!result.resumed) {
|
||||
result.resumed = dialog.ResumeReason.forward;
|
||||
}
|
||||
waterfallAction(s, result);
|
||||
};
|
||||
try {
|
||||
|
@ -56,9 +57,6 @@ var DialogAction = (function () {
|
|||
case dialog.ResumeReason.back:
|
||||
step -= 1;
|
||||
break;
|
||||
case dialog.ResumeReason.forward:
|
||||
step += 2;
|
||||
break;
|
||||
default:
|
||||
step++;
|
||||
}
|
||||
|
|
|
@ -506,7 +506,7 @@ export interface ISkypeBotOptions {
|
|||
/** Storage system to use for persisting Session.sessionState values. By default the MemoryStorage is used. */
|
||||
sessionStore?: IStorage;
|
||||
|
||||
/** Maximum time since ISessionState.lastAccess before the current session state is discarded. Default is 4 hours. */
|
||||
/** Maximum time (in milliseconds) since ISessionState.lastAccess before the current session state is discarded. Default is 4 hours. */
|
||||
maxSessionAge?: number;
|
||||
|
||||
/** Optional localizer used to localize the bots responses to the user. */
|
||||
|
@ -536,7 +536,7 @@ export interface ISkypeBotOptions {
|
|||
|
||||
/** Options used to configure the SlackBot. */
|
||||
export interface ISlackBotOptions {
|
||||
/** Maximum time since ISessionState.lastAccess before the current session state is discarded. Default is 4 hours. */
|
||||
/** Maximum time (in milliseconds) since ISessionState.lastAccess before the current session state is discarded. Default is 4 hours. */
|
||||
maxSessionAge?: number;
|
||||
|
||||
/** Optional localizer used to localize the bots responses to the user. */
|
||||
|
@ -547,6 +547,9 @@ export interface ISlackBotOptions {
|
|||
|
||||
/** Optional arguments to pass to the initial dialog for a conversation. */
|
||||
defaultDialogArgs?: any;
|
||||
|
||||
/** Maximum time (in milliseconds) that a bot continues to recieve ambient messages after its been @mentioned. Default 5 minutes. */
|
||||
ambientMentionDuration?: number;
|
||||
}
|
||||
|
||||
/** Address info passed to SlackBot.beginDialog() calls. Specifies the address of the user or channel to start a conversation with. */
|
||||
|
@ -572,7 +575,7 @@ export interface ITextBotOptions {
|
|||
/** Storage system to use for persisting Session.sessionState values. By default the MemoryStorage is used. */
|
||||
sessionStore?: IStorage;
|
||||
|
||||
/** Maximum time since ISessionState.lastAccess before the current session state is discarded. Default is 4 hours. */
|
||||
/** Maximum time (in milliseconds) since ISessionState.lastAccess before the current session state is discarded. Default is 4 hours. */
|
||||
maxSessionAge?: number;
|
||||
|
||||
/** Optional localizer used to localize the bots responses to the user. */
|
||||
|
@ -587,8 +590,7 @@ export interface ITextBotOptions {
|
|||
|
||||
/** Signature for function passed as a step to DialogAction.waterfall(). */
|
||||
export interface IDialogWaterfallStep {
|
||||
<T>(session: Session, result?: T, skip?: (count?: number, results?: IDialogResult<any>) => void): any;
|
||||
<T>(session: Session, result?: IDialogResult<T>, skip?: (count?: number, results?: IDialogResult<any>) => void): any;
|
||||
<T>(session: Session, result?: IDialogResult<T>, skip?: (results?: IDialogResult<any>) => void): any;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1559,11 +1561,10 @@ export class SlackBot extends DialogCollection {
|
|||
/**
|
||||
* Creates a new instance of the Slack bot using BotKit.
|
||||
* @param controller Controller created from a call to Botkit.slackbot().
|
||||
* @param bot Optional bot created from a call to controller.spawn(). If you don't pass this in
|
||||
* you won't be able to initiate outgoing conversations using SlackBot.beginDialog().
|
||||
* @param bot The bot created from a call to controller.spawn().
|
||||
* @param options Optional configuration settings for the bot.
|
||||
*/
|
||||
constructor(controller: any, bot?: any, options?: ISlackBotOptions);
|
||||
constructor(controller: any, bot: any, options?: ISlackBotOptions);
|
||||
|
||||
/**
|
||||
* Registers an event listener to get notified of bot related events.
|
||||
|
@ -1572,10 +1573,11 @@ export class SlackBot extends DialogCollection {
|
|||
* - reply: A reply to an existing message was sent. [IBotMessageEvent]
|
||||
* - send: A new message was sent to start a new conversation. [IBotMessageEvent]
|
||||
* - quit: The bot has elected to ended the current conversation. [IBotMessageEvent]
|
||||
* - ambient: An ambient message was received. [IBotMessageEvent]
|
||||
* - direct_mention: The bot was directly mentioned in a message. [IBotMessageEvent]
|
||||
* - mention: The bot was mentioned in a message. [IBotMessageEvent]
|
||||
* - direct_message: The bot has received a direct 1:1 chat message. [IBotMessageEvent]
|
||||
* - message_received: The bot received a message. [IBotMessageEvent]
|
||||
* - bot_channel_join: The bot has joined a channel. [IBotMessageEvent]
|
||||
* - user_channel_join: A user has joined a channel. [IBotMessageEvent]
|
||||
* - bot_group_join: The bot has joined a group. [IBotMessageEvent]
|
||||
* - user_group_join: A user has joined a group. [IBotMessageEvent]
|
||||
* @param event Name of event to listen for.
|
||||
* @param listener Function to invoke.
|
||||
*/
|
||||
|
@ -1593,9 +1595,22 @@ export class SlackBot extends DialogCollection {
|
|||
* - direct_mention: Direct mentions are messages that begin with the bot's name, as in "@bot hello".
|
||||
* - mention: Mentions are messages that contain the bot's name, but not at the beginning, as in "hello @bot".
|
||||
* - direct_message: Direct messages are sent via private 1:1 direct message channels.
|
||||
* @param types The type of events to listen for,
|
||||
* @param dialogId Optional ID of the bots dialog to begin for new conversations.
|
||||
* @param dialogArgs Optional arguments to pass to the dialog.
|
||||
*/
|
||||
listen(types: string[], dialogId?: string, dialogArgs?: any): SlackBot;
|
||||
|
||||
/**
|
||||
* Begins listening for messages sent to the bot. The bot will recieve direct messages,
|
||||
* direct mentions, and mentions. One the bot has been mentioned it will continue to receive
|
||||
* ambient messages from the user that mentioned them for a short period of time. This time
|
||||
* can be configured using ISlackBotOptions.ambientMentionDuration.
|
||||
* @param dialogId Optional ID of the bots dialog to begin for new conversations.
|
||||
* @param dialogArgs Optional arguments to pass to the dialog.
|
||||
*/
|
||||
listenForMentions(dialogId?: string, dialogArgs?: any): SlackBot;
|
||||
|
||||
/**
|
||||
* Starts a new conversation with a user.
|
||||
* @param address Address of the user to begin the conversation with.
|
||||
|
|
|
@ -54,6 +54,7 @@ export interface ISlackBotOptions {
|
|||
localizer?: ILocalizer;
|
||||
defaultDialogId?: string;
|
||||
defaultDialogArgs?: any;
|
||||
ambientMentionDuration?: number;
|
||||
}
|
||||
|
||||
export interface ISlackBeginDialogAddress {
|
||||
|
@ -64,14 +65,20 @@ export interface ISlackBeginDialogAddress {
|
|||
}
|
||||
|
||||
export class SlackBot extends collection.DialogCollection {
|
||||
protected options: ISlackBotOptions = {
|
||||
private options: ISlackBotOptions = {
|
||||
maxSessionAge: 14400000, // <-- default max session age of 4 hours
|
||||
defaultDialogId: '/'
|
||||
defaultDialogId: '/',
|
||||
ambientMentionDuration: 300000 // <-- default duration of 5 minutes
|
||||
};
|
||||
|
||||
constructor(protected controller: BotKitController, protected bot: Bot, options?: ISlackBotOptions) {
|
||||
constructor(private controller: BotKitController, private bot: Bot, options?: ISlackBotOptions) {
|
||||
super();
|
||||
this.configure(options);
|
||||
['message_received','bot_channel_join','user_channel_join','bot_group_join','user_group_join'].forEach((type) => {
|
||||
this.controller.on(type, (bot: Bot, msg: ISlackMessage) => {
|
||||
this.emit(type, bot, msg);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public configure(options: ISlackBotOptions): this {
|
||||
|
@ -86,18 +93,57 @@ export class SlackBot extends collection.DialogCollection {
|
|||
}
|
||||
|
||||
public listen(types: string[], dialogId?: string, dialogArgs?: any): this {
|
||||
dialogId = dialogId || this.options.defaultDialogId;
|
||||
dialogArgs = dialogArgs || this.options.defaultDialogArgs;
|
||||
types.forEach((type) => {
|
||||
this.controller.on(type, (bot: Bot, msg: ISlackMessage) => {
|
||||
bot.identifyTeam((err, teamId) => {
|
||||
msg.team = teamId;
|
||||
this.emit(type, msg);
|
||||
this.dispatchMessage(bot, msg, dialogId || this.options.defaultDialogId, dialogArgs || this.options.defaultDialogArgs);
|
||||
this.dispatchMessage(bot, msg, dialogId, dialogArgs);
|
||||
});
|
||||
});
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
public listenForMentions(dialogId?: string, dialogArgs?: any): this {
|
||||
var sessions: { [key: string]: ISessionState; } = {};
|
||||
var dispatch = (bot: Bot, msg: ISlackMessage, ss?: ISessionState) => {
|
||||
bot.identifyTeam((err, teamId) => {
|
||||
msg.team = teamId;
|
||||
this.dispatchMessage(bot, msg, dialogId, dialogArgs, ss);
|
||||
});
|
||||
};
|
||||
|
||||
dialogId = dialogId || this.options.defaultDialogId;
|
||||
dialogArgs = dialogArgs || this.options.defaultDialogArgs;
|
||||
this.controller.on('direct_message', (bot: Bot, msg: ISlackMessage) => {
|
||||
dispatch(bot, msg);
|
||||
});
|
||||
['direct_mention','mention'].forEach((type) => {
|
||||
this.controller.on(type, (bot: Bot, msg: ISlackMessage) => {
|
||||
// Create a new session
|
||||
var key = msg.channel + ':' + msg.user;
|
||||
var ss = sessions[key] = { callstack: <any>[], lastAccess: new Date().getTime() };
|
||||
dispatch(bot, msg, ss);
|
||||
});
|
||||
});
|
||||
this.controller.on('ambient', (bot: Bot, msg: ISlackMessage) => {
|
||||
// Conditionally dispatch the message
|
||||
var key = msg.channel + ':' + msg.user;
|
||||
if (sessions.hasOwnProperty(key)) {
|
||||
// Validate session
|
||||
var ss = sessions[key];
|
||||
if (ss.callstack && ss.callstack.length > 0 && (new Date().getTime() - ss.lastAccess) <= this.options.ambientMentionDuration) {
|
||||
dispatch(bot, msg, ss);
|
||||
} else {
|
||||
delete sessions[key];
|
||||
}
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
public beginDialog(address: ISlackBeginDialogAddress, dialogId: string, dialogArgs?: any): this {
|
||||
// Validate args
|
||||
if (!address.user && !address.channel) {
|
||||
|
@ -112,7 +158,7 @@ export class SlackBot extends collection.DialogCollection {
|
|||
return this;
|
||||
}
|
||||
|
||||
private dispatchMessage(bot: Bot, msg: ISlackMessage, dialogId: string, dialogArgs: any) {
|
||||
private dispatchMessage(bot: Bot, msg: ISlackMessage, dialogId: string, dialogArgs: any, smartState?: ISessionState) {
|
||||
var onError = (err: Error) => {
|
||||
this.emit('error', err, msg);
|
||||
};
|
||||
|
@ -129,7 +175,7 @@ export class SlackBot extends collection.DialogCollection {
|
|||
var teamData = ses.teamData && ses.teamData.id ? utils.clone(ses.teamData) : null;
|
||||
var channelData = ses.channelData && ses.channelData.id ? utils.clone(ses.channelData) : null;
|
||||
var userData = ses.userData && ses.userData.id ? utils.clone(ses.userData) : null;
|
||||
if (channelData) {
|
||||
if (channelData && !smartState) {
|
||||
channelData[consts.Data.SessionState] = ses.sessionState;
|
||||
}
|
||||
|
||||
|
@ -179,7 +225,9 @@ export class SlackBot extends collection.DialogCollection {
|
|||
}
|
||||
|
||||
// Unpack session state
|
||||
if (data.channelData && data.channelData.hasOwnProperty(consts.Data.SessionState)) {
|
||||
if (smartState) {
|
||||
sessionState = smartState;
|
||||
} else if (data.channelData && data.channelData.hasOwnProperty(consts.Data.SessionState)) {
|
||||
sessionState = (<any>data.channelData)[consts.Data.SessionState];
|
||||
delete (<any>data.channelData)[consts.Data.SessionState];
|
||||
}
|
||||
|
|
|
@ -3,11 +3,7 @@ import dialog = require('./Dialog');
|
|||
import consts = require('../consts');
|
||||
|
||||
export interface IDialogWaterfallStep {
|
||||
(session: ISession, result?: any, skip?: IDialogWaterfallCursor): void;
|
||||
}
|
||||
|
||||
export interface IDialogWaterfallCursor {
|
||||
(count?: number, results?: dialog.IDialogResult<any>): void;
|
||||
(session: ISession, result?: any, skip?: (results?: dialog.IDialogResult<any>) => void): void;
|
||||
}
|
||||
|
||||
export class DialogAction {
|
||||
|
@ -48,9 +44,11 @@ export class DialogAction {
|
|||
|
||||
static waterfall(steps: IDialogWaterfallStep[]): IDialogHandler<any> {
|
||||
return function waterfallAction(s: ISession, r: dialog.IDialogResult<any>) {
|
||||
var skip = (count = 1, result?: dialog.IDialogResult<any>) => {
|
||||
result = result || { resumed: dialog.ResumeReason.forward };
|
||||
s.dialogData[consts.Data.WaterfallStep] += count;
|
||||
var skip = (result?: dialog.IDialogResult<any>) => {
|
||||
result = result || <any>{};
|
||||
if (!result.resumed) {
|
||||
result.resumed = dialog.ResumeReason.forward;
|
||||
}
|
||||
waterfallAction(s, result);
|
||||
};
|
||||
|
||||
|
@ -63,9 +61,6 @@ export class DialogAction {
|
|||
case dialog.ResumeReason.back:
|
||||
step -= 1;
|
||||
break;
|
||||
case dialog.ResumeReason.forward:
|
||||
step += 2;
|
||||
break;
|
||||
default:
|
||||
step++;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче