Remove web service
This commit is contained in:
parent
02ae7570db
commit
2968081358
|
@ -24,7 +24,5 @@ meval = "0.2"
|
||||||
lazy_static = "1.4"
|
lazy_static = "1.4"
|
||||||
sedregex = "0.2"
|
sedregex = "0.2"
|
||||||
rusqlite = { version = "0.28", features = ["bundled"] }
|
rusqlite = { version = "0.28", features = ["bundled"] }
|
||||||
warp = "0.3"
|
|
||||||
futures-util = "0.3"
|
futures-util = "0.3"
|
||||||
irc = { version = "0.15", default-features = false, features = ["tls-rust"] }
|
irc = { version = "0.15", default-features = false, features = ["tls-rust"] }
|
||||||
tera = { version = "1.15", default-features = false }
|
|
||||||
|
|
|
@ -9,8 +9,8 @@ use tokio::sync::{
|
||||||
enum Task {
|
enum Task {
|
||||||
AddQuote(oneshot::Sender<bool>, Quote),
|
AddQuote(oneshot::Sender<bool>, Quote),
|
||||||
GetQuote(oneshot::Sender<Option<Quote>>, Option<String>),
|
GetQuote(oneshot::Sender<Option<Quote>>, Option<String>),
|
||||||
Search(oneshot::Sender<Option<Vec<Quote>>>, String),
|
SearchQuotes(oneshot::Sender<Option<Vec<Quote>>>, String),
|
||||||
Random20(oneshot::Sender<Option<Vec<Quote>>>),
|
RandomNQuotes(oneshot::Sender<Option<Vec<Quote>>>, u8),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DbExecutor {
|
pub struct DbExecutor {
|
||||||
|
@ -29,8 +29,7 @@ impl DbExecutor {
|
||||||
let (tx, rx) = unbounded_channel();
|
let (tx, rx) = unbounded_channel();
|
||||||
let db = rusqlite::Connection::open(dbpath)?;
|
let db = rusqlite::Connection::open(dbpath)?;
|
||||||
db.execute(
|
db.execute(
|
||||||
"create table if not exists quotes(id integer primary key,\
|
"create table if not exists quotes(id integer primary key, username text not null, quote text not null)",
|
||||||
username text not null, quote text not null)",
|
|
||||||
[],
|
[],
|
||||||
)?;
|
)?;
|
||||||
tracing::debug!("Database connected ({})", dbpath);
|
tracing::debug!("Database connected ({})", dbpath);
|
||||||
|
@ -62,13 +61,13 @@ impl DbExecutor {
|
||||||
});
|
});
|
||||||
tx.send(quote).unwrap();
|
tx.send(quote).unwrap();
|
||||||
}
|
}
|
||||||
Task::Search(tx, query) => {
|
Task::SearchQuotes(tx, query) => {
|
||||||
tx.send(self.yield_quotes("select quote,username from quotes where quote like '%'||?1||'%' order by quote asc limit 50", params![query])).unwrap();
|
tx.send(self.yield_quotes("select quote,username from quotes where quote like '%'||?1||'%' order by quote asc limit 5", params![query])).unwrap();
|
||||||
}
|
}
|
||||||
Task::Random20(tx) => {
|
Task::RandomNQuotes(tx, count) => {
|
||||||
tx.send(self.yield_quotes(
|
tx.send(self.yield_quotes(
|
||||||
"select quote,username from quotes order by random() limit 20",
|
"select quote,username from quotes order by random() limit ?",
|
||||||
params![],
|
params![count],
|
||||||
))
|
))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
@ -136,6 +135,6 @@ impl ExecutorConnection {
|
||||||
Option<Quote>,
|
Option<Quote>,
|
||||||
author: Option<String>
|
author: Option<String>
|
||||||
);
|
);
|
||||||
executor_wrapper!(search, Task::Search, Option<Vec<Quote>>, query: String);
|
executor_wrapper!(search_quotes, Task::SearchQuotes, Option<Vec<Quote>>, query: String);
|
||||||
executor_wrapper!(random20, Task::Random20, Option<Vec<Quote>>);
|
executor_wrapper!(random_n_quotes, Task::RandomNQuotes, Option<Vec<Quote>>, count: u8);
|
||||||
}
|
}
|
||||||
|
|
17
src/main.rs
17
src/main.rs
|
@ -25,7 +25,6 @@ use crate::database::{DbExecutor, ExecutorConnection, Quote};
|
||||||
|
|
||||||
mod bots;
|
mod bots;
|
||||||
mod database;
|
mod database;
|
||||||
mod web_service;
|
|
||||||
|
|
||||||
// this will be displayed when the help command is used
|
// this will be displayed when the help command is used
|
||||||
const HELP: &[&str] = &[
|
const HELP: &[&str] = &[
|
||||||
|
@ -79,8 +78,9 @@ struct ClientConf {
|
||||||
spotify_client_secret: String,
|
spotify_client_secret: String,
|
||||||
prefix: String,
|
prefix: String,
|
||||||
db_path: Option<String>,
|
db_path: Option<String>,
|
||||||
http_listen: Option<SocketAddr>,
|
// reserved for future
|
||||||
git_channel: String,
|
_http_listen: Option<SocketAddr>,
|
||||||
|
_git_channel: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
|
@ -138,14 +138,6 @@ async fn main() -> anyhow::Result<()> {
|
||||||
let (ctx, _) = broadcast::channel(1);
|
let (ctx, _) = broadcast::channel(1);
|
||||||
let (etx, mut erx) = unbounded_channel();
|
let (etx, mut erx) = unbounded_channel();
|
||||||
|
|
||||||
let web_task = tokio::spawn(web_service::run(
|
|
||||||
db_conn.clone(),
|
|
||||||
client.clone(),
|
|
||||||
client_config.git_channel,
|
|
||||||
http_listen,
|
|
||||||
ctx.subscribe(),
|
|
||||||
));
|
|
||||||
|
|
||||||
let state = AppState {
|
let state = AppState {
|
||||||
prefix: client_config.prefix,
|
prefix: client_config.prefix,
|
||||||
client: client.clone(),
|
client: client.clone(),
|
||||||
|
@ -177,9 +169,6 @@ async fn main() -> anyhow::Result<()> {
|
||||||
|
|
||||||
tracing::info!("Closing services...");
|
tracing::info!("Closing services...");
|
||||||
let _ = ctx.send(());
|
let _ = ctx.send(());
|
||||||
web_task
|
|
||||||
.await
|
|
||||||
.unwrap_or_else(|e| tracing::warn!("Couldn't join the web service: {:?}", e));
|
|
||||||
message_loop_task
|
message_loop_task
|
||||||
.await
|
.await
|
||||||
.unwrap_or_else(|e| tracing::warn!("Couldn't join the web service: {:?}", e));
|
.unwrap_or_else(|e| tracing::warn!("Couldn't join the web service: {:?}", e));
|
||||||
|
|
|
@ -1,143 +0,0 @@
|
||||||
use crate::database::Quote;
|
|
||||||
use crate::ExecutorConnection;
|
|
||||||
use irc::client::Client;
|
|
||||||
use lazy_static::lazy_static;
|
|
||||||
use reqwest::StatusCode;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use serde_json::Value::Null;
|
|
||||||
use std::net::SocketAddr;
|
|
||||||
use std::sync::Arc;
|
|
||||||
use tera::{Context, Tera};
|
|
||||||
use tokio::sync::broadcast::Receiver;
|
|
||||||
use warp::{reply, Filter, Reply};
|
|
||||||
|
|
||||||
lazy_static! {
|
|
||||||
static ref TERA: Tera = Tera::new("templates/**/*").unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn run(
|
|
||||||
db: ExecutorConnection,
|
|
||||||
wh_irc: Arc<Client>,
|
|
||||||
wh_channel: String,
|
|
||||||
listen: SocketAddr,
|
|
||||||
mut cancel: Receiver<()>,
|
|
||||||
) {
|
|
||||||
let quote_get = warp::get()
|
|
||||||
.and(warp::path("quotes"))
|
|
||||||
.and(warp::query::<QuotesQuery>())
|
|
||||||
.and(warp::any().map(move || db.clone()))
|
|
||||||
.then(handle_get_quote);
|
|
||||||
|
|
||||||
let webhook_post = warp::path("webhook")
|
|
||||||
.and(warp::post())
|
|
||||||
.and(warp::body::json())
|
|
||||||
.and(warp::any().map(move || wh_irc.clone()))
|
|
||||||
.and(warp::any().map(move || wh_channel.clone()))
|
|
||||||
.map(handle_webhook);
|
|
||||||
|
|
||||||
let routes = webhook_post.or(quote_get);
|
|
||||||
warp::serve(routes)
|
|
||||||
.bind_with_graceful_shutdown(listen, async move {
|
|
||||||
let _ = cancel.recv().await;
|
|
||||||
})
|
|
||||||
.1
|
|
||||||
.await;
|
|
||||||
tracing::info!("Web service finished");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize)]
|
|
||||||
struct QuotesTemplate {
|
|
||||||
quotes: Option<Vec<Quote>>,
|
|
||||||
flash: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct QuotesQuery {
|
|
||||||
q: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn handle_get_quote(query: QuotesQuery, db: ExecutorConnection) -> impl Reply {
|
|
||||||
let template = if let Some(q) = query.q {
|
|
||||||
if let Some(quotes) = db.search(q.clone()).await {
|
|
||||||
let quotes_count = quotes.len();
|
|
||||||
QuotesTemplate {
|
|
||||||
quotes: Some(quotes),
|
|
||||||
flash: Some(format!(
|
|
||||||
"Displaying {}/50 results for query \"{}\"",
|
|
||||||
quotes_count, q
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
QuotesTemplate {
|
|
||||||
quotes: None,
|
|
||||||
flash: Some("A database error has occurred".into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
QuotesTemplate {
|
|
||||||
quotes: db.random20().await,
|
|
||||||
flash: Some("Displaying up to 20 random quotes".into()),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
match TERA.render("quotes.html", &Context::from_serialize(&template).unwrap()) {
|
|
||||||
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)]
|
|
||||||
fn handle_webhook(json: serde_json::Value, irc: Arc<Client>, channel: String) -> impl Reply {
|
|
||||||
if json["commits"] != Null {
|
|
||||||
let commits = json["commits"].as_array().unwrap();
|
|
||||||
let repo = &json["repository"]["full_name"].as_str().unwrap().trim();
|
|
||||||
if commits.len() == 1 {
|
|
||||||
let author = &json["commits"][0]["author"]["name"]
|
|
||||||
.as_str()
|
|
||||||
.unwrap()
|
|
||||||
.trim();
|
|
||||||
let message = &json["commits"][0]["message"].as_str().unwrap().trim();
|
|
||||||
if let Err(e) = irc.send_privmsg(
|
|
||||||
channel,
|
|
||||||
format!("New commit on {}: {} - {}", repo, message, author),
|
|
||||||
) {
|
|
||||||
return reply::with_status(
|
|
||||||
format!("An error has occurred: {}", e),
|
|
||||||
StatusCode::INTERNAL_SERVER_ERROR,
|
|
||||||
)
|
|
||||||
.into_response();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if let Err(e) = irc.send_privmsg(
|
|
||||||
channel.clone(),
|
|
||||||
format!("{} new commits on {}:", commits.len(), repo),
|
|
||||||
) {
|
|
||||||
return reply::with_status(
|
|
||||||
format!("An error has occurred: {}", e),
|
|
||||||
StatusCode::INTERNAL_SERVER_ERROR,
|
|
||||||
)
|
|
||||||
.into_response();
|
|
||||||
}
|
|
||||||
for commit in commits {
|
|
||||||
let author = &commit["author"]["name"].as_str().unwrap().trim();
|
|
||||||
let message = &commit["message"].as_str().unwrap().trim();
|
|
||||||
if let Err(e) =
|
|
||||||
irc.send_privmsg(channel.clone(), format!("{} - {}", author, message))
|
|
||||||
{
|
|
||||||
return reply::with_status(
|
|
||||||
format!("An error has occurred: {}", e),
|
|
||||||
StatusCode::INTERNAL_SERVER_ERROR,
|
|
||||||
)
|
|
||||||
.into_response();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
StatusCode::CREATED.into_response()
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
<!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="get">
|
|
||||||
<label>
|
|
||||||
Search query
|
|
||||||
<input type="text" name="q" placeholder="Search..." required>
|
|
||||||
</label>
|
|
||||||
<button type="submit">Search</button>
|
|
||||||
</form>
|
|
||||||
{% if flash %}
|
|
||||||
<p>{{ flash }}</p>
|
|
||||||
{% endif %}
|
|
||||||
{% if quotes %}
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<td>Author</td>
|
|
||||||
<td>Quote</td>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for q in quotes %}
|
|
||||||
<tr>
|
|
||||||
<td>{{ q.author }}</td>
|
|
||||||
<td>{{ q.quote }}</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
{% endif %}
|
|
||||||
</main>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
Loading…
Reference in a new issue