|
|
|
@ -1,17 +1,26 @@ |
|
|
|
use crate::{Error, PduEvent, Result}; |
|
|
|
use crate::{Database, Error, PduEvent, Result}; |
|
|
|
|
|
|
|
use log::{error, info, warn}; |
|
|
|
use ruma::{ |
|
|
|
use ruma::{ |
|
|
|
api::client::r0::push::{Pusher, PusherKind}, |
|
|
|
api::{ |
|
|
|
events::{ |
|
|
|
client::r0::push::{Pusher, PusherKind}, |
|
|
|
room::{ |
|
|
|
push_gateway::send_event_notification::{ |
|
|
|
member::MemberEventContent, |
|
|
|
self, |
|
|
|
message::{MessageEventContent, TextMessageEventContent}, |
|
|
|
v1::{Device, Notification, NotificationCounts, NotificationPriority}, |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
OutgoingRequest, |
|
|
|
}, |
|
|
|
}, |
|
|
|
EventType, |
|
|
|
events::room::{ |
|
|
|
|
|
|
|
member::{MemberEventContent, MembershipState}, |
|
|
|
|
|
|
|
message::{MessageEventContent, TextMessageEventContent}, |
|
|
|
|
|
|
|
power_levels::PowerLevelsEventContent, |
|
|
|
}, |
|
|
|
}, |
|
|
|
push::{PushCondition, Ruleset}, |
|
|
|
events::EventType, |
|
|
|
UserId, |
|
|
|
push::{Action, PushCondition, PushFormat, Ruleset, Tweak}, |
|
|
|
|
|
|
|
uint, UInt, UserId, |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
use std::{convert::TryFrom, fmt::Debug, time::Duration}; |
|
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone)] |
|
|
|
#[derive(Debug, Clone)] |
|
|
|
pub struct PushData { |
|
|
|
pub struct PushData { |
|
|
|
/// UserId + pushkey -> Pusher
|
|
|
|
/// UserId + pushkey -> Pusher
|
|
|
|
@ -29,6 +38,15 @@ impl PushData { |
|
|
|
let mut key = sender.as_bytes().to_vec(); |
|
|
|
let mut key = sender.as_bytes().to_vec(); |
|
|
|
key.extend_from_slice(pusher.pushkey.as_bytes()); |
|
|
|
key.extend_from_slice(pusher.pushkey.as_bytes()); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// There are 2 kinds of pushers but the spec says: null deletes the pusher.
|
|
|
|
|
|
|
|
if pusher.kind.is_none() { |
|
|
|
|
|
|
|
return self |
|
|
|
|
|
|
|
.senderkey_pusher |
|
|
|
|
|
|
|
.remove(key) |
|
|
|
|
|
|
|
.map(|_| ()) |
|
|
|
|
|
|
|
.map_err(Into::into); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
self.senderkey_pusher.insert( |
|
|
|
self.senderkey_pusher.insert( |
|
|
|
key, |
|
|
|
key, |
|
|
|
&*serde_json::to_string(&pusher).expect("Pusher is valid JSON string"), |
|
|
|
&*serde_json::to_string(&pusher).expect("Pusher is valid JSON string"), |
|
|
|
@ -46,15 +64,95 @@ impl PushData { |
|
|
|
Ok(serde_json::from_slice(&*push) |
|
|
|
Ok(serde_json::from_slice(&*push) |
|
|
|
.map_err(|_| Error::bad_database("Invalid Pusher in db."))?) |
|
|
|
.map_err(|_| Error::bad_database("Invalid Pusher in db."))?) |
|
|
|
}) |
|
|
|
}) |
|
|
|
.collect::<Result<Vec<_>>>() |
|
|
|
.collect() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub async fn send_request<T: OutgoingRequest>( |
|
|
|
|
|
|
|
globals: &crate::database::globals::Globals, |
|
|
|
|
|
|
|
destination: &str, |
|
|
|
|
|
|
|
request: T, |
|
|
|
|
|
|
|
) -> Result<T::IncomingResponse> |
|
|
|
|
|
|
|
where |
|
|
|
|
|
|
|
T: Debug, |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
let destination = destination.replace("/_matrix/push/v1/notify", ""); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let http_request = request |
|
|
|
|
|
|
|
.try_into_http_request(&destination, Some("")) |
|
|
|
|
|
|
|
.map_err(|e| { |
|
|
|
|
|
|
|
warn!("Failed to find destination {}: {}", destination, e); |
|
|
|
|
|
|
|
Error::BadServerResponse("Invalid destination") |
|
|
|
|
|
|
|
})?; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let mut reqwest_request = reqwest::Request::try_from(http_request) |
|
|
|
|
|
|
|
.expect("all http requests are valid reqwest requests"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// TODO: we could keep this very short and let expo backoff do it's thing...
|
|
|
|
|
|
|
|
*reqwest_request.timeout_mut() = Some(Duration::from_secs(5)); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let url = reqwest_request.url().clone(); |
|
|
|
|
|
|
|
let reqwest_response = globals.reqwest_client().execute(reqwest_request).await; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Because reqwest::Response -> http::Response is complicated:
|
|
|
|
|
|
|
|
match reqwest_response { |
|
|
|
|
|
|
|
Ok(mut reqwest_response) => { |
|
|
|
|
|
|
|
let status = reqwest_response.status(); |
|
|
|
|
|
|
|
let mut http_response = http::Response::builder().status(status); |
|
|
|
|
|
|
|
let headers = http_response.headers_mut().unwrap(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (k, v) in reqwest_response.headers_mut().drain() { |
|
|
|
|
|
|
|
if let Some(key) = k { |
|
|
|
|
|
|
|
headers.insert(key, v); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let status = reqwest_response.status(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let body = reqwest_response |
|
|
|
|
|
|
|
.bytes() |
|
|
|
|
|
|
|
.await |
|
|
|
|
|
|
|
.unwrap_or_else(|e| { |
|
|
|
|
|
|
|
warn!("server error {}", e); |
|
|
|
|
|
|
|
Vec::new().into() |
|
|
|
|
|
|
|
}) // TODO: handle timeout
|
|
|
|
|
|
|
|
.into_iter() |
|
|
|
|
|
|
|
.collect::<Vec<_>>(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if status != 200 { |
|
|
|
|
|
|
|
info!( |
|
|
|
|
|
|
|
"Push gateway returned bad response {} {}\n{}\n{:?}", |
|
|
|
|
|
|
|
destination, |
|
|
|
|
|
|
|
status, |
|
|
|
|
|
|
|
url, |
|
|
|
|
|
|
|
crate::utils::string_from_bytes(&body) |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let response = T::IncomingResponse::try_from( |
|
|
|
|
|
|
|
http_response |
|
|
|
|
|
|
|
.body(body) |
|
|
|
|
|
|
|
.expect("reqwest body is valid http body"), |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
response.map_err(|_| { |
|
|
|
|
|
|
|
info!( |
|
|
|
|
|
|
|
"Push gateway returned invalid response bytes {}\n{}", |
|
|
|
|
|
|
|
destination, url |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
Error::BadServerResponse("Push gateway returned bad response.") |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
Err(e) => Err(e.into()), |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pub async fn send_push_notice( |
|
|
|
pub async fn send_push_notice( |
|
|
|
user: &UserId, |
|
|
|
user: &UserId, |
|
|
|
pusher: &Pusher, |
|
|
|
unread: UInt, |
|
|
|
|
|
|
|
pushers: &[Pusher], |
|
|
|
ruleset: Ruleset, |
|
|
|
ruleset: Ruleset, |
|
|
|
pdu: &PduEvent, |
|
|
|
pdu: &PduEvent, |
|
|
|
|
|
|
|
db: &Database, |
|
|
|
) -> Result<()> { |
|
|
|
) -> Result<()> { |
|
|
|
for rule in ruleset.into_iter() { |
|
|
|
for rule in ruleset.into_iter() { |
|
|
|
// TODO: can actions contain contradictory Actions
|
|
|
|
// TODO: can actions contain contradictory Actions
|
|
|
|
@ -69,8 +167,44 @@ pub async fn send_push_notice( |
|
|
|
|
|
|
|
|
|
|
|
match rule.rule_id.as_str() { |
|
|
|
match rule.rule_id.as_str() { |
|
|
|
".m.rule.master" => {} |
|
|
|
".m.rule.master" => {} |
|
|
|
".m.rule.suppress_notices" => {} |
|
|
|
".m.rule.suppress_notices" => { |
|
|
|
".m.rule.invite_for_me" => {} |
|
|
|
if pdu.kind == EventType::RoomMessage |
|
|
|
|
|
|
|
&& pdu |
|
|
|
|
|
|
|
.content |
|
|
|
|
|
|
|
.get("msgtype") |
|
|
|
|
|
|
|
.map_or(false, |ty| ty == "m.notice") |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
let tweaks = rule |
|
|
|
|
|
|
|
.actions |
|
|
|
|
|
|
|
.iter() |
|
|
|
|
|
|
|
.filter_map(|a| match a { |
|
|
|
|
|
|
|
Action::SetTweak(tweak) => Some(tweak.clone()), |
|
|
|
|
|
|
|
_ => None, |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
.collect::<Vec<_>>(); |
|
|
|
|
|
|
|
send_notice(unread, pushers, tweaks, pdu, db).await?; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
".m.rule.invite_for_me" => { |
|
|
|
|
|
|
|
if let EventType::RoomMember = &pdu.kind { |
|
|
|
|
|
|
|
if pdu.state_key.as_deref() == Some(user.as_str()) |
|
|
|
|
|
|
|
&& serde_json::from_value::<MemberEventContent>(pdu.content.clone()) |
|
|
|
|
|
|
|
.map_err(|_| Error::bad_database("PDU contained bad message content"))? |
|
|
|
|
|
|
|
.membership |
|
|
|
|
|
|
|
== MembershipState::Invite |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
let tweaks = rule |
|
|
|
|
|
|
|
.actions |
|
|
|
|
|
|
|
.iter() |
|
|
|
|
|
|
|
.filter_map(|a| match a { |
|
|
|
|
|
|
|
Action::SetTweak(tweak) => Some(tweak.clone()), |
|
|
|
|
|
|
|
_ => None, |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
.collect::<Vec<_>>(); |
|
|
|
|
|
|
|
send_notice(unread, pushers, tweaks, pdu, db).await?; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
".m.rule.member_event" => { |
|
|
|
".m.rule.member_event" => { |
|
|
|
if let EventType::RoomMember = &pdu.kind { |
|
|
|
if let EventType::RoomMember = &pdu.kind { |
|
|
|
// TODO use this?
|
|
|
|
// TODO use this?
|
|
|
|
@ -88,7 +222,17 @@ pub async fn send_push_notice( |
|
|
|
json.to_string().contains(pattern) |
|
|
|
json.to_string().contains(pattern) |
|
|
|
} |
|
|
|
} |
|
|
|
_ => false, |
|
|
|
_ => false, |
|
|
|
}) {} |
|
|
|
}) { |
|
|
|
|
|
|
|
let tweaks = rule |
|
|
|
|
|
|
|
.actions |
|
|
|
|
|
|
|
.iter() |
|
|
|
|
|
|
|
.filter_map(|a| match a { |
|
|
|
|
|
|
|
Action::SetTweak(tweak) => Some(tweak.clone()), |
|
|
|
|
|
|
|
_ => None, |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
.collect::<Vec<_>>(); |
|
|
|
|
|
|
|
send_notice(unread, pushers, tweaks, pdu, db).await?; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
@ -103,13 +247,71 @@ pub async fn send_push_notice( |
|
|
|
&msg_content |
|
|
|
&msg_content |
|
|
|
{ |
|
|
|
{ |
|
|
|
if body.contains(user.localpart()) { |
|
|
|
if body.contains(user.localpart()) { |
|
|
|
send_notice(user, &pusher, &pdu).await?; |
|
|
|
let tweaks = rule |
|
|
|
|
|
|
|
.actions |
|
|
|
|
|
|
|
.iter() |
|
|
|
|
|
|
|
.filter_map(|a| match a { |
|
|
|
|
|
|
|
Action::SetTweak(tweak) => Some(tweak.clone()), |
|
|
|
|
|
|
|
_ => None, |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
.collect::<Vec<_>>(); |
|
|
|
|
|
|
|
send_notice(unread, pushers, tweaks, pdu, db).await?; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
".m.rule.tombstone" => { |
|
|
|
|
|
|
|
if pdu.kind == EventType::RoomTombstone && pdu.state_key.as_deref() == Some("") { |
|
|
|
|
|
|
|
let tweaks = rule |
|
|
|
|
|
|
|
.actions |
|
|
|
|
|
|
|
.iter() |
|
|
|
|
|
|
|
.filter_map(|a| match a { |
|
|
|
|
|
|
|
Action::SetTweak(tweak) => Some(tweak.clone()), |
|
|
|
|
|
|
|
_ => None, |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
.collect::<Vec<_>>(); |
|
|
|
|
|
|
|
send_notice(unread, pushers, tweaks, pdu, db).await?; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
".m.rule.roomnotif" => { |
|
|
|
|
|
|
|
if let EventType::RoomMessage = &pdu.kind { |
|
|
|
|
|
|
|
let msg_content = |
|
|
|
|
|
|
|
serde_json::from_value::<MessageEventContent>(pdu.content.clone()) |
|
|
|
|
|
|
|
.map_err(|_| { |
|
|
|
|
|
|
|
Error::bad_database("PDU contained bad message content") |
|
|
|
|
|
|
|
})?; |
|
|
|
|
|
|
|
if let MessageEventContent::Text(TextMessageEventContent { body, .. }) = |
|
|
|
|
|
|
|
&msg_content |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
let power_level_cmp = |pl: PowerLevelsEventContent| { |
|
|
|
|
|
|
|
&pl.notifications.room |
|
|
|
|
|
|
|
<= pl.users.get(&pdu.sender).unwrap_or(&ruma::int!(0)) |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
let deserialize = |pl: PduEvent| { |
|
|
|
|
|
|
|
serde_json::from_value::<PowerLevelsEventContent>(pl.content).ok() |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
if body.contains("@room") |
|
|
|
|
|
|
|
&& db |
|
|
|
|
|
|
|
.rooms |
|
|
|
|
|
|
|
.room_state_get(&pdu.room_id, &EventType::RoomPowerLevels, "")? |
|
|
|
|
|
|
|
.map(|(_, pl)| pl) |
|
|
|
|
|
|
|
.map(deserialize) |
|
|
|
|
|
|
|
.flatten() |
|
|
|
|
|
|
|
.map_or(false, power_level_cmp) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
let tweaks = rule |
|
|
|
|
|
|
|
.actions |
|
|
|
|
|
|
|
.iter() |
|
|
|
|
|
|
|
.filter_map(|a| match a { |
|
|
|
|
|
|
|
Action::SetTweak(tweak) => Some(tweak.clone()), |
|
|
|
|
|
|
|
_ => None, |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
.collect::<Vec<_>>(); |
|
|
|
|
|
|
|
send_notice(unread, pushers, tweaks, pdu, db).await?; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
".m.rule.tombstone" => {} |
|
|
|
|
|
|
|
".m.rule.roomnotif" => {} |
|
|
|
|
|
|
|
".m.rule.contains_user_name" => { |
|
|
|
".m.rule.contains_user_name" => { |
|
|
|
if let EventType::RoomMessage = &pdu.kind { |
|
|
|
if let EventType::RoomMessage = &pdu.kind { |
|
|
|
let msg_content = |
|
|
|
let msg_content = |
|
|
|
@ -121,28 +323,180 @@ pub async fn send_push_notice( |
|
|
|
&msg_content |
|
|
|
&msg_content |
|
|
|
{ |
|
|
|
{ |
|
|
|
if body.contains(user.localpart()) { |
|
|
|
if body.contains(user.localpart()) { |
|
|
|
send_notice(user, &pusher, &pdu).await?; |
|
|
|
let tweaks = rule |
|
|
|
|
|
|
|
.actions |
|
|
|
|
|
|
|
.iter() |
|
|
|
|
|
|
|
.filter_map(|a| match a { |
|
|
|
|
|
|
|
Action::SetTweak(tweak) => Some(tweak.clone()), |
|
|
|
|
|
|
|
_ => None, |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
.collect::<Vec<_>>(); |
|
|
|
|
|
|
|
send_notice(unread, pushers, tweaks, pdu, db).await?; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
".m.rule.call" => { |
|
|
|
|
|
|
|
if pdu.kind == EventType::CallInvite { |
|
|
|
|
|
|
|
let tweaks = rule |
|
|
|
|
|
|
|
.actions |
|
|
|
|
|
|
|
.iter() |
|
|
|
|
|
|
|
.filter_map(|a| match a { |
|
|
|
|
|
|
|
Action::SetTweak(tweak) => Some(tweak.clone()), |
|
|
|
|
|
|
|
_ => None, |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
.collect::<Vec<_>>(); |
|
|
|
|
|
|
|
send_notice(unread, pushers, tweaks, pdu, db).await?; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
".m.rule.encrypted_room_one_to_one" => { |
|
|
|
|
|
|
|
if db.rooms.room_members(&pdu.room_id).count() == 2 |
|
|
|
|
|
|
|
&& pdu.kind == EventType::RoomEncrypted |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
let tweaks = rule |
|
|
|
|
|
|
|
.actions |
|
|
|
|
|
|
|
.iter() |
|
|
|
|
|
|
|
.filter_map(|a| match a { |
|
|
|
|
|
|
|
Action::SetTweak(tweak) => Some(tweak.clone()), |
|
|
|
|
|
|
|
_ => None, |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
.collect::<Vec<_>>(); |
|
|
|
|
|
|
|
send_notice(unread, pushers, tweaks, pdu, db).await?; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
".m.rule.room_one_to_one" => { |
|
|
|
|
|
|
|
if db.rooms.room_members(&pdu.room_id).count() == 2 |
|
|
|
|
|
|
|
&& pdu.kind == EventType::RoomMessage |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
let tweaks = rule |
|
|
|
|
|
|
|
.actions |
|
|
|
|
|
|
|
.iter() |
|
|
|
|
|
|
|
.filter_map(|a| match a { |
|
|
|
|
|
|
|
Action::SetTweak(tweak) => Some(tweak.clone()), |
|
|
|
|
|
|
|
_ => None, |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
.collect::<Vec<_>>(); |
|
|
|
|
|
|
|
send_notice(unread, pushers, tweaks, pdu, db).await?; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
".m.rule.message" => { |
|
|
|
|
|
|
|
if pdu.kind == EventType::RoomMessage { |
|
|
|
|
|
|
|
let tweaks = rule |
|
|
|
|
|
|
|
.actions |
|
|
|
|
|
|
|
.iter() |
|
|
|
|
|
|
|
.filter_map(|a| match a { |
|
|
|
|
|
|
|
Action::SetTweak(tweak) => Some(tweak.clone()), |
|
|
|
|
|
|
|
_ => None, |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
.collect::<Vec<_>>(); |
|
|
|
|
|
|
|
send_notice(unread, pushers, tweaks, pdu, db).await?; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
".m.rule.encrypted" => { |
|
|
|
|
|
|
|
if pdu.kind == EventType::RoomEncrypted { |
|
|
|
|
|
|
|
let tweaks = rule |
|
|
|
|
|
|
|
.actions |
|
|
|
|
|
|
|
.iter() |
|
|
|
|
|
|
|
.filter_map(|a| match a { |
|
|
|
|
|
|
|
Action::SetTweak(tweak) => Some(tweak.clone()), |
|
|
|
|
|
|
|
_ => None, |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
.collect::<Vec<_>>(); |
|
|
|
|
|
|
|
send_notice(unread, pushers, tweaks, pdu, db).await?; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
".m.rule.call" => {} |
|
|
|
|
|
|
|
".m.rule.encrypted_room_one_to_one" => {} |
|
|
|
|
|
|
|
".m.rule.room_one_to_one" => {} |
|
|
|
|
|
|
|
".m.rule.message" => {} |
|
|
|
|
|
|
|
".m.rule.encrypted" => {} |
|
|
|
|
|
|
|
_ => {} |
|
|
|
_ => {} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
Ok(()) |
|
|
|
Ok(()) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
async fn send_notice(_sender: &UserId, pusher: &Pusher, _event: &PduEvent) -> Result<()> { |
|
|
|
async fn send_notice( |
|
|
|
if let Some(PusherKind::Http) = pusher.kind { |
|
|
|
unread: UInt, |
|
|
|
log::error!("YAHOOO"); |
|
|
|
pushers: &[Pusher], |
|
|
|
|
|
|
|
tweaks: Vec<Tweak>, |
|
|
|
|
|
|
|
event: &PduEvent, |
|
|
|
|
|
|
|
db: &Database, |
|
|
|
|
|
|
|
) -> Result<()> { |
|
|
|
|
|
|
|
let (http, _emails): (Vec<&Pusher>, _) = pushers |
|
|
|
|
|
|
|
.iter() |
|
|
|
|
|
|
|
.partition(|pusher| pusher.kind == Some(PusherKind::Http)); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// TODO:
|
|
|
|
|
|
|
|
// Two problems with this
|
|
|
|
|
|
|
|
// 1. if "event_id_only" is the only format kind it seems we should never add more info
|
|
|
|
|
|
|
|
// 2. can pusher/devices have conflicting formats
|
|
|
|
|
|
|
|
for pusher in http { |
|
|
|
|
|
|
|
let event_id_only = pusher.data.format == Some(PushFormat::EventIdOnly); |
|
|
|
|
|
|
|
let url = if let Some(url) = pusher.data.url.as_ref() { |
|
|
|
|
|
|
|
url |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
// EMAIL
|
|
|
|
error!("Http Pusher must have URL specified."); |
|
|
|
todo!("send an email") |
|
|
|
continue; |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let mut device = Device::new(pusher.app_id.clone(), pusher.pushkey.clone()); |
|
|
|
|
|
|
|
device.data = Some(pusher.data.clone()); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// this is not done if "event_id_only" is the format
|
|
|
|
|
|
|
|
device.tweaks = tweaks.clone(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let d = &[device]; |
|
|
|
|
|
|
|
let mut notifi = Notification::new(d); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
notifi.prio = NotificationPriority::Low; |
|
|
|
|
|
|
|
notifi.event_id = Some(&event.event_id); |
|
|
|
|
|
|
|
notifi.room_id = Some(&event.room_id); |
|
|
|
|
|
|
|
// TODO: missed calls
|
|
|
|
|
|
|
|
notifi.counts = NotificationCounts::new(unread, uint!(0)); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if event.kind == EventType::RoomEncrypted |
|
|
|
|
|
|
|
|| tweaks |
|
|
|
|
|
|
|
.iter() |
|
|
|
|
|
|
|
.any(|t| matches!(t, Tweak::Highlight(true) | Tweak::Sound(_))) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
notifi.prio = NotificationPriority::High |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if event_id_only { |
|
|
|
|
|
|
|
// send_request(
|
|
|
|
|
|
|
|
// &db.globals,
|
|
|
|
|
|
|
|
// &url,
|
|
|
|
|
|
|
|
// send_event_notification::v1::Request::new(notifi),
|
|
|
|
|
|
|
|
// )
|
|
|
|
|
|
|
|
// .await?;
|
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
notifi.sender = Some(&event.sender); |
|
|
|
|
|
|
|
notifi.event_type = Some(&event.kind); |
|
|
|
|
|
|
|
notifi.content = serde_json::value::to_raw_value(&event.content).ok(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if event.kind == EventType::RoomMember { |
|
|
|
|
|
|
|
notifi.user_is_target = event.state_key.as_deref() == Some(event.sender.as_str()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let name = db.users.displayname(&event.sender)?; |
|
|
|
|
|
|
|
notifi.sender_display_name = name.as_deref(); |
|
|
|
|
|
|
|
let room_name = db |
|
|
|
|
|
|
|
.rooms |
|
|
|
|
|
|
|
.room_state_get(&event.room_id, &EventType::RoomName, "")? |
|
|
|
|
|
|
|
.map(|(_, pdu)| match pdu.content.get("name") { |
|
|
|
|
|
|
|
Some(serde_json::Value::String(s)) => Some(s.to_string()), |
|
|
|
|
|
|
|
_ => None, |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
.flatten(); |
|
|
|
|
|
|
|
notifi.room_name = room_name.as_deref(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
send_request( |
|
|
|
|
|
|
|
&db.globals, |
|
|
|
|
|
|
|
&url, |
|
|
|
|
|
|
|
send_event_notification::v1::Request::new(notifi), |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
.await?; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// TODO: email
|
|
|
|
|
|
|
|
// for email in emails {}
|
|
|
|
|
|
|
|
|
|
|
|
Ok(()) |
|
|
|
Ok(()) |
|
|
|
} |
|
|
|
} |
|
|
|
|