Use webhooks where possible when interacting with Discord
parent
a0d7b59a33
commit
f3dce91862
|
@ -1695,6 +1695,7 @@ dependencies = [
|
|||
"log",
|
||||
"matrix-sdk",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serenity",
|
||||
"sled",
|
||||
"tokio",
|
||||
|
@ -2479,9 +2480,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.67"
|
||||
version = "1.0.68"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7f9e390c27c3c0ce8bc5d725f6e4d30a29d26659494aa4b17535f7522c5c950"
|
||||
checksum = "0f690853975602e1bfe1ccbf50504d67174e3bcf340f23b5ea9992e0587a52d8"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
|
|
|
@ -16,6 +16,7 @@ env_logger = "0.9.0"
|
|||
html-escape = "0.2.9"
|
||||
html5ever = "0.25.1"
|
||||
kuchiki = "0.8.1"
|
||||
serde_json = "1.0.68"
|
||||
|
||||
[dependencies.serenity]
|
||||
version = "0.10.9"
|
||||
|
|
|
@ -176,6 +176,10 @@ impl Bridgers {
|
|||
}
|
||||
|
||||
pub async fn send_message(&self, message: SentMessage) {
|
||||
if self.get_related_messages(&message.source).is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut related_messages = vec![message.source.clone()];
|
||||
|
||||
if let Some(discord) = self.discord.lock().unwrap().borrow().as_ref() {
|
||||
|
@ -250,8 +254,6 @@ impl Bridgers {
|
|||
pub async fn delete_message(&self, message: DeletedMessage) {
|
||||
if let Some(related_messages) = self.get_related_messages(&message.reference) {
|
||||
for related_message in related_messages.iter() {
|
||||
// TODO: What do we want to do with the success / failure ???
|
||||
|
||||
match related_message {
|
||||
MessageReference::Discord(channel_id, message_id) => {
|
||||
if let Some(discord) = self.discord.lock().unwrap().borrow().as_ref() {
|
||||
|
|
157
src/discord.rs
157
src/discord.rs
|
@ -24,20 +24,28 @@ struct DiscordHandler {
|
|||
event_tx: mpsc::UnboundedSender<MessageEvent>,
|
||||
}
|
||||
|
||||
async fn get_message_author(ctx: &Context, message: &Message) -> MessageAuthor {
|
||||
MessageAuthor {
|
||||
display_name: message
|
||||
.author_nick(&ctx)
|
||||
.await
|
||||
.unwrap_or_else(|| message.author.name.clone()),
|
||||
avatar_url: message
|
||||
.author
|
||||
.static_avatar_url()
|
||||
.unwrap_or_else(|| message.author.default_avatar_url()),
|
||||
service_name: "discord".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl EventHandler for DiscordHandler {
|
||||
async fn ready(&self, ctx: Context, _ready: Ready) {
|
||||
let _ = self.ctx_tx.send(ctx);
|
||||
info!("Discord ready!");
|
||||
|
||||
// TODO: Scan for channels to link
|
||||
}
|
||||
|
||||
async fn message(&self, ctx: Context, message: Message) {
|
||||
if message.author.id == ctx.cache.current_user_id().await {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(target) = message.content.strip_prefix("phoebe!link ") {
|
||||
if message
|
||||
.member(&ctx)
|
||||
|
@ -72,12 +80,7 @@ impl EventHandler for DiscordHandler {
|
|||
let _ = self.event_tx.send(MessageEvent::Send(SentMessage {
|
||||
source: message_ref,
|
||||
content,
|
||||
author: MessageAuthor {
|
||||
display_name: message
|
||||
.author_nick(&ctx)
|
||||
.await
|
||||
.unwrap_or(message.author.name),
|
||||
},
|
||||
author: get_message_author(&ctx, &message).await,
|
||||
replies_to,
|
||||
}));
|
||||
}
|
||||
|
@ -104,12 +107,7 @@ impl EventHandler for DiscordHandler {
|
|||
let _ = self.event_tx.send(MessageEvent::Edit(EditedMessage {
|
||||
replacing: message_ref,
|
||||
content,
|
||||
author: MessageAuthor {
|
||||
display_name: new_message
|
||||
.author_nick(&ctx)
|
||||
.await
|
||||
.unwrap_or(new_message.author.name),
|
||||
},
|
||||
author: get_message_author(&ctx, &new_message).await,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
@ -128,20 +126,117 @@ impl EventHandler for DiscordHandler {
|
|||
}
|
||||
}
|
||||
|
||||
async fn get_webhook_for_channel(discord_ctx: &Context, channel: &ChannelId) -> Option<Webhook> {
|
||||
if let Ok(webhooks) = channel.webhooks(discord_ctx).await {
|
||||
for webhook in webhooks {
|
||||
if matches!(webhook.name.as_deref(), Some("phoebe")) {
|
||||
return Some(webhook);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
async fn get_or_create_webhook_for_channel(
|
||||
discord_ctx: &Context,
|
||||
channel: &ChannelId,
|
||||
) -> Option<Webhook> {
|
||||
if let Some(webhook) = get_webhook_for_channel(discord_ctx, channel).await {
|
||||
return Some(webhook);
|
||||
}
|
||||
|
||||
if let Ok(webhook) = channel.create_webhook(discord_ctx, "phoebe").await {
|
||||
return Some(webhook);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
async fn create_webhook_reply_embeds(
|
||||
discord_ctx: &Context,
|
||||
reply: Option<(u64, u64)>,
|
||||
) -> Vec<serde_json::Value> {
|
||||
if let Some((channel_id, message_id)) = reply {
|
||||
if let Ok(replied_message) = ChannelId(channel_id)
|
||||
.message(discord_ctx, MessageId(message_id))
|
||||
.await
|
||||
{
|
||||
let replied_author_name = format!(
|
||||
"{} ↩️",
|
||||
replied_message
|
||||
.author_nick(discord_ctx)
|
||||
.await
|
||||
.as_ref()
|
||||
.unwrap_or(&replied_message.author.name)
|
||||
);
|
||||
|
||||
let reply_description = format!(
|
||||
"**[Reply to:]({})**\n{}",
|
||||
replied_message.id.link(
|
||||
ChannelId(channel_id),
|
||||
discord_ctx
|
||||
.cache
|
||||
.guild_channel(channel_id)
|
||||
.await
|
||||
.map(|gc| gc.guild_id)
|
||||
),
|
||||
&replied_message.content
|
||||
);
|
||||
|
||||
return vec![Embed::fake(|e| {
|
||||
e.author(|a| {
|
||||
a.icon_url(
|
||||
&replied_message
|
||||
.author
|
||||
.static_avatar_url()
|
||||
.unwrap_or_else(|| replied_message.author.default_avatar_url()),
|
||||
)
|
||||
.name(replied_author_name)
|
||||
})
|
||||
.description(reply_description)
|
||||
})];
|
||||
}
|
||||
}
|
||||
|
||||
vec![]
|
||||
}
|
||||
|
||||
pub async fn forward_to_discord(
|
||||
discord_ctx: &Context,
|
||||
channel: ChannelId,
|
||||
message: &SentMessage,
|
||||
reply: Option<(u64, u64)>,
|
||||
) -> Option<MessageReference> {
|
||||
if let Some(webhook) = get_or_create_webhook_for_channel(discord_ctx, &channel).await {
|
||||
let reply_embeds = create_webhook_reply_embeds(discord_ctx, reply).await;
|
||||
|
||||
return webhook
|
||||
.execute(discord_ctx, true, |w| {
|
||||
w.content(format_discord(&message.content))
|
||||
.username(format!(
|
||||
"{} ({})",
|
||||
&message.author.display_name, &message.author.service_name
|
||||
))
|
||||
.avatar_url(&message.author.avatar_url)
|
||||
.embeds(reply_embeds)
|
||||
})
|
||||
.await
|
||||
.ok()
|
||||
.flatten()
|
||||
.as_ref()
|
||||
.map(MessageReference::from);
|
||||
}
|
||||
|
||||
channel
|
||||
.send_message(&discord_ctx, |m| {
|
||||
let b = m.content(format_discord(&message.content));
|
||||
.send_message(discord_ctx, |m| {
|
||||
let content = format_discord(&message.content);
|
||||
|
||||
if let Some((channel_id, message_id)) = reply {
|
||||
b.reference_message((ChannelId(channel_id), MessageId(message_id)))
|
||||
m.content(&content)
|
||||
.reference_message((ChannelId(channel_id), MessageId(message_id)))
|
||||
} else {
|
||||
b
|
||||
m.content(&content)
|
||||
}
|
||||
})
|
||||
.await
|
||||
|
@ -156,6 +251,17 @@ pub async fn edit_on_discord(
|
|||
message_id: MessageId,
|
||||
message: &EditedMessage,
|
||||
) -> Option<MessageReference> {
|
||||
if let Some(webhook) = get_or_create_webhook_for_channel(discord_ctx, &channel_id).await {
|
||||
return webhook
|
||||
.edit_message(discord_ctx, message_id, |w| {
|
||||
w.content(format_discord(&message.content))
|
||||
})
|
||||
.await
|
||||
.as_ref()
|
||||
.ok()
|
||||
.map(MessageReference::from);
|
||||
}
|
||||
|
||||
channel_id
|
||||
.edit_message(&discord_ctx, &message_id, |m| {
|
||||
m.content(format_discord(&message.content))
|
||||
|
@ -172,6 +278,13 @@ pub async fn delete_on_discord(
|
|||
message_id: MessageId,
|
||||
_message: &DeletedMessage,
|
||||
) -> bool {
|
||||
if let Some(webhook) = get_or_create_webhook_for_channel(discord_ctx, &channel_id).await {
|
||||
return webhook
|
||||
.delete_message(discord_ctx, message_id)
|
||||
.await
|
||||
.is_ok();
|
||||
}
|
||||
|
||||
channel_id
|
||||
.delete_message(&discord_ctx, &message_id)
|
||||
.await
|
||||
|
|
|
@ -81,7 +81,6 @@ fn _find_content(event: &AnySyncRoomEvent) -> Option<AnyMessageEventContent> {
|
|||
|
||||
struct MatrixHandler {
|
||||
message_tx: mpsc::UnboundedSender<MessageEvent>,
|
||||
current_user_id: UserId,
|
||||
}
|
||||
|
||||
impl MatrixHandler {
|
||||
|
@ -92,6 +91,19 @@ impl MatrixHandler {
|
|||
.display_name()
|
||||
.unwrap_or_else(|| sender.name())
|
||||
.to_string(),
|
||||
avatar_url: sender
|
||||
.avatar_url()
|
||||
.map(|u| {
|
||||
format!(
|
||||
"https://matrix.org/_matrix/media/r0/thumbnail/{}?width=500&height=500",
|
||||
u.as_str().to_string().replace("mxc://", "")
|
||||
)
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
"data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
|
||||
.to_string()
|
||||
}),
|
||||
service_name: "matrix".to_string(),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
|
@ -138,10 +150,6 @@ async fn on_room_message_event(
|
|||
event: SyncMessageEvent<MessageEventContent>,
|
||||
room: Room,
|
||||
) {
|
||||
if event.sender == ctx.current_user_id {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Room::Joined(room) = room {
|
||||
if let Some(Relation::Replacement(replacement)) = &event.content.relates_to {
|
||||
on_message_edited(ctx, &event, room, replacement).await;
|
||||
|
@ -363,12 +371,7 @@ pub async fn create_matrix_client(
|
|||
info!("Matrix starting…");
|
||||
client.sync_once(SyncSettings::default()).await.unwrap();
|
||||
|
||||
let current_user_id = client.user_id().await.unwrap();
|
||||
|
||||
let event_handler = Arc::new(MatrixHandler {
|
||||
message_tx,
|
||||
current_user_id,
|
||||
});
|
||||
let event_handler = Arc::new(MatrixHandler { message_tx });
|
||||
|
||||
let on_msg_ctx = event_handler.clone();
|
||||
client
|
||||
|
|
|
@ -10,6 +10,8 @@ pub enum MessageReference {
|
|||
|
||||
pub struct MessageAuthor {
|
||||
pub display_name: String,
|
||||
pub avatar_url: String,
|
||||
pub service_name: String,
|
||||
}
|
||||
|
||||
pub struct SentMessage {
|
||||
|
|
Loading…
Reference in New Issue