change apns to use sandbox, and (somewhat) provide a custom payload

Signed-off-by: IAmTomahawkx <iamtomahawkx@gmail.com>
This commit is contained in:
IAmTomahawkx
2024-07-05 20:43:08 -07:00
parent f9d9059e73
commit 27f15f7b02
3 changed files with 54 additions and 4 deletions

View File

@@ -30,6 +30,7 @@ public_key = "BGcvgR-i2z4IQ5Mw841vJvkLjt8wY-FjmWrw83jOLCY52qcGZS0OF7nfLzuYbjsQIS
api_key = "" api_key = ""
[api.apn] [api.apn]
sandbox = false
pkcs8 = "" pkcs8 = ""
key_id = "" key_id = ""
team_id = "" team_id = ""

View File

@@ -79,6 +79,7 @@ pub struct ApiFcm {
#[derive(Deserialize, Debug, Clone)] #[derive(Deserialize, Debug, Clone)]
pub struct ApiApn { pub struct ApiApn {
pub sandbox: bool,
pub pkcs8: String, pub pkcs8: String,
pub key_id: String, pub key_id: String,
pub team_id: String, pub team_id: String,

View File

@@ -6,13 +6,23 @@ use base64::{
}; };
use deadqueue::limited::Queue; use deadqueue::limited::Queue;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use revolt_a2::{Client, ClientConfig, DefaultNotificationBuilder}; use revolt_a2::{Client, ClientConfig, DefaultNotificationBuilder, Endpoint};
use revolt_a2::{Error, ErrorBody, ErrorReason, NotificationBuilder, Response}; use revolt_a2::{Error, ErrorBody, ErrorReason, NotificationBuilder, Response};
use revolt_config::config; use revolt_config::config;
use revolt_models::v0::PushNotification; use revolt_models::v0::{Message, PushNotification};
use crate::Database; use crate::Database;
/// Payload information
#[derive(Debug)]
pub struct ApnCustomPayload {
message: Message,
serverId: String,
authorAvatar: String,
authorDisplayName: String,
channelName: String,
}
/// Task information /// Task information
#[derive(Debug)] #[derive(Debug)]
pub struct ApnTask { pub struct ApnTask {
@@ -30,9 +40,24 @@ pub struct ApnTask {
/// Thread Id /// Thread Id
thread_id: String, thread_id: String,
/// Category (informs the client what kind of notification is being sent.)
category: String,
/// Payload used by the iOS client to modify the notification
payload: ApnCustomPayload,
} }
impl ApnTask { impl ApnTask {
fn format_title(notification: &PushNotification) -> String {
// ideally this changes depending on context
// in a server, it would look like "Sendername, #channelname in servername"
// in a group, it would look like "Sendername in groupname"
// in a dm it should just be "Sendername".
// not sure how feasible all those are given the PushNotification object as it currently stands.
todo!();
}
pub fn from_notification( pub fn from_notification(
session_id: String, session_id: String,
device_token: String, device_token: String,
@@ -41,9 +66,17 @@ impl ApnTask {
ApnTask { ApnTask {
session_id, session_id,
device_token, device_token,
title: notification.author.to_string(), title: ApnTask::format_title(notification),
body: notification.body.to_string(), body: notification.body.to_string(),
thread_id: notification.tag.to_string(), thread_id: notification.tag.to_string(),
category: "ALERT_MESSAGE".to_string(),
payload: ApnCustomPayload {
message: (),
serverId: (),
authorAvatar: notification.icon.to_string(),
authorDisplayName: notification.author.to_string(),
channelName: (),
},
} }
} }
} }
@@ -67,15 +100,24 @@ pub async fn worker(db: Database) {
return; return;
} }
let endpoint = if config.api.apn.sandbox {
Endpoint::Sandbox
} else {
Endpoint::Production
};
let pkcs8 = engine::general_purpose::STANDARD let pkcs8 = engine::general_purpose::STANDARD
.decode(config.api.apn.pkcs8) .decode(config.api.apn.pkcs8)
.expect("valid `pcks8`"); .expect("valid `pcks8`");
let client_config = ClientConfig::default();
client_config.endpoint = endpoint;
let client = Client::token( let client = Client::token(
&mut Cursor::new(pkcs8), &mut Cursor::new(pkcs8),
config.api.apn.key_id, config.api.apn.key_id,
config.api.apn.team_id, config.api.apn.team_id,
ClientConfig::default(), client_config,
) )
.expect("could not create APN client"); .expect("could not create APN client");
@@ -85,8 +127,14 @@ pub async fn worker(db: Database) {
.set_title(&task.title) .set_title(&task.title)
.set_body(&task.body) .set_body(&task.body)
.set_thread_id(&task.thread_id) .set_thread_id(&task.thread_id)
.set_category(&task.category)
.set_mutable_content() // allows the service extension to execute
.build(&task.device_token, Default::default()); .build(&task.device_token, Default::default());
payload.data = &task.payload; // this looks like a job for someone more rust-inclined than me. -tom
println!("sending APNS payload: {:?}", payload.data);
if let Err(err) = client.send(payload).await { if let Err(err) = client.send(payload).await {
match err { match err {
Error::ResponseError(Response { Error::ResponseError(Response {