git.iko.soy/personality-traits
on master help sign in
34 files changed+4835−4
since splitsnapshots
unifiedsplit
.env.example+5 .gitignore+7 Cargo.lock+3122 Cargo.toml+20 README.md+74 ac.md+11 migrations/0001_init.sql+47 questionnaire/abfi-1/centralkeys.yaml+365 questionnaire/abfi-1/data/1.2.png questionnaire/abfi-1/data/2.1.png questionnaire/abfi-1/data/2.10.png questionnaire/abfi-1/data/2.11.png questionnaire/abfi-1/data/2.12.png questionnaire/abfi-1/data/2.13.png questionnaire/abfi-1/data/2.14.png questionnaire/abfi-1/data/2.2.png questionnaire/abfi-1/data/2.3.png questionnaire/abfi-1/data/2.4.png questionnaire/abfi-1/data/2.5.png questionnaire/abfi-1/data/2.6.png questionnaire/abfi-1/data/2.7.png questionnaire/abfi-1/data/2.8.png questionnaire/abfi-1/data/2.9.png questionnaire/abfi-1/data/9.png questionnaire/abfi-1/data/Контрольный список негативных мыслей.pdf questionnaire/abfi-1/events.yaml+42−4 src/config.rs+25 src/db.rs+239 src/events.rs+161 src/handlers.rs+390 src/main.rs+52 src/media.rs+157 src/scheduler.rs+85 tests/parse_events.rs+33
.env.examplenew file+5
@@ -0,0 +1,5 @@
1+TELOXIDE_TOKEN=put_your_bot_token_here
2+DATABASE_URL=sqlite://bot.db?mode=rwc
3+EVENTS_FILE=questionnaire/abfi-1/events.yaml
4+DATA_DIR=questionnaire/abfi-1/data
5+RUST_LOG=info,personality_traits_bot=debug,teloxide=info
.gitignore+7
@@ -13,6 +13,13 @@ target
1313# Contains mutation testing data
1414**/mutants.out*/
1515
16+# Env and local DB
17+.env
18+*.db
19+*.db-journal
20+*.db-wal
21+*.db-shm
22+
1623# RustRover
1724# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
1825# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
Cargo.locknew file+3122
@@ -0,0 +1,3122 @@
1+# This file is automatically @generated by Cargo.
2+# It is not intended for manual editing.
3+version = 4
4+
5+[[package]]
6+name = "aho-corasick"
7+version = "1.1.4"
8+source = "registry+https://github.com/rust-lang/crates.io-index"
9+checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
10+dependencies = [
11+ "memchr",
12+]
13+
14+[[package]]
15+name = "allocator-api2"
16+version = "0.2.21"
17+source = "registry+https://github.com/rust-lang/crates.io-index"
18+checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
19+
20+[[package]]
21+name = "android_system_properties"
22+version = "0.1.5"
23+source = "registry+https://github.com/rust-lang/crates.io-index"
24+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
25+dependencies = [
26+ "libc",
27+]
28+
29+[[package]]
30+name = "anyhow"
31+version = "1.0.102"
32+source = "registry+https://github.com/rust-lang/crates.io-index"
33+checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
34+
35+[[package]]
36+name = "aquamarine"
37+version = "0.5.0"
38+source = "registry+https://github.com/rust-lang/crates.io-index"
39+checksum = "21cc1548309245035eb18aa7f0967da6bc65587005170c56e6ef2788a4cf3f4e"
40+dependencies = [
41+ "include_dir",
42+ "itertools",
43+ "proc-macro-error",
44+ "proc-macro2",
45+ "quote",
46+ "syn 2.0.117",
47+]
48+
49+[[package]]
50+name = "atoi"
51+version = "2.0.0"
52+source = "registry+https://github.com/rust-lang/crates.io-index"
53+checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528"
54+dependencies = [
55+ "num-traits",
56+]
57+
58+[[package]]
59+name = "autocfg"
60+version = "1.5.0"
61+source = "registry+https://github.com/rust-lang/crates.io-index"
62+checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
63+
64+[[package]]
65+name = "base64"
66+version = "0.21.7"
67+source = "registry+https://github.com/rust-lang/crates.io-index"
68+checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
69+
70+[[package]]
71+name = "base64"
72+version = "0.22.1"
73+source = "registry+https://github.com/rust-lang/crates.io-index"
74+checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
75+
76+[[package]]
77+name = "base64ct"
78+version = "1.8.3"
79+source = "registry+https://github.com/rust-lang/crates.io-index"
80+checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06"
81+
82+[[package]]
83+name = "bitflags"
84+version = "1.3.2"
85+source = "registry+https://github.com/rust-lang/crates.io-index"
86+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
87+
88+[[package]]
89+name = "bitflags"
90+version = "2.11.1"
91+source = "registry+https://github.com/rust-lang/crates.io-index"
92+checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3"
93+dependencies = [
94+ "serde_core",
95+]
96+
97+[[package]]
98+name = "block-buffer"
99+version = "0.10.4"
100+source = "registry+https://github.com/rust-lang/crates.io-index"
101+checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
102+dependencies = [
103+ "generic-array",
104+]
105+
106+[[package]]
107+name = "bumpalo"
108+version = "3.20.2"
109+source = "registry+https://github.com/rust-lang/crates.io-index"
110+checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb"
111+
112+[[package]]
113+name = "byteorder"
114+version = "1.5.0"
115+source = "registry+https://github.com/rust-lang/crates.io-index"
116+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
117+
118+[[package]]
119+name = "bytes"
120+version = "1.11.1"
121+source = "registry+https://github.com/rust-lang/crates.io-index"
122+checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
123+
124+[[package]]
125+name = "cc"
126+version = "1.2.62"
127+source = "registry+https://github.com/rust-lang/crates.io-index"
128+checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98"
129+dependencies = [
130+ "find-msvc-tools",
131+ "shlex",
132+]
133+
134+[[package]]
135+name = "cfg-if"
136+version = "1.0.4"
137+source = "registry+https://github.com/rust-lang/crates.io-index"
138+checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
139+
140+[[package]]
141+name = "chrono"
142+version = "0.4.44"
143+source = "registry+https://github.com/rust-lang/crates.io-index"
144+checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0"
145+dependencies = [
146+ "iana-time-zone",
147+ "js-sys",
148+ "num-traits",
149+ "serde",
150+ "wasm-bindgen",
151+ "windows-link",
152+]
153+
154+[[package]]
155+name = "concurrent-queue"
156+version = "2.5.0"
157+source = "registry+https://github.com/rust-lang/crates.io-index"
158+checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973"
159+dependencies = [
160+ "crossbeam-utils",
161+]
162+
163+[[package]]
164+name = "const-oid"
165+version = "0.9.6"
166+source = "registry+https://github.com/rust-lang/crates.io-index"
167+checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
168+
169+[[package]]
170+name = "convert_case"
171+version = "0.4.0"
172+source = "registry+https://github.com/rust-lang/crates.io-index"
173+checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
174+
175+[[package]]
176+name = "core-foundation"
177+version = "0.9.4"
178+source = "registry+https://github.com/rust-lang/crates.io-index"
179+checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
180+dependencies = [
181+ "core-foundation-sys",
182+ "libc",
183+]
184+
185+[[package]]
186+name = "core-foundation"
187+version = "0.10.1"
188+source = "registry+https://github.com/rust-lang/crates.io-index"
189+checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6"
190+dependencies = [
191+ "core-foundation-sys",
192+ "libc",
193+]
194+
195+[[package]]
196+name = "core-foundation-sys"
197+version = "0.8.7"
198+source = "registry+https://github.com/rust-lang/crates.io-index"
199+checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
200+
201+[[package]]
202+name = "cpufeatures"
203+version = "0.2.17"
204+source = "registry+https://github.com/rust-lang/crates.io-index"
205+checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
206+dependencies = [
207+ "libc",
208+]
209+
210+[[package]]
211+name = "crc"
212+version = "3.4.0"
213+source = "registry+https://github.com/rust-lang/crates.io-index"
214+checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d"
215+dependencies = [
216+ "crc-catalog",
217+]
218+
219+[[package]]
220+name = "crc-catalog"
221+version = "2.5.0"
222+source = "registry+https://github.com/rust-lang/crates.io-index"
223+checksum = "217698eaf96b4a3f0bc4f3662aaa55bdf913cd54d7204591faa790070c6d0853"
224+
225+[[package]]
226+name = "crossbeam-queue"
227+version = "0.3.12"
228+source = "registry+https://github.com/rust-lang/crates.io-index"
229+checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115"
230+dependencies = [
231+ "crossbeam-utils",
232+]
233+
234+[[package]]
235+name = "crossbeam-utils"
236+version = "0.8.21"
237+source = "registry+https://github.com/rust-lang/crates.io-index"
238+checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
239+
240+[[package]]
241+name = "crypto-common"
242+version = "0.1.7"
243+source = "registry+https://github.com/rust-lang/crates.io-index"
244+checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
245+dependencies = [
246+ "generic-array",
247+ "typenum",
248+]
249+
250+[[package]]
251+name = "darling"
252+version = "0.13.4"
253+source = "registry+https://github.com/rust-lang/crates.io-index"
254+checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c"
255+dependencies = [
256+ "darling_core",
257+ "darling_macro",
258+]
259+
260+[[package]]
261+name = "darling_core"
262+version = "0.13.4"
263+source = "registry+https://github.com/rust-lang/crates.io-index"
264+checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610"
265+dependencies = [
266+ "fnv",
267+ "ident_case",
268+ "proc-macro2",
269+ "quote",
270+ "strsim",
271+ "syn 1.0.109",
272+]
273+
274+[[package]]
275+name = "darling_macro"
276+version = "0.13.4"
277+source = "registry+https://github.com/rust-lang/crates.io-index"
278+checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835"
279+dependencies = [
280+ "darling_core",
281+ "quote",
282+ "syn 1.0.109",
283+]
284+
285+[[package]]
286+name = "der"
287+version = "0.7.10"
288+source = "registry+https://github.com/rust-lang/crates.io-index"
289+checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb"
290+dependencies = [
291+ "const-oid",
292+ "pem-rfc7468",
293+ "zeroize",
294+]
295+
296+[[package]]
297+name = "derive_more"
298+version = "0.99.20"
299+source = "registry+https://github.com/rust-lang/crates.io-index"
300+checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f"
301+dependencies = [
302+ "convert_case",
303+ "proc-macro2",
304+ "quote",
305+ "rustc_version",
306+ "syn 2.0.117",
307+]
308+
309+[[package]]
310+name = "digest"
311+version = "0.10.7"
312+source = "registry+https://github.com/rust-lang/crates.io-index"
313+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
314+dependencies = [
315+ "block-buffer",
316+ "const-oid",
317+ "crypto-common",
318+ "subtle",
319+]
320+
321+[[package]]
322+name = "displaydoc"
323+version = "0.2.5"
324+source = "registry+https://github.com/rust-lang/crates.io-index"
325+checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
326+dependencies = [
327+ "proc-macro2",
328+ "quote",
329+ "syn 2.0.117",
330+]
331+
332+[[package]]
333+name = "dotenvy"
334+version = "0.15.7"
335+source = "registry+https://github.com/rust-lang/crates.io-index"
336+checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
337+
338+[[package]]
339+name = "dptree"
340+version = "0.3.0"
341+source = "registry+https://github.com/rust-lang/crates.io-index"
342+checksum = "d81175dab5ec79c30e0576df2ed2c244e1721720c302000bb321b107e82e265c"
343+dependencies = [
344+ "futures",
345+]
346+
347+[[package]]
348+name = "either"
349+version = "1.15.0"
350+source = "registry+https://github.com/rust-lang/crates.io-index"
351+checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
352+dependencies = [
353+ "serde",
354+]
355+
356+[[package]]
357+name = "encoding_rs"
358+version = "0.8.35"
359+source = "registry+https://github.com/rust-lang/crates.io-index"
360+checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
361+dependencies = [
362+ "cfg-if",
363+]
364+
365+[[package]]
366+name = "equivalent"
367+version = "1.0.2"
368+source = "registry+https://github.com/rust-lang/crates.io-index"
369+checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
370+
371+[[package]]
372+name = "erasable"
373+version = "1.3.0"
374+source = "registry+https://github.com/rust-lang/crates.io-index"
375+checksum = "437cfb75878119ed8265685c41a115724eae43fb7cc5a0bf0e4ecc3b803af1c4"
376+dependencies = [
377+ "autocfg",
378+ "scopeguard",
379+]
380+
381+[[package]]
382+name = "errno"
383+version = "0.3.14"
384+source = "registry+https://github.com/rust-lang/crates.io-index"
385+checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
386+dependencies = [
387+ "libc",
388+ "windows-sys 0.61.2",
389+]
390+
391+[[package]]
392+name = "etcetera"
393+version = "0.8.0"
394+source = "registry+https://github.com/rust-lang/crates.io-index"
395+checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943"
396+dependencies = [
397+ "cfg-if",
398+ "home",
399+ "windows-sys 0.48.0",
400+]
401+
402+[[package]]
403+name = "event-listener"
404+version = "5.4.1"
405+source = "registry+https://github.com/rust-lang/crates.io-index"
406+checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab"
407+dependencies = [
408+ "concurrent-queue",
409+ "parking",
410+ "pin-project-lite",
411+]
412+
413+[[package]]
414+name = "fastrand"
415+version = "2.4.1"
416+source = "registry+https://github.com/rust-lang/crates.io-index"
417+checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6"
418+
419+[[package]]
420+name = "find-msvc-tools"
421+version = "0.1.9"
422+source = "registry+https://github.com/rust-lang/crates.io-index"
423+checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
424+
425+[[package]]
426+name = "flume"
427+version = "0.11.1"
428+source = "registry+https://github.com/rust-lang/crates.io-index"
429+checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095"
430+dependencies = [
431+ "futures-core",
432+ "futures-sink",
433+ "spin",
434+]
435+
436+[[package]]
437+name = "fnv"
438+version = "1.0.7"
439+source = "registry+https://github.com/rust-lang/crates.io-index"
440+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
441+
442+[[package]]
443+name = "foldhash"
444+version = "0.1.5"
445+source = "registry+https://github.com/rust-lang/crates.io-index"
446+checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
447+
448+[[package]]
449+name = "foreign-types"
450+version = "0.3.2"
451+source = "registry+https://github.com/rust-lang/crates.io-index"
452+checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
453+dependencies = [
454+ "foreign-types-shared",
455+]
456+
457+[[package]]
458+name = "foreign-types-shared"
459+version = "0.1.1"
460+source = "registry+https://github.com/rust-lang/crates.io-index"
461+checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
462+
463+[[package]]
464+name = "form_urlencoded"
465+version = "1.2.2"
466+source = "registry+https://github.com/rust-lang/crates.io-index"
467+checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf"
468+dependencies = [
469+ "percent-encoding",
470+]
471+
472+[[package]]
473+name = "futures"
474+version = "0.3.32"
475+source = "registry+https://github.com/rust-lang/crates.io-index"
476+checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d"
477+dependencies = [
478+ "futures-channel",
479+ "futures-core",
480+ "futures-executor",
481+ "futures-io",
482+ "futures-sink",
483+ "futures-task",
484+ "futures-util",
485+]
486+
487+[[package]]
488+name = "futures-channel"
489+version = "0.3.32"
490+source = "registry+https://github.com/rust-lang/crates.io-index"
491+checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d"
492+dependencies = [
493+ "futures-core",
494+ "futures-sink",
495+]
496+
497+[[package]]
498+name = "futures-core"
499+version = "0.3.32"
500+source = "registry+https://github.com/rust-lang/crates.io-index"
501+checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
502+
503+[[package]]
504+name = "futures-executor"
505+version = "0.3.32"
506+source = "registry+https://github.com/rust-lang/crates.io-index"
507+checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d"
508+dependencies = [
509+ "futures-core",
510+ "futures-task",
511+ "futures-util",
512+]
513+
514+[[package]]
515+name = "futures-intrusive"
516+version = "0.5.0"
517+source = "registry+https://github.com/rust-lang/crates.io-index"
518+checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f"
519+dependencies = [
520+ "futures-core",
521+ "lock_api",
522+ "parking_lot",
523+]
524+
525+[[package]]
526+name = "futures-io"
527+version = "0.3.32"
528+source = "registry+https://github.com/rust-lang/crates.io-index"
529+checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718"
530+
531+[[package]]
532+name = "futures-macro"
533+version = "0.3.32"
534+source = "registry+https://github.com/rust-lang/crates.io-index"
535+checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b"
536+dependencies = [
537+ "proc-macro2",
538+ "quote",
539+ "syn 2.0.117",
540+]
541+
542+[[package]]
543+name = "futures-sink"
544+version = "0.3.32"
545+source = "registry+https://github.com/rust-lang/crates.io-index"
546+checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893"
547+
548+[[package]]
549+name = "futures-task"
550+version = "0.3.32"
551+source = "registry+https://github.com/rust-lang/crates.io-index"
552+checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393"
553+
554+[[package]]
555+name = "futures-util"
556+version = "0.3.32"
557+source = "registry+https://github.com/rust-lang/crates.io-index"
558+checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
559+dependencies = [
560+ "futures-channel",
561+ "futures-core",
562+ "futures-io",
563+ "futures-macro",
564+ "futures-sink",
565+ "futures-task",
566+ "memchr",
567+ "pin-project-lite",
568+ "slab",
569+]
570+
571+[[package]]
572+name = "generic-array"
573+version = "0.14.7"
574+source = "registry+https://github.com/rust-lang/crates.io-index"
575+checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
576+dependencies = [
577+ "typenum",
578+ "version_check",
579+]
580+
581+[[package]]
582+name = "getrandom"
583+version = "0.2.17"
584+source = "registry+https://github.com/rust-lang/crates.io-index"
585+checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
586+dependencies = [
587+ "cfg-if",
588+ "libc",
589+ "wasi",
590+]
591+
592+[[package]]
593+name = "getrandom"
594+version = "0.4.2"
595+source = "registry+https://github.com/rust-lang/crates.io-index"
596+checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555"
597+dependencies = [
598+ "cfg-if",
599+ "libc",
600+ "r-efi",
601+ "wasip2",
602+ "wasip3",
603+]
604+
605+[[package]]
606+name = "h2"
607+version = "0.3.27"
608+source = "registry+https://github.com/rust-lang/crates.io-index"
609+checksum = "0beca50380b1fc32983fc1cb4587bfa4bb9e78fc259aad4a0032d2080309222d"
610+dependencies = [
611+ "bytes",
612+ "fnv",
613+ "futures-core",
614+ "futures-sink",
615+ "futures-util",
616+ "http",
617+ "indexmap",
618+ "slab",
619+ "tokio",
620+ "tokio-util",
621+ "tracing",
622+]
623+
624+[[package]]
625+name = "hashbrown"
626+version = "0.15.5"
627+source = "registry+https://github.com/rust-lang/crates.io-index"
628+checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
629+dependencies = [
630+ "allocator-api2",
631+ "equivalent",
632+ "foldhash",
633+]
634+
635+[[package]]
636+name = "hashbrown"
637+version = "0.17.1"
638+source = "registry+https://github.com/rust-lang/crates.io-index"
639+checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
640+
641+[[package]]
642+name = "hashlink"
643+version = "0.10.0"
644+source = "registry+https://github.com/rust-lang/crates.io-index"
645+checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1"
646+dependencies = [
647+ "hashbrown 0.15.5",
648+]
649+
650+[[package]]
651+name = "heck"
652+version = "0.4.1"
653+source = "registry+https://github.com/rust-lang/crates.io-index"
654+checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
655+
656+[[package]]
657+name = "heck"
658+version = "0.5.0"
659+source = "registry+https://github.com/rust-lang/crates.io-index"
660+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
661+
662+[[package]]
663+name = "hex"
664+version = "0.4.3"
665+source = "registry+https://github.com/rust-lang/crates.io-index"
666+checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
667+
668+[[package]]
669+name = "hkdf"
670+version = "0.12.4"
671+source = "registry+https://github.com/rust-lang/crates.io-index"
672+checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7"
673+dependencies = [
674+ "hmac",
675+]
676+
677+[[package]]
678+name = "hmac"
679+version = "0.12.1"
680+source = "registry+https://github.com/rust-lang/crates.io-index"
681+checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
682+dependencies = [
683+ "digest",
684+]
685+
686+[[package]]
687+name = "home"
688+version = "0.5.12"
689+source = "registry+https://github.com/rust-lang/crates.io-index"
690+checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d"
691+dependencies = [
692+ "windows-sys 0.61.2",
693+]
694+
695+[[package]]
696+name = "http"
697+version = "0.2.12"
698+source = "registry+https://github.com/rust-lang/crates.io-index"
699+checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1"
700+dependencies = [
701+ "bytes",
702+ "fnv",
703+ "itoa",
704+]
705+
706+[[package]]
707+name = "http-body"
708+version = "0.4.6"
709+source = "registry+https://github.com/rust-lang/crates.io-index"
710+checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2"
711+dependencies = [
712+ "bytes",
713+ "http",
714+ "pin-project-lite",
715+]
716+
717+[[package]]
718+name = "httparse"
719+version = "1.10.1"
720+source = "registry+https://github.com/rust-lang/crates.io-index"
721+checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
722+
723+[[package]]
724+name = "httpdate"
725+version = "1.0.3"
726+source = "registry+https://github.com/rust-lang/crates.io-index"
727+checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
728+
729+[[package]]
730+name = "hyper"
731+version = "0.14.32"
732+source = "registry+https://github.com/rust-lang/crates.io-index"
733+checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7"
734+dependencies = [
735+ "bytes",
736+ "futures-channel",
737+ "futures-core",
738+ "futures-util",
739+ "h2",
740+ "http",
741+ "http-body",
742+ "httparse",
743+ "httpdate",
744+ "itoa",
745+ "pin-project-lite",
746+ "socket2 0.5.10",
747+ "tokio",
748+ "tower-service",
749+ "tracing",
750+ "want",
751+]
752+
753+[[package]]
754+name = "hyper-tls"
755+version = "0.5.0"
756+source = "registry+https://github.com/rust-lang/crates.io-index"
757+checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
758+dependencies = [
759+ "bytes",
760+ "hyper",
761+ "native-tls",
762+ "tokio",
763+ "tokio-native-tls",
764+]
765+
766+[[package]]
767+name = "iana-time-zone"
768+version = "0.1.65"
769+source = "registry+https://github.com/rust-lang/crates.io-index"
770+checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470"
771+dependencies = [
772+ "android_system_properties",
773+ "core-foundation-sys",
774+ "iana-time-zone-haiku",
775+ "js-sys",
776+ "log",
777+ "wasm-bindgen",
778+ "windows-core",
779+]
780+
781+[[package]]
782+name = "iana-time-zone-haiku"
783+version = "0.1.2"
784+source = "registry+https://github.com/rust-lang/crates.io-index"
785+checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
786+dependencies = [
787+ "cc",
788+]
789+
790+[[package]]
791+name = "icu_collections"
792+version = "2.2.0"
793+source = "registry+https://github.com/rust-lang/crates.io-index"
794+checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c"
795+dependencies = [
796+ "displaydoc",
797+ "potential_utf",
798+ "utf8_iter",
799+ "yoke",
800+ "zerofrom",
801+ "zerovec",
802+]
803+
804+[[package]]
805+name = "icu_locale_core"
806+version = "2.2.0"
807+source = "registry+https://github.com/rust-lang/crates.io-index"
808+checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29"
809+dependencies = [
810+ "displaydoc",
811+ "litemap",
812+ "tinystr",
813+ "writeable",
814+ "zerovec",
815+]
816+
817+[[package]]
818+name = "icu_normalizer"
819+version = "2.2.0"
820+source = "registry+https://github.com/rust-lang/crates.io-index"
821+checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4"
822+dependencies = [
823+ "icu_collections",
824+ "icu_normalizer_data",
825+ "icu_properties",
826+ "icu_provider",
827+ "smallvec",
828+ "zerovec",
829+]
830+
831+[[package]]
832+name = "icu_normalizer_data"
833+version = "2.2.0"
834+source = "registry+https://github.com/rust-lang/crates.io-index"
835+checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38"
836+
837+[[package]]
838+name = "icu_properties"
839+version = "2.2.0"
840+source = "registry+https://github.com/rust-lang/crates.io-index"
841+checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de"
842+dependencies = [
843+ "icu_collections",
844+ "icu_locale_core",
845+ "icu_properties_data",
846+ "icu_provider",
847+ "zerotrie",
848+ "zerovec",
849+]
850+
851+[[package]]
852+name = "icu_properties_data"
853+version = "2.2.0"
854+source = "registry+https://github.com/rust-lang/crates.io-index"
855+checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14"
856+
857+[[package]]
858+name = "icu_provider"
859+version = "2.2.0"
860+source = "registry+https://github.com/rust-lang/crates.io-index"
861+checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421"
862+dependencies = [
863+ "displaydoc",
864+ "icu_locale_core",
865+ "writeable",
866+ "yoke",
867+ "zerofrom",
868+ "zerotrie",
869+ "zerovec",
870+]
871+
872+[[package]]
873+name = "id-arena"
874+version = "2.3.0"
875+source = "registry+https://github.com/rust-lang/crates.io-index"
876+checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
877+
878+[[package]]
879+name = "ident_case"
880+version = "1.0.1"
881+source = "registry+https://github.com/rust-lang/crates.io-index"
882+checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
883+
884+[[package]]
885+name = "idna"
886+version = "1.1.0"
887+source = "registry+https://github.com/rust-lang/crates.io-index"
888+checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de"
889+dependencies = [
890+ "idna_adapter",
891+ "smallvec",
892+ "utf8_iter",
893+]
894+
895+[[package]]
896+name = "idna_adapter"
897+version = "1.2.2"
898+source = "registry+https://github.com/rust-lang/crates.io-index"
899+checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714"
900+dependencies = [
901+ "icu_normalizer",
902+ "icu_properties",
903+]
904+
905+[[package]]
906+name = "include_dir"
907+version = "0.7.4"
908+source = "registry+https://github.com/rust-lang/crates.io-index"
909+checksum = "923d117408f1e49d914f1a379a309cffe4f18c05cf4e3d12e613a15fc81bd0dd"
910+dependencies = [
911+ "include_dir_macros",
912+]
913+
914+[[package]]
915+name = "include_dir_macros"
916+version = "0.7.4"
917+source = "registry+https://github.com/rust-lang/crates.io-index"
918+checksum = "7cab85a7ed0bd5f0e76d93846e0147172bed2e2d3f859bcc33a8d9699cad1a75"
919+dependencies = [
920+ "proc-macro2",
921+ "quote",
922+]
923+
924+[[package]]
925+name = "indexmap"
926+version = "2.14.0"
927+source = "registry+https://github.com/rust-lang/crates.io-index"
928+checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
929+dependencies = [
930+ "equivalent",
931+ "hashbrown 0.17.1",
932+ "serde",
933+ "serde_core",
934+]
935+
936+[[package]]
937+name = "ipnet"
938+version = "2.12.0"
939+source = "registry+https://github.com/rust-lang/crates.io-index"
940+checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2"
941+
942+[[package]]
943+name = "itertools"
944+version = "0.10.5"
945+source = "registry+https://github.com/rust-lang/crates.io-index"
946+checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
947+dependencies = [
948+ "either",
949+]
950+
951+[[package]]
952+name = "itoa"
953+version = "1.0.18"
954+source = "registry+https://github.com/rust-lang/crates.io-index"
955+checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
956+
957+[[package]]
958+name = "js-sys"
959+version = "0.3.98"
960+source = "registry+https://github.com/rust-lang/crates.io-index"
961+checksum = "67df7112613f8bfd9150013a0314e196f4800d3201ae742489d999db2f979f08"
962+dependencies = [
963+ "cfg-if",
964+ "futures-util",
965+ "once_cell",
966+ "wasm-bindgen",
967+]
968+
969+[[package]]
970+name = "lazy_static"
971+version = "1.5.0"
972+source = "registry+https://github.com/rust-lang/crates.io-index"
973+checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
974+dependencies = [
975+ "spin",
976+]
977+
978+[[package]]
979+name = "leb128fmt"
980+version = "0.1.0"
981+source = "registry+https://github.com/rust-lang/crates.io-index"
982+checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
983+
984+[[package]]
985+name = "libc"
986+version = "0.2.186"
987+source = "registry+https://github.com/rust-lang/crates.io-index"
988+checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
989+
990+[[package]]
991+name = "libm"
992+version = "0.2.16"
993+source = "registry+https://github.com/rust-lang/crates.io-index"
994+checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981"
995+
996+[[package]]
997+name = "libredox"
998+version = "0.1.16"
999+source = "registry+https://github.com/rust-lang/crates.io-index"
1000+checksum = "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c"
1001+dependencies = [
1002+ "bitflags 2.11.1",
1003+ "libc",
1004+ "plain",
1005+ "redox_syscall 0.7.5",
1006+]
1007+
1008+[[package]]
1009+name = "libsqlite3-sys"
1010+version = "0.30.1"
1011+source = "registry+https://github.com/rust-lang/crates.io-index"
1012+checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149"
1013+dependencies = [
1014+ "cc",
1015+ "pkg-config",
1016+ "vcpkg",
1017+]
1018+
1019+[[package]]
1020+name = "linux-raw-sys"
1021+version = "0.12.1"
1022+source = "registry+https://github.com/rust-lang/crates.io-index"
1023+checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
1024+
1025+[[package]]
1026+name = "litemap"
1027+version = "0.8.2"
1028+source = "registry+https://github.com/rust-lang/crates.io-index"
1029+checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0"
1030+
1031+[[package]]
1032+name = "lock_api"
1033+version = "0.4.14"
1034+source = "registry+https://github.com/rust-lang/crates.io-index"
1035+checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
1036+dependencies = [
1037+ "scopeguard",
1038+]
1039+
1040+[[package]]
1041+name = "log"
1042+version = "0.4.29"
1043+source = "registry+https://github.com/rust-lang/crates.io-index"
1044+checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
1045+
1046+[[package]]
1047+name = "matchers"
1048+version = "0.2.0"
1049+source = "registry+https://github.com/rust-lang/crates.io-index"
1050+checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9"
1051+dependencies = [
1052+ "regex-automata",
1053+]
1054+
1055+[[package]]
1056+name = "md-5"
1057+version = "0.10.6"
1058+source = "registry+https://github.com/rust-lang/crates.io-index"
1059+checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf"
1060+dependencies = [
1061+ "cfg-if",
1062+ "digest",
1063+]
1064+
1065+[[package]]
1066+name = "memchr"
1067+version = "2.8.0"
1068+source = "registry+https://github.com/rust-lang/crates.io-index"
1069+checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
1070+
1071+[[package]]
1072+name = "mime"
1073+version = "0.3.17"
1074+source = "registry+https://github.com/rust-lang/crates.io-index"
1075+checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
1076+
1077+[[package]]
1078+name = "mime_guess"
1079+version = "2.0.5"
1080+source = "registry+https://github.com/rust-lang/crates.io-index"
1081+checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e"
1082+dependencies = [
1083+ "mime",
1084+ "unicase",
1085+]
1086+
1087+[[package]]
1088+name = "mio"
1089+version = "1.2.0"
1090+source = "registry+https://github.com/rust-lang/crates.io-index"
1091+checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1"
1092+dependencies = [
1093+ "libc",
1094+ "wasi",
1095+ "windows-sys 0.61.2",
1096+]
1097+
1098+[[package]]
1099+name = "native-tls"
1100+version = "0.2.18"
1101+source = "registry+https://github.com/rust-lang/crates.io-index"
1102+checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2"
1103+dependencies = [
1104+ "libc",
1105+ "log",
1106+ "openssl",
1107+ "openssl-probe",
1108+ "openssl-sys",
1109+ "schannel",
1110+ "security-framework",
1111+ "security-framework-sys",
1112+ "tempfile",
1113+]
1114+
1115+[[package]]
1116+name = "nu-ansi-term"
1117+version = "0.50.3"
1118+source = "registry+https://github.com/rust-lang/crates.io-index"
1119+checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
1120+dependencies = [
1121+ "windows-sys 0.61.2",
1122+]
1123+
1124+[[package]]
1125+name = "num-bigint-dig"
1126+version = "0.8.6"
1127+source = "registry+https://github.com/rust-lang/crates.io-index"
1128+checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7"
1129+dependencies = [
1130+ "lazy_static",
1131+ "libm",
1132+ "num-integer",
1133+ "num-iter",
1134+ "num-traits",
1135+ "rand",
1136+ "smallvec",
1137+ "zeroize",
1138+]
1139+
1140+[[package]]
1141+name = "num-integer"
1142+version = "0.1.46"
1143+source = "registry+https://github.com/rust-lang/crates.io-index"
1144+checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
1145+dependencies = [
1146+ "num-traits",
1147+]
1148+
1149+[[package]]
1150+name = "num-iter"
1151+version = "0.1.45"
1152+source = "registry+https://github.com/rust-lang/crates.io-index"
1153+checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf"
1154+dependencies = [
1155+ "autocfg",
1156+ "num-integer",
1157+ "num-traits",
1158+]
1159+
1160+[[package]]
1161+name = "num-traits"
1162+version = "0.2.19"
1163+source = "registry+https://github.com/rust-lang/crates.io-index"
1164+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
1165+dependencies = [
1166+ "autocfg",
1167+ "libm",
1168+]
1169+
1170+[[package]]
1171+name = "once_cell"
1172+version = "1.21.4"
1173+source = "registry+https://github.com/rust-lang/crates.io-index"
1174+checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
1175+
1176+[[package]]
1177+name = "openssl"
1178+version = "0.10.79"
1179+source = "registry+https://github.com/rust-lang/crates.io-index"
1180+checksum = "bf0b434746ee2832f4f0baf10137e1cabb18cbe6912c69e2e33263c45250f542"
1181+dependencies = [
1182+ "bitflags 2.11.1",
1183+ "cfg-if",
1184+ "foreign-types",
1185+ "libc",
1186+ "openssl-macros",
1187+ "openssl-sys",
1188+]
1189+
1190+[[package]]
1191+name = "openssl-macros"
1192+version = "0.1.1"
1193+source = "registry+https://github.com/rust-lang/crates.io-index"
1194+checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
1195+dependencies = [
1196+ "proc-macro2",
1197+ "quote",
1198+ "syn 2.0.117",
1199+]
1200+
1201+[[package]]
1202+name = "openssl-probe"
1203+version = "0.2.1"
1204+source = "registry+https://github.com/rust-lang/crates.io-index"
1205+checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe"
1206+
1207+[[package]]
1208+name = "openssl-sys"
1209+version = "0.9.115"
1210+source = "registry+https://github.com/rust-lang/crates.io-index"
1211+checksum = "158fe5b292746440aa6e7a7e690e55aeb72d41505e2804c23c6973ad0e9c9781"
1212+dependencies = [
1213+ "cc",
1214+ "libc",
1215+ "pkg-config",
1216+ "vcpkg",
1217+]
1218+
1219+[[package]]
1220+name = "parking"
1221+version = "2.2.1"
1222+source = "registry+https://github.com/rust-lang/crates.io-index"
1223+checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba"
1224+
1225+[[package]]
1226+name = "parking_lot"
1227+version = "0.12.5"
1228+source = "registry+https://github.com/rust-lang/crates.io-index"
1229+checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
1230+dependencies = [
1231+ "lock_api",
1232+ "parking_lot_core",
1233+]
1234+
1235+[[package]]
1236+name = "parking_lot_core"
1237+version = "0.9.12"
1238+source = "registry+https://github.com/rust-lang/crates.io-index"
1239+checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
1240+dependencies = [
1241+ "cfg-if",
1242+ "libc",
1243+ "redox_syscall 0.5.18",
1244+ "smallvec",
1245+ "windows-link",
1246+]
1247+
1248+[[package]]
1249+name = "pem-rfc7468"
1250+version = "0.7.0"
1251+source = "registry+https://github.com/rust-lang/crates.io-index"
1252+checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412"
1253+dependencies = [
1254+ "base64ct",
1255+]
1256+
1257+[[package]]
1258+name = "percent-encoding"
1259+version = "2.3.2"
1260+source = "registry+https://github.com/rust-lang/crates.io-index"
1261+checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
1262+
1263+[[package]]
1264+name = "personality-traits-bot"
1265+version = "0.1.0"
1266+dependencies = [
1267+ "anyhow",
1268+ "chrono",
1269+ "dotenvy",
1270+ "once_cell",
1271+ "regex",
1272+ "serde",
1273+ "serde_yaml",
1274+ "sqlx",
1275+ "teloxide",
1276+ "thiserror 1.0.69",
1277+ "tokio",
1278+ "tracing",
1279+ "tracing-subscriber",
1280+ "url",
1281+]
1282+
1283+[[package]]
1284+name = "pin-project"
1285+version = "1.1.12"
1286+source = "registry+https://github.com/rust-lang/crates.io-index"
1287+checksum = "cbf0d9e68100b3a7989b4901972f265cd542e560a3a8a724e1e20322f4d06ce9"
1288+dependencies = [
1289+ "pin-project-internal",
1290+]
1291+
1292+[[package]]
1293+name = "pin-project-internal"
1294+version = "1.1.12"
1295+source = "registry+https://github.com/rust-lang/crates.io-index"
1296+checksum = "a990e22f43e84855daf260dded30524ef4a9021cc7541c26540500a50b624389"
1297+dependencies = [
1298+ "proc-macro2",
1299+ "quote",
1300+ "syn 2.0.117",
1301+]
1302+
1303+[[package]]
1304+name = "pin-project-lite"
1305+version = "0.2.17"
1306+source = "registry+https://github.com/rust-lang/crates.io-index"
1307+checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
1308+
1309+[[package]]
1310+name = "pkcs1"
1311+version = "0.7.5"
1312+source = "registry+https://github.com/rust-lang/crates.io-index"
1313+checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f"
1314+dependencies = [
1315+ "der",
1316+ "pkcs8",
1317+ "spki",
1318+]
1319+
1320+[[package]]
1321+name = "pkcs8"
1322+version = "0.10.2"
1323+source = "registry+https://github.com/rust-lang/crates.io-index"
1324+checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
1325+dependencies = [
1326+ "der",
1327+ "spki",
1328+]
1329+
1330+[[package]]
1331+name = "pkg-config"
1332+version = "0.3.33"
1333+source = "registry+https://github.com/rust-lang/crates.io-index"
1334+checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e"
1335+
1336+[[package]]
1337+name = "plain"
1338+version = "0.2.3"
1339+source = "registry+https://github.com/rust-lang/crates.io-index"
1340+checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
1341+
1342+[[package]]
1343+name = "potential_utf"
1344+version = "0.1.5"
1345+source = "registry+https://github.com/rust-lang/crates.io-index"
1346+checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564"
1347+dependencies = [
1348+ "zerovec",
1349+]
1350+
1351+[[package]]
1352+name = "ppv-lite86"
1353+version = "0.2.21"
1354+source = "registry+https://github.com/rust-lang/crates.io-index"
1355+checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
1356+dependencies = [
1357+ "zerocopy",
1358+]
1359+
1360+[[package]]
1361+name = "prettyplease"
1362+version = "0.2.37"
1363+source = "registry+https://github.com/rust-lang/crates.io-index"
1364+checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
1365+dependencies = [
1366+ "proc-macro2",
1367+ "syn 2.0.117",
1368+]
1369+
1370+[[package]]
1371+name = "proc-macro-error"
1372+version = "1.0.4"
1373+source = "registry+https://github.com/rust-lang/crates.io-index"
1374+checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
1375+dependencies = [
1376+ "proc-macro-error-attr",
1377+ "proc-macro2",
1378+ "quote",
1379+ "syn 1.0.109",
1380+ "version_check",
1381+]
1382+
1383+[[package]]
1384+name = "proc-macro-error-attr"
1385+version = "1.0.4"
1386+source = "registry+https://github.com/rust-lang/crates.io-index"
1387+checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
1388+dependencies = [
1389+ "proc-macro2",
1390+ "quote",
1391+ "version_check",
1392+]
1393+
1394+[[package]]
1395+name = "proc-macro2"
1396+version = "1.0.106"
1397+source = "registry+https://github.com/rust-lang/crates.io-index"
1398+checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
1399+dependencies = [
1400+ "unicode-ident",
1401+]
1402+
1403+[[package]]
1404+name = "quote"
1405+version = "1.0.45"
1406+source = "registry+https://github.com/rust-lang/crates.io-index"
1407+checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
1408+dependencies = [
1409+ "proc-macro2",
1410+]
1411+
1412+[[package]]
1413+name = "r-efi"
1414+version = "6.0.0"
1415+source = "registry+https://github.com/rust-lang/crates.io-index"
1416+checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
1417+
1418+[[package]]
1419+name = "rand"
1420+version = "0.8.6"
1421+source = "registry+https://github.com/rust-lang/crates.io-index"
1422+checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a"
1423+dependencies = [
1424+ "libc",
1425+ "rand_chacha",
1426+ "rand_core",
1427+]
1428+
1429+[[package]]
1430+name = "rand_chacha"
1431+version = "0.3.1"
1432+source = "registry+https://github.com/rust-lang/crates.io-index"
1433+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
1434+dependencies = [
1435+ "ppv-lite86",
1436+ "rand_core",
1437+]
1438+
1439+[[package]]
1440+name = "rand_core"
1441+version = "0.6.4"
1442+source = "registry+https://github.com/rust-lang/crates.io-index"
1443+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
1444+dependencies = [
1445+ "getrandom 0.2.17",
1446+]
1447+
1448+[[package]]
1449+name = "rc-box"
1450+version = "1.3.0"
1451+source = "registry+https://github.com/rust-lang/crates.io-index"
1452+checksum = "897fecc9fac6febd4408f9e935e86df739b0023b625e610e0357535b9c8adad0"
1453+dependencies = [
1454+ "erasable",
1455+]
1456+
1457+[[package]]
1458+name = "redox_syscall"
1459+version = "0.5.18"
1460+source = "registry+https://github.com/rust-lang/crates.io-index"
1461+checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
1462+dependencies = [
1463+ "bitflags 2.11.1",
1464+]
1465+
1466+[[package]]
1467+name = "redox_syscall"
1468+version = "0.7.5"
1469+source = "registry+https://github.com/rust-lang/crates.io-index"
1470+checksum = "4666a1a60d8412eab19d94f6d13dcc9cea0a5ef4fdf6a5db306537413c661b1b"
1471+dependencies = [
1472+ "bitflags 2.11.1",
1473+]
1474+
1475+[[package]]
1476+name = "regex"
1477+version = "1.12.3"
1478+source = "registry+https://github.com/rust-lang/crates.io-index"
1479+checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276"
1480+dependencies = [
1481+ "aho-corasick",
1482+ "memchr",
1483+ "regex-automata",
1484+ "regex-syntax",
1485+]
1486+
1487+[[package]]
1488+name = "regex-automata"
1489+version = "0.4.14"
1490+source = "registry+https://github.com/rust-lang/crates.io-index"
1491+checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
1492+dependencies = [
1493+ "aho-corasick",
1494+ "memchr",
1495+ "regex-syntax",
1496+]
1497+
1498+[[package]]
1499+name = "regex-syntax"
1500+version = "0.8.10"
1501+source = "registry+https://github.com/rust-lang/crates.io-index"
1502+checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
1503+
1504+[[package]]
1505+name = "reqwest"
1506+version = "0.11.27"
1507+source = "registry+https://github.com/rust-lang/crates.io-index"
1508+checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62"
1509+dependencies = [
1510+ "base64 0.21.7",
1511+ "bytes",
1512+ "encoding_rs",
1513+ "futures-core",
1514+ "futures-util",
1515+ "h2",
1516+ "http",
1517+ "http-body",
1518+ "hyper",
1519+ "hyper-tls",
1520+ "ipnet",
1521+ "js-sys",
1522+ "log",
1523+ "mime",
1524+ "mime_guess",
1525+ "native-tls",
1526+ "once_cell",
1527+ "percent-encoding",
1528+ "pin-project-lite",
1529+ "rustls-pemfile",
1530+ "serde",
1531+ "serde_json",
1532+ "serde_urlencoded",
1533+ "sync_wrapper",
1534+ "system-configuration",
1535+ "tokio",
1536+ "tokio-native-tls",
1537+ "tokio-util",
1538+ "tower-service",
1539+ "url",
1540+ "wasm-bindgen",
1541+ "wasm-bindgen-futures",
1542+ "wasm-streams",
1543+ "web-sys",
1544+ "winreg",
1545+]
1546+
1547+[[package]]
1548+name = "rsa"
1549+version = "0.9.10"
1550+source = "registry+https://github.com/rust-lang/crates.io-index"
1551+checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d"
1552+dependencies = [
1553+ "const-oid",
1554+ "digest",
1555+ "num-bigint-dig",
1556+ "num-integer",
1557+ "num-traits",
1558+ "pkcs1",
1559+ "pkcs8",
1560+ "rand_core",
1561+ "signature",
1562+ "spki",
1563+ "subtle",
1564+ "zeroize",
1565+]
1566+
1567+[[package]]
1568+name = "rustc_version"
1569+version = "0.4.1"
1570+source = "registry+https://github.com/rust-lang/crates.io-index"
1571+checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
1572+dependencies = [
1573+ "semver",
1574+]
1575+
1576+[[package]]
1577+name = "rustix"
1578+version = "1.1.4"
1579+source = "registry+https://github.com/rust-lang/crates.io-index"
1580+checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
1581+dependencies = [
1582+ "bitflags 2.11.1",
1583+ "errno",
1584+ "libc",
1585+ "linux-raw-sys",
1586+ "windows-sys 0.61.2",
1587+]
1588+
1589+[[package]]
1590+name = "rustls-pemfile"
1591+version = "1.0.4"
1592+source = "registry+https://github.com/rust-lang/crates.io-index"
1593+checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c"
1594+dependencies = [
1595+ "base64 0.21.7",
1596+]
1597+
1598+[[package]]
1599+name = "rustversion"
1600+version = "1.0.22"
1601+source = "registry+https://github.com/rust-lang/crates.io-index"
1602+checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
1603+
1604+[[package]]
1605+name = "ryu"
1606+version = "1.0.23"
1607+source = "registry+https://github.com/rust-lang/crates.io-index"
1608+checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f"
1609+
1610+[[package]]
1611+name = "schannel"
1612+version = "0.1.29"
1613+source = "registry+https://github.com/rust-lang/crates.io-index"
1614+checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939"
1615+dependencies = [
1616+ "windows-sys 0.61.2",
1617+]
1618+
1619+[[package]]
1620+name = "scopeguard"
1621+version = "1.2.0"
1622+source = "registry+https://github.com/rust-lang/crates.io-index"
1623+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
1624+
1625+[[package]]
1626+name = "security-framework"
1627+version = "3.7.0"
1628+source = "registry+https://github.com/rust-lang/crates.io-index"
1629+checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d"
1630+dependencies = [
1631+ "bitflags 2.11.1",
1632+ "core-foundation 0.10.1",
1633+ "core-foundation-sys",
1634+ "libc",
1635+ "security-framework-sys",
1636+]
1637+
1638+[[package]]
1639+name = "security-framework-sys"
1640+version = "2.17.0"
1641+source = "registry+https://github.com/rust-lang/crates.io-index"
1642+checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3"
1643+dependencies = [
1644+ "core-foundation-sys",
1645+ "libc",
1646+]
1647+
1648+[[package]]
1649+name = "semver"
1650+version = "1.0.28"
1651+source = "registry+https://github.com/rust-lang/crates.io-index"
1652+checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
1653+
1654+[[package]]
1655+name = "serde"
1656+version = "1.0.228"
1657+source = "registry+https://github.com/rust-lang/crates.io-index"
1658+checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
1659+dependencies = [
1660+ "serde_core",
1661+ "serde_derive",
1662+]
1663+
1664+[[package]]
1665+name = "serde_core"
1666+version = "1.0.228"
1667+source = "registry+https://github.com/rust-lang/crates.io-index"
1668+checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
1669+dependencies = [
1670+ "serde_derive",
1671+]
1672+
1673+[[package]]
1674+name = "serde_derive"
1675+version = "1.0.228"
1676+source = "registry+https://github.com/rust-lang/crates.io-index"
1677+checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
1678+dependencies = [
1679+ "proc-macro2",
1680+ "quote",
1681+ "syn 2.0.117",
1682+]
1683+
1684+[[package]]
1685+name = "serde_json"
1686+version = "1.0.149"
1687+source = "registry+https://github.com/rust-lang/crates.io-index"
1688+checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
1689+dependencies = [
1690+ "itoa",
1691+ "memchr",
1692+ "serde",
1693+ "serde_core",
1694+ "zmij",
1695+]
1696+
1697+[[package]]
1698+name = "serde_urlencoded"
1699+version = "0.7.1"
1700+source = "registry+https://github.com/rust-lang/crates.io-index"
1701+checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
1702+dependencies = [
1703+ "form_urlencoded",
1704+ "itoa",
1705+ "ryu",
1706+ "serde",
1707+]
1708+
1709+[[package]]
1710+name = "serde_with"
1711+version = "1.14.0"
1712+source = "registry+https://github.com/rust-lang/crates.io-index"
1713+checksum = "678b5a069e50bf00ecd22d0cd8ddf7c236f68581b03db652061ed5eb13a312ff"
1714+dependencies = [
1715+ "serde",
1716+ "serde_with_macros",
1717+]
1718+
1719+[[package]]
1720+name = "serde_with_macros"
1721+version = "1.5.2"
1722+source = "registry+https://github.com/rust-lang/crates.io-index"
1723+checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082"
1724+dependencies = [
1725+ "darling",
1726+ "proc-macro2",
1727+ "quote",
1728+ "syn 1.0.109",
1729+]
1730+
1731+[[package]]
1732+name = "serde_yaml"
1733+version = "0.9.34+deprecated"
1734+source = "registry+https://github.com/rust-lang/crates.io-index"
1735+checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47"
1736+dependencies = [
1737+ "indexmap",
1738+ "itoa",
1739+ "ryu",
1740+ "serde",
1741+ "unsafe-libyaml",
1742+]
1743+
1744+[[package]]
1745+name = "sha1"
1746+version = "0.10.6"
1747+source = "registry+https://github.com/rust-lang/crates.io-index"
1748+checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
1749+dependencies = [
1750+ "cfg-if",
1751+ "cpufeatures",
1752+ "digest",
1753+]
1754+
1755+[[package]]
1756+name = "sha2"
1757+version = "0.10.9"
1758+source = "registry+https://github.com/rust-lang/crates.io-index"
1759+checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
1760+dependencies = [
1761+ "cfg-if",
1762+ "cpufeatures",
1763+ "digest",
1764+]
1765+
1766+[[package]]
1767+name = "sharded-slab"
1768+version = "0.1.7"
1769+source = "registry+https://github.com/rust-lang/crates.io-index"
1770+checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
1771+dependencies = [
1772+ "lazy_static",
1773+]
1774+
1775+[[package]]
1776+name = "shlex"
1777+version = "1.3.0"
1778+source = "registry+https://github.com/rust-lang/crates.io-index"
1779+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
1780+
1781+[[package]]
1782+name = "signal-hook-registry"
1783+version = "1.4.8"
1784+source = "registry+https://github.com/rust-lang/crates.io-index"
1785+checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b"
1786+dependencies = [
1787+ "errno",
1788+ "libc",
1789+]
1790+
1791+[[package]]
1792+name = "signature"
1793+version = "2.2.0"
1794+source = "registry+https://github.com/rust-lang/crates.io-index"
1795+checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
1796+dependencies = [
1797+ "digest",
1798+ "rand_core",
1799+]
1800+
1801+[[package]]
1802+name = "slab"
1803+version = "0.4.12"
1804+source = "registry+https://github.com/rust-lang/crates.io-index"
1805+checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
1806+
1807+[[package]]
1808+name = "smallvec"
1809+version = "1.15.1"
1810+source = "registry+https://github.com/rust-lang/crates.io-index"
1811+checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
1812+dependencies = [
1813+ "serde",
1814+]
1815+
1816+[[package]]
1817+name = "socket2"
1818+version = "0.5.10"
1819+source = "registry+https://github.com/rust-lang/crates.io-index"
1820+checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678"
1821+dependencies = [
1822+ "libc",
1823+ "windows-sys 0.52.0",
1824+]
1825+
1826+[[package]]
1827+name = "socket2"
1828+version = "0.6.3"
1829+source = "registry+https://github.com/rust-lang/crates.io-index"
1830+checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e"
1831+dependencies = [
1832+ "libc",
1833+ "windows-sys 0.61.2",
1834+]
1835+
1836+[[package]]
1837+name = "spin"
1838+version = "0.9.8"
1839+source = "registry+https://github.com/rust-lang/crates.io-index"
1840+checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
1841+dependencies = [
1842+ "lock_api",
1843+]
1844+
1845+[[package]]
1846+name = "spki"
1847+version = "0.7.3"
1848+source = "registry+https://github.com/rust-lang/crates.io-index"
1849+checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d"
1850+dependencies = [
1851+ "base64ct",
1852+ "der",
1853+]
1854+
1855+[[package]]
1856+name = "sqlx"
1857+version = "0.8.6"
1858+source = "registry+https://github.com/rust-lang/crates.io-index"
1859+checksum = "1fefb893899429669dcdd979aff487bd78f4064e5e7907e4269081e0ef7d97dc"
1860+dependencies = [
1861+ "sqlx-core",
1862+ "sqlx-macros",
1863+ "sqlx-mysql",
1864+ "sqlx-postgres",
1865+ "sqlx-sqlite",
1866+]
1867+
1868+[[package]]
1869+name = "sqlx-core"
1870+version = "0.8.6"
1871+source = "registry+https://github.com/rust-lang/crates.io-index"
1872+checksum = "ee6798b1838b6a0f69c007c133b8df5866302197e404e8b6ee8ed3e3a5e68dc6"
1873+dependencies = [
1874+ "base64 0.22.1",
1875+ "bytes",
1876+ "chrono",
1877+ "crc",
1878+ "crossbeam-queue",
1879+ "either",
1880+ "event-listener",
1881+ "futures-core",
1882+ "futures-intrusive",
1883+ "futures-io",
1884+ "futures-util",
1885+ "hashbrown 0.15.5",
1886+ "hashlink",
1887+ "indexmap",
1888+ "log",
1889+ "memchr",
1890+ "once_cell",
1891+ "percent-encoding",
1892+ "serde",
1893+ "serde_json",
1894+ "sha2",
1895+ "smallvec",
1896+ "thiserror 2.0.18",
1897+ "tokio",
1898+ "tokio-stream",
1899+ "tracing",
1900+ "url",
1901+]
1902+
1903+[[package]]
1904+name = "sqlx-macros"
1905+version = "0.8.6"
1906+source = "registry+https://github.com/rust-lang/crates.io-index"
1907+checksum = "a2d452988ccaacfbf5e0bdbc348fb91d7c8af5bee192173ac3636b5fb6e6715d"
1908+dependencies = [
1909+ "proc-macro2",
1910+ "quote",
1911+ "sqlx-core",
1912+ "sqlx-macros-core",
1913+ "syn 2.0.117",
1914+]
1915+
1916+[[package]]
1917+name = "sqlx-macros-core"
1918+version = "0.8.6"
1919+source = "registry+https://github.com/rust-lang/crates.io-index"
1920+checksum = "19a9c1841124ac5a61741f96e1d9e2ec77424bf323962dd894bdb93f37d5219b"
1921+dependencies = [
1922+ "dotenvy",
1923+ "either",
1924+ "heck 0.5.0",
1925+ "hex",
1926+ "once_cell",
1927+ "proc-macro2",
1928+ "quote",
1929+ "serde",
1930+ "serde_json",
1931+ "sha2",
1932+ "sqlx-core",
1933+ "sqlx-mysql",
1934+ "sqlx-postgres",
1935+ "sqlx-sqlite",
1936+ "syn 2.0.117",
1937+ "tokio",
1938+ "url",
1939+]
1940+
1941+[[package]]
1942+name = "sqlx-mysql"
1943+version = "0.8.6"
1944+source = "registry+https://github.com/rust-lang/crates.io-index"
1945+checksum = "aa003f0038df784eb8fecbbac13affe3da23b45194bd57dba231c8f48199c526"
1946+dependencies = [
1947+ "atoi",
1948+ "base64 0.22.1",
1949+ "bitflags 2.11.1",
1950+ "byteorder",
1951+ "bytes",
1952+ "chrono",
1953+ "crc",
1954+ "digest",
1955+ "dotenvy",
1956+ "either",
1957+ "futures-channel",
1958+ "futures-core",
1959+ "futures-io",
1960+ "futures-util",
1961+ "generic-array",
1962+ "hex",
1963+ "hkdf",
1964+ "hmac",
1965+ "itoa",
1966+ "log",
1967+ "md-5",
1968+ "memchr",
1969+ "once_cell",
1970+ "percent-encoding",
1971+ "rand",
1972+ "rsa",
1973+ "serde",
1974+ "sha1",
1975+ "sha2",
1976+ "smallvec",
1977+ "sqlx-core",
1978+ "stringprep",
1979+ "thiserror 2.0.18",
1980+ "tracing",
1981+ "whoami",
1982+]
1983+
1984+[[package]]
1985+name = "sqlx-postgres"
1986+version = "0.8.6"
1987+source = "registry+https://github.com/rust-lang/crates.io-index"
1988+checksum = "db58fcd5a53cf07c184b154801ff91347e4c30d17a3562a635ff028ad5deda46"
1989+dependencies = [
1990+ "atoi",
1991+ "base64 0.22.1",
1992+ "bitflags 2.11.1",
1993+ "byteorder",
1994+ "chrono",
1995+ "crc",
1996+ "dotenvy",
1997+ "etcetera",
1998+ "futures-channel",
1999+ "futures-core",
2000+ "futures-util",
2001+ "hex",
2002+ "hkdf",
2003+ "hmac",
2004+ "home",
2005+ "itoa",
2006+ "log",
2007+ "md-5",
2008+ "memchr",
2009+ "once_cell",
2010+ "rand",
2011+ "serde",
2012+ "serde_json",
2013+ "sha2",
2014+ "smallvec",
2015+ "sqlx-core",
2016+ "stringprep",
2017+ "thiserror 2.0.18",
2018+ "tracing",
2019+ "whoami",
2020+]
2021+
2022+[[package]]
2023+name = "sqlx-sqlite"
2024+version = "0.8.6"
2025+source = "registry+https://github.com/rust-lang/crates.io-index"
2026+checksum = "c2d12fe70b2c1b4401038055f90f151b78208de1f9f89a7dbfd41587a10c3eea"
2027+dependencies = [
2028+ "atoi",
2029+ "chrono",
2030+ "flume",
2031+ "futures-channel",
2032+ "futures-core",
2033+ "futures-executor",
2034+ "futures-intrusive",
2035+ "futures-util",
2036+ "libsqlite3-sys",
2037+ "log",
2038+ "percent-encoding",
2039+ "serde",
2040+ "serde_urlencoded",
2041+ "sqlx-core",
2042+ "thiserror 2.0.18",
2043+ "tracing",
2044+ "url",
2045+]
2046+
2047+[[package]]
2048+name = "stable_deref_trait"
2049+version = "1.2.1"
2050+source = "registry+https://github.com/rust-lang/crates.io-index"
2051+checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
2052+
2053+[[package]]
2054+name = "stringprep"
2055+version = "0.1.5"
2056+source = "registry+https://github.com/rust-lang/crates.io-index"
2057+checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1"
2058+dependencies = [
2059+ "unicode-bidi",
2060+ "unicode-normalization",
2061+ "unicode-properties",
2062+]
2063+
2064+[[package]]
2065+name = "strsim"
2066+version = "0.10.0"
2067+source = "registry+https://github.com/rust-lang/crates.io-index"
2068+checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
2069+
2070+[[package]]
2071+name = "subtle"
2072+version = "2.6.1"
2073+source = "registry+https://github.com/rust-lang/crates.io-index"
2074+checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
2075+
2076+[[package]]
2077+name = "syn"
2078+version = "1.0.109"
2079+source = "registry+https://github.com/rust-lang/crates.io-index"
2080+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
2081+dependencies = [
2082+ "proc-macro2",
2083+ "quote",
2084+ "unicode-ident",
2085+]
2086+
2087+[[package]]
2088+name = "syn"
2089+version = "2.0.117"
2090+source = "registry+https://github.com/rust-lang/crates.io-index"
2091+checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
2092+dependencies = [
2093+ "proc-macro2",
2094+ "quote",
2095+ "unicode-ident",
2096+]
2097+
2098+[[package]]
2099+name = "sync_wrapper"
2100+version = "0.1.2"
2101+source = "registry+https://github.com/rust-lang/crates.io-index"
2102+checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
2103+
2104+[[package]]
2105+name = "synstructure"
2106+version = "0.13.2"
2107+source = "registry+https://github.com/rust-lang/crates.io-index"
2108+checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
2109+dependencies = [
2110+ "proc-macro2",
2111+ "quote",
2112+ "syn 2.0.117",
2113+]
2114+
2115+[[package]]
2116+name = "system-configuration"
2117+version = "0.5.1"
2118+source = "registry+https://github.com/rust-lang/crates.io-index"
2119+checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7"
2120+dependencies = [
2121+ "bitflags 1.3.2",
2122+ "core-foundation 0.9.4",
2123+ "system-configuration-sys",
2124+]
2125+
2126+[[package]]
2127+name = "system-configuration-sys"
2128+version = "0.5.0"
2129+source = "registry+https://github.com/rust-lang/crates.io-index"
2130+checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9"
2131+dependencies = [
2132+ "core-foundation-sys",
2133+ "libc",
2134+]
2135+
2136+[[package]]
2137+name = "take_mut"
2138+version = "0.2.2"
2139+source = "registry+https://github.com/rust-lang/crates.io-index"
2140+checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60"
2141+
2142+[[package]]
2143+name = "takecell"
2144+version = "0.1.1"
2145+source = "registry+https://github.com/rust-lang/crates.io-index"
2146+checksum = "20f34339676cdcab560c9a82300c4c2581f68b9369aedf0fae86f2ff9565ff3e"
2147+
2148+[[package]]
2149+name = "teloxide"
2150+version = "0.13.0"
2151+source = "registry+https://github.com/rust-lang/crates.io-index"
2152+checksum = "5f79dd283eb21b90451c03fa7c7f83b9985130efb876b33bad89a2c208ccbc16"
2153+dependencies = [
2154+ "aquamarine",
2155+ "bytes",
2156+ "derive_more",
2157+ "dptree",
2158+ "either",
2159+ "futures",
2160+ "log",
2161+ "mime",
2162+ "pin-project",
2163+ "serde",
2164+ "serde_json",
2165+ "teloxide-core",
2166+ "teloxide-macros",
2167+ "thiserror 1.0.69",
2168+ "tokio",
2169+ "tokio-stream",
2170+ "tokio-util",
2171+ "url",
2172+]
2173+
2174+[[package]]
2175+name = "teloxide-core"
2176+version = "0.10.1"
2177+source = "registry+https://github.com/rust-lang/crates.io-index"
2178+checksum = "9e1642a7ef10e7af63b8298c8d13c0f986d4fc646d42649ff060359607f62f69"
2179+dependencies = [
2180+ "bitflags 1.3.2",
2181+ "bytes",
2182+ "chrono",
2183+ "derive_more",
2184+ "either",
2185+ "futures",
2186+ "log",
2187+ "mime",
2188+ "once_cell",
2189+ "pin-project",
2190+ "rc-box",
2191+ "reqwest",
2192+ "serde",
2193+ "serde_json",
2194+ "serde_with",
2195+ "take_mut",
2196+ "takecell",
2197+ "thiserror 1.0.69",
2198+ "tokio",
2199+ "tokio-util",
2200+ "url",
2201+ "uuid",
2202+]
2203+
2204+[[package]]
2205+name = "teloxide-macros"
2206+version = "0.8.0"
2207+source = "registry+https://github.com/rust-lang/crates.io-index"
2208+checksum = "7e2d33d809c3e7161a9ab18bedddf98821245014f0a78fa4d2c9430b2ec018c1"
2209+dependencies = [
2210+ "heck 0.4.1",
2211+ "proc-macro2",
2212+ "quote",
2213+ "syn 1.0.109",
2214+]
2215+
2216+[[package]]
2217+name = "tempfile"
2218+version = "3.27.0"
2219+source = "registry+https://github.com/rust-lang/crates.io-index"
2220+checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
2221+dependencies = [
2222+ "fastrand",
2223+ "getrandom 0.4.2",
2224+ "once_cell",
2225+ "rustix",
2226+ "windows-sys 0.61.2",
2227+]
2228+
2229+[[package]]
2230+name = "thiserror"
2231+version = "1.0.69"
2232+source = "registry+https://github.com/rust-lang/crates.io-index"
2233+checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
2234+dependencies = [
2235+ "thiserror-impl 1.0.69",
2236+]
2237+
2238+[[package]]
2239+name = "thiserror"
2240+version = "2.0.18"
2241+source = "registry+https://github.com/rust-lang/crates.io-index"
2242+checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
2243+dependencies = [
2244+ "thiserror-impl 2.0.18",
2245+]
2246+
2247+[[package]]
2248+name = "thiserror-impl"
2249+version = "1.0.69"
2250+source = "registry+https://github.com/rust-lang/crates.io-index"
2251+checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
2252+dependencies = [
2253+ "proc-macro2",
2254+ "quote",
2255+ "syn 2.0.117",
2256+]
2257+
2258+[[package]]
2259+name = "thiserror-impl"
2260+version = "2.0.18"
2261+source = "registry+https://github.com/rust-lang/crates.io-index"
2262+checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
2263+dependencies = [
2264+ "proc-macro2",
2265+ "quote",
2266+ "syn 2.0.117",
2267+]
2268+
2269+[[package]]
2270+name = "thread_local"
2271+version = "1.1.9"
2272+source = "registry+https://github.com/rust-lang/crates.io-index"
2273+checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
2274+dependencies = [
2275+ "cfg-if",
2276+]
2277+
2278+[[package]]
2279+name = "tinystr"
2280+version = "0.8.3"
2281+source = "registry+https://github.com/rust-lang/crates.io-index"
2282+checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d"
2283+dependencies = [
2284+ "displaydoc",
2285+ "zerovec",
2286+]
2287+
2288+[[package]]
2289+name = "tinyvec"
2290+version = "1.11.0"
2291+source = "registry+https://github.com/rust-lang/crates.io-index"
2292+checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3"
2293+dependencies = [
2294+ "tinyvec_macros",
2295+]
2296+
2297+[[package]]
2298+name = "tinyvec_macros"
2299+version = "0.1.1"
2300+source = "registry+https://github.com/rust-lang/crates.io-index"
2301+checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
2302+
2303+[[package]]
2304+name = "tokio"
2305+version = "1.52.3"
2306+source = "registry+https://github.com/rust-lang/crates.io-index"
2307+checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe"
2308+dependencies = [
2309+ "bytes",
2310+ "libc",
2311+ "mio",
2312+ "pin-project-lite",
2313+ "signal-hook-registry",
2314+ "socket2 0.6.3",
2315+ "tokio-macros",
2316+ "windows-sys 0.61.2",
2317+]
2318+
2319+[[package]]
2320+name = "tokio-macros"
2321+version = "2.7.0"
2322+source = "registry+https://github.com/rust-lang/crates.io-index"
2323+checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496"
2324+dependencies = [
2325+ "proc-macro2",
2326+ "quote",
2327+ "syn 2.0.117",
2328+]
2329+
2330+[[package]]
2331+name = "tokio-native-tls"
2332+version = "0.3.1"
2333+source = "registry+https://github.com/rust-lang/crates.io-index"
2334+checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
2335+dependencies = [
2336+ "native-tls",
2337+ "tokio",
2338+]
2339+
2340+[[package]]
2341+name = "tokio-stream"
2342+version = "0.1.18"
2343+source = "registry+https://github.com/rust-lang/crates.io-index"
2344+checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70"
2345+dependencies = [
2346+ "futures-core",
2347+ "pin-project-lite",
2348+ "tokio",
2349+]
2350+
2351+[[package]]
2352+name = "tokio-util"
2353+version = "0.7.18"
2354+source = "registry+https://github.com/rust-lang/crates.io-index"
2355+checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098"
2356+dependencies = [
2357+ "bytes",
2358+ "futures-core",
2359+ "futures-sink",
2360+ "pin-project-lite",
2361+ "tokio",
2362+]
2363+
2364+[[package]]
2365+name = "tower-service"
2366+version = "0.3.3"
2367+source = "registry+https://github.com/rust-lang/crates.io-index"
2368+checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
2369+
2370+[[package]]
2371+name = "tracing"
2372+version = "0.1.44"
2373+source = "registry+https://github.com/rust-lang/crates.io-index"
2374+checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
2375+dependencies = [
2376+ "log",
2377+ "pin-project-lite",
2378+ "tracing-attributes",
2379+ "tracing-core",
2380+]
2381+
2382+[[package]]
2383+name = "tracing-attributes"
2384+version = "0.1.31"
2385+source = "registry+https://github.com/rust-lang/crates.io-index"
2386+checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
2387+dependencies = [
2388+ "proc-macro2",
2389+ "quote",
2390+ "syn 2.0.117",
2391+]
2392+
2393+[[package]]
2394+name = "tracing-core"
2395+version = "0.1.36"
2396+source = "registry+https://github.com/rust-lang/crates.io-index"
2397+checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
2398+dependencies = [
2399+ "once_cell",
2400+ "valuable",
2401+]
2402+
2403+[[package]]
2404+name = "tracing-log"
2405+version = "0.2.0"
2406+source = "registry+https://github.com/rust-lang/crates.io-index"
2407+checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
2408+dependencies = [
2409+ "log",
2410+ "once_cell",
2411+ "tracing-core",
2412+]
2413+
2414+[[package]]
2415+name = "tracing-subscriber"
2416+version = "0.3.23"
2417+source = "registry+https://github.com/rust-lang/crates.io-index"
2418+checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319"
2419+dependencies = [
2420+ "matchers",
2421+ "nu-ansi-term",
2422+ "once_cell",
2423+ "regex-automata",
2424+ "sharded-slab",
2425+ "smallvec",
2426+ "thread_local",
2427+ "tracing",
2428+ "tracing-core",
2429+ "tracing-log",
2430+]
2431+
2432+[[package]]
2433+name = "try-lock"
2434+version = "0.2.5"
2435+source = "registry+https://github.com/rust-lang/crates.io-index"
2436+checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
2437+
2438+[[package]]
2439+name = "typenum"
2440+version = "1.20.0"
2441+source = "registry+https://github.com/rust-lang/crates.io-index"
2442+checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de"
2443+
2444+[[package]]
2445+name = "unicase"
2446+version = "2.9.0"
2447+source = "registry+https://github.com/rust-lang/crates.io-index"
2448+checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142"
2449+
2450+[[package]]
2451+name = "unicode-bidi"
2452+version = "0.3.18"
2453+source = "registry+https://github.com/rust-lang/crates.io-index"
2454+checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5"
2455+
2456+[[package]]
2457+name = "unicode-ident"
2458+version = "1.0.24"
2459+source = "registry+https://github.com/rust-lang/crates.io-index"
2460+checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
2461+
2462+[[package]]
2463+name = "unicode-normalization"
2464+version = "0.1.25"
2465+source = "registry+https://github.com/rust-lang/crates.io-index"
2466+checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8"
2467+dependencies = [
2468+ "tinyvec",
2469+]
2470+
2471+[[package]]
2472+name = "unicode-properties"
2473+version = "0.1.4"
2474+source = "registry+https://github.com/rust-lang/crates.io-index"
2475+checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d"
2476+
2477+[[package]]
2478+name = "unicode-xid"
2479+version = "0.2.6"
2480+source = "registry+https://github.com/rust-lang/crates.io-index"
2481+checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
2482+
2483+[[package]]
2484+name = "unsafe-libyaml"
2485+version = "0.2.11"
2486+source = "registry+https://github.com/rust-lang/crates.io-index"
2487+checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
2488+
2489+[[package]]
2490+name = "url"
2491+version = "2.5.8"
2492+source = "registry+https://github.com/rust-lang/crates.io-index"
2493+checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed"
2494+dependencies = [
2495+ "form_urlencoded",
2496+ "idna",
2497+ "percent-encoding",
2498+ "serde",
2499+ "serde_derive",
2500+]
2501+
2502+[[package]]
2503+name = "utf8_iter"
2504+version = "1.0.4"
2505+source = "registry+https://github.com/rust-lang/crates.io-index"
2506+checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
2507+
2508+[[package]]
2509+name = "uuid"
2510+version = "1.23.1"
2511+source = "registry+https://github.com/rust-lang/crates.io-index"
2512+checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76"
2513+dependencies = [
2514+ "getrandom 0.4.2",
2515+ "js-sys",
2516+ "wasm-bindgen",
2517+]
2518+
2519+[[package]]
2520+name = "valuable"
2521+version = "0.1.1"
2522+source = "registry+https://github.com/rust-lang/crates.io-index"
2523+checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
2524+
2525+[[package]]
2526+name = "vcpkg"
2527+version = "0.2.15"
2528+source = "registry+https://github.com/rust-lang/crates.io-index"
2529+checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
2530+
2531+[[package]]
2532+name = "version_check"
2533+version = "0.9.5"
2534+source = "registry+https://github.com/rust-lang/crates.io-index"
2535+checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
2536+
2537+[[package]]
2538+name = "want"
2539+version = "0.3.1"
2540+source = "registry+https://github.com/rust-lang/crates.io-index"
2541+checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
2542+dependencies = [
2543+ "try-lock",
2544+]
2545+
2546+[[package]]
2547+name = "wasi"
2548+version = "0.11.1+wasi-snapshot-preview1"
2549+source = "registry+https://github.com/rust-lang/crates.io-index"
2550+checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
2551+
2552+[[package]]
2553+name = "wasip2"
2554+version = "1.0.3+wasi-0.2.9"
2555+source = "registry+https://github.com/rust-lang/crates.io-index"
2556+checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6"
2557+dependencies = [
2558+ "wit-bindgen 0.57.1",
2559+]
2560+
2561+[[package]]
2562+name = "wasip3"
2563+version = "0.4.0+wasi-0.3.0-rc-2026-01-06"
2564+source = "registry+https://github.com/rust-lang/crates.io-index"
2565+checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5"
2566+dependencies = [
2567+ "wit-bindgen 0.51.0",
2568+]
2569+
2570+[[package]]
2571+name = "wasite"
2572+version = "0.1.0"
2573+source = "registry+https://github.com/rust-lang/crates.io-index"
2574+checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b"
2575+
2576+[[package]]
2577+name = "wasm-bindgen"
2578+version = "0.2.121"
2579+source = "registry+https://github.com/rust-lang/crates.io-index"
2580+checksum = "49ace1d07c165b0864824eee619580c4689389afa9dc9ed3a4c75040d82e6790"
2581+dependencies = [
2582+ "cfg-if",
2583+ "once_cell",
2584+ "rustversion",
2585+ "wasm-bindgen-macro",
2586+ "wasm-bindgen-shared",
2587+]
2588+
2589+[[package]]
2590+name = "wasm-bindgen-futures"
2591+version = "0.4.71"
2592+source = "registry+https://github.com/rust-lang/crates.io-index"
2593+checksum = "96492d0d3ffba25305a7dc88720d250b1401d7edca02cc3bcd50633b424673b8"
2594+dependencies = [
2595+ "js-sys",
2596+ "wasm-bindgen",
2597+]
2598+
2599+[[package]]
2600+name = "wasm-bindgen-macro"
2601+version = "0.2.121"
2602+source = "registry+https://github.com/rust-lang/crates.io-index"
2603+checksum = "8e68e6f4afd367a562002c05637acb8578ff2dea1943df76afb9e83d177c8578"
2604+dependencies = [
2605+ "quote",
2606+ "wasm-bindgen-macro-support",
2607+]
2608+
2609+[[package]]
2610+name = "wasm-bindgen-macro-support"
2611+version = "0.2.121"
2612+source = "registry+https://github.com/rust-lang/crates.io-index"
2613+checksum = "d95a9ec35c64b2a7cb35d3fead40c4238d0940c86d107136999567a4703259f2"
2614+dependencies = [
2615+ "bumpalo",
2616+ "proc-macro2",
2617+ "quote",
2618+ "syn 2.0.117",
2619+ "wasm-bindgen-shared",
2620+]
2621+
2622+[[package]]
2623+name = "wasm-bindgen-shared"
2624+version = "0.2.121"
2625+source = "registry+https://github.com/rust-lang/crates.io-index"
2626+checksum = "c4e0100b01e9f0d03189a92b96772a1fb998639d981193d7dbab487302513441"
2627+dependencies = [
2628+ "unicode-ident",
2629+]
2630+
2631+[[package]]
2632+name = "wasm-encoder"
2633+version = "0.244.0"
2634+source = "registry+https://github.com/rust-lang/crates.io-index"
2635+checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319"
2636+dependencies = [
2637+ "leb128fmt",
2638+ "wasmparser",
2639+]
2640+
2641+[[package]]
2642+name = "wasm-metadata"
2643+version = "0.244.0"
2644+source = "registry+https://github.com/rust-lang/crates.io-index"
2645+checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909"
2646+dependencies = [
2647+ "anyhow",
2648+ "indexmap",
2649+ "wasm-encoder",
2650+ "wasmparser",
2651+]
2652+
2653+[[package]]
2654+name = "wasm-streams"
2655+version = "0.4.2"
2656+source = "registry+https://github.com/rust-lang/crates.io-index"
2657+checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65"
2658+dependencies = [
2659+ "futures-util",
2660+ "js-sys",
2661+ "wasm-bindgen",
2662+ "wasm-bindgen-futures",
2663+ "web-sys",
2664+]
2665+
2666+[[package]]
2667+name = "wasmparser"
2668+version = "0.244.0"
2669+source = "registry+https://github.com/rust-lang/crates.io-index"
2670+checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
2671+dependencies = [
2672+ "bitflags 2.11.1",
2673+ "hashbrown 0.15.5",
2674+ "indexmap",
2675+ "semver",
2676+]
2677+
2678+[[package]]
2679+name = "web-sys"
2680+version = "0.3.98"
2681+source = "registry+https://github.com/rust-lang/crates.io-index"
2682+checksum = "4b572dff8bcf38bad0fa19729c89bb5748b2b9b1d8be70cf90df697e3a8f32aa"
2683+dependencies = [
2684+ "js-sys",
2685+ "wasm-bindgen",
2686+]
2687+
2688+[[package]]
2689+name = "whoami"
2690+version = "1.6.1"
2691+source = "registry+https://github.com/rust-lang/crates.io-index"
2692+checksum = "5d4a4db5077702ca3015d3d02d74974948aba2ad9e12ab7df718ee64ccd7e97d"
2693+dependencies = [
2694+ "libredox",
2695+ "wasite",
2696+]
2697+
2698+[[package]]
2699+name = "windows-core"
2700+version = "0.62.2"
2701+source = "registry+https://github.com/rust-lang/crates.io-index"
2702+checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
2703+dependencies = [
2704+ "windows-implement",
2705+ "windows-interface",
2706+ "windows-link",
2707+ "windows-result",
2708+ "windows-strings",
2709+]
2710+
2711+[[package]]
2712+name = "windows-implement"
2713+version = "0.60.2"
2714+source = "registry+https://github.com/rust-lang/crates.io-index"
2715+checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
2716+dependencies = [
2717+ "proc-macro2",
2718+ "quote",
2719+ "syn 2.0.117",
2720+]
2721+
2722+[[package]]
2723+name = "windows-interface"
2724+version = "0.59.3"
2725+source = "registry+https://github.com/rust-lang/crates.io-index"
2726+checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
2727+dependencies = [
2728+ "proc-macro2",
2729+ "quote",
2730+ "syn 2.0.117",
2731+]
2732+
2733+[[package]]
2734+name = "windows-link"
2735+version = "0.2.1"
2736+source = "registry+https://github.com/rust-lang/crates.io-index"
2737+checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
2738+
2739+[[package]]
2740+name = "windows-result"
2741+version = "0.4.1"
2742+source = "registry+https://github.com/rust-lang/crates.io-index"
2743+checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
2744+dependencies = [
2745+ "windows-link",
2746+]
2747+
2748+[[package]]
2749+name = "windows-strings"
2750+version = "0.5.1"
2751+source = "registry+https://github.com/rust-lang/crates.io-index"
2752+checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
2753+dependencies = [
2754+ "windows-link",
2755+]
2756+
2757+[[package]]
2758+name = "windows-sys"
2759+version = "0.48.0"
2760+source = "registry+https://github.com/rust-lang/crates.io-index"
2761+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
2762+dependencies = [
2763+ "windows-targets 0.48.5",
2764+]
2765+
2766+[[package]]
2767+name = "windows-sys"
2768+version = "0.52.0"
2769+source = "registry+https://github.com/rust-lang/crates.io-index"
2770+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
2771+dependencies = [
2772+ "windows-targets 0.52.6",
2773+]
2774+
2775+[[package]]
2776+name = "windows-sys"
2777+version = "0.61.2"
2778+source = "registry+https://github.com/rust-lang/crates.io-index"
2779+checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
2780+dependencies = [
2781+ "windows-link",
2782+]
2783+
2784+[[package]]
2785+name = "windows-targets"
2786+version = "0.48.5"
2787+source = "registry+https://github.com/rust-lang/crates.io-index"
2788+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
2789+dependencies = [
2790+ "windows_aarch64_gnullvm 0.48.5",
2791+ "windows_aarch64_msvc 0.48.5",
2792+ "windows_i686_gnu 0.48.5",
2793+ "windows_i686_msvc 0.48.5",
2794+ "windows_x86_64_gnu 0.48.5",
2795+ "windows_x86_64_gnullvm 0.48.5",
2796+ "windows_x86_64_msvc 0.48.5",
2797+]
2798+
2799+[[package]]
2800+name = "windows-targets"
2801+version = "0.52.6"
2802+source = "registry+https://github.com/rust-lang/crates.io-index"
2803+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
2804+dependencies = [
2805+ "windows_aarch64_gnullvm 0.52.6",
2806+ "windows_aarch64_msvc 0.52.6",
2807+ "windows_i686_gnu 0.52.6",
2808+ "windows_i686_gnullvm",
2809+ "windows_i686_msvc 0.52.6",
2810+ "windows_x86_64_gnu 0.52.6",
2811+ "windows_x86_64_gnullvm 0.52.6",
2812+ "windows_x86_64_msvc 0.52.6",
2813+]
2814+
2815+[[package]]
2816+name = "windows_aarch64_gnullvm"
2817+version = "0.48.5"
2818+source = "registry+https://github.com/rust-lang/crates.io-index"
2819+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
2820+
2821+[[package]]
2822+name = "windows_aarch64_gnullvm"
2823+version = "0.52.6"
2824+source = "registry+https://github.com/rust-lang/crates.io-index"
2825+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
2826+
2827+[[package]]
2828+name = "windows_aarch64_msvc"
2829+version = "0.48.5"
2830+source = "registry+https://github.com/rust-lang/crates.io-index"
2831+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
2832+
2833+[[package]]
2834+name = "windows_aarch64_msvc"
2835+version = "0.52.6"
2836+source = "registry+https://github.com/rust-lang/crates.io-index"
2837+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
2838+
2839+[[package]]
2840+name = "windows_i686_gnu"
2841+version = "0.48.5"
2842+source = "registry+https://github.com/rust-lang/crates.io-index"
2843+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
2844+
2845+[[package]]
2846+name = "windows_i686_gnu"
2847+version = "0.52.6"
2848+source = "registry+https://github.com/rust-lang/crates.io-index"
2849+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
2850+
2851+[[package]]
2852+name = "windows_i686_gnullvm"
2853+version = "0.52.6"
2854+source = "registry+https://github.com/rust-lang/crates.io-index"
2855+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
2856+
2857+[[package]]
2858+name = "windows_i686_msvc"
2859+version = "0.48.5"
2860+source = "registry+https://github.com/rust-lang/crates.io-index"
2861+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
2862+
2863+[[package]]
2864+name = "windows_i686_msvc"
2865+version = "0.52.6"
2866+source = "registry+https://github.com/rust-lang/crates.io-index"
2867+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
2868+
2869+[[package]]
2870+name = "windows_x86_64_gnu"
2871+version = "0.48.5"
2872+source = "registry+https://github.com/rust-lang/crates.io-index"
2873+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
2874+
2875+[[package]]
2876+name = "windows_x86_64_gnu"
2877+version = "0.52.6"
2878+source = "registry+https://github.com/rust-lang/crates.io-index"
2879+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
2880+
2881+[[package]]
2882+name = "windows_x86_64_gnullvm"
2883+version = "0.48.5"
2884+source = "registry+https://github.com/rust-lang/crates.io-index"
2885+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
2886+
2887+[[package]]
2888+name = "windows_x86_64_gnullvm"
2889+version = "0.52.6"
2890+source = "registry+https://github.com/rust-lang/crates.io-index"
2891+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
2892+
2893+[[package]]
2894+name = "windows_x86_64_msvc"
2895+version = "0.48.5"
2896+source = "registry+https://github.com/rust-lang/crates.io-index"
2897+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
2898+
2899+[[package]]
2900+name = "windows_x86_64_msvc"
2901+version = "0.52.6"
2902+source = "registry+https://github.com/rust-lang/crates.io-index"
2903+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
2904+
2905+[[package]]
2906+name = "winreg"
2907+version = "0.50.0"
2908+source = "registry+https://github.com/rust-lang/crates.io-index"
2909+checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1"
2910+dependencies = [
2911+ "cfg-if",
2912+ "windows-sys 0.48.0",
2913+]
2914+
2915+[[package]]
2916+name = "wit-bindgen"
2917+version = "0.51.0"
2918+source = "registry+https://github.com/rust-lang/crates.io-index"
2919+checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
2920+dependencies = [
2921+ "wit-bindgen-rust-macro",
2922+]
2923+
2924+[[package]]
2925+name = "wit-bindgen"
2926+version = "0.57.1"
2927+source = "registry+https://github.com/rust-lang/crates.io-index"
2928+checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e"
2929+
2930+[[package]]
2931+name = "wit-bindgen-core"
2932+version = "0.51.0"
2933+source = "registry+https://github.com/rust-lang/crates.io-index"
2934+checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc"
2935+dependencies = [
2936+ "anyhow",
2937+ "heck 0.5.0",
2938+ "wit-parser",
2939+]
2940+
2941+[[package]]
2942+name = "wit-bindgen-rust"
2943+version = "0.51.0"
2944+source = "registry+https://github.com/rust-lang/crates.io-index"
2945+checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21"
2946+dependencies = [
2947+ "anyhow",
2948+ "heck 0.5.0",
2949+ "indexmap",
2950+ "prettyplease",
2951+ "syn 2.0.117",
2952+ "wasm-metadata",
2953+ "wit-bindgen-core",
2954+ "wit-component",
2955+]
2956+
2957+[[package]]
2958+name = "wit-bindgen-rust-macro"
2959+version = "0.51.0"
2960+source = "registry+https://github.com/rust-lang/crates.io-index"
2961+checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a"
2962+dependencies = [
2963+ "anyhow",
2964+ "prettyplease",
2965+ "proc-macro2",
2966+ "quote",
2967+ "syn 2.0.117",
2968+ "wit-bindgen-core",
2969+ "wit-bindgen-rust",
2970+]
2971+
2972+[[package]]
2973+name = "wit-component"
2974+version = "0.244.0"
2975+source = "registry+https://github.com/rust-lang/crates.io-index"
2976+checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
2977+dependencies = [
2978+ "anyhow",
2979+ "bitflags 2.11.1",
2980+ "indexmap",
2981+ "log",
2982+ "serde",
2983+ "serde_derive",
2984+ "serde_json",
2985+ "wasm-encoder",
2986+ "wasm-metadata",
2987+ "wasmparser",
2988+ "wit-parser",
2989+]
2990+
2991+[[package]]
2992+name = "wit-parser"
2993+version = "0.244.0"
2994+source = "registry+https://github.com/rust-lang/crates.io-index"
2995+checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736"
2996+dependencies = [
2997+ "anyhow",
2998+ "id-arena",
2999+ "indexmap",
3000+ "log",
3001+ "semver",
3002+ "serde",
3003+ "serde_derive",
3004+ "serde_json",
3005+ "unicode-xid",
3006+ "wasmparser",
3007+]
3008+
3009+[[package]]
3010+name = "writeable"
3011+version = "0.6.3"
3012+source = "registry+https://github.com/rust-lang/crates.io-index"
3013+checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4"
3014+
3015+[[package]]
3016+name = "yoke"
3017+version = "0.8.2"
3018+source = "registry+https://github.com/rust-lang/crates.io-index"
3019+checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca"
3020+dependencies = [
3021+ "stable_deref_trait",
3022+ "yoke-derive",
3023+ "zerofrom",
3024+]
3025+
3026+[[package]]
3027+name = "yoke-derive"
3028+version = "0.8.2"
3029+source = "registry+https://github.com/rust-lang/crates.io-index"
3030+checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e"
3031+dependencies = [
3032+ "proc-macro2",
3033+ "quote",
3034+ "syn 2.0.117",
3035+ "synstructure",
3036+]
3037+
3038+[[package]]
3039+name = "zerocopy"
3040+version = "0.8.48"
3041+source = "registry+https://github.com/rust-lang/crates.io-index"
3042+checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9"
3043+dependencies = [
3044+ "zerocopy-derive",
3045+]
3046+
3047+[[package]]
3048+name = "zerocopy-derive"
3049+version = "0.8.48"
3050+source = "registry+https://github.com/rust-lang/crates.io-index"
3051+checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4"
3052+dependencies = [
3053+ "proc-macro2",
3054+ "quote",
3055+ "syn 2.0.117",
3056+]
3057+
3058+[[package]]
3059+name = "zerofrom"
3060+version = "0.1.8"
3061+source = "registry+https://github.com/rust-lang/crates.io-index"
3062+checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272"
3063+dependencies = [
3064+ "zerofrom-derive",
3065+]
3066+
3067+[[package]]
3068+name = "zerofrom-derive"
3069+version = "0.1.7"
3070+source = "registry+https://github.com/rust-lang/crates.io-index"
3071+checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1"
3072+dependencies = [
3073+ "proc-macro2",
3074+ "quote",
3075+ "syn 2.0.117",
3076+ "synstructure",
3077+]
3078+
3079+[[package]]
3080+name = "zeroize"
3081+version = "1.8.2"
3082+source = "registry+https://github.com/rust-lang/crates.io-index"
3083+checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
3084+
3085+[[package]]
3086+name = "zerotrie"
3087+version = "0.2.4"
3088+source = "registry+https://github.com/rust-lang/crates.io-index"
3089+checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf"
3090+dependencies = [
3091+ "displaydoc",
3092+ "yoke",
3093+ "zerofrom",
3094+]
3095+
3096+[[package]]
3097+name = "zerovec"
3098+version = "0.11.6"
3099+source = "registry+https://github.com/rust-lang/crates.io-index"
3100+checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239"
3101+dependencies = [
3102+ "yoke",
3103+ "zerofrom",
3104+ "zerovec-derive",
3105+]
3106+
3107+[[package]]
3108+name = "zerovec-derive"
3109+version = "0.11.3"
3110+source = "registry+https://github.com/rust-lang/crates.io-index"
3111+checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555"
3112+dependencies = [
3113+ "proc-macro2",
3114+ "quote",
3115+ "syn 2.0.117",
3116+]
3117+
3118+[[package]]
3119+name = "zmij"
3120+version = "1.0.21"
3121+source = "registry+https://github.com/rust-lang/crates.io-index"
3122+checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
Cargo.tomlnew file+20
@@ -0,0 +1,20 @@
1+[package]
2+name = "personality-traits-bot"
3+version = "0.1.0"
4+edition = "2021"
5+
6+[dependencies]
7+teloxide = { version = "0.13", features = ["macros", "ctrlc_handler"] }
8+tokio = { version = "1", features = ["rt-multi-thread", "macros", "signal", "time", "sync"] }
9+serde = { version = "1", features = ["derive"] }
10+serde_yaml = "0.9"
11+sqlx = { version = "0.8", features = ["runtime-tokio", "sqlite", "chrono", "migrate"] }
12+chrono = { version = "0.4", features = ["serde"] }
13+dotenvy = "0.15"
14+tracing = "0.1"
15+tracing-subscriber = { version = "0.3", features = ["env-filter"] }
16+anyhow = "1"
17+thiserror = "1"
18+regex = "1"
19+once_cell = "1"
20+url = "2"
README.mdnew file+74
@@ -0,0 +1,74 @@
1+# personality-traits-bot
2+
3+Telegram-бот для практикума по снижению тревожности (Rust + teloxide + SQLite).
4+
5+## Что реализовано
6+
7+- Авторизация по email (одно сообщение с корректным email сохраняет пользователя).
8+- Кнопка «Начать курс» при первом запуске и «Начать курс заново» — после старта.
9+- Сценарий курса описан в `questionnaire/abfi-1/events.yaml`. Сообщения отправляются с задержкой `delay_minutes`, которая отсчитывается от нажатия кнопки в предыдущем сообщении.
10+- Сообщения вида `day_*` сопровождаются inline-кнопкой «Начать практику», `practice_*` — кнопкой «Завершить практику» (тексты берутся прямо из yaml).
11+- Все нажатия инлайн-кнопок логируются в таблицу `button_events` (telegram_id, message_id, button_id, event_target, время).
12+- Медиа из `questionnaire/abfi-1/data/`: `.png/.jpg/.jpeg` → `sendPhoto`, `.mp4` → `sendVideo`, `.mp3/.m4a` → `sendAudio`, `.ogg` → `sendVoice`, всё прочее (включая `.pdf` и `.webp`) → `sendDocument`. Отсутствующие файлы (например, `foo.mp4`) логируются как warning и пропускаются.
13+
14+## Команды
15+
16+- `/start` — зарегистрироваться (если ещё нет) или показать главное меню.
17+- `/restart` — завершить текущую сессию и начать курс заново.
18+- `/help` — список команд.
19+
20+## Запуск
21+
22+```sh
23+# 1. Скопируйте .env.example в .env и поставьте токен бота от @BotFather:
24+cp .env.example .env
25+# отредактируйте TELOXIDE_TOKEN
26+
27+# 2. Запустите бота:
28+cargo run --release
29+```
30+
31+При первом запуске:
32+- автоматически создаётся `bot.db` (SQLite);
33+- применяются миграции из `migrations/`;
34+- бот начинает long-polling Telegram.
35+
36+## Переменные окружения
37+
38+| Переменная | Значение по умолчанию (см. `.env.example`) |
39+|-------------------|----------------------------------------------------|
40+| `TELOXIDE_TOKEN` | токен бота от @BotFather |
41+| `DATABASE_URL` | `sqlite://bot.db?mode=rwc` |
42+| `EVENTS_FILE` | `questionnaire/abfi-1/events.yaml` |
43+| `DATA_DIR` | `questionnaire/abfi-1/data` |
44+| `RUST_LOG` | `info,personality_traits_bot=debug,teloxide=info` |
45+
46+## Структура хранения
47+
48+- `users` — `telegram_id`, `email`, `username`, `first_name`, `registered_at`.
49+- `course_sessions` — одна активная сессия на пользователя; `finished_at IS NULL` ⇒ курс идёт.
50+- `scheduled_messages` — очередь отложенной отправки (фоновый шедулер раз в 10 c забирает `send_after <= now`).
51+- `button_events` — журнал всех нажатий: `message_id`, `button_id`, `action`, `event_target`, `pressed_at`.
52+
53+## Поток выполнения
54+
55+1. Пользователь пишет `/start` → бот просит email.
56+2. После сохранения email бот отправляет первое сообщение из yaml (`id: start`) с кнопкой «Начать курс».
57+3. Нажатие кнопки → создаётся сессия, в `scheduled_messages` ставится следующее событие.
58+4. Шедулер каждые 10 секунд забирает «созревшие» сообщения, отправляет их, обновляет `current_index`.
59+5. Если у отправленного события нет кнопок (например, `day_1_end`), шедулер сразу планирует следующее по порядку с его `delay_minutes`.
60+6. Если кнопка ведёт на несуществующий `event` (например, опечатка `message_1` вместо `practice_1`), используется fallback — следующее сообщение по порядку.
61+
62+## Известные нюансы yaml
63+
64+- `event: message_1` в `day_1` — опечатка, по факту это `practice_1`. Обрабатывается fallback'ом.
65+- `id: day 4` (с пробелом) парсится, но не достижим по id. Обработчик переходит к нему через «next in order».
66+- `foo.mp4` в `start` отсутствует — будет warning в логах.
67+
68+## Отладка
69+
70+Поднять уровень логирования:
71+
72+```sh
73+RUST_LOG=debug cargo run
74+```
ac.mdnew file+11
@@ -0,0 +1,11 @@
1+Авторизация должна осуществляться через номер с указанием почты (с этой же почты участники должны проходить опросы)
2+Должна быть кнопка “Начать курс” в самом начале, а также кнопка “Начать курс заново” – после того, как он уже был начат.
3+Отсчет времени происходит от начала курса (первичного или повторного) ИЛИ от момента нажатия на предыдущую кнопку
4+Бот должен отправлять сообщения с определенной периодичностью, которая будет указана в json
5+После каждого текстового сообщения (day_) должна быть кнопка “Начать практику”
6+После каждой практики (practice_) должна быть кнопка “Завершить практику”
7+Необходимо собирать события нажатий на кнопки по каждому пользователю (а именно – дата и время)
8+
9+В папке questionnaire находятся материалы для бота.
10+В папке events.yaml находятся текстовки для бота.
11+В папкe Data залиты визуальные материалы, указанные в events.yaml.
migrations/0001_init.sqlnew file+47
@@ -0,0 +1,47 @@
1+CREATE TABLE IF NOT EXISTS users (
2+ telegram_id INTEGER PRIMARY KEY,
3+ email TEXT NOT NULL,
4+ username TEXT,
5+ first_name TEXT,
6+ registered_at TEXT NOT NULL
7+);
8+
9+CREATE TABLE IF NOT EXISTS course_sessions (
10+ id INTEGER PRIMARY KEY AUTOINCREMENT,
11+ telegram_id INTEGER NOT NULL,
12+ started_at TEXT NOT NULL,
13+ finished_at TEXT,
14+ current_index INTEGER NOT NULL DEFAULT 0,
15+ last_action_at TEXT NOT NULL,
16+ FOREIGN KEY (telegram_id) REFERENCES users(telegram_id)
17+);
18+
19+CREATE INDEX IF NOT EXISTS idx_sessions_active
20+ ON course_sessions(telegram_id) WHERE finished_at IS NULL;
21+
22+CREATE TABLE IF NOT EXISTS scheduled_messages (
23+ id INTEGER PRIMARY KEY AUTOINCREMENT,
24+ session_id INTEGER NOT NULL,
25+ telegram_id INTEGER NOT NULL,
26+ message_id TEXT NOT NULL,
27+ send_after TEXT NOT NULL,
28+ sent_at TEXT,
29+ FOREIGN KEY (session_id) REFERENCES course_sessions(id)
30+);
31+
32+CREATE INDEX IF NOT EXISTS idx_pending_scheduled
33+ ON scheduled_messages(send_after) WHERE sent_at IS NULL;
34+
35+CREATE TABLE IF NOT EXISTS button_events (
36+ id INTEGER PRIMARY KEY AUTOINCREMENT,
37+ telegram_id INTEGER NOT NULL,
38+ session_id INTEGER,
39+ message_id TEXT NOT NULL,
40+ button_id TEXT NOT NULL,
41+ action TEXT NOT NULL,
42+ event_target TEXT,
43+ pressed_at TEXT NOT NULL
44+);
45+
46+CREATE INDEX IF NOT EXISTS idx_button_events_user
47+ ON button_events(telegram_id, pressed_at);
questionnaire/abfi-1/centralkeys.yamlnew file+365
@@ -0,0 +1,365 @@
1+messages:
2+- id: start
3+ text: |
4+ Добро пожаловать на практикум по снижению тревожности! Практикум основан на доказательных техниках, проверенных большим количеством исследований и показавший свою эффективность в прошлых запусках. В этом практикуме вам будет предложено:
5+ - две лекции
6+ - 8 заданий с подробным описанием их научной эффективности
7+ - текстовые и аудиоматериалы для выполнения заданий
8+ - напоминания для закрепления освоенных навыков
9+
10+ Просьба, пожалуйста, авторизоваться по почте и указывать эту же почту при прохождении анкет — так мы сможем идентифицировать вас. Обещаем строгую конфиденциальность!
11+
12+ По всем вопросам вы можете обращаться к @vv_penner. Приятного вам путешествия в мир осознанности!
13+ file:
14+ - 1.2.png
15+ buttons:
16+ - id: 1
17+ text: Начать курс
18+ action: start_course
19+ event: day_1
20+ delay_minutes: 0
21+- id: day_1
22+ text: |
23+ Состояния тревоги, хронического стресса и эмоционального выгорания оказывают значительное негативное воздействие на психическое и физическое здоровье, нарушая нашу способность адекватной эмоциональной регуляции и снижая качество жизни. Эти состояния характеризуются активацией автономных когнитивных процессов, направленных на постоянное планирование, анализ и самоконтроль, что приводит к отстранению от настоящего момента и утрате переживания его положительных аспектов.
24+
25+ Научные исследования подтверждают, что хронический стресс является ключевым фактором, способствующим развитию синдрома эмоционального выгорания, который рассматривается как комплексное состояние физического, когнитивного и эмоционального истощения организма. Это истощение развивается под влиянием как внешних (например, неблагоприятные условия рабочей среды), так и внутренних (например, перфекционизм, особенности личности) факторов. Последствия выгорания не ограничиваются сферой психического здоровья; они включают повышение риска развития сердечно-сосудистых заболеваний и ускоренное биологическое старение организма.
26+
27+ В качестве эффективного научно обоснованного вмешательства для преодоления данных состояний рекомендуется практика осознанности (mindfulness). Она направлена на десинхронизацию автоматических когнитивных циклов и восстановление внимания к настоящему моменту. Регулярная практика помогает вернуть контроль над вниманием, улучшить эмоциональную регуляцию и восстановить способность переживать положительные эмоции.
28+
29+ Сегодня мы предлагаем вам практику "Ешьте изюм осознанно"
30+ file:
31+ - 2.2.png
32+ buttons:
33+ - id: 1
34+ text: Начать практику
35+ action: start_course
36+ event: practice_1
37+ delay_minutes: 0
38+- id: practice_1
39+ text: |
40+ Техника “Ешьте изюм осознанно”
41+ Вам понадобится несколько изюминок (или чего угодно!) для этой техники. Сядьте удобно там, где есть хорошее освещение и вас не будут беспокоить. Затем медленно настройте себя на прохождение практики
42+ file:
43+ - Ешьте изюм осознанно.2.pdf
44+ buttons:
45+ - id: 1
46+ text: Завершить практику
47+ action: start_course
48+ event: day_1_end
49+ delay_minutes: 0
50+- id: day_1_end
51+ text: |
52+ Поздравляем вас с прохождением первой практики! Как вы оцениваете этот опыт? Что вы ощущали? Чем этот опыт отличается от обычного опыта употребления пищи?
53+ Для закрепления навыка предлагаем вам проделывать это в течение нескольких ближайших дней. До встречи завтра!
54+ delay_minutes: 10
55+ - id: day_2
56+ text: |
57+ Осознанность — это научно обоснованный подход к улучшению психического здоровья, основанный на внимании к настоящему моменту без оценки. Исследования показывают, что практики осознанности способствуют снижению тревожности и депрессии, улучшению эмоциональной регуляции и повышению устойчивости к стрессу.
58+
59+ Научно доказано, что осознанность работает как через сложные нейрокогнитивные механизмы (усиление саморегуляции, переработку эмоций), так и через прямое воздействие на биологические маркеры стресса и настроения. Регулярная практика способствует повышению общего благополучия и качества жизни за счет освоения новых, более конструктивных навыков реагирования.
60+
61+ Именно поэтому сегодня мы предлагаем вам расширить практику, о которой рассказывали вчера!
62+ file:
63+ - 2.3.png
64+ buttons:
65+ - id: 1
66+ text: Начать практику
67+ action: start_course
68+ event: practice_2
69+ delay_minutes: 1140
70+- id: practice_2
71+ text: |
72+ Выберите одно повседневное действие, которое выполняете каждый день, и решите, что на этой неделе, насколько сможете, привнесете в каждый его элемент частичку намеренной и мягкой осознанности, как вы это делали с изюмом. Лучше это делать с одним и тем же действием каждый день в течение всей недели.
73+ file:
74+ - 2.4.png
75+ buttons:
76+ - id: 1
77+ text: Завершить практику
78+ action: start_course
79+ event: day_2_end
80+ delay_minutes: 0
81+- id: day_2_end
82+ text: |
83+ Как вы себя чувствуете после практики? Осознайте свои ощущения, проанализируйте их, закрепите этот опыт повторением практики. До встречи завтра!
84+ delay_minutes: 10
85+- id: day_3
86+ text: |
87+ Доброго вам дня!
88+ Как вы себя чувствуете после наших первых практик?
89+
90+ Предлагаем вам сегодня повторить любую из пройденных практик. Продолжаем развивать навык осознанности – у вас обязательно получится!
91+ delay_minutes: 1140
92+- id: day_4
93+ text: |
94+ Доброго вам дня!
95+ Работа на "автопилоте" часто сопровождается захватом сознания потоком мыслей — будь то планирование, воспоминания или мечты. Этот бесконечный цикл самокопания (руминации) является значимым фактором риска развития тревожных расстройств и депрессии.
96+
97+ Ключевым механизмом, с помощью которого осознанность помогает выйти из этого состояния, является развитие децентрации (decentering). Это метакогнитивный процесс, при котором человек учится воспринимать свои мысли и эмоции не как абсолютную реальность, с которой необходимо идентифицироваться, а как временные психические события, проходящие через сознание.
98+
99+ Вместо того чтобы сливаться с негативной мыслью (например, "Я неудачник"), практика осознанности учит замечать её: "Я замечаю, что у меня возникает мысль о неудаче". Это создает пространство между наблюдателем и мыслью, позволяя выйти из поглощающего потока руминации.
100+
101+ Таким образом, осознанность не пытается подавить мысли силой воли, а меняет наше отношение к ним. Это позволяет прервать автоматические циклы, вернуться в настоящее и восстановить контакт с реальностью через прямое переживание — видение, слышание, ощущение.
102+
103+ Сегодня мы предлагаем практику “Другой способ познания”
104+ file:
105+ - 2.5.png
106+ buttons:
107+ - id: 1
108+ text: Начать практику
109+ action: start_course
110+ event: practice_2
111+ delay_minutes: 1140
112+- id: practice_3
113+ text: |
114+ Функциональная МРТ и статистические исследования выявили, что практика осознанности приводит к:
115+ – Снижению связи между медиальной префронтальной корой и миндалевидным телом, что уменьшает эмоциональную реактивность на триггеры.
116+ – Усилению активности передней поясной коры, отвечающей за контроль внимания и поведения.
117+
118+ Это нейропластическое изменение позволяет нам дистанцироваться от когнитивных содержаний. Вместо автоматической идентификации с мыслью («Я неудачник») формируется мета-когнитивная позиция («Я наблюдаю мысль о том, что я неудачник»).
119+ Рандомизированные контролируемые испытания демонстрируют, что обучение навыку децентрации мыслей снижает частоту рецидивов депрессии на 43–50% по сравнению со стандартной терапией, особенно у пациентов с тремя и более предыдущими эпизодами.
120+
121+ Успешной практики!
122+ file:
123+ - 2.6.png
124+ buttons:
125+ - id: 1
126+ text: Завершить практику
127+ action: start_course
128+ event: day_4_end
129+ delay_minutes: 0
130+- id: day_4_end
131+ text: |
132+ Поздравляем с прохождением третьей практики!
133+
134+ Что вы заметили, когда подключались к ощущениям в своем теле? Что больше всего поразило вас в различии между двумя способами познания – думать о своем теле или поместить свое внимание внутрь телесных ощущений?
135+
136+ До встречи завтра!
137+ delay_minutes: 0
138+- id: day_5
139+ text: |
140+ Доброго дня!
141+ Практики осознанности переключают мозг с режима «анализ и решение проблем» на режим «непосредственного опыта». Нейробиологически это означает снижение активности зон, отвечающих за постоянный мысленный шум и руминацию, и активацию областей, обрабатывающих прямые сенсорные ощущения. В результате «громкий» критический ум уступает место более тихому, интуитивному состоянию мета-осведомленности, позволяющему наблюдать за мыслями и чувствами без автоматической реакции.
142+ Для закрепления этого навыка мы предлагаем вам упражнение «Встреча на улице».
143+ Успешной практики!
144+ file:
145+ - 2.7.png
146+ buttons:
147+ - id: 1
148+ text: Начать практику
149+ action: start_course
150+ event: practice_4
151+ delay_minutes: 1140
152+- id: practice_4
153+ text: |
154+ Данная техника - практическая тренировка переключения внимания в реальной среде: при столкновении с внешними событиями или людьми вы сознательно останавливаете автоматический внутренний диалог и переводите фокус на непосредственные ощущения (звуки, свет, тактильные чувства). Такая практика укрепляет способность мозга быстро выходить из режима «думания» в режим «бытия», снижая эмоциональную реактивность и повышая устойчивость к стрессу.
155+ file:
156+ - 2.8.png
157+ buttons:
158+ - id: 1
159+ text: Завершить практику
160+ action: start_course
161+ event: day_5_end
162+ delay_minutes: 0
163+- id: day_5_end
164+ text: |
165+ Поздравляем с прохождением четвертой практики!
166+
167+ Научная психология утверждает, что мысли не являются отражением объективной реальности, а представляют собой временные ментальные события, возникающие в сознании. Эмоциональные реакции человека определяются не самим событием, а интерпретацией, которую он этому событию придает. Например, одна и та же ситуация (друг не ответил на приветствие) может вызвать у человека в плохом настроении мысли "Я никому не нужен", ведущие к грусти, а у человека в хорошем настроении — нейтральную мысль "Возможно, он меня не увидел", не вызывающую негативных эмоций.
168+ Этот процесс создает замкнутый круг: наше настроение влияет на интерпретацию событий, а последующие эмоции укрепляют исходное настроение.
169+
170+ Практики осознанности помогает разорвать этот порочный круг. Они обучают децентрации — способности наблюдать за своими мыслями и чувствами как за проходящими событиями, а не идентифицировать себя с ними, тем самым помогая разорвать порочный круг.
171+
172+ До встречи завтра!
173+ delay_minutes: 0
174+- id: day_6
175+ text: |
176+ Доброго вам дня!
177+ Как вы себя чувствуете после предложенных нами практик?
178+
179+ Предлагаем вам сегодня повторить любую из пройденных практик. Продолжаем развивать навык осознанности – у вас обязательно получится!
180+ delay_minutes: 1140
181+- id: day_7
182+ text: |
183+ Доброго дня!
184+
185+ Научные исследования подтверждают, что блуждание ума в прошлое и будущее — это не метафора, а обозначение конкретного когнитивного процесса, известного как "ментальное путешествие во времени" (mental time travel). Этот процесс лежит в основе эпизодической памяти и планирования будущего.
186+ Он активируется как при воспоминании прошлых событий, так и при построении симуляций будущего . Именно такая фоновая работа и приводит к тому, что мы "теряемся" в сожалениях или тревогах, уходя из настоящего момента.
187+
188+ Практики осознанности действуют как противоположный механизм. Они направляет внимание на прямые сенсорные ощущения (дыхание, звуки, прикосновения), активируя сенсорные и проприоцептивные области мозга. Это позволяет вернуться к переживанию реальности "здесь и сейчас", где и происходит жизнь.
189+
190+ Сегодня мы предлагаем вам трехминутную медитативную передышку.
191+ file:
192+ - 2.9.png
193+ buttons:
194+ - id: 1
195+ text: Начать практику
196+ action: start_course
197+ event: practice_5
198+ delay_minutes: 1140
199+- id: practice_5
200+ text: |
201+ «Трехминутная медитативная передышка» представляет собой краткосрочную интервенцию на основе осознанности, разработанную для оперативного переключения когнитивного режима с автоматического «режима выполнения» на адаптивный «режим бытия». С точки зрения нейробиологии, эта практика действует как «когнитивный переключатель», прерывая автоматические петли руминации и эмоциональной реактивности, которые поддерживаются гиперактивностью сети пассивного режима работы мозга и миндалевидного тела.
202+ file:
203+ - 2.10.png
204+ buttons:
205+ - id: 1
206+ text: Завершить практику
207+ action: start_course
208+ event: day_7_end
209+ delay_minutes: 0
210+- id: day_7_end
211+ text: |
212+ Поздравляем с прохождением пятой практики!
213+ Что вы сделали после того, как заметили, что ваши мысли блуждают?
214+
215+ Не ругайте себя за это. В случае обнаружения отклонения внимания от объекта медитации (феномен блуждания ума), критически важно исключить самокритику и негативную эмоциональную оценку данного события, так как подобные реакции активируют дополнительные стрессовые контуры и усиливают когнитивную нагрузку. Вместо этого следует применить стратегию мягкого, безоценочного возвращения фокуса внимания к исходному объекту концентрации. Именно это и представляет собой фундаментальный элемент тренировки мета-когнитивного контроля и нейропластичности.
216+ До встречи завтра!
217+ delay_minutes: 0
218+- id: day_8
219+ text: |
220+ Как ваше самочувствие сегодня?
221+ Практика отслеживания негативных эмоций работает через механизм мета-когнитивной осведомленности. Исследования показывают, что вербализация и фиксация эмоций (т.н. «аффективная маркировка») снижают активность миндалевидного тела и помогают префронтальной коре лучше регулировать эмоциональный ответ.
222+
223+ Сегодня мы предлагаем опробовать одну из таких практик – “Календарь неприятных переживаний”.
224+ file:
225+ - 2.11.png
226+ buttons:
227+ - id: 1
228+ text: Начать практику
229+ action: start_course
230+ event: practice_6
231+ delay_minutes: 1140
232+- id: practice_6
233+ text: |
234+ Хотите по-новому взглянуть на неприятные переживания?
235+ Попробуйте разложить их на части: заметьте чувство, мысли, телесные ощущения — и отдельно от всего этого — свою реакцию на них. Осознанность учит видеть дискомфорт без оценки, как наблюдатель. Это даёт пространство: вы перестаете сливаться с эмоцией и учитесь встречать трудное не борясь, а присутствуя.
236+ Хорошей вам практики!
237+ file:
238+ - 2.12.png
239+ - СЮДА ДОК
240+ buttons:
241+ - id: 1
242+ text: Завершить практику
243+ action: start_course
244+ event: day_8_end
245+ delay_minutes: 0
246+- id: day_8_end
247+ text: |
248+ Как вы себя чувствуете после практики?
249+ Исследования подтверждают, что негативные настроения поддерживаются двумя ключевыми когнитивными процессами:
250+
251+ - Руминация — повторяющийся поток мыслей об анализе своего состояния; не решает проблему, а синергетически усиливает негативные эмоции и углубляет депрессивное состояние, искажая восприятие стресса.
252+
253+ - Слияние — идентификация мыслей с реальностью; мысль «Я слаб» воспринимается не как ментальное событие, а как объективная истина, что автоматически усиливает страдание.
254+
255+ Практики осознанности разрывают этот цикл: они снижают руминацию и развивают децентрирование — способность наблюдать за мыслями как за проходящими событиями, а не сливаться с ними.
256+
257+ До встречи завтра!
258+ delay_minutes: 0
259+- id: day_9
260+ text: |
261+ Доброго вам дня!
262+ Как вы себя чувствуете после предложенных нами практик?
263+
264+ Предлагаем вам сегодня повторить любую из пройденных практик. Исследования показывают, что поведенческая активация способствует закреплению новых навыков - продолжайте в том же духе!
265+ delay_minutes: 1140
266+- id: day_10
267+ text: |
268+ Доброго дня! Сегодня мы хотели бы поговорить о таком феномене, как аверсия.
269+
270+ В психологии "аверсия" к внутреннему опыту — это склонность испытывать сильное отторжение к негативным эмоциям, мыслям и телесным ощущениям, что приводит к попыткам их избежать, подавить или устранить. Этот механизм является ключевым в поддержании состояний тревоги, депрессии и других эмоциональных расстройств.
271+
272+ Научные исследования показывают, что стратегии избегания, основанные на аверсии, являются малоадаптивными. Хотя они могут временно снизить уровень дистресса, в долгосрочной перспективе они только усугубляют страдания. Попытки избежать неприятных переживаний (например, переключение внимания, подавление, отвлечение) препятствуют привыканию — естественному процессу, при котором интенсивность эмоций снижается при их спокойном переживании.
273+
274+ Таким образом, аверсия создает порочный круг: исходное неприятное чувство вызывает желание избавиться от него, а сама попытка избегания поддерживает и усиливает это чувство, не давая ему естественным образом пройти 13.
275+
276+ Практики осознанности предлагают противоположный путь — экспозиционную стратегию, основанную на принятии и внимательном наблюдении за переживаниями без сопротивления.
277+
278+ Сегодня мы предлагаем вам практику “Контрольный список негативных мыслей”
279+ file:
280+ - 2.13.png
281+ - СЮДА ДОК
282+ buttons:
283+ - id: 1
284+ text: Начать практику
285+ action: start_course
286+ event: practice_7
287+ delay_minutes: 1140
288+- id: practice_7
289+ text: |
290+ Вместо того, чтобы избегать, мы попробуем направить внимание на дискомфорт с любопытством. Такой подход способствует когнитивной переработке и долгосрочному снижению страданий. Следовательно, главная цель практики — не избавиться от неприятных состояний, а изменить к ним свое отношение, став "экспертом" в механизмах своей аверсии.
291+
292+ Желаем успехов!
293+ file:
294+ - СЮДА ДОК
295+ buttons:
296+ - id: 1
297+ text: Завершить практику
298+ action: start_course
299+ event: day_10_end
300+ delay_minutes: 0
301+- id: day_10_end
302+ text: |
303+ Как вы себя чувствуете после практики?
304+
305+ Когда мы сможем относиться к негативным мыслям и депрессивным чувствам как к тому, чем они на самом деле являются, - симптомами, а не истиной, - нам не нужно будет воспринимать их как относящиеся к себе, и мы с меньшей вероятностью будем реагировать на них с аверсией.
306+
307+ До встречи завтра!
308+ delay_minutes: 0
309+- id: day_11
310+ text: |
311+ Доброго вам дня! Как вы сегодня? Хотим рассказать вам о том, что является альтернативой аверсии – о практику осознанного исследования неприятных переживаний.
312+
313+ Практика осознанного исследования неприятных переживаний – это терапевтический подход, основанный на механизмах экспозиции и эмоциональной регуляции. В отличие от аверсии, этот метод предполагает внимательное наблюдение за неприятными ощущениями, эмоциями и мыслями в настоящем моменте без попытки их подавить. Такой процесс помогает выявить индивидуальные паттерны реакции на стресс — например, мышечное напряжение, учащенное дыхание или автоматические негативные мысли.
314+
315+ Исследования показывают, что осознанное внимание к аверсивным переживаниям снижает избегание и повышает толерантность к дистрессу, что особенно важно при тревожных расстройствах. Нейробиологические данные также свидетельствуют о том, что mindfulness-практики изменяют механизмы эмоциональной регуляции: снижается активность миндалины, связанной со страхом и тревогой, и усиливается работа областей мозга, отвечающих за осознанное восприятие и контроль эмоций.
316+
317+ Сегодня предлагаем вам практику, которая поможет развить осознанное отношение к своим переживаниям – медитация на осознанность дыхания, тела, звуков и безусловное осознание
318+ file:
319+ - 2.14.png
320+ buttons:
321+ - id: 1
322+ text: Начать практику
323+ action: start_course
324+ event: practice_8
325+ delay_minutes: 1140
326+- id: practice_8
327+ text: |
328+ Осознанное исследование не устраняет неприятные переживания, а меняет отношение к ним. Развитие метакогнитивной осознанности создает пространство между переживанием и реакцией, что способствует снижению эмоциональной реактивности и улучшению психологической адаптации
329+ Желаем успехов!
330+ file:
331+ - СЮДА ДОК
332+ buttons:
333+ - id: 1
334+ text: Завершить практику
335+ action: start_course
336+ event: day_11_end
337+ delay_minutes: 0
338+- id: day_11_end
339+ text: |
340+ Как вам практика?
341+
342+ Осознанное исследование не борется с аверсией, а трансформирует к ней отношение. Фиксация наблюдений и метка "Это аверсия" развивает метакогнитивную осознанность, создавая пространство для выбора более адаптивной реакции, что в конечном итоге приводит к снижению эмоциональной реактивности и улучшению психического здоровья.
343+
344+ До встречи завтра!
345+ delay_minutes: 0
346+- id: day_12
347+ text: |
348+ Доброго вам дня!
349+ Как вы себя чувствуете после предложенных нами практик?
350+
351+ Предлагаем вам сегодня повторить любую из пройденных практик. Вера в достижение цели - залог успеха!
352+ delay_minutes: 1140
353+- id: day_13
354+ text: |
355+ Доброго вам дня!
356+ СЮДА ССЫЛКУ НА ОПРОС Сегодня мы предлагаем пройти промежуточное тестирование - оно поможет вам отследить свой прогресс по завершении курса. Пожалуйста, авторизуйтесь по той же почтой, что была указана в данном боте.
357+ buttons:
358+ - id: 1
359+ text: Завершить прохождение опроса,
360+ action: start_course
361+ event: day_13_end
362+ delay_minutes: 1140
363+- id: day_13_end
364+ text: |
365+ Благодарим за прохождение опроса! До встречи завтра!
questionnaire/abfi-1/data/1.2.pngnew file

Binary file not shown.

questionnaire/abfi-1/data/2.1.pngnew file

Binary file not shown.

questionnaire/abfi-1/data/2.10.pngnew file

Binary file not shown.

questionnaire/abfi-1/data/2.11.pngnew file

Binary file not shown.

questionnaire/abfi-1/data/2.12.pngnew file

Binary file not shown.

questionnaire/abfi-1/data/2.13.pngnew file

Binary file not shown.

questionnaire/abfi-1/data/2.14.pngnew file

Binary file not shown.

questionnaire/abfi-1/data/2.2.pngnew file

Binary file not shown.

questionnaire/abfi-1/data/2.3.pngnew file

Binary file not shown.

questionnaire/abfi-1/data/2.4.pngnew file

Binary file not shown.

questionnaire/abfi-1/data/2.5.pngnew file

Binary file not shown.

questionnaire/abfi-1/data/2.6.pngnew file

Binary file not shown.

questionnaire/abfi-1/data/2.7.pngnew file

Binary file not shown.

questionnaire/abfi-1/data/2.8.pngnew file

Binary file not shown.

questionnaire/abfi-1/data/2.9.pngnew file

Binary file not shown.

questionnaire/abfi-1/data/9.pngnew file

Binary file not shown.

questionnaire/abfi-1/data/Контрольный список негативных мыслей.pdfnew file

Binary file not shown.

questionnaire/abfi-1/events.yaml+42−4
@@ -177,7 +177,12 @@ messages:
177177 delay_minutes: 1140
178178- id: practice_6
179179 text: |
180− Используйте следующие вопросы, чтобы сосредоточить свое осознание на деталях переживания, когда оно происходит.
180+ Используйте следующие вопросы, чтобы сосредоточить свое осознание на деталях негативного переживания, когда оно происходит:
181+ - Что вы переживали?
182+ - Что вы ощущали в своем теле?
183+ - Какие чувства и настроения возникали?
184+ - Какие мысли появлялись?
185+ - Какие мысли об этом сейчас?
181186 Запишите свои мысли слово в слово, если они отражают то, что вы переживаете, или опишите любые образы, возникающие перед вашими глазами; обратите внимание, где именно в теле вы испытывали какие-то ощущения, и какими они были. Используйте сам процесс записи как возможность осознавать мысли, возникающие в голове, когда вы это пишете.
182187 file:
183188 - 8.png
@@ -190,10 +195,43 @@ messages:
190195 text: |
191196 Попробуйте заметить способы, которыми ваше мышление питает порочный круг, усиливая несчастные настроения. Часто истории, которые мы рассказываем себе о своих неприятных переживаниях, - мысли, которые вызваны ими, - оживляют и поддерживают наши страдания.
192197 Например, мы можем говорить себе: "Я не должен так себя чувствовать. Почему я так глуп и слаб?" Или мы можем задаваться вопросом: "Что, если это будет продолжаться?" И тогда мы почувствуем себя еще хуже. У нас есть автоматическая тенденция реагировать на неприятные ощущения желанием избавиться от них или избежать их. Это "нежелание'; или аверсия, сама по себе чувствуется как нечто неприятное.
193− Дальше поговорим о том, как можно реагировать иначе.
198+ Дальше поговорим о том, как можно реагировать иначе. До встречи!
194199 delay_minutes: 10
195−
196−
200+- id: day_9
201+ text: |
202+ Доброго дня!
203+ Прежде, чем перейти к изучению новых способов реагирования на негативные переживания, предлагаем вам закрепить один из освоенных нами навыков. Повторите любое задание сегодня.
204+ До встречи!
205+ delay_minutes: 1140
206+- id: day_10
207+ text: |
208+ Доброго дня!
209+ Как ваше самочувствие сегодня?
210+ Мы можем реагировать на неприятные чувства и неудобные состояния разными способами. Зачастую же мы просто стремимся избежать этого опыта.
211+ Аверсия - это стремление скрыться, избавиться,отключиться или уничтожить то, что мы воспринимаем как неприятное. Это та сила, которая стоит за управляемым действием, удерживает нас в таких негативных состояниях, как депрессия, тревога, гнев и стресс. На карточке представлен механизм аверсии.
212+ Главная цель практики на этой неделе – начать избавляться от привычки избегать и отключаться от неприятных переживаний. Способ сделать это - стать экспертом в том, как аверсия влияет на вас. Предлагаем вам упражнение “Контрольный список негативных мыслей”
213+ file:
214+ - 9.png
215+ buttons:
216+ - id: 1
217+ text: Начать практику
218+ action: start_course
219+ event: practice_7
220+ delay_minutes: 1140
221+- id: practice_7
222+ text: |
223+ Вспомните тот период, когда вы переживали тяжелые состояния. В этой таблице собраны мысли, которые могли возникать тогда.
224+ Отметьте (✓) в столбце А те, что возникали у вас.
225+ Далее оцените, насколько вы верили в каждую из них в пик тяжелого состояния — от 0 («не верил(а) вообще») до 10 («верил(а) полностью») в столбце Б.
226+ Теперь вспомните время, когда вам было хорошо. Пересмотрите те же мысли и поставьте оценки от 0 до 10 в столбце В — насколько вы в них верили, будучи в здравом состоянии.
227+ Это упражнение помогает увидеть: негатив искажает реальность. То, что в тяжёлый период кажется истиной, на самом деле — лишь её тень
228+ file:
229+ - Контрольный список негативных мыслей.pdf
230+ buttons:
231+ - id: 1
232+ text: Завершить практику
233+ action: start_course
234+ event: day_10_end
197235
198236
199237
src/config.rsnew file+25
@@ -0,0 +1,25 @@
1+use std::path::PathBuf;
2+
3+use anyhow::{Context, Result};
4+
5+#[derive(Debug, Clone)]
6+pub struct Config {
7+ pub database_url: String,
8+ pub events_file: PathBuf,
9+ pub data_dir: PathBuf,
10+}
11+
12+impl Config {
13+ pub fn from_env() -> Result<Self> {
14+ Ok(Self {
15+ database_url: std::env::var("DATABASE_URL")
16+ .context("DATABASE_URL must be set (see .env.example)")?,
17+ events_file: PathBuf::from(
18+ std::env::var("EVENTS_FILE").context("EVENTS_FILE must be set")?,
19+ ),
20+ data_dir: PathBuf::from(
21+ std::env::var("DATA_DIR").context("DATA_DIR must be set")?,
22+ ),
23+ })
24+ }
25+}
src/db.rsnew file+239
@@ -0,0 +1,239 @@
1+use std::str::FromStr;
2+
3+use anyhow::{Context, Result};
4+use chrono::{DateTime, Utc};
5+use sqlx::sqlite::{SqliteConnectOptions, SqlitePoolOptions};
6+use sqlx::SqlitePool;
7+
8+pub type Pool = SqlitePool;
9+
10+pub async fn connect(url: &str) -> Result<Pool> {
11+ let opts = SqliteConnectOptions::from_str(url)?.create_if_missing(true);
12+ let pool = SqlitePoolOptions::new()
13+ .max_connections(5)
14+ .connect_with(opts)
15+ .await?;
16+ sqlx::migrate!("./migrations")
17+ .run(&pool)
18+ .await
19+ .context("run migrations")?;
20+ Ok(pool)
21+}
22+
23+#[derive(Debug)]
24+#[allow(dead_code)]
25+pub struct User {
26+ pub telegram_id: i64,
27+ pub email: String,
28+}
29+
30+pub async fn get_user(pool: &Pool, tg_id: i64) -> Result<Option<User>> {
31+ let row: Option<(i64, String)> =
32+ sqlx::query_as("SELECT telegram_id, email FROM users WHERE telegram_id = ?")
33+ .bind(tg_id)
34+ .fetch_optional(pool)
35+ .await?;
36+ Ok(row.map(|(telegram_id, email)| User { telegram_id, email }))
37+}
38+
39+pub async fn upsert_user(
40+ pool: &Pool,
41+ tg_id: i64,
42+ email: &str,
43+ username: Option<&str>,
44+ first_name: Option<&str>,
45+) -> Result<()> {
46+ sqlx::query(
47+ "INSERT INTO users (telegram_id, email, username, first_name, registered_at) \
48+ VALUES (?, ?, ?, ?, ?) \
49+ ON CONFLICT(telegram_id) DO UPDATE SET \
50+ email = excluded.email, \
51+ username = excluded.username, \
52+ first_name = excluded.first_name",
53+ )
54+ .bind(tg_id)
55+ .bind(email)
56+ .bind(username)
57+ .bind(first_name)
58+ .bind(Utc::now().to_rfc3339())
59+ .execute(pool)
60+ .await?;
61+ Ok(())
62+}
63+
64+#[derive(Debug, Clone)]
65+#[allow(dead_code)]
66+pub struct Session {
67+ pub id: i64,
68+ pub telegram_id: i64,
69+ pub current_index: i64,
70+}
71+
72+pub async fn active_session(pool: &Pool, tg_id: i64) -> Result<Option<Session>> {
73+ let row: Option<(i64, i64, i64)> = sqlx::query_as(
74+ "SELECT id, telegram_id, current_index FROM course_sessions \
75+ WHERE telegram_id = ? AND finished_at IS NULL \
76+ ORDER BY id DESC LIMIT 1",
77+ )
78+ .bind(tg_id)
79+ .fetch_optional(pool)
80+ .await?;
81+ Ok(row.map(|(id, telegram_id, current_index)| Session {
82+ id,
83+ telegram_id,
84+ current_index,
85+ }))
86+}
87+
88+pub async fn start_session(pool: &Pool, tg_id: i64) -> Result<Session> {
89+ let now = Utc::now().to_rfc3339();
90+ let id = sqlx::query(
91+ "INSERT INTO course_sessions (telegram_id, started_at, current_index, last_action_at) \
92+ VALUES (?, ?, ?, ?)",
93+ )
94+ .bind(tg_id)
95+ .bind(&now)
96+ .bind(0_i64)
97+ .bind(&now)
98+ .execute(pool)
99+ .await?
100+ .last_insert_rowid();
101+ Ok(Session {
102+ id,
103+ telegram_id: tg_id,
104+ current_index: 0,
105+ })
106+}
107+
108+pub async fn finish_all_active(pool: &Pool, tg_id: i64) -> Result<()> {
109+ let now = Utc::now().to_rfc3339();
110+ sqlx::query(
111+ "UPDATE scheduled_messages SET sent_at = ? \
112+ WHERE sent_at IS NULL AND session_id IN \
113+ (SELECT id FROM course_sessions WHERE telegram_id = ? AND finished_at IS NULL)",
114+ )
115+ .bind(&now)
116+ .bind(tg_id)
117+ .execute(pool)
118+ .await?;
119+ sqlx::query(
120+ "UPDATE course_sessions SET finished_at = ? \
121+ WHERE telegram_id = ? AND finished_at IS NULL",
122+ )
123+ .bind(&now)
124+ .bind(tg_id)
125+ .execute(pool)
126+ .await?;
127+ Ok(())
128+}
129+
130+pub async fn finish_session(pool: &Pool, session_id: i64) -> Result<()> {
131+ let now = Utc::now().to_rfc3339();
132+ sqlx::query("UPDATE scheduled_messages SET sent_at = ? WHERE session_id = ? AND sent_at IS NULL")
133+ .bind(&now)
134+ .bind(session_id)
135+ .execute(pool)
136+ .await?;
137+ sqlx::query("UPDATE course_sessions SET finished_at = ? WHERE id = ?")
138+ .bind(&now)
139+ .bind(session_id)
140+ .execute(pool)
141+ .await?;
142+ Ok(())
143+}
144+
145+pub async fn update_session_index(pool: &Pool, session_id: i64, index: i64) -> Result<()> {
146+ sqlx::query("UPDATE course_sessions SET current_index = ?, last_action_at = ? WHERE id = ?")
147+ .bind(index)
148+ .bind(Utc::now().to_rfc3339())
149+ .bind(session_id)
150+ .execute(pool)
151+ .await?;
152+ Ok(())
153+}
154+
155+pub async fn record_button(
156+ pool: &Pool,
157+ tg_id: i64,
158+ session_id: Option<i64>,
159+ message_id: &str,
160+ button_id: &str,
161+ action: &str,
162+ event_target: Option<&str>,
163+) -> Result<()> {
164+ sqlx::query(
165+ "INSERT INTO button_events \
166+ (telegram_id, session_id, message_id, button_id, action, event_target, pressed_at) \
167+ VALUES (?, ?, ?, ?, ?, ?, ?)",
168+ )
169+ .bind(tg_id)
170+ .bind(session_id)
171+ .bind(message_id)
172+ .bind(button_id)
173+ .bind(action)
174+ .bind(event_target)
175+ .bind(Utc::now().to_rfc3339())
176+ .execute(pool)
177+ .await?;
178+ Ok(())
179+}
180+
181+pub async fn schedule(
182+ pool: &Pool,
183+ session_id: i64,
184+ tg_id: i64,
185+ message_id: &str,
186+ send_after: DateTime<Utc>,
187+) -> Result<i64> {
188+ let id = sqlx::query(
189+ "INSERT INTO scheduled_messages (session_id, telegram_id, message_id, send_after) \
190+ VALUES (?, ?, ?, ?)",
191+ )
192+ .bind(session_id)
193+ .bind(tg_id)
194+ .bind(message_id)
195+ .bind(send_after.to_rfc3339())
196+ .execute(pool)
197+ .await?
198+ .last_insert_rowid();
199+ Ok(id)
200+}
201+
202+#[derive(Debug)]
203+pub struct ScheduledMessage {
204+ pub id: i64,
205+ pub session_id: i64,
206+ pub telegram_id: i64,
207+ pub message_id: String,
208+}
209+
210+pub async fn due_messages(pool: &Pool, now: DateTime<Utc>) -> Result<Vec<ScheduledMessage>> {
211+ let rows: Vec<(i64, i64, i64, String)> = sqlx::query_as(
212+ "SELECT id, session_id, telegram_id, message_id FROM scheduled_messages \
213+ WHERE sent_at IS NULL AND send_after <= ? \
214+ ORDER BY send_after ASC LIMIT 50",
215+ )
216+ .bind(now.to_rfc3339())
217+ .fetch_all(pool)
218+ .await?;
219+ Ok(rows
220+ .into_iter()
221+ .map(
222+ |(id, session_id, telegram_id, message_id)| ScheduledMessage {
223+ id,
224+ session_id,
225+ telegram_id,
226+ message_id,
227+ },
228+ )
229+ .collect())
230+}
231+
232+pub async fn mark_sent(pool: &Pool, id: i64) -> Result<()> {
233+ sqlx::query("UPDATE scheduled_messages SET sent_at = ? WHERE id = ?")
234+ .bind(Utc::now().to_rfc3339())
235+ .bind(id)
236+ .execute(pool)
237+ .await?;
238+ Ok(())
239+}
src/events.rsnew file+161
@@ -0,0 +1,161 @@
1+use std::collections::HashMap;
2+use std::path::{Path, PathBuf};
3+
4+use anyhow::{Context, Result};
5+use serde::Deserialize;
6+
7+#[derive(Debug, Deserialize)]
8+struct RawFile {
9+ messages: Vec<RawEvent>,
10+}
11+
12+#[derive(Debug, Deserialize)]
13+struct RawEvent {
14+ id: String,
15+ #[serde(default)]
16+ text: String,
17+ #[serde(default)]
18+ file: Vec<Option<String>>,
19+ #[serde(default)]
20+ buttons: Vec<RawButton>,
21+ #[serde(default)]
22+ delay_minutes: i64,
23+}
24+
25+#[derive(Debug, Deserialize)]
26+struct RawButton {
27+ #[serde(default)]
28+ id: serde_yaml::Value,
29+ text: String,
30+ #[serde(default)]
31+ action: String,
32+ #[serde(default)]
33+ event: String,
34+}
35+
36+#[derive(Debug, Clone)]
37+pub struct Event {
38+ pub id: String,
39+ pub text: String,
40+ pub files: Vec<String>,
41+ pub buttons: Vec<Button>,
42+ pub delay_minutes: i64,
43+}
44+
45+#[derive(Debug, Clone)]
46+pub struct Button {
47+ pub id: String,
48+ pub text: String,
49+ pub action: String,
50+ pub event: String,
51+}
52+
53+#[derive(Debug)]
54+pub struct Course {
55+ pub events: Vec<Event>,
56+ by_id: HashMap<String, usize>,
57+ data_dir: PathBuf,
58+}
59+
60+impl Course {
61+ pub fn load(yaml_path: &Path, data_dir: &Path) -> Result<Self> {
62+ let content = std::fs::read_to_string(yaml_path)
63+ .with_context(|| format!("read {}", yaml_path.display()))?;
64+ let raw: RawFile = serde_yaml::from_str(&content)
65+ .with_context(|| format!("parse {}", yaml_path.display()))?;
66+
67+ let mut events = Vec::with_capacity(raw.messages.len());
68+ let mut by_id = HashMap::new();
69+ for (idx, e) in raw.messages.into_iter().enumerate() {
70+ let id = e.id.trim().to_string();
71+ let files: Vec<String> = e
72+ .file
73+ .into_iter()
74+ .flatten()
75+ .map(|f| f.trim().to_string())
76+ .filter(|f| !f.is_empty())
77+ .collect();
78+ let buttons: Vec<Button> = e
79+ .buttons
80+ .into_iter()
81+ .enumerate()
82+ .map(|(i, b)| Button {
83+ id: button_id_to_string(&b.id, i),
84+ text: b.text,
85+ action: b.action,
86+ event: b.event.trim().to_string(),
87+ })
88+ .collect();
89+
90+ if by_id.insert(id.clone(), idx).is_some() {
91+ tracing::warn!("duplicate event id in yaml: {}", id);
92+ }
93+ events.push(Event {
94+ id,
95+ text: e.text,
96+ files,
97+ buttons,
98+ delay_minutes: e.delay_minutes,
99+ });
100+ }
101+ Ok(Self {
102+ events,
103+ by_id,
104+ data_dir: data_dir.to_path_buf(),
105+ })
106+ }
107+
108+ pub fn first(&self) -> Option<&Event> {
109+ self.events.first()
110+ }
111+
112+ pub fn at(&self, idx: usize) -> Option<&Event> {
113+ self.events.get(idx)
114+ }
115+
116+ pub fn index_of(&self, id: &str) -> Option<usize> {
117+ self.by_id.get(id.trim()).copied()
118+ }
119+
120+ pub fn next_after_button(&self, current_idx: usize, button_event: &str) -> Option<usize> {
121+ let target = button_event.trim();
122+ if !target.is_empty() {
123+ if let Some(&idx) = self.by_id.get(target) {
124+ return Some(idx);
125+ }
126+ tracing::warn!(
127+ "button.event '{}' not found in yaml, falling back to next in order",
128+ target
129+ );
130+ }
131+ self.next_in_order(current_idx)
132+ }
133+
134+ pub fn next_in_order(&self, current_idx: usize) -> Option<usize> {
135+ let next = current_idx + 1;
136+ if next < self.events.len() {
137+ Some(next)
138+ } else {
139+ None
140+ }
141+ }
142+
143+ pub fn resolve_file(&self, name: &str) -> Option<PathBuf> {
144+ let p = self.data_dir.join(name);
145+ if p.exists() {
146+ Some(p)
147+ } else {
148+ tracing::warn!("attached file not found: {}", p.display());
149+ None
150+ }
151+ }
152+}
153+
154+fn button_id_to_string(v: &serde_yaml::Value, fallback: usize) -> String {
155+ match v {
156+ serde_yaml::Value::String(s) => s.clone(),
157+ serde_yaml::Value::Number(n) => n.to_string(),
158+ serde_yaml::Value::Bool(b) => b.to_string(),
159+ _ => fallback.to_string(),
160+ }
161+}
src/handlers.rsnew file+390
@@ -0,0 +1,390 @@
1+use std::sync::Arc;
2+
3+use anyhow::Result;
4+use chrono::Utc;
5+use once_cell::sync::Lazy;
6+use regex::Regex;
7+use teloxide::prelude::*;
8+use teloxide::types::{InlineKeyboardButton, InlineKeyboardMarkup, MaybeInaccessibleMessage};
9+
10+use crate::db::{self, Pool};
11+use crate::events::Course;
12+use crate::media;
13+
14+static EMAIL_RE: Lazy<Regex> = Lazy::new(|| {
15+ Regex::new(r"(?i)^[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,}$").unwrap()
16+});
17+
18+pub async fn on_message(
19+ bot: Bot,
20+ msg: Message,
21+ pool: Pool,
22+ course: Arc<Course>,
23+) -> Result<(), teloxide::RequestError> {
24+ if let Err(e) = on_message_inner(&bot, &msg, &pool, &course).await {
25+ tracing::error!("message handler error: {:?}", e);
26+ if let Some(user) = msg.from.as_ref() {
27+ let _ = bot
28+ .send_message(
29+ ChatId(user.id.0 as i64),
30+ "Что-то пошло не так. Попробуйте /start.",
31+ )
32+ .await;
33+ }
34+ }
35+ Ok(())
36+}
37+
38+async fn on_message_inner(bot: &Bot, msg: &Message, pool: &Pool, course: &Course) -> Result<()> {
39+ let Some(user) = msg.from.as_ref() else {
40+ return Ok(());
41+ };
42+ let tg_id = user.id.0 as i64;
43+ let chat = msg.chat.id;
44+ let text = msg.text().unwrap_or("").trim();
45+ let username = user.username.as_deref();
46+ let first_name = Some(user.first_name.as_str());
47+
48+ match text {
49+ "/start" => handle_start(bot, chat, tg_id, pool, course).await,
50+ "/restart" => handle_restart(bot, chat, tg_id, pool, course).await,
51+ "/help" => {
52+ bot.send_message(
53+ chat,
54+ "Команды:\n\
55+ /start — начать или продолжить курс\n\
56+ /restart — начать курс заново",
57+ )
58+ .await?;
59+ Ok(())
60+ }
61+ _ => {
62+ if db::get_user(pool, tg_id).await?.is_none() {
63+ handle_email_input(bot, chat, tg_id, username, first_name, text, pool, course).await
64+ } else {
65+ bot.send_message(
66+ chat,
67+ "Пожалуйста, используйте кнопки. Команды: /start, /restart, /help.",
68+ )
69+ .await?;
70+ Ok(())
71+ }
72+ }
73+ }
74+}
75+
76+async fn handle_email_input(
77+ bot: &Bot,
78+ chat: ChatId,
79+ tg_id: i64,
80+ username: Option<&str>,
81+ first_name: Option<&str>,
82+ text: &str,
83+ pool: &Pool,
84+ course: &Course,
85+) -> Result<()> {
86+ if EMAIL_RE.is_match(text) {
87+ db::upsert_user(pool, tg_id, text, username, first_name).await?;
88+ bot.send_message(
89+ chat,
90+ format!(
91+ "Email сохранён: {}\n\
92+ Пожалуйста, указывайте этот же email при заполнении анкет.",
93+ text
94+ ),
95+ )
96+ .await?;
97+ send_intro(bot, chat, course, false).await?;
98+ } else {
99+ bot.send_message(
100+ chat,
101+ "Похоже, это не email. Пожалуйста, отправьте корректный email одним сообщением \
102+ (например, name@example.com).",
103+ )
104+ .await?;
105+ }
106+ Ok(())
107+}
108+
109+async fn handle_start(
110+ bot: &Bot,
111+ chat: ChatId,
112+ tg_id: i64,
113+ pool: &Pool,
114+ course: &Course,
115+) -> Result<()> {
116+ if db::get_user(pool, tg_id).await?.is_none() {
117+ bot.send_message(
118+ chat,
119+ "Здравствуйте! Чтобы зарегистрироваться, пожалуйста, отправьте ваш email \
120+ одним сообщением. Этот же email нужно будет указывать при заполнении анкет.",
121+ )
122+ .await?;
123+ return Ok(());
124+ }
125+ let has_session = db::active_session(pool, tg_id).await?.is_some();
126+ send_intro(bot, chat, course, has_session).await?;
127+ Ok(())
128+}
129+
130+async fn handle_restart(
131+ bot: &Bot,
132+ chat: ChatId,
133+ tg_id: i64,
134+ pool: &Pool,
135+ course: &Course,
136+) -> Result<()> {
137+ if db::get_user(pool, tg_id).await?.is_none() {
138+ return handle_start(bot, chat, tg_id, pool, course).await;
139+ }
140+ send_intro(bot, chat, course, true).await?;
141+ Ok(())
142+}
143+
144+async fn send_intro(bot: &Bot, chat: ChatId, course: &Course, restart_mode: bool) -> Result<()> {
145+ let Some(intro) = course.first() else {
146+ bot.send_message(chat, "Курс пуст. Свяжитесь с администратором.")
147+ .await?;
148+ return Ok(());
149+ };
150+
151+ let files: Vec<_> = intro
152+ .files
153+ .iter()
154+ .filter_map(|f| course.resolve_file(f))
155+ .collect();
156+
157+ let (button_text, callback_data) = if restart_mode {
158+ ("Начать курс заново".to_string(), "crs:restart".to_string())
159+ } else {
160+ let label = intro
161+ .buttons
162+ .first()
163+ .map(|b| b.text.clone())
164+ .unwrap_or_else(|| "Начать курс".to_string());
165+ (label, "crs:start".to_string())
166+ };
167+
168+ let kb = InlineKeyboardMarkup::new(vec![vec![InlineKeyboardButton::callback(
169+ button_text,
170+ callback_data,
171+ )]]);
172+
173+ let text = if intro.text.trim().is_empty() {
174+ "Добро пожаловать!"
175+ } else {
176+ intro.text.as_str()
177+ };
178+ media::send_event_message(bot, chat, &files, text, Some(kb)).await;
179+ Ok(())
180+}
181+
182+pub async fn on_callback(
183+ bot: Bot,
184+ q: CallbackQuery,
185+ pool: Pool,
186+ course: Arc<Course>,
187+) -> Result<(), teloxide::RequestError> {
188+ if let Err(e) = on_callback_inner(&bot, &q, &pool, &course).await {
189+ tracing::error!("callback handler error: {:?}", e);
190+ }
191+ let _ = bot.answer_callback_query(q.id.clone()).await;
192+ Ok(())
193+}
194+
195+async fn on_callback_inner(
196+ bot: &Bot,
197+ q: &CallbackQuery,
198+ pool: &Pool,
199+ course: &Course,
200+) -> Result<()> {
201+ let tg_id = q.from.id.0 as i64;
202+ let chat = ChatId(tg_id);
203+ let data = q.data.clone().unwrap_or_default();
204+
205+ if let Some(msg) = q.message.as_ref() {
206+ let (cid, mid) = match msg {
207+ MaybeInaccessibleMessage::Regular(m) => (m.chat.id, m.id),
208+ MaybeInaccessibleMessage::Inaccessible(im) => (im.chat.id, im.message_id),
209+ };
210+ let _ = bot.edit_message_reply_markup(cid, mid).await;
211+ }
212+
213+ match data.as_str() {
214+ "crs:start" => handle_course_start(bot, chat, tg_id, pool, course, false).await,
215+ "crs:restart" => handle_course_start(bot, chat, tg_id, pool, course, true).await,
216+ d if d.starts_with("btn:") => handle_event_button(bot, chat, tg_id, pool, course, d).await,
217+ _ => {
218+ tracing::warn!("unknown callback data: {}", data);
219+ Ok(())
220+ }
221+ }
222+}
223+
224+async fn handle_course_start(
225+ bot: &Bot,
226+ chat: ChatId,
227+ tg_id: i64,
228+ pool: &Pool,
229+ course: &Course,
230+ restart: bool,
231+) -> Result<()> {
232+ if db::get_user(pool, tg_id).await?.is_none() {
233+ bot.send_message(chat, "Пожалуйста, сначала отправьте ваш email одним сообщением.")
234+ .await?;
235+ return Ok(());
236+ }
237+
238+ db::finish_all_active(pool, tg_id).await?;
239+ let session = db::start_session(pool, tg_id).await?;
240+
241+ let intro = course
242+ .first()
243+ .ok_or_else(|| anyhow::anyhow!("course has no events"))?;
244+ let button = intro.buttons.first();
245+ let target = button.map(|b| b.event.as_str()).unwrap_or("");
246+
247+ db::record_button(
248+ pool,
249+ tg_id,
250+ Some(session.id),
251+ &intro.id,
252+ button.map(|b| b.id.as_str()).unwrap_or("0"),
253+ if restart { "restart_course" } else { "start_course" },
254+ if target.is_empty() { None } else { Some(target) },
255+ )
256+ .await?;
257+
258+ if let Some(next_idx) = course.next_after_button(0, target) {
259+ schedule_next(pool, &session, course, next_idx, Utc::now()).await?;
260+ if restart {
261+ bot.send_message(chat, "Курс начат заново. Скоро вы получите первое сообщение.")
262+ .await?;
263+ } else {
264+ bot.send_message(chat, "Курс начат. Скоро вы получите первое сообщение.")
265+ .await?;
266+ }
267+ } else {
268+ db::finish_session(pool, session.id).await?;
269+ bot.send_message(chat, "В курсе нет следующих сообщений после стартового.")
270+ .await?;
271+ }
272+ Ok(())
273+}
274+
275+async fn handle_event_button(
276+ bot: &Bot,
277+ chat: ChatId,
278+ tg_id: i64,
279+ pool: &Pool,
280+ course: &Course,
281+ data: &str,
282+) -> Result<()> {
283+ let parts: Vec<&str> = data.splitn(3, ':').collect();
284+ if parts.len() != 3 {
285+ return Ok(());
286+ }
287+ let event_idx: usize = parts[1].parse().unwrap_or(usize::MAX);
288+ let btn_idx: usize = parts[2].parse().unwrap_or(usize::MAX);
289+
290+ let Some(session) = db::active_session(pool, tg_id).await? else {
291+ bot.send_message(chat, "Нет активного курса. Нажмите /start, чтобы начать.")
292+ .await?;
293+ return Ok(());
294+ };
295+
296+ let Some(event) = course.at(event_idx) else {
297+ tracing::warn!("callback references unknown event idx {}", event_idx);
298+ return Ok(());
299+ };
300+ let Some(button) = event.buttons.get(btn_idx) else {
301+ tracing::warn!(
302+ "callback references unknown button idx {} on event {}",
303+ btn_idx,
304+ event.id
305+ );
306+ return Ok(());
307+ };
308+
309+ let target = button.event.as_str();
310+ db::record_button(
311+ pool,
312+ tg_id,
313+ Some(session.id),
314+ &event.id,
315+ &button.id,
316+ &button.action,
317+ if target.is_empty() { None } else { Some(target) },
318+ )
319+ .await?;
320+
321+ let Some(next_idx) = course.next_after_button(event_idx, target) else {
322+ db::finish_session(pool, session.id).await?;
323+ bot.send_message(chat, "Поздравляем, вы прошли весь практикум!")
324+ .await?;
325+ return Ok(());
326+ };
327+
328+ schedule_next(pool, &session, course, next_idx, Utc::now()).await?;
329+ Ok(())
330+}
331+
332+async fn schedule_next(
333+ pool: &Pool,
334+ session: &db::Session,
335+ course: &Course,
336+ next_idx: usize,
337+ trigger_at: chrono::DateTime<Utc>,
338+) -> Result<()> {
339+ let next = course
340+ .at(next_idx)
341+ .ok_or_else(|| anyhow::anyhow!("invalid next_idx {}", next_idx))?;
342+ let send_after = trigger_at + chrono::Duration::minutes(next.delay_minutes.max(0));
343+ db::schedule(pool, session.id, session.telegram_id, &next.id, send_after).await?;
344+ tracing::info!(
345+ "scheduled '{}' (idx {}) for user {} at {} (delay {} min)",
346+ next.id,
347+ next_idx,
348+ session.telegram_id,
349+ send_after,
350+ next.delay_minutes
351+ );
352+ Ok(())
353+}
354+
355+pub async fn send_event_with_keyboard(
356+ bot: &Bot,
357+ chat: ChatId,
358+ course: &Course,
359+ event_idx: usize,
360+) -> Result<()> {
361+ let event = course
362+ .at(event_idx)
363+ .ok_or_else(|| anyhow::anyhow!("event idx {} not found", event_idx))?;
364+
365+ let files: Vec<_> = event
366+ .files
367+ .iter()
368+ .filter_map(|f| course.resolve_file(f))
369+ .collect();
370+
371+ let rows: Vec<Vec<InlineKeyboardButton>> = event
372+ .buttons
373+ .iter()
374+ .enumerate()
375+ .map(|(i, b)| {
376+ vec![InlineKeyboardButton::callback(
377+ b.text.clone(),
378+ format!("btn:{}:{}", event_idx, i),
379+ )]
380+ })
381+ .collect();
382+
383+ let keyboard = if rows.is_empty() {
384+ None
385+ } else {
386+ Some(InlineKeyboardMarkup::new(rows))
387+ };
388+ media::send_event_message(bot, chat, &files, &event.text, keyboard).await;
389+ Ok(())
390+}
src/main.rsnew file+52
@@ -0,0 +1,52 @@
1+mod config;
2+mod db;
3+mod events;
4+mod handlers;
5+mod media;
6+mod scheduler;
7+
8+use std::sync::Arc;
9+
10+use teloxide::prelude::*;
11+use tracing_subscriber::EnvFilter;
12+
13+#[tokio::main]
14+async fn main() -> anyhow::Result<()> {
15+ let _ = dotenvy::dotenv();
16+ tracing_subscriber::fmt()
17+ .with_env_filter(
18+ EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")),
19+ )
20+ .init();
21+
22+ let cfg = config::Config::from_env()?;
23+ tracing::info!("loading events from {}", cfg.events_file.display());
24+ let course = Arc::new(events::Course::load(&cfg.events_file, &cfg.data_dir)?);
25+ tracing::info!("loaded {} events", course.events.len());
26+
27+ let pool = db::connect(&cfg.database_url).await?;
28+ tracing::info!("database ready at {}", cfg.database_url);
29+
30+ let bot = Bot::from_env();
31+
32+ let sched_bot = bot.clone();
33+ let sched_pool = pool.clone();
34+ let sched_course = course.clone();
35+ tokio::spawn(async move {
36+ scheduler::run(sched_bot, sched_pool, sched_course).await;
37+ });
38+
39+ let handler = dptree::entry()
40+ .branch(Update::filter_message().endpoint(handlers::on_message))
41+ .branch(Update::filter_callback_query().endpoint(handlers::on_callback));
42+
43+ Dispatcher::builder(bot, handler)
44+ .dependencies(dptree::deps![pool, course])
45+ .default_handler(|_| async {})
46+ .enable_ctrlc_handler()
47+ .build()
48+ .dispatch()
49+ .await;
50+
51+ Ok(())
52+}
src/media.rsnew file+157
@@ -0,0 +1,157 @@
1+use std::path::{Path, PathBuf};
2+
3+use teloxide::prelude::*;
4+use teloxide::types::{InlineKeyboardMarkup, InputFile};
5+
6+const CAPTION_LIMIT: usize = 1024;
7+
8+pub async fn send_event_message(
9+ bot: &Bot,
10+ chat: ChatId,
11+ files: &[PathBuf],
12+ text: &str,
13+ keyboard: Option<InlineKeyboardMarkup>,
14+) {
15+ if files.is_empty() {
16+ send_text(bot, chat, text, keyboard).await;
17+ return;
18+ }
19+
20+ let fits_in_caption = text.chars().count() <= CAPTION_LIMIT;
21+ if files.len() == 1 && fits_in_caption {
22+ match send_one_with_caption(bot, chat, &files[0], text, keyboard.clone()).await {
23+ Ok(()) => return,
24+ Err(e) => tracing::warn!(
25+ "failed to send {} with caption: {}; falling back to separate messages",
26+ files[0].display(),
27+ e
28+ ),
29+ }
30+ }
31+
32+ for path in files {
33+ send_one(bot, chat, path).await;
34+ }
35+ send_text(bot, chat, text, keyboard).await;
36+}
37+
38+async fn send_text(bot: &Bot, chat: ChatId, text: &str, keyboard: Option<InlineKeyboardMarkup>) {
39+ let text = if text.trim().is_empty() {
40+ "(пустое сообщение)"
41+ } else {
42+ text
43+ };
44+ let result = match keyboard {
45+ Some(kb) => bot.send_message(chat, text).reply_markup(kb).await.map(|_| ()),
46+ None => bot.send_message(chat, text).await.map(|_| ()),
47+ };
48+ if let Err(e) = result {
49+ tracing::error!("failed to send text to {}: {}", chat, e);
50+ }
51+}
52+
53+async fn send_one_with_caption(
54+ bot: &Bot,
55+ chat: ChatId,
56+ path: &Path,
57+ caption: &str,
58+ keyboard: Option<InlineKeyboardMarkup>,
59+) -> Result<(), teloxide::RequestError> {
60+ let ext = path
61+ .extension()
62+ .and_then(|e| e.to_str())
63+ .unwrap_or("")
64+ .to_ascii_lowercase();
65+
66+ match ext.as_str() {
67+ "jpg" | "jpeg" | "png" | "webp" => {
68+ let req = bot
69+ .send_photo(chat, InputFile::file(path))
70+ .caption(caption);
71+ match keyboard {
72+ Some(kb) => req.reply_markup(kb).await?,
73+ None => req.await?,
74+ };
75+ }
76+ "mp4" => {
77+ let req = bot
78+ .send_video(chat, InputFile::file(path))
79+ .caption(caption);
80+ match keyboard {
81+ Some(kb) => req.reply_markup(kb).await?,
82+ None => req.await?,
83+ };
84+ }
85+ "mp3" | "m4a" => {
86+ let req = bot
87+ .send_audio(chat, InputFile::file(path))
88+ .caption(caption);
89+ match keyboard {
90+ Some(kb) => req.reply_markup(kb).await?,
91+ None => req.await?,
92+ };
93+ }
94+ "ogg" => {
95+ let req = bot
96+ .send_voice(chat, InputFile::file(path))
97+ .caption(caption);
98+ match keyboard {
99+ Some(kb) => req.reply_markup(kb).await?,
100+ None => req.await?,
101+ };
102+ }
103+ _ => {
104+ let req = bot
105+ .send_document(chat, InputFile::file(path))
106+ .caption(caption);
107+ match keyboard {
108+ Some(kb) => req.reply_markup(kb).await?,
109+ None => req.await?,
110+ };
111+ }
112+ }
113+ Ok(())
114+}
115+
116+async fn send_one(bot: &Bot, chat: ChatId, path: &Path) {
117+ let ext = path
118+ .extension()
119+ .and_then(|e| e.to_str())
120+ .unwrap_or("")
121+ .to_ascii_lowercase();
122+
123+ let result = match ext.as_str() {
124+ "jpg" | "jpeg" | "png" | "webp" => bot
125+ .send_photo(chat, InputFile::file(path))
126+ .await
127+ .map(|_| ()),
128+ "mp4" => bot
129+ .send_video(chat, InputFile::file(path))
130+ .await
131+ .map(|_| ()),
132+ "mp3" | "m4a" => bot
133+ .send_audio(chat, InputFile::file(path))
134+ .await
135+ .map(|_| ()),
136+ "ogg" => bot
137+ .send_voice(chat, InputFile::file(path))
138+ .await
139+ .map(|_| ()),
140+ _ => bot
141+ .send_document(chat, InputFile::file(path))
142+ .await
143+ .map(|_| ()),
144+ };
145+
146+ if let Err(e) = result {
147+ tracing::warn!(
148+ "failed to send {} as {}: {}; retrying as document",
149+ path.display(),
150+ ext,
151+ e
152+ );
153+ if let Err(e2) = bot.send_document(chat, InputFile::file(path)).await {
154+ tracing::error!("failed to send {} as document: {}", path.display(), e2);
155+ }
156+ }
157+}
src/scheduler.rsnew file+85
@@ -0,0 +1,85 @@
1+use std::sync::Arc;
2+use std::time::Duration;
3+
4+use anyhow::Result;
5+use chrono::Utc;
6+use teloxide::prelude::*;
7+use tokio::time::sleep;
8+
9+use crate::db::{self, Pool};
10+use crate::events::Course;
11+use crate::handlers;
12+
13+const TICK_INTERVAL: Duration = Duration::from_secs(10);
14+
15+pub async fn run(bot: Bot, pool: Pool, course: Arc<Course>) {
16+ tracing::info!("scheduler started, tick every {:?}", TICK_INTERVAL);
17+ loop {
18+ if let Err(e) = tick(&bot, &pool, &course).await {
19+ tracing::error!("scheduler tick error: {:?}", e);
20+ }
21+ sleep(TICK_INTERVAL).await;
22+ }
23+}
24+
25+async fn tick(bot: &Bot, pool: &Pool, course: &Course) -> Result<()> {
26+ let due = db::due_messages(pool, Utc::now()).await?;
27+ for sm in due {
28+ if let Err(e) = deliver(bot, pool, course, &sm).await {
29+ tracing::error!(
30+ "failed to deliver message id={} user={}: {:?}",
31+ sm.id,
32+ sm.telegram_id,
33+ e
34+ );
35+ }
36+ }
37+ Ok(())
38+}
39+
40+async fn deliver(
41+ bot: &Bot,
42+ pool: &Pool,
43+ course: &Course,
44+ sm: &db::ScheduledMessage,
45+) -> Result<()> {
46+ let Some(idx) = course.index_of(&sm.message_id) else {
47+ tracing::warn!(
48+ "scheduled message id '{}' not found in current yaml; skipping",
49+ sm.message_id
50+ );
51+ db::mark_sent(pool, sm.id).await?;
52+ return Ok(());
53+ };
54+
55+ let chat = ChatId(sm.telegram_id);
56+ handlers::send_event_with_keyboard(bot, chat, course, idx).await?;
57+ db::mark_sent(pool, sm.id).await?;
58+ db::update_session_index(pool, sm.session_id, idx as i64).await?;
59+
60+ let event = course.at(idx).expect("idx valid: just looked it up");
61+ if event.buttons.is_empty() {
62+ match course.next_in_order(idx) {
63+ Some(next_idx) => {
64+ let next = course.at(next_idx).expect("idx valid");
65+ let send_after = Utc::now() + chrono::Duration::minutes(next.delay_minutes.max(0));
66+ db::schedule(pool, sm.session_id, sm.telegram_id, &next.id, send_after).await?;
67+ tracing::info!(
68+ "auto-scheduled '{}' (idx {}) for user {} at {} (delay {} min)",
69+ next.id,
70+ next_idx,
71+ sm.telegram_id,
72+ send_after,
73+ next.delay_minutes
74+ );
75+ }
76+ None => {
77+ db::finish_session(pool, sm.session_id).await?;
78+ let _ = bot
79+ .send_message(chat, "Поздравляем, вы прошли весь практикум!")
80+ .await;
81+ }
82+ }
83+ }
84+ Ok(())
85+}
tests/parse_events.rsnew file+33
@@ -0,0 +1,33 @@
1+use std::path::PathBuf;
2+
3+#[path = "../src/events.rs"]
4+mod events;
5+
6+#[test]
7+fn loads_real_yaml() {
8+ let yaml = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
9+ .join("questionnaire/abfi-1/events.yaml");
10+ let data = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
11+ .join("questionnaire/abfi-1/data");
12+ let course = events::Course::load(&yaml, &data).expect("yaml must parse");
13+ assert!(!course.events.is_empty(), "course must have events");
14+
15+ let start = course.first().expect("has first event");
16+ assert_eq!(start.id, "start");
17+ assert!(
18+ !start.buttons.is_empty(),
19+ "start event must have a button to begin"
20+ );
21+
22+ let day1 = course.index_of("day_1").expect("day_1 present");
23+ assert!(day1 > 0);
24+
25+ let practice1 = course.index_of("practice_1").expect("practice_1 present");
26+ let after_day1 = course
27+ .next_after_button(day1, "message_1")
28+ .expect("fallback should resolve next-in-order even for typo");
29+ assert_eq!(
30+ after_day1, practice1,
31+ "typo 'message_1' must fall back to next-in-order = practice_1"
32+ );
33+}