Skip to content

Commit 94f41d7

Browse files
authored
Merge pull request #725 from the-draupnir-project/gnuxie/revision-feedback
Show the time of the last `RoomStateRevision` for each room in the `!draupnir rooms` command. We also do this for policy lists. #718
2 parents 711b9b7 + 30ad9f0 commit 94f41d7

20 files changed

+267
-285
lines changed

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,8 @@
6363
"jsdom": "^24.0.0",
6464
"matrix-appservice-bridge": "^10.3.1",
6565
"matrix-bot-sdk": "npm:@vector-im/matrix-bot-sdk@^0.7.1-element.6",
66-
"matrix-protection-suite": "npm:@gnuxie/matrix-protection-suite@2.8.0",
67-
"matrix-protection-suite-for-matrix-bot-sdk": "npm:@gnuxie/matrix-protection-suite-for-matrix-bot-sdk@2.8.0",
66+
"matrix-protection-suite": "npm:@gnuxie/matrix-protection-suite@2.9.0",
67+
"matrix-protection-suite-for-matrix-bot-sdk": "npm:@gnuxie/matrix-protection-suite-for-matrix-bot-sdk@2.9.0",
6868
"pg": "^8.8.0",
6969
"yaml": "^2.3.2"
7070
},

src/Draupnir.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,13 +298,13 @@ export class Draupnir implements Client, MatrixAdaptorContext {
298298
* This means this is only used in the index.ts.
299299
*/
300300
public async startupComplete(): Promise<void> {
301-
const statusInfo = await draupnirStatusInfo(this);
302301
try {
303302
await this.managementRoomOutput.logMessage(
304303
LogLevel.INFO,
305304
"Draupnir@startup",
306305
"Startup complete. Now monitoring rooms."
307306
);
307+
const statusInfo = draupnirStatusInfo(this);
308308
await sendMatrixEventsFromDeadDocument(
309309
this.clientPlatform.toRoomMessageSender(),
310310
this.managementRoomID,

src/appservice/AppServiceDraupnirManager.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import {
2323
ActionExceptionKind,
2424
ActionResult,
2525
Ok,
26-
PropagationType,
2726
Task,
2827
isError,
2928
} from "matrix-protection-suite";
@@ -412,9 +411,7 @@ async function createFirstList(
412411
if (isError(addRoomResult)) {
413412
return addRoomResult;
414413
}
415-
return await draupnir.protectedRoomsSet.issuerManager.watchList(
416-
PropagationType.Direct,
417-
policyRoom.ok,
418-
{}
414+
return await draupnir.protectedRoomsSet.watchedPolicyRooms.watchPolicyRoomDirectly(
415+
policyRoom.ok
419416
);
420417
}

src/commands/Ban.tsx

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2022 - 2023 Gnuxie <[email protected]>
1+
// Copyright 2022 - 2025 Gnuxie <[email protected]>
22
// Copyright 2019 - 2021 The Matrix.org Foundation C.I.C.
33
//
44
// SPDX-License-Identifier: AFL-3.0 AND Apache-2.0
@@ -16,9 +16,8 @@ import {
1616
Ok,
1717
PolicyRoomManager,
1818
RoomResolver,
19-
PolicyListConfig,
19+
WatchedPolicyRooms,
2020
} from "matrix-protection-suite";
21-
import { findPolicyRoomIDFromShortcode } from "./CreateBanListCommand";
2221
import {
2322
MatrixRoomReference,
2423
MatrixUserID,
@@ -38,6 +37,7 @@ import {
3837
DraupnirContextToCommandContextTranslator,
3938
DraupnirInterfaceAdaptor,
4039
} from "./DraupnirCommandPrerequisites";
40+
import { ResultError } from "@gnuxie/typescript-result";
4141

4242
export async function findPolicyRoomEditorFromRoomReference(
4343
roomResolver: RoomResolver,
@@ -53,7 +53,7 @@ export async function findPolicyRoomEditorFromRoomReference(
5353

5454
export type DraupnirBanCommandContext = {
5555
policyRoomManager: PolicyRoomManager;
56-
issuerManager: PolicyListConfig;
56+
watchedPolicyRooms: WatchedPolicyRooms;
5757
defaultReasons: string[];
5858
roomResolver: RoomResolver;
5959
clientUserID: StringUserID;
@@ -104,10 +104,9 @@ export const DraupnirBanCommand = describeCommand({
104104
},
105105
async executor(
106106
{
107-
issuerManager,
107+
watchedPolicyRooms,
108108
policyRoomManager,
109109
roomResolver,
110-
clientUserID,
111110
}: DraupnirBanCommandContext,
112111
_info: BasicInvocationInformation,
113112
_keywords,
@@ -117,16 +116,19 @@ export const DraupnirBanCommand = describeCommand({
117116
): Promise<ActionResult<string>> {
118117
const policyRoomReference =
119118
typeof policyRoomDesignator === "string"
120-
? await findPolicyRoomIDFromShortcode(
121-
issuerManager,
122-
policyRoomManager,
123-
clientUserID,
124-
policyRoomDesignator
119+
? Ok(
120+
watchedPolicyRooms.findPolicyRoomFromShortcode(policyRoomDesignator)
121+
?.room
125122
)
126123
: Ok(policyRoomDesignator);
127124
if (isError(policyRoomReference)) {
128125
return policyRoomReference;
129126
}
127+
if (policyRoomReference.ok === undefined) {
128+
return ResultError.Result(
129+
`Unable to find a policy room from the shortcode ${policyRoomDesignator.toString()}`
130+
);
131+
}
130132
const policyListEditorResult = await findPolicyRoomEditorFromRoomReference(
131133
roomResolver,
132134
policyRoomManager,
@@ -168,7 +170,7 @@ DraupnirContextToCommandContextTranslator.registerTranslation(
168170
function (draupnir) {
169171
return {
170172
policyRoomManager: draupnir.policyRoomManager,
171-
issuerManager: draupnir.protectedRoomsSet.issuerManager,
173+
watchedPolicyRooms: draupnir.protectedRoomsSet.watchedPolicyRooms,
172174
defaultReasons: draupnir.config.commands.ban.defaultReasons,
173175
roomResolver: draupnir.clientPlatform.toRoomResolver(),
174176
clientUserID: draupnir.clientUserID,

src/commands/CreateBanListCommand.ts

Lines changed: 6 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,9 @@
88
// https://github.com/matrix-org/mjolnir
99
// </text>
1010

11-
import {
12-
ActionError,
13-
ActionResult,
14-
Ok,
15-
PolicyListConfig,
16-
PolicyRoomManager,
17-
PolicyRuleType,
18-
PropagationType,
19-
isError,
20-
} from "matrix-protection-suite";
21-
import { listInfo } from "./StatusCommand";
11+
import { ActionResult, isError } from "matrix-protection-suite";
2212
import { Draupnir } from "../Draupnir";
23-
import {
24-
MatrixRoomID,
25-
StringUserID,
26-
} from "@the-draupnir-project/matrix-basic-types";
13+
import { MatrixRoomID } from "@the-draupnir-project/matrix-basic-types";
2714
import {
2815
BasicInvocationInformation,
2916
ParsedKeywords,
@@ -52,11 +39,10 @@ export async function createList(
5239
if (isError(newList)) {
5340
return newList;
5441
}
55-
const watchResult = await draupnir.protectedRoomsSet.issuerManager.watchList(
56-
PropagationType.Direct,
57-
newList.ok,
58-
{}
59-
);
42+
const watchResult =
43+
await draupnir.protectedRoomsSet.watchedPolicyRooms.watchPolicyRoomDirectly(
44+
newList.ok
45+
);
6046
if (isError(watchResult)) {
6147
return watchResult;
6248
}
@@ -87,36 +73,3 @@ export const DraupnirListCreateCommand = describeCommand({
8773
DraupnirInterfaceAdaptor.describeRenderer(DraupnirListCreateCommand, {
8874
isAlwaysSupposedToUseDefaultRenderer: true,
8975
});
90-
91-
export async function findPolicyRoomIDFromShortcode(
92-
issuerManager: PolicyListConfig,
93-
policyRoomManager: PolicyRoomManager,
94-
editingClientUserID: StringUserID,
95-
shortcode: string
96-
): Promise<ActionResult<MatrixRoomID>> {
97-
const info = await listInfo(issuerManager, policyRoomManager);
98-
const matchingRevisions = info.filter(
99-
(list) => list.revision.shortcode === shortcode
100-
);
101-
if (matchingRevisions.length === 0 || matchingRevisions[0] === undefined) {
102-
return ActionError.Result(
103-
`Could not find a policy room from the shortcode: ${shortcode}`
104-
);
105-
} else if (matchingRevisions.length === 1) {
106-
return Ok(matchingRevisions[0].revision.room);
107-
} else {
108-
const remainingRevisions = matchingRevisions.filter((revision) =>
109-
revision.revision.isAbleToEdit(editingClientUserID, PolicyRuleType.User)
110-
);
111-
if (
112-
remainingRevisions.length !== 1 ||
113-
remainingRevisions[0] === undefined
114-
) {
115-
return ActionError.Result(
116-
`The shortcode ${shortcode} is ambiguous and is currently used by ${remainingRevisions.length} lists.`
117-
);
118-
} else {
119-
return Ok(remainingRevisions[0].revision.room);
120-
}
121-
}
122-
}

src/commands/Rooms.tsx

Lines changed: 85 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import {
1313
ActionExceptionKind,
1414
ActionResult,
1515
Ok,
16+
Revision,
17+
WatchedPolicyRoom,
1618
isError,
1719
} from "matrix-protection-suite";
1820
import {
@@ -29,11 +31,24 @@ import {
2931
import { Draupnir } from "../Draupnir";
3032
import { Result } from "@gnuxie/typescript-result";
3133
import { DraupnirInterfaceAdaptor } from "./DraupnirCommandPrerequisites";
34+
import {
35+
groupWatchedPolicyRoomsByProtectionStatus,
36+
renderPolicyList,
37+
} from "./StatusCommand";
38+
39+
type RoomListItem = {
40+
// used for room pill rendering
41+
room: MatrixRoomID;
42+
mostRecentRevision?: Revision;
43+
};
3244

3345
type ListRoomsCommandInfo = {
34-
joinedAndProtectedRooms: MatrixRoomID[];
35-
joinedAndUnprotectedRooms: MatrixRoomID[];
36-
partedAndProtectedRooms: MatrixRoomID[];
46+
joinedAndProtectedLists: WatchedPolicyRoom[];
47+
joinedAndWatchedLists: WatchedPolicyRoom[];
48+
partedAndWatchedLists: WatchedPolicyRoom[];
49+
joinedAndProtectedRooms: RoomListItem[];
50+
joinedAndUnprotectedRooms: RoomListItem[];
51+
partedAndProtectedRooms: RoomListItem[];
3752
};
3853

3954
export const DraupnirListProtectedRoomsCommand = describeCommand({
@@ -42,25 +57,62 @@ export const DraupnirListProtectedRoomsCommand = describeCommand({
4257
async executor(draupnir: Draupnir): Promise<Result<ListRoomsCommandInfo>> {
4358
const allJoinedRooms = draupnir.clientRooms.currentRevision.allJoinedRooms;
4459
const allProtectedRooms = draupnir.protectedRoomsSet.allProtectedRooms;
60+
const listInfo = groupWatchedPolicyRoomsByProtectionStatus(
61+
draupnir.protectedRoomsSet.watchedPolicyRooms,
62+
draupnir.clientRooms.currentRevision.allJoinedRooms,
63+
draupnir.protectedRoomsSet.allProtectedRooms
64+
);
65+
const makeRoomListItem = (room: MatrixRoomID) => {
66+
const revision = draupnir.protectedRoomsSet.setRoomState.getRevision(
67+
room.toRoomIDOrAlias()
68+
);
69+
if (revision) {
70+
return { room, mostRecentRevision: revision.revisionID };
71+
} else {
72+
return { room };
73+
}
74+
};
4575
return Ok({
46-
joinedAndProtectedRooms: allProtectedRooms.filter((room) =>
47-
allJoinedRooms.includes(room.toRoomIDOrAlias())
48-
),
76+
joinedAndProtectedLists: listInfo.subscribedAndProtectedLists,
77+
joinedAndWatchedLists: listInfo.subscribedLists,
78+
partedAndWatchedLists: listInfo.subscribedButPartedLists,
79+
joinedAndProtectedRooms: allProtectedRooms
80+
.filter((room) => allJoinedRooms.includes(room.toRoomIDOrAlias()))
81+
.map(makeRoomListItem),
4982
joinedAndUnprotectedRooms: allJoinedRooms
5083
.filter(
5184
(roomID) =>
5285
!allProtectedRooms.find((room) => room.toRoomIDOrAlias() === roomID)
5386
)
54-
.map((roomID) => MatrixRoomReference.fromRoomID(roomID)),
55-
partedAndProtectedRooms: allProtectedRooms.filter(
56-
(room) => !allJoinedRooms.includes(room.toRoomIDOrAlias())
57-
),
87+
.map((roomID) =>
88+
makeRoomListItem(MatrixRoomReference.fromRoomID(roomID))
89+
),
90+
partedAndProtectedRooms: allProtectedRooms
91+
.filter((room) => !allJoinedRooms.includes(room.toRoomIDOrAlias()))
92+
.map(makeRoomListItem),
5893
});
5994
},
6095
});
6196

97+
function renderPolicyLists(
98+
rooms: WatchedPolicyRoom[],
99+
options: { name: string }
100+
): DocumentNode {
101+
if (rooms.length === 0) {
102+
return <fragment></fragment>;
103+
}
104+
return (
105+
<details>
106+
<summary>
107+
{options.name} ({rooms.length}):
108+
</summary>
109+
<ul>{rooms.map(renderPolicyList)}</ul>
110+
</details>
111+
);
112+
}
113+
62114
function renderRoomList(
63-
rooms: MatrixRoomID[],
115+
rooms: RoomListItem[],
64116
options: { name: string }
65117
): DocumentNode {
66118
if (rooms.length === 0) {
@@ -72,9 +124,20 @@ function renderRoomList(
72124
{options.name} ({rooms.length}):
73125
</summary>
74126
<ul>
75-
{rooms.map((r) => (
127+
{rooms.map((item) => (
76128
<li>
77-
<a href={r.toPermalink()}>{r.toRoomIDOrAlias()}</a>
129+
<a href={item.room.toPermalink()}>{item.room.toRoomIDOrAlias()}</a>{" "}
130+
{"mostRecentRevision" in item ? (
131+
<fragment>
132+
(last update:{" "}
133+
<code>
134+
{new Date(item.mostRecentRevision.time).toLocaleString()}
135+
</code>
136+
)
137+
</fragment>
138+
) : (
139+
<fragment></fragment>
140+
)}
78141
</li>
79142
))}
80143
</ul>
@@ -94,6 +157,15 @@ DraupnirInterfaceAdaptor.describeRenderer(DraupnirListProtectedRoomsCommand, {
94157
) : (
95158
<fragment></fragment>
96159
)}
160+
{renderPolicyLists(result.ok.joinedAndProtectedLists, {
161+
name: "Joined and protected policy rooms",
162+
})}
163+
{renderPolicyLists(result.ok.joinedAndWatchedLists, {
164+
name: "Joined and watched unprotected policy rooms",
165+
})}
166+
{renderPolicyLists(result.ok.partedAndWatchedLists, {
167+
name: "Parted policy rooms that are still marked as watched",
168+
})}
97169
{renderRoomList(result.ok.joinedAndProtectedRooms, {
98170
name: "Protected rooms",
99171
})}

0 commit comments

Comments
 (0)