Compare commits

..

No commits in common. "56f7459ad8df9f247b21bce925c0661bc20257a9" and "5b485c0329d6fffdc7e3d580817d219fcaa64017" have entirely different histories.

19 changed files with 1549 additions and 1056 deletions

538
Cargo.lock generated
View File

@ -106,12 +106,6 @@ version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
[[package]]
name = "az"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973"
[[package]]
name = "backtrace"
version = "0.3.74"
@ -145,25 +139,6 @@ version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "base64-simd"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "339abbe78e73178762e23bea9dfd08e697eb3f3301cd4be981c0f78ba5859195"
dependencies = [
"outref",
"vsimd",
]
[[package]]
name = "bincode"
version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
dependencies = [
"serde",
]
[[package]]
name = "bindgen"
version = "0.71.1"
@ -184,21 +159,6 @@ dependencies = [
"syn 2.0.100",
]
[[package]]
name = "bit-set"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3"
dependencies = [
"bit-vec",
]
[[package]]
name = "bit-vec"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
[[package]]
name = "bitflags"
version = "1.3.2"
@ -256,16 +216,6 @@ dependencies = [
"uuid",
]
[[package]]
name = "bstr"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4"
dependencies = [
"memchr",
"serde",
]
[[package]]
name = "build-time"
version = "0.1.3"
@ -303,26 +253,6 @@ version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
[[package]]
name = "capacity_builder"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f2d24a6dcf0cd402a21b65d35340f3a49ff3475dc5fdac91d22d2733e6641c6"
dependencies = [
"capacity_builder_macros",
"itoa",
]
[[package]]
name = "capacity_builder_macros"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b4a6cae9efc04cc6cbb8faf338d2c497c165c83e74509cf4dbedea948bbf6e5"
dependencies = [
"quote",
"syn 2.0.100",
]
[[package]]
name = "cc"
version = "1.2.17"
@ -440,12 +370,6 @@ dependencies = [
"unicode-segmentation",
]
[[package]]
name = "cooked-waker"
version = "5.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147be55d677052dabc6b22252d5dd0fd4c29c8c27aa4f2fbef0f94aa003b406f"
[[package]]
name = "copy_dir"
version = "0.1.3"
@ -495,15 +419,6 @@ version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
[[package]]
name = "crc32fast"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
dependencies = [
"cfg-if",
]
[[package]]
name = "crossbeam-queue"
version = "0.3.12"
@ -576,126 +491,6 @@ version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476"
[[package]]
name = "debugid"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d"
dependencies = [
"serde",
"uuid",
]
[[package]]
name = "deno_core"
version = "0.350.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e273b731fce500130790e777cb2631dc451db412975304d23816c1e444a10c5b"
dependencies = [
"anyhow",
"az",
"bincode",
"bit-set",
"bit-vec",
"bytes",
"capacity_builder",
"cooked-waker",
"deno_core_icudata",
"deno_error",
"deno_ops",
"deno_path_util",
"deno_unsync",
"futures",
"indexmap 2.8.0",
"libc",
"parking_lot",
"percent-encoding",
"pin-project",
"serde",
"serde_json",
"serde_v8",
"smallvec",
"sourcemap",
"static_assertions",
"thiserror 2.0.12",
"tokio",
"url",
"v8",
"wasm_dep_analyzer",
]
[[package]]
name = "deno_core_icudata"
version = "0.74.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe4dccb6147bb3f3ba0c7a48e993bfeb999d2c2e47a81badee80e2b370c8d695"
[[package]]
name = "deno_error"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "612ec3fc481fea759141b0c57810889b0a4fb6fee8f10748677bfe492fd30486"
dependencies = [
"deno_error_macro",
"libc",
"serde",
"serde_json",
"tokio",
"url",
]
[[package]]
name = "deno_error_macro"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8380a4224d5d2c3f84da4d764c4326cac62e9a1e3d4960442d29136fc07be863"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.100",
]
[[package]]
name = "deno_ops"
version = "0.226.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c28b12489187c71fa123731cc783d48beb17ae5df04da991909cc2ae5a3d0ef9"
dependencies = [
"indexmap 2.8.0",
"proc-macro-rules",
"proc-macro2",
"quote",
"stringcase",
"strum",
"strum_macros",
"syn 2.0.100",
"thiserror 2.0.12",
]
[[package]]
name = "deno_path_util"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "516f813389095889776b81cc9108ff6f336fd9409b4b12fc0138aea23d2708e1"
dependencies = [
"deno_error",
"percent-encoding",
"sys_traits",
"thiserror 2.0.12",
"url",
]
[[package]]
name = "deno_unsync"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6742a724e8becb372a74c650a1aefb8924a5b8107f7d75b3848763ea24b27a87"
dependencies = [
"futures-util",
"parking_lot",
"tokio",
]
[[package]]
name = "deranged"
version = "0.4.1"
@ -881,16 +676,6 @@ dependencies = [
"scopeguard",
]
[[package]]
name = "erased-serde"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e004d887f51fcb9fef17317a2f3525c887d8aa3f4f50fed920816a688284a5b7"
dependencies = [
"serde",
"typeid",
]
[[package]]
name = "errno"
version = "0.3.10"
@ -976,16 +761,6 @@ dependencies = [
"percent-encoding",
]
[[package]]
name = "fslock"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04412b8935272e3a9bae6f48c7bfff74c2911f60525404edfdd28e49884c3bfb"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "funty"
version = "2.0.0"
@ -1151,13 +926,11 @@ checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
name = "gongbotrs"
version = "0.1.0"
dependencies = [
"anyhow",
"async-trait",
"bson",
"build-time",
"chrono",
"chrono-tz",
"deno_core",
"dotenvy",
"enum_stringify",
"envconfig",
@ -1166,26 +939,14 @@ dependencies = [
"itertools 0.14.0",
"lazy_static",
"log",
"mlua",
"mongodb",
"pretty_env_logger",
"quickjs-rusty",
"serde",
"serde_json",
"serde_v8",
"teloxide",
"thiserror 2.0.12",
"tokio",
"v8",
]
[[package]]
name = "gzip-header"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95cc527b92e6029a62960ad99aa8a6660faa4555fe5f731aab13aa6a921795a2"
dependencies = [
"crc32fast",
]
[[package]]
@ -1584,12 +1345,6 @@ dependencies = [
"icu_properties",
]
[[package]]
name = "if_chain"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed"
[[package]]
name = "include_dir"
version = "0.7.4"
@ -1753,12 +1508,6 @@ version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
[[package]]
name = "linux-raw-sys"
version = "0.4.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
[[package]]
name = "linux-raw-sys"
version = "0.9.3"
@ -1796,15 +1545,6 @@ dependencies = [
"linked-hash-map",
]
[[package]]
name = "luau0-src"
version = "0.12.3+luau663"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76ae337c644bbf86a8d8e9ce3ee023311833d41741baf5e51acc31b37843aba1"
dependencies = [
"cc",
]
[[package]]
name = "macro_magic"
version = "0.5.1"
@ -1893,9 +1633,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "miniz_oxide"
version = "0.8.8"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5"
dependencies = [
"adler2",
]
@ -1911,37 +1651,6 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "mlua"
version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1f5f8fbebc7db5f671671134b9321c4b9aa9adeafccfd9a8c020ae45c6a35d0"
dependencies = [
"bstr",
"either",
"erased-serde",
"libloading",
"mlua-sys",
"num-traits",
"parking_lot",
"rustc-hash",
"rustversion",
"serde",
"serde-value",
]
[[package]]
name = "mlua-sys"
version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "380c1f7e2099cafcf40e51d3a9f20a346977587aa4d012eae1f043149a728a93"
dependencies = [
"cc",
"cfg-if",
"luau0-src",
"pkg-config",
]
[[package]]
name = "mongodb"
version = "3.2.3"
@ -2038,7 +1747,6 @@ checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
dependencies = [
"num-integer",
"num-traits",
"rand 0.8.5",
]
[[package]]
@ -2124,21 +1832,6 @@ dependencies = [
"vcpkg",
]
[[package]]
name = "ordered-float"
version = "2.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c"
dependencies = [
"num-traits",
]
[[package]]
name = "outref"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a80800c0488c3a21695ea981a54918fbb37abf04f4d0720c453632255e2ff0e"
[[package]]
name = "parking"
version = "2.2.1"
@ -2177,12 +1870,6 @@ dependencies = [
"regex",
]
[[package]]
name = "paste"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]]
name = "pbkdf2"
version = "0.11.0"
@ -2330,29 +2017,6 @@ dependencies = [
"quote",
]
[[package]]
name = "proc-macro-rules"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07c277e4e643ef00c1233393c673f655e3672cf7eb3ba08a00bdd0ea59139b5f"
dependencies = [
"proc-macro-rules-macros",
"proc-macro2",
"syn 2.0.100",
]
[[package]]
name = "proc-macro-rules-macros"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "207fffb0fe655d1d47f6af98cc2793405e85929bdbc420d685554ff07be27ac7"
dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.100",
]
[[package]]
name = "proc-macro2"
version = "1.0.94"
@ -2611,19 +2275,6 @@ dependencies = [
"semver",
]
[[package]]
name = "rustix"
version = "0.38.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
dependencies = [
"bitflags 2.9.0",
"errno",
"libc",
"linux-raw-sys 0.4.15",
"windows-sys 0.59.0",
]
[[package]]
name = "rustix"
version = "1.0.3"
@ -2633,7 +2284,7 @@ dependencies = [
"bitflags 2.9.0",
"errno",
"libc",
"linux-raw-sys 0.9.3",
"linux-raw-sys",
"windows-sys 0.59.0",
]
@ -2767,16 +2418,6 @@ dependencies = [
"serde_derive",
]
[[package]]
name = "serde-value"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c"
dependencies = [
"ordered-float",
"serde",
]
[[package]]
name = "serde_bytes"
version = "0.11.17"
@ -2822,20 +2463,6 @@ dependencies = [
"serde",
]
[[package]]
name = "serde_v8"
version = "0.259.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7e4c5f439cffc03021c8bfd380cd1ef6acb4788a72b7e54bf4a83c73f91f8a0"
dependencies = [
"deno_error",
"num-bigint",
"serde",
"smallvec",
"thiserror 2.0.12",
"v8",
]
[[package]]
name = "serde_with"
version = "3.12.0"
@ -2937,24 +2564,6 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "sourcemap"
version = "9.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e22afbcb92ce02d23815b9795523c005cb9d3c214f8b7a66318541c240ea7935"
dependencies = [
"base64-simd",
"bitvec",
"data-encoding",
"debugid",
"if_chain",
"rustc-hash",
"serde",
"serde_json",
"unicode-id-start",
"url",
]
[[package]]
name = "spin"
version = "0.9.8"
@ -3114,18 +2723,6 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "stringcase"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72abeda133c49d7bddece6c154728f83eec8172380c80ab7096da9487e20d27c"
[[package]]
name = "stringprep"
version = "0.1.5"
@ -3143,28 +2740,6 @@ version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "strum"
version = "0.27.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f64def088c51c9510a8579e3c5d67c65349dcf755e5479ad3d010aa6454e2c32"
dependencies = [
"strum_macros",
]
[[package]]
name = "strum_macros"
version = "0.27.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c77a8c5abcaf0f9ce05d62342b7d298c346515365c36b673df4ebe3ced01fde8"
dependencies = [
"heck",
"proc-macro2",
"quote",
"rustversion",
"syn 2.0.100",
]
[[package]]
name = "subtle"
version = "2.6.1"
@ -3213,26 +2788,6 @@ dependencies = [
"syn 2.0.100",
]
[[package]]
name = "sys_traits"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc4707edf3196e8037ee45018d1bb1bfb233b0e4fc440fa3d3f25bc69bfdaf26"
dependencies = [
"sys_traits_macros",
]
[[package]]
name = "sys_traits_macros"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "181f22127402abcf8ee5c83ccd5b408933fec36a6095cf82cda545634692657e"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.100",
]
[[package]]
name = "take_mut"
version = "0.2.2"
@ -3330,7 +2885,7 @@ dependencies = [
"fastrand",
"getrandom 0.3.2",
"once_cell",
"rustix 1.0.3",
"rustix",
"windows-sys 0.59.0",
]
@ -3458,7 +3013,6 @@ dependencies = [
"bytes",
"libc",
"mio",
"parking_lot",
"pin-project-lite",
"signal-hook-registry",
"socket2",
@ -3598,12 +3152,6 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "typeid"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c"
[[package]]
name = "typenum"
version = "1.18.0"
@ -3622,12 +3170,6 @@ version = "0.3.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5"
[[package]]
name = "unicode-id-start"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f322b60f6b9736017344fa0635d64be2f458fbc04eef65f6be22976dd1ffd5b"
[[package]]
name = "unicode-ident"
version = "1.0.18"
@ -3703,22 +3245,6 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "v8"
version = "137.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2b387c1c5731284e756c03280032068e68e5b52f6c4714492403c30f650ad52"
dependencies = [
"bindgen",
"bitflags 2.9.0",
"fslock",
"gzip-header",
"home",
"miniz_oxide",
"paste",
"which",
]
[[package]]
name = "vcpkg"
version = "0.2.15"
@ -3731,12 +3257,6 @@ version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "vsimd"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64"
[[package]]
name = "walkdir"
version = "2.5.0"
@ -3861,16 +3381,6 @@ dependencies = [
"web-sys",
]
[[package]]
name = "wasm_dep_analyzer"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e51cf5f08b357e64cd7642ab4bbeb11aecab9e15520692129624fb9908b8df2c"
dependencies = [
"deno_error",
"thiserror 2.0.12",
]
[[package]]
name = "web-sys"
version = "0.3.77"
@ -3887,18 +3397,6 @@ version = "0.25.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1"
[[package]]
name = "which"
version = "6.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4ee928febd44d98f2f459a4a79bd4d928591333a494a10a868418ac1b39cf1f"
dependencies = [
"either",
"home",
"rustix 0.38.44",
"winsafe",
]
[[package]]
name = "whoami"
version = "1.6.0"
@ -3915,22 +3413,6 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd7cf3379ca1aac9eea11fba24fd7e315d621f8dfe35c8d7d2be8b793726e07d"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.9"
@ -3940,12 +3422,6 @@ dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-core"
version = "0.52.0"
@ -4212,12 +3688,6 @@ dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "winsafe"
version = "0.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904"
[[package]]
name = "wit-bindgen-rt"
version = "0.39.0"

View File

@ -6,13 +6,11 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1.0.98"
async-trait = "0.1.88"
bson = { version = "2.14.0", features = ["chrono-0_4"] }
build-time = "0.1.3"
chrono = { version = "0.4.40", features = ["serde"] }
chrono-tz = "0.10.3"
deno_core = "0.350.0"
dotenvy = "0.15.7"
enum_stringify = "0.6.3"
envconfig = "0.11.0"
@ -21,17 +19,14 @@ git-const = "1.1.0"
itertools = "0.14.0"
lazy_static = "1.5.0"
log = "0.4.27"
mlua = { version = "0.10.5", features = ["luau", "serialize"] }
mongodb = "3.2.3"
pretty_env_logger = "0.5.0"
quickjs-rusty = { git = "https://github.com/akulij/quickjs-rusty.git", rev = "549f830" }
serde = { version = "1.0.219", features = ["derive", "serde_derive"] }
serde_json = "1.0.140"
serde_v8 = "0.259.0"
teloxide = { version = "0.14.0", features = ["macros", "postgres-storage-nativetls"] }
thiserror = "2.0.12"
tokio = { version = "1.44.1", features = ["rt-multi-thread", "macros"] }
v8 = "137.2.0"
[lints.clippy]
print_stdout = "warn"

View File

@ -93,22 +93,14 @@ print(JSON.stringify(dialog.buttons))
const config = {
version: 1.1,
timezone: 5,
timezone: 3,
}
const notifications = [
{
// time: "17:38",
time: {once: "17:49"},
message: {literal: "show_projects"},
},
// {
// time: {
// hour: 0,
// delta_minutes: 2,
// },
// time: "18:14",
// message: {literal: "show_projects"},
// }
// },
]
// {config, dialog}

View File

@ -178,9 +178,12 @@ pub async fn admin_command_handler(
}
};
BotInstance::new(name.clone(), token.to_string(), DEFAULT_SCRIPT.to_string())
.store(&mut db)
.await?
let bi =
BotInstance::new(name.clone(), token.to_string(), DEFAULT_SCRIPT.to_string())
.store(&mut db)
.await?;
bi
};
bot.send_message(

View File

@ -1,40 +1,29 @@
use futures::future::join_all;
use log::{error, info};
use quickjs_rusty::serde::to_js;
use serde_json::Value;
use quickjs_rusty::serde::{from_js, to_js};
use std::{
str::FromStr,
sync::{Arc, Mutex},
sync::{Arc, Mutex, RwLock},
};
use teloxide::{
dispatching::{dialogue::GetChatId, UpdateFilterExt},
dptree::{self, Handler},
prelude::{DependencyMap, Requester},
types::{CallbackQuery, InlineKeyboardMarkup, Message, Update},
prelude::DependencyMap,
types::{CallbackQuery, InlineKeyboardButton, InlineKeyboardMarkup, Message, Update},
Bot,
};
use crate::{
botscript::{self, message_info::MessageInfoBuilder, ScriptError},
botscript::{self, message_info::MessageInfoBuilder, BotMessage, RunnerConfig},
commands::BotCommand,
config::{
dialog::{button::ButtonLayout, message::BotMessage},
traits::ProviderSerialize,
Provider,
},
db::{callback_info::CallbackInfo, CallDB, DB},
db::{CallDB, DB},
message_answerer::MessageAnswerer,
notify_admin, update_user_tg,
utils::callback_button,
BotError, BotResult, BotRuntime,
notify_admin, update_user_tg, BotError, BotResult, BotRuntime,
};
pub type BotHandler =
Handler<'static, DependencyMap, BotResult<()>, teloxide::dispatching::DpHandlerDescription>;
type CallbackStore = CallbackInfo<Value>;
pub fn script_handler<P: Provider + Send + Sync>(r: Arc<Mutex<BotRuntime<P>>>) -> BotHandler {
pub fn script_handler(r: Arc<Mutex<BotRuntime>>) -> BotHandler {
let cr = r.clone();
dptree::entry()
.branch(
@ -59,50 +48,21 @@ pub fn script_handler<P: Provider + Send + Sync>(r: Arc<Mutex<BotRuntime<P>>>) -
)
.branch(
Update::filter_callback_query()
.filter_map_async(move |q: CallbackQuery, mut db: DB| {
let r = Arc::clone(&cr);
async move {
let data = match q.data {
Some(data) => data,
None => return None,
};
let ci = match CallbackStore::get(&mut db, &data).await {
Ok(ci) => ci,
Err(err) => {
notify_admin(&format!(
"Failed to get callback from CallbackInfo, err: {err}"
))
.await;
return None;
}
};
let ci = match ci {
Some(ci) => ci,
None => return None,
};
let data = match ci.literal {
Some(data) => data,
None => return None,
};
.filter_map(move |q: CallbackQuery| {
q.data.and_then(|data| {
let r = std::sync::Arc::clone(&cr);
let r = r.lock().expect("RwLock lock on commands map failed");
let rc = &r.rc;
rc.get_callback_message(&data)
}
})
})
.endpoint(handle_callback),
)
}
async fn handle_botmessage<P: Provider>(
bot: Bot,
mut db: DB,
bm: BotMessage<P>,
msg: Message,
) -> BotResult<()> {
// info!("Eval BM: {:?}", bm);
async fn handle_botmessage(bot: Bot, mut db: DB, bm: BotMessage, msg: Message) -> BotResult<()> {
info!("Eval BM: {:?}", bm);
let tguser = match msg.from.clone() {
Some(user) => user,
None => return Ok(()), // do nothing, cause its not usecase of function
@ -118,7 +78,7 @@ async fn handle_botmessage<P: Provider>(
Err(_) => None,
};
if bm.meta() {
if bm.meta() == true {
if let Some(ref meta) = variant {
user.insert_meta(&mut db, meta).await?;
};
@ -126,34 +86,31 @@ async fn handle_botmessage<P: Provider>(
let is_propagate: bool = match bm.get_handler() {
Some(handler) => 'prop: {
// let ctx = match handler.context() {
// Some(ctx) => ctx,
// // falling back to propagation
// None => break 'prop true,
// };
// let jsuser = to_js(ctx, &tguser).map_err(ScriptError::from)?;
let puser = <P::Value as ProviderSerialize>::se_from(&tguser).unwrap();
let ctx = match handler.context() {
Some(ctx) => ctx,
// falling back to propagation
None => break 'prop true,
};
let jsuser = to_js(ctx, &tguser).unwrap();
let mi = MessageInfoBuilder::new()
.set_variant(variant.clone())
.build();
// let mi = to_js(ctx, &mi).map_err(ScriptError::from)?;
let pmi = <P::Value as ProviderSerialize>::se_from(&mi).unwrap();
// info!(
// "Calling handler {:?} with msg literal: {:?}",
// handler,
// bm.literal()
// );
match handler.call_args(vec![puser, pmi]) {
let mi = to_js(ctx, &mi).unwrap();
info!(
"Calling handler {:?} with msg literal: {:?}",
handler,
bm.literal()
);
match handler.call_args(vec![jsuser, mi]) {
Ok(v) => {
todo!()
// if v.is_bool() {
// v.to_bool().unwrap_or(true)
// } else if v.is_int() {
// v.to_int().unwrap_or(1) != 0
// } else {
// // falling back to propagation
// true
// }
if v.is_bool() {
v.to_bool().unwrap_or(true)
} else if v.is_int() {
v.to_int().unwrap_or(1) != 0
} else {
// falling back to propagation
true
}
}
Err(err) => {
error!("Failed to get return of handler, err: {err}");
@ -169,56 +126,36 @@ async fn handle_botmessage<P: Provider>(
return Ok(());
}
let button_db = db.clone();
let buttons = bm.resolve_buttons(&mut db).await?.map(async |buttons| {
join_all(buttons.iter().map(async |r| {
join_all(r.iter().map(async |b| {
match b {
ButtonLayout::Callback {
name,
literal: _,
callback,
} => {
callback_button(
name,
callback.to_string(),
None::<bool>,
&mut button_db.clone(),
)
.await
}
}
}))
.await
.into_iter()
.collect::<Result<_, _>>()
}))
.await
.into_iter()
.collect::<Result<_, _>>()
});
let buttons = match buttons {
Some(b) => Some(InlineKeyboardMarkup {
inline_keyboard: b.await?,
}),
None => None,
};
let buttons = bm
.resolve_buttons(&mut db)
.await?
.map(|buttons| InlineKeyboardMarkup {
inline_keyboard: buttons
.iter()
.map(|r| {
r.iter()
.map(|b| match b {
botscript::ButtonLayout::Callback {
name,
literal: _,
callback,
} => InlineKeyboardButton::callback(name, callback),
})
.collect()
})
.collect(),
});
let literal = bm.literal().map_or("", |s| s.as_str());
let ma = MessageAnswerer::new(&bot, &mut db, msg.chat.id.0);
ma.answer(literal, variant.as_deref(), buttons).await?;
ma.answer(literal, variant.as_ref().map(|v| v.as_str()), buttons)
.await?;
Ok(())
}
async fn handle_callback<P: Provider>(
bot: Bot,
mut db: DB,
bm: BotMessage<P>,
q: CallbackQuery,
) -> BotResult<()> {
bot.answer_callback_query(&q.id).await?;
// info!("Eval BM: {:?}", bm);
async fn handle_callback(bot: Bot, mut db: DB, bm: BotMessage, q: CallbackQuery) -> BotResult<()> {
info!("Eval BM: {:?}", bm);
let tguser = q.from.clone();
let user = db
.get_or_init_user(tguser.id.0 as i64, &tguser.first_name)
@ -226,24 +163,36 @@ async fn handle_callback<P: Provider>(
let user = update_user_tg(user, &tguser);
user.update_user(&mut db).await?;
println!("Is handler set: {}", bm.get_handler().is_some());
let is_propagate: bool = match bm.get_handler() {
Some(handler) => 'prop: {
let puser = <P::Value as ProviderSerialize>::se_from(&tguser).unwrap();
let ctx = match handler.context() {
Some(ctx) => ctx,
// falling back to propagation
None => break 'prop true,
};
let jsuser = to_js(ctx, &tguser).unwrap();
let mi = MessageInfoBuilder::new().build();
let pmi = <P::Value as ProviderSerialize>::se_from(&mi).unwrap();
match handler.call_args(vec![puser, pmi]) {
let mi = to_js(ctx, &mi).unwrap();
println!(
"Calling handler {:?} with msg literal: {:?}",
handler,
bm.literal()
);
match handler.call_args(vec![jsuser, mi]) {
Ok(v) => {
todo!()
// if v.is_bool() {
// v.to_bool().unwrap_or(true)
// } else if v.is_int() {
// v.to_int().unwrap_or(1) != 0
// } else {
// // falling back to propagation
// true
// }
println!("Ok branch, got value: {v:?}");
if v.is_bool() {
v.to_bool().unwrap_or(true)
} else if v.is_int() {
v.to_int().unwrap_or(1) != 0
} else {
// falling back to propagation
true
}
}
Err(err) => {
println!("ERR branch");
error!("Failed to get return of handler, err: {err}");
// falling back to propagation
true
@ -257,40 +206,25 @@ async fn handle_callback<P: Provider>(
return Ok(());
}
let button_db = db.clone();
let buttons = bm.resolve_buttons(&mut db).await?.map(async |buttons| {
join_all(buttons.iter().map(async |r| {
join_all(r.iter().map(async |b| {
match b {
ButtonLayout::Callback {
name,
literal: _,
callback,
} => {
callback_button(
name,
callback.to_string(),
None::<bool>,
&mut button_db.clone(),
)
.await
}
}
}))
.await
.into_iter()
.collect::<Result<_, _>>()
}))
.await
.into_iter()
.collect::<Result<_, _>>()
});
let buttons = match buttons {
Some(b) => Some(InlineKeyboardMarkup {
inline_keyboard: b.await?,
}),
None => None,
};
let buttons = bm
.resolve_buttons(&mut db)
.await?
.map(|buttons| InlineKeyboardMarkup {
inline_keyboard: buttons
.iter()
.map(|r| {
r.iter()
.map(|b| match b {
botscript::ButtonLayout::Callback {
name,
literal: _,
callback,
} => InlineKeyboardButton::callback(name, callback),
})
.collect()
})
.collect(),
});
let literal = bm.literal().map_or("", |s| s.as_str());
let (chat_id, msg_id) = {
@ -318,7 +252,7 @@ async fn handle_callback<P: Provider>(
Ok(msg_id) => {
ma.replace_message(msg_id, literal, buttons).await?;
}
Err(_) => {
Err(err) => {
ma.answer(literal, None, buttons).await?;
}
};

View File

@ -1,34 +1,39 @@
use std::{
collections::HashMap,
future::Future,
sync::{Arc, Mutex},
sync::{Arc, Mutex, RwLock},
thread::JoinHandle,
time::Duration,
};
use lazy_static::lazy_static;
use log::{error, info};
use teloxide::{dispatching::dialogue::serializer::Json, dptree, prelude::Dispatcher, Bot};
use teloxide::{
dispatching::dialogue::serializer::Json,
dptree,
prelude::{Dispatcher, Requester},
types::{ChatId, UserId},
Bot,
};
use tokio::runtime::Handle;
use crate::{
bot_handler::{script_handler, BotHandler},
db::{bots::BotInstance, DbError, DB},
message_answerer::MessageAnswerer,
mongodb_storage::MongodbStorage,
BotController, BotResult, BotRuntime,
BotController, BotError, BotResult, BotRuntime,
};
pub type BotThread = JoinHandle<BotResult<()>>;
pub struct BotRunner {
controller: BotController,
info: BotInfo,
notificator: NotificatorThread,
thread: Option<BotThread>,
thread: Option<JoinHandle<BotResult<()>>>,
}
#[derive(Debug)]
pub enum NotificatorThread {
Running(Option<BotThread>),
Running(Option<JoinHandle<BotResult<()>>>),
Done,
}
@ -40,28 +45,32 @@ pub struct BotInfo {
pub static DEFAULT_SCRIPT: &str =
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/default_script.js"));
pub struct BotManager<BIG, BHG, BII, BHI>
pub struct BotManager<BIG, HM, BIS, HI, FBIS, FHI>
where
BIG: AsyncFnMut() -> BII, // BotInstance Getter
BII: Iterator<Item = BotInstance>, // BotInstance Iterator
BHG: AsyncFnMut(BotInstance) -> BHI, // BotHandler Getter
BHI: Iterator<Item = BotHandler>, // BotHandler Iterator
BIG: FnMut() -> FBIS,
FBIS: Future<Output = BIS>,
BIS: Iterator<Item = BotInstance>,
HM: FnMut(BotInstance) -> FHI,
FHI: Future<Output = HI>,
HI: Iterator<Item = BotHandler>,
{
bot_pool: HashMap<String, BotRunner>,
bi_getter: BIG,
h_mapper: BHG,
h_mapper: HM,
}
impl<BIG, BHG, BII, BHI> BotManager<BIG, BHG, BII, BHI>
impl<BIG, HM, BIS, HI, FBIS, FHI> BotManager<BIG, HM, BIS, HI, FBIS, FHI>
where
BIG: AsyncFnMut() -> BII, // BotInstance Getter
BII: Iterator<Item = BotInstance>, // BotInstance Iterator
BHG: AsyncFnMut(BotInstance) -> BHI, // BotHandler Getter
BHI: Iterator<Item = BotHandler>, // BotHandler Iterator
BIG: FnMut() -> FBIS,
FBIS: Future<Output = BIS>,
BIS: Iterator<Item = BotInstance>,
HM: FnMut(BotInstance) -> FHI,
FHI: Future<Output = HI>,
HI: Iterator<Item = BotHandler>,
{
/// bi_getter - async fnmut that returns iterator over BotInstance
/// h_map - async fnmut that returns iterator over handlers by BotInstance
pub fn with(bi_getter: BIG, h_mapper: BHG) -> Self {
/// bi_getter - fnmut that returns iterator over BotInstance
/// h_map - fnmut that returns iterator over handlers by BotInstance
pub fn with(bi_getter: BIG, h_mapper: HM) -> Self {
Self {
bot_pool: Default::default(),
bi_getter,
@ -69,9 +78,9 @@ where
}
}
pub async fn dispatch(mut self, db: &mut DB) -> BotResult<()> {
pub async fn dispatch(mut self, db: &mut DB) -> ! {
loop {
for bi in (self.bi_getter)().await {
'biter: for bi in (self.bi_getter)().await {
// removing handler to force restart
// TODO: wait till all updates are processed in bot
// Temporarly disabling code, because it's free of js runtime
@ -81,18 +90,32 @@ where
"Trying to restart bot `{}`, new script: {}",
bi.name, bi.script
);
let _runner = self.bot_pool.remove(&bi.name);
let runner = self.bot_pool.remove(&bi.name);
};
// start, if not started
let mut bot_runner = match self.bot_pool.remove(&bi.name) {
Some(br) => br,
None => {
let handlers = (self.h_mapper)(bi.clone()).await;
info!("NEW INSTANCE: Starting new instance! bot name: {}", bi.name);
self.create_bot_runner(&bi, db).await?
self.start_bot(bi, db, handlers.collect()).await.unwrap();
continue 'biter;
}
};
bot_runner.thread = clear_finished_thread(bot_runner.thread, &bi);
// checking if thread is not finished, otherwise clearing handler
bot_runner.thread = match bot_runner.thread {
Some(thread) => {
if thread.is_finished() {
let err = thread.join();
error!("Thread bot `{}` finished with error: {:?}", bi.name, err);
None
} else {
Some(thread)
}
}
None => None,
};
// checking if thread is running, otherwise start thread
bot_runner.thread = match bot_runner.thread {
@ -110,88 +133,46 @@ where
bot_runner.controller.db.clone(),
handler,
)
.await?,
.await
.unwrap(),
)
}
};
bot_runner.notificator = check_notificator_done(bot_runner.notificator);
bot_runner.notificator = match bot_runner.notificator {
NotificatorThread::Done => NotificatorThread::Done,
NotificatorThread::Running(thread) => {
NotificatorThread::Running(match thread {
Some(thread) => Some(thread),
None => {
let thread =
spawn_notificator_thread(bot_runner.controller.clone()).await?;
Some(thread)
}
})
}
};
self.bot_pool.insert(bi.name.clone(), bot_runner);
}
tokio::time::sleep(Duration::from_secs(1)).await;
}
}
pub async fn create_bot_runner(
pub async fn start_bot(
&mut self,
bi: &BotInstance,
bi: BotInstance,
db: &mut DB,
) -> BotResult<BotRunner> {
plug_handlers: Vec<BotHandler>,
) -> BotResult<BotInfo> {
let db = db.clone().with_name(bi.name.clone());
let controller = BotController::with_db(db.clone(), &bi.token, &bi.script).await?;
let handler = script_handler_gen(controller.runtime.clone(), plug_handlers).await;
let thread =
spawn_bot_thread(controller.bot.clone(), controller.db.clone(), handler).await?;
let notificator = spawn_notificator_thread(controller.clone()).await?;
let notificator = NotificatorThread::Running(Some(notificator));
let info = BotInfo {
name: bi.name.clone(),
};
let runner = BotRunner {
controller,
info,
notificator: NotificatorThread::Running(None),
thread: None,
info: info.clone(),
notificator,
thread: Some(thread),
};
Ok(runner)
}
}
self.bot_pool.insert(bi.name.clone(), runner);
/// checking if thread is not finished, otherwise clearing handler
fn clear_finished_thread(thread: Option<BotThread>, bi: &BotInstance) -> Option<BotThread> {
thread.and_then(|thread| match thread.is_finished() {
false => Some(thread),
// if finished, join it (should return immidiatly), and print cause of stop
true => {
let err = thread.join();
error!("Thread bot `{}` finished with error: {:?}", bi.name, err);
None
}
})
}
// sets NotificatorThread to Done if running thread returned Ok(...)
fn check_notificator_done(n: NotificatorThread) -> NotificatorThread {
match n {
NotificatorThread::Running(Some(thread)) if thread.is_finished() => {
match thread.join() {
// if thread returns Ok(_), then do not run it again
Ok(result) if result.is_ok() => NotificatorThread::Done,
// but try to restart, if returned an error
Ok(result) => {
error!("Notificator thread returned error: {result:?}");
NotificatorThread::Running(None)
}
Err(panicerr) => {
error!("Notificator thread paniced: {panicerr:?}");
NotificatorThread::Running(None)
}
}
}
other => other,
Ok(info)
}
}
@ -209,14 +190,21 @@ async fn script_handler_gen(
handler
}
pub async fn spawn_bot_thread(bot: Bot, mut db: DB, handler: BotHandler) -> BotResult<BotThread> {
pub async fn spawn_bot_thread(
bot: Bot,
mut db: DB,
handler: BotHandler,
) -> BotResult<JoinHandle<BotResult<()>>> {
let state_mgr = MongodbStorage::from_db(&mut db, Json)
.await
.map_err(DbError::from)?;
let thread = std::thread::spawn(move || -> BotResult<()> {
let state_mgr = state_mgr;
let rt = tokio::runtime::Runtime::new()?;
// let rt = tokio::runtime::Builder::new_current_thread()
// .enable_all()
// .build()?;
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(
Dispatcher::builder(bot, handler)
@ -231,22 +219,23 @@ pub async fn spawn_bot_thread(bot: Bot, mut db: DB, handler: BotHandler) -> BotR
Ok(thread)
}
pub async fn spawn_notificator_thread(mut c: BotController) -> BotResult<BotThread> {
pub async fn spawn_notificator_thread(
mut c: BotController,
) -> BotResult<JoinHandle<BotResult<()>>> {
let thread = std::thread::spawn(move || -> BotResult<()> {
let rt = tokio::runtime::Runtime::new()?;
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
loop {
let notifications = {
let r = c.runtime.lock().expect("Poisoned Runtime lock");
r.rc.get_nearest_notifications()
};
let r = c.runtime.lock().unwrap();
let notifications = r.rc.get_nearest_notifications();
drop(r); // unlocking mutex
match notifications {
Some(n) => {
// waiting time to send notification
tokio::time::sleep(n.wait_for()).await;
'n: for n in n.notifications().iter() {
'n: for n in n.notifications().into_iter() {
for user in n.get_users(&c.db).await?.into_iter() {
let text = match n.resolve_message(&c.db, &user).await? {
Some(text) => text,

File diff suppressed because it is too large Load Diff

View File

@ -1,11 +1,14 @@
use std::sync::RwLock;
use log::info;
use quickjs_rusty::{context::Context, serde::from_js, OwnedJsObject};
use teloxide::Bot;
use tokio::runtime::Handle;
use crate::{
db::{application::Application, message_forward::MessageForward, DB},
db::{application::Application, message_forward::MessageForward, CallDB, DB},
message_answerer::MessageAnswerer,
send_application_to_chat,
send_application_to_chat, BotError,
};
use super::ScriptError;
@ -13,42 +16,66 @@ use super::ScriptError;
pub fn attach_user_application(
c: &Context,
o: &mut OwnedJsObject,
db: DB,
bot: Bot,
db: &DB,
bot: &Bot,
) -> Result<(), ScriptError> {
// To guarantee that closure is valid if thread panics
let db: std::sync::Mutex<DB> = std::sync::Mutex::new(db);
let bot: std::sync::Mutex<Bot> = std::sync::Mutex::new(bot);
let db: std::sync::Arc<RwLock<DB>> = std::sync::Arc::new(RwLock::new(db.clone()));
let dbbox = Box::new(db.clone());
let db: &'static _ = Box::leak(dbbox);
let bot: std::sync::Arc<RwLock<Bot>> = std::sync::Arc::new(RwLock::new(bot.clone()));
let botbox = Box::new(bot.clone());
let bot: &'static _ = Box::leak(botbox);
let user_application =
c.create_callback(move |q: OwnedJsObject| -> Result<_, ScriptError> {
let mut db = { db.lock().map_err(ScriptError::from)?.clone() };
let bot = { bot.lock().map_err(ScriptError::from)?.clone() };
println!("user_application is called");
let db = db.clone();
let user: teloxide::types::User = match from_js(q.context(), &q) {
Ok(q) => q,
Err(_) => todo!(),
};
let application =
futures::executor::block_on(Application::new(user.clone()).store_db(&mut db))?;
let application = futures::executor::block_on(
Application::new(user.clone()).store_db(&mut db.write().unwrap()),
)?;
println!("there1");
let msg = tokio::task::block_in_place(|| {
Handle::current()
.block_on(async { send_application_to_chat(&bot, &mut db, &application).await })
});
let msg = msg.map_err(ScriptError::from)?;
let (chat_id, msg_id) = tokio::task::block_in_place(|| {
Handle::current().block_on(async {
MessageAnswerer::new(&bot, &mut db, user.id.0 as i64)
.answer("left_application_msg", None, None)
.await
let db2 = db.clone();
let msg = tokio::task::block_in_place(move || {
Handle::current().block_on(async move {
send_application_to_chat(
&bot.read().unwrap(),
&mut db2.write().unwrap(),
&application,
)
.await
})
})?;
});
println!("there2");
let msg = match msg {
Ok(msg) => msg,
Err(err) => {
info!("Got err: {err}");
return Err(ScriptError::MutexError("🤦‍♂️".to_string()));
}
};
let (chat_id, msg_id) = futures::executor::block_on(
MessageAnswerer::new(
&bot.read().unwrap(),
&mut db.write().unwrap(),
user.id.0 as i64,
)
.answer("left_application_msg", None, None),
)
.unwrap();
println!("there3");
futures::executor::block_on(
MessageForward::new(msg.chat.id.0, msg.id.0, chat_id, msg_id, false)
.store_db(&mut db),
.store_db(&mut db.write().unwrap()),
)?;
println!("there4");
let ret = true;
Ok(ret)

View File

@ -16,14 +16,16 @@ pub fn attach_db_obj(c: &Context, o: &mut OwnedJsObject, db: &DB) -> Result<(),
.expect("the created object was not an object :/");
let db: std::sync::Arc<RwLock<DB>> = std::sync::Arc::new(RwLock::new(db.clone()));
let dbbox = Box::new(db);
let db: &'static _ = Box::leak(dbbox);
let find_one = c.create_callback(
move |collection: String, q: OwnedJsObject| -> Result<_, ScriptError> {
// let db = db.clone();
|collection: String, q: OwnedJsObject| -> Result<_, ScriptError> {
let query: serde_json::Value = match from_js(q.context(), &q) {
Ok(q) => q,
Err(_) => todo!(),
};
let db = db.clone();
let value = futures::executor::block_on(
db.write()

View File

@ -9,12 +9,6 @@ pub struct MessageInfoBuilder {
inner: MessageInfo,
}
impl Default for MessageInfoBuilder {
fn default() -> Self {
Self::new()
}
}
impl MessageInfoBuilder {
pub fn new() -> Self {
Self {

View File

@ -67,7 +67,6 @@ impl BotCommand {
}
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
use super::*;

View File

@ -1,5 +1,7 @@
use bson::doc;
use bson::oid::ObjectId;
use chrono::{DateTime, FixedOffset, Local};
use futures::StreamExt;
use futures::TryStreamExt;
use serde::{Deserialize, Serialize};
@ -43,23 +45,19 @@ impl BotInstance {
Ok(self)
});
pub async fn get_all<D: GetCollection>(db: &mut D) -> DbResult<Vec<Self>> {
pub async fn get_all<D: CallDB>(db: &mut D) -> DbResult<Vec<Self>> {
let bi = db.get_collection::<Self>().await;
Ok(bi.find(doc! {}).await?.try_collect().await?)
}
pub async fn get_by_name<D: GetCollection>(db: &mut D, name: &str) -> DbResult<Option<Self>> {
pub async fn get_by_name<D: CallDB>(db: &mut D, name: &str) -> DbResult<Option<Self>> {
let bi = db.get_collection::<Self>().await;
Ok(bi.find_one(doc! {"name": name}).await?)
}
pub async fn restart_one<D: GetCollection>(
db: &mut D,
name: &str,
restart: bool,
) -> DbResult<()> {
pub async fn restart_one<D: CallDB>(db: &mut D, name: &str, restart: bool) -> DbResult<()> {
let bi = db.get_collection::<Self>().await;
bi.update_one(
@ -70,7 +68,7 @@ impl BotInstance {
Ok(())
}
pub async fn restart_all<D: GetCollection>(db: &mut D, restart: bool) -> DbResult<()> {
pub async fn restart_all<D: CallDB>(db: &mut D, restart: bool) -> DbResult<()> {
let bi = db.get_collection::<Self>().await;
bi.update_many(doc! {}, doc! { "$set": { "restart_flag": restart } })
@ -78,11 +76,7 @@ impl BotInstance {
Ok(())
}
pub async fn update_script<D: GetCollection>(
db: &mut D,
name: &str,
script: &str,
) -> DbResult<()> {
pub async fn update_script<D: CallDB>(db: &mut D, name: &str, script: &str) -> DbResult<()> {
let bi = db.get_collection::<Self>().await;
bi.update_one(

View File

@ -7,10 +7,11 @@ pub mod raw_calls;
use std::time::Duration;
use async_trait::async_trait;
use chrono::{DateTime, Local, Utc};
use chrono::{DateTime, FixedOffset, Local, Utc};
use enum_stringify::EnumStringify;
use futures::stream::TryStreamExt;
use futures::StreamExt;
use mongodb::bson::serde_helpers::chrono_datetime_as_bson_datetime;
use mongodb::options::IndexOptions;
use mongodb::{bson::doc, options::ClientOptions, Client};
@ -56,7 +57,7 @@ macro_rules! query_call {
#[macro_export]
macro_rules! query_call_consume {
($func_name:ident, $self:ident, $db:ident, $return_type:ty, $body:block) => {
pub async fn $func_name<D: $crate::db::GetCollection + CallDB>($self, $db: &mut D)
pub async fn $func_name<D: CallDB>($self, $db: &mut D)
-> DbResult<$return_type> $body
};
}
@ -206,7 +207,6 @@ pub trait DbCollection {
const COLLECTION: &str;
}
#[async_trait]
pub trait GetCollection {
async fn get_collection<C: DbCollection + Send + Sync>(&mut self) -> Collection<C>;
}
@ -222,8 +222,7 @@ impl CallDB for DB {
}
}
#[async_trait]
impl<T: CallDB + Send> GetCollection for T {
impl<T: CallDB> GetCollection for T {
async fn get_collection<C: DbCollection + Send + Sync>(&mut self) -> Collection<C> {
self.get_database()
.await
@ -253,17 +252,6 @@ pub trait CallDB {
Ok(users.find(doc! {}).await?.try_collect().await?)
}
async fn get_users_by_ids(&self, ids: Vec<i64>) -> DbResult<Vec<User>> {
let db = self.get_database_immut().await;
let users = db.collection::<User>("users");
Ok(users
.find(doc! {"id": {"$in": ids}})
.await?
.try_collect()
.await?)
}
async fn get_random_users(&self, n: u32) -> DbResult<Vec<User>> {
let db = self.get_database_immut().await;
let users = db.collection::<User>("users");

View File

@ -1,4 +1,3 @@
use async_trait::async_trait;
use mongodb::Database;
use super::CallDB;
@ -15,7 +14,6 @@ pub enum RawCallError {
}
pub type RawCallResult<T> = Result<T, RawCallError>;
#[async_trait]
pub trait RawCall {
async fn get_database(&mut self) -> Database;
async fn find_one(&mut self, collection: &str, query: Value) -> RawCallResult<Option<Value>> {
@ -33,8 +31,7 @@ pub trait RawCall {
}
}
#[async_trait]
impl<T: CallDB + Send> RawCall for T {
impl<T: CallDB> RawCall for T {
async fn get_database(&mut self) -> Database {
CallDB::get_database(self).await
}

View File

@ -10,7 +10,7 @@ async fn setup_db() -> DB {
dotenvy::dotenv().unwrap();
let db_url = std::env::var("DATABASE_URL").unwrap();
DB::new(db_url, "tests".to_string()).await.unwrap()
DB::new(db_url, "gongbot".to_string()).await.unwrap()
}
#[tokio::test]
@ -72,8 +72,6 @@ async fn test_add_media() {
async fn test_drop_media() {
let mut db = setup_db().await;
let _result = db.drop_media("test_drop_media_literal").await.unwrap();
let _result = db
.add_media("test_drop_media_literal", "photo", "file_id_1", None)
.await
@ -181,8 +179,6 @@ async fn test_drop_media_except() {
async fn test_get_random_users() {
let mut db = setup_db().await;
let _ = db.get_or_init_user(1, "Nick").await;
let users = db.get_random_users(1).await.unwrap();
assert_eq!(users.len(), 1);
}

View File

@ -1,3 +1,5 @@
use std::str::FromStr;
use itertools::Itertools;
use log::{info, warn};
use std::time::Duration;
@ -17,7 +19,7 @@ use crate::db::bots::BotInstance;
use crate::db::message_forward::MessageForward;
use crate::db::{CallDB, DB};
use crate::mongodb_storage::MongodbStorage;
use crate::{notify_admin, BotDialogue, BotError, BotResult, CallbackStore, State};
use crate::{BotDialogue, BotError, BotResult, CallbackStore, State};
pub fn admin_handler() -> BotHandler {
dptree::entry()
@ -103,28 +105,18 @@ async fn newscript_handler(bot: Bot, mut db: DB, msg: Message, name: String) ->
let mut stream = bot.download_file_stream(&file.path);
let mut buf: Vec<u8> = Vec::new();
while let Some(bytes) = stream.next().await {
let mut bytes = match bytes {
Ok(bytes) => bytes.to_vec(),
Err(err) => {
notify_admin(&format!(
"Failed to download file: {}, err: {err}",
file.path
))
.await;
return Ok(());
}
};
let mut bytes = bytes.unwrap().to_vec();
buf.append(&mut bytes);
}
match String::from_utf8(buf) {
let script = match String::from_utf8(buf) {
Ok(s) => s,
Err(err) => {
warn!("Failed to parse buf to string, err: {err}");
bot.send_message(msg.chat.id, format!("Failed to Convert file to script: file is not UTF-8, err: {err}")).await?;
return Ok(());
}
}
};
script
}
_ => todo!(),
}
@ -137,7 +129,7 @@ async fn newscript_handler(bot: Bot, mut db: DB, msg: Message, name: String) ->
None => {
bot.send_message(
msg.chat.id,
"Failed to set script, possibly bots name is incorrent".to_string(),
format!("Failed to set script, possibly bots name is incorrent"),
)
.await?;
return Ok(());

View File

@ -3,26 +3,24 @@ pub mod bot_handler;
pub mod bot_manager;
pub mod botscript;
pub mod commands;
pub mod config;
pub mod db;
pub mod handlers;
pub mod message_answerer;
pub mod mongodb_storage;
pub mod runtimes;
pub mod utils;
use bot_manager::BotManager;
use botscript::application::attach_user_application;
use botscript::{Runner, ScriptError, ScriptResult};
use config::result::ConfigError;
use config::{Provider, RunnerConfig};
use botscript::{BotMessage, Runner, RunnerConfig, ScriptError, ScriptResult};
use db::application::Application;
use db::bots::BotInstance;
use db::callback_info::CallbackInfo;
use db::message_forward::MessageForward;
use handlers::admin::admin_handler;
use log::{error, info};
use message_answerer::MessageAnswererError;
use std::sync::{Arc, Mutex};
use log::{error, info, warn};
use message_answerer::MessageAnswerer;
use std::sync::{Arc, Mutex, RwLock};
use utils::create_callback_button;
use crate::db::{CallDB, DB};
use crate::mongodb_storage::MongodbStorage;
@ -31,8 +29,13 @@ use db::DbError;
use envconfig::Envconfig;
use serde::{Deserialize, Serialize};
use teloxide::dispatching::dialogue::serializer::Json;
use teloxide::dispatching::dialogue::Serializer;
use teloxide::prelude::*;
use teloxide::dispatching::dialogue::{GetChatId, Serializer};
use teloxide::types::{InlineKeyboardButton, InlineKeyboardMarkup};
use teloxide::{
payloads::SendMessageSetters,
prelude::*,
utils::{command::BotCommands, render::RenderMessageTextHelper},
};
type BotDialogue = Dialogue<State, MongodbStorage<Json>>;
@ -50,6 +53,15 @@ pub struct Config {
pub bot_name: String,
}
#[derive(BotCommands, Clone)]
#[command(rename_rule = "lowercase")]
enum UserCommands {
/// The first message of user
Start(String),
/// Shows this message.
Help,
}
trait LogMsg {
fn log(self) -> Self;
}
@ -95,11 +107,11 @@ pub struct BotController {
pub runtime: Arc<Mutex<BotRuntime>>,
}
pub struct BotRuntime<P: Provider> {
pub rc: RunnerConfig<P>,
pub struct BotRuntime {
pub rc: RunnerConfig,
pub runner: Runner,
}
unsafe impl<P: Provider> Send for BotRuntime<P> {}
unsafe impl Send for BotRuntime {}
impl Drop for BotController {
fn drop(&mut self) {
@ -130,7 +142,7 @@ impl BotController {
let bot = Bot::new(token);
let mut runner = Runner::init_with_db(&mut db)?;
// runner.call_attacher(|c, o| attach_user_application(c, o, db.clone(), bot.clone()))??;
runner.call_attacher(|c, o| attach_user_application(c, o, &db, &bot))??;
let rc = runner.init_config(script)?;
let runtime = Arc::new(Mutex::new(BotRuntime { rc, runner }));
@ -150,8 +162,6 @@ pub enum BotError {
ScriptError(#[from] ScriptError),
IoError(#[from] std::io::Error),
RwLockError(String),
MAError(#[from] MessageAnswererError),
ConfigError(#[from] ConfigError),
}
pub type BotResult<T> = Result<T, BotError>;
@ -171,8 +181,6 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut db = DB::init(&config.db_url, config.bot_name.to_owned()).await?;
BotInstance::restart_all(&mut db, false).await?;
// if we can't get info for main bot, we should stop anyway
#[allow(clippy::unwrap_used)]
let bm = BotManager::with(
async || {
let config = config.clone();
@ -189,10 +197,167 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
BotInstance::restart_all(&mut db, false).await.unwrap();
std::iter::once(bi).chain(instances)
},
async |_| vec![admin_handler()].into_iter(),
async |bi| vec![admin_handler()].into_iter(),
);
bm.dispatch(&mut db).await?;
bm.dispatch(&mut db).await;
}
async fn botscript_command_handler(
bot: Bot,
mut db: DB,
bm: BotMessage,
msg: Message,
) -> BotResult<()> {
info!("Eval BM: {:?}", bm);
let buttons = bm
.resolve_buttons(&mut db)
.await?
.map(|buttons| InlineKeyboardMarkup {
inline_keyboard: buttons
.iter()
.map(|r| {
r.iter()
.map(|b| match b {
botscript::ButtonLayout::Callback {
name,
literal: _,
callback,
} => InlineKeyboardButton::callback(name, callback),
})
.collect()
})
.collect(),
});
let literal = bm.literal().map_or("", |s| s.as_str());
let ma = MessageAnswerer::new(&bot, &mut db, msg.chat.id.0);
ma.answer(literal, None, buttons).await?;
Ok(())
}
async fn callback_handler(bot: Bot, mut db: DB, q: CallbackQuery) -> BotResult<()> {
bot.answer_callback_query(&q.id).await?;
let data = match q.data {
Some(ref data) => data,
None => {
// not really our case to handle
return Ok(());
}
};
let callback = match CallbackStore::get_callback(&mut db, data).await? {
Some(callback) => callback,
None => {
warn!("Not found callback for data: {data}");
// doing this silently beacuse end user shouldn't know about backend internal data
return Ok(());
}
};
match callback {
Callback::MoreInfo => {
let keyboard = Some(single_button_markup!(
create_callback_button("go_home", Callback::GoHome, &mut db).await?
));
let chat_id = q.chat_id().map(|i| i.0).unwrap_or(q.from.id.0 as i64);
let message_id = q.message.map_or_else(
|| {
Err(BotError::MsgTooOld(
"Failed to get message id, probably message too old".to_string(),
))
},
|m| Ok(m.id().0),
)?;
MessageAnswerer::new(&bot, &mut db, chat_id)
.replace_message(message_id, "more_info_msg", keyboard)
.await?
}
Callback::ProjectPage { id } => {
let nextproject = match db
.get_literal_value(&format!("project_{}_msg", id + 1))
.await?
.unwrap_or("emptyproject".into())
.as_str()
{
"end" | "empty" | "none" => None,
_ => Some(
create_callback_button(
"next_project",
Callback::ProjectPage { id: id + 1 },
&mut db,
)
.await?,
),
};
let prevproject = match id.wrapping_sub(1) {
0 => None,
_ => Some(
create_callback_button(
"prev_project",
Callback::ProjectPage {
id: id.wrapping_sub(1),
},
&mut db,
)
.await?,
),
};
let keyboard = buttons_markup!(
[prevproject, nextproject].into_iter().flatten(),
[create_callback_button("go_home", Callback::GoHome, &mut db).await?]
);
let chat_id = q.chat_id().map(|i| i.0).unwrap_or(q.from.id.0 as i64);
let message_id = q.message.map_or_else(
|| {
Err(BotError::MsgTooOld(
"Failed to get message id, probably message too old".to_string(),
))
},
|m| Ok(m.id().0),
)?;
MessageAnswerer::new(&bot, &mut db, chat_id)
.replace_message(message_id, &format!("project_{}_msg", id), Some(keyboard))
.await?
}
Callback::GoHome => {
let keyboard = make_start_buttons(&mut db).await?;
let chat_id = q.chat_id().map(|i| i.0).unwrap_or(q.from.id.0 as i64);
let message_id = q.message.map_or_else(
|| {
Err(BotError::MsgTooOld(
"Failed to get message id, probably message too old".to_string(),
))
},
|m| Ok(m.id().0),
)?;
MessageAnswerer::new(&bot, &mut db, chat_id)
.replace_message(message_id, "start", Some(keyboard))
.await?
}
Callback::LeaveApplication => {
let application = Application::new(q.from.clone()).store(&mut db).await?;
let msg = send_application_to_chat(&bot, &mut db, &application).await?;
let (chat_id, msg_id) = MessageAnswerer::new(&bot, &mut db, q.from.id.0 as i64)
.answer("left_application_msg", None, None)
.await?;
MessageForward::new(msg.chat.id.0, msg.id.0, chat_id, msg_id, false)
.store(&mut db)
.await?;
}
Callback::AskQuestion => {
MessageAnswerer::new(&bot, &mut db, q.from.id.0 as i64)
.answer("ask_question_msg", None, None)
.await?;
}
};
Ok(())
}
@ -220,9 +385,9 @@ async fn send_application_to_chat(
app.from
))
.await;
return Err(BotError::AdminMisconfiguration(
"admin forget to set support_chat_id".to_string(),
));
return Err(BotError::AdminMisconfiguration(format!(
"admin forget to set support_chat_id"
)));
}
};
let msg = match db.get_literal_value("application_format").await? {
@ -238,9 +403,9 @@ async fn send_application_to_chat(
),
None => {
notify_admin("format for support_chat_id is not set").await;
return Err(BotError::AdminMisconfiguration(
"admin forget to set application_format".to_string(),
));
return Err(BotError::AdminMisconfiguration(format!(
"admin forget to set application_format"
)));
}
};
@ -266,6 +431,76 @@ async fn notify_admin(text: &str) {
}
}
async fn user_command_handler(
mut db: DB,
bot: Bot,
msg: Message,
cmd: UserCommands,
) -> BotResult<()> {
let tguser = match msg.from.clone() {
Some(user) => user,
None => return Ok(()), // do nothing, cause its not usecase of function
};
let user = db
.get_or_init_user(tguser.id.0 as i64, &tguser.first_name)
.await?;
let user = update_user_tg(user, &tguser);
user.update_user(&mut db).await?;
info!(
"MSG: {}",
msg.html_text().unwrap_or("|EMPTY_MESSAGE|".into())
);
match cmd {
UserCommands::Start(meta) => {
if !meta.is_empty() {
user.insert_meta(&mut db, &meta).await?;
}
let variant = match meta.as_str() {
"" => None,
variant => Some(variant),
};
let mut db2 = db.clone();
MessageAnswerer::new(&bot, &mut db, msg.chat.id.0)
.answer("start", variant, Some(make_start_buttons(&mut db2).await?))
.await?;
Ok(())
}
UserCommands::Help => {
bot.send_message(msg.chat.id, UserCommands::descriptions().to_string())
.await?;
Ok(())
}
}
}
async fn make_start_buttons(db: &mut DB) -> BotResult<InlineKeyboardMarkup> {
let mut buttons: Vec<Vec<InlineKeyboardButton>> = Vec::new();
buttons.push(vec![
create_callback_button("show_projects", Callback::ProjectPage { id: 1 }, db).await?,
]);
buttons.push(vec![
create_callback_button("more_info", Callback::MoreInfo, db).await?,
]);
buttons.push(vec![
create_callback_button("leave_application", Callback::LeaveApplication, db).await?,
]);
buttons.push(vec![
create_callback_button("ask_question", Callback::AskQuestion, db).await?,
]);
Ok(InlineKeyboardMarkup::new(buttons))
}
async fn echo(bot: Bot, msg: Message) -> BotResult<()> {
if let Some(photo) = msg.photo() {
info!("File ID: {}", photo[0].file.id);
}
bot.send_message(msg.chat.id, msg.html_text().unwrap_or("UNWRAP".into()))
.parse_mode(teloxide::types::ParseMode::Html)
.await?;
Ok(())
}
fn update_user_tg(user: db::User, tguser: &teloxide::types::User) -> db::User {
db::User {
first_name: tguser.first_name.clone(),

View File

@ -8,10 +8,10 @@ use teloxide::{
Bot,
};
use crate::db::{DbError, DbResult, Media};
use crate::db::Media;
use crate::{
db::{CallDB, DB},
notify_admin,
notify_admin, BotResult,
};
macro_rules! send_media {
@ -40,16 +40,6 @@ pub struct MessageAnswerer<'a> {
db: &'a mut DB,
}
#[derive(thiserror::Error, Debug)]
pub enum MessageAnswererError {
#[error("Failed request to DB: {0:?}")]
DbError(#[from] DbError),
#[error("Failed teloxide request: {0:?}")]
RequestError(#[from] teloxide::RequestError),
}
pub type MAResult<T> = Result<T, MessageAnswererError>;
impl<'a> MessageAnswerer<'a> {
pub fn new(bot: &'a Bot, db: &'a mut DB, chat_id: i64) -> Self {
Self { bot, chat_id, db }
@ -60,7 +50,7 @@ impl<'a> MessageAnswerer<'a> {
literal: &str,
variant: Option<&str>,
is_replace: bool,
) -> DbResult<String> {
) -> BotResult<String> {
let variant_text = match variant {
Some(variant) => {
let value = self
@ -91,7 +81,7 @@ impl<'a> MessageAnswerer<'a> {
literal: &str,
variant: Option<&str>,
keyboard: Option<InlineKeyboardMarkup>,
) -> MAResult<(i64, i32)> {
) -> BotResult<(i64, i32)> {
let text = self.get_text(literal, variant, false).await?;
self.answer_inner(text, literal, variant, keyboard).await
}
@ -100,10 +90,8 @@ impl<'a> MessageAnswerer<'a> {
self,
text: String,
keyboard: Option<InlineKeyboardMarkup>,
) -> MAResult<(i64, i32)> {
self.send_message(text, keyboard)
.await
.map_err(MessageAnswererError::from)
) -> BotResult<(i64, i32)> {
self.send_message(text, keyboard).await
}
async fn answer_inner(
@ -112,7 +100,7 @@ impl<'a> MessageAnswerer<'a> {
literal: &str,
variant: Option<&str>,
keyboard: Option<InlineKeyboardMarkup>,
) -> MAResult<(i64, i32)> {
) -> BotResult<(i64, i32)> {
let media = self.db.get_media(literal).await?;
let (chat_id, msg_id) = match media.len() {
// just a text
@ -131,7 +119,7 @@ impl<'a> MessageAnswerer<'a> {
message_id: i32,
literal: &str,
keyboard: Option<InlineKeyboardMarkup>,
) -> MAResult<()> {
) -> BotResult<()> {
let variant = self
.db
.get_message(self.chat_id, message_id)
@ -139,7 +127,7 @@ impl<'a> MessageAnswerer<'a> {
.and_then(|m| m.variant);
let text = self.get_text(literal, variant.as_deref(), true).await?;
let media = self.db.get_media(literal).await?;
let (_, msg_id) = match media.len() {
let (chat_id, msg_id) = match media.len() {
// just a text
0 => {
let msg =
@ -215,7 +203,7 @@ impl<'a> MessageAnswerer<'a> {
message_id: i32,
literal: &str,
variant: Option<&str>,
) -> DbResult<()> {
) -> BotResult<()> {
match variant {
Some(variant) => {
self.db
@ -236,7 +224,7 @@ impl<'a> MessageAnswerer<'a> {
&self,
text: String,
keyboard: Option<InlineKeyboardMarkup>,
) -> Result<(i64, i32), teloxide::RequestError> {
) -> BotResult<(i64, i32)> {
let msg = self.bot.send_message(ChatId(self.chat_id), text);
let msg = match keyboard {
Some(kbd) => msg.reply_markup(kbd),
@ -254,7 +242,7 @@ impl<'a> MessageAnswerer<'a> {
media: &Media,
text: String,
keyboard: Option<InlineKeyboardMarkup>,
) -> Result<(i64, i32), teloxide::RequestError> {
) -> BotResult<(i64, i32)> {
match media.media_type.as_str() {
"photo" => {
send_media!(
@ -282,11 +270,7 @@ impl<'a> MessageAnswerer<'a> {
}
}
async fn send_media_group(
&self,
media: Vec<Media>,
text: String,
) -> Result<(i64, i32), teloxide::RequestError> {
async fn send_media_group(&self, media: Vec<Media>, text: String) -> BotResult<(i64, i32)> {
let media: Vec<InputMedia> = media
.into_iter()
.enumerate()

View File

@ -66,26 +66,9 @@ where
))
}
pub async fn callback_button<C, D>(
name: &str,
callback_name: String,
callback_data: C,
db: &mut D,
) -> BotResult<InlineKeyboardButton>
where
C: Serialize + for<'a> Deserialize<'a> + Send + Sync,
D: CallDB + Send + Sync,
{
let ci = CallbackInfo::new_with_literal(callback_data, callback_name)
.store(db)
.await?;
Ok(InlineKeyboardButton::callback(name, ci.get_id()))
}
#[cfg(test)]
mod tests {
use super::*;
use teloxide::types::InlineKeyboardButton;
use teloxide::types::InlineKeyboardMarkup;