@elizaos/plugin-matrix 2.0.0-beta.1 → 2.0.3-beta.3

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/dist/index.js DELETED
@@ -1,981 +0,0 @@
1
- // src/index.ts
2
- import { getConnectorAccountManager, logger as logger2 } from "@elizaos/core";
3
-
4
- // src/accounts.ts
5
- var DEFAULT_MATRIX_ACCOUNT_ID = "default";
6
- function stringSetting(runtime, key) {
7
- const value = runtime.getSetting(key);
8
- return typeof value === "string" && value.trim() ? value.trim() : undefined;
9
- }
10
- function characterConfig(runtime) {
11
- const settings = runtime.character?.settings;
12
- const raw = settings?.matrix;
13
- return raw && typeof raw === "object" ? raw : {};
14
- }
15
- function parseAccountsJson(runtime) {
16
- const raw = stringSetting(runtime, "MATRIX_ACCOUNTS");
17
- if (!raw)
18
- return {};
19
- try {
20
- const parsed = JSON.parse(raw);
21
- if (Array.isArray(parsed)) {
22
- return Object.fromEntries(parsed.filter((item) => Boolean(item) && typeof item === "object").map((item) => [normalizeMatrixAccountId(item.accountId ?? item.id), item]));
23
- }
24
- return parsed && typeof parsed === "object" ? parsed : {};
25
- } catch {
26
- return {};
27
- }
28
- }
29
- function allAccountConfigs(runtime) {
30
- return {
31
- ...characterConfig(runtime).accounts ?? {},
32
- ...parseAccountsJson(runtime)
33
- };
34
- }
35
- function accountConfig(runtime, accountId) {
36
- const accounts = allAccountConfigs(runtime);
37
- return accounts[accountId] ?? accounts[normalizeMatrixAccountId(accountId)] ?? {};
38
- }
39
- function boolValue(value, fallback = false) {
40
- if (typeof value === "boolean")
41
- return value;
42
- if (typeof value === "string")
43
- return value.trim().toLowerCase() === "true";
44
- return fallback;
45
- }
46
- function roomsValue(value) {
47
- if (Array.isArray(value)) {
48
- return value.map((room) => String(room).trim()).filter(Boolean);
49
- }
50
- if (typeof value === "string") {
51
- return value.split(",").map((room) => room.trim()).filter(Boolean);
52
- }
53
- return [];
54
- }
55
- function normalizeMatrixAccountId(accountId) {
56
- if (typeof accountId !== "string")
57
- return DEFAULT_MATRIX_ACCOUNT_ID;
58
- const trimmed = accountId.trim();
59
- return trimmed || DEFAULT_MATRIX_ACCOUNT_ID;
60
- }
61
- function listMatrixAccountIds(runtime) {
62
- const ids = new Set;
63
- const config = characterConfig(runtime);
64
- if (stringSetting(runtime, "MATRIX_ACCESS_TOKEN") || config.homeserver && config.userId && config.accessToken) {
65
- ids.add(DEFAULT_MATRIX_ACCOUNT_ID);
66
- }
67
- for (const id of Object.keys(allAccountConfigs(runtime))) {
68
- ids.add(normalizeMatrixAccountId(id));
69
- }
70
- return Array.from(ids.size ? ids : new Set([DEFAULT_MATRIX_ACCOUNT_ID])).sort((a, b) => a.localeCompare(b));
71
- }
72
- function resolveDefaultMatrixAccountId(runtime) {
73
- const requested = stringSetting(runtime, "MATRIX_DEFAULT_ACCOUNT_ID") ?? stringSetting(runtime, "MATRIX_ACCOUNT_ID");
74
- if (requested)
75
- return normalizeMatrixAccountId(requested);
76
- const ids = listMatrixAccountIds(runtime);
77
- return ids.includes(DEFAULT_MATRIX_ACCOUNT_ID) ? DEFAULT_MATRIX_ACCOUNT_ID : ids[0];
78
- }
79
- function readMatrixAccountId(...sources) {
80
- for (const source of sources) {
81
- if (!source || typeof source !== "object")
82
- continue;
83
- const record = source;
84
- const parameters = record.parameters && typeof record.parameters === "object" ? record.parameters : {};
85
- const data = record.data && typeof record.data === "object" ? record.data : {};
86
- const metadata = record.metadata && typeof record.metadata === "object" ? record.metadata : {};
87
- const matrix = data.matrix && typeof data.matrix === "object" ? data.matrix : {};
88
- const value = record.accountId ?? parameters.accountId ?? data.accountId ?? matrix.accountId ?? metadata.accountId;
89
- if (typeof value === "string" && value.trim())
90
- return normalizeMatrixAccountId(value);
91
- }
92
- return;
93
- }
94
- function resolveMatrixAccountSettings(runtime, requestedAccountId) {
95
- const accountId = normalizeMatrixAccountId(requestedAccountId ?? resolveDefaultMatrixAccountId(runtime));
96
- const base = characterConfig(runtime);
97
- const account = accountConfig(runtime, accountId);
98
- const allowEnv = accountId === DEFAULT_MATRIX_ACCOUNT_ID;
99
- return {
100
- accountId,
101
- homeserver: account.homeserver ?? base.homeserver ?? (allowEnv ? stringSetting(runtime, "MATRIX_HOMESERVER") : undefined) ?? "",
102
- userId: account.userId ?? base.userId ?? (allowEnv ? stringSetting(runtime, "MATRIX_USER_ID") : undefined) ?? "",
103
- accessToken: account.accessToken ?? base.accessToken ?? (allowEnv ? stringSetting(runtime, "MATRIX_ACCESS_TOKEN") : undefined) ?? "",
104
- deviceId: account.deviceId ?? base.deviceId ?? (allowEnv ? stringSetting(runtime, "MATRIX_DEVICE_ID") : undefined),
105
- rooms: roomsValue(account.rooms ?? base.rooms ?? (allowEnv ? stringSetting(runtime, "MATRIX_ROOMS") : undefined)),
106
- autoJoin: boolValue(account.autoJoin ?? base.autoJoin ?? (allowEnv ? stringSetting(runtime, "MATRIX_AUTO_JOIN") : undefined)),
107
- encryption: boolValue(account.encryption ?? base.encryption ?? (allowEnv ? stringSetting(runtime, "MATRIX_ENCRYPTION") : undefined)),
108
- requireMention: boolValue(account.requireMention ?? base.requireMention ?? (allowEnv ? stringSetting(runtime, "MATRIX_REQUIRE_MENTION") : undefined)),
109
- enabled: boolValue(account.enabled ?? base.enabled, true)
110
- };
111
- }
112
- // src/service.ts
113
- import {
114
- logger,
115
- Service
116
- } from "@elizaos/core";
117
- import * as sdk from "matrix-js-sdk";
118
-
119
- // src/types.ts
120
- var MAX_MATRIX_MESSAGE_LENGTH = 4000;
121
- var MATRIX_SERVICE_NAME = "matrix";
122
- var MatrixEventTypes;
123
- ((MatrixEventTypes2) => {
124
- MatrixEventTypes2["MESSAGE_RECEIVED"] = "MATRIX_MESSAGE_RECEIVED";
125
- MatrixEventTypes2["MESSAGE_SENT"] = "MATRIX_MESSAGE_SENT";
126
- MatrixEventTypes2["ROOM_JOINED"] = "MATRIX_ROOM_JOINED";
127
- MatrixEventTypes2["ROOM_LEFT"] = "MATRIX_ROOM_LEFT";
128
- MatrixEventTypes2["INVITE_RECEIVED"] = "MATRIX_INVITE_RECEIVED";
129
- MatrixEventTypes2["REACTION_RECEIVED"] = "MATRIX_REACTION_RECEIVED";
130
- MatrixEventTypes2["TYPING_RECEIVED"] = "MATRIX_TYPING_RECEIVED";
131
- MatrixEventTypes2["SYNC_COMPLETE"] = "MATRIX_SYNC_COMPLETE";
132
- MatrixEventTypes2["CONNECTION_READY"] = "MATRIX_CONNECTION_READY";
133
- MatrixEventTypes2["CONNECTION_LOST"] = "MATRIX_CONNECTION_LOST";
134
- })(MatrixEventTypes ||= {});
135
- function isValidMatrixUserId(userId) {
136
- return /^@[^:]+:.+$/.test(userId);
137
- }
138
- function isValidMatrixRoomId(roomId) {
139
- return /^![^:]+:.+$/.test(roomId);
140
- }
141
- function isValidMatrixRoomAlias(alias) {
142
- return /^#[^:]+:.+$/.test(alias);
143
- }
144
- function getMatrixLocalpart(matrixId) {
145
- const match = matrixId.match(/^[@#!]([^:]+):/);
146
- return match ? match[1] : matrixId;
147
- }
148
- function getMatrixServerpart(matrixId) {
149
- const match = matrixId.match(/:(.+)$/);
150
- return match ? match[1] : "";
151
- }
152
- function getMatrixUserDisplayName(user) {
153
- return user.displayName || getMatrixLocalpart(user.userId);
154
- }
155
- function matrixMxcToHttp(mxcUrl, homeserver) {
156
- if (!mxcUrl.startsWith("mxc://")) {
157
- return;
158
- }
159
- const [serverName, mediaId] = mxcUrl.slice(6).split("/");
160
- if (!serverName || !mediaId) {
161
- return;
162
- }
163
- const base = homeserver.replace(/\/$/, "");
164
- return `${base}/_matrix/media/v3/download/${serverName}/${mediaId}`;
165
- }
166
-
167
- class MatrixPluginError extends Error {
168
- constructor(message) {
169
- super(message);
170
- this.name = "MatrixPluginError";
171
- }
172
- }
173
-
174
- class MatrixServiceNotInitializedError extends MatrixPluginError {
175
- constructor(message = "Matrix service is not initialized") {
176
- super(message);
177
- this.name = "MatrixServiceNotInitializedError";
178
- }
179
- }
180
-
181
- class MatrixNotConnectedError extends MatrixPluginError {
182
- constructor(message = "Matrix client is not connected") {
183
- super(message);
184
- this.name = "MatrixNotConnectedError";
185
- }
186
- }
187
-
188
- class MatrixConfigurationError extends MatrixPluginError {
189
- settingName;
190
- constructor(message, settingName) {
191
- super(message);
192
- this.name = "MatrixConfigurationError";
193
- this.settingName = settingName;
194
- }
195
- }
196
-
197
- class MatrixApiError extends MatrixPluginError {
198
- errcode;
199
- constructor(message, errcode) {
200
- super(message);
201
- this.name = "MatrixApiError";
202
- this.errcode = errcode;
203
- }
204
- }
205
-
206
- // src/service.ts
207
- function normalizeSearchQuery(query) {
208
- return query.trim().toLowerCase();
209
- }
210
- function matrixRoomSearchText(room) {
211
- return [room.roomId, room.name, room.topic, room.canonicalAlias].filter((value) => typeof value === "string" && value.length > 0).join(" ").toLowerCase();
212
- }
213
- function scoreMatrixRoom(room, query) {
214
- const normalized = normalizeSearchQuery(query);
215
- if (!normalized) {
216
- return 0.4;
217
- }
218
- const candidates = [room.roomId, room.canonicalAlias, room.name].filter((value) => typeof value === "string" && value.length > 0);
219
- if (candidates.some((candidate) => candidate.toLowerCase() === normalized)) {
220
- return 1;
221
- }
222
- if (candidates.some((candidate) => candidate.toLowerCase().includes(normalized))) {
223
- return 0.85;
224
- }
225
- return matrixRoomSearchText(room).includes(normalized) ? 0.65 : 0;
226
- }
227
- function matrixRoomToConnectorTarget(room, score = 0.5, accountId = DEFAULT_MATRIX_ACCOUNT_ID) {
228
- const label = room.name || room.canonicalAlias || room.roomId;
229
- return {
230
- target: {
231
- source: MATRIX_SERVICE_NAME,
232
- accountId,
233
- channelId: room.roomId
234
- },
235
- label,
236
- kind: room.isDirect ? "user" : "room",
237
- description: room.topic || `${room.memberCount} Matrix member${room.memberCount === 1 ? "" : "s"}`,
238
- score,
239
- contexts: ["social", "connectors"],
240
- metadata: {
241
- accountId,
242
- roomId: room.roomId,
243
- canonicalAlias: room.canonicalAlias,
244
- isEncrypted: room.isEncrypted,
245
- isDirect: room.isDirect,
246
- memberCount: room.memberCount
247
- }
248
- };
249
- }
250
- function normalizeConnectorLimit(limit, fallback = 50) {
251
- if (!Number.isFinite(limit) || !limit || limit <= 0) {
252
- return fallback;
253
- }
254
- return Math.min(Math.floor(limit), 200);
255
- }
256
- async function readStoredMessageMemories(runtime, roomId, limit) {
257
- return runtime.getMemories({
258
- tableName: "messages",
259
- roomId,
260
- limit,
261
- orderBy: "createdAt",
262
- orderDirection: "desc"
263
- });
264
- }
265
- async function readStoredMessagesForTargets(runtime, targets, limit) {
266
- const roomIds = Array.from(new Set(targets.map((target) => target.target.roomId).filter((id) => Boolean(id))));
267
- const chunks = await Promise.all(roomIds.map((roomId) => readStoredMessageMemories(runtime, roomId, limit)));
268
- return chunks.flat().sort((left, right) => (right.createdAt ?? 0) - (left.createdAt ?? 0)).slice(0, limit);
269
- }
270
- function filterMemoriesByQuery(memories, query, limit) {
271
- const normalized = query.trim().toLowerCase();
272
- if (!normalized) {
273
- return memories.slice(0, limit);
274
- }
275
- return memories.filter((memory) => {
276
- const text = typeof memory.content?.text === "string" ? memory.content.text : "";
277
- return text.toLowerCase().includes(normalized);
278
- }).slice(0, limit);
279
- }
280
- function extractMatrixSendOptions(content, target) {
281
- const data = content.data;
282
- const matrixData = data?.matrix && typeof data.matrix === "object" ? data.matrix : data;
283
- return {
284
- threadId: target.threadId || (typeof matrixData?.threadId === "string" ? matrixData.threadId : undefined),
285
- replyTo: typeof matrixData?.replyTo === "string" ? matrixData.replyTo : undefined,
286
- formatted: matrixData?.formatted === true
287
- };
288
- }
289
-
290
- class MatrixService extends Service {
291
- static serviceType = MATRIX_SERVICE_NAME;
292
- capabilityDescription = "Matrix messaging service for chat communication";
293
- states = new Map;
294
- defaultAccountId = DEFAULT_MATRIX_ACCOUNT_ID;
295
- static async start(runtime) {
296
- const service = new MatrixService;
297
- await service.initialize(runtime);
298
- return service;
299
- }
300
- static async stopRuntime(runtime) {
301
- const service = runtime.getService(MATRIX_SERVICE_NAME);
302
- if (service) {
303
- await service.stop();
304
- }
305
- }
306
- static registerSendHandlers(runtime, service, accountId = service.getAccountId(runtime)) {
307
- accountId = normalizeMatrixAccountId(accountId);
308
- const sendHandler = async (handlerRuntime, target, content) => {
309
- await service.handleSendMessage(handlerRuntime, target, content);
310
- return;
311
- };
312
- if (typeof runtime.registerMessageConnector === "function") {
313
- const registration = {
314
- source: MATRIX_SERVICE_NAME,
315
- accountId,
316
- label: "Matrix",
317
- capabilities: [
318
- "send_message",
319
- "send_thread_reply",
320
- "send_formatted_message",
321
- "react_to_message",
322
- "list_rooms",
323
- "join_room"
324
- ],
325
- supportedTargetKinds: ["room", "channel", "thread", "user"],
326
- contexts: ["social", "connectors"],
327
- description: "Send messages to joined Matrix rooms, aliases, encrypted rooms, and known direct-message rooms.",
328
- metadata: {
329
- accountId,
330
- service: MATRIX_SERVICE_NAME
331
- },
332
- sendHandler,
333
- resolveTargets: async (query) => {
334
- const rooms = await service.getJoinedRooms(accountId);
335
- return rooms.map((room) => ({ room, score: scoreMatrixRoom(room, query) })).filter(({ score }) => score > 0).sort((left, right) => right.score - left.score).slice(0, 10).map(({ room, score }) => matrixRoomToConnectorTarget(room, score, accountId));
336
- },
337
- listRecentTargets: async () => (await service.getJoinedRooms(accountId)).slice(0, 10).map((room) => matrixRoomToConnectorTarget(room, 0.5, accountId)),
338
- listRooms: async () => (await service.getJoinedRooms(accountId)).map((room) => matrixRoomToConnectorTarget(room, 0.5, accountId)),
339
- fetchMessages: async (context, params) => {
340
- const limit = normalizeConnectorLimit(params?.limit);
341
- const target = params?.target ?? context.target;
342
- if (target?.roomId) {
343
- return readStoredMessageMemories(context.runtime, target.roomId, limit);
344
- }
345
- const targets = (await service.getJoinedRooms(accountId)).slice(0, 10).map((room) => matrixRoomToConnectorTarget(room, 0.5, accountId));
346
- return readStoredMessagesForTargets(context.runtime, targets, limit);
347
- },
348
- searchMessages: async (context, params) => {
349
- const limit = normalizeConnectorLimit(params?.limit);
350
- const target = params?.target ?? context.target;
351
- const messages = target?.roomId ? await readStoredMessageMemories(context.runtime, target.roomId, Math.max(limit, 100)) : await readStoredMessagesForTargets(context.runtime, (await service.getJoinedRooms(accountId)).slice(0, 10).map((room) => matrixRoomToConnectorTarget(room, 0.5, accountId)), Math.max(limit, 100));
352
- return filterMemoriesByQuery(messages, params.query, limit);
353
- },
354
- reactHandler: async (handlerRuntime, params) => {
355
- const target = params.target ?? { source: MATRIX_SERVICE_NAME };
356
- const room = target.roomId ? await handlerRuntime.getRoom(target.roomId) : null;
357
- const roomId = String(target.channelId ?? room?.channelId ?? "").trim();
358
- const mutationParams = params;
359
- const eventId = String(mutationParams.eventId ?? params.messageId ?? "").trim();
360
- const emoji = String(params.emoji ?? "").trim();
361
- if (!roomId || !eventId || !emoji) {
362
- throw new Error("Matrix reactHandler requires room, event id, and emoji");
363
- }
364
- const result = await service.sendReaction(roomId, eventId, emoji, accountId);
365
- if (!result.success) {
366
- throw new Error(result.error || "Matrix reaction failed");
367
- }
368
- },
369
- joinHandler: async (_handlerRuntime, params) => {
370
- const membershipParams = params;
371
- const roomIdOrAlias = String(membershipParams.roomIdOrAlias ?? params.alias ?? params.invite ?? params.channelId ?? params.roomId ?? "").trim();
372
- if (!roomIdOrAlias) {
373
- throw new Error("Matrix joinHandler requires a room ID or alias");
374
- }
375
- await service.joinRoom(roomIdOrAlias, accountId);
376
- },
377
- leaveHandler: async (handlerRuntime, params) => {
378
- const target = params.target ?? { source: MATRIX_SERVICE_NAME };
379
- const room = target.roomId ? await handlerRuntime.getRoom(target.roomId) : null;
380
- const roomId = String(params?.roomId ?? params?.channelId ?? target.channelId ?? room?.channelId ?? "");
381
- if (!roomId) {
382
- throw new Error("Matrix leaveHandler requires a room ID");
383
- }
384
- await service.leaveRoom(roomId, accountId);
385
- },
386
- getChatContext: async (target, context) => {
387
- const room = target.roomId ? await context.runtime.getRoom(target.roomId) : null;
388
- const channelId = String(target.channelId ?? room?.channelId ?? "").trim();
389
- const joinedRoom = (await service.getJoinedRooms(accountId)).find((candidate) => candidate.roomId === channelId || candidate.canonicalAlias === channelId);
390
- if (!joinedRoom) {
391
- return null;
392
- }
393
- return {
394
- target: {
395
- source: MATRIX_SERVICE_NAME,
396
- accountId,
397
- channelId: joinedRoom.roomId,
398
- roomId: target.roomId
399
- },
400
- label: joinedRoom.name || joinedRoom.canonicalAlias || joinedRoom.roomId,
401
- summary: joinedRoom.topic,
402
- metadata: {
403
- accountId,
404
- roomId: joinedRoom.roomId,
405
- canonicalAlias: joinedRoom.canonicalAlias,
406
- isEncrypted: joinedRoom.isEncrypted,
407
- isDirect: joinedRoom.isDirect,
408
- memberCount: joinedRoom.memberCount
409
- }
410
- };
411
- },
412
- getUserContext: async (entityId, context) => {
413
- if (typeof context.runtime.getEntityById !== "function") {
414
- return null;
415
- }
416
- const entity = await context.runtime.getEntityById(String(entityId));
417
- if (!entity) {
418
- return null;
419
- }
420
- return {
421
- entityId,
422
- label: entity.names?.[0],
423
- aliases: entity.names,
424
- handles: {},
425
- metadata: entity.metadata
426
- };
427
- }
428
- };
429
- runtime.registerMessageConnector(registration);
430
- return;
431
- }
432
- runtime.registerSendHandler(MATRIX_SERVICE_NAME, sendHandler);
433
- }
434
- async initialize(runtime) {
435
- this.runtime = runtime;
436
- this.defaultAccountId = normalizeMatrixAccountId(resolveDefaultMatrixAccountId(runtime));
437
- const accountIds = listMatrixAccountIds(runtime);
438
- for (const accountId of accountIds) {
439
- const settings = this.loadSettings(accountId);
440
- if (settings.enabled === false) {
441
- continue;
442
- }
443
- this.validateSettings(settings);
444
- const state = {
445
- accountId: normalizeMatrixAccountId(settings.accountId),
446
- settings,
447
- client: sdk.createClient({
448
- baseUrl: settings.homeserver,
449
- userId: settings.userId,
450
- accessToken: settings.accessToken,
451
- deviceId: settings.deviceId
452
- }),
453
- connected: false,
454
- syncing: false
455
- };
456
- this.states.set(state.accountId, state);
457
- this.setupEventHandlers(state);
458
- await this.connect(state);
459
- MatrixService.registerSendHandlers(runtime, this, state.accountId);
460
- logger.info(`Matrix service initialized for ${settings.userId} on ${settings.homeserver}`);
461
- }
462
- if (this.states.size === 0) {
463
- const settings = this.loadSettings(this.defaultAccountId);
464
- this.validateSettings(settings);
465
- }
466
- }
467
- loadSettings(accountId) {
468
- return resolveMatrixAccountSettings(this.runtime, accountId);
469
- }
470
- validateSettings(settings) {
471
- if (!settings.homeserver) {
472
- throw new MatrixConfigurationError("MATRIX_HOMESERVER is required", "MATRIX_HOMESERVER");
473
- }
474
- if (!settings.userId) {
475
- throw new MatrixConfigurationError("MATRIX_USER_ID is required", "MATRIX_USER_ID");
476
- }
477
- if (!settings.accessToken) {
478
- throw new MatrixConfigurationError("MATRIX_ACCESS_TOKEN is required", "MATRIX_ACCESS_TOKEN");
479
- }
480
- }
481
- setupEventHandlers(state) {
482
- state.client.on(sdk.ClientEvent.Sync, (syncState) => {
483
- if (syncState === "PREPARED") {
484
- state.syncing = true;
485
- logger.info("Matrix sync complete");
486
- this.runtime.emitEvent("MATRIX_SYNC_COMPLETE" /* SYNC_COMPLETE */, {
487
- runtime: this.runtime,
488
- accountId: state.accountId
489
- });
490
- }
491
- });
492
- state.client.on(sdk.RoomEvent.Timeline, (event, room, toStartOfTimeline) => {
493
- if (toStartOfTimeline)
494
- return;
495
- if (event.getType() !== "m.room.message")
496
- return;
497
- if (event.getSender() === state.settings.userId)
498
- return;
499
- this.handleRoomMessage(state, event, room);
500
- });
501
- state.client.on(sdk.RoomMemberEvent.Membership, (event, member) => {
502
- if (member.userId !== state.settings.userId)
503
- return;
504
- if (member.membership === "invite" && state.settings.autoJoin) {
505
- const roomId = event.getRoomId();
506
- if (roomId) {
507
- logger.info(`Auto-joining room ${roomId}`);
508
- state.client.joinRoom(roomId).catch((err) => {
509
- logger.error(`Failed to auto-join room: ${err.message}`);
510
- });
511
- }
512
- }
513
- });
514
- }
515
- handleRoomMessage(state, event, room) {
516
- const content = event.getContent();
517
- const msgType = content.msgtype;
518
- if (msgType !== "m.text")
519
- return;
520
- const roomId = event.getRoomId();
521
- if (!roomId || !room)
522
- return;
523
- if (state.settings.requireMention) {
524
- const body = content.body || "";
525
- const localpart = getMatrixLocalpart(state.settings.userId);
526
- const mentionPattern = new RegExp(`@?${localpart}`, "i");
527
- if (!mentionPattern.test(body)) {
528
- return;
529
- }
530
- }
531
- const sender = event.getSender();
532
- const senderMember = room.getMember(sender || "");
533
- const senderInfo = {
534
- userId: sender || "",
535
- displayName: senderMember?.name,
536
- avatarUrl: senderMember?.getMxcAvatarUrl() || undefined
537
- };
538
- const relatesTo = content["m.relates_to"];
539
- const isEdit = relatesTo?.rel_type === "m.replace";
540
- const threadId = relatesTo?.rel_type === "m.thread" ? relatesTo.event_id : undefined;
541
- const replyTo = relatesTo?.["m.in_reply_to"]?.event_id;
542
- const message = {
543
- eventId: event.getId() || "",
544
- roomId,
545
- sender: sender || "",
546
- senderInfo,
547
- content: content.body || "",
548
- msgType,
549
- formattedBody: content.formatted_body,
550
- timestamp: event.getTs(),
551
- threadId,
552
- replyTo,
553
- isEdit,
554
- replacesEventId: isEdit ? relatesTo?.event_id : undefined
555
- };
556
- const matrixRoom = {
557
- roomId,
558
- name: room.name,
559
- topic: room.currentState.getStateEvents("m.room.topic", "")?.getContent()?.topic,
560
- canonicalAlias: room.getCanonicalAlias() || undefined,
561
- isEncrypted: room.hasEncryptionStateEvent(),
562
- isDirect: state.client.getAccountData(sdk.EventType.Direct)?.getContent()?.[sender || ""]?.includes(roomId) || false,
563
- memberCount: room.getJoinedMemberCount()
564
- };
565
- logger.debug(`Matrix message from ${senderInfo.displayName || sender} in ${room.name || roomId}: ${message.content.slice(0, 50)}...`);
566
- this.runtime.emitEvent("MATRIX_MESSAGE_RECEIVED" /* MESSAGE_RECEIVED */, {
567
- message,
568
- room: matrixRoom,
569
- runtime: this.runtime,
570
- accountId: state.accountId
571
- });
572
- }
573
- async connect(state) {
574
- await state.client.startClient({ initialSyncLimit: 10 });
575
- state.connected = true;
576
- await new Promise((resolve) => {
577
- const listener = (syncState) => {
578
- if (syncState === "PREPARED") {
579
- state.client.removeListener(sdk.ClientEvent.Sync, listener);
580
- resolve();
581
- }
582
- };
583
- state.client.on(sdk.ClientEvent.Sync, listener);
584
- });
585
- for (const room of state.settings.rooms) {
586
- try {
587
- await this.joinRoom(room, state.accountId);
588
- } catch (err) {
589
- logger.warn(`Failed to join room ${room}: ${err}`);
590
- }
591
- }
592
- }
593
- async stop() {
594
- for (const state of this.states.values()) {
595
- state.client.stopClient();
596
- state.connected = false;
597
- state.syncing = false;
598
- }
599
- logger.info("Matrix service stopped");
600
- }
601
- isConnected() {
602
- const legacy = this;
603
- const states = this.states ?? new Map;
604
- if (states.size === 0 && typeof legacy.connected === "boolean") {
605
- return legacy.connected && (legacy.syncing ?? true);
606
- }
607
- return Array.from(states.values()).some((state) => state.connected && state.syncing);
608
- }
609
- getAccountId(runtime) {
610
- const legacy = this;
611
- const states = this.states ?? new Map;
612
- if (states.size === 0 && legacy.settings?.accountId) {
613
- return normalizeMatrixAccountId(legacy.settings.accountId);
614
- }
615
- return normalizeMatrixAccountId(this.defaultAccountId !== DEFAULT_MATRIX_ACCOUNT_ID ? this.defaultAccountId : runtime ? resolveDefaultMatrixAccountId(runtime) : this.defaultAccountId);
616
- }
617
- getUserId() {
618
- return this.getState().settings.userId;
619
- }
620
- getHomeserver() {
621
- return this.getState().settings.homeserver;
622
- }
623
- async getJoinedRooms(accountId) {
624
- const state = this.getState(accountId);
625
- const rooms = state.client.getRooms();
626
- return rooms.filter((room) => room.getMyMembership() === "join").map((room) => ({
627
- roomId: room.roomId,
628
- name: room.name,
629
- topic: room.currentState.getStateEvents("m.room.topic", "")?.getContent()?.topic,
630
- canonicalAlias: room.getCanonicalAlias() || undefined,
631
- isEncrypted: room.hasEncryptionStateEvent(),
632
- isDirect: false,
633
- memberCount: room.getJoinedMemberCount()
634
- }));
635
- }
636
- async sendMessage(text, options) {
637
- const state = this.getState(options?.accountId);
638
- if (!state.connected || !state.syncing) {
639
- throw new MatrixNotConnectedError;
640
- }
641
- const roomId = options?.roomId;
642
- if (!roomId) {
643
- return { success: false, error: "Room ID is required" };
644
- }
645
- let resolvedRoomId = roomId;
646
- if (isValidMatrixRoomAlias(roomId)) {
647
- const resolved = await state.client.getRoomIdForAlias(roomId);
648
- resolvedRoomId = resolved.room_id;
649
- }
650
- const content = {
651
- msgtype: sdk.MsgType.Text,
652
- body: text
653
- };
654
- if (options?.formatted) {
655
- content.format = "org.matrix.custom.html";
656
- content.formatted_body = text;
657
- }
658
- if (options?.threadId || options?.replyTo) {
659
- content["m.relates_to"] = {};
660
- if (options.threadId) {
661
- content["m.relates_to"].rel_type = sdk.RelationType.Thread;
662
- content["m.relates_to"].event_id = options.threadId;
663
- }
664
- if (options.replyTo) {
665
- content["m.relates_to"]["m.in_reply_to"] = {
666
- event_id: options.replyTo
667
- };
668
- }
669
- }
670
- const response = await state.client.sendMessage(resolvedRoomId, content);
671
- const eventId = response.event_id;
672
- this.runtime.emitEvent("MATRIX_MESSAGE_SENT" /* MESSAGE_SENT */, {
673
- roomId: resolvedRoomId,
674
- eventId,
675
- content: text,
676
- runtime: this.runtime,
677
- accountId: state.accountId
678
- });
679
- return {
680
- success: true,
681
- eventId,
682
- roomId: resolvedRoomId
683
- };
684
- }
685
- async sendReaction(roomId, eventId, emoji, accountId) {
686
- const state = this.getState(accountId);
687
- if (!state.connected || !state.syncing) {
688
- throw new MatrixNotConnectedError;
689
- }
690
- const content = {
691
- "m.relates_to": {
692
- rel_type: sdk.RelationType.Annotation,
693
- event_id: eventId,
694
- key: emoji
695
- }
696
- };
697
- const response = await state.client.sendEvent(roomId, sdk.EventType.Reaction, content);
698
- return {
699
- success: true,
700
- eventId: response.event_id,
701
- roomId
702
- };
703
- }
704
- async joinRoom(roomIdOrAlias, accountId) {
705
- const state = this.getState(accountId);
706
- if (!state.connected || !state.syncing) {
707
- throw new MatrixNotConnectedError;
708
- }
709
- const response = await state.client.joinRoom(roomIdOrAlias);
710
- const roomId = response.roomId;
711
- logger.info(`Joined room ${roomId}`);
712
- this.runtime.emitEvent("MATRIX_ROOM_JOINED" /* ROOM_JOINED */, {
713
- room: { roomId },
714
- runtime: this.runtime,
715
- accountId: state.accountId
716
- });
717
- return roomId;
718
- }
719
- async leaveRoom(roomId, accountId) {
720
- const state = this.getState(accountId);
721
- if (!state.connected || !state.syncing) {
722
- throw new MatrixNotConnectedError;
723
- }
724
- await state.client.leave(roomId);
725
- logger.info(`Left room ${roomId}`);
726
- this.runtime.emitEvent("MATRIX_ROOM_LEFT" /* ROOM_LEFT */, {
727
- roomId,
728
- runtime: this.runtime,
729
- accountId: state.accountId
730
- });
731
- }
732
- async sendTyping(roomId, typing, timeout = 30000, accountId) {
733
- const state = this.getState(accountId);
734
- if (!state.connected || !state.syncing) {
735
- return;
736
- }
737
- await state.client.sendTyping(roomId, typing, timeout);
738
- }
739
- async sendReadReceipt(roomId, eventId, accountId) {
740
- const state = this.getState(accountId);
741
- if (!state.connected || !state.syncing) {
742
- return;
743
- }
744
- await state.client.sendReadReceipt(new sdk.MatrixEvent({ event_id: eventId, room_id: roomId }));
745
- }
746
- async sendRoomMessage(roomIdOrAlias, content) {
747
- const text = typeof content.text === "string" ? content.text.trim() : "";
748
- if (!text) {
749
- return;
750
- }
751
- await this.sendMessage(text, {
752
- accountId: readMatrixAccountId(content) ?? this.getAccountId(),
753
- roomId: roomIdOrAlias
754
- });
755
- }
756
- async sendDirectMessage(roomIdOrAlias, content) {
757
- await this.sendRoomMessage(roomIdOrAlias, content);
758
- }
759
- async handleSendMessage(runtime, target, content) {
760
- const requestedAccountId = normalizeMatrixAccountId(target.accountId ?? readMatrixAccountId(content, target) ?? this.getAccountId());
761
- this.getState(requestedAccountId);
762
- const text = typeof content.text === "string" ? content.text.trim() : "";
763
- if (!text) {
764
- return;
765
- }
766
- const room = target.roomId ? await runtime.getRoom(target.roomId) : null;
767
- const roomIdOrAlias = String(target.channelId || room?.channelId || (typeof target.roomId === "string" && (isValidMatrixRoomId(target.roomId) || isValidMatrixRoomAlias(target.roomId)) ? target.roomId : "")).trim();
768
- if (!roomIdOrAlias) {
769
- throw new Error("Matrix target is missing a room ID or alias");
770
- }
771
- await this.sendMessage(text, {
772
- accountId: requestedAccountId,
773
- roomId: roomIdOrAlias,
774
- ...extractMatrixSendOptions(content, target)
775
- });
776
- }
777
- getState(accountId = this.defaultAccountId) {
778
- const normalized = normalizeMatrixAccountId(accountId);
779
- const states = this.states ?? new Map;
780
- const state = states.get(normalized);
781
- if (state) {
782
- return state;
783
- }
784
- const legacy = this;
785
- if (legacy.settings) {
786
- return {
787
- accountId: normalizeMatrixAccountId(legacy.settings.accountId ?? normalized),
788
- settings: legacy.settings,
789
- client: legacy.client ?? {},
790
- connected: legacy.connected ?? true,
791
- syncing: legacy.syncing ?? true
792
- };
793
- }
794
- throw new Error(`Matrix account '${normalized}' is not available in this service instance`);
795
- }
796
- }
797
- // src/connector-account-provider.ts
798
- var MATRIX_PROVIDER_ID = "matrix";
799
- function accountKey(settings) {
800
- if (settings.homeserver && settings.userId) {
801
- return `${settings.homeserver}/${settings.userId}`;
802
- }
803
- return normalizeMatrixAccountId(settings.accountId);
804
- }
805
- function toConnectorAccount(settings) {
806
- const now = Date.now();
807
- const configured = Boolean(settings.homeserver && settings.userId && settings.accessToken);
808
- return {
809
- id: normalizeMatrixAccountId(settings.accountId),
810
- provider: MATRIX_PROVIDER_ID,
811
- label: settings.userId || settings.accountId,
812
- role: "OWNER",
813
- purpose: ["messaging"],
814
- accessGate: "open",
815
- status: settings.enabled !== false && configured ? "connected" : "disabled",
816
- externalId: accountKey(settings),
817
- displayHandle: settings.userId || undefined,
818
- createdAt: now,
819
- updatedAt: now,
820
- metadata: {
821
- homeserver: settings.homeserver ?? "",
822
- userId: settings.userId ?? "",
823
- deviceId: settings.deviceId ?? "",
824
- encryption: settings.encryption ?? false,
825
- autoJoin: settings.autoJoin ?? false
826
- }
827
- };
828
- }
829
- function createMatrixConnectorAccountProvider(runtime) {
830
- return {
831
- provider: MATRIX_PROVIDER_ID,
832
- label: "Matrix",
833
- listAccounts: async (_manager) => {
834
- const ids = listMatrixAccountIds(runtime);
835
- if (ids.length === 0) {
836
- return [
837
- toConnectorAccount(resolveMatrixAccountSettings(runtime, DEFAULT_MATRIX_ACCOUNT_ID))
838
- ];
839
- }
840
- return ids.map((id) => toConnectorAccount(resolveMatrixAccountSettings(runtime, id)));
841
- },
842
- createAccount: async (input, _manager) => {
843
- return {
844
- ...input,
845
- provider: MATRIX_PROVIDER_ID,
846
- role: input.role ?? "OWNER",
847
- purpose: input.purpose ?? ["messaging"],
848
- accessGate: input.accessGate ?? "open",
849
- status: input.status ?? "pending"
850
- };
851
- },
852
- patchAccount: async (_accountId, patch, _manager) => {
853
- return { ...patch, provider: MATRIX_PROVIDER_ID };
854
- },
855
- deleteAccount: async (_accountId, _manager) => {}
856
- };
857
- }
858
-
859
- // src/workflow-credential-provider.ts
860
- import { Service as Service2 } from "@elizaos/core";
861
- var WORKFLOW_CREDENTIAL_PROVIDER_TYPE = "workflow_credential_provider";
862
- var SUPPORTED = ["matrixApi"];
863
-
864
- class MatrixWorkflowCredentialProvider extends Service2 {
865
- static serviceType = WORKFLOW_CREDENTIAL_PROVIDER_TYPE;
866
- capabilityDescription = "Supplies Matrix credentials to the workflow plugin.";
867
- static async start(runtime) {
868
- return new MatrixWorkflowCredentialProvider(runtime);
869
- }
870
- async stop() {}
871
- async resolve(_userId, credType) {
872
- if (credType !== "matrixApi")
873
- return null;
874
- const accessToken = this.runtime.getSetting("MATRIX_ACCESS_TOKEN");
875
- const homeserver = this.runtime.getSetting("MATRIX_HOMESERVER");
876
- if (!accessToken?.trim() || !homeserver?.trim())
877
- return null;
878
- return {
879
- status: "credential_data",
880
- data: { accessToken: accessToken.trim(), homeserverUrl: homeserver.trim() }
881
- };
882
- }
883
- checkCredentialTypes(credTypes) {
884
- return {
885
- supported: credTypes.filter((t) => SUPPORTED.includes(t)),
886
- unsupported: credTypes.filter((t) => !SUPPORTED.includes(t))
887
- };
888
- }
889
- }
890
-
891
- // src/index.ts
892
- var matrixPlugin = {
893
- name: "matrix",
894
- description: "Matrix messaging integration plugin for ElizaOS with E2EE support",
895
- services: [MatrixService, MatrixWorkflowCredentialProvider],
896
- actions: [],
897
- providers: [],
898
- tests: [],
899
- autoEnable: {
900
- connectorKeys: ["matrix"]
901
- },
902
- init: async (_config, runtime) => {
903
- try {
904
- const manager = getConnectorAccountManager(runtime);
905
- manager.registerProvider(createMatrixConnectorAccountProvider(runtime));
906
- } catch (err) {
907
- logger2.warn({
908
- src: "plugin:matrix",
909
- err: err instanceof Error ? err.message : String(err)
910
- }, "Failed to register Matrix provider with ConnectorAccountManager");
911
- }
912
- const homeserver = runtime.getSetting("MATRIX_HOMESERVER");
913
- const userId = runtime.getSetting("MATRIX_USER_ID");
914
- const accessToken = runtime.getSetting("MATRIX_ACCESS_TOKEN");
915
- logger2.info("=".repeat(60));
916
- logger2.info("Matrix Plugin Configuration");
917
- logger2.info("=".repeat(60));
918
- logger2.info(` Homeserver: ${homeserver ? `✓ ${homeserver}` : "✗ Missing (required)"}`);
919
- logger2.info(` User ID: ${userId ? `✓ ${userId}` : "✗ Missing (required)"}`);
920
- logger2.info(` Access Token: ${accessToken ? "✓ Set" : "✗ Missing (required)"}`);
921
- logger2.info("=".repeat(60));
922
- const missing = [];
923
- if (!homeserver)
924
- missing.push("MATRIX_HOMESERVER");
925
- if (!userId)
926
- missing.push("MATRIX_USER_ID");
927
- if (!accessToken)
928
- missing.push("MATRIX_ACCESS_TOKEN");
929
- if (missing.length > 0) {
930
- logger2.warn(`Matrix plugin: Missing required configuration: ${missing.join(", ")}`);
931
- }
932
- const deviceId = runtime.getSetting("MATRIX_DEVICE_ID");
933
- const rooms = runtime.getSetting("MATRIX_ROOMS");
934
- const autoJoin = runtime.getSetting("MATRIX_AUTO_JOIN");
935
- const encryption = runtime.getSetting("MATRIX_ENCRYPTION");
936
- const requireMention = runtime.getSetting("MATRIX_REQUIRE_MENTION");
937
- if (deviceId) {
938
- logger2.info(` Device ID: ${deviceId}`);
939
- }
940
- if (rooms) {
941
- logger2.info(` Auto-join Rooms: ${rooms}`);
942
- }
943
- if (autoJoin === "true") {
944
- logger2.info(" Auto-join Invites: ✓ Enabled");
945
- }
946
- if (encryption === "true") {
947
- logger2.info(" End-to-End Encryption: ✓ Enabled");
948
- }
949
- if (requireMention === "true") {
950
- logger2.info(" Require Mention: ✓ Enabled (will only respond to mentions in rooms)");
951
- }
952
- }
953
- };
954
- var src_default = matrixPlugin;
955
- export {
956
- resolveMatrixAccountSettings,
957
- resolveDefaultMatrixAccountId,
958
- readMatrixAccountId,
959
- normalizeMatrixAccountId,
960
- matrixMxcToHttp,
961
- listMatrixAccountIds,
962
- isValidMatrixUserId,
963
- isValidMatrixRoomId,
964
- isValidMatrixRoomAlias,
965
- getMatrixUserDisplayName,
966
- getMatrixServerpart,
967
- getMatrixLocalpart,
968
- src_default as default,
969
- MatrixServiceNotInitializedError,
970
- MatrixService,
971
- MatrixPluginError,
972
- MatrixNotConnectedError,
973
- MatrixEventTypes,
974
- MatrixConfigurationError,
975
- MatrixApiError,
976
- MAX_MATRIX_MESSAGE_LENGTH,
977
- MATRIX_SERVICE_NAME,
978
- DEFAULT_MATRIX_ACCOUNT_ID
979
- };
980
-
981
- //# debugId=2890053EB234064664756E2164756E21