feat: complete Ancestor quote bot with production-ready Docker support
This commit is contained in:
203
node_modules/matrix-js-sdk/lib/sync-accumulator.js
generated
vendored
203
node_modules/matrix-js-sdk/lib/sync-accumulator.js
generated
vendored
@@ -1,4 +1,15 @@
|
||||
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
||||
"use strict";
|
||||
|
||||
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.SyncAccumulator = exports.Category = void 0;
|
||||
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
||||
var _logger = require("./logger");
|
||||
var _utils = require("./utils");
|
||||
var _sync = require("./@types/sync");
|
||||
var _receiptAccumulator = require("./receipt-accumulator");
|
||||
/*
|
||||
Copyright 2017 - 2023 The Matrix.org Foundation C.I.C.
|
||||
|
||||
@@ -14,39 +25,18 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This is an internal module. See {@link SyncAccumulator} for the public class.
|
||||
*/
|
||||
|
||||
import { logger } from "./logger.js";
|
||||
import { deepCopy } from "./utils.js";
|
||||
import { MAX_STICKY_DURATION_MS } from "./models/event.js";
|
||||
import { UNREAD_THREAD_NOTIFICATIONS } from "./@types/sync.js";
|
||||
import { ReceiptAccumulator } from "./receipt-accumulator.js";
|
||||
|
||||
/** A to-device message as received from the sync. */
|
||||
|
||||
/**
|
||||
* A (possibly decrypted) to-device message after it has been successfully processed by the sdk.
|
||||
*
|
||||
* If the message was encrypted, the `encryptionInfo` field will contain the encryption information.
|
||||
* If the message was sent in clear, this field will be null.
|
||||
*
|
||||
* The `message` field contains the message `type`, `content`, and `sender` as if the message was sent in clear.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The "users" section of the sync update which contains extended profile updates.
|
||||
*/
|
||||
|
||||
export let Category = /*#__PURE__*/function (Category) {
|
||||
/* eslint-disable camelcase */
|
||||
/* eslint-enable camelcase */
|
||||
let Category = /*#__PURE__*/function (Category) {
|
||||
Category["Invite"] = "invite";
|
||||
Category["Leave"] = "leave";
|
||||
Category["Join"] = "join";
|
||||
Category["Knock"] = "knock";
|
||||
return Category;
|
||||
}({});
|
||||
exports.Category = Category;
|
||||
function isTaggedEvent(event) {
|
||||
return "_localTs" in event && event["_localTs"] !== undefined;
|
||||
}
|
||||
@@ -61,21 +51,19 @@ function isTaggedEvent(event) {
|
||||
* be loaded from disk and incremental syncs can be performed on the server,
|
||||
* rather than asking the server to do an initial sync on startup.
|
||||
*/
|
||||
export class SyncAccumulator {
|
||||
class SyncAccumulator {
|
||||
constructor(opts = {}) {
|
||||
_defineProperty(this, "accountData", {});
|
||||
this.opts = opts;
|
||||
(0, _defineProperty2.default)(this, "accountData", {});
|
||||
// $event_type: Object
|
||||
_defineProperty(this, "inviteRooms", {});
|
||||
(0, _defineProperty2.default)(this, "inviteRooms", {});
|
||||
// $roomId: { ... sync 'invite' json data ... }
|
||||
_defineProperty(this, "knockRooms", {});
|
||||
// $roomId: { ... sync 'knock' json data ... }
|
||||
_defineProperty(this, "joinRooms", {});
|
||||
(0, _defineProperty2.default)(this, "joinRooms", {});
|
||||
// the /sync token which corresponds to the last time rooms were
|
||||
// accumulated. We remember this so that any caller can obtain a
|
||||
// coherent /sync response and know at what point they should be
|
||||
// streaming from without losing events.
|
||||
_defineProperty(this, "nextBatch", null);
|
||||
this.opts = opts;
|
||||
(0, _defineProperty2.default)(this, "nextBatch", null);
|
||||
this.opts.maxTimelineEntries = this.opts.maxTimelineEntries || 50;
|
||||
}
|
||||
accumulate(syncResponse, fromDatabase = false) {
|
||||
@@ -117,11 +105,6 @@ export class SyncAccumulator {
|
||||
this.accumulateRoom(roomId, Category.Leave, syncResponse.rooms.leave[roomId], fromDatabase);
|
||||
});
|
||||
}
|
||||
if (syncResponse.rooms.knock) {
|
||||
Object.keys(syncResponse.rooms.knock).forEach(roomId => {
|
||||
this.accumulateRoom(roomId, Category.Knock, syncResponse.rooms.knock[roomId], fromDatabase);
|
||||
});
|
||||
}
|
||||
}
|
||||
accumulateRoom(roomId, category, data, fromDatabase = false) {
|
||||
// Valid /sync state transitions
|
||||
@@ -138,20 +121,10 @@ export class SyncAccumulator {
|
||||
switch (category) {
|
||||
case Category.Invite:
|
||||
// (5)
|
||||
if (this.knockRooms[roomId]) {
|
||||
// was previously knock, now invite, need to delete knock state
|
||||
delete this.knockRooms[roomId];
|
||||
}
|
||||
this.accumulateInviteState(roomId, data);
|
||||
break;
|
||||
case Category.Knock:
|
||||
this.accumulateKnockState(roomId, data);
|
||||
break;
|
||||
case Category.Join:
|
||||
if (this.knockRooms[roomId]) {
|
||||
// delete knock state on join
|
||||
delete this.knockRooms[roomId];
|
||||
} else if (this.inviteRooms[roomId]) {
|
||||
if (this.inviteRooms[roomId]) {
|
||||
// (1)
|
||||
// was previously invite, now join. We expect /sync to give
|
||||
// the entire state and timeline on 'join', so delete previous
|
||||
@@ -162,10 +135,7 @@ export class SyncAccumulator {
|
||||
this.accumulateJoinState(roomId, data, fromDatabase);
|
||||
break;
|
||||
case Category.Leave:
|
||||
if (this.knockRooms[roomId]) {
|
||||
// delete knock state on leave
|
||||
delete this.knockRooms[roomId];
|
||||
} else if (this.inviteRooms[roomId]) {
|
||||
if (this.inviteRooms[roomId]) {
|
||||
// (4)
|
||||
delete this.inviteRooms[roomId];
|
||||
} else {
|
||||
@@ -174,7 +144,7 @@ export class SyncAccumulator {
|
||||
}
|
||||
break;
|
||||
default:
|
||||
logger.error("Unknown cateogory: ", category);
|
||||
_logger.logger.error("Unknown cateogory: ", category);
|
||||
}
|
||||
}
|
||||
accumulateInviteState(roomId, data) {
|
||||
@@ -206,39 +176,10 @@ export class SyncAccumulator {
|
||||
}
|
||||
});
|
||||
}
|
||||
accumulateKnockState(roomId, data) {
|
||||
if (!data.knock_state || !data.knock_state.events) {
|
||||
// no new data
|
||||
return;
|
||||
}
|
||||
if (!this.knockRooms[roomId]) {
|
||||
this.knockRooms[roomId] = {
|
||||
knock_state: data.knock_state
|
||||
};
|
||||
return;
|
||||
}
|
||||
// accumulate extra keys
|
||||
// clobber based on event type / state key
|
||||
// We expect knock_state to be small, so just loop over the events
|
||||
const currentData = this.knockRooms[roomId];
|
||||
data.knock_state.events.forEach(e => {
|
||||
let hasAdded = false;
|
||||
for (let i = 0; i < currentData.knock_state.events.length; i++) {
|
||||
const current = currentData.knock_state.events[i];
|
||||
if (current.type === e.type && current.state_key == e.state_key) {
|
||||
currentData.knock_state.events[i] = e; // update
|
||||
hasAdded = true;
|
||||
}
|
||||
}
|
||||
if (!hasAdded) {
|
||||
currentData.knock_state.events.push(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Accumulate timeline and state events in a room.
|
||||
accumulateJoinState(roomId, data, fromDatabase = false) {
|
||||
const now = Date.now();
|
||||
var _ref, _data, _data$ephemeral, _data$state, _data$timeline;
|
||||
// We expect this function to be called a lot (every /sync) so we want
|
||||
// this to be fast. /sync stores events in an array but we often want
|
||||
// to clobber based on type/state_key. Rather than convert arrays to
|
||||
@@ -284,8 +225,7 @@ export class SyncAccumulator {
|
||||
_unreadNotifications: {},
|
||||
_unreadThreadNotifications: {},
|
||||
_summary: {},
|
||||
_receipts: new ReceiptAccumulator(),
|
||||
_stickyEvents: []
|
||||
_receipts: new _receiptAccumulator.ReceiptAccumulator()
|
||||
};
|
||||
}
|
||||
const currentData = this.joinRooms[roomId];
|
||||
@@ -300,16 +240,17 @@ export class SyncAccumulator {
|
||||
if (data.unread_notifications) {
|
||||
currentData._unreadNotifications = data.unread_notifications;
|
||||
}
|
||||
currentData._unreadThreadNotifications = data[UNREAD_THREAD_NOTIFICATIONS.stable] ?? data[UNREAD_THREAD_NOTIFICATIONS.unstable] ?? undefined;
|
||||
currentData._unreadThreadNotifications = (_ref = (_data = data[_sync.UNREAD_THREAD_NOTIFICATIONS.stable]) !== null && _data !== void 0 ? _data : data[_sync.UNREAD_THREAD_NOTIFICATIONS.unstable]) !== null && _ref !== void 0 ? _ref : undefined;
|
||||
if (data.summary) {
|
||||
var _sum$HEROES_KEY, _sum$JOINED_COUNT_KEY, _sum$INVITED_COUNT_KE;
|
||||
const HEROES_KEY = "m.heroes";
|
||||
const INVITED_COUNT_KEY = "m.invited_member_count";
|
||||
const JOINED_COUNT_KEY = "m.joined_member_count";
|
||||
const acc = currentData._summary;
|
||||
const sum = data.summary;
|
||||
acc[HEROES_KEY] = sum[HEROES_KEY] ?? acc[HEROES_KEY];
|
||||
acc[JOINED_COUNT_KEY] = sum[JOINED_COUNT_KEY] ?? acc[JOINED_COUNT_KEY];
|
||||
acc[INVITED_COUNT_KEY] = sum[INVITED_COUNT_KEY] ?? acc[INVITED_COUNT_KEY];
|
||||
acc[HEROES_KEY] = (_sum$HEROES_KEY = sum[HEROES_KEY]) !== null && _sum$HEROES_KEY !== void 0 ? _sum$HEROES_KEY : acc[HEROES_KEY];
|
||||
acc[JOINED_COUNT_KEY] = (_sum$JOINED_COUNT_KEY = sum[JOINED_COUNT_KEY]) !== null && _sum$JOINED_COUNT_KEY !== void 0 ? _sum$JOINED_COUNT_KEY : acc[JOINED_COUNT_KEY];
|
||||
acc[INVITED_COUNT_KEY] = (_sum$INVITED_COUNT_KE = sum[INVITED_COUNT_KEY]) !== null && _sum$INVITED_COUNT_KE !== void 0 ? _sum$INVITED_COUNT_KE : acc[INVITED_COUNT_KEY];
|
||||
}
|
||||
|
||||
// We purposefully do not persist m.typing events.
|
||||
@@ -322,7 +263,7 @@ export class SyncAccumulator {
|
||||
// clobber the one we persisted).
|
||||
|
||||
// Persist the receipts
|
||||
currentData._receipts.consumeEphemeralEvents(data.ephemeral?.events);
|
||||
currentData._receipts.consumeEphemeralEvents((_data$ephemeral = data.ephemeral) === null || _data$ephemeral === void 0 ? void 0 : _data$ephemeral.events);
|
||||
|
||||
// if we got a limited sync, we need to remove all timeline entries or else
|
||||
// we will have gaps in the timeline.
|
||||
@@ -333,18 +274,14 @@ export class SyncAccumulator {
|
||||
// Work out the current state. The deltas need to be applied in the order:
|
||||
// - existing state which didn't come down /sync.
|
||||
// - State events under the 'state' key.
|
||||
// - State events under the 'state_after' key OR state events in the 'timeline' if 'state_after' is not present.
|
||||
data.state?.events?.forEach(e => {
|
||||
// - State events in the 'timeline'.
|
||||
(_data$state = data.state) === null || _data$state === void 0 || (_data$state = _data$state.events) === null || _data$state === void 0 ? void 0 : _data$state.forEach(e => {
|
||||
setState(currentData._currentState, e);
|
||||
});
|
||||
data["org.matrix.msc4222.state_after"]?.events?.forEach(e => {
|
||||
(_data$timeline = data.timeline) === null || _data$timeline === void 0 || (_data$timeline = _data$timeline.events) === null || _data$timeline === void 0 ? void 0 : _data$timeline.forEach((e, index) => {
|
||||
var _data$timeline$prev_b;
|
||||
// this nops if 'e' isn't a state event
|
||||
setState(currentData._currentState, e);
|
||||
});
|
||||
data.timeline?.events?.forEach((e, index) => {
|
||||
if (!data["org.matrix.msc4222.state_after"]) {
|
||||
// this nops if 'e' isn't a state event
|
||||
setState(currentData._currentState, e);
|
||||
}
|
||||
// append the event to the timeline. The back-pagination token
|
||||
// corresponds to the first event in the timeline
|
||||
let transformedEvent;
|
||||
@@ -353,38 +290,17 @@ export class SyncAccumulator {
|
||||
if (transformedEvent.unsigned !== undefined) {
|
||||
transformedEvent.unsigned = Object.assign({}, transformedEvent.unsigned);
|
||||
}
|
||||
const age = e.unsigned?.age;
|
||||
const age = e.unsigned ? e.unsigned.age : e.age;
|
||||
if (age !== undefined) transformedEvent._localTs = Date.now() - age;
|
||||
} else {
|
||||
transformedEvent = e;
|
||||
}
|
||||
currentData._timeline.push({
|
||||
event: transformedEvent,
|
||||
token: index === 0 ? data.timeline.prev_batch ?? null : null
|
||||
token: index === 0 ? (_data$timeline$prev_b = data.timeline.prev_batch) !== null && _data$timeline$prev_b !== void 0 ? _data$timeline$prev_b : null : null
|
||||
});
|
||||
});
|
||||
|
||||
// Prune out any events in our stores that have since expired, do this before we
|
||||
// insert new events.
|
||||
currentData._stickyEvents = currentData._stickyEvents.filter(({
|
||||
expiresTs
|
||||
}) => expiresTs > now);
|
||||
|
||||
// We want this to be fast, so don't worry about duplicate events here. The RoomStickyEventsStore will
|
||||
// process these events into the correct mapped order.
|
||||
if (data.msc4354_sticky?.events) {
|
||||
currentData._stickyEvents = currentData._stickyEvents.concat(data.msc4354_sticky.events.map(event => {
|
||||
// If `duration_ms` exceeds the spec limit of a hour, we cap it.
|
||||
const cappedDuration = Math.min(event.msc4354_sticky.duration_ms, MAX_STICKY_DURATION_MS);
|
||||
// If `origin_server_ts` claims to have been from the future, we still bound it to now.
|
||||
const createdTs = Math.min(event.origin_server_ts, now);
|
||||
return {
|
||||
event,
|
||||
expiresTs: cappedDuration + createdTs
|
||||
};
|
||||
}));
|
||||
}
|
||||
|
||||
// attempt to prune the timeline by jumping between events which have
|
||||
// pagination tokens.
|
||||
if (currentData._timeline.length > this.opts.maxTimelineEntries) {
|
||||
@@ -392,7 +308,7 @@ export class SyncAccumulator {
|
||||
for (let i = startIndex; i < currentData._timeline.length; i++) {
|
||||
if (currentData._timeline[i].token) {
|
||||
// keep all events after this, including this one
|
||||
currentData._timeline = currentData._timeline.slice(i);
|
||||
currentData._timeline = currentData._timeline.slice(i, currentData._timeline.length);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -418,7 +334,6 @@ export class SyncAccumulator {
|
||||
const data = {
|
||||
join: {},
|
||||
invite: {},
|
||||
knock: {},
|
||||
// always empty. This is set by /sync when a room was previously
|
||||
// in 'invite' or 'join'. On fresh startup, the client won't know
|
||||
// about any previous room being in 'invite' or 'join' so we can
|
||||
@@ -435,34 +350,25 @@ export class SyncAccumulator {
|
||||
Object.keys(this.inviteRooms).forEach(roomId => {
|
||||
data.invite[roomId] = this.inviteRooms[roomId];
|
||||
});
|
||||
Object.keys(this.knockRooms).forEach(roomId => {
|
||||
data.knock[roomId] = this.knockRooms[roomId];
|
||||
});
|
||||
Object.keys(this.joinRooms).forEach(roomId => {
|
||||
const roomData = this.joinRooms[roomId];
|
||||
const roomJson = {
|
||||
"ephemeral": {
|
||||
ephemeral: {
|
||||
events: []
|
||||
},
|
||||
"account_data": {
|
||||
account_data: {
|
||||
events: []
|
||||
},
|
||||
"state": {
|
||||
state: {
|
||||
events: []
|
||||
},
|
||||
"org.matrix.msc4222.state_after": {
|
||||
events: []
|
||||
},
|
||||
"timeline": {
|
||||
timeline: {
|
||||
events: [],
|
||||
prev_batch: null
|
||||
},
|
||||
"unread_notifications": roomData._unreadNotifications,
|
||||
"unread_thread_notifications": roomData._unreadThreadNotifications,
|
||||
"summary": roomData._summary,
|
||||
"msc4354_sticky": roomData._stickyEvents?.length ? {
|
||||
events: roomData._stickyEvents.map(e => e.event)
|
||||
} : undefined
|
||||
unread_notifications: roomData._unreadNotifications,
|
||||
unread_thread_notifications: roomData._unreadThreadNotifications,
|
||||
summary: roomData._summary
|
||||
};
|
||||
// Add account data
|
||||
Object.keys(roomData._accountData).forEach(evType => {
|
||||
@@ -483,6 +389,7 @@ export class SyncAccumulator {
|
||||
if (!msgData.token) {
|
||||
return; // this shouldn't happen as we prune constantly.
|
||||
}
|
||||
|
||||
roomJson.timeline.prev_batch = msgData.token;
|
||||
}
|
||||
let transformedEvent;
|
||||
@@ -520,7 +427,7 @@ export class SyncAccumulator {
|
||||
// since we're going back in time, we need to use the previous
|
||||
// state value else we'll break causality. We don't have the
|
||||
// complete previous state event, so we need to create one.
|
||||
const prevStateEvent = deepCopy(timelineEvent);
|
||||
const prevStateEvent = (0, _utils.deepCopy)(timelineEvent);
|
||||
if (prevStateEvent.unsigned) {
|
||||
if (prevStateEvent.unsigned.prev_content) {
|
||||
prevStateEvent.content = prevStateEvent.unsigned.prev_content;
|
||||
@@ -534,11 +441,8 @@ export class SyncAccumulator {
|
||||
Object.keys(roomData._currentState).forEach(evType => {
|
||||
Object.keys(roomData._currentState[evType]).forEach(stateKey => {
|
||||
let ev = roomData._currentState[evType][stateKey];
|
||||
// Push to both fields to provide downgrade compatibility in the sync accumulator db
|
||||
// the code will prefer `state_after` if it is present
|
||||
roomJson["org.matrix.msc4222.state_after"].events.push(ev);
|
||||
// Roll the state back to the value at the start of the timeline if it was changed
|
||||
if (rollBackState[evType] && rollBackState[evType][stateKey]) {
|
||||
// use the reverse clobbered event instead.
|
||||
ev = rollBackState[evType][stateKey];
|
||||
}
|
||||
roomJson.state.events.push(ev);
|
||||
@@ -561,11 +465,8 @@ export class SyncAccumulator {
|
||||
getNextBatchToken() {
|
||||
return this.nextBatch;
|
||||
}
|
||||
removeEventsFromRoom(roomId, eventIds) {
|
||||
this.joinRooms[roomId]._timeline = this.joinRooms[roomId]._timeline.filter(ev => !eventIds.includes(ev.event.event_id));
|
||||
this.joinRooms[roomId]._stickyEvents = this.joinRooms[roomId]._stickyEvents.filter(ev => !eventIds.includes(ev.event.event_id));
|
||||
}
|
||||
}
|
||||
exports.SyncAccumulator = SyncAccumulator;
|
||||
function setState(eventMap, event) {
|
||||
if (event.state_key === null || event.state_key === undefined || !event.type) {
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user