瀏覽代碼

[Telegram] Attempt to rework

Slava Barinov 3 年之前
父節點
當前提交
f8afcc7965
共有 5 個文件被更改,包括 430 次插入126 次删除
  1. 148 29
      Cargo.lock
  2. 4 3
      Cargo.toml
  3. 0 1
      src/main.rs
  4. 277 92
      src/telegram.rs
  5. 1 1
      src/tgusermanager.rs

+ 148 - 29
Cargo.lock

@@ -2,6 +2,12 @@
 # It is not intended for manual editing.
 version = 3
 
+[[package]]
+name = "ahash"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8fd72866655d1904d6b0997d0b07ba561047d070fbe29de039031c641b61217"
+
 [[package]]
 name = "aho-corasick"
 version = "0.7.18"
@@ -99,6 +105,12 @@ version = "1.0.72"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee"
 
+[[package]]
+name = "cfg-if"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
+
 [[package]]
 name = "cfg-if"
 version = "1.0.0"
@@ -144,6 +156,15 @@ dependencies = [
  "winapi",
 ]
 
+[[package]]
+name = "cloudabi"
+version = "0.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
+dependencies = [
+ "bitflags",
+]
+
 [[package]]
 name = "const_format"
 version = "0.2.22"
@@ -186,6 +207,32 @@ version = "0.8.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
 
+[[package]]
+name = "crossbeam-epoch"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace"
+dependencies = [
+ "autocfg",
+ "cfg-if 0.1.10",
+ "crossbeam-utils",
+ "lazy_static",
+ "maybe-uninit",
+ "memoffset 0.5.6",
+ "scopeguard",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
+dependencies = [
+ "autocfg",
+ "cfg-if 0.1.10",
+ "lazy_static",
+]
+
 [[package]]
 name = "csv"
 version = "1.1.6"
@@ -262,7 +309,7 @@ version = "2.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1"
 dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
  "dirs-sys-next",
 ]
 
@@ -277,13 +324,19 @@ dependencies = [
  "winapi",
 ]
 
+[[package]]
+name = "either"
+version = "1.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
+
 [[package]]
 name = "encoding_rs"
 version = "0.8.30"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7896dc8abb250ffdda33912550faa54c88ec8b998dec0b2c55ab224921ce11df"
 dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
 ]
 
 [[package]]
@@ -351,11 +404,23 @@ version = "3.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "fcef756dea9cf3db5ce73759cf0467330427a786b47711b8d6c97620d718ceb9"
 dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
  "rustix",
  "windows-sys",
 ]
 
+[[package]]
+name = "flurry"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c0a35f7b50e99185a2825541946252f669f3c3ca77801357cd682a1b356bb3e"
+dependencies = [
+ "ahash",
+ "crossbeam-epoch",
+ "num_cpus",
+ "parking_lot",
+]
+
 [[package]]
 name = "fnv"
 version = "1.0.7"
@@ -482,7 +547,7 @@ version = "0.2.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "418d37c8b1d42553c93648be529cb70f920d3baf8ef469b74b9638df426e0b4c"
 dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
  "libc",
  "wasi",
 ]
@@ -649,7 +714,7 @@ version = "0.1.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
 dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
 ]
 
 [[package]]
@@ -713,12 +778,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "95f5690fef754d905294c56f7ac815836f2513af966aa47f2e07ac79be07827f"
 
 [[package]]
-name = "lockfree"
-version = "0.5.1"
+name = "lock_api"
+version = "0.3.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "74ee94b5ad113c7cb98c5a040f783d0952ee4fe100993881d1673c2cb002dd23"
+checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75"
 dependencies = [
- "owned-alloc",
+ "scopeguard",
 ]
 
 [[package]]
@@ -727,7 +792,7 @@ version = "0.4.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
 dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
 ]
 
 [[package]]
@@ -736,12 +801,27 @@ version = "0.1.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
 
+[[package]]
+name = "maybe-uninit"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
+
 [[package]]
 name = "memchr"
 version = "2.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
 
+[[package]]
+name = "memoffset"
+version = "0.5.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa"
+dependencies = [
+ "autocfg",
+]
+
 [[package]]
 name = "memoffset"
 version = "0.6.5"
@@ -830,9 +910,9 @@ checksum = "9f866317acbd3a240710c63f065ffb1e4fd466259045ccb504130b7f668f35c6"
 dependencies = [
  "bitflags",
  "cc",
- "cfg-if",
+ "cfg-if 1.0.0",
  "libc",
- "memoffset",
+ "memoffset 0.6.5",
 ]
 
 [[package]]
@@ -886,7 +966,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0c7ae222234c30df141154f159066c5093ff73b63204dcda7121eb082fc56a95"
 dependencies = [
  "bitflags",
- "cfg-if",
+ "cfg-if 1.0.0",
  "foreign-types",
  "libc",
  "once_cell",
@@ -913,10 +993,28 @@ dependencies = [
 ]
 
 [[package]]
-name = "owned-alloc"
-version = "0.2.0"
+name = "parking_lot"
+version = "0.10.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "30fceb411f9a12ff9222c5f824026be368ff15dc2f13468d850c7d3f502205d6"
+checksum = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e"
+dependencies = [
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3"
+dependencies = [
+ "cfg-if 0.1.10",
+ "cloudabi",
+ "libc",
+ "redox_syscall 0.1.57",
+ "smallvec",
+ "winapi",
+]
 
 [[package]]
 name = "percent-encoding"
@@ -1079,8 +1177,15 @@ dependencies = [
  "teloxide",
  "thiserror",
  "tokio",
+ "tokio-stream",
 ]
 
+[[package]]
+name = "redox_syscall"
+version = "0.1.57"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
+
 [[package]]
 name = "redox_syscall"
 version = "0.2.10"
@@ -1097,7 +1202,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64"
 dependencies = [
  "getrandom",
- "redox_syscall",
+ "redox_syscall 0.2.10",
 ]
 
 [[package]]
@@ -1200,7 +1305,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "db7826789c0e25614b03e5a54a0717a86f9ff6e6e5247f92b369472869320039"
 dependencies = [
  "bitflags",
- "cfg-if",
+ "cfg-if 1.0.0",
  "clipboard-win",
  "dirs-next",
  "fd-lock",
@@ -1364,6 +1469,15 @@ dependencies = [
  "dirs-next",
 ]
 
+[[package]]
+name = "signal-hook-registry"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0"
+dependencies = [
+ "libc",
+]
+
 [[package]]
 name = "slab"
 version = "0.4.5"
@@ -1441,20 +1555,19 @@ dependencies = [
 
 [[package]]
 name = "teloxide"
-version = "0.4.0"
+version = "0.5.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d336ef631944ed9d696be4e06be884b7e9c60947639f224b862fef2f1d0c2f2e"
+checksum = "9964854e5ec3a5a44a9f50ebb7641327f1084ab4fc37a6c4a23cc011a388dc2e"
 dependencies = [
  "async-trait",
  "bincode",
  "bytes",
  "derive_more",
+ "flurry",
  "futures",
- "lockfree",
  "log",
  "mime",
  "pin-project",
- "reqwest",
  "serde",
  "serde_json",
  "serde_with_macros",
@@ -1468,12 +1581,14 @@ dependencies = [
 
 [[package]]
 name = "teloxide-core"
-version = "0.2.2"
+version = "0.3.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "236c295b0826d2fdd9ebca5ec7c06989ec049328b8eac09c10be3ab2ac9a748b"
+checksum = "114c9057a3a2f74d937ece64029b362f583a69fb4b7405722c6dc03cd5bb4658"
 dependencies = [
  "bytes",
+ "chrono",
  "derive_more",
+ "either",
  "futures",
  "log",
  "mime",
@@ -1487,6 +1602,7 @@ dependencies = [
  "thiserror",
  "tokio",
  "tokio-util",
+ "url",
  "uuid",
 ]
 
@@ -1507,10 +1623,10 @@ version = "3.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
 dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
  "fastrand",
  "libc",
- "redox_syscall",
+ "redox_syscall 0.2.10",
  "remove_dir_all",
  "winapi",
 ]
@@ -1589,7 +1705,9 @@ dependencies = [
  "memchr",
  "mio",
  "num_cpus",
+ "once_cell",
  "pin-project-lite",
+ "signal-hook-registry",
  "tokio-macros",
  "winapi",
 ]
@@ -1652,7 +1770,7 @@ version = "0.1.29"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105"
 dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
  "pin-project-lite",
  "tracing-core",
 ]
@@ -1724,6 +1842,7 @@ dependencies = [
  "idna",
  "matches",
  "percent-encoding",
+ "serde",
 ]
 
 [[package]]
@@ -1781,7 +1900,7 @@ version = "0.2.79"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06"
 dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
  "wasm-bindgen-macro",
 ]
 
@@ -1806,7 +1925,7 @@ version = "0.4.29"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2eb6ec270a31b1d3c7e266b999739109abce8b6c87e4b31fcfcd788b65267395"
 dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
  "js-sys",
  "wasm-bindgen",
  "web-sys",

+ 4 - 3
Cargo.toml

@@ -2,7 +2,7 @@
 name = "receqif"
 version = "0.1.0"
 authors = ["Slava Barinov <rayslava@gmail.com>"]
-edition = "2018"
+edition = "2021"
 
 [dependencies]
 serde = { version = "1.0", features = ["derive"] }
@@ -17,10 +17,11 @@ radix_trie = { version = "0.2", features = ["serde"] }
 libc = { version = "0.2" }
 const_format = "0.2"
 futures = { version = "0.3.0", optional = true }
-teloxide = { version = "0.4.0", features = ["auto-send", "macros", "bincode-serializer"], optional = true }
+teloxide = { version = "0.5.3", features = ["auto-send", "macros", "bincode-serializer"], optional = true }
 log = { version = "0.4.8", optional = true }
 pretty_env_logger = { version = "0.4.0", optional = true }
 tokio = { version =  "1.3", features = ["rt-multi-thread", "macros"], optional = true }
+tokio-stream = { version = "0.1.8", optional = true }
 derive_more = { version = "0.99.13", optional = true }
 thiserror = { version = "1.0.24", optional = true }
 rustyline = "9.1.1"
@@ -33,4 +34,4 @@ pkg-config = { version = "0.3", optional = true }
 [features]
 default = [ "telegram" ]
 tv = [ "cc", "pkg-config" ]
-telegram = [ "teloxide", "log", "pretty_env_logger", "tokio", "derive_more", "thiserror", "futures" ]
+telegram = [ "teloxide", "log", "pretty_env_logger", "tokio", "tokio-stream", "derive_more", "thiserror", "futures" ]

+ 0 - 1
src/main.rs

@@ -51,7 +51,6 @@ struct Cli {
 
 #[cfg(not(tarpaulin_include))]
 fn main() {
-    pretty_env_logger::init();
     log::debug!("Log started");
     let args = Cli::from_args();
 

+ 277 - 92
src/telegram.rs

@@ -1,13 +1,17 @@
-// This bot throws a dice on each incoming message.
-
 use crate::categories::{get_category_from_tg, CatStats};
 use crate::convert::{convert, non_cat_items};
 use crate::tgusermanager::{user_manager, TgManagerCommand};
 use crate::user::User;
+use std::error::Error as StdError;
 
 use derive_more::From;
 use qif_generator::{account::Account, account::AccountType};
-use std::sync::atomic::{AtomicBool, Ordering};
+use std::fmt::Debug;
+use std::str::FromStr;
+use std::sync::{
+    atomic::{AtomicBool, Ordering},
+    Arc,
+};
 use teloxide::types::*;
 use teloxide::{
     dispatching::dialogue::{InMemStorage, Storage},
@@ -19,6 +23,7 @@ use thiserror::Error;
 use tokio::fs::File;
 use tokio::io::AsyncWriteExt;
 use tokio::sync::{mpsc, oneshot};
+use tokio_stream::wrappers::UnboundedReceiverStream;
 
 #[cfg(feature = "telegram")]
 #[tokio::main]
@@ -124,7 +129,7 @@ pub async fn input_category_from_tg(
     item: &str,
     _cats: &CatStats,
     accounts: &[String],
-    ctx: &UpdateWithCx<AutoSend<Bot>, Message>,
+    cx: &UpdateWithCx<AutoSend<Bot>, Message>,
 ) -> String {
     log::info!("{:?}", accounts);
     let keyboard = InlineKeyboardMarkup::default().append_row(
@@ -138,62 +143,122 @@ pub async fn input_category_from_tg(
                 )
             }),
     );
-    ctx.answer(format!("Input category for {}", item))
+    cx.answer(format!("Input category for {}", item))
         .reply_markup(ReplyMarkup::InlineKeyboard(keyboard))
         .await
         .unwrap();
     String::new()
 }
 
-#[derive(Transition, From)]
+#[derive(Transition, From, Clone)]
 pub enum Dialogue {
+    Idle(IdleState),
     NewJson(NewJsonState),
     CategorySelect(CategorySelectState),
     SubCategorySelect(SubCategorySelectState),
+    ItemReady(ItemReadyState),
     Ready(QIFReadyState),
 }
 
 impl Default for Dialogue {
     fn default() -> Self {
-        Self::NewJson(NewJsonState)
+        Self::Idle(IdleState)
     }
 }
 
-pub struct NewJsonState;
+#[derive(Clone)]
+pub struct IdleState;
+
+#[derive(Clone)]
+pub struct NewJsonState {
+    pub filename: String,
+}
 
+#[derive(Clone)]
 pub struct CategorySelectState {
+    pub filename: String,
     pub item: String,
 }
 
+#[derive(Clone)]
 pub struct SubCategorySelectState {
+    pub filename: String,
     pub item: String,
+    pub category: String,
 }
 
+#[derive(Clone)]
+pub struct ItemReadyState {
+    pub filename: String,
+    pub item: String,
+    pub fullcat: String,
+}
+
+#[derive(Clone)]
 pub struct QIFReadyState;
 
 #[teloxide(subtransition)]
 async fn new_json(
     state: NewJsonState,
     cx: TransitionIn<AutoSend<Bot>>,
-    file_id: String,
+    item: String,
 ) -> TransitionOut<Dialogue> {
+    log::info!("File {}", &state.filename);
+    let mut is_file = false;
+    let mut file_id: String = "".to_string();
+    {
+        let update = &cx.update;
+        if let MessageKind::Common(msg) = &update.kind {
+            if let MediaKind::Document(doc) = &msg.media_kind {
+                is_file = true;
+                file_id = String::from_str(&state.filename).unwrap_or("".to_string());
+            }
+        }
+    }
+    if is_file {
+        log::info!("File {} received", file_id);
+        cx.answer(format!("New file received!!!111 {}", file_id))
+            .await?;
+    } else {
+        cx.answer(format!("Unsupported media provided")).await?;
+    }
+
     if let Ok(newfile) = download_file(cx.requester.inner(), &file_id).await {
         cx.answer(format!("File received: {:} ", newfile)).await?;
         if let Some(tguser) = cx.update.from() {
-            let mut user = User::new(tguser.id, &None);
-            cx.answer(format!("Created user: {:} ", tguser.id)).await?;
-            if let Ok(result) = convert_file(&newfile, &mut user, &cx).await {
-                cx.answer(format!("File converted into: {:} ", result))
-                    .await?;
-                next(CategorySelectState { item: file_id })
+            let user = User::new(tguser.id, &None);
+            cx.answer(format!("Active user: {:} ", tguser.id)).await?;
+            let filepath = format!("{}.qif", &newfile);
+            log::info!("Received file {}", &filepath);
+            let mut i = non_cat_items(&newfile, &user);
+            if let Some(item) = i.pop() {
+                log::info!("No category for {}", &item);
+                cx.answer(format!("Select category for {}", item)).await?;
+                next(CategorySelectState {
+                    filename: state.filename,
+                    item,
+                })
             } else {
+                log::info!("Empty state");
                 next(state)
             }
+
+        /*            if let Ok(result) = convert_file(&newfile, &mut user, &cx).await {
+                        cx.answer(format!("File converted into: {:} ", result))
+                            .await?;
+                        next(CategorySelectState { item: file_id })
+
+                    } else {
+                        next(state)
+                    }
+        */
         } else {
+            log::info!("Empty state 2");
             next(state)
         }
     } else {
-        cx.answer("Waiting for a JSON receipt").await?;
+        log::info!("Newfile {} fail", item);
+        cx.answer("Waiting for a JSON receipt in new_json").await?;
         next(state)
     }
 }
@@ -202,31 +267,81 @@ async fn new_json(
 async fn category_select(
     state: CategorySelectState,
     cx: TransitionIn<AutoSend<Bot>>,
-    item: String,
+    ans: String,
 ) -> TransitionOut<Dialogue> {
-    cx.answer(format!("Selecting category for {}", item))
+    let accounts = [
+        "Expenses:Alco".to_string(),
+        "Expenses:Groceries".to_string(),
+    ];
+    let keyboard = InlineKeyboardMarkup::default().append_row(
+        accounts
+            .iter()
+            .filter(|l| l.starts_with("Expenses:"))
+            .map(|line| {
+                InlineKeyboardButton::new(
+                    line.strip_prefix("Expenses:").unwrap(),
+                    InlineKeyboardButtonKind::CallbackData(line.into()),
+                )
+            }),
+    );
+
+    cx.answer(format!("Input category for {}", state.item))
+        .reply_markup(ReplyMarkup::InlineKeyboard(keyboard))
         .await?;
-    next(state)
+
+    next(SubCategorySelectState {
+        filename: state.filename,
+        item: state.item,
+        category: ans,
+    })
 }
 
 #[teloxide(subtransition)]
 async fn subcategory_select(
     state: SubCategorySelectState,
     cx: TransitionIn<AutoSend<Bot>>,
-    item: String,
+    subcategory: String,
 ) -> TransitionOut<Dialogue> {
-    cx.answer(format!("Selecting subcategory for {}", item))
+    cx.answer(format!("Select subcategory for {}", state.item))
         .await?;
-    next(state)
+    next(ItemReadyState {
+        filename: state.filename,
+        item: state.item,
+        fullcat: format!("{}:{}", state.category, subcategory),
+    })
 }
 
 #[teloxide(subtransition)]
-async fn subcategory_select(
+async fn item_ready(
+    state: ItemReadyState,
+    cx: TransitionIn<AutoSend<Bot>>,
+    item: String,
+) -> TransitionOut<Dialogue> {
+    cx.answer(format!(
+        "Item {} is ready for caterogy {}",
+        state.item, state.fullcat
+    ))
+    .await?;
+    next(QIFReadyState)
+}
+
+#[teloxide(subtransition)]
+async fn qif_ready(
     state: QIFReadyState,
     cx: TransitionIn<AutoSend<Bot>>,
     item: String,
 ) -> TransitionOut<Dialogue> {
     cx.answer(format!("QIF is ready for {}", item)).await?;
+    next(IdleState)
+}
+
+#[teloxide(subtransition)]
+async fn idling(
+    state: IdleState,
+    cx: TransitionIn<AutoSend<Bot>>,
+    item: String,
+) -> TransitionOut<Dialogue> {
+    cx.answer(format!("Waiting for json or command")).await?;
     next(state)
 }
 
@@ -245,69 +360,122 @@ async fn handle_message(
     dialogue: Dialogue,
     tx: mpsc::Sender<TgManagerCommand>,
 ) -> TransitionOut<Dialogue> {
-    match cx.update.text().map(ToOwned::to_owned) {
-        None => {
-            let mut is_file = false;
-            let mut file_id: String = "".to_string();
-            {
-                let update = &cx.update;
-                if let MessageKind::Common(msg) = &update.kind {
-                    if let MediaKind::Document(doc) = &msg.media_kind {
-                        is_file = true;
-                        file_id = doc.document.file_id.clone();
-                    }
-                }
-            }
-            if is_file {
-                Ok(dialogue.react(cx, file_id).await?)
-            } else {
-                next(dialogue)
-            }
-        }
-        Some(ans) => {
-            if let Ok(command) = Command::parse(&ans, "tgqif") {
-                match command {
-                    Command::Help => {
-                        cx.answer(Command::descriptions()).send().await?;
-                    }
-                    Command::Start => {
-                        if let Some(user) = cx.update.from() {
-                            cx.answer(format!(
-                                "You registered as @{} with id {}.",
-                                user.first_name, user.id
-                            ))
-                            .await?;
+    let ans = cx.update.text().map(ToOwned::to_owned);
+    match dialogue {
+        Dialogue::Idle(_) => {
+            match ans {
+                None => {
+                    log::info!("No text");
+                    let mut is_file = false;
+                    let mut file_id: String = "".to_string();
+                    {
+                        let update = &cx.update;
+                        if let MessageKind::Common(msg) = &update.kind {
+                            if let MediaKind::Document(doc) = &msg.media_kind {
+                                is_file = true;
+                                file_id = doc.document.file_id.clone();
+                            }
                         }
                     }
-                    Command::Delete => {
-                        if let Some(user) = cx.update.from() {
-                            cx.answer(format!("Deleting data for user {}", user.id))
-                                .await?;
-                        }
+                    if is_file {
+                        log::info!("File {} received", file_id);
+                        next(NewJsonState { filename: file_id })
+                    //	dialogue.react(cx, file_id).await
+                    } else {
+                        cx.answer(format!("Unsupported media provided")).await?;
+                        next(dialogue)
                     }
-                    Command::Request => {
-                        let (send, recv) = oneshot::channel();
-                        if tx
-                            .send(TgManagerCommand::Get {
-                                user_id: ans,
-                                reply_to: send,
-                            })
-                            .await
-                            .is_err()
-                        {
-                            cx.answer("Can't request data").await?;
-                        };
-
-                        match recv.await {
-                            Ok(value) => cx.answer(format!("I have an answer: {} ", value)).await?,
-                            Err(_) => cx.answer("No data available").await?,
-                        };
+                }
+                Some(ans) => {
+                    if let Ok(command) = Command::parse(&ans, "tgqif") {
+                        match command {
+                            Command::Help => {
+                                cx.answer(Command::descriptions()).send().await?;
+                                next(dialogue)
+                            }
+                            Command::Start => {
+                                if let Some(user) = cx.update.from() {
+                                    cx.answer(format!(
+                                        "You registered as @{} with id {}.",
+                                        user.first_name, user.id
+                                    ))
+                                    .await?;
+                                }
+                                next(dialogue)
+                            }
+                            Command::Delete => {
+                                if let Some(user) = cx.update.from() {
+                                    cx.answer(format!("Deleting data for user {}", user.id))
+                                        .await?;
+                                }
+                                next(dialogue)
+                            }
+                            Command::Request => {
+                                let (send, recv) = oneshot::channel();
+                                if tx
+                                    .send(TgManagerCommand::Get {
+                                        user_id: ans.clone(),
+                                        reply_to: send,
+                                    })
+                                    .await
+                                    .is_err()
+                                {
+                                    cx.answer("Can't request data").await?;
+                                };
+
+                                match recv.await {
+                                    Ok(value) => {
+                                        cx.answer(format!("I have an answer: {} ", value)).await?
+                                    }
+                                    Err(_) => cx.answer("No data available").await?,
+                                };
+                                next(dialogue)
+                            }
+                        }
+                    } else {
+                        next(dialogue)
                     }
                 }
             }
-            next(dialogue)
         }
+        _ => dialogue.react(cx, ans.unwrap_or(String::new())).await, //next(dialogue)
+                                                                     //	    dialogue.react(cx, ans).await
+    }
+}
+
+/// When it receives a callback from a button it edits the message with all
+/// those buttons writing a text with the selected Debian version.
+async fn callback_handler(
+    cx: UpdateWithCx<AutoSend<Bot>, CallbackQuery>,
+    stor: Arc<InMemStorage<Dialogue>>,
+) -> Result<(), Box<dyn StdError + Send + Sync>>
+where
+{
+    let UpdateWithCx {
+        requester: bot,
+        update: query,
+    } = cx;
+
+    if let Some(version) = query.data {
+        let text = format!("{}", version);
+
+        match query.message {
+            Some(Message { id, chat, .. }) => {
+                //                bot.edit_message_text(chat.id, id, text).await?;
+                bot.send_message(chat.id, text).await?;
+            }
+            None => {
+                if let Some(id) = query.inline_message_id {
+                    //                    bot.edit_message_text_inline(dbg!(id), text).await?;
+                    bot.send_message(id, text).await?;
+                }
+            }
+        }
+
+        log::info!("You chose: {}", version);
     }
+
+    Ok(())
 }
 
 #[cfg(feature = "telegram")]
@@ -319,23 +487,40 @@ async fn run() {
 
     let manager = tokio::spawn(async move { user_manager(&mut rx).await });
 
+    let storage = InMemStorage::new();
     let bot = Bot::from_env().auto_send();
     // TODO: Add Dispatcher to process UpdateKinds
-    Dispatcher::new(bot)
-        .messages_handler(DialogueDispatcher::with_storage(
-            move |DialogueWithCx { cx, dialogue }: In| {
-                let _tx = tx.clone();
-                async move {
-                    let dialogue = dialogue.expect("std::convert::Infallible");
-                    handle_message(cx, dialogue, _tx)
-                        .await
-                        .expect("Something wrong with the bot!")
+    {
+        let storage = storage.clone();
+        Dispatcher::new(bot)
+            .messages_handler(DialogueDispatcher::with_storage(
+                move |DialogueWithCx { cx, dialogue }: In| {
+                    let _tx = tx.clone();
+                    async move {
+                        let dialogue = dialogue.expect("std::convert::Infallible");
+                        handle_message(cx, dialogue, _tx)
+                            .await
+                            .expect("Something wrong with the bot!")
+                    }
+                },
+                storage.clone(),
+            ))
+            .callback_queries_handler({
+                let storage1 = Arc::clone(&storage);
+                move |rx: DispatcherHandlerRx<AutoSend<Bot>, CallbackQuery>| {
+                    let storage2 = Arc::clone(&storage1);
+                    UnboundedReceiverStream::new(rx).for_each_concurrent(None, {
+                        let storage3 = Arc::clone(&storage2);
+                        |cx| async move {
+                            let storage4 = Arc::clone(&storage3);
+                            callback_handler(cx, storage4).await.log_on_error().await;
+                        }
+                    })
                 }
-            },
-            InMemStorage::new(),
-        ))
-        .dispatch()
-        .await;
+            })
+            .dispatch()
+            .await;
+    }
     drop(manager);
     IS_RUNNING.store(false, Ordering::SeqCst);
 }

+ 1 - 1
src/tgusermanager.rs

@@ -9,7 +9,7 @@ pub enum TgManagerCommand {
 }
 
 pub async fn user_manager(rx: &mut mpsc::Receiver<TgManagerCommand>) {
-    log::info!("Reqest came");
+    log::info!("Request came");
     while let Some(cmd) = rx.recv().await {
         use TgManagerCommand::*;
         log::info!("Command received");