2
0
mirror of https://github.com/yagop/node-telegram-bot-api synced 2025-08-22 09:57:10 +00:00

First commit

This commit is contained in:
yago 2015-06-29 00:37:40 +02:00
commit 9beb32366d
10 changed files with 567 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
node_modules

22
examples/crt.pem Normal file
View File

@ -0,0 +1,22 @@
-----BEGIN CERTIFICATE-----
MIIDmTCCAoGgAwIBAgIJAPz/mOxHHCRKMA0GCSqGSIb3DQEBCwUAMGMxCzAJBgNV
BAYTAlVTMQ0wCwYDVQQIDARVdGFoMQ4wDAYDVQQHDAVQcm92bzEjMCEGA1UECgwa
QUNNRSBTaWduaW5nIEF1dGhvcml0eSBJbmMxEDAOBgNVBAMMB3lhZ28ubWUwHhcN
MTUwNjI4MTk0MjAxWhcNMTgwNzA2MTk0MjAxWjBjMQswCQYDVQQGEwJVUzENMAsG
A1UECAwEVXRhaDEOMAwGA1UEBwwFUHJvdm8xIzAhBgNVBAoMGkFDTUUgU2lnbmlu
ZyBBdXRob3JpdHkgSW5jMRAwDgYDVQQDDAd5YWdvLm1lMIIBIjANBgkqhkiG9w0B
AQEFAAOCAQ8AMIIBCgKCAQEAw9YiYXN1s5KcoZy7UZyiXULpTrYPhlPhzlyJJdwg
e61C/swbqtnh/+fPZp8g8a15ond9ShUvWLcxeoDBzxn0hJIEe+DlNNHUAdWoTWUx
OP4hHDA6wCFepHWBlw10AoKAjoQA+nCX6NrdiFTpbodkEK0H4uOSCt37H616kdKU
wRgXlca2Kw88UQ0qhKteb5hYD5tm4aCv6eRCqwYdYKUG+D1uJuJ+YZmaaIXp/5QZ
q3a6mFsKLtUC33bhZZPr1qjh3zwF2JTZX1WFAxUHNxY5NVchUYDHjw0djXvw85il
iwWKFjFXfvk8WTfW3Ge3754BhYSt92Qj6BROD2AODhI8jwIDAQABo1AwTjAdBgNV
HQ4EFgQUpgp5hovXcW+eIb3xRkF1KSJb/rwwHwYDVR0jBBgwFoAUpgp5hovXcW+e
Ib3xRkF1KSJb/rwwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAjnKW
+3lHe92Ut9XJdqGJsuRV5OUh8suOicz+AXtqUdoG9xbkv5N6Ynt+r06NjnYgTIzk
i+9fXBZLrXH8qNT2PTzErs0LMXPWxWbiZwY9mI2z/xW/K6CFjb1h33hk+ypwTr1J
Q1Eqy77FXKfQ2Y8kNLARSkvUEMm7UnVbUqRbA8AlWk9HZmoPHYfKPRGRVeIugH76
b6Gm3ztmIgTZQ88+DxfedIjPib3LPsHIXrA2Qd8yrIaYDiE2HMMJ5q3SYdRY4yYB
2a3P7jCPZfKVKpRE0J0yeNH+wQL0bzCMbl2wBUhivXD+sM00Xe3a22eAYbNgLdEg
4Hvd/YIKm9yOjRolmw==
-----END CERTIFICATE-----

12
examples/httpsWebHook.js Normal file
View File

@ -0,0 +1,12 @@
var TelegramBot = require('../src/telegram');
var options = {
webHook: {
port: 443,
key: __dirname+'/key.pem',
cert: __dirname+'/crt.pem'
}
};
var bot = new TelegramBot('BOTTOKEN', options);
bot.setWebHook('IP:PORT');

27
examples/key.pem Normal file
View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAw9YiYXN1s5KcoZy7UZyiXULpTrYPhlPhzlyJJdwge61C/swb
qtnh/+fPZp8g8a15ond9ShUvWLcxeoDBzxn0hJIEe+DlNNHUAdWoTWUxOP4hHDA6
wCFepHWBlw10AoKAjoQA+nCX6NrdiFTpbodkEK0H4uOSCt37H616kdKUwRgXlca2
Kw88UQ0qhKteb5hYD5tm4aCv6eRCqwYdYKUG+D1uJuJ+YZmaaIXp/5QZq3a6mFsK
LtUC33bhZZPr1qjh3zwF2JTZX1WFAxUHNxY5NVchUYDHjw0djXvw85iliwWKFjFX
fvk8WTfW3Ge3754BhYSt92Qj6BROD2AODhI8jwIDAQABAoIBAQCga6gMNh2DtSTT
imUzrGCgjvA5RxAelFYTyl+agOCnDz4jJKXBZewoygZuZQoCj31lJgafCg2X2bER
Tan1caiIdGhx5b88bmoB+rh8ddlFe3857RQjUPKLO6qlRyLx719J3z5B6Lu3xpnU
VOJHZWcF9gfQx2RZvI862svd6idqqFfKRVr7jxur1VuTQpk6g0xi6GnFk9s6sPw9
ChT6ykxzx+fQmYzeEW6SbWilOnm9BGuAEI2G7/mDQ6NFGFvFdPivI908vHPGbhFz
Ifdwt5F9NwQrSzYaDnYzCWrEmqSz9uRmX5DD9FwFokjN2d6o3V0/+1BrWLKDBDj+
jYcOV5IBAoGBAOFLTbfRLPP0z4vgTX8FeaZXreJQPblHmJQNwvJEYiiug4aDXCLv
uBEVR+H1Y/Pm1U4s5LNESg2pOC0IZOElvAck+SY/K1KZFI9EzBLa0aybFOuLaHY1
Z9cZfc1Cg3Vmpqnkyqahi+Tt1U/nayL3DFNcIwI30DweS+KLUmjIxxMBAoGBAN6H
ByD0+d3pfqkusTI3GT1NfWMBwS+usCayLP1gMpA+0tT+/lnYLLbmINaD6hoztWWD
OUZ7PM3HkOXKly2bfKxlT8Bi3b0QpNyd54ybj4/60JLRAO5OU773an8MMsov+q1V
xWYGVMnNihXFFVGaIK8dG/2mYomHjbzx3az/EZ+PAoGAJs3pnPeSXpKUDOudbXtr
8JK5iHl5qCgEx7t3EHNm1Mr6LHkDraDMe2TG9MxnYuMnakehPJ9OgfvbiSYg+gad
1D0yDLxkod1sBSE8ZSL7aldrywY//9xC/nGNkYUbT2VW33xgy0KX7d5pF1IsyeDz
ZohAH2mtnC07tNF6aEHsyAECgYAJ3EHcm/5WbvpF1OPVLcvYg46CzJka28rCbDLC
J3kWGzKMbaAnqwSQNjJOTxoYfyISlXX8QYm4NJefFxML2k/z86lNBRR+RDaJ8BVK
jboWzy5e0xQPezkKxTva1VeKzgV1mM9ebflj18++lzUSoJnCKLAM1UqYfYEyViVU
fRjy0QKBgQCopUy7KDdKngBrSQwI9lMi1/bZJTXw1WktLZRma3uIw8uBKB2Fyf4/
7xFo49Ha7l1W38PfkqOS+539V8cJSyyJKq+PgBQ8fuLCplCDeZCieSiYm+FkpIr0
4V+hEMIkVuUBDwCbyMM5mUH+sBVtmzNYDRgYa5QN4FIBk8VcBgsIhw==
-----END RSA PRIVATE KEY-----

36
examples/polling.js Normal file
View File

@ -0,0 +1,36 @@
var TelegramBot = require('../src/telegram');
var request = require('request');
var options = {
polling: true
};
var token = 'YOUR_TELEGRAM_BOT_TOKEN';
var bot = new TelegramBot(token, options);
bot.getMe().then(function (me) {
console.log('Hi my name is %s!', me.username);
});
bot.on('message', function (msg) {
var chatId = msg.chat.id;
if (msg.text == '/photo') {
// From file
var photo = __dirname+'/../test/bot.gif';
bot.sendPhoto(chatId, photo, {caption: "I'm a bot!"});
}
if (msg.text == '/audio') {
var url = 'https://upload.wikimedia.org/wikipedia/commons/c/c8/Example.ogg';
// From HTTP request!
var audio = request(url);
bot.sendAudio(chatId, audio)
.then(function (resp) {
// Forward the msg
var messageId = resp.message_id;
bot.forwardMessage(chatId, chatId, messageId);
});
}
if (msg.text == '/help') {
var opts = {reply_to_message_id: msg.message_id};
bot.sendMessage(chatId, 'This is only a test :D', opts);
}
});

1
index.js Normal file
View File

@ -0,0 +1 @@
module.exports = require('./src/telegram');

30
package.json Normal file
View File

@ -0,0 +1,30 @@
{
"name": "telegram-bot-api",
"version": "0.1.0",
"description": "Telegram Bot API",
"main": "index.js",
"directories": {
"test": "test",
"src": "src"
},
"keywords": [
"telegram",
"telegram bot",
"telegram bot api",
"bot"
],
"scripts": {
"test": "./node_modules/.bin/mocha test/index.js"
},
"author": "Yago Pérez <yagoperezs@gmail.com>",
"license": "MIT",
"dependencies": {
"bluebird": "^2.9.30",
"mime": "^1.3.4",
"request": "^2.58.0"
},
"devDependencies": {
"mocha": "^2.2.5",
"should": "^7.0.1"
}
}

290
src/telegram.js Normal file
View File

@ -0,0 +1,290 @@
var EventEmitter = require('events').EventEmitter;
var Promise = require("bluebird");
var request = require("request");
var stream = require('stream');
var https = require('https');
var http = require('http');
var util = require('util');
var mime = require('mime');
var path = require('path');
var URL = require('url');
var fs = require('fs');
var requestPromise = Promise.promisify(request);
/**
* Telegram Bot API. Suppor for WebHooks and long polling. Emits `message` when
* a message arrives.
*
* @class TelegramBot
* @constructor
* @param {String} token Bot Token
* @param {Object} [options]
* @param {Boolean|Object} [options.polling=false] Set true to enable polling
* @param {String|Number} [options.polling.timeout=4] Polling time
* @param {Boolean|Object} [options.webHook=false] Set true to enable WebHook
* @param {String} [options.webHook.key] PEM private key to webHook server
* @param {String} [options.webHook.cert] PEM certificate key to webHook server
* @see https://core.telegram.org/bots/api
*/
var TelegramBot = function (token, options) {
options = options || {};
this.token = token;
this.offset = 0;
this._webServer = null;
if (options.polling) {
// By default polling for 4 seconds
var timeout = options.polling.timeout || 4;
this._polling(timeout);
}
if (options.webHook) {
var port = options.webHook.port || 8443;
var key = options.webHook.key;
var cert = options.webHook.cert;
this._configureWebHook(port, key, cert);
}
};
util.inherits(TelegramBot, EventEmitter);
TelegramBot.prototype._configureWebHook = function (port, key, cert) {
var protocol = 'HTTP';
var self = this;
if (key && cert) { // HTTPS Server
protocol = 'HTTPS';
var options = {
key: fs.readFileSync(key),
cert: fs.readFileSync(cert)
};
this._webServer = https.createServer(options, function (req, res) {
self._requestListener.call(self, req, res);
});
} else { // HTTP Server
this._webServer = http.createServer(function (req, res) {
self._requestListener.call(self, req, res);
});
}
this._webServer.listen(port, function () {
console.log(protocol+" WebHook listening on:", port);
});
};
TelegramBot.prototype._requestListener = function (req, res) {
var self = this;
if (req.url == '/bot' && req.method == 'POST') {
var fullBody = '';
req.on('data', function (chunk) {
fullBody += chunk.toString();
});
req.on('end', function () {
try {
var data = JSON.parse(fullBody);
self.emit('message', data);
} catch (error) {
console.error(error);
}
res.end('OK\n');
});
} else {
res.end('OK\n');
}
};
TelegramBot.prototype._processUpdates = function (updates) {
for (var i = 0; i < updates.length; i++) {
if (updates[i].message) {
this.emit('message', updates[i].message);
}
}
};
TelegramBot.prototype._polling = function (timeout) {
var self = this;
this.getUpdates(timeout).then(function (data) {
self._processUpdates(data);
self._polling(timeout);
});
};
TelegramBot.prototype._request = function (path, options) {
if (!this.token) {
throw new Error('Telegram Bot Token not provided!');
}
options = options || {};
options.url = URL.format({
protocol: 'https',
host: 'api.telegram.org',
pathname: '/bot'+this.token+'/'+path
});
return requestPromise(options)
.then(function (resp) {
if (resp[0].statusCode != 200) {
throw new Error(resp[0].statusCode+' '+resp[0].body);
}
var data = JSON.parse(resp[0].body);
if (data.ok) {
return data.result;
} else {
throw new Error(data.error_code+' '+data.description);
}
});
};
/**
* Returns basic information about the bot in form of a `User` object.
* @return {Promise}
*/
TelegramBot.prototype.getMe = function () {
var path = 'getMe';
return this._request(path);
};
/**
* Specify a url to receive incoming updates via an outgoing webHook.
* @param {String} url URL
*/
TelegramBot.prototype.setWebHook = function (url) {
var path = 'setWebHook';
var qs = {url: url};
return this._request(path, {method: 'POST'})
.then(function (resp) {
if (!resp) {
throw new Error(resp);
}
});
};
/**
* Use this method to receive incoming updates using long polling
* @param {Number|String} [timeout] Timeout in seconds for long polling.
* @param {Number|String} [limit] Limits the number of updates to be retrieved.
* @param {Number|String} [offset] Identifier of the first update to be returned.
* @return {Promise} Updates
* @see https://core.telegram.org/bots/api#getupdates
*/
TelegramBot.prototype.getUpdates = function (timeout, limit, offset) {
var self = this;
var query = {
offset: offset || this.offset+1,
limit: limit,
timeout: timeout
};
return this._request('getUpdates', {qs: query})
.then(function (result) {
var last = result[result.length-1];
if (last) {
self.offset = last.update_id;
}
return result;
});
};
/**
* Send text message.
* @param {Number|String} chatId Unique identifier for the message recipient
* @param {Sting} text Text of the message to be sent
* @param {Object} [options] Additional Telegram query options
* @return {Promise}
* @see https://core.telegram.org/bots/api#sendmessage
*/
TelegramBot.prototype.sendMessage = function (chatId, text, options) {
var query = options || {};
query.chat_id = chatId;
query.text = text;
return this._request('sendMessage', {qs: query});
};
/**
* Forward messages of any kind.
* @param {Number|String} chatId Unique identifier for the message recipient
* @param {Number|String} fromChatId Unique identifier for the chat where the
* original message was sent
* @param {Number|String} messageId Unique message identifier
* @return {Promise}
*/
TelegramBot.prototype.forwardMessage = function (chatId, fromChatId, messageId) {
var query = {
chat_id: chatId,
from_chat_id: fromChatId,
message_id: messageId
};
return this._request('forwardMessage', {qs: query});
};
TelegramBot.prototype._formatSendData = function (type, data) {
var formData;
var fileName;
var fileId;
if (data instanceof stream.Stream) {
fileName = path.basename(data.path);
formData = {};
formData[type] = {
value: data,
options: {
filename: fileName,
contentType: mime.lookup(fileName)
}
};
} else if (fs.existsSync(data)) {
fileName = path.basename(data);
formData = {};
formData[type] = {
value: fs.createReadStream(data),
options: {
filename: fileName,
contentType: mime.lookup(fileName)
}
};
} else {
fileId = data;
}
return [formData, fileId];
};
/**
* Send photo
* @param {Number|String} chatId Unique identifier for the message recipient
* @param {String|stream.Stream} photo A file path or a Stream. Can
* also be a `file_id` previously uploaded
* @param {Object} [options] Additional Telegram query options
* @return {Promise}
* @see https://core.telegram.org/bots/api#sendphoto
*/
TelegramBot.prototype.sendPhoto = function (chatId, photo, options) {
var opts = {
qs: options || {}
};
opts.qs.chat_id = chatId;
var content = this._formatSendData('photo', photo);
opts.formData = content[0];
opts.qs.photo = content[1];
return this._request('sendPhoto', opts);
};
/**
* Send audio
* @param {Number|String} chatId Unique identifier for the message recipient
* @param {String|stream.Stream} audio A file path or a Stream. Can
* also be a `file_id` previously uploaded.
* @param {Object} [options] Additional Telegram query options
* @return {Promise}
* @see https://core.telegram.org/bots/api#sendaudio
*/
TelegramBot.prototype.sendAudio = function (chatId, audio, options) {
var opts = {
qs: options || {}
};
opts.qs.chat_id = chatId;
var content = this._formatSendData('audio', audio);
opts.formData = content[0];
opts.qs.audio = content[1];
return this._request('sendAudio', opts);
};
module.exports = TelegramBot;

BIN
test/bot.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

148
test/index.js Normal file
View File

@ -0,0 +1,148 @@
var Telegram = require('../src/telegram');
var request = require('request');
var should = require('should');
var fs = require('fs');
var TOKEN = process.env.TEST_TELEGRAM_TOKEN;
if (!TOKEN) {
throw new Error('Bot token not provided');
}
// Telegram service if not User Id
var USERID = process.env.TEST_USER_ID || 777000;
describe('Telegram', function () {
describe('#emit', function () {
it('should emit a `message` on polling', function (done) {
var bot = new Telegram(TOKEN);
bot.on('message', function (msg) {
msg.should.be.an.instanceOf(Object);
bot._polling = function () {};
done();
});
bot.getUpdates = function() {
return {
then: function (cb) {
cb([{update_id: 0, message: {}}]);
}
};
};
bot._polling();
});
it('should emit a `message` on WebHook', function (done) {
var bot = new Telegram(TOKEN, {webHook: true});
bot.on('message', function (msg) {
msg.should.be.an.instanceOf(Object);
done();
});
var url = 'http://localhost:8443/bot';
request({
url: url,
method: 'POST',
json: true,
headers: {
"content-type": "application/json",
},
body: JSON.stringify({update_id: 0, message: {text: 'test'}})
});
});
});
describe('#getMe', function () {
it('should return an User object', function (done) {
var bot = new Telegram(TOKEN);
bot.getMe().then(function (resp) {
resp.should.be.an.instanceOf(Object);
done();
});
});
});
describe('#getUpdates', function () {
it('should return an Array', function (done) {
var bot = new Telegram(TOKEN);
bot.getUpdates().then(function (resp) {
resp.should.be.an.instanceOf(Array);
done();
});
});
});
describe('#sendMessage', function () {
it('should send a message', function (done) {
var bot = new Telegram(TOKEN);
bot.sendMessage(USERID, 'test').then(function (resp) {
resp.should.be.an.instanceOf(Object);
done();
});
});
});
describe('#forwardMessage', function () {
it('should forward a message', function (done) {
var bot = new Telegram(TOKEN);
bot.sendMessage(USERID, 'test').then(function (resp) {
var messageId = resp.message_id;
bot.forwardMessage(USERID, USERID, messageId)
.then(function (resp) {
resp.should.be.an.instanceOf(Object);
done();
});
});
});
});
describe('#sendPhoto', function () {
var photoId;
it('should send a photo from file', function (done) {
var bot = new Telegram(TOKEN);
var photo = __dirname+'/bot.gif';
bot.sendPhoto(USERID, photo).then(function (resp) {
resp.should.be.an.instanceOf(Object);
photoId = resp.photo[0].file_id;
done();
});
});
it('should send a photo from id', function (done) {
var bot = new Telegram(TOKEN);
// Send the same photo as before
var photo = photoId;
bot.sendPhoto(USERID, photo).then(function (resp) {
resp.should.be.an.instanceOf(Object);
done();
});
});
it('should send a photo from fs.readStream', function (done) {
var bot = new Telegram(TOKEN);
var photo = fs.createReadStream(__dirname+'/bot.gif');
bot.sendPhoto(USERID, photo).then(function (resp) {
resp.should.be.an.instanceOf(Object);
done();
});
});
it('should send a photo from request Stream', function (done) {
var bot = new Telegram(TOKEN);
var photo = request('https://telegram.org/img/t_logo.png');
bot.sendPhoto(USERID, photo).then(function (resp) {
resp.should.be.an.instanceOf(Object);
done();
});
});
});
describe('#sendAudio', function () {
it('should send an OGG audio', function (done) {
var bot = new Telegram(TOKEN);
var audio = request('https://upload.wikimedia.org/wikipedia/commons/c/c8/Example.ogg');
bot.sendAudio(USERID, audio).then(function (resp) {
resp.should.be.an.instanceOf(Object);
done();
});
});
});
});