Git Product home page Git Product logo

telegraf-i18n'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-i18n's People

Contributors

dmbaranov avatar dotcypress avatar edjopato avatar fazendaaa avatar fcfn avatar igorivaniuk avatar krolov avatar loskir 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

Watchers

 avatar  avatar  avatar  avatar

telegraf-i18n's Issues

custom template functions

Is it possible to add your own functions to the templates, for example pluralize.js?

example

const i18n = new telegrafI18n({
    context: {
        functions: {
            hello: (str) => `great ${str}`
        }
    }
});

template:

key:
   hello, ${hello('world')}

will be produce string hello, great world

Suggestion: Accept arrays in files

Accept arrays in translation files. In that case, a random string would be selected.
Example:

// en.json
{
  "hello": [
    "Hello, user!",
    "Hey!",
    "World"
  ]
}
// bot.js
ctx.command("hello", (ctx) => {
  ctx.reply(ctx.i18n.t("hello")) // Hello, user!
  ctx.reply(ctx.i18n.t("hello")) // World
  ctx.reply(ctx.i18n.t("hello")) // Hey!
  ctx.reply(ctx.i18n.t("hello")) // World
})

Needs to sanitize html entities in telegram.sendCopy

The code

app.on('message', ctx => {
  ctx.telegram.sendCopy(ctx.from.id, ctx.message);
});

Case 1

Input
Let's **test** __markdown__

Result (fine as expected)
screen shot 2017-08-21 at 19 49 12

Case 2

Input
Let's **test** __markdown__ and <b>some</b> HTML

Result (some became bold, which should not be )
screen shot 2017-08-21 at 19 49 40

Case 3

Input
<b>Some html<b> and some __markdown__ text

Result (no response due to error)
screen shot 2017-08-21 at 19 50 15

(node:42651) UnhandledPromiseRejectionWarning: 
Unhandled promise rejection (rejection id: 1): 
Error: 400: Bad Request: can't parse entities in message text: 
Expected end tag at byte offset 12

Tested on

Nested templates

Add the t function to templateData so that we can use t inside the template itself

For example:

greeting: hello!
main: i like trains.
end: bye!

text: |-
  ${t('greeting')}

  ${t('main')}

  ${t('end')}

Is there any markdown support?

My .yaml files are with Telegram's markdown syntax, my problem is that is not working. Example:

Hello: "**Hello**"

Result:

Hello

Add parameters handler

Suppose I have the following json string:

"invalidProjectName": "Name of the project must be less than %s characters and must not be greather than %s characters!"

I want replace the %s from the caller, eg:

ctx.reply(ctx.i18n.t('invalidProjectName', 2, 12));

so I don't need to update the json string with the variables values.

Failed to compile template

bug

This problem randomly appears during calls to i18n.t
Examples of where it happens

context.i18n.t('info')
context.i18n.t('welcome',{username:username})

What could be the problem?

[BUG] directory: path.resolve(__dirname, "locales")

// i18n options
const i18n = new TelegrafI18n({
  defaultLanguage: "ru",
  useSession: true,
  directory: path.resolve(__dirname, "locales")
});

when try to start bot it says:
Cannot convert undefined or null to object - it is related with the directory attribute of i18n config object. Who knows what is it and how can I resolve this issue?

Latest Telegraf Support

I can not install i18n over the latest Telegraf

npm ERR! Could not resolve dependency:
npm ERR! peer telegraf@"^3.7.1" from [email protected]
npm ERR! node_modules/telegraf-i18n
npm ERR!   telegraf-i18n@"*" from the root project
npm ERR!
npm ERR! Fix the upstream dependency conflict, or retry
npm ERR! this command with --force or --legacy-peer-deps
npm ERR! to accept an incorrect (and potentially broken) dependency resolution.

小ontext i18n is inaccessible in 'Telegraf/Stage' scene action

Hi! Please help me. I am used Telegraf/Stage (Simple scene-based control flow middleware). But when i'am in scene/Action have an error:

Cannot read property 't' of undefined

ctx.i18n also undefined. It should be noted that in 'scene/Enter' ctx.i18n.t is working.

If i make 'scene.use(i18n.middleware())' in scene file the problem will be solved.

How to fix this bug?
Thanks!

i18n.t is undefined

Hi,

I saw a similar issue but I can't get this working, hope you will help. Essentially I have app.js as entry point of my application, this file contains the following:

const express = require('express');
const app = express();

require('./config/server')(app, express);

const { Bot } = require('./controllers/bot.controller.js');

 let bot = new Bot(process.env.BOT_TOKEN);
 await bot.init();
 bot.start();

I load the server configuration passing in the constructor app and express:

const path = require('path');
const I18n = require('telegraf-i18n');
const session = require('telegraf/session');

module.exports = function (app, express) {

    // Session
    app.use(session());

    // Localization
    const i18n = new I18n({
        useSession: true,
        defaultLanguage: 'it',
        directory: path.join(__dirname, "../locales")
    });

    app.use(i18n.middleware());
}`

the main logic for the menu is on bot.controller which contains this definition:

`
const Telegraf = require('telegraf');
const { MainMenu } = require('../middlewares/menu/mainMenu');

class Bot {
    constructor(token) {
    this.bot = new Telegraf(token);
    this.bot.catch(error => {
        console.error(`Bot error: ${error}`);
    });
}

async init() {
    this.bot.use(new MainMenu(this.bot));
}

start() {
    this.bot.startPolling();
}

}

exports.Bot = Bot;`

and here is the problem:

`
const TelegrafInlineMenu = require('telegraf-inline-menu');
const SubscriptionMenu = require('./submenu/subscriptionMenu');

class MainMenu {

constructor(bot) {
    
    const menu = new TelegrafInlineMenu(ctx => ctx.i18n.t('welcomeMsg', {
        username: ctx.from.first_name
    }));
    menu.setCommand('start');

    bot.use(menu.init({
        backButtonText: '馃敊 Back ...',
        mainMenuButtonText: '馃彔 Main menu'
    }));
   }
}

exports.MainMenu = MainMenu;`

here I get:

Bot error: TypeError: Cannot read property 't' of undefined

what I did wrong? Thanks, and good holidays

Invalid locale produces undefined output

which causes errors from telegraf like this

Error: 400: Bad Request: can't parse message text: Can't find end of the entity starting at byte offset 5

If locale is not found - should default to defaultLocale

match functions not work

bot.hears(match('btn'); work only on one language.
I have two langauges and change on ctx.i18n.locale().
After swtich only keyboard doesn't handling

TypeError: Cannot read properties of undefined (reading 't') in WizardScene

Everything works, but in the second stage of the scene, i18n is undefined. i18n is registered, as are the scenes in the bot.

const createPostHere = new Scenes.WizardScene('createPostHere',
    async (ctx) => {
        ctx.wizard.state.post = {}
        await ctx.reply(await ctx.i18n.t('createPost.promptTitle')) << everything works
        ctx.wizard.next()
    },
    async (ctx) => {
        ctx.wizard.state.post.title = ctx.message.text
        await ctx.reply(await ctx.i18n.t('createPost.promptText')) << node crashes
        ctx.wizard.next()
    },
bot.use(stage.middleware());
bot.use(i18n.middleware())

I really don`t know where is the problem

Can't permanently change session locale

Hi there, thanks for the middleware, at first.

This is probably more a lack of understanding from me. I'm trying to set commands to change the language, just as you demonstrated in the example file. The problem is that whenever I call i18n.locale('pt'), it does change the language, but only for that message reply. If then I call another message and reply, this comes in the default language ('en').

I suppose this has something to do with me not using the session feature, but I did that because I'm using mysql session middleware instead of the memory one. Also, I see that we're calling i18n from the context of a message so... yeah, makes sense that it works only there. But how would I change it globally?

Could you help me finding out how it would work? This is my first attempt on Telegraf.

pluralize function should use actual locale

pluralize function should use actual locale instead of user session locale.

Workaround:

const i18n = new TelegrafI18n({
  sessionName: 'session',
  defaultLanguage: 'ru',
  allowMissing: true,
  directory: path.resolve(__dirname, 'locales')
});

app.use(Telegraf.memorySession());
app.use(i18n.middleware());
app.use((ctx, next) => {
  ctx.i18n.locale('ru'); // <- Force using Russian pluralization rules
  next()
});

Selected language not presisting in session

Hi, I was following along your example code to figure out how to let the user set their language in my bot. The problem is that the bot does not seem to be saving the locale for the session. For example, on doing
i18n.locale('ru')
the 'greeting' message does indeed have it's language changed. However, every subsequent message after that is still in the default language. I do have useSession set to true, and am also doing bot.use(Telegraf.session()).

Am I missing something? Is there no way to make the language persist for the session?

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.