koishi-plugin-chat-patch 5.5.0 → 5.6.0

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.
Files changed (44) hide show
  1. package/client/vue/index.vue +8 -1
  2. package/client/web/dist/assets/{Chat-DTEsebnY.css → Chat-BXVWj5Bd.css} +1 -1
  3. package/client/web/dist/assets/{Chat-Clqxn7CB.js → Chat-m4-U5uHa.js} +4 -4
  4. package/client/web/dist/assets/{MsgBody-C0TKsXhC.js → MsgBody-CzrxyiFO.js} +1 -1
  5. package/client/web/dist/assets/MsgBody.vue_vue_type_script_setup_true_lang-BBF1uxm6.js +67 -0
  6. package/client/web/dist/assets/{index-BA37EQGa.js → index-EJ2CcODr.js} +72 -78
  7. package/client/web/dist/assets/index-X9hFOAza.css +1 -0
  8. package/client/web/dist/index.html +2 -2
  9. package/client/web/src/App.vue +7 -0
  10. package/client/web/src/assets/css/chat.css +6 -4
  11. package/client/web/src/assets/l10n/zh-CN.po +0 -6
  12. package/client/web/src/components/History.vue +27 -0
  13. package/client/web/src/components/MsgBody.vue +18 -9
  14. package/client/web/src/function/connect.ts +208 -46
  15. package/client/web/src/function/msg.ts +8 -6
  16. package/client/web/src/function/option.ts +1 -1
  17. package/client/web/src/function/satori-model.ts +20 -2
  18. package/client/web/src/function/satori.ts +64 -0
  19. package/client/web/src/pages/Chat.vue +0 -4
  20. package/client/web/src/pages/Friends.vue +7 -9
  21. package/client/web/src/pages/Messages.vue +6 -7
  22. package/client/web/src/pages/options/OptDev.vue +1 -25
  23. package/client/web/src/pages/options/OptFunction.vue +23 -0
  24. package/client/web/tsconfig.tsbuildinfo +1 -1
  25. package/dist/index.js +1 -1
  26. package/dist/style.css +1 -1
  27. package/lib/bootstrap.d.ts +6 -0
  28. package/lib/database.d.ts +5 -2
  29. package/lib/gateway.d.ts +46 -0
  30. package/lib/index.js +1468 -131
  31. package/lib/recorder.d.ts +3 -5
  32. package/lib/satori.d.ts +3 -0
  33. package/lib/types.d.ts +17 -0
  34. package/package.json +4 -1
  35. package/src/bootstrap.ts +5 -10
  36. package/src/database.ts +745 -662
  37. package/src/gateway.ts +259 -0
  38. package/src/index.ts +63 -53
  39. package/src/recorder.ts +97 -77
  40. package/src/satori.ts +19 -0
  41. package/src/server.d.ts +26 -26
  42. package/src/types.ts +19 -0
  43. package/client/web/dist/assets/MsgBody.vue_vue_type_script_setup_true_lang-CCdBwlN_.js +0 -67
  44. package/client/web/dist/assets/index-B71pPwae.css +0 -1
package/lib/index.js CHANGED
@@ -365,25 +365,93 @@ function isUsableGroupContact(item) {
365
365
  return true;
366
366
  }
367
367
  __name(isUsableGroupContact, "isUsableGroupContact");
368
+ var sharedDatabases = /* @__PURE__ */ new Map();
369
+ async function acquireSharedDatabase(dir) {
370
+ let shared = sharedDatabases.get(dir);
371
+ if (shared?.closing) {
372
+ await shared.closing.catch(() => void 0);
373
+ shared = sharedDatabases.get(dir);
374
+ }
375
+ if (!shared) {
376
+ shared = {
377
+ dir,
378
+ db: new import_level.Level(dir, { keyEncoding: "utf8", valueEncoding: "utf8" }),
379
+ refs: 0,
380
+ opened: false
381
+ };
382
+ sharedDatabases.set(dir, shared);
383
+ }
384
+ shared.refs += 1;
385
+ return shared;
386
+ }
387
+ __name(acquireSharedDatabase, "acquireSharedDatabase");
388
+ async function releaseSharedDatabase(shared) {
389
+ shared.refs -= 1;
390
+ if (shared.refs > 0) return false;
391
+ if (shared.closing) {
392
+ await shared.closing;
393
+ return true;
394
+ }
395
+ if (!shared.opened) {
396
+ if (sharedDatabases.get(shared.dir) === shared) sharedDatabases.delete(shared.dir);
397
+ return true;
398
+ }
399
+ const closing = shared.db.close().catch(() => void 0).finally(() => {
400
+ shared.opened = false;
401
+ if (sharedDatabases.get(shared.dir) === shared) sharedDatabases.delete(shared.dir);
402
+ });
403
+ shared.closing = closing;
404
+ await closing;
405
+ return true;
406
+ }
407
+ __name(releaseSharedDatabase, "releaseSharedDatabase");
368
408
  var _ChatDatabase = class _ChatDatabase {
369
409
  constructor(ctx, config, logger) {
370
410
  this.ctx = ctx;
371
411
  this.config = config;
372
412
  this.logger = logger;
373
- this.opened = false;
374
- const dir = import_node_path.default.resolve(ctx.baseDir, "data", "chat-patch", "db");
375
- this.db = new import_level.Level(dir, { keyEncoding: "utf8", valueEncoding: "utf8" });
413
+ this.dir = import_node_path.default.resolve(ctx.baseDir, "data", "chat-patch", "db");
414
+ }
415
+ get db() {
416
+ if (!this.shared) throw new Error("LevelDB is not initialized");
417
+ return this.shared.db;
376
418
  }
377
419
  async initialize() {
378
- await this.db.open();
379
- this.opened = true;
380
- this.logger.logInfo("LevelDB 已打开:", this.db.location);
420
+ this.shared = await acquireSharedDatabase(this.dir);
421
+ try {
422
+ await this.ensureOpen();
423
+ this.logger.logInfo("LevelDB 已打开:", this.shared.db.location);
424
+ } catch (error) {
425
+ await this.releaseShared();
426
+ throw error;
427
+ }
381
428
  }
382
429
  async dispose() {
383
- if (!this.opened) return;
384
- await this.db.close();
385
- this.opened = false;
386
- this.logger.logInfo("LevelDB closed");
430
+ const closed = await this.releaseShared();
431
+ if (closed) this.logger.logInfo("LevelDB closed");
432
+ }
433
+ async ensureOpen() {
434
+ const shared = this.shared;
435
+ if (!shared || shared.opened) return;
436
+ if (shared.opening) {
437
+ await shared.opening;
438
+ return;
439
+ }
440
+ const opening = shared.db.open().then(() => {
441
+ shared.opened = true;
442
+ });
443
+ shared.opening = opening;
444
+ try {
445
+ await opening;
446
+ } finally {
447
+ shared.opening = void 0;
448
+ }
449
+ }
450
+ async releaseShared() {
451
+ const shared = this.shared;
452
+ this.shared = void 0;
453
+ if (shared) return releaseSharedDatabase(shared);
454
+ return false;
387
455
  }
388
456
  async clearAll() {
389
457
  await this.db.clear();
@@ -842,100 +910,129 @@ var ChatDatabase = _ChatDatabase;
842
910
 
843
911
  // src/recorder.ts
844
912
  var MESSAGE_TYPES = /* @__PURE__ */ new Set(["message", "message-created"]);
913
+ function getObject(value) {
914
+ return typeof value === "object" && value !== null ? value : {};
915
+ }
916
+ __name(getObject, "getObject");
917
+ function getString2(value) {
918
+ return typeof value === "string" ? value : "";
919
+ }
920
+ __name(getString2, "getString");
921
+ function getNumber(value) {
922
+ const num = Number(value);
923
+ return Number.isFinite(num) ? num : 0;
924
+ }
925
+ __name(getNumber, "getNumber");
845
926
  function normalizeGroupId2(value) {
846
927
  return String(value);
847
928
  }
848
929
  __name(normalizeGroupId2, "normalizeGroupId");
930
+ function isPrivateChannelType3(value) {
931
+ const num = Number(value);
932
+ if (Number.isFinite(num)) return num === 1;
933
+ const text = String(value ?? "").toLowerCase();
934
+ return text === "direct" || text === "private";
935
+ }
936
+ __name(isPrivateChannelType3, "isPrivateChannelType");
849
937
  var _Recorder = class _Recorder {
850
- constructor(ctx, config, database, media, contactCache, logger) {
851
- this.ctx = ctx;
938
+ constructor(config, database, media, contactCache, logger) {
852
939
  this.config = config;
853
940
  this.database = database;
854
941
  this.media = media;
855
942
  this.contactCache = contactCache;
856
943
  this.logger = logger;
857
944
  }
858
- start() {
859
- this.ctx.on("internal/session", (session) => {
860
- if (session.type === "message-deleted") {
861
- void this.handleMessageDeleted(session).catch((error) => {
862
- this.logger.warn("处理消息撤回失败:", error);
863
- });
864
- return;
865
- }
866
- void this.handleSession(session);
867
- });
945
+ // 后端直连 Satori 后,由 SatoriGateway 逐条送入原始事件
946
+ async handleEvent(body) {
947
+ const type = getString2(body.type);
948
+ const login = getObject(body.login);
949
+ const platform = getString2(body.platform) || getString2(login.platform);
950
+ if (this.isBlocked(platform)) return false;
951
+ if (type === "message-deleted") {
952
+ await this.handleMessageDeleted(body);
953
+ return true;
954
+ }
955
+ if (!MESSAGE_TYPES.has(type)) return false;
956
+ await this.handleMessageCreated(body);
957
+ return true;
868
958
  }
869
959
  isBlocked(platform) {
870
960
  return (this.config.blockedPlatforms ?? []).some((item) => {
871
961
  return item.exactMatch ? platform === item.platformName : platform.includes(item.platformName);
872
962
  });
873
963
  }
874
- async handleSession(session) {
875
- const platform = session.platform || "unknown";
876
- if (!MESSAGE_TYPES.has(session.type)) return;
877
- if (this.isBlocked(platform)) return;
878
- const event = session.toJSON();
879
- const message = event.message;
964
+ async handleMessageCreated(body) {
965
+ const login = getObject(body.login);
966
+ const loginUser = getObject(login.user);
967
+ const platform = getString2(body.platform) || getString2(login.platform);
968
+ const selfId = getString2(body.self_id) || getString2(body.selfId) || getString2(loginUser.id);
969
+ const message = getObject(body.message);
970
+ const channel = getObject(body.channel);
971
+ const guild = getObject(body.guild);
972
+ const user = getObject(body.user);
973
+ const sn = getNumber(body.sn);
974
+ const timestamp = getNumber(body.timestamp) || Date.now();
880
975
  const record = {
881
- id: message?.id || `local-${event.sn}`,
882
- sequence: event.sn,
883
- type: session.type,
976
+ id: getString2(message.id) || `satori-${sn}`,
977
+ sequence: sn,
978
+ type: getString2(body.type),
884
979
  platform,
885
- selfId: session.selfId,
886
- channelId: session.channelId,
887
- guildId: session.guildId,
888
- userId: session.userId,
889
- timestamp: session.timestamp,
890
- timestampMs: session.timestamp,
980
+ selfId,
981
+ channelId: getString2(channel.id) || void 0,
982
+ guildId: getString2(guild.id) || void 0,
983
+ userId: getString2(user.id) || void 0,
984
+ timestamp,
985
+ timestampMs: timestamp,
891
986
  receivedAt: Date.now(),
892
- content: session.content || message?.content,
893
- elements: message?.elements,
894
- raw: event
987
+ content: getString2(message.content) || getString2(message.raw_message) || void 0,
988
+ elements: Array.isArray(message.elements) ? message.elements : void 0,
989
+ raw: body
895
990
  };
896
991
  try {
897
992
  await this.database.appendMessage(record);
898
993
  } catch (error) {
899
994
  this.logger.warn("写入历史消息失败:", error);
900
995
  }
901
- void this.cacheMessageContacts(event).catch((error) => {
996
+ void this.cacheMessageContacts(body).catch((error) => {
902
997
  this.logger.warn("缓存消息联系人失败:", error);
903
998
  });
904
- void this.cacheMessageMedia(event).catch((error) => {
999
+ void this.cacheMessageMedia(message, getString2(channel.id)).catch((error) => {
905
1000
  this.logger.warn("异步缓存消息媒体失败:", error);
906
1001
  });
907
1002
  }
908
- async handleMessageDeleted(session) {
909
- const platform = session.platform || "unknown";
910
- if (this.isBlocked(platform)) return;
911
- const event = session.toJSON();
912
- const rawMessage = typeof event.message === "object" && event.message !== null ? event.message : {};
913
- const messageId = session.messageId || String(rawMessage.id ?? "");
914
- const channelId = String(
915
- event.channel?.id || event.guild?.id || session.channelId || ""
916
- );
1003
+ async handleMessageDeleted(body) {
1004
+ const login = getObject(body.login);
1005
+ const loginUser = getObject(login.user);
1006
+ const platform = getString2(body.platform) || getString2(login.platform);
1007
+ const selfId = getString2(body.self_id) || getString2(body.selfId) || getString2(loginUser.id);
1008
+ const message = getObject(body.message);
1009
+ const channel = getObject(body.channel);
1010
+ const guild = getObject(body.guild);
1011
+ const messageId = getString2(message.id);
1012
+ const channelId = getString2(channel.id) || getString2(guild.id);
917
1013
  if (!messageId || !channelId) return;
918
1014
  const patch = { revoked: true, revokedAt: Date.now() };
919
- await this.database.updateMessageRevoked(platform, session.selfId, channelId, messageId, patch);
920
- await this.database.updateSelfMessageByMessageId(platform, session.selfId, channelId, messageId, patch);
921
- }
922
- async cacheMessageContacts(event) {
923
- const platform = event.platform || "";
924
- const selfId = event.selfId || "";
925
- const user = event.user;
926
- const guild = event.guild;
927
- const channel = event.channel;
928
- const member = event.member;
929
- const userId = user?.id || "";
930
- const guildId = guild?.id || "";
931
- const channelId = channel?.id || "";
932
- const channelType = String(channel?.type ?? "");
933
- const isPrivateChannel = channelType === "1" || ["direct", "private"].includes(channelType.toLowerCase());
1015
+ await this.database.updateMessageRevoked(platform, selfId, channelId, messageId, patch);
1016
+ await this.database.updateSelfMessageByMessageId(platform, selfId, channelId, messageId, patch);
1017
+ }
1018
+ async cacheMessageContacts(body) {
1019
+ const login = getObject(body.login);
1020
+ const loginUser = getObject(login.user);
1021
+ const platform = getString2(body.platform) || getString2(login.platform);
1022
+ const selfId = getString2(body.self_id) || getString2(body.selfId) || getString2(loginUser.id);
1023
+ const user = getObject(body.user);
1024
+ const guild = getObject(body.guild);
1025
+ const channel = getObject(body.channel);
1026
+ const member = getObject(body.member);
1027
+ const userId = getString2(user.id);
1028
+ const guildId = getString2(guild.id);
1029
+ const channelId = getString2(channel.id);
1030
+ const isPrivateChannel = isPrivateChannelType3(channel.type);
934
1031
  const groupId = guildId || (isPrivateChannel ? "" : normalizeGroupId2(channelId)) || "";
935
- const userName = user?.name || user?.nick || member?.nick || member?.name || "";
936
- const userAvatar = user?.avatar || member?.avatar || "";
937
- const groupName = guild?.name || channel?.name || "";
938
- const groupAvatar = guild?.avatar || "";
1032
+ const userName = getString2(user.name) || getString2(user.nick) || getString2(member.nick) || getString2(member.name);
1033
+ const userAvatar = getString2(user.avatar) || getString2(member.avatar);
1034
+ const groupName = getString2(guild.name) || getString2(channel.name);
1035
+ const groupAvatar = getString2(guild.avatar);
939
1036
  if (groupId) {
940
1037
  const groupItem = await this.contactCache.getGroup(
941
1038
  platform,
@@ -945,7 +1042,7 @@ var _Recorder = class _Recorder {
945
1042
  channelId || groupId,
946
1043
  groupName,
947
1044
  groupAvatar,
948
- channel?.type
1045
+ channel.type
949
1046
  );
950
1047
  if (userId) {
951
1048
  const userItem = await this.contactCache.getUser(
@@ -969,9 +1066,9 @@ var _Recorder = class _Recorder {
969
1066
  id: userId,
970
1067
  user_id: userId,
971
1068
  nickname: userName || userItem?.name,
972
- card: member?.nick || member?.name || "",
1069
+ card: member.nick || member.name || "",
973
1070
  avatar: userItem?.avatar || userAvatar || void 0,
974
- role: member?.title || ""
1071
+ role: member.title || ""
975
1072
  }
976
1073
  }
977
1074
  );
@@ -989,16 +1086,1255 @@ var _Recorder = class _Recorder {
989
1086
  );
990
1087
  }
991
1088
  }
992
- async cacheMessageMedia(event) {
993
- const channelId = String(
994
- event.channel?.id || event.guild?.id || ""
995
- );
996
- await this.media.cacheMessageMedia(event.message, channelId);
1089
+ async cacheMessageMedia(message, channelId) {
1090
+ await this.media.cacheMessageMedia(message, channelId);
997
1091
  }
998
1092
  };
999
1093
  __name(_Recorder, "Recorder");
1000
1094
  var Recorder = _Recorder;
1001
1095
 
1096
+ // ../../node_modules/cosmokit/lib/index.mjs
1097
+ var __defProp2 = Object.defineProperty;
1098
+ var __name2 = /* @__PURE__ */ __name((target, value) => __defProp2(target, "name", { value, configurable: true }), "__name");
1099
+ function noop() {
1100
+ }
1101
+ __name(noop, "noop");
1102
+ __name2(noop, "noop");
1103
+ function isNullable(value) {
1104
+ return value === null || value === void 0;
1105
+ }
1106
+ __name(isNullable, "isNullable");
1107
+ __name2(isNullable, "isNullable");
1108
+ function isNonNullable(value) {
1109
+ return !isNullable(value);
1110
+ }
1111
+ __name(isNonNullable, "isNonNullable");
1112
+ __name2(isNonNullable, "isNonNullable");
1113
+ function isPlainObject(data) {
1114
+ return data && typeof data === "object" && !Array.isArray(data);
1115
+ }
1116
+ __name(isPlainObject, "isPlainObject");
1117
+ __name2(isPlainObject, "isPlainObject");
1118
+ function filterKeys(object, filter2) {
1119
+ return Object.fromEntries(Object.entries(object).filter(([key, value]) => filter2(key, value)));
1120
+ }
1121
+ __name(filterKeys, "filterKeys");
1122
+ __name2(filterKeys, "filterKeys");
1123
+ function mapValues(object, transform) {
1124
+ return Object.fromEntries(Object.entries(object).map(([key, value]) => [key, transform(value, key)]));
1125
+ }
1126
+ __name(mapValues, "mapValues");
1127
+ __name2(mapValues, "mapValues");
1128
+ function pick(source, keys, forced) {
1129
+ if (!keys) return { ...source };
1130
+ const result = {};
1131
+ for (const key of keys) {
1132
+ if (forced || source[key] !== void 0) result[key] = source[key];
1133
+ }
1134
+ return result;
1135
+ }
1136
+ __name(pick, "pick");
1137
+ __name2(pick, "pick");
1138
+ function omit(source, keys) {
1139
+ if (!keys) return { ...source };
1140
+ const result = { ...source };
1141
+ for (const key of keys) {
1142
+ Reflect.deleteProperty(result, key);
1143
+ }
1144
+ return result;
1145
+ }
1146
+ __name(omit, "omit");
1147
+ __name2(omit, "omit");
1148
+ function defineProperty(object, key, value) {
1149
+ return Object.defineProperty(object, key, { writable: true, value, enumerable: false });
1150
+ }
1151
+ __name(defineProperty, "defineProperty");
1152
+ __name2(defineProperty, "defineProperty");
1153
+ function contain(array1, array2) {
1154
+ return array2.every((item) => array1.includes(item));
1155
+ }
1156
+ __name(contain, "contain");
1157
+ __name2(contain, "contain");
1158
+ function intersection(array1, array2) {
1159
+ return array1.filter((item) => array2.includes(item));
1160
+ }
1161
+ __name(intersection, "intersection");
1162
+ __name2(intersection, "intersection");
1163
+ function difference(array1, array2) {
1164
+ return array1.filter((item) => !array2.includes(item));
1165
+ }
1166
+ __name(difference, "difference");
1167
+ __name2(difference, "difference");
1168
+ function union(array1, array2) {
1169
+ return Array.from(/* @__PURE__ */ new Set([...array1, ...array2]));
1170
+ }
1171
+ __name(union, "union");
1172
+ __name2(union, "union");
1173
+ function deduplicate(array) {
1174
+ return [...new Set(array)];
1175
+ }
1176
+ __name(deduplicate, "deduplicate");
1177
+ __name2(deduplicate, "deduplicate");
1178
+ function remove(list, item) {
1179
+ const index = list?.indexOf(item);
1180
+ if (index >= 0) {
1181
+ list.splice(index, 1);
1182
+ return true;
1183
+ } else {
1184
+ return false;
1185
+ }
1186
+ }
1187
+ __name(remove, "remove");
1188
+ __name2(remove, "remove");
1189
+ function makeArray(source) {
1190
+ return Array.isArray(source) ? source : isNullable(source) ? [] : [source];
1191
+ }
1192
+ __name(makeArray, "makeArray");
1193
+ __name2(makeArray, "makeArray");
1194
+ function is(type, value) {
1195
+ if (arguments.length === 1) return (value2) => is(type, value2);
1196
+ return type in globalThis && value instanceof globalThis[type] || Object.prototype.toString.call(value).slice(8, -1) === type;
1197
+ }
1198
+ __name(is, "is");
1199
+ __name2(is, "is");
1200
+ function isArrayBufferLike(value) {
1201
+ return is("ArrayBuffer", value) || is("SharedArrayBuffer", value);
1202
+ }
1203
+ __name(isArrayBufferLike, "isArrayBufferLike");
1204
+ __name2(isArrayBufferLike, "isArrayBufferLike");
1205
+ function isArrayBufferSource(value) {
1206
+ return isArrayBufferLike(value) || ArrayBuffer.isView(value);
1207
+ }
1208
+ __name(isArrayBufferSource, "isArrayBufferSource");
1209
+ __name2(isArrayBufferSource, "isArrayBufferSource");
1210
+ var Binary;
1211
+ ((Binary2) => {
1212
+ Binary2.is = isArrayBufferLike;
1213
+ Binary2.isSource = isArrayBufferSource;
1214
+ function fromSource(source) {
1215
+ if (ArrayBuffer.isView(source)) {
1216
+ return source.buffer.slice(source.byteOffset, source.byteOffset + source.byteLength);
1217
+ } else {
1218
+ return source;
1219
+ }
1220
+ }
1221
+ __name(fromSource, "fromSource");
1222
+ Binary2.fromSource = fromSource;
1223
+ __name2(fromSource, "fromSource");
1224
+ function toBase64(source) {
1225
+ if (typeof Buffer !== "undefined") {
1226
+ return Buffer.from(source).toString("base64");
1227
+ }
1228
+ let binary = "";
1229
+ const bytes = new Uint8Array(source);
1230
+ for (let i = 0; i < bytes.byteLength; i++) {
1231
+ binary += String.fromCharCode(bytes[i]);
1232
+ }
1233
+ return btoa(binary);
1234
+ }
1235
+ __name(toBase64, "toBase64");
1236
+ Binary2.toBase64 = toBase64;
1237
+ __name2(toBase64, "toBase64");
1238
+ function fromBase64(source) {
1239
+ if (typeof Buffer !== "undefined") return fromSource(Buffer.from(source, "base64"));
1240
+ return Uint8Array.from(atob(source), (c) => c.charCodeAt(0));
1241
+ }
1242
+ __name(fromBase64, "fromBase64");
1243
+ Binary2.fromBase64 = fromBase64;
1244
+ __name2(fromBase64, "fromBase64");
1245
+ function toHex(source) {
1246
+ if (typeof Buffer !== "undefined") return Buffer.from(source).toString("hex");
1247
+ return Array.from(new Uint8Array(source), (byte) => byte.toString(16).padStart(2, "0")).join("");
1248
+ }
1249
+ __name(toHex, "toHex");
1250
+ Binary2.toHex = toHex;
1251
+ __name2(toHex, "toHex");
1252
+ function fromHex(source) {
1253
+ if (typeof Buffer !== "undefined") return fromSource(Buffer.from(source, "hex"));
1254
+ const hex = source.length % 2 === 0 ? source : source.slice(0, source.length - 1);
1255
+ const buffer = [];
1256
+ for (let i = 0; i < hex.length; i += 2) {
1257
+ buffer.push(parseInt(`${hex[i]}${hex[i + 1]}`, 16));
1258
+ }
1259
+ return Uint8Array.from(buffer).buffer;
1260
+ }
1261
+ __name(fromHex, "fromHex");
1262
+ Binary2.fromHex = fromHex;
1263
+ __name2(fromHex, "fromHex");
1264
+ })(Binary || (Binary = {}));
1265
+ var base64ToArrayBuffer = Binary.fromBase64;
1266
+ var arrayBufferToBase64 = Binary.toBase64;
1267
+ var hexToArrayBuffer = Binary.fromHex;
1268
+ var arrayBufferToHex = Binary.toHex;
1269
+ function clone(source, refs = /* @__PURE__ */ new Map()) {
1270
+ if (!source || typeof source !== "object") return source;
1271
+ if (is("Date", source)) return new Date(source.valueOf());
1272
+ if (is("RegExp", source)) return new RegExp(source.source, source.flags);
1273
+ if (isArrayBufferLike(source)) return source.slice(0);
1274
+ if (ArrayBuffer.isView(source)) return source.buffer.slice(source.byteOffset, source.byteOffset + source.byteLength);
1275
+ const cached = refs.get(source);
1276
+ if (cached) return cached;
1277
+ if (Array.isArray(source)) {
1278
+ const result2 = [];
1279
+ refs.set(source, result2);
1280
+ source.forEach((value, index) => {
1281
+ result2[index] = Reflect.apply(clone, null, [value, refs]);
1282
+ });
1283
+ return result2;
1284
+ }
1285
+ const result = Object.create(Object.getPrototypeOf(source));
1286
+ refs.set(source, result);
1287
+ for (const key of Reflect.ownKeys(source)) {
1288
+ const descriptor = { ...Reflect.getOwnPropertyDescriptor(source, key) };
1289
+ if ("value" in descriptor) {
1290
+ descriptor.value = Reflect.apply(clone, null, [descriptor.value, refs]);
1291
+ }
1292
+ Reflect.defineProperty(result, key, descriptor);
1293
+ }
1294
+ return result;
1295
+ }
1296
+ __name(clone, "clone");
1297
+ __name2(clone, "clone");
1298
+ function deepEqual(a, b, strict) {
1299
+ if (a === b) return true;
1300
+ if (!strict && isNullable(a) && isNullable(b)) return true;
1301
+ if (typeof a !== typeof b) return false;
1302
+ if (typeof a !== "object") return false;
1303
+ if (!a || !b) return false;
1304
+ function check(test, then) {
1305
+ return test(a) ? test(b) ? then(a, b) : false : test(b) ? false : void 0;
1306
+ }
1307
+ __name(check, "check");
1308
+ __name2(check, "check");
1309
+ return check(Array.isArray, (a2, b2) => a2.length === b2.length && a2.every((item, index) => deepEqual(item, b2[index]))) ?? check(is("Date"), (a2, b2) => a2.valueOf() === b2.valueOf()) ?? check(is("RegExp"), (a2, b2) => a2.source === b2.source && a2.flags === b2.flags) ?? check(isArrayBufferLike, (a2, b2) => {
1310
+ if (a2.byteLength !== b2.byteLength) return false;
1311
+ const viewA = new Uint8Array(a2);
1312
+ const viewB = new Uint8Array(b2);
1313
+ for (let i = 0; i < viewA.length; i++) {
1314
+ if (viewA[i] !== viewB[i]) return false;
1315
+ }
1316
+ return true;
1317
+ }) ?? Object.keys({ ...a, ...b }).every((key) => deepEqual(a[key], b[key], strict));
1318
+ }
1319
+ __name(deepEqual, "deepEqual");
1320
+ __name2(deepEqual, "deepEqual");
1321
+ function capitalize(source) {
1322
+ return source.charAt(0).toUpperCase() + source.slice(1);
1323
+ }
1324
+ __name(capitalize, "capitalize");
1325
+ __name2(capitalize, "capitalize");
1326
+ function uncapitalize(source) {
1327
+ return source.charAt(0).toLowerCase() + source.slice(1);
1328
+ }
1329
+ __name(uncapitalize, "uncapitalize");
1330
+ __name2(uncapitalize, "uncapitalize");
1331
+ function camelCase(source) {
1332
+ return source.replace(/[_-][a-z]/g, (str) => str.slice(1).toUpperCase());
1333
+ }
1334
+ __name(camelCase, "camelCase");
1335
+ __name2(camelCase, "camelCase");
1336
+ function tokenize(source, delimiters, delimiter) {
1337
+ const output = [];
1338
+ let state = 0;
1339
+ for (let i = 0; i < source.length; i++) {
1340
+ const code = source.charCodeAt(i);
1341
+ if (code >= 65 && code <= 90) {
1342
+ if (state === 1) {
1343
+ const next = source.charCodeAt(i + 1);
1344
+ if (next >= 97 && next <= 122) {
1345
+ output.push(delimiter);
1346
+ }
1347
+ output.push(code + 32);
1348
+ } else {
1349
+ if (state !== 0) {
1350
+ output.push(delimiter);
1351
+ }
1352
+ output.push(code + 32);
1353
+ }
1354
+ state = 1;
1355
+ } else if (code >= 97 && code <= 122) {
1356
+ output.push(code);
1357
+ state = 2;
1358
+ } else if (delimiters.includes(code)) {
1359
+ if (state !== 0) {
1360
+ output.push(delimiter);
1361
+ }
1362
+ state = 0;
1363
+ } else {
1364
+ output.push(code);
1365
+ }
1366
+ }
1367
+ return String.fromCharCode(...output);
1368
+ }
1369
+ __name(tokenize, "tokenize");
1370
+ __name2(tokenize, "tokenize");
1371
+ function paramCase(source) {
1372
+ return tokenize(source, [45, 95], 45);
1373
+ }
1374
+ __name(paramCase, "paramCase");
1375
+ __name2(paramCase, "paramCase");
1376
+ function snakeCase(source) {
1377
+ return tokenize(source, [45, 95], 95);
1378
+ }
1379
+ __name(snakeCase, "snakeCase");
1380
+ __name2(snakeCase, "snakeCase");
1381
+ var camelize = camelCase;
1382
+ var hyphenate = paramCase;
1383
+ function formatProperty(key) {
1384
+ if (typeof key !== "string") return `[${key.toString()}]`;
1385
+ return /^[a-z_$][\w$]*$/i.test(key) ? `.${key}` : `[${JSON.stringify(key)}]`;
1386
+ }
1387
+ __name(formatProperty, "formatProperty");
1388
+ __name2(formatProperty, "formatProperty");
1389
+ function trimSlash(source) {
1390
+ return source.replace(/\/$/, "");
1391
+ }
1392
+ __name(trimSlash, "trimSlash");
1393
+ __name2(trimSlash, "trimSlash");
1394
+ function sanitize(source) {
1395
+ if (!source.startsWith("/")) source = "/" + source;
1396
+ return trimSlash(source);
1397
+ }
1398
+ __name(sanitize, "sanitize");
1399
+ __name2(sanitize, "sanitize");
1400
+ var Time;
1401
+ ((Time2) => {
1402
+ Time2.millisecond = 1;
1403
+ Time2.second = 1e3;
1404
+ Time2.minute = Time2.second * 60;
1405
+ Time2.hour = Time2.minute * 60;
1406
+ Time2.day = Time2.hour * 24;
1407
+ Time2.week = Time2.day * 7;
1408
+ let timezoneOffset = (/* @__PURE__ */ new Date()).getTimezoneOffset();
1409
+ function setTimezoneOffset(offset) {
1410
+ timezoneOffset = offset;
1411
+ }
1412
+ __name(setTimezoneOffset, "setTimezoneOffset");
1413
+ Time2.setTimezoneOffset = setTimezoneOffset;
1414
+ __name2(setTimezoneOffset, "setTimezoneOffset");
1415
+ function getTimezoneOffset() {
1416
+ return timezoneOffset;
1417
+ }
1418
+ __name(getTimezoneOffset, "getTimezoneOffset");
1419
+ Time2.getTimezoneOffset = getTimezoneOffset;
1420
+ __name2(getTimezoneOffset, "getTimezoneOffset");
1421
+ function getDateNumber(date = /* @__PURE__ */ new Date(), offset) {
1422
+ if (typeof date === "number") date = new Date(date);
1423
+ if (offset === void 0) offset = timezoneOffset;
1424
+ return Math.floor((date.valueOf() / Time2.minute - offset) / 1440);
1425
+ }
1426
+ __name(getDateNumber, "getDateNumber");
1427
+ Time2.getDateNumber = getDateNumber;
1428
+ __name2(getDateNumber, "getDateNumber");
1429
+ function fromDateNumber(value, offset) {
1430
+ const date = new Date(value * Time2.day);
1431
+ if (offset === void 0) offset = timezoneOffset;
1432
+ return new Date(+date + offset * Time2.minute);
1433
+ }
1434
+ __name(fromDateNumber, "fromDateNumber");
1435
+ Time2.fromDateNumber = fromDateNumber;
1436
+ __name2(fromDateNumber, "fromDateNumber");
1437
+ const numeric = /\d+(?:\.\d+)?/.source;
1438
+ const timeRegExp = new RegExp(`^${[
1439
+ "w(?:eek(?:s)?)?",
1440
+ "d(?:ay(?:s)?)?",
1441
+ "h(?:our(?:s)?)?",
1442
+ "m(?:in(?:ute)?(?:s)?)?",
1443
+ "s(?:ec(?:ond)?(?:s)?)?"
1444
+ ].map((unit) => `(${numeric}${unit})?`).join("")}$`);
1445
+ function parseTime(source) {
1446
+ const capture = timeRegExp.exec(source);
1447
+ if (!capture) return 0;
1448
+ return (parseFloat(capture[1]) * Time2.week || 0) + (parseFloat(capture[2]) * Time2.day || 0) + (parseFloat(capture[3]) * Time2.hour || 0) + (parseFloat(capture[4]) * Time2.minute || 0) + (parseFloat(capture[5]) * Time2.second || 0);
1449
+ }
1450
+ __name(parseTime, "parseTime");
1451
+ Time2.parseTime = parseTime;
1452
+ __name2(parseTime, "parseTime");
1453
+ function parseDate(date) {
1454
+ const parsed = parseTime(date);
1455
+ if (parsed) {
1456
+ date = Date.now() + parsed;
1457
+ } else if (/^\d{1,2}(:\d{1,2}){1,2}$/.test(date)) {
1458
+ date = `${(/* @__PURE__ */ new Date()).toLocaleDateString()}-${date}`;
1459
+ } else if (/^\d{1,2}-\d{1,2}-\d{1,2}(:\d{1,2}){1,2}$/.test(date)) {
1460
+ date = `${(/* @__PURE__ */ new Date()).getFullYear()}-${date}`;
1461
+ }
1462
+ return date ? new Date(date) : /* @__PURE__ */ new Date();
1463
+ }
1464
+ __name(parseDate, "parseDate");
1465
+ Time2.parseDate = parseDate;
1466
+ __name2(parseDate, "parseDate");
1467
+ function format(ms) {
1468
+ const abs = Math.abs(ms);
1469
+ if (abs >= Time2.day - Time2.hour / 2) {
1470
+ return Math.round(ms / Time2.day) + "d";
1471
+ } else if (abs >= Time2.hour - Time2.minute / 2) {
1472
+ return Math.round(ms / Time2.hour) + "h";
1473
+ } else if (abs >= Time2.minute - Time2.second / 2) {
1474
+ return Math.round(ms / Time2.minute) + "m";
1475
+ } else if (abs >= Time2.second) {
1476
+ return Math.round(ms / Time2.second) + "s";
1477
+ }
1478
+ return ms + "ms";
1479
+ }
1480
+ __name(format, "format");
1481
+ Time2.format = format;
1482
+ __name2(format, "format");
1483
+ function toDigits(source, length = 2) {
1484
+ return source.toString().padStart(length, "0");
1485
+ }
1486
+ __name(toDigits, "toDigits");
1487
+ Time2.toDigits = toDigits;
1488
+ __name2(toDigits, "toDigits");
1489
+ function template(template2, time = /* @__PURE__ */ new Date()) {
1490
+ return template2.replace("yyyy", time.getFullYear().toString()).replace("yy", time.getFullYear().toString().slice(2)).replace("MM", toDigits(time.getMonth() + 1)).replace("dd", toDigits(time.getDate())).replace("hh", toDigits(time.getHours())).replace("mm", toDigits(time.getMinutes())).replace("ss", toDigits(time.getSeconds())).replace("SSS", toDigits(time.getMilliseconds(), 3));
1491
+ }
1492
+ __name(template, "template");
1493
+ Time2.template = template;
1494
+ __name2(template, "template");
1495
+ })(Time || (Time = {}));
1496
+
1497
+ // ../../node_modules/@satorijs/element/lib/index.mjs
1498
+ var __defProp3 = Object.defineProperty;
1499
+ var __getOwnPropNames2 = Object.getOwnPropertyNames;
1500
+ var __name3 = /* @__PURE__ */ __name((target, value) => __defProp3(target, "name", { value, configurable: true }), "__name");
1501
+ var __commonJS = /* @__PURE__ */ __name((cb, mod) => /* @__PURE__ */ __name(function __require() {
1502
+ return mod || (0, cb[__getOwnPropNames2(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
1503
+ }, "__require"), "__commonJS");
1504
+ var require_index = __commonJS({
1505
+ "src/index.ts"(exports2, module2) {
1506
+ var _a;
1507
+ var kElement = Symbol.for("satori.element");
1508
+ var ElementConstructor = (_a = class {
1509
+ get data() {
1510
+ return this.attrs;
1511
+ }
1512
+ getTagName() {
1513
+ if (this.type === "component") {
1514
+ return this.attrs.is?.name ?? "component";
1515
+ } else {
1516
+ return this.type;
1517
+ }
1518
+ }
1519
+ toAttrString() {
1520
+ return Object.entries(this.attrs).map(([key, value]) => {
1521
+ if (isNullable(value)) return "";
1522
+ key = hyphenate(key);
1523
+ if (value === true) return ` ${key}`;
1524
+ if (value === false) return ` no-${key}`;
1525
+ return ` ${key}="${Element.escape("" + value, true)}"`;
1526
+ }).join("");
1527
+ }
1528
+ toString(strip = false) {
1529
+ if (this.type === "text" && "content" in this.attrs) {
1530
+ return strip ? this.attrs.content : Element.escape(this.attrs.content);
1531
+ }
1532
+ const inner = this.children.map((child) => child.toString(strip)).join("");
1533
+ if (strip) return inner;
1534
+ const attrs = this.toAttrString();
1535
+ const tag = this.getTagName();
1536
+ if (!this.children.length) return `<${tag}${attrs}/>`;
1537
+ return `<${tag}${attrs}>${inner}</${tag}>`;
1538
+ }
1539
+ }, __name(_a, "ElementConstructor"), __name3(_a, "ElementConstructor"), _a);
1540
+ defineProperty(ElementConstructor, "name", "Element");
1541
+ defineProperty(ElementConstructor.prototype, kElement, true);
1542
+ function Element(type, ...args) {
1543
+ const el = Object.create(ElementConstructor.prototype);
1544
+ const attrs = {}, children = [];
1545
+ if (args[0] && typeof args[0] === "object" && !Element.isElement(args[0]) && !Array.isArray(args[0])) {
1546
+ const props = args.shift();
1547
+ for (const [key, value] of Object.entries(props)) {
1548
+ if (isNullable(value)) continue;
1549
+ if (key === "children") {
1550
+ args.push(...makeArray(value));
1551
+ } else {
1552
+ attrs[camelize(key)] = value;
1553
+ }
1554
+ }
1555
+ }
1556
+ for (const child of args) {
1557
+ children.push(...Element.toElementArray(child));
1558
+ }
1559
+ if (typeof type === "function") {
1560
+ attrs.is = type;
1561
+ type = "component";
1562
+ }
1563
+ return Object.assign(el, { type, attrs, children });
1564
+ }
1565
+ __name(Element, "Element");
1566
+ __name3(Element, "Element");
1567
+ var evaluate = new Function("expr", "context", `
1568
+ try {
1569
+ with (context) {
1570
+ return eval(expr)
1571
+ }
1572
+ } catch {}
1573
+ `);
1574
+ ((Element2) => {
1575
+ Element2.jsx = Element2;
1576
+ Element2.jsxs = Element2;
1577
+ Element2.jsxDEV = Element2;
1578
+ Element2.Fragment = "template";
1579
+ function isElement(source) {
1580
+ return source && typeof source === "object" && source[kElement];
1581
+ }
1582
+ __name(isElement, "isElement");
1583
+ Element2.isElement = isElement;
1584
+ __name3(isElement, "isElement");
1585
+ function toElement(content) {
1586
+ if (typeof content === "string" || typeof content === "number" || typeof content === "boolean") {
1587
+ content = "" + content;
1588
+ if (content) return Element2("text", { content });
1589
+ } else if (isElement(content)) {
1590
+ return content;
1591
+ } else if (!isNullable(content)) {
1592
+ throw new TypeError(`Invalid content: ${content}`);
1593
+ }
1594
+ }
1595
+ __name(toElement, "toElement");
1596
+ Element2.toElement = toElement;
1597
+ __name3(toElement, "toElement");
1598
+ function toElementArray(content) {
1599
+ if (Array.isArray(content)) {
1600
+ return content.map(toElement).filter(isNonNullable);
1601
+ } else {
1602
+ return [toElement(content)].filter(isNonNullable);
1603
+ }
1604
+ }
1605
+ __name(toElementArray, "toElementArray");
1606
+ Element2.toElementArray = toElementArray;
1607
+ __name3(toElementArray, "toElementArray");
1608
+ function normalize(source, context) {
1609
+ return typeof source === "string" ? parse(source, context) : toElementArray(source);
1610
+ }
1611
+ __name(normalize, "normalize");
1612
+ Element2.normalize = normalize;
1613
+ __name3(normalize, "normalize");
1614
+ function escape(source, inline = false) {
1615
+ const result = (source ?? "").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
1616
+ return inline ? result.replace(/"/g, "&quot;") : result;
1617
+ }
1618
+ __name(escape, "escape");
1619
+ Element2.escape = escape;
1620
+ __name3(escape, "escape");
1621
+ function unescape(source) {
1622
+ return source.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#(\d+);/g, (_, code) => code === "38" ? _ : String.fromCharCode(+code)).replace(/&#x([0-9a-f]+);/gi, (_, code) => code === "26" ? _ : String.fromCharCode(parseInt(code, 16))).replace(/&(amp|#38|#x26);/g, "&");
1623
+ }
1624
+ __name(unescape, "unescape");
1625
+ Element2.unescape = unescape;
1626
+ __name3(unescape, "unescape");
1627
+ function from(source, options = {}) {
1628
+ const elements = parse(source);
1629
+ if (options.caret) {
1630
+ if (options.type && elements[0]?.type !== options.type) return;
1631
+ return elements[0];
1632
+ }
1633
+ return select(elements, options.type || "*")[0];
1634
+ }
1635
+ __name(from, "from");
1636
+ Element2.from = from;
1637
+ __name3(from, "from");
1638
+ const combRegExp = / *([ >+~]) */g;
1639
+ function parseSelector(input) {
1640
+ return input.split(",").map((query) => {
1641
+ const selectors = [];
1642
+ query = query.trim();
1643
+ let combCap, combinator = " ";
1644
+ while (combCap = combRegExp.exec(query)) {
1645
+ selectors.push({ type: query.slice(0, combCap.index), combinator });
1646
+ combinator = combCap[1];
1647
+ query = query.slice(combCap.index + combCap[0].length);
1648
+ }
1649
+ selectors.push({ type: query, combinator });
1650
+ return selectors;
1651
+ });
1652
+ }
1653
+ __name(parseSelector, "parseSelector");
1654
+ Element2.parseSelector = parseSelector;
1655
+ __name3(parseSelector, "parseSelector");
1656
+ function select(source, query) {
1657
+ if (!source || !query) return [];
1658
+ if (typeof source === "string") source = parse(source);
1659
+ if (typeof query === "string") query = parseSelector(query);
1660
+ if (!query.length) return [];
1661
+ let adjacent = [];
1662
+ const results = [];
1663
+ for (const [index, element] of source.entries()) {
1664
+ const inner = [];
1665
+ const local = [...query, ...adjacent];
1666
+ adjacent = [];
1667
+ let matched = false;
1668
+ for (const group of local) {
1669
+ const { type, combinator } = group[0];
1670
+ if (type === element.type || type === "*") {
1671
+ if (group.length === 1) {
1672
+ matched = true;
1673
+ } else if ([" ", ">"].includes(group[1].combinator)) {
1674
+ inner.push(group.slice(1));
1675
+ } else if (group[1].combinator === "+") {
1676
+ adjacent.push(group.slice(1));
1677
+ } else {
1678
+ query.push(group.slice(1));
1679
+ }
1680
+ }
1681
+ if (combinator === " ") {
1682
+ inner.push(group);
1683
+ }
1684
+ }
1685
+ if (matched) results.push(source[index]);
1686
+ results.push(...select(element.children, inner));
1687
+ }
1688
+ return results;
1689
+ }
1690
+ __name(select, "select");
1691
+ Element2.select = select;
1692
+ __name3(select, "select");
1693
+ function interpolate(expr, context) {
1694
+ expr = expr.trim();
1695
+ if (!/^[\w.]+$/.test(expr)) {
1696
+ return evaluate(expr, context) ?? "";
1697
+ }
1698
+ let value = context;
1699
+ for (const part of expr.split(".")) {
1700
+ value = value[part];
1701
+ if (isNullable(value)) return "";
1702
+ }
1703
+ return value ?? "";
1704
+ }
1705
+ __name(interpolate, "interpolate");
1706
+ Element2.interpolate = interpolate;
1707
+ __name3(interpolate, "interpolate");
1708
+ const tagRegExp1 = /(?<comment><!--[\s\S]*?-->)|(?<tag><(\/?)([^!\s>/]*)([^>]*?)\s*(\/?)>)/;
1709
+ const tagRegExp2 = /(?<comment><!--[\s\S]*?-->)|(?<tag><(\/?)([^!\s>/]*)([^>]*?)\s*(\/?)>)|(?<curly>\{(?<derivative>[@:/#][^\s}]*)?[\s\S]*?\})/;
1710
+ const attrRegExp1 = /([^\s=]+)(?:="(?<value1>[^"]*)"|='(?<value2>[^']*)')?/g;
1711
+ const attrRegExp2 = /([^\s=]+)(?:="(?<value1>[^"]*)"|='(?<value2>[^']*)'|=\{(?<curly>[^}]+)\})?/g;
1712
+ let Position;
1713
+ ((Position2) => {
1714
+ Position2[Position2["OPEN"] = 0] = "OPEN";
1715
+ Position2[Position2["CLOSE"] = 1] = "CLOSE";
1716
+ Position2[Position2["EMPTY"] = 2] = "EMPTY";
1717
+ Position2[Position2["CONTINUE"] = 3] = "CONTINUE";
1718
+ })(Position || (Position = {}));
1719
+ function parse(source, context) {
1720
+ const tokens = [];
1721
+ function pushText(content) {
1722
+ if (content) tokens.push(content);
1723
+ }
1724
+ __name(pushText, "pushText");
1725
+ __name3(pushText, "pushText");
1726
+ const tagRegExp = context ? tagRegExp2 : tagRegExp1;
1727
+ let tagCap;
1728
+ let trimStart = true;
1729
+ while (tagCap = tagRegExp.exec(source)) {
1730
+ const { curly, comment, derivative } = tagCap.groups;
1731
+ const trimEnd = !curly;
1732
+ parseContent(source.slice(0, tagCap.index), trimStart, trimEnd);
1733
+ trimStart = trimEnd;
1734
+ source = source.slice(tagCap.index + tagCap[0].length);
1735
+ const [_, , , close, type, extra, empty] = tagCap;
1736
+ if (comment) continue;
1737
+ if (curly) {
1738
+ let name2 = "", position = 2;
1739
+ if (derivative) {
1740
+ name2 = derivative.slice(1);
1741
+ position = {
1742
+ "@": 2,
1743
+ "#": 0,
1744
+ "/": 1,
1745
+ ":": 3
1746
+ /* CONTINUE */
1747
+ }[derivative[0]];
1748
+ }
1749
+ tokens.push({
1750
+ type: "curly",
1751
+ name: name2,
1752
+ position,
1753
+ source: curly,
1754
+ extra: curly.slice(1 + (derivative ?? "").length, -1)
1755
+ });
1756
+ continue;
1757
+ }
1758
+ tokens.push({
1759
+ type: "angle",
1760
+ source: _,
1761
+ name: type || Element2.Fragment,
1762
+ position: close ? 1 : empty ? 2 : 0,
1763
+ extra
1764
+ });
1765
+ }
1766
+ parseContent(source, trimStart, true);
1767
+ function parseContent(source2, trimStart2, trimEnd) {
1768
+ source2 = unescape(source2);
1769
+ if (trimStart2) source2 = source2.replace(/^\s*\n\s*/, "");
1770
+ if (trimEnd) source2 = source2.replace(/\s*\n\s*$/, "");
1771
+ pushText(source2);
1772
+ }
1773
+ __name(parseContent, "parseContent");
1774
+ __name3(parseContent, "parseContent");
1775
+ return parseTokens(foldTokens(tokens), context);
1776
+ }
1777
+ __name(parse, "parse");
1778
+ Element2.parse = parse;
1779
+ __name3(parse, "parse");
1780
+ function foldTokens(tokens) {
1781
+ const stack = [[{
1782
+ type: "angle",
1783
+ name: Element2.Fragment,
1784
+ position: 0,
1785
+ source: "",
1786
+ extra: "",
1787
+ children: { default: [] }
1788
+ }, "default"]];
1789
+ function pushToken(...tokens2) {
1790
+ const [token, slot] = stack[0];
1791
+ token.children[slot].push(...tokens2);
1792
+ }
1793
+ __name(pushToken, "pushToken");
1794
+ __name3(pushToken, "pushToken");
1795
+ for (const token of tokens) {
1796
+ if (typeof token === "string") {
1797
+ pushToken(token);
1798
+ continue;
1799
+ }
1800
+ const { name: name2, position } = token;
1801
+ if (position === 1) {
1802
+ if (stack[0][0].name === name2) {
1803
+ stack.shift();
1804
+ }
1805
+ } else if (position === 3) {
1806
+ stack[0][0].children[name2] = [];
1807
+ stack[0][1] = name2;
1808
+ } else if (position === 0) {
1809
+ pushToken(token);
1810
+ token.children = { default: [] };
1811
+ stack.unshift([token, "default"]);
1812
+ } else {
1813
+ pushToken(token);
1814
+ }
1815
+ }
1816
+ return stack[stack.length - 1][0].children.default;
1817
+ }
1818
+ __name(foldTokens, "foldTokens");
1819
+ __name3(foldTokens, "foldTokens");
1820
+ function parseTokens(tokens, context) {
1821
+ const result = [];
1822
+ for (const token of tokens) {
1823
+ if (typeof token === "string") {
1824
+ result.push(Element2("text", { content: token }));
1825
+ } else if (token.type === "angle") {
1826
+ const attrs = {};
1827
+ const attrRegExp = context ? attrRegExp2 : attrRegExp1;
1828
+ let attrCap;
1829
+ while (attrCap = attrRegExp.exec(token.extra)) {
1830
+ const [, key, v1, v2 = v1, v3] = attrCap;
1831
+ if (v3) {
1832
+ attrs[key] = interpolate(v3, context);
1833
+ } else if (!isNullable(v2)) {
1834
+ attrs[key] = unescape(v2);
1835
+ } else if (key.startsWith("no-")) {
1836
+ attrs[key.slice(3)] = false;
1837
+ } else {
1838
+ attrs[key] = true;
1839
+ }
1840
+ }
1841
+ result.push(Element2(token.name, attrs, token.children && parseTokens(token.children.default, context)));
1842
+ } else if (!token.name) {
1843
+ result.push(...toElementArray(interpolate(token.extra, context)));
1844
+ } else if (token.name === "if") {
1845
+ if (evaluate(token.extra, context)) {
1846
+ result.push(...parseTokens(token.children.default, context));
1847
+ } else {
1848
+ result.push(...parseTokens(token.children.else || [], context));
1849
+ }
1850
+ } else if (token.name === "each") {
1851
+ const [expr, ident] = token.extra.split(/\s+as\s+/);
1852
+ const items = interpolate(expr, context);
1853
+ if (!items || !items[Symbol.iterator]) continue;
1854
+ for (const item of items) {
1855
+ result.push(...parseTokens(token.children.default, { ...context, [ident]: item }));
1856
+ }
1857
+ }
1858
+ }
1859
+ return result;
1860
+ }
1861
+ __name(parseTokens, "parseTokens");
1862
+ __name3(parseTokens, "parseTokens");
1863
+ function visit(element, rules, session) {
1864
+ const { type, attrs, children } = element;
1865
+ if (typeof rules === "function") {
1866
+ return rules(element, session);
1867
+ } else {
1868
+ let result = rules[typeof type === "string" ? type : ""] ?? rules.default ?? true;
1869
+ if (typeof result === "function") {
1870
+ result = result(attrs, children, session);
1871
+ }
1872
+ return result;
1873
+ }
1874
+ }
1875
+ __name(visit, "visit");
1876
+ __name3(visit, "visit");
1877
+ function transform(source, rules, session) {
1878
+ const elements = typeof source === "string" ? parse(source) : source;
1879
+ const output = [];
1880
+ elements.forEach((element) => {
1881
+ const { type, attrs, children } = element;
1882
+ const result = visit(element, rules, session);
1883
+ if (result === true) {
1884
+ output.push(Element2(type, attrs, transform(children, rules, session)));
1885
+ } else if (result !== false) {
1886
+ output.push(...toElementArray(result));
1887
+ }
1888
+ });
1889
+ return typeof source === "string" ? output.join("") : output;
1890
+ }
1891
+ __name(transform, "transform");
1892
+ Element2.transform = transform;
1893
+ __name3(transform, "transform");
1894
+ async function transformAsync(source, rules, session) {
1895
+ const elements = typeof source === "string" ? parse(source) : source;
1896
+ const children = (await Promise.all(elements.map(async (element) => {
1897
+ const { type, attrs, children: children2 } = element;
1898
+ const result = await visit(element, rules, session);
1899
+ if (result === true) {
1900
+ return [Element2(type, attrs, await transformAsync(children2, rules, session))];
1901
+ } else if (result !== false) {
1902
+ return toElementArray(result);
1903
+ } else {
1904
+ return [];
1905
+ }
1906
+ }))).flat(1);
1907
+ return typeof source === "string" ? children.join("") : children;
1908
+ }
1909
+ __name(transformAsync, "transformAsync");
1910
+ Element2.transformAsync = transformAsync;
1911
+ __name3(transformAsync, "transformAsync");
1912
+ function createFactory(type, ...keys) {
1913
+ return (...args) => {
1914
+ const element = Element2(type);
1915
+ keys.forEach((key, index) => {
1916
+ if (!isNullable(args[index])) {
1917
+ element.attrs[key] = args[index];
1918
+ }
1919
+ });
1920
+ if (args[keys.length]) {
1921
+ Object.assign(element.attrs, args[keys.length]);
1922
+ }
1923
+ return element;
1924
+ };
1925
+ }
1926
+ __name(createFactory, "createFactory");
1927
+ __name3(createFactory, "createFactory");
1928
+ Element2.warn = /* @__PURE__ */ __name3(() => {
1929
+ }, "warn");
1930
+ function createAssetFactory(type) {
1931
+ return (src, ...args) => {
1932
+ let prefix = "base64://";
1933
+ if (typeof args[0] === "string") {
1934
+ prefix = `data:${args.shift()};base64,`;
1935
+ }
1936
+ if (is("Buffer", src)) {
1937
+ src = prefix + src.toString("base64");
1938
+ } else if (is("ArrayBuffer", src)) {
1939
+ src = prefix + Binary.toBase64(src);
1940
+ } else if (ArrayBuffer.isView(src)) {
1941
+ src = prefix + Binary.toBase64(src.buffer);
1942
+ }
1943
+ if (src.startsWith("base64://")) {
1944
+ (0, Element2.warn)(`protocol "base64:" is deprecated and will be removed in the future, please use "data:" instead`);
1945
+ }
1946
+ return Element2(type, { ...args[0], src });
1947
+ };
1948
+ }
1949
+ __name(createAssetFactory, "createAssetFactory");
1950
+ __name3(createAssetFactory, "createAssetFactory");
1951
+ Element2.text = createFactory("text", "content");
1952
+ Element2.at = createFactory("at", "id");
1953
+ Element2.sharp = createFactory("sharp", "id");
1954
+ Element2.quote = createFactory("quote", "id");
1955
+ Element2.image = createAssetFactory("img");
1956
+ Element2.img = createAssetFactory("img");
1957
+ Element2.video = createAssetFactory("video");
1958
+ Element2.audio = createAssetFactory("audio");
1959
+ Element2.file = createAssetFactory("file");
1960
+ function i18n(path5, children) {
1961
+ return Element2("i18n", typeof path5 === "string" ? { path: path5 } : path5, children);
1962
+ }
1963
+ __name(i18n, "i18n");
1964
+ Element2.i18n = i18n;
1965
+ __name3(i18n, "i18n");
1966
+ })(Element || (Element = {}));
1967
+ module2.exports = Element;
1968
+ }
1969
+ });
1970
+ var lib_default = require_index();
1971
+
1972
+ // ../../node_modules/@satorijs/protocol/lib/index.mjs
1973
+ var __defProp4 = Object.defineProperty;
1974
+ var __name4 = /* @__PURE__ */ __name((target, value) => __defProp4(target, "name", { value, configurable: true }), "__name");
1975
+ function Field(name2) {
1976
+ return { name: name2 };
1977
+ }
1978
+ __name(Field, "Field");
1979
+ __name4(Field, "Field");
1980
+ function Method(name2, fields, isForm = false) {
1981
+ return { name: name2, fields: fields.map(Field), isForm };
1982
+ }
1983
+ __name(Method, "Method");
1984
+ __name4(Method, "Method");
1985
+ var Methods = {
1986
+ "channel.get": Method("getChannel", ["channel_id", "guild_id"]),
1987
+ "channel.list": Method("getChannelList", ["guild_id", "next"]),
1988
+ "channel.create": Method("createChannel", ["guild_id", "data"]),
1989
+ "channel.update": Method("updateChannel", ["channel_id", "data"]),
1990
+ "channel.delete": Method("deleteChannel", ["channel_id"]),
1991
+ "channel.mute": Method("muteChannel", ["channel_id", "guild_id", "enable"]),
1992
+ "message.create": Method("createMessage", ["channel_id", "content", "referrer"]),
1993
+ "message.update": Method("editMessage", ["channel_id", "message_id", "content"]),
1994
+ "message.delete": Method("deleteMessage", ["channel_id", "message_id"]),
1995
+ "message.get": Method("getMessage", ["channel_id", "message_id"]),
1996
+ "message.list": Method("getMessageList", ["channel_id", "next", "direction", "limit", "order"]),
1997
+ "reaction.create": Method("createReaction", ["channel_id", "message_id", "emoji"]),
1998
+ "reaction.delete": Method("deleteReaction", ["channel_id", "message_id", "emoji", "user_id"]),
1999
+ "reaction.clear": Method("clearReaction", ["channel_id", "message_id", "emoji"]),
2000
+ "reaction.list": Method("getReactionList", ["channel_id", "message_id", "emoji", "next"]),
2001
+ "upload.create": Method("createUpload", [], true),
2002
+ "guild.get": Method("getGuild", ["guild_id"]),
2003
+ "guild.list": Method("getGuildList", ["next"]),
2004
+ "guild.member.get": Method("getGuildMember", ["guild_id", "user_id"]),
2005
+ "guild.member.list": Method("getGuildMemberList", ["guild_id", "next"]),
2006
+ "guild.member.kick": Method("kickGuildMember", ["guild_id", "user_id", "permanent"]),
2007
+ "guild.member.mute": Method("muteGuildMember", ["guild_id", "user_id", "duration", "reason"]),
2008
+ "guild.member.role.set": Method("setGuildMemberRole", ["guild_id", "user_id", "role_id"]),
2009
+ "guild.member.role.unset": Method("unsetGuildMemberRole", ["guild_id", "user_id", "role_id"]),
2010
+ "guild.member.role.list": Method("getGuildMemberRoleList", ["guild_id", "user_id", "next"]),
2011
+ "guild.role.list": Method("getGuildRoleList", ["guild_id", "next"]),
2012
+ "guild.role.create": Method("createGuildRole", ["guild_id", "data"]),
2013
+ "guild.role.update": Method("updateGuildRole", ["guild_id", "role_id", "data"]),
2014
+ "guild.role.delete": Method("deleteGuildRole", ["guild_id", "role_id"]),
2015
+ "login.get": Method("getLogin", []),
2016
+ "user.get": Method("getUser", ["user_id"]),
2017
+ "user.channel.create": Method("createDirectChannel", ["user_id", "guild_id"]),
2018
+ "friend.list": Method("getFriendList", ["next"]),
2019
+ "friend.delete": Method("deleteFriend", ["user_id"]),
2020
+ "friend.approve": Method("handleFriendRequest", ["message_id", "approve", "comment"]),
2021
+ "guild.approve": Method("handleGuildRequest", ["message_id", "approve", "comment"]),
2022
+ "guild.member.approve": Method("handleGuildMemberRequest", ["message_id", "approve", "comment"])
2023
+ };
2024
+ var Channel;
2025
+ ((Channel2) => {
2026
+ let Type;
2027
+ ((Type2) => {
2028
+ Type2[Type2["TEXT"] = 0] = "TEXT";
2029
+ Type2[Type2["DIRECT"] = 1] = "DIRECT";
2030
+ Type2[Type2["CATEGORY"] = 2] = "CATEGORY";
2031
+ Type2[Type2["VOICE"] = 3] = "VOICE";
2032
+ })(Type = Channel2.Type || (Channel2.Type = {}));
2033
+ })(Channel || (Channel = {}));
2034
+ function Resource(attrs = [], children = [], content) {
2035
+ return { attrs, children, content };
2036
+ }
2037
+ __name(Resource, "Resource");
2038
+ __name4(Resource, "Resource");
2039
+ ((Resource2) => {
2040
+ const Definitions = {
2041
+ user: Resource2(["id", "name", "nick", "avatar", "isBot"]),
2042
+ member: Resource2(["name", "nick", "avatar"]),
2043
+ channel: Resource2(["id", "type", "name"]),
2044
+ guild: Resource2(["id", "name", "avatar"]),
2045
+ quote: Resource2(["id"], ["quote", "user", "member", "channel"], "content")
2046
+ };
2047
+ function encode(type, data) {
2048
+ const resource = Definitions[type];
2049
+ const element = lib_default(type, pick(data, resource.attrs));
2050
+ for (const key of resource.children) {
2051
+ if (isNullable(data[key])) continue;
2052
+ element.children.push(encode(key, data[key]));
2053
+ }
2054
+ if (resource.content && !isNullable(data[resource.content])) {
2055
+ element.children.push(...lib_default.parse(data[resource.content]));
2056
+ }
2057
+ return element;
2058
+ }
2059
+ __name(encode, "encode");
2060
+ Resource2.encode = encode;
2061
+ __name4(encode, "encode");
2062
+ function decode(element) {
2063
+ const data = element.attrs;
2064
+ const resource = Definitions[element.type];
2065
+ for (const key of resource.children) {
2066
+ const index = element.children.findIndex((el) => el.type === key);
2067
+ if (index === -1) continue;
2068
+ const [child] = element.children.splice(index, 1);
2069
+ data[key] = decode(child);
2070
+ }
2071
+ if (resource.content && element.children.length) {
2072
+ data[resource.content] = element.children.join("");
2073
+ }
2074
+ return data;
2075
+ }
2076
+ __name(decode, "decode");
2077
+ Resource2.decode = decode;
2078
+ __name4(decode, "decode");
2079
+ })(Resource || (Resource = {}));
2080
+ function transformKey(source, callback) {
2081
+ if (!source || typeof source !== "object") return source;
2082
+ if (Array.isArray(source)) return source.map((value) => transformKey(value, callback));
2083
+ return Object.fromEntries(Object.entries(source).map(([key, value]) => {
2084
+ if (key.startsWith("_") || key === "referrer") return [key, value];
2085
+ return [callback(key), transformKey(value, callback)];
2086
+ }));
2087
+ }
2088
+ __name(transformKey, "transformKey");
2089
+ __name4(transformKey, "transformKey");
2090
+ var Opcode = /* @__PURE__ */ ((Opcode2) => {
2091
+ Opcode2[Opcode2["EVENT"] = 0] = "EVENT";
2092
+ Opcode2[Opcode2["PING"] = 1] = "PING";
2093
+ Opcode2[Opcode2["PONG"] = 2] = "PONG";
2094
+ Opcode2[Opcode2["IDENTIFY"] = 3] = "IDENTIFY";
2095
+ Opcode2[Opcode2["READY"] = 4] = "READY";
2096
+ Opcode2[Opcode2["META"] = 5] = "META";
2097
+ return Opcode2;
2098
+ })(Opcode || {});
2099
+ var WebSocket;
2100
+ ((WebSocket2) => {
2101
+ WebSocket2.CONNECTING = 0;
2102
+ WebSocket2.OPEN = 1;
2103
+ WebSocket2.CLOSING = 2;
2104
+ WebSocket2.CLOSED = 3;
2105
+ })(WebSocket || (WebSocket = {}));
2106
+
2107
+ // src/satori.ts
2108
+ function resolveSatoriEndpoint(ctx) {
2109
+ const url = ctx.satori?.server?.url ?? "/satori";
2110
+ const clean = url.startsWith("undefined") ? url.slice(9) : url;
2111
+ if (/^https?:\/\//i.test(clean)) return clean;
2112
+ const base = ctx.server?.selfUrl ?? ctx.server?.config?.selfUrl ?? "";
2113
+ return `${base}${clean.startsWith("/") ? clean : `/${clean}`}`;
2114
+ }
2115
+ __name(resolveSatoriEndpoint, "resolveSatoriEndpoint");
2116
+ function toSatoriEventUrl(endpoint) {
2117
+ const url = new URL(endpoint);
2118
+ url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
2119
+ url.pathname = `${url.pathname.replace(/\/+$/, "")}/v1/events`;
2120
+ url.search = "";
2121
+ return url.href;
2122
+ }
2123
+ __name(toSatoriEventUrl, "toSatoriEventUrl");
2124
+
2125
+ // src/gateway.ts
2126
+ var WS_OPEN = 1;
2127
+ var RECONNECT_MAX_DELAY = 3e4;
2128
+ function getObject2(value) {
2129
+ return typeof value === "object" && value !== null ? value : {};
2130
+ }
2131
+ __name(getObject2, "getObject");
2132
+ function getString3(value) {
2133
+ return typeof value === "string" ? value : "";
2134
+ }
2135
+ __name(getString3, "getString");
2136
+ function getNumber2(value) {
2137
+ const num = Number(value);
2138
+ return Number.isFinite(num) ? num : 0;
2139
+ }
2140
+ __name(getNumber2, "getNumber");
2141
+ var _SatoriGateway = class _SatoriGateway {
2142
+ constructor(ctx, config, database, recorder, logger, onPayload) {
2143
+ this.ctx = ctx;
2144
+ this.config = config;
2145
+ this.database = database;
2146
+ this.recorder = recorder;
2147
+ this.logger = logger;
2148
+ this.onPayload = onPayload;
2149
+ this.stopped = true;
2150
+ this.retry = 0;
2151
+ this.sequence = 0;
2152
+ this.logins = [];
2153
+ this.online = false;
2154
+ }
2155
+ async start() {
2156
+ this.stopped = false;
2157
+ this.retry = 0;
2158
+ this.sequence = await this.loadSequence();
2159
+ this.connect();
2160
+ }
2161
+ dispose() {
2162
+ this.stopped = true;
2163
+ this.pingDispose?.();
2164
+ this.reconnectDispose?.();
2165
+ this.socket?.close();
2166
+ this.socket = void 0;
2167
+ this.setOnline(false);
2168
+ }
2169
+ getLogins() {
2170
+ return this.logins.map((item) => ({ ...item }));
2171
+ }
2172
+ async loadSequence() {
2173
+ const value = await this.database.getMeta("satori:sn");
2174
+ return getNumber2(value);
2175
+ }
2176
+ connect() {
2177
+ if (this.stopped) return;
2178
+ let socket;
2179
+ try {
2180
+ socket = this.ctx.http.ws(toSatoriEventUrl(resolveSatoriEndpoint(this.ctx)));
2181
+ } catch (error) {
2182
+ this.logger.warn("Satori 连接创建失败:", error);
2183
+ this.scheduleReconnect();
2184
+ return;
2185
+ }
2186
+ this.socket = socket;
2187
+ socket.addEventListener("open", () => {
2188
+ if (this.socket !== socket) {
2189
+ socket.close();
2190
+ return;
2191
+ }
2192
+ this.retry = 0;
2193
+ this.setOnline(true);
2194
+ socket.send(JSON.stringify({
2195
+ op: Opcode.IDENTIFY,
2196
+ body: {
2197
+ token: this.token,
2198
+ sn: this.sequence || void 0
2199
+ }
2200
+ }));
2201
+ if (!this.pingDispose) {
2202
+ this.pingDispose = this.ctx.setInterval(() => {
2203
+ if (this.socket?.readyState === WS_OPEN) {
2204
+ this.socket.send(JSON.stringify({ op: Opcode.PING, body: {} }));
2205
+ }
2206
+ }, 1e4);
2207
+ }
2208
+ });
2209
+ socket.addEventListener("message", (event) => {
2210
+ void this.handleMessage(event.data).catch((error) => {
2211
+ this.logger.warn("Satori 事件处理失败:", error);
2212
+ });
2213
+ });
2214
+ socket.addEventListener("close", () => {
2215
+ if (this.socket === socket) this.socket = void 0;
2216
+ this.pingDispose?.();
2217
+ this.pingDispose = void 0;
2218
+ this.setOnline(false);
2219
+ this.scheduleReconnect();
2220
+ });
2221
+ socket.addEventListener("error", () => {
2222
+ socket.close();
2223
+ });
2224
+ }
2225
+ get token() {
2226
+ return getString3(this.ctx.satori?.server?.config?.token);
2227
+ }
2228
+ scheduleReconnect() {
2229
+ if (this.stopped || this.reconnectDispose) return;
2230
+ const delay = Math.min(RECONNECT_MAX_DELAY, 1e3 * 2 ** this.retry);
2231
+ this.retry += 1;
2232
+ this.logger.logInfo("Satori 将在", delay, "ms 后重连");
2233
+ this.reconnectDispose = this.ctx.setTimeout(() => {
2234
+ this.reconnectDispose = void 0;
2235
+ this.connect();
2236
+ }, delay);
2237
+ }
2238
+ async handleMessage(data) {
2239
+ let payload;
2240
+ try {
2241
+ payload = JSON.parse(String(data));
2242
+ } catch {
2243
+ this.logger.warn("Satori 消息解析失败");
2244
+ return;
2245
+ }
2246
+ if (payload.op === Opcode.READY) {
2247
+ const body2 = getObject2(payload.body);
2248
+ this.logins = this.normalizeLogins(body2.logins);
2249
+ this.onPayload({ kind: "ready", logins: this.getLogins() });
2250
+ return;
2251
+ }
2252
+ if (payload.op !== Opcode.EVENT) return;
2253
+ const body = getObject2(payload.body);
2254
+ const sn = getNumber2(body.sn);
2255
+ if (sn) {
2256
+ this.sequence = sn;
2257
+ void this.database.setMeta("satori:sn", sn).catch((error) => {
2258
+ this.logger.warn("Satori sequence 保存失败:", error);
2259
+ });
2260
+ }
2261
+ const type = getString3(body.type);
2262
+ if (type === "login-added" || type === "login-updated") {
2263
+ const next = this.normalizeLogins([body.login]);
2264
+ if (next.length) {
2265
+ this.logins = this.logins.filter((item) => {
2266
+ return !(item.platform === next[0].platform && item.selfId === next[0].selfId);
2267
+ });
2268
+ this.logins.push(next[0]);
2269
+ this.onPayload({ kind: "ready", logins: this.getLogins() });
2270
+ }
2271
+ return;
2272
+ }
2273
+ if (type === "login-removed") {
2274
+ const login2 = getObject2(body.login);
2275
+ const loginUser2 = getObject2(login2.user);
2276
+ const platform2 = getString3(body.platform) || getString3(login2.platform);
2277
+ const selfId2 = getString3(body.self_id) || getString3(body.selfId) || getString3(loginUser2.id);
2278
+ this.logins = this.logins.filter((item) => {
2279
+ return !(item.platform === platform2 && item.selfId === selfId2);
2280
+ });
2281
+ this.onPayload({ kind: "ready", logins: this.getLogins() });
2282
+ return;
2283
+ }
2284
+ const login = getObject2(body.login);
2285
+ const loginUser = getObject2(login.user);
2286
+ const platform = getString3(body.platform) || getString3(login.platform);
2287
+ const selfId = getString3(body.self_id) || getString3(body.selfId) || getString3(loginUser.id);
2288
+ const event = {
2289
+ type: getString3(body.type),
2290
+ platform,
2291
+ selfId,
2292
+ timestamp: getNumber2(body.timestamp) || Date.now(),
2293
+ sn,
2294
+ body
2295
+ };
2296
+ if (this.isBlocked(platform)) return;
2297
+ try {
2298
+ await this.recorder.handleEvent(body);
2299
+ } catch (error) {
2300
+ this.logger.warn("Satori 消息写入失败:", error);
2301
+ }
2302
+ this.onPayload({ kind: "event", event });
2303
+ }
2304
+ normalizeLogins(value) {
2305
+ if (!Array.isArray(value)) return [];
2306
+ const result = [];
2307
+ for (const item of value) {
2308
+ const login = getObject2(item);
2309
+ const user = getObject2(login.user);
2310
+ const platform = getString3(login.platform) || getString3(user.platform);
2311
+ const selfId = getString3(login.self_id) || getString3(login.selfId) || getString3(user.id);
2312
+ if (!platform || !selfId || this.isBlocked(platform)) continue;
2313
+ result.push({
2314
+ platform,
2315
+ selfId,
2316
+ name: getString3(user.name) || getString3(user.nick) || getString3(user.nickname) || selfId,
2317
+ avatar: getString3(user.avatar) || void 0,
2318
+ status: getNumber2(login.status),
2319
+ features: Array.isArray(login.features) ? login.features.map(String) : []
2320
+ });
2321
+ }
2322
+ return result;
2323
+ }
2324
+ isBlocked(platform) {
2325
+ return (this.config.blockedPlatforms ?? []).some((item) => {
2326
+ return item.exactMatch ? platform === item.platformName : platform.includes(item.platformName);
2327
+ });
2328
+ }
2329
+ setOnline(online) {
2330
+ if (this.online === online) return;
2331
+ this.online = online;
2332
+ this.onPayload({ kind: "status", online });
2333
+ }
2334
+ };
2335
+ __name(_SatoriGateway, "SatoriGateway");
2336
+ var SatoriGateway = _SatoriGateway;
2337
+
1002
2338
  // src/media.ts
1003
2339
  var import_node_crypto2 = require("node:crypto");
1004
2340
  var import_node_fs = require("node:fs");
@@ -1357,26 +2693,26 @@ var KOISHI_STATUS_I18N_FALLBACK = {
1357
2693
  "commands.status.messages.status.2": "连接中",
1358
2694
  "commands.status.messages.status.3": "异常"
1359
2695
  };
1360
- function getString2(value) {
2696
+ function getString4(value) {
1361
2697
  return typeof value === "string" ? value : "";
1362
2698
  }
1363
- __name(getString2, "getString");
2699
+ __name(getString4, "getString");
1364
2700
  function getMessageId(value) {
1365
- const raw = getObject(value).id ?? getObject(value).message_id;
2701
+ const raw = getObject3(value).id ?? getObject3(value).message_id;
1366
2702
  return raw == null ? "" : String(raw);
1367
2703
  }
1368
2704
  __name(getMessageId, "getMessageId");
1369
- function getNumber(value) {
2705
+ function getNumber3(value) {
1370
2706
  const num = Number(value);
1371
2707
  return Number.isFinite(num) ? num : 0;
1372
2708
  }
1373
- __name(getNumber, "getNumber");
1374
- function getObject(value) {
2709
+ __name(getNumber3, "getNumber");
2710
+ function getObject3(value) {
1375
2711
  return typeof value === "object" && value !== null ? value : {};
1376
2712
  }
1377
- __name(getObject, "getObject");
2713
+ __name(getObject3, "getObject");
1378
2714
  function toSatoriElement(value) {
1379
- const raw = getObject(value);
2715
+ const raw = getObject3(value);
1380
2716
  const rawAttrs = raw.attrs ?? raw.data;
1381
2717
  const attrs = typeof rawAttrs === "object" && rawAttrs !== null ? rawAttrs : {};
1382
2718
  const children = Array.isArray(raw.children) ? raw.children.map(toSatoriElement) : [];
@@ -1396,37 +2732,37 @@ function toSegments(elements, resolveI18n) {
1396
2732
  for (const raw of elements) {
1397
2733
  const element = toSatoriElement(raw);
1398
2734
  const attrs = element.attrs ?? {};
1399
- const type = getString2(element.type);
2735
+ const type = getString4(element.type);
1400
2736
  if (type === "text") {
1401
- result.push({ type: "text", text: getString2(attrs.content) || getString2(attrs.text) });
2737
+ result.push({ type: "text", text: getString4(attrs.content) || getString4(attrs.text) });
1402
2738
  } else if (type === "at") {
1403
- const id = getString2(attrs.id) || getString2(attrs.qq);
1404
- const name2 = getString2(attrs.name) || (attrs.type === "all" ? "所有人" : id);
2739
+ const id = getString4(attrs.id) || getString4(attrs.qq);
2740
+ const name2 = getString4(attrs.name) || (attrs.type === "all" ? "所有人" : id);
1405
2741
  result.push({ type: "at", qq: id, text: name2.startsWith("@") ? name2 : `@${name2}` });
1406
2742
  } else if (type === "img" || type === "image") {
1407
- const src = getString2(attrs.src) || getString2(attrs.url) || getString2(attrs.file);
1408
- result.push({ type: "image", file: src, url: src, summary: getString2(attrs.title) });
2743
+ const src = getString4(attrs.src) || getString4(attrs.url) || getString4(attrs.file);
2744
+ result.push({ type: "image", file: src, url: src, summary: getString4(attrs.title) });
1409
2745
  } else if (type === "audio" || type === "record") {
1410
- const src = getString2(attrs.src) || getString2(attrs.url) || getString2(attrs.file);
2746
+ const src = getString4(attrs.src) || getString4(attrs.url) || getString4(attrs.file);
1411
2747
  result.push({ type: "record", file: src, url: src });
1412
2748
  } else if (type === "video") {
1413
- const src = getString2(attrs.src) || getString2(attrs.url) || getString2(attrs.file);
2749
+ const src = getString4(attrs.src) || getString4(attrs.url) || getString4(attrs.file);
1414
2750
  result.push({ type: "video", file: src, url: src });
1415
2751
  } else if (type === "file") {
1416
- const src = getString2(attrs.src) || getString2(attrs.url) || getString2(attrs.file);
1417
- result.push({ type: "file", file: src, url: src, name: getString2(attrs.name) });
2752
+ const src = getString4(attrs.src) || getString4(attrs.url) || getString4(attrs.file);
2753
+ result.push({ type: "file", file: src, url: src, name: getString4(attrs.name) });
1418
2754
  } else if (type === "quote") {
1419
- result.push({ type: "reply", id: getString2(attrs.id) });
2755
+ result.push({ type: "reply", id: getString4(attrs.id) });
1420
2756
  } else if (type === "json") {
1421
- result.push({ type: "json", data: getString2(attrs.data) || getString2(attrs.content) });
2757
+ result.push({ type: "json", data: getString4(attrs.data) || getString4(attrs.content) });
1422
2758
  } else if (type === "xml") {
1423
- result.push({ type: "xml", data: getString2(attrs.data) || getString2(attrs.content) });
2759
+ result.push({ type: "xml", data: getString4(attrs.data) || getString4(attrs.content) });
1424
2760
  } else if (type === "markdown") {
1425
- result.push({ type: "markdown", content: getString2(attrs.content) });
2761
+ result.push({ type: "markdown", content: getString4(attrs.content) });
1426
2762
  } else if (isForwardContainer(element)) {
1427
2763
  result.push({
1428
2764
  type: "forward",
1429
- id: getString2(attrs.id),
2765
+ id: getString4(attrs.id),
1430
2766
  content: toForwardNodes(element.children ?? [], resolveI18n) ?? []
1431
2767
  });
1432
2768
  } else if (type === "p") {
@@ -1435,7 +2771,7 @@ function toSegments(elements, resolveI18n) {
1435
2771
  } else if (type === "br") {
1436
2772
  result.push({ type: "text", text: "\n" });
1437
2773
  } else if (type === "i18n") {
1438
- const path5 = getString2(attrs.path);
2774
+ const path5 = getString4(attrs.path);
1439
2775
  const resolved = resolveI18n ? resolveI18n(attrs) : "";
1440
2776
  result.push({ type: "text", text: resolved || `[${path5 || "i18n"}]` });
1441
2777
  } else if (element.children?.length) {
@@ -1466,12 +2802,12 @@ function toForwardNodes(elements, resolveI18n) {
1466
2802
  const authorAttrs = author?.attrs ?? {};
1467
2803
  const content = children.filter((item) => item.type !== "author");
1468
2804
  nodes.push({
1469
- message_id: getString2(attrs.id),
1470
- time: getNumber(authorAttrs.time) || getNumber(attrs.time),
2805
+ message_id: getString4(attrs.id),
2806
+ time: getNumber3(authorAttrs.time) || getNumber3(attrs.time),
1471
2807
  sender: {
1472
- user_id: getString2(authorAttrs.id),
1473
- nickname: getString2(authorAttrs.name),
1474
- avatar: getString2(authorAttrs.avatar)
2808
+ user_id: getString4(authorAttrs.id),
2809
+ nickname: getString4(authorAttrs.name),
2810
+ avatar: getString4(authorAttrs.avatar)
1475
2811
  },
1476
2812
  message: toSegments(content, resolveI18n)
1477
2813
  });
@@ -1486,7 +2822,7 @@ function detectKind(elements) {
1486
2822
  while (stack.length) {
1487
2823
  const element = stack.pop();
1488
2824
  if (!element) continue;
1489
- const type = getString2(element.type);
2825
+ const type = getString4(element.type);
1490
2826
  if (isForwardContainer(element)) return "forward";
1491
2827
  if (type === "img" || type === "image" || type === "mface") return "image";
1492
2828
  if (type === "audio" || type === "record") return "voice";
@@ -1503,7 +2839,7 @@ function forwardId(elements) {
1503
2839
  const element = stack.pop();
1504
2840
  if (!element) continue;
1505
2841
  if (isForwardContainer(element)) {
1506
- const id = getString2(element.attrs?.id);
2842
+ const id = getString4(element.attrs?.id);
1507
2843
  if (id) return id;
1508
2844
  }
1509
2845
  stack.push(...element.children ?? []);
@@ -1656,10 +2992,10 @@ var _SelfMessageRecorder = class _SelfMessageRecorder {
1656
2992
  if (session.type !== "send") return;
1657
2993
  const platform = session.platform || "";
1658
2994
  const selfId = session.selfId || "";
1659
- const event = getObject(session.event);
1660
- const eventChannel = getObject(event.channel);
1661
- const eventMessage = getObject(event.message);
1662
- const channelId = session.channelId || getString2(eventChannel.id);
2995
+ const event = getObject3(session.event);
2996
+ const eventChannel = getObject3(event.channel);
2997
+ const eventMessage = getObject3(event.message);
2998
+ const channelId = session.channelId || getString4(eventChannel.id);
1663
2999
  if (this.isBlocked(platform) || !platform || !selfId || !channelId) return;
1664
3000
  const messageId = session.messageId || getMessageId(eventMessage);
1665
3001
  if (messageId) {
@@ -1691,7 +3027,7 @@ var _SelfMessageRecorder = class _SelfMessageRecorder {
1691
3027
  });
1692
3028
  }
1693
3029
  resolveI18nElement(attrs) {
1694
- const path5 = getString2(attrs.path);
3030
+ const path5 = getString4(attrs.path);
1695
3031
  if (!path5 || !this.ctx.i18n) return `[${path5 || "i18n"}]`;
1696
3032
  try {
1697
3033
  const locales = this.ctx.i18n.fallback([]);
@@ -1737,9 +3073,9 @@ var _SelfMessageRecorder = class _SelfMessageRecorder {
1737
3073
  normalizeElements(content) {
1738
3074
  try {
1739
3075
  const normalized = typeof content === "string" ? content : Array.isArray(content) ? content.map((raw) => {
1740
- const item = getObject(raw);
3076
+ const item = getObject3(raw);
1741
3077
  if (typeof item.data === "object" && item.data !== null && item.attrs === void 0) {
1742
- return (0, import_koishi.h)(getString2(item.type), item.data);
3078
+ return (0, import_koishi.h)(getString4(item.type), item.data);
1743
3079
  }
1744
3080
  return raw;
1745
3081
  }) : content;
@@ -1754,24 +3090,17 @@ __name(_SelfMessageRecorder, "SelfMessageRecorder");
1754
3090
  var SelfMessageRecorder = _SelfMessageRecorder;
1755
3091
 
1756
3092
  // src/bootstrap.ts
1757
- function resolveEndpoint(ctx, config) {
1758
- const url = ctx.satori?.server?.url ?? "/satori";
1759
- const clean = url.startsWith("undefined") ? url.slice(9) : url;
1760
- if (/^https?:\/\//i.test(clean)) return clean;
1761
- const base = ctx.server?.selfUrl ?? ctx.server?.config?.selfUrl ?? "";
1762
- return `${base}${clean.startsWith("/") ? clean : `/${clean}`}`;
1763
- }
1764
- __name(resolveEndpoint, "resolveEndpoint");
1765
- function registerBootstrap(ctx, config, database, logger) {
3093
+ function registerBootstrap(ctx, config, database, logger, getLogins) {
1766
3094
  ctx.console.addListener("chat-patch/bootstrap", () => {
1767
3095
  logger.logInfo("返回 Satori bootstrap:", {
1768
- endpoint: resolveEndpoint(ctx, config),
3096
+ endpoint: resolveSatoriEndpoint(ctx),
1769
3097
  basePath: config.basePath
1770
3098
  });
1771
3099
  return {
1772
- endpoint: resolveEndpoint(ctx, config),
3100
+ endpoint: resolveSatoriEndpoint(ctx),
1773
3101
  token: ctx.satori?.server?.config?.token ?? "",
1774
3102
  basePath: config.basePath,
3103
+ logins: getLogins(),
1775
3104
  blockedPlatforms: config.blockedPlatforms ?? []
1776
3105
  };
1777
3106
  }, { authority: 4 });
@@ -2449,7 +3778,7 @@ var name = "chat-patch";
2449
3778
  var reusable = false;
2450
3779
  var filter = false;
2451
3780
  var inject = {
2452
- required: ["console", "server", "satori.server"]
3781
+ required: ["console", "server", "http", "satori.server"]
2453
3782
  };
2454
3783
  var usage = `
2455
3784
  ---
@@ -2466,17 +3795,25 @@ async function apply(ctx, config) {
2466
3795
  const contactCache = new ContactCacheService(ctx, database, pluginLogger);
2467
3796
  const media = new MediaManager(ctx, config, database, pluginLogger);
2468
3797
  media.start();
2469
- const recorder = new Recorder(ctx, config, database, media, contactCache, pluginLogger);
2470
- recorder.start();
3798
+ const recorder = new Recorder(config, database, media, contactCache, pluginLogger);
3799
+ const gateway = new SatoriGateway(ctx, config, database, recorder, pluginLogger, (payload) => {
3800
+ void ctx.console.broadcast("chat-patch/event", payload).catch((error) => {
3801
+ pluginLogger.warn("推送前端事件失败:", error);
3802
+ });
3803
+ });
2471
3804
  const selfMessages = new SelfMessageRecorder(ctx, config, database, media, pluginLogger);
2472
3805
  selfMessages.start();
2473
- registerBootstrap(ctx, config, database, pluginLogger);
3806
+ void gateway.start().catch((error) => {
3807
+ pluginLogger.warn("Satori 网关启动失败:", error);
3808
+ });
3809
+ registerBootstrap(ctx, config, database, pluginLogger, () => gateway.getLogins());
2474
3810
  registerWeb(ctx, config, database, contactCache, media, pluginLogger);
2475
3811
  ctx.console.addEntry({
2476
3812
  dev: import_node_path4.default.resolve(__dirname, "../client/index.ts"),
2477
3813
  prod: import_node_path4.default.resolve(__dirname, "../dist")
2478
3814
  });
2479
3815
  ctx.on("dispose", async () => {
3816
+ gateway.dispose();
2480
3817
  selfMessages.dispose();
2481
3818
  media.dispose();
2482
3819
  await database.dispose();