mirror of
https://github.com/house-of-vanity/desubot.git
synced 2025-07-08 04:54:08 +00:00
Compare commits
88 Commits
Author | SHA1 | Date | |
---|---|---|---|
2fb5b03fbf | |||
55a7dd6a67 | |||
ff6e04988e | |||
7c067065bd | |||
175f056add | |||
20f366572b | |||
e5079fa584 | |||
fdc52b7198 | |||
789b918bab | |||
66a93e85b8 | |||
e13a2688ab | |||
04220703a3 | |||
9518ffd69b | |||
5a6cb37ebb | |||
6c2837a76f | |||
d97eaf4284 | |||
e7e0c6923e | |||
382cc56492 | |||
6f48f116c9 | |||
282efe5be4 | |||
4175fe9029 | |||
f1dc1d0897 | |||
d5879a82b6 | |||
456c887a53 | |||
d8b37b32df | |||
47c68ee432 | |||
da53927288 | |||
ac2be9929a | |||
7432ce6398 | |||
049a3c4987 | |||
0831e3f503 | |||
a0f4c40be0 | |||
1facef6897 | |||
428416a2a3 | |||
77dec205f1 | |||
6c761d7576 | |||
865fd3bbe4 | |||
30bdb23a32 | |||
f97562e9b7 | |||
2d000101c2 | |||
a26d227190 | |||
cc44f0e23b | |||
96df636195 | |||
a48e25800c | |||
36660d384d | |||
0d24976ec2 | |||
6ae3b2af1f | |||
a39f6a8c2a | |||
788c2cbbd4 | |||
9d5e5a3217 | |||
945da05794 | |||
3085d4c450 | |||
4b142ae8ef | |||
8f3cccf01d | |||
3136bce84f | |||
e09047bb5b | |||
5528177e52 | |||
ed32bd9195 | |||
d14aa3beb4 | |||
43e4f26af1 | |||
5d8d3441d2 | |||
34fa54e6f4 | |||
d5b30cc94e | |||
7a66034381 | |||
47906fe22d | |||
ea6d9b55a1 | |||
83a6045b18 | |||
c8c55782ec | |||
a1b272bd40 | |||
ed68dbd4bd | |||
3f00505659 | |||
5a41d4a0b9 | |||
3fd5b124f3 | |||
17442819c4 | |||
7adc629292 | |||
39640139fa | |||
0812c9e371 | |||
f111f54606 | |||
7e17851131 | |||
b674ae5b15 | |||
412c3f313c | |||
1838674cab | |||
30d9d470cd | |||
3236131377 | |||
9aaa8a94f1 | |||
2d43a7d875 | |||
00b15e01b6 | |||
731232804b |
27
.github/workflows/automerge.yml
vendored
Normal file
27
.github/workflows/automerge.yml
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
name: automerge
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types:
|
||||||
|
- labeled
|
||||||
|
- unlabeled
|
||||||
|
- synchronize
|
||||||
|
- opened
|
||||||
|
- edited
|
||||||
|
- ready_for_review
|
||||||
|
- reopened
|
||||||
|
- unlocked
|
||||||
|
pull_request_review:
|
||||||
|
types:
|
||||||
|
- submitted
|
||||||
|
check_suite:
|
||||||
|
types:
|
||||||
|
- completed
|
||||||
|
status: {}
|
||||||
|
jobs:
|
||||||
|
automerge:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: automerge
|
||||||
|
uses: "pascalgn/automerge-action@v0.13.0"
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
36
.github/workflows/build-push.yml
vendored
36
.github/workflows/build-push.yml
vendored
@ -31,6 +31,41 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
name: desubot.exe
|
name: desubot.exe
|
||||||
path: ./target/release/desubot.exe
|
path: ./target/release/desubot.exe
|
||||||
|
build-push-docker:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Branch name
|
||||||
|
id: branch_name
|
||||||
|
run: |
|
||||||
|
echo ::set-output name=SOURCE_NAME::${GITHUB_REF#refs/*/}
|
||||||
|
echo ::set-output name=SOURCE_BRANCH::${GITHUB_REF#refs/heads/}
|
||||||
|
echo ::set-output name=SOURCE_TAG::${GITHUB_REF#refs/tags/}
|
||||||
|
-
|
||||||
|
name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v1
|
||||||
|
-
|
||||||
|
name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v1
|
||||||
|
-
|
||||||
|
name: Login to DockerHub
|
||||||
|
uses: docker/login-action@v1
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
-
|
||||||
|
name: Build and push
|
||||||
|
id: docker_build_latest
|
||||||
|
uses: docker/build-push-action@v2
|
||||||
|
with:
|
||||||
|
push: true
|
||||||
|
tags: ultradesu/desubot:latest
|
||||||
|
-
|
||||||
|
name: Build and push
|
||||||
|
id: docker_build_tag
|
||||||
|
uses: docker/build-push-action@v2
|
||||||
|
with:
|
||||||
|
push: true
|
||||||
|
tags: ultradesu/desubot:${{ steps.branch_name.outputs.SOURCE_TAG }}
|
||||||
|
|
||||||
publish:
|
publish:
|
||||||
name: Publish release
|
name: Publish release
|
||||||
@ -86,5 +121,4 @@ jobs:
|
|||||||
--name ${artifact} \
|
--name ${artifact} \
|
||||||
--file ${artifact}
|
--file ${artifact}
|
||||||
done
|
done
|
||||||
./bin/linux/amd64/github-release upload -u house-of-vanity -r furumi --tag ${{ steps.get_version.outputs.FULL_TAG }} --name arch_linux_furumi-${{ steps.get_version.outputs.VERSION }}-x86_64.pkg.tar.zst --file ./furumi-x86_64.pkg.tar.zst
|
|
||||||
|
|
||||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -6,3 +6,6 @@ memory.sqlite3
|
|||||||
/video
|
/video
|
||||||
/voice
|
/voice
|
||||||
/.idea
|
/.idea
|
||||||
|
Cargo.lock
|
||||||
|
k8s/k8s-deploy.yaml
|
||||||
|
/identifier.sqlite
|
||||||
|
2064
Cargo.lock
generated
2064
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
24
Cargo.toml
24
Cargo.toml
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "desubot"
|
name = "desubot"
|
||||||
version = "0.1.0"
|
version = "0.5.9"
|
||||||
authors = ["AB <ab@hexor.ru>"]
|
authors = ["AB <ab@hexor.ru>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
@ -9,26 +9,34 @@ edition = "2018"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
bytes = "0.5"
|
bytes = "0.5"
|
||||||
tokio = { version = "0.2", features = ["full"]}
|
tokio = { version = "0.2", features = ["full"]}
|
||||||
|
|
||||||
tracing = "0.1.9"
|
tracing = "0.1.9"
|
||||||
tracing-futures = "0.2"
|
tracing-futures = "0.2"
|
||||||
multipart = { version = "0.16", default-features = false, features = ["client"] }
|
multipart = { version = "0.16", default-features = false, features = ["client"] }
|
||||||
|
#telegram-bot = "0.8.0"
|
||||||
telegram-bot = "0.8.0"
|
telegram-bot = { git = "https://github.com/ayrat555/telegram-bot", branch = "ayrat555/api-fixes-10" }
|
||||||
|
silicon = "0.4.0"
|
||||||
hyper = "0.13"
|
hyper = "0.13"
|
||||||
hyper-tls = { version = "0.4", optional = true }
|
hyper-tls = { version = "0.4", optional = true }
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
hyper-rustls = { version = "0.19", optional = true }
|
hyper-rustls = { version = "0.19", optional = true }
|
||||||
|
rusqlite = { version = "0.24.2", features = ["bundled"]}
|
||||||
rusqlite = { version = "0.24.1", features = ["bundled"]}
|
|
||||||
html-escape = "0.2"
|
html-escape = "0.2"
|
||||||
|
regex = "1"
|
||||||
reqwest = "0.10.9"
|
reqwest = "0.10.9"
|
||||||
uuid = { version = "0.8", features = ["v4"] }
|
uuid = { version = "0.8", features = ["v4"] }
|
||||||
sha1 = "*"
|
sha1 = "0.6.0"
|
||||||
env_logger = "0.7"
|
env_logger = "0.7"
|
||||||
log = { version = "^0.4.5", features = ["std"] }
|
log = { version = "^0.4.5", features = ["std"] }
|
||||||
subprocess = "0.2.6"
|
subprocess = "0.2.6"
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
markov = "1.1.0"
|
markov = "1.1.0"
|
||||||
rand = "0.7.3"
|
rand = "0.7.3"
|
||||||
|
mystem = "^0.2"
|
||||||
|
#mystem = { path = "../mystem-rs" }
|
||||||
|
async-trait = "0.1.42"
|
||||||
|
sqlparser = "0.7.0"
|
||||||
|
|
||||||
|
[dependencies.syntect]
|
||||||
|
version = "4.4"
|
||||||
|
default-features = false
|
||||||
|
features = ["parsing", "dump-load", "regex-onig"]
|
||||||
|
14
Dockerfile
Normal file
14
Dockerfile
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# syntax=docker/dockerfile:1
|
||||||
|
|
||||||
|
FROM rust:latest AS builder
|
||||||
|
WORKDIR /desubot
|
||||||
|
ADD ./ /desubot/
|
||||||
|
RUN cargo build --release
|
||||||
|
|
||||||
|
FROM ubuntu:latest
|
||||||
|
WORKDIR /storage
|
||||||
|
COPY --from=builder /desubot/target/release/desubot /usr/bin/
|
||||||
|
COPY mystem /usr/bin/
|
||||||
|
RUN apt update && apt install -y fontconfig openssl ca-certificates && rm -rf /var/lib/apt/lists/*
|
||||||
|
ENTRYPOINT desubot
|
||||||
|
|
15
README
15
README
@ -1,15 +0,0 @@
|
|||||||
Desubot
|
|
||||||
Telegram bot with light group statistic and heavy spy features.
|
|
||||||
|
|
||||||
== Features ==
|
|
||||||
* Collect all the messages sent to group.
|
|
||||||
* Collect all the media sent to group including voice, stickers, video, video notes, documents.
|
|
||||||
* /here command to mention all members.
|
|
||||||
* Alongside with saving whole message bot perform blacklist filter and stemming for every word (only Russian). "Красивую собаку мыли негры" -> "красивый собака мыть негр"
|
|
||||||
* Generate sentences using Markov Chains trained on history with /markov_all.
|
|
||||||
|
|
||||||
== TODO ==
|
|
||||||
* Syntax highlighting for code exported to image.
|
|
||||||
|
|
||||||
== Notes ==
|
|
||||||
* Desubot uses MyStem by Yandex for word stemming and assume that mystem binary is available in PATH. On Windows it may be placed on working directory. Both Linux and Windows mystem binary is in repo.
|
|
24
README.md
Normal file
24
README.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
[](https://app.fossa.com/projects/git%2Bgithub.com%2Fhouse-of-vanity%2Fdesubot?ref=badge_shield)
|
||||||
|
|
||||||
|
Desubot
|
||||||
|
Telegram bot with light group statistic and heavy spy features.
|
||||||
|
|
||||||
|
== Features ==
|
||||||
|
* Collect all the messages sent to group.
|
||||||
|
* Collect all the media sent to group including voice, stickers, video, video notes, documents.
|
||||||
|
* /here command to mention all members.
|
||||||
|
* Alongside with saving whole message bot perform blacklist filter and stemming for every word (only Russian). "Красивую собаку мыли негры" -> "красивый собака мыть негр"
|
||||||
|
* Generate sentences using Markov Chains trained on history with /markov_all.
|
||||||
|
* Syntax highlighting for CODE exported to image.
|
||||||
|
|
||||||
|
== Important ==
|
||||||
|
* Desubot uses MyStem by Yandex for word stemming and assume that mystem binary is available in PATH.
|
||||||
|
* ubuntu deps: libssl-dev libsqlite3-dev cmake libfreetype-dev
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## License
|
||||||
|
[](https://app.fossa.com/projects/git%2Bgithub.com%2Fhouse-of-vanity%2Fdesubot?ref=badge_large)
|
||||||
|
|
3
assets/README.md
Normal file
3
assets/README.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Assets
|
||||||
|
|
||||||
|
Silicon uses [bat](https://github.com/sharkdp/bat/tree/master/assets)'s syntax and theme resources.
|
BIN
assets/fonts/Hack-Bold.ttf
Normal file
BIN
assets/fonts/Hack-Bold.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/Hack-BoldItalic.ttf
Normal file
BIN
assets/fonts/Hack-BoldItalic.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/Hack-Italic.ttf
Normal file
BIN
assets/fonts/Hack-Italic.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/Hack-Regular.ttf
Normal file
BIN
assets/fonts/Hack-Regular.ttf
Normal file
Binary file not shown.
43
assets/help_text.rs
Normal file
43
assets/help_text.rs
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) static CODE_HELP: &str = "<b>Code highlighter</b>
|
||||||
|
|
||||||
|
<i>Usage</i>
|
||||||
|
<pre>/code
|
||||||
|
<CODE>
|
||||||
|
#<lang - JS by default> #<theme - Dracula by default></pre>
|
||||||
|
|
||||||
|
Language may be defined by both name and extension - Rust, rs...
|
||||||
|
Max length - 4000
|
||||||
|
|
||||||
|
List of themes:
|
||||||
|
1337
|
||||||
|
DarkNeon
|
||||||
|
Dracula
|
||||||
|
GitHub
|
||||||
|
Monokai_Extended
|
||||||
|
Monokai_Extended_Bright
|
||||||
|
Monokai_Extended_Light
|
||||||
|
Monokai_Extended_Origin
|
||||||
|
Nord
|
||||||
|
OneHalfDark
|
||||||
|
OneHalfLight
|
||||||
|
Solarized_(dark)
|
||||||
|
Solarized_(light)
|
||||||
|
Sublime_Snazzy
|
||||||
|
TwoDark
|
||||||
|
ansi-dark
|
||||||
|
ansi-light
|
||||||
|
base16
|
||||||
|
base16-256
|
||||||
|
gruvbox
|
||||||
|
gruvbox-light
|
||||||
|
gruvbox-white
|
||||||
|
zenburn
|
||||||
|
";
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub static SQL_HELP: &str = "<b>Perform an SQL command</b>
|
||||||
|
<i>* Only one sentence per message.
|
||||||
|
* Only SELECT command.
|
||||||
|
* Max result length is 100 lines. Use LIMIT 100.
|
||||||
|
* SQLITE syntax is available only.</i>";
|
431
assets/stop-words.txt
Normal file
431
assets/stop-words.txt
Normal file
@ -0,0 +1,431 @@
|
|||||||
|
а
|
||||||
|
в
|
||||||
|
г
|
||||||
|
е
|
||||||
|
ж
|
||||||
|
и
|
||||||
|
к
|
||||||
|
м
|
||||||
|
о
|
||||||
|
с
|
||||||
|
т
|
||||||
|
у
|
||||||
|
я
|
||||||
|
чо
|
||||||
|
че
|
||||||
|
ща
|
||||||
|
ваще
|
||||||
|
бы
|
||||||
|
stat
|
||||||
|
вообще
|
||||||
|
ThreadTopBot
|
||||||
|
/stat
|
||||||
|
во
|
||||||
|
вы
|
||||||
|
да
|
||||||
|
до
|
||||||
|
ее
|
||||||
|
ей
|
||||||
|
ею
|
||||||
|
её
|
||||||
|
же
|
||||||
|
за
|
||||||
|
из
|
||||||
|
им
|
||||||
|
их
|
||||||
|
ли
|
||||||
|
мы
|
||||||
|
на
|
||||||
|
не
|
||||||
|
ни
|
||||||
|
но
|
||||||
|
ну
|
||||||
|
нх
|
||||||
|
об
|
||||||
|
он
|
||||||
|
от
|
||||||
|
по
|
||||||
|
со
|
||||||
|
та
|
||||||
|
те
|
||||||
|
то
|
||||||
|
ту
|
||||||
|
ты
|
||||||
|
уж
|
||||||
|
без
|
||||||
|
был
|
||||||
|
вам
|
||||||
|
вас
|
||||||
|
ваш
|
||||||
|
вон
|
||||||
|
вот
|
||||||
|
все
|
||||||
|
всю
|
||||||
|
вся
|
||||||
|
всё
|
||||||
|
где
|
||||||
|
год
|
||||||
|
два
|
||||||
|
две
|
||||||
|
дел
|
||||||
|
для
|
||||||
|
его
|
||||||
|
ему
|
||||||
|
еще
|
||||||
|
ещё
|
||||||
|
или
|
||||||
|
ими
|
||||||
|
имя
|
||||||
|
как
|
||||||
|
кем
|
||||||
|
ком
|
||||||
|
кто
|
||||||
|
лет
|
||||||
|
мне
|
||||||
|
мог
|
||||||
|
мож
|
||||||
|
мои
|
||||||
|
мой
|
||||||
|
мор
|
||||||
|
моя
|
||||||
|
моё
|
||||||
|
над
|
||||||
|
нам
|
||||||
|
нас
|
||||||
|
наш
|
||||||
|
нее
|
||||||
|
ней
|
||||||
|
нем
|
||||||
|
нет
|
||||||
|
нею
|
||||||
|
неё
|
||||||
|
них
|
||||||
|
оба
|
||||||
|
она
|
||||||
|
они
|
||||||
|
оно
|
||||||
|
под
|
||||||
|
пор
|
||||||
|
при
|
||||||
|
про
|
||||||
|
раз
|
||||||
|
сам
|
||||||
|
сих
|
||||||
|
так
|
||||||
|
там
|
||||||
|
тем
|
||||||
|
тех
|
||||||
|
том
|
||||||
|
тот
|
||||||
|
тою
|
||||||
|
три
|
||||||
|
тут
|
||||||
|
уже
|
||||||
|
чем
|
||||||
|
что
|
||||||
|
эта
|
||||||
|
эти
|
||||||
|
это
|
||||||
|
эту
|
||||||
|
алло
|
||||||
|
буду
|
||||||
|
будь
|
||||||
|
бывь
|
||||||
|
была
|
||||||
|
были
|
||||||
|
было
|
||||||
|
быть
|
||||||
|
вами
|
||||||
|
ваша
|
||||||
|
ваше
|
||||||
|
ваши
|
||||||
|
ведь
|
||||||
|
весь
|
||||||
|
вниз
|
||||||
|
всем
|
||||||
|
всех
|
||||||
|
всею
|
||||||
|
года
|
||||||
|
году
|
||||||
|
даже
|
||||||
|
двух
|
||||||
|
день
|
||||||
|
если
|
||||||
|
есть
|
||||||
|
зато
|
||||||
|
кого
|
||||||
|
кому
|
||||||
|
куда
|
||||||
|
лишь
|
||||||
|
люди
|
||||||
|
мало
|
||||||
|
меля
|
||||||
|
меня
|
||||||
|
мимо
|
||||||
|
мира
|
||||||
|
мной
|
||||||
|
мною
|
||||||
|
мочь
|
||||||
|
надо
|
||||||
|
нами
|
||||||
|
наша
|
||||||
|
наше
|
||||||
|
наши
|
||||||
|
него
|
||||||
|
нему
|
||||||
|
ниже
|
||||||
|
ними
|
||||||
|
один
|
||||||
|
пока
|
||||||
|
пора
|
||||||
|
пять
|
||||||
|
рано
|
||||||
|
сама
|
||||||
|
сами
|
||||||
|
само
|
||||||
|
саму
|
||||||
|
свое
|
||||||
|
свои
|
||||||
|
свою
|
||||||
|
себе
|
||||||
|
себя
|
||||||
|
семь
|
||||||
|
стал
|
||||||
|
суть
|
||||||
|
твой
|
||||||
|
твоя
|
||||||
|
твоё
|
||||||
|
тебе
|
||||||
|
тебя
|
||||||
|
теми
|
||||||
|
того
|
||||||
|
тоже
|
||||||
|
тому
|
||||||
|
туда
|
||||||
|
хоть
|
||||||
|
хотя
|
||||||
|
чаще
|
||||||
|
чего
|
||||||
|
чему
|
||||||
|
чтоб
|
||||||
|
чуть
|
||||||
|
этим
|
||||||
|
этих
|
||||||
|
этой
|
||||||
|
этом
|
||||||
|
этот
|
||||||
|
более
|
||||||
|
будем
|
||||||
|
будет
|
||||||
|
будто
|
||||||
|
будут
|
||||||
|
вверх
|
||||||
|
вдали
|
||||||
|
вдруг
|
||||||
|
везде
|
||||||
|
внизу
|
||||||
|
время
|
||||||
|
всего
|
||||||
|
всеми
|
||||||
|
всему
|
||||||
|
всюду
|
||||||
|
давно
|
||||||
|
даром
|
||||||
|
долго
|
||||||
|
друго
|
||||||
|
занят
|
||||||
|
затем
|
||||||
|
зачем
|
||||||
|
здесь
|
||||||
|
иметь
|
||||||
|
какая
|
||||||
|
какой
|
||||||
|
когда
|
||||||
|
кроме
|
||||||
|
лучше
|
||||||
|
между
|
||||||
|
менее
|
||||||
|
много
|
||||||
|
могут
|
||||||
|
может
|
||||||
|
можно
|
||||||
|
можхо
|
||||||
|
назад
|
||||||
|
низко
|
||||||
|
нужно
|
||||||
|
одной
|
||||||
|
около
|
||||||
|
опять
|
||||||
|
очень
|
||||||
|
перед
|
||||||
|
позже
|
||||||
|
после
|
||||||
|
потом
|
||||||
|
почти
|
||||||
|
пятый
|
||||||
|
разве
|
||||||
|
рядом
|
||||||
|
самим
|
||||||
|
самих
|
||||||
|
самой
|
||||||
|
самом
|
||||||
|
своей
|
||||||
|
своих
|
||||||
|
сеаой
|
||||||
|
снова
|
||||||
|
собой
|
||||||
|
собою
|
||||||
|
такая
|
||||||
|
также
|
||||||
|
такие
|
||||||
|
такое
|
||||||
|
такой
|
||||||
|
тобой
|
||||||
|
тобою
|
||||||
|
тогда
|
||||||
|
тысяч
|
||||||
|
уметь
|
||||||
|
часто
|
||||||
|
через
|
||||||
|
чтобы
|
||||||
|
шесть
|
||||||
|
этими
|
||||||
|
этого
|
||||||
|
этому
|
||||||
|
близко
|
||||||
|
больше
|
||||||
|
будете
|
||||||
|
будешь
|
||||||
|
бывает
|
||||||
|
важная
|
||||||
|
важное
|
||||||
|
важные
|
||||||
|
важный
|
||||||
|
вокруг
|
||||||
|
восемь
|
||||||
|
всегда
|
||||||
|
второй
|
||||||
|
далеко
|
||||||
|
дальше
|
||||||
|
девять
|
||||||
|
десять
|
||||||
|
должно
|
||||||
|
другая
|
||||||
|
другие
|
||||||
|
других
|
||||||
|
другое
|
||||||
|
другой
|
||||||
|
занята
|
||||||
|
занято
|
||||||
|
заняты
|
||||||
|
значит
|
||||||
|
именно
|
||||||
|
иногда
|
||||||
|
каждая
|
||||||
|
каждое
|
||||||
|
каждые
|
||||||
|
каждый
|
||||||
|
кругом
|
||||||
|
меньше
|
||||||
|
начала
|
||||||
|
нельзя
|
||||||
|
нибудь
|
||||||
|
никуда
|
||||||
|
ничего
|
||||||
|
обычно
|
||||||
|
однако
|
||||||
|
одного
|
||||||
|
отсюда
|
||||||
|
первый
|
||||||
|
потому
|
||||||
|
почему
|
||||||
|
просто
|
||||||
|
против
|
||||||
|
раньше
|
||||||
|
самими
|
||||||
|
самого
|
||||||
|
самому
|
||||||
|
своего
|
||||||
|
сейчас
|
||||||
|
сказал
|
||||||
|
совсем
|
||||||
|
теперь
|
||||||
|
только
|
||||||
|
третий
|
||||||
|
хорошо
|
||||||
|
хотеть
|
||||||
|
хочешь
|
||||||
|
четыре
|
||||||
|
шестой
|
||||||
|
восьмой
|
||||||
|
впрочем
|
||||||
|
времени
|
||||||
|
говорил
|
||||||
|
говорит
|
||||||
|
девятый
|
||||||
|
десятый
|
||||||
|
кажется
|
||||||
|
конечно
|
||||||
|
которая
|
||||||
|
которой
|
||||||
|
которые
|
||||||
|
который
|
||||||
|
которых
|
||||||
|
наверху
|
||||||
|
наконец
|
||||||
|
недавно
|
||||||
|
немного
|
||||||
|
нередко
|
||||||
|
никогда
|
||||||
|
однажды
|
||||||
|
посреди
|
||||||
|
сегодня
|
||||||
|
седьмой
|
||||||
|
сказала
|
||||||
|
сказать
|
||||||
|
сколько
|
||||||
|
слишком
|
||||||
|
сначала
|
||||||
|
спасибо
|
||||||
|
двадцать
|
||||||
|
довольно
|
||||||
|
которого
|
||||||
|
наиболее
|
||||||
|
недалеко
|
||||||
|
особенно
|
||||||
|
отовсюду
|
||||||
|
двадцатый
|
||||||
|
миллионов
|
||||||
|
несколько
|
||||||
|
прекрасно
|
||||||
|
процентов
|
||||||
|
четвертый
|
||||||
|
двенадцать
|
||||||
|
непрерывно
|
||||||
|
пожалуйста
|
||||||
|
пятнадцать
|
||||||
|
семнадцать
|
||||||
|
тринадцать
|
||||||
|
двенадцатый
|
||||||
|
одиннадцать
|
||||||
|
пятнадцатый
|
||||||
|
семнадцатый
|
||||||
|
тринадцатый
|
||||||
|
шестнадцать
|
||||||
|
восемнадцать
|
||||||
|
девятнадцать
|
||||||
|
одиннадцатый
|
||||||
|
четырнадцать
|
||||||
|
шестнадцатый
|
||||||
|
восемнадцатый
|
||||||
|
девятнадцатый
|
||||||
|
действительно
|
||||||
|
четырнадцатый
|
||||||
|
многочисленная
|
||||||
|
многочисленное
|
||||||
|
многочисленные
|
||||||
|
многочисленный
|
||||||
|
ага
|
||||||
|
делать
|
||||||
|
писать
|
||||||
|
бот
|
21
assets/sync_from_bat.py
Normal file
21
assets/sync_from_bat.py
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# Sync themes and syntaxes from [bat](https://github.com/sharkdp/bat/tree/master/assets)
|
||||||
|
|
||||||
|
import os
|
||||||
|
from glob import glob
|
||||||
|
from shutil import copy
|
||||||
|
|
||||||
|
if not os.path.exists('../../bat'):
|
||||||
|
os.system('git clone https://github.com/sharkdp/bat ../../bat')
|
||||||
|
else:
|
||||||
|
os.system('git -C ../../bat pull')
|
||||||
|
|
||||||
|
for syntax_file in glob('../../bat/assets/syntaxes/**/*.sublime-syntax'):
|
||||||
|
copy(syntax_file, './syntaxes/')
|
||||||
|
|
||||||
|
for theme_file in glob('../../bat/assets/themes/**/*.tmTheme'):
|
||||||
|
copy(theme_file, './themes/')
|
||||||
|
|
||||||
|
os.system('bat cache --build --source . --target .')
|
||||||
|
|
||||||
|
print('Finished.')
|
BIN
assets/syntaxes.bin
Normal file
BIN
assets/syntaxes.bin
Normal file
Binary file not shown.
4
assets/syntaxes/.gitignore
vendored
Normal file
4
assets/syntaxes/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# Ignore everything in this directory
|
||||||
|
*
|
||||||
|
# Except this file
|
||||||
|
!.gitignore
|
BIN
assets/themes.bin
Normal file
BIN
assets/themes.bin
Normal file
Binary file not shown.
5
assets/themes/.gitignore
vendored
Normal file
5
assets/themes/.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# Ignore everything in this directory
|
||||||
|
*
|
||||||
|
# Except this file
|
||||||
|
!.gitignore
|
||||||
|
!Dracula.tmTheme
|
940
assets/themes/Dracula.tmTheme
Normal file
940
assets/themes/Dracula.tmTheme
Normal file
@ -0,0 +1,940 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<!-- Dracula Theme v1.4.3
|
||||||
|
#
|
||||||
|
# https://github.com/dracula/sublime
|
||||||
|
#
|
||||||
|
# Copyright 2013-present, All rights reserved
|
||||||
|
#
|
||||||
|
# Code licensed under the MIT license
|
||||||
|
#
|
||||||
|
# @author Zeno Rocha <hi@zenorocha.com>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Dracula</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>background</key>
|
||||||
|
<string>#282a36</string>
|
||||||
|
<key>caret</key>
|
||||||
|
<string>#f8f8f0</string>
|
||||||
|
<key>block_caret</key>
|
||||||
|
<string>#999a9e</string>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#f8f8f2</string>
|
||||||
|
<key>invisibles</key>
|
||||||
|
<string>#3B3A32</string>
|
||||||
|
<key>lineHighlight</key>
|
||||||
|
<string>#44475a</string>
|
||||||
|
<key>selection</key>
|
||||||
|
<string>#44475a</string>
|
||||||
|
<key>findHighlight</key>
|
||||||
|
<string>#effb7b</string>
|
||||||
|
<key>findHighlightForeground</key>
|
||||||
|
<string>#000000</string>
|
||||||
|
<key>selectionBorder</key>
|
||||||
|
<string>#222218</string>
|
||||||
|
<key>activeGuide</key>
|
||||||
|
<string>#9D550FB0</string>
|
||||||
|
<key>bracketsForeground</key>
|
||||||
|
<string>#F8F8F2A5</string>
|
||||||
|
<key>bracketsOptions</key>
|
||||||
|
<string>underline</string>
|
||||||
|
<key>bracketContentsForeground</key>
|
||||||
|
<string>#F8F8F2A5</string>
|
||||||
|
<key>bracketContentsOptions</key>
|
||||||
|
<string>underline</string>
|
||||||
|
<key>tagsOptions</key>
|
||||||
|
<string>stippled_underline</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Comment</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>comment</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#6272a4</string>
|
||||||
|
<key>fontStyle</key>
|
||||||
|
<string></string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>String</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>string</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#f1fa8c</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Number</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>constant.numeric</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#bd93f9</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Built-in constant</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>constant.language</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#bd93f9</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>User-defined constant</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>constant.character, constant.other</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#bd93f9</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Variable</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>variable</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>fontStyle</key>
|
||||||
|
<string></string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Ruby's @variable</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>variable.other.readwrite.instance</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>fontStyle</key>
|
||||||
|
<string></string>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#ffb86c</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>String interpolation</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>constant.character.escaped, constant.character.escape, string source, string source.ruby</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>fontStyle</key>
|
||||||
|
<string></string>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#ff79c6</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Ruby Regexp</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>source.ruby string.regexp.classic.ruby,source.ruby string.regexp.mod-r.ruby</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>fontStyle</key>
|
||||||
|
<string></string>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#ff5555</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Keyword</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>keyword</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#ff79c6</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Storage</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>storage</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>fontStyle</key>
|
||||||
|
<string></string>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#ff79c6</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Storage type</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>storage.type</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>fontStyle</key>
|
||||||
|
<string>italic</string>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#8be9fd</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Storage Type Namespace</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>storage.type.namespace</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>fontStyle</key>
|
||||||
|
<string>italic</string>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#8be9fd</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Storage Type Class</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>storage.type.class</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>fontStyle</key>
|
||||||
|
<string>italic</string>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#ff79c6</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Class name</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>entity.name.class</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>fontStyle</key>
|
||||||
|
<string>underline</string>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#8be9fd</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Meta Path</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>meta.path</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>fontStyle</key>
|
||||||
|
<string>underline</string>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#66d9ef</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Inherited class</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>entity.other.inherited-class</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>fontStyle</key>
|
||||||
|
<string>italic underline</string>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#8be9fd</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Function name</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>entity.name.function</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>fontStyle</key>
|
||||||
|
<string></string>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#50fa7b</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Function argument</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>variable.parameter</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>fontStyle</key>
|
||||||
|
<string>italic</string>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#ffb86c</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Tag name</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>entity.name.tag</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>fontStyle</key>
|
||||||
|
<string></string>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#ff79c6</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Tag attribute</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>entity.other.attribute-name</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>fontStyle</key>
|
||||||
|
<string></string>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#50fa7b</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Library function</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>support.function</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>fontStyle</key>
|
||||||
|
<string></string>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#8be9fd</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Library constant</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>support.constant</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>fontStyle</key>
|
||||||
|
<string></string>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#6be5fd</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Library class/type</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>support.type, support.class</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>fontStyle</key>
|
||||||
|
<string>italic</string>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#66d9ef</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Library variable</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>support.other.variable</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>fontStyle</key>
|
||||||
|
<string></string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Support Other Namespace</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>support.other.namespace</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>fontStyle</key>
|
||||||
|
<string>italic</string>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#66d9ef</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Invalid</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>invalid</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>background</key>
|
||||||
|
<string>#ff79c6</string>
|
||||||
|
<key>fontStyle</key>
|
||||||
|
<string></string>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#F8F8F0</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Invalid deprecated</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>invalid.deprecated</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>background</key>
|
||||||
|
<string>#bd93f9</string>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#F8F8F0</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>JSON String</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>meta.structure.dictionary.json string.quoted.double.json</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#CFCFC2</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>diff.header</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>meta.diff, meta.diff.header</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#6272a4</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>diff.deleted</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>markup.deleted</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#ff79c6</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>diff.inserted</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>markup.inserted</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#50fa7b</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>diff.changed</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>markup.changed</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#E6DB74</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>constant.numeric.line-number.find-in-files - match</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#bd93f9</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>entity.name.filename</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#E6DB74</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>message.error</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#F83333</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>JSON Punctuation</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>punctuation.definition.string.begin.json - meta.structure.dictionary.value.json, punctuation.definition.string.end.json - meta.structure.dictionary.value.json</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#EEEEEE</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>JSON Structure</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>meta.structure.dictionary.json string.quoted.double.json</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#8be9fd</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>JSON String</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>meta.structure.dictionary.value.json string.quoted.double.json</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#f1fa8c</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>JSON: 6 deep</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>meta meta meta meta meta meta meta.structure.dictionary.value string</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#50fa7b</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>JSON: 5 deep</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>meta meta meta meta meta meta.structure.dictionary.value string</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#ffb86c</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>JSON: 4 deep</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>meta meta meta meta meta.structure.dictionary.value string</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#ff79c6</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>JSON: 3 deep</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>meta meta meta meta.structure.dictionary.value string</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#bd93f9</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>JSON: 2 deep</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>meta meta meta.structure.dictionary.value string</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#50fa7b</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>JSON: 1 deep</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>meta meta.structure.dictionary.value string</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#ffb86c</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
|
||||||
|
<!-- Markdown Tweaks -->
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Markup: strike</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>markup.strike</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>fontStyle</key>
|
||||||
|
<string>italic</string>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#FFB86C</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Markup: bold</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>markup.bold</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>fontStyle</key>
|
||||||
|
<string>bold</string>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#FFB86C</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Markup: italic</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>markup.italic</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>fontStyle</key>
|
||||||
|
<string>italic</string>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#FFB86C</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Markdown: heading</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>markup.heading</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#8BE9FD</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Markdown: List Items Punctuation</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>punctuation.definition.list_item.markdown</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#FF79C6</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Markdown: Blockquote</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>markup.quote</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>fontStyle</key>
|
||||||
|
<string>italic</string>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#6272A4</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Markdown: Blockquote Punctuation</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>punctuation.definition.blockquote.markdown</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>fontStyle</key>
|
||||||
|
<string>italic</string>
|
||||||
|
<key>background</key>
|
||||||
|
<string>#6272A4</string>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#6272A4</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Markdown: Separator</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>meta.separator</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#6272A4</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Markup: raw inline</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>text.html.markdown markup.raw.inline</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#50FA7B</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Markup: underline</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>markup.underline</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>fontStyle</key>
|
||||||
|
<string>underline</string>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#BD93F9</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Markup: Raw block</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>markup.raw.block</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#CFCFC2</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Markdown: Raw Block fenced source</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>markup.raw.block.fenced.markdown source</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#F8F8F2</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Markdown: Fenced Bode Block</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>punctuation.definition.fenced.markdown, variable.language.fenced.markdown</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>fontStyle</key>
|
||||||
|
<string>italic</string>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#6272A4</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Markdown: Fenced Language</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>variable.language.fenced.markdown</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>fontStyle</key>
|
||||||
|
<string>italic</string>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#6272A4</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Punctuation Accessor</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>punctuation.accessor</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#FF79C6</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Meta Function Return Type</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>meta.function.return-type</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#FF79C6</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Punctuation Section Block Begin</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>punctuation.section.block.begin</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#ffffff</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Punctuation Section Block End</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>punctuation.section.block.end</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#ffffff</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Punctuation Section Embedded Begin</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>punctuation.section.embedded.begin</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#ff79c6</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Punctuation Section Embedded End</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>punctuation.section.embedded.end</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#ff79c6</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Punctuation Separator Namespace</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>punctuation.separator.namespace</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#ff79c6</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Variable Function</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>variable.function</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#50fa7b</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Variable Other</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>variable.other</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#ffffff</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Variable Language</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>variable.language</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#bd93f9</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Entity Name Module Ruby</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>entity.name.module.ruby</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#8be9fd</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Entity Name Constant Ruby</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>entity.name.constant.ruby</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#bd93f9</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Support Function Builtin Ruby</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>support.function.builtin.ruby</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#ffffff</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Storage Type Namespace CS</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>storage.type.namespace.cs</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#ff79c6</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Entity Name Namespace CS</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>entity.name.namespace.cs</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#8be9fd</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>uuid</key>
|
||||||
|
<string>83091B89-765E-4F0D-9275-0EC6CB084126</string>
|
||||||
|
<key>colorSpaceName</key>
|
||||||
|
<string>sRGB</string>
|
||||||
|
<key>semanticClass</key>
|
||||||
|
<string>theme.dracula</string>
|
||||||
|
<key>author</key>
|
||||||
|
<string>Zeno Rocha</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
43
k8s/k8s-deploy-example.yaml
Normal file
43
k8s/k8s-deploy-example.yaml
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: desubot-api-token
|
||||||
|
data:
|
||||||
|
token: 123.... # Base64 encoded token.
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: StatefulSet
|
||||||
|
metadata:
|
||||||
|
name: desubot
|
||||||
|
spec:
|
||||||
|
serviceName: "desubot"
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: desubot
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: desubot
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: desubot
|
||||||
|
image: ultradesu/desubot:latest
|
||||||
|
volumeMounts:
|
||||||
|
- name: storage
|
||||||
|
mountPath: /storage
|
||||||
|
env:
|
||||||
|
- name: TELEGRAM_BOT_TOKEN
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: desubot-api-token
|
||||||
|
key: token
|
||||||
|
volumeClaimTemplates:
|
||||||
|
- metadata:
|
||||||
|
name: storage
|
||||||
|
spec:
|
||||||
|
accessModes: [ "ReadWriteOnce" ]
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 50Gi
|
860
src/commands.rs
860
src/commands.rs
@ -1,103 +1,801 @@
|
|||||||
|
#![allow(unused_variables)]
|
||||||
use crate::db;
|
use crate::db;
|
||||||
use crate::errors::Error;
|
use crate::errors::Error;
|
||||||
|
use crate::errors::Error::{CodeHighlightningError, SQLITE3Error, SQLInvalidCommand};
|
||||||
|
use async_trait::async_trait;
|
||||||
use html_escape::encode_text;
|
use html_escape::encode_text;
|
||||||
use markov::Chain;
|
use markov::Chain;
|
||||||
|
use mystem::Case::Nominative;
|
||||||
|
use mystem::Gender::Feminine;
|
||||||
|
use mystem::MyStem;
|
||||||
|
use mystem::Person::First;
|
||||||
|
use mystem::Tense::{Inpresent, Past};
|
||||||
|
use rand::seq::SliceRandom;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
|
use regex::Regex;
|
||||||
|
use sqlparser::ast::Statement;
|
||||||
|
use sqlparser::dialect::GenericDialect;
|
||||||
|
use sqlparser::parser::Parser;
|
||||||
|
use syntect::easy::HighlightLines;
|
||||||
|
use syntect::highlighting::Theme;
|
||||||
|
use syntect::parsing::SyntaxReference;
|
||||||
|
use syntect::util::LinesWithEndings;
|
||||||
use telegram_bot::prelude::*;
|
use telegram_bot::prelude::*;
|
||||||
use telegram_bot::{Api, Message, ParseMode};
|
use telegram_bot::*;
|
||||||
|
use telegram_bot::{Api, Message, ParseMode, UserId};
|
||||||
|
|
||||||
pub(crate) async fn here(api: Api, message: Message) -> Result<(), Error> {
|
include!("../assets/help_text.rs");
|
||||||
let members: Vec<telegram_bot::User> = db::get_members(message.chat.id()).unwrap();
|
|
||||||
for u in &members {
|
pub struct Here {
|
||||||
debug!("Found user {:?} in chat {}", u, message.chat.id());
|
pub data: String,
|
||||||
}
|
}
|
||||||
let mut msg = "<b>I summon you</b>, ".to_string();
|
pub struct Top {
|
||||||
for user in members {
|
pub data: String,
|
||||||
let mention = match user.username {
|
}
|
||||||
Some(username) => format!("@{}", username),
|
pub struct ConfTop {
|
||||||
_ => format!(
|
pub data: String,
|
||||||
"<a href=\"tg://user?id={}\">{}</a>",
|
}
|
||||||
encode_text(&user.id.to_string()),
|
pub struct GlobalTop {
|
||||||
encode_text(&user.first_name)
|
pub data: String,
|
||||||
),
|
}
|
||||||
|
pub struct MarkovAll {
|
||||||
|
pub data: String,
|
||||||
|
}
|
||||||
|
pub struct Markov {
|
||||||
|
pub data: String,
|
||||||
|
}
|
||||||
|
pub struct Omedeto {
|
||||||
|
pub data: String,
|
||||||
|
}
|
||||||
|
pub struct Sql {
|
||||||
|
pub data: String,
|
||||||
|
}
|
||||||
|
pub struct Code {
|
||||||
|
pub data: String,
|
||||||
|
}
|
||||||
|
pub struct Scheme {
|
||||||
|
pub data: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
pub trait Execute {
|
||||||
|
async fn exec(&self, api: &Api, message: &Message) -> Result<(), Error>;
|
||||||
|
async fn exec_with_result(&self, api: &Api, message: &Message) -> Result<String, Error>;
|
||||||
|
async fn exec_mystem(
|
||||||
|
&self,
|
||||||
|
api: &Api,
|
||||||
|
message: &Message,
|
||||||
|
mystem: &mut MyStem,
|
||||||
|
) -> Result<(), Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl Execute for Scheme {
|
||||||
|
async fn exec(&self, api: &Api, message: &Message) -> Result<(), Error> {
|
||||||
|
match api
|
||||||
|
.send(
|
||||||
|
message
|
||||||
|
.text_reply(format!(
|
||||||
|
"{}{}{}",
|
||||||
|
"<pre>",
|
||||||
|
include_str!("../assets/scheme.sql").to_string(),
|
||||||
|
"</pre>"
|
||||||
|
))
|
||||||
|
.parse_mode(ParseMode::Html),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(_) => debug!("/scheme command sent to {}", message.chat.id()),
|
||||||
|
Err(_) => warn!("/scheme command sent failed to {}", message.chat.id()),
|
||||||
};
|
};
|
||||||
msg = format!("{} {}", msg, mention);
|
match {
|
||||||
|
Code {
|
||||||
|
data: format!(
|
||||||
|
"{}{}",
|
||||||
|
include_str!("../assets/scheme.sql").to_string(),
|
||||||
|
"\n#sql"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
.exec_with_result(&api, &message)
|
||||||
|
.await
|
||||||
|
} {
|
||||||
|
Ok(path) => {
|
||||||
|
let file = InputFileUpload::with_path(path.clone());
|
||||||
|
// api.send(message.chat.document(&file)).await?;
|
||||||
|
//
|
||||||
|
// // Send an image from disk
|
||||||
|
api.send(message.chat.document(&file)).await?;
|
||||||
|
//debug!("{:#?}", formatter);
|
||||||
|
let _ = std::fs::remove_file(&path);
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
let _ = api
|
||||||
|
.send(message.text_reply(CODE_HELP).parse_mode(ParseMode::Html))
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
match api
|
async fn exec_with_result(&self, api: &Api, message: &Message) -> Result<String, Error> {
|
||||||
.send(message.text_reply(msg).parse_mode(ParseMode::Html))
|
unimplemented!()
|
||||||
.await
|
}
|
||||||
{
|
|
||||||
Ok(_) => debug!("/here command sent to {}", message.chat.id()),
|
async fn exec_mystem(
|
||||||
Err(_) => warn!("/here command sent failed to {}", message.chat.id()),
|
&self,
|
||||||
|
api: &Api,
|
||||||
|
message: &Message,
|
||||||
|
mystem: &mut MyStem,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
unimplemented!()
|
||||||
}
|
}
|
||||||
//api.send(message.chat.text("Text to message chat")).await?;
|
|
||||||
//api.send(message.from.text("Private text")).await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn top(api: Api, message: Message) -> Result<(), Error> {
|
#[async_trait]
|
||||||
let top = db::get_top(&message).await?;
|
impl Execute for Sql {
|
||||||
let mut msg = "<b>Your top using words:</b>\n<pre>".to_string();
|
async fn exec(&self, api: &Api, message: &Message) -> Result<(), Error> {
|
||||||
let mut counter = 1;
|
unimplemented!()
|
||||||
for word in top.iter() {
|
}
|
||||||
msg = format!(
|
|
||||||
"{} <b>{}</b> {} - {}\n",
|
async fn exec_with_result(&self, api: &Api, message: &Message) -> Result<String, Error> {
|
||||||
msg, counter, word.word, word.count
|
let mut sql = self.data.clone();
|
||||||
|
debug!("PIZDA - {}", sql);
|
||||||
|
if sql == "/sql" || sql == "/sql-" {
|
||||||
|
return Ok(SQL_HELP.to_string());
|
||||||
|
}
|
||||||
|
let is_head = if sql.starts_with('-') {
|
||||||
|
sql = sql.replacen("-", "", 1);
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
};
|
||||||
|
let dialect = GenericDialect {};
|
||||||
|
let ast: Vec<Statement> = match Parser::parse_sql(&dialect, &sql) {
|
||||||
|
Ok(ast) => ast,
|
||||||
|
Err(_) => {
|
||||||
|
warn!("Invalid SQL - {}", sql);
|
||||||
|
return Err(SQLInvalidCommand);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
match ast.len() {
|
||||||
|
l if l > 1 => {
|
||||||
|
return Err(Error::SQLBannedCommand(
|
||||||
|
"🚫 One statement per message allowed 🚫".into(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
match ast[0] {
|
||||||
|
sqlparser::ast::Statement::Query { .. } => {}
|
||||||
|
_ => {
|
||||||
|
return Err(Error::SQLBannedCommand(
|
||||||
|
"🚫 SELECT requests allowed only 🚫".into(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let conn = db::open()?;
|
||||||
|
let mut stmt = conn.prepare_cached(&sql)?;
|
||||||
|
|
||||||
|
let mut rows = match stmt.query(rusqlite::NO_PARAMS) {
|
||||||
|
Err(e) => return Err(SQLITE3Error(e)),
|
||||||
|
Ok(rows) => rows,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut res: Vec<Vec<String>> = match rows.column_names() {
|
||||||
|
Some(n) => vec![n
|
||||||
|
.into_iter()
|
||||||
|
.map(|s| {
|
||||||
|
let t = String::from(s);
|
||||||
|
if t.len() > 10 {
|
||||||
|
"EMSGSIZE".to_string()
|
||||||
|
} else {
|
||||||
|
t
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect()],
|
||||||
|
None => return Err(SQLInvalidCommand),
|
||||||
|
};
|
||||||
|
|
||||||
|
let index_count = match rows.column_count() {
|
||||||
|
Some(c) => c,
|
||||||
|
None => return Err(SQLInvalidCommand),
|
||||||
|
};
|
||||||
|
while let Some(row) = rows.next().unwrap() {
|
||||||
|
let mut tmp: Vec<String> = Vec::new();
|
||||||
|
for i in 0..index_count {
|
||||||
|
match row.get(i).unwrap_or(None) {
|
||||||
|
Some(rusqlite::types::Value::Text(t)) => tmp.push(t),
|
||||||
|
Some(rusqlite::types::Value::Integer(t)) => tmp.push(t.to_string()),
|
||||||
|
Some(rusqlite::types::Value::Blob(_)) => tmp.push("Binary".to_string()),
|
||||||
|
Some(rusqlite::types::Value::Real(t)) => tmp.push(t.to_string()),
|
||||||
|
Some(rusqlite::types::Value::Null) => tmp.push("Null".to_string()),
|
||||||
|
None => tmp.push("Null".to_string()),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
res.push(tmp);
|
||||||
|
}
|
||||||
|
if res.len() >= 100 {
|
||||||
|
return Err(Error::SQLResultTooLong(
|
||||||
|
"SQL result too long. Lines limit is 100. Use LIMIT".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
// add Header
|
||||||
|
let mut msg = if is_head {
|
||||||
|
let mut x = String::from("<b>");
|
||||||
|
for head in res[0].iter() {
|
||||||
|
x = format!("{} {}", x, head);
|
||||||
|
}
|
||||||
|
format!("{}{}", x, "</b>\n")
|
||||||
|
} else {
|
||||||
|
String::new()
|
||||||
|
};
|
||||||
|
|
||||||
|
// remove header
|
||||||
|
res.remove(0);
|
||||||
|
|
||||||
|
msg = format!("{}{}", msg, "<pre>");
|
||||||
|
for line in res.iter() {
|
||||||
|
for field in line.iter() {
|
||||||
|
msg = format!("{}{}", msg, format!("{} ", field));
|
||||||
|
}
|
||||||
|
msg = format!("{}{}", msg, "\n");
|
||||||
|
}
|
||||||
|
msg = format!("{}{}", msg, "</pre>");
|
||||||
|
msg = if msg.len() > 4096 {
|
||||||
|
"🚫 Result is too big. Use LIMIT 🚫".into()
|
||||||
|
} else {
|
||||||
|
msg
|
||||||
|
};
|
||||||
|
Ok(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
async fn exec_mystem(
|
||||||
|
&self,
|
||||||
|
api: &Api,
|
||||||
|
message: &Message,
|
||||||
|
mystem: &mut MyStem,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl Execute for Here {
|
||||||
|
async fn exec(&self, api: &Api, message: &Message) -> Result<(), Error> {
|
||||||
|
let members: Vec<telegram_bot::User> =
|
||||||
|
db::get_members(message.chat.id(), 60).unwrap_or(vec![telegram_bot::User {
|
||||||
|
id: UserId::new(124317807),
|
||||||
|
first_name: "Ultradesu".to_string(),
|
||||||
|
last_name: None,
|
||||||
|
username: None,
|
||||||
|
is_bot: false,
|
||||||
|
language_code: None,
|
||||||
|
}]);
|
||||||
|
for u in &members {
|
||||||
|
debug!("Found user {:?} in chat {}", u, message.chat.id());
|
||||||
|
}
|
||||||
|
let mut msg = "<b>I summon you</b>, ".to_string();
|
||||||
|
for user in members {
|
||||||
|
let mention = match user.username {
|
||||||
|
Some(username) => format!("@{}", username),
|
||||||
|
_ => format!(
|
||||||
|
"<a href=\"tg://user?id={}\">{}</a>",
|
||||||
|
encode_text(&user.id.to_string()),
|
||||||
|
encode_text(&user.first_name)
|
||||||
|
),
|
||||||
|
};
|
||||||
|
msg = format!("{} {}", msg, mention);
|
||||||
|
}
|
||||||
|
|
||||||
|
match api
|
||||||
|
.send(message.text_reply(msg).parse_mode(ParseMode::Html))
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(_) => debug!("/here command sent to {}", message.chat.id()),
|
||||||
|
Err(_) => warn!("/here command sent failed to {}", message.chat.id()),
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn exec_with_result(&self, api: &Api, message: &Message) -> Result<String, Error> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
async fn exec_mystem(
|
||||||
|
&self,
|
||||||
|
api: &Api,
|
||||||
|
message: &Message,
|
||||||
|
mystem: &mut MyStem,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl Execute for Top {
|
||||||
|
async fn exec(&self, api: &Api, message: &Message) -> Result<(), Error> {
|
||||||
|
let top = db::get_top(&message).await?;
|
||||||
|
let mut msg = "<b>Your top using words:</b>\n<pre>".to_string();
|
||||||
|
let mut counter = 1;
|
||||||
|
for word in top.iter() {
|
||||||
|
msg = format!(
|
||||||
|
"{} <b>{}</b> {} - {}\n",
|
||||||
|
msg, counter, word.word, word.count
|
||||||
|
);
|
||||||
|
counter += 1;
|
||||||
|
}
|
||||||
|
msg = format!("{}{}", msg, "</pre>");
|
||||||
|
match api
|
||||||
|
.send(message.text_reply(msg).parse_mode(ParseMode::Html))
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(_) => debug!("/top command sent to {}", message.chat.id()),
|
||||||
|
Err(_) => warn!("/top command sent failed to {}", message.chat.id()),
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn exec_with_result(&self, api: &Api, message: &Message) -> Result<String, Error> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
async fn exec_mystem(
|
||||||
|
&self,
|
||||||
|
api: &Api,
|
||||||
|
message: &Message,
|
||||||
|
mystem: &mut MyStem,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl Execute for GlobalTop {
|
||||||
|
async fn exec(&self, api: &Api, message: &Message) -> Result<(), Error> {
|
||||||
|
let top = db::get_global_top().await?;
|
||||||
|
let mut msg = "<b>Global top words:</b>\n<pre>".to_string();
|
||||||
|
let mut counter = 1;
|
||||||
|
for word in top.iter() {
|
||||||
|
msg = format!(
|
||||||
|
"{} <b>{}</b> {} - {}\n",
|
||||||
|
msg, counter, word.word, word.count
|
||||||
|
);
|
||||||
|
counter += 1;
|
||||||
|
}
|
||||||
|
msg = format!("{}{}", msg, "</pre>");
|
||||||
|
match api
|
||||||
|
.send(message.text_reply(msg).parse_mode(ParseMode::Html))
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(_) => debug!("/global_top command sent to {}", message.chat.id()),
|
||||||
|
Err(_) => warn!("/global_top command sent failed to {}", message.chat.id()),
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn exec_with_result(&self, api: &Api, message: &Message) -> Result<String, Error> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
async fn exec_mystem(
|
||||||
|
&self,
|
||||||
|
api: &Api,
|
||||||
|
message: &Message,
|
||||||
|
mystem: &mut MyStem,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl Execute for ConfTop {
|
||||||
|
async fn exec(&self, api: &Api, message: &Message) -> Result<(), Error> {
|
||||||
|
let top = db::get_conf_top(&message).await?;
|
||||||
|
let mut msg = "<b>Conf top words:</b>\n<pre>".to_string();
|
||||||
|
let mut counter = 1;
|
||||||
|
for word in top.iter() {
|
||||||
|
msg = format!(
|
||||||
|
"{} <b>{}</b> {} - {}\n",
|
||||||
|
msg, counter, word.word, word.count
|
||||||
|
);
|
||||||
|
counter += 1;
|
||||||
|
}
|
||||||
|
msg = format!("{}{}", msg, "</pre>");
|
||||||
|
match api
|
||||||
|
.send(message.text_reply(msg).parse_mode(ParseMode::Html))
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(_) => debug!("/conf_top command sent to {}", message.chat.id()),
|
||||||
|
Err(_) => warn!("/conf_top command sent failed to {}", message.chat.id()),
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn exec_with_result(&self, api: &Api, message: &Message) -> Result<String, Error> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
async fn exec_mystem(
|
||||||
|
&self,
|
||||||
|
api: &Api,
|
||||||
|
message: &Message,
|
||||||
|
mystem: &mut MyStem,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl Execute for MarkovAll {
|
||||||
|
async fn exec(&self, api: &Api, message: &Message) -> Result<(), Error> {
|
||||||
|
let messages = db::get_messages_random_all().await?;
|
||||||
|
let mut chain = Chain::new();
|
||||||
|
chain.feed(messages);
|
||||||
|
let mut sentences = chain.generate();
|
||||||
|
let mut msg = String::new();
|
||||||
|
for _ in 1..rand::thread_rng().gen_range(2, 10) {
|
||||||
|
msg = format!("{} {}", msg, sentences.pop().unwrap());
|
||||||
|
}
|
||||||
|
match api
|
||||||
|
.send(message.text_reply(msg.trim()).parse_mode(ParseMode::Html))
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(_) => debug!("/markov_all command sent to {}", message.chat.id()),
|
||||||
|
Err(_) => warn!("/markov_all command sent failed to {}", message.chat.id()),
|
||||||
|
}
|
||||||
|
//api.send(message.chat.text("Text to message chat")).await?;
|
||||||
|
//api.send(message.from.text("Private text")).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn exec_with_result(&self, api: &Api, message: &Message) -> Result<String, Error> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
async fn exec_mystem(
|
||||||
|
&self,
|
||||||
|
api: &Api,
|
||||||
|
message: &Message,
|
||||||
|
mystem: &mut MyStem,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl Execute for Markov {
|
||||||
|
async fn exec(&self, api: &Api, message: &Message) -> Result<(), Error> {
|
||||||
|
let messages = db::get_messages_random_group(&message).await?;
|
||||||
|
let mut chain = Chain::new();
|
||||||
|
chain.feed(messages);
|
||||||
|
let mut sentences = chain.generate();
|
||||||
|
let mut msg = String::new();
|
||||||
|
for _ in 1..rand::thread_rng().gen_range(2, 10) {
|
||||||
|
msg = format!("{} {}", msg, sentences.pop().unwrap_or(" ".into()));
|
||||||
|
}
|
||||||
|
match api
|
||||||
|
.send(message.text_reply(msg.trim()).parse_mode(ParseMode::Html))
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(_) => debug!("/markov command sent to {}", message.chat.id()),
|
||||||
|
Err(_) => warn!("/markov command sent failed to {}", message.chat.id()),
|
||||||
|
}
|
||||||
|
//api.send(message.chat.text("Text to message chat")).await?;
|
||||||
|
//api.send(message.from.text("Private text")).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn exec_with_result(&self, api: &Api, message: &Message) -> Result<String, Error> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
async fn exec_mystem(
|
||||||
|
&self,
|
||||||
|
api: &Api,
|
||||||
|
message: &Message,
|
||||||
|
mystem: &mut MyStem,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl Execute for Omedeto {
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
async fn exec(&self, api: &Api, message: &Message) -> Result<(), Error> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn exec_with_result(&self, api: &Api, message: &Message) -> Result<String, Error> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[warn(unused_must_use)]
|
||||||
|
async fn exec_mystem(
|
||||||
|
&self,
|
||||||
|
api: &Api,
|
||||||
|
message: &Message,
|
||||||
|
mystem: &mut MyStem,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let all_msg = db::get_messages_user_all(&message).await?;
|
||||||
|
let re = Regex::new(r"^[яЯ] [а-яА-Я]+(-[а-яА-Я]+(_[а-яА-Я]+)*)*").unwrap();
|
||||||
|
let mut nouns: Vec<String> = all_msg
|
||||||
|
.iter()
|
||||||
|
.filter(|m| re.is_match(m))
|
||||||
|
.map(|m| m.split(' ').map(|s| s.to_string()).collect::<Vec<String>>()[1].clone())
|
||||||
|
.filter(|m| {
|
||||||
|
let stem = mystem.stemming(m.clone()).unwrap_or_default();
|
||||||
|
if !stem.is_empty() {
|
||||||
|
if !stem[0].lex.is_empty() {
|
||||||
|
match stem[0].lex[0].grammem.part_of_speech {
|
||||||
|
mystem::PartOfSpeech::Noun => stem[0].lex[0]
|
||||||
|
.grammem
|
||||||
|
.facts
|
||||||
|
.contains(&mystem::Fact::Case(Nominative)),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map(|w| w.replace(|z| z == '.' || z == ',', ""))
|
||||||
|
.collect();
|
||||||
|
nouns.sort();
|
||||||
|
nouns.dedup();
|
||||||
|
nouns.shuffle(&mut rand::thread_rng());
|
||||||
|
//debug!("Found {} nouns. {:#?}", nouns.len(), nouns);
|
||||||
|
|
||||||
|
let mut verbs_p: Vec<String> = all_msg
|
||||||
|
.iter()
|
||||||
|
.filter(|m| re.is_match(m))
|
||||||
|
.map(|m| m.split(' ').map(|s| s.to_string()).collect::<Vec<String>>()[1].clone())
|
||||||
|
.filter(|m| {
|
||||||
|
let stem = mystem.stemming(m.clone()).unwrap_or_default();
|
||||||
|
#[allow(clippy::if_same_then_else)]
|
||||||
|
if stem.is_empty() {
|
||||||
|
false
|
||||||
|
} else if stem[0].lex.is_empty() {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
match stem[0].lex[0].grammem.part_of_speech {
|
||||||
|
mystem::PartOfSpeech::Verb => stem[0].lex[0]
|
||||||
|
.grammem
|
||||||
|
.facts
|
||||||
|
.contains(&mystem::Fact::Tense(Past)),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map(|w| w.replace(|z| z == '.' || z == ',', ""))
|
||||||
|
.collect();
|
||||||
|
verbs_p.sort();
|
||||||
|
verbs_p.dedup();
|
||||||
|
verbs_p.shuffle(&mut rand::thread_rng());
|
||||||
|
//debug!("Found {} past verbs. {:#?}", verbs_p.len(), verbs_p);
|
||||||
|
|
||||||
|
let mut verbs_i: Vec<String> = all_msg
|
||||||
|
.iter()
|
||||||
|
.filter(|m| re.is_match(m))
|
||||||
|
.map(|m| m.split(' ').map(|s| s.to_string()).collect::<Vec<String>>()[1].clone())
|
||||||
|
.filter(|m| {
|
||||||
|
let stem = mystem.stemming(m.clone()).unwrap_or_default();
|
||||||
|
#[allow(clippy::if_same_then_else)]
|
||||||
|
if stem.is_empty() {
|
||||||
|
false
|
||||||
|
} else if stem[0].lex.is_empty() {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
match stem[0].lex[0].grammem.part_of_speech {
|
||||||
|
mystem::PartOfSpeech::Verb => {
|
||||||
|
stem[0].lex[0]
|
||||||
|
.grammem
|
||||||
|
.facts
|
||||||
|
.contains(&mystem::Fact::Tense(Inpresent))
|
||||||
|
&& stem[0].lex[0]
|
||||||
|
.grammem
|
||||||
|
.facts
|
||||||
|
.contains(&mystem::Fact::Person(First))
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map(|w| w.replace(|z| z == '.' || z == ',', ""))
|
||||||
|
.collect();
|
||||||
|
verbs_i.sort();
|
||||||
|
verbs_i.dedup();
|
||||||
|
verbs_i.shuffle(&mut rand::thread_rng());
|
||||||
|
//debug!("Found {} inpresent verbs. {:#?}", verbs_i.len(), verbs_i);
|
||||||
|
|
||||||
|
if nouns.is_empty() {
|
||||||
|
nouns.push(message.from.first_name.to_string());
|
||||||
|
}
|
||||||
|
let start: Vec<String> = vec![
|
||||||
|
"С новым годом".into(),
|
||||||
|
"С НГ тебя".into(),
|
||||||
|
"Поздравляю".into(),
|
||||||
|
"Поздравляю с НГ".into(),
|
||||||
|
];
|
||||||
|
let placeholders: Vec<String> = vec![
|
||||||
|
"[ДАННЫЕ УДАЛЕНЫ]".into(),
|
||||||
|
"[СЕКРЕТНО]".into(),
|
||||||
|
"[НЕТ ДАННЫХ]".into(),
|
||||||
|
"[ОШИБКА ДОСТУПА]".into(),
|
||||||
|
];
|
||||||
|
//debug!("Nouns: {:#?}", nouns);
|
||||||
|
//debug!("Verbs: {:#?}", verbs);
|
||||||
|
|
||||||
|
let fem = {
|
||||||
|
let mut fm = 0;
|
||||||
|
let mut mu = 0;
|
||||||
|
all_msg
|
||||||
|
.clone()
|
||||||
|
.into_iter()
|
||||||
|
.filter(|m| re.is_match(m))
|
||||||
|
.map(|m| m.split(' ').map(|s| s.to_string()).collect::<Vec<String>>()[1].clone())
|
||||||
|
.map(|m| {
|
||||||
|
let stem = mystem.stemming(m).unwrap_or_default();
|
||||||
|
#[allow(clippy::if_same_then_else)]
|
||||||
|
if stem.is_empty() {
|
||||||
|
} else if stem[0].lex.is_empty() {
|
||||||
|
} else {
|
||||||
|
if let mystem::PartOfSpeech::Verb = stem[0].lex[0].grammem.part_of_speech {
|
||||||
|
match stem[0].lex[0]
|
||||||
|
.grammem
|
||||||
|
.facts
|
||||||
|
.contains(&mystem::Fact::Tense(Past))
|
||||||
|
{
|
||||||
|
true => {
|
||||||
|
if stem[0].lex[0]
|
||||||
|
.grammem
|
||||||
|
.facts
|
||||||
|
.contains(&mystem::Fact::Gender(Feminine))
|
||||||
|
{
|
||||||
|
fm += 1;
|
||||||
|
} else {
|
||||||
|
mu += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.for_each(drop);
|
||||||
|
//debug!("fm - {}, mu - {}", fm, mu);
|
||||||
|
fm >= mu
|
||||||
|
};
|
||||||
|
//debug!("Is Feminine - {}", fem);
|
||||||
|
let result = format!(
|
||||||
|
"{} {} известн{} как {}, {}, а так же конечно {}. В прошедшем году ты часто давал{} нам знать, что ты {}, {} и {}. Нередко ты говорил{} я {}, я {} или даже я {}. =*",
|
||||||
|
start.choose(&mut rand::thread_rng()).unwrap(),
|
||||||
|
message.from.first_name.to_string(),
|
||||||
|
{ if fem { "ая" } else { "ый" } },
|
||||||
|
nouns.pop().unwrap_or_else(|| placeholders.choose(&mut rand::thread_rng()).unwrap().to_string()),
|
||||||
|
nouns.pop().unwrap_or_else(|| placeholders.choose(&mut rand::thread_rng()).unwrap().to_string()),
|
||||||
|
nouns.pop().unwrap_or_else(|| placeholders.choose(&mut rand::thread_rng()).unwrap().to_string()),
|
||||||
|
{ if fem { "а" } else { "" } },
|
||||||
|
verbs_p.pop().unwrap_or_else(|| placeholders.choose(&mut rand::thread_rng()).unwrap().to_string()),
|
||||||
|
verbs_p.pop().unwrap_or_else(|| placeholders.choose(&mut rand::thread_rng()).unwrap().to_string()),
|
||||||
|
verbs_p.pop().unwrap_or_else(|| placeholders.choose(&mut rand::thread_rng()).unwrap().to_string()),
|
||||||
|
{ if fem { "а" } else { "" } },
|
||||||
|
verbs_i.pop().unwrap_or_else(|| placeholders.choose(&mut rand::thread_rng()).unwrap().to_string()),
|
||||||
|
verbs_i.pop().unwrap_or_else(|| placeholders.choose(&mut rand::thread_rng()).unwrap().to_string()),
|
||||||
|
verbs_i.pop().unwrap_or_else(|| placeholders.choose(&mut rand::thread_rng()).unwrap().to_string()),
|
||||||
);
|
);
|
||||||
counter += 1;
|
match api
|
||||||
|
.send(
|
||||||
|
message
|
||||||
|
.text_reply(result.trim())
|
||||||
|
.parse_mode(ParseMode::Html),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(_) => debug!("/omedeto command sent to {}", message.chat.id()),
|
||||||
|
Err(_) => warn!("/omedeto command sent failed to {}", message.chat.id()),
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
msg = format!("{}{}", msg, "</pre>");
|
|
||||||
match api
|
|
||||||
.send(message.text_reply(msg).parse_mode(ParseMode::Html))
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
Ok(_) => debug!("/top command sent to {}", message.chat.id()),
|
|
||||||
Err(_) => warn!("/top command sent failed to {}", message.chat.id()),
|
|
||||||
}
|
|
||||||
//api.send(message.chat.text("Text to message chat")).await?;
|
|
||||||
//api.send(message.from.text("Private text")).await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn markov_all(api: Api, message: Message) -> Result<(), Error> {
|
#[async_trait]
|
||||||
let messages = db::get_random_messages().await?;
|
impl Execute for Code {
|
||||||
let mut chain = Chain::new();
|
async fn exec(&self, api: &Api, message: &Message) -> Result<(), Error> {
|
||||||
chain.feed(messages);
|
unimplemented!()
|
||||||
let mut sentences = chain.generate();
|
|
||||||
let mut msg = String::new();
|
|
||||||
for _ in 1..rand::thread_rng().gen_range(2, 10) {
|
|
||||||
msg = format!("{} {}", msg, sentences.pop().unwrap());
|
|
||||||
}
|
}
|
||||||
match api
|
async fn exec_with_result(&self, api: &Api, message: &Message) -> Result<String, Error> {
|
||||||
.send(message.text_reply(msg.trim()).parse_mode(ParseMode::Html))
|
let mut lines: Vec<String> = self
|
||||||
.await
|
.data
|
||||||
{
|
.trim()
|
||||||
Ok(_) => debug!("/markov_all command sent to {}", message.chat.id()),
|
.split("\n")
|
||||||
Err(_) => warn!("/markov_all command sent failed to {}", message.chat.id()),
|
.map(|s| s.to_string())
|
||||||
}
|
.collect();
|
||||||
//api.send(message.chat.text("Text to message chat")).await?;
|
let last_line = &lines[lines.len() - 1];
|
||||||
//api.send(message.from.text("Private text")).await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn markov(api: Api, message: Message) -> Result<(), Error> {
|
let tags = last_line
|
||||||
let messages = db::get_random_messages_group(&message).await?;
|
.trim()
|
||||||
let mut chain = Chain::new();
|
.split(|s| s == ' ' || s == '\n')
|
||||||
chain.feed(messages);
|
.filter(|s| s.starts_with("#"))
|
||||||
let mut sentences = chain.generate();
|
.map(|s| s.to_string().replace("#", ""))
|
||||||
let mut msg = String::new();
|
.map(|s| s.to_string().replace("_", " "))
|
||||||
for _ in 1..rand::thread_rng().gen_range(2, 10) {
|
.collect::<Vec<_>>();
|
||||||
msg = format!("{} {}", msg, sentences.pop().unwrap());
|
|
||||||
|
let code = if tags.is_empty() {
|
||||||
|
self.data.trim().to_string()
|
||||||
|
} else {
|
||||||
|
let _ = lines.pop();
|
||||||
|
lines.join("\n")
|
||||||
|
};
|
||||||
|
if code.is_empty() {
|
||||||
|
return Err(CodeHighlightningError);
|
||||||
|
}
|
||||||
|
let (ps, ts) = silicon::utils::init_syntect();
|
||||||
|
let syntax: Vec<&SyntaxReference> = tags
|
||||||
|
.iter()
|
||||||
|
.map(|s| ps.find_syntax_by_token(s))
|
||||||
|
.filter(|s| s.is_some())
|
||||||
|
.map(|s| s.unwrap())
|
||||||
|
.collect();
|
||||||
|
let syntax = if syntax.len() != 1 {
|
||||||
|
ps.find_syntax_by_token("js").unwrap()
|
||||||
|
} else {
|
||||||
|
syntax[0]
|
||||||
|
};
|
||||||
|
let theme: Vec<&Theme> = tags
|
||||||
|
.iter()
|
||||||
|
.map(|s| ts.themes.get(s))
|
||||||
|
.filter(|s| s.is_some())
|
||||||
|
.map(|s| s.unwrap())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let theme = if theme.len() != 1 {
|
||||||
|
ts.themes.get("gruvbox").unwrap()
|
||||||
|
} else {
|
||||||
|
theme[0]
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut h = HighlightLines::new(syntax, theme);
|
||||||
|
let highlight = LinesWithEndings::from(&code)
|
||||||
|
.map(|line| h.highlight(line, &ps))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let formatter = silicon::formatter::ImageFormatterBuilder::<String>::new()
|
||||||
|
.window_controls(false)
|
||||||
|
.line_offset(1)
|
||||||
|
.round_corner(false);
|
||||||
|
let mut formatter = formatter.build().unwrap();
|
||||||
|
let image = formatter.format(&highlight, &theme);
|
||||||
|
let path = "code.png";
|
||||||
|
image
|
||||||
|
.save(&path)
|
||||||
|
.map_err(|e| error!("Failed to save image to {}: {}", path, e))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// let file = InputFileUpload::with_path("CODE.png");
|
||||||
|
// api.send(message.chat.document(&file)).await?;
|
||||||
|
//
|
||||||
|
// // Send an image from disk
|
||||||
|
// api.send( message.chat.photo(&file)).await?;
|
||||||
|
//debug!("{:#?}", formatter);
|
||||||
|
Ok(path.into())
|
||||||
}
|
}
|
||||||
match api
|
|
||||||
.send(message.text_reply(msg.trim()).parse_mode(ParseMode::Html))
|
async fn exec_mystem(
|
||||||
.await
|
&self,
|
||||||
{
|
api: &Api,
|
||||||
Ok(_) => debug!("/markov command sent to {}", message.chat.id()),
|
message: &Message,
|
||||||
Err(_) => warn!("/markov command sent failed to {}", message.chat.id()),
|
mystem: &mut MyStem,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
unimplemented!()
|
||||||
}
|
}
|
||||||
//api.send(message.chat.text("Text to message chat")).await?;
|
|
||||||
//api.send(message.from.text("Private text")).await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
200
src/db.rs
200
src/db.rs
@ -1,5 +1,6 @@
|
|||||||
|
#[allow(unused_mut)]
|
||||||
|
#[allow(dead_code)]
|
||||||
use crate::errors;
|
use crate::errors;
|
||||||
use crate::mystem;
|
|
||||||
use crate::utils;
|
use crate::utils;
|
||||||
use rusqlite::{named_params, params, Connection, Error, Result};
|
use rusqlite::{named_params, params, Connection, Error, Result};
|
||||||
use std::time::SystemTime;
|
use std::time::SystemTime;
|
||||||
@ -35,6 +36,30 @@ pub(crate) fn update_scheme() -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn load_stopwords() -> Result<()> {
|
||||||
|
info!("Populating stop words wait please.");
|
||||||
|
let conn = open()?;
|
||||||
|
for table in include_str!("../assets/stop-words.txt")
|
||||||
|
.split('\n')
|
||||||
|
.into_iter()
|
||||||
|
{
|
||||||
|
let word = table.trim();
|
||||||
|
if word != "" {
|
||||||
|
let mut _stmt = conn
|
||||||
|
.prepare_cached(
|
||||||
|
"
|
||||||
|
INSERT OR IGNORE INTO
|
||||||
|
stop_words('word')
|
||||||
|
VALUES (:word)
|
||||||
|
",
|
||||||
|
)?
|
||||||
|
.insert(params![word]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
info!("Stop words updated.");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn get_user(id: telegram_bot::UserId) -> Result<telegram_bot::User, errors::Error> {
|
pub(crate) fn get_user(id: telegram_bot::UserId) -> Result<telegram_bot::User, errors::Error> {
|
||||||
let conn = open()?;
|
let conn = open()?;
|
||||||
let mut stmt = conn.prepare_cached(
|
let mut stmt = conn.prepare_cached(
|
||||||
@ -55,7 +80,7 @@ pub(crate) fn get_user(id: telegram_bot::UserId) -> Result<telegram_bot::User, e
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if users.len() == 0 {
|
if users.is_empty() {
|
||||||
Err(errors::Error::UserNotFound)
|
Err(errors::Error::UserNotFound)
|
||||||
} else {
|
} else {
|
||||||
Ok(users[0].clone())
|
Ok(users[0].clone())
|
||||||
@ -75,14 +100,14 @@ pub(crate) fn get_conf(id: telegram_bot::ChatId) -> Result<Conf, errors::Error>
|
|||||||
date: row.get(2)?,
|
date: row.get(2)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if confs.len() == 0 {
|
if confs.is_empty() {
|
||||||
Err(errors::Error::ConfNotFound)
|
Err(errors::Error::ConfNotFound)
|
||||||
} else {
|
} else {
|
||||||
Ok(confs[0].clone())
|
Ok(confs[0].clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
#[allow(dead_code)]
|
||||||
pub(crate) fn get_confs() -> Result<Vec<Conf>> {
|
pub(crate) fn get_confs() -> Result<Vec<Conf>> {
|
||||||
let conn = open()?;
|
let conn = open()?;
|
||||||
let mut stmt = conn.prepare("SELECT id, title, date FROM conf")?;
|
let mut stmt = conn.prepare("SELECT id, title, date FROM conf")?;
|
||||||
@ -101,8 +126,8 @@ pub(crate) fn get_confs() -> Result<Vec<Conf>> {
|
|||||||
|
|
||||||
Ok(confs)
|
Ok(confs)
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
pub(crate) async fn get_random_messages() -> Result<Vec<String>, Error> {
|
pub(crate) async fn get_messages_random_all() -> Result<Vec<String>, Error> {
|
||||||
let conn = open()?;
|
let conn = open()?;
|
||||||
let mut stmt = conn.prepare_cached("SELECT text FROM messages ORDER BY RANDOM() LIMIT 50")?;
|
let mut stmt = conn.prepare_cached("SELECT text FROM messages ORDER BY RANDOM() LIMIT 50")?;
|
||||||
let mut rows = stmt.query_named(named_params![])?;
|
let mut rows = stmt.query_named(named_params![])?;
|
||||||
@ -114,17 +139,18 @@ pub(crate) async fn get_random_messages() -> Result<Vec<String>, Error> {
|
|||||||
Ok(messages)
|
Ok(messages)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn get_random_messages_group(
|
pub(crate) async fn get_messages_random_group(
|
||||||
message: &telegram_bot::Message
|
message: &telegram_bot::Message,
|
||||||
) -> Result<Vec<String>, Error> {
|
) -> Result<Vec<String>, Error> {
|
||||||
let conf_id = i64::from(message.chat.id());
|
let conf_id = i64::from(message.chat.id());
|
||||||
let conn = open()?;
|
let conn = open()?;
|
||||||
let mut stmt = conn.prepare_cached("
|
let mut stmt = conn.prepare_cached(
|
||||||
|
"
|
||||||
SELECT m.text FROM messages m
|
SELECT m.text FROM messages m
|
||||||
LEFT JOIN relations r ON r.msg_id = m.id
|
LEFT JOIN relations r ON r.msg_id = m.id
|
||||||
WHERE r.conf_id = :conf_id
|
WHERE r.conf_id = :conf_id
|
||||||
ORDER BY RANDOM() LIMIT 50
|
ORDER BY RANDOM() LIMIT 50
|
||||||
"
|
",
|
||||||
)?;
|
)?;
|
||||||
let mut rows = stmt.query_named(named_params! {":conf_id": conf_id})?;
|
let mut rows = stmt.query_named(named_params! {":conf_id": conf_id})?;
|
||||||
let mut messages = Vec::new();
|
let mut messages = Vec::new();
|
||||||
@ -135,18 +161,74 @@ pub(crate) async fn get_random_messages_group(
|
|||||||
Ok(messages)
|
Ok(messages)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_members(id: telegram_bot::ChatId) -> Result<Vec<telegram_bot::User>> {
|
#[allow(dead_code)]
|
||||||
|
pub(crate) async fn get_messages_user_group(
|
||||||
|
message: &telegram_bot::Message,
|
||||||
|
) -> Result<Vec<String>, Error> {
|
||||||
|
let conf_id = i64::from(message.chat.id());
|
||||||
|
let user_id = i64::from(message.from.id);
|
||||||
let conn = open()?;
|
let conn = open()?;
|
||||||
let mut stmt = conn.prepare_cached(
|
let mut stmt = conn.prepare_cached(
|
||||||
"
|
"
|
||||||
SELECT DISTINCT(u.username), u.id, u.first_name, u.last_name, u.date
|
SELECT m.text FROM messages m
|
||||||
|
LEFT JOIN relations r ON r.msg_id = m.id
|
||||||
|
WHERE r.conf_id = :conf_id
|
||||||
|
AND r.user_id = :user_id
|
||||||
|
",
|
||||||
|
)?;
|
||||||
|
let mut rows = stmt.query_named(named_params! {":conf_id": conf_id, ":user_id": user_id})?;
|
||||||
|
let mut messages = Vec::new();
|
||||||
|
|
||||||
|
while let Some(row) = rows.next()? {
|
||||||
|
messages.push(row.get(0)?)
|
||||||
|
}
|
||||||
|
Ok(messages)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn get_messages_user_all(
|
||||||
|
message: &telegram_bot::Message,
|
||||||
|
) -> Result<Vec<String>, Error> {
|
||||||
|
let user_id = i64::from(message.from.id);
|
||||||
|
let conn = open()?;
|
||||||
|
let mut stmt = conn.prepare_cached(
|
||||||
|
"
|
||||||
|
SELECT m.text FROM messages m
|
||||||
|
LEFT JOIN relations r ON r.msg_id = m.id
|
||||||
|
WHERE r.user_id = :user_id
|
||||||
|
",
|
||||||
|
)?;
|
||||||
|
let mut rows = stmt.query_named(named_params! {":user_id": user_id})?;
|
||||||
|
let mut messages = Vec::new();
|
||||||
|
|
||||||
|
while let Some(row) = rows.next()? {
|
||||||
|
messages.push(row.get(0)?)
|
||||||
|
}
|
||||||
|
Ok(messages)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_members(id: telegram_bot::ChatId, limit: u32) -> Result<Vec<telegram_bot::User>> {
|
||||||
|
let where_statement = if limit > 0 {
|
||||||
|
format!("and days_seen <= {}", limit)
|
||||||
|
} else {
|
||||||
|
"".into()
|
||||||
|
};
|
||||||
|
debug!("{}", where_statement);
|
||||||
|
let conn = open()?;
|
||||||
|
let mut stmt = conn.prepare_cached(&format!(
|
||||||
|
"
|
||||||
|
SELECT DISTINCT(u.username), u.id, u.first_name, u.last_name, u.date,
|
||||||
|
(strftime('%s','now')-r.date)/60/60/24 as days_seen
|
||||||
FROM relations r
|
FROM relations r
|
||||||
JOIN user u
|
JOIN user u
|
||||||
ON u.id = r.user_id
|
ON u.id = r.user_id
|
||||||
LEFT JOIN conf c
|
LEFT JOIN conf c
|
||||||
ON r.conf_id = c.id
|
ON r.conf_id = c.id
|
||||||
WHERE c.id = :id",
|
WHERE c.id = :id
|
||||||
)?;
|
{}
|
||||||
|
GROUP BY u.id
|
||||||
|
ORDER BY r.date DESC",
|
||||||
|
where_statement
|
||||||
|
))?;
|
||||||
let mut rows = stmt.query_named(&[(":id", &id.to_string())])?;
|
let mut rows = stmt.query_named(&[(":id", &id.to_string())])?;
|
||||||
let mut users = Vec::new();
|
let mut users = Vec::new();
|
||||||
|
|
||||||
@ -169,7 +251,7 @@ pub(crate) async fn add_conf(message: Message) -> Result<(), Error> {
|
|||||||
|
|
||||||
match get_conf(message.chat.id()) {
|
match get_conf(message.chat.id()) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
//info!("Group found: {:?}", message.chat.id());
|
debug!("Group found: {:?}", message.chat.id());
|
||||||
let update = Conf {
|
let update = Conf {
|
||||||
id: message.chat.id(),
|
id: message.chat.id(),
|
||||||
title,
|
title,
|
||||||
@ -183,10 +265,10 @@ pub(crate) async fn add_conf(message: Message) -> Result<(), Error> {
|
|||||||
id = :id",
|
id = :id",
|
||||||
)?;
|
)?;
|
||||||
stmt.execute_named(&[(":id", &update.id.to_string()), (":title", &update.title)])?;
|
stmt.execute_named(&[(":id", &update.id.to_string()), (":title", &update.title)])?;
|
||||||
//info!("Conf {:?} updated: {:?}", update.title, get_conf(update.id));
|
debug!("Conf {:?} updated: {:?}", update.title, get_conf(update.id));
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
//info!("Group didn't found: {:?}", message.chat.id());
|
debug!("Group didn't found: {:?}", message.chat.id());
|
||||||
|
|
||||||
let update = Conf {
|
let update = Conf {
|
||||||
id: message.chat.id(),
|
id: message.chat.id(),
|
||||||
@ -205,7 +287,7 @@ pub(crate) async fn add_conf(message: Message) -> Result<(), Error> {
|
|||||||
(":title", &update.title),
|
(":title", &update.title),
|
||||||
(":date", &unix_time),
|
(":date", &unix_time),
|
||||||
])?;
|
])?;
|
||||||
//info!("Conf {:?} added: {:?}", update.title, get_conf(update.id));
|
debug!("Conf {:?} added: {:?}", update.title, get_conf(update.id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -238,7 +320,11 @@ pub(crate) async fn add_user(message: Message) -> Result<(), Error> {
|
|||||||
(":first_name", &update.first_name),
|
(":first_name", &update.first_name),
|
||||||
(":last_name", &update.last_name),
|
(":last_name", &update.last_name),
|
||||||
])?;
|
])?;
|
||||||
//println!("User {} updated: {:?}", update.first_name, get_user(user.id));
|
debug!(
|
||||||
|
"User {} updated: {:?}",
|
||||||
|
update.first_name,
|
||||||
|
get_user(update.id)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
let unix_time = SystemTime::now()
|
let unix_time = SystemTime::now()
|
||||||
@ -265,7 +351,7 @@ pub(crate) async fn add_user(message: Message) -> Result<(), Error> {
|
|||||||
(":last_name", &user.last_name),
|
(":last_name", &user.last_name),
|
||||||
(":date", &unix_time),
|
(":date", &unix_time),
|
||||||
])?;
|
])?;
|
||||||
//println!("User added: {:?}", user);
|
debug!("User added: {:?}", user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -300,14 +386,12 @@ pub(crate) async fn get_file(file_id: String) -> Result<i64, errors::Error> {
|
|||||||
Ok(id) => Ok(id),
|
Ok(id) => Ok(id),
|
||||||
Err(_) => Err(errors::Error::FileNotFound),
|
Err(_) => Err(errors::Error::FileNotFound),
|
||||||
};
|
};
|
||||||
|
|
||||||
file_rowid
|
file_rowid
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn add_word(word: &String) -> Result<i64, errors::Error> {
|
async fn add_word(word: &str) -> Result<i64, errors::Error> {
|
||||||
match get_stop_word(&word).await {
|
if get_stop_word(&word).await.is_err() {
|
||||||
Err(_) => return Err(errors::Error::WordInStopList),
|
return Err(errors::Error::WordInStopList);
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
let conn = open()?;
|
let conn = open()?;
|
||||||
let word_rowid =
|
let word_rowid =
|
||||||
@ -321,7 +405,7 @@ async fn add_word(word: &String) -> Result<i64, errors::Error> {
|
|||||||
Ok(word_rowid)
|
Ok(word_rowid)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_stop_word(stop_word: &String) -> Result<(), errors::Error> {
|
async fn get_stop_word(stop_word: &str) -> Result<(), errors::Error> {
|
||||||
let conn = open()?;
|
let conn = open()?;
|
||||||
match conn.execute_named(
|
match conn.execute_named(
|
||||||
"SELECT rowid FROM stop_words WHERE word = (:stop_word)",
|
"SELECT rowid FROM stop_words WHERE word = (:stop_word)",
|
||||||
@ -355,6 +439,7 @@ async fn add_relation(word_id: i64, msg_id: i64, message: &Message) -> Result<i6
|
|||||||
Ok(rowid)
|
Ok(rowid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused_must_use)]
|
||||||
pub(crate) async fn add_sentence(
|
pub(crate) async fn add_sentence(
|
||||||
message: &telegram_bot::Message,
|
message: &telegram_bot::Message,
|
||||||
mystem: &mut mystem::MyStem,
|
mystem: &mut mystem::MyStem,
|
||||||
@ -373,18 +458,22 @@ pub(crate) async fn add_sentence(
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Save stemmed words
|
// Save stemmed words
|
||||||
let words = mystem.stemming(text).await?;
|
debug!("Going to stem: {}", text);
|
||||||
|
let words = mystem.stemming(text)?;
|
||||||
conn.execute("BEGIN TRANSACTION", params![]);
|
conn.execute("BEGIN TRANSACTION", params![]);
|
||||||
for word in words {
|
for word in words {
|
||||||
match add_word(&word).await {
|
if word.lex.is_empty() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
match add_word(&word.lex[0].lex).await {
|
||||||
Ok(id) => {
|
Ok(id) => {
|
||||||
debug!("Added {}: rowid: {}", &word, id);
|
debug!("Added {}: rowid: {}", &word.lex[0].lex, id);
|
||||||
match add_relation(id, msg_rowid, message).await {
|
match add_relation(id, msg_rowid, message).await {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(e) => panic!("SQLITE3 Error: Relations failed: {:?}", e),
|
Err(e) => panic!("SQLITE3 Error: Relations failed: {:?}", e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(_) => debug!("Word {} is in stop list.", &word),
|
Err(_) => debug!("Word {} is in a stop list.", &word.lex[0].lex),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
conn.execute("END TRANSACTION", params![]);
|
conn.execute("END TRANSACTION", params![]);
|
||||||
@ -424,4 +513,55 @@ pub(crate) async fn get_top(
|
|||||||
Ok(top)
|
Ok(top)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn get_global_top() -> Result<Vec<TopWord>, errors::Error> {
|
||||||
|
let conn = open()?;
|
||||||
|
let mut stmt = conn.prepare_cached(
|
||||||
|
"
|
||||||
|
SELECT w.word, COUNT(*) as count FROM relations r
|
||||||
|
LEFT JOIN word w ON w.id = r.word_id
|
||||||
|
GROUP BY w.word
|
||||||
|
ORDER BY count DESC
|
||||||
|
LIMIT 50
|
||||||
|
",
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let mut rows = stmt.query_named(named_params! {})?;
|
||||||
|
let mut top = Vec::new();
|
||||||
|
|
||||||
|
while let Some(row) = rows.next()? {
|
||||||
|
top.push(TopWord {
|
||||||
|
word: row.get(0)?,
|
||||||
|
count: row.get(1)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Ok(top)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn get_conf_top(
|
||||||
|
message: &telegram_bot::Message,
|
||||||
|
) -> Result<Vec<TopWord>, errors::Error> {
|
||||||
|
let conf_id = i64::from(message.chat.id());
|
||||||
|
|
||||||
|
let conn = open()?;
|
||||||
|
let mut stmt = conn.prepare_cached(
|
||||||
|
"
|
||||||
|
SELECT w.word, COUNT(*) as count FROM relations r
|
||||||
|
LEFT JOIN word w ON w.id = r.word_id
|
||||||
|
WHERE r.conf_id = :conf_id
|
||||||
|
GROUP BY w.word
|
||||||
|
ORDER BY count DESC
|
||||||
|
LIMIT 10
|
||||||
|
",
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let mut rows = stmt.query_named(named_params! {":conf_id": conf_id})?;
|
||||||
|
let mut top = Vec::new();
|
||||||
|
|
||||||
|
while let Some(row) = rows.next()? {
|
||||||
|
top.push(TopWord {
|
||||||
|
word: row.get(0)?,
|
||||||
|
count: row.get(1)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Ok(top)
|
||||||
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use mystem::AppError as mystem_error;
|
||||||
use reqwest::Error as reqwest_error;
|
use reqwest::Error as reqwest_error;
|
||||||
use rusqlite::Error as sqlite_error;
|
use rusqlite::Error as sqlite_error;
|
||||||
use serde_json::Error as serde_error;
|
use serde_json::Error as serde_error;
|
||||||
@ -18,45 +19,84 @@ pub enum Error {
|
|||||||
FileNotFound,
|
FileNotFound,
|
||||||
JsonParseError(serde_error),
|
JsonParseError(serde_error),
|
||||||
PopenError(popen_error),
|
PopenError(popen_error),
|
||||||
|
MystemError(mystem_error),
|
||||||
|
SQLBannedCommand(String),
|
||||||
|
SQLInvalidCommand,
|
||||||
|
SQLResultTooLong(String),
|
||||||
|
CodeHighlightningError,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
impl fmt::Display for Error {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "An error occurred.")
|
write!(f, "An error occurred.")
|
||||||
|
// match self {
|
||||||
|
// _ => write!(f, "An error occurred."),
|
||||||
|
// // Error::UserNotFound => {}
|
||||||
|
// // Error::SQLITE3Error(_) => {}
|
||||||
|
// // Error::TelegramError(_) => {}
|
||||||
|
// // Error::ReqwestError(_) => {}
|
||||||
|
// // Error::ConfNotFound => {}
|
||||||
|
// // Error::WordNotFound => {}
|
||||||
|
// // Error::WordInStopList => {}
|
||||||
|
// // Error::IOError(_) => {}
|
||||||
|
// // Error::FileNotFound => {}
|
||||||
|
// // Error::JsonParseError(_) => {}
|
||||||
|
// // Error::PopenError(_) => {}
|
||||||
|
// // Error::MystemError(_) => {}
|
||||||
|
// // Error::SQLBannedCommand(_) => {}
|
||||||
|
// // Error::SQLInvalidCommand => {}
|
||||||
|
// // Error::SQLResultTooLong(_) => {}
|
||||||
|
// // Error::CodeHighlightningError(Help) => write!(f, "Code highlighter.\
|
||||||
|
// // <b>Usage</b><pre>/CODE\
|
||||||
|
// // #<theme>\
|
||||||
|
// // <CODE>\
|
||||||
|
// // #<lang></pre>\
|
||||||
|
// // \
|
||||||
|
// // List of themes:\
|
||||||
|
// // .")
|
||||||
|
// Error::CodeHighlightningError(help) => write!(f, "{}", help.description)
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<sqlite_error> for Error {
|
impl From<sqlite_error> for Error {
|
||||||
fn from(e: sqlite_error) -> Error {
|
fn from(e: sqlite_error) -> Error {
|
||||||
return Error::SQLITE3Error(e);
|
Error::SQLITE3Error(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<tg_error> for Error {
|
impl From<tg_error> for Error {
|
||||||
fn from(e: tg_error) -> Error {
|
fn from(e: tg_error) -> Error {
|
||||||
return Error::TelegramError(e);
|
Error::TelegramError(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<reqwest_error> for Error {
|
impl From<reqwest_error> for Error {
|
||||||
fn from(e: reqwest_error) -> Error {
|
fn from(e: reqwest_error) -> Error {
|
||||||
return Error::ReqwestError(e);
|
Error::ReqwestError(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<io_error> for Error {
|
impl From<io_error> for Error {
|
||||||
fn from(e: io_error) -> Error {
|
fn from(e: io_error) -> Error {
|
||||||
return Error::IOError(e);
|
Error::IOError(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<serde_error> for Error {
|
impl From<serde_error> for Error {
|
||||||
fn from(e: serde_error) -> Error {
|
fn from(e: serde_error) -> Error {
|
||||||
return Error::JsonParseError(e);
|
Error::JsonParseError(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<popen_error> for Error {
|
impl From<popen_error> for Error {
|
||||||
fn from(e: popen_error) -> Error {
|
fn from(e: popen_error) -> Error {
|
||||||
return Error::PopenError(e);
|
Error::PopenError(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<mystem_error> for Error {
|
||||||
|
fn from(e: mystem_error) -> Error {
|
||||||
|
Error::MystemError(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
160
src/handlers.rs
160
src/handlers.rs
@ -1,10 +1,14 @@
|
|||||||
use telegram_bot::*;
|
//use crate::commands::Command;
|
||||||
use crate::mystem::MyStem;
|
use crate::commands::{
|
||||||
use crate::errors;
|
Code, ConfTop, Execute, GlobalTop, Here, Markov, MarkovAll, Omedeto, Scheme, Sql, Top,
|
||||||
|
};
|
||||||
use crate::db;
|
use crate::db;
|
||||||
use crate::commands;
|
use crate::errors;
|
||||||
use crate::utils;
|
use crate::utils;
|
||||||
|
use mystem::MyStem;
|
||||||
|
use telegram_bot::*;
|
||||||
|
|
||||||
|
include!("../assets/help_text.rs");
|
||||||
|
|
||||||
pub async fn handler(
|
pub async fn handler(
|
||||||
api: Api,
|
api: Api,
|
||||||
@ -13,7 +17,6 @@ pub async fn handler(
|
|||||||
mystem: &mut MyStem,
|
mystem: &mut MyStem,
|
||||||
me: User,
|
me: User,
|
||||||
) -> Result<(), errors::Error> {
|
) -> Result<(), errors::Error> {
|
||||||
|
|
||||||
match message.kind {
|
match message.kind {
|
||||||
MessageKind::Text { ref data, .. } => {
|
MessageKind::Text { ref data, .. } => {
|
||||||
let title = utils::get_title(&message);
|
let title = utils::get_title(&message);
|
||||||
@ -23,16 +26,141 @@ pub async fn handler(
|
|||||||
title,
|
title,
|
||||||
&message.from.id,
|
&message.from.id,
|
||||||
&message.from.first_name,
|
&message.from.first_name,
|
||||||
data
|
data.replace("\n", " ")
|
||||||
);
|
);
|
||||||
db::add_sentence(&message, mystem).await?;
|
|
||||||
match data.as_str() {
|
let cleaned_message = data.replace(&format!("@{}", me.clone().username.unwrap()), "");
|
||||||
"/here" => commands::here(api, message).await?,
|
match cleaned_message.as_str() {
|
||||||
"/top" => commands::top(api, message).await?,
|
s if s.to_string().starts_with("/code") => {
|
||||||
"/stat" => commands::top(api, message).await?,
|
match {
|
||||||
"/markov_all" => commands::markov_all(api, message).await?,
|
Code {
|
||||||
"/markov" => commands::markov(api, message).await?,
|
data: s.replace("/code", ""),
|
||||||
_ => (),
|
}
|
||||||
|
.exec_with_result(&api, &message)
|
||||||
|
.await
|
||||||
|
} {
|
||||||
|
Ok(path) => {
|
||||||
|
let mut cnt_lines = 0;
|
||||||
|
for _ in s.lines() {
|
||||||
|
cnt_lines = cnt_lines + 1;
|
||||||
|
}
|
||||||
|
let mut cnt_chars = 0;
|
||||||
|
for _ in s.chars() {
|
||||||
|
cnt_chars = cnt_chars + 1;
|
||||||
|
}
|
||||||
|
let file = InputFileUpload::with_path(path.clone());
|
||||||
|
info!("lines: {}, chars: {}", cnt_lines, cnt_chars);
|
||||||
|
// api.send(message.chat.document(&file)).await?;
|
||||||
|
//
|
||||||
|
// // Send an image from disk
|
||||||
|
if cnt_chars > 4000 {
|
||||||
|
let _ = api
|
||||||
|
.send(message.text_reply(CODE_HELP).parse_mode(ParseMode::Html))
|
||||||
|
.await?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
if cnt_lines < 81 {
|
||||||
|
api.send(message.chat.photo(&file)).await?;
|
||||||
|
} else {
|
||||||
|
api.send(message.chat.document(&file)).await?;
|
||||||
|
}
|
||||||
|
//debug!("{:#?}", formatter);
|
||||||
|
let _ = std::fs::remove_file(&path);
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
let _ = api
|
||||||
|
.send(message.text_reply(CODE_HELP).parse_mode(ParseMode::Html))
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s if s.contains("/here")
|
||||||
|
|| s.contains("@here")
|
||||||
|
|| s.contains("/хере")
|
||||||
|
|| s.contains("@хере")
|
||||||
|
|| s.contains("@all")
|
||||||
|
|| s.contains("\"руку")
|
||||||
|
|| s.contains("\"хере") =>
|
||||||
|
{
|
||||||
|
db::add_sentence(&message, mystem).await?;
|
||||||
|
Here {
|
||||||
|
data: "".to_string(),
|
||||||
|
}
|
||||||
|
.exec(&api, &message)
|
||||||
|
.await?
|
||||||
|
}
|
||||||
|
s if s.to_string().starts_with("/sql") => match {
|
||||||
|
Sql {
|
||||||
|
data: s.replace("/sql ", ""),
|
||||||
|
}
|
||||||
|
.exec_with_result(&api, &message)
|
||||||
|
.await
|
||||||
|
} {
|
||||||
|
Ok(msg) => {
|
||||||
|
let _ = api
|
||||||
|
.send(message.text_reply(msg).parse_mode(ParseMode::Html))
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
let _ = api
|
||||||
|
.send(
|
||||||
|
message
|
||||||
|
.text_reply(format!("Error: {:#?}", e))
|
||||||
|
.parse_mode(ParseMode::Html),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/top" | "/stat" => {
|
||||||
|
Top {
|
||||||
|
data: "".to_string(),
|
||||||
|
}
|
||||||
|
.exec(&api, &message)
|
||||||
|
.await?
|
||||||
|
}
|
||||||
|
"/global_top" | "/global_stat" => {
|
||||||
|
GlobalTop {
|
||||||
|
data: "".to_string(),
|
||||||
|
}
|
||||||
|
.exec(&api, &message)
|
||||||
|
.await?
|
||||||
|
}
|
||||||
|
"/conf_stat" | "/conf_top" => {
|
||||||
|
ConfTop {
|
||||||
|
data: "".to_string(),
|
||||||
|
}
|
||||||
|
.exec(&api, &message)
|
||||||
|
.await?
|
||||||
|
}
|
||||||
|
"/markov_all" => {
|
||||||
|
MarkovAll {
|
||||||
|
data: "".to_string(),
|
||||||
|
}
|
||||||
|
.exec(&api, &message)
|
||||||
|
.await?
|
||||||
|
}
|
||||||
|
"/markov" => {
|
||||||
|
Markov {
|
||||||
|
data: "".to_string(),
|
||||||
|
}
|
||||||
|
.exec(&api, &message)
|
||||||
|
.await?
|
||||||
|
}
|
||||||
|
s if s == "/scheme" || s == "/schema" => {
|
||||||
|
Scheme {
|
||||||
|
data: "".to_string(),
|
||||||
|
}
|
||||||
|
.exec(&api, &message)
|
||||||
|
.await?
|
||||||
|
}
|
||||||
|
"/omedeto" => {
|
||||||
|
Omedeto {
|
||||||
|
data: "".to_string(),
|
||||||
|
}
|
||||||
|
.exec_mystem(&api, &message, mystem)
|
||||||
|
.await?
|
||||||
|
}
|
||||||
|
_ => db::add_sentence(&message, mystem).await?,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MessageKind::Photo { ref caption, .. } => {
|
MessageKind::Photo { ref caption, .. } => {
|
||||||
@ -43,7 +171,7 @@ pub async fn handler(
|
|||||||
title,
|
title,
|
||||||
&message.from.id,
|
&message.from.id,
|
||||||
&message.from.first_name,
|
&message.from.first_name,
|
||||||
caption.clone().unwrap_or("NO_TITLE".to_string())
|
caption.clone().unwrap_or_else(|| "NO_TITLE".to_string())
|
||||||
);
|
);
|
||||||
utils::get_files(api, message, token).await?;
|
utils::get_files(api, message, token).await?;
|
||||||
}
|
}
|
||||||
@ -56,7 +184,7 @@ pub async fn handler(
|
|||||||
title,
|
title,
|
||||||
&message.from.id,
|
&message.from.id,
|
||||||
&message.from.first_name,
|
&message.from.first_name,
|
||||||
caption.clone().unwrap_or("NO_TITLE".to_string())
|
caption.clone().unwrap_or_else(|| "NO_TITLE".to_string())
|
||||||
);
|
);
|
||||||
utils::get_files(api, message, token).await?;
|
utils::get_files(api, message, token).await?;
|
||||||
}
|
}
|
||||||
|
58
src/main.rs
58
src/main.rs
@ -1,4 +1,6 @@
|
|||||||
|
#![allow(unreachable_code)]
|
||||||
use std::{env, process};
|
use std::{env, process};
|
||||||
|
use tokio::time::{delay_for, Duration};
|
||||||
|
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use telegram_bot::*;
|
use telegram_bot::*;
|
||||||
@ -9,20 +11,29 @@ use env_logger::Env;
|
|||||||
mod commands;
|
mod commands;
|
||||||
mod db;
|
mod db;
|
||||||
mod errors;
|
mod errors;
|
||||||
mod mystem;
|
|
||||||
mod utils;
|
|
||||||
mod handlers;
|
mod handlers;
|
||||||
|
mod utils;
|
||||||
|
|
||||||
use mystem::MyStem;
|
use mystem::MyStem;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<(), errors::Error> {
|
async fn main() -> Result<(), errors::Error> {
|
||||||
env_logger::from_env(Env::default().default_filter_or("info")).init();
|
env_logger::Builder::from_env(Env::default().default_filter_or("info")).init();
|
||||||
let mut mystem = MyStem::new()?;
|
let mut mystem = match MyStem::new() {
|
||||||
|
Ok(mystem) => mystem,
|
||||||
|
Err(e) => {
|
||||||
|
error!("MyStem init error. {:?}", e);
|
||||||
|
process::exit(0x0002);
|
||||||
|
}
|
||||||
|
};
|
||||||
match db::update_scheme() {
|
match db::update_scheme() {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(e) => panic!("Database error: {:?}", e),
|
Err(e) => panic!("Database error: {:?}", e),
|
||||||
}
|
}
|
||||||
|
match db::load_stopwords() {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(e) => panic!("Database error: {:?}", e),
|
||||||
|
}
|
||||||
let token = match env::var("TELEGRAM_BOT_TOKEN") {
|
let token = match env::var("TELEGRAM_BOT_TOKEN") {
|
||||||
Ok(token) => token,
|
Ok(token) => token,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
@ -33,14 +44,39 @@ async fn main() -> Result<(), errors::Error> {
|
|||||||
let api = Api::new(token.clone());
|
let api = Api::new(token.clone());
|
||||||
let mut stream = api.stream();
|
let mut stream = api.stream();
|
||||||
let me = api.send(GetMe).await?;
|
let me = api.send(GetMe).await?;
|
||||||
info!("GetMe result: Username: {}, First Name: {}, ID {}", me.username.as_ref().unwrap(), me.first_name, me.id);
|
info!(
|
||||||
while let Some(update) = stream.next().await {
|
"GetMe result: Username: {}, First Name: {}, ID {}",
|
||||||
let update = update?;
|
me.username.as_ref().unwrap(),
|
||||||
if let UpdateKind::Message(message) = update.kind {
|
me.first_name,
|
||||||
db::add_conf(message.clone()).await?;
|
me.id
|
||||||
db::add_user(message.clone()).await?;
|
);
|
||||||
handlers::handler(api.clone(), message, token.clone(), &mut mystem, me.clone()).await?;
|
loop {
|
||||||
|
while let Some(update) = stream.next().await {
|
||||||
|
match update {
|
||||||
|
Ok(u) => {
|
||||||
|
if let UpdateKind::Message(message) = u.kind {
|
||||||
|
db::add_conf(message.clone()).await?;
|
||||||
|
db::add_user(message.clone()).await?;
|
||||||
|
match handlers::handler(
|
||||||
|
api.clone(),
|
||||||
|
message,
|
||||||
|
token.clone(),
|
||||||
|
&mut mystem,
|
||||||
|
me.clone(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(e) => warn!("An error occurred handling command. {:?}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
warn!("Telegram API Error: {:?}", e);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
delay_for(Duration::from_secs(2)).await;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1,73 +0,0 @@
|
|||||||
use crate::errors;
|
|
||||||
use serde_json::Value;
|
|
||||||
use std::io::{Error, Write, BufReader, prelude::*};
|
|
||||||
use subprocess::{Popen, PopenConfig, PopenError, Redirection};
|
|
||||||
|
|
||||||
pub struct MyStem {
|
|
||||||
pub process: Popen,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MyStem {
|
|
||||||
pub fn new() -> Result<Self, PopenError> {
|
|
||||||
Ok(Self {
|
|
||||||
process: MyStem::open_process()?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn open_process() -> Result<Popen, PopenError> {
|
|
||||||
Popen::create(
|
|
||||||
&["mystem", "-d", "--format", "json"],
|
|
||||||
PopenConfig {
|
|
||||||
stdout: Redirection::Pipe,
|
|
||||||
stdin: Redirection::Pipe,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn terminate(&mut self) -> Result<(), Error> {
|
|
||||||
self.process.terminate()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unused_must_use)]
|
|
||||||
pub async fn stemming(&mut self, text: String) -> Result<Vec<String>, errors::Error> {
|
|
||||||
if let Some(exit_status) = self.process.poll() {
|
|
||||||
warn!(
|
|
||||||
"MyStem process exited with: {:?}. Restarting...",
|
|
||||||
exit_status
|
|
||||||
);
|
|
||||||
self.process = MyStem::open_process()?;
|
|
||||||
}
|
|
||||||
let mut words: Vec<String> = vec![];
|
|
||||||
let clean_text = format!("{}{}", text.trim(), "\n");
|
|
||||||
self.process
|
|
||||||
.stdin
|
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
|
||||||
.write(clean_text.as_bytes());
|
|
||||||
let mut contents = String::new();
|
|
||||||
let mut buf_reader = BufReader::new(self.process.stdout.as_ref().unwrap());
|
|
||||||
buf_reader.read_line(&mut contents);
|
|
||||||
|
|
||||||
match Some(contents) {
|
|
||||||
Some(contents) => {
|
|
||||||
let v: Vec<Value> = match serde_json::from_str(contents.as_str()) {
|
|
||||||
Ok(val) => val,
|
|
||||||
Err(_) => return Ok(vec![]),
|
|
||||||
};
|
|
||||||
for i in v {
|
|
||||||
words.push(i["analysis"][0]["lex"].to_string().replace("\"", ""));
|
|
||||||
}
|
|
||||||
words.retain(|x| x != "null");
|
|
||||||
debug!(
|
|
||||||
"Mystem PID: {}. Parsed words: {}.",
|
|
||||||
self.process.pid().unwrap(),
|
|
||||||
words.join(", ")
|
|
||||||
);
|
|
||||||
Ok(words)
|
|
||||||
}
|
|
||||||
None => return Ok(vec![]),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -13,15 +13,14 @@ pub(crate) fn get_title(message: &Message) -> String {
|
|||||||
match &message.chat {
|
match &message.chat {
|
||||||
MessageChat::Supergroup(chat) => chat.title.clone(),
|
MessageChat::Supergroup(chat) => chat.title.clone(),
|
||||||
MessageChat::Group(chat) => chat.title.clone(),
|
MessageChat::Group(chat) => chat.title.clone(),
|
||||||
MessageChat::Private(_) => format!("PRIVATE"),
|
MessageChat::Private(_) => "PRIVATE".to_string(),
|
||||||
_ => "PRIVATE".to_string(),
|
_ => "PRIVATE".to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn create_dir(dir: &String) -> () {
|
pub(crate) async fn create_dir(dir: &str) {
|
||||||
match fs_create_dir(dir) {
|
if fs_create_dir(dir).is_ok() {
|
||||||
Ok(_) => info!("Dir {} created.", dir),
|
info!("Dir {} created.", dir)
|
||||||
Err(_) => (),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user