Changelog
PATCH 3.1.1
Just a couple bugfixes. Use 3.1.1 instead of 3.1.0.
MINOR 3.1.0
WebhookEndpoints can now be specified either with pre-pending or trailing slash (Thanks to @Damell for that (https://github.com/botmasterai/botmaster/pull/26)).
Webhook endpoints don't need to be prepended with the bot type anymore (request by @younes200 (https://github.com/botmasterai/botmaster/pull/23)). A new option was added for this in the botmaster controller. And it is used as such:
const botmaster = new Botmaster({
useDefaultMountPathPrepend: false,
});
By doing this, if you setup a webhook using bot like the in the botmaster-messenger
package as such:
const messengerSettings = {
credentials: someCredentials,
webhookEndpoint: 'webhook/endoint/1234',
};
You would then be settings up your webhook in Messenger to be:
<your_base_url>/webhook/endpoint/1234
rather than <your_base_url>/messenger/webhook/endpoint/1234
None of these changes should impact your code
Another change was made in the structure of botmaster, but that should not impact you unless you were accessing the private variable sendOptions.__update
in your middleware. If so, know that it has been moved to bot.__associatedUpdate
and have a look at the code that was part of this release.
MAJOR 3.0.8
Botmaster v3 is almost a complete rewrite of botmaster. Although much of the philosophy is the same, a lot of breaking changes have been introduced. Surely, you will find that they are for the better. This changelog doubles as a migration help.
1. Upgrading to botmastser v3
You most likely have a line that looks like this in your package.json:
"dependencies": {
"botmaster": "^2.x.x",
}
In order to change that, either do a
yarn upgrade botmaster
if using yarn
or:
npm install --save botmaster@3
if using npm
This will change that line in your dependencies to something like:
"dependencies": {
"botmaster": "^3.0.8",
}
Or newer if newer versions were released recently.
2. Bot classes now have their own packages
Firstly, all the bot classes have been taken out and put in their own packages. This means if you want to write a bot that only works on, say, socket.io and Messenger, you don't actually have the code for the other bot classes anymore. You would need to do something like this:
yarn add botmaster
yarn add botmaster-messenger
yarn add botmaster-socket.io
or if still using npm
npm install --save botmaster
npm install --save botmaster-messenger
npm install --save botmaster-socket.io
then in your code:
const Botmaster = require('botmaster');
const MessengerBot = require('botmaster-messenger');
const SocketioBot = require('botmaster-socket.io');
.
.
.
// then rest of code
The other bot classes that were available in 2.x.x can be found at:
botmaster-slack
botmaster-telegram
botmaster-twitter-dm
3. Botmaster is not built on top of express anymore
First of all, this is only important for you to note if you were using your own express app and not letting Botmaster create it for you. If that isn't your case, you won't need to change anything here.
In order to make Botmaster really flexible, moving away from a dependency on express was important. This means that you can now either just let botmaster create its own server as it used to do via express in 2.x.x. Or, you can completely manage your own server (using express, koa, plain http or other).
This means that doing this will now throw an error:
// DON'T DO THIS ANYMORE!!!
const Botmaster = require('botmaster');
const express = require('express');
const app = express();
const botmaster = new Botmaster({ app });
app.listen(3000, '0.0.0.0', () => { // remember this creates an http server under the hood
console.log('My express app is listening and its server is used in Botmaster');
})
Instead, if you want to manage your own app object, you will need to do something like this
// DO THIS INSTEAD!!
const Botmaster = require('botmaster');
const express = require('express');
const app = express();
const myServer = app.listen(3000, '0.0.0.0');
const botmaster = new Botmaster({ server: myServer });
myServer.on('listening', () => {
console.log('My express app is listening and its server is used in Botmaster');
})
Now, this might look like nothing much has changed, but here's why this is an important change. If instead of having an express app, you have a Koa app (or other), this will essentially still work as all that Botmaster now asks of you is an instance of http server.
// DO THIS TO USE KOA!!
const Botmaster = require('botmaster');
const Koa = require('koa');
const app = new Koa();
const myServer = app.listen(3000, '0.0.0.0');
const botmaster = new Botmaster({ server: myServer });
myServer.on('listening', () => {
console.log('My Koa app is listening and its server is used in Botmaster too');
})
This was not possible in Botmaster 2.x.x.
4. Middleware
The biggest changes have been made with respect to middleware. Although your middleware functions will still work, they will need to be moved into a slightly new syntax.
Where you had something like this in 2.x.x:
botmaster.use('incoming', (bot, update, next) => {
// your stuff
});
You will now have something like this in 3.x.x:
botmaster.use({
type: 'incoming',
name: 'some-name-of-your-choosing', // this is optional, but nice for debugging
controller: (bot, update, next) => {
// your stuff
}
});
Essentially, the callback has been moved to a controller within an object that describes the middleware in general. You'll need to do the same thing for your outgoing middleware.
This is what needs to be changed. However, the core of middleware has changes quite a bit. Middleware can now either use the old syntax using next (as it used to in 2.x.x). Or it can leverage newer syntax by returning a promise. I.e. this is now valid middleware
botmaster.use({
type: 'incoming',
name: 'some-middleware',
controller: (bot, update) => {
return bot.reply(update, 'Hey there')
.then((body) => {
// this is run after the message is sent. I.e. also after all the outgoing middleware has been executed
})
}
});
This means that you can now write your code in a very synchronous looking manner if using node 7+ and running node
with the --harmony-async-await
flag or simply if you use a transpiler like Babel. Code like this will be valid
botmaster.use({
type: 'incoming',
name: 'some-middleware',
controller: async (bot, update) => {
const body = await bot.reply(update, 'Hey there')
// this is run after the message is sent. I.e. also after all the outgoing middleware has been executed
}
});
If returning a promise, using next within this promise will emit/throw an error depending on if you are in incoming or outgoing middleware. It will emit in incoming middleware and throw in outgoing middleware.
Please note that the returned promise's resolved value (whether you use async-await or not) will not be used
I.e. if in the last example after the const body = ...
line, you return any value. This will be used nowhere.
Like in the old middleware, you are expected to make changes to the update
object. Those changes will be available
in the following middleware. All that botmaster assures you with is that the next middleware in the stack will be called
only after the promise from the previous one has resolved. Here's an example using promises:
botmaster.use({
type: 'incoming',
name: 'first-middleware',
controller: (bot, update) => {
return useSomePromiseBasedFunction('something')
.then((valueFromFunction) => {
update.value = valueFromFunction;
})
}
});
botmaster.use({
type: 'incoming',
name: 'second-middleware',
controller: (bot, update, next) => {
console.log(update.value); // prints valueFromFunction
next();
}
});
Note how in this example, we are mixing both types of syntax (using promises and next). This is completely fine and even suggested if, say, your second middleware here is synchronous.
Another addition has been made to middleware. As it turns out, I lied when I said that values resolved by promise-based middleware will be ignored.
There are two cases where they won't be ignored. That's if you return skip
or cancel
.
See here to see how to use them.
Botmaster 3 also add support for using the useWrapped
method for adding middleware. This method when used
will add an incoming middleware at the beginning of the middleware incoming stack and another outgoing middleware at the end
of the outgoing middeware stack. It'll look something like this:
botmaster.useWrapped(incomingMiddleware, outgoingMiddleware);
Where incomingMiddleware
and outgoingMiddleware
are valid middlewares of their respective types.
This is useful if writing a middleware package that want to be first to get the update object and last to see it when it goes out.
It is used in botmaster-session-ware
for instance.
5. .on('update')
no longer exists.
As it says. You are now expected to move your code that lived in there to an incoming middleware. Should be the last one you declare so it is run at the end of all your incoming middleware.
6. Bot classes are better defined.
Bot classes now come with 3 new settings that help you know whether you want to execute a middleware or not on an specific update from a bot. They are:
bot.receives
. Will look something like this:
this.receives = {
text: false,
attachment: {
audio: false,
file: false,
image: false,
video: false,
location: false,
// can occur in FB messenger when user sends a message which only contains a URL
// most platforms won't support that
fallback: false,
},
echo: false,
read: false,
delivery: false,
postback: false,
// in FB Messenger, this will exist whenever a user clicks on
// a quick_reply button. It will contain the payload set by the developer
// when sending the outgoing message. Bot classes should only set this
// value to true if the platform they are building for has an equivalent
// to this.
quickReply: false,
};
bot.sends
. Will look something like this:
this.sends = {
text: false,
quickReply: false,
locationQuickReply: false,
senderAction: {
typingOn: false,
typingOff: false,
markSeen: false,
},
attachment: {
audio: false,
file: false,
image: false,
video: false,
},
};
All values will either be falsy or truthy. Ideally, they will even either be true
or false
.
And bot.retrievesUserInfo
which will either be truthy or falsy.
7. OutgoingMessage class has been added
In order to make it easier to compose outgoing messages without having to mess about with the object directly, Botmaster now sends instances of OutgoingMessage to the outgoing middleware. These objects are just like old message objects in 2.x.x. (go ahead, you can console log them to see for yourselves), but they just come with a few helper methods to add and remove stuff from the object. Have a look at the API reference for them to see how you can leverage that.
8. SendMessage type methods
There are THREE big changes in the way the sendMessage type methods now work.
1. You can't use them with callbacks anymore. The only supported way to do anything after the message is sent is to then
them using promises.
So if you had code like this:
bot.sendMessage(someMessage, (body) => {
console.log(body);
});
you'll need to make it look like this:
bot.sendMessage(someMessage).then((body) => {
console.log(body);
});
This was done because forcing promises means that using async-await syntax will be a breeze with Botmaster. And async-await is the future. So we didn't want to have you miss on that.
2. You can now use sendRaw
with all bot classes
Any bot class will have a valid sendRaw
method (that is just sugar for the underlying Bot class's __sendMessage
method). This method
allows you to send a message to the platform your bot object is based from without going through any formatting or outgoing middleware.
I.e. it could be used as such:
// within a middleware controller
if (bot.type === 'slack') { // for example
rawSlackMessage = {
token: 'someToken',
channel: 'someChannel',
as_user: true,
text: 'someText',
.
.
.
};
return bot.sendRaw(rawSlackMessage);
} else {
return bot.reply(update, 'Thank you for subscribing');
}
3. The resolved body from sendMessage type functions now contains much more information. It is now composed of the following:
- sentOutgoingMessage - the object before going through its formatting
- sentRawMessage - the sentOutgoinMessage after its formatting
- raw - the raw response from the platform after sending the obejct
- recipient_id - the id of the recipient
- message_id - the id of the sent message (if available)
On top of that the following two methods have changed:
1. sendDefaultButtonMessageTo()
Now has recipientId as last argument
2. sendCascade()
Now only accepts raw or full message objects. You are expected to compose the messages you are sending with the OutgoingMessage helper methods
Have a look at the api-reference to see examples of these methods now
9. WebhookEndpoint has no initial slash anymore.
When using a bot class that uses webhooks, no need to add a slash at the beginning of the webhookEnpoint paramter. I.e. if you had something that looked like this:
const messengerSettings = {
credentials: someCredentials,
webhookEndpoint: '/webhook1234',
};
Change it to:
const messengerSettings = {
credentials: someCredentials,
webhookEndpoint: 'webhook1234',
};
MINOR 2.3.0
Outgoing middleware now has access to the incoming update. I.e. outgoing middleware can be used like this:
botmaster.use('outgoing', (bot, update, message, next) => {
console.log(update);
console.log(message);
})
PATCH 2.2.7
Add the concept of implements
for bot classes. Now every bot object has a bot.implements
object that specifies which functionalities are implemented by the bot class. Currently, the values that exist and can be tested against are:
quickReply
// for quick replies
attachment
// does it support attachments
typing
// does it support typing status
PATCH 2.2.6
Just fix a bug in outgoing middleware
PATCH 2.2.5
This patch adds to the body returned when using any of the bot.sendMessage
type helper methods.
Now the body also contains a body.sent_message
parameter that is simply the full object that was sent by the bot
PATCH 2.2.4
This patch allows users to use sendMessage
type functions with an optional sendOptions
object. Currently, this can be used to bypass outgoing middleware
when sending messages by using the ignoreMiddleware
option. Using this looks something like this:
bot.reply(update, 'Hello world!', { ignoreMiddleware: true })
or using a callback function
bot.reply(update, 'Hello world!', { ignoreMiddleware: true }, (body) =>
console.log(body);
);
or for buttons
bot.sendDefaultButtonMessageTo(
['button1', 'button2'], sender.user.id, 'click on a button',
{ ignoreMiddleware: true })
or with cascade messages
bot.sendTextCascadeTo(
['message1', 'message2'], sender.user.id,
{ ignoreMiddleware: true }, (bodies) => {
console.log(bodies);
})
PATCH 2.2.3
This patch adds support for the bot.sendCascadeTo
and bot.sendTextCascadeTo
methods. Allowing users to send a cascade of message with just one command rather than having to deal with that themselves. Read more about it here:
PATCH: 2.2.2
This patch allows users to set the userId from a sender when using the bot socket.io class. socket now needs to be opened with something like this on the client side:
var socket = io('?botmasterUserId=wantedUserId');
See updated Botmaster Socket.io bot mini-tutorial here
MINOR: Botmaster 2.2.0
This minor release allows developers to create news instances of Botmaster without bots settings by writing something like:
const Botmaster = require('botmaster');
const MessengerBot = Botmaster.botTypes.MessengerBot;
.
.
const botmaster = new Botmaster();
.
. // full settings objects omitted for brevity
.
const messengerBot = new MessengerBot(messengerSettings);
const slackBot = new SlackBot(slackSettings);
const twitterBot = new TwitterBot(twitterSettings);
const socketioBot = new SocketioBot(socketioSettings);
const telegramBot = new TelegramBot(telegramSettings);
botmaster.addBot(messengerBot);
botmaster.addBot(slackBot);
botmaster.addBot(twitterBot);
botmaster.addBot(socketioBot);
botmaster.addBot(telegramBot);
This is because it might be viewed as cleaner by some to add bots in the following way rather than doing this in the constructor.
PATCH: Botmaster 2.1.1
This patch fixes a bug whereby one couldn't instantiate a botmaster object that would use socket.io in all reasonably expected ways. See here for a discussion.
MINOR: Botmaster 2.1.0
This version adds support for socket.io bots within the botmaster core. This is the last bot class that will be in the core
MAJOR: Botmaster 2.0.0
In this new version, a lot of new things were added to Botmaster. A few others were removed.
Breaking Changes
If you were using SessionStore in version 1.x.x, you won't be able to anymore in version 2.x.x. They have been scratched for the far more common middleware design pattern common in so many other frameworks (e.g. express). Middleware can be hooked into right before receiving an update and right before sending out a message. It fits ideally with people wanting to setup session storage at these points.
Adding Slack
Support for Slack as the fourth channel supported by Botmaster has been added. Using the Events API, you can now send and receive messages on the platform.
get User info
If the platform supports it and the bot class you are using supports it too, you can now use the bot.getUserInfo
method to retrieve basic information on a user, including their name and profile pic.
bug fixes
As with any release, a bunch of bugfixes were done.