altherego remove
This commit is contained in:
BIN
alterego/.DS_Store
vendored
BIN
alterego/.DS_Store
vendored
Binary file not shown.
@ -1,93 +0,0 @@
|
|||||||
local image = std.extVar("image");
|
|
||||||
local app_name = std.extVar("app_name");
|
|
||||||
local target_arch = std.extVar("target_arch");
|
|
||||||
|
|
||||||
local flags = " --release --target=" + target_arch;
|
|
||||||
|
|
||||||
local volume(name, path) = {
|
|
||||||
name: name,
|
|
||||||
path: path,
|
|
||||||
};
|
|
||||||
|
|
||||||
local volumes = [
|
|
||||||
volume("cargo", "/usr/local/cargo"),
|
|
||||||
volume("target", "/cache/target"),
|
|
||||||
volume("rustup", "/usr/local/rustup"),
|
|
||||||
];
|
|
||||||
|
|
||||||
local step(name, depends=[], commands=[], env={}) = {
|
|
||||||
name: name,
|
|
||||||
volumes: volumes,
|
|
||||||
image: image,
|
|
||||||
depends_on: depends,
|
|
||||||
commands: commands,
|
|
||||||
environment: env + {
|
|
||||||
CARGO_TARGET_DIR: "/cache/target",
|
|
||||||
APP_NAME: app_name,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
local temp_volume(name) = {
|
|
||||||
name: name,
|
|
||||||
temp: {},
|
|
||||||
};
|
|
||||||
|
|
||||||
local host_volume(name, path) = {
|
|
||||||
name: name,
|
|
||||||
host: {
|
|
||||||
path: path,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
{
|
|
||||||
kind: "pipeline",
|
|
||||||
type: "docker",
|
|
||||||
name: "default",
|
|
||||||
platform: {
|
|
||||||
"os": "linux",
|
|
||||||
"arch": "arm",
|
|
||||||
},
|
|
||||||
|
|
||||||
steps: [
|
|
||||||
step(
|
|
||||||
"validate",
|
|
||||||
commands=["cargo test" + flags],
|
|
||||||
),
|
|
||||||
step(
|
|
||||||
"test",
|
|
||||||
depends=["validate"],
|
|
||||||
commands=["cargo test" + flags],
|
|
||||||
),
|
|
||||||
step(
|
|
||||||
"build",
|
|
||||||
depends=["test"],
|
|
||||||
commands=["cargo build" + flags],
|
|
||||||
env={
|
|
||||||
GIT_REVISION: "${DRONE_COMMIT:0:8}",
|
|
||||||
GIT_BRANCH: "${DRONE_COMMIT_BRANCH}",
|
|
||||||
},
|
|
||||||
),
|
|
||||||
step(
|
|
||||||
"deploy",
|
|
||||||
depends=["build"],
|
|
||||||
commands=["sh scripts/deploy.sh"],
|
|
||||||
env={
|
|
||||||
TARGET_DIR: "/cache/target/" + target_arch + "/release",
|
|
||||||
SSH_PRIVATE_KEY: {"from_secret": "ssh_pk_base64"},
|
|
||||||
SSH_USER: {"from_secret": "SSH_USER"},
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
|
|
||||||
volumes: [
|
|
||||||
temp_volume("target"),
|
|
||||||
host_volume("cargo", "/home/pi/.cargo"),
|
|
||||||
host_volume("rustup", "/home/pi/.rustup"),
|
|
||||||
],
|
|
||||||
|
|
||||||
// BUG: thid does not add.
|
|
||||||
// environment: {
|
|
||||||
// CARGO_TARGET_DIR: "/cache/target/",
|
|
||||||
// APP_NAME: "deploytest",
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
@ -1,98 +0,0 @@
|
|||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
type: docker
|
|
||||||
name: default
|
|
||||||
|
|
||||||
platform:
|
|
||||||
os: linux
|
|
||||||
arch: arm
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: validate
|
|
||||||
image: rust:1.49
|
|
||||||
commands:
|
|
||||||
- cargo test --release --target=armv7-unknown-linux-gnueabihf
|
|
||||||
environment:
|
|
||||||
APP_NAME: altherego
|
|
||||||
CARGO_TARGET_DIR: /cache/target
|
|
||||||
volumes:
|
|
||||||
- name: cargo
|
|
||||||
path: /usr/local/cargo
|
|
||||||
- name: target
|
|
||||||
path: /cache/target
|
|
||||||
- name: rustup
|
|
||||||
path: /usr/local/rustup
|
|
||||||
|
|
||||||
- name: test
|
|
||||||
image: rust:1.49
|
|
||||||
commands:
|
|
||||||
- cargo test --release --target=armv7-unknown-linux-gnueabihf
|
|
||||||
environment:
|
|
||||||
APP_NAME: altherego
|
|
||||||
CARGO_TARGET_DIR: /cache/target
|
|
||||||
volumes:
|
|
||||||
- name: cargo
|
|
||||||
path: /usr/local/cargo
|
|
||||||
- name: target
|
|
||||||
path: /cache/target
|
|
||||||
- name: rustup
|
|
||||||
path: /usr/local/rustup
|
|
||||||
depends_on:
|
|
||||||
- validate
|
|
||||||
|
|
||||||
- name: build
|
|
||||||
image: rust:1.49
|
|
||||||
commands:
|
|
||||||
- cargo build --release --target=armv7-unknown-linux-gnueabihf
|
|
||||||
environment:
|
|
||||||
APP_NAME: altherego
|
|
||||||
CARGO_TARGET_DIR: /cache/target
|
|
||||||
GIT_BRANCH: ${DRONE_COMMIT_BRANCH}
|
|
||||||
GIT_REVISION: ${DRONE_COMMIT:0:8}
|
|
||||||
volumes:
|
|
||||||
- name: cargo
|
|
||||||
path: /usr/local/cargo
|
|
||||||
- name: target
|
|
||||||
path: /cache/target
|
|
||||||
- name: rustup
|
|
||||||
path: /usr/local/rustup
|
|
||||||
depends_on:
|
|
||||||
- test
|
|
||||||
|
|
||||||
- name: deploy
|
|
||||||
image: rust:1.49
|
|
||||||
commands:
|
|
||||||
- sh scripts/deploy.sh
|
|
||||||
environment:
|
|
||||||
APP_NAME: altherego
|
|
||||||
CARGO_TARGET_DIR: /cache/target
|
|
||||||
SSH_PRIVATE_KEY:
|
|
||||||
from_secret: ssh_pk_base64
|
|
||||||
SSH_USER:
|
|
||||||
from_secret: SSH_USER
|
|
||||||
TARGET_DIR: /cache/target/armv7-unknown-linux-gnueabihf/release
|
|
||||||
volumes:
|
|
||||||
- name: cargo
|
|
||||||
path: /usr/local/cargo
|
|
||||||
- name: target
|
|
||||||
path: /cache/target
|
|
||||||
- name: rustup
|
|
||||||
path: /usr/local/rustup
|
|
||||||
depends_on:
|
|
||||||
- build
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
- name: target
|
|
||||||
temp: {}
|
|
||||||
- name: cargo
|
|
||||||
host:
|
|
||||||
path: /home/pi/.cargo
|
|
||||||
- name: rustup
|
|
||||||
host:
|
|
||||||
path: /home/pi/.rustup
|
|
||||||
|
|
||||||
---
|
|
||||||
kind: signature
|
|
||||||
hmac: a942d89af2c38916d55ebe377709febf08145ce8e08a2b585dda8a1c251eaca0
|
|
||||||
|
|
||||||
...
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "alterego"
|
|
||||||
version = "0.1.0"
|
|
||||||
authors = ["Aleksandr Trushkin <aleksandr.trushkin@rt.ru>"]
|
|
||||||
edition = "2018"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
anyhow = {workspace = true}
|
|
||||||
futures = {workspace = true}
|
|
||||||
log = {workspace = true}
|
|
||||||
reqwest = {workspace = true}
|
|
||||||
serde = {workspace = true, features = ["derive"]}
|
|
||||||
serde_json = {workspace = true}
|
|
||||||
tokio = {workspace = true}
|
|
||||||
@ -1,41 +0,0 @@
|
|||||||
use std::env;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let rev = get_value_from_env("GIT_VERSION")
|
|
||||||
.or_else(|| get_value_from_command("git", &["rev-parse", "--short", "HEAD"]))
|
|
||||||
.unwrap_or_else(|| "unknown".to_owned());
|
|
||||||
|
|
||||||
let branch = get_value_from_env("GIT_BRANCH")
|
|
||||||
.or_else(|| get_value_from_command("git", &["rev-parse", "--abbrev-ref", "HEAD"]))
|
|
||||||
.unwrap_or_else(|| "unknown".to_owned());
|
|
||||||
|
|
||||||
println!("cargo:rustc-env=GIT_REVISION={}", rev);
|
|
||||||
println!("cargo:rustc-env=GIT_BRANCH={}", branch);
|
|
||||||
println!("cargo:rerun-if-env-changed=GIT_REVISION");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_value_from_env(key: &str) -> Option<String> {
|
|
||||||
env::var(key).map_or_else(|_| None, Some)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_value_from_command<I: IntoIterator<Item = S>, S: AsRef<std::ffi::OsStr>>(
|
|
||||||
cmd: &str,
|
|
||||||
args: I,
|
|
||||||
) -> Option<String> {
|
|
||||||
std::process::Command::new(cmd)
|
|
||||||
.args(args)
|
|
||||||
.output()
|
|
||||||
.map_or_else(
|
|
||||||
|_| None,
|
|
||||||
|out| {
|
|
||||||
if !out.status.success() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
match std::str::from_utf8(&out.stdout) {
|
|
||||||
Ok(value) => Some(value.to_owned()),
|
|
||||||
Err(_) => None,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@ -1,40 +0,0 @@
|
|||||||
export DOCKER_BUILDKIT=1
|
|
||||||
|
|
||||||
DOCKERFLAGS:=-it --rm \
|
|
||||||
-v "${PWD}":"/app" \
|
|
||||||
--workdir "/app" \
|
|
||||||
-e "PWD=/app"
|
|
||||||
|
|
||||||
DOCKERIMG:="rust-build-env:V1"
|
|
||||||
|
|
||||||
APP_NAME:=altherego
|
|
||||||
IMAGE:=rust:1.49
|
|
||||||
TARGET_ARCH:=armv7-unknown-linux-gnueabihf
|
|
||||||
|
|
||||||
image:
|
|
||||||
docker build -t rust-build-env:V1 .
|
|
||||||
.PHONY: image
|
|
||||||
|
|
||||||
ARM_PREFIX:=CARGO_TARGET_ARM_UNKNOWN_LINUX_MUSLEABIHF_LINKER=arm-linux-gnueabihf-ld \
|
|
||||||
REALGCC=arm-linux-gnueabihf-gcc-8 \
|
|
||||||
TARGET_CC=musl-gcc
|
|
||||||
|
|
||||||
build_debug_arm:
|
|
||||||
${ARM_PREFIX} cargo build --target=armv7-unknown-linux-musleabihf
|
|
||||||
.PHONY: build_debug_arm
|
|
||||||
|
|
||||||
build_release_arm:
|
|
||||||
docker run ${DOCKERFLAGS} ${DOCKERIMG} /bin/sh -c 'cargo build --release --target=armv7-unknown-linux-gnueabihf'
|
|
||||||
.PHONY: build_release_arm
|
|
||||||
|
|
||||||
docker_build_release_arm:
|
|
||||||
docker run ${DOCKERFLAGS} ${DOCKERIMG} make build_release_arm
|
|
||||||
|
|
||||||
dronefile:
|
|
||||||
drone jsonnet \
|
|
||||||
--format \
|
|
||||||
-V app_name=${APP_NAME} \
|
|
||||||
-V image=${IMAGE} \
|
|
||||||
-V target_arch=${TARGET_ARCH}
|
|
||||||
drone sign frx/altherego --save
|
|
||||||
.PHONY: dronefile
|
|
||||||
@ -1 +0,0 @@
|
|||||||
pub mod telegram;
|
|
||||||
@ -1,70 +0,0 @@
|
|||||||
use alterego::telegram;
|
|
||||||
|
|
||||||
use tokio::runtime;
|
|
||||||
const BOT_TOKEN: &str = "170515067:AAElDJ8Sq_oIqo9WaL4DKvUr13nSEIdHCYs";
|
|
||||||
|
|
||||||
fn main() -> anyhow::Result<()> {
|
|
||||||
println!("Hello, world!");
|
|
||||||
|
|
||||||
let rt = runtime::Builder::new_current_thread()
|
|
||||||
.enable_io()
|
|
||||||
.enable_time()
|
|
||||||
.build()
|
|
||||||
.expect("making runtime");
|
|
||||||
|
|
||||||
rt.block_on(app())?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
use futures::StreamExt;
|
|
||||||
|
|
||||||
async fn app() -> anyhow::Result<()> {
|
|
||||||
let bot = telegram::bot::Client::new(BOT_TOKEN.to_owned());
|
|
||||||
|
|
||||||
let mut stream = bot.updates_stream();
|
|
||||||
while let Some(update) = stream.next().await {
|
|
||||||
let update = update?;
|
|
||||||
|
|
||||||
println!("{:?}", update);
|
|
||||||
|
|
||||||
handle_update(update);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
use log::{debug, trace, warn};
|
|
||||||
use telegram::types::UpdateKind;
|
|
||||||
|
|
||||||
fn handle_update(update: telegram::types::Update) {
|
|
||||||
match update.kind {
|
|
||||||
UpdateKind::Message(msg) => {
|
|
||||||
let text = msg.text.unwrap_or_default();
|
|
||||||
|
|
||||||
if !text.starts_with('/') {
|
|
||||||
trace!("it's not a command, skipping");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
match text.as_str() {
|
|
||||||
"/help" => {
|
|
||||||
println!("help command called");
|
|
||||||
}
|
|
||||||
"/temp" => {
|
|
||||||
println!("temp command called");
|
|
||||||
}
|
|
||||||
"/versionrequest" => {}
|
|
||||||
other => {
|
|
||||||
println!("unknown command {}", other);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
UpdateKind::EditedMessage(msg) => {
|
|
||||||
debug!("edited message: {:?}", msg);
|
|
||||||
}
|
|
||||||
UpdateKind::Undefined => {
|
|
||||||
warn!("message udentified");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,184 +0,0 @@
|
|||||||
use super::types::*;
|
|
||||||
|
|
||||||
use std::{
|
|
||||||
cmp::max,
|
|
||||||
collections::VecDeque,
|
|
||||||
future::Future,
|
|
||||||
pin::Pin,
|
|
||||||
sync::atomic::{AtomicI32, Ordering},
|
|
||||||
task::{Context, Poll},
|
|
||||||
};
|
|
||||||
|
|
||||||
use log::{debug, trace};
|
|
||||||
use reqwest;
|
|
||||||
|
|
||||||
const TELEGRAM_URL: &str = "https://api.telegram.org";
|
|
||||||
const HTTP_CLIENT: &str = "alterego-http-client/1.0";
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum BotError {
|
|
||||||
API(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Display for BotError {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
let text = match self {
|
|
||||||
BotError::API(msg) => format!("api error: {}", msg),
|
|
||||||
};
|
|
||||||
|
|
||||||
write!(f, "{}", text)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::error::Error for BotError {}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
/// Client provides API for communicating with Telegram Bot API.
|
|
||||||
pub struct Client {
|
|
||||||
pub token: String,
|
|
||||||
pub client: reqwest::Client,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Client {
|
|
||||||
pub fn new(token: String) -> Self {
|
|
||||||
let client = reqwest::ClientBuilder::default()
|
|
||||||
.connect_timeout(std::time::Duration::from_secs(5))
|
|
||||||
.https_only(true)
|
|
||||||
.user_agent(HTTP_CLIENT)
|
|
||||||
.build()
|
|
||||||
.expect("building http client");
|
|
||||||
|
|
||||||
Self { token, client }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn make_url(&self, method: &str) -> String {
|
|
||||||
format!("{}/bot{}/{}", TELEGRAM_URL, self.token, method)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fetch_updates(
|
|
||||||
&self,
|
|
||||||
offset: i32,
|
|
||||||
) -> impl Future<Output = anyhow::Result<Option<Vec<Update>>>> {
|
|
||||||
let client = self.clone();
|
|
||||||
async move { client.get_updates(offset).await }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn get_updates(&self, offset: i32) -> anyhow::Result<Option<Vec<Update>>> {
|
|
||||||
const METHOD: &str = "getUpdates";
|
|
||||||
|
|
||||||
trace!("getting updates");
|
|
||||||
|
|
||||||
let url = {
|
|
||||||
let mut url = reqwest::Url::parse(&self.make_url(METHOD))?;
|
|
||||||
url.set_query(Some(format!("offset={}", offset).as_str()));
|
|
||||||
url
|
|
||||||
};
|
|
||||||
|
|
||||||
debug!("requesting: {}", url.as_str());
|
|
||||||
let body = self.client.get(url).send().await?.bytes().await?;
|
|
||||||
|
|
||||||
let response: Response<Update> = serde_json::from_reader(&body[..])?;
|
|
||||||
if response.is_err() {
|
|
||||||
debug!("response finished with error");
|
|
||||||
return Err(anyhow::anyhow!(BotError::API(
|
|
||||||
response.description.unwrap()
|
|
||||||
)));
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(response.result)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn updates_stream(&self) -> ClientUpdateStream {
|
|
||||||
ClientUpdateStream::new(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn reply(&self) -> anyhow::Result<()> {
|
|
||||||
todo!("soon")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type PinnedRequest = Pin<Box<dyn Future<Output = anyhow::Result<Option<Vec<Update>>>> + Send>>;
|
|
||||||
|
|
||||||
pub struct ClientUpdateStream {
|
|
||||||
client: Client,
|
|
||||||
next_id: AtomicI32,
|
|
||||||
buffer: VecDeque<Update>,
|
|
||||||
pinned_request: Option<PinnedRequest>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ClientUpdateStream {
|
|
||||||
pub fn new(client: &Client) -> Self {
|
|
||||||
Self {
|
|
||||||
client: client.clone(),
|
|
||||||
next_id: AtomicI32::new(0),
|
|
||||||
buffer: VecDeque::new(),
|
|
||||||
pinned_request: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl futures::Stream for ClientUpdateStream {
|
|
||||||
type Item = anyhow::Result<Update>;
|
|
||||||
|
|
||||||
/// poll_next fetches updates from client and appends it to
|
|
||||||
/// buffer.
|
|
||||||
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
|
||||||
let myself = self.get_mut();
|
|
||||||
|
|
||||||
let mut current_id = myself.next_id.load(Ordering::SeqCst);
|
|
||||||
|
|
||||||
if let Some(value) = myself.buffer.pop_front() {
|
|
||||||
return Poll::Ready(Some(Ok(value)));
|
|
||||||
};
|
|
||||||
|
|
||||||
// Check the current state of myself.
|
|
||||||
// In case request is being processed, return Pending;
|
|
||||||
// In case request is ready, insert updates and return Ok(true);
|
|
||||||
// In case there is no request or updates are empty, return Ok(false);
|
|
||||||
// Ok(state), where states defines whether new request should be
|
|
||||||
// created or not.
|
|
||||||
let result = match myself.pinned_request {
|
|
||||||
None => Ok(false),
|
|
||||||
Some(ref mut request) => {
|
|
||||||
let request = request.as_mut();
|
|
||||||
let polled_request = request.poll(cx);
|
|
||||||
|
|
||||||
match polled_request {
|
|
||||||
Poll::Pending => return Poll::Pending,
|
|
||||||
Poll::Ready(Ok(None)) => Ok(false),
|
|
||||||
Poll::Ready(Ok(Some(ref updates))) if updates.is_empty() => Ok(false),
|
|
||||||
Poll::Ready(Ok(Some(updates))) => {
|
|
||||||
for update in updates {
|
|
||||||
current_id = max(current_id, update.id + 1);
|
|
||||||
myself.buffer.push_back(update);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(true)
|
|
||||||
}
|
|
||||||
Poll::Ready(Err(err)) => Err(err),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
myself.next_id.store(current_id, Ordering::SeqCst);
|
|
||||||
|
|
||||||
match result {
|
|
||||||
Ok(true) => {
|
|
||||||
myself.pinned_request = None;
|
|
||||||
Pin::new(myself).poll_next(cx)
|
|
||||||
}
|
|
||||||
Ok(false) => {
|
|
||||||
let next_request = myself.client.fetch_updates(current_id);
|
|
||||||
myself.pinned_request = Some(Box::pin(next_request));
|
|
||||||
|
|
||||||
Pin::new(myself).poll_next(cx)
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
let next_request = myself.client.fetch_updates(current_id);
|
|
||||||
myself.pinned_request = Some(Box::pin(next_request));
|
|
||||||
|
|
||||||
Poll::Ready(Some(Err(err)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,2 +0,0 @@
|
|||||||
pub mod bot;
|
|
||||||
pub mod types;
|
|
||||||
@ -1,63 +0,0 @@
|
|||||||
use serde::Deserialize;
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
pub struct Response<T> {
|
|
||||||
pub ok: bool,
|
|
||||||
pub error_code: Option<i32>,
|
|
||||||
pub description: Option<String>,
|
|
||||||
pub result: Option<Vec<T>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Response<T> {
|
|
||||||
pub fn is_err(&self) -> bool {
|
|
||||||
!self.ok
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
pub struct Update {
|
|
||||||
#[serde(rename = "update_id")]
|
|
||||||
pub id: i32,
|
|
||||||
|
|
||||||
#[serde(flatten)]
|
|
||||||
pub kind: UpdateKind,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
pub enum UpdateKind {
|
|
||||||
#[serde(rename = "message")]
|
|
||||||
Message(Message),
|
|
||||||
|
|
||||||
#[serde(rename = "edited_message")]
|
|
||||||
EditedMessage(Message),
|
|
||||||
|
|
||||||
Undefined,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
pub struct Message {
|
|
||||||
pub message_id: i32,
|
|
||||||
pub from: Option<User>,
|
|
||||||
pub sender_chat: Option<Chat>,
|
|
||||||
pub date: i64,
|
|
||||||
pub chat: Chat,
|
|
||||||
pub text: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
pub struct User {
|
|
||||||
pub id: i32,
|
|
||||||
pub is_bot: bool,
|
|
||||||
pub first_name: String,
|
|
||||||
pub last_name: Option<String>,
|
|
||||||
pub username: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
pub struct Chat {
|
|
||||||
pub id: i32,
|
|
||||||
#[serde(alias = "type")]
|
|
||||||
pub chat_type: String,
|
|
||||||
pub title: Option<String>,
|
|
||||||
pub username: Option<String>,
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user