22 Commits

Author SHA1 Message Date
7432ce6398 Bump many libs 2021-08-19 18:08:19 +03:00
AB
0831e3f503 Add хере command. 2021-06-12 15:02:09 +03:00
AB
a0f4c40be0 Add хере command. 2021-06-12 14:58:38 +03:00
AB
1facef6897 Add debug message. 2021-04-04 22:35:33 +03:00
AB
428416a2a3 Add handler in case of error in here command. 2021-03-10 19:57:45 +03:00
AB
77dec205f1 Bump version. 2021-01-20 20:16:08 +03:00
AB
6c761d7576 @here command now call only active users (at least 1 message in last 60 days) 2021-01-20 20:15:46 +03:00
AB
865fd3bbe4 Bump 2021-01-20 15:54:05 +03:00
AB
30bdb23a32 Add @here command 2021-01-20 15:53:22 +03:00
AB
f97562e9b7 Merge remote-tracking branch 'origin/main' into main 2021-01-11 11:39:34 +03:00
AB
2d000101c2 Merge 2021-01-11 11:39:20 +03:00
a26d227190 Merge pull request #10 from house-of-vanity/code
code
2021-01-11 11:26:51 +03:00
AB
cc44f0e23b Merge remote-tracking branch 'origin/main' into main
# Conflicts:
#	assets/help_text.rs
2021-01-11 11:22:47 +03:00
AB
96df636195 Add automerge action. 2021-01-11 11:21:18 +03:00
AB
a48e25800c Improve logging. Fix /sql limit. 2021-01-11 11:12:38 +03:00
36660d384d Merge pull request #9 from house-of-vanity/code
Code
2021-01-10 21:39:38 +03:00
AB
0d24976ec2 Fix warnings. Bump version. 2021-01-10 21:38:18 +03:00
AB
6ae3b2af1f Fix warnings. 2021-01-10 21:22:27 +03:00
AB
a39f6a8c2a Update /code feature. 2021-01-10 21:21:07 +03:00
AB
788c2cbbd4 Fix type in help. 2021-01-09 01:06:10 +03:00
AB
9d5e5a3217 Fix type in help. 2021-01-08 17:54:05 +03:00
945da05794 Update README 2021-01-08 06:42:32 -08:00
8 changed files with 153 additions and 84 deletions

27
.github/workflows/automerge.yml vendored Normal file
View File

@ -0,0 +1,27 @@
name: automerge
on:
pull_request:
types:
- labeled
- unlabeled
- synchronize
- opened
- edited
- ready_for_review
- reopened
- unlocked
pull_request_review:
types:
- submitted
check_suite:
types:
- completed
status: {}
jobs:
automerge:
runs-on: ubuntu-latest
steps:
- name: automerge
uses: "pascalgn/automerge-action@v0.13.0"
env:
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"

View File

@ -1,6 +1,6 @@
[package]
name = "desubot"
version = "0.5.0"
version = "0.5.5"
authors = ["AB <ab@hexor.ru>"]
edition = "2018"
@ -8,31 +8,32 @@ edition = "2018"
[dependencies]
bytes = "0.5"
tokio = { version = "0.2", features = ["full"]}
tokio = { version = "1.10.0", 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 = "0.14"
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"
reqwest = "0.11.4"
uuid = { version = "0.8", features = ["v4"] }
sha1 = "0.6.0"
env_logger = "0.7"
env_logger = "0.9.0"
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.1"
mystem = "^0.2"
#mystem = { path = "../mystem-rs" }
async-trait = "0.1.42"
sqlparser = "0.7.0"
sqlparser = "0.9.0"
[dependencies.syntect]
version = "4.4"

2
README
View File

@ -7,8 +7,6 @@ Telegram bot with light group statistic and heavy spy features.
* /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.
== TODO ==
* Syntax highlighting for CODE exported to image.
== Important ==

View File

@ -1,10 +1,10 @@
static CODE: &str = "<b>Code highlighter</b>
#[allow(dead_code)]
static CODE_HELP: &str = "<b>Code highlighter</b>
<i>Usage</i>
<pre>/CODE
#&lt;theme - Dracula by default&gt;
<pre>/code
&lt;CODE&gt;
#&lt;lang - JS by default&gt;</pre>
#&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
@ -34,3 +34,10 @@ 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>";

View File

@ -17,11 +17,13 @@ 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};
use syntect::highlighting::Theme;
use telegram_bot::{Api, Message, ParseMode, UserId};
include!("../assets/help_text.rs");
pub struct Here {
pub data: String,
@ -65,6 +67,10 @@ impl Execute for Sql {
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
@ -137,7 +143,7 @@ impl Execute for Sql {
}
res.push(tmp);
}
if res.len() > 100 {
if res.len() >= 100 {
return Err(Error::SQLResultTooLong(
"SQL result too long. Lines limit is 100. Use LIMIT".to_string(),
));
@ -186,7 +192,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()).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());
}
@ -551,11 +565,16 @@ impl Execute for Code {
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();
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 last_line = &lines[lines.len() - 1];
let tags = last_line
.trim()
@ -593,7 +612,6 @@ impl Execute for Code {
.map(|s| s.unwrap())
.collect();
let theme = if theme.len() != 1 {
ts.themes.get("Dracula").unwrap()
} else {
@ -606,6 +624,7 @@ impl Execute for Code {
.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);

View File

@ -180,18 +180,29 @@ pub(crate) async fn get_messages_user_all(
Ok(messages)
}
pub(crate) fn get_members(id: telegram_bot::ChatId) -> Result<Vec<telegram_bot::User>> {
pub(crate) fn get_members(id: telegram_bot::ChatId, limit: u32) -> Result<Vec<telegram_bot::User>> {
let where_statement = if limit > 0 {
format!("and days_seen <= {}", limit)
} else {
"".into()
};
debug!("{}", where_statement);
let conn = open()?;
let mut stmt = conn.prepare_cached(
let mut stmt = conn.prepare_cached(&format!(
"
SELECT DISTINCT(u.username), u.id, u.first_name, u.last_name, u.date
SELECT DISTINCT(u.username), u.id, u.first_name, u.last_name, u.date,
(strftime('%s','now')-r.date)/60/60/24 as days_seen
FROM relations r
JOIN user u
ON u.id = r.user_id
LEFT JOIN conf c
ON r.conf_id = c.id
WHERE c.id = :id",
)?;
WHERE c.id = :id
{}
GROUP BY u.id
ORDER BY r.date DESC",
where_statement
))?;
let mut rows = stmt.query_named(&[(":id", &id.to_string())])?;
let mut users = Vec::new();
@ -214,7 +225,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 +239,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 +261,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 +294,11 @@ 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 +325,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(())
@ -417,6 +432,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 {

View File

@ -6,7 +6,6 @@ use crate::utils;
use mystem::MyStem;
use telegram_bot::*;
include!("../assets/help_text.rs");
pub async fn handler(
@ -25,13 +24,12 @@ pub async fn handler(
title,
&message.from.id,
&message.from.first_name,
{if data.len() <= 200 {data} else {&data[..200]}}.replace("\n", " ")
data.replace("\n", " ")
);
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", ""),
@ -50,12 +48,12 @@ pub async fn handler(
}
Err(_) => {
let _ = api
.send(message.text_reply(CODE).parse_mode(ParseMode::Html))
.send(message.text_reply(CODE_HELP).parse_mode(ParseMode::Html))
.await?;
}
}
}
s if s.contains("/here") => {
s if s.contains("/here") || s.contains("@here") || s.contains("/хере") || s.contains("@хере") || s.contains("\"хере") => {
db::add_sentence(&message, mystem).await?;
Here {
data: "".to_string(),

View File

@ -1,3 +1,4 @@
#![allow(unreachable_code)]
use std::{env, process};
use futures::StreamExt;
@ -16,7 +17,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) => {
@ -44,6 +45,7 @@ async fn main() -> Result<(), errors::Error> {
me.first_name,
me.id
);
loop {
while let Some(update) = stream.next().await {
let update = update?;
if let UpdateKind::Message(message) = update.kind {
@ -57,5 +59,6 @@ async fn main() -> Result<(), errors::Error> {
}
}
}
}
Ok(())
}