Skip to main content

tuwunel_api/client/push/
pushrules_rule.rs

1use axum::extract::State;
2use ruma::{
3	api::client::push::{delete_pushrule, get_pushrule, set_pushrule},
4	events::{GlobalAccountDataEventType, push_rules::PushRulesEvent},
5	push::{
6		InsertPushRuleError, PredefinedContentRuleId, PredefinedOverrideRuleId,
7		RemovePushRuleError,
8	},
9};
10use tuwunel_core::{Err, Result, err};
11
12use crate::Ruma;
13
14/// # `GET /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}`
15///
16/// Retrieves a single specified push rule for this user.
17pub(crate) async fn get_pushrule_route(
18	State(services): State<crate::State>,
19	body: Ruma<get_pushrule::v3::Request>,
20) -> Result<get_pushrule::v3::Response> {
21	let sender_user = body
22		.sender_user
23		.as_ref()
24		.expect("user is authenticated");
25
26	// remove old deprecated mentions push rules as per MSC4210
27	#[expect(deprecated)]
28	if body.rule_id.as_str() == PredefinedContentRuleId::ContainsUserName.as_str()
29		|| body.rule_id.as_str() == PredefinedOverrideRuleId::ContainsDisplayName.as_str()
30		|| body.rule_id.as_str() == PredefinedOverrideRuleId::RoomNotif.as_str()
31	{
32		return Err!(Request(NotFound("Push rule not found.")));
33	}
34
35	let event: PushRulesEvent = services
36		.account_data
37		.get_global(sender_user, GlobalAccountDataEventType::PushRules)
38		.await
39		.map_err(|_| err!(Request(NotFound("PushRules event not found."))))?;
40
41	let rule = event
42		.content
43		.global
44		.get(body.kind.clone(), &body.rule_id)
45		.map(Into::into);
46
47	if let Some(rule) = rule {
48		Ok(get_pushrule::v3::Response { rule })
49	} else {
50		Err!(Request(NotFound("Push rule not found.")))
51	}
52}
53
54/// # `PUT /_matrix/client/r0/pushrules/global/{kind}/{ruleId}`
55///
56/// Creates a single specified push rule for this user.
57pub(crate) async fn set_pushrule_route(
58	State(services): State<crate::State>,
59	body: Ruma<set_pushrule::v3::Request>,
60) -> Result<set_pushrule::v3::Response> {
61	let sender_user = body.sender_user();
62	let mut account_data: PushRulesEvent = services
63		.account_data
64		.get_global(sender_user, GlobalAccountDataEventType::PushRules)
65		.await
66		.map_err(|_| err!(Request(NotFound("PushRules event not found."))))?;
67
68	if let Err(error) = account_data.content.global.insert(
69		body.rule.clone(),
70		body.after.as_deref(),
71		body.before.as_deref(),
72	) {
73		use InsertPushRuleError::*;
74
75		return match error {
76			| ServerDefaultRuleId => Err!(Request(InvalidParam(
77				"Rule IDs starting with a dot are reserved for server-default rules."
78			))),
79			| RelativeToServerDefaultRule => Err!(Request(InvalidParam(
80				"Can't place a push rule relatively to a server-default rule."
81			))),
82			| BeforeHigherThanAfter => Err!(Request(InvalidParam(
83				"The before rule has a higher priority than the after rule."
84			))),
85			| InvalidRuleId =>
86				Err!(Request(InvalidParam("Rule ID containing invalid characters."))),
87
88			| UnknownRuleId =>
89				Err!(Request(NotFound("The before or after rule could not be found."))),
90
91			| _ => Err!(Request(InvalidParam("Invalid data."))),
92		};
93	}
94
95	let ty = GlobalAccountDataEventType::PushRules;
96	services
97		.account_data
98		.update(None, sender_user, ty.to_string().into(), &serde_json::to_value(account_data)?)
99		.await?;
100
101	Ok(set_pushrule::v3::Response {})
102}
103
104/// # `DELETE /_matrix/client/r0/pushrules/global/{kind}/{ruleId}`
105///
106/// Deletes a single specified push rule for this user.
107pub(crate) async fn delete_pushrule_route(
108	State(services): State<crate::State>,
109	body: Ruma<delete_pushrule::v3::Request>,
110) -> Result<delete_pushrule::v3::Response> {
111	let sender_user = body.sender_user();
112
113	let mut account_data: PushRulesEvent = services
114		.account_data
115		.get_global(sender_user, GlobalAccountDataEventType::PushRules)
116		.await
117		.map_err(|_| err!(Request(NotFound("PushRules event not found."))))?;
118
119	if let Err(error) = account_data
120		.content
121		.global
122		.remove(body.kind.clone(), &body.rule_id)
123	{
124		return match error {
125			| RemovePushRuleError::ServerDefault =>
126				Err!(Request(InvalidParam("Cannot delete a server-default pushrule."))),
127
128			| RemovePushRuleError::NotFound => Err!(Request(NotFound("Push rule not found."))),
129
130			| _ => Err!(Request(InvalidParam("Invalid data."))),
131		};
132	}
133
134	let ty = GlobalAccountDataEventType::PushRules;
135	services
136		.account_data
137		.update(None, sender_user, ty.to_string().into(), &serde_json::to_value(account_data)?)
138		.await?;
139
140	Ok(delete_pushrule::v3::Response {})
141}