mirror of
https://github.com/house-of-vanity/desubot.git
synced 2025-07-07 20:54:06 +00:00
Compare commits
25 Commits
Author | SHA1 | Date | |
---|---|---|---|
739109e5f7 | |||
4ac7920815 | |||
47f1ab9348 | |||
28cdf4be2e | |||
0bcb99089b | |||
0bba91486e | |||
207717491c | |||
f185c31a17 | |||
aeacc15439 | |||
ccf54bc279 | |||
e07f173b7c | |||
bcd869273d | |||
1ee12fa0c0 | |||
76dab182cf | |||
bacdf559c4 | |||
82a81fffb6 | |||
392669703d | |||
2fb5b03fbf | |||
55a7dd6a67 | |||
ff6e04988e | |||
7c067065bd | |||
175f056add | |||
20f366572b | |||
e5079fa584 | |||
fdc52b7198 |
75
.github/workflows/build-push.yml
vendored
75
.github/workflows/build-push.yml
vendored
@ -11,26 +11,15 @@ jobs:
|
|||||||
build-linux:
|
build-linux:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
- name: Build
|
- name: Build
|
||||||
run: cargo build --verbose --release
|
run: cargo build --verbose --release
|
||||||
- name: Upload Linux binary
|
- name: Upload Linux binary
|
||||||
uses: actions/upload-artifact@v1
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: desubot
|
name: desubot
|
||||||
path: ./target/release/desubot
|
path: ./target/release/desubot
|
||||||
|
|
||||||
build-windows:
|
|
||||||
runs-on: windows-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- name: Build
|
|
||||||
run: cargo build --verbose --release
|
|
||||||
- name: Upload Windows binary
|
|
||||||
uses: actions/upload-artifact@v1
|
|
||||||
with:
|
|
||||||
name: desubot.exe
|
|
||||||
path: ./target/release/desubot.exe
|
|
||||||
build-push-docker:
|
build-push-docker:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
@ -42,42 +31,45 @@ jobs:
|
|||||||
echo ::set-output name=SOURCE_TAG::${GITHUB_REF#refs/tags/}
|
echo ::set-output name=SOURCE_TAG::${GITHUB_REF#refs/tags/}
|
||||||
-
|
-
|
||||||
name: Set up QEMU
|
name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v1
|
uses: docker/setup-qemu-action@v3
|
||||||
-
|
-
|
||||||
name: Set up Docker Buildx
|
name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v1
|
uses: docker/setup-buildx-action@v3
|
||||||
-
|
-
|
||||||
name: Login to DockerHub
|
name: Login to DockerHub
|
||||||
uses: docker/login-action@v1
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
-
|
-
|
||||||
name: Build and push
|
name: Build and push
|
||||||
id: docker_build_latest
|
id: docker_build_latest
|
||||||
uses: docker/build-push-action@v2
|
uses: docker/build-push-action@v6
|
||||||
with:
|
with:
|
||||||
push: true
|
push: true
|
||||||
tags: ultradesu/desubot:latest
|
tags: ultradesu/desubot:latest
|
||||||
-
|
-
|
||||||
name: Build and push
|
name: Build and push
|
||||||
id: docker_build_tag
|
id: docker_build_tag
|
||||||
uses: docker/build-push-action@v2
|
uses: docker/build-push-action@v6
|
||||||
with:
|
with:
|
||||||
push: true
|
push: true
|
||||||
tags: ultradesu/desubot:${{ steps.branch_name.outputs.SOURCE_TAG }}
|
tags: ultradesu/desubot:${{ steps.branch_name.outputs.SOURCE_TAG }}
|
||||||
|
|
||||||
publish:
|
publish:
|
||||||
name: Publish release
|
name: Publish release
|
||||||
needs: [build-windows, build-linux]
|
needs: [build-linux]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
# - name: Checkout
|
||||||
|
# uses: actions/checkout@v4
|
||||||
- name: Get the version (git tag)
|
- name: Get the version (git tag)
|
||||||
id: get_version
|
id: get_version
|
||||||
run: |
|
run: |
|
||||||
echo ${GITHUB_REF/refs\/tags\/v/}
|
echo ${GITHUB_REF/refs\/tags\/v/}
|
||||||
echo ::set-output name=VERSION::${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\//}
|
echo ::set-output name=FULL_TAG::${GITHUB_REF/refs\/tags\//}
|
||||||
|
|
||||||
- name: Get the repo data (git tag)
|
- name: Get the repo data (git tag)
|
||||||
id: get_repo_data
|
id: get_repo_data
|
||||||
run: |
|
run: |
|
||||||
@ -85,40 +77,25 @@ jobs:
|
|||||||
echo ::set-output name=AUTHOR::$(echo "$GITHUB_REPOSITORY" | awk -F / '{print $1}')
|
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}')
|
echo ::set-output name=REPO_NAME::$(echo "$GITHUB_REPOSITORY" | awk -F / '{print $2}')
|
||||||
shell: bash
|
shell: bash
|
||||||
- name: Create Release
|
|
||||||
id: create_release
|
- name: Prepare release downloading
|
||||||
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
|
|
||||||
run: |
|
run: |
|
||||||
mkdir artifacts
|
mkdir artifacts
|
||||||
|
|
||||||
- name: Download Linux binary
|
- name: Download Linux binary
|
||||||
uses: actions/download-artifact@v1
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: desubot
|
name: desubot
|
||||||
path: ./artifacts/
|
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/*'
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "desubot"
|
name = "desubot"
|
||||||
version = "0.5.8"
|
version = "0.5.12"
|
||||||
authors = ["AB <ab@hexor.ru>"]
|
authors = ["AB <ab@hexor.ru>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ 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.8.5"
|
||||||
mystem = "^0.2"
|
mystem = "^0.2"
|
||||||
#mystem = { path = "../mystem-rs" }
|
#mystem = { path = "../mystem-rs" }
|
||||||
async-trait = "0.1.42"
|
async-trait = "0.1.42"
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
# syntax=docker/dockerfile:1
|
# syntax=docker/dockerfile:1
|
||||||
|
|
||||||
FROM rust:latest AS builder
|
FROM rust:bookworm AS builder
|
||||||
WORKDIR /desubot
|
WORKDIR /desubot
|
||||||
ADD ./ /desubot/
|
ADD ./ /desubot/
|
||||||
RUN cargo build --release
|
RUN cargo build --release
|
||||||
|
|
||||||
FROM ubuntu:latest
|
FROM debian:bookworm
|
||||||
WORKDIR /storage
|
WORKDIR /storage
|
||||||
COPY --from=builder /desubot/target/release/desubot /usr/bin/
|
COPY --from=builder /desubot/target/release/desubot /usr/bin/
|
||||||
COPY mystem /usr/bin/
|
COPY mystem /usr/bin/
|
||||||
|
38
README.md
38
README.md
@ -1,20 +1,34 @@
|
|||||||
[](https://app.fossa.com/projects/git%2Bgithub.com%2Fhouse-of-vanity%2Fdesubot?ref=badge_shield)
|
[](https://app.fossa.com/projects/git%2Bgithub.com%2Fhouse-of-vanity%2Fdesubot?ref=badge_shield)
|
||||||
|
|
||||||
Desubot
|
# Desubot Telegram Bot
|
||||||
Telegram bot with light group statistic and heavy spy features.
|
|
||||||
|
|
||||||
== Features ==
|
**Desubot** is a Telegram bot with light group statistics and powerful spy 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 ==
|
## Features
|
||||||
* 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.
|
|
||||||
|
|
||||||
|
- **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)
|
||||||
|
|
||||||
|
|
||||||
|

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

|
||||||
|
|
||||||
## License
|
## License
|
||||||
[](https://app.fossa.com/projects/git%2Bgithub.com%2Fhouse-of-vanity%2Fdesubot?ref=badge_large)
|
[](https://app.fossa.com/projects/git%2Bgithub.com%2Fhouse-of-vanity%2Fdesubot?ref=badge_large)
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
static CODE_HELP: &str = "<b>Code highlighter</b>
|
pub(crate) static CODE_HELP: &str = "<b>Code highlighter</b>
|
||||||
|
|
||||||
<i>Usage</i>
|
<i>Usage</i>
|
||||||
<pre>/code
|
<pre>/code
|
||||||
@ -7,7 +7,7 @@ static CODE_HELP: &str = "<b>Code highlighter</b>
|
|||||||
#<lang - JS by default> #<theme - Dracula by default></pre>
|
#<lang - JS by default> #<theme - Dracula by default></pre>
|
||||||
|
|
||||||
Language may be defined by both name and extension - Rust, rs...
|
Language may be defined by both name and extension - Rust, rs...
|
||||||
Max lines - 80
|
Max length - 4000
|
||||||
|
|
||||||
List of themes:
|
List of themes:
|
||||||
1337
|
1337
|
||||||
|
@ -11,6 +11,10 @@
|
|||||||
т
|
т
|
||||||
у
|
у
|
||||||
я
|
я
|
||||||
|
чо
|
||||||
|
че
|
||||||
|
ща
|
||||||
|
ваще
|
||||||
бы
|
бы
|
||||||
stat
|
stat
|
||||||
вообще
|
вообще
|
||||||
@ -424,4 +428,4 @@ ThreadTopBot
|
|||||||
ага
|
ага
|
||||||
делать
|
делать
|
||||||
писать
|
писать
|
||||||
бот
|
бот
|
||||||
|
159
src/commands.rs
159
src/commands.rs
@ -21,6 +21,7 @@ use syntect::highlighting::Theme;
|
|||||||
use syntect::parsing::SyntaxReference;
|
use syntect::parsing::SyntaxReference;
|
||||||
use syntect::util::LinesWithEndings;
|
use syntect::util::LinesWithEndings;
|
||||||
use telegram_bot::prelude::*;
|
use telegram_bot::prelude::*;
|
||||||
|
use telegram_bot::*;
|
||||||
use telegram_bot::{Api, Message, ParseMode, UserId};
|
use telegram_bot::{Api, Message, ParseMode, UserId};
|
||||||
|
|
||||||
include!("../assets/help_text.rs");
|
include!("../assets/help_text.rs");
|
||||||
@ -31,6 +32,12 @@ pub struct Here {
|
|||||||
pub struct Top {
|
pub struct Top {
|
||||||
pub data: String,
|
pub data: String,
|
||||||
}
|
}
|
||||||
|
pub struct ConfTop {
|
||||||
|
pub data: String,
|
||||||
|
}
|
||||||
|
pub struct GlobalTop {
|
||||||
|
pub data: String,
|
||||||
|
}
|
||||||
pub struct MarkovAll {
|
pub struct MarkovAll {
|
||||||
pub data: String,
|
pub data: String,
|
||||||
}
|
}
|
||||||
@ -46,6 +53,9 @@ pub struct Sql {
|
|||||||
pub struct Code {
|
pub struct Code {
|
||||||
pub data: String,
|
pub data: String,
|
||||||
}
|
}
|
||||||
|
pub struct Scheme {
|
||||||
|
pub data: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait Execute {
|
pub trait Execute {
|
||||||
@ -59,6 +69,68 @@ pub trait Execute {
|
|||||||
) -> Result<(), Error>;
|
) -> 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]
|
#[async_trait]
|
||||||
impl Execute for Sql {
|
impl Execute for Sql {
|
||||||
async fn exec(&self, api: &Api, message: &Message) -> Result<(), Error> {
|
async fn exec(&self, api: &Api, message: &Message) -> Result<(), Error> {
|
||||||
@ -281,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]
|
#[async_trait]
|
||||||
impl Execute for MarkovAll {
|
impl Execute for MarkovAll {
|
||||||
async fn exec(&self, api: &Api, message: &Message) -> Result<(), Error> {
|
async fn exec(&self, api: &Api, message: &Message) -> Result<(), Error> {
|
||||||
@ -289,7 +439,7 @@ impl Execute for MarkovAll {
|
|||||||
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());
|
||||||
}
|
}
|
||||||
match api
|
match api
|
||||||
@ -327,7 +477,7 @@ impl Execute for Markov {
|
|||||||
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_or(" ".into()));
|
msg = format!("{} {}", msg, sentences.pop().unwrap_or(" ".into()));
|
||||||
}
|
}
|
||||||
match api
|
match api
|
||||||
@ -571,9 +721,6 @@ impl Execute for Code {
|
|||||||
.split("\n")
|
.split("\n")
|
||||||
.map(|s| s.to_string())
|
.map(|s| s.to_string())
|
||||||
.collect();
|
.collect();
|
||||||
if lines.len() >= 81 {
|
|
||||||
return Err(CodeHighlightningError);
|
|
||||||
}
|
|
||||||
let last_line = &lines[lines.len() - 1];
|
let last_line = &lines[lines.len() - 1];
|
||||||
|
|
||||||
let tags = last_line
|
let tags = last_line
|
||||||
@ -613,7 +760,7 @@ impl Execute for Code {
|
|||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let theme = if theme.len() != 1 {
|
let theme = if theme.len() != 1 {
|
||||||
ts.themes.get("Dracula").unwrap()
|
ts.themes.get("gruvbox").unwrap()
|
||||||
} else {
|
} else {
|
||||||
theme[0]
|
theme[0]
|
||||||
};
|
};
|
||||||
|
70
src/db.rs
70
src/db.rs
@ -1,3 +1,5 @@
|
|||||||
|
#[allow(unused_mut)]
|
||||||
|
#[allow(dead_code)]
|
||||||
use crate::errors;
|
use crate::errors;
|
||||||
use crate::utils;
|
use crate::utils;
|
||||||
use rusqlite::{named_params, params, Connection, Error, Result};
|
use rusqlite::{named_params, params, Connection, Error, Result};
|
||||||
@ -35,18 +37,23 @@ pub(crate) fn update_scheme() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn load_stopwords() -> Result<()> {
|
pub(crate) fn load_stopwords() -> Result<()> {
|
||||||
|
info!("Populating stop words wait please.");
|
||||||
let conn = open()?;
|
let conn = open()?;
|
||||||
for table in include_str!("../assets/stop-words.txt").split('\n').into_iter() {
|
for table in include_str!("../assets/stop-words.txt")
|
||||||
|
.split('\n')
|
||||||
|
.into_iter()
|
||||||
|
{
|
||||||
let word = table.trim();
|
let word = table.trim();
|
||||||
if word != "" {
|
if word != "" {
|
||||||
let mut stmt = conn.prepare_cached(
|
let mut _stmt = conn
|
||||||
"
|
.prepare_cached(
|
||||||
|
"
|
||||||
INSERT OR IGNORE INTO
|
INSERT OR IGNORE INTO
|
||||||
stop_words('word')
|
stop_words('word')
|
||||||
VALUES (:word)
|
VALUES (:word)
|
||||||
",
|
",
|
||||||
)?.insert(params![word]);
|
)?
|
||||||
//let mut rows = stmt.word(named_params! {":conf_id": conf_id})?;
|
.insert(params![word]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
info!("Stop words updated.");
|
info!("Stop words updated.");
|
||||||
@ -505,3 +512,56 @@ pub(crate) async fn get_top(
|
|||||||
}
|
}
|
||||||
Ok(top)
|
Ok(top)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn get_global_top() -> Result<Vec<TopWord>, errors::Error> {
|
||||||
|
let conn = open()?;
|
||||||
|
let mut stmt = conn.prepare_cached(
|
||||||
|
"
|
||||||
|
SELECT w.word, COUNT(*) as count FROM relations r
|
||||||
|
LEFT JOIN word w ON w.id = r.word_id
|
||||||
|
GROUP BY w.word
|
||||||
|
ORDER BY count DESC
|
||||||
|
LIMIT 50
|
||||||
|
",
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let mut rows = stmt.query_named(named_params! {})?;
|
||||||
|
let mut top = Vec::new();
|
||||||
|
|
||||||
|
while let Some(row) = rows.next()? {
|
||||||
|
top.push(TopWord {
|
||||||
|
word: row.get(0)?,
|
||||||
|
count: row.get(1)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Ok(top)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn get_conf_top(
|
||||||
|
message: &telegram_bot::Message,
|
||||||
|
) -> Result<Vec<TopWord>, errors::Error> {
|
||||||
|
let conf_id = i64::from(message.chat.id());
|
||||||
|
|
||||||
|
let conn = open()?;
|
||||||
|
let mut stmt = conn.prepare_cached(
|
||||||
|
"
|
||||||
|
SELECT w.word, COUNT(*) as count FROM relations r
|
||||||
|
LEFT JOIN word w ON w.id = r.word_id
|
||||||
|
WHERE r.conf_id = :conf_id
|
||||||
|
GROUP BY w.word
|
||||||
|
ORDER BY count DESC
|
||||||
|
LIMIT 10
|
||||||
|
",
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let mut rows = stmt.query_named(named_params! {":conf_id": conf_id})?;
|
||||||
|
let mut top = Vec::new();
|
||||||
|
|
||||||
|
while let Some(row) = rows.next()? {
|
||||||
|
top.push(TopWord {
|
||||||
|
word: row.get(0)?,
|
||||||
|
count: row.get(1)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Ok(top)
|
||||||
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
//use crate::commands::Command;
|
//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::db;
|
||||||
use crate::errors;
|
use crate::errors;
|
||||||
use crate::utils;
|
use crate::utils;
|
||||||
@ -38,11 +40,30 @@ pub async fn handler(
|
|||||||
.await
|
.await
|
||||||
} {
|
} {
|
||||||
Ok(path) => {
|
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());
|
let file = InputFileUpload::with_path(path.clone());
|
||||||
|
info!("lines: {}, chars: {}", cnt_lines, cnt_chars);
|
||||||
// api.send(message.chat.document(&file)).await?;
|
// api.send(message.chat.document(&file)).await?;
|
||||||
//
|
//
|
||||||
// // Send an image from disk
|
// // 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);
|
//debug!("{:#?}", formatter);
|
||||||
let _ = std::fs::remove_file(&path);
|
let _ = std::fs::remove_file(&path);
|
||||||
}
|
}
|
||||||
@ -57,6 +78,8 @@ pub async fn handler(
|
|||||||
|| s.contains("@here")
|
|| s.contains("@here")
|
||||||
|| s.contains("/хере")
|
|| s.contains("/хере")
|
||||||
|| s.contains("@хере")
|
|| s.contains("@хере")
|
||||||
|
|| s.contains("@all")
|
||||||
|
|| s.contains("\"руку")
|
||||||
|| s.contains("\"хере") =>
|
|| s.contains("\"хере") =>
|
||||||
{
|
{
|
||||||
db::add_sentence(&message, mystem).await?;
|
db::add_sentence(&message, mystem).await?;
|
||||||
@ -88,15 +111,22 @@ pub async fn handler(
|
|||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/top" => {
|
"/top" | "/stat" => {
|
||||||
Top {
|
Top {
|
||||||
data: "".to_string(),
|
data: "".to_string(),
|
||||||
}
|
}
|
||||||
.exec(&api, &message)
|
.exec(&api, &message)
|
||||||
.await?
|
.await?
|
||||||
}
|
}
|
||||||
"/stat" => {
|
"/global_top" | "/global_stat" => {
|
||||||
Top {
|
GlobalTop {
|
||||||
|
data: "".to_string(),
|
||||||
|
}
|
||||||
|
.exec(&api, &message)
|
||||||
|
.await?
|
||||||
|
}
|
||||||
|
"/conf_stat" | "/conf_top" => {
|
||||||
|
ConfTop {
|
||||||
data: "".to_string(),
|
data: "".to_string(),
|
||||||
}
|
}
|
||||||
.exec(&api, &message)
|
.exec(&api, &message)
|
||||||
@ -110,7 +140,24 @@ pub async fn handler(
|
|||||||
.await?
|
.await?
|
||||||
}
|
}
|
||||||
"/markov" => {
|
"/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(),
|
data: "".to_string(),
|
||||||
}
|
}
|
||||||
.exec(&api, &message)
|
.exec(&api, &message)
|
||||||
|
Reference in New Issue
Block a user