matrix-react-sdk
Version:
SDK for matrix.org using React
93 lines (90 loc) • 17.7 kB
JavaScript
;
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.buildActivityScores = buildActivityScores;
exports.buildMemberScores = buildMemberScores;
exports.compareMembers = void 0;
var _lodash = require("lodash");
var _types = require("matrix-js-sdk/src/types");
var _DMRoomMap = _interopRequireDefault(require("./DMRoomMap"));
/*
Copyright 2024 New Vector Ltd.
Copyright 2022 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
const compareMembers = (activityScores, memberScores) => (a, b) => {
const aActivityScore = activityScores[a.userId]?.score ?? 0;
const aMemberScore = memberScores[a.userId]?.score ?? 0;
const aScore = aActivityScore + aMemberScore;
const aNumRooms = memberScores[a.userId]?.numRooms ?? 0;
const bActivityScore = activityScores[b.userId]?.score ?? 0;
const bMemberScore = memberScores[b.userId]?.score ?? 0;
const bScore = bActivityScore + bMemberScore;
const bNumRooms = memberScores[b.userId]?.numRooms ?? 0;
if (aScore === bScore) {
if (aNumRooms === bNumRooms) {
// If there is no activity between members,
// keep the order received from the user directory search results
return 0;
}
return bNumRooms - aNumRooms;
}
return bScore - aScore;
};
exports.compareMembers = compareMembers;
function joinedRooms(cli) {
return cli.getRooms().filter(r => r.getMyMembership() === _types.KnownMembership.Join)
// Skip low priority rooms and DMs
.filter(r => !_DMRoomMap.default.shared().getUserIdForRoomId(r.roomId)).filter(r => !Object.keys(r.tags).includes("m.lowpriority"));
}
// Score people based on who have sent messages recently, as a way to improve the quality of suggestions.
// We do this by checking every room to see who has sent a message in the last few hours, and giving them
// a score which correlates to the freshness of their message. In theory, this results in suggestions
// which are closer to "continue this conversation" rather than "this person exists".
function buildActivityScores(cli) {
const now = new Date().getTime();
const earliestAgeConsidered = now - 60 * 60 * 1000; // 1 hour ago
const maxMessagesConsidered = 50; // so we don't iterate over a huge amount of traffic
const events = joinedRooms(cli).flatMap(room => (0, _lodash.takeRight)(room.getLiveTimeline().getEvents(), maxMessagesConsidered)).filter(ev => ev.getTs() > earliestAgeConsidered);
const senderEvents = (0, _lodash.groupBy)(events, ev => ev.getSender());
// If the iteratee in mapValues returns undefined that key will be removed from the resultant object
return (0, _lodash.mapValues)(senderEvents, events => {
if (!events.length) return;
const lastEvent = (0, _lodash.maxBy)(events, ev => ev.getTs());
const distanceFromNow = Math.abs(now - lastEvent.getTs()); // abs to account for slight future messages
const inverseTime = now - earliestAgeConsidered - distanceFromNow;
return {
lastSpoke: lastEvent.getTs(),
// Scores from being in a room give a 'good' score of about 1.0-1.5, so for our
// score we'll try and award at least 1.0 for making the list, with 4.0 being
// an approximate maximum for being selected.
score: Math.max(1, inverseTime / (15 * 60 * 1000)) // 15min segments to keep scores sane
};
});
}
function buildMemberScores(cli) {
const maxConsideredMembers = 200;
const consideredRooms = joinedRooms(cli).filter(room => room.getJoinedMemberCount() < maxConsideredMembers);
const memberPeerEntries = consideredRooms.flatMap(room => room.getJoinedMembers().map(member => ({
member,
roomSize: room.getJoinedMemberCount()
})));
const userMeta = (0, _lodash.groupBy)(memberPeerEntries, ({
member
}) => member.userId);
// If the iteratee in mapValues returns undefined that key will be removed from the resultant object
return (0, _lodash.mapValues)(userMeta, roomMemberships => {
if (!roomMemberships.length) return;
const maximumPeers = maxConsideredMembers * roomMemberships.length;
const totalPeers = (0, _lodash.sumBy)(roomMemberships, entry => entry.roomSize);
return {
member: (0, _lodash.minBy)(roomMemberships, entry => entry.roomSize).member,
numRooms: roomMemberships.length,
score: Math.max(0, Math.pow(1 - totalPeers / maximumPeers, 5))
};
});
}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfbG9kYXNoIiwicmVxdWlyZSIsIl90eXBlcyIsIl9ETVJvb21NYXAiLCJfaW50ZXJvcFJlcXVpcmVEZWZhdWx0IiwiY29tcGFyZU1lbWJlcnMiLCJhY3Rpdml0eVNjb3JlcyIsIm1lbWJlclNjb3JlcyIsImEiLCJiIiwiYUFjdGl2aXR5U2NvcmUiLCJ1c2VySWQiLCJzY29yZSIsImFNZW1iZXJTY29yZSIsImFTY29yZSIsImFOdW1Sb29tcyIsIm51bVJvb21zIiwiYkFjdGl2aXR5U2NvcmUiLCJiTWVtYmVyU2NvcmUiLCJiU2NvcmUiLCJiTnVtUm9vbXMiLCJleHBvcnRzIiwiam9pbmVkUm9vbXMiLCJjbGkiLCJnZXRSb29tcyIsImZpbHRlciIsInIiLCJnZXRNeU1lbWJlcnNoaXAiLCJLbm93bk1lbWJlcnNoaXAiLCJKb2luIiwiRE1Sb29tTWFwIiwic2hhcmVkIiwiZ2V0VXNlcklkRm9yUm9vbUlkIiwicm9vbUlkIiwiT2JqZWN0Iiwia2V5cyIsInRhZ3MiLCJpbmNsdWRlcyIsImJ1aWxkQWN0aXZpdHlTY29yZXMiLCJub3ciLCJEYXRlIiwiZ2V0VGltZSIsImVhcmxpZXN0QWdlQ29uc2lkZXJlZCIsIm1heE1lc3NhZ2VzQ29uc2lkZXJlZCIsImV2ZW50cyIsImZsYXRNYXAiLCJyb29tIiwidGFrZVJpZ2h0IiwiZ2V0TGl2ZVRpbWVsaW5lIiwiZ2V0RXZlbnRzIiwiZXYiLCJnZXRUcyIsInNlbmRlckV2ZW50cyIsImdyb3VwQnkiLCJnZXRTZW5kZXIiLCJtYXBWYWx1ZXMiLCJsZW5ndGgiLCJsYXN0RXZlbnQiLCJtYXhCeSIsImRpc3RhbmNlRnJvbU5vdyIsIk1hdGgiLCJhYnMiLCJpbnZlcnNlVGltZSIsImxhc3RTcG9rZSIsIm1heCIsImJ1aWxkTWVtYmVyU2NvcmVzIiwibWF4Q29uc2lkZXJlZE1lbWJlcnMiLCJjb25zaWRlcmVkUm9vbXMiLCJnZXRKb2luZWRNZW1iZXJDb3VudCIsIm1lbWJlclBlZXJFbnRyaWVzIiwiZ2V0Sm9pbmVkTWVtYmVycyIsIm1hcCIsIm1lbWJlciIsInJvb21TaXplIiwidXNlck1ldGEiLCJyb29tTWVtYmVyc2hpcHMiLCJtYXhpbXVtUGVlcnMiLCJ0b3RhbFBlZXJzIiwic3VtQnkiLCJlbnRyeSIsIm1pbkJ5IiwicG93Il0sInNvdXJjZXMiOlsiLi4vLi4vc3JjL3V0aWxzL1NvcnRNZW1iZXJzLnRzIl0sInNvdXJjZXNDb250ZW50IjpbIi8qXG5Db3B5cmlnaHQgMjAyNCBOZXcgVmVjdG9yIEx0ZC5cbkNvcHlyaWdodCAyMDIyIFRoZSBNYXRyaXgub3JnIEZvdW5kYXRpb24gQy5JLkMuXG5cblNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBR1BMLTMuMC1vbmx5IE9SIEdQTC0zLjAtb25seVxuUGxlYXNlIHNlZSBMSUNFTlNFIGZpbGVzIGluIHRoZSByZXBvc2l0b3J5IHJvb3QgZm9yIGZ1bGwgZGV0YWlscy5cbiovXG5cbmltcG9ydCB7IGdyb3VwQnksIG1hcFZhbHVlcywgbWF4QnksIG1pbkJ5LCBzdW1CeSwgdGFrZVJpZ2h0IH0gZnJvbSBcImxvZGFzaFwiO1xuaW1wb3J0IHsgTWF0cml4Q2xpZW50LCBSb29tLCBSb29tTWVtYmVyIH0gZnJvbSBcIm1hdHJpeC1qcy1zZGsvc3JjL21hdHJpeFwiO1xuaW1wb3J0IHsgS25vd25NZW1iZXJzaGlwIH0gZnJvbSBcIm1hdHJpeC1qcy1zZGsvc3JjL3R5cGVzXCI7XG5cbmltcG9ydCB7IE1lbWJlciB9IGZyb20gXCIuL2RpcmVjdC1tZXNzYWdlc1wiO1xuaW1wb3J0IERNUm9vbU1hcCBmcm9tIFwiLi9ETVJvb21NYXBcIjtcblxuZXhwb3J0IGNvbnN0IGNvbXBhcmVNZW1iZXJzID1cbiAgICAoXG4gICAgICAgIGFjdGl2aXR5U2NvcmVzOiBSZWNvcmQ8c3RyaW5nLCBJQWN0aXZpdHlTY29yZSB8IHVuZGVmaW5lZD4sXG4gICAgICAgIG1lbWJlclNjb3JlczogUmVjb3JkPHN0cmluZywgSU1lbWJlclNjb3JlIHwgdW5kZWZpbmVkPixcbiAgICApID0+XG4gICAgKGE6IE1lbWJlciB8IFJvb21NZW1iZXIsIGI6IE1lbWJlciB8IFJvb21NZW1iZXIpOiBudW1iZXIgPT4ge1xuICAgICAgICBjb25zdCBhQWN0aXZpdHlTY29yZSA9IGFjdGl2aXR5U2NvcmVzW2EudXNlcklkXT8uc2NvcmUgPz8gMDtcbiAgICAgICAgY29uc3QgYU1lbWJlclNjb3JlID0gbWVtYmVyU2NvcmVzW2EudXNlcklkXT8uc2NvcmUgPz8gMDtcbiAgICAgICAgY29uc3QgYVNjb3JlID0gYUFjdGl2aXR5U2NvcmUgKyBhTWVtYmVyU2NvcmU7XG4gICAgICAgIGNvbnN0IGFOdW1Sb29tcyA9IG1lbWJlclNjb3Jlc1thLnVzZXJJZF0/Lm51bVJvb21zID8/IDA7XG5cbiAgICAgICAgY29uc3QgYkFjdGl2aXR5U2NvcmUgPSBhY3Rpdml0eVNjb3Jlc1tiLnVzZXJJZF0/LnNjb3JlID8/IDA7XG4gICAgICAgIGNvbnN0IGJNZW1iZXJTY29yZSA9IG1lbWJlclNjb3Jlc1tiLnVzZXJJZF0/LnNjb3JlID8/IDA7XG4gICAgICAgIGNvbnN0IGJTY29yZSA9IGJBY3Rpdml0eVNjb3JlICsgYk1lbWJlclNjb3JlO1xuICAgICAgICBjb25zdCBiTnVtUm9vbXMgPSBtZW1iZXJTY29yZXNbYi51c2VySWRdPy5udW1Sb29tcyA/PyAwO1xuXG4gICAgICAgIGlmIChhU2NvcmUgPT09IGJTY29yZSkge1xuICAgICAgICAgICAgaWYgKGFOdW1Sb29tcyA9PT0gYk51bVJvb21zKSB7XG4gICAgICAgICAgICAgICAgLy8gSWYgdGhlcmUgaXMgbm8gYWN0aXZpdHkgYmV0d2VlbiBtZW1iZXJzLFxuICAgICAgICAgICAgICAgIC8vIGtlZXAgdGhlIG9yZGVyIHJlY2VpdmVkIGZyb20gdGhlIHVzZXIgZGlyZWN0b3J5IHNlYXJjaCByZXN1bHRzXG4gICAgICAgICAgICAgICAgcmV0dXJuIDA7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHJldHVybiBiTnVtUm9vbXMgLSBhTnVtUm9vbXM7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGJTY29yZSAtIGFTY29yZTtcbiAgICB9O1xuXG5mdW5jdGlvbiBqb2luZWRSb29tcyhjbGk6IE1hdHJpeENsaWVudCk6IFJvb21bXSB7XG4gICAgcmV0dXJuIChcbiAgICAgICAgY2xpXG4gICAgICAgICAgICAuZ2V0Um9vbXMoKVxuICAgICAgICAgICAgLmZpbHRlcigocikgPT4gci5nZXRNeU1lbWJlcnNoaXAoKSA9PT0gS25vd25NZW1iZXJzaGlwLkpvaW4pXG4gICAgICAgICAgICAvLyBTa2lwIGxvdyBwcmlvcml0eSByb29tcyBhbmQgRE1zXG4gICAgICAgICAgICAuZmlsdGVyKChyKSA9PiAhRE1Sb29tTWFwLnNoYXJlZCgpLmdldFVzZXJJZEZvclJvb21JZChyLnJvb21JZCkpXG4gICAgICAgICAgICAuZmlsdGVyKChyKSA9PiAhT2JqZWN0LmtleXMoci50YWdzKS5pbmNsdWRlcyhcIm0ubG93cHJpb3JpdHlcIikpXG4gICAgKTtcbn1cblxuaW50ZXJmYWNlIElBY3Rpdml0eVNjb3JlIHtcbiAgICBsYXN0U3Bva2U6IG51bWJlcjtcbiAgICBzY29yZTogbnVtYmVyO1xufVxuXG4vLyBTY29yZSBwZW9wbGUgYmFzZWQgb24gd2hvIGhhdmUgc2VudCBtZXNzYWdlcyByZWNlbnRseSwgYXMgYSB3YXkgdG8gaW1wcm92ZSB0aGUgcXVhbGl0eSBvZiBzdWdnZXN0aW9ucy5cbi8vIFdlIGRvIHRoaXMgYnkgY2hlY2tpbmcgZXZlcnkgcm9vbSB0byBzZWUgd2hvIGhhcyBzZW50IGEgbWVzc2FnZSBpbiB0aGUgbGFzdCBmZXcgaG91cnMsIGFuZCBnaXZpbmcgdGhlbVxuLy8gYSBzY29yZSB3aGljaCBjb3JyZWxhdGVzIHRvIHRoZSBmcmVzaG5lc3Mgb2YgdGhlaXIgbWVzc2FnZS4gSW4gdGhlb3J5LCB0aGlzIHJlc3VsdHMgaW4gc3VnZ2VzdGlvbnNcbi8vIHdoaWNoIGFyZSBjbG9zZXIgdG8gXCJjb250aW51ZSB0aGlzIGNvbnZlcnNhdGlvblwiIHJhdGhlciB0aGFuIFwidGhpcyBwZXJzb24gZXhpc3RzXCIuXG5leHBvcnQgZnVuY3Rpb24gYnVpbGRBY3Rpdml0eVNjb3JlcyhjbGk6IE1hdHJpeENsaWVudCk6IHsgW3VzZXJJZDogc3RyaW5nXTogSUFjdGl2aXR5U2NvcmUgfSB7XG4gICAgY29uc3Qgbm93ID0gbmV3IERhdGUoKS5nZXRUaW1lKCk7XG4gICAgY29uc3QgZWFybGllc3RBZ2VDb25zaWRlcmVkID0gbm93IC0gNjAgKiA2MCAqIDEwMDA7IC8vIDEgaG91ciBhZ29cbiAgICBjb25zdCBtYXhNZXNzYWdlc0NvbnNpZGVyZWQgPSA1MDsgLy8gc28gd2UgZG9uJ3QgaXRlcmF0ZSBvdmVyIGEgaHVnZSBhbW91bnQgb2YgdHJhZmZpY1xuICAgIGNvbnN0IGV2ZW50cyA9IGpvaW5lZFJvb21zKGNsaSlcbiAgICAgICAgLmZsYXRNYXAoKHJvb20pID0+IHRha2VSaWdodChyb29tLmdldExpdmVUaW1lbGluZSgpLmdldEV2ZW50cygpLCBtYXhNZXNzYWdlc0NvbnNpZGVyZWQpKVxuICAgICAgICAuZmlsdGVyKChldikgPT4gZXYuZ2V0VHMoKSA+IGVhcmxpZXN0QWdlQ29uc2lkZXJlZCk7XG4gICAgY29uc3Qgc2VuZGVyRXZlbnRzID0gZ3JvdXBCeShldmVudHMsIChldikgPT4gZXYuZ2V0U2VuZGVyKCkpO1xuICAgIC8vIElmIHRoZSBpdGVyYXRlZSBpbiBtYXBWYWx1ZXMgcmV0dXJucyB1bmRlZmluZWQgdGhhdCBrZXkgd2lsbCBiZSByZW1vdmVkIGZyb20gdGhlIHJlc3VsdGFudCBvYmplY3RcbiAgICByZXR1cm4gbWFwVmFsdWVzKHNlbmRlckV2ZW50cywgKGV2ZW50cykgPT4ge1xuICAgICAgICBpZiAoIWV2ZW50cy5sZW5ndGgpIHJldHVybjtcbiAgICAgICAgY29uc3QgbGFzdEV2ZW50ID0gbWF4QnkoZXZlbnRzLCAoZXYpID0+IGV2LmdldFRzKCkpITtcbiAgICAgICAgY29uc3QgZGlzdGFuY2VGcm9tTm93ID0gTWF0aC5hYnMobm93IC0gbGFzdEV2ZW50LmdldFRzKCkpOyAvLyBhYnMgdG8gYWNjb3VudCBmb3Igc2xpZ2h0IGZ1dHVyZSBtZXNzYWdlc1xuICAgICAgICBjb25zdCBpbnZlcnNlVGltZSA9IG5vdyAtIGVhcmxpZXN0QWdlQ29uc2lkZXJlZCAtIGRpc3RhbmNlRnJvbU5vdztcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIGxhc3RTcG9rZTogbGFzdEV2ZW50LmdldFRzKCksXG4gICAgICAgICAgICAvLyBTY29yZXMgZnJvbSBiZWluZyBpbiBhIHJvb20gZ2l2ZSBhICdnb29kJyBzY29yZSBvZiBhYm91dCAxLjAtMS41LCBzbyBmb3Igb3VyXG4gICAgICAgICAgICAvLyBzY29yZSB3ZSdsbCB0cnkgYW5kIGF3YXJkIGF0IGxlYXN0IDEuMCBmb3IgbWFraW5nIHRoZSBsaXN0LCB3aXRoIDQuMCBiZWluZ1xuICAgICAgICAgICAgLy8gYW4gYXBwcm94aW1hdGUgbWF4aW11bSBmb3IgYmVpbmcgc2VsZWN0ZWQuXG4gICAgICAgICAgICBzY29yZTogTWF0aC5tYXgoMSwgaW52ZXJzZVRpbWUgLyAoMTUgKiA2MCAqIDEwMDApKSwgLy8gMTVtaW4gc2VnbWVudHMgdG8ga2VlcCBzY29yZXMgc2FuZVxuICAgICAgICB9O1xuICAgIH0pIGFzIHsgW2tleTogc3RyaW5nXTogSUFjdGl2aXR5U2NvcmUgfTtcbn1cblxuaW50ZXJmYWNlIElNZW1iZXJTY29yZSB7XG4gICAgbWVtYmVyOiBSb29tTWVtYmVyO1xuICAgIHNjb3JlOiBudW1iZXI7XG4gICAgbnVtUm9vbXM6IG51bWJlcjtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGJ1aWxkTWVtYmVyU2NvcmVzKGNsaTogTWF0cml4Q2xpZW50KTogeyBbdXNlcklkOiBzdHJpbmddOiBJTWVtYmVyU2NvcmUgfSB7XG4gICAgY29uc3QgbWF4Q29uc2lkZXJlZE1lbWJlcnMgPSAyMDA7XG4gICAgY29uc3QgY29uc2lkZXJlZFJvb21zID0gam9pbmVkUm9vbXMoY2xpKS5maWx0ZXIoKHJvb20pID0+IHJvb20uZ2V0Sm9pbmVkTWVtYmVyQ291bnQoKSA8IG1heENvbnNpZGVyZWRNZW1iZXJzKTtcbiAgICBjb25zdCBtZW1iZXJQZWVyRW50cmllcyA9IGNvbnNpZGVyZWRSb29tcy5mbGF0TWFwKChyb29tKSA9PlxuICAgICAgICByb29tLmdldEpvaW5lZE1lbWJlcnMoKS5tYXAoKG1lbWJlcikgPT4gKHsgbWVtYmVyLCByb29tU2l6ZTogcm9vbS5nZXRKb2luZWRNZW1iZXJDb3VudCgpIH0pKSxcbiAgICApO1xuICAgIGNvbnN0IHVzZXJNZXRhID0gZ3JvdXBCeShtZW1iZXJQZWVyRW50cmllcywgKHsgbWVtYmVyIH0pID0+IG1lbWJlci51c2VySWQpO1xuICAgIC8vIElmIHRoZSBpdGVyYXRlZSBpbiBtYXBWYWx1ZXMgcmV0dXJucyB1bmRlZmluZWQgdGhhdCBrZXkgd2lsbCBiZSByZW1vdmVkIGZyb20gdGhlIHJlc3VsdGFudCBvYmplY3RcbiAgICByZXR1cm4gbWFwVmFsdWVzKHVzZXJNZXRhLCAocm9vbU1lbWJlcnNoaXBzKSA9PiB7XG4gICAgICAgIGlmICghcm9vbU1lbWJlcnNoaXBzLmxlbmd0aCkgcmV0dXJuO1xuICAgICAgICBjb25zdCBtYXhpbXVtUGVlcnMgPSBtYXhDb25zaWRlcmVkTWVtYmVycyAqIHJvb21NZW1iZXJzaGlwcy5sZW5ndGg7XG4gICAgICAgIGNvbnN0IHRvdGFsUGVlcnMgPSBzdW1CeShyb29tTWVtYmVyc2hpcHMsIChlbnRyeSkgPT4gZW50cnkucm9vbVNpemUpO1xuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgbWVtYmVyOiBtaW5CeShyb29tTWVtYmVyc2hpcHMsIChlbnRyeSkgPT4gZW50cnkucm9vbVNpemUpIS5tZW1iZXIsXG4gICAgICAgICAgICBudW1Sb29tczogcm9vbU1lbWJlcnNoaXBzLmxlbmd0aCxcbiAgICAgICAgICAgIHNjb3JlOiBNYXRoLm1heCgwLCBNYXRoLnBvdygxIC0gdG90YWxQZWVycyAvIG1heGltdW1QZWVycywgNSkpLFxuICAgICAgICB9O1xuICAgIH0pIGFz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