1use std::time::Duration;
24
25use crate::msg::Msg;
26
27
28const TOKEN_OFFSET: u64 = 2700;
29const TEAM_ID: &str = "5U8LBRXG3A";
30const AUTH_KEY_ID: &str = "LH4T9V5U4R";
31const TOPIC: &str = "me.fin.bark";
32
33const KEY: &str = r#"-----BEGIN PRIVATE KEY-----
34MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQg4vtC3g5L5HgKGJ2+
35T1eA0tOivREvEAY2g+juRXJkYL2gCgYIKoZIzj0DAQehRANCAASmOs3JkSyoGEWZ
36sUGxFs/4pw1rIlSV2IC19M8u3G5kq36upOwyFWj9Gi3Ejc9d3sC7+SHRqXrEAJow
378/7tRpV+
38-----END PRIVATE KEY-----
39"#;
40pub struct Bark {
41 team_id: String,
42 auth_key_id: String,
43 topic: String,
44 key: String,
45 token: String
46}
47
48
49impl Bark {
50 pub fn new() -> Self {
51 Self {
52 team_id : TEAM_ID.to_string(),
53 auth_key_id : AUTH_KEY_ID.to_string(),
54 topic : TOPIC.to_string(),
55 key : KEY.to_string(),
56 token : ".".to_string(),
57 }
58 }
59
60 pub fn born(timestamp: u64, token: String) -> Self {
61 if timestamp + TOKEN_OFFSET <= Self::ts() {
62 println!("warning: token expired, bark will new one");
63 return Self::new();
64 }
65 Self {
66 team_id : TEAM_ID.to_string(),
67 auth_key_id : AUTH_KEY_ID.to_string(),
68 topic: TOPIC.to_string(),
69 key: KEY.to_string(),
70 token: format!("{}.{}", timestamp, token),
71 }
72 }
73
74 fn ts() -> u64 {
75 std::time::SystemTime::now()
76 .duration_since(std::time::UNIX_EPOCH)
77 .unwrap_or_else(|_e| Duration::from_secs(0))
78 .as_secs()
79 }
80
81 pub fn token(&mut self) -> (u64, String) {
85 let token = self.token.split_once(".").unwrap();
86 (token.0.parse::<u64>().unwrap_or_else(|_e| 0), token.1.to_string())
87 }
88
89 pub fn force_refresh_token(&mut self) -> (u64, String) {
93 self.get_token();
94 self.token()
95 }
96 pub fn send<T>(&mut self, msg: &Msg, devices: T) -> Option<Vec<String>>
100 where
101 T: IntoIterator<Item = String>
102 {
103 crate::apns::send(&msg, self.topic.clone().as_str(), &self.get_token(), devices)
104 }
105
106 pub async fn async_send<T>(&mut self, msg: &Msg, devices: T) -> Option<Vec<String>>
110 where
111 T: IntoIterator<Item = String>
112 {
113 crate::apns::async_send(&msg, self.topic.clone().as_str(), &self.get_token(), devices).await
114 }
115
116 fn get_token(&mut self) -> String {
117 let time_stamp: u64 = Self::ts();
118
119 if let Some((ts, token)) = self.token.split_once(".") {
120 if ts.parse::<u64>().unwrap_or_else(|_e| 0) + TOKEN_OFFSET >= time_stamp {
122 return token.to_string();
123 }
124 }
125
126 let jwt_header: String = Self::clean_str(
127 openssl::base64::encode_block(
128 format!("{{ \"alg\": \"ES256\", \"kid\": \"{}\" }}", self.auth_key_id)
129 .as_bytes()
130 )
131 );
132
133 let jwt_claims: String = Self::clean_str(
134 openssl::base64::encode_block(
135 format!("{{ \"iss\": \"{}\", \"iat\": {} }}",
136 self.team_id, time_stamp
137 )
138 .as_bytes()
139 )
140 );
141
142 let mut singer: openssl::sign::Signer<'_> = openssl::sign::Signer::new(
143 openssl::hash::MessageDigest::sha256(),
144 &openssl::pkey::PKey::from_ec_key(
145 openssl::ec::EcKey::private_key_from_pem(self.key.as_bytes()).expect("init key data failed")
146 ).expect("generate private key failed")
147 ).expect("init signer failed");
148
149 let jwt_header: String = format!("{}.{}", jwt_header, jwt_claims);
150 singer.update(jwt_header.as_bytes()).expect("fill sign data failed");
151 let sign: Vec<u8> = singer.sign_to_vec().expect("sign failed");
152 let jwt_signature: String = Self::clean_str(openssl::base64::encode_block(&sign));
153 let token: String= format!("{}.{}", jwt_header, jwt_signature);
154
155 self.token = format!("{}.{}", time_stamp, token);
156 token
157 }
158
159 fn clean_str(str: String) -> String {
160 str.replace("+", "-")
161 .replace("/", "_")
162 .replace("=", "")
163 }
164}