From f34144557ab3ff73ba80f583b1716e424a3b2c58 Mon Sep 17 00:00:00 2001 From: N Pex Date: Tue, 31 Jan 2023 09:39:30 +0000 Subject: [PATCH] Room purge fixes --- src/components/Chat.vue | 4 - src/services/matrix.service.js | 269 ++++++++++++--------------------- 2 files changed, 93 insertions(+), 180 deletions(-) diff --git a/src/components/Chat.vue b/src/components/Chat.vue index acecb6c..0469baa 100644 --- a/src/components/Chat.vue +++ b/src/components/Chat.vue @@ -1018,7 +1018,6 @@ export default { }, handleScrolledToTop() { - console.log("@top"); if ( this.timelineWindow && this.timelineWindow.canPaginate(EventTimeline.BACKWARDS) && @@ -1045,7 +1044,6 @@ export default { }, handleScrolledToBottom(scrollToEnd) { - console.log("@bottom"); if ( this.timelineWindow && this.timelineWindow.canPaginate(EventTimeline.FORWARDS) && @@ -1287,7 +1285,6 @@ export default { * Start/restart the timer to Read Receipts. */ restartRRTimer() { - console.log("Restart RR timer"); this.stopRRTimer(); let eventIdFirst = null; @@ -1307,7 +1304,6 @@ export default { }, rrTimerElapsed(eventIdFirst, eventIdLast) { - console.log("RR timer elapsed", eventIdFirst, eventIdLast); this.rrTimer = null; this.sendRR(eventIdFirst, eventIdLast); this.restartRRTimer(); diff --git a/src/services/matrix.service.js b/src/services/matrix.service.js index ffe78fd..4c1a592 100644 --- a/src/services/matrix.service.js +++ b/src/services/matrix.service.js @@ -5,8 +5,8 @@ import { TimelineWindow, EventTimeline } from "matrix-js-sdk"; import util from "../plugins/utils"; import User from "../models/user"; -const LocalStorageCryptoStore = require("matrix-js-sdk/lib/crypto/store/localStorage-crypto-store") - .LocalStorageCryptoStore; +const LocalStorageCryptoStore = + require("matrix-js-sdk/lib/crypto/store/localStorage-crypto-store").LocalStorageCryptoStore; export default { install(Vue, options) { @@ -67,7 +67,7 @@ export default { }, currentUserHomeServer() { - return this.$config.homeServer ? this.$config.homeServer : User.serverName(this.currentUserId); + return this.$config.homeServer ? this.$config.homeServer : User.serverName(this.currentUserId); }, currentRoomId() { @@ -141,18 +141,16 @@ export default { if (user.device_id) { data.device_id = user.device_id; } - promiseLogin = tempMatrixClient - .login("m.login.password", data) - .then((response) => { - var u = Object.assign({}, response); - if (user.is_guest) { - // Copy over needed properties - u = Object.assign(user, response); - } - u.home_server = tempMatrixClient.baseUrl; // Don't use deprecated field from response. - this.$store.commit("setUser", u); - return u; - }); + promiseLogin = tempMatrixClient.login("m.login.password", data).then((response) => { + var u = Object.assign({}, response); + if (user.is_guest) { + // Copy over needed properties + u = Object.assign(user, response); + } + u.home_server = tempMatrixClient.baseUrl; // Don't use deprecated field from response. + this.$store.commit("setUser", u); + return u; + }); } return promiseLogin.then((user) => { @@ -261,11 +259,7 @@ export default { return matrixClient; } else { return new Promise((resolve, reject) => { - matrixClient.once("sync", function( - state, - ignoredprevState, - ignoredres - ) { + matrixClient.once("sync", function (state, ignoredprevState, ignoredres) { console.log(state); // state will be 'PREPARED' when the client is ready to use if (state == "PREPARED") { resolve(matrixClient); @@ -293,11 +287,7 @@ export default { if (this.ready) { return Promise.resolve(this.currentUser); } - return this.$store.dispatch( - "login", - this.currentUser || - new User(this.$config.defaultServer, "", "", true) - ); + return this.$store.dispatch("login", this.currentUser || new User(this.$config.defaultServer, "", "", true)); }, addMatrixClientListeners(client) { @@ -337,13 +327,7 @@ export default { Vue.set( room, "avatar", - room.getAvatarUrl( - this.matrixClient.getHomeserverUrl(), - 80, - 80, - "scale", - true - ) + room.getAvatarUrl(this.matrixClient.getHomeserverUrl(), 80, 80, "scale", true) ); } } @@ -351,26 +335,20 @@ export default { case "m.room.member": { - if ( - this.currentRoom && - event.getRoomId() == this.currentRoom.roomId - ) { + if (this.currentRoom && event.getRoomId() == this.currentRoom.roomId) { // Don't use this.currentRoomId, may be an alias. We need the real id! - if (( - event.getContent().membership == "leave" && - (event.getPrevContent() || {}).membership == "join" && - event.getStateKey() == this.currentUserId && - event.getSender() != this.currentUserId - ) || (event.getContent().membership == "ban" && event.getStateKey() == this.currentUserId)) { + if ( + (event.getContent().membership == "leave" && + (event.getPrevContent() || {}).membership == "join" && + event.getStateKey() == this.currentUserId && + event.getSender() != this.currentUserId) || + (event.getContent().membership == "ban" && event.getStateKey() == this.currentUserId) + ) { // We were kicked or banned // If this is a live event (not just backpaging) then redirect to goodbye! if (this.matrixClientReady) { - const wasPurged = - event.getContent().reason == "Room Deleted"; - this.$navigation.push( - { name: "Goodbye", params: { roomWasPurged: wasPurged } }, - -1 - ); + const wasPurged = event.getContent().reason == "Room Deleted"; + this.$navigation.push({ name: "Goodbye", params: { roomWasPurged: wasPurged } }, -1); } } } @@ -414,15 +392,15 @@ export default { this.$store.commit("setUser", user); // Login again - this.login(user).catch(error => { - if (error.data.errcode ==='M_FORBIDDEN' && this.currentUser.is_guest) { + this.login(user).catch((error) => { + if (error.data.errcode === "M_FORBIDDEN" && this.currentUser.is_guest) { // Guest account and password don't work. We are in a strange state, probably because // of server cleanup of accounts or similar. Wipe account and restart... this.$store.commit("setUser", null); } this.$store.commit("setCurrentRoomId", null); this.$navigation.push({ path: "/login" }, -1); - }) + }); } else { this.$store.commit("setUser", null); this.$store.commit("setCurrentRoomId", null); @@ -435,24 +413,11 @@ export default { // each time! var updatedRooms = this.matrixClient.getVisibleRooms(); updatedRooms = updatedRooms.filter((room) => { - return ( - room.selfMembership && - (room.selfMembership == "invite" || room.selfMembership == "join") - ); + return room.selfMembership && (room.selfMembership == "invite" || room.selfMembership == "join"); }); updatedRooms.forEach((room) => { if (!room.avatar) { - Vue.set( - room, - "avatar", - room.getAvatarUrl( - this.matrixClient.getHomeserverUrl(), - 80, - 80, - "scale", - true - ) - ); + Vue.set(room, "avatar", room.getAvatarUrl(this.matrixClient.getHomeserverUrl(), 80, 80, "scale", true)); } }); Vue.set(this, "rooms", updatedRooms); @@ -491,15 +456,9 @@ export default { var ids = {}; const ret = []; for (const room of this.rooms) { - if ( - room.selfMembership == "join" && - this.getRoomJoinRule(room) == "invite" - ) { + if (room.selfMembership == "join" && this.getRoomJoinRule(room) == "invite") { for (const member of room.getJoinedMembers()) { - if ( - member.userId != this.currentUserId && - !ids[member.userId] - ) { + if (member.userId != this.currentUserId && !ids[member.userId]) { ids[member.userId] = member; ret.push(member); } @@ -516,10 +475,7 @@ export default { getRoomJoinRule(room) { if (room) { - const joinRules = room.currentState.getStateEvents( - "m.room.join_rules", - "" - ); + const joinRules = room.currentState.getStateEvents("m.room.join_rules", ""); return joinRules && joinRules.getContent().join_rule; } return null; @@ -527,14 +483,8 @@ export default { getRoomHistoryVisibility(room) { if (room) { - const historyVisibility = room.currentState.getStateEvents( - "m.room.history_visibility", - "" - ); - return ( - historyVisibility && - historyVisibility.getContent().history_visibility - ); + const historyVisibility = room.currentState.getStateEvents("m.room.history_visibility", ""); + return historyVisibility && historyVisibility.getContent().history_visibility; } return null; }, @@ -551,21 +501,13 @@ export default { kickUser(roomId, userId) { if (this.matrixClient && roomId && userId) { - this.matrixClient.kick( - roomId, - userId, - "" - ) + this.matrixClient.kick(roomId, userId, ""); } }, banUser(roomId, userId) { if (this.matrixClient && roomId && userId) { - this.matrixClient.ban( - roomId, - userId, - "" - ) + this.matrixClient.ban(roomId, userId, ""); } }, @@ -577,20 +519,20 @@ export default { if (powerLevelEvent) { this.matrixClient.setPowerLevel(roomId, userId, 100, powerLevelEvent); } - } + } } }, makeModerator(roomId, userId) { if (this.matrixClient && roomId && userId) { const room = this.getRoom(roomId); - console.log("Room", room); + console.log("Room", room); if (room && room.currentState) { const powerLevelEvent = room.currentState.getStateEvents("m.room.power_levels", ""); if (powerLevelEvent) { this.matrixClient.setPowerLevel(roomId, userId, 50, powerLevelEvent); } - } + } } }, @@ -602,7 +544,7 @@ export default { if (powerLevelEvent) { this.matrixClient.setPowerLevel(roomId, userId, 0, powerLevelEvent); } - } + } } }, @@ -624,22 +566,13 @@ export default { return; } - const timelineWindow = new TimelineWindow( - this.matrixClient, - room.getUnfilteredTimelineSet(), - {} - ); + const timelineWindow = new TimelineWindow(this.matrixClient, room.getUnfilteredTimelineSet(), {}); const self = this; //console.log("Purge: set invite only"); statusCallback(this.$t("room.purge_set_room_state")); this.matrixClient - .sendStateEvent( - roomId, - "m.room.join_rules", - { join_rule: "invite" }, - "" - ) + .sendStateEvent(roomId, "m.room.join_rules", { join_rule: "invite" }, "") .then(() => { //console.log("Purge: forbid guest access"); return this.matrixClient.sendStateEvent( @@ -651,13 +584,9 @@ export default { }) .then(() => { //console.log("Purge: set history visibility to 'joined'"); - return this.matrixClient.sendStateEvent( - roomId, - "m.room.history_visibility", - { - history_visibility: "joined", - } - ); + return this.matrixClient.sendStateEvent(roomId, "m.room.history_visibility", { + history_visibility: "joined", + }); }) .then(() => { //console.log("Purge: create timeline"); @@ -667,11 +596,12 @@ export default { const getMoreIfAvailable = function _getMoreIfAvailable() { if (timelineWindow.canPaginate(EventTimeline.BACKWARDS)) { //console.log("Purge: page back"); - return timelineWindow - .paginate(EventTimeline.BACKWARDS, 100, true, 5) - .then((ignoredsuccess) => { + return timelineWindow.paginate(EventTimeline.BACKWARDS, 100, true, 5).then((gotmore) => { + if (gotmore) { return _getMoreIfAvailable.call(self); - }); + } + return Promise.resolve("Done"); + }); } else { return Promise.resolve("Done"); } @@ -685,18 +615,9 @@ export default { this.matrixClient.setGlobalErrorOnUnknownDevices(false); var redactionPromises = []; timelineWindow.getEvents().forEach((event) => { - if ( - !event.isRedacted() && - !event.isRedaction() && - !event.isState() - ) { + if (!event.isRedacted() && !event.isRedaction() && !event.isState()) { // Redact! - redactionPromises.push( - this.matrixClient.redactEvent( - event.getRoomId(), - event.getId() - ) - ); + redactionPromises.push(this.matrixClient.redactEvent(event.getRoomId(), event.getId())); } }); return Promise.all(redactionPromises); @@ -706,27 +627,44 @@ export default { statusCallback(this.$t("room.purge_removing_members")); var joined = room.getMembersWithMembership("join"); var invited = room.getMembersWithMembership("invite"); - var members = joined.concat(invited); + var allMembers = joined.concat(invited); + + function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); + } - var kickPromises = []; - members.forEach((member) => { - if (member.userId != self.currentUserId) { - kickPromises.push( - this.matrixClient.kick( - roomId, - member.userId, - "Room Deleted" - ) - ); + const kickFirstMember = (members) => { + //console.log(`Kicking ${members.length} members`); + if (members.length == 0) { + return Promise.resolve(true); } - }); - return Promise.all(kickPromises); + const member = members[0]; + if (member.userId == self.currentUserId) { + return kickFirstMember(members.slice(1)); + } else { + // Slight pause to avoid rate limiting. + return sleep(0.1) + .then(() => this.matrixClient.kick(roomId, member.userId, "Room Deleted")) + .catch((error) => { + if (error && error.errcode == "M_LIMIT_EXCEEDED") { + var retry = 1000; + if (error.data) { + const retryIn = error.data.retry_after_ms; + retry = Math.max(retry, retryIn ? retryIn : 0); + } + //console.log("Rate limited, retry in", retry); + return sleep(retry).then(() => kickFirstMember(members)); + } + }) + .finally(() => kickFirstMember(members.slice(1))) + } + }; + + return kickFirstMember(allMembers); }) .then(() => { statusCallback(null); - this.matrixClient.setGlobalErrorOnUnknownDevices( - oldGlobalErrorSetting - ); + this.matrixClient.setGlobalErrorOnUnknownDevices(oldGlobalErrorSetting); return this.leaveRoom(roomId); }) .then(() => { @@ -734,9 +672,7 @@ export default { }) .catch((err) => { console.error("Error purging room", err); - this.matrixClient.setGlobalErrorOnUnknownDevices( - oldGlobalErrorSetting - ); + this.matrixClient.setGlobalErrorOnUnknownDevices(oldGlobalErrorSetting); reject(err); }); }); @@ -814,10 +750,7 @@ export default { * @param {*} userId */ isDirectRoomWith(room, userId) { - if ( - room.getJoinRule() == "invite" && - room.getMembers().length == 2 - ) { + if (room.getJoinRule() == "invite" && room.getMembers().length == 2) { let other = room.getMember(userId); if (other) { if (room.getMyMembership() == "invite" && other.membership == "join") { @@ -889,9 +822,7 @@ export default { return this.matrixClient; }); } else { - const tempMatrixClient = sdk.createClient( - this.$config.defaultServer - ); + const tempMatrixClient = sdk.createClient(this.$config.defaultServer); var tempUserString = this.$store.state.tempuser; var tempUser = null; if (tempUserString) { @@ -971,13 +902,7 @@ export default { }) .then((response) => { if (response.avatar_url) { - response.avatar = matrixClient.mxcUrlToHttp( - response.avatar_url, - 80, - 80, - "scale", - true - ); + response.avatar = matrixClient.mxcUrlToHttp(response.avatar_url, 80, 80, "scale", true); } return Promise.resolve(response); }) @@ -1006,13 +931,7 @@ export default { (roomId.startsWith("!") && room.room_id == roomId) ) { if (room.avatar_url) { - room.avatar = client.mxcUrlToHttp( - room.avatar_url, - 80, - 80, - "scale", - true - ); + room.avatar = client.mxcUrlToHttp(room.avatar_url, 80, 80, "scale", true); } return Promise.resolve(room); } @@ -1059,9 +978,7 @@ export default { }, }); - sdk.setCryptoStoreFactory( - matrixService.createCryptoStore.bind(matrixService) - ); + sdk.setCryptoStoreFactory(matrixService.createCryptoStore.bind(matrixService)); Vue.prototype.$matrix = matrixService; },