use std::{ borrow::ToOwned, default::Default, }; use diesel_async::AsyncPgConnection; use grate::tracing; use rand::random; use serenity::{ all::ReactionType, async_trait, builder::{ CreateAttachment, CreateMessage, }, }; use songbird::input::{ core::{ io::MediaSource, probe::Hint, }, AudioStream, AudioStreamError, Compose, Input, }; pub use self::{ create::*, delete::*, history::*, invoke::*, }; use crate::{ bot::PlaybackKey, commands::{ playback, playback::{ songbird, InvokeInfo, }, }, db::{ Audio, Meme, }, util, PoiseContext, }; mod create; mod delete; mod history; pub(crate) mod invoke; pub fn commands() -> Vec> { vec![ meme(), silent_meme(), audio_meme(), rare_meme(), omen(), silentomen(), audioomen(), addmeme(), addaudiomeme(), delmeme(), history(), stats(), memers(), wat(), query(), ] } async fn send_meme( ctx: PoiseContext<'_>, t: &Meme, conn: &mut AsyncPgConnection, ) -> anyhow::Result<()> { let should_tts = t.content.as_ref().map(|t| !t.is_empty()).unwrap_or(false) && random::() % 25 == 0; tracing::debug!(should_tts, meme = ?t, "sending meme"); let image = t.image(conn).await; let audio = t.audio(conn).await; let cmsg = { let ret = CreateMessage::default().tts(should_tts); match t.content { Some(ref text) if !text.is_empty() => ret.content(text), _ => ret, } }; match image { Some(image) => { let image = image?; let att = CreateAttachment::bytes(image.data.as_slice(), &image.filename); ctx.channel_id().send_files(ctx, vec![att], cmsg).await?; }, None => { if t.content.is_some() { ctx.channel_id().send_message(ctx, cmsg).await?; } }, }; if let Some(audio) = audio { let audio = audio?; let Some(voice_channel) = util::best_voice_channel(ctx) else { tracing::error!(?ctx, "couldn't find a relevant voice channel"); util::react(ctx, '🔇').await?; return Ok(()); }; let volume = util::volume(ctx).await; tracing::debug!(volume); let playback = { let data = ctx.serenity_context().data.read().await; data.get::().unwrap().clone() }; { let (_sb, call) = songbird(ctx).await?; let mut call = call.lock().await; if call.current_channel().is_none() { call.join(voice_channel).await?; } let input = Input::Lazy(Box::new(audio)); let handle = call.enqueue_input(input).await; handle.set_volume(volume as _)?; playback.insert(handle.uuid(), playback::Metadata { invoker: ctx.author().id, invoke_info: InvokeInfo::Meme { meme: t.clone(), }, }); } util::react(ctx, ReactionType::Unicode("📣".to_owned())).await?; } Ok(()) } #[async_trait] impl Compose for Audio { fn create(&mut self) -> Result>, AudioStreamError> { let ms = std::io::Cursor::new(self.data.clone()); let ms: Box = Box::new(ms); let mut hint = Hint::new(); hint.mime_type("audio/opus"); Ok(AudioStream { input: ms, hint: Some(hint), }) } #[inline] async fn create_async( &mut self, ) -> Result>, AudioStreamError> { self.create() } #[inline] fn should_create_async(&self) -> bool { false } }