4 // - Linux/macOS: matrix__bridge__applicationServicePort=9001 node ./scripts/utils/clean-up-orphaned-matrix-rooms.js
5 // - Windows: set matrix__bridge__applicationServicePort=9001&&node ./scripts/utils/clean-up-orphaned-matrix-rooms.js
9 const assert = require('assert');
10 const shutdown = require('shutdown');
11 const persistence = require('gitter-web-persistence');
12 const { iterableFromMongooseCursor } = require('gitter-web-persistence-utils/lib/mongoose-utils');
13 const mongoReadPrefs = require('gitter-web-persistence-utils/lib/mongo-read-prefs');
15 const installBridge = require('gitter-web-matrix-bridge');
16 const matrixBridge = require('gitter-web-matrix-bridge/lib/matrix-bridge');
17 const MatrixUtils = require('gitter-web-matrix-bridge/lib/matrix-utils');
19 require('../../server/event-listeners').install();
21 const matrixUtils = new MatrixUtils(matrixBridge);
23 const opts = require('yargs')
30 'Delay timeout(in milliseconds) between rooms to shutdown to not overwhelm the homeserver'
33 description: 'Dry-run. Do not execute, just print',
38 .alias('help', 'h').argv;
40 let numberOfRoomsShutdown = 0;
41 let numberOfRoomsIgnored = 0;
42 const failedBridgedRoomShutdowns = [];
44 async function shutdownBridgedMatrixRoom(bridgedRoomEntry) {
46 assert(bridgedRoomEntry.matrixRoomId);
47 assert(bridgedRoomEntry.troupeId);
49 `${opts.dryRun ? 'Dry-run: ' : ''}Shutting down matrixRoomId=${
50 bridgedRoomEntry.matrixRoomId
51 }, gitterRoomId=${bridgedRoomEntry.troupeId}`
55 await matrixUtils.shutdownMatrixRoom(bridgedRoomEntry.matrixRoomId);
57 numberOfRoomsShutdown += 1;
59 // This error occurs for rooms which don't exist or we can't get access to
60 // the room anyway. We don't need to worry about these cases. e.g.
61 // "M_FORBIDDEN: User @gitter-badger:my.matrix.host not in room
62 // !1605079432013:localhost, and room previews are disabled"
63 if (err.errcode === 'M_FORBIDDEN') {
65 `${bridgedRoomEntry.matrixRoomId} is already deleted or we don't have access anymore to delete it so we can just ignore it -> ${err.errcode}: ${err.error}`
67 numberOfRoomsIgnored += 1;
74 async function shutdownOrphanedRooms() {
75 // Find bridged Matrix rooms where the Gitter room (troupe) no longer exists
76 const cursor = await persistence.MatrixBridgedRoom.aggregate([
78 // Lookup troupes._id === matricesbridgedroom.troupeId
81 // Field from MatrixBridgedRoom
82 localField: 'troupeId',
96 .read(mongoReadPrefs.secondaryPreferred)
97 .cursor({ batchSize: 1000, async: true })
100 const iterable = iterableFromMongooseCursor(cursor);
102 for await (let bridgedRoomEntry of iterable) {
104 await shutdownBridgedMatrixRoom(bridgedRoomEntry);
107 `Failed to shutdown matrixRoomId=${bridgedRoomEntry.matrixRoomId}, gitterRoomId=${bridgedRoomEntry.troupeId}`,
111 failedBridgedRoomShutdowns.push(bridgedRoomEntry);
114 // Put a delay between each time we process and shutdown a bridged room
115 // to avoid overwhelming and hitting the rate-limits on the Matrix homeserver
116 if (opts.delay > 0) {
117 await new Promise(resolve => {
118 setTimeout(resolve, opts.delay);
124 async function run() {
127 console.log('Dry-run, nothing will actually be deleted =================');
128 console.log('===========================================================');
131 console.log('Setting up Matrix bridge');
132 await installBridge();
134 console.log('Starting to shutdown orphaned bridged rooms');
135 await shutdownOrphanedRooms();
137 `${numberOfRoomsShutdown} orphaned bridged shutdown! Ignored ${numberOfRoomsIgnored} orphaned rooms which are already deleted.`
140 if (failedBridgedRoomShutdowns.length) {
142 `But some rooms failed to shutdown (${failedBridgedRoomShutdowns.length})`,
143 failedBridgedRoomShutdowns
147 // wait 5 seconds to allow for asynchronous `event-listeners` to finish
148 // This isn't clean but works
149 // https://github.com/troupe/gitter-webapp/issues/580#issuecomment-147445395
150 // https://gitlab.com/gitterHQ/webapp/merge_requests/1605#note_222861592
151 console.log(`Waiting 5 seconds to allow for the asynchronous \`event-listeners\` to finish...`);
152 await new Promise(resolve => setTimeout(resolve, 5000));
154 console.error(err, err.stack);
156 shutdown.shutdownGracefully();