Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
617 changes: 249 additions & 368 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion twixel/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ sqlx = { version = "0.8", features = ["chrono", "derive", "json", "macros", "mig
thiserror = "2.0"
tokio = { version = "1.45", features = ["full"] }
tokio-util = { version = "0.7", features = ["rt"] }
twixel_core = { path = "../twixel_core", features = ["rustls", "chrono"] }
twixel_core = { path = "../twixel_core", features = ["rustls", "chrono", "connection", "serde", "unstable"] }
unicode-segmentation = "1.12"

[dependencies.reqwest]
Expand Down
33 changes: 15 additions & 18 deletions twixel/src/bot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ use futures::StreamExt;
use hashbrown::HashMap;
use tokio::signal::unix::{SignalKind, signal};
use twixel_core::{
Auth, ConnectionPool, MessageBuilder,
irc_message::{AnySemantic, PrivMsg, tags::OwnedTag},
auth::OAuth, irc_message::{tags::OwnedTag, AnySemantic, PrivMsg}, ConnectionPool, MessageBuilder
};

use crate::{
Expand Down Expand Up @@ -41,7 +40,7 @@ impl BotData {
}

pub struct Bot {
conn_pool: ConnectionPool,
conn_pool: ConnectionPool<OAuth>,
commands: Vec<Command>,
catchall: Vec<DynHandler>,
data: BotData,
Expand Down Expand Up @@ -87,7 +86,7 @@ impl Bot {
Self {
conn_pool: ConnectionPool::new(
core::iter::empty::<String>(),
Auth::OAuth { username, token },
OAuth{ nick: username, oauth: token },
)
.await
.unwrap(),
Expand Down Expand Up @@ -123,7 +122,7 @@ impl Bot {

/// Returns whether to shut down or not
async fn handle_cmd(
conn_pool: &mut ConnectionPool,
conn_pool: &mut ConnectionPool<OAuth>,
cmd: BotCommand,
last_sent_msg: &mut HashMap<String, String>,
) -> bool {
Expand Down Expand Up @@ -202,19 +201,17 @@ impl Bot {
loop {
tokio::select! {
// Handle message received from twitch IRC
Some(recv) = self.conn_pool.next() => {
let idx = recv.as_ref().map(|r| r.1).ok();
for msg in recv.map(|r| r.0).into_iter().flatten() {
let cx = HandlerContext {
msg: msg.into(),
connection_idx: idx.unwrap(),
bot_tx: self.cmd_tx.clone(),
data_store: Arc::clone(&data_store)
};
Some(msg) = self.conn_pool.next() => {
let (msg, idx) = msg.unwrap();
let cx = HandlerContext {
msg: msg.into(),
connection_idx: idx,
bot_tx: self.cmd_tx.clone(),
data_store: Arc::clone(&data_store)
};

let new_tx = tx.clone();
tokio::spawn(async move { new_tx.send(cx).await.unwrap(); });
}
let new_tx = tx.clone();
tokio::spawn(async move { new_tx.send(cx).await.unwrap(); });
}
// Handle bot actions
cmd = self.cmd_rx.recv() => { match cmd {
Expand Down Expand Up @@ -301,7 +298,7 @@ async fn bot_worker(
continue;
}
msg => {
log::warn!("untreated message kind: {:?}", msg.raw());
log::warn!("untreated message kind: {:?}", msg.inner());
continue;
}
}
Expand Down
2 changes: 2 additions & 0 deletions twixel/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ mod catfact;
mod gpt;
mod join;
mod part;
mod raw;
mod remindfish;
mod sql;
mod strdbg;
Expand All @@ -16,6 +17,7 @@ pub use catfact::*;
pub use gpt::*;
pub use join::*;
pub use part::*;
pub use raw::*;
pub use remindfish::*;
pub use sql::*;
pub use strdbg::*;
Expand Down
23 changes: 23 additions & 0 deletions twixel/src/commands/raw.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use reqwest::Client;
use serde::Deserialize;
use twixel_core::irc_message::PrivMsg;

#[derive(Debug, Deserialize)]
struct HasteResp {
key: String
}

pub async fn raw(msg: PrivMsg) -> String {

let resp = Client::new().post("https://haste.potat.app/documents")
.body(reqwest::Body::from(serde_json::to_string_pretty(&*msg).unwrap()))
.send()
.await;
let Ok(r) = resp else {
return "Error: couldnt upload to hastebin lole".into();
};
let Ok(key) = r.json::<HasteResp>().await.map(|b| b.key) else {
return "Error: couldnt deserialize hastebin response lole".into();
};
format!("https://haste.potat.app/{key}")
}
5 changes: 1 addition & 4 deletions twixel/src/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,10 +153,7 @@ fn repl_print_value(val: Value<'_>) -> LocalBoxFuture<'_, String> {
Type::String => val
.as_string()
.map(|i| {
format!(
"\"{}\"",
i.to_string().unwrap_or("invalid UTF-8 string".into())
)
i.to_string().unwrap_or("invalid UTF-8 string".into())
})
.unwrap(),
Type::Array | Type::Exception | Type::Object | Type::Module | Type::Unknown => ctx
Expand Down
4 changes: 2 additions & 2 deletions twixel/src/guard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ use crate::bot::BotData;

pub struct GuardContext<'a> {
pub data_store: &'a BotData,
pub message: &'a AnySemantic<'a>,
pub message: &'a AnySemantic,
}

impl<'a> GuardContext<'a> {
pub fn data_store(&'a self) -> &'a BotData {
self.data_store
}

pub fn message(&self) -> &AnySemantic<'a> {
pub fn message(&self) -> &AnySemantic {
self.message
}
}
Expand Down
9 changes: 7 additions & 2 deletions twixel/src/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pub mod response;

#[derive(Clone)]
pub struct HandlerContext {
pub msg: AnySemantic<'static>,
pub msg: AnySemantic,
pub connection_idx: usize,
pub bot_tx: tokio::sync::mpsc::Sender<BotCommand>,
pub data_store: Arc<BotData>,
Expand Down Expand Up @@ -213,7 +213,7 @@ impl Command {
}
}

async fn handle_resp(resp: BotResponse, privmsg: &AnySemantic<'_>, sender: Sender<BotCommand>) {
async fn handle_resp(resp: BotResponse, privmsg: &AnySemantic, sender: Sender<BotCommand>) {
match resp {
BotResponse::Message(msg) => {
if let AnySemantic::PrivMsg(privmsg) = privmsg {
Expand All @@ -223,6 +223,11 @@ impl Command {
.unwrap();
}
}
BotResponse::Raw(raw) => {
sender.send(BotCommand::SendRawIrc(raw, 0))
.await
.unwrap()
}
BotResponse::Join(chan) => {
sender.send(BotCommand::JoinChannel(chan)).await.unwrap();
}
Expand Down
32 changes: 16 additions & 16 deletions twixel/src/handler/extract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ pub trait Extract: Sized + Send + 'static {
type Error: IntoResponse + Send + 'static;

fn extract(
msg: &AnySemantic<'_>,
msg: &AnySemantic,
data: Arc<BotData>,
) -> impl Future<Output = Result<Self, Self::Error>> + Send;
}
Expand All @@ -29,16 +29,16 @@ pub trait ExtractFull: Sized {
type Error: IntoResponse + Send + 'static;

fn extract_full(
msg: AnySemantic<'static>,
msg: AnySemantic,
data: Arc<BotData>,
) -> impl Future<Output = Result<Self, Self::Error>> + Send;
}

impl ExtractFull for AnySemantic<'static> {
impl ExtractFull for AnySemantic {
type Error = Infallible;

fn extract_full(
msg: AnySemantic<'static>,
msg: AnySemantic,
_data: Arc<BotData>,
) -> impl Future<Output = Result<Self, Self::Error>> + Send {
ready(Ok(msg))
Expand All @@ -49,7 +49,7 @@ impl<T: Extract> ExtractFull for T {
type Error = <T as Extract>::Error;

async fn extract_full(
msg: AnySemantic<'static>,
msg: AnySemantic,
data: Arc<BotData>,
) -> Result<Self, Self::Error> {
T::extract(&msg, data).await
Expand All @@ -60,11 +60,11 @@ macro_rules! impl_semantic {
($($ty:tt),+) => {
mod semantic {
$(
impl super::ExtractFull for twixel_core::irc_message::$ty<'static> {
impl super::ExtractFull for twixel_core::irc_message::$ty {
type Error = ();

fn extract_full(
msg: twixel_core::irc_message::AnySemantic<'static>,
msg: twixel_core::irc_message::AnySemantic,
_data: std::sync::Arc<crate::bot::BotData>
) -> impl futures::Future<Output = Result<Self, Self::Error>> + std::marker::Send {
std::future::ready(twixel_core::irc_message::$ty::from_any(msg).ok_or(()))
Expand Down Expand Up @@ -104,7 +104,7 @@ impl<T: Extract> Extract for Option<T> {
type Error = Infallible;

fn extract(
msg: &AnySemantic<'_>,
msg: &AnySemantic,
data: Arc<BotData>,
) -> impl Future<Output = Result<Self, Self::Error>> + Send {
T::extract(msg, data).map(|t| Ok(t.ok()))
Expand Down Expand Up @@ -140,8 +140,8 @@ where
{
type Error = Infallible;

fn extract(msg: &AnySemantic<'_>, data: Arc<BotData>) -> Ready<Result<Self, Infallible>> {
let msg = msg.clone().to_static();
fn extract(msg: &AnySemantic, data: Arc<BotData>) -> Ready<Result<Self, Infallible>> {
let msg = msg.clone();
let init = Box::pin(async move { T::extract_full(msg, data).await });
ready(Ok(Self::NotInitialized { init }))
}
Expand All @@ -159,7 +159,7 @@ impl Extract for MessageText {
type Error = ();

fn extract(
msg: &AnySemantic<'_>,
msg: &AnySemantic,
_data: Arc<BotData>,
) -> impl Future<Output = Result<Self, Self::Error>> + Send {
let text = match msg {
Expand All @@ -186,7 +186,7 @@ impl Extract for Username {
type Error = ();

fn extract(
msg: &AnySemantic<'_>,
msg: &AnySemantic,
_data: Arc<BotData>,
) -> impl Future<Output = Result<Self, Self::Error>> + Send {
ready(match msg {
Expand All @@ -204,7 +204,7 @@ impl Extract for SenderId {
type Error = ();

fn extract(
msg: &AnySemantic<'_>,
msg: &AnySemantic,
_data: Arc<BotData>,
) -> impl Future<Output = Result<Self, Self::Error>> + Send {
match msg {
Expand All @@ -225,7 +225,7 @@ impl Extract for Channel {
type Error = ();

fn extract(
msg: &AnySemantic<'_>,
msg: &AnySemantic,
_data: Arc<BotData>,
) -> impl Future<Output = Result<Self, Self::Error>> + Send {
let chan = if msg.get_command() == IrcCommand::PrivMsg {
Expand Down Expand Up @@ -254,7 +254,7 @@ impl<T: Send + Sync + 'static> Extract for Data<T> {
type Error = Infallible;

fn extract(
_msg: &AnySemantic<'_>,
_msg: &AnySemantic,
data: Arc<BotData>,
) -> impl Future<Output = Result<Self, Self::Error>> + Send {
let data = data.get::<T>().expect("Failed to find data");
Expand All @@ -278,7 +278,7 @@ impl<T: clap::Parser + Send + 'static> Extract for Clap<T> {
type Error = Option<clap::Error>;

fn extract(
msg: &AnySemantic<'_>,
msg: &AnySemantic,
_data: Arc<BotData>,
) -> impl Future<Output = Result<Self, Self::Error>> + Send {
let AnySemantic::PrivMsg(msg) = msg else {
Expand Down
2 changes: 2 additions & 0 deletions twixel/src/handler/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ use std::{
};

use clap::error::{ContextKind, ErrorKind};
use twixel_core::MessageBuilder;

pub enum BotResponse {
Message(String),
Raw(MessageBuilder<'static>),
Join(String),
Part(String),
Shutdown,
Expand Down
5 changes: 3 additions & 2 deletions twixel/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use guard::UserGuard;
use handler::{Command, CommandBuilder, response::BotResponse};
use sqlx::{Sqlite, sqlite::SqliteConnectOptions};

use crate::commands::gpt;
use crate::commands::{gpt, raw};

mod anymap;
mod bot;
Expand Down Expand Up @@ -130,10 +130,11 @@ async fn main() -> Result<(), anyhow::Error> {
.add_command(Command::new(bread_fact, vec!["breadfact".into()], "%"))
.add_command(Command::new(argtest, vec!["argtest".into()], "%"))
.add_command(Command::new(test, vec!["test".into()], "%"))
.add_command(Command::new(raw, vec!["raw".into()], "%"))
.add_command(
CommandBuilder::new(
async || ("shutting down!", BotResponse::Shutdown),
vec!["strdbg".into()],
vec!["shutdown".into()],
"%",
)
.and(UserGuard::allow([JULIA_ID]))
Expand Down
2 changes: 1 addition & 1 deletion twixel/src/util/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ impl IntoResponse for sqlx::Error {
impl Extract for TwixelUser {
type Error = Option<sqlx::Error>;

async fn extract(msg: &AnySemantic<'_>, data: Arc<BotData>) -> Result<Self, Self::Error> {
async fn extract(msg: &AnySemantic, data: Arc<BotData>) -> Result<Self, Self::Error> {
let pool = Data::<SqlitePool>::extract(msg, data).await.unwrap();

let AnySemantic::PrivMsg(msg) = msg else {
Expand Down
7 changes: 5 additions & 2 deletions twixel_core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ hashbrown = { version = "0.15" }
log = "0.4"
serde = { version = "1.0", features = ["derive"], optional = true }
tokio = { version = "1.35", features = ["sync", "time"] }
tokio-tungstenite = { version = "0.26" }
tokio-tungstenite = { version = "0.26", optional = true}
rand = "0.9"
thiserror = "2.0"
chrono = { version = "0.4", optional = true }
Expand All @@ -20,14 +20,17 @@ bitflags = "2.4"
either = "1.13"

[dev-dependencies]
criterion = "0.5"
divan = "0.1"
mimalloc = "0.1"
serde_json = "1.0"

[[bench]]
name = "benches"
harness = false

[features]
default = ["connection"]
connection = ["dep:tokio-tungstenite"]
native-tls = ["tokio-tungstenite/native-tls"]
rustls = [
"tokio-tungstenite/rustls",
Expand Down
Loading
Loading