Telegram client for rust.
This crate use libtdjson call telegram client api, libtdjson create is rtdlib
Notice
Currently, not all features are available. about how to support it, see Advanced
toml
telegram-client="0.2"
Note that you need libtdjson.so.1.4.0 in your path for building and running your application. See also rtdlib for more details.
```rust fn main() { let api = Api::default(); let mut client = Client::new(api.clone()); let listener = client.listener();
listener.onreceive(|(api, object)| { println!("receive {:?} => {}", object.tdtype(), object.to_json()); });
client.daemon("telegram-rs"); } ```
more examples
If you see the code, in types, let see TGMessageForwardInfo
rust
impl TGMessageForwardInfo {
pub fn from_message_id(&self) -> i64 { self.td_origin().from_message_id().map(|v| v).expect(&errors::data_fail_with_rtd(self.td_origin())[..]) }
}
When json can not parse to TGFile
, will panic progress.
Why it's? instead of returning a Result
to you?
Yes, maybe returning Result
is best, won't let program exit. But this will be complicated by the code.
First of all to know what causes this error.
telegram-client
crate covert fail.The first you need create issue to td
The second, only from_message_id
field is Option field, check the documentation to see if some are optional field, and update it.
When telegram-client
is stable, In principle, no errors will occur unless td is changed
Because telegram client has a lot of events, this crate may not contain all the event handling code. If found will print a WARN level logger. Guide you to submit an issue.
In addition, you have at least two ways to solve this problem.
When you get listener
from Client, you can use listener
add on_receive
to handle any data from tdlib.
```rust fn main() { let mut client = Client::default(); let listener = client.listener();
listener.onreceive(|(api: &Api, object: &Box
RTDType
is an enum, include all tdlib types. and on_receive
as long as the callback is called after receiving the data.
Then combine rtdtype and receive to handle all events
The code for this crate is designed to be configurable. You can check build/tpl directory to see the configuration file.
Let's take a look at the listener_rs.tpl.toml file.
```toml [info]
uses = [ "rtdlib::types as tdtypes", "crate::types as tgtypes", "crate::api::Api", ]
commentlistener = "Telegram client event listener" commentlout = "Get listener"
[lin]
receive = { tt = { object = "Box
[lin.option] tdtype = "updateOption" tt = { namespace = "tgtypes", object = "TGUpdateOption"} comment = "An option changed its value."
[lin.authorizationstate] tdtype = "updateAuthorizationState" tt = { namespace = "tg_types", object = "TGAuthorizationState" } comment = "The user authorization state has changed." ```
This config will be generated src/listener.rs
and src/handler/handler_receive.rs
file.
Listen.rs will provide the user with the registration of the event,
[lin.name]
name is the name of the eventtd_type
match the event corresponding to tdlibtt
is the type of parameter that the event should return, this type should Is a struct that implements rtdlib::types::Object
comment
comment of this listenerGenerated src/listener.rs
like this:
```rust use std::sync::Arc;
use rtdlib::types as tdtypes; use crate::types as tgtypes; use crate::api::Api;
/// Telegram client event listener
pub struct Listener {
lauthorizationstate: Option
impl Listener { pub fn new() -> Self { Self { lauthorizationstate: None, l_option: None, } }
/// when receive data from tdlib
pub fn onreceive
And then you can use this listener
```rust fn main() { let api = Api::default(); let mut client = Client::new(api.clone()); let listener = client.listener();
listener.onoption(|(api, option)| { let value = option.value(); if value.isempty() { debug!(exmlog::examples(), "Receive an option {} but it's empty", option.name()) } if value.isstring() { debug!(exmlog::examples(), "Receive an option {}: String => {}", option.name(), value.asstring().mapor("None".tostring(), |v| v)) } if value.isinteger() { debug!(exmlog::examples(), "Receive an option {}: i32 => {}", option.name(), value.asinteger().mapor(-1, |v| v)) } if value.isbool() { debug!(exmlog::examples(), "Receive an option {}: bool => {}", option.name(), value.asbool().mapor(false, |v| v)) }
option.on_name("version", |value| {
value.as_string().map(|v| { debug!(exmlog::examples(), "VERSION IS {}", v); });
});
});
client.daemon("telegram-rs"); } ```
rtdtype
has many primitive types, if want to use it better, you can remodel it with gen_types.
```toml [tmod]
uses = [ "self::fupdateoption::TGOptionValue", "self::fmessagecontent::*", ]
[[tmod.mods]] name = "tgmacro" macrouse = true
[tgypes]
[[tgypes.update_option]] uses = [] typen = "TGUpdateOption" inner = "UpdateOption" comment = "An option changed its value."
[[tgypes.message_content]] inner = "VoiceNote"
[[tgypes.message_content]] inner = "Venue" ```
You can edit tg_types.tpl.toml, tmod
will generate the mod.rs
file, tgypes
will generate t_*.rs
and f_*.rs
.
t_*.rs
will be rebuilt each time, f_*.rs
will only be built once, f_*.rs
is suitable for writing supplementary code.
The above configuration will generate the following code
src/types/mod.rs
```rust pub use self::tupdateoption::; pub use self::f_update_option::TGOptionValue; pub use self::t_message_content::; pub use self::fmessagecontent::*;
mod tupdateoption; mod fupdateoption; mod tmessagecontent; mod fmessagecontent; ```
src/types/tupdateoption.rs
```rust use rtdlib::types::*; use serde::{Serialize, Serializer, Deserialize, Deserializer}; use rtdlib::types::{RObject, RTDType};
/// An option changed its value.
pub struct TGUpdateOption { inner: UpdateOption }
impl RObject for TGUpdateOption { fn tdname(&self) -> &'static str { self.inner.tdname() }
fn tdtype(&self) -> RTDType { self.inner.tdtype() }
fn tojson(&self) -> String { self.inner.tojson() } }
impl Serialize for TGUpdateOption {
fn serialize(&self, serializer: S) -> Result
impl<'de> Deserialize<'de> for TGUpdateOption {
fn deserialize
impl TGUpdateOption { pub fn new(inner: UpdateOption) -> Self { Self { inner } }
pub fn fromjson
pub fn td_origin(&self) -> &UpdateOption { &self.inner } } ```
src/types/tmessagecontent.rs
```rust
pub struct TGVoiceNote { inner: VoiceNote }
impl RObject for TGVoiceNote { fn tdname(&self) -> &'static str { self.inner.tdname() }
fn tdtype(&self) -> RTDType { self.inner.tdtype() }
fn tojson(&self) -> String { self.inner.tojson() } }
impl Serialize for TGVoiceNote {
fn serialize(&self, serializer: S) -> Result
impl<'de> Deserialize<'de> for TGVoiceNote {
fn deserialize
impl TGVoiceNote { pub fn new(inner: VoiceNote) -> Self { Self { inner } }
pub fn fromjson
pub fn td_origin(&self) -> &VoiceNote { &self.inner } }
pub struct TGVenue { inner: Venue }
impl RObject for TGVenue { fn tdname(&self) -> &'static str { self.inner.tdname() }
fn tdtype(&self) -> RTDType { self.inner.tdtype() }
fn tojson(&self) -> String { self.inner.tojson() } }
impl Serialize for TGVenue {
fn serialize(&self, serializer: S) -> Result
impl<'de> Deserialize<'de> for TGVenue {
fn deserialize
impl TGVenue { pub fn new(inner: Venue) -> Self { Self { inner } }
pub fn fromjson
pub fn td_origin(&self) -> &Venue { &self.inner } } ```
The generated code is like this. Next, you can supplement the generated code.
src/types/fupdateoptions.rs
```rust use crate::types::TGUpdateOption; use rtdlib::types as td_type; use crate::errors;
impl TGUpdateOption {
pub fn name(&self) -> String { self.tdorigin().name().expect(&errors::datafailwithrtd(self.td_origin())[..]) }
pub fn value(&self) -> TGOptionValue { TGOptionValue::new(self.td_origin().value()) }
pub fn onname
}
pub struct TGOptionValue {
value: Option
macrorules! optionvalueas {
($valueclass:ident, $retype:tt) => (
fn ovas(value: &Option
impl TGOptionValue {
fn new(value: Option
fn issome(&self) -> bool { self.value.issome() }
pub fn isbool(&self) -> bool { self.value.clone().map(|v| v.tdtype() == tdtype::RTDType::OptionValueBoolean) .mapor(false, |v| v) }
pub fn isempty(&self) -> bool { self.value.clone().map(|v| v.tdtype() == tdtype::RTDType::OptionValueEmpty) .mapor(false, |v| v) }
pub fn isstring(&self) -> bool { self.value.clone().map(|v| v.tdtype() == tdtype::RTDType::OptionValueString) .mapor(false, |v| v) }
pub fn isinteger(&self) -> bool { self.value.clone().map(|v| v.tdtype() == tdtype::RTDType::OptionValueInteger) .mapor(false, |v| v) }
pub fn asstring(&self) -> Option
pub fn asinteger(&self) -> Option
pub fn asbool(&self) -> Option
} ```