You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-08-19 16:42:09 +03:00
Port over push rule eveluator from the angular SDK and make it available on MatrixEvent
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
var PushProcessor = require('../pushprocessor');
|
||||
|
||||
"use strict";
|
||||
/**
|
||||
* This is an internal module. See {@link MatrixEvent} and {@link RoomEvent} for
|
||||
@@ -142,5 +144,13 @@ module.exports.MatrixEvent.prototype = {
|
||||
*/
|
||||
isState: function() {
|
||||
return this.event.state_key !== undefined;
|
||||
},
|
||||
|
||||
getPushActions(client) {
|
||||
if (this.pushActions === undefined) {
|
||||
var pushProcessor = new PushProcessor(client);
|
||||
this.pushActions = pushProcessor.actionsForEvent(this.event);
|
||||
}
|
||||
return this.pushActions;
|
||||
}
|
||||
};
|
||||
|
216
lib/pushprocessor.js
Normal file
216
lib/pushprocessor.js
Normal file
@@ -0,0 +1,216 @@
|
||||
var micromatch = require("micromatch");
|
||||
|
||||
module.exports = function(client) {
|
||||
|
||||
|
||||
var escapeRegExp = function(string){
|
||||
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
};
|
||||
|
||||
var matchingRuleFromKindSet = function(ev, kindset, device) {
|
||||
var rulekinds_in_order = ['override', 'content', 'room', 'sender', 'underride'];
|
||||
for (var ruleKindIndex = 0; ruleKindIndex < rulekinds_in_order.length; ++ruleKindIndex) {
|
||||
var kind = rulekinds_in_order[ruleKindIndex];
|
||||
var ruleset = kindset[kind];
|
||||
|
||||
for (var ruleIndex = 0; ruleIndex < ruleset.length; ++ruleIndex) {
|
||||
var rule = ruleset[ruleIndex];
|
||||
if (!rule.enabled) { continue; }
|
||||
|
||||
var rawrule = templateRuleToRaw(kind, rule, device);
|
||||
if (!rawrule) { continue; }
|
||||
|
||||
if (ruleMatchesEvent(rawrule, ev)) {
|
||||
rule.kind = kind;
|
||||
return rule;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
var templateRuleToRaw = function(kind, tprule, device) {
|
||||
var rawrule = {
|
||||
'rule_id': tprule.rule_id,
|
||||
'actions': tprule.actions,
|
||||
'conditions': []
|
||||
};
|
||||
switch (kind) {
|
||||
case 'underride':
|
||||
case 'override':
|
||||
rawrule.conditions = tprule.conditions;
|
||||
break;
|
||||
case 'room':
|
||||
if (!tprule.rule_id) { return null; }
|
||||
rawrule.conditions.push({
|
||||
'kind': 'event_match',
|
||||
'key': 'room_id',
|
||||
'pattern': tprule.rule_id
|
||||
});
|
||||
break;
|
||||
case 'sender':
|
||||
if (!tprule.rule_id) { return null; }
|
||||
rawrule.conditions.push({
|
||||
'kind': 'event_match',
|
||||
'key': 'user_id',
|
||||
'pattern': tprule.rule_id
|
||||
});
|
||||
break;
|
||||
case 'content':
|
||||
if (!tprule.pattern) { return null; }
|
||||
rawrule.conditions.push({
|
||||
'kind': 'event_match',
|
||||
'key': 'content.body',
|
||||
'pattern': tprule.pattern
|
||||
});
|
||||
break;
|
||||
}
|
||||
if (device) {
|
||||
rawrule.conditions.push({
|
||||
'kind': 'device',
|
||||
'profile_tag': device,
|
||||
});
|
||||
}
|
||||
return rawrule;
|
||||
};
|
||||
|
||||
var ruleMatchesEvent = function(rule, ev) {
|
||||
var ret = true;
|
||||
for (var i = 0; i < rule.conditions.length; ++i) {
|
||||
var cond = rule.conditions[i];
|
||||
ret &= eventFulfillsCondition(cond, ev);
|
||||
}
|
||||
//console.log("Rule "+rule.rule_id+(ret ? " matches" : " doesn't match"));
|
||||
return ret;
|
||||
};
|
||||
|
||||
var eventFulfillsCondition = function(cond, ev) {
|
||||
var condition_functions = {
|
||||
"event_match": eventFulfillsEventMatchCondition,
|
||||
"device": eventFulfillsDeviceCondition,
|
||||
"contains_display_name": eventFulfillsDisplayNameCondition,
|
||||
"room_member_count": eventFulfillsRoomMemberCountCondition
|
||||
};
|
||||
if (condition_functions[cond.kind]) { return condition_functions[cond.kind](cond, ev); }
|
||||
return true;
|
||||
};
|
||||
|
||||
var eventFulfillsRoomMemberCountCondition = function(cond, ev) {
|
||||
if (!cond.is) { return false; }
|
||||
|
||||
var room = client.getRoom(ev.room_id);
|
||||
if (!room || !room.currentState || !room.currentState.members) { return false; }
|
||||
|
||||
var memberCount = Object.keys(room.currentState.members).length;
|
||||
|
||||
var m = cond.is.match(/^([=<>]*)([0-9]*)$/);
|
||||
if (!m) { return false; }
|
||||
var ineq = m[1];
|
||||
var rhs = parseInt(m[2]);
|
||||
if (isNaN(rhs)) { return false; }
|
||||
switch (ineq) {
|
||||
case '':
|
||||
case '==':
|
||||
return memberCount == rhs;
|
||||
case '<':
|
||||
return memberCount < rhs;
|
||||
case '>':
|
||||
return memberCount > rhs;
|
||||
case '<=':
|
||||
return memberCount <= rhs;
|
||||
case '>=':
|
||||
return memberCount >= rhs;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
var eventFulfillsDisplayNameCondition = function(cond, ev) {
|
||||
if (!ev.content || ! ev.content.body || typeof ev.content.body != 'string') { return false; }
|
||||
|
||||
var room = client.getRoom(ev.room_id);
|
||||
var displayName = room.currentState.getMember(client.credentials.userId).name;
|
||||
|
||||
var pat = new RegExp("\\b"+escapeRegExp(displayName)+"\\b", 'i');
|
||||
return ev.content.body.search(pat) > -1;
|
||||
};
|
||||
|
||||
var eventFulfillsDeviceCondition = function(cond, ev) {
|
||||
return false; // XXX: Allow a profile tag to be set for the web client instance
|
||||
};
|
||||
|
||||
var eventFulfillsEventMatchCondition = function(cond, ev) {
|
||||
var val = valueForDottedKey(cond.key, ev);
|
||||
if (!val || typeof val != 'string') { return false; }
|
||||
|
||||
// Supportting ! in globs would mean figuring out when we don't want to use things as a regex, like room IDs
|
||||
var pat = cond.pattern.replace("!", "\\!");
|
||||
|
||||
if (cond.key == 'content.body') {
|
||||
return micromatch.contains(val, pat);
|
||||
} else {
|
||||
return micromatch.isMatch(val, pat);
|
||||
}
|
||||
};
|
||||
|
||||
var valueForDottedKey = function(key, ev) {
|
||||
var parts = key.split('.');
|
||||
var val = ev;
|
||||
while (parts.length > 0) {
|
||||
var thispart = parts.shift();
|
||||
if (!val[thispart]) { return null; }
|
||||
val = val[thispart];
|
||||
}
|
||||
return val;
|
||||
};
|
||||
|
||||
var matchingRuleForEventWithRulesets = function(ev, rulesets) {
|
||||
if (!rulesets) { return null; }
|
||||
if (ev.user_id == client.credentials.userId) { return null; }
|
||||
|
||||
var allDevNames = Object.keys(rulesets.device);
|
||||
for (var i = 0; i < allDevNames.length; ++i) {
|
||||
var devname = allDevNames[i];
|
||||
var devrules = rulesets.device[devname];
|
||||
|
||||
var matchingRule = matchingRuleFromKindSet(devrules, devname);
|
||||
if (matchingRule) { return matchingRule; }
|
||||
}
|
||||
return matchingRuleFromKindSet(ev, rulesets.global);
|
||||
};
|
||||
|
||||
var actionListToActionsObject = function(actionlist) {
|
||||
var actionobj = { 'notify': false, 'tweaks': {} };
|
||||
for (var i = 0; i < actionlist.length; ++i) {
|
||||
var action = actionlist[i];
|
||||
if (action === 'notify') {
|
||||
actionobj.notify = true;
|
||||
} else if (typeof action === 'object') {
|
||||
if (action.value === undefined) { action.value = true; }
|
||||
actionobj.tweaks[action.set_tweak] = action.value;
|
||||
}
|
||||
}
|
||||
return actionobj;
|
||||
};
|
||||
|
||||
var pushActionsForEventAndRulesets = function(ev, rulesets) {
|
||||
var rule = matchingRuleForEventWithRulesets(ev, rulesets);
|
||||
if (!rule) { return {}; }
|
||||
|
||||
var actionObj = actionListToActionsObject(rule.actions);
|
||||
|
||||
// Some actions are implicit in some situations: we add those here
|
||||
if (actionObj.tweaks.highlight === undefined) {
|
||||
// if it isn't specified, highlight if it's a content
|
||||
// rule but otherwise not
|
||||
actionObj.tweaks.highlight = (rule.kind == 'content');
|
||||
}
|
||||
|
||||
return actionObj;
|
||||
};
|
||||
|
||||
this.actionsForEvent = function(ev) {
|
||||
return pushActionsForEventAndRulesets(ev, client.pushRules);
|
||||
};
|
||||
}
|
||||
|
@@ -22,6 +22,7 @@
|
||||
"dependencies": {
|
||||
"browser-request": "^0.3.3",
|
||||
"browserify": "^10.2.3",
|
||||
"micromatch": "^2.1.6",
|
||||
"q": "^1.4.1",
|
||||
"request": "^2.53.0"
|
||||
},
|
||||
|
Reference in New Issue
Block a user