34 Commits
0.3.1 ... code

Author SHA1 Message Date
AB
a48e25800c Improve logging. Fix /sql limit. 2021-01-11 11:12: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
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
d5b30cc94e Merge pull request #6 from house-of-vanity/sql
Simplify SQL command. Add limit.
2021-01-05 03:46:03 +03:00
7a66034381 Update handlers.rs 2021-01-04 16:40:46 -08:00
AB
47906fe22d Simplify SQL command. Add limit. 2021-01-05 03:30:21 +03:00
ea6d9b55a1 Merge pull request #5 from house-of-vanity/matching_commands
Fix typo.
2021-01-05 01:44:09 +03:00
AB
83a6045b18 Fix typo. 2021-01-04 15:13:06 +03:00
c8c55782ec Merge pull request #4 from house-of-vanity/matching_commands
lint.
2021-01-03 22:44:46 +03:00
AB
a1b272bd40 lint. 2021-01-03 22:42:49 +03:00
ed68dbd4bd Merge pull request #3 from house-of-vanity/matching_commands
Add /sql command.
2021-01-03 22:38:54 +03:00
AB
3f00505659 Add /sql command. 2021-01-03 22:37:37 +03:00
5a41d4a0b9 Merge pull request #2 from house-of-vanity/matching_commands
Rewrite command parsing.
2020-12-31 01:59:28 +03:00
AB
3fd5b124f3 Bump mystem lib. Lint code. 2020-12-31 01:56:20 +03:00
AB
17442819c4 Rewrite command parsing. 2020-12-31 01:42:36 +03:00
AB
7adc629292 Improve omedeto. Detect feminine by verbs. Fix 2020-12-30 15:25:33 +03:00
AB
39640139fa Improve omedeto. Detect feminine by verbs. 2020-12-30 15:12:17 +03:00
AB
0812c9e371 Improve omedeto. Nouns. 2020-12-30 14:30:53 +03:00
AB
f111f54606 Improve omedeto. Nouns. 2020-12-30 11:56:25 +03:00
AB
7e17851131 Improve omedeto 2020-12-30 11:50:19 +03:00
AB
b674ae5b15 Update omedeto 2020-12-30 09:58:17 +03:00
AB
412c3f313c Fix typo. 2020-12-30 09:47:33 +03:00
AB
1838674cab Fix typo. 2020-12-30 09:38:33 +03:00
21 changed files with 1844 additions and 306 deletions

1
.gitignore vendored
View File

@ -6,3 +6,4 @@ memory.sqlite3
/video
/voice
/.idea
Cargo.lock

View File

@ -1,6 +1,6 @@
[package]
name = "desubot"
version = "0.1.0"
version = "0.5.1"
authors = ["AB <ab@hexor.ru>"]
edition = "2018"
@ -9,28 +9,33 @@ 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"
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.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 = "^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"]

2
README
View File

@ -9,7 +9,7 @@ Telegram bot with light group statistic and heavy spy features.
* Generate sentences using Markov Chains trained on history with /markov_all.
== TODO ==
* Syntax highlighting for code exported to image.
* 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.

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>";

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

@ -1,254 +1,646 @@
#![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};
pub(crate) async fn here(api: Api, message: Message) -> Result<(), Error> {
let members: Vec<telegram_bot::User> = db::get_members(message.chat.id()).unwrap();
for u in &members {
debug!("Found user {:?} in chat {}", u, message.chat.id());
}
let mut msg = "<b>I summon you</b>, ".to_string();
for user in members {
let mention = match user.username {
Some(username) => format!("@{}", username),
_ => format!(
"<a href=\"tg://user?id={}\">{}</a>",
encode_text(&user.id.to_string()),
encode_text(&user.first_name)
),
};
msg = format!("{} {}", msg, mention);
}
include!("../assets/help_text.rs");
match api
.send(message.text_reply(msg).parse_mode(ParseMode::Html))
.await
{
Ok(_) => debug!("/here command sent to {}", message.chat.id()),
Err(_) => warn!("/here command sent failed to {}", message.chat.id()),
}
//api.send(message.chat.text("Text to message chat")).await?;
//api.send(message.from.text("Private text")).await?;
Ok(())
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(crate) async fn top(api: Api, message: Message) -> Result<(), Error> {
let top = db::get_top(&message).await?;
let mut msg = "<b>Your top using words:</b>\n<pre>".to_string();
let mut counter = 1;
for word in top.iter() {
msg = format!(
"{} <b>{}</b> {} - {}\n",
msg, counter, word.word, word.count
);
counter += 1;
}
msg = format!("{}{}", msg, "</pre>");
match api
.send(message.text_reply(msg).parse_mode(ParseMode::Html))
.await
{
Ok(_) => debug!("/top command sent to {}", message.chat.id()),
Err(_) => warn!("/top command sent failed to {}", message.chat.id()),
}
//api.send(message.chat.text("Text to message chat")).await?;
//api.send(message.from.text("Private text")).await?;
Ok(())
#[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>;
}
pub(crate) async fn markov_all(api: Api, message: Message) -> Result<(), Error> {
let messages = db::get_messages_random_all().await?;
let mut chain = Chain::new();
chain.feed(messages);
let mut sentences = chain.generate();
let mut msg = String::new();
for _ in 1..rand::thread_rng().gen_range(2, 10) {
msg = format!("{} {}", msg, sentences.pop().unwrap());
#[async_trait]
impl Execute for Sql {
async fn exec(&self, api: &Api, message: &Message) -> Result<(), Error> {
unimplemented!()
}
match api
.send(message.text_reply(msg.trim()).parse_mode(ParseMode::Html))
.await
{
Ok(_) => debug!("/markov_all command sent to {}", message.chat.id()),
Err(_) => warn!("/markov_all command sent failed to {}", message.chat.id()),
}
//api.send(message.chat.text("Text to message chat")).await?;
//api.send(message.from.text("Private text")).await?;
Ok(())
}
pub(crate) async fn markov(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());
}
match api
.send(message.text_reply(msg.trim()).parse_mode(ParseMode::Html))
.await
{
Ok(_) => debug!("/markov command sent to {}", message.chat.id()),
Err(_) => warn!("/markov command sent failed to {}", message.chat.id()),
}
//api.send(message.chat.text("Text to message chat")).await?;
//api.send(message.from.text("Private text")).await?;
Ok(())
}
pub(crate) async fn omedeto(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
.clone()
.into_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() {
false
} else if stem[0].lex.is_empty() {
false
} else {
match stem[0].lex[0].grammem.part_of_speech {
mystem::PartOfSpeech::Noun => true,
_ => false,
}
}
})
.collect();
nouns.sort();
nouns.dedup();
nouns.shuffle(&mut rand::thread_rng());
let mut verbs_p: Vec<String> = 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())
.filter(|m| {
let stem = mystem.stemming(m.clone()).unwrap_or_default();
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,
}
}
})
.collect();
verbs_p.sort();
verbs_p.dedup();
verbs_p.shuffle(&mut rand::thread_rng());
let mut verbs_i: Vec<String> = 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())
.filter(|m| {
let stem = mystem.stemming(m.clone()).unwrap_or_default();
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)),
_ => false,
}
}
})
.collect();
verbs_i.sort();
verbs_i.dedup();
verbs_i.shuffle(&mut rand::thread_rng());
if nouns.is_empty() {
nouns.push(message.from.first_name.to_string());
}
let start: Vec<String> = vec![
"С новыйм годом.".into(),
"С НГ тебя".into(),
"Поздравляю".into(),
"Поздравляю с НГ".into(),
];
//debug!("Nouns: {:#?}", nouns);
//debug!("Verbs: {:#?}", verbs);
let fem = {
let z = mystem
.stemming(message.from.first_name.to_string())
.unwrap();
if z.is_empty() {
false
} else if z[0].lex.is_empty() {
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 {
if z[0].lex[0]
.grammem
.facts
.contains(&mystem::Fact::Gender(Feminine))
{
true
} else {
false
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 result = format!(
"{} {} известн{} как {}, {}, а так же конечно {}. В прошедшем году ты часто давал{} нам знать, что ты {}, {} и {}. Не редко ты говорил{} я {}, я {} или даже я {}. =*",
start.choose(&mut rand::thread_rng()).unwrap(),
message.from.first_name.to_string(),
{if fem {"ая"} else {"ый"}},
nouns.pop().unwrap_or("=(".to_string()),
nouns.pop().unwrap_or("=(".to_string()),
nouns.pop().unwrap_or("=(".to_string()),
{if fem {"а"} else {""}},
verbs_p.pop().unwrap_or("=(".to_string()),
verbs_p.pop().unwrap_or("=(".to_string()),
verbs_p.pop().unwrap_or("=(".to_string()),
{if fem {"а"} else {""}},
verbs_i.pop().unwrap_or("=(".to_string()),
verbs_i.pop().unwrap_or("=(".to_string()),
verbs_i.pop().unwrap_or("=(".to_string()),
);
debug!("{:?}", result);
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()),
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)
}
// '^я [а-яА-Я]+(-[а-яА-Я]+(_[а-яА-Я]+)*)*$'
Ok(())
#[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()).unwrap();
for u in &members {
debug!("Found user {:?} in chat {}", u, message.chat.id());
}
let mut msg = "<b>I summon you</b>, ".to_string();
for user in members {
let mention = match user.username {
Some(username) => format!("@{}", username),
_ => format!(
"<a href=\"tg://user?id={}\">{}</a>",
encode_text(&user.id.to_string()),
encode_text(&user.first_name)
),
};
msg = format!("{} {}", msg, mention);
}
match api
.send(message.text_reply(msg).parse_mode(ParseMode::Html))
.await
{
Ok(_) => debug!("/here command sent to {}", message.chat.id()),
Err(_) => warn!("/here command sent failed to {}", message.chat.id()),
}
Ok(())
}
async fn exec_with_result(&self, api: &Api, message: &Message) -> Result<String, Error> {
unimplemented!()
}
#[allow(unused_variables)]
async fn exec_mystem(
&self,
api: &Api,
message: &Message,
mystem: &mut MyStem,
) -> Result<(), Error> {
unimplemented!()
}
}
#[async_trait]
impl Execute for Top {
async fn exec(&self, api: &Api, message: &Message) -> Result<(), Error> {
let top = db::get_top(&message).await?;
let mut msg = "<b>Your top using words:</b>\n<pre>".to_string();
let mut counter = 1;
for word in top.iter() {
msg = format!(
"{} <b>{}</b> {} - {}\n",
msg, counter, word.word, word.count
);
counter += 1;
}
msg = format!("{}{}", msg, "</pre>");
match api
.send(message.text_reply(msg).parse_mode(ParseMode::Html))
.await
{
Ok(_) => debug!("/top command sent to {}", message.chat.id()),
Err(_) => warn!("/top command sent failed to {}", message.chat.id()),
}
Ok(())
}
async fn exec_with_result(&self, api: &Api, message: &Message) -> Result<String, Error> {
unimplemented!()
}
#[allow(unused_variables)]
async fn exec_mystem(
&self,
api: &Api,
message: &Message,
mystem: &mut MyStem,
) -> Result<(), Error> {
unimplemented!()
}
}
#[async_trait]
impl Execute for MarkovAll {
async fn exec(&self, api: &Api, message: &Message) -> Result<(), Error> {
let messages = db::get_messages_random_all().await?;
let mut chain = Chain::new();
chain.feed(messages);
let mut sentences = chain.generate();
let mut msg = String::new();
for _ in 1..rand::thread_rng().gen_range(2, 10) {
msg = format!("{} {}", msg, sentences.pop().unwrap());
}
match api
.send(message.text_reply(msg.trim()).parse_mode(ParseMode::Html))
.await
{
Ok(_) => debug!("/markov_all command sent to {}", message.chat.id()),
Err(_) => warn!("/markov_all command sent failed to {}", message.chat.id()),
}
//api.send(message.chat.text("Text to message chat")).await?;
//api.send(message.from.text("Private text")).await?;
Ok(())
}
async fn exec_with_result(&self, api: &Api, message: &Message) -> Result<String, Error> {
unimplemented!()
}
#[allow(unused_variables)]
async fn exec_mystem(
&self,
api: &Api,
message: &Message,
mystem: &mut MyStem,
) -> Result<(), Error> {
unimplemented!()
}
}
#[async_trait]
impl Execute for Markov {
async fn exec(&self, api: &Api, message: &Message) -> Result<(), Error> {
let messages = db::get_messages_random_group(&message).await?;
let mut chain = Chain::new();
chain.feed(messages);
let mut sentences = chain.generate();
let mut msg = String::new();
for _ in 1..rand::thread_rng().gen_range(2, 10) {
msg = format!("{} {}", msg, sentences.pop().unwrap());
}
match api
.send(message.text_reply(msg.trim()).parse_mode(ParseMode::Html))
.await
{
Ok(_) => debug!("/markov command sent to {}", message.chat.id()),
Err(_) => warn!("/markov command sent failed to {}", message.chat.id()),
}
//api.send(message.chat.text("Text to message chat")).await?;
//api.send(message.from.text("Private text")).await?;
Ok(())
}
async fn exec_with_result(&self, api: &Api, message: &Message) -> Result<String, Error> {
unimplemented!()
}
#[allow(unused_variables)]
async fn exec_mystem(
&self,
api: &Api,
message: &Message,
mystem: &mut MyStem,
) -> Result<(), Error> {
unimplemented!()
}
}
#[async_trait]
impl Execute for Omedeto {
#[allow(unused_variables)]
async fn exec(&self, api: &Api, message: &Message) -> Result<(), Error> {
unimplemented!()
}
async fn exec_with_result(&self, api: &Api, message: &Message) -> Result<String, Error> {
unimplemented!()
}
#[warn(unused_must_use)]
async fn exec_mystem(
&self,
api: &Api,
message: &Message,
mystem: &mut MyStem,
) -> Result<(), Error> {
let all_msg = db::get_messages_user_all(&message).await?;
let re = Regex::new(r"^[яЯ] [а-яА-Я]+(-[а-яА-Я]+(_[а-яА-Я]+)*)*").unwrap();
let mut nouns: Vec<String> = all_msg
.iter()
.filter(|m| re.is_match(m))
.map(|m| m.split(' ').map(|s| s.to_string()).collect::<Vec<String>>()[1].clone())
.filter(|m| {
let stem = mystem.stemming(m.clone()).unwrap_or_default();
if !stem.is_empty() {
if !stem[0].lex.is_empty() {
match stem[0].lex[0].grammem.part_of_speech {
mystem::PartOfSpeech::Noun => stem[0].lex[0]
.grammem
.facts
.contains(&mystem::Fact::Case(Nominative)),
_ => false,
}
} else {
false
}
} else {
false
}
})
.map(|w| w.replace(|z| z == '.' || z == ',', ""))
.collect();
nouns.sort();
nouns.dedup();
nouns.shuffle(&mut rand::thread_rng());
//debug!("Found {} nouns. {:#?}", nouns.len(), nouns);
let mut verbs_p: Vec<String> = all_msg
.iter()
.filter(|m| re.is_match(m))
.map(|m| m.split(' ').map(|s| s.to_string()).collect::<Vec<String>>()[1].clone())
.filter(|m| {
let stem = mystem.stemming(m.clone()).unwrap_or_default();
#[allow(clippy::if_same_then_else)]
if stem.is_empty() {
false
} else if stem[0].lex.is_empty() {
false
} else {
match stem[0].lex[0].grammem.part_of_speech {
mystem::PartOfSpeech::Verb => stem[0].lex[0]
.grammem
.facts
.contains(&mystem::Fact::Tense(Past)),
_ => false,
}
}
})
.map(|w| w.replace(|z| z == '.' || z == ',', ""))
.collect();
verbs_p.sort();
verbs_p.dedup();
verbs_p.shuffle(&mut rand::thread_rng());
//debug!("Found {} past verbs. {:#?}", verbs_p.len(), verbs_p);
let mut verbs_i: Vec<String> = all_msg
.iter()
.filter(|m| re.is_match(m))
.map(|m| m.split(' ').map(|s| s.to_string()).collect::<Vec<String>>()[1].clone())
.filter(|m| {
let stem = mystem.stemming(m.clone()).unwrap_or_default();
#[allow(clippy::if_same_then_else)]
if stem.is_empty() {
false
} else if stem[0].lex.is_empty() {
false
} else {
match stem[0].lex[0].grammem.part_of_speech {
mystem::PartOfSpeech::Verb => {
stem[0].lex[0]
.grammem
.facts
.contains(&mystem::Fact::Tense(Inpresent))
&& stem[0].lex[0]
.grammem
.facts
.contains(&mystem::Fact::Person(First))
}
_ => false,
}
}
})
.map(|w| w.replace(|z| z == '.' || z == ',', ""))
.collect();
verbs_i.sort();
verbs_i.dedup();
verbs_i.shuffle(&mut rand::thread_rng());
//debug!("Found {} inpresent verbs. {:#?}", verbs_i.len(), verbs_i);
if nouns.is_empty() {
nouns.push(message.from.first_name.to_string());
}
let start: Vec<String> = vec![
"С новым годом".into(),
"С НГ тебя".into(),
"Поздравляю".into(),
"Поздравляю с НГ".into(),
];
let placeholders: Vec<String> = vec![
"[ДАННЫЕ УДАЛЕНЫ]".into(),
"[СЕКРЕТНО]".into(),
"[НЕТ ДАННЫХ]".into(),
"[ОШИБКА ДОСТУПА]".into(),
];
//debug!("Nouns: {:#?}", nouns);
//debug!("Verbs: {:#?}", verbs);
let fem = {
let mut fm = 0;
let mut mu = 0;
all_msg
.clone()
.into_iter()
.filter(|m| re.is_match(m))
.map(|m| m.split(' ').map(|s| s.to_string()).collect::<Vec<String>>()[1].clone())
.map(|m| {
let stem = mystem.stemming(m).unwrap_or_default();
#[allow(clippy::if_same_then_else)]
if stem.is_empty() {
} else if stem[0].lex.is_empty() {
} else {
if let mystem::PartOfSpeech::Verb = stem[0].lex[0].grammem.part_of_speech {
match stem[0].lex[0]
.grammem
.facts
.contains(&mystem::Fact::Tense(Past))
{
true => {
if stem[0].lex[0]
.grammem
.facts
.contains(&mystem::Fact::Gender(Feminine))
{
fm += 1;
} else {
mu += 1;
}
}
false => (),
}
}
}
})
.for_each(drop);
//debug!("fm - {}, mu - {}", fm, mu);
fm >= mu
};
//debug!("Is Feminine - {}", fem);
let result = format!(
"{} {} известн{} как {}, {}, а так же конечно {}. В прошедшем году ты часто давал{} нам знать, что ты {}, {} и {}. Нередко ты говорил{} я {}, я {} или даже я {}. =*",
start.choose(&mut rand::thread_rng()).unwrap(),
message.from.first_name.to_string(),
{ if fem { "ая" } else { "ый" } },
nouns.pop().unwrap_or_else(|| placeholders.choose(&mut rand::thread_rng()).unwrap().to_string()),
nouns.pop().unwrap_or_else(|| placeholders.choose(&mut rand::thread_rng()).unwrap().to_string()),
nouns.pop().unwrap_or_else(|| placeholders.choose(&mut rand::thread_rng()).unwrap().to_string()),
{ if fem { "а" } else { "" } },
verbs_p.pop().unwrap_or_else(|| placeholders.choose(&mut rand::thread_rng()).unwrap().to_string()),
verbs_p.pop().unwrap_or_else(|| placeholders.choose(&mut rand::thread_rng()).unwrap().to_string()),
verbs_p.pop().unwrap_or_else(|| placeholders.choose(&mut rand::thread_rng()).unwrap().to_string()),
{ if fem { "а" } else { "" } },
verbs_i.pop().unwrap_or_else(|| placeholders.choose(&mut rand::thread_rng()).unwrap().to_string()),
verbs_i.pop().unwrap_or_else(|| placeholders.choose(&mut rand::thread_rng()).unwrap().to_string()),
verbs_i.pop().unwrap_or_else(|| placeholders.choose(&mut rand::thread_rng()).unwrap().to_string()),
);
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();
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

@ -1,6 +1,5 @@
use crate::errors;
use crate::utils;
use futures::StreamExt;
use rusqlite::{named_params, params, Connection, Error, Result};
use std::time::SystemTime;
use telegram_bot::*;
@ -55,7 +54,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 +74,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,7 +100,7 @@ pub(crate) fn get_confs() -> Result<Vec<Conf>> {
Ok(confs)
}
*/
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")?;
@ -136,6 +135,7 @@ pub(crate) async fn get_messages_random_group(
Ok(messages)
}
#[allow(dead_code)]
pub(crate) async fn get_messages_user_group(
message: &telegram_bot::Message,
) -> Result<Vec<String>, Error> {
@ -214,7 +214,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,
@ -228,10 +228,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(),
@ -250,7 +250,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(())
@ -283,7 +283,7 @@ 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()
@ -310,7 +310,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(())
@ -345,14 +345,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 =
@ -366,7 +364,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)",
@ -400,6 +398,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,

View File

@ -20,51 +20,83 @@ pub enum Error {
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\
// // #&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 {
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 {
return Error::MystemError(e);
Error::MystemError(e)
}
}

View File

@ -1,10 +1,13 @@
use crate::commands;
//use crate::commands::Command;
use crate::commands::{Code, Execute, Here, Markov, MarkovAll, Omedeto, Sql, Top};
use crate::db;
use crate::errors;
use crate::utils;
use mystem::MyStem;
use telegram_bot::*;
include!("../assets/help_text.rs");
pub async fn handler(
api: Api,
message: Message,
@ -21,17 +24,103 @@ pub async fn handler(
title,
&message.from.id,
&message.from.first_name,
data
data.replace("\n", " ")
);
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?,
"/omedeto" => commands::omedeto(api, message, mystem).await?,
_ => (),
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 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") => {
db::add_sentence(&message, mystem).await?;
Here {
data: "".to_string(),
}
.exec(&api, &message)
.await?
}
s if s.to_string().starts_with("/sql") => match {
Sql {
data: s.replace("/sql ", ""),
}
.exec_with_result(&api, &message)
.await
} {
Ok(msg) => {
let _ = api
.send(message.text_reply(msg).parse_mode(ParseMode::Html))
.await?;
}
Err(e) => {
let _ = api
.send(
message
.text_reply(format!("Error: {:#?}", e))
.parse_mode(ParseMode::Html),
)
.await?;
}
},
"/top" => {
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?
}
"/omedeto" => {
Omedeto {
data: "".to_string(),
}
.exec_mystem(&api, &message, mystem)
.await?
}
_ => {
db::add_sentence(&message, mystem).await?
}
}
}
MessageKind::Photo { ref caption, .. } => {
@ -42,7 +131,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?;
}
@ -55,7 +144,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?;
}

View File

@ -49,7 +49,12 @@ async fn main() -> Result<(), errors::Error> {
if let UpdateKind::Message(message) = update.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),
}
}
}
Ok(())

View File

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