You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

163 lines
3.7 KiB
JavaScript

"use strict";
const matchValue = require("match-value");
const defaultValue = require("default-value");
const syncpipe = require("syncpipe");
const splitFilter = require("split-filter");
const flatten = require("flatten");
const unreachable = require("@joepie91/unreachable")("@modular-matrix/client"); // FIXME
function parsePredicate(predicate) {
let match = /^(>|>=|<|<=|==)?([0-9]+)$/.exec(predicate);
if (match != null) {
return {
operator: defaultValue(match[1], "=="),
value: match[2]
};
} else {
unreachable("Failed to parse predicate");
}
}
function mapCondition(condition) {
if (condition.kind === "event_match") {
return {
type: "matchEventField",
field: condition.key,
pattern: condition.pattern
};
} else if (condition.kind === "contains_display_name") {
return {
type: "matchDisplayName"
};
} else if (condition.kind === "room_member_count") {
let { operator, value } = parsePredicate(condition.is);
return {
type: "matchRoomMemberCount",
operator: operator,
value: value
};
} else if (condition.kind === "sender_notification_permission") {
return {
type: "requireEventPowerLevel",
notificationType: condition.key
};
} else {
// Spec: "Unrecognised conditions MUST NOT match any events, effectively making the push rule disabled."
// FIXME: Log warning?
return {
type: "neverMatch"
};
}
}
function mapAction(action) {
if (action === "notify") {
return {
type: "notification"
};
} else if (action === "dont_notify") {
return {
type: "preventNotification"
};
} else if (action === "coalesce") {
return {
type: "digestNotification"
};
} else if (action.set_tweak != null) {
let key = action.set_tweak;
return {
type: "setTweak",
key: key,
value: matchValue(key, {
sound: () => (action.value === "default") ? true : action.value,
highlight: () => defaultValue(action.value, true),
_: () => action.value
})
};
} else {
unreachable(`Unrecognized action type`);
}
}
function mapRule(rule) {
let mappedActions = rule.actions.map(mapAction);
let [ tweaks, actualActions ] = splitFilter(mappedActions, (action) => action.type === "setTweak");
return {
id: rule.rule_id,
isDefault: rule.default,
isEnabled: rule.enabled,
actions: actualActions,
// FIXME: Clearly document that these 'options' are called 'tweaks' on the push gateway side!
notificationOptions: Object.fromEntries(tweaks.map(({ key, value }) => [ key, value ]))
};
}
function mapRuleset(rules, key) {
if (rules == null) {
return [];
} else if (key === "content") {
return rules.map((rule) => {
return {
... mapRule(rule),
conditions: [{
type: "matchContent",
pattern: rule.pattern
}]
};
});
} else if (key === "room") {
return rules.map((rule) => {
return {
... mapRule(rule),
conditions: [{
type: "matchRoom",
roomID: rule.rule_id
}]
};
});
} else if (key === "sender") {
return rules.map((rule) => {
return {
... mapRule(rule),
conditions: [{
type: "matchSender",
userID: rule.rule_id
}]
};
});
} else if (key === "override" || key === "underride") {
return rules.map((rule) => {
return {
... mapRule(rule),
conditions: rule.conditions.map(mapCondition)
};
});
} else {
throw unreachable("Unrecognized ruleset key");
}
}
function mapRulesets(rulesets) {
if (rulesets != null) {
return syncpipe([ "override", "content", "room", "sender", "underride" ], [
(_) => _.map((key) => mapRuleset(rulesets[key], key)),
(_) => _.filter((ruleset) => ruleset.length > 0),
(_) => flatten(_)
]);
} else {
return [];
}
}
module.exports = function mapPushRulesEvent(event, _context) {
return {
type: "pushRulesChanged",
rulesets: mapRulesets(event.content.global)
};
};