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,173 +65,111 @@ 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 { .. } => {
let conn = db::open()?;
let x = match conn.prepare_cached(&sql) {
Ok(mut stmt) => {
let query = match stmt.query(rusqlite::NO_PARAMS) {
Err(_) => Err(SQLInvalidCommand),
Ok(mut rows) => {
let mut res: Vec<Vec<String>> = match rows.column_names() {
Some(n) => vec![n
.into_iter()
.map(|s| {
let t = String::from(s);
if t.len() > 10 {
"EMSGSIZE".to_string()
} else {
t
}
})
.collect()],
None => return Err(SQLInvalidCommand),
};
let index_count = match rows.column_count() {
Some(c) => c,
None => return Err(SQLInvalidCommand),
};
while let Some(row) = rows.next().unwrap() {
let mut tmp: Vec<String> = Vec::new();
for i in 0..index_count {
match row.get(i).unwrap_or(None) {
Some(rusqlite::types::Value::Text(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::Real(t)) => {
tmp.push(t.to_string())
}
Some(rusqlite::types::Value::Null) => {
tmp.push("Null".to_string())
}
None => tmp.push("Null".to_string()),
};
}
res.push(tmp);
}
// add Header
let mut msg = if is_head {
let mut x = String::from("<b>");
for head in res[0].iter() {
x = format!("{} {}", x, head);
}
format!("{}{}", x, "</b>\n")
} else {
String::new()
};
// remove header
res.remove(0);
msg = format!("{}{}", msg, "<pre>");
for line in res.iter() {
for field in line.iter() {
msg = format!("{}{}", msg, format!("{} ", field));
}
msg = format!("{}{}", msg, "\n");
}
msg = format!("{}{}", msg, "</pre>");
msg = if msg.len() > 4096 {
"🚫 Result is too big. Use LIMIT 🚫".into()
} else {
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(()) match ast[0] {
sqlparser::ast::Statement::Query { .. } => {}
_ => {
return Err(Error::SQLBannedCommand(
"🚫 SELECT requests allowed only 🚫".into(),
))
}
}
let conn = db::open()?;
let mut stmt = conn.prepare_cached(&sql)?;
let mut rows = match stmt.query(rusqlite::NO_PARAMS) {
Err(e) => return Err(SQLITE3Error(e)),
Ok(rows) => rows,
};
let mut res: Vec<Vec<String>> = match rows.column_names() {
Some(n) => vec![n
.into_iter()
.map(|s| {
let t = String::from(s);
if t.len() > 10 {
"EMSGSIZE".to_string()
} else {
t
}
})
.collect()],
None => return Err(SQLInvalidCommand),
};
let index_count = match rows.column_count() {
Some(c) => c,
None => return Err(SQLInvalidCommand),
};
while let Some(row) = rows.next().unwrap() {
let mut tmp: Vec<String> = Vec::new();
for i in 0..index_count {
match row.get(i).unwrap_or(None) {
Some(rusqlite::types::Value::Text(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::Real(t)) => tmp.push(t.to_string()),
Some(rusqlite::types::Value::Null) => tmp.push("Null".to_string()),
None => tmp.push("Null".to_string()),
};
}
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
let mut msg = if is_head {
let mut x = String::from("<b>");
for head in res[0].iter() {
x = format!("{} {}", x, head);
}
format!("{}{}", x, "</b>\n")
} else {
String::new()
};
// remove header
res.remove(0);
msg = format!("{}{}", msg, "<pre>");
for line in res.iter() {
for field in line.iter() {
msg = format!("{}{}", msg, format!("{} ", field));
}
msg = format!("{}{}", msg, "\n");
}
msg = format!("{}{}", msg, "</pre>");
msg = if msg.len() > 4096 {
"🚫 Result is too big. Use LIMIT 🚫".into()
} else {
msg
};
Ok(msg)
} }
#[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?
} }
_ => (), _ => (),