Skip to content

Commit 88f227f

Browse files
authored
Merge pull request #101 from approvers/uo
feat: uo
2 parents 80484fe + 2289dce commit 88f227f

File tree

7 files changed

+149
-11
lines changed

7 files changed

+149
-11
lines changed

src/bot/gh/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,11 @@ mod test {
319319
let output = output.into();
320320

321321
struct Msg(&'static str);
322+
#[async_trait]
322323
impl Message for Msg {
324+
async fn reply(&self, _msg: &str) -> Result<()> {
325+
unimplemented!()
326+
}
323327
fn author(&self) -> &dyn User {
324328
unimplemented!()
325329
}

src/bot/meigen/mod.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use {
2-
crate::bot::{parse_command, ui, BotService, Context, IsUpdated, Message},
2+
crate::bot::{
3+
parse_command, ui, BotService, Context, IsUpdated, Message, KAWAEMON_DISCORD_USER_ID,
4+
},
35
anyhow::{Context as _, Result},
46
async_trait::async_trait,
57
model::{Meigen, MeigenId},
@@ -220,7 +222,6 @@ impl<D: MeigenDatabase> MeigenBot<D> {
220222
}
221223

222224
async fn delete(&self, caller: u64, id: MeigenId) -> Result<String> {
223-
const KAWAEMON_DISCORD_USER_ID: u64 = 391857452360007680;
224225
if caller != KAWAEMON_DISCORD_USER_ID {
225226
return Ok("名言削除はかわえもんにしか出来ません".into());
226227
}

src/bot/mod.rs

+5
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ use {
44
std::{future::Future, pin::Pin},
55
};
66

7+
const KAWAEMON_DISCORD_USER_ID: u64 = 391857452360007680;
8+
79
/// 変更が生じた場合 true
810
pub type IsUpdated = bool;
911

@@ -12,9 +14,12 @@ pub mod auth;
1214
pub mod genkai_point;
1315
pub mod gh;
1416
pub mod meigen;
17+
pub mod uo;
1518
pub mod vc_diff;
1619

20+
#[async_trait]
1721
pub(crate) trait Message: Send + Sync {
22+
async fn reply(&self, msg: &str) -> Result<()>;
1823
fn author(&self) -> &dyn User;
1924
fn content(&self) -> &str;
2025
fn attachments(&self) -> &[&dyn Attachment];

src/bot/uo/mod.rs

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
use crate::bot::{parse_command, ui, BotService, Context, Message, KAWAEMON_DISCORD_USER_ID};
2+
use anyhow::{Context as _, Result};
3+
use async_trait::async_trait;
4+
use tokio::sync::Mutex;
5+
6+
const NAME: &str = "rusty_ponyo::bot::uo";
7+
const PREFIX: &str = "g!uo";
8+
const UO: &str = "ウーォ";
9+
10+
ui! {
11+
/// ランダムでウーォと言います
12+
struct Ui {
13+
name: NAME,
14+
prefix: PREFIX,
15+
command: Command,
16+
}
17+
}
18+
19+
#[derive(Debug, clap::Subcommand)]
20+
enum Command {
21+
Status,
22+
Reroll,
23+
}
24+
25+
pub(crate) struct UoBot {
26+
prob_percent: Mutex<u8>,
27+
}
28+
29+
impl UoBot {
30+
pub fn new() -> Self {
31+
Self {
32+
prob_percent: Mutex::new(3),
33+
}
34+
}
35+
36+
async fn reroll(&self) {
37+
*self.prob_percent.lock().await = 1 + (rand::random::<u8>() % 10);
38+
}
39+
}
40+
41+
#[async_trait]
42+
impl BotService for UoBot {
43+
fn name(&self) -> &'static str {
44+
NAME
45+
}
46+
47+
async fn on_message(&self, msg: &dyn Message, ctx: &dyn Context) -> Result<()> {
48+
{
49+
let p = *self.prob_percent.lock().await;
50+
if rand::random::<f64>() < (p as f64 / 100.0) {
51+
msg.reply(UO).await?;
52+
}
53+
}
54+
55+
if !msg.content().starts_with(PREFIX) {
56+
return Ok(());
57+
}
58+
59+
let Some(parsed) = parse_command::<Ui>(msg.content(), ctx).await? else {
60+
return Ok(());
61+
};
62+
63+
use Command::*;
64+
65+
let msg = match parsed.command {
66+
Status => {
67+
let prob = self.prob_percent.lock().await;
68+
format!("```{UO}確率: {prob}%```")
69+
}
70+
Reroll => {
71+
if msg.author().id() == KAWAEMON_DISCORD_USER_ID {
72+
self.reroll().await;
73+
"振り直しました".to_owned()
74+
} else {
75+
"かわえもんでないとこの処理はできません".to_owned()
76+
}
77+
}
78+
};
79+
80+
ctx.send_text_message(&msg)
81+
.await
82+
.context("failed to send message")?;
83+
84+
Ok(())
85+
}
86+
}

src/client/console.rs

+15
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ impl<'a> ConsoleClient<'a> {
8686
};
8787

8888
let message = ConsoleMessage {
89+
service_name: service.name(),
90+
begin,
8991
content: content.clone(),
9092
attachments: attachments.clone(),
9193
user: ConsoleUser {
@@ -105,12 +107,25 @@ impl<'a> ConsoleClient<'a> {
105107
}
106108

107109
struct ConsoleMessage<'a> {
110+
service_name: &'a str,
111+
begin: Instant,
108112
content: String,
109113
attachments: Vec<&'a dyn Attachment>,
110114
user: ConsoleUser<'a>,
111115
}
112116

117+
#[async_trait]
113118
impl Message for ConsoleMessage<'_> {
119+
async fn reply(&self, content: &str) -> Result<()> {
120+
println!(
121+
"({}, reply, {}ms): {}",
122+
self.service_name,
123+
self.begin.elapsed().as_millis(),
124+
content
125+
);
126+
Ok(())
127+
}
128+
114129
fn content(&self) -> &str {
115130
&self.content
116131
}

src/client/discord.rs

+17-6
Original file line numberDiff line numberDiff line change
@@ -332,11 +332,12 @@ impl EventHandler for EvHandler {
332332
.insert(message.author.id, message.author.name.clone());
333333

334334
let converted_message = DiscordMessage {
335-
content: message.content.clone(),
335+
ctx: &ctx,
336+
message: &message,
336337
attachments: converted_attachments.iter().map(|x| x as _).collect(),
337338
author: DiscordAuthor {
338339
id: message.author.id.get(),
339-
name: message.author.name,
340+
name: &message.author.name,
340341
ctx: &ctx,
341342
},
342343
};
@@ -360,14 +361,24 @@ struct NicknameCache(HashMap<SerenityUserId, String>);
360361
struct IsBotCache(HashMap<SerenityUserId, bool>);
361362

362363
struct DiscordMessage<'a> {
363-
content: String,
364+
ctx: &'a SerenityContext,
365+
message: &'a SerenityMessage,
364366
attachments: Vec<&'a dyn Attachment>,
365367
author: DiscordAuthor<'a>,
366368
}
367369

370+
#[async_trait]
368371
impl Message for DiscordMessage<'_> {
372+
async fn reply(&self, text: &str) -> Result<()> {
373+
self.message
374+
.reply_ping(&self.ctx.http, text)
375+
.await
376+
.context("failed to reply with discord feature")?;
377+
Ok(())
378+
}
379+
369380
fn content(&self) -> &str {
370-
&self.content
381+
&self.message.content
371382
}
372383

373384
fn attachments(&self) -> &[&dyn Attachment] {
@@ -382,7 +393,7 @@ impl Message for DiscordMessage<'_> {
382393
struct DiscordAuthor<'a> {
383394
id: u64,
384395
#[allow(unused)]
385-
name: String,
396+
name: &'a str,
386397
ctx: &'a SerenityContext,
387398
}
388399

@@ -393,7 +404,7 @@ impl<'a> User for DiscordAuthor<'a> {
393404
}
394405

395406
fn name(&self) -> &str {
396-
&self.name
407+
self.name
397408
}
398409

399410
async fn dm(&self, msg: SendMessage<'_>) -> Result<()> {

src/main.rs

+19-3
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use {
1111
vc_diff::VcDiffBot,
1212
},
1313
anyhow::{Context as _, Result},
14-
bot::meigen::MeigenBot,
14+
bot::{meigen::MeigenBot, uo::UoBot},
1515
};
1616

1717
assert_one_feature!("discord_client", "console_client");
@@ -68,9 +68,25 @@ async fn async_main() -> Result<()> {
6868
.add_service(VcDiffBot::new());
6969

7070
#[cfg(feature = "console_client")]
71-
client.run().await?;
71+
{
72+
client.add_service(UoBot::new());
73+
client.run().await?;
74+
}
7275
#[cfg(feature = "discord_client")]
73-
client.run(&env_var("DISCORD_TOKEN")?).await?;
76+
{
77+
let token = env_var("DISCORD_TOKEN")?;
78+
let base = tokio::spawn(async move { client.run(&token).await });
79+
80+
let token = env_var("DISCORD_UO_TOKEN")?;
81+
let mut uo_client = crate::client::discord::DiscordClient::new();
82+
uo_client.add_service(UoBot::new());
83+
let uo = tokio::spawn(async move { uo_client.run(&token).await });
84+
85+
tokio::select! {
86+
r = base => r??,
87+
r = uo => r??,
88+
}
89+
}
7490

7591
Ok(())
7692
}

0 commit comments

Comments
 (0)