| 1 | #pragma once |
| 2 | |
| 3 | /// @file |
| 4 | /// @brief Pushrules and notification settings. |
| 5 | |
| 6 | #if __has_include(<nlohmann/json_fwd.hpp>) |
| 7 | #include <nlohmann/json_fwd.hpp> |
| 8 | #else |
| 9 | #include <nlohmann/json.hpp> |
| 10 | #endif |
| 11 | |
| 12 | #include <compare> |
| 13 | #include <string> |
| 14 | #include <variant> |
| 15 | #include <vector> |
| 16 | |
| 17 | #include "mtx/events/common.hpp" |
| 18 | #include "mtx/events/power_levels.hpp" |
| 19 | |
| 20 | namespace mtx { |
| 21 | namespace events { |
| 22 | namespace collections { |
| 23 | struct TimelineEvent; |
| 24 | } |
| 25 | } |
| 26 | |
| 27 | //! Namespace for the pushrules specific endpoints. |
| 28 | namespace pushrules { |
| 29 | //! A condition to match pushrules on. |
| 30 | struct PushCondition |
| 31 | { |
| 32 | //! Required. The kind of condition to apply. See conditions for more information on the |
| 33 | //! allowed kinds and how they work. |
| 34 | std::string kind; |
| 35 | //! Required for event_match conditions. The dot- separated field of the event to match. |
| 36 | //! |
| 37 | //! Required for sender_notification_permission conditions. The field in the power level |
| 38 | //! event the user needs a minimum power level for. Fields must be specified under the |
| 39 | //! notifications property in the power level event's content. |
| 40 | std::string key; |
| 41 | //! Required for event_match conditions. The glob- style pattern to match against. Patterns |
| 42 | //! with no special glob characters should be treated as having asterisks prepended and |
| 43 | //! appended when testing the condition. |
| 44 | std::string pattern; |
| 45 | //! Required for room_member_count conditions. A decimal integer optionally prefixed by one |
| 46 | //! of, ==, <, >, >= or <=. A prefix of < matches rooms where the member count is strictly |
| 47 | //! less than the given number and so forth. If no prefix is present, this parameter |
| 48 | //! defaults to ==. |
| 49 | std::string is; |
| 50 | |
| 51 | //! The relation type to match on. Only valid for `im.nheko.msc3664.related_event_match` |
| 52 | //! conditions. |
| 53 | mtx::common::RelationType rel_type = mtx::common::RelationType::Unsupported; |
| 54 | //! Wether to match fallback relations or not. |
| 55 | bool include_fallback = false; |
| 56 | |
| 57 | friend void to_json(nlohmann::json &obj, const PushCondition &condition); |
| 58 | friend void from_json(const nlohmann::json &obj, PushCondition &condition); |
| 59 | }; |
| 60 | //! Namespace for the different push actions. |
| 61 | namespace actions { |
| 62 | //! Notify the user. |
| 63 | struct notify |
| 64 | { |
| 65 | bool operator==(const notify &) const noexcept = default; |
| 66 | }; |
| 67 | //! Don't notify the user. |
| 68 | struct dont_notify |
| 69 | { |
| 70 | bool operator==(const dont_notify &) const noexcept = default; |
| 71 | }; |
| 72 | /// @brief This enables notifications for matching events but activates homeserver specific |
| 73 | /// behaviour to intelligently coalesce multiple events into a single notification. |
| 74 | /// |
| 75 | /// Not all homeservers may support this. Those that do not support it should treat it as the notify |
| 76 | /// action. |
| 77 | struct coalesce |
| 78 | { |
| 79 | bool operator==(const coalesce &) const noexcept = default; |
| 80 | }; |
| 81 | //! Play a sound. |
| 82 | struct set_tweak_sound |
| 83 | { |
| 84 | //! The sound to play. |
| 85 | std::string value = "default" ; |
| 86 | |
| 87 | bool operator==(const set_tweak_sound &) const noexcept = default; |
| 88 | }; |
| 89 | //! Highlight the message. |
| 90 | struct set_tweak_highlight |
| 91 | { |
| 92 | bool value = true; |
| 93 | |
| 94 | bool operator==(const set_tweak_highlight &) const noexcept = default; |
| 95 | }; |
| 96 | |
| 97 | //! A collection for the different actions. |
| 98 | using Action = std::variant<actions::notify, |
| 99 | actions::dont_notify, |
| 100 | actions::coalesce, |
| 101 | actions::set_tweak_sound, |
| 102 | actions::set_tweak_highlight>; |
| 103 | |
| 104 | void |
| 105 | to_json(nlohmann::json &obj, const Action &action); |
| 106 | |
| 107 | void |
| 108 | from_json(const nlohmann::json &obj, Action &action); |
| 109 | |
| 110 | //! A list of actions. |
| 111 | struct Actions |
| 112 | { |
| 113 | std::vector<Action> actions; |
| 114 | |
| 115 | friend void to_json(nlohmann::json &obj, const Actions &action); |
| 116 | friend void from_json(const nlohmann::json &obj, Actions &action); |
| 117 | |
| 118 | bool operator==(const Actions &) const noexcept = default; |
| 119 | }; |
| 120 | } |
| 121 | |
| 122 | //! A pushrule defining the notification behaviour for a message. |
| 123 | struct PushRule |
| 124 | { |
| 125 | //! Required. Whether this is a default rule, or has been set explicitly. |
| 126 | bool default_ = false; |
| 127 | //! Required. Whether the push rule is enabled or not. |
| 128 | bool enabled = true; |
| 129 | //! Required. The actions to perform when this rule is matched. |
| 130 | std::vector<actions::Action> actions; |
| 131 | //! Required. The ID of this rule. |
| 132 | std::string rule_id; |
| 133 | //! The glob-style pattern to match against. Only applicable to content rules. |
| 134 | std::string pattern; |
| 135 | //! The conditions that must hold true for an event in order for a rule to be applied to an |
| 136 | //! event. A rule with no conditions always matches. Only applicable to underride and |
| 137 | //! override rules. |
| 138 | std::vector<PushCondition> conditions; |
| 139 | |
| 140 | friend void to_json(nlohmann::json &obj, const PushRule &condition); |
| 141 | friend void from_json(const nlohmann::json &obj, PushRule &condition); |
| 142 | }; |
| 143 | |
| 144 | //! All the pushrules to evaluate for events. |
| 145 | struct Ruleset |
| 146 | { |
| 147 | //! see https://matrix.org/docs/spec/client_server/latest#push-rules |
| 148 | // |
| 149 | // A push rule is a single rule that states under what conditions an event should be passed |
| 150 | // onto a push gateway and how the notification should be presented. There are different |
| 151 | // "kinds" of push rules and each rule has an associated priority. Every push rule MUST have |
| 152 | // a kind and rule_id. The rule_id is a unique string within the kind of rule and its' |
| 153 | // scope: rule_ids do not need to be unique between rules of the same kind on different |
| 154 | // devices. Rules may have extra keys depending on the value of kind. The different kinds of |
| 155 | // rule in descending order of priority are: |
| 156 | std::vector<PushRule> override_; |
| 157 | std::vector<PushRule> content; |
| 158 | std::vector<PushRule> room; |
| 159 | std::vector<PushRule> sender; |
| 160 | std::vector<PushRule> underride; |
| 161 | |
| 162 | friend void to_json(nlohmann::json &obj, const Ruleset &condition); |
| 163 | friend void from_json(const nlohmann::json &obj, Ruleset &condition); |
| 164 | }; |
| 165 | |
| 166 | //! The global ruleset applied to all events. |
| 167 | struct GlobalRuleset |
| 168 | { |
| 169 | //! The actual ruleset. |
| 170 | Ruleset global; |
| 171 | |
| 172 | friend void to_json(nlohmann::json &obj, const GlobalRuleset &set); |
| 173 | friend void from_json(const nlohmann::json &obj, GlobalRuleset &set); |
| 174 | }; |
| 175 | |
| 176 | //! The response for queries, if a specific ruleset is enabled. |
| 177 | struct Enabled |
| 178 | { |
| 179 | bool enabled = true; |
| 180 | |
| 181 | friend void to_json(nlohmann::json &obj, const Enabled &enabled); |
| 182 | friend void from_json(const nlohmann::json &obj, Enabled &enabled); |
| 183 | }; |
| 184 | |
| 185 | //! An optimized structure to calculate notifications for events. |
| 186 | /// |
| 187 | /// You will want to cache this for as long as possible (until the pushrules change), since |
| 188 | /// constructing this is somewhat expensive. |
| 189 | class PushRuleEvaluator |
| 190 | { |
| 191 | public: |
| 192 | //! Construct a new push evaluator. Pass the current set of pushrules to evaluate. |
| 193 | PushRuleEvaluator(const Ruleset &rules); |
| 194 | ~PushRuleEvaluator(); |
| 195 | |
| 196 | //! Additional room information needed to evaluate push rules. |
| 197 | struct RoomContext |
| 198 | { |
| 199 | //! The displayname of the user in the room. |
| 200 | std::string user_display_name; |
| 201 | //! the membercount of the room |
| 202 | std::size_t member_count = 0; |
| 203 | //! The powerlevels event in this room |
| 204 | mtx::events::state::PowerLevels power_levels; |
| 205 | }; |
| 206 | |
| 207 | //! Evaluate the pushrules for @event . |
| 208 | /// |
| 209 | /// You need to have the room_id set for the event. |
| 210 | /// `relatedEvents` is a mapping of rel_type to event. Pass all the events that are related to |
| 211 | /// by this event here. |
| 212 | /// \returns the actions to apply. |
| 213 | [[nodiscard]] std::vector<actions::Action> evaluate( |
| 214 | const mtx::events::collections::TimelineEvent &event, |
| 215 | const RoomContext &ctx, |
| 216 | const std::vector<std::pair<mtx::common::Relation, mtx::events::collections::TimelineEvent>> |
| 217 | &relatedEvents) const; |
| 218 | |
| 219 | private: |
| 220 | struct OptimizedRules; |
| 221 | std::unique_ptr<OptimizedRules> rules; |
| 222 | }; |
| 223 | } |
| 224 | } |
| 225 | |