22 Commits
0.5.8 ... 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
8 changed files with 231 additions and 78 deletions

View File

@ -11,26 +11,15 @@ 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
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:
runs-on: ubuntu-latest
steps:
@ -42,42 +31,45 @@ jobs:
echo ::set-output name=SOURCE_TAG::${GITHUB_REF#refs/tags/}
-
name: Set up QEMU
uses: docker/setup-qemu-action@v1
uses: docker/setup-qemu-action@v3
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
uses: docker/setup-buildx-action@v3
-
name: Login to DockerHub
uses: docker/login-action@v1
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
-
name: Build and push
id: docker_build_latest
uses: docker/build-push-action@v2
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@v2
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: |
@ -85,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/*'

View File

@ -1,6 +1,6 @@
[package]
name = "desubot"
version = "0.5.9"
version = "0.5.12"
authors = ["AB <ab@hexor.ru>"]
edition = "2018"
@ -30,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"

View File

@ -1,11 +1,11 @@
# syntax=docker/dockerfile:1
FROM rust:latest AS builder
FROM rust:bookworm AS builder
WORKDIR /desubot
ADD ./ /desubot/
RUN cargo build --release
FROM ubuntu:latest
FROM debian:bookworm
WORKDIR /storage
COPY --from=builder /desubot/target/release/desubot /usr/bin/
COPY mystem /usr/bin/

View File

@ -1,20 +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 with light group statistic and heavy spy features.
# Desubot Telegram Bot
== 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.
**Desubot** is a Telegram bot with light group statistics and powerful spy features.
== Important ==
* Desubot uses MyStem by Yandex for word stemming and assume that mystem binary is available in PATH.
* ubuntu deps: libssl-dev libsqlite3-dev cmake libfreetype-dev
## 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

@ -11,6 +11,10 @@
т
у
я
чо
че
ща
ваще
бы
stat
вообще
@ -424,4 +428,4 @@ ThreadTopBot
ага
делать
писать
бот
бот

View File

@ -32,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,
}
@ -347,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> {
@ -355,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
@ -393,7 +477,7 @@ 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) {
for _ in 1..rand::thread_rng().gen_range(2..10) {
msg = format!("{} {}", msg, sentences.pop().unwrap_or(" ".into()));
}
match api
@ -676,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

@ -512,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, Scheme, 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;
@ -76,6 +78,8 @@ pub async fn handler(
|| s.contains("@here")
|| s.contains("/хере")
|| s.contains("@хере")
|| s.contains("@all")
|| s.contains("\"руку")
|| s.contains("\"хере") =>
{
db::add_sentence(&message, mystem).await?;
@ -107,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)
@ -129,13 +140,23 @@ pub async fn handler(
.await?
}
"/markov" => {
Markov {
data: "".to_string(),
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?;
}
.exec(&api, &message)
.await?
}
s if s =="/scheme" || s == "/schema" => {
"/scheme" | "/schema" => {
Scheme {
data: "".to_string(),
}