@openclaw/matrix 2026.1.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +59 -0
- package/index.ts +18 -0
- package/openclaw.plugin.json +11 -0
- package/package.json +36 -0
- package/src/actions.ts +185 -0
- package/src/channel.directory.test.ts +56 -0
- package/src/channel.ts +417 -0
- package/src/config-schema.ts +62 -0
- package/src/directory-live.ts +175 -0
- package/src/group-mentions.ts +61 -0
- package/src/matrix/accounts.test.ts +83 -0
- package/src/matrix/accounts.ts +63 -0
- package/src/matrix/actions/client.ts +53 -0
- package/src/matrix/actions/messages.ts +120 -0
- package/src/matrix/actions/pins.ts +70 -0
- package/src/matrix/actions/reactions.ts +84 -0
- package/src/matrix/actions/room.ts +88 -0
- package/src/matrix/actions/summary.ts +77 -0
- package/src/matrix/actions/types.ts +84 -0
- package/src/matrix/actions.ts +15 -0
- package/src/matrix/active-client.ts +11 -0
- package/src/matrix/client/config.ts +165 -0
- package/src/matrix/client/create-client.ts +127 -0
- package/src/matrix/client/logging.ts +35 -0
- package/src/matrix/client/runtime.ts +4 -0
- package/src/matrix/client/shared.ts +169 -0
- package/src/matrix/client/storage.ts +131 -0
- package/src/matrix/client/types.ts +34 -0
- package/src/matrix/client.test.ts +57 -0
- package/src/matrix/client.ts +9 -0
- package/src/matrix/credentials.ts +103 -0
- package/src/matrix/deps.ts +57 -0
- package/src/matrix/format.test.ts +34 -0
- package/src/matrix/format.ts +22 -0
- package/src/matrix/index.ts +11 -0
- package/src/matrix/monitor/allowlist.ts +58 -0
- package/src/matrix/monitor/auto-join.ts +68 -0
- package/src/matrix/monitor/direct.ts +105 -0
- package/src/matrix/monitor/events.ts +103 -0
- package/src/matrix/monitor/handler.ts +645 -0
- package/src/matrix/monitor/index.ts +279 -0
- package/src/matrix/monitor/location.ts +83 -0
- package/src/matrix/monitor/media.test.ts +103 -0
- package/src/matrix/monitor/media.ts +113 -0
- package/src/matrix/monitor/mentions.ts +31 -0
- package/src/matrix/monitor/replies.ts +96 -0
- package/src/matrix/monitor/room-info.ts +58 -0
- package/src/matrix/monitor/rooms.ts +43 -0
- package/src/matrix/monitor/threads.ts +64 -0
- package/src/matrix/monitor/types.ts +39 -0
- package/src/matrix/poll-types.test.ts +22 -0
- package/src/matrix/poll-types.ts +157 -0
- package/src/matrix/probe.ts +70 -0
- package/src/matrix/send/client.ts +63 -0
- package/src/matrix/send/formatting.ts +92 -0
- package/src/matrix/send/media.ts +220 -0
- package/src/matrix/send/targets.test.ts +102 -0
- package/src/matrix/send/targets.ts +144 -0
- package/src/matrix/send/types.ts +109 -0
- package/src/matrix/send.test.ts +172 -0
- package/src/matrix/send.ts +255 -0
- package/src/onboarding.ts +432 -0
- package/src/outbound.ts +53 -0
- package/src/resolve-targets.ts +89 -0
- package/src/runtime.ts +14 -0
- package/src/tool-actions.ts +160 -0
- package/src/types.ts +95 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import type { MatrixClient } from "@vector-im/matrix-bot-sdk";
|
|
2
|
+
|
|
3
|
+
type DirectMessageCheck = {
|
|
4
|
+
roomId: string;
|
|
5
|
+
senderId?: string;
|
|
6
|
+
selfUserId?: string;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
type DirectRoomTrackerOptions = {
|
|
10
|
+
log?: (message: string) => void;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const DM_CACHE_TTL_MS = 30_000;
|
|
14
|
+
|
|
15
|
+
export function createDirectRoomTracker(
|
|
16
|
+
client: MatrixClient,
|
|
17
|
+
opts: DirectRoomTrackerOptions = {},
|
|
18
|
+
) {
|
|
19
|
+
const log = opts.log ?? (() => {});
|
|
20
|
+
let lastDmUpdateMs = 0;
|
|
21
|
+
let cachedSelfUserId: string | null = null;
|
|
22
|
+
const memberCountCache = new Map<string, { count: number; ts: number }>();
|
|
23
|
+
|
|
24
|
+
const ensureSelfUserId = async (): Promise<string | null> => {
|
|
25
|
+
if (cachedSelfUserId) return cachedSelfUserId;
|
|
26
|
+
try {
|
|
27
|
+
cachedSelfUserId = await client.getUserId();
|
|
28
|
+
} catch {
|
|
29
|
+
cachedSelfUserId = null;
|
|
30
|
+
}
|
|
31
|
+
return cachedSelfUserId;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const refreshDmCache = async (): Promise<void> => {
|
|
35
|
+
const now = Date.now();
|
|
36
|
+
if (now - lastDmUpdateMs < DM_CACHE_TTL_MS) return;
|
|
37
|
+
lastDmUpdateMs = now;
|
|
38
|
+
try {
|
|
39
|
+
await client.dms.update();
|
|
40
|
+
} catch (err) {
|
|
41
|
+
log(`matrix: dm cache refresh failed (${String(err)})`);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const resolveMemberCount = async (roomId: string): Promise<number | null> => {
|
|
46
|
+
const cached = memberCountCache.get(roomId);
|
|
47
|
+
const now = Date.now();
|
|
48
|
+
if (cached && now - cached.ts < DM_CACHE_TTL_MS) {
|
|
49
|
+
return cached.count;
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
const members = await client.getJoinedRoomMembers(roomId);
|
|
53
|
+
const count = members.length;
|
|
54
|
+
memberCountCache.set(roomId, { count, ts: now });
|
|
55
|
+
return count;
|
|
56
|
+
} catch (err) {
|
|
57
|
+
log(`matrix: dm member count failed room=${roomId} (${String(err)})`);
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const hasDirectFlag = async (roomId: string, userId?: string): Promise<boolean> => {
|
|
63
|
+
const target = userId?.trim();
|
|
64
|
+
if (!target) return false;
|
|
65
|
+
try {
|
|
66
|
+
const state = await client.getRoomStateEvent(roomId, "m.room.member", target);
|
|
67
|
+
return state?.is_direct === true;
|
|
68
|
+
} catch {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
isDirectMessage: async (params: DirectMessageCheck): Promise<boolean> => {
|
|
75
|
+
const { roomId, senderId } = params;
|
|
76
|
+
await refreshDmCache();
|
|
77
|
+
|
|
78
|
+
if (client.dms.isDm(roomId)) {
|
|
79
|
+
log(`matrix: dm detected via m.direct room=${roomId}`);
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const memberCount = await resolveMemberCount(roomId);
|
|
84
|
+
if (memberCount === 2) {
|
|
85
|
+
log(`matrix: dm detected via member count room=${roomId} members=${memberCount}`);
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const selfUserId = params.selfUserId ?? (await ensureSelfUserId());
|
|
90
|
+
const directViaState =
|
|
91
|
+
(await hasDirectFlag(roomId, senderId)) || (await hasDirectFlag(roomId, selfUserId ?? ""));
|
|
92
|
+
if (directViaState) {
|
|
93
|
+
log(`matrix: dm detected via member state room=${roomId}`);
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
log(
|
|
98
|
+
`matrix: dm check room=${roomId} result=group members=${
|
|
99
|
+
memberCount ?? "unknown"
|
|
100
|
+
}`,
|
|
101
|
+
);
|
|
102
|
+
return false;
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import type { MatrixClient } from "@vector-im/matrix-bot-sdk";
|
|
2
|
+
import type { PluginRuntime } from "openclaw/plugin-sdk";
|
|
3
|
+
|
|
4
|
+
import type { MatrixAuth } from "../client.js";
|
|
5
|
+
import type { MatrixRawEvent } from "./types.js";
|
|
6
|
+
import { EventType } from "./types.js";
|
|
7
|
+
|
|
8
|
+
export function registerMatrixMonitorEvents(params: {
|
|
9
|
+
client: MatrixClient;
|
|
10
|
+
auth: MatrixAuth;
|
|
11
|
+
logVerboseMessage: (message: string) => void;
|
|
12
|
+
warnedEncryptedRooms: Set<string>;
|
|
13
|
+
warnedCryptoMissingRooms: Set<string>;
|
|
14
|
+
logger: { warn: (meta: Record<string, unknown>, message: string) => void };
|
|
15
|
+
formatNativeDependencyHint: PluginRuntime["system"]["formatNativeDependencyHint"];
|
|
16
|
+
onRoomMessage: (roomId: string, event: MatrixRawEvent) => void | Promise<void>;
|
|
17
|
+
}): void {
|
|
18
|
+
const {
|
|
19
|
+
client,
|
|
20
|
+
auth,
|
|
21
|
+
logVerboseMessage,
|
|
22
|
+
warnedEncryptedRooms,
|
|
23
|
+
warnedCryptoMissingRooms,
|
|
24
|
+
logger,
|
|
25
|
+
formatNativeDependencyHint,
|
|
26
|
+
onRoomMessage,
|
|
27
|
+
} = params;
|
|
28
|
+
|
|
29
|
+
client.on("room.message", onRoomMessage);
|
|
30
|
+
|
|
31
|
+
client.on("room.encrypted_event", (roomId: string, event: MatrixRawEvent) => {
|
|
32
|
+
const eventId = event?.event_id ?? "unknown";
|
|
33
|
+
const eventType = event?.type ?? "unknown";
|
|
34
|
+
logVerboseMessage(`matrix: encrypted event room=${roomId} type=${eventType} id=${eventId}`);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
client.on("room.decrypted_event", (roomId: string, event: MatrixRawEvent) => {
|
|
38
|
+
const eventId = event?.event_id ?? "unknown";
|
|
39
|
+
const eventType = event?.type ?? "unknown";
|
|
40
|
+
logVerboseMessage(`matrix: decrypted event room=${roomId} type=${eventType} id=${eventId}`);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
client.on(
|
|
44
|
+
"room.failed_decryption",
|
|
45
|
+
async (roomId: string, event: MatrixRawEvent, error: Error) => {
|
|
46
|
+
logger.warn(
|
|
47
|
+
{ roomId, eventId: event.event_id, error: error.message },
|
|
48
|
+
"Failed to decrypt message",
|
|
49
|
+
);
|
|
50
|
+
logVerboseMessage(
|
|
51
|
+
`matrix: failed decrypt room=${roomId} id=${event.event_id ?? "unknown"} error=${error.message}`,
|
|
52
|
+
);
|
|
53
|
+
},
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
client.on("room.invite", (roomId: string, event: MatrixRawEvent) => {
|
|
57
|
+
const eventId = event?.event_id ?? "unknown";
|
|
58
|
+
const sender = event?.sender ?? "unknown";
|
|
59
|
+
const isDirect = (event?.content as { is_direct?: boolean } | undefined)?.is_direct === true;
|
|
60
|
+
logVerboseMessage(
|
|
61
|
+
`matrix: invite room=${roomId} sender=${sender} direct=${String(isDirect)} id=${eventId}`,
|
|
62
|
+
);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
client.on("room.join", (roomId: string, event: MatrixRawEvent) => {
|
|
66
|
+
const eventId = event?.event_id ?? "unknown";
|
|
67
|
+
logVerboseMessage(`matrix: join room=${roomId} id=${eventId}`);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
client.on("room.event", (roomId: string, event: MatrixRawEvent) => {
|
|
71
|
+
const eventType = event?.type ?? "unknown";
|
|
72
|
+
if (eventType === EventType.RoomMessageEncrypted) {
|
|
73
|
+
logVerboseMessage(
|
|
74
|
+
`matrix: encrypted raw event room=${roomId} id=${event?.event_id ?? "unknown"}`,
|
|
75
|
+
);
|
|
76
|
+
if (auth.encryption !== true && !warnedEncryptedRooms.has(roomId)) {
|
|
77
|
+
warnedEncryptedRooms.add(roomId);
|
|
78
|
+
const warning =
|
|
79
|
+
"matrix: encrypted event received without encryption enabled; set channels.matrix.encryption=true and verify the device to decrypt";
|
|
80
|
+
logger.warn({ roomId }, warning);
|
|
81
|
+
}
|
|
82
|
+
if (auth.encryption === true && !client.crypto && !warnedCryptoMissingRooms.has(roomId)) {
|
|
83
|
+
warnedCryptoMissingRooms.add(roomId);
|
|
84
|
+
const hint = formatNativeDependencyHint({
|
|
85
|
+
packageName: "@matrix-org/matrix-sdk-crypto-nodejs",
|
|
86
|
+
manager: "pnpm",
|
|
87
|
+
downloadCommand:
|
|
88
|
+
"node node_modules/@matrix-org/matrix-sdk-crypto-nodejs/download-lib.js",
|
|
89
|
+
});
|
|
90
|
+
const warning = `matrix: encryption enabled but crypto is unavailable; ${hint}`;
|
|
91
|
+
logger.warn({ roomId }, warning);
|
|
92
|
+
}
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
if (eventType === EventType.RoomMember) {
|
|
96
|
+
const membership = (event?.content as { membership?: string } | undefined)?.membership;
|
|
97
|
+
const stateKey = (event as { state_key?: string }).state_key ?? "";
|
|
98
|
+
logVerboseMessage(
|
|
99
|
+
`matrix: member event room=${roomId} stateKey=${stateKey} membership=${membership ?? "unknown"}`,
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
}
|