mirror of
https://github.com/teloxide/teloxide.git
synced 2024-12-22 22:46:39 +01:00
Merge branch 'dev' into fix-serde-export-master
This commit is contained in:
commit
dbc03c0769
77 changed files with 2014 additions and 521 deletions
25
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
25
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
|
@ -0,0 +1,25 @@
|
|||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
I tried this code:
|
||||
```rust
|
||||
<code>
|
||||
```
|
||||
I expected to see this happen: _explanation_
|
||||
|
||||
Instead, this happened: _explanation_
|
||||
|
||||
## Meta
|
||||
|
||||
- `teloxide` version: <!-- (e.g.: `0.3.1`) -->
|
||||
- rustc version:
|
||||
```
|
||||
<version>
|
||||
```
|
||||
<!-- use `rustc --version --verbose` to get it -->
|
22
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
22
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: 'Feature Request: <feature>'
|
||||
labels: feature-request
|
||||
assignees: Hirrolot, WaffleLapkin
|
||||
|
||||
---
|
||||
|
||||
<!-- Describe your idea here -->
|
||||
|
||||
### Pros
|
||||
|
||||
<!-- Describe good parts of your idea -->
|
||||
|
||||
### Cons
|
||||
|
||||
<!-- Describe bad parts of your idea -->
|
||||
|
||||
### Alternatives
|
||||
|
||||
<!-- Of any, describe alternatives here -->
|
30
.github/ISSUE_TEMPLATE/parse-error.md
vendored
Normal file
30
.github/ISSUE_TEMPLATE/parse-error.md
vendored
Normal file
|
@ -0,0 +1,30 @@
|
|||
---
|
||||
name: Parse error
|
||||
about: Report issue with `teloxide` parsing of telegram response
|
||||
title: 'Parse Error: <type or error description>'
|
||||
labels: FIXME, bug
|
||||
assignees: WaffleLapkin
|
||||
|
||||
---
|
||||
|
||||
When using `<...>` method I've got `RequestError::InvalidJson` error with the following description:
|
||||
```text
|
||||
<Description of the inner serde error here>
|
||||
```
|
||||
|
||||
## Steps to reproduce
|
||||
|
||||
<!-- Steps to reproduce the issue - get the same error -->
|
||||
|
||||
## Meta
|
||||
|
||||
- `teloxide` version: <!-- (e.g.: `0.3.1`) -->
|
||||
- rustc version:
|
||||
```
|
||||
<version>
|
||||
```
|
||||
<!-- use `rustc --version --verbose` to get it -->
|
||||
|
||||
### Additional context
|
||||
|
||||
<!-- Describe any additional context here, if needed-->
|
30
.github/ISSUE_TEMPLATE/unknown-telegram-error.md
vendored
Normal file
30
.github/ISSUE_TEMPLATE/unknown-telegram-error.md
vendored
Normal file
|
@ -0,0 +1,30 @@
|
|||
---
|
||||
name: Unknown telegram error
|
||||
about: You've found telegram error which is not known to teloxide
|
||||
title: 'Unknown Error: <error description>'
|
||||
labels: FIXME, bug, good first issue
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
When using `<...>` method I've got `ApiError::Unknown` error with the following description:
|
||||
```text
|
||||
<Description of the error here (an inner string of ApiError::Unknown)>
|
||||
```
|
||||
|
||||
## Steps to reproduce
|
||||
|
||||
<!-- Steps to reproduce the issue - get the same error -->
|
||||
|
||||
## Meta
|
||||
|
||||
- `teloxide` version: <!-- (e.g.: `0.3.1`) -->
|
||||
- rustc version:
|
||||
```
|
||||
<version>
|
||||
```
|
||||
<!-- use `rustc --version --verbose` to get it -->
|
||||
|
||||
### Additional context
|
||||
|
||||
<!-- Describe any additional context here, if needed-->
|
41
.github/workflows/ci.yml
vendored
41
.github/workflows/ci.yml
vendored
|
@ -3,7 +3,7 @@ on:
|
|||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
branches: [ master, dev ]
|
||||
|
||||
name: Continuous integration
|
||||
|
||||
|
@ -27,23 +27,52 @@ jobs:
|
|||
override: true
|
||||
- name: Cargo clippy
|
||||
run: cargo clippy --all --all-targets --all-features -- -D warnings
|
||||
stable-test:
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
rust:
|
||||
- stable
|
||||
- beta
|
||||
- nightly
|
||||
|
||||
include:
|
||||
- rust: stable
|
||||
features: "--features \"redis-storage sqlite-storage cbor-serializer bincode-serializer frunk-\""
|
||||
- rust: beta
|
||||
features: "--features \"redis-storage sqlite-storage cbor-serializer bincode-serializer frunk-\""
|
||||
- rust: nightly
|
||||
features: "--all-features"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
toolchain: ${{ matrix.rust }}
|
||||
override: true
|
||||
|
||||
- name: Build
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --verbose ${{ matrix.features }}
|
||||
|
||||
- name: Setup redis
|
||||
run: |
|
||||
sudo apt install redis-server
|
||||
redis-server --port 7777 > /dev/null &
|
||||
redis-server --port 7778 > /dev/null &
|
||||
redis-server --port 7779 > /dev/null &
|
||||
- name: Cargo test
|
||||
run: cargo test --all --all-features
|
||||
|
||||
- name: Test
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --verbose ${{ matrix.features }}
|
||||
|
||||
build-example:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
|
@ -65,5 +94,5 @@ jobs:
|
|||
profile: minimal
|
||||
toolchain: stable
|
||||
override: true
|
||||
- name: Test the example
|
||||
- name: Check the example
|
||||
run: cd examples && cd ${{ matrix.example }} && cargo check
|
||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -4,3 +4,4 @@ Cargo.lock
|
|||
.idea/
|
||||
.vscode/
|
||||
examples/*/target
|
||||
*.sqlite
|
||||
|
|
|
@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
### Added
|
||||
- Allow arbitrary error types to be returned from (sub)transitions ([issue 242](https://github.com/teloxide/teloxide/issues/242)).
|
||||
- The `respond` function, a shortcut for `ResponseResult::Ok(())`.
|
||||
- The `sqlite-storage` feature -- enables SQLite support.
|
||||
|
||||
### Changed
|
||||
- Allow `bot_name` be `N`, where `N: Into<String> + ...` in `commands_repl` & `commands_repl_with_listener`.
|
||||
|
|
|
@ -123,3 +123,4 @@ C: Into<String>, { ... }
|
|||
## Misc
|
||||
1. Use `Into<...>` only where there exists at least one conversion **and** it will be logically to use.
|
||||
2. Always mark a function as `#[must_use]` if its return value **must** be used.
|
||||
3. `Box::pin(async [move] { ... })` instead of `async [move] { ... }.boxed()`.
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
# Contributing
|
||||
|
||||
Before contributing, please read [our code style](https://github.com/teloxide/teloxide/blob/master/CODE_STYLE.md) and [the license](https://github.com/teloxide/teloxide/blob/master/LICENSE).
|
||||
|
||||
To change the source code, fork the `master` branch of this repository and work inside your own branch. Then send us a PR into `master` branch and wait for the CI to check everything. However, you'd better check changes first locally:
|
||||
To change the source code, fork the `dev` branch of this repository and work inside your own branch. Then send us a PR into `dev` branch and wait for the CI to check everything. However, you'd better check changes first locally:
|
||||
|
||||
```
|
||||
cargo clippy --all --all-features --all-targets
|
||||
cargo test --all
|
||||
cargo doc --open
|
||||
RUSTDOCFLAGS="--cfg docsrs" cargo doc --open --all-features
|
||||
# Using nightly rustfmt
|
||||
cargo +nightly fmt --all -- --check
|
||||
```
|
||||
|
|
26
Cargo.toml
26
Cargo.toml
|
@ -24,12 +24,17 @@ authors = [
|
|||
maintenance = { status = "actively-developed" }
|
||||
|
||||
[features]
|
||||
sqlite-storage = ["sqlx"]
|
||||
redis-storage = ["redis"]
|
||||
cbor-serializer = ["serde_cbor"]
|
||||
bincode-serializer = ["bincode"]
|
||||
|
||||
frunk- = ["frunk"]
|
||||
|
||||
macros = ["teloxide-macros"]
|
||||
|
||||
nightly = [] # currently used for `README.md` tests and building docs for `docsrs` to add `This is supported on feature="..." only.`
|
||||
|
||||
[dependencies]
|
||||
serde_json = "1.0.55"
|
||||
serde = { version = "1.0.114", features = ["derive"] }
|
||||
|
@ -50,12 +55,17 @@ futures = "0.3.5"
|
|||
pin-project = "0.4.22"
|
||||
serde_with_macros = "1.1.0"
|
||||
|
||||
sqlx = { version = "0.4.0-beta.1", optional = true, default-features = false, features = [
|
||||
"runtime-tokio-native-tls",
|
||||
"macros",
|
||||
"sqlite",
|
||||
] }
|
||||
redis = { version = "0.16.0", optional = true }
|
||||
serde_cbor = { version = "0.11.1", optional = true }
|
||||
bincode = { version = "1.3.1", optional = true }
|
||||
frunk = { version = "0.3.1", optional = true }
|
||||
|
||||
teloxide-macros = "0.3.2"
|
||||
teloxide-macros = { git = "https://github.com/teloxide/teloxide-macros", branch = "master", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
smart-default = "0.6.0"
|
||||
|
@ -63,3 +73,17 @@ rand = "0.7.3"
|
|||
pretty_env_logger = "0.4.0"
|
||||
lazy_static = "1.4.0"
|
||||
tokio = { version = "0.2.21", features = ["fs", "stream", "rt-threaded", "macros"] }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[[test]]
|
||||
name = "redis"
|
||||
path = "tests/redis.rs"
|
||||
required-features = ["redis-storage", "cbor-serializer", "bincode-serializer"]
|
||||
|
||||
[[test]]
|
||||
name = "sqlite"
|
||||
path = "tests/sqlite.rs"
|
||||
required-features = ["sqlite-storage", "cbor-serializer", "bincode-serializer"]
|
||||
|
|
130
README.md
130
README.md
|
@ -1,52 +1,55 @@
|
|||
<div align="center">
|
||||
<img src="ICON.png" width="250"/>
|
||||
<h1>teloxide</h1>
|
||||
|
||||
<a href="https://docs.rs/teloxide/">
|
||||
<img src="https://docs.rs/teloxide/badge.svg">
|
||||
</a>
|
||||
<a href="https://github.com/teloxide/teloxide/actions">
|
||||
<img src="https://github.com/teloxide/teloxide/workflows/Continuous%20integration/badge.svg">
|
||||
</a>
|
||||
<a href="https://teloxide.netlify.com">
|
||||
<img src="https://img.shields.io/badge/docs-dev-blue)">
|
||||
</a>
|
||||
<a href="https://crates.io/crates/teloxide">
|
||||
<img src="https://img.shields.io/crates/v/teloxide.svg">
|
||||
</a>
|
||||
<a href="https://core.telegram.org/bots/api">
|
||||
<img src="https://img.shields.io/badge/API coverage-Up to 0.4.9 (inclusively)-green.svg">
|
||||
</a>
|
||||
<a href="https://t.me/teloxide">
|
||||
<img src="https://img.shields.io/badge/official%20chat-t.me%2Fteloxide-blueviolet">
|
||||
</a>
|
||||
<a href="https://core.telegram.org/bots/api">
|
||||
<img src="https://img.shields.io/badge/API coverage-Up to 0.4.7 (inclusively)-green.svg">
|
||||
</a>
|
||||
|
||||
A full-featured framework that empowers you to easily build [Telegram bots](https://telegram.org/blog/bot-revolution) using the [`async`/`.await`](https://rust-lang.github.io/async-book/01_getting_started/01_chapter.html) syntax in [Rust](https://www.rust-lang.org/). It handles all the difficult stuff so you can focus only on your business logic.
|
||||
</div>
|
||||
|
||||
## Table of contents
|
||||
- [Highlights](https://github.com/teloxide/teloxide#highlights)
|
||||
- [Setting up your environment](https://github.com/teloxide/teloxide#setting-up-your-environment)
|
||||
- [API overview](https://github.com/teloxide/teloxide#api-overview)
|
||||
- [The dices bot](https://github.com/teloxide/teloxide#the-dices-bot)
|
||||
- [Commands](https://github.com/teloxide/teloxide#commands)
|
||||
- [Dialogues management](https://github.com/teloxide/teloxide#dialogues-management)
|
||||
- [Recommendations](https://github.com/teloxide/teloxide#recommendations)
|
||||
- [Cargo features](https://github.com/teloxide/teloxide#cargo-features)
|
||||
- [FAQ](https://github.com/teloxide/teloxide#faq)
|
||||
- [Community bots](https://github.com/teloxide/teloxide#community-bots)
|
||||
- [Contributing](https://github.com/teloxide/teloxide#contributing)
|
||||
- [Highlights](#highlights)
|
||||
- [Setting up your environment](#setting-up-your-environment)
|
||||
- [API overview](#api-overview)
|
||||
- [The dices bot](#the-dices-bot)
|
||||
- [Commands](#commands)
|
||||
- [Dialogues management](#dialogues-management)
|
||||
- [Recommendations](#recommendations)
|
||||
- [Cargo features](#cargo-features)
|
||||
- [FAQ](#faq)
|
||||
- [Community bots](#community-bots)
|
||||
- [Contributing](#contributing)
|
||||
|
||||
## Highlights
|
||||
|
||||
- **Functional reactive design.** teloxide has [functional reactive design], allowing you to declaratively manipulate streams of updates from Telegram using filters, maps, folds, zips, and a lot of [other adaptors].
|
||||
- **Functional reactive design.** teloxide follows [functional reactive design], allowing you to declaratively manipulate streams of updates from Telegram using filters, maps, folds, zips, and a lot of [other adaptors].
|
||||
|
||||
[functional reactive design]: https://en.wikipedia.org/wiki/Functional_reactive_programming
|
||||
[other adaptors]: https://docs.rs/futures/latest/futures/stream/trait.StreamExt.html
|
||||
|
||||
- **Persistence.** Dialogues management is independent of how/where dialogues are stored: you can just replace one line and make them [persistent]. Out-of-the-box storages include [Redis].
|
||||
- **Dialogues management subsystem.** We have designed our dialogues management subsystem to be easy-to-use, and, furthermore, to be 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].
|
||||
|
||||
[persistent]: https://en.wikipedia.org/wiki/Persistence_(computer_science)
|
||||
[persistence]: https://en.wikipedia.org/wiki/Persistence_(computer_science)
|
||||
[Redis]: https://redis.io/
|
||||
[Sqlite]: https://www.sqlite.org
|
||||
|
||||
- **Strongly typed bot commands.** You can describe bot commands as enumerations, and then they'll be automatically constructed from strings. Just like you describe JSON structures in [serde-json] and command-line arguments in [structopt].
|
||||
- **Strongly typed bot commands.** You can describe bot commands as enumerations, and then they'll be automatically constructed from strings — just like JSON structures in [serde-json] and command-line arguments in [structopt].
|
||||
|
||||
[structopt]: https://github.com/TeXitoi/structopt
|
||||
[serde-json]: https://github.com/serde-rs/json
|
||||
|
@ -62,7 +65,7 @@ $ export TELOXIDE_TOKEN=<Your token here>
|
|||
# Windows
|
||||
$ set TELOXIDE_TOKEN=<Your token here>
|
||||
```
|
||||
4. Be sure that you are up to date:
|
||||
4. Make sure that your Rust compiler is up to date:
|
||||
```bash
|
||||
# If you're using stable
|
||||
$ rustup update stable
|
||||
|
@ -73,7 +76,7 @@ $ rustup update nightly
|
|||
$ rustup override set nightly
|
||||
```
|
||||
|
||||
5. Execute `cargo new my_bot`, enter the directory and put these lines into your `Cargo.toml`:
|
||||
5. Run `cargo new my_bot`, enter the directory and put these lines into your `Cargo.toml`:
|
||||
```toml
|
||||
[dependencies]
|
||||
teloxide = "0.3"
|
||||
|
@ -88,10 +91,10 @@ tokio = { version = "0.2.11", features = ["rt-threaded", "macros"] }
|
|||
## API overview
|
||||
|
||||
### The dices bot
|
||||
This bot throws a dice on each incoming message:
|
||||
This bot replies with a dice throw to each received message:
|
||||
|
||||
([Full](https://github.com/teloxide/teloxide/blob/master/examples/dices_bot/src/main.rs))
|
||||
```rust
|
||||
([Full](./examples/dices_bot/src/main.rs))
|
||||
```rust,no_run
|
||||
use teloxide::prelude::*;
|
||||
|
||||
#[tokio::main]
|
||||
|
@ -107,12 +110,11 @@ async fn main() {
|
|||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
<div align="center">
|
||||
<kbd>
|
||||
<img src=https://github.com/teloxide/teloxide/raw/master/media/DICES_BOT.gif />
|
||||
<img src=../../raw/master/media/DICES_BOT.gif />
|
||||
</kbd>
|
||||
</div>
|
||||
|
||||
|
@ -126,9 +128,9 @@ Commands are strongly typed and defined declaratively, similar to how we define
|
|||
[structopt]: https://docs.rs/structopt/0.3.9/structopt/
|
||||
[serde-json]: https://github.com/serde-rs/json
|
||||
|
||||
([Full](https://github.com/teloxide/teloxide/blob/master/examples/simple_commands_bot/src/main.rs))
|
||||
```rust
|
||||
// Imports are omitted...
|
||||
([Full](./examples/simple_commands_bot/src/main.rs))
|
||||
```rust,no_run
|
||||
use teloxide::{utils::command::BotCommand, prelude::*};
|
||||
|
||||
#[derive(BotCommand)]
|
||||
#[command(rename = "lowercase", description = "These commands are supported:")]
|
||||
|
@ -162,25 +164,26 @@ async fn main() {
|
|||
|
||||
let bot = Bot::from_env();
|
||||
|
||||
teloxide::commands_repl(bot, panic!("Your bot's name here"), answer).await;
|
||||
let bot_name: String = panic!("Your bot's name here");
|
||||
teloxide::commands_repl(bot, bot_name, answer).await;
|
||||
}
|
||||
```
|
||||
|
||||
<div align="center">
|
||||
<kbd>
|
||||
<img src=https://github.com/teloxide/teloxide/raw/master/media/SIMPLE_COMMANDS_BOT.gif />
|
||||
<img src=../../raw/master/media/SIMPLE_COMMANDS_BOT.gif />
|
||||
</kbd>
|
||||
</div>
|
||||
|
||||
### Dialogues management
|
||||
A dialogue is described by an enumeration, where each variant is one of possible dialogue's states. There are also _subtransition functions_, which turn a dialogue from one state to another, thereby forming a [FSM].
|
||||
A dialogue is described by an enumeration where each variant is one of possible dialogue's states. There are also _subtransition functions_, which turn a dialogue from one state to another, thereby forming a [FSM].
|
||||
|
||||
[FSM]: https://en.wikipedia.org/wiki/Finite-state_machine
|
||||
|
||||
Below is a bot, which asks you three questions and then sends the answers back to you. First, let's start with an enumeration (a collection of our dialogue's states):
|
||||
Below is a bot that asks you three questions and then sends the answers back to you. First, let's start with an enumeration (a collection of our dialogue's states):
|
||||
|
||||
([dialogue_bot/src/dialogue/mod.rs](https://github.com/teloxide/teloxide/blob/master/examples/dialogue_bot/src/dialogue/mod.rs))
|
||||
```rust
|
||||
([dialogue_bot/src/dialogue/mod.rs](./examples/dialogue_bot/src/dialogue/mod.rs))
|
||||
```rust,ignore
|
||||
// Imports are omitted...
|
||||
|
||||
#[derive(Transition, From)]
|
||||
|
@ -198,13 +201,13 @@ impl Default for Dialogue {
|
|||
}
|
||||
```
|
||||
|
||||
When a user sends a message to our bot, and such a dialogue does not yet exist, `Dialogue::default()` is invoked, which is `Dialogue::Start`. Every time a message is received, an associated dialogue is extracted, and then passed to a corresponding subtransition function:
|
||||
When a user sends a message to our bot and such a dialogue does not exist yet, a `Dialogue::default()` is invoked, which is a `Dialogue::Start` in this case. Every time a message is received, an associated dialogue is extracted and then passed to a corresponding subtransition function:
|
||||
|
||||
<details>
|
||||
<summary>Dialogue::Start</summary>
|
||||
|
||||
([dialogue_bot/src/dialogue/states/start.rs](https://github.com/teloxide/teloxide/blob/master/examples/dialogue_bot/src/dialogue/states/start.rs))
|
||||
```rust
|
||||
([dialogue_bot/src/dialogue/states/start.rs](./examples/dialogue_bot/src/dialogue/states/start.rs))
|
||||
```rust,ignore
|
||||
// Imports are omitted...
|
||||
|
||||
pub struct StartState;
|
||||
|
@ -221,8 +224,8 @@ async fn start(_state: StartState, cx: TransitionIn, _ans: String) -> Transition
|
|||
<details>
|
||||
<summary>Dialogue::ReceiveFullName</summary>
|
||||
|
||||
([dialogue_bot/src/dialogue/states/receive_full_name.rs](https://github.com/teloxide/teloxide/blob/master/examples/dialogue_bot/src/dialogue/states/receive_full_name.rs))
|
||||
```rust
|
||||
([dialogue_bot/src/dialogue/states/receive_full_name.rs](./examples/dialogue_bot/src/dialogue/states/receive_full_name.rs))
|
||||
```rust,ignore
|
||||
// Imports are omitted...
|
||||
|
||||
#[derive(Generic)]
|
||||
|
@ -244,8 +247,8 @@ async fn receive_full_name(
|
|||
<details>
|
||||
<summary>Dialogue::ReceiveAge</summary>
|
||||
|
||||
([dialogue_bot/src/dialogue/states/receive_age.rs](https://github.com/teloxide/teloxide/blob/master/examples/dialogue_bot/src/dialogue/states/receive_age.rs))
|
||||
```rust
|
||||
([dialogue_bot/src/dialogue/states/receive_age.rs](./examples/dialogue_bot/src/dialogue/states/receive_age.rs))
|
||||
```rust,ignore
|
||||
// Imports are omitted...
|
||||
|
||||
#[derive(Generic)]
|
||||
|
@ -277,8 +280,8 @@ async fn receive_age_state(
|
|||
<details>
|
||||
<summary>Dialogue::ReceiveLocation</summary>
|
||||
|
||||
([dialogue_bot/src/dialogue/states/receive_location.rs](https://github.com/teloxide/teloxide/blob/master/examples/dialogue_bot/src/dialogue/states/receive_location.rs))
|
||||
```rust
|
||||
([dialogue_bot/src/dialogue/states/receive_location.rs](./examples/dialogue_bot/src/dialogue/states/receive_location.rs))
|
||||
```rust,ignore
|
||||
// Imports are omitted...
|
||||
|
||||
#[derive(Generic)]
|
||||
|
@ -301,12 +304,12 @@ async fn receive_location(
|
|||
|
||||
</details>
|
||||
|
||||
All these subtransitions accept a corresponding state (one of the many variants of `Dialogue`), a context, and a textual message. They return `TransitionOut<Dialogue>`, e.g. a mapping from `<your state type>` to `Dialogue`.
|
||||
All these subtransition functions accept a corresponding state (one of the many variants of `Dialogue`), a context, and a textual message. They return `TransitionOut<Dialogue>`, e.g. a mapping from `<your state type>` to `Dialogue`.
|
||||
|
||||
Finally, the `main` function looks like this:
|
||||
|
||||
([dialogue_bot/src/main.rs](https://github.com/teloxide/teloxide/blob/master/examples/dialogue_bot/src/main.rs))
|
||||
```rust
|
||||
([dialogue_bot/src/main.rs](./examples/dialogue_bot/src/main.rs))
|
||||
```rust,ignore
|
||||
// Imports are omitted...
|
||||
|
||||
#[tokio::main]
|
||||
|
@ -335,11 +338,11 @@ async fn handle_message(cx: UpdateWithCx<Message>, dialogue: Dialogue) -> Transi
|
|||
|
||||
<div align="center">
|
||||
<kbd>
|
||||
<img src=https://github.com/teloxide/teloxide/raw/master/media/DIALOGUE_BOT.gif />
|
||||
<img src=../../raw/master/media/DIALOGUE_BOT.gif />
|
||||
</kbd>
|
||||
</div>
|
||||
|
||||
[More examples!](https://github.com/teloxide/teloxide/tree/master/examples)
|
||||
[More examples!](./examples)
|
||||
|
||||
## Recommendations
|
||||
- Use this pattern:
|
||||
|
@ -364,11 +367,12 @@ async fn handle_message(cx: UpdateWithCx<Message>, dialogue: Dialogue) -> Transi
|
|||
}
|
||||
```
|
||||
|
||||
The second one produces very strange compiler messages because of the `#[tokio::main]` macro. However, the examples in this README use the second variant for brevity.
|
||||
The second one produces very strange compiler messages due to the `#[tokio::main]` macro. However, the examples in this README use the second variant for brevity.
|
||||
|
||||
## Cargo features
|
||||
|
||||
- `redis-storage` -- enables the [Redis] support.
|
||||
- `sqlite-storage` -- enables the [Sqlite] support.
|
||||
- `cbor-serializer` -- enables the [CBOR] serializer for dialogues.
|
||||
- `bincode-serializer` -- enables the [Bincode] serializer for dialogues.
|
||||
- `frunk` -- enables [`teloxide::utils::UpState`], which allows mapping from a structure of `field1, ..., fieldN` to a structure of `field1, ..., fieldN, fieldN+1`.
|
||||
|
@ -378,7 +382,7 @@ The second one produces very strange compiler messages because of the `#[tokio::
|
|||
[`teloxide::utils::UpState`]: https://docs.rs/teloxide/latest/teloxide/utils/trait.UpState.html
|
||||
|
||||
## FAQ
|
||||
Q: Where I can ask questions?
|
||||
**Q: Where I can ask questions?**
|
||||
|
||||
A: [Issues](https://github.com/teloxide/teloxide/issues) is a good place for well-formed questions, for example, about:
|
||||
|
||||
|
@ -389,27 +393,27 @@ A: [Issues](https://github.com/teloxide/teloxide/issues) is a good place for wel
|
|||
|
||||
If you can't compile your bot due to compilation errors and need quick help, feel free to ask in [our official Telegram group](https://t.me/teloxide).
|
||||
|
||||
Q: Do you support the Telegram API for clients?
|
||||
**Q: Do you support the Telegram API for clients?**
|
||||
|
||||
A: No, only the bots API.
|
||||
|
||||
Q: Why Rust?
|
||||
**Q: Why Rust?**
|
||||
|
||||
A: Most programming languages have their own implementations of Telegram bots frameworks, so why not Rust? We think Rust provides enough good ecosystem and the language itself to be suitable for writing bots.
|
||||
A: Most programming languages have their own implementations of Telegram bots frameworks, so why not Rust? We think Rust provides a good enough ecosystem and the language for it to be suitable for writing bots.
|
||||
|
||||
UPD: The current design spreads wide and deep trait bounds, thereby increasing cognitive complexity. It can be avoided using [mux-stream], but currently the stable Rust channel doesn't support necessary features to use [mux-stream] conveniently. What is even more interesting is that [mux-stream] could make a library from teloxide, not a framework, since the design could be defined by just combining streams of updates.
|
||||
UPD: The current design relies on wide and deep trait bounds, thereby increasing cognitive complexity. It can be avoided using [mux-stream], but currently the stable Rust channel doesn't support necessary features to use [mux-stream] conveniently. Furthermore, the [mux-stream] could help to make a library out of teloxide, not a framework, since the design in this case could be defined by just combining streams of updates.
|
||||
|
||||
[mux-stream]: https://github.com/Hirrolot/mux-stream
|
||||
|
||||
Q: Can I use webhooks?
|
||||
**Q: Can I use webhooks?**
|
||||
|
||||
A: teloxide doesn't provide special API for working with webhooks due to their nature with lots of subtle settings. Instead, you setup your webhook by yourself, as shown in [`examples/ngrok_ping_pong_bot`](examples/ngrok_ping_pong_bot/src/main.rs) and [`examples/heroku_ping_pong_bot`](examples/heroku_ping_pong_bot/src/main.rs).
|
||||
A: teloxide doesn't provide special API for working with webhooks due to their nature with lots of subtle settings. Instead, you should setup your webhook by yourself, as shown in [`examples/ngrok_ping_pong_bot`](./examples/ngrok_ping_pong_bot/src/main.rs) and [`examples/heroku_ping_pong_bot`](./examples/heroku_ping_pong_bot/src/main.rs).
|
||||
|
||||
Associated links:
|
||||
- [Marvin's Marvellous Guide to All Things Webhook](https://core.telegram.org/bots/webhooks)
|
||||
- [Using self-signed certificates](https://core.telegram.org/bots/self-signed)
|
||||
|
||||
Q: Can I use different loggers?
|
||||
**Q: Can I use different loggers?**
|
||||
|
||||
A: Yes. You can setup any logger, for example, [fern], e.g. teloxide has no specific requirements as it depends only on [log]. Remember that [`enable_logging!`] and [`enable_logging_with_filter!`] are just **optional** utilities.
|
||||
|
||||
|
@ -421,9 +425,13 @@ A: Yes. You can setup any logger, for example, [fern], e.g. teloxide has no spec
|
|||
## Community bots
|
||||
Feel free to push your own bot into our collection!
|
||||
|
||||
- [_Rust subreddit reader_](https://github.com/steadylearner/Rust-Full-Stack/tree/master/commits/teloxide/subreddit_reader)
|
||||
- [_vzmuinebot -- Telegram bot for food menu navigate_](https://github.com/ArtHome12/vzmuinebot)
|
||||
- [_Tepe -- A CLI to command a bot to send messages and files over Telegram_](https://lib.rs/crates/tepe)
|
||||
- [_steadylearner/subreddit_reader_](https://github.com/steadylearner/Rust-Full-Stack/tree/master/commits/teloxide/subreddit_reader)
|
||||
- [_ArtHome12/vzmuinebot -- Telegram bot for food menu navigate_](https://github.com/ArtHome12/vzmuinebot)
|
||||
- [_Hermitter/tepe -- A CLI to command a bot to send messages and files over Telegram_](https://github.com/Hermitter/tepe)
|
||||
- [_ArtHome12/cognito_bot -- The bot is designed to anonymize messages to a group_](https://github.com/ArtHome12/cognito_bot)
|
||||
- [_GoldsteinE/tg-vimhelpbot -- Link `:help` for Vim in Telegram_](https://github.com/GoldsteinE/tg-vimhelpbot)
|
||||
- [_sschiz/janitor-bot_ -- A bot that removes users trying to join to a chat that is designed for comments](https://github.com/sschiz/janitor-bot)
|
||||
- [ myblackbeard/basketball-betting-bot -- The bot lets you bet on NBA games against your buddies](https://github.com/myblackbeard/basketball-betting-bot)
|
||||
|
||||
## Contributing
|
||||
See [CONRIBUTING.md](https://github.com/teloxide/teloxide/blob/master/CONTRIBUTING.md).
|
||||
|
|
|
@ -2,11 +2,11 @@
|
|||
Just enter the directory (for example, `cd dialogue_bot`) and execute `cargo run` to run an example. Don't forget to initialise the `TELOXIDE_TOKEN` environmental variable.
|
||||
| Bot | Description |
|
||||
|---|-----------|
|
||||
| [dices_bot](dices_bot) | This bot throws a dice on each incoming message. |
|
||||
| [dices_bot](dices_bot) | Throws a dice on each incoming message. |
|
||||
| [ngrok_ping_pong_bot](ngrok_ping_pong_bot) | The ngrok version of ping-pong-bot that uses webhooks. |
|
||||
| [heroku_ping_pong_bot](heroku_ping_pong_bot) | The Heroku version of ping-pong-bot that uses webhooks. |
|
||||
| [simple_commands_bot](simple_commands_bot) | Shows how to deal with bot's commands. |
|
||||
| [guess_a_number_bot](guess_a_number_bot) | The "guess a number" game. |
|
||||
| [dialogue_bot](dialogue_bot) | Drive a dialogue with a user using a type-safe finite automaton. |
|
||||
| [admin_bot](admin_bot) | A bot, which can ban, kick, and mute on a command. |
|
||||
| [shared_state_bot](shared_state_bot) | A bot that shows how to deal with shared state. |
|
||||
| [redis_remember_bot](redis_remember_bot) | Uses `RedisStorage` instead of `InMemStorage`. |
|
||||
| [dialogue_bot](dialogue_bot) | How to deal with dialogues. |
|
||||
| [admin_bot](admin_bot) | Ban, kick, and mute on a command. |
|
||||
| [shared_state_bot](shared_state_bot) | How to deal with shared state. |
|
||||
|
|
|
@ -10,7 +10,7 @@ edition = "2018"
|
|||
log = "0.4.8"
|
||||
pretty_env_logger = "0.4.0"
|
||||
tokio = { version = "0.2.11", features = ["rt-threaded", "macros"] }
|
||||
teloxide = { path = "../../" }
|
||||
teloxide = { path = "../../", features = ["macros"] }
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
|
@ -139,5 +139,6 @@ async fn run() {
|
|||
|
||||
let bot = Bot::from_env();
|
||||
|
||||
teloxide::commands_repl(bot, panic!("Your bot's name here"), action).await;
|
||||
let bot_name: String = panic!("Your bot's name here");
|
||||
teloxide::commands_repl(bot, bot_name, action).await;
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ futures = "0.3.5"
|
|||
tokio = { version = "0.2.11", features = ["rt-threaded", "macros"] }
|
||||
|
||||
teloxide = { path = "../../", features = ["frunk"] }
|
||||
teloxide-macros = "0.3.2"
|
||||
teloxide-macros = { git = "https://github.com/teloxide/teloxide-macros", branch = "master" }
|
||||
|
||||
derive_more = "0.99.9"
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ async fn run() {
|
|||
|
||||
teloxide::repl(bot, |message| async move {
|
||||
message.answer_dice().send().await?;
|
||||
ResponseResult::<()>::Ok(())
|
||||
respond(())
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ tokio = { version = "0.2.11", features = ["rt-threaded", "macros"] }
|
|||
|
||||
# You can also choose "cbor-serializer" or built-in JSON serializer
|
||||
teloxide = { path = "../../", features = ["redis-storage", "bincode-serializer"] }
|
||||
teloxide-macros = "0.3.2"
|
||||
teloxide-macros = { git = "https://github.com/teloxide/teloxide-macros", branch = "master" }
|
||||
|
||||
serde = "1.0.104"
|
||||
futures = "0.3.5"
|
||||
|
|
|
@ -10,4 +10,4 @@ edition = "2018"
|
|||
log = "0.4.8"
|
||||
pretty_env_logger = "0.4.0"
|
||||
tokio = { version = "0.2.11", features = ["rt-threaded", "macros"] }
|
||||
teloxide = { path = "../../" }
|
||||
teloxide = { path = "../../", features = ["macros"] }
|
||||
|
|
|
@ -36,5 +36,6 @@ async fn run() {
|
|||
|
||||
let bot = Bot::from_env();
|
||||
|
||||
teloxide::commands_repl(bot, panic!("Your bot's name here"), answer).await;
|
||||
let bot_name: String = panic!("Your bot's name here");
|
||||
teloxide::commands_repl(bot, bot_name, answer).await;
|
||||
}
|
||||
|
|
20
examples/sqlite_remember_bot/Cargo.toml
Normal file
20
examples/sqlite_remember_bot/Cargo.toml
Normal file
|
@ -0,0 +1,20 @@
|
|||
[package]
|
||||
name = "sqlite_remember_bot"
|
||||
version = "0.1.0"
|
||||
authors = ["Maximilian Siling <mouse-art@ya.ru>", "Sergey Levitin <selevit@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
log = "0.4.8"
|
||||
pretty_env_logger = "0.4.0"
|
||||
tokio = { version = "0.2.11", features = ["rt-threaded", "macros"] }
|
||||
|
||||
# You can also choose "cbor-serializer" or built-in JSON serializer
|
||||
teloxide = { path = "../../", features = ["sqlite-storage", "bincode-serializer", "redis-storage"] }
|
||||
teloxide-macros = { git = "https://github.com/teloxide/teloxide-macros", branch = "master" }
|
||||
|
||||
serde = "1.0.104"
|
||||
futures = "0.3.5"
|
||||
|
||||
thiserror = "1.0.15"
|
||||
derive_more = "0.99.9"
|
50
examples/sqlite_remember_bot/src/main.rs
Normal file
50
examples/sqlite_remember_bot/src/main.rs
Normal file
|
@ -0,0 +1,50 @@
|
|||
#[macro_use]
|
||||
extern crate derive_more;
|
||||
|
||||
mod states;
|
||||
mod transitions;
|
||||
|
||||
use states::*;
|
||||
|
||||
use teloxide::{
|
||||
dispatching::dialogue::{serializer::JSON, SqliteStorage, Storage},
|
||||
prelude::*,
|
||||
};
|
||||
use thiserror::Error;
|
||||
|
||||
type StorageError = <SqliteStorage<JSON> as Storage<Dialogue>>::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
enum Error {
|
||||
#[error("error from Telegram: {0}")]
|
||||
TelegramError(#[from] RequestError),
|
||||
#[error("error from storage: {0}")]
|
||||
StorageError(#[from] StorageError),
|
||||
}
|
||||
|
||||
type In = DialogueWithCx<Message, Dialogue, StorageError>;
|
||||
|
||||
async fn handle_message(cx: UpdateWithCx<Message>, dialogue: Dialogue) -> TransitionOut<Dialogue> {
|
||||
match cx.update.text_owned() {
|
||||
None => {
|
||||
cx.answer_str("Send me a text message.").await?;
|
||||
next(dialogue)
|
||||
}
|
||||
Some(ans) => dialogue.react(cx, ans).await,
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let bot = Bot::from_env();
|
||||
Dispatcher::new(bot)
|
||||
.messages_handler(DialogueDispatcher::with_storage(
|
||||
|DialogueWithCx { cx, dialogue }: In| async move {
|
||||
let dialogue = dialogue.expect("std::convert::Infallible");
|
||||
handle_message(cx, dialogue).await.expect("Something wrong with the bot!")
|
||||
},
|
||||
SqliteStorage::open("db.sqlite", JSON).await.unwrap(),
|
||||
))
|
||||
.dispatch()
|
||||
.await;
|
||||
}
|
23
examples/sqlite_remember_bot/src/states.rs
Normal file
23
examples/sqlite_remember_bot/src/states.rs
Normal file
|
@ -0,0 +1,23 @@
|
|||
use teloxide_macros::Transition;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Transition, From, Serialize, Deserialize)]
|
||||
pub enum Dialogue {
|
||||
Start(StartState),
|
||||
HaveNumber(HaveNumberState),
|
||||
}
|
||||
|
||||
impl Default for Dialogue {
|
||||
fn default() -> Self {
|
||||
Self::Start(StartState)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct StartState;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct HaveNumberState {
|
||||
pub number: i32,
|
||||
}
|
35
examples/sqlite_remember_bot/src/transitions.rs
Normal file
35
examples/sqlite_remember_bot/src/transitions.rs
Normal file
|
@ -0,0 +1,35 @@
|
|||
use teloxide::prelude::*;
|
||||
use teloxide_macros::teloxide;
|
||||
|
||||
use super::states::*;
|
||||
|
||||
#[teloxide(subtransition)]
|
||||
async fn start(state: StartState, cx: TransitionIn, ans: String) -> TransitionOut<Dialogue> {
|
||||
if let Ok(number) = ans.parse() {
|
||||
cx.answer_str(format!("Remembered number {}. Now use /get or /reset", number)).await?;
|
||||
next(HaveNumberState { number })
|
||||
} else {
|
||||
cx.answer_str("Please, send me a number").await?;
|
||||
next(state)
|
||||
}
|
||||
}
|
||||
|
||||
#[teloxide(subtransition)]
|
||||
async fn have_number(
|
||||
state: HaveNumberState,
|
||||
cx: TransitionIn,
|
||||
ans: String,
|
||||
) -> TransitionOut<Dialogue> {
|
||||
let num = state.number;
|
||||
|
||||
if ans.starts_with("/get") {
|
||||
cx.answer_str(format!("Here is your number: {}", num)).await?;
|
||||
next(state)
|
||||
} else if ans.starts_with("/reset") {
|
||||
cx.answer_str("Resetted number").await?;
|
||||
next(StartState)
|
||||
} else {
|
||||
cx.answer_str("Please, send /get or /reset").await?;
|
||||
next(state)
|
||||
}
|
||||
}
|
8
netlify.toml
Normal file
8
netlify.toml
Normal file
|
@ -0,0 +1,8 @@
|
|||
[build]
|
||||
command = "rustup install nightly --profile minimal && cargo +nightly doc --all-features --no-deps && cp -r target/doc _netlify_out"
|
||||
environment = { RUSTDOCFLAGS= "--cfg docsrs" }
|
||||
publish = "_netlify_out"
|
||||
|
||||
[[redirects]]
|
||||
from = "/"
|
||||
to = "/teloxide"
|
507
src/bot/api.rs
507
src/bot/api.rs
|
@ -2,26 +2,27 @@ use crate::{
|
|||
requests::{
|
||||
AddStickerToSet, AnswerCallbackQuery, AnswerInlineQuery, AnswerPreCheckoutQuery,
|
||||
AnswerShippingQuery, CreateNewStickerSet, DeleteChatPhoto, DeleteChatStickerSet,
|
||||
DeleteMessage, DeleteStickerFromSet, DeleteWebhook, EditMessageCaption,
|
||||
EditMessageLiveLocation, EditMessageMedia, EditMessageReplyMarkup, EditMessageText,
|
||||
ExportChatInviteLink, ForwardMessage, GetChat, GetChatAdministrators, GetChatMember,
|
||||
GetChatMembersCount, GetFile, GetGameHighScores, GetMe, GetMyCommands, GetStickerSet,
|
||||
GetUpdates, GetUserProfilePhotos, GetWebhookInfo, KickChatMember, LeaveChat,
|
||||
PinChatMessage, PromoteChatMember, RestrictChatMember, SendAnimation, SendAudio,
|
||||
SendChatAction, SendChatActionKind, SendContact, SendDice, SendDocument, SendGame,
|
||||
SendInvoice, SendLocation, SendMediaGroup, SendMessage, SendPhoto, SendPoll, SendSticker,
|
||||
SendVenue, SendVideo, SendVideoNote, SendVoice, SetChatAdministratorCustomTitle,
|
||||
SetChatDescription, SetChatPermissions, SetChatPhoto, SetChatStickerSet, SetChatTitle,
|
||||
SetGameScore, SetMyCommands, SetStickerPositionInSet, SetStickerSetThumb, SetWebhook,
|
||||
StopMessageLiveLocation, StopPoll, UnbanChatMember, UnpinChatMessage, UploadStickerFile,
|
||||
DeleteMessage, DeleteStickerFromSet, DeleteWebhook, EditInlineMessageCaption,
|
||||
EditInlineMessageLiveLocation, EditInlineMessageMedia, EditInlineMessageReplyMarkup,
|
||||
EditInlineMessageText, EditMessageCaption, EditMessageLiveLocation, EditMessageMedia,
|
||||
EditMessageReplyMarkup, EditMessageText, ExportChatInviteLink, ForwardMessage, GetChat,
|
||||
GetChatAdministrators, GetChatMember, GetChatMembersCount, GetFile, GetGameHighScores,
|
||||
GetMe, GetMyCommands, GetStickerSet, GetUpdates, GetUserProfilePhotos, GetWebhookInfo,
|
||||
KickChatMember, LeaveChat, PinChatMessage, PromoteChatMember, RestrictChatMember,
|
||||
SendAnimation, SendAudio, SendChatAction, SendChatActionKind, SendContact, SendDice,
|
||||
SendDocument, SendGame, SendInvoice, SendLocation, SendMediaGroup, SendMessage, SendPhoto,
|
||||
SendPoll, SendSticker, SendVenue, SendVideo, SendVideoNote, SendVoice,
|
||||
SetChatAdministratorCustomTitle, SetChatDescription, SetChatPermissions, SetChatPhoto,
|
||||
SetChatStickerSet, SetChatTitle, SetGameScore, SetMyCommands, SetStickerPositionInSet,
|
||||
SetStickerSetThumb, SetWebhook, StopInlineMessageLiveLocation, StopMessageLiveLocation,
|
||||
StopPoll, UnbanChatMember, UnpinChatMessage, UploadStickerFile,
|
||||
},
|
||||
types::{
|
||||
BotCommand, ChatId, ChatOrInlineMessage, ChatPermissions, InlineQueryResult, InputFile,
|
||||
InputMedia, LabeledPrice, ParseMode, StickerType,
|
||||
BotCommand, ChatId, ChatPermissions, InlineQueryResult, InputFile, InputMedia,
|
||||
LabeledPrice, ParseMode, StickerType, TargetMessage,
|
||||
},
|
||||
Bot,
|
||||
};
|
||||
use std::ops::Deref;
|
||||
|
||||
impl Bot {
|
||||
/// Use this method to receive incoming updates using long polling ([wiki]).
|
||||
|
@ -155,16 +156,17 @@ impl Bot {
|
|||
/// # Params
|
||||
/// - `chat_id`: Unique identifier for the target chat or username of the
|
||||
/// target supergroup or channel (in the format `@channelusername`).
|
||||
/// - `photo`: Photo to send.
|
||||
/// - `photo`: Pass [`InputFile::FileId`] to send a photo that exists on
|
||||
/// the Telegram servers (recommended), pass an [`InputFile::Url`] for
|
||||
/// Telegram to get a photo from the Internet (5MB max.), pass
|
||||
/// [`InputFile::File`] to upload a picture from the file system or
|
||||
/// [`InputFile::Memory`] to upload a photo from memory (10MB max.
|
||||
/// each). [More info on Sending Files »].
|
||||
///
|
||||
/// Pass [`InputFile::File`] to send a photo that exists on
|
||||
/// the Telegram servers (recommended), pass an [`InputFile::Url`] for
|
||||
/// Telegram to get a .webp file from the Internet, or upload a new one
|
||||
/// using [`InputFile::FileId`]. [More info on Sending Files »].
|
||||
///
|
||||
/// [`InputFile::File`]: crate::types::InputFile::File
|
||||
/// [`InputFile::Url`]: crate::types::InputFile::Url
|
||||
/// [`InputFile::FileId`]: crate::types::InputFile::FileId
|
||||
/// [`InputFile::Url`]: crate::types::InputFile::Url
|
||||
/// [`InputFile::File`]: crate::types::InputFile::File
|
||||
/// [`InputFile::Memory`]: crate::types::InputFile::Memory
|
||||
///
|
||||
/// [More info on Sending Files »]: https://core.telegram.org/bots/api#sending-files
|
||||
///
|
||||
|
@ -183,11 +185,26 @@ impl Bot {
|
|||
)
|
||||
}
|
||||
|
||||
/// Use this method to send audio files
|
||||
///
|
||||
/// [The official docs](https://core.telegram.org/bots/api#sendaudio).
|
||||
///
|
||||
/// # Params
|
||||
/// - `chat_id`: Unique identifier for the target chat or username of the
|
||||
/// target supergroup or channel (in the format `@channelusername`).
|
||||
/// - `audio`: Pass [`InputFile::FileId`] to send an audio file that
|
||||
/// exists on the Telegram servers (recommended), pass an
|
||||
/// [`InputFile::Url`] for Telegram to get a file from the Internet
|
||||
/// (20MB max.), pass [`InputFile::File`] to upload a file from the file
|
||||
/// system or [`InputFile::Memory`] to upload a file from memory (50MB
|
||||
/// max. each). [More info on Sending Files »].
|
||||
///
|
||||
/// [`InputFile::FileId`]: crate::types::InputFile::FileId
|
||||
/// [`InputFile::Url`]: crate::types::InputFile::Url
|
||||
/// [`InputFile::File`]: crate::types::InputFile::File
|
||||
/// [`InputFile::Memory`]: crate::types::InputFile::Memory
|
||||
///
|
||||
/// [More info on Sending Files »]: https://core.telegram.org/bots/api#sending-files
|
||||
///
|
||||
/// # Notes
|
||||
/// Uses [a default parse mode] if specified in [`BotBuilder`].
|
||||
|
@ -206,20 +223,22 @@ impl Bot {
|
|||
|
||||
/// Use this method to send general files.
|
||||
///
|
||||
/// Bots can currently send files of any type of up to 50 MB in size, this
|
||||
/// limit may be changed in the future.
|
||||
///
|
||||
/// [The official docs](https://core.telegram.org/bots/api#senddocument).
|
||||
///
|
||||
/// # Params
|
||||
/// - `chat_id`: Unique identifier for the target chat or username of the
|
||||
/// target supergroup or channel (in the format `@channelusername`).
|
||||
/// - `document`: File to send.
|
||||
/// - `document`: Pass [`InputFile::FileId`] to send a file that exists on
|
||||
/// the Telegram servers (recommended), pass an [`InputFile::Url`] for
|
||||
/// Telegram to get a file from the Internet (20MB max.), pass
|
||||
/// [`InputFile::File`] to upload a file from the file system or
|
||||
/// [`InputFile::Memory`] to upload a file from memory (50MB max. each).
|
||||
/// [More info on Sending Files »].
|
||||
///
|
||||
/// Pass a file_id as String to send a file that exists on the
|
||||
/// Telegram servers (recommended), pass an HTTP URL as a String for
|
||||
/// Telegram to get a file from the Internet, or upload a new one using
|
||||
/// `multipart/form-data`. [More info on Sending Files »].
|
||||
/// [`InputFile::FileId`]: crate::types::InputFile::FileId
|
||||
/// [`InputFile::Url`]: crate::types::InputFile::Url
|
||||
/// [`InputFile::File`]: crate::types::InputFile::File
|
||||
/// [`InputFile::Memory`]: crate::types::InputFile::Memory
|
||||
///
|
||||
/// [More info on Sending Files »]: https://core.telegram.org/bots/api#sending-files
|
||||
///
|
||||
|
@ -241,24 +260,24 @@ impl Bot {
|
|||
/// Use this method to send video files, Telegram clients support mp4 videos
|
||||
/// (other formats may be sent as Document).
|
||||
///
|
||||
/// Bots can currently send video files of up to 50 MB in size, this
|
||||
/// limit may be changed in the future.
|
||||
///
|
||||
/// [The official docs](https://core.telegram.org/bots/api#sendvideo).
|
||||
///
|
||||
/// # Params
|
||||
/// - `chat_id`: Unique identifier for the target chat or username of the
|
||||
/// target supergroup or channel (in the format `@channelusername`).
|
||||
/// - `video`: Video to sent.
|
||||
/// - `video`: Pass [`InputFile::FileId`] to send a file that exists on
|
||||
/// the Telegram servers (recommended), pass an [`InputFile::Url`] for
|
||||
/// Telegram to get a file from the Internet (20MB max.), pass
|
||||
/// [`InputFile::File`] to upload a file from the file system or
|
||||
/// [`InputFile::Memory`] to upload a file from memory (50MB max. each).
|
||||
/// [More info on Sending Files »].
|
||||
///
|
||||
/// Pass [`InputFile::File`] to send a file that exists on
|
||||
/// the Telegram servers (recommended), pass an [`InputFile::Url`] for
|
||||
/// Telegram to get a .webp file from the Internet, or upload a new one
|
||||
/// using [`InputFile::FileId`]. [More info on Sending Files »].
|
||||
///
|
||||
/// [`InputFile::File`]: crate::types::InputFile::File
|
||||
/// [`InputFile::Url`]: crate::types::InputFile::Url
|
||||
/// [`InputFile::FileId`]: crate::types::InputFile::FileId
|
||||
/// [`InputFile::Url`]: crate::types::InputFile::Url
|
||||
/// [`InputFile::File`]: crate::types::InputFile::File
|
||||
/// [`InputFile::Memory`]: crate::types::InputFile::Memory
|
||||
///
|
||||
/// [More info on Sending Files »]: https://core.telegram.org/bots/api#sending-files
|
||||
///
|
||||
/// # Notes
|
||||
/// Uses [a default parse mode] if specified in [`BotBuilder`].
|
||||
|
@ -278,15 +297,24 @@ impl Bot {
|
|||
/// Use this method to send animation files (GIF or H.264/MPEG-4 AVC video
|
||||
/// without sound).
|
||||
///
|
||||
/// Bots can currently send animation files of up to 50 MB in size, this
|
||||
/// limit may be changed in the future.
|
||||
///
|
||||
/// [The official docs](https://core.telegram.org/bots/api#sendanimation).
|
||||
///
|
||||
/// # Params
|
||||
/// - `chat_id`: Unique identifier for the target chat or username of the
|
||||
/// target supergroup or channel (in the format `@channelusername`).
|
||||
/// - `animation`: Animation to send.
|
||||
/// - `animation`: Pass [`InputFile::FileId`] to send a file that exists
|
||||
/// on the Telegram servers (recommended), pass an [`InputFile::Url`]
|
||||
/// for Telegram to get a file from the Internet (20MB max.), pass
|
||||
/// [`InputFile::File`] to upload a file from the file system or
|
||||
/// [`InputFile::Memory`] to upload a file from memory (50MB max. each).
|
||||
/// [More info on Sending Files »].
|
||||
///
|
||||
/// [`InputFile::FileId`]: crate::types::InputFile::FileId
|
||||
/// [`InputFile::Url`]: crate::types::InputFile::Url
|
||||
/// [`InputFile::File`]: crate::types::InputFile::File
|
||||
/// [`InputFile::Memory`]: crate::types::InputFile::Memory
|
||||
///
|
||||
/// [More info on Sending Files »]: https://core.telegram.org/bots/api#sending-files
|
||||
///
|
||||
/// # Notes
|
||||
/// Uses [a default parse mode] if specified in [`BotBuilder`].
|
||||
|
@ -307,9 +335,7 @@ impl Bot {
|
|||
/// display the file as a playable voice message.
|
||||
///
|
||||
/// For this to work, your audio must be in an .ogg file encoded with OPUS
|
||||
/// (other formats may be sent as [`Audio`] or [`Document`]). Bots can
|
||||
/// currently send voice messages of up to 50 MB in size, this limit may
|
||||
/// be changed in the future.
|
||||
/// (other formats may be sent as [`Audio`] or [`Document`]).
|
||||
///
|
||||
/// [The official docs](https://core.telegram.org/bots/api#sendvoice).
|
||||
///
|
||||
|
@ -319,16 +345,18 @@ impl Bot {
|
|||
/// # Params
|
||||
/// - `chat_id`: Unique identifier for the target chat or username of the
|
||||
/// target supergroup or channel (in the format `@channelusername`).
|
||||
/// - `voice`: Audio file to send.
|
||||
/// - `voice`: Pass [`InputFile::FileId`] to send a file that exists on
|
||||
/// the Telegram servers (recommended), pass an [`InputFile::Url`] for
|
||||
/// Telegram to get a file from the Internet (20MB max.), pass
|
||||
/// [`InputFile::File`] to upload a file from the file system or
|
||||
/// [`InputFile::Memory`] to upload a file from memory (50MB max. each).
|
||||
/// [More info on Sending Files »].
|
||||
///
|
||||
/// Pass [`InputFile::File`] to send a file that exists on
|
||||
/// the Telegram servers (recommended), pass an [`InputFile::Url`] for
|
||||
/// Telegram to get a .webp file from the Internet, or upload a new one
|
||||
/// using [`InputFile::FileId`]. [More info on Sending Files »].
|
||||
///
|
||||
/// [`InputFile::File`]: crate::types::InputFile::File
|
||||
/// [`InputFile::Url`]: crate::types::InputFile::Url
|
||||
/// [`InputFile::FileId`]: crate::types::InputFile::FileId
|
||||
/// [`InputFile::Url`]: crate::types::InputFile::Url
|
||||
/// [`InputFile::File`]: crate::types::InputFile::File
|
||||
/// [`InputFile::Memory`]: crate::types::InputFile::Memory
|
||||
///
|
||||
/// [More info on Sending Files »]: https://core.telegram.org/bots/api#sending-files
|
||||
///
|
||||
/// # Notes
|
||||
|
@ -351,20 +379,23 @@ impl Bot {
|
|||
///
|
||||
/// [The official docs](https://core.telegram.org/bots/api#sendvideonote).
|
||||
///
|
||||
/// [v.4.0]: https://telegram.org/blog/video-messages-and-telescope
|
||||
///
|
||||
/// # Params
|
||||
/// - `chat_id`: Unique identifier for the target chat or username of the
|
||||
/// target supergroup or channel (in the format `@channelusername`).
|
||||
/// - `video_note`: Video note to send.
|
||||
/// - `video_note`: Pass [`InputFile::FileId`] to send a file that exists
|
||||
/// on the Telegram servers (recommended), pass an [`InputFile::Url`]
|
||||
/// for Telegram to get a file from the Internet (20MB max.), pass
|
||||
/// [`InputFile::File`] to upload a file from the file system or
|
||||
/// [`InputFile::Memory`] to upload a file from memory (50MB max. each).
|
||||
/// [More info on Sending Files »].
|
||||
///
|
||||
/// Pass [`InputFile::File`] to send a file that exists on the Telegram
|
||||
/// servers (recommended), pass an [`InputFile::Url`] for Telegram to get a
|
||||
/// .webp file from the Internet, or upload a new one using
|
||||
/// [`InputFile::FileId`]. [More info on Sending Files »].
|
||||
///
|
||||
/// [v.4.0]: https://telegram.org/blog/video-messages-and-telescope
|
||||
/// [`InputFile::File`]: crate::types::InputFile::File
|
||||
/// [`InputFile::Url`]: crate::types::InputFile::Url
|
||||
/// [`InputFile::FileId`]: crate::types::InputFile::FileId
|
||||
/// [`InputFile::Url`]: crate::types::InputFile::Url
|
||||
/// [`InputFile::File`]: crate::types::InputFile::File
|
||||
/// [`InputFile::Memory`]: crate::types::InputFile::Memory
|
||||
///
|
||||
/// [More info on Sending Files »]: https://core.telegram.org/bots/api#sending-files
|
||||
|
||||
pub fn send_video_note<C>(&self, chat_id: C, video_note: InputFile) -> SendVideoNote
|
||||
|
@ -381,8 +412,8 @@ impl Bot {
|
|||
/// # Params
|
||||
/// - `chat_id`: Unique identifier for the target chat or username of the
|
||||
/// target supergroup or channel (in the format `@channelusername`).
|
||||
/// - `media`: A JSON-serialized array describing photos and videos to be
|
||||
/// sent, must include 2–10 items.
|
||||
/// - `media`: A vector of photos and videos as [`InputMedia`] to be sent,
|
||||
/// must include 2–10 items.
|
||||
pub fn send_media_group<C, M>(&self, chat_id: C, media: M) -> SendMediaGroup
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
|
@ -410,42 +441,89 @@ impl Bot {
|
|||
/// Use this method to edit live location messages.
|
||||
///
|
||||
/// A location can be edited until its live_period expires or editing is
|
||||
/// explicitly disabled by a call to stopMessageLiveLocation. On success, if
|
||||
/// the edited message was sent by the bot, the edited [`Message`] is
|
||||
/// returned, otherwise [`True`] is returned.
|
||||
/// explicitly disabled by a call to stopMessageLiveLocation. On success,
|
||||
/// the edited [`Message`] is returned.
|
||||
///
|
||||
/// [The official docs](https://core.telegram.org/bots/api#editmessagelivelocation).
|
||||
///
|
||||
/// [`Message`]: crate::types::Message
|
||||
///
|
||||
/// # Params
|
||||
/// - `latitude`: Latitude of new location.
|
||||
/// - `longitude`: Longitude of new location.
|
||||
///
|
||||
/// [`Message`]: crate::types::Message
|
||||
/// [`True`]: crate::types::True
|
||||
pub fn edit_message_live_location(
|
||||
pub fn edit_message_live_location<C>(
|
||||
&self,
|
||||
chat_or_inline_message: ChatOrInlineMessage,
|
||||
chat_id: C,
|
||||
message_id: i32,
|
||||
latitude: f32,
|
||||
longitude: f32,
|
||||
) -> EditMessageLiveLocation {
|
||||
EditMessageLiveLocation::new(self.clone(), chat_or_inline_message, latitude, longitude)
|
||||
) -> EditMessageLiveLocation
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
{
|
||||
EditMessageLiveLocation::new(self.clone(), chat_id, message_id, latitude, longitude)
|
||||
}
|
||||
|
||||
/// Use this method to edit live location messages sent via the bot.
|
||||
///
|
||||
/// A location can be edited until its live_period expires or editing is
|
||||
/// explicitly disabled by a call to stopMessageLiveLocation. On success,
|
||||
/// [`True`] is returned.
|
||||
///
|
||||
/// [The official docs](https://core.telegram.org/bots/api#editmessagelivelocation).
|
||||
///
|
||||
/// [`True`]: crate::types::True
|
||||
///
|
||||
/// # Params
|
||||
/// - `latitude`: Latitude of new location.
|
||||
/// - `longitude`: Longitude of new location.
|
||||
pub fn edit_inline_message_live_location<I>(
|
||||
&self,
|
||||
inline_message_id: I,
|
||||
latitude: f32,
|
||||
longitude: f32,
|
||||
) -> EditInlineMessageLiveLocation
|
||||
where
|
||||
I: Into<String>,
|
||||
{
|
||||
EditInlineMessageLiveLocation::new(self.clone(), inline_message_id, latitude, longitude)
|
||||
}
|
||||
|
||||
/// Use this method to stop updating a live location message before
|
||||
/// `live_period` expires.
|
||||
///
|
||||
/// On success, if the message was sent by the bot, the sent [`Message`] is
|
||||
/// returned, otherwise [`True`] is returned.
|
||||
/// On success, the sent [`Message`] is returned.
|
||||
///
|
||||
/// [The official docs](https://core.telegram.org/bots/api#stopmessagelivelocation).
|
||||
///
|
||||
/// [`Message`]: crate::types::Message
|
||||
/// [`True`]: crate::types::True
|
||||
pub fn stop_message_live_location(
|
||||
pub fn stop_message_live_location<C>(
|
||||
&self,
|
||||
chat_or_inline_message: ChatOrInlineMessage,
|
||||
) -> StopMessageLiveLocation {
|
||||
StopMessageLiveLocation::new(self.clone(), chat_or_inline_message)
|
||||
chat_id: C,
|
||||
message_id: i32,
|
||||
) -> StopMessageLiveLocation
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
{
|
||||
StopMessageLiveLocation::new(self.clone(), chat_id, message_id)
|
||||
}
|
||||
|
||||
/// Use this method to stop updating a live location message (sent via the
|
||||
/// bot) before `live_period` expires.
|
||||
///
|
||||
/// On success, [`True`] is returned.
|
||||
///
|
||||
/// [The official docs](https://core.telegram.org/bots/api#stopmessagelivelocation).
|
||||
///
|
||||
/// [`True`]: crate::types::True
|
||||
pub fn stop_inline_message_live_location<I>(
|
||||
&self,
|
||||
inline_message_id: I,
|
||||
) -> StopInlineMessageLiveLocation
|
||||
where
|
||||
I: Into<String>,
|
||||
{
|
||||
StopInlineMessageLiveLocation::new(self.clone(), inline_message_id)
|
||||
}
|
||||
|
||||
/// Use this method to send information about a venue.
|
||||
|
@ -743,7 +821,14 @@ impl Bot {
|
|||
/// # Params
|
||||
/// - `chat_id`: Unique identifier for the target chat or username of the
|
||||
/// target supergroup or channel (in the format `@channelusername`).
|
||||
/// - `photo`: New chat photo, uploaded using `multipart/form-data`.
|
||||
/// - `photo`: New chat photo, pass [`InputFile::File`] to upload a file
|
||||
/// from the file system or [`InputFile::Memory`] to upload a file from
|
||||
/// memory (10MB max. each). [More info on Sending Files »].
|
||||
///
|
||||
/// [`InputFile::File`]: crate::types::InputFile::File
|
||||
/// [`InputFile::Memory`]: crate::types::InputFile::Memory
|
||||
///
|
||||
/// [More info on Sending Files »]: https://core.telegram.org/bots/api#sending-files
|
||||
pub fn set_chat_photo<C>(&self, chat_id: C, photo: InputFile) -> SetChatPhoto
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
|
@ -983,45 +1068,105 @@ impl Bot {
|
|||
|
||||
/// Use this method to edit text and game messages.
|
||||
///
|
||||
/// On success, if edited message is sent by the bot, the edited [`Message`]
|
||||
/// is returned, otherwise [`True`] is returned.
|
||||
/// On success, the edited [`Message`] is returned.
|
||||
///
|
||||
/// [The official docs](https://core.telegram.org/bots/api#editmessagetext).
|
||||
///
|
||||
/// # Params
|
||||
/// - New text of the message.
|
||||
///
|
||||
/// [`Message`]: crate::types::Message
|
||||
/// [`True`]: crate::types::True
|
||||
///
|
||||
/// # Params
|
||||
///
|
||||
/// - `chat_id`: Unique identifier for the target chat or username of the
|
||||
/// target channel (in the format `@channelusername`).
|
||||
/// - `message_id`: Identifier of the message to edit.
|
||||
/// - `text`: New text of the message.
|
||||
///
|
||||
/// # Notes
|
||||
///
|
||||
/// Uses [a default parse mode] if specified in [`BotBuilder`].
|
||||
///
|
||||
/// [a default parse mode]: crate::BotBuilder::parse_mode
|
||||
/// [`BotBuilder`]: crate::BotBuilder
|
||||
pub fn edit_message_text<T>(
|
||||
&self,
|
||||
chat_or_inline_message: ChatOrInlineMessage,
|
||||
text: T,
|
||||
) -> EditMessageText
|
||||
pub fn edit_message_text<C, T>(&self, chat_id: C, message_id: i32, text: T) -> EditMessageText
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
T: Into<String>,
|
||||
{
|
||||
match self.parse_mode.deref() {
|
||||
None => EditMessageText::new(self.clone(), chat_or_inline_message, text),
|
||||
Some(parse_mode) => EditMessageText::new(self.clone(), chat_or_inline_message, text)
|
||||
.parse_mode(*parse_mode.deref()),
|
||||
match self.parse_mode {
|
||||
None => EditMessageText::new(self.clone(), chat_id, message_id, text),
|
||||
Some(parse_mode) => {
|
||||
EditMessageText::new(self.clone(), chat_id, message_id, text).parse_mode(parse_mode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Use this method to edit captions of messages.
|
||||
/// Use this method to edit text and game messages sent via the bot.
|
||||
///
|
||||
/// On success, if edited message is sent by the bot, the edited [`Message`]
|
||||
/// is returned, otherwise [`True`] is returned.
|
||||
/// On success, [`True`] is returned.
|
||||
///
|
||||
/// [The official docs](https://core.telegram.org/bots/api#editmessagetext).
|
||||
///
|
||||
/// [`True`]: crate::types::True
|
||||
///
|
||||
/// # Params
|
||||
///
|
||||
/// - `inline_message_id`: Identifier of the inline message.
|
||||
/// - `text`: New text of the message.
|
||||
///
|
||||
/// # Notes
|
||||
///
|
||||
/// Uses [a default parse mode] if specified in [`BotBuilder`].
|
||||
///
|
||||
/// [a default parse mode]: crate::BotBuilder::parse_mode
|
||||
/// [`BotBuilder`]: crate::BotBuilder
|
||||
pub fn edit_inline_message_text<I, T>(
|
||||
&self,
|
||||
inline_message_id: I,
|
||||
text: T,
|
||||
) -> EditInlineMessageText
|
||||
where
|
||||
I: Into<String>,
|
||||
T: Into<String>,
|
||||
{
|
||||
match self.parse_mode {
|
||||
None => EditInlineMessageText::new(self.clone(), inline_message_id, text),
|
||||
Some(parse_mode) => EditInlineMessageText::new(self.clone(), inline_message_id, text)
|
||||
.parse_mode(parse_mode),
|
||||
}
|
||||
}
|
||||
|
||||
/// Use this method to edit captions of messages sent via the bot.
|
||||
///
|
||||
/// On success, [`True`] is returned.
|
||||
///
|
||||
/// [The official docs](https://core.telegram.org/bots/api#editmessagecaption).
|
||||
///
|
||||
/// [`True`]: crate::types::True
|
||||
///
|
||||
/// # Notes
|
||||
///
|
||||
/// Uses [a default parse mode] if specified in [`BotBuilder`].
|
||||
///
|
||||
/// [a default parse mode]: crate::BotBuilder::parse_mode
|
||||
/// [`BotBuilder`]: crate::BotBuilder
|
||||
pub fn edit_message_caption<C>(&self, chat_id: C, message_id: i32) -> EditMessageCaption
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
{
|
||||
match self.parse_mode {
|
||||
None => EditMessageCaption::new(self.clone(), chat_id, message_id),
|
||||
Some(parse_mode) => {
|
||||
EditMessageCaption::new(self.clone(), chat_id, message_id).parse_mode(parse_mode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Use this method to edit captions of messages sent via the bot.
|
||||
///
|
||||
/// On success, [`True`] is returned.
|
||||
///
|
||||
/// [The official docs](https://core.telegram.org/bots/api#editmessagecaption).
|
||||
///
|
||||
/// [`Message`]: crate::types::Message
|
||||
/// [`True`]: crate::types::True
|
||||
///
|
||||
/// # Notes
|
||||
|
@ -1029,14 +1174,14 @@ impl Bot {
|
|||
///
|
||||
/// [a default parse mode]: crate::BotBuilder::parse_mode
|
||||
/// [`BotBuilder`]: crate::BotBuilder
|
||||
pub fn edit_message_caption(
|
||||
&self,
|
||||
chat_or_inline_message: ChatOrInlineMessage,
|
||||
) -> EditMessageCaption {
|
||||
match self.parse_mode.deref() {
|
||||
None => EditMessageCaption::new(self.clone(), chat_or_inline_message),
|
||||
Some(parse_mode) => EditMessageCaption::new(self.clone(), chat_or_inline_message)
|
||||
.parse_mode(*parse_mode.deref()),
|
||||
pub fn edit_inline_message_caption<I>(&self, inline_message_id: I) -> EditInlineMessageCaption
|
||||
where
|
||||
I: Into<String>,
|
||||
{
|
||||
match self.parse_mode {
|
||||
None => EditInlineMessageCaption::new(self.clone(), inline_message_id),
|
||||
Some(parse_mode) => EditInlineMessageCaption::new(self.clone(), inline_message_id)
|
||||
.parse_mode(parse_mode),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1045,37 +1190,81 @@ impl Bot {
|
|||
///
|
||||
/// If a message is a part of a message album, then it can be edited only to
|
||||
/// a photo or a video. Otherwise, message type can be changed
|
||||
/// arbitrarily. When inline message is edited, new file can't be
|
||||
/// uploaded. Use previously uploaded file via its `file_id` or specify
|
||||
/// a URL. On success, if the edited message was sent by the bot, the
|
||||
/// edited [`Message`] is returned, otherwise [`True`] is returned.
|
||||
/// arbitrarily. On success, the edited [`Message`] is returned.
|
||||
///
|
||||
/// [The official docs](https://core.telegram.org/bots/api#editmessagemedia).
|
||||
///
|
||||
/// [`Message`]: crate::types::Message
|
||||
/// [`True`]: crate::types::True
|
||||
pub fn edit_message_media(
|
||||
pub fn edit_message_media<C>(
|
||||
&self,
|
||||
chat_or_inline_message: ChatOrInlineMessage,
|
||||
chat_id: C,
|
||||
message_id: i32,
|
||||
media: InputMedia,
|
||||
) -> EditMessageMedia {
|
||||
EditMessageMedia::new(self.clone(), chat_or_inline_message, media)
|
||||
) -> EditMessageMedia
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
{
|
||||
EditMessageMedia::new(self.clone(), chat_id, message_id, media)
|
||||
}
|
||||
|
||||
/// Use this method to edit animation, audio, document, photo, or video
|
||||
/// messages sent via the bot.
|
||||
///
|
||||
/// If a message is a part of a message album, then it can be edited only to
|
||||
/// a photo or a video. Otherwise, message type can be changed
|
||||
/// arbitrarily. When this method is used, new file can't be uploaded.
|
||||
/// Use previously uploaded file via its `file_id` or specify a URL. On
|
||||
/// success, [`True`] is returned.
|
||||
///
|
||||
/// [The official docs](https://core.telegram.org/bots/api#editmessagemedia).
|
||||
///
|
||||
/// [`True`]: crate::types::True
|
||||
pub fn edit_inline_message_media<I>(
|
||||
&self,
|
||||
inline_message_id: I,
|
||||
media: InputMedia,
|
||||
) -> EditInlineMessageMedia
|
||||
where
|
||||
I: Into<String>,
|
||||
{
|
||||
EditInlineMessageMedia::new(self.clone(), inline_message_id, media)
|
||||
}
|
||||
|
||||
/// Use this method to edit only the reply markup of messages.
|
||||
///
|
||||
/// On success, if edited message is sent by the bot, the edited [`Message`]
|
||||
/// is returned, otherwise [`True`] is returned.
|
||||
/// On success, the edited [`Message`] is returned.
|
||||
///
|
||||
/// [The official docs](https://core.telegram.org/bots/api#editmessagereplymarkup).
|
||||
///
|
||||
/// [`Message`]: crate::types::Message
|
||||
pub fn edit_message_reply_markup<C>(
|
||||
&self,
|
||||
chat_id: C,
|
||||
message_id: i32,
|
||||
) -> EditMessageReplyMarkup
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
{
|
||||
EditMessageReplyMarkup::new(self.clone(), chat_id, message_id)
|
||||
}
|
||||
|
||||
/// Use this method to edit only the reply markup of messages sent via the
|
||||
/// bot.
|
||||
///
|
||||
/// On success, [`True`] is returned.
|
||||
///
|
||||
/// [The official docs](https://core.telegram.org/bots/api#editmessagereplymarkup).
|
||||
///
|
||||
/// [`Message`]: crate::types::Message
|
||||
/// [`True`]: crate::types::True
|
||||
pub fn edit_message_reply_markup(
|
||||
pub fn edit_inline_message_reply_markup<I>(
|
||||
&self,
|
||||
chat_or_inline_message: ChatOrInlineMessage,
|
||||
) -> EditMessageReplyMarkup {
|
||||
EditMessageReplyMarkup::new(self.clone(), chat_or_inline_message)
|
||||
inline_message_id: I,
|
||||
) -> EditInlineMessageReplyMarkup
|
||||
where
|
||||
I: Into<String>,
|
||||
{
|
||||
EditInlineMessageReplyMarkup::new(self.clone(), inline_message_id)
|
||||
}
|
||||
|
||||
/// Use this method to stop a poll which was sent by the bot.
|
||||
|
@ -1125,20 +1314,23 @@ impl Bot {
|
|||
///
|
||||
/// [The official docs](https://core.telegram.org/bots/api#sendsticker).
|
||||
///
|
||||
/// [animated]: https://telegram.org/blog/animated-stickers
|
||||
///
|
||||
/// # Params
|
||||
/// - `chat_id`: Unique identifier for the target chat or username of the
|
||||
/// target channel (in the format `@channelusername`).
|
||||
/// - `sticker`: Sticker to send.
|
||||
/// - `sticker`: Pass [`InputFile::FileId`] to send a sticker that exists
|
||||
/// on the Telegram servers (recommended), pass an [`InputFile::Url`]
|
||||
/// for Telegram to get a sticker (.WEBP file) from the Internet, pass
|
||||
/// [`InputFile::File`] to upload a sticker from the file system or
|
||||
/// [`InputFile::Memory`] to upload a sticker from memory [More info on
|
||||
/// Sending Files »].
|
||||
///
|
||||
/// Pass [`InputFile::File`] to send a file that exists on the Telegram
|
||||
/// servers (recommended), pass an [`InputFile::Url`] for Telegram to get a
|
||||
/// .webp file from the Internet, or upload a new one using
|
||||
/// [`InputFile::FileId`]. [More info on Sending Files »].
|
||||
///
|
||||
/// [animated]: https://telegram.org/blog/animated-stickers
|
||||
/// [`InputFile::File`]: crate::types::InputFile::File
|
||||
/// [`InputFile::Url`]: crate::types::InputFile::Url
|
||||
/// [`InputFile::FileId`]: crate::types::InputFile::FileId
|
||||
/// [`InputFile::Url`]: crate::types::InputFile::Url
|
||||
/// [`InputFile::File`]: crate::types::InputFile::File
|
||||
/// [`InputFile::Memory`]: crate::types::InputFile::Memory
|
||||
///
|
||||
/// [More info on Sending Files »]: https://core.telegram.org/bots/api#sending-files
|
||||
pub fn send_sticker<C>(&self, chat_id: C, sticker: InputFile) -> SendSticker
|
||||
where
|
||||
|
@ -1170,12 +1362,14 @@ impl Bot {
|
|||
/// - `user_id`: User identifier of sticker file owner.
|
||||
/// - `png_sticker`: **Png** image with the sticker, must be up to 512
|
||||
/// kilobytes in size, dimensions must not exceed 512px, and either
|
||||
/// width or height must be exactly 512px. [More info on Sending Files
|
||||
/// »].
|
||||
/// width or height must be exactly 512px. Pass [`InputFile::File`] to
|
||||
/// upload a file from the file system or [`InputFile::Memory`] to
|
||||
/// upload a file from memory. [More info on Sending Files »].
|
||||
///
|
||||
/// [`InputFile::File`]: crate::types::InputFile::File
|
||||
/// [`InputFile::Memory`]: crate::types::InputFile::Memory
|
||||
///
|
||||
/// [More info on Sending Files »]: https://core.telegram.org/bots/api#sending-files
|
||||
/// [`Bot::create_new_sticker_set`]: crate::Bot::create_new_sticker_set
|
||||
/// [`Bot::add_sticker_to_set`]: crate::Bot::add_sticker_to_set
|
||||
pub fn upload_sticker_file(&self, user_id: i32, png_sticker: InputFile) -> UploadStickerFile {
|
||||
UploadStickerFile::new(self.clone(), user_id, png_sticker)
|
||||
}
|
||||
|
@ -1413,18 +1607,18 @@ impl Bot {
|
|||
/// [The official docs](https://core.telegram.org/bots/api#setgamescore).
|
||||
///
|
||||
/// # Params
|
||||
/// - `target`: Target message, either chat id and message id or inline
|
||||
/// message id.
|
||||
/// - `user_id`: User identifier.
|
||||
/// - `score`: New score, must be non-negative.
|
||||
///
|
||||
/// [`Message`]: crate::types::Message
|
||||
/// [`True`]: crate::types::True
|
||||
pub fn set_game_score(
|
||||
&self,
|
||||
chat_or_inline_message: ChatOrInlineMessage,
|
||||
user_id: i32,
|
||||
score: i32,
|
||||
) -> SetGameScore {
|
||||
SetGameScore::new(self.clone(), chat_or_inline_message, user_id, score)
|
||||
pub fn set_game_score<T>(&self, target: T, user_id: i32, score: i32) -> SetGameScore
|
||||
where
|
||||
T: Into<TargetMessage>,
|
||||
{
|
||||
SetGameScore::new(self.clone(), target, user_id, score)
|
||||
}
|
||||
|
||||
/// Use this method to get data for high score tables.
|
||||
|
@ -1441,13 +1635,14 @@ impl Bot {
|
|||
/// [The official docs](https://core.telegram.org/bots/api#getgamehighscores).
|
||||
///
|
||||
/// # Params
|
||||
/// - `target`: Target message, either chat id and message id or inline
|
||||
/// message id.
|
||||
/// - `user_id`: Target user id.
|
||||
pub fn get_game_high_scores(
|
||||
&self,
|
||||
chat_or_inline_message: ChatOrInlineMessage,
|
||||
user_id: i32,
|
||||
) -> GetGameHighScores {
|
||||
GetGameHighScores::new(self.clone(), chat_or_inline_message, user_id)
|
||||
pub fn get_game_high_scores<T>(&self, target: T, user_id: i32) -> GetGameHighScores
|
||||
where
|
||||
T: Into<TargetMessage>,
|
||||
{
|
||||
GetGameHighScores::new(self.clone(), target, user_id)
|
||||
}
|
||||
|
||||
/// Use this method to set a custom title for an administrator in a
|
||||
|
@ -1530,9 +1725,9 @@ impl Bot {
|
|||
builder: Builder,
|
||||
f: fn(Builder, ParseMode) -> Builder,
|
||||
) -> Builder {
|
||||
match self.parse_mode.deref() {
|
||||
match self.parse_mode {
|
||||
None => builder,
|
||||
Some(parse_mode) => f(builder, *parse_mode.deref()),
|
||||
Some(parse_mode) => f(builder, parse_mode),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,6 +56,8 @@ impl Bot {
|
|||
/// [`tokio::fs::File`]: tokio::fs::File
|
||||
/// [`Bot::download_file`]: crate::Bot::download_file
|
||||
#[cfg(feature = "unstable-stream")]
|
||||
// FIXME(waffle): use `docsrs` here when issue with combine is resolved <https://github.com/teloxide/teloxide/pull/305#issuecomment-716172103>
|
||||
#[cfg_attr(all(teloxide_docsrs, feature = "nightly"), doc(cfg(feature = "unstable-stream")))]
|
||||
pub async fn download_file_stream(
|
||||
&self,
|
||||
path: &str,
|
||||
|
|
|
@ -20,7 +20,7 @@ pub(crate) const TELOXIDE_PROXY: &str = "TELOXIDE_PROXY";
|
|||
pub struct Bot {
|
||||
token: Arc<str>,
|
||||
client: Client,
|
||||
parse_mode: Arc<Option<ParseMode>>,
|
||||
parse_mode: Option<ParseMode>,
|
||||
}
|
||||
|
||||
impl Bot {
|
||||
|
@ -41,8 +41,7 @@ impl Bot {
|
|||
/// client.
|
||||
///
|
||||
/// # Panics
|
||||
/// - If cannot get the `TELOXIDE_TOKEN` and `TELOXIDE_PROXY` environmental
|
||||
/// variables.
|
||||
/// - If cannot get the `TELOXIDE_TOKEN` environmental variable.
|
||||
/// - If it cannot create [`reqwest::Client`].
|
||||
///
|
||||
/// [`reqwest::Client`]: https://docs.rs/reqwest/0.10.1/reqwest/struct.Client.html
|
||||
|
@ -106,7 +105,7 @@ impl Bot {
|
|||
Self {
|
||||
token: Into::<Arc<str>>::into(Into::<String>::into(token)),
|
||||
client,
|
||||
parse_mode: Arc::new(None),
|
||||
parse_mode: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -231,11 +230,11 @@ impl BotBuilder {
|
|||
///
|
||||
/// This method will attempt to build a new client with a proxy, specified
|
||||
/// in the `TELOXIDE_PROXY` (passed into [`reqwest::Proxy::all`])
|
||||
/// environmental variable, if a client haven't been specified.
|
||||
/// environmental variable, if a client haven't been specified. If
|
||||
/// `TELOXIDE_PROXY` is unspecified, it'll use no proxy.
|
||||
///
|
||||
/// # Panics
|
||||
/// - If cannot get the `TELOXIDE_TOKEN` and `TELOXIDE_PROXY` environmental
|
||||
/// variables.
|
||||
/// - If cannot get the `TELOXIDE_TOKEN` environmental variable.
|
||||
/// - If it cannot create [`reqwest::Client`].
|
||||
///
|
||||
/// [`reqwest::Client`]: https://docs.rs/reqwest/0.10.1/reqwest/struct.Client.html
|
||||
|
@ -247,7 +246,7 @@ impl BotBuilder {
|
|||
Bot {
|
||||
client: self.client.unwrap_or_else(crate::utils::client_from_env),
|
||||
token: self.token.unwrap_or_else(|| get_env(TELOXIDE_TOKEN)).into(),
|
||||
parse_mode: Arc::new(self.parse_mode),
|
||||
parse_mode: self.parse_mode,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ use crate::dispatching::{
|
|||
};
|
||||
use std::{convert::Infallible, marker::PhantomData};
|
||||
|
||||
use futures::{future::BoxFuture, StreamExt};
|
||||
use futures::{future::BoxFuture, FutureExt, StreamExt};
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
use lockfree::map::Map;
|
||||
|
@ -137,28 +137,30 @@ where
|
|||
{
|
||||
let this = Arc::new(self);
|
||||
|
||||
Box::pin(updates.for_each(move |cx| {
|
||||
let this = Arc::clone(&this);
|
||||
let chat_id = cx.update.chat_id();
|
||||
updates
|
||||
.for_each(move |cx| {
|
||||
let this = Arc::clone(&this);
|
||||
let chat_id = cx.update.chat_id();
|
||||
|
||||
match this.senders.get(&chat_id) {
|
||||
// An old dialogue
|
||||
Some(tx) => {
|
||||
if tx.1.send(cx).is_err() {
|
||||
panic!("We are not dropping a receiver or call .close() on it",);
|
||||
match this.senders.get(&chat_id) {
|
||||
// An old dialogue
|
||||
Some(tx) => {
|
||||
if tx.1.send(cx).is_err() {
|
||||
panic!("We are not dropping a receiver or call .close() on it",);
|
||||
}
|
||||
}
|
||||
None => {
|
||||
let tx = this.new_tx();
|
||||
if tx.send(cx).is_err() {
|
||||
panic!("We are not dropping a receiver or call .close() on it",);
|
||||
}
|
||||
this.senders.insert(chat_id, tx);
|
||||
}
|
||||
}
|
||||
None => {
|
||||
let tx = this.new_tx();
|
||||
if tx.send(cx).is_err() {
|
||||
panic!("We are not dropping a receiver or call .close() on it",);
|
||||
}
|
||||
this.senders.insert(chat_id, tx);
|
||||
}
|
||||
}
|
||||
|
||||
async {}
|
||||
}))
|
||||
async {}
|
||||
})
|
||||
.boxed()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ pub enum DialogueStage<D> {
|
|||
///
|
||||
/// [`From`]: std::convert::From
|
||||
/// [derive-more]: https://crates.io/crates/derive_more
|
||||
pub fn next<Dialogue, State>(new_state: State) -> TransitionOut<Dialogue>
|
||||
pub fn next<Dialogue, State, E>(new_state: State) -> TransitionOut<Dialogue, E>
|
||||
where
|
||||
Dialogue: From<State>,
|
||||
{
|
||||
|
@ -32,6 +32,6 @@ where
|
|||
///
|
||||
/// See [the module-level documentation for the design
|
||||
/// overview](crate::dispatching::dialogue).
|
||||
pub fn exit<D>() -> TransitionOut<D> {
|
||||
pub fn exit<D, E>() -> TransitionOut<D, E> {
|
||||
Ok(DialogueStage::Exit)
|
||||
}
|
||||
|
|
|
@ -30,16 +30,16 @@
|
|||
//! skeleton should look like:
|
||||
//!
|
||||
//! ```no_run
|
||||
//! # #[cfg(feature = "macros")] {
|
||||
//! use std::convert::Infallible;
|
||||
//!
|
||||
//! use teloxide::prelude::*;
|
||||
//! use teloxide_macros::{teloxide, Transition};
|
||||
//! use teloxide::{dispatching::dialogue::Transition, prelude::*, teloxide};
|
||||
//!
|
||||
//! struct _1State;
|
||||
//! struct _2State;
|
||||
//! struct _3State;
|
||||
//!
|
||||
//! type Out = TransitionOut<D>;
|
||||
//! type Out = TransitionOut<D, RequestError>;
|
||||
//!
|
||||
//! #[teloxide(subtransition)]
|
||||
//! async fn _1_transition(_state: _1State, _cx: TransitionIn) -> Out {
|
||||
|
@ -97,6 +97,7 @@
|
|||
//! .dispatch()
|
||||
//! .await;
|
||||
//! }
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! - `#[teloxide(subtransition)]` implements [`Subtransition`] for the first
|
||||
|
@ -156,7 +157,17 @@ pub use transition::{
|
|||
Subtransition, SubtransitionOutputType, Transition, TransitionIn, TransitionOut,
|
||||
};
|
||||
|
||||
#[cfg(feature = "macros")]
|
||||
// FIXME(waffle): use `docsrs` here when issue with combine is resolved <https://github.com/teloxide/teloxide/pull/305#issuecomment-716172103>
|
||||
#[cfg_attr(all(teloxide_docsrs, feature = "nightly"), doc(cfg(feature = "macros")))]
|
||||
pub use teloxide_macros::Transition;
|
||||
|
||||
#[cfg(feature = "redis-storage")]
|
||||
// FIXME(waffle): use `docsrs` here when issue with combine is resolved <https://github.com/teloxide/teloxide/pull/305#issuecomment-716172103>
|
||||
#[cfg_attr(all(teloxide_docsrs, feature = "nightly"), doc(cfg(feature = "redis-storage")))]
|
||||
pub use storage::{RedisStorage, RedisStorageError};
|
||||
|
||||
pub use storage::{serializer, InMemStorage, Serializer, Storage};
|
||||
#[cfg(feature = "sqlite-storage")]
|
||||
pub use storage::{SqliteStorage, SqliteStorageError};
|
||||
|
||||
pub use storage::{serializer, InMemStorage, Serializer, Storage, TraceStorage};
|
||||
|
|
|
@ -8,8 +8,11 @@ use tokio::sync::Mutex;
|
|||
///
|
||||
/// ## Note
|
||||
/// All the dialogues will be lost after you restart your bot. If you need to
|
||||
/// store them somewhere on a drive, you need to implement a storage
|
||||
/// communicating with a DB.
|
||||
/// store them somewhere on a drive, you should use [`SqliteStorage`],
|
||||
/// [`RedisStorage`] or implement your own.
|
||||
///
|
||||
/// [`RedisStorage`]: crate::dispatching::dialogue::RedisStorage
|
||||
/// [`SqliteStorage`]: crate::dispatching::dialogue::SqliteStorage
|
||||
#[derive(Debug)]
|
||||
pub struct InMemStorage<D> {
|
||||
map: Mutex<HashMap<i64, D>>,
|
||||
|
|
|
@ -1,26 +1,42 @@
|
|||
pub mod serializer;
|
||||
|
||||
mod in_mem_storage;
|
||||
mod trace_storage;
|
||||
|
||||
#[cfg(feature = "redis-storage")]
|
||||
mod redis_storage;
|
||||
|
||||
#[cfg(feature = "sqlite-storage")]
|
||||
mod sqlite_storage;
|
||||
|
||||
use futures::future::BoxFuture;
|
||||
|
||||
pub use in_mem_storage::InMemStorage;
|
||||
pub use self::{in_mem_storage::InMemStorage, trace_storage::TraceStorage};
|
||||
|
||||
#[cfg(feature = "redis-storage")]
|
||||
// FIXME(waffle): use `docsrs` here when issue with combine is resolved <https://github.com/teloxide/teloxide/pull/305#issuecomment-716172103>
|
||||
#[cfg_attr(all(teloxide_docsrs, feature = "nightly"), doc(cfg(feature = "redis-storage")))]
|
||||
pub use redis_storage::{RedisStorage, RedisStorageError};
|
||||
pub use serializer::Serializer;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[cfg(feature = "sqlite-storage")]
|
||||
pub use sqlite_storage::{SqliteStorage, SqliteStorageError};
|
||||
|
||||
/// A storage of dialogues.
|
||||
///
|
||||
/// You can implement this trait for a structure that communicates with a DB and
|
||||
/// be sure that after you restart your bot, all the dialogues won't be lost.
|
||||
///
|
||||
/// For a storage based on a simple hash map, see [`InMemStorage`].
|
||||
/// Currently we support the following storages out of the box:
|
||||
///
|
||||
/// - [`InMemStorage`] - a storage based on a simple hash map
|
||||
/// - [`RedisStorage`] - a Redis-based storage
|
||||
/// - [`SqliteStorage`] - an SQLite-based persistent storage
|
||||
///
|
||||
/// [`InMemStorage`]: crate::dispatching::dialogue::InMemStorage
|
||||
/// [`RedisStorage`]: crate::dispatching::dialogue::RedisStorage
|
||||
/// [`SqliteStorage`]: crate::dispatching::dialogue::SqliteStorage
|
||||
pub trait Storage<D> {
|
||||
type Error;
|
||||
|
||||
|
|
|
@ -32,9 +32,13 @@ where
|
|||
///
|
||||
/// [CBOR]: https://en.wikipedia.org/wiki/CBOR
|
||||
#[cfg(feature = "cbor-serializer")]
|
||||
// FIXME(waffle): use `docsrs` here when issue with combine is resolved <https://github.com/teloxide/teloxide/pull/305#issuecomment-716172103>
|
||||
#[cfg_attr(all(teloxide_docsrs, feature = "nightly"), doc(cfg(feature = "cbor-serializer")))]
|
||||
pub struct CBOR;
|
||||
|
||||
#[cfg(feature = "cbor-serializer")]
|
||||
// FIXME(waffle): use `docsrs` here when issue with combine is resolved <https://github.com/teloxide/teloxide/pull/305#issuecomment-716172103>
|
||||
#[cfg_attr(all(teloxide_docsrs, feature = "nightly"), doc(cfg(feature = "cbor-serializer")))]
|
||||
impl<D> Serializer<D> for CBOR
|
||||
where
|
||||
D: Serialize + DeserializeOwned,
|
||||
|
@ -54,9 +58,13 @@ where
|
|||
///
|
||||
/// [Bincode]: https://github.com/servo/bincode
|
||||
#[cfg(feature = "bincode-serializer")]
|
||||
// FIXME(waffle): use `docsrs` here when issue with combine is resolved <https://github.com/teloxide/teloxide/pull/305#issuecomment-716172103>
|
||||
#[cfg_attr(all(teloxide_docsrs, feature = "nightly"), doc(cfg(feature = "bincode-serializer")))]
|
||||
pub struct Bincode;
|
||||
|
||||
#[cfg(feature = "bincode-serializer")]
|
||||
// FIXME(waffle): use `docsrs` here when issue with combine is resolved <https://github.com/teloxide/teloxide/pull/305#issuecomment-716172103>
|
||||
#[cfg_attr(all(teloxide_docsrs, feature = "nightly"), doc(cfg(feature = "bincode-serializer")))]
|
||||
impl<D> Serializer<D> for Bincode
|
||||
where
|
||||
D: Serialize + DeserializeOwned,
|
||||
|
|
137
src/dispatching/dialogue/storage/sqlite_storage.rs
Normal file
137
src/dispatching/dialogue/storage/sqlite_storage.rs
Normal file
|
@ -0,0 +1,137 @@
|
|||
use super::{serializer::Serializer, Storage};
|
||||
use futures::future::BoxFuture;
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
use sqlx::{sqlite::SqlitePool, Executor};
|
||||
use std::{
|
||||
convert::Infallible,
|
||||
fmt::{Debug, Display},
|
||||
str,
|
||||
sync::Arc,
|
||||
};
|
||||
use thiserror::Error;
|
||||
|
||||
/// A persistent storage based on [SQLite](https://www.sqlite.org/).
|
||||
pub struct SqliteStorage<S> {
|
||||
pool: SqlitePool,
|
||||
serializer: S,
|
||||
}
|
||||
|
||||
/// An error returned from [`SqliteStorage`].
|
||||
///
|
||||
/// [`SqliteStorage`]: struct.SqliteStorage.html
|
||||
#[derive(Debug, Error)]
|
||||
pub enum SqliteStorageError<SE>
|
||||
where
|
||||
SE: Debug + Display,
|
||||
{
|
||||
#[error("dialogue serialization error: {0}")]
|
||||
SerdeError(SE),
|
||||
#[error("sqlite error: {0}")]
|
||||
SqliteError(#[from] sqlx::Error),
|
||||
}
|
||||
|
||||
impl<S> SqliteStorage<S> {
|
||||
pub async fn open(
|
||||
path: &str,
|
||||
serializer: S,
|
||||
) -> Result<Arc<Self>, SqliteStorageError<Infallible>> {
|
||||
let pool = SqlitePool::connect(format!("sqlite:{}?mode=rwc", path).as_str()).await?;
|
||||
let mut conn = pool.acquire().await?;
|
||||
sqlx::query(
|
||||
r#"
|
||||
CREATE TABLE IF NOT EXISTS teloxide_dialogues (
|
||||
chat_id BIGINT PRIMARY KEY,
|
||||
dialogue BLOB NOT NULL
|
||||
);
|
||||
"#,
|
||||
)
|
||||
.execute(&mut conn)
|
||||
.await?;
|
||||
|
||||
Ok(Arc::new(Self { pool, serializer }))
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, D> Storage<D> for SqliteStorage<S>
|
||||
where
|
||||
S: Send + Sync + Serializer<D> + 'static,
|
||||
D: Send + Serialize + DeserializeOwned + 'static,
|
||||
<S as Serializer<D>>::Error: Debug + Display,
|
||||
{
|
||||
type Error = SqliteStorageError<<S as Serializer<D>>::Error>;
|
||||
|
||||
fn remove_dialogue(
|
||||
self: Arc<Self>,
|
||||
chat_id: i64,
|
||||
) -> BoxFuture<'static, Result<Option<D>, Self::Error>> {
|
||||
Box::pin(async move {
|
||||
Ok(match get_dialogue(&self.pool, chat_id).await? {
|
||||
Some(d) => {
|
||||
let prev_dialogue =
|
||||
self.serializer.deserialize(&d).map_err(SqliteStorageError::SerdeError)?;
|
||||
sqlx::query("DELETE FROM teloxide_dialogues WHERE chat_id = ?")
|
||||
.bind(chat_id)
|
||||
.execute(&self.pool)
|
||||
.await?;
|
||||
Some(prev_dialogue)
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn update_dialogue(
|
||||
self: Arc<Self>,
|
||||
chat_id: i64,
|
||||
dialogue: D,
|
||||
) -> BoxFuture<'static, Result<Option<D>, Self::Error>> {
|
||||
Box::pin(async move {
|
||||
let prev_dialogue = match get_dialogue(&self.pool, chat_id).await? {
|
||||
Some(d) => {
|
||||
Some(self.serializer.deserialize(&d).map_err(SqliteStorageError::SerdeError)?)
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
let upd_dialogue =
|
||||
self.serializer.serialize(&dialogue).map_err(SqliteStorageError::SerdeError)?;
|
||||
self.pool
|
||||
.acquire()
|
||||
.await?
|
||||
.execute(
|
||||
sqlx::query(
|
||||
r#"
|
||||
INSERT INTO teloxide_dialogues VALUES (?, ?)
|
||||
ON CONFLICT(chat_id) DO UPDATE SET dialogue=excluded.dialogue
|
||||
"#,
|
||||
)
|
||||
.bind(chat_id)
|
||||
.bind(upd_dialogue),
|
||||
)
|
||||
.await?;
|
||||
Ok(prev_dialogue)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(sqlx::FromRow)]
|
||||
struct DialogueDBRow {
|
||||
dialogue: Vec<u8>,
|
||||
}
|
||||
|
||||
async fn get_dialogue(
|
||||
pool: &SqlitePool,
|
||||
chat_id: i64,
|
||||
) -> Result<Option<Box<Vec<u8>>>, sqlx::Error> {
|
||||
Ok(
|
||||
match sqlx::query_as::<_, DialogueDBRow>(
|
||||
"SELECT dialogue FROM teloxide_dialogues WHERE chat_id = ?",
|
||||
)
|
||||
.bind(chat_id)
|
||||
.fetch_optional(pool)
|
||||
.await?
|
||||
{
|
||||
Some(r) => Some(Box::new(r.dialogue)),
|
||||
_ => None,
|
||||
},
|
||||
)
|
||||
}
|
70
src/dispatching/dialogue/storage/trace_storage.rs
Normal file
70
src/dispatching/dialogue/storage/trace_storage.rs
Normal file
|
@ -0,0 +1,70 @@
|
|||
use std::{
|
||||
fmt::Debug,
|
||||
marker::{Send, Sync},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use futures::future::BoxFuture;
|
||||
use log::{log_enabled, trace, Level::Trace};
|
||||
|
||||
use crate::dispatching::dialogue::Storage;
|
||||
|
||||
/// Storage wrapper for logging purposes
|
||||
///
|
||||
/// Reports about any dialogue update or removal action on `trace` level
|
||||
/// using `log` crate.
|
||||
pub struct TraceStorage<S> {
|
||||
inner: Arc<S>,
|
||||
}
|
||||
|
||||
impl<S> TraceStorage<S> {
|
||||
#[must_use]
|
||||
pub fn new(inner: Arc<S>) -> Arc<Self> {
|
||||
Arc::new(Self { inner })
|
||||
}
|
||||
|
||||
pub fn into_inner(self) -> Arc<S> {
|
||||
self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, D> Storage<D> for TraceStorage<S>
|
||||
where
|
||||
D: Debug,
|
||||
S: Storage<D> + Send + Sync + 'static,
|
||||
{
|
||||
type Error = <S as Storage<D>>::Error;
|
||||
|
||||
fn remove_dialogue(
|
||||
self: Arc<Self>,
|
||||
chat_id: i64,
|
||||
) -> BoxFuture<'static, Result<Option<D>, Self::Error>>
|
||||
where
|
||||
D: Send + 'static,
|
||||
{
|
||||
trace!("Removing dialogue with {}", chat_id);
|
||||
<S as Storage<D>>::remove_dialogue(self.inner.clone(), chat_id)
|
||||
}
|
||||
|
||||
fn update_dialogue(
|
||||
self: Arc<Self>,
|
||||
chat_id: i64,
|
||||
dialogue: D,
|
||||
) -> BoxFuture<'static, Result<Option<D>, Self::Error>>
|
||||
where
|
||||
D: Send + 'static,
|
||||
{
|
||||
if log_enabled!(Trace) {
|
||||
Box::pin(async move {
|
||||
let to = format!("{:#?}", dialogue);
|
||||
let from =
|
||||
<S as Storage<D>>::update_dialogue(self.inner.clone(), chat_id, dialogue)
|
||||
.await?;
|
||||
trace!("Updated dialogue with {}, {:#?} -> {}", chat_id, from, to);
|
||||
Ok(from)
|
||||
})
|
||||
} else {
|
||||
<S as Storage<D>>::update_dialogue(self.inner.clone(), chat_id, dialogue)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
use crate::{
|
||||
dispatching::{dialogue::DialogueStage, UpdateWithCx},
|
||||
requests::ResponseResult,
|
||||
types::Message,
|
||||
};
|
||||
use futures::future::BoxFuture;
|
||||
|
@ -8,11 +7,16 @@ use futures::future::BoxFuture;
|
|||
/// Represents a transition function of a dialogue FSM.
|
||||
pub trait Transition: Sized {
|
||||
type Aux;
|
||||
type Error;
|
||||
|
||||
/// Turns itself into another state, depending on the input message.
|
||||
///
|
||||
/// `aux` will be passed to each subtransition function.
|
||||
fn react(self, cx: TransitionIn, aux: Self::Aux) -> BoxFuture<'static, TransitionOut<Self>>;
|
||||
fn react(
|
||||
self,
|
||||
cx: TransitionIn,
|
||||
aux: Self::Aux,
|
||||
) -> BoxFuture<'static, TransitionOut<Self, Self::Error>>;
|
||||
}
|
||||
|
||||
/// Like [`Transition`], but from `StateN` -> `Dialogue`.
|
||||
|
@ -24,6 +28,7 @@ where
|
|||
{
|
||||
type Aux;
|
||||
type Dialogue;
|
||||
type Error;
|
||||
|
||||
/// Turns itself into another state, depending on the input message.
|
||||
///
|
||||
|
@ -33,7 +38,7 @@ where
|
|||
self,
|
||||
cx: TransitionIn,
|
||||
aux: Self::Aux,
|
||||
) -> BoxFuture<'static, TransitionOut<Self::Dialogue>>;
|
||||
) -> BoxFuture<'static, TransitionOut<Self::Dialogue, Self::Error>>;
|
||||
}
|
||||
|
||||
/// A type returned from a FSM subtransition function.
|
||||
|
@ -41,14 +46,16 @@ where
|
|||
/// Now it is used only inside `#[teloxide(subtransition)]` for type inference.
|
||||
pub trait SubtransitionOutputType {
|
||||
type Output;
|
||||
type Error;
|
||||
}
|
||||
|
||||
impl<D> SubtransitionOutputType for TransitionOut<D> {
|
||||
impl<D, E> SubtransitionOutputType for TransitionOut<D, E> {
|
||||
type Output = D;
|
||||
type Error = E;
|
||||
}
|
||||
|
||||
/// An input passed into a FSM (sub)transition function.
|
||||
pub type TransitionIn = UpdateWithCx<Message>;
|
||||
|
||||
/// A type returned from a FSM (sub)transition function.
|
||||
pub type TransitionOut<D> = ResponseResult<DialogueStage<D>>;
|
||||
pub type TransitionOut<D, E = crate::RequestError> = Result<DialogueStage<D>, E>;
|
||||
|
|
|
@ -30,7 +30,7 @@ where
|
|||
where
|
||||
Self: Stream<Item = UpdateWithCx<Message>>,
|
||||
{
|
||||
Box::pin(self.filter_map(|cx| async move { cx.update.text_owned().map(|text| (cx, text)) }))
|
||||
self.filter_map(|cx| async move { cx.update.text_owned().map(|text| (cx, text)) }).boxed()
|
||||
}
|
||||
|
||||
fn commands<C, N>(self, bot_name: N) -> BoxStream<'static, (UpdateWithCx<Message>, C)>
|
||||
|
@ -41,10 +41,12 @@ where
|
|||
{
|
||||
let bot_name = bot_name.into();
|
||||
|
||||
Box::pin(self.text_messages().filter_map(move |(cx, text)| {
|
||||
let bot_name = bot_name.clone();
|
||||
self.text_messages()
|
||||
.filter_map(move |(cx, text)| {
|
||||
let bot_name = bot_name.clone();
|
||||
|
||||
async move { C::parse(&text, &bot_name).map(|command| (cx, command)).ok() }
|
||||
}))
|
||||
async move { C::parse(&text, &bot_name).map(|command| (cx, command)).ok() }
|
||||
})
|
||||
.boxed()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,13 +22,14 @@ use std::{fmt::Debug, future::Future, sync::Arc};
|
|||
///
|
||||
/// [REPL]: https://en.wikipedia.org/wiki/Read-eval-print_loop
|
||||
/// [`Dispatcher`]: crate::dispatching::Dispatcher
|
||||
pub async fn commands_repl<Cmd, H, Fut, HandlerE>(bot: Bot, bot_name: &'static str, handler: H)
|
||||
pub async fn commands_repl<Cmd, H, Fut, HandlerE, N>(bot: Bot, bot_name: N, handler: H)
|
||||
where
|
||||
Cmd: BotCommand + Send + 'static,
|
||||
H: Fn(UpdateWithCx<Message>, Cmd) -> Fut + Send + Sync + 'static,
|
||||
Fut: Future<Output = Result<(), HandlerE>> + Send + 'static,
|
||||
Result<(), HandlerE>: OnError<HandlerE>,
|
||||
HandlerE: Debug + Send,
|
||||
N: Into<String> + Send + 'static,
|
||||
{
|
||||
let cloned_bot = bot.clone();
|
||||
|
||||
|
@ -53,9 +54,9 @@ where
|
|||
/// [`Dispatcher`]: crate::dispatching::Dispatcher
|
||||
/// [`commands_repl`]: crate::dispatching::repls::commands_repl()
|
||||
/// [`UpdateListener`]: crate::dispatching::update_listeners::UpdateListener
|
||||
pub async fn commands_repl_with_listener<'a, Cmd, H, Fut, L, ListenerE, HandlerE>(
|
||||
pub async fn commands_repl_with_listener<'a, Cmd, H, Fut, L, ListenerE, HandlerE, N>(
|
||||
bot: Bot,
|
||||
bot_name: &'static str,
|
||||
bot_name: N,
|
||||
handler: H,
|
||||
listener: L,
|
||||
) where
|
||||
|
@ -66,21 +67,19 @@ pub async fn commands_repl_with_listener<'a, Cmd, H, Fut, L, ListenerE, HandlerE
|
|||
ListenerE: Debug + Send + 'a,
|
||||
Result<(), HandlerE>: OnError<HandlerE>,
|
||||
HandlerE: Debug + Send,
|
||||
N: Into<String> + Send + 'static,
|
||||
{
|
||||
let handler = Arc::new(handler);
|
||||
|
||||
Dispatcher::new(bot)
|
||||
.messages_handler(move |rx: DispatcherHandlerRx<Message>| {
|
||||
rx.commands::<Cmd, &'static str>(bot_name).for_each_concurrent(
|
||||
None,
|
||||
move |(cx, cmd)| {
|
||||
let handler = Arc::clone(&handler);
|
||||
rx.commands::<Cmd, N>(bot_name).for_each_concurrent(None, move |(cx, cmd)| {
|
||||
let handler = Arc::clone(&handler);
|
||||
|
||||
async move {
|
||||
handler(cx, cmd).await.log_on_error().await;
|
||||
}
|
||||
},
|
||||
)
|
||||
async move {
|
||||
handler(cx, cmd).await.log_on_error().await;
|
||||
}
|
||||
})
|
||||
})
|
||||
.dispatch_with_listener(
|
||||
listener,
|
||||
|
|
|
@ -6,7 +6,7 @@ use crate::{
|
|||
SendLocation, SendMediaGroup, SendMessage, SendPhoto, SendSticker, SendVenue, SendVideo,
|
||||
SendVideoNote, SendVoice,
|
||||
},
|
||||
types::{ChatId, ChatOrInlineMessage, InputFile, InputMedia, Message},
|
||||
types::{ChatId, InputFile, InputMedia, Message},
|
||||
Bot,
|
||||
};
|
||||
|
||||
|
@ -130,20 +130,11 @@ impl UpdateWithCx<Message> {
|
|||
where
|
||||
T: Into<String>,
|
||||
{
|
||||
self.bot.edit_message_text(
|
||||
ChatOrInlineMessage::Chat {
|
||||
chat_id: self.update.chat.id.into(),
|
||||
message_id: self.update.id,
|
||||
},
|
||||
text,
|
||||
)
|
||||
self.bot.edit_message_text(self.update.chat.id, self.update.id, text)
|
||||
}
|
||||
|
||||
pub fn edit_message_caption(&self) -> EditMessageCaption {
|
||||
self.bot.edit_message_caption(ChatOrInlineMessage::Chat {
|
||||
chat_id: self.update.chat.id.into(),
|
||||
message_id: self.update.id,
|
||||
})
|
||||
self.bot.edit_message_caption(self.update.chat.id, self.update.id)
|
||||
}
|
||||
|
||||
pub fn delete_message(&self) -> DeleteMessage {
|
||||
|
|
19
src/lib.rs
19
src/lib.rs
|
@ -40,6 +40,16 @@
|
|||
)]
|
||||
#![allow(clippy::match_bool)]
|
||||
#![forbid(unsafe_code)]
|
||||
#![cfg_attr(all(feature = "nightly", doctest), feature(external_doc))]
|
||||
// we pass "--cfg docsrs" when building docs to add `This is supported on feature="..." only.`
|
||||
//
|
||||
// To properly build docs of this crate run
|
||||
// ```console
|
||||
// FIXME(waffle): use `docsrs` here when issue with combine is resolved <https://github.com/teloxide/teloxide/pull/305#issuecomment-716172103>
|
||||
// $ RUSTDOCFLAGS="--cfg teloxide_docsrs" cargo +nightly doc --open --all-features
|
||||
// ```
|
||||
// FIXME(waffle): use `docsrs` here when issue with combine is resolved <https://github.com/teloxide/teloxide/pull/305#issuecomment-716172103>
|
||||
#![cfg_attr(all(teloxide_docsrs, feature = "nightly"), feature(doc_cfg))]
|
||||
|
||||
pub use bot::{Bot, BotBuilder};
|
||||
pub use dispatching::repls::{
|
||||
|
@ -60,4 +70,11 @@ pub mod requests;
|
|||
pub mod types;
|
||||
pub mod utils;
|
||||
|
||||
extern crate teloxide_macros;
|
||||
#[cfg(feature = "macros")]
|
||||
// FIXME(waffle): use `docsrs` here when issue with combine is resolved <https://github.com/teloxide/teloxide/pull/305#issuecomment-716172103>
|
||||
#[cfg_attr(all(teloxide_docsrs, feature = "nightly"), doc(cfg(feature = "macros")))]
|
||||
pub use teloxide_macros::teloxide;
|
||||
|
||||
#[cfg(all(feature = "nightly", doctest))]
|
||||
#[doc(include = "../README.md")]
|
||||
enum ReadmeDocTests {}
|
||||
|
|
|
@ -28,6 +28,8 @@ where
|
|||
}
|
||||
|
||||
#[cfg(feature = "unstable-stream")]
|
||||
// FIXME(waffle): use `docsrs` here when issue with combine is resolved <https://github.com/teloxide/teloxide/pull/305#issuecomment-716172103>
|
||||
#[cfg_attr(all(teloxide_docsrs, feature = "nightly"), doc(cfg(feature = "unstable-stream")))]
|
||||
pub async fn download_file_stream(
|
||||
client: &Client,
|
||||
token: &str,
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
#[cfg(feature = "unstable-stream")]
|
||||
// FIXME(waffle): use `docsrs` here when issue with combine is resolved <https://github.com/teloxide/teloxide/pull/305#issuecomment-716172103>
|
||||
#[cfg_attr(all(teloxide_docsrs, feature = "nightly"), doc(cfg(feature = "unstable-stream")))]
|
||||
pub use download::download_file_stream;
|
||||
|
||||
pub use self::{
|
||||
|
|
|
@ -9,12 +9,14 @@ pub use crate::{
|
|||
Dispatcher, DispatcherHandlerRx, DispatcherHandlerRxExt, UpdateWithCx,
|
||||
},
|
||||
error_handlers::{LoggingErrorHandler, OnError},
|
||||
requests::{Request, ResponseResult},
|
||||
requests::{respond, Request, ResponseResult},
|
||||
types::{Message, Update},
|
||||
Bot, RequestError,
|
||||
};
|
||||
|
||||
#[cfg(feature = "frunk")]
|
||||
// FIXME(waffle): use `docsrs` here when issue with combine is resolved <https://github.com/teloxide/teloxide/pull/305#issuecomment-716172103>
|
||||
#[cfg_attr(all(teloxide_docsrs, feature = "nightly"), doc(cfg(feature = "frunk")))]
|
||||
pub use crate::utils::UpState;
|
||||
|
||||
pub use tokio::sync::mpsc::UnboundedReceiver;
|
||||
|
|
83
src/requests/all/edit_inline_message_caption.rs
Normal file
83
src/requests/all/edit_inline_message_caption.rs
Normal file
|
@ -0,0 +1,83 @@
|
|||
use serde::Serialize;
|
||||
|
||||
use crate::{
|
||||
net,
|
||||
requests::{Request, ResponseResult},
|
||||
types::{InlineKeyboardMarkup, ParseMode, True},
|
||||
Bot,
|
||||
};
|
||||
|
||||
/// Use this method to edit captions of messages sent via the bot.
|
||||
///
|
||||
/// On success, [`True`] is returned.
|
||||
///
|
||||
/// [The official docs](https://core.telegram.org/bots/api#editmessagecaption).
|
||||
///
|
||||
/// [`True`]: crate::types::True
|
||||
#[serde_with_macros::skip_serializing_none]
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct EditInlineMessageCaption {
|
||||
#[serde(skip_serializing)]
|
||||
bot: Bot,
|
||||
inline_message_id: String,
|
||||
caption: Option<String>,
|
||||
parse_mode: Option<ParseMode>,
|
||||
reply_markup: Option<InlineKeyboardMarkup>,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl Request for EditInlineMessageCaption {
|
||||
type Output = True;
|
||||
|
||||
async fn send(&self) -> ResponseResult<True> {
|
||||
net::request_json(self.bot.client(), self.bot.token(), "editMessageCaption", &self).await
|
||||
}
|
||||
}
|
||||
|
||||
impl EditInlineMessageCaption {
|
||||
pub(crate) fn new<I>(bot: Bot, inline_message_id: I) -> Self
|
||||
where
|
||||
I: Into<String>,
|
||||
{
|
||||
let inline_message_id = inline_message_id.into();
|
||||
Self { bot, inline_message_id, caption: None, parse_mode: None, reply_markup: None }
|
||||
}
|
||||
|
||||
/// Identifier of the inline message.
|
||||
pub fn inline_message_id<T>(mut self, val: T) -> Self
|
||||
where
|
||||
T: Into<String>,
|
||||
{
|
||||
self.inline_message_id = val.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// New caption of the message.
|
||||
pub fn caption<T>(mut self, val: T) -> Self
|
||||
where
|
||||
T: Into<String>,
|
||||
{
|
||||
self.caption = Some(val.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Send [Markdown] or [HTML], if you want Telegram apps to show
|
||||
/// [bold, italic, fixed-width text or inline URLs] in the media caption.
|
||||
///
|
||||
/// [Markdown]: crate::types::ParseMode::Markdown
|
||||
/// [HTML]: crate::types::ParseMode::HTML
|
||||
/// [bold, italic, fixed-width text or inline URLs]:
|
||||
/// crate::types::ParseMode
|
||||
pub fn parse_mode(mut self, val: ParseMode) -> Self {
|
||||
self.parse_mode = Some(val);
|
||||
self
|
||||
}
|
||||
|
||||
/// A JSON-serialized object for an [inline keyboard].
|
||||
///
|
||||
/// [inline keyboard]: https://core.telegram.org/bots#inline-keyboards-and-on-the-fly-updating
|
||||
pub fn reply_markup(mut self, val: InlineKeyboardMarkup) -> Self {
|
||||
self.reply_markup = Some(val);
|
||||
self
|
||||
}
|
||||
}
|
77
src/requests/all/edit_inline_message_live_location.rs
Normal file
77
src/requests/all/edit_inline_message_live_location.rs
Normal file
|
@ -0,0 +1,77 @@
|
|||
use serde::Serialize;
|
||||
|
||||
use crate::{
|
||||
net,
|
||||
requests::{Request, ResponseResult},
|
||||
types::{InlineKeyboardMarkup, True},
|
||||
Bot,
|
||||
};
|
||||
|
||||
/// Use this method to edit live location messages sent via the bot.
|
||||
///
|
||||
/// A location can be edited until its live_period expires or editing is
|
||||
/// explicitly disabled by a call to stopMessageLiveLocation. On success,
|
||||
/// [`True`] is returned.
|
||||
///
|
||||
/// [The official docs](https://core.telegram.org/bots/api#editmessagelivelocation).
|
||||
///
|
||||
/// [`True`]: crate::types::True
|
||||
#[serde_with_macros::skip_serializing_none]
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct EditInlineMessageLiveLocation {
|
||||
#[serde(skip_serializing)]
|
||||
bot: Bot,
|
||||
inline_message_id: String,
|
||||
latitude: f32,
|
||||
longitude: f32,
|
||||
reply_markup: Option<InlineKeyboardMarkup>,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl Request for EditInlineMessageLiveLocation {
|
||||
type Output = True;
|
||||
|
||||
async fn send(&self) -> ResponseResult<True> {
|
||||
net::request_json(self.bot.client(), self.bot.token(), "editMessageLiveLocation", &self)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
impl EditInlineMessageLiveLocation {
|
||||
pub(crate) fn new<I>(bot: Bot, inline_message_id: I, latitude: f32, longitude: f32) -> Self
|
||||
where
|
||||
I: Into<String>,
|
||||
{
|
||||
let inline_message_id = inline_message_id.into();
|
||||
Self { bot, inline_message_id, latitude, longitude, reply_markup: None }
|
||||
}
|
||||
|
||||
/// Identifier of the inline message.
|
||||
pub fn inline_message_id<T>(mut self, val: T) -> Self
|
||||
where
|
||||
T: Into<String>,
|
||||
{
|
||||
self.inline_message_id = val.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Latitude of new location.
|
||||
pub fn latitude(mut self, val: f32) -> Self {
|
||||
self.latitude = val;
|
||||
self
|
||||
}
|
||||
|
||||
/// Longitude of new location.
|
||||
pub fn longitude(mut self, val: f32) -> Self {
|
||||
self.longitude = val;
|
||||
self
|
||||
}
|
||||
|
||||
/// A JSON-serialized object for a new [inline keyboard].
|
||||
///
|
||||
/// [inline keyboard]: https://core.telegram.org/bots#inline-keyboards-and-on-the-fly-updating
|
||||
pub fn reply_markup(mut self, val: InlineKeyboardMarkup) -> Self {
|
||||
self.reply_markup = Some(val);
|
||||
self
|
||||
}
|
||||
}
|
78
src/requests/all/edit_inline_message_media.rs
Normal file
78
src/requests/all/edit_inline_message_media.rs
Normal file
|
@ -0,0 +1,78 @@
|
|||
use crate::{
|
||||
net,
|
||||
requests::{form_builder::FormBuilder, Request, ResponseResult},
|
||||
types::{InlineKeyboardMarkup, InputMedia, True},
|
||||
Bot,
|
||||
};
|
||||
|
||||
/// Use this method to edit animation, audio, document, photo, or video
|
||||
/// messages sent via the bot.
|
||||
///
|
||||
/// If a message is a part of a message album, then it can be edited only to a
|
||||
/// photo or a video. Otherwise, message type can be changed arbitrarily. When
|
||||
/// this method is used, new file can't be uploaded. Use previously
|
||||
/// uploaded file via its `file_id` or specify a URL. On success, [`True`] is
|
||||
/// returned.
|
||||
///
|
||||
/// [The official docs](https://core.telegram.org/bots/api#editmessagemedia).
|
||||
///
|
||||
/// [`True`]: crate::types::True
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EditInlineMessageMedia {
|
||||
bot: Bot,
|
||||
inline_message_id: String,
|
||||
media: InputMedia,
|
||||
reply_markup: Option<InlineKeyboardMarkup>,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl Request for EditInlineMessageMedia {
|
||||
type Output = True;
|
||||
|
||||
async fn send(&self) -> ResponseResult<True> {
|
||||
net::request_multipart(
|
||||
self.bot.client(),
|
||||
self.bot.token(),
|
||||
"editMessageMedia",
|
||||
FormBuilder::new()
|
||||
.add_text("media", &self.media)
|
||||
.add_text("reply_markup", &self.reply_markup)
|
||||
.add_text("inline_message_id", &self.inline_message_id)
|
||||
.build(),
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
impl EditInlineMessageMedia {
|
||||
pub(crate) fn new<I>(bot: Bot, inline_message_id: I, media: InputMedia) -> Self
|
||||
where
|
||||
I: Into<String>,
|
||||
{
|
||||
let inline_message_id = inline_message_id.into();
|
||||
Self { bot, inline_message_id, media, reply_markup: None }
|
||||
}
|
||||
|
||||
/// Identifier of the inline message.
|
||||
pub fn inline_message_id<T>(mut self, val: T) -> Self
|
||||
where
|
||||
T: Into<String>,
|
||||
{
|
||||
self.inline_message_id = val.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// A JSON-serialized object for a new media content of the message.
|
||||
pub fn media(mut self, val: InputMedia) -> Self {
|
||||
self.media = val;
|
||||
self
|
||||
}
|
||||
|
||||
/// A JSON-serialized object for a new [inline keyboard].
|
||||
///
|
||||
/// [inline keyboard]: https://core.telegram.org/bots#inline-keyboards-and-on-the-fly-updating
|
||||
pub fn reply_markup(mut self, val: InlineKeyboardMarkup) -> Self {
|
||||
self.reply_markup = Some(val);
|
||||
self
|
||||
}
|
||||
}
|
62
src/requests/all/edit_inline_message_reply_markup.rs
Normal file
62
src/requests/all/edit_inline_message_reply_markup.rs
Normal file
|
@ -0,0 +1,62 @@
|
|||
use serde::Serialize;
|
||||
|
||||
use crate::{
|
||||
net,
|
||||
requests::{Request, ResponseResult},
|
||||
types::{InlineKeyboardMarkup, True},
|
||||
Bot,
|
||||
};
|
||||
|
||||
/// Use this method to edit only the reply markup of messages sent via the bot.
|
||||
///
|
||||
/// On success, [`True`] is returned.
|
||||
///
|
||||
/// [The official docs](https://core.telegram.org/bots/api#editmessagereplymarkup).
|
||||
///
|
||||
/// [`Message`]: crate::types::Message
|
||||
/// [`True`]: crate::types::True
|
||||
#[serde_with_macros::skip_serializing_none]
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct EditInlineMessageReplyMarkup {
|
||||
#[serde(skip_serializing)]
|
||||
bot: Bot,
|
||||
inline_message_id: String,
|
||||
reply_markup: Option<InlineKeyboardMarkup>,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl Request for EditInlineMessageReplyMarkup {
|
||||
type Output = True;
|
||||
|
||||
async fn send(&self) -> ResponseResult<True> {
|
||||
net::request_json(self.bot.client(), self.bot.token(), "editMessageReplyMarkup", &self)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
impl EditInlineMessageReplyMarkup {
|
||||
pub(crate) fn new<I>(bot: Bot, inline_message_id: I) -> Self
|
||||
where
|
||||
I: Into<String>,
|
||||
{
|
||||
let inline_message_id = inline_message_id.into();
|
||||
Self { bot, inline_message_id, reply_markup: None }
|
||||
}
|
||||
|
||||
/// Identifier of the inline message.
|
||||
pub fn inline_message_id<T>(mut self, val: T) -> Self
|
||||
where
|
||||
T: Into<String>,
|
||||
{
|
||||
self.inline_message_id = val.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// A JSON-serialized object for an [inline keyboard].
|
||||
///
|
||||
/// [inline keyboard]: https://core.telegram.org/bots#inline-keyboards-and-on-the-fly-updating
|
||||
pub fn reply_markup(mut self, val: InlineKeyboardMarkup) -> Self {
|
||||
self.reply_markup = Some(val);
|
||||
self
|
||||
}
|
||||
}
|
98
src/requests/all/edit_inline_message_text.rs
Normal file
98
src/requests/all/edit_inline_message_text.rs
Normal file
|
@ -0,0 +1,98 @@
|
|||
use serde::Serialize;
|
||||
|
||||
use crate::{
|
||||
net,
|
||||
requests::{Request, ResponseResult},
|
||||
types::{InlineKeyboardMarkup, Message, ParseMode},
|
||||
Bot,
|
||||
};
|
||||
|
||||
/// Use this method to edit text and game messages sent via the bot.
|
||||
///
|
||||
/// On success, [`True`] is returned.
|
||||
///
|
||||
/// [The official docs](https://core.telegram.org/bots/api#editmessagetext).
|
||||
///
|
||||
/// [`True`]: crate::types::True
|
||||
#[serde_with_macros::skip_serializing_none]
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct EditInlineMessageText {
|
||||
#[serde(skip_serializing)]
|
||||
bot: Bot,
|
||||
inline_message_id: String,
|
||||
text: String,
|
||||
parse_mode: Option<ParseMode>,
|
||||
disable_web_page_preview: Option<bool>,
|
||||
reply_markup: Option<InlineKeyboardMarkup>,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl Request for EditInlineMessageText {
|
||||
type Output = Message;
|
||||
|
||||
async fn send(&self) -> ResponseResult<Message> {
|
||||
net::request_json(self.bot.client(), self.bot.token(), "editMessageText", &self).await
|
||||
}
|
||||
}
|
||||
|
||||
impl EditInlineMessageText {
|
||||
pub(crate) fn new<I, T>(bot: Bot, inline_message_id: I, text: T) -> Self
|
||||
where
|
||||
I: Into<String>,
|
||||
T: Into<String>,
|
||||
{
|
||||
let inline_message_id = inline_message_id.into();
|
||||
let text = text.into();
|
||||
Self {
|
||||
bot,
|
||||
inline_message_id,
|
||||
text,
|
||||
parse_mode: None,
|
||||
disable_web_page_preview: None,
|
||||
reply_markup: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Identifier of the inline message.
|
||||
pub fn inline_message_id<T>(mut self, val: T) -> Self
|
||||
where
|
||||
T: Into<String>,
|
||||
{
|
||||
self.inline_message_id = val.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// New text of the message.
|
||||
pub fn text<T>(mut self, val: T) -> Self
|
||||
where
|
||||
T: Into<String>,
|
||||
{
|
||||
self.text = val.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Send [Markdown] or [HTML], if you want Telegram apps to show [bold,
|
||||
/// italic, fixed-width text or inline URLs] in your bot's message.
|
||||
///
|
||||
/// [Markdown]: https://core.telegram.org/bots/api#markdown-style
|
||||
/// [HTML]: https://core.telegram.org/bots/api#html-style
|
||||
/// [bold, italic, fixed-width text or inline URLs]: https://core.telegram.org/bots/api#formatting-options
|
||||
pub fn parse_mode(mut self, val: ParseMode) -> Self {
|
||||
self.parse_mode = Some(val);
|
||||
self
|
||||
}
|
||||
|
||||
/// Disables link previews for links in this message.
|
||||
pub fn disable_web_page_preview(mut self, val: bool) -> Self {
|
||||
self.disable_web_page_preview = Some(val);
|
||||
self
|
||||
}
|
||||
|
||||
/// A JSON-serialized object for an [inline keyboard].
|
||||
///
|
||||
/// [inline keyboard]: https://core.telegram.org/bots#inline-keyboards-and-on-the-fly-updating
|
||||
pub fn reply_markup(mut self, val: InlineKeyboardMarkup) -> Self {
|
||||
self.reply_markup = Some(val);
|
||||
self
|
||||
}
|
||||
}
|
|
@ -3,26 +3,24 @@ use serde::Serialize;
|
|||
use crate::{
|
||||
net,
|
||||
requests::{Request, ResponseResult},
|
||||
types::{ChatOrInlineMessage, InlineKeyboardMarkup, Message, ParseMode},
|
||||
types::{ChatId, InlineKeyboardMarkup, Message, ParseMode},
|
||||
Bot,
|
||||
};
|
||||
|
||||
/// Use this method to edit captions of messages.
|
||||
/// Use this method to edit captions of messages sent by the bot.
|
||||
///
|
||||
/// On success, if edited message is sent by the bot, the edited [`Message`] is
|
||||
/// returned, otherwise [`True`] is returned.
|
||||
/// On success, the edited [`Message`] is returned.
|
||||
///
|
||||
/// [The official docs](https://core.telegram.org/bots/api#editmessagecaption).
|
||||
///
|
||||
/// [`Message`]: crate::types::Message
|
||||
/// [`True`]: crate::types::True
|
||||
#[serde_with_macros::skip_serializing_none]
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct EditMessageCaption {
|
||||
#[serde(skip_serializing)]
|
||||
bot: Bot,
|
||||
#[serde(flatten)]
|
||||
chat_or_inline_message: ChatOrInlineMessage,
|
||||
chat_id: ChatId,
|
||||
message_id: i32,
|
||||
caption: Option<String>,
|
||||
parse_mode: Option<ParseMode>,
|
||||
reply_markup: Option<InlineKeyboardMarkup>,
|
||||
|
@ -38,12 +36,27 @@ impl Request for EditMessageCaption {
|
|||
}
|
||||
|
||||
impl EditMessageCaption {
|
||||
pub(crate) fn new(bot: Bot, chat_or_inline_message: ChatOrInlineMessage) -> Self {
|
||||
Self { bot, chat_or_inline_message, caption: None, parse_mode: None, reply_markup: None }
|
||||
pub(crate) fn new<C>(bot: Bot, chat_id: C, message_id: i32) -> Self
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
{
|
||||
let chat_id = chat_id.into();
|
||||
Self { bot, chat_id, message_id, caption: None, parse_mode: None, reply_markup: None }
|
||||
}
|
||||
|
||||
pub fn chat_or_inline_message(mut self, val: ChatOrInlineMessage) -> Self {
|
||||
self.chat_or_inline_message = val;
|
||||
/// Unique identifier for the target chat or username of the target channel
|
||||
/// (in the format `@channelusername`)
|
||||
pub fn chat_id<T>(mut self, val: T) -> Self
|
||||
where
|
||||
T: Into<ChatId>,
|
||||
{
|
||||
self.chat_id = val.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Identifier of the message to edit
|
||||
pub fn message_id(mut self, val: i32) -> Self {
|
||||
self.message_id = val;
|
||||
self
|
||||
}
|
||||
|
||||
|
|
|
@ -3,28 +3,26 @@ use serde::Serialize;
|
|||
use crate::{
|
||||
net,
|
||||
requests::{Request, ResponseResult},
|
||||
types::{ChatOrInlineMessage, InlineKeyboardMarkup, Message},
|
||||
types::{ChatId, InlineKeyboardMarkup, Message},
|
||||
Bot,
|
||||
};
|
||||
|
||||
/// Use this method to edit live location messages.
|
||||
///
|
||||
/// A location can be edited until its live_period expires or editing is
|
||||
/// explicitly disabled by a call to stopMessageLiveLocation. On success, if the
|
||||
/// edited message was sent by the bot, the edited [`Message`] is returned,
|
||||
/// otherwise [`True`] is returned.
|
||||
/// explicitly disabled by a call to stopMessageLiveLocation. On success, the
|
||||
/// edited [`Message`] is returned.
|
||||
///
|
||||
/// [The official docs](https://core.telegram.org/bots/api#editmessagelivelocation).
|
||||
///
|
||||
/// [`Message`]: crate::types::Message
|
||||
/// [`True`]: crate::types::True
|
||||
#[serde_with_macros::skip_serializing_none]
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct EditMessageLiveLocation {
|
||||
#[serde(skip_serializing)]
|
||||
bot: Bot,
|
||||
#[serde(flatten)]
|
||||
chat_or_inline_message: ChatOrInlineMessage,
|
||||
chat_id: ChatId,
|
||||
message_id: i32,
|
||||
latitude: f32,
|
||||
longitude: f32,
|
||||
reply_markup: Option<InlineKeyboardMarkup>,
|
||||
|
@ -41,17 +39,33 @@ impl Request for EditMessageLiveLocation {
|
|||
}
|
||||
|
||||
impl EditMessageLiveLocation {
|
||||
pub(crate) fn new(
|
||||
pub(crate) fn new<C>(
|
||||
bot: Bot,
|
||||
chat_or_inline_message: ChatOrInlineMessage,
|
||||
chat_id: C,
|
||||
message_id: i32,
|
||||
latitude: f32,
|
||||
longitude: f32,
|
||||
) -> Self {
|
||||
Self { bot, chat_or_inline_message, latitude, longitude, reply_markup: None }
|
||||
) -> Self
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
{
|
||||
let chat_id = chat_id.into();
|
||||
Self { bot, chat_id, message_id, latitude, longitude, reply_markup: None }
|
||||
}
|
||||
|
||||
pub fn chat_or_inline_message(mut self, val: ChatOrInlineMessage) -> Self {
|
||||
self.chat_or_inline_message = val;
|
||||
/// Unique identifier for the target chat or username of the target channel
|
||||
/// (in the format `@channelusername`)
|
||||
pub fn chat_id<T>(mut self, val: T) -> Self
|
||||
where
|
||||
T: Into<ChatId>,
|
||||
{
|
||||
self.chat_id = val.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Identifier of the message to edit
|
||||
pub fn message_id(mut self, val: i32) -> Self {
|
||||
self.message_id = val;
|
||||
self
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::{
|
||||
net,
|
||||
requests::{form_builder::FormBuilder, Request, ResponseResult},
|
||||
types::{ChatOrInlineMessage, InlineKeyboardMarkup, InputMedia, Message},
|
||||
types::{ChatId, InlineKeyboardMarkup, InputMedia, Message},
|
||||
Bot,
|
||||
};
|
||||
|
||||
|
@ -9,20 +9,17 @@ use crate::{
|
|||
/// messages.
|
||||
///
|
||||
/// If a message is a part of a message album, then it can be edited only to a
|
||||
/// photo or a video. Otherwise, message type can be changed arbitrarily. When
|
||||
/// inline message is edited, new file can't be uploaded. Use previously
|
||||
/// uploaded file via its `file_id` or specify a URL. On success, if the edited
|
||||
/// message was sent by the bot, the edited [`Message`] is returned,
|
||||
/// otherwise [`True`] is returned.
|
||||
/// photo or a video. Otherwise, message type can be changed arbitrarily. On
|
||||
/// success, the edited [`Message`] is returned.
|
||||
///
|
||||
/// [The official docs](https://core.telegram.org/bots/api#editmessagemedia).
|
||||
///
|
||||
/// [`Message`]: crate::types::Message
|
||||
/// [`True`]: crate::types::True
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EditMessageMedia {
|
||||
bot: Bot,
|
||||
chat_or_inline_message: ChatOrInlineMessage,
|
||||
chat_id: ChatId,
|
||||
message_id: i32,
|
||||
media: InputMedia,
|
||||
reply_markup: Option<InlineKeyboardMarkup>,
|
||||
}
|
||||
|
@ -32,22 +29,13 @@ impl Request for EditMessageMedia {
|
|||
type Output = Message;
|
||||
|
||||
async fn send(&self) -> ResponseResult<Message> {
|
||||
let mut params = FormBuilder::new();
|
||||
|
||||
match &self.chat_or_inline_message {
|
||||
ChatOrInlineMessage::Chat { chat_id, message_id } => {
|
||||
params = params.add_text("chat_id", chat_id).add_text("message_id", message_id);
|
||||
}
|
||||
ChatOrInlineMessage::Inline { inline_message_id } => {
|
||||
params = params.add_text("inline_message_id", inline_message_id);
|
||||
}
|
||||
}
|
||||
|
||||
net::request_multipart(
|
||||
self.bot.client(),
|
||||
self.bot.token(),
|
||||
"editMessageMedia",
|
||||
params
|
||||
FormBuilder::new()
|
||||
.add_text("chat_id", &self.chat_id)
|
||||
.add_text("message_id", &self.message_id)
|
||||
.add_text("media", &self.media)
|
||||
.add_text("reply_markup", &self.reply_markup)
|
||||
.build(),
|
||||
|
@ -57,16 +45,27 @@ impl Request for EditMessageMedia {
|
|||
}
|
||||
|
||||
impl EditMessageMedia {
|
||||
pub(crate) fn new(
|
||||
bot: Bot,
|
||||
chat_or_inline_message: ChatOrInlineMessage,
|
||||
media: InputMedia,
|
||||
) -> Self {
|
||||
Self { bot, chat_or_inline_message, media, reply_markup: None }
|
||||
pub(crate) fn new<C>(bot: Bot, chat_id: C, message_id: i32, media: InputMedia) -> Self
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
{
|
||||
let chat_id = chat_id.into();
|
||||
Self { bot, chat_id, message_id, media, reply_markup: None }
|
||||
}
|
||||
|
||||
pub fn chat_or_inline_message(mut self, val: ChatOrInlineMessage) -> Self {
|
||||
self.chat_or_inline_message = val;
|
||||
/// Unique identifier for the target chat or username of the target channel
|
||||
/// (in the format `@channelusername`)
|
||||
pub fn chat_id<T>(mut self, val: T) -> Self
|
||||
where
|
||||
T: Into<ChatId>,
|
||||
{
|
||||
self.chat_id = val.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Identifier of the message to edit
|
||||
pub fn message_id(mut self, val: i32) -> Self {
|
||||
self.message_id = val;
|
||||
self
|
||||
}
|
||||
|
||||
|
|
|
@ -3,26 +3,24 @@ use serde::Serialize;
|
|||
use crate::{
|
||||
net,
|
||||
requests::{Request, ResponseResult},
|
||||
types::{ChatOrInlineMessage, InlineKeyboardMarkup, Message},
|
||||
types::{ChatId, InlineKeyboardMarkup, Message},
|
||||
Bot,
|
||||
};
|
||||
|
||||
/// Use this method to edit only the reply markup of messages.
|
||||
///
|
||||
/// On success, if edited message is sent by the bot, the edited [`Message`] is
|
||||
/// returned, otherwise [`True`] is returned.
|
||||
/// On success, the edited [`Message`] is returned.
|
||||
///
|
||||
/// [The official docs](https://core.telegram.org/bots/api#editmessagereplymarkup).
|
||||
///
|
||||
/// [`Message`]: crate::types::Message
|
||||
/// [`True`]: crate::types::True
|
||||
#[serde_with_macros::skip_serializing_none]
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct EditMessageReplyMarkup {
|
||||
#[serde(skip_serializing)]
|
||||
bot: Bot,
|
||||
#[serde(flatten)]
|
||||
chat_or_inline_message: ChatOrInlineMessage,
|
||||
chat_id: ChatId,
|
||||
message_id: i32,
|
||||
reply_markup: Option<InlineKeyboardMarkup>,
|
||||
}
|
||||
|
||||
|
@ -37,12 +35,27 @@ impl Request for EditMessageReplyMarkup {
|
|||
}
|
||||
|
||||
impl EditMessageReplyMarkup {
|
||||
pub(crate) fn new(bot: Bot, chat_or_inline_message: ChatOrInlineMessage) -> Self {
|
||||
Self { bot, chat_or_inline_message, reply_markup: None }
|
||||
pub(crate) fn new<C>(bot: Bot, chat_id: C, message_id: i32) -> Self
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
{
|
||||
let chat_id = chat_id.into();
|
||||
Self { bot, chat_id, message_id, reply_markup: None }
|
||||
}
|
||||
|
||||
pub fn chat_or_inline_message(mut self, val: ChatOrInlineMessage) -> Self {
|
||||
self.chat_or_inline_message = val;
|
||||
/// Unique identifier for the target chat or username of the target channel
|
||||
/// (in the format `@channelusername`)
|
||||
pub fn chat_id<T>(mut self, val: T) -> Self
|
||||
where
|
||||
T: Into<ChatId>,
|
||||
{
|
||||
self.chat_id = val.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Identifier of the message to edit
|
||||
pub fn message_id(mut self, val: i32) -> Self {
|
||||
self.message_id = val;
|
||||
self
|
||||
}
|
||||
|
||||
|
|
|
@ -3,26 +3,24 @@ use serde::Serialize;
|
|||
use crate::{
|
||||
net,
|
||||
requests::{Request, ResponseResult},
|
||||
types::{ChatOrInlineMessage, InlineKeyboardMarkup, Message, ParseMode},
|
||||
types::{ChatId, InlineKeyboardMarkup, Message, ParseMode},
|
||||
Bot,
|
||||
};
|
||||
|
||||
/// Use this method to edit text and game messages.
|
||||
///
|
||||
/// On success, if edited message is sent by the bot, the edited [`Message`] is
|
||||
/// returned, otherwise [`True`] is returned.
|
||||
/// On success, the edited [`Message`] is returned.
|
||||
///
|
||||
/// [The official docs](https://core.telegram.org/bots/api#editmessagetext).
|
||||
///
|
||||
/// [`Message`]: crate::types::Message
|
||||
/// [`True`]: crate::types::True
|
||||
#[serde_with_macros::skip_serializing_none]
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct EditMessageText {
|
||||
#[serde(skip_serializing)]
|
||||
bot: Bot,
|
||||
#[serde(flatten)]
|
||||
chat_or_inline_message: ChatOrInlineMessage,
|
||||
chat_id: ChatId,
|
||||
message_id: i32,
|
||||
text: String,
|
||||
parse_mode: Option<ParseMode>,
|
||||
disable_web_page_preview: Option<bool>,
|
||||
|
@ -39,22 +37,37 @@ impl Request for EditMessageText {
|
|||
}
|
||||
|
||||
impl EditMessageText {
|
||||
pub(crate) fn new<T>(bot: Bot, chat_or_inline_message: ChatOrInlineMessage, text: T) -> Self
|
||||
pub(crate) fn new<C, T>(bot: Bot, chat_id: C, message_id: i32, text: T) -> Self
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
T: Into<String>,
|
||||
{
|
||||
let chat_id = chat_id.into();
|
||||
let text = text.into();
|
||||
Self {
|
||||
bot,
|
||||
chat_or_inline_message,
|
||||
text: text.into(),
|
||||
chat_id,
|
||||
message_id,
|
||||
text,
|
||||
parse_mode: None,
|
||||
disable_web_page_preview: None,
|
||||
reply_markup: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn chat_or_inline_message(mut self, val: ChatOrInlineMessage) -> Self {
|
||||
self.chat_or_inline_message = val;
|
||||
/// Unique identifier for the target chat or username of the target channel
|
||||
/// (in the format `@channelusername`)
|
||||
pub fn chat_id<T>(mut self, val: T) -> Self
|
||||
where
|
||||
T: Into<ChatId>,
|
||||
{
|
||||
self.chat_id = val.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Identifier of the message to edit
|
||||
pub fn message_id(mut self, val: i32) -> Self {
|
||||
self.message_id = val;
|
||||
self
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ use serde::Serialize;
|
|||
use crate::{
|
||||
net,
|
||||
requests::{Request, ResponseResult},
|
||||
types::{ChatOrInlineMessage, GameHighScore},
|
||||
types::{GameHighScore, TargetMessage},
|
||||
Bot,
|
||||
};
|
||||
|
||||
|
@ -18,14 +18,13 @@ use crate::{
|
|||
/// the user and his neighbors are not among them. Please note that this
|
||||
/// behavior is subject to change.
|
||||
///
|
||||
/// [The official docs](https://core.telegram.org/bots/api#getgamehighscores).
|
||||
#[serde_with_macros::skip_serializing_none]
|
||||
/// [The official docs](https://core.telegram.org/bots/api#getgamehighscores)
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct GetGameHighScores {
|
||||
#[serde(skip_serializing)]
|
||||
bot: Bot,
|
||||
#[serde(flatten)]
|
||||
chat_or_inline_message: ChatOrInlineMessage,
|
||||
target: TargetMessage,
|
||||
user_id: i32,
|
||||
}
|
||||
|
||||
|
@ -39,12 +38,20 @@ impl Request for GetGameHighScores {
|
|||
}
|
||||
|
||||
impl GetGameHighScores {
|
||||
pub(crate) fn new(bot: Bot, chat_or_inline_message: ChatOrInlineMessage, user_id: i32) -> Self {
|
||||
Self { bot, chat_or_inline_message, user_id }
|
||||
pub(crate) fn new<T>(bot: Bot, target: T, user_id: i32) -> Self
|
||||
where
|
||||
T: Into<TargetMessage>,
|
||||
{
|
||||
let target = target.into();
|
||||
Self { bot, target, user_id }
|
||||
}
|
||||
|
||||
pub fn chat_or_inline_message(mut self, val: ChatOrInlineMessage) -> Self {
|
||||
self.chat_or_inline_message = val;
|
||||
/// Target message, either chat id and message id or inline message id.
|
||||
pub fn target<T>(mut self, val: T) -> Self
|
||||
where
|
||||
T: Into<TargetMessage>,
|
||||
{
|
||||
self.target = val.into();
|
||||
self
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,11 @@ mod delete_chat_sticker_set;
|
|||
mod delete_message;
|
||||
mod delete_sticker_from_set;
|
||||
mod delete_webhook;
|
||||
mod edit_inline_message_caption;
|
||||
mod edit_inline_message_live_location;
|
||||
mod edit_inline_message_media;
|
||||
mod edit_inline_message_reply_markup;
|
||||
mod edit_inline_message_text;
|
||||
mod edit_message_caption;
|
||||
mod edit_message_live_location;
|
||||
mod edit_message_media;
|
||||
|
@ -62,6 +67,7 @@ mod set_my_commands;
|
|||
mod set_sticker_position_in_set;
|
||||
mod set_sticker_set_thumb;
|
||||
mod set_webhook;
|
||||
mod stop_inline_message_live_location;
|
||||
mod stop_message_live_location;
|
||||
mod stop_poll;
|
||||
mod unban_chat_member;
|
||||
|
@ -79,6 +85,11 @@ pub use delete_chat_sticker_set::*;
|
|||
pub use delete_message::*;
|
||||
pub use delete_sticker_from_set::*;
|
||||
pub use delete_webhook::*;
|
||||
pub use edit_inline_message_caption::*;
|
||||
pub use edit_inline_message_live_location::*;
|
||||
pub use edit_inline_message_media::*;
|
||||
pub use edit_inline_message_reply_markup::*;
|
||||
pub use edit_inline_message_text::*;
|
||||
pub use edit_message_caption::*;
|
||||
pub use edit_message_live_location::*;
|
||||
pub use edit_message_media::*;
|
||||
|
@ -133,6 +144,7 @@ pub use set_sticker_position_in_set::*;
|
|||
pub use set_sticker_set_thumb::*;
|
||||
pub use set_webhook::*;
|
||||
pub use std::pin::Pin;
|
||||
pub use stop_inline_message_live_location::*;
|
||||
pub use stop_message_live_location::*;
|
||||
pub use stop_poll::*;
|
||||
pub use unban_chat_member::*;
|
||||
|
|
|
@ -90,6 +90,19 @@ impl SendAnimation {
|
|||
}
|
||||
|
||||
/// Animation to send.
|
||||
///
|
||||
/// Pass [`InputFile::FileId`] to send a file that exists on the Telegram
|
||||
/// servers (recommended), pass an [`InputFile::Url`] for Telegram to get a
|
||||
/// file from the Internet (20MB max.), pass [`InputFile::File`] to upload
|
||||
/// a file from the file system or [`InputFile::Memory`] to upload a file
|
||||
/// from memory (50MB max. each). [More info on Sending Files »].
|
||||
///
|
||||
/// [`InputFile::FileId`]: crate::types::InputFile::FileId
|
||||
/// [`InputFile::Url`]: crate::types::InputFile::Url
|
||||
/// [`InputFile::File`]: crate::types::InputFile::File
|
||||
/// [`InputFile::Memory`]: crate::types::InputFile::Memory
|
||||
///
|
||||
/// [More info on Sending Files »]: https://core.telegram.org/bots/api#sending-files
|
||||
pub fn animation(mut self, val: InputFile) -> Self {
|
||||
self.animation = val;
|
||||
self
|
||||
|
@ -116,13 +129,18 @@ impl SendAnimation {
|
|||
/// Thumbnail of the file sent; can be ignored if thumbnail generation for
|
||||
/// the file is supported server-side.
|
||||
///
|
||||
/// The thumbnail should be in JPEG format and less than 200 kB in size. A
|
||||
/// The thumbnail should be in JPEG format and less than 200kB in size. A
|
||||
/// thumbnail‘s width and height should not exceed 320. Ignored if the
|
||||
/// file is not uploaded using [`InputFile::File`]. Thumbnails can’t be
|
||||
/// reused and can be only uploaded as a new file, with
|
||||
/// [`InputFile::File`].
|
||||
/// animation is not uploaded using [`InputFile::File`] or
|
||||
/// [`InputFile::Memory`]. Thumbnails can’t be reused and can be only
|
||||
/// uploaded as a new file. Pass [`InputFile::File`] to upload a file from
|
||||
/// the file system or [`InputFile::Memory`] to upload a file from memory.
|
||||
/// [More info on Sending Files »].
|
||||
///
|
||||
/// [`InputFile::File`]: crate::types::InputFile::File
|
||||
/// [`InputFile::Memory`]: crate::types::InputFile::Memory
|
||||
///
|
||||
/// [More info on Sending Files »]: https://core.telegram.org/bots/api#sending-files
|
||||
pub fn thumb(mut self, value: InputFile) -> Self {
|
||||
self.thumb = Some(value);
|
||||
self
|
||||
|
|
|
@ -95,14 +95,17 @@ impl SendAudio {
|
|||
|
||||
/// Audio file to send.
|
||||
///
|
||||
/// Pass [`InputFile::File`] to send a file that exists on
|
||||
/// the Telegram servers (recommended), pass an [`InputFile::Url`] for
|
||||
/// Telegram to get a .webp file from the Internet, or upload a new one
|
||||
/// using [`InputFile::FileId`]. [More info on Sending Files »].
|
||||
/// Pass [`InputFile::FileId`] to send an audio file that exists on the
|
||||
/// Telegram servers (recommended), pass an [`InputFile::Url`] for Telegram
|
||||
/// to get a file from the Internet (20MB max.), pass [`InputFile::File`]
|
||||
/// to upload a file from the file system or [`InputFile::Memory`] to
|
||||
/// upload a file from memory (50MB max. each).
|
||||
/// [More info on Sending Files »].
|
||||
///
|
||||
/// [`InputFile::File`]: crate::types::InputFile::File
|
||||
/// [`InputFile::Url`]: crate::types::InputFile::Url
|
||||
/// [`InputFile::FileId`]: crate::types::InputFile::FileId
|
||||
/// [`InputFile::Url`]: crate::types::InputFile::Url
|
||||
/// [`InputFile::File`]: crate::types::InputFile::File
|
||||
/// [`InputFile::Memory`]: crate::types::InputFile::Memory
|
||||
///
|
||||
/// [More info on Sending Files »]: https://core.telegram.org/bots/api#sending-files
|
||||
pub fn audio(mut self, val: InputFile) -> Self {
|
||||
|
@ -158,13 +161,16 @@ impl SendAudio {
|
|||
/// Thumbnail of the file sent; can be ignored if thumbnail generation for
|
||||
/// the file is supported server-side.
|
||||
///
|
||||
/// The thumbnail should be in JPEG format and less than 200 kB in size. A
|
||||
/// The thumbnail should be in JPEG format and less than 200kB in size. A
|
||||
/// thumbnail‘s width and height should not exceed 320. Ignored if the
|
||||
/// file is not uploaded using `multipart/form-data`. Thumbnails can’t
|
||||
/// be reused and can be only uploaded as a new file, so you can pass
|
||||
/// `attach://<file_attach_name>` if the thumbnail was uploaded using
|
||||
/// `multipart/form-data` under `<file_attach_name>`. [More info on
|
||||
/// Sending Files »].
|
||||
/// audio file is not uploaded using [`InputFile::File`] or
|
||||
/// [`InputFile::Memory`]. Thumbnails can’t be reused and can be only
|
||||
/// uploaded as a new file. Pass [`InputFile::File`] to upload a file from
|
||||
/// the file system or [`InputFile::Memory`] to upload a file from memory.
|
||||
/// [More info on Sending Files »].
|
||||
///
|
||||
/// [`InputFile::File`]: crate::types::InputFile::File
|
||||
/// [`InputFile::Memory`]: crate::types::InputFile::Memory
|
||||
///
|
||||
/// [More info on Sending Files »]: https://core.telegram.org/bots/api#sending-files
|
||||
pub fn thumb(mut self, val: InputFile) -> Self {
|
||||
|
|
|
@ -81,10 +81,16 @@ impl SendDocument {
|
|||
|
||||
/// File to send.
|
||||
///
|
||||
/// Pass a file_id as String to send a file that exists on the
|
||||
/// Telegram servers (recommended), pass an HTTP URL as a String for
|
||||
/// Telegram to get a file from the Internet, or upload a new one using
|
||||
/// `multipart/form-data`. [More info on Sending Files »].
|
||||
/// Pass [`InputFile::FileId`] to send a file that exists on the Telegram
|
||||
/// servers (recommended), pass an [`InputFile::Url`] for Telegram to get a
|
||||
/// file from the Internet (20MB max.), pass [`InputFile::File`] to upload
|
||||
/// a file from the file system or [`InputFile::Memory`] to upload a file
|
||||
/// from memory (50MB max. each). [More info on Sending Files »].
|
||||
///
|
||||
/// [`InputFile::FileId`]: crate::types::InputFile::FileId
|
||||
/// [`InputFile::Url`]: crate::types::InputFile::Url
|
||||
/// [`InputFile::File`]: crate::types::InputFile::File
|
||||
/// [`InputFile::Memory`]: crate::types::InputFile::Memory
|
||||
///
|
||||
/// [More info on Sending Files »]: https://core.telegram.org/bots/api#sending-files
|
||||
pub fn document(mut self, val: InputFile) -> Self {
|
||||
|
@ -95,13 +101,16 @@ impl SendDocument {
|
|||
/// Thumbnail of the file sent; can be ignored if thumbnail generation for
|
||||
/// the file is supported server-side.
|
||||
///
|
||||
/// The thumbnail should be in JPEG format and less than 200 kB in size. A
|
||||
/// The thumbnail should be in JPEG format and less than 200kB in size. A
|
||||
/// thumbnail‘s width and height should not exceed 320. Ignored if the
|
||||
/// file is not uploaded using `multipart/form-data`. Thumbnails can’t
|
||||
/// be reused and can be only uploaded as a new file, so you can pass
|
||||
/// “attach://<file_attach_name>” if the thumbnail was uploaded using
|
||||
/// `multipart/form-data` under `<file_attach_name>`. [More info on
|
||||
/// Sending Files »].
|
||||
/// document is not uploaded using [`InputFile::File`] or
|
||||
/// [`InputFile::Memory`]. Thumbnails can’t be reused and can be only
|
||||
/// uploaded as a new file. Pass [`InputFile::File`] to upload a file from
|
||||
/// the file system or [`InputFile::Memory`] to upload a file from memory.
|
||||
/// [More info on Sending Files »].
|
||||
///
|
||||
/// [`InputFile::File`]: crate::types::InputFile::File
|
||||
/// [`InputFile::Memory`]: crate::types::InputFile::Memory
|
||||
///
|
||||
/// [More info on Sending Files »]: https://core.telegram.org/bots/api#sending-files
|
||||
pub fn thumb(mut self, val: InputFile) -> Self {
|
||||
|
|
|
@ -73,14 +73,16 @@ impl SendPhoto {
|
|||
|
||||
/// Photo to send.
|
||||
///
|
||||
/// Pass [`InputFile::File`] to send a photo that exists on
|
||||
/// the Telegram servers (recommended), pass an [`InputFile::Url`] for
|
||||
/// Telegram to get a .webp file from the Internet, or upload a new one
|
||||
/// using [`InputFile::FileId`]. [More info on Sending Files »].
|
||||
/// Pass [`InputFile::FileId`] to send a photo that exists on the Telegram
|
||||
/// servers (recommended), pass an [`InputFile::Url`] for Telegram to get a
|
||||
/// photo from the Internet (5MB max.), pass [`InputFile::File`] to upload
|
||||
/// a picture from the file system or [`InputFile::Memory`] to upload a
|
||||
/// photo from memory (10MB max. each). [More info on Sending Files »].
|
||||
///
|
||||
/// [`InputFile::File`]: crate::types::InputFile::File
|
||||
/// [`InputFile::Url`]: crate::types::InputFile::Url
|
||||
/// [`InputFile::FileId`]: crate::types::InputFile::FileId
|
||||
/// [`InputFile::Url`]: crate::types::InputFile::Url
|
||||
/// [`InputFile::File`]: crate::types::InputFile::File
|
||||
/// [`InputFile::Memory`]: crate::types::InputFile::Memory
|
||||
///
|
||||
/// [More info on Sending Files »]: https://core.telegram.org/bots/api#sending-files
|
||||
pub fn photo(mut self, val: InputFile) -> Self {
|
||||
|
|
|
@ -69,14 +69,18 @@ impl SendSticker {
|
|||
|
||||
/// Sticker to send.
|
||||
///
|
||||
/// Pass [`InputFile::File`] to send a file that exists on
|
||||
/// the Telegram servers (recommended), pass an [`InputFile::Url`] for
|
||||
/// Telegram to get a .webp file from the Internet, or upload a new one
|
||||
/// using [`InputFile::FileId`]. [More info on Sending Files »].
|
||||
/// Pass [`InputFile::FileId`] to send a sticker that exists on the
|
||||
/// Telegram servers (recommended), pass an [`InputFile::Url`] for Telegram
|
||||
/// to get a sticker (.WEBP file) from the Internet, pass
|
||||
/// [`InputFile::File`] to upload a sticker from the file system or
|
||||
/// [`InputFile::Memory`] to upload a sticker from memory
|
||||
/// [More info on Sending Files »].
|
||||
///
|
||||
/// [`InputFile::File`]: crate::types::InputFile::File
|
||||
/// [`InputFile::Url`]: crate::types::InputFile::Url
|
||||
/// [`InputFile::FileId`]: crate::types::InputFile::FileId
|
||||
/// [`InputFile::Url`]: crate::types::InputFile::Url
|
||||
/// [`InputFile::File`]: crate::types::InputFile::File
|
||||
/// [`InputFile::Memory`]: crate::types::InputFile::Memory
|
||||
///
|
||||
/// [More info on Sending Files »]: https://core.telegram.org/bots/api#sending-files
|
||||
pub fn sticker(mut self, val: InputFile) -> Self {
|
||||
self.sticker = val;
|
||||
|
|
|
@ -92,16 +92,20 @@ impl SendVideo {
|
|||
self
|
||||
}
|
||||
|
||||
/// Video to sent.
|
||||
/// Video to send.
|
||||
///
|
||||
/// Pass [`InputFile::File`] to send a file that exists on
|
||||
/// the Telegram servers (recommended), pass an [`InputFile::Url`] for
|
||||
/// Telegram to get a .webp file from the Internet, or upload a new one
|
||||
/// using [`InputFile::FileId`]. [More info on Sending Files »].
|
||||
/// Pass [`InputFile::FileId`] to send a file that exists on the Telegram
|
||||
/// servers (recommended), pass an [`InputFile::Url`] for Telegram to get a
|
||||
/// file from the Internet (20MB max.), pass [`InputFile::File`] to upload
|
||||
/// a file from the file system or [`InputFile::Memory`] to upload a file
|
||||
/// from memory (50MB max. each). [More info on Sending Files »].
|
||||
///
|
||||
/// [`InputFile::File`]: crate::types::InputFile::File
|
||||
/// [`InputFile::Url`]: crate::types::InputFile::Url
|
||||
/// [`InputFile::FileId`]: crate::types::InputFile::FileId
|
||||
/// [`InputFile::Url`]: crate::types::InputFile::Url
|
||||
/// [`InputFile::File`]: crate::types::InputFile::File
|
||||
/// [`InputFile::Memory`]: crate::types::InputFile::Memory
|
||||
///
|
||||
/// [More info on Sending Files »]: https://core.telegram.org/bots/api#sending-files
|
||||
pub fn video(mut self, val: InputFile) -> Self {
|
||||
self.video = val;
|
||||
self
|
||||
|
@ -128,13 +132,16 @@ impl SendVideo {
|
|||
/// Thumbnail of the file sent; can be ignored if thumbnail generation for
|
||||
/// the file is supported server-side.
|
||||
///
|
||||
/// The thumbnail should be in JPEG format and less than 200 kB in size. A
|
||||
/// The thumbnail should be in JPEG format and less than 200kB in size. A
|
||||
/// thumbnail‘s width and height should not exceed 320. Ignored if the
|
||||
/// file is not uploaded using `multipart/form-data`. Thumbnails can’t be
|
||||
/// reused and can be only uploaded as a new file, so you can pass
|
||||
/// `attach://<file_attach_name>` if the thumbnail was uploaded using
|
||||
/// `multipart/form-data` under `<file_attach_name>`. [More info on Sending
|
||||
/// Files »].
|
||||
/// video file is not uploaded using [`InputFile::File`] or
|
||||
/// [`InputFile::Memory`]. Thumbnails can’t be reused and can be only
|
||||
/// uploaded as a new file. Pass [`InputFile::File`] to upload a file from
|
||||
/// the file system or [`InputFile::Memory`] to upload a file from memory.
|
||||
/// [More info on Sending Files »].
|
||||
///
|
||||
/// [`InputFile::File`]: crate::types::InputFile::File
|
||||
/// [`InputFile::Memory`]: crate::types::InputFile::Memory
|
||||
///
|
||||
/// [More info on Sending Files »]: https://core.telegram.org/bots/api#sending-files
|
||||
pub fn thumb(mut self, val: InputFile) -> Self {
|
||||
|
|
|
@ -81,14 +81,17 @@ impl SendVideoNote {
|
|||
|
||||
/// Video note to send.
|
||||
///
|
||||
/// Pass [`InputFile::File`] to send a file that exists on
|
||||
/// the Telegram servers (recommended), pass an [`InputFile::Url`] for
|
||||
/// Telegram to get a .webp file from the Internet, or upload a new one
|
||||
/// using [`InputFile::FileId`]. [More info on Sending Files »].
|
||||
/// Pass [`InputFile::FileId`] to send a file that exists on the Telegram
|
||||
/// servers (recommended), pass an [`InputFile::Url`] for Telegram to get a
|
||||
/// file from the Internet (20MB max.), pass [`InputFile::File`] to upload
|
||||
/// a file from the file system or [`InputFile::Memory`] to upload a file
|
||||
/// from memory (50MB max. each). [More info on Sending Files »].
|
||||
///
|
||||
/// [`InputFile::File`]: crate::types::InputFile::File
|
||||
/// [`InputFile::Url`]: crate::types::InputFile::Url
|
||||
/// [`InputFile::FileId`]: crate::types::InputFile::FileId
|
||||
/// [`InputFile::Url`]: crate::types::InputFile::Url
|
||||
/// [`InputFile::File`]: crate::types::InputFile::File
|
||||
/// [`InputFile::Memory`]: crate::types::InputFile::Memory
|
||||
///
|
||||
/// [More info on Sending Files »]: https://core.telegram.org/bots/api#sending-files
|
||||
pub fn video_note(mut self, val: InputFile) -> Self {
|
||||
self.video_note = val;
|
||||
|
@ -110,13 +113,18 @@ impl SendVideoNote {
|
|||
/// Thumbnail of the file sent; can be ignored if thumbnail generation for
|
||||
/// the file is supported server-side.
|
||||
///
|
||||
/// The thumbnail should be in JPEG format and less than 200 kB in size. A
|
||||
/// The thumbnail should be in JPEG format and less than 200kB in size. A
|
||||
/// thumbnail‘s width and height should not exceed 320. Ignored if the
|
||||
/// file is not uploaded using `multipart/form-data`. Thumbnails can’t
|
||||
/// be reused and can be only uploaded as a new file, so you can pass
|
||||
/// `attach://<file_attach_name>` if the thumbnail was uploaded using
|
||||
/// `multipart/form-data` under `<file_attach_name>`. [More info on
|
||||
/// Sending Files »].
|
||||
/// video note is not uploaded using [`InputFile::File`] or
|
||||
/// [`InputFile::Memory`]. Thumbnails can’t be reused and can be only
|
||||
/// uploaded as a new file. Pass [`InputFile::File`] to upload a file from
|
||||
/// the file system or [`InputFile::Memory`] to upload a file from memory.
|
||||
/// [More info on Sending Files »].
|
||||
///
|
||||
/// [`InputFile::File`]: crate::types::InputFile::File
|
||||
/// [`InputFile::Memory`]: crate::types::InputFile::Memory
|
||||
///
|
||||
/// [More info on Sending Files »]: https://core.telegram.org/bots/api#sending-files
|
||||
pub fn thumb(mut self, val: InputFile) -> Self {
|
||||
self.thumb = Some(val);
|
||||
self
|
||||
|
|
|
@ -85,14 +85,17 @@ impl SendVoice {
|
|||
|
||||
/// Audio file to send.
|
||||
///
|
||||
/// Pass [`InputFile::File`] to send a file that exists on
|
||||
/// the Telegram servers (recommended), pass an [`InputFile::Url`] for
|
||||
/// Telegram to get a .webp file from the Internet, or upload a new one
|
||||
/// using [`InputFile::FileId`]. [More info on Sending Files »].
|
||||
/// Pass [`InputFile::FileId`] to send a file that exists on the Telegram
|
||||
/// servers (recommended), pass an [`InputFile::Url`] for Telegram to get a
|
||||
/// file from the Internet (20MB max.), pass [`InputFile::File`] to upload
|
||||
/// a file from the file system or [`InputFile::Memory`] to upload a file
|
||||
/// from memory (50MB max. each). [More info on Sending Files »].
|
||||
///
|
||||
/// [`InputFile::File`]: crate::types::InputFile::File
|
||||
/// [`InputFile::Url`]: crate::types::InputFile::Url
|
||||
/// [`InputFile::FileId`]: crate::types::InputFile::FileId
|
||||
/// [`InputFile::Url`]: crate::types::InputFile::Url
|
||||
/// [`InputFile::File`]: crate::types::InputFile::File
|
||||
/// [`InputFile::Memory`]: crate::types::InputFile::Memory
|
||||
///
|
||||
/// [More info on Sending Files »]: https://core.telegram.org/bots/api#sending-files
|
||||
pub fn voice(mut self, val: InputFile) -> Self {
|
||||
self.voice = val;
|
||||
|
|
|
@ -3,7 +3,7 @@ use serde::Serialize;
|
|||
use crate::{
|
||||
net,
|
||||
requests::{Request, ResponseResult},
|
||||
types::{ChatOrInlineMessage, Message},
|
||||
types::{Message, TargetMessage},
|
||||
Bot,
|
||||
};
|
||||
|
||||
|
@ -24,7 +24,7 @@ pub struct SetGameScore {
|
|||
#[serde(skip_serializing)]
|
||||
bot: Bot,
|
||||
#[serde(flatten)]
|
||||
chat_or_inline_message: ChatOrInlineMessage,
|
||||
target: TargetMessage,
|
||||
user_id: i32,
|
||||
score: i32,
|
||||
force: Option<bool>,
|
||||
|
@ -41,24 +41,20 @@ impl Request for SetGameScore {
|
|||
}
|
||||
|
||||
impl SetGameScore {
|
||||
pub(crate) fn new(
|
||||
bot: Bot,
|
||||
chat_or_inline_message: ChatOrInlineMessage,
|
||||
user_id: i32,
|
||||
score: i32,
|
||||
) -> Self {
|
||||
Self {
|
||||
bot,
|
||||
chat_or_inline_message,
|
||||
user_id,
|
||||
score,
|
||||
force: None,
|
||||
disable_edit_message: None,
|
||||
}
|
||||
pub(crate) fn new<T>(bot: Bot, target: T, user_id: i32, score: i32) -> Self
|
||||
where
|
||||
T: Into<TargetMessage>,
|
||||
{
|
||||
let target = target.into();
|
||||
Self { bot, target, user_id, score, force: None, disable_edit_message: None }
|
||||
}
|
||||
|
||||
pub fn chat_or_inline_message(mut self, val: ChatOrInlineMessage) -> Self {
|
||||
self.chat_or_inline_message = val;
|
||||
/// Target message, either chat id and message id or inline message id.
|
||||
pub fn target<T>(mut self, val: T) -> Self
|
||||
where
|
||||
T: Into<TargetMessage>,
|
||||
{
|
||||
self.target = val.into();
|
||||
self
|
||||
}
|
||||
|
||||
|
|
|
@ -58,11 +58,12 @@ impl SetStickerSetThumb {
|
|||
/// thumbnail up to 32 kilobytes in size; see https://core.telegram.org/animated_stickers#technical-requirements
|
||||
/// for animated sticker technical requirements.
|
||||
///
|
||||
/// Pass [`InputFile::FileId`] as a String to send a file that already
|
||||
/// exists on the Telegram servers, pass [`InputFile::Url`] for Telegram
|
||||
/// to get a file from the Internet, or upload a new one using
|
||||
/// multipart/form-data. More info on Sending Files ». Animated sticker
|
||||
/// set thumbnail can't be uploaded via HTTP URL.
|
||||
/// Pass [`InputFile::FileId`] to send a file that exists on the Telegram
|
||||
/// servers (recommended), pass an [`InputFile::Url`] for Telegram to get a
|
||||
/// file from the Internet (20MB max.), pass [`InputFile::File`] to upload
|
||||
/// a file from the file system or [`InputFile::Memory`] to upload a file
|
||||
/// from memory (50MB max. each). [More info on Sending Files »]. Animated
|
||||
/// sticker set thumbnail can't be uploaded via HTTP URL.
|
||||
///
|
||||
/// [`InputFile::FileId`]: crate::types::InputFile::FileId
|
||||
/// [`InputFile::Url]: crate::types::InputFile::Url
|
||||
|
|
62
src/requests/all/stop_inline_message_live_location.rs
Normal file
62
src/requests/all/stop_inline_message_live_location.rs
Normal file
|
@ -0,0 +1,62 @@
|
|||
use serde::Serialize;
|
||||
|
||||
use crate::{
|
||||
net,
|
||||
requests::{Request, ResponseResult},
|
||||
types::{InlineKeyboardMarkup, Message},
|
||||
Bot,
|
||||
};
|
||||
|
||||
/// Use this method to stop updating a live location message (sent via the bot)
|
||||
/// before `live_period` expires.
|
||||
///
|
||||
/// On success, [`True`] is returned.
|
||||
///
|
||||
/// [The official docs](https://core.telegram.org/bots/api#stopmessagelivelocation).
|
||||
///
|
||||
/// [`True`]: crate::types::True
|
||||
#[serde_with_macros::skip_serializing_none]
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct StopInlineMessageLiveLocation {
|
||||
#[serde(skip_serializing)]
|
||||
bot: Bot,
|
||||
inline_message_id: String,
|
||||
reply_markup: Option<InlineKeyboardMarkup>,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl Request for StopInlineMessageLiveLocation {
|
||||
type Output = Message;
|
||||
|
||||
async fn send(&self) -> ResponseResult<Message> {
|
||||
net::request_json(self.bot.client(), self.bot.token(), "stopMessageLiveLocation", &self)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
impl StopInlineMessageLiveLocation {
|
||||
pub(crate) fn new<I>(bot: Bot, inline_message_id: I) -> Self
|
||||
where
|
||||
I: Into<String>,
|
||||
{
|
||||
let inline_message_id = inline_message_id.into();
|
||||
Self { bot, inline_message_id, reply_markup: None }
|
||||
}
|
||||
|
||||
/// Identifier of the inline message.
|
||||
pub fn inline_message_id<T>(mut self, val: T) -> Self
|
||||
where
|
||||
T: Into<String>,
|
||||
{
|
||||
self.inline_message_id = val.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// A JSON-serialized object for a new [inline keyboard].
|
||||
///
|
||||
/// [inline keyboard]: https://core.telegram.org/bots#inline-keyboards-and-on-the-fly-updating
|
||||
pub fn reply_markup(mut self, val: InlineKeyboardMarkup) -> Self {
|
||||
self.reply_markup = Some(val);
|
||||
self
|
||||
}
|
||||
}
|
|
@ -3,27 +3,25 @@ use serde::Serialize;
|
|||
use crate::{
|
||||
net,
|
||||
requests::{Request, ResponseResult},
|
||||
types::{ChatOrInlineMessage, InlineKeyboardMarkup, Message},
|
||||
types::{ChatId, InlineKeyboardMarkup, Message},
|
||||
Bot,
|
||||
};
|
||||
|
||||
/// Use this method to stop updating a live location message before
|
||||
/// `live_period` expires.
|
||||
///
|
||||
/// On success, if the message was sent by the bot, the sent [`Message`] is
|
||||
/// returned, otherwise [`True`] is returned.
|
||||
/// On success, the sent [`Message`] is returned.
|
||||
///
|
||||
/// [The official docs](https://core.telegram.org/bots/api#stopmessagelivelocation).
|
||||
///
|
||||
/// [`Message`]: crate::types::Message
|
||||
/// [`True`]: crate::types::True
|
||||
#[serde_with_macros::skip_serializing_none]
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct StopMessageLiveLocation {
|
||||
#[serde(skip_serializing)]
|
||||
bot: Bot,
|
||||
#[serde(flatten)]
|
||||
chat_or_inline_message: ChatOrInlineMessage,
|
||||
chat_id: ChatId,
|
||||
message_id: i32,
|
||||
reply_markup: Option<InlineKeyboardMarkup>,
|
||||
}
|
||||
|
||||
|
@ -38,12 +36,27 @@ impl Request for StopMessageLiveLocation {
|
|||
}
|
||||
|
||||
impl StopMessageLiveLocation {
|
||||
pub(crate) fn new(bot: Bot, chat_or_inline_message: ChatOrInlineMessage) -> Self {
|
||||
Self { bot, chat_or_inline_message, reply_markup: None }
|
||||
pub(crate) fn new<C>(bot: Bot, chat_id: C, message_id: i32) -> Self
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
{
|
||||
let chat_id = chat_id.into();
|
||||
Self { bot, chat_id, message_id, reply_markup: None }
|
||||
}
|
||||
|
||||
pub fn chat_or_inline_message(mut self, val: ChatOrInlineMessage) -> Self {
|
||||
self.chat_or_inline_message = val;
|
||||
/// Unique identifier for the target chat or username of the target channel
|
||||
/// (in the format `@channelusername`)
|
||||
pub fn chat_id<T>(mut self, val: T) -> Self
|
||||
where
|
||||
T: Into<ChatId>,
|
||||
{
|
||||
self.chat_id = val.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Identifier of the message to edit
|
||||
pub fn message_id(mut self, val: i32) -> Self {
|
||||
self.message_id = val;
|
||||
self
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,11 @@ pub use all::*;
|
|||
/// A type that is returned after making a request to Telegram.
|
||||
pub type ResponseResult<T> = Result<T, crate::RequestError>;
|
||||
|
||||
/// A shortcut for `ResponseResult::Ok(val)`.
|
||||
pub fn respond<T>(val: T) -> ResponseResult<T> {
|
||||
ResponseResult::Ok(val)
|
||||
}
|
||||
|
||||
/// Designates an API request.
|
||||
#[async_trait::async_trait]
|
||||
pub trait Request {
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
use crate::types::ChatId;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// A chat message or inline message.
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
#[non_exhaustive]
|
||||
pub enum ChatOrInlineMessage {
|
||||
Chat { chat_id: ChatId, message_id: i32 },
|
||||
Inline { inline_message_id: i32 },
|
||||
}
|
|
@ -10,7 +10,6 @@ pub use chat::*;
|
|||
pub use chat_action::*;
|
||||
pub use chat_id::*;
|
||||
pub use chat_member::*;
|
||||
pub use chat_or_inline_message::*;
|
||||
pub use chat_permissions::*;
|
||||
pub use chat_photo::*;
|
||||
pub use chosen_inline_result::*;
|
||||
|
@ -83,6 +82,7 @@ pub use sticker::*;
|
|||
pub use sticker_set::*;
|
||||
pub use sticker_type::*;
|
||||
pub use successful_payment::*;
|
||||
pub use target_message::*;
|
||||
pub use unit_false::*;
|
||||
pub use unit_true::*;
|
||||
pub use update::*;
|
||||
|
@ -104,7 +104,6 @@ mod chat;
|
|||
mod chat_action;
|
||||
mod chat_id;
|
||||
mod chat_member;
|
||||
mod chat_or_inline_message;
|
||||
mod chat_permissions;
|
||||
mod chat_photo;
|
||||
mod chosen_inline_result;
|
||||
|
@ -150,6 +149,7 @@ mod sticker;
|
|||
mod sticker_set;
|
||||
mod sticker_type;
|
||||
mod successful_payment;
|
||||
mod target_message;
|
||||
mod unit_false;
|
||||
mod unit_true;
|
||||
mod update;
|
||||
|
|
|
@ -7,14 +7,16 @@ pub enum StickerType {
|
|||
/// dimensions must not exceed 512px, and either width or height must be
|
||||
/// exactly 512px.
|
||||
///
|
||||
/// Pass [`InputFile::File`] to send a file that exists on
|
||||
/// the Telegram servers (recommended), pass an [`InputFile::Url`] for
|
||||
/// Telegram to get a .webp file from the Internet, or upload a new one
|
||||
/// using [`InputFile::FileId`]. [More info on Sending Files »].
|
||||
/// Pass [`InputFile::FileId`] to send a sticker that exists on the Telegram
|
||||
/// servers (recommended), pass an [`InputFile::Url`] for Telegram to get a
|
||||
/// sticker (.WEBP file) from the Internet, pass [`InputFile::File`] to
|
||||
/// upload a sticker from the file system or [`InputFile::Memory`] to upload
|
||||
/// a sticker from memory [More info on Sending Files »].
|
||||
///
|
||||
/// [`InputFile::File`]: crate::types::InputFile::File
|
||||
/// [`InputFile::Url`]: crate::types::InputFile::Url
|
||||
/// [`InputFile::FileId`]: crate::types::InputFile::FileId
|
||||
/// [`InputFile::Url`]: crate::types::InputFile::Url
|
||||
/// [`InputFile::File`]: crate::types::InputFile::File
|
||||
/// [`InputFile::Memory`]: crate::types::InputFile::Memory
|
||||
///
|
||||
/// [More info on Sending Files »]: https://core.telegram.org/bots/api#sending-files
|
||||
Png(InputFile),
|
||||
|
|
17
src/types/target_message.rs
Normal file
17
src/types/target_message.rs
Normal file
|
@ -0,0 +1,17 @@
|
|||
use crate::types::ChatId;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// A message in chat or inline message.
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum TargetMessage {
|
||||
Common { chat_id: ChatId, message_id: i32 },
|
||||
Inline { inline_message_id: String },
|
||||
}
|
||||
|
||||
impl From<String> for TargetMessage {
|
||||
fn from(inline_message_id: String) -> Self {
|
||||
Self::Inline { inline_message_id }
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@
|
|||
//!
|
||||
//! # Using BotCommand
|
||||
//! ```
|
||||
//! # #[cfg(feature = "macros")] {
|
||||
//! use teloxide::utils::command::BotCommand;
|
||||
//!
|
||||
//! type UnitOfTime = u8;
|
||||
|
@ -19,6 +20,7 @@
|
|||
//!
|
||||
//! let command = AdminCommand::parse("/ban 5 h", "bot_name").unwrap();
|
||||
//! assert_eq!(command, AdminCommand::Ban(5, 'h'));
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! # Using parse_command
|
||||
|
@ -48,12 +50,18 @@ use std::{
|
|||
error::Error,
|
||||
fmt::{Display, Formatter},
|
||||
};
|
||||
|
||||
|
||||
#[cfg(feature = "macros")]
|
||||
// FIXME(waffle): use `docsrs` here when issue with combine is resolved <https://github.com/teloxide/teloxide/pull/305#issuecomment-716172103>
|
||||
#[cfg_attr(all(teloxide_docsrs, feature = "nightly"), doc(cfg(feature = "macros")))]
|
||||
pub use teloxide_macros::BotCommand;
|
||||
|
||||
/// An enumeration of bot's commands.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # #[cfg(feature = "macros")] {
|
||||
/// use teloxide::utils::command::BotCommand;
|
||||
///
|
||||
/// type UnitOfTime = u8;
|
||||
|
@ -67,6 +75,7 @@ pub use teloxide_macros::BotCommand;
|
|||
///
|
||||
/// let command = AdminCommand::parse("/ban 5 h", "bot_name").unwrap();
|
||||
/// assert_eq!(command, AdminCommand::Ban(5, 'h'));
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// ## Enum attributes
|
||||
|
@ -88,6 +97,7 @@ pub use teloxide_macros::BotCommand;
|
|||
///
|
||||
/// ### Example
|
||||
/// ```
|
||||
/// # #[cfg(feature = "macros")] {
|
||||
/// use teloxide::utils::command::BotCommand;
|
||||
///
|
||||
/// #[derive(BotCommand, PartialEq, Debug)]
|
||||
|
@ -98,6 +108,7 @@ pub use teloxide_macros::BotCommand;
|
|||
///
|
||||
/// let command = Command::parse("/text hello my dear friend!", "").unwrap();
|
||||
/// assert_eq!(command, Command::Text("hello my dear friend!".to_string()));
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// - `split` - separates a messsage by a given separator (the default is the
|
||||
|
@ -106,6 +117,7 @@ pub use teloxide_macros::BotCommand;
|
|||
///
|
||||
/// ### Example
|
||||
/// ```
|
||||
/// # #[cfg(feature = "macros")] {
|
||||
/// use teloxide::utils::command::BotCommand;
|
||||
///
|
||||
/// #[derive(BotCommand, PartialEq, Debug)]
|
||||
|
@ -116,6 +128,7 @@ pub use teloxide_macros::BotCommand;
|
|||
///
|
||||
/// let command = Command::parse("/nums 1 32 -5", "").unwrap();
|
||||
/// assert_eq!(command, Command::Nums(1, 32, -5));
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// 5. `#[command(separator = "sep")]`
|
||||
|
@ -124,6 +137,7 @@ pub use teloxide_macros::BotCommand;
|
|||
///
|
||||
/// ### Example
|
||||
/// ```
|
||||
/// # #[cfg(feature = "macros")] {
|
||||
/// use teloxide::utils::command::BotCommand;
|
||||
///
|
||||
/// #[derive(BotCommand, PartialEq, Debug)]
|
||||
|
@ -134,6 +148,7 @@ pub use teloxide_macros::BotCommand;
|
|||
///
|
||||
/// let command = Command::parse("/nums 1|32|5", "").unwrap();
|
||||
/// assert_eq!(command, Command::Nums(1, 32, 5));
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// ## Variant attributes
|
||||
|
@ -151,6 +166,7 @@ pub use teloxide_macros::BotCommand;
|
|||
///
|
||||
/// ### Example
|
||||
/// ```
|
||||
/// # #[cfg(feature = "macros")] {
|
||||
/// use teloxide::utils::command::{BotCommand, ParseError};
|
||||
///
|
||||
/// fn accept_two_digits(input: String) -> Result<(u8,), ParseError> {
|
||||
|
@ -174,6 +190,7 @@ pub use teloxide_macros::BotCommand;
|
|||
/// assert_eq!(command, Command::Num(12));
|
||||
/// let command = Command::parse("/num 333", "");
|
||||
/// assert!(command.is_err());
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// 3. `#[command(prefix = "prefix")]`
|
||||
|
|
|
@ -9,4 +9,6 @@ mod up_state;
|
|||
pub use client_from_env::client_from_env;
|
||||
|
||||
#[cfg(feature = "frunk")]
|
||||
// FIXME(waffle): use `docsrs` here when issue with combine is resolved <https://github.com/teloxide/teloxide/pull/305#issuecomment-716172103>
|
||||
#[cfg_attr(all(teloxide_docsrs, feature = "nightly"), doc(cfg(feature = "frunk")))]
|
||||
pub use up_state::UpState;
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
#[cfg(feature = "macros")]
|
||||
use teloxide::utils::command::{BotCommand, ParseError};
|
||||
|
||||
// We put tests here because macro expand in unit tests in module
|
||||
// teloxide::utils::command was a failure
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "macros")]
|
||||
fn parse_command_with_args() {
|
||||
#[command(rename = "lowercase")]
|
||||
#[derive(BotCommand, Debug, PartialEq)]
|
||||
|
@ -19,6 +21,23 @@ fn parse_command_with_args() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "macros")]
|
||||
fn parse_command_with_non_string_arg() {
|
||||
#[command(rename = "lowercase")]
|
||||
#[derive(BotCommand, Debug, PartialEq)]
|
||||
enum DefaultCommands {
|
||||
Start(i32),
|
||||
Help,
|
||||
}
|
||||
|
||||
let data = "/start -50";
|
||||
let expected = DefaultCommands::Start("-50".parse().unwrap());
|
||||
let actual = DefaultCommands::parse(data, "").unwrap();
|
||||
assert_eq!(actual, expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "macros")]
|
||||
fn attribute_prefix() {
|
||||
#[command(rename = "lowercase")]
|
||||
#[derive(BotCommand, Debug, PartialEq)]
|
||||
|
@ -35,6 +54,7 @@ fn attribute_prefix() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "macros")]
|
||||
fn many_attributes() {
|
||||
#[command(rename = "lowercase")]
|
||||
#[derive(BotCommand, Debug, PartialEq)]
|
||||
|
@ -49,6 +69,7 @@ fn many_attributes() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "macros")]
|
||||
fn global_attributes() {
|
||||
#[command(prefix = "!", rename = "lowercase", description = "Bot commands")]
|
||||
#[derive(BotCommand, Debug, PartialEq)]
|
||||
|
@ -64,6 +85,7 @@ fn global_attributes() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "macros")]
|
||||
fn parse_command_with_bot_name() {
|
||||
#[command(rename = "lowercase")]
|
||||
#[derive(BotCommand, Debug, PartialEq)]
|
||||
|
@ -80,6 +102,7 @@ fn parse_command_with_bot_name() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "macros")]
|
||||
fn parse_with_split() {
|
||||
#[command(rename = "lowercase")]
|
||||
#[command(parse_with = "split")]
|
||||
|
@ -96,6 +119,7 @@ fn parse_with_split() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "macros")]
|
||||
fn parse_with_split2() {
|
||||
#[command(rename = "lowercase")]
|
||||
#[command(parse_with = "split", separator = "|")]
|
||||
|
@ -112,6 +136,7 @@ fn parse_with_split2() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "macros")]
|
||||
fn parse_custom_parser() {
|
||||
fn custom_parse_function(s: String) -> Result<(u8, String), ParseError> {
|
||||
let vec = s.split_whitespace().collect::<Vec<_>>();
|
||||
|
@ -139,6 +164,7 @@ fn parse_custom_parser() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "macros")]
|
||||
fn parse_named_fields() {
|
||||
#[command(rename = "lowercase")]
|
||||
#[command(parse_with = "split")]
|
||||
|
@ -155,6 +181,7 @@ fn parse_named_fields() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "macros")]
|
||||
fn descriptions_off() {
|
||||
#[command(rename = "lowercase")]
|
||||
#[derive(BotCommand, Debug, PartialEq)]
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#![cfg(feature = "redis_storage")]
|
||||
|
||||
use std::{
|
||||
fmt::{Debug, Display},
|
||||
future::Future,
|
||||
|
@ -8,7 +6,6 @@ use std::{
|
|||
use teloxide::dispatching::dialogue::{RedisStorage, Serializer, Storage};
|
||||
|
||||
#[tokio::test]
|
||||
#[cfg(feature = "redis_storage")]
|
||||
async fn test_redis_json() {
|
||||
let storage = RedisStorage::open(
|
||||
"redis://127.0.0.1:7777",
|
||||
|
@ -19,7 +16,6 @@ async fn test_redis_json() {
|
|||
test_redis(storage).await;
|
||||
}
|
||||
|
||||
#[cfg(feature = "bincode_serializer")]
|
||||
#[tokio::test]
|
||||
async fn test_redis_bincode() {
|
||||
let storage = RedisStorage::open(
|
||||
|
@ -31,7 +27,6 @@ async fn test_redis_bincode() {
|
|||
test_redis(storage).await;
|
||||
}
|
||||
|
||||
#[cfg(feature = "cbor_serializer")]
|
||||
#[tokio::test]
|
||||
async fn test_redis_cbor() {
|
||||
let storage = RedisStorage::open(
|
||||
|
|
67
tests/sqlite.rs
Normal file
67
tests/sqlite.rs
Normal file
|
@ -0,0 +1,67 @@
|
|||
use std::{
|
||||
fmt::{Debug, Display},
|
||||
future::Future,
|
||||
sync::Arc,
|
||||
};
|
||||
use teloxide::dispatching::dialogue::{Serializer, SqliteStorage, Storage};
|
||||
|
||||
#[tokio::test(threaded_scheduler)]
|
||||
async fn test_sqlite_json() {
|
||||
let storage =
|
||||
SqliteStorage::open("./test_db1.sqlite", teloxide::dispatching::dialogue::serializer::JSON)
|
||||
.await
|
||||
.unwrap();
|
||||
test_sqlite(storage).await;
|
||||
}
|
||||
|
||||
#[tokio::test(threaded_scheduler)]
|
||||
async fn test_sqlite_bincode() {
|
||||
let storage = SqliteStorage::open(
|
||||
"./test_db2.sqlite",
|
||||
teloxide::dispatching::dialogue::serializer::Bincode,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
test_sqlite(storage).await;
|
||||
}
|
||||
|
||||
#[tokio::test(threaded_scheduler)]
|
||||
async fn test_sqlite_cbor() {
|
||||
let storage =
|
||||
SqliteStorage::open("./test_db3.sqlite", teloxide::dispatching::dialogue::serializer::CBOR)
|
||||
.await
|
||||
.unwrap();
|
||||
test_sqlite(storage).await;
|
||||
}
|
||||
|
||||
type Dialogue = String;
|
||||
|
||||
async fn test_sqlite<S>(storage: Arc<SqliteStorage<S>>)
|
||||
where
|
||||
S: Send + Sync + Serializer<Dialogue> + 'static,
|
||||
<S as Serializer<Dialogue>>::Error: Debug + Display,
|
||||
{
|
||||
check_dialogue(None, Arc::clone(&storage).update_dialogue(1, "ABC".to_owned())).await;
|
||||
check_dialogue(None, Arc::clone(&storage).update_dialogue(11, "DEF".to_owned())).await;
|
||||
check_dialogue(None, Arc::clone(&storage).update_dialogue(256, "GHI".to_owned())).await;
|
||||
|
||||
// 1 - ABC, 11 - DEF, 256 - GHI
|
||||
|
||||
check_dialogue("ABC", Arc::clone(&storage).update_dialogue(1, "JKL".to_owned())).await;
|
||||
check_dialogue("GHI", Arc::clone(&storage).update_dialogue(256, "MNO".to_owned())).await;
|
||||
|
||||
// 1 - GKL, 11 - DEF, 256 - MNO
|
||||
|
||||
check_dialogue("JKL", Arc::clone(&storage).remove_dialogue(1)).await;
|
||||
check_dialogue("DEF", Arc::clone(&storage).remove_dialogue(11)).await;
|
||||
check_dialogue("MNO", Arc::clone(&storage).remove_dialogue(256)).await;
|
||||
}
|
||||
|
||||
async fn check_dialogue<E>(
|
||||
expected: impl Into<Option<&str>>,
|
||||
actual: impl Future<Output = Result<Option<Dialogue>, E>>,
|
||||
) where
|
||||
E: Debug,
|
||||
{
|
||||
assert_eq!(expected.into().map(ToOwned::to_owned), actual.await.unwrap())
|
||||
}
|
Loading…
Reference in a new issue