diff --git a/lib/src/Room.dart b/lib/src/Room.dart index c44fd85..dda8216 100644 --- a/lib/src/Room.dart +++ b/lib/src/Room.dart @@ -852,4 +852,107 @@ class Room { return ownPowerLevel >= getState("m.room.power_levels").content["events"][eventType]; } + + /// Returns the [PushRuleState] for this room, based on the m.push_rules stored in + /// the account_data. + PushRuleState get pushRuleState { + if (!client.accountData.containsKey("m.push_rules") || + !(client.accountData["m.push_rules"].content["global"] is Map)) + return PushRuleState.notify; + final Map globalPushRules = + client.accountData["m.push_rules"].content["global"]; + if (globalPushRules == null) return PushRuleState.notify; + + if (globalPushRules["override"] is List) { + for (var i = 0; i < globalPushRules["override"].length; i++) { + if (globalPushRules["override"][i]["rule_id"] == id) { + if (globalPushRules["override"][i]["actions"] + .indexOf("dont_notify") != + -1) { + return PushRuleState.dont_notify; + } + break; + } + } + } + + if (globalPushRules["room"] is List) { + for (var i = 0; i < globalPushRules["room"].length; i++) { + if (globalPushRules["room"][i]["rule_id"] == id) { + if (globalPushRules["room"][i]["actions"].indexOf("dont_notify") != + -1) { + return PushRuleState.mentions_only; + } + break; + } + } + } + + return PushRuleState.notify; + } + + /// Sends a request to the homeserver to set the [PushRuleState] for this room. + /// Returns ErrorResponse if something goes wrong. + Future setPushRuleState(PushRuleState newState) async { + if (newState == pushRuleState) return null; + dynamic resp; + switch (newState) { + // All push notifications should be sent to the user + case PushRuleState.notify: + if (pushRuleState == PushRuleState.dont_notify) + resp = await client.connection.jsonRequest( + type: HTTPType.DELETE, + action: "/client/r0/pushrules/global/override/$id", + data: {}); + else if (pushRuleState == PushRuleState.mentions_only) + resp = await client.connection.jsonRequest( + type: HTTPType.DELETE, + action: "/client/r0/pushrules/global/room/$id", + data: {}); + break; + // Only when someone mentions the user, a push notification should be sent + case PushRuleState.mentions_only: + if (pushRuleState == PushRuleState.dont_notify) { + resp = await client.connection.jsonRequest( + type: HTTPType.DELETE, + action: "/client/r0/pushrules/global/override/$id", + data: {}); + if (resp == ErrorResponse) return resp; + resp = await client.connection.jsonRequest( + type: HTTPType.PUT, + action: "/client/r0/pushrules/global/room/$id", + data: { + "actions": ["dont_notify"] + }); + } else if (pushRuleState == PushRuleState.notify) + resp = await client.connection.jsonRequest( + type: HTTPType.PUT, + action: "/client/r0/pushrules/global/room/$id", + data: { + "actions": ["dont_notify"] + }); + break; + // No push notification should be ever sent for this room. + case PushRuleState.dont_notify: + if (pushRuleState == PushRuleState.mentions_only) { + resp = await client.connection.jsonRequest( + type: HTTPType.DELETE, + action: "/client/r0/pushrules/global/room/$id", + data: {}); + if (resp == ErrorResponse) return resp; + } + resp = await client.connection.jsonRequest( + type: HTTPType.PUT, + action: "/client/r0/pushrules/global/override/$id", + data: { + "actions": ["dont_notify"], + "conditions": [ + {"key": "room_id", "kind": "event_match", "pattern": id} + ] + }); + } + return resp; + } } + +enum PushRuleState { notify, mentions_only, dont_notify } diff --git a/lib/src/utils/PushRule.dart b/lib/src/utils/PushRule.dart new file mode 100644 index 0000000..7a404ca --- /dev/null +++ b/lib/src/utils/PushRule.dart @@ -0,0 +1,64 @@ +class PushRule { + final String ruleId; + final bool isDefault; + final bool enabled; + final List conditions; + final List actions; + + PushRule( + {this.ruleId, + this.isDefault, + this.enabled, + this.conditions, + this.actions}); + + PushRule.fromJson(Map json) + : ruleId = json['rule_id'], + isDefault = json['is_default'], + enabled = json['enabled'], + conditions = _getConditionsFromJson(json['conditions']), + actions = json['actions']; + + Map toJson() { + final Map data = new Map(); + data['rule_id'] = this.ruleId; + data['is_default'] = this.isDefault; + data['enabled'] = this.enabled; + if (this.conditions != null) { + data['conditions'] = this.conditions.map((v) => v.toJson()).toList(); + } + data['actions'] = this.actions; + return data; + } + + static List _getConditionsFromJson(List json) { + List conditions = []; + if (json == null) return conditions; + for (int i = 0; i < json.length; i++) { + conditions.add(Conditions.fromJson(json[i])); + } + return conditions; + } +} + +class Conditions { + String key; + String kind; + String pattern; + + Conditions({this.key, this.kind, this.pattern}); + + Conditions.fromJson(Map json) { + key = json['key']; + kind = json['kind']; + pattern = json['pattern']; + } + + Map toJson() { + final Map data = new Map(); + data['key'] = this.key; + data['kind'] = this.kind; + data['pattern'] = this.pattern; + return data; + } +} diff --git a/test/Client_test.dart b/test/Client_test.dart index fb0be17..3e1d74e 100644 --- a/test/Client_test.dart +++ b/test/Client_test.dart @@ -120,7 +120,7 @@ void main() { expect(firstSync, true); expect(sync["next_batch"] == matrix.prevBatch, true); - expect(matrix.accountData.length, 2); + expect(matrix.accountData.length, 3); expect(matrix.getDirectChatFromUserId("@bob:example.com"), "!726s6s6q:example.com"); expect(matrix.roomList.rooms[1].directChatMatrixID, "@bob:example.com"); @@ -147,7 +147,7 @@ void main() { expect( matrix.presences["@alice:example.com"].presence, PresenceType.online); expect(presenceCounter, 1); - expect(accountDataCounter, 2); + expect(accountDataCounter, 3); matrix.connection.onEvent.add( EventUpdate( @@ -283,13 +283,16 @@ void main() { List eventUpdateList = await userUpdateListFuture; - expect(eventUpdateList.length, 4); + expect(eventUpdateList.length, 5); - expect(eventUpdateList[0].eventType == "m.presence", true); - expect(eventUpdateList[0].type == "presence", true); + expect(eventUpdateList[0].eventType, "m.presence"); + expect(eventUpdateList[0].type, "presence"); - expect(eventUpdateList[1].eventType == "org.example.custom.config", true); - expect(eventUpdateList[1].type == "account_data", true); + expect(eventUpdateList[1].eventType, "m.push_rules"); + expect(eventUpdateList[1].type, "account_data"); + + expect(eventUpdateList[2].eventType, "org.example.custom.config"); + expect(eventUpdateList[2].type, "account_data"); }); test('Login', () async { diff --git a/test/FakeMatrixApi.dart b/test/FakeMatrixApi.dart index 982f4cf..ea3e275 100644 --- a/test/FakeMatrixApi.dart +++ b/test/FakeMatrixApi.dart @@ -75,6 +75,173 @@ class FakeMatrixApi extends MockClient { }, "account_data": { "events": [ + { + "content": { + "global": { + "content": [ + { + "actions": [ + "notify", + {"set_tweak": "sound", "value": "default"}, + {"set_tweak": "highlight"} + ], + "default": true, + "enabled": true, + "pattern": "alice", + "rule_id": ".m.rule.contains_user_name" + } + ], + "override": [ + { + "actions": ["dont_notify"], + "conditions": [], + "default": true, + "enabled": false, + "rule_id": ".m.rule.master" + }, + { + "actions": ["dont_notify"], + "conditions": [ + { + "key": "content.msgtype", + "kind": "event_match", + "pattern": "m.notice" + } + ], + "default": true, + "enabled": true, + "rule_id": ".m.rule.suppress_notices" + } + ], + "room": [ + { + "actions": ["dont_notify"], + "conditions": [ + { + "key": "room_id", + "kind": "event_match", + "pattern": "!localpart:server.abc", + } + ], + "default": true, + "enabled": true, + "rule_id": "!localpart:server.abc" + } + ], + "sender": [], + "underride": [ + { + "actions": [ + "notify", + {"set_tweak": "sound", "value": "ring"}, + {"set_tweak": "highlight", "value": false} + ], + "conditions": [ + { + "key": "type", + "kind": "event_match", + "pattern": "m.call.invite" + } + ], + "default": true, + "enabled": true, + "rule_id": ".m.rule.call" + }, + { + "actions": [ + "notify", + {"set_tweak": "sound", "value": "default"}, + {"set_tweak": "highlight"} + ], + "conditions": [ + {"kind": "contains_display_name"} + ], + "default": true, + "enabled": true, + "rule_id": ".m.rule.contains_display_name" + }, + { + "actions": [ + "notify", + {"set_tweak": "sound", "value": "default"}, + {"set_tweak": "highlight", "value": false} + ], + "conditions": [ + {"is": "2", "kind": "room_member_count"}, + { + "key": "type", + "kind": "event_match", + "pattern": "m.room.message" + } + ], + "default": true, + "enabled": true, + "rule_id": ".m.rule.room_one_to_one" + }, + { + "actions": [ + "notify", + {"set_tweak": "sound", "value": "default"}, + {"set_tweak": "highlight", "value": false} + ], + "conditions": [ + { + "key": "type", + "kind": "event_match", + "pattern": "m.room.member" + }, + { + "key": "content.membership", + "kind": "event_match", + "pattern": "invite" + }, + { + "key": "state_key", + "kind": "event_match", + "pattern": "@alice:example.com" + } + ], + "default": true, + "enabled": true, + "rule_id": ".m.rule.invite_for_me" + }, + { + "actions": [ + "notify", + {"set_tweak": "highlight", "value": false} + ], + "conditions": [ + { + "key": "type", + "kind": "event_match", + "pattern": "m.room.member" + } + ], + "default": true, + "enabled": true, + "rule_id": ".m.rule.member_event" + }, + { + "actions": [ + "notify", + {"set_tweak": "highlight", "value": false} + ], + "conditions": [ + { + "key": "type", + "kind": "event_match", + "pattern": "m.room.message" + } + ], + "default": true, + "enabled": true, + "rule_id": ".m.rule.message" + } + ] + } + }, + "type": "m.push_rules" + }, { "type": "org.example.custom.config", "content": {"custom_config_key": "custom_config_value"} diff --git a/test/Room_test.dart b/test/Room_test.dart index 7c55d29..0eaf4f8 100644 --- a/test/Room_test.dart +++ b/test/Room_test.dart @@ -335,5 +335,12 @@ void main() { await room.sendFileEvent(testFile, "m.file", txid: "testtxid"); expect(resp, "42"); }); + + test('pushRuleState', () async { + expect(room.pushRuleState, PushRuleState.mentions_only); + matrix.accountData["m.push_rules"].content["global"]["override"] + .add(matrix.accountData["m.push_rules"].content["global"]["room"][0]); + expect(room.pushRuleState, PushRuleState.dont_notify); + }); }); }