initial commit
This commit is contained in:
2
.cargo/config.toml
Normal file
2
.cargo/config.toml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
[target.x86_64-unknown-linux-gnu]
|
||||||
|
rustflags = ["-C", "linker=clang", "-C", "link-arg=-fuse-ld=lld"]
|
||||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[submodule "rustree"]
|
||||||
|
path = rustree
|
||||||
|
url = git@git.loyso.art:frx/rustree.git
|
||||||
20
Cargo.toml
Normal file
20
Cargo.toml
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
[workspace]
|
||||||
|
members = [
|
||||||
|
"alterego",
|
||||||
|
"rustree",
|
||||||
|
]
|
||||||
|
|
||||||
|
[workspace.dependencies]
|
||||||
|
anyhow = "1.0.69"
|
||||||
|
futures = "0.3.26"
|
||||||
|
log = "0.4.17"
|
||||||
|
reqwest = "0.11.14"
|
||||||
|
serde = { version = "1.0.152", features = ["derive"]}
|
||||||
|
serde_json = "1.0.93"
|
||||||
|
tokio = { version = "1.25.0", features = ["full"]}
|
||||||
|
chrono = "0.4.23"
|
||||||
|
hex = "0.4.3"
|
||||||
|
hmac = "0.12.1"
|
||||||
|
percent-encoding = "2.2.0"
|
||||||
|
sha2 = "0.10.6"
|
||||||
|
urlencoding = "2.1.2"
|
||||||
BIN
alterego/.DS_Store
vendored
Normal file
BIN
alterego/.DS_Store
vendored
Normal file
Binary file not shown.
93
alterego/.drone.jsonnet
Normal file
93
alterego/.drone.jsonnet
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
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",
|
||||||
|
// }
|
||||||
|
}
|
||||||
98
alterego/.drone.yml
Normal file
98
alterego/.drone.yml
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
---
|
||||||
|
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
|
||||||
|
|
||||||
|
...
|
||||||
16
alterego/Cargo.toml
Normal file
16
alterego/Cargo.toml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
[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}
|
||||||
41
alterego/build.rs
Normal file
41
alterego/build.rs
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
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,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
40
alterego/makefile
Normal file
40
alterego/makefile
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
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
alterego/src/lib.rs
Normal file
1
alterego/src/lib.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
pub mod telegram;
|
||||||
70
alterego/src/main.rs
Normal file
70
alterego/src/main.rs
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
184
alterego/src/telegram/bot.rs
Normal file
184
alterego/src/telegram/bot.rs
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
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)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
2
alterego/src/telegram/mod.rs
Normal file
2
alterego/src/telegram/mod.rs
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
pub mod bot;
|
||||||
|
pub mod types;
|
||||||
63
alterego/src/telegram/types.rs
Normal file
63
alterego/src/telegram/types.rs
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
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>,
|
||||||
|
}
|
||||||
1
rustree
Submodule
1
rustree
Submodule
Submodule rustree added at 12e86d7079
Reference in New Issue
Block a user