Compare commits
4 Commits
25d290b640
...
822f32ae5d
Author | SHA1 | Date |
---|---|---|
boring_nick | 822f32ae5d | |
boring_nick | e8ed3654b7 | |
boring_nick | 70d8af69f6 | |
boring_nick | 4098036d31 |
31
src/bot.rs
31
src/bot.rs
|
@ -13,7 +13,7 @@ use tokio::{
|
|||
sync::mpsc::{Receiver, Sender},
|
||||
time::sleep,
|
||||
};
|
||||
use tracing::{debug, error, info, trace};
|
||||
use tracing::{debug, error, info, log::warn, trace};
|
||||
use twitch_irc::{
|
||||
login::LoginCredentials,
|
||||
message::{AsRawIRC, IRCMessage, ServerMessage},
|
||||
|
@ -155,13 +155,11 @@ impl Bot {
|
|||
if let ServerMessage::Privmsg(privmsg) = &msg {
|
||||
trace!("Processing message {}", privmsg.message_text);
|
||||
if let Some(cmd) = privmsg.message_text.strip_prefix(COMMAND_PREFIX) {
|
||||
if self.app.config.admins.contains(&privmsg.sender.login) {
|
||||
self.handle_command(cmd, client, &privmsg.sender.id).await?;
|
||||
} else {
|
||||
info!(
|
||||
"User {} is not an admin to use commands",
|
||||
privmsg.sender.login
|
||||
);
|
||||
if let Err(err) = self
|
||||
.handle_command(cmd, client, &privmsg.sender.id, &privmsg.sender.login)
|
||||
.await
|
||||
{
|
||||
warn!("Could not handle command {cmd}: {err:#}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -171,6 +169,20 @@ impl Bot {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn check_admin(&self, user_login: &str) -> anyhow::Result<()> {
|
||||
if self
|
||||
.app
|
||||
.config
|
||||
.admins
|
||||
.iter()
|
||||
.any(|login| login == user_login)
|
||||
{
|
||||
Ok(())
|
||||
} else {
|
||||
Err(anyhow!("User {user_login} is not an admin"))
|
||||
}
|
||||
}
|
||||
|
||||
async fn write_message(&self, msg: ServerMessage) -> anyhow::Result<()> {
|
||||
// Ignore
|
||||
if matches!(msg, ServerMessage::RoomState(_)) {
|
||||
|
@ -211,6 +223,7 @@ impl Bot {
|
|||
cmd: &str,
|
||||
client: &TwitchClient<C>,
|
||||
sender_id: &str,
|
||||
sender_login: &str,
|
||||
) -> anyhow::Result<()> {
|
||||
debug!("Processing command {cmd}");
|
||||
let mut split = cmd.split_whitespace();
|
||||
|
@ -219,10 +232,12 @@ impl Bot {
|
|||
|
||||
match action {
|
||||
"join" => {
|
||||
self.check_admin(sender_login)?;
|
||||
self.update_channels(client, &args, ChannelAction::Join)
|
||||
.await?
|
||||
}
|
||||
"leave" | "part" => {
|
||||
self.check_admin(sender_login)?;
|
||||
self.update_channels(client, &args, ChannelAction::Part)
|
||||
.await?
|
||||
}
|
||||
|
|
|
@ -23,9 +23,12 @@ pub async fn read_channel(
|
|||
channel_id: &str,
|
||||
log_date: ChannelLogDate,
|
||||
reverse: bool,
|
||||
limit: Option<u64>,
|
||||
offset: Option<u64>,
|
||||
) -> Result<LogsStream> {
|
||||
let suffix = if reverse { "DESC" } else { "ASC" };
|
||||
let query = format!("SELECT raw FROM message WHERE channel_id = ? AND toStartOfDay(timestamp) = ? ORDER BY timestamp {suffix}");
|
||||
let mut query = format!("SELECT raw FROM message WHERE channel_id = ? AND toStartOfDay(timestamp) = ? ORDER BY timestamp {suffix}");
|
||||
apply_limit_offset(&mut query, limit, offset);
|
||||
|
||||
let cursor = db
|
||||
.query(&query)
|
||||
|
@ -41,9 +44,12 @@ pub async fn read_user(
|
|||
user_id: &str,
|
||||
log_date: UserLogDate,
|
||||
reverse: bool,
|
||||
limit: Option<u64>,
|
||||
offset: Option<u64>,
|
||||
) -> Result<LogsStream> {
|
||||
let suffix = if reverse { "DESC" } else { "ASC" };
|
||||
let query = format!("SELECT raw FROM message WHERE channel_id = ? AND user_id = ? AND toStartOfMonth(timestamp) = ? ORDER BY timestamp {suffix}");
|
||||
let mut query = format!("SELECT raw FROM message WHERE channel_id = ? AND user_id = ? AND toStartOfMonth(timestamp) = ? ORDER BY timestamp {suffix}");
|
||||
apply_limit_offset(&mut query, limit, offset);
|
||||
|
||||
let cursor = db
|
||||
.query(&query)
|
||||
|
@ -188,3 +194,12 @@ pub async fn delete_user_logs(db: &Client, user_id: &str) -> Result<()> {
|
|||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn apply_limit_offset(query: &mut String, limit: Option<u64>, offset: Option<u64>) {
|
||||
if let Some(limit) = limit {
|
||||
*query = format!("{query} LIMIT {limit}");
|
||||
}
|
||||
if let Some(offset) = offset {
|
||||
*query = format!("{query} OFFSET {offset}");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,6 @@ use axum::{
|
|||
response::Redirect,
|
||||
Json, TypedHeader,
|
||||
};
|
||||
use chrono::{Datelike, Utc};
|
||||
use rand::{distributions::Alphanumeric, thread_rng, Rng};
|
||||
use std::time::Duration;
|
||||
use tracing::debug;
|
||||
|
@ -72,7 +71,15 @@ pub async fn get_channel_logs(
|
|||
let log_date = ChannelLogDate::try_from(channel_log_params.date)?;
|
||||
debug!("Querying logs for date {log_date:?}");
|
||||
|
||||
let stream = read_channel(&app.db, &channel_id, log_date, logs_params.reverse).await?;
|
||||
let stream = read_channel(
|
||||
&app.db,
|
||||
&channel_id,
|
||||
log_date,
|
||||
logs_params.reverse,
|
||||
logs_params.limit,
|
||||
logs_params.offset,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let logs = LogsResponse {
|
||||
response_type: logs_params.response_type(),
|
||||
|
@ -143,6 +150,8 @@ async fn get_user_logs(
|
|||
&user_id,
|
||||
log_date,
|
||||
logs_params.reverse,
|
||||
logs_params.limit,
|
||||
logs_params.offset,
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
@ -194,54 +203,72 @@ pub async fn redirect_to_latest_channel_logs(
|
|||
channel,
|
||||
}): Path<LogsPathChannel>,
|
||||
RawQuery(query): RawQuery,
|
||||
) -> Redirect {
|
||||
let today = Utc::now();
|
||||
let year = today.year();
|
||||
let month = today.month();
|
||||
let day = today.day();
|
||||
app: State<App>,
|
||||
) -> Result<Redirect> {
|
||||
let channel_id = match channel_id_type {
|
||||
ChannelIdType::Name => app.get_user_id_by_name(&channel).await?,
|
||||
ChannelIdType::Id => channel.clone(),
|
||||
};
|
||||
|
||||
let mut new_uri = format!("/{channel_id_type}/{channel}/{year}/{month}/{day}");
|
||||
let available_logs = read_available_channel_logs(&app.db, &channel_id).await?;
|
||||
let latest_log = available_logs.first().ok_or(Error::NotFound)?;
|
||||
|
||||
let mut new_uri = format!("/{channel_id_type}/{channel}/{latest_log}");
|
||||
if let Some(query) = query {
|
||||
new_uri.push('?');
|
||||
new_uri.push_str(&query);
|
||||
}
|
||||
|
||||
Redirect::to(&new_uri)
|
||||
Ok(Redirect::to(&new_uri))
|
||||
}
|
||||
|
||||
pub async fn redirect_to_latest_user_name_logs(
|
||||
path: Path<UserLogPathParams>,
|
||||
query: RawQuery,
|
||||
) -> Redirect {
|
||||
redirect_to_latest_user_logs(path, query, "user")
|
||||
app: State<App>,
|
||||
) -> Result<Redirect> {
|
||||
redirect_to_latest_user_logs(path, query, false, app).await
|
||||
}
|
||||
|
||||
pub async fn redirect_to_latest_user_id_logs(
|
||||
path: Path<UserLogPathParams>,
|
||||
query: RawQuery,
|
||||
) -> Redirect {
|
||||
redirect_to_latest_user_logs(path, query, "userid")
|
||||
app: State<App>,
|
||||
) -> Result<Redirect> {
|
||||
redirect_to_latest_user_logs(path, query, true, app).await
|
||||
}
|
||||
|
||||
fn redirect_to_latest_user_logs(
|
||||
async fn redirect_to_latest_user_logs(
|
||||
Path(UserLogPathParams {
|
||||
channel_id_type,
|
||||
channel,
|
||||
user,
|
||||
}): Path<UserLogPathParams>,
|
||||
RawQuery(query): RawQuery,
|
||||
user_id_type: &str,
|
||||
) -> Redirect {
|
||||
let today = Utc::now();
|
||||
let year = today.year();
|
||||
let month = today.month();
|
||||
user_is_id: bool,
|
||||
app: State<App>,
|
||||
) -> Result<Redirect> {
|
||||
let channel_id = match channel_id_type {
|
||||
ChannelIdType::Name => app.get_user_id_by_name(&channel).await?,
|
||||
ChannelIdType::Id => channel.clone(),
|
||||
};
|
||||
let user_id = if user_is_id {
|
||||
user.clone()
|
||||
} else {
|
||||
app.get_user_id_by_name(&user).await?
|
||||
};
|
||||
|
||||
let mut new_uri = format!("/{channel_id_type}/{channel}/{user_id_type}/{user}/{year}/{month}");
|
||||
let available_logs = read_available_user_logs(&app.db, &channel_id, &user_id).await?;
|
||||
let latest_log = available_logs.first().ok_or(Error::NotFound)?;
|
||||
|
||||
let user_id_type = if user_is_id { "userid" } else { "user" };
|
||||
|
||||
let mut new_uri = format!("/{channel_id_type}/{channel}/{user_id_type}/{user}/{latest_log}");
|
||||
if let Some(query) = query {
|
||||
new_uri.push('?');
|
||||
new_uri.push_str(&query);
|
||||
}
|
||||
Redirect::to(&new_uri)
|
||||
Ok(Redirect::to(&new_uri))
|
||||
}
|
||||
|
||||
pub async fn random_channel_line(
|
||||
|
|
|
@ -89,6 +89,8 @@ pub struct LogsParams {
|
|||
pub reverse: bool,
|
||||
#[serde(default, deserialize_with = "deserialize_bool_param")]
|
||||
pub ndjson: bool,
|
||||
pub limit: Option<u64>,
|
||||
pub offset: Option<u64>,
|
||||
}
|
||||
|
||||
impl LogsParams {
|
||||
|
@ -139,6 +141,18 @@ pub struct AvailableLogDate {
|
|||
pub day: Option<String>,
|
||||
}
|
||||
|
||||
impl Display for AvailableLogDate {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}/{}", self.year, self.month)?;
|
||||
|
||||
if let Some(day) = &self.day {
|
||||
write!(f, "/{day}")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, JsonSchema)]
|
||||
pub struct AvailableLogsParams {
|
||||
#[serde(flatten)]
|
||||
|
|
Loading…
Reference in New Issue