5 Commits
0.4.0 ... sql

Author SHA1 Message Date
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
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
AB
83a6045b18 Fix typo. 2021-01-04 15:13:06 +03:00
4 changed files with 2238 additions and 199 deletions

2057
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,7 @@
#![allow(unused_variables)]
use crate::db; use crate::db;
use crate::errors::Error; use crate::errors::Error;
use crate::errors::Error::SQLInvalidCommand; use crate::errors::Error::{SQLITE3Error, SQLInvalidCommand};
use async_trait::async_trait; use async_trait::async_trait;
use html_escape::encode_text; use html_escape::encode_text;
use markov::Chain; use markov::Chain;
@ -39,19 +40,24 @@ pub struct Sql {
#[async_trait] #[async_trait]
pub trait Execute { pub trait Execute {
async fn run(&self, api: Api, message: Message) -> Result<(), Error>; async fn exec(&self, api: &Api, message: &Message) -> Result<(), Error>;
async fn run_mystem( async fn exec_with_result(&self, api: &Api, message: &Message) -> Result<String, Error>;
async fn exec_mystem(
&self, &self,
api: Api, api: &Api,
message: Message, message: &Message,
mystem: &mut MyStem, mystem: &mut MyStem,
) -> Result<(), Error>; ) -> Result<(), Error>;
} }
#[async_trait] #[async_trait]
impl Execute for Sql { impl Execute for Sql {
async fn run(&self, api: Api, message: Message) -> Result<(), Error> { async fn exec(&self, api: &Api, message: &Message) -> Result<(), Error> {
let mut sql = self.data.to_uppercase(); unimplemented!()
}
async fn exec_with_result(&self, api: &Api, message: &Message) -> Result<String, Error> {
let mut sql = self.data.clone();
let is_head = if sql.starts_with('-') { let is_head = if sql.starts_with('-') {
sql = sql.replacen("-", "", 1); sql = sql.replacen("-", "", 1);
false false
@ -59,39 +65,38 @@ impl Execute for Sql {
true true
}; };
let dialect = GenericDialect {}; let dialect = GenericDialect {};
let ast: Result<Vec<Statement>, Error> = match Parser::parse_sql(&dialect, &sql) { let ast: Vec<Statement> = match Parser::parse_sql(&dialect, &sql) {
Ok(ast) => Ok(ast), Ok(ast) => ast,
Err(_) => { Err(_) => {
warn!("Invalid SQL - {}", sql); warn!("Invalid SQL - {}", sql);
Err(SQLInvalidCommand)
}
};
let ast = match ast {
Err(_) => {
let _ = api
.send(
message
.text_reply(format!("❌ Invalid SQL. Syntax error ❌"))
.parse_mode(ParseMode::Html),
)
.await;
return Err(SQLInvalidCommand); return Err(SQLInvalidCommand);
} }
Ok(ast) => ast,
}; };
let msg: Result<String, Error> = match ast.len() { match ast.len() {
l if l > 1 => { l if l > 1 => {
//Max 1 request per message allowed only. return Err(Error::SQLBannedCommand(
Err(Error::SQLBannedCommand) "🚫 One statement per message allowed 🚫".into(),
))
} }
_ => match ast[0] { _ => (),
sqlparser::ast::Statement::Query { .. } => { }
match ast[0] {
sqlparser::ast::Statement::Query { .. } => {}
_ => {
return Err(Error::SQLBannedCommand(
"🚫 SELECT requests allowed only 🚫".into(),
))
}
}
let conn = db::open()?; let conn = db::open()?;
let x = match conn.prepare_cached(&sql) { let mut stmt = conn.prepare_cached(&sql)?;
Ok(mut stmt) => {
let query = match stmt.query(rusqlite::NO_PARAMS) { let mut rows = match stmt.query(rusqlite::NO_PARAMS) {
Err(_) => Err(SQLInvalidCommand), Err(e) => return Err(SQLITE3Error(e)),
Ok(mut rows) => { Ok(rows) => rows,
};
let mut res: Vec<Vec<String>> = match rows.column_names() { let mut res: Vec<Vec<String>> = match rows.column_names() {
Some(n) => vec![n Some(n) => vec![n
.into_iter() .into_iter()
@ -106,6 +111,7 @@ impl Execute for Sql {
.collect()], .collect()],
None => return Err(SQLInvalidCommand), None => return Err(SQLInvalidCommand),
}; };
let index_count = match rows.column_count() { let index_count = match rows.column_count() {
Some(c) => c, Some(c) => c,
None => return Err(SQLInvalidCommand), None => return Err(SQLInvalidCommand),
@ -114,27 +120,21 @@ impl Execute for Sql {
let mut tmp: Vec<String> = Vec::new(); let mut tmp: Vec<String> = Vec::new();
for i in 0..index_count { for i in 0..index_count {
match row.get(i).unwrap_or(None) { match row.get(i).unwrap_or(None) {
Some(rusqlite::types::Value::Text(t)) => { Some(rusqlite::types::Value::Text(t)) => tmp.push(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::Integer(t)) => { Some(rusqlite::types::Value::Real(t)) => tmp.push(t.to_string()),
tmp.push(t.to_string()) Some(rusqlite::types::Value::Null) => tmp.push("Null".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()), None => tmp.push("Null".to_string()),
}; };
} }
res.push(tmp); 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 // add Header
let mut msg = if is_head { let mut msg = if is_head {
let mut x = String::from("<b>"); let mut x = String::from("<b>");
@ -164,68 +164,12 @@ impl Execute for Sql {
}; };
Ok(msg) Ok(msg)
} }
};
query
}
Err(e) => Err(Error::SQLITE3Error(e)),
};
x
}
_ => {
warn!("SELECT requests allowed only.");
Err(Error::SQLBannedCommand)
}
},
};
match msg {
Ok(msg) => {
match api
.send(message.text_reply(msg).parse_mode(ParseMode::Html))
.await
{
Ok(_) => debug!("/sql command sent to {}", message.chat.id()),
Err(_) => warn!("/sql command sent failed to {}", message.chat.id()),
}
}
Err(e) => match e {
Error::SQLITE3Error(e) => {
let _ = api
.send(
message
.text_reply(format!("❌ An error ocurred {}", e))
.parse_mode(ParseMode::Html),
)
.await;
}
Error::SQLBannedCommand => {
let _ = api
.send(
message
.text_reply(format!("🚫 SELECT requests allowed only 🚫"))
.parse_mode(ParseMode::Html),
)
.await;
}
Error::SQLInvalidCommand => {
let _ = api
.send(
message
.text_reply(format!("🚫 Invalid SQL. Check DB scheme. 🚫"))
.parse_mode(ParseMode::Html),
)
.await;
}
_ => {}
},
}
Ok(())
}
#[allow(unused_variables)] #[allow(unused_variables)]
async fn run_mystem( async fn exec_mystem(
&self, &self,
api: Api, api: &Api,
message: Message, message: &Message,
mystem: &mut MyStem, mystem: &mut MyStem,
) -> Result<(), Error> { ) -> Result<(), Error> {
unimplemented!() unimplemented!()
@ -234,7 +178,7 @@ impl Execute for Sql {
#[async_trait] #[async_trait]
impl Execute for Here { impl Execute for Here {
async fn run(&self, api: Api, message: Message) -> Result<(), Error> { 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()).unwrap();
for u in &members { for u in &members {
debug!("Found user {:?} in chat {}", u, message.chat.id()); debug!("Found user {:?} in chat {}", u, message.chat.id());
@ -262,11 +206,15 @@ impl Execute for Here {
Ok(()) Ok(())
} }
async fn exec_with_result(&self, api: &Api, message: &Message) -> Result<String, Error> {
unimplemented!()
}
#[allow(unused_variables)] #[allow(unused_variables)]
async fn run_mystem( async fn exec_mystem(
&self, &self,
api: Api, api: &Api,
message: Message, message: &Message,
mystem: &mut MyStem, mystem: &mut MyStem,
) -> Result<(), Error> { ) -> Result<(), Error> {
unimplemented!() unimplemented!()
@ -275,7 +223,7 @@ impl Execute for Here {
#[async_trait] #[async_trait]
impl Execute for Top { impl Execute for Top {
async fn run(&self, api: Api, message: Message) -> Result<(), Error> { async fn exec(&self, api: &Api, message: &Message) -> Result<(), Error> {
let top = db::get_top(&message).await?; let top = db::get_top(&message).await?;
let mut msg = "<b>Your top using words:</b>\n<pre>".to_string(); let mut msg = "<b>Your top using words:</b>\n<pre>".to_string();
let mut counter = 1; let mut counter = 1;
@ -297,11 +245,15 @@ impl Execute for Top {
Ok(()) Ok(())
} }
async fn exec_with_result(&self, api: &Api, message: &Message) -> Result<String, Error> {
unimplemented!()
}
#[allow(unused_variables)] #[allow(unused_variables)]
async fn run_mystem( async fn exec_mystem(
&self, &self,
api: Api, api: &Api,
message: Message, message: &Message,
mystem: &mut MyStem, mystem: &mut MyStem,
) -> Result<(), Error> { ) -> Result<(), Error> {
unimplemented!() unimplemented!()
@ -310,7 +262,7 @@ impl Execute for Top {
#[async_trait] #[async_trait]
impl Execute for MarkovAll { impl Execute for MarkovAll {
async fn run(&self, api: Api, message: Message) -> Result<(), Error> { async fn exec(&self, api: &Api, message: &Message) -> Result<(), Error> {
let messages = db::get_messages_random_all().await?; let messages = db::get_messages_random_all().await?;
let mut chain = Chain::new(); let mut chain = Chain::new();
chain.feed(messages); chain.feed(messages);
@ -331,11 +283,15 @@ impl Execute for MarkovAll {
Ok(()) Ok(())
} }
async fn exec_with_result(&self, api: &Api, message: &Message) -> Result<String, Error> {
unimplemented!()
}
#[allow(unused_variables)] #[allow(unused_variables)]
async fn run_mystem( async fn exec_mystem(
&self, &self,
api: Api, api: &Api,
message: Message, message: &Message,
mystem: &mut MyStem, mystem: &mut MyStem,
) -> Result<(), Error> { ) -> Result<(), Error> {
unimplemented!() unimplemented!()
@ -344,7 +300,7 @@ impl Execute for MarkovAll {
#[async_trait] #[async_trait]
impl Execute for Markov { impl Execute for Markov {
async fn run(&self, api: Api, message: Message) -> Result<(), Error> { async fn exec(&self, api: &Api, message: &Message) -> Result<(), Error> {
let messages = db::get_messages_random_group(&message).await?; let messages = db::get_messages_random_group(&message).await?;
let mut chain = Chain::new(); let mut chain = Chain::new();
chain.feed(messages); chain.feed(messages);
@ -365,11 +321,15 @@ impl Execute for Markov {
Ok(()) Ok(())
} }
async fn exec_with_result(&self, api: &Api, message: &Message) -> Result<String, Error> {
unimplemented!()
}
#[allow(unused_variables)] #[allow(unused_variables)]
async fn run_mystem( async fn exec_mystem(
&self, &self,
api: Api, api: &Api,
message: Message, message: &Message,
mystem: &mut MyStem, mystem: &mut MyStem,
) -> Result<(), Error> { ) -> Result<(), Error> {
unimplemented!() unimplemented!()
@ -379,22 +339,25 @@ impl Execute for Markov {
#[async_trait] #[async_trait]
impl Execute for Omedeto { impl Execute for Omedeto {
#[allow(unused_variables)] #[allow(unused_variables)]
async fn run(&self, api: Api, message: Message) -> Result<(), Error> { 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!() unimplemented!()
} }
#[warn(unused_must_use)] #[warn(unused_must_use)]
async fn run_mystem( async fn exec_mystem(
&self, &self,
api: Api, api: &Api,
message: Message, message: &Message,
mystem: &mut MyStem, mystem: &mut MyStem,
) -> Result<(), Error> { ) -> Result<(), Error> {
let all_msg = db::get_messages_user_all(&message).await?; let all_msg = db::get_messages_user_all(&message).await?;
let re = Regex::new(r"^[яЯ] [а-яА-Я]+(-[а-яА-Я]+(_[а-яА-Я]+)*)*").unwrap(); let re = Regex::new(r"^[яЯ] [а-яА-Я]+(-[а-яА-Я]+(_[а-яА-Я]+)*)*").unwrap();
let mut nouns: Vec<String> = all_msg let mut nouns: Vec<String> = all_msg
.clone() .iter()
.into_iter()
.filter(|m| re.is_match(m)) .filter(|m| re.is_match(m))
.map(|m| m.split(' ').map(|s| s.to_string()).collect::<Vec<String>>()[1].clone()) .map(|m| m.split(' ').map(|s| s.to_string()).collect::<Vec<String>>()[1].clone())
.filter(|m| { .filter(|m| {
@ -421,8 +384,7 @@ impl Execute for Omedeto {
//debug!("Found {} nouns. {:#?}", nouns.len(), nouns); //debug!("Found {} nouns. {:#?}", nouns.len(), nouns);
let mut verbs_p: Vec<String> = all_msg let mut verbs_p: Vec<String> = all_msg
.clone() .iter()
.into_iter()
.filter(|m| re.is_match(m)) .filter(|m| re.is_match(m))
.map(|m| m.split(' ').map(|s| s.to_string()).collect::<Vec<String>>()[1].clone()) .map(|m| m.split(' ').map(|s| s.to_string()).collect::<Vec<String>>()[1].clone())
.filter(|m| { .filter(|m| {
@ -449,8 +411,7 @@ impl Execute for Omedeto {
//debug!("Found {} past verbs. {:#?}", verbs_p.len(), verbs_p); //debug!("Found {} past verbs. {:#?}", verbs_p.len(), verbs_p);
let mut verbs_i: Vec<String> = all_msg let mut verbs_i: Vec<String> = all_msg
.clone() .iter()
.into_iter()
.filter(|m| re.is_match(m)) .filter(|m| re.is_match(m))
.map(|m| m.split(' ').map(|s| s.to_string()).collect::<Vec<String>>()[1].clone()) .map(|m| m.split(' ').map(|s| s.to_string()).collect::<Vec<String>>()[1].clone())
.filter(|m| { .filter(|m| {

View File

@ -20,9 +20,11 @@ pub enum Error {
JsonParseError(serde_error), JsonParseError(serde_error),
PopenError(popen_error), PopenError(popen_error),
MystemError(mystem_error), MystemError(mystem_error),
SQLBannedCommand, SQLBannedCommand(String),
SQLInvalidCommand, SQLInvalidCommand,
SQLResultTooLong(String),
} }
impl fmt::Display for Error { impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "An error occurred.") write!(f, "An error occurred.")

View File

@ -31,49 +31,68 @@ pub async fn handler(
Here { Here {
data: "".to_string(), data: "".to_string(),
} }
.run(api, message) .exec(&api, &message)
.await? .await?
} }
s if s.to_string().starts_with("/sql") => { s if s.to_string().starts_with("/sql") => match {
Sql { Sql {
data: s.replace("/sql ", ""), data: s.replace("/sql ", ""),
} }
.run(api, message) .exec_with_result(&api, &message)
.await? .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" => {
Top { Top {
data: "".to_string(), data: "".to_string(),
} }
.run(api, message) .exec(&api, &message)
.await? .await?
} }
"/stat" => { "/stat" => {
Top { Top {
data: "".to_string(), data: "".to_string(),
} }
.run(api, message) .exec(&api, &message)
.await? .await?
} }
"/markov_all" => { "/markov_all" => {
MarkovAll { MarkovAll {
data: "".to_string(), data: "".to_string(),
} }
.run(api, message) .exec(&api, &message)
.await? .await?
} }
"/markov" => { "/markov" => {
Markov { Markov {
data: "".to_string(), data: "".to_string(),
} }
.run(api, message) .exec(&api, &message)
.await? .await?
} }
"/omedeto" => { "/omedeto" => {
Omedeto { Omedeto {
data: "".to_string(), data: "".to_string(),
} }
.run_mystem(api, message, mystem) .exec_mystem(&api, &message, mystem)
.await? .await?
} }
_ => (), _ => (),