Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(WIP) 웹훅을 사용하여 이쁘게 메세지 전송하기 구현 #10

Closed
wants to merge 4 commits into from
Closed
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
9 changes: 0 additions & 9 deletions src/discord_client/channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,3 @@ pub(super) enum ChannelRef<'a> {
Guild(&'a str, &'a GuildChannel),
Private(&'a PrivateChannel),
}

impl<'a> ChannelRef<'a> {
pub(super) fn id(&self) -> ChannelId {
match self {
ChannelRef::Guild(_, ch) => ch.id,
ChannelRef::Private(ch) => ch.id,
}
}
}
10 changes: 7 additions & 3 deletions src/discord_client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ use futures::{
channel::mpsc,
};
use serenity::model::{
channel::Channel as SerenityChannel,
prelude::*,
prelude::Message as SerenityMessage,
};
Expand Down Expand Up @@ -352,6 +351,7 @@ impl Handler<HandleMessage> for Discord {
let response = self.handle_bot_command(msg);
self.worker.do_send(SendMessage {
channel: channel_id,
nickname: None,
content: response.unwrap(),
});
}
Expand All @@ -376,8 +376,12 @@ impl Handler<MessageCreated> for Discord {
return;
}
for channel in self.find_channels(&msg.channel[1..]) {
let m = format!("<{}> {}", msg.nickname, msg.content);
self.worker.do_send(SendMessage { channel: channel.id, content: m });
let msg = SendMessage {
channel: channel.id,
nickname: Some(msg.nickname.clone()),
content: msg.content.clone(),
};
self.worker.do_send(msg);
}
}
}
63 changes: 52 additions & 11 deletions src/discord_client/worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ use std::thread;
use std::time::Duration;

use ::actix::prelude::*;
use ::serenity::{
model::prelude::*,
};
use serenity::model::prelude::*;
use serenity::builder::ExecuteWebhook;
use serenity::utils::vecmap_to_json_map;
use serenity::http::raw::execute_webhook;

use crate::Error;

Expand All @@ -29,25 +30,65 @@ impl Actor for DiscordWorker {
#[rtype(result = "Result<(), crate::Error>")]
pub(super) struct SendMessage {
pub(super) channel: ChannelId,
/// 닉네임이 지정되지 않은 경우 Discord Bot API의 `send_message` 함수를 사용하고,
/// 닉네임이 지정된 경우 Discord WebHook을 사용하여 주어진 닉네임과 함께 메세지를 전송한다.
pub(super) nickname: Option<String>,
pub(super) content: String,
}

impl Handler<SendMessage> for DiscordWorker {
type Result = Result<(), Error>;

fn handle(&mut self, msg: SendMessage, _: &mut Self::Context) -> Self::Result {
let SendMessage { channel, content, .. } = msg;
while let Err(e) = channel.send_message(|msg| msg.content(content.clone())) {
use serenity::Error::*;
match e {
Http(..) | Hyper(..) | WebSocket(..) => {
thread::sleep(Duration::from_millis(100));
use serenity::Error::{Http, Hyper, WebSocket};

// TODO: 설정을 어딘가 괜찮은곳에 저장해야함
let id = 245037420704169985;
let token = "ig5AO-wdVWpCBtUUMxmgsWryqgsW3DChbKYOINftJ4DCrUbnkedoYZD0VOH1QLr-S3sV";

let send = || if let Some(nick) = &msg.nickname {
// 닉네임이 있음; Webhook을 사용해 전송함

// 닉네임을 [2, 32] 글자 범위로 바꿔야만 함
//
// References:
// https://discordapp.com/developers/docs/resources/user#usernames-and-nicknames
// https://github.com/reactiflux/discord-irc/pull/454
let truncated_nick = &format!("{:_<2}", nick)[..32];

// 웹훅 발송
let data = ExecuteWebhook::default().content(&msg.content).username(truncated_nick);
let map = vecmap_to_json_map(data.0);
execute_webhook(id, token, false, &map).map(|_| ())
} else {
// 닉네임이 없음; Bot API를 사용해 전송함
msg.channel.send_message(|m| m.content(&msg.content)).map(|_| ())
};

// 메세지 전송
let result = send();

// 실패한경우 유한번 재시도함
if let Err(mut error) = result {
for _ in 0..1000 {
match error {
// 아래 세 유형의 에러일경우 재시도
Http(_) | Hyper(_) | WebSocket(_) => (),
// 그 외에는 포기하고 즉시 에러 반환
_ => break
}
_ => {
return Err(From::from(e));

thread::sleep(Duration::from_millis(100));

match send() {
Ok(_) => return Ok(()),
Err(e) => error = e,
}
}

return Err(From::from(error))
}

Ok(())
}
}
Expand Down
2 changes: 0 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ mod message;
mod prelude;
mod util;

use failure::Fail;

pub use crate::{
bus::{Bus, BusId},
config::{Config, fetch_config},
Expand Down