This commit is contained in:
lemonsh 2022-07-17 20:10:52 +02:00
parent 25a7635408
commit 329f6d6dae
5 changed files with 64 additions and 73 deletions

View file

@ -1,14 +1,13 @@
use async_trait::async_trait;
use crate::bot::{Command, Context};
use async_trait::async_trait;
pub struct LastMsg;
#[async_trait]
impl Command for LastMsg {
//noinspection RsNeedlessLifetimes
async fn execute<'a>(&mut self, msg: Context<'a>) -> anyhow::Result<String> {
async fn execute(&mut self, msg: Context<'_>) -> anyhow::Result<String> {
let nick = msg.content.unwrap_or(msg.author);
let lastmsg = msg.last_msg.read().await;
let lastmsg = msg.history.read().await;
Ok(format!("{}: {:?}", nick, lastmsg.get(nick)))
}
}
}

View file

@ -1,9 +1,9 @@
use rspotify::{ClientCredsSpotify, Credentials};
use crate::bot::{Context, Trigger};
use async_trait::async_trait;
use fancy_regex::Captures;
use rspotify::clients::BaseClient;
use rspotify::model::{Id, PlayableItem};
use crate::bot::{Context, Trigger};
use rspotify::{ClientCredsSpotify, Credentials};
pub struct Spotify {
spotify: ClientCredsSpotify,
@ -13,22 +13,25 @@ impl Spotify {
pub async fn new(creds: Credentials) -> anyhow::Result<Self> {
let mut spotify = ClientCredsSpotify::new(creds);
spotify.request_token().await?;
Ok(Self {
spotify
})
Ok(Self { spotify })
}
}
#[async_trait]
impl Trigger for Spotify {
async fn execute<'a>(&mut self, msg: Context<'a>, captures: Captures<'a>) -> anyhow::Result<String> {
async fn execute<'a>(
&mut self,
msg: Context<'a>,
captures: Captures<'a>,
) -> anyhow::Result<String> {
let tp_group = captures.get(1).unwrap();
let id_group = captures.get(2).unwrap();
resolve_spotify(
&mut self.spotify,
&msg.content.unwrap()[tp_group.start()..tp_group.end()],
&msg.content.unwrap()[id_group.start()..id_group.end()],
).await
)
.await
}
}

View file

@ -1,26 +1,30 @@
use crate::bot::{Context, Trigger};
use async_trait::async_trait;
use fancy_regex::{Captures, Regex};
use reqwest::Client;
use htmlescape::decode_html;
use crate::bot::{Context, Trigger};
use reqwest::Client;
pub struct Title {
http: Client,
title_regex: Regex
title_regex: Regex,
}
impl Title {
pub fn new() -> anyhow::Result<Self> {
Ok(Title {
http: Client::new(),
title_regex: Regex::new(r"(?<=<title>)(.*)(?=</title>)")?
title_regex: Regex::new(r"(?<=<title>)(.*)(?=</title>)")?,
})
}
}
#[async_trait]
impl Trigger for Title {
async fn execute<'a>(&mut self, _msg: Context<'a>, captures: Captures<'a>) -> anyhow::Result<String> {
async fn execute<'a>(
&mut self,
_msg: Context<'a>,
captures: Captures<'a>,
) -> anyhow::Result<String> {
let url = captures.get(0).unwrap().as_str();
tracing::debug!("url: {}", url);
@ -33,18 +37,19 @@ impl Trigger for Title {
let body = response.text().await?;
if let Some(tm) = self.title_regex.find(&body)? {
let title_match = &body[tm.start()..tm.end()];
let result = decode_html(title_match).unwrap_or_else(|_| title_match.to_string());
let result =
decode_html(title_match).unwrap_or_else(|_| title_match.to_string());
Ok(format!("\x039[Title]\x0311 {}", result))
} else {
Ok("\x039[Title]\x0311 No title".into())
}
} else {
let content_length = response.content_length().map(|l| (l/1024).to_string());
let content_length = response.content_length().map(|l| (l / 1024).to_string());
let size = content_length.as_deref().unwrap_or("unknown");
Ok(format!("\x039[Title]\x0311 File: {}; {}kb", mime, size))
}
} else {
Ok("\x039[Title]\x0311 No Content-Type header".into())
}
};
}
}
}

View file

@ -1,22 +1,22 @@
use crate::bot::{Context, Command};
use crate::bot::{Command, Context};
use async_trait::async_trait;
use reqwest::Client;
use serde_json::Value;
#[derive(Default)]
pub struct Waifu {
http: Client
http: Client,
}
#[async_trait]
impl Command for Waifu {
async fn execute(&mut self, msg: Context<'_>) -> anyhow::Result<String> {
let category = msg.content.unwrap_or("waifu");
let request = self.http.get(format!("https://api.waifu.pics/sfw/{}", category)).build()?;
let response = self.http.execute(request)
.await?
.text()
.await?;
let request = self
.http
.get(format!("https://api.waifu.pics/sfw/{}", category))
.build()?;
let response = self.http.execute(request).await?.text().await?;
let response = response.trim();
let value: Value = serde_json::from_str(response)?;
let url = value["url"]

View file

@ -7,10 +7,12 @@ use tokio::sync::{
#[derive(Debug)]
enum Task {
AddQuote(oneshot::Sender<bool>, Quote),
GetQuote(oneshot::Sender<Option<Quote>>, Option<String>),
SearchQuotes(oneshot::Sender<Option<Vec<Quote>>>, String),
RandomNQuotes(oneshot::Sender<Option<Vec<Quote>>>, u8),
AddQuote(oneshot::Sender<rusqlite::Result<()>>, Quote),
GetQuote(
oneshot::Sender<rusqlite::Result<Option<Quote>>>,
Option<String>,
),
SearchQuotes(oneshot::Sender<rusqlite::Result<Vec<Quote>>>, String),
}
pub struct DbExecutor {
@ -40,45 +42,34 @@ impl DbExecutor {
while let Some(task) = self.rx.blocking_recv() {
match task {
Task::AddQuote(tx, quote) => {
if let Err(e) = self.db.execute(
"insert into quotes(quote,username) values(?,?)",
params![quote.quote, quote.author],
) {
tracing::error!("A database error has occurred: {}", e);
tx.send(false).unwrap();
} else {
tx.send(true).unwrap();
}
let result = self
.db
.execute(
"insert into quotes(quote,username) values(?,?)",
params![quote.quote, quote.author],
)
.map(|_| ());
tx.send(result).unwrap();
}
Task::GetQuote(tx, author) => {
let quote = if let Some(ref author) = author {
let result = 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(Quote {quote:v.get(0)?, author:v.get(1)?}))
} else {
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| {
tracing::error!("A database error has occurred: {}", e);
None
});
tx.send(quote).unwrap();
}.optional();
tx.send(result).unwrap();
}
Task::SearchQuotes(tx, query) => {
tx.send(self.yield_quotes("select quote,username from quotes where quote like '%'||?1||'%' order by quote asc limit 5", params![query])).unwrap();
}
Task::RandomNQuotes(tx, count) => {
tx.send(self.yield_quotes(
"select quote,username from quotes order by random() limit ?",
params![count],
))
.unwrap();
}
}
}
}
fn yield_quotes<P: Params>(&self, sql: &str, params: P) -> Option<Vec<Quote>> {
match self.db.prepare(sql).and_then(|mut v| {
fn yield_quotes<P: Params>(&self, sql: &str, params: P) -> rusqlite::Result<Vec<Quote>> {
self.db.prepare(sql).and_then(|mut v| {
v.query(params).and_then(|mut v| {
let mut quotes: Vec<Quote> = Vec::with_capacity(50);
let mut quotes: Vec<Quote> = Vec::new();
while let Some(row) = v.next()? {
quotes.push(Quote {
quote: row.get(0)?,
@ -87,13 +78,7 @@ impl DbExecutor {
}
Ok(quotes)
})
}) {
Ok(o) => Some(o),
Err(e) => {
tracing::error!("A database error has occurred: {}", e);
None
}
}
})
}
}
@ -128,23 +113,22 @@ macro_rules! executor_wrapper {
impl ExecutorConnection {
// WARNING: these methods are NOT cancel-safe
executor_wrapper!(add_quote, Task::AddQuote, bool, quote: Quote);
executor_wrapper!(
add_quote,
Task::AddQuote,
rusqlite::Result<()>,
quote: Quote
);
executor_wrapper!(
get_quote,
Task::GetQuote,
Option<Quote>,
rusqlite::Result<Option<Quote>>,
author: Option<String>
);
executor_wrapper!(
search_quotes,
Task::SearchQuotes,
Option<Vec<Quote>>,
rusqlite::Result<Vec<Quote>>,
query: String
);
executor_wrapper!(
random_n_quotes,
Task::RandomNQuotes,
Option<Vec<Quote>>,
count: u8
);
}