Giter Site home page Giter Site logo

teloxide / teloxide Goto Github PK

View Code? Open in Web Editor NEW
2.7K 2.7K 195.0 10.86 MB

πŸ€– An elegant Telegram bots framework for Rust

Home Page: https://docs.rs/teloxide/

License: MIT License

Rust 100.00% Procfile 0.01%
rust telegram telegram-bot-api telegram-bot-framework teloxide

teloxide's Introduction

v0.11 -> v0.12 migration guide >>

teloxide

A full-featured framework that empowers you to easily build Telegram bots using Rust. It handles all the difficult stuff so you can focus only on your business logic.

Highlights

  • Declarative design. teloxide is based upon dptree, a functional chain of responsibility pattern that allows you to express pipelines of message processing in a highly declarative and extensible style.
  • Feature-rich. You can use both long polling and webhooks, configure an underlying HTTPS client, set a custom URL of a Telegram API server, do graceful shutdown, and much more.

  • Simple dialogues. Our dialogues subsystem is simple and easy-to-use, and, furthermore, is agnostic of how/where dialogues are stored. For example, you can just replace a one line to achieve persistence. Out-of-the-box storages include Redis and Sqlite.

  • Strongly typed commands. Define bot commands as an enum and teloxide will parse them automatically β€” just like JSON structures in serde-json and command-line arguments in structopt.

Setting up your environment

  1. Download Rust.
  2. Create a new bot using @Botfather to get a token in the format 123456789:blablabla.
  3. Initialise the TELOXIDE_TOKEN environmental variable to your token:
# Unix-like
$ export TELOXIDE_TOKEN=<Your token here>

# Windows command line
$ set TELOXIDE_TOKEN=<Your token here>

# Windows PowerShell
$ $env:TELOXIDE_TOKEN=<Your token here>
  1. Make sure that your Rust compiler is up to date (teloxide currently requires rustc at least version 1.70):
# If you're using stable
$ rustup update stable
$ rustup override set stable

# If you're using nightly
$ rustup update nightly
$ rustup override set nightly
  1. Run cargo new my_bot, enter the directory and put these lines into your Cargo.toml:
[dependencies]
teloxide = { version = "0.12", features = ["macros"] }
log = "0.4"
pretty_env_logger = "0.4"
tokio = { version =  "1.8", features = ["rt-multi-thread", "macros"] }

API overview

The dices bot

This bot replies with a dice to each received message:

[examples/throw_dice.rs]

use teloxide::prelude::*;

#[tokio::main]
async fn main() {
    pretty_env_logger::init();
    log::info!("Starting throw dice bot...");

    let bot = Bot::from_env();

    teloxide::repl(bot, |bot: Bot, msg: Message| async move {
        bot.send_dice(msg.chat.id).await?;
        Ok(())
    })
    .await;
}

Commands

Commands are strongly typed and defined declaratively, similar to how we define CLI using structopt and JSON structures in serde-json. The following bot accepts these commands:

  • /username <your username>
  • /usernameandage <your username> <your age>
  • /help

[examples/command.rs]

use teloxide::{prelude::*, utils::command::BotCommands};

#[tokio::main]
async fn main() {
    pretty_env_logger::init();
    log::info!("Starting command bot...");

    let bot = Bot::from_env();

    Command::repl(bot, answer).await;
}

#[derive(BotCommands, Clone)]
#[command(rename_rule = "lowercase", description = "These commands are supported:")]
enum Command {
    #[command(description = "display this text.")]
    Help,
    #[command(description = "handle a username.")]
    Username(String),
    #[command(description = "handle a username and an age.", parse_with = "split")]
    UsernameAndAge { username: String, age: u8 },
}

async fn answer(bot: Bot, msg: Message, cmd: Command) -> ResponseResult<()> {
    match cmd {
        Command::Help => bot.send_message(msg.chat.id, Command::descriptions().to_string()).await?,
        Command::Username(username) => {
            bot.send_message(msg.chat.id, format!("Your username is @{username}.")).await?
        }
        Command::UsernameAndAge { username, age } => {
            bot.send_message(msg.chat.id, format!("Your username is @{username} and age is {age}."))
                .await?
        }
    };

    Ok(())
}

Dialogues management

A dialogue is typically described by an enumeration where each variant is one possible state of the dialogue. There are also state handler functions, which may turn a dialogue from one state to another, thereby forming an FSM.

Below is a bot that asks you three questions and then sends the answers back to you:

[examples/dialogue.rs]

use teloxide::{dispatching::dialogue::InMemStorage, prelude::*};

type MyDialogue = Dialogue<State, InMemStorage<State>>;
type HandlerResult = Result<(), Box<dyn std::error::Error + Send + Sync>>;

#[derive(Clone, Default)]
pub enum State {
    #[default]
    Start,
    ReceiveFullName,
    ReceiveAge {
        full_name: String,
    },
    ReceiveLocation {
        full_name: String,
        age: u8,
    },
}

#[tokio::main]
async fn main() {
    pretty_env_logger::init();
    log::info!("Starting dialogue bot...");

    let bot = Bot::from_env();

    Dispatcher::builder(
        bot,
        Update::filter_message()
            .enter_dialogue::<Message, InMemStorage<State>, State>()
            .branch(dptree::case![State::Start].endpoint(start))
            .branch(dptree::case![State::ReceiveFullName].endpoint(receive_full_name))
            .branch(dptree::case![State::ReceiveAge { full_name }].endpoint(receive_age))
            .branch(
                dptree::case![State::ReceiveLocation { full_name, age }].endpoint(receive_location),
            ),
    )
    .dependencies(dptree::deps![InMemStorage::<State>::new()])
    .enable_ctrlc_handler()
    .build()
    .dispatch()
    .await;
}

async fn start(bot: Bot, dialogue: MyDialogue, msg: Message) -> HandlerResult {
    bot.send_message(msg.chat.id, "Let's start! What's your full name?").await?;
    dialogue.update(State::ReceiveFullName).await?;
    Ok(())
}

async fn receive_full_name(bot: Bot, dialogue: MyDialogue, msg: Message) -> HandlerResult {
    match msg.text() {
        Some(text) => {
            bot.send_message(msg.chat.id, "How old are you?").await?;
            dialogue.update(State::ReceiveAge { full_name: text.into() }).await?;
        }
        None => {
            bot.send_message(msg.chat.id, "Send me plain text.").await?;
        }
    }

    Ok(())
}

async fn receive_age(
    bot: Bot,
    dialogue: MyDialogue,
    full_name: String, // Available from `State::ReceiveAge`.
    msg: Message,
) -> HandlerResult {
    match msg.text().map(|text| text.parse::<u8>()) {
        Some(Ok(age)) => {
            bot.send_message(msg.chat.id, "What's your location?").await?;
            dialogue.update(State::ReceiveLocation { full_name, age }).await?;
        }
        _ => {
            bot.send_message(msg.chat.id, "Send me a number.").await?;
        }
    }

    Ok(())
}

async fn receive_location(
    bot: Bot,
    dialogue: MyDialogue,
    (full_name, age): (String, u8), // Available from `State::ReceiveLocation`.
    msg: Message,
) -> HandlerResult {
    match msg.text() {
        Some(location) => {
            let report = format!("Full name: {full_name}\nAge: {age}\nLocation: {location}");
            bot.send_message(msg.chat.id, report).await?;
            dialogue.exit().await?;
        }
        None => {
            bot.send_message(msg.chat.id, "Send me plain text.").await?;
        }
    }

    Ok(())
}

More examples >>

Tutorials

FAQ

Q: Where I can ask questions?

A:

  • Issues is a good place for well-formed questions about the library design, enhancements, and bug reports.
  • GitHub Discussions is a place where you can ask us for help in a less formal manner.
  • If you need quick help in real-time, you should ask a question in our official Telegram group.

Q: Do you support the Telegram API for clients?

A: No, only the bots API.

Q: Can I use webhooks?

A: You can! teloxide has a built-in support for webhooks in dispatching::update_listeners::webhooks module. See how it's used in examples/ngrok_ping_pong_bot and examples/heroku_ping_pong_bot.

Q: Can I handle both callback queries and messages within a single dialogue?

A: Yes, see examples/purchase.rs.

Community bots

Feel free to propose your own bot to our collection!

Show bots using `teloxide` older than v0.6.0

See 1600+ other public repositories using teloxide >>

Contributing

See CONRIBUTING.md.

teloxide's People

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

teloxide's Issues

More storages

  • RedisStorage - store data in Redis (#202).
  • SqliteStorage - store data in sqlite database.
  • MongoStorage - store data in MongoDB.

Cannot parse update

ERROR teloxide::dispatching::update_listeners > Cannot parse an update.
Error: Error("data did not match any variant of untagged enum MessageKind", line: 1, column: 696)
  Value: {
    "message":{
        "chat":{
            "id":-1001276785818,
            "title":"teloxide dev",
            "type":"supergroup",
            "username":"teloxide_dev"
        },
        "date":1582134655,
        "from": {"first_name":"Hirrolot","id":408258968,"is_bot":false,"username":"hirrolot"},
        "message_id":20225,
        "pinned_message":{
            "chat":{
                "id":-1001276785818,
                "title":"teloxide dev",
                "type":"supergroup",
                "username":"teloxide_dev"
            },
            "date":1582134643,
            "from":{
                "first_name":"Hirrolot",
                "id":408258968,
                "is_bot":false,
                "username":"hirrolot"
            },
            "message_id":20224,
            "text":"Для Π½ΠΎΠ²ΠΎΠΏΡ€ΠΈΡˆΠ΅Π΄ΡˆΠΈΡ… - отмСчаСмся, Ρ‡Ρ‚ΠΎ Π³ΠΎΡ‚ΠΎΠ²Ρ‹ Ρ€Π°Π·Ρ€Π°Π±Π°Ρ‚Ρ‹Π²Π°Ρ‚ΡŒ, послС Ρ‡Π΅Π³ΠΎ я Π½Π°Π·Π½Π°Ρ‡Π°ΡŽ Π²Π°ΠΌ ΠΎΠ΄ΠΈΠ½ ΠΈΠ· Π½Π°ΡˆΠΈΡ… ишъюсов"
        }
    },
    "update_id":845402291
}

I think that was happen due to pinned_message

Clean skipping `None`s

For now, there are many structures in the lib with code like that:

struct Struct {
    #[serde(skip_serializing_if = "Option::is_none")]
    field: Option<i32>,
    #[serde(skip_serializing_if = "Option::is_none")]
    field2: Option<i32>,
    /* ... */
    #[serde(skip_serializing_if = "Option::is_none")]
    fieldN: Option<i32>,
}

That's a lot of boilerplate. The same struct can be written like this:

#[serde_with_macros::skip_serializing_none]
struct Struct {
    field: Option<i32>,
    field2: Option<i32>,
    /* ... */
    fieldN: Option<i32>,
}

Create message builders for HTML and Markdown

In the ticket #80 we implemented utils for working with HTML and MarkdownV2 markups.

They can be used as simple functions to format HTML and markdown.
But for building large messages it would be more convenient to have chain-like builders likes this:

let msg = MsgBuilder::new("My message")
    .bold()
    .italic()
    .stroke()
    .finish();

Instead of Lisp-style expressions with lots of braces:

let msg = stroke(italic(bold("My message")));

Write style guide

Some rules

  • line <= 80 columns

  • generics always with where

  • Order: mods -> pub uses -> uses

  • use order

    1. std:: uses
    2. line break
    3. non-crate:: uses
    4. line break
    5. crate:: uses
  • always use multi (?) uses like

    Example

    // Just an example
    mod download;
    mod request;
    
    pub use self::{
        download::download_file,
        request::{request_json, request_multipart, request_simple},
    };
    
    use std::path::PathBuf;
    
    use reqwest::Client;
    use tokio::io::{AsyncWrite, AsyncWriteExt};
    
    #[cfg(feature = "unstable-stream")]
    use ::{bytes::Bytes, tokio::stream::Stream}; // `::{}` used to write #[cfg] only once
    
    use crate::{
        network,
        requests::{Request, RequestContext, ResponseResult},
        types::True,
    };

  • unit test for path module in path::tests module

  • (de)serialization tests as follows:

    Deserialize

    let json = r#"{
        "data":"someData",
        "hash":"1122",
        "secret":"secret"
    }"#;
    
    let expected = EncryptedCredentials {
        data: String::from("someData"),
        hash: String::from("1122"),
        secret: String::from("secret"),
    };
    let actual: EncryptedCredentials = serde_json::from_str(&json).unwrap();
    
    assert_eq!(expected, actual);

    Serialize

    let data = EncryptedCredentials {
        data: String::from("someData"),
        hash: String::from("1122"),
        secret: String::from("secret"),
    };
    
    let expected = r#"{
        "data":"someData",
        "hash":"1122",
        "secret":"secret"
    }"#;
    let actual = serde_json::to_string(&data).unwrap();
    
    assert_eq!(expected, actual);

  • write tests for fails

  • Use enums instead of telegram's optional fields (needs better explanation)

  • derive

    • Debug, Clone(?), PartialEq always
    • Deserialize for all types in types:: these can be returned from telegram
    • Serialize for
      1. all types in types:: these can be sended to telegram
      2. json-requests
    • Hash, Eq when possible
    • Copy for ZSTs and for types that are small and they need copy
    • Default when type has default (paired with ::new())
    • PartialOrd, Ord hardly ever
  • prefer match over if let (?)

  • refer types/fns in doc-comments when possible

    Example

    /// Download file from telegram into `destination`.
    /// `path` can be obtained from [`get_file`] method.
    ///
    /// For downloading as Stream of Chunks see [`download_file_stream`].
    ///
    /// <...>
    ///
    /// [`get_file`]: crate::bot::Bot::get_file
    /// [`download_file_stream`]: crate::bot::Bot::download_file_stream
    pub async fn download_file<D>(

  • use T as generic parameter and value as a param name in setters

  • remove dublication (e.g. Message::message_id -> Message::id + #[serde(rename = "message_id")])

  • line break t the end of the file

  • use Self type

  • write examples in doc-comments

  • try to avoid boxing and be "zero-cost", but not at cost of everything

Unresolved

  • generic parameters names (first letter of the param where the type is used? first letter of the type-bound? A, B, C, D...? T or A, B, C, D...? One letter or many? etc...)
    • [My suggestion]:
      • One letter (except ambiguous cases)
      • F for functions
      • Fut for futures
      • S for streams
      • First letter of the param name for requests (in bot methods and news)
      • T if struct/fn have 1 type parameter and there is no better letter (see above)
      • A, B, C... if struct/fn have more that 1 type parameter and there is no better letter (see above)
      • 'a, 'b, 'c... for lifetimes (except serde's 'de) (but naming with first letter(s) of type/param name in some cases is possible too)
  • Naming for tests
  • use super?
  • deriving order
  • enum { A { b: B, c: C }, ... } or enum { A(A), ... } + struct A { b: B, c: C }
  • String::from() (my favourite) or .to_string() or .to_owned() or .into()
  • copy-pasting telegram docs (e.g. shall we remove "Optional.")
  • Where to (not) use Into<...>
  • A(_, _) or A::new(_, _) or _.a(_) or a(_, _) (see filters from dp pr)
  • Use BitAnd, BitOr, BitXor, etc? (also see dp pr)
  • Write diagrams in doc comments? (yep, see dp prhttps://github.com/async-telegram-bot/async-telegram-bot/pull/20/files#diff-9d880ab6e47b866d28f2f44a39fdfeffR26-R84)
  • Self::Variant or Enum::Variant?

See also fmt-rfcs

teloxide-macros 0.1.1 breaks compatibility

[  642s] error[E0049]: method `parse` has 1 type parameter but its trait declaration has 0 type parameters
[  642s]    --> src/utils/command.rs:165:18
[  642s]     |
[  642s] 95  |     fn parse(s: &str) -> Option<(Self, Vec<&str>)>;
[  642s]     |             - expected 0 type parameters
[  642s] ...
[  642s] 165 |         #[derive(BotCommand, Debug, PartialEq)]
[  642s]     |                  ^^^^^^^^^^ found 1 type parameter
[  642s] 
[  642s] error[E0049]: method `parse` has 1 type parameter but its trait declaration has 0 type parameters
[  642s]    --> src/utils/command.rs:180:18
[  642s]     |
[  642s] 95  |     fn parse(s: &str) -> Option<(Self, Vec<&str>)>;
[  642s]     |             - expected 0 type parameters
[  642s] ...
[  642s] 180 |         #[derive(BotCommand, Debug, PartialEq)]
[  642s]     |                  ^^^^^^^^^^ found 1 type parameter
[  642s] 
[  642s] error[E0049]: method `parse` has 1 type parameter but its trait declaration has 0 type parameters
[  642s]    --> src/utils/command.rs:196:18
[  642s]     |
[  642s] 95  |     fn parse(s: &str) -> Option<(Self, Vec<&str>)>;
[  642s]     |             - expected 0 type parameters
[  642s] ...
[  642s] 196 |         #[derive(BotCommand, Debug, PartialEq)]
[  642s]     |                  ^^^^^^^^^^ found 1 type parameter
[  642s] 
[  642s] error[E0049]: method `parse` has 1 type parameter but its trait declaration has 0 type parameters
[  642s]    --> src/utils/command.rs:220:18
[  642s]     |
[  642s] 95  |     fn parse(s: &str) -> Option<(Self, Vec<&str>)>;
[  642s]     |             - expected 0 type parameters
[  642s] ...
[  642s] 220 |         #[derive(BotCommand, Debug, PartialEq)]
[  642s]     |                  ^^^^^^^^^^ found 1 type parameter
[  642s] 

Implement abstract automata

Something like this:

pub type TransitionFn<T: Default> = FnMut(state: T, update: &Update) -> T;

struct FSM<T: Default> {
    let state: T,
    let transition: TransitionFn<T>,
}

impl<T: Default> FSM<T> {
    pub fn new(transition: TransitionFn) -> Self {
        Self { state: T::default(), transition }
    }

    pub fn accept(&mut self, update: &Update) {
        self.state = self.transition(self.state, update);
    }
}

Improve README.md

Improve README with

  • Add description / slogan
  • Do smt to logo (either align it to the left or make it wider)
  • Write usage guide
  • Write notes about changes to telegram types
  • Add required rustc version

Also: what is the purpose of "Pulse Β· Stargazers Β· Releases Β· Contributing"? first 2 are 100% useless (in my opinion) and all of them are easily accessible.

Add macros with common `derive`s

Add macros with common derives:

  • Debug
  • PartialEq
  • Eq (for types without f32),
  • Hash (for types without f32)
  • Clone
  • Deserialize
  • Serialize
    This (yet unnamed) macro can be used on payloads and types.

Unresolved questions

  • How should we name the macro?
  • Is it possible to implement this with "macro-by-example"?

Example with a shared storage

Hi!
Thanks for the nice crate. It seems to be very well design.

I've spent a weekend playing around with it.
However I struggle with some difficulties.
Would you mind adding an example with a simple shared storage?

E.g. when bot receives a message, it replies how many messages it has received in total.

Thanks in advance.

Dispatcher

  1. Rename ChatDispatcher to ChatRouter.
  2. Add type Dispatcher, which contains routers, middlewares, updater, Bot, Storage
  3. Add InlineQueryRouter.
  4. Add ChannelRouter.
  5. Add PollRouter.

Rename lib

If you have an idea - suggest it here!

problems with polls with missing field 'poll_type' /// and InvalidJson

 ERROR teloxide::error_handlers > Error: InvalidJson(Error("data did not match any variant of untagged enum TelegramResponse", line: 0, column: 0))
 ERROR teloxide::dispatching::update_listeners > Cannot parse an update.
Error: Error("missing field `poll_type`", line: 1, column: 359)
Value: {"poll":{"allows_multiple_answers":false,"id":"5377643193141559299","is_anonymous":true,"is_closed":false,"options":[{"text":"1","voter_count":1},{"text":"2","voter_count":0},{"text":"3","voter_count":0},{"text":"4","voter_count":0},{"text":"5","voter_count":0}],"question":"Rate me from 1 to 5.","total_voter_count":1,"type":"regular"},"update_id":376447145}
This is a bug in teloxide, please open an issue here: https://github.com/teloxide/teloxide/issues.

Typed bot commands

Use typed arguments instead of just strings:

#[derive(BotCommand)]
enum Command {
    A(i32),
    B(String, f64),
    C(MyRandomType),
}

The variant's data must implement FromStr.

`.rs` files on crates.io with executable bit

On github repo no this issue but on crates.io all .rs files with executable bits. Also .md files and LICENSE file.

drwxr-xr-x. bot/
drwxr-xr-x. dispatching/
-rwxrwxrwx. error_handlers.rs*
-rwxrwxrwx. errors.rs*
-rwxrwxrwx. lib.rs*
-rwxrwxrwx. logging.rs*
drwxr-xr-x. net/
-rwxrwxrwx. prelude.rs*
drwxr-xr-x. requests/
drwxr-xr-x. types/
drwxr-xr-x. utils/

Please remove executable bit to make some linters happier.

How to use command handler at the same time as a command handler?

Hi how can I combine:

// Only iterate through commands in a proper format:
    rx.commands::<Command, &str>(&bot_name)
        // Execute all incoming commands concurrently:
        .for_each_concurrent(None, |(cx, command, _)| async move {
            answer(cx, command).await.log_on_error().await;
        })
        .await;

with

rx.for_each(|message| async move {
        message.answer("pong").send().await.log_on_error().await;
    })
    .await;

?

error: there is no argument named `text`

Hello. Trying to build teloxide and got this error during build:

error: there is no argument named `text`
   --> src/error_handlers.rs:211:22
    |
211 |         log::error!("{text}: {:?}", error, text = self.text);
    |                      ^^^^^^

error: argument never used
   --> src/error_handlers.rs:211:44
    |
211 |         log::error!("{text}: {:?}", error, text = self.text);
    |                     --------------         ^^^^^^^^^^^^^^^^ argument never used
    |                     |
    |                     formatting specifier missing

error[E0425]: cannot find value `text` in this scope
   --> src/error_handlers.rs:211:44
    |
211 |         log::error!("{text}: {:?}", error, text = self.text);
    |                                            ^^^^ help: you might have meant to use the available field: `self.text`

error: aborting due to 3 previous errors

For more information about this error, try `rustc --explain E0425`.
error: could not compile `teloxide`.

Remove 'RequestContext'

Bot and RequestContext have the same functionality.

  • Remove RequestContext;
  • Accept &Bot in requests instead of RequestContext;
  • Provide &self to the requests in the Bot's methods.

Dispatching

TODO:

  • Update listeners
    • Implement webhooks (probably under cargo-feature)
    • Design & implement stopping
  • Implement dispatchers

The design of teloxide::requests

Today's design isn't the best one, because when we call .clone() on requests, a bot is cloned, and we also cannot compare two requests, hash them and etc.

Rewrite `requests::` module

  • Remove Bot ref from requests
  • Rename requests to payloads
  • Add JsonPayload, SimplePayload, MultipartPayload traits
  • Add JsonRequest<'b, P>, SimpleRequest<'b, P>, MultipartRequest<'b, P> structs (that will hold payload + &'b Bot)
  • Implement setters for fields of payloads on <...>Request<Payload>
  • Implement .send() for <...>Request<'_, P> where P: <...>Payload
  • Reimplemet payloads (old requests)

How about using macro builders for requests?

We don't use macro builders for requests because it will harm the user experience with IDEs (usually IDEs don't highlight items produced by macros).

UPD: Intellij IDEA now supports a macro expansion engine.

Filters

We need to create the following filters:

  • MessageTextFilter;
  • MessageCaptionFilter;
  • MessageTextCaptionFilter;
  • CommandFilter;
  • RegExpFilter;
  • RegExpCommandFilter (which validates the command and matches the regex with arguments);
  • InlineQueryTextFilter;
  • CallbackQueryTextFilter;
  • filters for all available types of the message:
    NOTE: we can do this otherwise.
    • IsAudioFilter;
    • IsDocumentFilter;
    • IsPhotoFilter;
    • IsStickerFilter;
    • IsVideoFilter;
    • IsVoiceFilter;
    • etc
  • filters for all available actions in the chat which bot can see
    NOTE: we can do this otherwise.
    • NewChatMembersFilter;
    • NewChatTitleFilter;
    • DeleteChatPhotoFilter;
    • etc;
  • ChosenInlineResultQueryFilter;

Maybe need to create CommandStartFilter, CommandHelpFilter, CommandSettingsFilter, because these commands are used often.

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.