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