From 70b176cc46762513be1e80a45c8330f1f373c70d Mon Sep 17 00:00:00 2001 From: Yuki Nagai Date: Fri, 11 Aug 2017 18:54:50 +0900 Subject: [PATCH 1/4] Typescript Declaration (#1) * d.ts * Conversation * slack * slack * cisco spark * storage & logger * remove others * example slack * facebook * studio * twilio * bot framework * rm lib/_Botkit.d.ts * package.json --- lib/Botkit.d.ts | 482 ++++++++++++++++++++++++++++++++++++++++++++++++ package.json | 1 + 2 files changed, 483 insertions(+) create mode 100644 lib/Botkit.d.ts diff --git a/lib/Botkit.d.ts b/lib/Botkit.d.ts new file mode 100644 index 000000000..666d86a2c --- /dev/null +++ b/lib/Botkit.d.ts @@ -0,0 +1,482 @@ +declare namespace botkit { + function botframeworkbot(configuration: BotFrameworkConfiguration): BotFrameworkController; + function consolebot(configuration: ConsoleConfiguration): ConsoleController; + function facebookbot(configuration: FacebookConfiguration): FacebookController; + function slackbot(configuration: SlackConfiguration): SlackController; + function sparkbot(configuration: CiscoSparkConfiguration): CiscoSparkController; + function twilioipmbot(configuration: TwilioIPMConfiguration): TwilioIPMController; + function twiliosmsbot(configuration: TwilioSMSConfiguration): TwilioSMSController; + interface Bot { + readonly botkit: Controller; + readonly identity: Identity; + readonly utterances: { + yes: RegExp; + no: RegExp; + quit: RegExp; + }; + createConversation(message: M, cb: (err: Error, convo: Conversation) => void): void; + reply(src: M, resp: string | M, cb?: (err: Error, res: any) => void): void; + startConversation(message: M, cb: (err: Error, convo: Conversation) => void): void; + } + interface BotFrameworkBot extends Bot { + } + interface BotFrameworkConfiguration extends Configuration { + } + interface BotFrameworkController extends Controller { + createWebhookEndpoints(webserver: any, bot: TwilioSMSBot, cb?: () => void): this; + } + interface BotFrameworkMessage extends Message { + } + interface BotFrameworkSpawnConfiguration { + appId: string; + appPassword: string; + } + interface Channel { + id: string; + } + interface CiscoSparkBot extends Bot { + retrieveFile(url: string, cb: (err: Error, body: any) => void): void; + retrieveFileInfo(url: string, cb: (err: Error, obj: any) => void): void; + startPrivateConversation(message: CiscoSparkMessage, cb: (err: Error, convo: Conversation) => void): void; + startPrivateConversationWithActor(message: CiscoSparkMessage, cb: (err: Error, convo: Conversation) => void): void; + startPrivateConversationWithPersonId(personId: string, cb: (err: Error, convo: Conversation) => void): void; + } + interface CiscoSparkConfiguration extends Configuration { + ciscospark_access_token: string; + limit_to_domain?: string | string[]; + limit_to_org?: string; + public_address: string; + secret?: string; + webhook_name?: string; + } + interface CiscoSparkController extends Controller { + createWebhookEndpoints(webserver: any, bot: CiscoSparkBot, cb?: () => void): this; + } + interface CiscoSparkMessage extends Message { + actorId?: string; + data?: { + personDisplayName: string; + }; + files?: any[]; + markdown?: string; + original_message?: CiscoSparkMessage; + } + interface CiscoSparkSpawnConfiguration { + } + interface Configuration { + debug?: boolean; + hostname?: string; + json_file_store?: string; + log?: boolean; + logger?: { log: Function; }; + storage?: { + users: Storage; + channels: Storage; + teams: Storage; + }; + studio_token?: string; + } + interface ConsoleBot extends Bot { + } + interface ConsoleConfiguration extends Configuration { + } + interface ConsoleController extends Controller { + } + interface ConsoleMessage extends Message { + } + interface ConsoleSpawnConfiguration { + } + interface Controller> { + readonly storage: { + users: Storage; + channels: Storage; + teams: Storage; + }; + readonly studio: Studio; + readonly log: { + (...params: any[]): void; + } + hears(keywords: string | string[] | RegExp | RegExp[], events: string | string[], cb: HearsCallback): this; + hears(keywords: string | string[] | RegExp | RegExp[], events: string | string[], middleware_or_cb: HearsFunction, cb: HearsCallback): this; + on(event: string, cb: HearsCallback): this; + setupWebserver(port: number | string, cb: (err: Error, webserver: any) => void): this; + spawn(config?: S, cb?: (worker: B) => void): B; + startTicking(): void; + } + interface Conversation { + readonly status: ConversationStatusType; + activate(): void; + addMessage(message: string | M, thread: string): void; + addQuestion(message: string | M, cb: ConversationCallback, capture_options: ConversationCaptureOptions, thread: string): void; + ask(message: string | M, cb: ConversationCallback, capture_options?: ConversationCaptureOptions): void; + beforeThread(thread: string, callback: (convo: this, next: (err: string | Error) => void) => void): void; + extractResponse(key: string): string; + extractResponses(): { [key: string]: string }; + gotoThread(thread: string): void; + next(): void; + on(event: string, cb: (convo: this) => void): void; + onTimeout(handler: (convo: this) => void): void; + repeat(): void; + say(message: string | M): void; + sayFirst(message: string | M): void; + setTimeout(timeout: number): void; + setVar(field: string, value: any): void; + silentRepeat(): void; + stop(status?: ConversationStatusType): void; + transitionTo(thread: string, message: string | M): void; + } + interface ConversationCaptureOptions { + key?: string; + multiple?: boolean; + } + interface FacebookAttachment { + type: 'audio' | 'file' | 'image' | 'video'; + payload: any; + } + interface FacebookBot extends Bot { + replyWithTyping(src: FacebookMessage, resp: string | FacebookMessage, cb?: (err: Error) => void): void; + startTyping(src: FacebookMessage, cb?: (err: Error) => void): void; + stopTyping(src: FacebookMessage, cb?: (err: Error) => void): void; + } + interface FacebookConfiguration extends Configuration { + access_token: string; + app_secret?: string; + receive_via_postback?: boolean; + require_delivery?: boolean; + validate_requests?: boolean; + verify_token: string; + } + interface FacebookController extends Controller { + readonly api: { + attachment_upload: { + upload(attachment: FacebookAttachment, cb: (err: Error, attachment_id: string) => void): void; + }; + messenger_profile: any; + thread_settings: any; + }; + createWebhookEndpoints(webserver: any, bot: FacebookBot, cb?: () => void): this; + } + interface FacebookMessage extends Message { + attachment?: FacebookAttachment; + notification_type: 'REGULAR' | 'SILENT_PUSH' | 'NO_PUSH'; + payload?: string; + sender_action?: 'typing_on' | 'typing_off'; + } + interface FacebookMessengerProfileAPI { + account_linking(payload: string): void; + delete_account_linking(): void; + delete_domain_whitelist(): void; + delete_get_started(): void; + delete_greeting(): void; + delete_home_url(): void; + delete_menu(): void; + delete_target_audience(): void; + domain_whitelist(payload: string | string[]): void; + get_account_linking(cb: (err: Error, body: any) => void): void; + get_domain_whitelist(cb: (err: Error, body: any) => void): void; + get_get_started(cb: (err: Error, body: any) => void): void; + get_greeting(cb: (err: Error, body: any) => void): void; + get_home_url(cb: (err: Error, body: any) => void): void; + get_started(payload: string): void; + get_menu(cb: (err: Error, body: any) => void): void; + get_messenger_code(image_size: number, cb: (err: Error, uri: string) => void, ref?: string): void; + get_target_audience(cb: (err: Error, body: any) => void): void; + greeting(payload: string | { locale: string; text: string; }[]): void; + home_url(payload: { url: string; webview_height_ratio: 'tall'; webview_share_button?: 'show' | 'hide'; in_test?: boolean; }): void; + menu(payload: any): void; + target_audience(payload: { audience_type: 'all' | 'custom' | 'none'; countries?: { blacklist?: string[]; whitelist?: string[]; }; }): void; + } + interface FacebookSpawnConfiguration { + } + interface Identity { + name: string; + emails: string[]; + } + interface Message { + action?: string; + channel?: string; + match?: RegExpMatchArray; + text?: string; + user?: string; + } + interface SlackAttachment { + author_icon?: string; + author_link?: string; + author_name?: string; + color?: string; + fallback?: string; + fields?: { + title: string; + value: string; + short: boolean; + }[]; + footer?: string; + footer_icon?: string; + image_url?: string; + pretext?: string; + text?: string; + thumb_url?: string; + title?: string; + title_link?: string; + ts?: string; + } + interface SlackBot extends Bot { + readonly api: SlackWebAPI; + configureIncomingWebhook(config: { url: string; }): this; + createConversationInThread(src: SlackMessage, cb: (err: Error, res: string) => void): void; + createPrivateConversation(message: SlackMessage & { user: string; }, cb: (err: Error, convo: Conversation) => void): void; + closeRTM(): void; + destroy(): void; + identifyTeam(): string; + identifyBot(): { id: string; name: string; team_id: string; }; + replyAcknowledge(cb?: (err: Error) => void): void; + replyAndUpdate(src: SlackMessage, resp: string | SlackMessage, cb: (err: Error, res: string) => void): void; + replyInThread(src: SlackMessage, resp: string | SlackMessage, cb: (err: Error, res: string) => void): void; + replyPrivate(src: SlackMessage, resp: string | SlackMessage, cb?: (err: Error) => void): void; + replyPrivateDelayed(src: SlackMessage, resp: string | SlackMessage, cb?: (err: Error) => void): void; + replyPublic(src: SlackMessage, resp: string | SlackMessage, cb?: (err: Error) => void): void; + replyPublicDelayed(src: SlackMessage, resp: string | SlackMessage, cb?: (err: Error) => void): void; + replyInteractive(src: SlackMessage, resp: string | SlackMessage, cb?: (err: Error) => void): void; + sendWebhook(options: SlackMessage, cb: (err: string, body: any) => void): void; + startPrivateConversation(message: SlackMessage & { user: string; }, cb: (err: Error, convo: Conversation) => void): void; + startConversationInThread(src: SlackMessage, cb: (err: Error, res: string) => void): void; + startRTM(cb?: (err: string, bot: SlackBot, payload: any) => void): SlackBot; + } + interface SlackConfiguration extends Configuration { + api_root?: string; + clientId?: string; + clientSecret?: string; + disable_startup_messages?: boolean; + incoming_webhook?: { url: string; }; + interactive_replies?: boolean; + rtm_receive_messages?: boolean; + require_delivery?: boolean; + retry?: number; + scopes?: string[]; + send_via_rtm?: boolean; + stale_connection_timeout?: number; + } + interface SlackController extends Controller { + configureSlackApp(config: { clientId: string; clientSecret: string; redirectUri: string; scopes: string[]; }): this; + createHomepageEndpoint(webserver: any): this; + createOauthEndpoints(webserver: any, callback: (err: Error, req: any, res: any) => void): this; + createWebhookEndpoints(webserver: any, authenticationTokens?: string[]): this; + setupWebserver(); + getAuthorizeURL(team_id: string, redirect_params: any): string; + } + interface SlackMessage extends Message { + attachments?: SlackAttachment[]; + icon_emoji?: string; + icon_url?: string; + link_names?: boolean; + parse?: string; + reply_broadcast?: boolean; + type?: string; + thread_ts?: string; + ts?: string; + unfurl_links?: boolean; + unfurl_media?: boolean; + username?: string; + } + interface SlackSpawnConfiguration { + token: string; + } + interface SlackWebAPI { + auth: { + test: SlackWebAPIMethod; + }, + oauth: { + access: SlackWebAPIMethod; + } + channels: { + archive: SlackWebAPIMethod; + create: SlackWebAPIMethod; + history: SlackWebAPIMethod; + info: SlackWebAPIMethod; + invite: SlackWebAPIMethod; + join: SlackWebAPIMethod; + kick: SlackWebAPIMethod; + leave: SlackWebAPIMethod; + list: SlackWebAPIMethod; + mark: SlackWebAPIMethod; + rename: SlackWebAPIMethod; + replies: SlackWebAPIMethod; + setPurpose: SlackWebAPIMethod; + setTopic: SlackWebAPIMethod; + unarchive: SlackWebAPIMethod; + }; + chat: { + delete: SlackWebAPIMethod; + postMessage: SlackWebAPIMethod; + update: SlackWebAPIMethod; + unfurl: SlackWebAPIMethod; + }; + dnd: { + endDnd: SlackWebAPIMethod; + endSnooze: SlackWebAPIMethod; + info: SlackWebAPIMethod; + setSnooze: SlackWebAPIMethod; + teamInfo: SlackWebAPIMethod; + }; + emoji: { + list: SlackWebAPIMethod; + }; + files: { + delete: SlackWebAPIMethod; + info: SlackWebAPIMethod; + list: SlackWebAPIMethod; + upload: SlackWebAPIMethod; + }; + groups: { + archive: SlackWebAPIMethod; + close: SlackWebAPIMethod; + create: SlackWebAPIMethod; + createChild: SlackWebAPIMethod; + history: SlackWebAPIMethod; + info: SlackWebAPIMethod; + invite: SlackWebAPIMethod; + kick: SlackWebAPIMethod; + leave: SlackWebAPIMethod; + list: SlackWebAPIMethod; + mark: SlackWebAPIMethod; + open: SlackWebAPIMethod; + rename: SlackWebAPIMethod; + replies: SlackWebAPIMethod; + setPurpose: SlackWebAPIMethod; + setTopic: SlackWebAPIMethod; + unarchive: SlackWebAPIMethod; + }; + im: { + close: SlackWebAPIMethod; + history: SlackWebAPIMethod; + list: SlackWebAPIMethod; + mark: SlackWebAPIMethod; + open: SlackWebAPIMethod; + replies: SlackWebAPIMethod; + }; + mpim: { + close: SlackWebAPIMethod; + history: SlackWebAPIMethod; + list: SlackWebAPIMethod; + mark: SlackWebAPIMethod; + open: SlackWebAPIMethod; + replies: SlackWebAPIMethod; + }; + pins: { + add: SlackWebAPIMethod; + list: SlackWebAPIMethod; + remove: SlackWebAPIMethod; + }; + reactions: { + add: SlackWebAPIMethod; + get: SlackWebAPIMethod; + list: SlackWebAPIMethod; + remove: SlackWebAPIMethod; + }; + reminders: { + add: SlackWebAPIMethod; + complete: SlackWebAPIMethod; + delete: SlackWebAPIMethod; + info: SlackWebAPIMethod; + list: SlackWebAPIMethod; + }; + rtm: { + start: SlackWebAPIMethod; + connect: SlackWebAPIMethod; + }; + search: { + all: SlackWebAPIMethod; + files: SlackWebAPIMethod; + messages: SlackWebAPIMethod; + }; + stars: { + add: SlackWebAPIMethod; + list: SlackWebAPIMethod; + remove: SlackWebAPIMethod; + }; + team: { + accessLogs: SlackWebAPIMethod; + info: SlackWebAPIMethod; + billableInfo: SlackWebAPIMethod; + integrationLogs: SlackWebAPIMethod; + profile: { + get: SlackWebAPIMethod; + }; + }; + users: { + getPresence: SlackWebAPIMethod; + info: SlackWebAPIMethod; + list: SlackWebAPIMethod; + setActive: SlackWebAPIMethod; + setPresence: SlackWebAPIMethod; + deletePhoto: SlackWebAPIMethod; + identity: SlackWebAPIMethod; + setPhoto: SlackWebAPIMethod; + profile: { + get: SlackWebAPIMethod; + set: SlackWebAPIMethod; + }; + }; + } + interface Storage { + save: (data: O, cb?: (err: Error, id: string) => void) => void; + get: (id: string, cb: (err: Error, data: O) => void) => void; + delete?: (id: string, cb?: (err: Error) => void) => void; + all?: (cb: (err: Error, data: O[]) => void) => void; + } + interface Studio> { + after(command_name: string, func: (convo: Conversation, next: () => void) => void): this; + before(command_name: string, func: (convo: Conversation, next: () => void) => void): this; + beforeThread(command_name: string, thread_name: string, func: (convo: Conversation, next: () => void) => void): this; + get(bot: B, input_text: string, user: string, channel: string): Promise>; + run(bot: B, input_text: string, user: string, channel: string): Promise>; + runTrigger(bot: B, input_text: string, user: string, channel: string): Promise>; + validate(command_name: string, key: string, func: (convo: Conversation, next: () => void) => void): this; + } + interface Team { + id: string; + } + interface TwilioIPMBot extends Bot { + readonly api: any; + } + interface TwilioIPMConfiguration extends Configuration { + } + interface TwilioIPMController extends Controller { + createWebhookEndpoints(webserver: any, bot: TwilioIPMBot): this; + } + interface TwilioIPMMessage extends Message { + } + interface TwilioIPMSpawnConfiguration { + autojoin?: boolean; + identity?: string; + TWILIO_IPM_SERVICE_SID: string; + TWILIO_ACCOUNT_SID: string; + TWILIO_API_KEY: string; + TWILIO_API_SECRET: string; + } + interface TwilioSMSBot extends Bot { + } + interface TwilioSMSConfiguration extends Configuration { + account_sid: string; + auth_token: string; + twilio_number: string; + } + interface TwilioSMSController extends Controller { + createWebhookEndpoints(webserver: any, bot: TwilioSMSBot, cb?: () => void): this; + } + interface TwilioSMSMessage extends Message { + } + interface TwilioSMSSpawnConfiguration { + } + interface User { + id: string; + name?: string; + } + type ConversationCallback = ((message: M, convo: Conversation) => void) | ({ pattern?: string | RegExp; default?: boolean; callback: (message: M, convo: Conversation) => void; }[]); + type ConversationStatusType = 'completed' | 'active' | 'stopped' | 'timeout' | 'ending' | 'inactive'; + type HearsCallback> = (bot: B, message: M) => void; + type HearsFunction = (tests: string | string[] | RegExp | RegExp[], message: M) => boolean; + type SlackWebAPIMethod = (data: any, cb: (err: Error, response: any) => void) => void; +} + +export default botkit; diff --git a/package.json b/package.json index 28c1087f3..466e44534 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "version": "0.5.7", "description": "Building blocks for Building Bots", "main": "lib/Botkit.js", + "types": "lib/Botkit.d.ts", "dependencies": { "async": "^2.1.5", "back": "^1.0.1", From 97fd4d06ad4b8aac5fb6eadb965e801d34d932aa Mon Sep 17 00:00:00 2001 From: Yuki Nagai Date: Tue, 15 Aug 2017 09:03:44 +0900 Subject: [PATCH 2/4] Add middleware property to Controller interface --- lib/Botkit.d.ts | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/lib/Botkit.d.ts b/lib/Botkit.d.ts index 666d86a2c..b6ffd5f06 100644 --- a/lib/Botkit.d.ts +++ b/lib/Botkit.d.ts @@ -87,15 +87,30 @@ declare namespace botkit { interface ConsoleSpawnConfiguration { } interface Controller> { + readonly changeEars: HearsFunction; + readonly log: { + (...params: any[]): void; + } + readonly middleware: { + capture: { + use(cb: (bot: B, message: M, convo: Conversation, next: () => void) => void): void; + }; + heard: { + use(cb: (bot: B, message: M, next: () => void) => void): void; + }; + receive: { + use(cb: (bot: B, message: M, next: () => void) => void): void; + }; + send: { + use(cb: (bot: B, message: M, next: () => void) => void): void; + }; + } readonly storage: { users: Storage; channels: Storage; teams: Storage; }; readonly studio: Studio; - readonly log: { - (...params: any[]): void; - } hears(keywords: string | string[] | RegExp | RegExp[], events: string | string[], cb: HearsCallback): this; hears(keywords: string | string[] | RegExp | RegExp[], events: string | string[], middleware_or_cb: HearsFunction, cb: HearsCallback): this; on(event: string, cb: HearsCallback): this; From 5951c740ed0448c86f1d62643b69472a785767d2 Mon Sep 17 00:00:00 2001 From: Yuki Nagai Date: Wed, 16 Aug 2017 04:49:26 +0900 Subject: [PATCH 3/4] fix import error --- lib/Botkit.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Botkit.d.ts b/lib/Botkit.d.ts index b6ffd5f06..829f195bf 100644 --- a/lib/Botkit.d.ts +++ b/lib/Botkit.d.ts @@ -494,4 +494,4 @@ declare namespace botkit { type SlackWebAPIMethod = (data: any, cb: (err: Error, response: any) => void) => void; } -export default botkit; +export = botkit; From b9800bca650bd82c58b33c5b506ae011a7412bb4 Mon Sep 17 00:00:00 2001 From: Yuki Nagai Date: Fri, 18 Aug 2017 07:26:40 +0900 Subject: [PATCH 4/4] Typescript example --- examples/typescript_bot.ts | 214 +++++++++++++++++++++++++++++++++++++ 1 file changed, 214 insertions(+) create mode 100644 examples/typescript_bot.ts diff --git a/examples/typescript_bot.ts b/examples/typescript_bot.ts new file mode 100644 index 000000000..222584935 --- /dev/null +++ b/examples/typescript_bot.ts @@ -0,0 +1,214 @@ +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ______ ______ ______ __ __ __ ______ + /\ == \ /\ __ \ /\__ _\ /\ \/ / /\ \ /\__ _\ + \ \ __< \ \ \/\ \ \/_/\ \/ \ \ _"-. \ \ \ \/_/\ \/ + \ \_____\ \ \_____\ \ \_\ \ \_\ \_\ \ \_\ \ \_\ + \/_____/ \/_____/ \/_/ \/_/\/_/ \/_/ \/_/ + + +This is a sample Slack bot built with Botkit and Typescript. + +# RUN THE BOT: + + Get a Bot token from Slack: + + -> http://my.slack.com/services/new/bot + + Compile typescript to javascript: + + tsc typescript_bot.ts + + Run your bot from the command line: + + token= node typescript_bot.js + +# USE THE BOT: + + Find your bot inside Slack to send it a direct message. + + Say: "Hello" + + The bot will reply "Hello!" + + Say: "who are you?" + + The bot will tell you its name, where it is running, and for how long. + + Say: "Call me " + + Tell the bot your nickname. Now you are friends. + + Say: "who am I?" + + The bot will tell you your nickname, if it knows one for you. + + Say: "shutdown" + + The bot will ask if you are sure, and then shut itself down. + + Make sure to invite your bot into other channels using /invite @! + +# EXTEND THE BOT: + + Botkit has many features for building cool and useful bots! + + Read all about it here: + + -> http://howdy.ai/botkit + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +declare let process: { + env: { + token: string; + }; + exit(status?: number); +}; + +import Botkit = require('../lib/botkit'); + +if (!process.env.token) { + console.log('Error: Specify token in environment'); + process.exit(1); +} + +const controller = Botkit.slackbot({ + debug: true, +}); + +const bot = controller.spawn({ + token: process.env.token +}).startRTM(); + +controller.hears(['hello', 'hi'], 'direct_message,direct_mention,mention', function(bot, message) { + + bot.api.reactions.add({ + timestamp: message.ts, + channel: message.channel, + name: 'robot_face', + }, function(err, res) { + if (err) { + bot.botkit.log('Failed to add emoji reaction :(', err); + } + }); + + + controller.storage.users.get(message.user, function(err, user) { + if (user && user.name) { + bot.reply(message, 'Hello ' + user.name + '!!'); + } else { + bot.reply(message, 'Hello.'); + } + }); +}); + +controller.hears(['call me (.*)', 'my name is (.*)'], 'direct_message,direct_mention,mention', function(bot, message) { + var name = message.match[1]; + controller.storage.users.get(message.user, function(err, user) { + if (!user) { + user = { + id: message.user, + }; + } + user.name = name; + controller.storage.users.save(user, function(err, id) { + bot.reply(message, 'Got it. I will call you ' + user.name + ' from now on.'); + }); + }); +}); + +controller.hears(['what is my name', 'who am i'], 'direct_message,direct_mention,mention', function(bot, message) { + + controller.storage.users.get(message.user, function(err, user) { + if (user && user.name) { + bot.reply(message, 'Your name is ' + user.name); + } else { + bot.startConversation(message, function(err, convo) { + if (!err) { + convo.say('I do not know your name yet!'); + convo.ask('What should I call you?', function(response, convo) { + convo.ask('You want me to call you `' + response.text + '`?', [ + { + pattern: 'yes', + callback: function(response, convo) { + // since no further messages are queued after this, + // the conversation will end naturally with status == 'completed' + convo.next(); + } + }, + { + pattern: 'no', + callback: function(response, convo) { + // stop the conversation. this will cause it to end with status == 'stopped' + convo.stop(); + } + }, + { + default: true, + callback: function(response, convo) { + convo.repeat(); + convo.next(); + } + } + ]); + + convo.next(); + + }, {'key': 'nickname'}); // store the results in a field called nickname + + convo.on('end', function(convo) { + if (convo.status == 'completed') { + bot.reply(message, 'OK! I will update my dossier...'); + + controller.storage.users.get(message.user, function(err, user) { + if (!user) { + user = { + id: message.user, + }; + } + user.name = convo.extractResponse('nickname'); + controller.storage.users.save(user, function(err, id) { + bot.reply(message, 'Got it. I will call you ' + user.name + ' from now on.'); + }); + }); + + + + } else { + // this happens if the conversation ended prematurely for some reason + bot.reply(message, 'OK, nevermind!'); + } + }); + } + }); + } + }); +}); + + +controller.hears(['shutdown'], 'direct_message,direct_mention,mention', function(bot, message) { + + bot.startConversation(message, function(err, convo) { + + convo.ask('Are you sure you want me to shutdown?', [ + { + pattern: bot.utterances.yes, + callback: function(response, convo) { + convo.say('Bye!'); + convo.next(); + setTimeout(function() { + process.exit(); + }, 3000); + } + }, + { + pattern: bot.utterances.no, + default: true, + callback: function(response, convo) { + convo.say('*Phew!*'); + convo.next(); + } + } + ]); + }); +});