54 Commits

Author SHA1 Message Date
AB
789b918bab Add stop-wards logic 2021-08-29 13:35:44 +03:00
AB
66a93e85b8 Add stop-wards logic 2021-08-29 13:35:29 +03:00
AB
e13a2688ab Merge branch 'main' of github.com:house-of-vanity/desubot 2021-08-29 13:34:30 +03:00
AB
04220703a3 Add stop-wards logic 2021-08-29 13:34:17 +03:00
9518ffd69b Rename README to README.md 2021-08-24 13:17:12 +03:00
5a6cb37ebb Fix CI 2021-08-24 12:59:51 +03:00
6c2837a76f Fix CI 2021-08-24 00:30:27 +03:00
d97eaf4284 Fix CI 2021-08-24 00:06:55 +03:00
AB
e7e0c6923e Fix WF 2021-08-23 01:25:01 +03:00
AB
382cc56492 docker build 2021-08-23 01:16:27 +03:00
6f48f116c9 Merge pull request #15 from house-of-vanity/add-docker-k8s
Add k8s deploy example
2021-08-22 15:07:06 -07:00
AB
282efe5be4 Add k8s deploy example 2021-08-23 01:06:11 +03:00
4175fe9029 Merge pull request #14 from house-of-vanity/add-docker-k8s
Add Dockerfile.
2021-08-22 14:59:22 -07:00
AB
f1dc1d0897 Add Dockerfile. 2021-08-23 00:58:25 +03:00
d5879a82b6 Merge pull request #13 from house-of-vanity/libs-bump
Update telegram-bot for a new UpdateKind
2021-08-20 12:24:53 -07:00
456c887a53 Merge pull request #11 from fossabot/add-license-scan-badge
Add license scan report and status
2021-08-20 11:25:12 -07:00
AB
d8b37b32df Bump version 2021-08-20 21:22:43 +03:00
AB
47c68ee432 Update telegram-bot for a new UPDATEKU 2021-08-20 20:38:13 +03:00
da53927288 Merge pull request #12 from house-of-vanity/libs-bump
Libs bump
2021-08-19 08:42:00 -07:00
AB
ac2be9929a Rollback tokio 2021-08-19 18:40:12 +03:00
7432ce6398 Bump many libs 2021-08-19 18:08:19 +03:00
049a3c4987 Add license scan report and status
Signed off by: fossabot <badges@fossa.com>
2021-06-15 15:54:38 -07:00
AB
0831e3f503 Add хере command. 2021-06-12 15:02:09 +03:00
AB
a0f4c40be0 Add хере command. 2021-06-12 14:58:38 +03:00
AB
1facef6897 Add debug message. 2021-04-04 22:35:33 +03:00
AB
428416a2a3 Add handler in case of error in here command. 2021-03-10 19:57:45 +03:00
AB
77dec205f1 Bump version. 2021-01-20 20:16:08 +03:00
AB
6c761d7576 @here command now call only active users (at least 1 message in last 60 days) 2021-01-20 20:15:46 +03:00
AB
865fd3bbe4 Bump 2021-01-20 15:54:05 +03:00
AB
30bdb23a32 Add @here command 2021-01-20 15:53:22 +03:00
AB
f97562e9b7 Merge remote-tracking branch 'origin/main' into main 2021-01-11 11:39:34 +03:00
AB
2d000101c2 Merge 2021-01-11 11:39:20 +03:00
a26d227190 Merge pull request #10 from house-of-vanity/code
code
2021-01-11 11:26:51 +03:00
AB
cc44f0e23b Merge remote-tracking branch 'origin/main' into main
# Conflicts:
#	assets/help_text.rs
2021-01-11 11:22:47 +03:00
AB
96df636195 Add automerge action. 2021-01-11 11:21:18 +03:00
AB
a48e25800c Improve logging. Fix /sql limit. 2021-01-11 11:12:38 +03:00
36660d384d Merge pull request #9 from house-of-vanity/code
Code
2021-01-10 21:39:38 +03:00
AB
0d24976ec2 Fix warnings. Bump version. 2021-01-10 21:38:18 +03:00
AB
6ae3b2af1f Fix warnings. 2021-01-10 21:22:27 +03:00
AB
a39f6a8c2a Update /code feature. 2021-01-10 21:21:07 +03:00
AB
788c2cbbd4 Fix type in help. 2021-01-09 01:06:10 +03:00
AB
9d5e5a3217 Fix type in help. 2021-01-08 17:54:05 +03:00
945da05794 Update README 2021-01-08 06:42:32 -08:00
3085d4c450 Merge pull request #8 from house-of-vanity/code
Code
2021-01-08 17:38:56 +03:00
AB
4b142ae8ef Add /code feature. 2021-01-08 17:32:39 +03:00
AB
8f3cccf01d Trying to fix clippy. doesn't work. 2021-01-05 14:56:31 +03:00
AB
3136bce84f Clippy lint 2021-01-05 05:17:22 +03:00
AB
e09047bb5b Simple lint 2021-01-05 05:14:44 +03:00
AB
5528177e52 Some /code staff 2021-01-05 05:04:26 +03:00
AB
ed32bd9195 Merge 2021-01-05 05:01:34 +03:00
AB
d14aa3beb4 Rebase 2021-01-05 04:39:32 +03:00
43e4f26af1 Merge pull request #7 from house-of-vanity/sql
Sql
2021-01-05 04:31:21 +03:00
AB
5d8d3441d2 Fix /sql command a lot. 2021-01-05 04:30:28 +03:00
AB
34fa54e6f4 add lock file. 2021-01-05 03:58:36 +03:00
26 changed files with 1975 additions and 147 deletions

27
.github/workflows/automerge.yml vendored Normal file
View 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 }}"

View File

@ -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

3
.gitignore vendored
View File

@ -6,3 +6,6 @@ memory.sqlite3
/video /video
/voice /voice
/.idea /.idea
Cargo.lock
k8s/k8s-deploy.yaml
/identifier.sqlite

View File

@ -1,6 +1,6 @@
[package] [package]
name = "desubot" name = "desubot"
version = "0.1.0" version = "0.5.8"
authors = ["AB <ab@hexor.ru>"] authors = ["AB <ab@hexor.ru>"]
edition = "2018" edition = "2018"
@ -9,30 +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.2", features = ["bundled"]}
html-escape = "0.2" html-escape = "0.2"
regex = "1" 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.1" mystem = "^0.2"
#mystem = { path = "../mystem-rs" }
async-trait = "0.1.42" async-trait = "0.1.42"
sqlparser = "0.7.0" sqlparser = "0.7.0"
[dependencies.syntect]
version = "4.4"
default-features = false
features = ["parsing", "dump-load", "regex-onig"]

14
Dockerfile Normal file
View 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

View File

@ -1,3 +1,5 @@
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fhouse-of-vanity%2Fdesubot.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Fhouse-of-vanity%2Fdesubot?ref=badge_shield)
Desubot Desubot
Telegram bot with light group statistic and heavy spy features. Telegram bot with light group statistic and heavy spy features.
@ -7,10 +9,12 @@ Telegram bot with light group statistic and heavy spy features.
* /here command to mention all members. * /here command to mention all members.
* Alongside with saving whole message bot perform blacklist filter and stemming for every word (only Russian). "Красивую собаку мыли негры" -> "красивый собака мыть негр" * 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. * 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 == == Important ==
* Desubot uses MyStem by Yandex for word stemming and assume that mystem binary is available in PATH. * 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. On Windows it may be placed on working directory. Both Linux and Windows mystem binary is in repo.
## License
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fhouse-of-vanity%2Fdesubot.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fhouse-of-vanity%2Fdesubot?ref=badge_large)

3
assets/README.md Normal file
View 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

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

43
assets/help_text.rs Normal file
View File

@ -0,0 +1,43 @@
#[allow(dead_code)]
static CODE_HELP: &str = "<b>Code highlighter</b>
<i>Usage</i>
<pre>/code
&lt;CODE&gt;
#&lt;lang - JS by default&gt; #&lt;theme - Dracula by default&gt;</pre>
Language may be defined by both name and extension - Rust, rs...
Max lines - 80
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
View File

@ -0,0 +1,427 @@
а
в
г
е
ж
и
к
м
о
с
т
у
я
бы
stat
вообще
ThreadTopBot
/stat
во
вы
да
до
ее
ей
ею
её
же
за
из
им
их
ли
мы
на
не
ни
но
ну
нх
об
он
от
по
со
та
те
то
ту
ты
уж
без
был
вам
вас
ваш
вон
вот
все
всю
вся
всё
где
год
два
две
дел
для
его
ему
еще
ещё
или
ими
имя
как
кем
ком
кто
лет
мне
мог
мож
мои
мой
мор
моя
моё
над
нам
нас
наш
нее
ней
нем
нет
нею
неё
них
оба
она
они
оно
под
пор
при
про
раз
сам
сих
так
там
тем
тех
том
тот
тою
три
тут
уже
чем
что
эта
эти
это
эту
алло
буду
будь
бывь
была
были
было
быть
вами
ваша
ваше
ваши
ведь
весь
вниз
всем
всех
всею
года
году
даже
двух
день
если
есть
зато
кого
кому
куда
лишь
люди
мало
меля
меня
мимо
мира
мной
мною
мочь
надо
нами
наша
наше
наши
него
нему
ниже
ними
один
пока
пора
пять
рано
сама
сами
само
саму
свое
свои
свою
себе
себя
семь
стал
суть
твой
твоя
твоё
тебе
тебя
теми
того
тоже
тому
туда
хоть
хотя
чаще
чего
чему
чтоб
чуть
этим
этих
этой
этом
этот
более
будем
будет
будто
будут
вверх
вдали
вдруг
везде
внизу
время
всего
всеми
всему
всюду
давно
даром
долго
друго
занят
затем
зачем
здесь
иметь
какая
какой
когда
кроме
лучше
между
менее
много
могут
может
можно
можхо
назад
низко
нужно
одной
около
опять
очень
перед
позже
после
потом
почти
пятый
разве
рядом
самим
самих
самой
самом
своей
своих
сеаой
снова
собой
собою
такая
также
такие
такое
такой
тобой
тобою
тогда
тысяч
уметь
часто
через
чтобы
шесть
этими
этого
этому
близко
больше
будете
будешь
бывает
важная
важное
важные
важный
вокруг
восемь
всегда
второй
далеко
дальше
девять
десять
должно
другая
другие
других
другое
другой
занята
занято
заняты
значит
именно
иногда
каждая
каждое
каждые
каждый
кругом
меньше
начала
нельзя
нибудь
никуда
ничего
обычно
однако
одного
отсюда
первый
потому
почему
просто
против
раньше
самими
самого
самому
своего
сейчас
сказал
совсем
теперь
только
третий
хорошо
хотеть
хочешь
четыре
шестой
восьмой
впрочем
времени
говорил
говорит
девятый
десятый
кажется
конечно
которая
которой
которые
который
которых
наверху
наконец
недавно
немного
нередко
никогда
однажды
посреди
сегодня
седьмой
сказала
сказать
сколько
слишком
сначала
спасибо
двадцать
довольно
которого
наиболее
недалеко
особенно
отовсюду
двадцатый
миллионов
несколько
прекрасно
процентов
четвертый
двенадцать
непрерывно
пожалуйста
пятнадцать
семнадцать
тринадцать
двенадцатый
одиннадцать
пятнадцатый
семнадцатый
тринадцатый
шестнадцать
восемнадцать
девятнадцать
одиннадцатый
четырнадцать
шестнадцатый
восемнадцатый
девятнадцатый
действительно
четырнадцатый
многочисленная
многочисленное
многочисленные
многочисленный
ага
делать
писать
бот

21
assets/sync_from_bat.py Normal file
View 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

Binary file not shown.

4
assets/syntaxes/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

BIN
assets/themes.bin Normal file

Binary file not shown.

5
assets/themes/.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore
!Dracula.tmTheme

View 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&#x2f;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>

View 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

View File

@ -1,6 +1,7 @@
#![allow(unused_variables)]
use crate::db; use crate::db;
use crate::errors::Error; use crate::errors::Error;
use crate::errors::Error::{SQLInvalidCommand, SQLITE3Error}; use crate::errors::Error::{CodeHighlightningError, SQLITE3Error, SQLInvalidCommand};
use async_trait::async_trait; use async_trait::async_trait;
use html_escape::encode_text; use html_escape::encode_text;
use markov::Chain; use markov::Chain;
@ -15,8 +16,14 @@ use regex::Regex;
use sqlparser::ast::Statement; use sqlparser::ast::Statement;
use sqlparser::dialect::GenericDialect; use sqlparser::dialect::GenericDialect;
use sqlparser::parser::Parser; 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::{Api, Message, ParseMode, UserId};
include!("../assets/help_text.rs");
pub struct Here { pub struct Here {
pub data: String, pub data: String,
@ -36,11 +43,15 @@ pub struct Omedeto {
pub struct Sql { pub struct Sql {
pub data: String, pub data: String,
} }
pub struct Code {
pub data: String,
}
#[async_trait] #[async_trait]
pub trait Execute { pub trait Execute {
async fn run(&self, api: &Api, message: &Message) -> Result<(), Error>; async fn exec(&self, api: &Api, message: &Message) -> Result<(), Error>;
async fn run_mystem( async fn exec_with_result(&self, api: &Api, message: &Message) -> Result<String, Error>;
async fn exec_mystem(
&self, &self,
api: &Api, api: &Api,
message: &Message, message: &Message,
@ -50,8 +61,16 @@ pub trait Execute {
#[async_trait] #[async_trait]
impl Execute for Sql { impl Execute for Sql {
async fn run(&self, api: &Api, message: &Message) -> Result<(), Error> { async fn exec(&self, api: &Api, message: &Message) -> Result<(), Error> {
let mut sql = self.data.to_uppercase(); 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('-') { let is_head = if sql.starts_with('-') {
sql = sql.replacen("-", "", 1); sql = sql.replacen("-", "", 1);
false false
@ -88,7 +107,7 @@ impl Execute for Sql {
let mut rows = match stmt.query(rusqlite::NO_PARAMS) { let mut rows = match stmt.query(rusqlite::NO_PARAMS) {
Err(e) => return Err(SQLITE3Error(e)), Err(e) => return Err(SQLITE3Error(e)),
Ok(mut rows) => rows, Ok(rows) => rows,
}; };
let mut res: Vec<Vec<String>> = match rows.column_names() { let mut res: Vec<Vec<String>> = match rows.column_names() {
@ -124,7 +143,7 @@ impl Execute for Sql {
} }
res.push(tmp); res.push(tmp);
} }
if res.len() > 100 { if res.len() >= 100 {
return Err(Error::SQLResultTooLong( return Err(Error::SQLResultTooLong(
"SQL result too long. Lines limit is 100. Use LIMIT".to_string(), "SQL result too long. Lines limit is 100. Use LIMIT".to_string(),
)); ));
@ -156,11 +175,11 @@ impl Execute for Sql {
} else { } else {
msg msg
}; };
Ok(()) Ok(msg)
} }
#[allow(unused_variables)] #[allow(unused_variables)]
async fn run_mystem( async fn exec_mystem(
&self, &self,
api: &Api, api: &Api,
message: &Message, message: &Message,
@ -172,8 +191,16 @@ impl Execute for Sql {
#[async_trait] #[async_trait]
impl Execute for Here { impl Execute for Here {
async fn run(&self, api: &Api, message: &Message) -> Result<(), Error> { async fn exec(&self, api: &Api, message: &Message) -> Result<(), Error> {
let members: Vec<telegram_bot::User> = db::get_members(message.chat.id()).unwrap(); 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 { for u in &members {
debug!("Found user {:?} in chat {}", u, message.chat.id()); debug!("Found user {:?} in chat {}", u, message.chat.id());
} }
@ -200,8 +227,12 @@ impl Execute for Here {
Ok(()) Ok(())
} }
async fn exec_with_result(&self, api: &Api, message: &Message) -> Result<String, Error> {
unimplemented!()
}
#[allow(unused_variables)] #[allow(unused_variables)]
async fn run_mystem( async fn exec_mystem(
&self, &self,
api: &Api, api: &Api,
message: &Message, message: &Message,
@ -213,7 +244,7 @@ impl Execute for Here {
#[async_trait] #[async_trait]
impl Execute for Top { impl Execute for Top {
async fn run(&self, api: &Api, message: &Message) -> Result<(), Error> { async fn exec(&self, api: &Api, message: &Message) -> Result<(), Error> {
let top = db::get_top(&message).await?; let top = db::get_top(&message).await?;
let mut msg = "<b>Your top using words:</b>\n<pre>".to_string(); let mut msg = "<b>Your top using words:</b>\n<pre>".to_string();
let mut counter = 1; let mut counter = 1;
@ -235,8 +266,12 @@ impl Execute for Top {
Ok(()) Ok(())
} }
async fn exec_with_result(&self, api: &Api, message: &Message) -> Result<String, Error> {
unimplemented!()
}
#[allow(unused_variables)] #[allow(unused_variables)]
async fn run_mystem( async fn exec_mystem(
&self, &self,
api: &Api, api: &Api,
message: &Message, message: &Message,
@ -248,7 +283,7 @@ impl Execute for Top {
#[async_trait] #[async_trait]
impl Execute for MarkovAll { impl Execute for MarkovAll {
async fn run(&self, api: &Api, message: &Message) -> Result<(), Error> { async fn exec(&self, api: &Api, message: &Message) -> Result<(), Error> {
let messages = db::get_messages_random_all().await?; let messages = db::get_messages_random_all().await?;
let mut chain = Chain::new(); let mut chain = Chain::new();
chain.feed(messages); chain.feed(messages);
@ -269,8 +304,12 @@ impl Execute for MarkovAll {
Ok(()) Ok(())
} }
async fn exec_with_result(&self, api: &Api, message: &Message) -> Result<String, Error> {
unimplemented!()
}
#[allow(unused_variables)] #[allow(unused_variables)]
async fn run_mystem( async fn exec_mystem(
&self, &self,
api: &Api, api: &Api,
message: &Message, message: &Message,
@ -282,14 +321,14 @@ impl Execute for MarkovAll {
#[async_trait] #[async_trait]
impl Execute for Markov { impl Execute for Markov {
async fn run(&self, api: &Api, message: &Message) -> Result<(), Error> { async fn exec(&self, api: &Api, message: &Message) -> Result<(), Error> {
let messages = db::get_messages_random_group(&message).await?; let messages = db::get_messages_random_group(&message).await?;
let mut chain = Chain::new(); let mut chain = Chain::new();
chain.feed(messages); chain.feed(messages);
let mut sentences = chain.generate(); let mut sentences = chain.generate();
let mut msg = String::new(); let mut msg = String::new();
for _ in 1..rand::thread_rng().gen_range(2, 10) { 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 match api
.send(message.text_reply(msg.trim()).parse_mode(ParseMode::Html)) .send(message.text_reply(msg.trim()).parse_mode(ParseMode::Html))
@ -303,8 +342,12 @@ impl Execute for Markov {
Ok(()) Ok(())
} }
async fn exec_with_result(&self, api: &Api, message: &Message) -> Result<String, Error> {
unimplemented!()
}
#[allow(unused_variables)] #[allow(unused_variables)]
async fn run_mystem( async fn exec_mystem(
&self, &self,
api: &Api, api: &Api,
message: &Message, message: &Message,
@ -317,12 +360,16 @@ impl Execute for Markov {
#[async_trait] #[async_trait]
impl Execute for Omedeto { impl Execute for Omedeto {
#[allow(unused_variables)] #[allow(unused_variables)]
async fn run(&self, api: &Api, message: &Message) -> Result<(), Error> { 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!() unimplemented!()
} }
#[warn(unused_must_use)] #[warn(unused_must_use)]
async fn run_mystem( async fn exec_mystem(
&self, &self,
api: &Api, api: &Api,
message: &Message, message: &Message,
@ -336,11 +383,8 @@ impl Execute for Omedeto {
.map(|m| m.split(' ').map(|s| s.to_string()).collect::<Vec<String>>()[1].clone()) .map(|m| m.split(' ').map(|s| s.to_string()).collect::<Vec<String>>()[1].clone())
.filter(|m| { .filter(|m| {
let stem = mystem.stemming(m.clone()).unwrap_or_default(); let stem = mystem.stemming(m.clone()).unwrap_or_default();
if stem.is_empty() { if !stem.is_empty() {
false if !stem[0].lex.is_empty() {
} else if stem[0].lex.is_empty() {
false
} else {
match stem[0].lex[0].grammem.part_of_speech { match stem[0].lex[0].grammem.part_of_speech {
mystem::PartOfSpeech::Noun => stem[0].lex[0] mystem::PartOfSpeech::Noun => stem[0].lex[0]
.grammem .grammem
@ -348,6 +392,11 @@ impl Execute for Omedeto {
.contains(&mystem::Fact::Case(Nominative)), .contains(&mystem::Fact::Case(Nominative)),
_ => false, _ => false,
} }
} else {
false
}
} else {
false
} }
}) })
.map(|w| w.replace(|z| z == '.' || z == ',', "")) .map(|w| w.replace(|z| z == '.' || z == ',', ""))
@ -363,6 +412,7 @@ impl Execute for Omedeto {
.map(|m| m.split(' ').map(|s| s.to_string()).collect::<Vec<String>>()[1].clone()) .map(|m| m.split(' ').map(|s| s.to_string()).collect::<Vec<String>>()[1].clone())
.filter(|m| { .filter(|m| {
let stem = mystem.stemming(m.clone()).unwrap_or_default(); let stem = mystem.stemming(m.clone()).unwrap_or_default();
#[allow(clippy::if_same_then_else)]
if stem.is_empty() { if stem.is_empty() {
false false
} else if stem[0].lex.is_empty() { } else if stem[0].lex.is_empty() {
@ -390,6 +440,7 @@ impl Execute for Omedeto {
.map(|m| m.split(' ').map(|s| s.to_string()).collect::<Vec<String>>()[1].clone()) .map(|m| m.split(' ').map(|s| s.to_string()).collect::<Vec<String>>()[1].clone())
.filter(|m| { .filter(|m| {
let stem = mystem.stemming(m.clone()).unwrap_or_default(); let stem = mystem.stemming(m.clone()).unwrap_or_default();
#[allow(clippy::if_same_then_else)]
if stem.is_empty() { if stem.is_empty() {
false false
} else if stem[0].lex.is_empty() { } else if stem[0].lex.is_empty() {
@ -444,14 +495,12 @@ impl Execute for Omedeto {
.filter(|m| re.is_match(m)) .filter(|m| re.is_match(m))
.map(|m| m.split(' ').map(|s| s.to_string()).collect::<Vec<String>>()[1].clone()) .map(|m| m.split(' ').map(|s| s.to_string()).collect::<Vec<String>>()[1].clone())
.map(|m| { .map(|m| {
let stem = mystem.stemming(m.clone()).unwrap_or_default(); let stem = mystem.stemming(m).unwrap_or_default();
#[allow(clippy::if_same_then_else)]
if stem.is_empty() { if stem.is_empty() {
()
} else if stem[0].lex.is_empty() { } else if stem[0].lex.is_empty() {
()
} else { } else {
match stem[0].lex[0].grammem.part_of_speech { if let mystem::PartOfSpeech::Verb = stem[0].lex[0].grammem.part_of_speech {
mystem::PartOfSpeech::Verb => {
match stem[0].lex[0] match stem[0].lex[0]
.grammem .grammem
.facts .facts
@ -463,25 +512,19 @@ impl Execute for Omedeto {
.facts .facts
.contains(&mystem::Fact::Gender(Feminine)) .contains(&mystem::Fact::Gender(Feminine))
{ {
fm = fm + 1; fm += 1;
} else { } else {
mu = mu + 1; mu += 1;
} }
} }
false => (), false => (),
} }
} }
_ => (),
}
} }
}) })
.for_each(drop); .for_each(drop);
//debug!("fm - {}, mu - {}", fm, mu); //debug!("fm - {}, mu - {}", fm, mu);
if fm >= mu { fm >= mu
true
} else {
false
}
}; };
//debug!("Is Feminine - {}", fem); //debug!("Is Feminine - {}", fem);
let result = format!( let result = format!(
@ -489,17 +532,17 @@ impl Execute for Omedeto {
start.choose(&mut rand::thread_rng()).unwrap(), start.choose(&mut rand::thread_rng()).unwrap(),
message.from.first_name.to_string(), message.from.first_name.to_string(),
{ if fem { "ая" } else { "ый" } }, { if fem { "ая" } else { "ый" } },
nouns.pop().unwrap_or(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(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(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 { "" } }, { if fem { "а" } else { "" } },
verbs_p.pop().unwrap_or(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(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(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 { "" } }, { if fem { "а" } else { "" } },
verbs_i.pop().unwrap_or(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(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(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 match api
.send( .send(
@ -515,3 +558,97 @@ impl Execute for Omedeto {
Ok(()) 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();
if lines.len() >= 81 {
return Err(CodeHighlightningError);
}
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!()
}
}

View File

@ -34,6 +34,25 @@ pub(crate) fn update_scheme() -> Result<()> {
Ok(()) Ok(())
} }
pub(crate) fn load_stopwords() -> Result<()> {
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]);
//let mut rows = stmt.word(named_params! {":conf_id": conf_id})?;
}
}
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(
@ -54,7 +73,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())
@ -74,7 +93,7 @@ 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())
@ -180,18 +199,29 @@ pub(crate) async fn get_messages_user_all(
Ok(messages) Ok(messages)
} }
pub(crate) fn get_members(id: telegram_bot::ChatId) -> Result<Vec<telegram_bot::User>> { 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 conn = open()?;
let mut stmt = conn.prepare_cached( let mut stmt = conn.prepare_cached(&format!(
" "
SELECT DISTINCT(u.username), u.id, u.first_name, u.last_name, u.date 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();
@ -214,7 +244,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,
@ -228,10 +258,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(),
@ -250,7 +280,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(())
@ -283,7 +313,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()
@ -310,7 +344,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(())
@ -348,10 +382,9 @@ pub(crate) async fn get_file(file_id: String) -> Result<i64, errors::Error> {
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 =
@ -365,7 +398,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)",
@ -418,6 +451,7 @@ pub(crate) async fn add_sentence(
}; };
// Save stemmed words // Save stemmed words
debug!("Going to stem: {}", text);
let words = mystem.stemming(text)?; let words = mystem.stemming(text)?;
conn.execute("BEGIN TRANSACTION", params![]); conn.execute("BEGIN TRANSACTION", params![]);
for word in words { for word in words {
@ -432,7 +466,7 @@ pub(crate) async fn add_sentence(
Err(e) => panic!("SQLITE3 Error: Relations failed: {:?}", e), Err(e) => panic!("SQLITE3 Error: Relations failed: {:?}", e),
} }
} }
Err(_) => debug!("Word {} is in stop list.", &word.lex[0].lex), Err(_) => debug!("Word {} is in a stop list.", &word.lex[0].lex),
} }
} }
conn.execute("END TRANSACTION", params![]); conn.execute("END TRANSACTION", params![]);

View File

@ -23,52 +23,80 @@ pub enum Error {
SQLBannedCommand(String), SQLBannedCommand(String),
SQLInvalidCommand, SQLInvalidCommand,
SQLResultTooLong(String), 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\
// // #&lt;theme&gt;\
// // &lt;CODE&gt;\
// // #&lt;lang&gt;</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 { impl From<mystem_error> for Error {
fn from(e: mystem_error) -> Error { fn from(e: mystem_error) -> Error {
return Error::MystemError(e); Error::MystemError(e)
} }
} }

View File

@ -1,11 +1,13 @@
//use crate::commands::Command; //use crate::commands::Command;
use crate::commands::{Execute, Here, Markov, MarkovAll, Omedeto, Sql, Top}; use crate::commands::{Code, Execute, Here, Markov, MarkovAll, Omedeto, Sql, Top};
use crate::db; use crate::db;
use crate::errors; use crate::errors;
use crate::utils; use crate::utils;
use mystem::MyStem; use mystem::MyStem;
use telegram_bot::*; use telegram_bot::*;
include!("../assets/help_text.rs");
pub async fn handler( pub async fn handler(
api: Api, api: Api,
message: Message, message: Message,
@ -22,72 +24,106 @@ 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?;
let cleaned_message = data.replace(&format!("@{}", me.clone().username.unwrap()), ""); let cleaned_message = data.replace(&format!("@{}", me.clone().username.unwrap()), "");
match cleaned_message.as_str() { match cleaned_message.as_str() {
s if s.contains("/here") => { s if s.to_string().starts_with("/code") => {
match {
Code {
data: s.replace("/code", ""),
}
.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.photo(&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?;
Here { Here {
data: "".to_string(), data: "".to_string(),
} }
.run(&api, &message) .exec(&api, &message)
.await? .await?
} }
s if s.to_string().starts_with("/sql") => match { s if s.to_string().starts_with("/sql") => match {
Sql { Sql {
data: s.replace("/sql ", ""), data: s.replace("/sql ", ""),
} }
.run(&api, &message) .exec_with_result(&api, &message)
.await .await
} { } {
Ok(_) => debug!("/sql command sent to {}", message.chat.id()), Ok(msg) => {
let _ = api
.send(message.text_reply(msg).parse_mode(ParseMode::Html))
.await?;
}
Err(e) => { Err(e) => {
api.send( let _ = api
.send(
message message
.text_reply(format!("Error: {:#?}", e)) .text_reply(format!("Error: {:#?}", e))
.parse_mode(ParseMode::Html), .parse_mode(ParseMode::Html),
) )
.await?; .await?;
()
} }
}, },
"/top" => { "/top" => {
Top { Top {
data: "".to_string(), data: "".to_string(),
} }
.run(&api, &message) .exec(&api, &message)
.await? .await?
} }
"/stat" => { "/stat" => {
Top { Top {
data: "".to_string(), data: "".to_string(),
} }
.run(&api, &message) .exec(&api, &message)
.await? .await?
} }
"/markov_all" => { "/markov_all" => {
MarkovAll { MarkovAll {
data: "".to_string(), data: "".to_string(),
} }
.run(&api, &message) .exec(&api, &message)
.await? .await?
} }
"/markov" => { "/markov" => {
Markov { Markov {
data: "".to_string(), data: "".to_string(),
} }
.run(&api, &message) .exec(&api, &message)
.await? .await?
} }
"/omedeto" => { "/omedeto" => {
Omedeto { Omedeto {
data: "".to_string(), data: "".to_string(),
} }
.run_mystem(&api, &message, mystem) .exec_mystem(&api, &message, mystem)
.await? .await?
} }
_ => (), _ => db::add_sentence(&message, mystem).await?,
} }
} }
MessageKind::Photo { ref caption, .. } => { MessageKind::Photo { ref caption, .. } => {
@ -98,7 +134,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?;
} }
@ -111,7 +147,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?;
} }

View File

@ -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::*;
@ -16,7 +18,7 @@ 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 = match MyStem::new() { let mut mystem = match MyStem::new() {
Ok(mystem) => mystem, Ok(mystem) => mystem,
Err(e) => { Err(e) => {
@ -28,6 +30,10 @@ async fn main() -> Result<(), errors::Error> {
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(_) => {
@ -44,12 +50,20 @@ async fn main() -> Result<(), errors::Error> {
me.first_name, me.first_name,
me.id me.id
); );
loop {
while let Some(update) = stream.next().await { while let Some(update) = stream.next().await {
let update = update?; match update {
if let UpdateKind::Message(message) = update.kind { Ok(u) => {
if let UpdateKind::Message(message) = u.kind {
db::add_conf(message.clone()).await?; db::add_conf(message.clone()).await?;
db::add_user(message.clone()).await?; db::add_user(message.clone()).await?;
match handlers::handler(api.clone(), message, token.clone(), &mut mystem, me.clone()) match handlers::handler(
api.clone(),
message,
token.clone(),
&mut mystem,
me.clone(),
)
.await .await
{ {
Ok(_) => {} Ok(_) => {}
@ -57,5 +71,12 @@ async fn main() -> Result<(), errors::Error> {
} }
} }
} }
Err(e) => {
warn!("Telegram API Error: {:?}", e);
}
};
}
delay_for(Duration::from_secs(2)).await;
}
Ok(()) Ok(())
} }

View File

@ -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(_) => (),
} }
} }