I GOT ZLINED
This commit is contained in:
parent
4224961c41
commit
49d69cef5c
|
@ -17,7 +17,7 @@ fancy-regex = "0.7"
|
||||||
rspotify = { version = "0.11", default-features = false, features = ["client-reqwest", "reqwest-rustls-tls"] }
|
rspotify = { version = "0.11", default-features = false, features = ["client-reqwest", "reqwest-rustls-tls"] }
|
||||||
htmlescape = "0.3"
|
htmlescape = "0.3"
|
||||||
toml = "0.5"
|
toml = "0.5"
|
||||||
serde = "1.0"
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
arrayvec = "0.7"
|
arrayvec = "0.7"
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
meval = "0.2"
|
meval = "0.2"
|
||||||
|
@ -27,6 +27,7 @@ rusqlite = { version = "0.26", features = ["bundled"] }
|
||||||
warp = "0.3"
|
warp = "0.3"
|
||||||
futures-util = "0.3"
|
futures-util = "0.3"
|
||||||
irc = { version = "0.15", default-features = false }
|
irc = { version = "0.15", default-features = false }
|
||||||
|
handlebars = "4.2"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
tls = ["irc/tls-rust"]
|
tls = ["irc/tls-rust"]
|
||||||
|
|
|
@ -3,11 +3,12 @@ use tokio::sync::{
|
||||||
mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender},
|
mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender},
|
||||||
oneshot,
|
oneshot,
|
||||||
};
|
};
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum Task {
|
enum Task {
|
||||||
AddQuote(oneshot::Sender<bool>, String, String),
|
AddQuote(oneshot::Sender<bool>, Quote),
|
||||||
GetQuote(oneshot::Sender<Option<(String, String)>>, Option<String>),
|
GetQuote(oneshot::Sender<Option<Quote>>, Option<String>),
|
||||||
// implement search WITH PAGINATION
|
// implement search WITH PAGINATION
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,6 +17,12 @@ pub struct DbExecutor {
|
||||||
db: rusqlite::Connection,
|
db: rusqlite::Connection,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Debug)]
|
||||||
|
pub struct Quote {
|
||||||
|
pub author: String,
|
||||||
|
pub quote: String
|
||||||
|
}
|
||||||
|
|
||||||
impl DbExecutor {
|
impl DbExecutor {
|
||||||
pub fn create(dbpath: &str) -> rusqlite::Result<(Self, ExecutorConnection)> {
|
pub fn create(dbpath: &str) -> rusqlite::Result<(Self, ExecutorConnection)> {
|
||||||
let (tx, rx) = unbounded_channel();
|
let (tx, rx) = unbounded_channel();
|
||||||
|
@ -32,10 +39,10 @@ impl DbExecutor {
|
||||||
pub fn run(mut self) {
|
pub fn run(mut self) {
|
||||||
while let Some(task) = self.rx.blocking_recv() {
|
while let Some(task) = self.rx.blocking_recv() {
|
||||||
match task {
|
match task {
|
||||||
Task::AddQuote(tx, quote, author) => {
|
Task::AddQuote(tx, quote) => {
|
||||||
if let Err(e) = self.db.execute(
|
if let Err(e) = self.db.execute(
|
||||||
"insert into quotes(quote,username) values(?,?)",
|
"insert into quotes(quote,username) values(?,?)",
|
||||||
params![quote, author],
|
params![quote.quote, quote.author],
|
||||||
) {
|
) {
|
||||||
tracing::error!("A database error has occurred: {}", e);
|
tracing::error!("A database error has occurred: {}", e);
|
||||||
tx.send(false).unwrap();
|
tx.send(false).unwrap();
|
||||||
|
@ -45,9 +52,9 @@ impl DbExecutor {
|
||||||
}
|
}
|
||||||
Task::GetQuote(tx, author) => {
|
Task::GetQuote(tx, author) => {
|
||||||
let quote = if let Some(ref author) = author {
|
let quote = if let Some(ref author) = author {
|
||||||
self.db.query_row("select quote,username from quotes where username=? order by random() limit 1", params![author], |v| Ok((v.get(0)?, v.get(1)?)))
|
self.db.query_row("select quote,username from quotes where username=? order by random() limit 1", params![author], |v| Ok(Quote {quote:v.get(0)?, author:v.get(1)?}))
|
||||||
} else {
|
} else {
|
||||||
self.db.query_row("select quote,username from quotes order by random() limit 1", params![], |v| Ok((v.get(0)?, v.get(1)?)))
|
self.db.query_row("select quote,username from quotes order by random() limit 1", params![], |v| Ok(Quote {quote:v.get(0)?, author:v.get(1)?}))
|
||||||
}.optional().unwrap_or_else(|e| {
|
}.optional().unwrap_or_else(|e| {
|
||||||
tracing::error!("A database error has occurred: {}", e);
|
tracing::error!("A database error has occurred: {}", e);
|
||||||
None
|
None
|
||||||
|
@ -72,12 +79,12 @@ impl Clone for ExecutorConnection {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExecutorConnection {
|
impl ExecutorConnection {
|
||||||
pub async fn add_quote(&self, quote: String, author: String) -> bool {
|
pub async fn add_quote(&self, quote: Quote) -> bool {
|
||||||
let (otx, orx) = oneshot::channel();
|
let (otx, orx) = oneshot::channel();
|
||||||
self.tx.send(Task::AddQuote(otx, quote, author)).unwrap();
|
self.tx.send(Task::AddQuote(otx, quote)).unwrap();
|
||||||
orx.await.unwrap()
|
orx.await.unwrap()
|
||||||
}
|
}
|
||||||
pub async fn get_quote(&self, author: Option<String>) -> Option<(String, String)> {
|
pub async fn get_quote(&self, author: Option<String>) -> Option<Quote> {
|
||||||
let (otx, orx) = oneshot::channel();
|
let (otx, orx) = oneshot::channel();
|
||||||
self.tx.send(Task::GetQuote(otx, author)).unwrap();
|
self.tx.send(Task::GetQuote(otx, author)).unwrap();
|
||||||
orx.await.unwrap()
|
orx.await.unwrap()
|
||||||
|
|
|
@ -20,7 +20,7 @@ use tokio::sync::mpsc::{unbounded_channel};
|
||||||
use tracing_subscriber::EnvFilter;
|
use tracing_subscriber::EnvFilter;
|
||||||
|
|
||||||
use crate::bots::{leek, misc, sed, title};
|
use crate::bots::{leek, misc, sed, title};
|
||||||
use crate::database::{DbExecutor, ExecutorConnection};
|
use crate::database::{DbExecutor, ExecutorConnection, Quote};
|
||||||
|
|
||||||
mod bots;
|
mod bots;
|
||||||
mod database;
|
mod database;
|
||||||
|
@ -299,7 +299,7 @@ async fn handle_privmsg(
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
if let Some(prev_msg) = state.last_msgs.get(target) {
|
if let Some(prev_msg) = state.last_msgs.get(target) {
|
||||||
if state.db.add_quote(prev_msg.clone(), target.into()).await {
|
if state.db.add_quote(Quote{quote:prev_msg.clone(), author:target.into()}).await {
|
||||||
state.client.send_privmsg(target, "Quote added")?;
|
state.client.send_privmsg(target, "Quote added")?;
|
||||||
} else {
|
} else {
|
||||||
state
|
state
|
||||||
|
@ -318,7 +318,7 @@ async fn handle_privmsg(
|
||||||
"quot" => {
|
"quot" => {
|
||||||
if let Some(quote) = state.db.get_quote(remainder.map(ToString::to_string)).await {
|
if let Some(quote) = state.db.get_quote(remainder.map(ToString::to_string)).await {
|
||||||
let mut resp = ArrayString::<512>::new();
|
let mut resp = ArrayString::<512>::new();
|
||||||
write!(resp, "\"{}\" ~{}", quote.0, quote.1)?;
|
write!(resp, "\"{}\" ~{}", quote.quote, quote.author)?;
|
||||||
state.client.send_privmsg(origin, &resp)?;
|
state.client.send_privmsg(origin, &resp)?;
|
||||||
} else {
|
} else {
|
||||||
state.client.send_privmsg(origin, "No quotes found")?;
|
state.client.send_privmsg(origin, "No quotes found")?;
|
||||||
|
|
46
src/res/quote_tmpl.hbs
Normal file
46
src/res/quote_tmpl.hbs
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en" data-theme="dark">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||||
|
<style>
|
||||||
|
main {
|
||||||
|
--primary: #d81b60;
|
||||||
|
--primary-hover: #e91e63;
|
||||||
|
--primary-focus: rgba(216, 27, 96, 0.25);
|
||||||
|
--primary-inverse: #FFF;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<link rel="stylesheet" href="https://unpkg.com/@picocss/pico@latest/css/pico.classless.min.css">
|
||||||
|
<title># überbot quotes</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main>
|
||||||
|
<form method="post">
|
||||||
|
<label>
|
||||||
|
Search query
|
||||||
|
<input type="text" name="query" placeholder="Search..." required>
|
||||||
|
</label>
|
||||||
|
<button type="submit">Search</button>
|
||||||
|
</form>
|
||||||
|
{{#if quotes}}
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<td>Author</td>
|
||||||
|
<td>Quote</td>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{{#each quotes}}
|
||||||
|
<tr>
|
||||||
|
<td>{{author}}</td>
|
||||||
|
<td>{{quote}}</td>
|
||||||
|
</tr>
|
||||||
|
{{/each}}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{{/if}}
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -1,11 +0,0 @@
|
||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
|
||||||
<title># überbot quotes</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -4,8 +4,20 @@ use reqwest::StatusCode;
|
||||||
use serde_json::Value::Null;
|
use serde_json::Value::Null;
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use handlebars::Handlebars;
|
||||||
|
use lazy_static::lazy_static;
|
||||||
use tokio::sync::broadcast::Receiver;
|
use tokio::sync::broadcast::Receiver;
|
||||||
use warp::{reply, Filter, Reply};
|
use warp::{reply, Filter, Reply};
|
||||||
|
use serde::Serialize;
|
||||||
|
use crate::database::Quote;
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref HANDLEBARS: Handlebars<'static> = {
|
||||||
|
let mut reg = Handlebars::new();
|
||||||
|
reg.register_template_string("quotes", include_str!("res/quote_tmpl.hbs")).unwrap();
|
||||||
|
reg
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn run(
|
pub async fn run(
|
||||||
db: ExecutorConnection,
|
db: ExecutorConnection,
|
||||||
|
@ -33,8 +45,24 @@ pub async fn run(
|
||||||
tracing::info!("Web service finished");
|
tracing::info!("Web service finished");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct QuotesTemplate {
|
||||||
|
quotes: Option<Vec<Quote>>
|
||||||
|
}
|
||||||
|
|
||||||
fn handle_get_quote(_: ExecutorConnection) -> impl Reply {
|
fn handle_get_quote(_: ExecutorConnection) -> impl Reply {
|
||||||
reply::html(include_str!("res/quote_tmpl.html"))
|
match HANDLEBARS.render("quotes", &QuotesTemplate{quotes: Some(vec![
|
||||||
|
Quote{quote:"something".into(),author:"by someone".into()},
|
||||||
|
Quote{quote:"something different".into(),author:"by someone else".into()},
|
||||||
|
Quote{quote:"something even more different".into(),author:"by nobody".into()}
|
||||||
|
])}) {
|
||||||
|
Ok(o) => reply::html(o).into_response(),
|
||||||
|
Err(e) => {
|
||||||
|
tracing::warn!("Error while rendering template: {}", e);
|
||||||
|
reply::with_status("Failed to render template", StatusCode::INTERNAL_SERVER_ERROR).into_response()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::needless_pass_by_value)]
|
#[allow(clippy::needless_pass_by_value)]
|
||||||
|
|
Loading…
Reference in a new issue