Skip to content

Instantly share code, notes, and snippets.

@yaron-idan
Created September 9, 2019 08:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yaron-idan/e38de24133841ccfda4f0be75d45034e to your computer and use it in GitHub Desktop.
Save yaron-idan/e38de24133841ccfda4f0be75d45034e to your computer and use it in GitHub Desktop.
Botkit Duplicate messages
// __ __ ___ ___
// |__) / \ | |__/ | |
// |__) \__/ | | \ | |
// This is the main file for the mock-bot bot.
// Import Botkit's core features
const { Botkit, BotkitConversation } = require('botkit');
const { BotkitCMSHelper } = require('botkit-plugin-cms');
// Import a platform-specific adapter for slack.
const { SlackAdapter, SlackMessageTypeMiddleware, SlackEventMiddleware } = require('botbuilder-adapter-slack');
const { MongoDbStorage } = require('botbuilder-storage-mongodb');
// Load process.env values from .env file
require('dotenv').config();
let storage = null;
if (process.env.MONGO_URI) {
storage = mongoStorage = new MongoDbStorage({
url : process.env.MONGO_URI,
});
}
const adapter = new SlackAdapter({
// REMOVE THIS OPTION AFTER YOU HAVE CONFIGURED YOUR APP!
enable_incomplete: true,
// parameters used to secure webhook endpoint
verificationToken: process.env.verificationToken,
clientSigningSecret: process.env.clientSigningSecret,
// auth token for a single-team app
botToken: process.env.botToken,
// credentials used to set up oauth for multi-team apps
clientId: process.env.clientId,
clientSecret: process.env.clientSecret,
scopes: ['bot'],
redirectUri: process.env.redirectUri,
// functions required for retrieving team-specific info
// for use in multi-team apps
getTokenForTeam: getTokenForTeam,
getBotUserByTeam: getBotUserByTeam,
});
// Use SlackEventMiddleware to emit events that match their original Slack event types.
adapter.use(new SlackEventMiddleware());
// Use SlackMessageType middleware to further classify messages as direct_message, direct_mention, or mention
adapter.use(new SlackMessageTypeMiddleware());
const controller = new Botkit({
webhook_uri: '/api/messages',
adapter: adapter,
storage
});
if (process.env.cms_uri) {
controller.usePlugin(new BotkitCMSHelper({
uri: process.env.cms_uri,
token: process.env.cms_token,
}));
}
// Once the bot has booted up its internal services, you can use them to do stuff.
controller.ready(() => {
// load traditional developer-created local custom feature modules
controller.loadModules(__dirname + '/features');
/* catch-all that uses the CMS to trigger dialogs */
if (controller.plugins.cms) {
controller.on('message,direct_message', async (bot, message) => {
let results = false;
results = await controller.plugins.cms.testTrigger(bot, message);
if (results !== false) {
// do not continue middleware!
return false;
}
});
}
});
controller.webserver.get('/', (req, res) => {
res.send(`This app is running Botkit ${ controller.version }.`);
});
controller.webserver.get('/install', (req, res) => {
// getInstallLink points to slack's oauth endpoint and includes clientId and scopes
res.redirect(controller.adapter.getInstallLink());
});
controller.webserver.get('/install/auth', async (req, res) => {
try {
const results = await controller.adapter.validateOauthCode(req.query.code);
console.log('FULL OAUTH DETAILS', results);
// Store token by team in bot state.
tokenCache[results.team_id] = results.bot.bot_access_token;
// Capture team to bot id
userCache[results.team_id] = results.bot.bot_user_id;
res.json('Success! Bot installed.');
} catch (err) {
console.error('OAUTH ERROR:', err);
res.status(401);
res.send(err.message);
}
});
let tokenCache = {};
let userCache = {};
if (process.env.TOKENS) {
tokenCache = JSON.parse(process.env.TOKENS);
}
if (process.env.USERS) {
userCache = JSON.parse(process.env.USERS);
}
const MY_DIALOG_ID = 'my-dialog-name-constant';
let convo = new BotkitConversation(MY_DIALOG_ID, controller);
convo.addAction('my_thread');
convo.addMessage("My thread starts here", "my_thread");
// do a simple conditional branch looking for user to say "no"
convo.addQuestion("Would you like this thread to wait a bit?", [{
pattern: 'no',
handler: async (response, convo, bot) => {
// if user says no, process to create repo.
}
},
{
pattern: 'yes',
handler: async (response, convo, bot) => {
// if user says yes, create the namespace.
console.log("Waiting a bit...");
let initResponse = await funcThatWaits();
let initMessage = await bot.say(initResponse);
await bot.say("Leaving the thread");
}
},
{
default: true,
handler: async (response, convo, bot) => {
// do nothing, allow convo to complete.
}
}
], 'confirm', 'my_thread');
convo.addMessage("end of thread and convo", "my_thread");
convo.addAction('complete');
controller.addDialog(convo);
async function funcThatWaits()
{
const timeout = ms => new Promise(res => setTimeout(res, ms))
console.log("Let's wait");
await timeout(5000);
console.log("Finished waiting");
return "Finished waiting";
}
async function getTokenForTeam(teamId) {
if (tokenCache[teamId]) {
return new Promise((resolve) => {
setTimeout(function() {
resolve(tokenCache[teamId]);
}, 150);
});
} else {
console.error('Team not found in tokenCache: ', teamId);
}
}
async function getBotUserByTeam(teamId) {
if (userCache[teamId]) {
return new Promise((resolve) => {
setTimeout(function() {
resolve(userCache[teamId]);
}, 150);
});
} else {
console.error('Team not found in userCache: ', teamId);
}
}
const { SlackDialog } = require('botbuilder-adapter-slack');
const { BotkitConversation } = require('botkit');
//TODO: This constant should be defined once, maybe in configuration
const MY_DIALOG_ID = 'my-dialog-name-constant';
module.exports = function(controller) {
controller.hears('waiting_on_a_thread', 'direct_message', async(bot, message) => {
await bot.startConversationInThread(message.channel, message.user, message.incoming_message.channelData.ts);
await bot.beginDialog(MY_DIALOG_ID);
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment