51 Commits
0.5.4 ... main

Author SHA1 Message Date
739109e5f7 pux
Some checks failed
Build / build-windows (push) Has been cancelled
Build / build-linux (push) Has been cancelled
2024-10-17 21:15:33 +03:00
4ac7920815 bump rand 2024-10-17 21:04:04 +03:00
47f1ab9348 Fux 2024-10-17 20:58:09 +03:00
28cdf4be2e Fixing shit 2024-10-17 20:55:41 +03:00
0bcb99089b Update README.md
Some checks failed
Build / build-windows (push) Has been cancelled
Build / build-linux (push) Has been cancelled
2024-08-21 15:51:20 +03:00
0bba91486e Fix ci
Some checks failed
Build / build-windows (push) Has been cancelled
Build / build-linux (push) Has been cancelled
2024-08-07 20:10:49 +03:00
207717491c Fix docker build
Some checks failed
Build / build-linux (push) Failing after 31s
Build / build-windows (push) Has been cancelled
2024-08-05 17:18:15 +03:00
f185c31a17 Fix docker build 2024-08-05 17:14:00 +03:00
aeacc15439 Fix docker build 2024-08-05 17:07:38 +03:00
ccf54bc279 Fix docker build 2024-08-05 16:51:59 +03:00
AB
e07f173b7c Fix CI
Some checks are pending
Build / build-windows (push) Waiting to run
Build / build-linux (push) Waiting to run
2024-08-04 23:34:19 +03:00
AB
bcd869273d Fix CI 2024-08-04 23:17:56 +03:00
AB
1ee12fa0c0 Fix CI 2024-08-04 23:14:59 +03:00
76dab182cf pin docker ubuntu version 2024-08-04 23:05:07 +03:00
bacdf559c4 Fix /markov in private chats 2023-07-24 14:38:19 +03:00
82a81fffb6 Rebuild new docker 2023-07-01 04:28:04 +03:00
392669703d Update README.md 2022-12-16 12:29:50 +02:00
2fb5b03fbf Add global, conf stat. 2022-11-23 17:53:56 +02:00
55a7dd6a67 add @all command 2022-11-03 19:01:17 +02:00
AB
ff6e04988e Merge branch 'main' of https://github.com/house-of-vanity/desubot into main 2022-05-05 12:25:10 +03:00
AB
7c067065bd add a few stop words 2022-05-05 12:24:38 +03:00
175f056add Update README.md 2022-01-23 15:09:05 +03:00
AB
20f366572b Add scheme. 2022-01-23 15:02:21 +03:00
e5079fa584 Update db.rs 2021-12-08 17:51:32 +03:00
fdc52b7198 Update README.md
Add deps
2021-12-08 17:46:33 +03:00
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
13 changed files with 935 additions and 101 deletions

View File

@ -11,38 +11,65 @@ jobs:
build-linux:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Build
run: cargo build --verbose --release
- name: Upload Linux binary
uses: actions/upload-artifact@v1
uses: actions/upload-artifact@v4
with:
name: desubot
path: ./target/release/desubot
build-windows:
runs-on: windows-latest
build-push-docker:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Build
run: cargo build --verbose --release
- name: Upload Windows binary
uses: actions/upload-artifact@v1
- 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@v3
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
-
name: Login to DockerHub
uses: docker/login-action@v3
with:
name: desubot.exe
path: ./target/release/desubot.exe
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
-
name: Build and push
id: docker_build_latest
uses: docker/build-push-action@v6
with:
push: true
tags: ultradesu/desubot:latest
-
name: Build and push
id: docker_build_tag
uses: docker/build-push-action@v6
with:
push: true
tags: ultradesu/desubot:${{ steps.branch_name.outputs.SOURCE_TAG }}
publish:
name: Publish release
needs: [build-windows, build-linux]
needs: [build-linux]
runs-on: ubuntu-latest
steps:
# - name: Checkout
# uses: actions/checkout@v4
- name: Get the version (git tag)
id: get_version
run: |
echo ${GITHUB_REF/refs\/tags\/v/}
echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\/v/}
echo ::set-output name=FULL_TAG::${GITHUB_REF/refs\/tags\//}
- name: Get the repo data (git tag)
id: get_repo_data
run: |
@ -50,40 +77,25 @@ jobs:
echo ::set-output name=AUTHOR::$(echo "$GITHUB_REPOSITORY" | awk -F / '{print $1}')
echo ::set-output name=REPO_NAME::$(echo "$GITHUB_REPOSITORY" | awk -F / '{print $2}')
shell: bash
- name: Create Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: Release ${{ github.ref }}
draft: false
prerelease: false
- name: Upload binary assets
- name: Prepare release downloading
run: |
mkdir artifacts
- name: Download Linux binary
uses: actions/download-artifact@v1
uses: actions/download-artifact@v4
with:
name: desubot
path: ./artifacts/
- name: Download Windows binary
uses: actions/download-artifact@v1
with:
name: desubot.exe
path: ./artifacts/
- name: Upload binary assets
run: |
wget https://github.com/aktau/github-release/releases/download/v0.7.2/linux-amd64-github-release.tar.bz2
tar xjf linux-amd64-github-release.tar.bz2
export GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }}
for artifact in ./artifacts/*; do
./bin/linux/amd64/github-release upload \
-u ${{ steps.get_repo_data.outputs.AUTHOR }} \
-r ${{ steps.get_repo_data.outputs.REPO_NAME }} \
--tag ${{ steps.get_version.outputs.FULL_TAG }} \
--name ${artifact} \
--file ${artifact}
done
- name: Build
run: echo ${{ github.sha }} > Release.txt
- name: Test
run: cat Release.txt
- name: Release
uses: softprops/action-gh-release@v2
if: startsWith(github.ref, 'refs/tags/')
with:
files: './artifacts/*'

4
.gitignore vendored
View File

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

View File

@ -1,6 +1,6 @@
[package]
name = "desubot"
version = "0.5.4"
version = "0.5.12"
authors = ["AB <ab@hexor.ru>"]
edition = "2018"
@ -12,7 +12,8 @@ tokio = { version = "0.2", features = ["full"]}
tracing = "0.1.9"
tracing-futures = "0.2"
multipart = { version = "0.16", default-features = false, features = ["client"] }
telegram-bot = "0.8.0"
#telegram-bot = "0.8.0"
telegram-bot = { git = "https://github.com/ayrat555/telegram-bot", branch = "ayrat555/api-fixes-10" }
silicon = "0.4.0"
hyper = "0.13"
hyper-tls = { version = "0.4", optional = true }
@ -29,7 +30,7 @@ log = { version = "^0.4.5", features = ["std"] }
subprocess = "0.2.6"
serde_json = "1.0"
markov = "1.1.0"
rand = "0.7.3"
rand = "0.8.5"
mystem = "^0.2"
#mystem = { path = "../mystem-rs" }
async-trait = "0.1.42"
@ -38,4 +39,4 @@ sqlparser = "0.7.0"
[dependencies.syntect]
version = "4.4"
default-features = false
features = ["parsing", "dump-load", "regex-onig"]
features = ["parsing", "dump-load", "regex-onig"]

14
Dockerfile Normal file
View File

@ -0,0 +1,14 @@
# syntax=docker/dockerfile:1
FROM rust:bookworm AS builder
WORKDIR /desubot
ADD ./ /desubot/
RUN cargo build --release
FROM debian:bookworm
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

14
README
View File

@ -1,14 +0,0 @@
Desubot
Telegram bot with light group statistic and heavy spy features.
== Features ==
* Collect all the messages sent to group.
* Collect all the media sent to group including voice, stickers, video, video notes, documents.
* /here command to mention all members.
* Alongside with saving whole message bot perform blacklist filter and stemming for every word (only Russian). "Красивую собаку мыли негры" -> "красивый собака мыть негр"
* Generate sentences using Markov Chains trained on history with /markov_all.
* 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.
On Windows it may be placed on working directory. Both Linux and Windows mystem binary is in repo.

34
README.md Normal file
View File

@ -0,0 +1,34 @@
[![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 Telegram Bot
**Desubot** is a Telegram bot with light group statistics and powerful spy features.
## Features
- **Collect all messages**: The bot collects all messages sent to the group.
- **Collect all media**: The bot saves all media sent to the group, including voice messages, stickers, videos, video notes, and documents.
- **/here command**: Mention all group members.
- **Blacklist filter and stemming**: The bot saves the entire message, performs blacklist filtering, and stems every word (Russian only). For example, "Красивую собаку мыли негры" -> "красивый собака мыть негр".
- **Markov Chain sentence generation**: The bot generates sentences using Markov Chains trained on the history with the `/markov_all` command.
- **Syntax highlighting for CODE**: Export code with syntax highlighting to an image.
## Important
- **MyStem**: Desubot uses MyStem by Yandex for word stemming and assumes that the `mystem` binary is available in the PATH.
- **Ubuntu dependencies**: The following packages are required:
```bash
libssl-dev libsqlite3-dev cmake libfreetype-dev pkg-config
[Docker Hub](https://hub.docker.com/repository/docker/ultradesu/desubot/general)
![image](https://user-images.githubusercontent.com/4666566/150677613-32bdedf9-4b4c-4ec5-99cd-3d0221e56fb5.png)
![image](https://user-images.githubusercontent.com/4666566/150677660-183572b4-2a69-425f-a32c-dba5ec97e438.png)
## 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)

View File

@ -1,5 +1,5 @@
#[allow(dead_code)]
static CODE_HELP: &str = "<b>Code highlighter</b>
pub(crate) static CODE_HELP: &str = "<b>Code highlighter</b>
<i>Usage</i>
<pre>/code
@ -7,7 +7,7 @@ static CODE_HELP: &str = "<b>Code highlighter</b>
#&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
Max length - 4000
List of themes:
1337

431
assets/stop-words.txt Normal file
View File

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

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

@ -21,7 +21,8 @@ use syntect::highlighting::Theme;
use syntect::parsing::SyntaxReference;
use syntect::util::LinesWithEndings;
use telegram_bot::prelude::*;
use telegram_bot::{Api, Message, ParseMode};
use telegram_bot::*;
use telegram_bot::{Api, Message, ParseMode, UserId};
include!("../assets/help_text.rs");
@ -31,6 +32,12 @@ pub struct Here {
pub struct Top {
pub data: String,
}
pub struct ConfTop {
pub data: String,
}
pub struct GlobalTop {
pub data: String,
}
pub struct MarkovAll {
pub data: String,
}
@ -46,6 +53,9 @@ pub struct Sql {
pub struct Code {
pub data: String,
}
pub struct Scheme {
pub data: String,
}
#[async_trait]
pub trait Execute {
@ -59,6 +69,68 @@ pub trait Execute {
) -> Result<(), Error>;
}
#[async_trait]
impl Execute for Scheme {
async fn exec(&self, api: &Api, message: &Message) -> Result<(), Error> {
match api
.send(
message
.text_reply(format!(
"{}{}{}",
"<pre>",
include_str!("../assets/scheme.sql").to_string(),
"</pre>"
))
.parse_mode(ParseMode::Html),
)
.await
{
Ok(_) => debug!("/scheme command sent to {}", message.chat.id()),
Err(_) => warn!("/scheme command sent failed to {}", message.chat.id()),
};
match {
Code {
data: format!(
"{}{}",
include_str!("../assets/scheme.sql").to_string(),
"\n#sql"
),
}
.exec_with_result(&api, &message)
.await
} {
Ok(path) => {
let file = InputFileUpload::with_path(path.clone());
// api.send(message.chat.document(&file)).await?;
//
// // Send an image from disk
api.send(message.chat.document(&file)).await?;
//debug!("{:#?}", formatter);
let _ = std::fs::remove_file(&path);
}
Err(_) => {
let _ = api
.send(message.text_reply(CODE_HELP).parse_mode(ParseMode::Html))
.await?;
}
}
Ok(())
}
async fn exec_with_result(&self, api: &Api, message: &Message) -> Result<String, Error> {
unimplemented!()
}
async fn exec_mystem(
&self,
api: &Api,
message: &Message,
mystem: &mut MyStem,
) -> Result<(), Error> {
unimplemented!()
}
}
#[async_trait]
impl Execute for Sql {
async fn exec(&self, api: &Api, message: &Message) -> Result<(), Error> {
@ -69,7 +141,7 @@ impl Execute for Sql {
let mut sql = self.data.clone();
debug!("PIZDA - {}", sql);
if sql == "/sql" || sql == "/sql-" {
return Ok(SQL_HELP.to_string())
return Ok(SQL_HELP.to_string());
}
let is_head = if sql.starts_with('-') {
sql = sql.replacen("-", "", 1);
@ -192,7 +264,15 @@ impl Execute for Sql {
#[async_trait]
impl Execute for Here {
async fn exec(&self, api: &Api, message: &Message) -> Result<(), Error> {
let members: Vec<telegram_bot::User> = db::get_members(message.chat.id(), 60).unwrap();
let members: Vec<telegram_bot::User> =
db::get_members(message.chat.id(), 60).unwrap_or(vec![telegram_bot::User {
id: UserId::new(124317807),
first_name: "Ultradesu".to_string(),
last_name: None,
username: None,
is_bot: false,
language_code: None,
}]);
for u in &members {
debug!("Found user {:?} in chat {}", u, message.chat.id());
}
@ -273,6 +353,84 @@ impl Execute for Top {
}
}
#[async_trait]
impl Execute for GlobalTop {
async fn exec(&self, api: &Api, message: &Message) -> Result<(), Error> {
let top = db::get_global_top().await?;
let mut msg = "<b>Global top words:</b>\n<pre>".to_string();
let mut counter = 1;
for word in top.iter() {
msg = format!(
"{} <b>{}</b> {} - {}\n",
msg, counter, word.word, word.count
);
counter += 1;
}
msg = format!("{}{}", msg, "</pre>");
match api
.send(message.text_reply(msg).parse_mode(ParseMode::Html))
.await
{
Ok(_) => debug!("/global_top command sent to {}", message.chat.id()),
Err(_) => warn!("/global_top command sent failed to {}", message.chat.id()),
}
Ok(())
}
async fn exec_with_result(&self, api: &Api, message: &Message) -> Result<String, Error> {
unimplemented!()
}
#[allow(unused_variables)]
async fn exec_mystem(
&self,
api: &Api,
message: &Message,
mystem: &mut MyStem,
) -> Result<(), Error> {
unimplemented!()
}
}
#[async_trait]
impl Execute for ConfTop {
async fn exec(&self, api: &Api, message: &Message) -> Result<(), Error> {
let top = db::get_conf_top(&message).await?;
let mut msg = "<b>Conf top words:</b>\n<pre>".to_string();
let mut counter = 1;
for word in top.iter() {
msg = format!(
"{} <b>{}</b> {} - {}\n",
msg, counter, word.word, word.count
);
counter += 1;
}
msg = format!("{}{}", msg, "</pre>");
match api
.send(message.text_reply(msg).parse_mode(ParseMode::Html))
.await
{
Ok(_) => debug!("/conf_top command sent to {}", message.chat.id()),
Err(_) => warn!("/conf_top command sent failed to {}", message.chat.id()),
}
Ok(())
}
async fn exec_with_result(&self, api: &Api, message: &Message) -> Result<String, Error> {
unimplemented!()
}
#[allow(unused_variables)]
async fn exec_mystem(
&self,
api: &Api,
message: &Message,
mystem: &mut MyStem,
) -> Result<(), Error> {
unimplemented!()
}
}
#[async_trait]
impl Execute for MarkovAll {
async fn exec(&self, api: &Api, message: &Message) -> Result<(), Error> {
@ -281,7 +439,7 @@ impl Execute for MarkovAll {
chain.feed(messages);
let mut sentences = chain.generate();
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());
}
match api
@ -319,8 +477,8 @@ impl Execute for Markov {
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());
for _ in 1..rand::thread_rng().gen_range(2..10) {
msg = format!("{} {}", msg, sentences.pop().unwrap_or(" ".into()));
}
match api
.send(message.text_reply(msg.trim()).parse_mode(ParseMode::Html))
@ -563,9 +721,6 @@ impl Execute for Code {
.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
@ -605,7 +760,7 @@ impl Execute for Code {
.collect();
let theme = if theme.len() != 1 {
ts.themes.get("Dracula").unwrap()
ts.themes.get("gruvbox").unwrap()
} else {
theme[0]
};

View File

@ -1,3 +1,5 @@
#[allow(unused_mut)]
#[allow(dead_code)]
use crate::errors;
use crate::utils;
use rusqlite::{named_params, params, Connection, Error, Result};
@ -34,6 +36,30 @@ pub(crate) fn update_scheme() -> Result<()> {
Ok(())
}
pub(crate) fn load_stopwords() -> Result<()> {
info!("Populating stop words wait please.");
let conn = open()?;
for table in include_str!("../assets/stop-words.txt")
.split('\n')
.into_iter()
{
let word = table.trim();
if word != "" {
let mut _stmt = conn
.prepare_cached(
"
INSERT OR IGNORE INTO
stop_words('word')
VALUES (:word)
",
)?
.insert(params![word]);
}
}
info!("Stop words updated.");
Ok(())
}
pub(crate) fn get_user(id: telegram_bot::UserId) -> Result<telegram_bot::User, errors::Error> {
let conn = open()?;
let mut stmt = conn.prepare_cached(
@ -188,8 +214,8 @@ pub(crate) fn get_members(id: telegram_bot::ChatId, limit: u32) -> Result<Vec<te
};
debug!("{}", where_statement);
let conn = open()?;
let mut stmt = conn.prepare_cached(
&format!("
let mut stmt = conn.prepare_cached(&format!(
"
SELECT DISTINCT(u.username), u.id, u.first_name, u.last_name, u.date,
(strftime('%s','now')-r.date)/60/60/24 as days_seen
FROM relations r
@ -200,8 +226,9 @@ pub(crate) fn get_members(id: telegram_bot::ChatId, limit: u32) -> Result<Vec<te
WHERE c.id = :id
{}
GROUP BY u.id
ORDER BY r.date DESC", where_statement),
)?;
ORDER BY r.date DESC",
where_statement
))?;
let mut rows = stmt.query_named(&[(":id", &id.to_string())])?;
let mut users = Vec::new();
@ -293,7 +320,11 @@ pub(crate) async fn add_user(message: Message) -> Result<(), Error> {
(":first_name", &update.first_name),
(":last_name", &update.last_name),
])?;
debug!("User {} updated: {:?}", update.first_name, get_user(update.id));
debug!(
"User {} updated: {:?}",
update.first_name,
get_user(update.id)
);
}
Err(_) => {
let unix_time = SystemTime::now()
@ -427,6 +458,7 @@ pub(crate) async fn add_sentence(
};
// Save stemmed words
debug!("Going to stem: {}", text);
let words = mystem.stemming(text)?;
conn.execute("BEGIN TRANSACTION", params![]);
for word in words {
@ -441,7 +473,7 @@ pub(crate) async fn add_sentence(
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![]);
@ -480,3 +512,56 @@ pub(crate) async fn get_top(
}
Ok(top)
}
pub(crate) async fn get_global_top() -> Result<Vec<TopWord>, errors::Error> {
let conn = open()?;
let mut stmt = conn.prepare_cached(
"
SELECT w.word, COUNT(*) as count FROM relations r
LEFT JOIN word w ON w.id = r.word_id
GROUP BY w.word
ORDER BY count DESC
LIMIT 50
",
)?;
let mut rows = stmt.query_named(named_params! {})?;
let mut top = Vec::new();
while let Some(row) = rows.next()? {
top.push(TopWord {
word: row.get(0)?,
count: row.get(1)?,
})
}
Ok(top)
}
pub(crate) async fn get_conf_top(
message: &telegram_bot::Message,
) -> Result<Vec<TopWord>, errors::Error> {
let conf_id = i64::from(message.chat.id());
let conn = open()?;
let mut stmt = conn.prepare_cached(
"
SELECT w.word, COUNT(*) as count FROM relations r
LEFT JOIN word w ON w.id = r.word_id
WHERE r.conf_id = :conf_id
GROUP BY w.word
ORDER BY count DESC
LIMIT 10
",
)?;
let mut rows = stmt.query_named(named_params! {":conf_id": conf_id})?;
let mut top = Vec::new();
while let Some(row) = rows.next()? {
top.push(TopWord {
word: row.get(0)?,
count: row.get(1)?,
})
}
Ok(top)
}

View File

@ -1,5 +1,7 @@
//use crate::commands::Command;
use crate::commands::{Code, Execute, Here, Markov, MarkovAll, Omedeto, Sql, Top};
use crate::commands::{
Code, ConfTop, Execute, GlobalTop, Here, Markov, MarkovAll, Omedeto, Scheme, Sql, Top,
};
use crate::db;
use crate::errors;
use crate::utils;
@ -38,11 +40,30 @@ pub async fn handler(
.await
} {
Ok(path) => {
let mut cnt_lines = 0;
for _ in s.lines() {
cnt_lines = cnt_lines + 1;
}
let mut cnt_chars = 0;
for _ in s.chars() {
cnt_chars = cnt_chars + 1;
}
let file = InputFileUpload::with_path(path.clone());
info!("lines: {}, chars: {}", cnt_lines, cnt_chars);
// api.send(message.chat.document(&file)).await?;
//
// // Send an image from disk
api.send(message.chat.photo(&file)).await?;
if cnt_chars > 4000 {
let _ = api
.send(message.text_reply(CODE_HELP).parse_mode(ParseMode::Html))
.await?;
return Ok(());
}
if cnt_lines < 81 {
api.send(message.chat.photo(&file)).await?;
} else {
api.send(message.chat.document(&file)).await?;
}
//debug!("{:#?}", formatter);
let _ = std::fs::remove_file(&path);
}
@ -53,7 +74,14 @@ pub async fn handler(
}
}
}
s if s.contains("/here") || s.contains("@here") => {
s if s.contains("/here")
|| s.contains("@here")
|| s.contains("/хере")
|| s.contains("@хере")
|| s.contains("@all")
|| s.contains("\"руку")
|| s.contains("\"хере") =>
{
db::add_sentence(&message, mystem).await?;
Here {
data: "".to_string(),
@ -83,15 +111,22 @@ pub async fn handler(
.await?;
}
},
"/top" => {
"/top" | "/stat" => {
Top {
data: "".to_string(),
}
.exec(&api, &message)
.await?
}
"/stat" => {
Top {
"/global_top" | "/global_stat" => {
GlobalTop {
data: "".to_string(),
}
.exec(&api, &message)
.await?
}
"/conf_stat" | "/conf_top" => {
ConfTop {
data: "".to_string(),
}
.exec(&api, &message)
@ -105,7 +140,24 @@ pub async fn handler(
.await?
}
"/markov" => {
Markov {
if title != "PRIVATE" {
Markov {
data: "".to_string(),
}
.exec(&api, &message)
.await?
} else {
let _ = api
.send(
message
.text_reply("Allowed in groups only")
.parse_mode(ParseMode::Html),
)
.await?;
}
}
"/scheme" | "/schema" => {
Scheme {
data: "".to_string(),
}
.exec(&api, &message)
@ -118,9 +170,7 @@ pub async fn handler(
.exec_mystem(&api, &message, mystem)
.await?
}
_ => {
db::add_sentence(&message, mystem).await?
}
_ => db::add_sentence(&message, mystem).await?,
}
}
MessageKind::Photo { ref caption, .. } => {

View File

@ -1,4 +1,6 @@
#![allow(unreachable_code)]
use std::{env, process};
use tokio::time::{delay_for, Duration};
use futures::StreamExt;
use telegram_bot::*;
@ -16,7 +18,7 @@ use mystem::MyStem;
#[tokio::main]
async fn main() -> Result<(), errors::Error> {
env_logger::from_env(Env::default().default_filter_or("info")).init();
env_logger::Builder::from_env(Env::default().default_filter_or("info")).init();
let mut mystem = match MyStem::new() {
Ok(mystem) => mystem,
Err(e) => {
@ -28,6 +30,10 @@ async fn main() -> Result<(), errors::Error> {
Ok(_) => {}
Err(e) => panic!("Database error: {:?}", e),
}
match db::load_stopwords() {
Ok(_) => {}
Err(e) => panic!("Database error: {:?}", e),
}
let token = match env::var("TELEGRAM_BOT_TOKEN") {
Ok(token) => token,
Err(_) => {
@ -44,18 +50,33 @@ async fn main() -> Result<(), errors::Error> {
me.first_name,
me.id
);
while let Some(update) = stream.next().await {
let update = update?;
if let UpdateKind::Message(message) = update.kind {
db::add_conf(message.clone()).await?;
db::add_user(message.clone()).await?;
match handlers::handler(api.clone(), message, token.clone(), &mut mystem, me.clone())
.await
{
Ok(_) => {}
Err(e) => warn!("An error occurred handling command. {:?}", e),
}
loop {
while let Some(update) = stream.next().await {
match update {
Ok(u) => {
if let UpdateKind::Message(message) = u.kind {
db::add_conf(message.clone()).await?;
db::add_user(message.clone()).await?;
match handlers::handler(
api.clone(),
message,
token.clone(),
&mut mystem,
me.clone(),
)
.await
{
Ok(_) => {}
Err(e) => warn!("An error occurred handling command. {:?}", e),
}
}
}
Err(e) => {
warn!("Telegram API Error: {:?}", e);
}
};
}
delay_for(Duration::from_secs(2)).await;
}
Ok(())
}