telegraf / telegraf Goto Github PK
View Code? Open in Web Editor NEWModern Telegram Bot Framework for Node.js
Home Page: https://telegraf.js.org
License: MIT License
Modern Telegram Bot Framework for Node.js
Home Page: https://telegraf.js.org
License: MIT License
My inline bot works fine until I tried to add reply_markup to it.
bot.on( 'callback_query', ctx => {
const response = ctx.update.callback_query
return ctx.answerCallbackQuery( response.id, response.data )
} )
function replyButton( plot ) {
return Markup.inlineKeyboard([
Markup.callbackButton( 'Plot', plot )
] )
}
function replyInline( data ) {
const poster = ( null != data.poster ) ? data.poster : 'http://bit.ly/2moqQnT'
const plot = verifyData( data.plot, '' )
return {
id: data.imdb.id,
title: data.title,
type: 'article',
input_message_content: {
message_text: replyMessage( data ),
parse_mode: 'Markdown',
},
reply_markup: replyButton( plot ),
description: plot,
thumb_url: poster,
}
}
I wanna know why isn't working. I tried the way in keyboard examples but didn't work, that's why I came up with this 'solution' but isn't working either. What is the problem?
p.s.: btw, the 'plot' variable is a String.
I am trying to send animated Gifs using:
app.hears('cat',(ctx) => { return ctx.replyWithPhoto({ url: 'http://thecatapi.com/api/images/get?format=src&type=gif' }) })
But when using 'replyWithPhoto', the gif is actually posted as an image. How are animated gifs sent?
Greets
Hi! Great library. Love the API.
Got an exception with v2.4.0 from npm and this example from README:
const Telegraf = require('telegraf')
const app = new Telegraf(process.env.BOT_TOKEN)
app.on('message', (ctx) => ctx.replyWithMarkdown('*42*'))
app.startPolling()
produces:
TypeError: Cannot read property 'markdown' of undefined
at TelegrafContext.replyWithMarkdown (node_modules\telegraf\lib\context.js:227:50)
Quick fix in lib/extra.js
:
class Extra {
load (opts) {
if (!opts) {
return this // return this instead of undefined
}
for (var key of Object.keys(opts)) {
this[key] = opts[key]
}
return this
}
Dunno if it's a hack and I'm doing something wrong..
Example: bot.getFileLink(ctx.message.photo[0].file_id);
I'm trying to make simple interactive keyboard and want to make button which will remove keyboard.
import config from './config';
import Telegraf from 'telegraf';
import { Extra, Markup } from 'telegraf';
const app = new Telegraf(config.token);
const registerMarkup = Extra
.markup((m) => m
.resize()
.keyboard([
m.contactRequestButton('ะะฐัะตะณะธัััะธัะพะฒะฐัั ะฝะพะผะตั'),
m.callbackButton('ะัะผะตะฝะฐ', 'removeKb')
])
);
const cancelMarkup = Extra.markup((m) => m.removeKeyboard());
app.command('register', (ctx) => {
ctx.reply('ะะตะพะฑั
ะพะดะธะผะพ ะทะฐัะตะณะธัััะธัะพะฒะฐัั ะฝะพะผะตั ัะตะปะตัะพะฝะฐ', registerMarkup);
});
app.on('callback_query', (ctx) => {
console.log('callbackQuery');
console.log(ctx.callbackQuery);
});
app.startPolling();
In this case "callbackButton" doesn't emit anything except simple message like "button". But if I try inlineKeyboard it works.
How to send an audio file?
Is it possible to add in typescript definition for this library? It would make development much easier.
There is a way to simulate a cards (as in messenger), with a picture, caption and some (inline) buttons?
TNX
on this page
http://telegraf.js.org/advanced.html
Under composer,
composer example link is broken.
takes you to https://github.com/telegraf/telegraf-flow/blob/master/lib/flows/default.js
I have 2 separate commands that both sometimes get completely ignored.
bot.hears(/gif (.+)/ig, google.getGifs, (ctx) => {
return console.log('something happened');
});
I have tried making one a middleware and that didn't solve anything.
i can test with gif cats
and if i do it 2 or 3 times in a row. at least one time will not register and console.log only appears 1 or 2 times.
i also have a seperate command, but same syntax.
using bot.hears and the same regex capture.
same issue.
other commands do not seem to have the problem and return reliably.
what is some debugging I can do to find why this is happening?
Hey there,
Is it possible to remove an inline keyboard once the user chose an answer?
I was trying the obvious way, but it seems to work only for custom keyboards.
const Telegraf = require('telegraf');
const { Extra, Markup } = require('telegraf');
const app = new Telegraf(token);
app.command('keyb', (ctx) => {
return ctx.reply('<b>Coke</b> or <i>Pepsi?</i>', Extra.HTML().markup((m) =>
m.inlineKeyboard([
m.callbackButton('Coke', 'Coke'),
m.callbackButton('Pepsi', 'Pepsi')
])));
});
app.on('callback_query', (ctx) => {
ctx.reply('Removing', Extra.markup((m) => m.removeKeyboard()));
});
app.startPolling();
Thanks.
I would like to have a middleware that does the following
-inserts animated gif into current chat
-waits for an event or callback
-upon completion, the initial gif message is edited with the actual reply you wanted in the first place.
bot.on('something', loaderMiddleware(), (ctx) =>
// a function that takes a longgg time (5+ seconds)
// when complete, event is triggered, or a callback occurs.
// ctx.reply( here is the payload ) ;
);
is it possible to create a middleware like this?
Ideally I would like some kind of progress bar indicator, but I understand that's a lot of added complexity.
Sometimes when I switch devices, I still see a keyboard with my bot. Is there a possibility for me to manually unset it after certain events?
if i want to send picture with caption?
i try:
return ctx.replyWithPhoto({ url: 'http://som-picture-image.jpg', caption : 'some caption' })
but didn't work
Hello!
Thank you for taking the time to read this.
I have a question regarding using the Telegraf Router together with Telegraf Flow
How does one execute a execute a flow after using a custom menu to route?
Example:
bot.command('manage', (ctx) => {
let mainAdminMenuMarkup = Extra
.HTML()
.markup((m) => m.inlineKeyboard([
m.callbackButton('๐ก Create Thing', 'main_admin_menu:create'),
m.callbackButton('๐ List All Things', 'main_admin_menu:read'),
m.callbackButton('โ๏ธ Edit Thing', 'main_admin_menu:update'),
m.callbackButton('๐ซ Delete Thing', 'main_admin_menu:delete'),
], {columns: 2}));
ctx.replyWithHTML("Select an option below to continue...",mainAdminMenuMarkup);
});
simpleRouter.on('main_admin_menu', (ctx) => {
switch(ctx.state.value){
case "create" :
ctx.flow.enter('createThingFlow')
break;
default:
// intentionally blank
}
});
I realised that calling ctx.flow.enter('createThingFlow')
does not work unless it is run this way instead:
flow.command('add', (ctx) => {
ctx.flow.enter('createThingFlow')
})
Is there a workaround for this? Hope to hear from you soon!
(node:3370) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: 403: Forbidden: Bot was blocked by the user
Error: 403: Forbidden: Bot was blocked by the user
at TelegramError (/home/ubuntu/remmynbot/node_modules/telegraf/lib/network/error.js:3:5)
at buildPayload.then.then.then.then (/home/ubuntu/remmynbot/node_modules/telegraf/lib/network/client.js:87:17)
at process._tickCallback (internal/process/next_tick.js:103:7)
Hello, im using your framework with express but when i tried to set my webhook , telegram doesn't seem to receive it .
Here are my code
bot.telegram.setWebhook('https://xxxxxxx.herokuapp.com/secret-path') app.use(bot.webhookCallback('/secret-path'))
For example I chat with a bot called TestBot. If I type /start@randomstring
the bot defines it as /start
and works fine, but when I type /start@TestBot
it doesn't work.
I am trying to do something like this
return ctx.reply('<b>Coke</b> or <i>Pepsi?</i>', Extra.HTML().markup(
Markup.inlineKeyboard([
Markup.callbackButton('Coke', 'Coke'),
Markup.callbackButton('Pepsi', 'Pepsi')
])))
but also passing in the options
{ disable_notifications: true, reply_to_message_id: 123}
I'm having some difficulties with ctx.message.text. When the user sends 'Gantz:o' as the argument, my bot receives :o as a emoji. How can I correct this?
Dear Telegraf Developer,
Thank you for your extensive API. I've been using it for a while already and its been really awesome! One of the BEST Telegram Bot APIs for Node out there!
However, I am trying to figure out how I can do this:
When the user does /upload , It will reply with "Upload a file please", after which it will wait for the user can upload a file which would be stored in the Telegram servers.
Would you have any idea how to do this?
At the moment, I can think of one solution, which is to use the 'telegraf-flow' library, which is a middleware package.
However, is there a way to do this with just the Telegraf API, without needing to use 'telegraf-flow'?
My develop environment is behind a http proxy which makes it hard for me to test the bot locally. Will Telegraf support HTTP proxy?
Hi,
Is there any way i can collect the metrics using Telegraf and pass it to Icinga2 using Icinga2 API call.
Thanks
Rahul
What am I doing wrong? Bcos the message
event never occurs no matter the message I send to the bot.
const
BOT_TOKEN = '<removed>',
fs = require('fs'),
Telegraf = require('telegraf'),
bot = new Telegraf(BOT_TOKEN);
// Set telegram webhook
bot.telegram.setWebHook('https://<ip address here>:8443/' + BOT_TOKEN, {
content: __dirname + '/public.pem'
})
// Start https webhook
bot.startWebHook('/' + BOT_TOKEN, {
key: fs.readFileSync(__dirname + '/private.key'),
cert: fs.readFileSync(__dirname + '/public.pem')
}, 8443);
bot.on('message', (ctx) => {
console.log(ctx.message)
return ctx.reply('Hey there!')
})
Hi, In your page I read that telegraf is ready for heroku, do you have any page explaining what features it makes available for bot deployment in heroku? And if possible some tutorial teaching this? Sorry if it is obvious, I'm starting at these web stuff.
I have tried running the code in the examples: https://github.com/telegraf/telegraf/blob/develop/examples/custom-router-bot.js
All of it works well (it is able to add and subtract), but once it hits the number 42, I get this error:
Error: 400: Bad Request: URL_INVALID
at TelegramError (/Users/joshuaquek/WebstormProjects/Tacts_Telegraf/node_modules/telegraf/lib/network/error.js:3:5)
at buildPayload.then.then.then.then (/Users/joshuaquek/WebstormProjects/Tacts_Telegraf/node_modules/telegraf/lib/network/client.js:99:17)
at process._tickCallback (internal/process/next_tick.js:103:7)
Failed to process updates. { Error: 400: Bad Request: URL_INVALID
at TelegramError (/Users/joshuaquek/WebstormProjects/Tacts_Telegraf/node_modules/telegraf/lib/network/error.js:3:5)
at buildPayload.then.then.then.then (/Users/joshuaquek/WebstormProjects/Tacts_Telegraf/node_modules/telegraf/lib/network/client.js:99:17)
at process._tickCallback (internal/process/next_tick.js:103:7)
code: 400,
description: 'Bad Request: URL_INVALID',
retryAfter: undefined,
migrateToChaId: undefined }
Any workaround to this?
How can I ensure that a message is sent only after another?
Using the media-bot.js example script with reply_to_message_id
added:
const Telegraf = require("telegraf");
const fs = require("fs");
const bot = new Telegraf(process.env.BOT_TOKEN);
bot.on("text", (ctx) => {
return Promise.all([
// file
ctx.replyWithPhoto({
source: '/example/cat.jpeg'
reply_to_message_id: ctx.message.message_id
}),
// Stream
ctx.replyWithPhoto({
source: fs.createReadStream('/example/cat2.jpeg')
reply_to_message_id: ctx.message.message_id
}),
// Buffer
ctx.replyWithPhoto({
source: fs.readFileSync('/example/cat3.jpeg')
reply_to_message_id: ctx.message.message_id
}),
// url
ctx.replyWithPhoto({
url: "http://lorempixel.com/400/200/cats/",
reply_to_message_id: ctx.message.message_id
})
]);
});
bot.startPolling();
Files are being sent just fine, but the extra params are ignored. I tested this with reply_to_message_id
and caption
but i assume it's the same for the others. The problem occurs with all send*
methods as far as i can tell (i tested with photo, video & voice).
The problem only occurs when the files are being sent via url
or source
option. Using the last example
ctx.replyWithPhoto("http://lorempixel.com/400/200/cats/", {
reply_to_message_id: ctx.message.message_id
})
works as expected.
I want to use telegraf to start a private chat with someone that messages a chat group, if I try to send the message directly to the user with
ctx.telegram.sendMessage(ctx.update.message.from.id, 'hello');
I will receive the error:
Error: 403: Bot can't initiate conversation with a user
But if the user opens a private chat with my bot then it works.
I am suspecting that initiating a chat with a user id would be possible if the createChat method was implemented, right?
Hi,
I'm kinda new to Telegram Bot and I'm trying to figure out which is the best library. I've found this one after few other experiences and right now it is the best one (thanks a lot for you effort!!).
But here comes my question: how would you solve my problem?
I'm doing a telegram interface for a web REST api.
My idea is: when someone types /start, the bot asks for the email address and then asks me (or better, the admin telegram profile) if that person can be enabled to use the bot. I answer to the enabling question and then a record is added to the mysql database to specify that the couple "email address - telegramId" is allowed.
My current solution:
// routes.js
app.command('start', startHandler);
app.hears(/<email regex>/, startEmailHandler);
app.action(/enable :userId :telegramId/, startEnableHandler); // the "regex" is express.js-style
// start.js
function startHandler(ctx) {
// this.session.started = true;
// sendMessage("Hi, type your email address to be enabled");
}
function startEmailHandler(ctx) {
// if (!this.session.started) return;
// const email
// delete this.session.started;
// sendMessage(adminId, "Hey, ${email} wants to be enabled, is it ok?", keyboardWithYesAndNo);
}
function startEnableHandler(ctx) {
// const userId
// const telegramId
// user.telegramId = telegramId;
// sendMessage(telegramId, "You are enabled");
// sendMessage(adminId, "Done");
}
This approach, in my opinion, fails for two reasons:
While the first problem isn't so bad, the second one is terrible: I have to use sessions or a shorter id/hash, with all the concurrency/security problems of both solutions.
With another library (that I DO NOT want to use because it fails in thousands of other use cases) I could register callback_query handlers "on the fly" inside the "startEmailHandler" so that I can use a "user" variable in it, and then de-register it, so that, even if I press the "YES" button more than once, my DB isn't queried...
Sorry for the long post, but I don't know if I am missing something, if there is another way to think in a more "telegram style" or another way to do what I need...
Thanks a lot!!!
Zwe
Hi,
I am trying to use your example in order to 'repost' an photo. Meaning chat member sends a photo, bot grabs photo and sends it too.
Got this code from your media-bot example:
const downloadPhotoMiddleware = (ctx, next) => {
return app.telegram.getFileLink(ctx.message.photo[0].file_id)
.then((link) => {
ctx.state.fileLink = link
console.log("LOG!!!:" + ctx.message.photo[0].file_id)
return next()
})
}
Now I am waiting for a 'photo' and try to reply:
app.on('photo', downloadPhotoMiddleware, (ctx, next) => {
return ctx.replyWithPhoto({ source: '/directory/file.jpeg' })
})
What I get is:
Error: Invalid file descriptor
at Array.map (native)
at Telegram.buildFormDataPayload (/app/node_modules/telegraf/lib/network/client.js:119:70)
at Telegram.callApi (/app/node_modules/telegraf/lib/network/client.js:79:45)
at Telegram.sendPhoto (/app/node_modules/telegraf/lib/telegram.js:103:17)
at TelegrafContext.replyWithPhoto (/app/node_modules/telegraf/lib/core/context.js:184:26)
at app.on (/app/index_bu.js:53:14) // Here: return ctx.replyWithPhoto({ source: '/directory/file.jpeg' })
at execute (/app/node_modules/telegraf/lib/core/composer.js:162:34)
at Promise.resolve.handler (/app/node_modules/telegraf/lib/core/composer.js:162:53)
at app.telegram.getFileLink.then (/app/index_bu.js:25:14)
So maybe I got it wrong, I thought that '/directory/file.jpeg' is some default path? Is there anything else I am missing?
If I try the following:
app.on('photo', (ctx, next) => {
return ctx.replyWithPhoto({ url: 'https://api.telegram.org/file/bo<BOT-TOTKEN>/photo/file_1.jpg' })
})
the bot does reply with the desired image/photo but it is in very low quality, why is that?
I have a bot that sends automated updates to users (say every hour), how can make the session with the user persistent over bot restarts? Should I persist the whole ctx object or there's e better way to do it? Or to make it simpler: what is the correct way to send a reply to a user without asking the user to recreate the session every time the bot is restarted?
Thanks.
lets say i have something like
bot.hears(/test (.+)/i, (ctx) => { //not important});
is there a way/method to get the regex into ctx?
so that ctx.exampleMethod would return (.+)/i
?
is there a method in this framework (or in telegram) to determine if a message is a type of message that is reply?
for example
person1: hello
person2: ^replies to this message with "howdy"
is there a way to do like
bot.something('howdy', (ctx) => { // stuff });
I can see many time this error.
Failed to get updates. { Error
at ClientRequest.<anonymous> (C:\<path>\node_modules\node-fetch\index.js:133:11)
at emitOne (events.js:96:13)
at ClientRequest.emit (events.js:188:7)
at TLSSocket.socketErrorListener (_http_client.js:309:9)
at emitOne (events.js:96:13)
at TLSSocket.emit (events.js:188:7)
at emitErrorNT (net.js:1281:8)
at _combinedTickCallback (internal/process/next_tick.js:74:11)
at process._tickCallback (internal/process/next_tick.js:98:9)
name: 'FetchError',
message: 'request to https://api.telegram.org/bot<token>/getUpdates?offset=933321000&limit=100&timeout=30 failed, reason: read ECONNRESET',
type: 'system',
errno: 'ECONNRESET',
code: 'ECONNRESET' }
Hello all,
I am using Telegraf with games. It's working very well, the game is opening without problems.
Now I want to sent user scores to Telegram. But my game has no communication at all with my bot. How do you recommend sending scores to Telegram, as I cannot use telegraf's implementation.
Thanks in advance not only for answering my question, but also for building Telegraf and sharing it with community. Cheers!
I left a polling bot running this weekend and looking at the logs I see some of those:
Failed to get updates. SyntaxError: Unexpected token < in JSON at position 0
at Object.parse (native)
at /app/node_modules/node-fetch/lib/body.js:48:15
at process._tickCallback (internal/process/next_tick.js:103:7)
By looking at the source I see that this log comes from here: https://github.com/telegraf/telegraf/blob/develop/lib/telegraf.js#L110
I think it is safe to ignore them because the getUpdates have a retry policy, but it would be nice to know what non-JSON response Telegram did sent us just to have better data.
Maybe tweak this line? https://github.com/telegraf/telegraf/blob/develop/lib/network/client.js#L71
Hello, I read all the docs and searched through the source code until I found the ForceReply under the Markup module, but I just can't understand how to use it :P
I read everything I could on the official Telegram API documentation as well.
Upon receiving a message with this object, Telegram clients will display a reply interface to the user
But I still don't understand how to achieve this. I'm currently getting a /command
from inside a group chat and I need to send back a message (a question) so the user might answer directly from inside the reply/quote thing.
I tried several approaches using .reply
shortcut and telegram's sendMessage
with the settings/extra object (with reply_markup and force_reply) and got nowhere.
Thanks :(
Hi, sorry if it is a dumb question. How would I check if a single message is both a message and (for example) a command?
I want to do something like this
app.on('message', ctx => console.log("It's a message")); app.command('start', ctx => console.log("And a command too"));
but it only execute wichever I wrote first.
The reason is because I want it to execute a function on every input the bot receives, and then another specific to the type of the input
Hello, sorry for the foolish question, but how can I do it in one message?
I have this code, but here 2 messages:
const testMenu = Telegraf.Extra
.markdown()
.markup((m) => m.inlineKeyboard([
m.callbackButton('Test button', 'test')
]));
const aboutMenu = Telegraf.Extra
.markdown()
.markup((m) => m.keyboard([
m.callbackButton('โฌ
๏ธ Back')
]).resize());
bot.hears('test', (ctx) => {
ctx.reply('test message', testMenu).then(() => {
ctx.reply('about', aboutMenu)
})
});
Or maybe you have a solution to send normal keyboard without message. Then I would send 'about' keyboard and after that sent test message with inline keyboard. But I read Bot API documentation and did not find this.
Also I don't understan why the 'about' reply sometimes comes first.
Regards!
Hello!
Thank you for taking the time to read this. The previous questions that you have answered were super super helpful!
Anyway, I just wanted to ask a question on how to correct chain message replies so that they will send sequentially.
Is this the correct way to do it? (Below)
bot.command('start', (ctx) => {
ctx.replyWithHTML("Hello").then(()=>{
ctx.replyWithHTML("This").then(()=>{
ctx.replyWithHTML("Should").then(()=>{
ctx.replyWithHTML("Send").then(()=>{
ctx.replyWithHTML("Sequentially")
})
})
})
})
});
...or is this the correct way? (below)
bot.command('start', (ctx) => {
return ctx.replyWithHTML("Hello").then(()=>{
return ctx.replyWithHTML("This").then(()=>{
return ctx.replyWithHTML("Should").then(()=>{
return ctx.replyWithHTML("Send").then(()=>{
return ctx.replyWithHTML("Sequentially")
})
})
})
})
});
I understand that we can use ES7's Async/Await feature, but if we don't use it, what is the proper way to chain replies?
Hope to hear from you soon!
Steps to reproduce:
@usernamebot request
node inline-bot.js
Failed to process updates. { Error: 400: Bad Request: QUERY_ID_INVALID
at buildPayload.then.then.then.then (\node_modules\telegraf\lib\network\client.js:99:17)
at process._tickCallback (internal/process/next_tick.js:103:7)
code: 400,
description: 'Bad Request: QUERY_ID_INVALID',
retryAfter: undefined,
migrateToChaId: undefined }
When POSTing is invoked from a non-Telegram source, such as Postman, the request might not contain valid JSON, or a different data type like x-www-form-urlencoded. The whole app crashes when this happens.
Hey,
it would be useful to have this. I.e. I have only one network interface to listen on but I want to handle other http[s] request in application besides telegram webhooks. Currently I have a conflict if I do http.createServer on same interface two times.
Are there any other way to set a state property or a way to send different/modified context to next middlewares that could play well with the no-param-reassign eslint rule?
I've tried to pass a different ctx as a parameter to the next() function, like next(newctx)
but it don't seem to work as expected.
Related discussion: eslint/eslint#6505
Ive got a small problem. When my telegraf bot is inside a group, telegram shows this strange syntax of commands which is like following: /start is described as /start@MyBot.
This is a Telegram feature that multiple bots in one group can be accessed correctly ( e.g if two bots have /test as a command ) ...
But my bot.command('test') code doesnt reply to this syntax, so i would have to duplicate my command like this what seems not to be the right solution
bot.command('link@FBExchangeBot', (ctx) => {
Could you implement routing this problem because this is a major bug that stops me pushing to production
greets from germany !
Heroku: SSL termination occurs at Heroku's load balancers; they send your app plain (non-SSL) traffic, so your app should create a non-HTTPS server.
So it'd be useful
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.