From 504c9f4f9c9be024e3390fc8c650fde1d919d416 Mon Sep 17 00:00:00 2001 From: kawaemon Date: Thu, 21 Nov 2024 03:07:06 +0900 Subject: [PATCH 1/2] feat: uo --- src/bot/gh/mod.rs | 4 ++ src/bot/mod.rs | 3 ++ src/bot/uo/mod.rs | 87 +++++++++++++++++++++++++++++++++++++++++++ src/client/console.rs | 15 ++++++++ src/client/discord.rs | 23 +++++++++--- src/main.rs | 22 +++++++++-- 6 files changed, 145 insertions(+), 9 deletions(-) create mode 100644 src/bot/uo/mod.rs diff --git a/src/bot/gh/mod.rs b/src/bot/gh/mod.rs index ee91e68..d9550bb 100644 --- a/src/bot/gh/mod.rs +++ b/src/bot/gh/mod.rs @@ -319,7 +319,11 @@ mod test { let output = output.into(); struct Msg(&'static str); + #[async_trait] impl Message for Msg { + async fn reply(&self, _msg: &str) -> Result<()> { + unimplemented!() + } fn author(&self) -> &dyn User { unimplemented!() } diff --git a/src/bot/mod.rs b/src/bot/mod.rs index 3ae88bc..5f8f546 100644 --- a/src/bot/mod.rs +++ b/src/bot/mod.rs @@ -12,9 +12,12 @@ pub mod auth; pub mod genkai_point; pub mod gh; pub mod meigen; +pub mod uo; pub mod vc_diff; +#[async_trait] pub(crate) trait Message: Send + Sync { + async fn reply(&self, msg: &str) -> Result<()>; fn author(&self) -> &dyn User; fn content(&self) -> &str; fn attachments(&self) -> &[&dyn Attachment]; diff --git a/src/bot/uo/mod.rs b/src/bot/uo/mod.rs new file mode 100644 index 0000000..372d882 --- /dev/null +++ b/src/bot/uo/mod.rs @@ -0,0 +1,87 @@ +use crate::bot::{parse_command, ui, BotService, Context, Message}; +use anyhow::{Context as _, Result}; +use async_trait::async_trait; +use tokio::sync::Mutex; + +const NAME: &str = "rusty_ponyo::bot::uo"; +const PREFIX: &str = "g!uo"; +const UO: &str = "ウーォ"; + +ui! { + /// ランダムでウーォと言います + struct Ui { + name: NAME, + prefix: PREFIX, + command: Command, + } +} + +#[derive(Debug, clap::Subcommand)] +enum Command { + Status, + Reroll, +} + +pub(crate) struct UoBot { + prob_percent: Mutex, +} + +impl UoBot { + pub fn new() -> Self { + Self { + prob_percent: Mutex::new(3), + } + } + + async fn reroll(&self) { + *self.prob_percent.lock().await = 1 + (rand::random::() % 10); + } +} + +#[async_trait] +impl BotService for UoBot { + fn name(&self) -> &'static str { + NAME + } + + async fn on_message(&self, msg: &dyn Message, ctx: &dyn Context) -> Result<()> { + { + let p = *self.prob_percent.lock().await; + if rand::random::() < (p as f64 / 100.0) { + msg.reply(UO).await?; + } + } + + if !msg.content().starts_with(PREFIX) { + return Ok(()); + } + + let Some(parsed) = parse_command::(msg.content(), ctx).await? else { + return Ok(()); + }; + + use Command::*; + + let msg = match parsed.command { + Status => { + let prob = self.prob_percent.lock().await; + format!("```{UO}確率: {prob}%```") + } + Reroll => { + const KAWAEMON_DISCORD_USER_ID: u64 = 391857452360007680; + if msg.author().id() == KAWAEMON_DISCORD_USER_ID { + self.reroll().await; + "振り直しました".to_owned() + } else { + "かわえもんでないとこの処理はできません".to_owned() + } + } + }; + + ctx.send_text_message(&msg) + .await + .context("failed to send message")?; + + Ok(()) + } +} diff --git a/src/client/console.rs b/src/client/console.rs index 496f357..8c14112 100644 --- a/src/client/console.rs +++ b/src/client/console.rs @@ -86,6 +86,8 @@ impl<'a> ConsoleClient<'a> { }; let message = ConsoleMessage { + service_name: service.name(), + begin, content: content.clone(), attachments: attachments.clone(), user: ConsoleUser { @@ -105,12 +107,25 @@ impl<'a> ConsoleClient<'a> { } struct ConsoleMessage<'a> { + service_name: &'a str, + begin: Instant, content: String, attachments: Vec<&'a dyn Attachment>, user: ConsoleUser<'a>, } +#[async_trait] impl Message for ConsoleMessage<'_> { + async fn reply(&self, content: &str) -> Result<()> { + println!( + "({}, reply, {}ms): {}", + self.service_name, + self.begin.elapsed().as_millis(), + content + ); + Ok(()) + } + fn content(&self) -> &str { &self.content } diff --git a/src/client/discord.rs b/src/client/discord.rs index 1125699..6261275 100644 --- a/src/client/discord.rs +++ b/src/client/discord.rs @@ -332,11 +332,12 @@ impl EventHandler for EvHandler { .insert(message.author.id, message.author.name.clone()); let converted_message = DiscordMessage { - content: message.content.clone(), + ctx: &ctx, + message: &message, attachments: converted_attachments.iter().map(|x| x as _).collect(), author: DiscordAuthor { id: message.author.id.get(), - name: message.author.name, + name: &message.author.name, ctx: &ctx, }, }; @@ -360,14 +361,24 @@ struct NicknameCache(HashMap); struct IsBotCache(HashMap); struct DiscordMessage<'a> { - content: String, + ctx: &'a SerenityContext, + message: &'a SerenityMessage, attachments: Vec<&'a dyn Attachment>, author: DiscordAuthor<'a>, } +#[async_trait] impl Message for DiscordMessage<'_> { + async fn reply(&self, text: &str) -> Result<()> { + self.message + .reply_ping(&self.ctx.http, text) + .await + .context("failed to reply with discord feature")?; + Ok(()) + } + fn content(&self) -> &str { - &self.content + &self.message.content } fn attachments(&self) -> &[&dyn Attachment] { @@ -382,7 +393,7 @@ impl Message for DiscordMessage<'_> { struct DiscordAuthor<'a> { id: u64, #[allow(unused)] - name: String, + name: &'a str, ctx: &'a SerenityContext, } @@ -393,7 +404,7 @@ impl<'a> User for DiscordAuthor<'a> { } fn name(&self) -> &str { - &self.name + self.name } async fn dm(&self, msg: SendMessage<'_>) -> Result<()> { diff --git a/src/main.rs b/src/main.rs index 8778a88..22ece54 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,7 +11,7 @@ use { vc_diff::VcDiffBot, }, anyhow::{Context as _, Result}, - bot::meigen::MeigenBot, + bot::{meigen::MeigenBot, uo::UoBot}, }; assert_one_feature!("discord_client", "console_client"); @@ -68,9 +68,25 @@ async fn async_main() -> Result<()> { .add_service(VcDiffBot::new()); #[cfg(feature = "console_client")] - client.run().await?; + { + client.add_service(UoBot::new()); + client.run().await?; + } #[cfg(feature = "discord_client")] - client.run(&env_var("DISCORD_TOKEN")?).await?; + { + let token = env_var("DISCORD_TOKEN")?; + let base = tokio::spawn(async move { client.run(&token).await }); + + let token = env_var("DISCORD_UO_TOKEN")?; + let mut uo_client = crate::client::discord::DiscordClient::new(); + uo_client.add_service(UoBot::new()); + let uo = tokio::spawn(async move { uo_client.run(&token).await }); + + tokio::select! { + r = base => r??, + r = uo => r??, + } + } Ok(()) } From 2289dce2a348ad9e869821c64ae988510ac7a414 Mon Sep 17 00:00:00 2001 From: kawaemon Date: Thu, 21 Nov 2024 03:09:41 +0900 Subject: [PATCH 2/2] =?UTF-8?q?=EF=BD=B3=EF=BD=B0=EF=BD=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/bot/meigen/mod.rs | 5 +++-- src/bot/mod.rs | 2 ++ src/bot/uo/mod.rs | 3 +-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/bot/meigen/mod.rs b/src/bot/meigen/mod.rs index a8057b5..bd402db 100644 --- a/src/bot/meigen/mod.rs +++ b/src/bot/meigen/mod.rs @@ -1,5 +1,7 @@ use { - crate::bot::{parse_command, ui, BotService, Context, IsUpdated, Message}, + crate::bot::{ + parse_command, ui, BotService, Context, IsUpdated, Message, KAWAEMON_DISCORD_USER_ID, + }, anyhow::{Context as _, Result}, async_trait::async_trait, model::{Meigen, MeigenId}, @@ -220,7 +222,6 @@ impl MeigenBot { } async fn delete(&self, caller: u64, id: MeigenId) -> Result { - const KAWAEMON_DISCORD_USER_ID: u64 = 391857452360007680; if caller != KAWAEMON_DISCORD_USER_ID { return Ok("名言削除はかわえもんにしか出来ません".into()); } diff --git a/src/bot/mod.rs b/src/bot/mod.rs index 5f8f546..34f512c 100644 --- a/src/bot/mod.rs +++ b/src/bot/mod.rs @@ -4,6 +4,8 @@ use { std::{future::Future, pin::Pin}, }; +const KAWAEMON_DISCORD_USER_ID: u64 = 391857452360007680; + /// 変更が生じた場合 true pub type IsUpdated = bool; diff --git a/src/bot/uo/mod.rs b/src/bot/uo/mod.rs index 372d882..ca7f2e8 100644 --- a/src/bot/uo/mod.rs +++ b/src/bot/uo/mod.rs @@ -1,4 +1,4 @@ -use crate::bot::{parse_command, ui, BotService, Context, Message}; +use crate::bot::{parse_command, ui, BotService, Context, Message, KAWAEMON_DISCORD_USER_ID}; use anyhow::{Context as _, Result}; use async_trait::async_trait; use tokio::sync::Mutex; @@ -68,7 +68,6 @@ impl BotService for UoBot { format!("```{UO}確率: {prob}%```") } Reroll => { - const KAWAEMON_DISCORD_USER_ID: u64 = 391857452360007680; if msg.author().id() == KAWAEMON_DISCORD_USER_ID { self.reroll().await; "振り直しました".to_owned()