feat: add support for Firebase Cloud Messaging

This commit is contained in:
Paul Makles
2023-08-27 15:18:19 +01:00
parent 7568f49755
commit 853366a297
2 changed files with 52 additions and 15 deletions

View File

@@ -2,7 +2,6 @@ use indexmap::{IndexMap, IndexSet};
use iso8601_timestamp::Timestamp;
use revolt_models::v0::{Embed, MessageAuthor, MessageSort, MessageWebhook, PushNotification};
use revolt_result::Result;
use serde_json::json;
use ulid::Ulid;
use crate::{
@@ -268,8 +267,7 @@ impl Message {
_ => vec![],
}
},
json!(PushNotification::from(self.clone().into(), Some(author), &channel.id()).await)
.to_string(),
PushNotification::from(self.clone().into(), Some(author), &channel.id()).await,
)
.await;

View File

@@ -8,7 +8,9 @@ use base64::{
use deadqueue::limited::Queue;
use once_cell::sync::Lazy;
use revolt_config::config;
use revolt_models::v0::PushNotification;
use revolt_presence::filter_online;
use serde_json::json;
use web_push::{
ContentEncoding, IsahcWebPushClient, SubscriptionInfo, SubscriptionKeys, VapidSignatureBuilder,
WebPushClient, WebPushMessageBuilder,
@@ -19,14 +21,14 @@ use web_push::{
struct PushTask {
/// User IDs of the targets that are to receive this notification
recipients: Vec<String>,
/// Raw JSON payload to send to clients
payload: String,
/// Push Notification
payload: PushNotification,
}
static Q: Lazy<Queue<PushTask>> = Lazy::new(|| Queue::new(10_000));
/// Queue a new task for a worker
pub async fn queue(recipients: Vec<String>, payload: String) {
pub async fn queue(recipients: Vec<String>, payload: PushNotification) {
if recipients.is_empty() {
return;
}
@@ -48,8 +50,15 @@ pub async fn queue(recipients: Vec<String>, payload: String) {
/// Start a new worker
pub async fn worker(db: Database) {
let config = config().await;
let client = IsahcWebPushClient::new().unwrap();
let key = engine::general_purpose::URL_SAFE_NO_PAD
let web_push_client = IsahcWebPushClient::new().unwrap();
let fcm_client = if config.api.fcm.api_key.is_empty() {
None
} else {
Some(fcm::Client::new())
};
let web_push_private_key = engine::general_purpose::URL_SAFE_NO_PAD
.decode(config.api.vapid.private_key)
.expect("valid `VAPID_PRIVATE_KEY`");
@@ -61,7 +70,37 @@ pub async fn worker(db: Database) {
if let Some(sub) = session.subscription {
if sub.endpoint == "fcm" {
// Use Firebase Cloud Messaging
info!("Skipping FCM client, unimplemented...");
if let Some(client) = &fcm_client {
let PushNotification {
author,
icon,
image: _,
body,
tag,
timestamp: _,
url: _,
} = &task.payload;
let mut notification = fcm::NotificationBuilder::new();
notification.title(author);
notification.icon(icon);
notification.body(body);
notification.tag(tag);
// TODO: expand support for fields
let notification = notification.finalize();
let mut message_builder =
fcm::MessageBuilder::new(&config.api.fcm.api_key, &sub.auth);
message_builder.notification(notification);
if let Err(err) = client.send(message_builder.finalize()).await {
error!("Failed to send FCM notification! {:?}", err);
} else {
info!("Sent FCM notification to {:?}.", session.id);
}
} else {
info!("No FCM token was specified!");
}
} else {
// Use Web Push Standard
let subscription = SubscriptionInfo {
@@ -73,20 +112,20 @@ pub async fn worker(db: Database) {
};
match VapidSignatureBuilder::from_pem(
std::io::Cursor::new(&key),
std::io::Cursor::new(&web_push_private_key),
&subscription,
) {
Ok(sig_builder) => match sig_builder.build() {
Ok(signature) => {
let mut builder = WebPushMessageBuilder::new(&subscription);
builder.set_vapid_signature(signature);
builder.set_payload(
ContentEncoding::AesGcm,
task.payload.as_bytes(),
);
let payload = json!(task.payload).to_string();
builder
.set_payload(ContentEncoding::AesGcm, payload.as_bytes());
match builder.build() {
Ok(msg) => match client.send(msg).await {
Ok(msg) => match web_push_client.send(msg).await {
Ok(_) => {
info!(
"Sent Web Push notification to {:?}.",