Coder Social home page Coder Social logo

telegraf / telegraf Goto Github PK

View Code? Open in Web Editor NEW
7.7K 7.7K 892.0 6.25 MB

Modern Telegram Bot Framework for Node.js

Home Page: https://telegraf.js.org

License: MIT License

JavaScript 19.49% TypeScript 80.51%
bot bot-api bot-framework hacktoberfest middleware telegraf telegram telegram-bot telegram-bot-api telegram-bots

telegraf's Introduction

logo

telegraf.js

Modern Telegram Bot API framework for Node.js

Bot API Version install size GitHub top language English chat

For 3.x users

Introduction

Bots are special Telegram accounts designed to handle messages automatically. Users can interact with bots by sending them command messages in private or group chats. These accounts serve as an interface for code running somewhere on your server.

Telegraf is a library that makes it simple for you to develop your own Telegram bots using JavaScript or TypeScript.

Features

Example

const { Telegraf } = require('telegraf')
const { message } = require('telegraf/filters')

const bot = new Telegraf(process.env.BOT_TOKEN)
bot.start((ctx) => ctx.reply('Welcome'))
bot.help((ctx) => ctx.reply('Send me a sticker'))
bot.on(message('sticker'), (ctx) => ctx.reply('馃憤'))
bot.hears('hi', (ctx) => ctx.reply('Hey there'))
bot.launch()

// Enable graceful stop
process.once('SIGINT', () => bot.stop('SIGINT'))
process.once('SIGTERM', () => bot.stop('SIGTERM'))
const { Telegraf } = require('telegraf')

const bot = new Telegraf(process.env.BOT_TOKEN)
bot.command('oldschool', (ctx) => ctx.reply('Hello'))
bot.command('hipster', Telegraf.reply('位'))
bot.launch()

// Enable graceful stop
process.once('SIGINT', () => bot.stop('SIGINT'))
process.once('SIGTERM', () => bot.stop('SIGTERM'))

For additional bot examples see the new docs repo.

Resources

Getting started

Telegram token

To use the Telegram Bot API, you first have to get a bot account by chatting with BotFather.

BotFather will give you a token, something like 123456789:AbCdefGhIJKlmNoPQRsTUVwxyZ.

Installation

$ npm install telegraf

or

$ yarn add telegraf

or

$ pnpm add telegraf

Telegraf class

Telegraf instance represents your bot. It's responsible for obtaining updates and passing them to your handlers.

Start by listening to commands and launching your bot.

Context class

ctx you can see in every example is a Context instance. Telegraf creates one for each incoming update and passes it to your middleware. It contains the update, botInfo, and telegram for making arbitrary Bot API requests, as well as shorthand methods and getters.

This is probably the class you'll be using the most.

Shorthand methods

import { Telegraf } from 'telegraf'
import { message } from 'telegraf/filters'

const bot = new Telegraf(process.env.BOT_TOKEN)

bot.command('quit', async (ctx) => {
  // Explicit usage
  await ctx.telegram.leaveChat(ctx.message.chat.id)

  // Using context shortcut
  await ctx.leaveChat()
})

bot.on(message('text'), async (ctx) => {
  // Explicit usage
  await ctx.telegram.sendMessage(ctx.message.chat.id, `Hello ${ctx.state.role}`)

  // Using context shortcut
  await ctx.reply(`Hello ${ctx.state.role}`)
})

bot.on('callback_query', async (ctx) => {
  // Explicit usage
  await ctx.telegram.answerCbQuery(ctx.callbackQuery.id)

  // Using context shortcut
  await ctx.answerCbQuery()
})

bot.on('inline_query', async (ctx) => {
  const result = []
  // Explicit usage
  await ctx.telegram.answerInlineQuery(ctx.inlineQuery.id, result)

  // Using context shortcut
  await ctx.answerInlineQuery(result)
})

bot.launch()

// Enable graceful stop
process.once('SIGINT', () => bot.stop('SIGINT'))
process.once('SIGTERM', () => bot.stop('SIGTERM'))

Production

Webhooks

import { Telegraf } from "telegraf";
import { message } from 'telegraf/filters';

const bot = new Telegraf(token);

bot.on(message("text"), ctx => ctx.reply("Hello"));

// Start webhook via launch method (preferred)
bot.launch({
  webhook: {
    // Public domain for webhook; e.g.: example.com
    domain: webhookDomain,

    // Port to listen on; e.g.: 8080
    port: port,

    // Optional path to listen for.
    // `bot.secretPathComponent()` will be used by default
    path: webhookPath,

    // Optional secret to be sent back in a header for security.
    // e.g.: `crypto.randomBytes(64).toString("hex")`
    secretToken: randomAlphaNumericString,
  },
});

Use createWebhook() if you want to attach Telegraf to an existing http server.

import { createServer } from "http";

createServer(await bot.createWebhook({ domain: "example.com" })).listen(3000);
import { createServer } from "https";

createServer(tlsOptions, await bot.createWebhook({ domain: "example.com" })).listen(8443);

Error handling

If middleware throws an error or times out, Telegraf calls bot.handleError. If it rethrows, update source closes, and then the error is printed to console and process terminates. If it does not rethrow, the error is swallowed.

Default bot.handleError always rethrows. You can overwrite it using bot.catch if you need to.

鈿狅笍 Swallowing unknown errors might leave the process in invalid state!

鈩癸笍 In production, systemd or pm2 can restart your bot if it exits for any reason.

Advanced topics

Working with files

Supported file sources:

  • Existing file_id
  • File path
  • Url
  • Buffer
  • ReadStream

Also, you can provide an optional name of a file as filename when you send the file.

bot.on('message', async (ctx) => {
  // resend existing file by file_id
  await ctx.replyWithSticker('123123jkbhj6b')

  // send file
  await ctx.replyWithVideo(Input.fromLocalFile('/path/to/video.mp4'))

  // send stream
  await ctx.replyWithVideo(
    Input.fromReadableStream(fs.createReadStream('/path/to/video.mp4'))
  )

  // send buffer
  await ctx.replyWithVoice(Input.fromBuffer(Buffer.alloc()))

  // send url via Telegram server
  await ctx.replyWithPhoto(Input.fromURL('https://picsum.photos/200/300/'))

  // pipe url content
  await ctx.replyWithPhoto(
    Input.fromURLStream('https://picsum.photos/200/300/?random', 'kitten.jpg')
  )
})

Middleware

In addition to ctx: Context, each middleware receives next: () => Promise<void>.

As in Koa and some other middleware-based libraries, await next() will call next middleware and wait for it to finish:

import { Telegraf } from 'telegraf';
import { message } from 'telegraf/filters';

const bot = new Telegraf(process.env.BOT_TOKEN);

bot.use(async (ctx, next) => {
  console.time(`Processing update ${ctx.update.update_id}`);
  await next() // runs next middleware
  // runs after next middleware finishes
  console.timeEnd(`Processing update ${ctx.update.update_id}`);
})

bot.on(message('text'), (ctx) => ctx.reply('Hello World'));
bot.launch();

// Enable graceful stop
process.once('SIGINT', () => bot.stop('SIGINT'));
process.once('SIGTERM', () => bot.stop('SIGTERM'));

With this simple ability, you can:

Usage with TypeScript

Telegraf is written in TypeScript and therefore ships with declaration files for the entire library. Moreover, it includes types for the complete Telegram API via the typegram package. While most types of Telegraf's API surface are self-explanatory, there's some notable things to keep in mind.

Extending Context

The exact shape of ctx can vary based on the installed middleware. Some custom middleware might register properties on the context object that Telegraf is not aware of. Consequently, you can change the type of ctx to fit your needs in order for you to have proper TypeScript types for your data. This is done through Generics:

import { Context, Telegraf } from 'telegraf'

// Define your own context type
interface MyContext extends Context {
  myProp?: string
  myOtherProp?: number
}

// Create your bot and tell it about your context type
const bot = new Telegraf<MyContext>('SECRET TOKEN')

// Register middleware and launch your bot as usual
bot.use((ctx, next) => {
  // Yay, `myProp` is now available here as `string | undefined`!
  ctx.myProp = ctx.chat?.first_name?.toUpperCase()
  return next()
})
// ...

telegraf's People

Contributors

backmeupplz avatar chiliec avatar denisx avatar dependabot[bot] avatar dotcypress avatar edgar-p-yan avatar edjopato avatar ejnshtein avatar fazendaaa avatar greenkeeperio-bot avatar hypernova7 avatar kavayzin avatar khuzha avatar knightniwrem avatar knorpelsenf avatar letitcode avatar loskir avatar mcrunix avatar mikemikhaylov avatar mkrhere avatar piterden avatar septs avatar sergeysova avatar sjinks avatar smaznet avatar ulrichb avatar vlakam avatar vrumger avatar wojpawlik avatar zfeed avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

telegraf's Issues

How to reply_markup?

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.

more verbose error message for when a getUpdates gets a non-JSON response

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

README example doesn't work

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..

Question: How to send score from my game?

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!

Proxy support

My develop environment is behind a http proxy which makes it hard for me to test the bot locally. Will Telegraf support HTTP proxy?

Requesting for a file and storing a file

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'?

Sending keyboard with message with buttons

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!

inlinekeyboard with options?

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}

typescript definition

Is it possible to add in typescript definition for this library? It would make development much easier.

Answer Callback Query causing " Error: 400: Bad Request: URL_INVALID"

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?

Query : Telegraf to Icinga2

Hi,

Is there any way i can collect the metrics using Telegraf and pass it to Icinga2 using Icinga2 API call.

Thanks
Rahul

callbackButton doesn't emit "calback_query" in keyboard

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 does heroku support work?

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.

Webhooks can't be set

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'))

.on( reply?);

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 });

Multiple check on same message

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

Feature: enable http for startWebHook

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

Can't get hold of photo

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?

loader middleware

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.

Problem with Bot Commands in Group

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 !

screen shot 2016-08-27 at 10 33 25

ForceReply?

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 :(

Handle ECONNRESET

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' }

Not working for me

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!')
})

Chaining Message Sending Properly

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!

createChat method

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?

https://core.telegram.org/method/messages.createChat

cards

There is a way to simulate a cards (as in messenger), with a picture, caption and some (inline) buttons?
TNX

picture with caption

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

Bad Request: QUERY_ID_INVALID for inline-bot.js

Steps to reproduce:

  1. Create inline bot
  2. Try to use the bot without init the script, example: @usernamebot request
  3. Run 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 }

How to execute a Flow (telegraf-flow) after using a router?

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!

some commands being skipped

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?

Sending Gifs

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

Question/Best Practice: How would you handle this?

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:

  • I have to handle (and then exit because there's no session) all the messages with an email address in it, isn't it possible to register and then de-register the handler?
  • I have to handle all the enable callback_query (security constraint) and, here is the bad part, my userId is a uuid.v4, so I exceed the 64byte limit of telegram callback query...

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

Is it possible to remove an inline keyboard?

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.

Feature: allow passing own http[s] server on initialisation

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.

Bots flip out on messaging users that block it

(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)

Persisting context

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.

sending files via source or url ignores extra options

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.

How to manually unset keyboard

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?

Crash when POST data is not JSON

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.

self argument regex?

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 ?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    馃枛 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 馃搳馃搱馃帀

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google 鉂わ笍 Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.