1.2.0 • Published 7 years ago

nullbot v1.2.0

Weekly downloads
2
License
MPL-2.0
Repository
github
Last release
7 years ago

nullbot

This is a full featured Telegram bot framework in the style of expressjs. It combines nullbot-core, nullbot-client, and restify to make boot-strapping a bot easy as pie.

Some features you might not find in other bots.

  • The ability to send data in the webhook response.
  • Extensive documentation and examples.
  • A well documented and modular codebase.
  • Middleware, middleware, middleware.
  • Full coverage of the Telegram Bot API.
  • A completely webhook oriented design. (For long-polling style just checkout nullbot-client)

Anyway, let's move on to using it, shall we?

Getting Started (The Easy Road)

Head on over to nullbot-example and follow the instructions there. It'll be painless, I swear.

Getting Started (The Hard Road)

Following are instructions for getting a nullbot running on your local machine. I've only tested these steps on macOS and Ubuntu, though it should work for any flavor of linux. You Windows folks are on your own.

1. Install nullbot and bunyan

Assuming you have nodejs v4.4.7 or higher..

npm install nullbot

I like bunyan a lot, so I always install it globally so I can get pretty log output and searching.

npm install -g bunyan

Note: You may need to use sudo for the global bunyan install.

2. Create the self-signed ssl key and certificate

This assumes you have openssl installed. If you don't, get it.

openssl genrsa -out bot-key.pem 2048
openssl req -new -sha256 -key bot-key.pem -out bot-cert.pem

3. Figure out your public ip

dig +short myip.opendns.com @resolver1.opendns.com

4. Forward port 8443 from your router to your local machine

There's not much I can help you with here. Generally you log into your router and manually setup port forwarding. You can also check out the MiniUPnP project, but that's probably more complicated than is necessary.

5. Get a bot token

For this, you need telegram installed and have to talk to @BotFather. Just message BotFather with /newbot it will guide you through the process.

6. Use the following code snippet

Save this file as something like bot.js. Be sure to modify name, token, and url with the appropriate values. If you don't have dns setup for a domain, (It's a slightly modified version of the script above.)

'use strict';

var options = {
    token: 'BOTTOKEN',
    url: 'WEBHOOK URL',
    port: 8443,
    key: 'bot-key.pem',
    cert: 'bot-cert.pem'
};

var bot = require('nullbot')(options);

bot.filter(/^hello$/).use(function (req, res, next) {
    res.sendMessage('world');
    next();
});

bot.listen();

Then start the bot.

node bot.js | bunyan

A message to your bot with 'hello' should now have the bot respond with 'world'.

Options

The following options can be supplied to the bot.

ParamDefaultTypeRequiredDescription
tokenNoneStringYesThe bot token required by Telegram.
namenullbotStringNoThe bot name used for logging.
urllocalhostStringNoThe url that the bot is listening at.
port8443NumberNoThe port that the bot is listening on.
path/Random ShortidStringNoThe url path that the bot is handling at.
hookurl:portStringNoThe public url sent to Telegram as the webhook.
leveldebugStringNoThe log level to output to stdout.
logBunyan LoggerObjectNoA bunyan logger to instantiate the bot with.
certNoneStringNoThe path to an ssl cert for hook.
keyNoneStringNoThe path to an ssl key for hook.

Handlers

Nullbot functions a lot like connect, express, and restify. In general the bot represents a stack of handlers. By default, nullbot adds the events handler and the response handler as the first to handlers on the stack. The req and res objects are passed through handlers in the same order that the handlers are added to the stack.

bot.use(Function)

Adds a middleware function to the root bot stack. This function will be called during every update from telegram. The function will receive three parameters, req, res, and next. Currently, req is a json update object that has been imbued with a type key and, if applicable, a subtype key. These additions are detailed below. The res object is the raw response object from your particular webserver handler. Lastly, next is a link to the next middleware function in the stack and must be called. Functions are appended to the bot stack in the order that they are added, such that...

function middlewareOne(req, res, next) {
    console.log('I am middleware one');
    next();
}

function middlewareTwo(req, res, next) {
    console.log('I am middleware two');
    next();
}

bot.use(middlewareOne).use(middlewareTwo);

...will run cycle through the middleware functions in the expected order. Notice that use can be chained the same way it can with other middleware handlers.

bot.filter(Filter)

This method adds a substack to the root stack that will only be called when the filter is passed. Filter definitions will be covered below. Filters have their own .use method that can also be chained. In fact. this allows you to use nullbot-core to create bot middleware with multiple filters and export it as a single middleware handler, just like connect does.

function middlewareOne(req, res, next) {
    console.log('I am middleware one');
    next();
}

function middlewareTwo(req, res, next) {
    console.log('I am middleware two');
    next();
}

function middlewareThree(req, res, next) {
    console.log('I am middleware three');
    next();
}

function photoHandler(req, res, next) {
    doThingsWithPhoto(req);
    next();
}

bot.use(middlewareOne);
bot.filter({subtype: 'photo'}).use(photoHandler).use(middlewareTwo);
bot.use(middlewareThree);

In this case, the order of handlers is:

1. middlewareOne
    2. Filter for photo subtype
    2a. photoHandler
    2b. middlewareTwo
3. middlewareThree

If the update subtype is not photo, then neither 2a nor 2b will be called.

Events

The instantiated bot object is also an event emitter that emits events when updates are received. The following events will call the listener function with the req object.

bot.events.on('update', Function)

This event is emitted every time the bot receives an update object. Function will receive the same update object that is passed as the request object that the handlers above receive.

bot.events.on('Update Type', Function)

Events for each type of update are emitted. Types include message, edited_message, inline_query, chosen_inline_result, and callback_query.

bot.events.on('Update Subtype', Function)

Events for each subtype of update are emitted. These types include , audio, document, photo, sticker, video, voice, contact, location, venue, new_chat_member, left_chat_member, new_chat_title, new_chat_photo, delete_chat_photo, group_created, and supergroup_chat_created.

Client

The instantiated bot object also contains all of the Telegram api methods outlined in nullbot-client. This means that you can do the following.

bot.sendMessage({chat_id: <CHATID>, text: 'Hello!'},
    function (err, body) {
        if (err) { bot.log.error(err); }
        if (!body.ok) { bot.log.error(body); }
    }
);

These methods are particularly useful in conjunction with the bot event emitter.

Update

The update object is defined by the telegram bot api here. Since the type of telegram update is a small pain to access, nullbot-core does some minor parsing and will add a type parameter that will be set to either message, edited_message, inline_query, chosen_inline_result, or callback_query. This makes accessing common update parameters such as from a bit easier. For example, the from user id can always be found with req[req.type].from.id.

Additionally, if the type is either message or edited_message then a subtype parameter will be defined as one of , audio, document, photo, sticker, video, voice, contact, location, venue, new_chat_member, left_chat_member, new_chat_title, new_chat_photo, delete_chat_photo, group_created, or supergroup_chat_created. The relevant and related objects can all be found in the telegram bot api documentation.

Filters

Filters, at the moment, are stupidly simple. In the next major revision of this software, filters will be expanded to allow custom filter functions to be supplied, but for now you have the following:

var filter = {
    name: 'Filter Name',
    type: [update Type],
    subtype: [update Subtype],
    regex: [Regex Object]
}

Here, none of the fields are required, and if none are supplied than the filter will not filter anything. The name field is used for logging purposes and is always good to supply if you want easier debugging experiences. For type and subtype, a string matching any of the options listed in the update section should work. Mismatched types and subtypes will result in a filter that does not allow any updates to pass through it. Lastly, regex will automatically set the subtype to , regardless of what it was set to before. Additionally, it will only be tested, not executed, on the req.[message|edited_message].text field.

When using bot.filter, some shortcuts have been implemented. First, bot.filter(/regex/) will result in the following filter being defined:

filter = {
    name: 'anonymous',
    subtype: 'text',
    regex: /regex/
}

Also, supplying a string like, bot.filter('hello') will result in:

filter = {
    name: 'hello'
}

If you are manually creating middleware handlers, it's almost always better to call bot.filter('handler-name').use(fn) instead of bot.use(fn) as finding errors inside of a named filter is much easier than finding them without names. However, the trace log level will show the handler path indexes, so it's not necessary.

Creating Middleware

If you've already got a nullbot, but want to add a feature that handles multiple commands, types, and subtypes, I'd recommend just using nullbot-core. You can build an entire middleware handler with it, and then simply bot.use it with your nullbot. I'll add an example of this soon enough.

Contact

If you have any questions, comments, or criticisms, please direct them to brandon@null.pub.

1.2.0

7 years ago

1.1.4

8 years ago

1.1.3

8 years ago

1.1.2

8 years ago

1.1.0

8 years ago

1.0.1

8 years ago

1.0.0

8 years ago

0.0.1

8 years ago