mirror of
https://github.com/house-of-vanity/desubot.git
synced 2025-07-08 13:04:06 +00:00
Compare commits
81 Commits
Author | SHA1 | Date | |
---|---|---|---|
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 |
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 }}"
|
35
.github/workflows/build-push.yml
vendored
35
.github/workflows/build-push.yml
vendored
@ -31,6 +31,41 @@ jobs:
|
||||
with:
|
||||
name: 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:
|
||||
name: Publish release
|
||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -6,3 +6,6 @@ memory.sqlite3
|
||||
/video
|
||||
/voice
|
||||
/.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]
|
||||
name = "desubot"
|
||||
version = "0.1.0"
|
||||
version = "0.5.9"
|
||||
authors = ["AB <ab@hexor.ru>"]
|
||||
edition = "2018"
|
||||
|
||||
@ -9,26 +9,34 @@ edition = "2018"
|
||||
[dependencies]
|
||||
bytes = "0.5"
|
||||
tokio = { version = "0.2", features = ["full"]}
|
||||
|
||||
tracing = "0.1.9"
|
||||
tracing-futures = "0.2"
|
||||
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-tls = { version = "0.4", optional = true }
|
||||
futures = "0.3"
|
||||
hyper-rustls = { version = "0.19", optional = true }
|
||||
|
||||
rusqlite = { version = "0.24.1", features = ["bundled"]}
|
||||
rusqlite = { version = "0.24.2", features = ["bundled"]}
|
||||
html-escape = "0.2"
|
||||
regex = "1"
|
||||
reqwest = "0.10.9"
|
||||
uuid = { version = "0.8", features = ["v4"] }
|
||||
sha1 = "*"
|
||||
sha1 = "0.6.0"
|
||||
env_logger = "0.7"
|
||||
log = { version = "^0.4.5", features = ["std"] }
|
||||
subprocess = "0.2.6"
|
||||
serde_json = "1.0"
|
||||
markov = "1.1.0"
|
||||
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
|
||||
|
@ -1,3 +1,5 @@
|
||||
[](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.
|
||||
|
||||
@ -7,9 +9,12 @@ Telegram bot with light group statistic and heavy spy features.
|
||||
* /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.
|
||||
|
||||
== TODO ==
|
||||
* 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
|
||||
|
||||
== 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.
|
||||
|
||||
## 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>";
|
427
assets/stop-words.txt
Normal file
427
assets/stop-words.txt
Normal file
@ -0,0 +1,427 @@
|
||||
а
|
||||
в
|
||||
г
|
||||
е
|
||||
ж
|
||||
и
|
||||
к
|
||||
м
|
||||
о
|
||||
с
|
||||
т
|
||||
у
|
||||
я
|
||||
бы
|
||||
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
|
640
src/commands.rs
640
src/commands.rs
@ -1,13 +1,272 @@
|
||||
#![allow(unused_variables)]
|
||||
use crate::db;
|
||||
use crate::errors::Error;
|
||||
use crate::errors::Error::{CodeHighlightningError, SQLITE3Error, SQLInvalidCommand};
|
||||
use async_trait::async_trait;
|
||||
use html_escape::encode_text;
|
||||
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 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::{Api, Message, ParseMode};
|
||||
use telegram_bot::*;
|
||||
use telegram_bot::{Api, Message, ParseMode, UserId};
|
||||
|
||||
pub(crate) async fn here(api: Api, message: Message) -> Result<(), Error> {
|
||||
let members: Vec<telegram_bot::User> = db::get_members(message.chat.id()).unwrap();
|
||||
include!("../assets/help_text.rs");
|
||||
|
||||
pub struct Here {
|
||||
pub data: String,
|
||||
}
|
||||
pub struct Top {
|
||||
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()),
|
||||
};
|
||||
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(())
|
||||
}
|
||||
|
||||
async fn exec_with_result(&self, api: &Api, message: &Message) -> Result<String, Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
async fn exec_mystem(
|
||||
&self,
|
||||
api: &Api,
|
||||
message: &Message,
|
||||
mystem: &mut MyStem,
|
||||
) -> Result<(), Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Execute for Sql {
|
||||
async fn exec(&self, api: &Api, message: &Message) -> Result<(), Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
async fn exec_with_result(&self, api: &Api, message: &Message) -> Result<String, Error> {
|
||||
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());
|
||||
}
|
||||
@ -31,12 +290,27 @@ pub(crate) async fn here(api: Api, message: Message) -> Result<(), Error> {
|
||||
Ok(_) => debug!("/here command sent to {}", message.chat.id()),
|
||||
Err(_) => warn!("/here 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!()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn top(api: Api, message: Message) -> Result<(), Error> {
|
||||
#[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;
|
||||
@ -55,13 +329,28 @@ pub(crate) async fn top(api: Api, message: Message) -> Result<(), Error> {
|
||||
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(())
|
||||
}
|
||||
|
||||
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!()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn markov_all(api: Api, message: Message) -> Result<(), Error> {
|
||||
let messages = db::get_random_messages().await?;
|
||||
#[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();
|
||||
@ -79,16 +368,33 @@ pub(crate) async fn markov_all(api: Api, message: Message) -> Result<(), Error>
|
||||
//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!()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn markov(api: Api, message: Message) -> Result<(), Error> {
|
||||
let messages = db::get_random_messages_group(&message).await?;
|
||||
#[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());
|
||||
msg = format!("{} {}", msg, sentences.pop().unwrap_or(" ".into()));
|
||||
}
|
||||
match api
|
||||
.send(message.text_reply(msg.trim()).parse_mode(ParseMode::Html))
|
||||
@ -100,4 +406,312 @@ pub(crate) async fn markov(api: Api, message: Message) -> Result<(), Error> {
|
||||
//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()),
|
||||
);
|
||||
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(())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Execute for Code {
|
||||
async fn exec(&self, api: &Api, message: &Message) -> Result<(), Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
async fn exec_with_result(&self, api: &Api, message: &Message) -> Result<String, Error> {
|
||||
let mut lines: Vec<String> = self
|
||||
.data
|
||||
.trim()
|
||||
.split("\n")
|
||||
.map(|s| s.to_string())
|
||||
.collect();
|
||||
let last_line = &lines[lines.len() - 1];
|
||||
|
||||
let tags = last_line
|
||||
.trim()
|
||||
.split(|s| s == ' ' || s == '\n')
|
||||
.filter(|s| s.starts_with("#"))
|
||||
.map(|s| s.to_string().replace("#", ""))
|
||||
.map(|s| s.to_string().replace("_", " "))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
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("Dracula").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())
|
||||
}
|
||||
|
||||
async fn exec_mystem(
|
||||
&self,
|
||||
api: &Api,
|
||||
message: &Message,
|
||||
mystem: &mut MyStem,
|
||||
) -> Result<(), Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
151
src/db.rs
151
src/db.rs
@ -1,5 +1,6 @@
|
||||
#[allow(unused_mut)]
|
||||
#[allow(dead_code)]
|
||||
use crate::errors;
|
||||
use crate::mystem;
|
||||
use crate::utils;
|
||||
use rusqlite::{named_params, params, Connection, Error, Result};
|
||||
use std::time::SystemTime;
|
||||
@ -35,6 +36,30 @@ pub(crate) fn update_scheme() -> Result<()> {
|
||||
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> {
|
||||
let conn = open()?;
|
||||
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)
|
||||
} else {
|
||||
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)?,
|
||||
})
|
||||
}
|
||||
if confs.len() == 0 {
|
||||
if confs.is_empty() {
|
||||
Err(errors::Error::ConfNotFound)
|
||||
} else {
|
||||
Ok(confs[0].clone())
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn get_confs() -> Result<Vec<Conf>> {
|
||||
let conn = open()?;
|
||||
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)
|
||||
}
|
||||
*/
|
||||
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 mut stmt = conn.prepare_cached("SELECT text FROM messages ORDER BY RANDOM() LIMIT 50")?;
|
||||
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)
|
||||
}
|
||||
|
||||
pub(crate) async fn get_random_messages_group(
|
||||
message: &telegram_bot::Message
|
||||
pub(crate) async fn get_messages_random_group(
|
||||
message: &telegram_bot::Message,
|
||||
) -> Result<Vec<String>, Error> {
|
||||
let conf_id = i64::from(message.chat.id());
|
||||
let conn = open()?;
|
||||
let mut stmt = conn.prepare_cached("
|
||||
let mut stmt = conn.prepare_cached(
|
||||
"
|
||||
SELECT m.text FROM messages m
|
||||
LEFT JOIN relations r ON r.msg_id = m.id
|
||||
WHERE r.conf_id = :conf_id
|
||||
ORDER BY RANDOM() LIMIT 50
|
||||
"
|
||||
",
|
||||
)?;
|
||||
let mut rows = stmt.query_named(named_params! {":conf_id": conf_id})?;
|
||||
let mut messages = Vec::new();
|
||||
@ -135,18 +161,74 @@ pub(crate) async fn get_random_messages_group(
|
||||
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 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
|
||||
JOIN user u
|
||||
ON u.id = r.user_id
|
||||
LEFT JOIN conf c
|
||||
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 users = Vec::new();
|
||||
|
||||
@ -169,7 +251,7 @@ pub(crate) async fn add_conf(message: Message) -> Result<(), Error> {
|
||||
|
||||
match get_conf(message.chat.id()) {
|
||||
Ok(_) => {
|
||||
//info!("Group found: {:?}", message.chat.id());
|
||||
debug!("Group found: {:?}", message.chat.id());
|
||||
let update = Conf {
|
||||
id: message.chat.id(),
|
||||
title,
|
||||
@ -183,10 +265,10 @@ pub(crate) async fn add_conf(message: Message) -> Result<(), Error> {
|
||||
id = :id",
|
||||
)?;
|
||||
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(_) => {
|
||||
//info!("Group didn't found: {:?}", message.chat.id());
|
||||
debug!("Group didn't found: {:?}", message.chat.id());
|
||||
|
||||
let update = Conf {
|
||||
id: message.chat.id(),
|
||||
@ -205,7 +287,7 @@ pub(crate) async fn add_conf(message: Message) -> Result<(), Error> {
|
||||
(":title", &update.title),
|
||||
(":date", &unix_time),
|
||||
])?;
|
||||
//info!("Conf {:?} added: {:?}", update.title, get_conf(update.id));
|
||||
debug!("Conf {:?} added: {:?}", update.title, get_conf(update.id));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@ -238,7 +320,11 @@ pub(crate) async fn add_user(message: Message) -> Result<(), Error> {
|
||||
(":first_name", &update.first_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(_) => {
|
||||
let unix_time = SystemTime::now()
|
||||
@ -265,7 +351,7 @@ pub(crate) async fn add_user(message: Message) -> Result<(), Error> {
|
||||
(":last_name", &user.last_name),
|
||||
(":date", &unix_time),
|
||||
])?;
|
||||
//println!("User added: {:?}", user);
|
||||
debug!("User added: {:?}", user);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@ -300,14 +386,12 @@ pub(crate) async fn get_file(file_id: String) -> Result<i64, errors::Error> {
|
||||
Ok(id) => Ok(id),
|
||||
Err(_) => Err(errors::Error::FileNotFound),
|
||||
};
|
||||
|
||||
file_rowid
|
||||
}
|
||||
|
||||
async fn add_word(word: &String) -> Result<i64, errors::Error> {
|
||||
match get_stop_word(&word).await {
|
||||
Err(_) => return Err(errors::Error::WordInStopList),
|
||||
_ => {}
|
||||
async fn add_word(word: &str) -> Result<i64, errors::Error> {
|
||||
if get_stop_word(&word).await.is_err() {
|
||||
return Err(errors::Error::WordInStopList);
|
||||
}
|
||||
let conn = open()?;
|
||||
let word_rowid =
|
||||
@ -321,7 +405,7 @@ async fn add_word(word: &String) -> Result<i64, errors::Error> {
|
||||
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()?;
|
||||
match conn.execute_named(
|
||||
"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)
|
||||
}
|
||||
|
||||
#[allow(unused_must_use)]
|
||||
pub(crate) async fn add_sentence(
|
||||
message: &telegram_bot::Message,
|
||||
mystem: &mut mystem::MyStem,
|
||||
@ -373,18 +458,22 @@ pub(crate) async fn add_sentence(
|
||||
};
|
||||
|
||||
// Save stemmed words
|
||||
let words = mystem.stemming(text).await?;
|
||||
debug!("Going to stem: {}", text);
|
||||
let words = mystem.stemming(text)?;
|
||||
conn.execute("BEGIN TRANSACTION", params![]);
|
||||
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) => {
|
||||
debug!("Added {}: rowid: {}", &word, id);
|
||||
debug!("Added {}: rowid: {}", &word.lex[0].lex, id);
|
||||
match add_relation(id, msg_rowid, message).await {
|
||||
Ok(_) => {}
|
||||
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![]);
|
||||
@ -423,5 +512,3 @@ pub(crate) async fn get_top(
|
||||
}
|
||||
Ok(top)
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
use mystem::AppError as mystem_error;
|
||||
use reqwest::Error as reqwest_error;
|
||||
use rusqlite::Error as sqlite_error;
|
||||
use serde_json::Error as serde_error;
|
||||
@ -18,45 +19,84 @@ pub enum Error {
|
||||
FileNotFound,
|
||||
JsonParseError(serde_error),
|
||||
PopenError(popen_error),
|
||||
MystemError(mystem_error),
|
||||
SQLBannedCommand(String),
|
||||
SQLInvalidCommand,
|
||||
SQLResultTooLong(String),
|
||||
CodeHighlightningError,
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
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 {
|
||||
fn from(e: sqlite_error) -> Error {
|
||||
return Error::SQLITE3Error(e);
|
||||
Error::SQLITE3Error(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<tg_error> for Error {
|
||||
fn from(e: tg_error) -> Error {
|
||||
return Error::TelegramError(e);
|
||||
Error::TelegramError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<reqwest_error> for Error {
|
||||
fn from(e: reqwest_error) -> Error {
|
||||
return Error::ReqwestError(e);
|
||||
Error::ReqwestError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<io_error> for Error {
|
||||
fn from(e: io_error) -> Error {
|
||||
return Error::IOError(e);
|
||||
Error::IOError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<serde_error> for Error {
|
||||
fn from(e: serde_error) -> Error {
|
||||
return Error::JsonParseError(e);
|
||||
Error::JsonParseError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<popen_error> for 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)
|
||||
}
|
||||
}
|
||||
|
147
src/handlers.rs
147
src/handlers.rs
@ -1,10 +1,12 @@
|
||||
use telegram_bot::*;
|
||||
use crate::mystem::MyStem;
|
||||
use crate::errors;
|
||||
//use crate::commands::Command;
|
||||
use crate::commands::{Code, Execute, Here, Markov, MarkovAll, Omedeto, Scheme, Sql, Top};
|
||||
use crate::db;
|
||||
use crate::commands;
|
||||
use crate::errors;
|
||||
use crate::utils;
|
||||
use mystem::MyStem;
|
||||
use telegram_bot::*;
|
||||
|
||||
include!("../assets/help_text.rs");
|
||||
|
||||
pub async fn handler(
|
||||
api: Api,
|
||||
@ -13,7 +15,6 @@ pub async fn handler(
|
||||
mystem: &mut MyStem,
|
||||
me: User,
|
||||
) -> Result<(), errors::Error> {
|
||||
|
||||
match message.kind {
|
||||
MessageKind::Text { ref data, .. } => {
|
||||
let title = utils::get_title(&message);
|
||||
@ -23,16 +24,132 @@ pub async fn handler(
|
||||
title,
|
||||
&message.from.id,
|
||||
&message.from.first_name,
|
||||
data
|
||||
data.replace("\n", " ")
|
||||
);
|
||||
|
||||
let cleaned_message = data.replace(&format!("@{}", me.clone().username.unwrap()), "");
|
||||
match cleaned_message.as_str() {
|
||||
s if s.to_string().starts_with("/code") => {
|
||||
match {
|
||||
Code {
|
||||
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("\"хере") =>
|
||||
{
|
||||
db::add_sentence(&message, mystem).await?;
|
||||
match data.as_str() {
|
||||
"/here" => commands::here(api, message).await?,
|
||||
"/top" => commands::top(api, message).await?,
|
||||
"/stat" => commands::top(api, message).await?,
|
||||
"/markov_all" => commands::markov_all(api, message).await?,
|
||||
"/markov" => commands::markov(api, message).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" => {
|
||||
Top {
|
||||
data: "".to_string(),
|
||||
}
|
||||
.exec(&api, &message)
|
||||
.await?
|
||||
}
|
||||
"/stat" => {
|
||||
Top {
|
||||
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, .. } => {
|
||||
@ -43,7 +160,7 @@ pub async fn handler(
|
||||
title,
|
||||
&message.from.id,
|
||||
&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?;
|
||||
}
|
||||
@ -56,7 +173,7 @@ pub async fn handler(
|
||||
title,
|
||||
&message.from.id,
|
||||
&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?;
|
||||
}
|
||||
|
44
src/main.rs
44
src/main.rs
@ -1,4 +1,6 @@
|
||||
#![allow(unreachable_code)]
|
||||
use std::{env, process};
|
||||
use tokio::time::{delay_for, Duration};
|
||||
|
||||
use futures::StreamExt;
|
||||
use telegram_bot::*;
|
||||
@ -9,15 +11,14 @@ use env_logger::Env;
|
||||
mod commands;
|
||||
mod db;
|
||||
mod errors;
|
||||
mod mystem;
|
||||
mod utils;
|
||||
mod handlers;
|
||||
mod utils;
|
||||
|
||||
use mystem::MyStem;
|
||||
|
||||
#[tokio::main]
|
||||
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 = match MyStem::new() {
|
||||
Ok(mystem) => mystem,
|
||||
Err(e) => {
|
||||
@ -29,6 +30,10 @@ async fn main() -> Result<(), errors::Error> {
|
||||
Ok(_) => {}
|
||||
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") {
|
||||
Ok(token) => token,
|
||||
Err(_) => {
|
||||
@ -39,14 +44,39 @@ async fn main() -> Result<(), errors::Error> {
|
||||
let api = Api::new(token.clone());
|
||||
let mut stream = api.stream();
|
||||
let me = api.send(GetMe).await?;
|
||||
info!("GetMe result: Username: {}, First Name: {}, ID {}", me.username.as_ref().unwrap(), me.first_name, me.id);
|
||||
info!(
|
||||
"GetMe result: Username: {}, First Name: {}, ID {}",
|
||||
me.username.as_ref().unwrap(),
|
||||
me.first_name,
|
||||
me.id
|
||||
);
|
||||
loop {
|
||||
while let Some(update) = stream.next().await {
|
||||
let update = update?;
|
||||
if let UpdateKind::Message(message) = update.kind {
|
||||
match update {
|
||||
Ok(u) => {
|
||||
if let UpdateKind::Message(message) = u.kind {
|
||||
db::add_conf(message.clone()).await?;
|
||||
db::add_user(message.clone()).await?;
|
||||
handlers::handler(api.clone(), message, token.clone(), &mut mystem, me.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(())
|
||||
}
|
||||
|
@ -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 {
|
||||
MessageChat::Supergroup(chat) => chat.title.clone(),
|
||||
MessageChat::Group(chat) => chat.title.clone(),
|
||||
MessageChat::Private(_) => format!("PRIVATE"),
|
||||
MessageChat::Private(_) => "PRIVATE".to_string(),
|
||||
_ => "PRIVATE".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn create_dir(dir: &String) -> () {
|
||||
match fs_create_dir(dir) {
|
||||
Ok(_) => info!("Dir {} created.", dir),
|
||||
Err(_) => (),
|
||||
pub(crate) async fn create_dir(dir: &str) {
|
||||
if fs_create_dir(dir).is_ok() {
|
||||
info!("Dir {} created.", dir)
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user