commit
						b102a87348
					
				|  | @ -32,7 +32,7 @@ class Pusher(object): | |||
|     INITIAL_BACKOFF = 1000 | ||||
|     MAX_BACKOFF = 60 * 60 * 1000 | ||||
|     GIVE_UP_AFTER = 24 * 60 * 60 * 1000 | ||||
|     DEFAULT_ACTIONS = ['notify'] | ||||
|     DEFAULT_ACTIONS = ['dont-notify'] | ||||
| 
 | ||||
|     INEQUALITY_EXPR = re.compile("^([=<>]*)([0-9]*)$") | ||||
| 
 | ||||
|  | @ -72,16 +72,14 @@ class Pusher(object): | |||
|             # let's assume you probably know about messages you sent yourself | ||||
|             defer.returnValue(['dont_notify']) | ||||
| 
 | ||||
|         if ev['type'] == 'm.room.member': | ||||
|             if ev['state_key'] != self.user_name: | ||||
|                 defer.returnValue(['dont_notify']) | ||||
| 
 | ||||
|         rawrules = yield self.store.get_push_rules_for_user_name(self.user_name) | ||||
|         rawrules = yield self.store.get_push_rules_for_user(self.user_name) | ||||
| 
 | ||||
|         for r in rawrules: | ||||
|             r['conditions'] = json.loads(r['conditions']) | ||||
|             r['actions'] = json.loads(r['actions']) | ||||
| 
 | ||||
|         enabled_map = yield self.store.get_push_rules_enabled_for_user(self.user_name) | ||||
| 
 | ||||
|         user = UserID.from_string(self.user_name) | ||||
| 
 | ||||
|         rules = baserules.list_with_base_rules(rawrules, user) | ||||
|  | @ -107,6 +105,8 @@ class Pusher(object): | |||
|             room_member_count += 1 | ||||
| 
 | ||||
|         for r in rules: | ||||
|             if r['rule_id'] in enabled_map and not enabled_map[r['rule_id']]: | ||||
|                 continue | ||||
|             matches = True | ||||
| 
 | ||||
|             conditions = r['conditions'] | ||||
|  | @ -117,7 +117,11 @@ class Pusher(object): | |||
|                     ev, c, display_name=my_display_name, | ||||
|                     room_member_count=room_member_count | ||||
|                 ) | ||||
|             # ignore rules with no actions (we have an explict 'dont_notify' | ||||
|             logger.debug( | ||||
|                 "Rule %s %s", | ||||
|                 r['rule_id'], "matches" if matches else "doesn't match" | ||||
|             ) | ||||
|             # ignore rules with no actions (we have an explict 'dont_notify') | ||||
|             if len(actions) == 0: | ||||
|                 logger.warn( | ||||
|                     "Ignoring rule id %s with no actions for user %s" % | ||||
|  |  | |||
|  | @ -32,12 +32,14 @@ def make_base_rules(user, kind): | |||
| 
 | ||||
|     if kind == 'override': | ||||
|         rules = make_base_override_rules() | ||||
|     elif kind == 'underride': | ||||
|         rules = make_base_underride_rules(user) | ||||
|     elif kind == 'content': | ||||
|         rules = make_base_content_rules(user) | ||||
| 
 | ||||
|     for r in rules: | ||||
|         r['priority_class'] = PRIORITY_CLASS_MAP[kind] | ||||
|         r['default'] = True | ||||
|         r['default'] = True  # Deprecated, left for backwards compat | ||||
| 
 | ||||
|     return rules | ||||
| 
 | ||||
|  | @ -45,6 +47,7 @@ def make_base_rules(user, kind): | |||
| def make_base_content_rules(user): | ||||
|     return [ | ||||
|         { | ||||
|             'rule_id': 'global/content/.m.rule.contains_user_name', | ||||
|             'conditions': [ | ||||
|                 { | ||||
|                     'kind': 'event_match', | ||||
|  | @ -57,6 +60,8 @@ def make_base_content_rules(user): | |||
|                 { | ||||
|                     'set_tweak': 'sound', | ||||
|                     'value': 'default', | ||||
|                 }, { | ||||
|                     'set_tweak': 'highlight' | ||||
|                 } | ||||
|             ] | ||||
|         }, | ||||
|  | @ -66,6 +71,20 @@ def make_base_content_rules(user): | |||
| def make_base_override_rules(): | ||||
|     return [ | ||||
|         { | ||||
|             'rule_id': 'global/underride/.m.rule.suppress_notices', | ||||
|             'conditions': [ | ||||
|                 { | ||||
|                     'kind': 'event_match', | ||||
|                     'key': 'content.msgtype', | ||||
|                     'pattern': 'm.notice', | ||||
|                 } | ||||
|             ], | ||||
|             'actions': [ | ||||
|                 'dont-notify', | ||||
|             ] | ||||
|         }, | ||||
|         { | ||||
|             'rule_id': 'global/override/.m.rule.contains_display_name', | ||||
|             'conditions': [ | ||||
|                 { | ||||
|                     'kind': 'contains_display_name' | ||||
|  | @ -76,10 +95,13 @@ def make_base_override_rules(): | |||
|                 { | ||||
|                     'set_tweak': 'sound', | ||||
|                     'value': 'default' | ||||
|                 }, { | ||||
|                     'set_tweak': 'highlight' | ||||
|                 } | ||||
|             ] | ||||
|         }, | ||||
|         { | ||||
|             'rule_id': 'global/override/.m.rule.room_one_to_one', | ||||
|             'conditions': [ | ||||
|                 { | ||||
|                     'kind': 'room_member_count', | ||||
|  | @ -95,3 +117,86 @@ def make_base_override_rules(): | |||
|             ] | ||||
|         } | ||||
|     ] | ||||
| 
 | ||||
| 
 | ||||
| def make_base_underride_rules(user): | ||||
|     return [ | ||||
|         { | ||||
|             'rule_id': 'global/underride/.m.rule.invite_for_me', | ||||
|             'conditions': [ | ||||
|                 { | ||||
|                     'kind': 'event_match', | ||||
|                     'key': 'type', | ||||
|                     'pattern': 'm.room.member', | ||||
|                 }, | ||||
|                 { | ||||
|                     'kind': 'event_match', | ||||
|                     'key': 'content.membership', | ||||
|                     'pattern': 'invite', | ||||
|                 }, | ||||
|                 { | ||||
|                     'kind': 'event_match', | ||||
|                     'key': 'state_key', | ||||
|                     'pattern': user.to_string(), | ||||
|                 }, | ||||
|             ], | ||||
|             'actions': [ | ||||
|                 'notify', | ||||
|                 { | ||||
|                     'set_tweak': 'sound', | ||||
|                     'value': 'default' | ||||
|                 } | ||||
|             ] | ||||
|         }, | ||||
|         { | ||||
|             'rule_id': 'global/underride/.m.rule.member_event', | ||||
|             'conditions': [ | ||||
|                 { | ||||
|                     'kind': 'event_match', | ||||
|                     'key': 'type', | ||||
|                     'pattern': 'm.room.member', | ||||
|                 } | ||||
|             ], | ||||
|             'actions': [ | ||||
|                 'notify', | ||||
|             ] | ||||
|         }, | ||||
|         { | ||||
|             'rule_id': 'global/underride/.m.rule.message', | ||||
|             'conditions': [ | ||||
|                 { | ||||
|                     'kind': 'event_match', | ||||
|                     'key': 'type', | ||||
|                     'pattern': 'm.room.message', | ||||
|                 } | ||||
|             ], | ||||
|             'actions': [ | ||||
|                 'notify', | ||||
|             ] | ||||
|         }, | ||||
|         { | ||||
|             'rule_id': 'global/underride/.m.rule.call', | ||||
|             'conditions': [ | ||||
|                 { | ||||
|                     'kind': 'event_match', | ||||
|                     'key': 'type', | ||||
|                     'pattern': 'm.call.invite', | ||||
|                 } | ||||
|             ], | ||||
|             'actions': [ | ||||
|                 'notify', | ||||
|                 { | ||||
|                     'set_tweak': 'sound', | ||||
|                     'value': 'ring' | ||||
|                 } | ||||
|             ] | ||||
|         }, | ||||
|         { | ||||
|             'rule_id': 'global/underride/.m.rule.fallback', | ||||
|             'conditions': [ | ||||
|             ], | ||||
|             'actions': [ | ||||
|                 'notify', | ||||
|             ] | ||||
|         }, | ||||
|     ] | ||||
|  |  | |||
|  | @ -50,6 +50,10 @@ class PushRuleRestServlet(ClientV1RestServlet): | |||
| 
 | ||||
|         content = _parse_json(request) | ||||
| 
 | ||||
|         if 'attr' in spec: | ||||
|             self.set_rule_attr(user.to_string(), spec, content) | ||||
|             defer.returnValue((200, {})) | ||||
| 
 | ||||
|         try: | ||||
|             (conditions, actions) = _rule_tuple_from_request_object( | ||||
|                 spec['template'], | ||||
|  | @ -110,7 +114,7 @@ class PushRuleRestServlet(ClientV1RestServlet): | |||
|         # we build up the full structure and then decide which bits of it | ||||
|         # to send which means doing unnecessary work sometimes but is | ||||
|         # is probably not going to make a whole lot of difference | ||||
|         rawrules = yield self.hs.get_datastore().get_push_rules_for_user_name( | ||||
|         rawrules = yield self.hs.get_datastore().get_push_rules_for_user( | ||||
|             user.to_string() | ||||
|         ) | ||||
| 
 | ||||
|  | @ -124,6 +128,9 @@ class PushRuleRestServlet(ClientV1RestServlet): | |||
| 
 | ||||
|         rules['global'] = _add_empty_priority_class_arrays(rules['global']) | ||||
| 
 | ||||
|         enabled_map = yield self.hs.get_datastore().\ | ||||
|             get_push_rules_enabled_for_user(user.to_string()) | ||||
| 
 | ||||
|         for r in ruleslist: | ||||
|             rulearray = None | ||||
| 
 | ||||
|  | @ -149,6 +156,9 @@ class PushRuleRestServlet(ClientV1RestServlet): | |||
| 
 | ||||
|             template_rule = _rule_to_template(r) | ||||
|             if template_rule: | ||||
|                 template_rule['enabled'] = True | ||||
|                 if r['rule_id'] in enabled_map: | ||||
|                     template_rule['enabled'] = enabled_map[r['rule_id']] | ||||
|                 rulearray.append(template_rule) | ||||
| 
 | ||||
|         path = request.postpath[1:] | ||||
|  | @ -189,6 +199,25 @@ class PushRuleRestServlet(ClientV1RestServlet): | |||
|     def on_OPTIONS(self, _): | ||||
|         return 200, {} | ||||
| 
 | ||||
|     def set_rule_attr(self, user_name, spec, val): | ||||
|         if spec['attr'] == 'enabled': | ||||
|             if not isinstance(val, bool): | ||||
|                 raise SynapseError(400, "Value for 'enabled' must be boolean") | ||||
|             namespaced_rule_id = _namespaced_rule_id_from_spec(spec) | ||||
|             self.hs.get_datastore().set_push_rule_enabled( | ||||
|                 user_name, namespaced_rule_id, val | ||||
|             ) | ||||
|         else: | ||||
|             raise UnrecognizedRequestError() | ||||
| 
 | ||||
|     def get_rule_attr(self, user_name, namespaced_rule_id, attr): | ||||
|         if attr == 'enabled': | ||||
|             return self.hs.get_datastore().get_push_rule_enabled_by_user_rule_id( | ||||
|                 user_name, namespaced_rule_id | ||||
|             ) | ||||
|         else: | ||||
|             raise UnrecognizedRequestError() | ||||
| 
 | ||||
| 
 | ||||
| def _rule_spec_from_path(path): | ||||
|     if len(path) < 2: | ||||
|  | @ -226,6 +255,12 @@ def _rule_spec_from_path(path): | |||
|     } | ||||
|     if device: | ||||
|         spec['profile_tag'] = device | ||||
| 
 | ||||
|     path = path[1:] | ||||
| 
 | ||||
|     if len(path) > 0 and len(path[0]) > 0: | ||||
|         spec['attr'] = path[0] | ||||
| 
 | ||||
|     return spec | ||||
| 
 | ||||
| 
 | ||||
|  | @ -275,7 +310,7 @@ def _rule_tuple_from_request_object(rule_template, rule_id, req_obj, device=None | |||
|     for a in actions: | ||||
|         if a in ['notify', 'dont_notify', 'coalesce']: | ||||
|             pass | ||||
|         elif isinstance(a, dict) and 'set_sound' in a: | ||||
|         elif isinstance(a, dict) and 'set_tweak' in a: | ||||
|             pass | ||||
|         else: | ||||
|             raise InvalidRuleException("Unrecognised action") | ||||
|  | @ -319,10 +354,23 @@ def _filter_ruleset_with_path(ruleset, path): | |||
|     if path[0] == '': | ||||
|         return ruleset[template_kind] | ||||
|     rule_id = path[0] | ||||
| 
 | ||||
|     the_rule = None | ||||
|     for r in ruleset[template_kind]: | ||||
|         if r['rule_id'] == rule_id: | ||||
|             return r | ||||
|     raise NotFoundError | ||||
|             the_rule = r | ||||
|     if the_rule is None: | ||||
|         raise NotFoundError | ||||
| 
 | ||||
|     path = path[1:] | ||||
|     if len(path) == 0: | ||||
|         return the_rule | ||||
| 
 | ||||
|     attr = path[0] | ||||
|     if attr in the_rule: | ||||
|         return the_rule[attr] | ||||
|     else: | ||||
|         raise UnrecognizedRequestError() | ||||
| 
 | ||||
| 
 | ||||
| def _priority_class_from_spec(spec): | ||||
|  | @ -339,7 +387,7 @@ def _priority_class_from_spec(spec): | |||
| def _priority_class_to_template_name(pc): | ||||
|     if pc > PRIORITY_CLASS_MAP['override']: | ||||
|         # per-device | ||||
|         prio_class_index = pc - len(PushRuleRestServlet.PRIORITY_CLASS_MAP) | ||||
|         prio_class_index = pc - len(PRIORITY_CLASS_MAP) | ||||
|         return PRIORITY_CLASS_INVERSE_MAP[prio_class_index] | ||||
|     else: | ||||
|         return PRIORITY_CLASS_INVERSE_MAP[pc] | ||||
|  | @ -399,9 +447,6 @@ class InvalidRuleException(Exception): | |||
| def _parse_json(request): | ||||
|     try: | ||||
|         content = json.loads(request.content.read()) | ||||
|         if type(content) != dict: | ||||
|             raise SynapseError(400, "Content must be a JSON object.", | ||||
|                                errcode=Codes.NOT_JSON) | ||||
|         return content | ||||
|     except ValueError: | ||||
|         raise SynapseError(400, "Content not JSON.", errcode=Codes.NOT_JSON) | ||||
|  |  | |||
|  | @ -27,7 +27,7 @@ logger = logging.getLogger(__name__) | |||
| 
 | ||||
| class PushRuleStore(SQLBaseStore): | ||||
|     @defer.inlineCallbacks | ||||
|     def get_push_rules_for_user_name(self, user_name): | ||||
|     def get_push_rules_for_user(self, user_name): | ||||
|         sql = ( | ||||
|             "SELECT "+",".join(PushRuleTable.fields)+" " | ||||
|             "FROM "+PushRuleTable.table_name+" " | ||||
|  | @ -45,6 +45,28 @@ class PushRuleStore(SQLBaseStore): | |||
| 
 | ||||
|         defer.returnValue(dicts) | ||||
| 
 | ||||
|     @defer.inlineCallbacks | ||||
|     def get_push_rules_enabled_for_user(self, user_name): | ||||
|         results = yield self._simple_select_list( | ||||
|             PushRuleEnableTable.table_name, | ||||
|             {'user_name': user_name}, | ||||
|             PushRuleEnableTable.fields | ||||
|         ) | ||||
|         defer.returnValue( | ||||
|             {r['rule_id']: False if r['enabled'] == 0 else True for r in results} | ||||
|         ) | ||||
| 
 | ||||
|     @defer.inlineCallbacks | ||||
|     def get_push_rule_enabled_by_user_rule_id(self, user_name, rule_id): | ||||
|         results = yield self._simple_select_list( | ||||
|             PushRuleEnableTable.table_name, | ||||
|             {'user_name': user_name, 'rule_id': rule_id}, | ||||
|             ['enabled'] | ||||
|         ) | ||||
|         if not results: | ||||
|             defer.returnValue(True) | ||||
|         defer.returnValue(results[0]) | ||||
| 
 | ||||
|     @defer.inlineCallbacks | ||||
|     def add_push_rule(self, before, after, **kwargs): | ||||
|         vals = copy.copy(kwargs) | ||||
|  | @ -193,6 +215,20 @@ class PushRuleStore(SQLBaseStore): | |||
|             {'user_name': user_name, 'rule_id': rule_id} | ||||
|         ) | ||||
| 
 | ||||
|     @defer.inlineCallbacks | ||||
|     def set_push_rule_enabled(self, user_name, rule_id, enabled): | ||||
|         if enabled: | ||||
|             yield self._simple_delete_one( | ||||
|                 PushRuleEnableTable.table_name, | ||||
|                 {'user_name': user_name, 'rule_id': rule_id} | ||||
|             ) | ||||
|         else: | ||||
|             yield self._simple_upsert( | ||||
|                 PushRuleEnableTable.table_name, | ||||
|                 {'user_name': user_name, 'rule_id': rule_id}, | ||||
|                 {'enabled': False} | ||||
|             ) | ||||
| 
 | ||||
| 
 | ||||
| class RuleNotFoundException(Exception): | ||||
|     pass | ||||
|  | @ -216,3 +252,13 @@ class PushRuleTable(Table): | |||
|     ] | ||||
| 
 | ||||
|     EntryType = collections.namedtuple("PushRuleEntry", fields) | ||||
| 
 | ||||
| 
 | ||||
| class PushRuleEnableTable(Table): | ||||
|     table_name = "push_rules_enable" | ||||
| 
 | ||||
|     fields = [ | ||||
|         "user_name", | ||||
|         "rule_id", | ||||
|         "enabled" | ||||
|     ] | ||||
|  |  | |||
|  | @ -0,0 +1,9 @@ | |||
| CREATE TABLE IF NOT EXISTS push_rules_enable ( | ||||
|   id INTEGER PRIMARY KEY AUTOINCREMENT, | ||||
|   user_name TEXT NOT NULL, | ||||
|   rule_id TEXT NOT NULL, | ||||
|   enabled TINYINT, | ||||
|   UNIQUE(user_name, rule_id) | ||||
| ); | ||||
| 
 | ||||
| CREATE INDEX IF NOT EXISTS push_rules_enable_user_name on push_rules_enable (user_name); | ||||
		Loading…
	
		Reference in New Issue
	
	 Erik Johnston
						Erik Johnston