Comparing version 0.6.3 to 0.7.0
@@ -15,3 +15,3 @@ var debug = require('debug')('telegraf:session-memory') | ||
} | ||
return `${event.from.id}:v${chatId}` | ||
return `${event.from.id}:${chatId}` | ||
} | ||
@@ -18,0 +18,0 @@ }, opts) |
var debug = require('debug')('telegraf:core') | ||
var compose = require('koa-compose') | ||
var fetch = require('node-fetch') | ||
@@ -11,53 +10,16 @@ var FormData = require('form-data') | ||
var matchOperatorsRe = /[|\\{}()[\]^$+*?.]/g | ||
var supportedUpdateTypes = [ | ||
'message', | ||
'callback_query', | ||
'inline_query', | ||
'chosen_inline_result' | ||
] | ||
var messageSubTypes = [ | ||
'text', | ||
'audio', | ||
'document', | ||
'photo', | ||
'sticker', | ||
'video', | ||
'voice', | ||
'contact', | ||
'location', | ||
'venue', | ||
'new_chat_participant', | ||
'left_chat_participant', | ||
'new_chat_title', | ||
'new_chat_photo', | ||
'delete_chat_photo', | ||
'group_chat_created', | ||
'supergroup_chat_created', | ||
'channel_chat_created', | ||
'migrate_to_chat_id', | ||
'migrate_from_chat_id', | ||
'pinned_message' | ||
] | ||
var shortcuts = [ | ||
{ name: 'reply', target: 'sendMessage' }, | ||
{ name: 'replyWithPhoto', target: 'sendPhoto' }, | ||
{ name: 'replyWithAudio', target: 'sendAudio' }, | ||
{ name: 'replyWithDocument', target: 'sendDocument' }, | ||
{ name: 'replyWithSticker', target: 'sendSticker' }, | ||
{ name: 'replyWithVideo', target: 'sendVideo' }, | ||
{ name: 'replyWithVoice', target: 'sendVoice' }, | ||
{ name: 'replyWithChatAction', target: 'sendChatAction' }, | ||
{ name: 'replyWithLocation', target: 'sendLocation' } | ||
] | ||
/** | ||
* Telegraf prototype. | ||
*/ | ||
var telegraf = Telegraf.prototype | ||
/** | ||
* Represents a Telegraf app. | ||
* @constructor | ||
* @param {string} token - Telegram token. | ||
*/ | ||
function Telegraf (token) { | ||
this.token = token | ||
this.handlers = [] | ||
this.middlewares = [] | ||
this.middleware = [] | ||
this.offset = 0 | ||
@@ -69,2 +31,10 @@ this.started = false | ||
/** | ||
* Start polling loop. | ||
* | ||
* @param {number} timeout | ||
* @param {number} limit | ||
* @return {Telegraf} self | ||
* @api public | ||
*/ | ||
telegraf.startPolling = function (timeout, limit) { | ||
@@ -74,6 +44,14 @@ this.started = true | ||
this.options.limit = limit || 100 | ||
this.updateLoop() | ||
this.pollingLoop() | ||
return this | ||
} | ||
/** | ||
* Return a callback function suitable for the http[s].createServer() method to handle a request. | ||
* You may also use this callback function to mount your telegraf app in a Koa/Connect/Express app. | ||
* | ||
* @param {string} webHookPath - Webhook secret path | ||
* @return {Function} | ||
* @api public | ||
*/ | ||
telegraf.webHookCallback = function (webHookPath) { | ||
@@ -96,8 +74,11 @@ webHookPath = webHookPath || '/' | ||
var update = JSON.parse(body) | ||
this.handleUpdate(update) | ||
.then(function () { | ||
res.writeHead(200) | ||
res.end() | ||
this.handleUpdate(update, res) | ||
.then(() => { | ||
if (!res.finished) { | ||
res.writeHead(200) | ||
res.end() | ||
} | ||
}) | ||
.catch(function (err) { | ||
.catch((err) => { | ||
debug(err) | ||
res.writeHead(500) | ||
@@ -115,2 +96,12 @@ res.end() | ||
/** | ||
* Start WebHook. | ||
* | ||
* @param {string} webHookPath - Webhook secret path | ||
* @param {Object} tlsOptions - TLS options | ||
* @param {number} port - Port number | ||
* @param {string} [host] - WebHook secret path | ||
* @return {Telegraf} self | ||
* @api public | ||
*/ | ||
telegraf.startWebHook = function (webHookPath, tlsOptions, port, host) { | ||
@@ -126,2 +117,8 @@ this.started = true | ||
/** | ||
* Stop WebHook/Polling. | ||
* | ||
* @return {Telegraf} self | ||
* @api public | ||
*/ | ||
telegraf.stop = function () { | ||
@@ -135,15 +132,22 @@ this.started = false | ||
/** | ||
* Register a middleware. | ||
* | ||
* @param {GeneratorFunction} fn - middleware | ||
* @return {Telegraf} self | ||
* @api public | ||
*/ | ||
telegraf.use = function (fn) { | ||
this.middlewares.push(fn) | ||
this.middleware.push(fn) | ||
return this | ||
} | ||
telegraf.onError = function (err) { | ||
var msg = err.stack || err.toString() | ||
console.error() | ||
console.error(msg.replace(/^/gm, ' ')) | ||
console.error() | ||
throw err | ||
} | ||
/** | ||
* Use the given middleware as handler for `eventType`. | ||
* | ||
* @param {string} eventType - Event type | ||
* @param {(GeneratorFunction|GeneratorFunction[])} fn - middleware | ||
* @return {Telegraf} self | ||
* @api public | ||
*/ | ||
telegraf.on = function (eventTypes) { | ||
@@ -156,3 +160,3 @@ if (typeof eventTypes === 'string') { | ||
eventTypes: eventTypes, | ||
middleware: compose(fns) | ||
middleware: Telegraf.compose(fns) | ||
}) | ||
@@ -162,2 +166,13 @@ return this | ||
// String -> RegEx magic! | ||
var matchOperatorsRe = /[|\\{}()[\]^$+*?.]/g | ||
/** | ||
* Use the given middleware as handler for text `trigger`. | ||
* | ||
* @param {(string|RegEx)} trigger - Text trigger | ||
* @param {(GeneratorFunction|GeneratorFunction[])} fn - middleware | ||
* @return {Telegraf} self | ||
* @api public | ||
*/ | ||
telegraf.hears = function (trigger) { | ||
@@ -167,3 +182,3 @@ var regex = trigger instanceof RegExp | ||
: new RegExp(trigger.replace(matchOperatorsRe, '\\$&')) | ||
var fns = compose([].slice.call(arguments, 1)) | ||
var handler = Telegraf.compose([].slice.call(arguments, 1)) | ||
this.handlers.push({ | ||
@@ -177,3 +192,3 @@ eventTypes: ['text'], | ||
}) | ||
yield fns | ||
yield handler | ||
} | ||
@@ -186,6 +201,8 @@ yield next | ||
telegraf.getUpdates = function (timeout, limit, offset) { | ||
return this.sendRequest(`getUpdates?offset=${offset}&limit=${limit}&timeout=${timeout}`) | ||
} | ||
/** | ||
* Returns basic information about the bot. | ||
* | ||
* @return {Promise} | ||
* @api public | ||
*/ | ||
telegraf.getMe = function () { | ||
@@ -195,2 +212,9 @@ return this.sendRequest('getMe') | ||
/** | ||
* Returns basic info about a file and prepare it for downloading. | ||
* | ||
* @param {string} fileId | ||
* @return {Promise} | ||
* @api public | ||
*/ | ||
telegraf.getFile = function (fileId) { | ||
@@ -200,2 +224,9 @@ return this.sendRequest('getFile', {file_id: fileId}) | ||
/** | ||
* Returns temporary public link to file. | ||
* | ||
* @param {string} fileId | ||
* @return {Promise} | ||
* @api public | ||
*/ | ||
telegraf.getFileLink = function (fileId) { | ||
@@ -205,2 +236,10 @@ return this.getFile(fileId).then((response) => `https://api.telegram.org/file/bot${this.token}/${response.file_path}`) | ||
/** | ||
* Specifies an url to receive incoming updates via an outgoing webHook. | ||
* | ||
* @param {string} url | ||
* @param {Object} cert | ||
* @return {Promise} | ||
* @api public | ||
*/ | ||
telegraf.setWebHook = function (url, cert) { | ||
@@ -214,2 +253,8 @@ var options = { | ||
/** | ||
* Removes webhook. Shortcut for `Telegraf.setWebHook('')` | ||
* | ||
* @return {Promise} | ||
* @api public | ||
*/ | ||
telegraf.removeWebHook = function () { | ||
@@ -219,2 +264,11 @@ return this.sendRequest('setWebHook', { url: '' }) | ||
/** | ||
* Sends text message. | ||
* | ||
* @param {(string|number)} chatId | ||
* @param {string} text | ||
* @param {Object} extra | ||
* @return {Promise} | ||
* @api public | ||
*/ | ||
telegraf.sendMessage = function (chatId, text, extra) { | ||
@@ -228,2 +282,12 @@ var options = { | ||
/** | ||
* Forwards message. | ||
* | ||
* @param {(string|number)} chatId | ||
* @param {(string|number)} fromChatId | ||
* @param {number} messageId | ||
* @param {Object} extra | ||
* @return {Promise} | ||
* @api public | ||
*/ | ||
telegraf.forwardMessage = function (chatId, fromChatId, messageId, extra) { | ||
@@ -238,2 +302,10 @@ var options = { | ||
/** | ||
* Sends chat action. | ||
* | ||
* @param {(string|number)} chatId | ||
* @param {string} action | ||
* @return {Promise} | ||
* @api public | ||
*/ | ||
telegraf.sendChatAction = function (chatId, action) { | ||
@@ -247,2 +319,11 @@ var options = { | ||
/** | ||
* Returns profiles photos for provided user. | ||
* | ||
* @param {(string|number)} userId | ||
* @param {number} offset | ||
* @param {number} limit | ||
* @return {Promise} | ||
* @api public | ||
*/ | ||
telegraf.getUserProfilePhotos = function (userId, offset, limit) { | ||
@@ -257,2 +338,12 @@ var options = { | ||
/** | ||
* Sends location. | ||
* | ||
* @param {(string|number)} chatId | ||
* @param {number} latitude | ||
* @param {number} longitude | ||
* @param {Object} extra | ||
* @return {Promise} | ||
* @api public | ||
*/ | ||
telegraf.sendLocation = function (chatId, latitude, longitude, extra) { | ||
@@ -267,2 +358,11 @@ var options = { | ||
/** | ||
* Use this method to send answers to an inline query. | ||
* | ||
* @param {number} inlineQueryId | ||
* @param {Object[]} results | ||
* @param {Object} extra | ||
* @return {Promise} | ||
* @api public | ||
*/ | ||
telegraf.answerInlineQuery = function (inlineQueryId, results, extra) { | ||
@@ -276,6 +376,15 @@ var options = { | ||
/** | ||
* Sends photo. | ||
* | ||
* @param {(string|number)} chatId | ||
* @param {(Object|string)} photo | ||
* @param {Object} extra | ||
* @return {Promise} | ||
* @api public | ||
*/ | ||
telegraf.sendPhoto = function (chatId, photo, extra) { | ||
var options = { | ||
chat_id: chatId, | ||
photo: {source: photo} | ||
photo: photo | ||
} | ||
@@ -285,6 +394,15 @@ return this.sendRequest('sendPhoto', Object.assign(options, extra)) | ||
/** | ||
* Send document. | ||
* | ||
* @param {(string|number)} chatId | ||
* @param {(Object|string)} doc | ||
* @param {Object} extra | ||
* @return {Promise} | ||
* @api public | ||
*/ | ||
telegraf.sendDocument = function (chatId, doc, extra) { | ||
var options = { | ||
chat_id: chatId, | ||
document: {source: doc} | ||
document: doc | ||
} | ||
@@ -294,6 +412,14 @@ return this.sendRequest('sendDocument', Object.assign(options, extra)) | ||
/** | ||
* Sends audio. | ||
* | ||
* @param {(string|number)} chatId | ||
* @param {(Object|string)} audio | ||
* @return {Promise} | ||
* @api public | ||
*/ | ||
telegraf.sendAudio = function (chatId, audio, extra) { | ||
var options = { | ||
chat_id: chatId, | ||
audio: {source: audio} | ||
audio: audio | ||
} | ||
@@ -303,6 +429,15 @@ return this.sendRequest('sendAudio', Object.assign(options, extra)) | ||
/** | ||
* Sends sticker. | ||
* | ||
* @param {(string|number)} chatId | ||
* @param {(Object|string)} sticker | ||
* @param {Object} extra | ||
* @return {Promise} | ||
* @api public | ||
*/ | ||
telegraf.sendSticker = function (chatId, sticker, extra) { | ||
var options = { | ||
chat_id: chatId, | ||
sticker: {source: sticker} | ||
sticker: sticker | ||
} | ||
@@ -312,6 +447,15 @@ return this.sendRequest('sendSticker', Object.assign(options, extra)) | ||
/** | ||
* Sends video. | ||
* | ||
* @param {(string|number)} chatId | ||
* @param {(Object|string)} video | ||
* @param {Object} extra | ||
* @return {Promise} | ||
* @api public | ||
*/ | ||
telegraf.sendVideo = function (chatId, video, extra) { | ||
var options = { | ||
chat_id: chatId, | ||
video: {source: video} | ||
video: video | ||
} | ||
@@ -321,6 +465,15 @@ return this.sendRequest('sendVideo', Object.assign(options, extra)) | ||
/** | ||
* Sends voice. | ||
* | ||
* @param {(string|number)} chatId | ||
* @param {(Object|string)} voice | ||
* @param {Object} extra | ||
* @return {Promise} | ||
* @api public | ||
*/ | ||
telegraf.sendVoice = function (chatId, voice, extra) { | ||
var options = { | ||
chat_id: chatId, | ||
voice: {source: voice} | ||
voice: voice | ||
} | ||
@@ -330,2 +483,10 @@ return this.sendRequest('sendVoice', Object.assign(options, extra)) | ||
/** | ||
* Use this method to kick a user from a group or a supergroup. | ||
* | ||
* @param {(string|number)} chatId | ||
* @param {number} userId | ||
* @return {Promise} | ||
* @api public | ||
*/ | ||
telegraf.kickChatMember = function (chatId, userId) { | ||
@@ -339,2 +500,10 @@ var options = { | ||
/** | ||
* Use this method to unban a previously kicked user in a supergroup. | ||
* | ||
* @param {(string|number)} chatId | ||
* @param {number} userId | ||
* @return {Promise} | ||
* @api public | ||
*/ | ||
telegraf.unbanChatMember = function (chatId, userId) { | ||
@@ -348,2 +517,11 @@ var options = { | ||
/** | ||
* Use this method to send answers to callback queries. | ||
* | ||
* @param {number} callbackQueryId | ||
* @param {string} text | ||
* @param {boolean} showAlert | ||
* @return {Promise} | ||
* @api public | ||
*/ | ||
telegraf.answerCallbackQuery = function (callbackQueryId, text, showAlert) { | ||
@@ -358,2 +536,12 @@ var options = { | ||
/** | ||
* Use this method to edit text messages sent by the bot or via the bot. | ||
* | ||
* @param {(string|number)} chatId | ||
* @param {number} messageId | ||
* @param {string} text | ||
* @param {Object} extra | ||
* @return {Promise} | ||
* @api public | ||
*/ | ||
telegraf.editMessageText = function (chatId, messageId, text, extra) { | ||
@@ -368,2 +556,12 @@ var options = { | ||
/** | ||
* Use this method to edit captions of messages sent by the bot or via the bot. | ||
* | ||
* @param {(string|number)} chatId | ||
* @param {number} messageId | ||
* @param {string} caption | ||
* @param {Object} extra | ||
* @return {Promise} | ||
* @api public | ||
*/ | ||
telegraf.editMessageCaption = function (chatId, messageId, caption, extra) { | ||
@@ -378,2 +576,12 @@ var options = { | ||
/** | ||
* Use this method to edit only the reply markup of messages sent by the bot or via the bot. | ||
* | ||
* @param {(string|number)} chatId | ||
* @param {number} messageId | ||
* @param {Object} markup | ||
* @param {Object} extra | ||
* @return {Promise} | ||
* @api public | ||
*/ | ||
telegraf.editMessageReplyMarkup = function (chatId, messageId, markup, extra) { | ||
@@ -388,44 +596,29 @@ var options = { | ||
telegraf.sendRequest = function (path, options) { | ||
/** | ||
* Fetch updates from Telegram servers. | ||
* | ||
* @param {number} timeout | ||
* @param {number} limit | ||
* @param {number} offset | ||
* @return {Promise} | ||
* @api private | ||
*/ | ||
telegraf.getUpdates = function (timeout, limit, offset) { | ||
return this.sendRequest(`getUpdates?offset=${offset}&limit=${limit}&timeout=${timeout}`) | ||
} | ||
/** | ||
* Send request to Telegram server | ||
* | ||
* @param {string} method - Telegram method | ||
* @param {Object} options - request options | ||
* @return {Promise} Promise with result | ||
* @api private | ||
*/ | ||
telegraf.sendRequest = function (method, options) { | ||
if (!this.token) { | ||
throw new Error('Telegram Bot Token is required') | ||
} | ||
var payload | ||
if (!options) { | ||
payload = { method: 'GET' } | ||
} else { | ||
options = options || {} | ||
if (options.reply_markup && typeof options.reply_markup !== 'string') { | ||
options.reply_markup = JSON.stringify(options.reply_markup) | ||
} | ||
var form = new FormData() | ||
Object.keys(options).forEach((key) => { | ||
var value = options[key] | ||
if (typeof value === 'undefined' || value == null) { | ||
return | ||
} | ||
if (typeof value === 'object') { | ||
var data = value.source | ||
if (data) { | ||
if (Buffer.isBuffer(data)) { | ||
form.append(key, data, { | ||
knownLength: data.length | ||
}) | ||
} else if (fs.existsSync(data)) { | ||
form.append(key, fs.createReadStream(data), { | ||
knownLength: fs.statSync(data).size | ||
}) | ||
} else { | ||
form.append(key, data) | ||
} | ||
} | ||
} else if (typeof value === 'boolean') { | ||
form.append(key, value.toString()) | ||
} else { | ||
form.append(key, value) | ||
} | ||
}) | ||
payload = { method: 'POST', body: form } | ||
} | ||
return fetch(`https://api.telegram.org/bot${this.token}/${path}`, payload) | ||
var payload = options ? {method: 'POST', body: this.buildForm(options)} : { method: 'GET' } | ||
return fetch(`https://api.telegram.org/bot${this.token}/${method}`, payload) | ||
.then((res) => res.json()) | ||
@@ -441,37 +634,49 @@ .then((data) => { | ||
telegraf.handleUpdate = function (update) { | ||
debug('update', update.update_id) | ||
var currentTypes = [] | ||
var payload | ||
supportedUpdateTypes.forEach((key) => { | ||
if (update[key]) { | ||
payload = update[key] | ||
currentTypes.push(key) | ||
/** | ||
* Build Form-Data from oprions | ||
* | ||
* @param {Object} options - Payload object | ||
* @return {Object} Form-Data | ||
* @api private | ||
*/ | ||
telegraf.buildForm = function (options) { | ||
if (options.reply_markup && typeof options.reply_markup !== 'string') { | ||
options.reply_markup = JSON.stringify(options.reply_markup) | ||
} | ||
var form = new FormData() | ||
Object.keys(options).forEach((key) => { | ||
var value = options[key] | ||
if (typeof value === 'undefined' || value == null) { | ||
return | ||
} | ||
}) | ||
if (update.message) { | ||
messageSubTypes.forEach((messageType) => { | ||
if (update.message[messageType]) { | ||
currentTypes.push(messageType) | ||
if (typeof value === 'object') { | ||
var data = value.source | ||
if (data) { | ||
if (Buffer.isBuffer(data)) { | ||
form.append(key, data, { | ||
knownLength: data.length | ||
}) | ||
} else if (fs.existsSync(data)) { | ||
form.append(key, fs.createReadStream(data), { | ||
knownLength: fs.statSync(data).size | ||
}) | ||
} else { | ||
form.append(key, data) | ||
} | ||
} | ||
}) | ||
} | ||
var handlerMiddlewares = this.handlers | ||
.filter((handler) => { | ||
return handler.eventTypes.filter((n) => { | ||
return currentTypes.indexOf(n) !== -1 | ||
}).length > 0 | ||
}) | ||
.map((handler) => handler.middleware) | ||
var context = this.createContext(currentTypes[currentTypes.length - 1], payload) | ||
context.use(compose(this.middlewares)) | ||
context.use(compose(handlerMiddlewares)) | ||
context.on('error', this.onError) | ||
return context.run().then(() => { | ||
this.offset = update.update_id + 1 | ||
} else if (typeof value === 'boolean') { | ||
form.append(key, value.toString()) | ||
} else { | ||
form.append(key, value) | ||
} | ||
}) | ||
return form | ||
} | ||
telegraf.updateLoop = function () { | ||
/** | ||
* Polling loop | ||
* | ||
* @api private | ||
*/ | ||
telegraf.pollingLoop = function () { | ||
if (!this.started) { | ||
@@ -484,3 +689,3 @@ return | ||
return new Promise((resolve) => { | ||
setTimeout(() => resolve([]), 100) | ||
setTimeout(() => resolve([]), 1000) | ||
}) | ||
@@ -491,3 +696,3 @@ }) | ||
}) | ||
.then(() => this.updateLoop()) | ||
.then(() => this.pollingLoop()) | ||
.catch((err) => { | ||
@@ -499,44 +704,188 @@ console.log('Telegraf: polling error', err) | ||
telegraf.createContext = function (payloadType, payload) { | ||
var state = {} | ||
var context = Object.assign({telegraf: this}, this.context) | ||
context.__defineGetter__('eventType', function () { | ||
return payloadType | ||
/** | ||
* Extract raw Telegram update | ||
* | ||
* @param {Object} update - raw Telegram update | ||
* @return {Object} normalized update | ||
* @api private | ||
*/ | ||
telegraf.extractUpdate = function (update) { | ||
var result = {} | ||
telegramUpdateTypes.forEach((key) => { | ||
if (update[key]) { | ||
result.payload = update[key] | ||
result.type = key | ||
} | ||
}) | ||
if (payloadType === 'message' || messageSubTypes.indexOf(payloadType) !== -1) { | ||
shortcuts.forEach((shortcut) => { | ||
context[shortcut.name] = this[shortcut.target].bind(this, payload.chat.id) | ||
if (update.message) { | ||
messageSubTypes.forEach((messageType) => { | ||
if (update.message[messageType]) { | ||
result.subType = messageType | ||
} | ||
}) | ||
context.__defineGetter__('message', function () { | ||
return payload | ||
}) | ||
} else if (payloadType === 'callback_query') { | ||
shortcuts.forEach((shortcut) => { | ||
context[shortcut.name] = this[shortcut.target].bind(this, payload.message.chat.id) | ||
}) | ||
context.__defineGetter__('callbackQuery', function () { | ||
return payload | ||
}) | ||
} else if (payloadType === 'inline_query') { | ||
context.__defineGetter__('inlineQuery', function () { | ||
return payload | ||
}) | ||
} else if (payloadType === 'chosen_inline_result') { | ||
context.__defineGetter__('chosenInlineResult', function () { | ||
return payload | ||
}) | ||
} | ||
context.__defineGetter__('state', function () { | ||
return state | ||
return result | ||
} | ||
/** | ||
* Handle Telegram update | ||
* | ||
* @param {Object} rawUpdate - Telegram update object | ||
* @param {Object} [webHookResponse] - http.ServerResponse | ||
* @return {Promise} | ||
* @api public | ||
*/ | ||
telegraf.handleUpdate = function (rawUpdate, webHookResponse) { | ||
var update = this.extractUpdate(rawUpdate) | ||
if (!update.type) { | ||
throw new Error('Undefined update type') | ||
} | ||
debug('update', update.type, update.subType || '--') | ||
var state = {} | ||
var context = Object.assign({ | ||
telegraf: this, | ||
eventType: update.type, | ||
eventSubType: update.subType, | ||
state: state | ||
}, this.context) | ||
var self = this | ||
var telegrafProxy = { | ||
sendRequest: function (method, options) { | ||
if (webHookResponse && !webHookResponse.finished && Object.keys(options).filter((x) => options[x].source).length === 0) { | ||
debug('webhook reply', method) | ||
options.method = method | ||
webHookResponse.setHeader('Content-Type', 'application/json') | ||
webHookResponse.end(JSON.stringify(options)) | ||
return Promise.resolve() | ||
} | ||
debug('http reply', method) | ||
return self.sendRequest(method, options) | ||
} | ||
} | ||
var chatId = (update.payload.chat && update.payload.chat.id) || (update.payload.message && update.payload.message.chat && update.payload.message.chat.id) | ||
if (chatId) { | ||
chatMethods.forEach((command) => context[command.name] = this[command.target].bind(telegrafProxy, chatId)) | ||
} | ||
// 🐫-case | ||
var payloadContextName = update.type.replace(/^([A-Z])|[\s-_](\w)/g, (match, group1, group2) => { | ||
return group2 ? group2.toUpperCase() : group1.toLowerCase() | ||
}) | ||
context.__defineSetter__('state', function (val) { | ||
state = Object.assign({}, val) | ||
context.__defineGetter__(payloadContextName, function () { | ||
return update.payload | ||
}) | ||
if (update.type === 'callback_query') { | ||
context.answerCallbackQuery = this.answerCallbackQuery.bind(telegrafProxy, update.payload.id) | ||
} | ||
if (update.type === 'inline_query') { | ||
context.answerInlineQuery = this.answerInlineQuery.bind(telegrafProxy, update.payload.id) | ||
} | ||
var handlers = this.handlers | ||
.filter((handler) => { | ||
return handler.eventTypes.filter((n) => { | ||
return update.type === n || update.subType === n | ||
}).length > 0 | ||
}) | ||
.map((handler) => handler.middleware) | ||
var ctx = ware() | ||
ctx.context = context | ||
return ctx | ||
ctx.use(Telegraf.compose(this.middleware)) | ||
ctx.use(Telegraf.compose(handlers)) | ||
ctx.on('error', this.onError) | ||
return ctx.run().then(() => { | ||
this.offset = rawUpdate.update_id + 1 | ||
}) | ||
} | ||
/** | ||
* Default error handler. | ||
* | ||
* @param {Error} err | ||
* @api private | ||
*/ | ||
telegraf.onError = function (err) { | ||
var msg = err.stack || err.toString() | ||
console.error() | ||
console.error(msg.replace(/^/gm, ' ')) | ||
console.error() | ||
throw err | ||
} | ||
var telegramUpdateTypes = [ | ||
'message', | ||
'callback_query', | ||
'inline_query', | ||
'chosen_inline_result' | ||
] | ||
var messageSubTypes = [ | ||
'text', | ||
'audio', | ||
'document', | ||
'photo', | ||
'sticker', | ||
'video', | ||
'voice', | ||
'contact', | ||
'location', | ||
'venue', | ||
'new_chat_participant', | ||
'left_chat_participant', | ||
'new_chat_title', | ||
'new_chat_photo', | ||
'delete_chat_photo', | ||
'group_chat_created', | ||
'supergroup_chat_created', | ||
'channel_chat_created', | ||
'migrate_to_chat_id', | ||
'migrate_from_chat_id', | ||
'pinned_message' | ||
] | ||
var chatMethods = [ | ||
{ name: 'reply', target: 'sendMessage' }, | ||
{ name: 'replyWithPhoto', target: 'sendPhoto' }, | ||
{ name: 'replyWithAudio', target: 'sendAudio' }, | ||
{ name: 'replyWithDocument', target: 'sendDocument' }, | ||
{ name: 'replyWithSticker', target: 'sendSticker' }, | ||
{ name: 'replyWithVideo', target: 'sendVideo' }, | ||
{ name: 'replyWithVoice', target: 'sendVoice' }, | ||
{ name: 'replyWithChatAction', target: 'sendChatAction' }, | ||
{ name: 'replyWithLocation', target: 'sendLocation' } | ||
] | ||
/** | ||
* Expose `memorySession`. | ||
*/ | ||
Telegraf.memorySession = memorySession | ||
/** | ||
* Expose `compose`. | ||
*/ | ||
Telegraf.compose = function (middleware) { | ||
return function * (next) { | ||
if (!next) { | ||
next = noop() | ||
} | ||
var i = middleware.length | ||
while (i--) { | ||
next = middleware[i].call(this, next) | ||
} | ||
return yield *next | ||
} | ||
} | ||
function * noop () {} | ||
/** | ||
* Expose `Telegraf`. | ||
*/ | ||
module.exports = Telegraf |
{ | ||
"name": "telegraf", | ||
"version": "0.6.3", | ||
"version": "0.7.0", | ||
"description": "📢 Modern Telegram bot framework", | ||
@@ -19,3 +19,5 @@ "main": "lib/telegraf.js", | ||
], | ||
"author": "Vitaly Domnikov <dotcypress@gmail.com>", | ||
"authors": [ | ||
"Vitaly Domnikov <dotcypress@gmail.com>" | ||
], | ||
"license": "MIT", | ||
@@ -27,3 +29,3 @@ "bugs": { | ||
"engines": { | ||
"node": ">=4.0.0" | ||
"node": ">=4.4.4" | ||
}, | ||
@@ -41,3 +43,2 @@ "files": [ | ||
"form-data": "^1.0.0-rc4", | ||
"koa-compose": "^2.4.0", | ||
"node-fetch": "^1.5.1" | ||
@@ -44,0 +45,0 @@ }, |
218
readme.md
@@ -0,3 +1,6 @@ | ||
[![npm](https://img.shields.io/npm/l/telegraf.svg?style=flat-square)](https://www.npmjs.com/package/telegraf) | ||
[![NPM Version](https://img.shields.io/npm/v/telegraf.svg?style=flat-square)](https://www.npmjs.com/package/telegraf) | ||
[![node](https://img.shields.io/node/v/telegraf.svg?style=flat-square)](https://www.npmjs.com/package/telegraf) | ||
[![David](https://img.shields.io/david/telegraf/telegraf.svg?style=flat-square)](https://www.npmjs.com/package/telegraf) | ||
[![Build Status](https://img.shields.io/travis/telegraf/telegraf.svg?branch=master&style=flat-square)](https://travis-ci.org/telegraf/telegraf) | ||
[![NPM Version](https://img.shields.io/npm/v/telegraf.svg?style=flat-square)](https://www.npmjs.com/package/telegraf) | ||
@@ -101,3 +104,4 @@ 📢 Modern Telegram bot framework for node.js | ||
this.telegraf // Telegraf instance | ||
this.eventType // Event type | ||
this.eventType // Event type(message, inline_query, etc.) | ||
this.eventSubType // Event subtype(text, sticker, audio, etc.) | ||
this.message // Received message | ||
@@ -114,9 +118,9 @@ this.inlineQuery // Received inline query | ||
```js | ||
var app = new Telegraf(process.env.BOT_TOKEN) | ||
var telegraf = new Telegraf(process.env.BOT_TOKEN) | ||
app.context.db = { | ||
telegraf.context.db = { | ||
getScores: function () { return 42 } | ||
} | ||
app.on('text', function * (){ | ||
telegraf.on('text', function * (){ | ||
var scores = this.db.getScores(this.message.from.username) | ||
@@ -146,8 +150,6 @@ this.reply(`${this.message.from.username}: ${score}`) | ||
For development you can use `Telegraf.memorySession()`, but session will be lost on app restart. | ||
For production environment use any [`telegraf-session-*`](https://www.npmjs.com/search?q=telegraf-session) middleware. | ||
```js | ||
var telegraf = new Telegraf(process.env.BOT_TOKEN) | ||
// Session will be lost on app restart | ||
telegraf.use(Telegraf.memorySession()) | ||
@@ -162,2 +164,5 @@ | ||
**Important: For production environment use any of [`telegraf-session-*`](https://www.npmjs.com/search?q=telegraf-session) middleware.** | ||
### Telegram WebHook | ||
@@ -173,3 +178,4 @@ | ||
cert: fs.readFileSync('server-cert.pem'), | ||
// This is necessary only if the client uses the self-signed certificate. | ||
// This is necessary only if the client | ||
// uses the self-signed certificate. | ||
ca: [ fs.readFileSync('client-cert.pem') ] | ||
@@ -179,3 +185,5 @@ } | ||
// Set telegram webhook | ||
telegraf.setWebHook('https://server.tld:8443/secret-path', {content: 'server-cert.pem'}) | ||
telegraf.setWebHook('https://server.tld:8443/secret-path', { | ||
content: 'server-cert.pem' | ||
}) | ||
@@ -191,5 +199,9 @@ // Start https webhook | ||
// Use webHookCallback() if you want attach telegraf to existing http server | ||
require('http').createServer(telegraf.webHookCallback('/secret-path')).listen(3000); | ||
require('https').createServer(tlsOptions, telegraf.webHookCallback('/secret-path')).listen(8443); | ||
require('http') | ||
.createServer(telegraf.webHookCallback('/secret-path')) | ||
.listen(3000) | ||
require('https') | ||
.createServer(tlsOptions, telegraf.webHookCallback('/secret-path')) | ||
.listen(8443) | ||
@@ -220,4 +232,2 @@ // Connect/Express.js integration | ||
log.error('server error', err) | ||
// If user messages is important for us, we will exit from update loop | ||
throw err | ||
@@ -231,5 +241,33 @@ } | ||
Note: shortcuts are not available for `inline_query` and `chosen_inline_result` events. | ||
**Available shortcuts for `message` event:** | ||
* `reply() -> telegraf.sendMessage()` | ||
* `replyWithPhoto() -> telegraf.sendPhoto()` | ||
* `replyWithAudio() -> telegraf.sendAudio()` | ||
* `replyWithDocument() -> telegraf.sendDocument()` | ||
* `replyWithSticker() -> telegraf.sendSticker()` | ||
* `replyWithVideo() -> telegraf.sendVideo()` | ||
* `replyWithVoice() -> telegraf.sendVoice()` | ||
* `replyWithChatAction() -> telegraf.sendChatAction()` | ||
* `replyWithLocation() -> telegraf.sendLocation()` | ||
**Shortcuts for `callback_query` event:** | ||
* `answerCallbackQuery() -> telegraf.answerCallbackQuery()` | ||
* `reply() -> telegraf.sendMessage()` | ||
* `replyWithPhoto() -> telegraf.sendPhoto()` | ||
* `replyWithAudio() -> telegraf.sendAudio()` | ||
* `replyWithDocument() -> telegraf.sendDocument()` | ||
* `replyWithSticker() -> telegraf.sendSticker()` | ||
* `replyWithVideo() -> telegraf.sendVideo()` | ||
* `replyWithVoice() -> telegraf.sendVoice()` | ||
* `replyWithChatAction() -> telegraf.sendChatAction()` | ||
* `replyWithLocation() -> telegraf.sendLocation()` | ||
**Shortcuts for `inline_query` event:** | ||
* `answerInlineQuery() -> telegraf.answerInlineQuery()` | ||
#### Examples | ||
```js | ||
@@ -248,14 +286,20 @@ var telegraf = new Telegraf(process.env.BOT_TOKEN) | ||
}) | ||
``` | ||
* `reply()` -> `telegraf.sendMessage()` | ||
* `replyWithPhoto()` -> `telegraf.sendPhoto()` | ||
* `replyWithAudio()` -> `telegraf.sendAudio()` | ||
* `replyWithDocument()` -> `telegraf.sendDocument()` | ||
* `replyWithSticker()` -> `telegraf.sendSticker()` | ||
* `replyWithVideo()` -> `telegraf.sendVideo()` | ||
* `replyWithVoice()` -> `telegraf.sendVoice()` | ||
* `replyWithChatAction()` -> `telegraf.sendChatAction()` | ||
* `replyWithLocation()` -> `telegraf.sendLocation()` | ||
telegraf.on('callback_query', function * (){ | ||
// Simple usage | ||
telegraf.answerCallbackQuery(this.callbackQuery.id) | ||
// Using shortcut | ||
this.answerCallbackQuery() | ||
}) | ||
telegraf.on('inline_query', function * (){ | ||
var result = [] | ||
// Simple usage | ||
telegraf.answerInlineQuery(this.inlineQuery.id, result) | ||
// Using shortcut | ||
this.answerInlineQuery(result) | ||
}) | ||
``` | ||
@@ -268,5 +312,6 @@ ## API reference | ||
* [`.setWebHook(url, cert)`](#setwebhook) | ||
* [`.startWebHook(webHookPath, tlsOptions, port, [host])`](#startWebHook) | ||
* [`.startWebHook(webHookPath, tlsOptions, port, [host])`](#startwebhook) | ||
* [`.startPolling(timeout, limit)`](#startPolling) | ||
* [`.stop()`](#stop) | ||
* [`.handleUpdate(rawUpdate, response)`](#handleupdate) | ||
* [`.use(function)`](#use) | ||
@@ -299,3 +344,3 @@ * [`.on(messageType, function)`](#on) | ||
<a name="new"></a> | ||
### `Telegraf.new(token)` | ||
##### `Telegraf.new(token)` | ||
@@ -308,7 +353,6 @@ Initialize new Telegraf app. | ||
* * * | ||
<a name="webhookcallback"></a> | ||
### `Telegraf.webHookCallback(webHookPath)` => `Function` | ||
##### `Telegraf.webHookCallback(webHookPath) => Function` | ||
@@ -325,5 +369,5 @@ Return a callback function suitable for the http[s].createServer() method to handle a request. | ||
<a name="setwebhook"></a> | ||
### `Telegraf.setWebHook(url, [cert])` => `Promise` | ||
##### `Telegraf.setWebHook(url, [cert]) => Promise` | ||
Specifies an url to receive incoming updates via an outgoing webHook. | ||
Specifies an url to receive incoming updates via an outgoing webhook. | ||
@@ -339,6 +383,6 @@ | Param | Type | Description | | ||
<a name="startWebHook"></a> | ||
### `Telegraf.startWebHook(token, tlsOptions, port, [host])` | ||
<a name="startwebhook"></a> | ||
##### `Telegraf.startWebHook(webHookPath, tlsOptions, port, [host])` | ||
Start listening @ `https://host:port/token` for Telegram calls. | ||
Start listening @ `https://host:port/webHookPath` for Telegram calls. | ||
@@ -350,3 +394,3 @@ | Param | Type | Description | | ||
| port | `Int` | Port number | | ||
| host | `String` | Hostname | | ||
| host | `String` | (Optional) Hostname | | ||
@@ -356,3 +400,3 @@ * * * | ||
<a name="startPolling"></a> | ||
### `Telegraf.startPolling(timeout, limit)` | ||
##### `Telegraf.startPolling(timeout, limit)` | ||
@@ -369,3 +413,3 @@ Start poll updates. | ||
<a name="stop"></a> | ||
### `Telegraf.stop()` | ||
##### `Telegraf.stop()` | ||
@@ -376,4 +420,17 @@ Stop WebHook and polling | ||
<a name="handleupdate"></a> | ||
##### `Telegraf.handleUpdate(rawUpdate, [webHookResponse])` | ||
Handle raw Telegram update. | ||
In case you use centralized webhook server, queue, etc. | ||
| Param | Type | Description | | ||
| --- | --- | --- | | ||
| rawUpdate | `Object` | Telegram update payload | | ||
| webHookResponse | `Object` | (Optional) [http.ServerResponse](https://nodejs.org/api/http.html#http_class_http_serverresponse) | | ||
* * * | ||
<a name="use"></a> | ||
### `Telegraf.use(middleware)` | ||
##### `Telegraf.use(middleware)` | ||
@@ -389,3 +446,3 @@ Registers a middleware. | ||
<a name="on"></a> | ||
### `Telegraf.on(eventType, handler)` | ||
##### `Telegraf.on(eventType, handler)` | ||
@@ -402,3 +459,3 @@ Registers handler for provided [event type](#events). | ||
<a name="hears"></a> | ||
### `Telegraf.hears(pattern, handler)` | ||
##### `Telegraf.hears(pattern, handler)` | ||
@@ -412,8 +469,6 @@ Registers handler only for `text` events using string pattern or RegEx. | ||
* * * | ||
<a name="sendmessage"></a> | ||
### `Telegraf.sendMessage(chatId, text, extra)` => `Promise` | ||
##### `Telegraf.sendMessage(chatId, text, extra) => Promise` | ||
@@ -431,3 +486,3 @@ Sends text message. | ||
<a name="forwardmessage"></a> | ||
### `Telegraf.forwardMessage(chatId, fromChatId, messageId, extra)` => `Promise` | ||
##### `Telegraf.forwardMessage(chatId, fromChatId, messageId, extra) => Promise` | ||
@@ -446,3 +501,3 @@ Forwards message. | ||
<a name="sendlocation"></a> | ||
### `Telegraf.sendLocation(chatId, latitude, longitude, extra)` => `Promise` | ||
##### `Telegraf.sendLocation(chatId, latitude, longitude, extra) => Promise` | ||
@@ -461,3 +516,3 @@ Sends location. | ||
<a name="sendphoto"></a> | ||
### `Telegraf.sendPhoto(chatId, photo, extra)` => `Promise` | ||
##### `Telegraf.sendPhoto(chatId, photo, extra) => Promise` | ||
@@ -475,3 +530,3 @@ Sends photo. | ||
<a name="senddocument"></a> | ||
### `Telegraf.sendDocument(chatId, doc, extra)` => `Promise` | ||
##### `Telegraf.sendDocument(chatId, doc, extra) => Promise` | ||
@@ -489,3 +544,3 @@ Sends document. | ||
<a name="sendaudio"></a> | ||
### `Telegraf.sendAudio(chatId, audio, extra)` => `Promise` | ||
##### `Telegraf.sendAudio(chatId, audio, extra) => Promise` | ||
@@ -503,3 +558,3 @@ Sends audio. | ||
<a name="sendsticker"></a> | ||
### `Telegraf.sendSticker(chatId, sticker, extra)` => `Promise` | ||
##### `Telegraf.sendSticker(chatId, sticker, extra) => Promise` | ||
@@ -517,3 +572,3 @@ Sends sticker. | ||
<a name="sendvideo"></a> | ||
### `Telegraf.sendVideo(chatId, video, extra)` => `Promise` | ||
##### `Telegraf.sendVideo(chatId, video, extra) => Promise` | ||
@@ -531,3 +586,3 @@ Sends video. | ||
<a name="sendvoice"></a> | ||
### `Telegraf.sendVoice(chatId, voice, extra)` => `Promise` | ||
##### `Telegraf.sendVoice(chatId, voice, extra) => Promise` | ||
@@ -545,3 +600,3 @@ Sends voice. | ||
<a name="sendchataction"></a> | ||
### `Telegraf.sendChatAction(chatId, action)` => `Promise` | ||
##### `Telegraf.sendChatAction(chatId, action) => Promise` | ||
@@ -558,3 +613,3 @@ Sends chat action. | ||
<a name="getme"></a> | ||
### `Telegraf.getMe()` => `Promise` | ||
##### `Telegraf.getMe() => Promise` | ||
@@ -568,3 +623,3 @@ Returns basic information about the bot. | ||
<a name="getuserprofilephotos"></a> | ||
### `Telegraf.getUserProfilePhotos(userId, offset, limit)` => `Promise` | ||
##### `Telegraf.getUserProfilePhotos(userId, offset, limit) => Promise` | ||
@@ -584,3 +639,3 @@ Returns profiles photos for provided user. | ||
<a name="getfile"></a> | ||
### `Telegraf.getFile(fileId)` => `Promise` | ||
##### `Telegraf.getFile(fileId) => Promise` | ||
@@ -598,3 +653,3 @@ Returns basic info about a file and prepare it for downloading. | ||
<a name="getFileLink"></a> | ||
### `Telegraf.getFileLink(fileId)` => `Promise` | ||
##### `Telegraf.getFileLink(fileId) => Promise` | ||
@@ -613,3 +668,3 @@ Returns link to file. | ||
<a name="removewebhook"></a> | ||
### `Telegraf.removeWebHook()` => `Promise` | ||
##### `Telegraf.removeWebHook() => Promise` | ||
@@ -621,4 +676,5 @@ Removes webhook. Shortcut for `Telegraf.setWebHook('')` | ||
* * * | ||
<a name="kickchatmember"></a> | ||
### `Telegraf.kickChatMember(chatId, userId)` => `Promise` | ||
##### `Telegraf.kickChatMember(chatId, userId) => Promise` | ||
@@ -637,3 +693,3 @@ Use this method to kick a user from a group or a supergroup. | ||
<a name="unbanchatmember"></a> | ||
### `Telegraf.unbanChatMember(chatId, userId)` => `Promise` | ||
##### `Telegraf.unbanChatMember(chatId, userId) => Promise` | ||
@@ -648,6 +704,7 @@ Use this method to unban a previously kicked user in a supergroup. | ||
[Related Telegram api docs](https://core.telegram.org/bots/api#unbanchatmember) | ||
* * * | ||
<a name="answerinlinequery"></a> | ||
### `Telegraf.answerInlineQuery(inlineQueryId, results, extra)` => `Promise` | ||
##### `Telegraf.answerInlineQuery(inlineQueryId, results, extra) => Promise` | ||
@@ -665,3 +722,3 @@ Use this method to send answers to an inline query. | ||
<a name="answercallbackquery"></a> | ||
### `Telegraf.answerCallbackQuery(callbackQueryId, text, showAlert)` => `Promise` | ||
##### `Telegraf.answerCallbackQuery(callbackQueryId, text, showAlert) => Promise` | ||
@@ -681,3 +738,3 @@ Use this method to send answers to callback queries. | ||
<a name="editmessagetext"></a> | ||
### `Telegraf.editMessageText(chatId, messageId, text, extra)` => `Promise` | ||
##### `Telegraf.editMessageText(chatId, messageId, text, extra) => Promise` | ||
@@ -696,3 +753,3 @@ Use this method to edit text messages sent by the bot or via the bot. | ||
<a name="editmessagecaption"></a> | ||
### `Telegraf.editMessageCaption(chatId, messageId, caption, extra)` => `Promise` | ||
##### `Telegraf.editMessageCaption(chatId, messageId, caption, extra) => Promise` | ||
@@ -711,3 +768,3 @@ Use this method to edit captions of messages sent by the bot or via the bot | ||
<a name="editmessagereplymarkup"></a> | ||
### `Telegraf.editMessageReplyMarkup(chatId, messageId, markup, extra)` => `Promise` | ||
##### `Telegraf.editMessageReplyMarkup(chatId, messageId, markup, extra) => Promise` | ||
@@ -737,12 +794,12 @@ Use this method to edit only the reply markup of messages sent by the bot or via the bot. | ||
// send file | ||
app.sendVideo('chatId', {source: '/path/to/video.mp4'}}) | ||
telegraf.sendVideo('chatId', {source: '/path/to/video.mp4'}}) | ||
// send buffer | ||
app.sendVoice('chatId', {source: new Buffer(...)}) | ||
telegraf.sendVoice('chatId', {source: new Buffer(...)}) | ||
// send stream | ||
app.sendAudio('chatId', {source: fs.createReadStream('/path/to/video.mp4')}) | ||
telegraf.sendAudio('chatId', {source: fs.createReadStream('/path/to/video.mp4')}) | ||
// resend existing file | ||
app.sendSticker('chatId', '123123jkbhj6b') | ||
telegraf.sendSticker('chatId', '123123jkbhj6b') | ||
``` | ||
@@ -754,4 +811,11 @@ | ||
Supported events: | ||
Supported event: | ||
* `message` | ||
* `inline_query` | ||
* `chosen_inline_result` | ||
* `callback_query` | ||
Available virtual events: | ||
* `text` | ||
@@ -778,11 +842,11 @@ * `audio` | ||
* `pinned_message` | ||
* `inline_query` | ||
* `chosen_inline_result` | ||
* `callback_query` | ||
Also, Telegraf will emit `message` event for for all messages except `inline_query`, `chosen_inline_result` and `callback_query`. | ||
```js | ||
// Handle stickers and photos | ||
// Handle all messages | ||
telegraf.on('message', function * () { | ||
this.reply('Hey there!') | ||
}) | ||
// Handle virtual events | ||
telegraf.on(['sticker', 'photo'], function * () { | ||
@@ -793,6 +857,2 @@ console.log(this.message) | ||
// Handle all messages except `inline_query`, `chosen_inline_result` and `callback_query` | ||
telegraf.on('message', function * () { | ||
this.reply('Hey there!') | ||
}) | ||
``` | ||
@@ -799,0 +859,0 @@ [Related Telegram api docs](https://core.telegram.org/bots/api#message) |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No contributors or author data
MaintenancePackage does not specify a list of contributors or an author in package.json.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
46751
4
835
837
1
- Removedkoa-compose@^2.4.0