@senguoyun/dsh-arkme 0.1.79 → 0.1.80
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/docs/reaction-desktop-review.md +33 -0
- package/docs/reaction-pre-merge-review.md +78 -0
- package/lib/client.js +14212 -10463
- package/lib/client.js.map +1 -1
- package/lib/harness-model-client.js +8 -0
- package/lib/harness-native-selection-client.js +8 -0
- package/lib/harness-sidebar-client.js +165 -16
- package/lib/index.js +84 -1
- package/lib/persistent-extension.js +1 -1
- package/lib/{persistent-runtime-CB7mEj3E.js → persistent-runtime-CGclHTN1.js} +792 -165
- package/lib/sdk.js +8 -0
- package/lib/types/arkme-service.d.ts +1 -0
- package/lib/types/arkme-service.d.ts.map +1 -1
- package/lib/types/client/ArkmeDataManagementSettings.d.ts.map +1 -1
- package/lib/types/client/ArkmeDayTimeline.d.ts +2 -0
- package/lib/types/client/ArkmeDayTimeline.d.ts.map +1 -1
- package/lib/types/client/ArkmeGroupChatControls.d.ts.map +1 -1
- package/lib/types/client/ArkmePersonalDayCalendar.d.ts.map +1 -1
- package/lib/types/client/ArkmeReactionActorCard.d.ts +7 -0
- package/lib/types/client/ArkmeReactionActorCard.d.ts.map +1 -0
- package/lib/types/client/ArkmeReactionHistorySettings.d.ts +7 -0
- package/lib/types/client/ArkmeReactionHistorySettings.d.ts.map +1 -0
- package/lib/types/client/ArkmeReactionNotification.d.ts +11 -0
- package/lib/types/client/ArkmeReactionNotification.d.ts.map +1 -0
- package/lib/types/client/ArkmeReactionPhrases.d.ts +8 -0
- package/lib/types/client/ArkmeReactionPhrases.d.ts.map +1 -0
- package/lib/types/client/ArkmeReactionPreview.d.ts +42 -0
- package/lib/types/client/ArkmeReactionPreview.d.ts.map +1 -0
- package/lib/types/client/ArkmeRichComposerInput.d.ts.map +1 -1
- package/lib/types/client/ArkmeSettingsSurface.d.ts.map +1 -1
- package/lib/types/client/ArkmeSidebar.d.ts.map +1 -1
- package/lib/types/client/ArkmeVirtualWorkspace.d.ts +2 -1
- package/lib/types/client/ArkmeVirtualWorkspace.d.ts.map +1 -1
- package/lib/types/client/ReactionLabel.d.ts +8 -0
- package/lib/types/client/ReactionLabel.d.ts.map +1 -0
- package/lib/types/client/ReactionPhraseDialog.d.ts +14 -0
- package/lib/types/client/ReactionPhraseDialog.d.ts.map +1 -0
- package/lib/types/client/calendar-activity-model.d.ts +1 -1
- package/lib/types/client/calendar-activity-model.d.ts.map +1 -1
- package/lib/types/client/composer-draft-store.d.ts +6 -1
- package/lib/types/client/composer-draft-store.d.ts.map +1 -1
- package/lib/types/client/conversation-window-sync.d.ts.map +1 -1
- package/lib/types/client/harness-account-sessions.d.ts +6 -0
- package/lib/types/client/harness-account-sessions.d.ts.map +1 -1
- package/lib/types/client/harness-session-dropdown.d.ts.map +1 -1
- package/lib/types/client/harness-session-origin.d.ts +2 -1
- package/lib/types/client/harness-session-origin.d.ts.map +1 -1
- package/lib/types/client/harness-session-summary.d.ts +11 -2
- package/lib/types/client/harness-session-summary.d.ts.map +1 -1
- package/lib/types/client/outgoing-call-runtime.d.ts.map +1 -1
- package/lib/types/client/reaction-expression.d.ts +5 -0
- package/lib/types/client/reaction-expression.d.ts.map +1 -0
- package/lib/types/client/reaction-label-content.d.ts +20 -0
- package/lib/types/client/reaction-label-content.d.ts.map +1 -0
- package/lib/types/client/reaction-library.d.ts +26 -0
- package/lib/types/client/reaction-library.d.ts.map +1 -0
- package/lib/types/client/reaction-locate-layout.d.ts +5 -0
- package/lib/types/client/reaction-locate-layout.d.ts.map +1 -0
- package/lib/types/client/reaction-notifications.d.ts +49 -0
- package/lib/types/client/reaction-notifications.d.ts.map +1 -0
- package/lib/types/client/reaction-phrase-palette.d.ts +99 -0
- package/lib/types/client/reaction-phrase-palette.d.ts.map +1 -0
- package/lib/types/client/reaction-phrases.d.ts +7 -0
- package/lib/types/client/reaction-phrases.d.ts.map +1 -0
- package/lib/types/client/reaction-preview-store.d.ts +70 -0
- package/lib/types/client/reaction-preview-store.d.ts.map +1 -0
- package/lib/types/client/reaction-visible-target.d.ts +3 -0
- package/lib/types/client/reaction-visible-target.d.ts.map +1 -0
- package/lib/types/client/record-input-capture.d.ts +2 -0
- package/lib/types/client/record-input-capture.d.ts.map +1 -1
- package/lib/types/client/redesign/styles.d.ts.map +1 -1
- package/lib/types/client/ui-controller.d.ts +2 -1
- package/lib/types/client/ui-controller.d.ts.map +1 -1
- package/lib/types/client/use-phrase-layout-motion.d.ts +2 -0
- package/lib/types/client/use-phrase-layout-motion.d.ts.map +1 -0
- package/lib/types/client/use-reaction-history.d.ts +10 -0
- package/lib/types/client/use-reaction-history.d.ts.map +1 -0
- package/lib/types/client/use-reaction-phrase-drag.d.ts +19 -0
- package/lib/types/client/use-reaction-phrase-drag.d.ts.map +1 -0
- package/lib/types/host-api.d.ts.map +1 -1
- package/lib/types/reaction-contract.d.ts +217 -0
- package/lib/types/reaction-contract.d.ts.map +1 -0
- package/lib/types/sdk/index.d.ts +2 -0
- package/lib/types/sdk/index.d.ts.map +1 -1
- package/lib/types/services/chat-realtime-service.d.ts.map +1 -1
- package/lib/types/services/chat-service.d.ts +6 -0
- package/lib/types/services/chat-service.d.ts.map +1 -1
- package/lib/types/services/profile-service.d.ts +1 -1
- package/lib/types/services/profile-service.d.ts.map +1 -1
- package/lib/types/services/reaction-service.d.ts +28 -0
- package/lib/types/services/reaction-service.d.ts.map +1 -0
- package/lib/types/services/recording-import-gateway.d.ts +3 -1
- package/lib/types/services/recording-import-gateway.d.ts.map +1 -1
- package/lib/types/services/recording-import-upload-retry.d.ts +6 -0
- package/lib/types/services/recording-import-upload-retry.d.ts.map +1 -0
- package/lib/types/services/world-service.d.ts +3 -0
- package/lib/types/services/world-service.d.ts.map +1 -1
- package/lib/types/tools/business/conversation/index.d.ts.map +1 -1
- package/lib/types/tools/business/conversation/reactions.d.ts +3 -0
- package/lib/types/tools/business/conversation/reactions.d.ts.map +1 -0
- package/lib/types/tools/business/index.d.ts.map +1 -1
- package/lib/types/tools/ports/conversations.d.ts +1 -0
- package/lib/types/tools/ports/conversations.d.ts.map +1 -1
- package/lib/types/tools/registry/registrar.d.ts.map +1 -1
- package/lib/types/types.d.ts +2 -1
- package/lib/types/types.d.ts.map +1 -1
- package/package.json +5 -5
|
@@ -1951,6 +1951,372 @@ var ServiceRuntime = class {
|
|
|
1951
1951
|
}
|
|
1952
1952
|
};
|
|
1953
1953
|
//#endregion
|
|
1954
|
+
//#region src/services/reaction-service.ts
|
|
1955
|
+
function invalid$11() {
|
|
1956
|
+
throw new ArkmePluginError("reaction-response-invalid", "表态数据不完整,请重试", true);
|
|
1957
|
+
}
|
|
1958
|
+
function integer$4(value, min = 0) {
|
|
1959
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= min ? value : invalid$11();
|
|
1960
|
+
}
|
|
1961
|
+
function expression(raw) {
|
|
1962
|
+
const value = objectValue$2(raw);
|
|
1963
|
+
if (typeof value.text !== "string" || Array.from(value.text).length > 20) return invalid$11();
|
|
1964
|
+
const result = { text: value.text };
|
|
1965
|
+
for (const key of [
|
|
1966
|
+
"emoji",
|
|
1967
|
+
"hand",
|
|
1968
|
+
"color"
|
|
1969
|
+
]) if (value[key] !== void 0) {
|
|
1970
|
+
if (typeof value[key] !== "string" || value[key].length > 64) return invalid$11();
|
|
1971
|
+
result[key] = value[key];
|
|
1972
|
+
}
|
|
1973
|
+
return result;
|
|
1974
|
+
}
|
|
1975
|
+
function array(raw, max) {
|
|
1976
|
+
return Array.isArray(raw) && raw.length <= max ? raw : invalid$11();
|
|
1977
|
+
}
|
|
1978
|
+
function key(raw) {
|
|
1979
|
+
return typeof raw === "string" && /^[a-f0-9]{64}$/.test(raw) ? raw : invalid$11();
|
|
1980
|
+
}
|
|
1981
|
+
function group(raw) {
|
|
1982
|
+
const value = objectValue$2(raw);
|
|
1983
|
+
return {
|
|
1984
|
+
key: key(value.key),
|
|
1985
|
+
expression: expression(value.expression),
|
|
1986
|
+
count: integer$4(value.count, 1),
|
|
1987
|
+
actorIds: array(value.actor_user_ids ?? [], 3).map((id) => integer$4(id, 1))
|
|
1988
|
+
};
|
|
1989
|
+
}
|
|
1990
|
+
function state(raw) {
|
|
1991
|
+
const value = objectValue$2(raw);
|
|
1992
|
+
return {
|
|
1993
|
+
revision: integer$4(value.revision),
|
|
1994
|
+
selections: array(value.selections, 56).map((raw) => {
|
|
1995
|
+
const item = objectValue$2(raw);
|
|
1996
|
+
return {
|
|
1997
|
+
key: key(item.key),
|
|
1998
|
+
expression: expression(item.expression),
|
|
1999
|
+
at: integer$4(item.at)
|
|
2000
|
+
};
|
|
2001
|
+
})
|
|
2002
|
+
};
|
|
2003
|
+
}
|
|
2004
|
+
function library(raw) {
|
|
2005
|
+
const value = objectValue$2(raw);
|
|
2006
|
+
return {
|
|
2007
|
+
revision: integer$4(value.revision),
|
|
2008
|
+
items: array(value.items, 58).map(expression)
|
|
2009
|
+
};
|
|
2010
|
+
}
|
|
2011
|
+
function boolean(raw) {
|
|
2012
|
+
return typeof raw === "boolean" ? raw : invalid$11();
|
|
2013
|
+
}
|
|
2014
|
+
function targetKey(target) {
|
|
2015
|
+
return JSON.stringify([
|
|
2016
|
+
target.chat_session_uid ?? "",
|
|
2017
|
+
target.rel_uid ?? "",
|
|
2018
|
+
target.record_uid ?? "",
|
|
2019
|
+
target.owned === true
|
|
2020
|
+
]);
|
|
2021
|
+
}
|
|
2022
|
+
/** One Host owner for Browser, SDK and granted Tools; identities are resolved from signed message references. */
|
|
2023
|
+
var ReactionService = class {
|
|
2024
|
+
runtime;
|
|
2025
|
+
resolve;
|
|
2026
|
+
worldReference;
|
|
2027
|
+
chatSourceKey;
|
|
2028
|
+
historyContext;
|
|
2029
|
+
constructor(runtime, resolve, worldReference, chatSourceKey, historyContext) {
|
|
2030
|
+
this.runtime = runtime;
|
|
2031
|
+
this.resolve = resolve;
|
|
2032
|
+
this.worldReference = worldReference;
|
|
2033
|
+
this.chatSourceKey = chatSourceKey;
|
|
2034
|
+
this.historyContext = historyContext;
|
|
2035
|
+
}
|
|
2036
|
+
async request(input, signal) {
|
|
2037
|
+
const session = await this.runtime.requireSession();
|
|
2038
|
+
const accountKey = `${this.runtime.config.environment}:${session.userId}`;
|
|
2039
|
+
if (!input || input.accountKey !== accountKey) throw new ArkmePluginError("reaction-account-changed", "账号已变化,请重新打开表态", false);
|
|
2040
|
+
let path, body;
|
|
2041
|
+
const ids = /* @__PURE__ */ new Map();
|
|
2042
|
+
const resolve = async (target) => {
|
|
2043
|
+
if (!target || typeof target.id !== "string" || !target.id || target.id.length > 1024 || !(typeof target.worldRecordRef === "string" && !target.sourceRef && !target.messageActionRef || !target.worldRecordRef && typeof target.sourceRef === "string" && typeof target.messageActionRef === "string")) throw new ArkmePluginError("reaction-target-invalid", "表态目标无效", false);
|
|
2044
|
+
return await this.resolve(target);
|
|
2045
|
+
};
|
|
2046
|
+
switch (input.action) {
|
|
2047
|
+
case "notifications":
|
|
2048
|
+
path = "notifications/query";
|
|
2049
|
+
body = {
|
|
2050
|
+
after_id: input.after_id ?? "",
|
|
2051
|
+
limit: input.limit
|
|
2052
|
+
};
|
|
2053
|
+
break;
|
|
2054
|
+
case "notifications-read":
|
|
2055
|
+
if (!Array.isArray(input.items) || input.items.length < 1 || input.items.length > 50) return invalid$11();
|
|
2056
|
+
path = "notifications/read";
|
|
2057
|
+
body = { items: input.items.map((item) => ({
|
|
2058
|
+
id: key(item.id),
|
|
2059
|
+
revision: integer$4(item.revision, 1)
|
|
2060
|
+
})) };
|
|
2061
|
+
break;
|
|
2062
|
+
case "query": {
|
|
2063
|
+
if (!Array.isArray(input.targets) || input.targets.length < 1 || input.targets.length > 50) throw new ArkmePluginError("reaction-query-invalid", "表态查询数量无效", false);
|
|
2064
|
+
const targets = [], seenIDs = /* @__PURE__ */ new Set();
|
|
2065
|
+
for (const target of input.targets) {
|
|
2066
|
+
const wire = await resolve(target), key = targetKey(wire);
|
|
2067
|
+
if (seenIDs.has(target.id)) throw new ArkmePluginError("reaction-query-duplicate", "表态目标重复", false);
|
|
2068
|
+
seenIDs.add(target.id);
|
|
2069
|
+
const aliases = ids.get(key);
|
|
2070
|
+
if (aliases) aliases.push(target.id);
|
|
2071
|
+
else {
|
|
2072
|
+
ids.set(key, [target.id]);
|
|
2073
|
+
targets.push(wire);
|
|
2074
|
+
}
|
|
2075
|
+
}
|
|
2076
|
+
path = "query";
|
|
2077
|
+
body = { targets };
|
|
2078
|
+
break;
|
|
2079
|
+
}
|
|
2080
|
+
case "set":
|
|
2081
|
+
if (typeof input.active !== "boolean") throw new ArkmePluginError("reaction-state-invalid", "缺少表态状态", false);
|
|
2082
|
+
path = "set";
|
|
2083
|
+
body = {
|
|
2084
|
+
target: await resolve(input.target),
|
|
2085
|
+
expression: input.expression,
|
|
2086
|
+
active: input.active,
|
|
2087
|
+
expected_revision: input.expected_revision,
|
|
2088
|
+
request_id: input.request_id
|
|
2089
|
+
};
|
|
2090
|
+
break;
|
|
2091
|
+
case "actors":
|
|
2092
|
+
path = "actors/query";
|
|
2093
|
+
body = {
|
|
2094
|
+
target: await resolve(input.target),
|
|
2095
|
+
key: input.key,
|
|
2096
|
+
after_user_id: input.after_user_id,
|
|
2097
|
+
limit: input.limit
|
|
2098
|
+
};
|
|
2099
|
+
break;
|
|
2100
|
+
case "groups":
|
|
2101
|
+
path = "groups/query";
|
|
2102
|
+
body = {
|
|
2103
|
+
target: await resolve(input.target),
|
|
2104
|
+
after_key: input.after_key,
|
|
2105
|
+
limit: input.limit
|
|
2106
|
+
};
|
|
2107
|
+
break;
|
|
2108
|
+
case "history":
|
|
2109
|
+
path = "history/query";
|
|
2110
|
+
body = {
|
|
2111
|
+
start_at: input.start_at,
|
|
2112
|
+
end_at: input.end_at,
|
|
2113
|
+
limit: input.limit,
|
|
2114
|
+
...input.before_at === void 0 ? {} : {
|
|
2115
|
+
before_at: input.before_at,
|
|
2116
|
+
before_id: input.before_id
|
|
2117
|
+
}
|
|
2118
|
+
};
|
|
2119
|
+
break;
|
|
2120
|
+
case "received":
|
|
2121
|
+
path = "received/query";
|
|
2122
|
+
body = {
|
|
2123
|
+
limit: input.limit,
|
|
2124
|
+
world_only: input.world_only,
|
|
2125
|
+
...input.before_at === void 0 ? {} : {
|
|
2126
|
+
before_at: input.before_at,
|
|
2127
|
+
before_id: input.before_id
|
|
2128
|
+
}
|
|
2129
|
+
};
|
|
2130
|
+
break;
|
|
2131
|
+
case "history-policy-query":
|
|
2132
|
+
path = "history-policy/query";
|
|
2133
|
+
body = {};
|
|
2134
|
+
break;
|
|
2135
|
+
case "history-policy-set":
|
|
2136
|
+
path = "history-policy/set";
|
|
2137
|
+
body = {
|
|
2138
|
+
expected_revision: input.expected_revision,
|
|
2139
|
+
locked: input.locked
|
|
2140
|
+
};
|
|
2141
|
+
break;
|
|
2142
|
+
case "library-query":
|
|
2143
|
+
path = "library/query";
|
|
2144
|
+
body = {};
|
|
2145
|
+
break;
|
|
2146
|
+
case "library-set":
|
|
2147
|
+
path = "library/set";
|
|
2148
|
+
body = {
|
|
2149
|
+
items: input.items,
|
|
2150
|
+
expected_revision: input.expected_revision,
|
|
2151
|
+
request_id: input.request_id
|
|
2152
|
+
};
|
|
2153
|
+
break;
|
|
2154
|
+
default: throw new ArkmePluginError("reaction-operation-invalid", "不支持的表态操作", false);
|
|
2155
|
+
}
|
|
2156
|
+
const before = await this.runtime.requireSession();
|
|
2157
|
+
if (`${this.runtime.config.environment}:${before.userId}` !== accountKey) throw new ArkmePluginError("reaction-account-changed", "账号已变化", false);
|
|
2158
|
+
const result = objectValue$2(await this.runtime.authenticatedPost(`/api/v1/reactions/${path}`, body, session, signal));
|
|
2159
|
+
const current = await this.runtime.requireSession();
|
|
2160
|
+
if (signal?.aborted || `${this.runtime.config.environment}:${current.userId}` !== accountKey) throw new ArkmePluginError("reaction-account-changed", "账号已变化", false);
|
|
2161
|
+
if (input.action === "query") {
|
|
2162
|
+
if (!Array.isArray(result.items)) throw new ArkmePluginError("reaction-response-invalid", "表态数据不完整", true);
|
|
2163
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2164
|
+
return { items: array(result.items, 50).flatMap((raw) => {
|
|
2165
|
+
const item = objectValue$2(raw), wireKey = targetKey(objectValue$2(item.target)), aliases = ids.get(wireKey);
|
|
2166
|
+
if (!aliases || seen.has(wireKey)) return invalid$11();
|
|
2167
|
+
seen.add(wireKey);
|
|
2168
|
+
const snapshot = {
|
|
2169
|
+
mine: state(item.mine),
|
|
2170
|
+
groups: array(item.groups, 100).map(group),
|
|
2171
|
+
has_more: boolean(item.has_more),
|
|
2172
|
+
actors_visible: boolean(item.actors_visible),
|
|
2173
|
+
private: boolean(item.private)
|
|
2174
|
+
};
|
|
2175
|
+
return aliases.map((id) => ({
|
|
2176
|
+
target_id: id,
|
|
2177
|
+
...snapshot
|
|
2178
|
+
}));
|
|
2179
|
+
}) };
|
|
2180
|
+
}
|
|
2181
|
+
if (input.action === "notifications-read") return { ok: boolean(result.ok) };
|
|
2182
|
+
if (input.action === "notifications") {
|
|
2183
|
+
if (!this.chatSourceKey) return invalid$11();
|
|
2184
|
+
const items = await Promise.all(array(result.items, 50).map(async (raw) => {
|
|
2185
|
+
const item = objectValue$2(raw), target = objectValue$2(item.target);
|
|
2186
|
+
if (typeof target.chat_session_uid !== "string" || !target.chat_session_uid || typeof item.record_uid !== "string" || !item.record_uid) return invalid$11();
|
|
2187
|
+
return {
|
|
2188
|
+
id: key(item.id),
|
|
2189
|
+
revision: integer$4(item.revision, 1),
|
|
2190
|
+
actorUserId: integer$4(item.actor_user_id, 1),
|
|
2191
|
+
sourceKey: await this.chatSourceKey(session.userId, target.chat_session_uid),
|
|
2192
|
+
itemUid: item.record_uid,
|
|
2193
|
+
recordOwnerUserId: integer$4(item.record_owner_user_id, 1),
|
|
2194
|
+
sendAtMillis: integer$4(item.attach_at),
|
|
2195
|
+
text: typeof item.text === "string" ? item.text.slice(0, 320) : "",
|
|
2196
|
+
selections: array(item.selections, 56).map((raw) => {
|
|
2197
|
+
const selected = objectValue$2(raw);
|
|
2198
|
+
return {
|
|
2199
|
+
key: key(selected.key),
|
|
2200
|
+
expression: expression(selected.expression),
|
|
2201
|
+
at: integer$4(selected.at)
|
|
2202
|
+
};
|
|
2203
|
+
})
|
|
2204
|
+
};
|
|
2205
|
+
}));
|
|
2206
|
+
if (signal?.aborted || `${this.runtime.config.environment}:${(await this.runtime.requireSession()).userId}` !== accountKey) throw new ArkmePluginError("reaction-account-changed", "账号已变化", false);
|
|
2207
|
+
return {
|
|
2208
|
+
items,
|
|
2209
|
+
after_id: result.after_id === "" ? "" : key(result.after_id),
|
|
2210
|
+
has_more: boolean(result.has_more)
|
|
2211
|
+
};
|
|
2212
|
+
}
|
|
2213
|
+
if (input.action === "library-query") return library(result);
|
|
2214
|
+
if (input.action === "history-policy-query") return {
|
|
2215
|
+
revision: integer$4(result.revision),
|
|
2216
|
+
locked: boolean(result.locked)
|
|
2217
|
+
};
|
|
2218
|
+
if (input.action === "history-policy-set") {
|
|
2219
|
+
if (![
|
|
2220
|
+
"updated",
|
|
2221
|
+
"unchanged",
|
|
2222
|
+
"revision_conflict"
|
|
2223
|
+
].includes(String(result.outcome))) return invalid$11();
|
|
2224
|
+
const policy = objectValue$2(result.policy);
|
|
2225
|
+
return {
|
|
2226
|
+
outcome: result.outcome,
|
|
2227
|
+
policy: {
|
|
2228
|
+
revision: integer$4(policy.revision),
|
|
2229
|
+
locked: boolean(policy.locked)
|
|
2230
|
+
}
|
|
2231
|
+
};
|
|
2232
|
+
}
|
|
2233
|
+
if (input.action === "library-set") {
|
|
2234
|
+
if (![
|
|
2235
|
+
"updated",
|
|
2236
|
+
"idempotent",
|
|
2237
|
+
"revision_conflict"
|
|
2238
|
+
].includes(String(result.outcome))) return invalid$11();
|
|
2239
|
+
return {
|
|
2240
|
+
outcome: result.outcome,
|
|
2241
|
+
library: library(result.library)
|
|
2242
|
+
};
|
|
2243
|
+
}
|
|
2244
|
+
if (input.action === "set") {
|
|
2245
|
+
if (![
|
|
2246
|
+
"updated",
|
|
2247
|
+
"unchanged",
|
|
2248
|
+
"idempotent",
|
|
2249
|
+
"revision_conflict"
|
|
2250
|
+
].includes(String(result.outcome))) return invalid$11();
|
|
2251
|
+
return {
|
|
2252
|
+
outcome: result.outcome,
|
|
2253
|
+
state: state(result.state)
|
|
2254
|
+
};
|
|
2255
|
+
}
|
|
2256
|
+
if (input.action === "actors") return {
|
|
2257
|
+
user_ids: array(result.user_ids, 100).map((id) => integer$4(id, 1)),
|
|
2258
|
+
has_more: boolean(result.has_more)
|
|
2259
|
+
};
|
|
2260
|
+
if (input.action === "groups") return {
|
|
2261
|
+
items: array(result.items, 100).map(group),
|
|
2262
|
+
has_more: boolean(result.has_more)
|
|
2263
|
+
};
|
|
2264
|
+
if (input.action === "history" || input.action === "received") {
|
|
2265
|
+
const rows = array(result.items, 100).map(objectValue$2);
|
|
2266
|
+
const contextRows = rows.filter((item) => item.restricted === false);
|
|
2267
|
+
const contexts = input.action === "history" && this.historyContext ? await this.historyContext(contextRows.map((item) => {
|
|
2268
|
+
const original = objectValue$2(item.original_message);
|
|
2269
|
+
return {
|
|
2270
|
+
sessionUID: String(objectValue$2(item.target).chat_session_uid ?? ""),
|
|
2271
|
+
senderID: integer$4(item.sender_user_id ?? 0),
|
|
2272
|
+
sourceKind: String(item.source_kind ?? ""),
|
|
2273
|
+
...typeof original.record_uid === "string" && original.record_uid ? { message: {
|
|
2274
|
+
recordUID: original.record_uid,
|
|
2275
|
+
ownerUserID: integer$4(original.owner_user_id, 1),
|
|
2276
|
+
sendAtMillis: integer$4(original.send_at, 1)
|
|
2277
|
+
} } : {}
|
|
2278
|
+
};
|
|
2279
|
+
}), signal) : [];
|
|
2280
|
+
if (signal?.aborted || `${this.runtime.config.environment}:${(await this.runtime.requireSession()).userId}` !== accountKey) throw new ArkmePluginError("reaction-account-changed", "账号已变化", false);
|
|
2281
|
+
const contextByRow = new Map(contextRows.map((row, index) => [row, contexts[index]]));
|
|
2282
|
+
return {
|
|
2283
|
+
items: await Promise.all(rows.map(async (item) => {
|
|
2284
|
+
const target = objectValue$2(item.target);
|
|
2285
|
+
if (typeof target.chat_session_uid !== "string" || typeof target.rel_uid !== "string") return invalid$11();
|
|
2286
|
+
const targetID = createHash("sha256").update(JSON.stringify([session.userId, targetKey(target)])).digest("hex");
|
|
2287
|
+
const received = input.action === "received" ? {
|
|
2288
|
+
actor_user_id: integer$4(item.actor_user_id, 1),
|
|
2289
|
+
private: boolean(item.private),
|
|
2290
|
+
...input.world_only && typeof target.record_uid === "string" && this.worldReference ? { target: {
|
|
2291
|
+
id: targetID,
|
|
2292
|
+
worldRecordRef: await this.worldReference(target.record_uid)
|
|
2293
|
+
} } : {}
|
|
2294
|
+
} : {};
|
|
2295
|
+
const restricted = boolean(item.restricted);
|
|
2296
|
+
return {
|
|
2297
|
+
...received,
|
|
2298
|
+
...!restricted ? contextByRow.get(item) : {},
|
|
2299
|
+
event_uid: key(item.event_uid),
|
|
2300
|
+
target_id: targetID,
|
|
2301
|
+
expression: expression(item.expression),
|
|
2302
|
+
active: boolean(item.active),
|
|
2303
|
+
at: integer$4(item.at),
|
|
2304
|
+
restricted,
|
|
2305
|
+
...!restricted && typeof item.source_kind === "string" ? { source_kind: item.source_kind } : {},
|
|
2306
|
+
...!restricted && typeof item.text === "string" ? { text: item.text.slice(0, 320) } : {}
|
|
2307
|
+
};
|
|
2308
|
+
})),
|
|
2309
|
+
has_more: boolean(result.has_more),
|
|
2310
|
+
...result.has_more ? {
|
|
2311
|
+
before_at: integer$4(result.before_at),
|
|
2312
|
+
before_id: key(result.before_id)
|
|
2313
|
+
} : {}
|
|
2314
|
+
};
|
|
2315
|
+
}
|
|
2316
|
+
return invalid$11();
|
|
2317
|
+
}
|
|
2318
|
+
};
|
|
2319
|
+
//#endregion
|
|
1954
2320
|
//#region src/managed-ai/image-preparer.ts
|
|
1955
2321
|
const MAXIMUM_DECODED_IMAGE_PIXELS = 4e7;
|
|
1956
2322
|
const MAXIMUM_SOURCE_IMAGE_BYTES = 64 << 20;
|
|
@@ -44298,7 +44664,6 @@ var ArkmeChatRealtimeRuntime = class {
|
|
|
44298
44664
|
//#region src/services/chat-realtime-service.ts
|
|
44299
44665
|
const MAX_PROJECTION_RETRIES = 5;
|
|
44300
44666
|
const MAX_ATTENTION_SUMMARY_RETRY_DELAY_MS = 3e4;
|
|
44301
|
-
const MAX_NATIVE_NOTIFICATION_DELIVERY_CONCURRENCY = 3;
|
|
44302
44667
|
const MAX_NOTIFICATION_HINTS_PER_TIMELINE_READ = 50;
|
|
44303
44668
|
const NOTIFICATION_EXPIRY_MILLIS = 5 * 6e4;
|
|
44304
44669
|
const CHAT_NOTIFICATION_DIAGNOSTICS = process.env.ARKME_CHAT_NOTIFICATION_DIAGNOSTICS === "1";
|
|
@@ -44410,6 +44775,7 @@ var ChatRealtimeService = class {
|
|
|
44410
44775
|
}
|
|
44411
44776
|
dispose() {
|
|
44412
44777
|
this.disposed = true;
|
|
44778
|
+
this.attentionOwnerGeneration += 1;
|
|
44413
44779
|
this.attentionController.abort();
|
|
44414
44780
|
if (this.projectionTimer !== void 0) clearTimeout(this.projectionTimer);
|
|
44415
44781
|
if (this.connectionBaselineRetryTimer !== void 0) clearTimeout(this.connectionBaselineRetryTimer);
|
|
@@ -45018,7 +45384,7 @@ var ChatRealtimeService = class {
|
|
|
45018
45384
|
accountUserId: notificationHint?.accountUserId ?? current?.accountUserId,
|
|
45019
45385
|
accountOwnerGeneration: notificationHint?.accountOwnerGeneration ?? current?.accountOwnerGeneration
|
|
45020
45386
|
});
|
|
45021
|
-
this.scheduleProjectionFlush(
|
|
45387
|
+
this.scheduleProjectionFlush(0);
|
|
45022
45388
|
}
|
|
45023
45389
|
scheduleProjectionFlush(delayMillis) {
|
|
45024
45390
|
if (this.projectionInFlight || this.disposed) return;
|
|
@@ -45037,7 +45403,7 @@ var ChatRealtimeService = class {
|
|
|
45037
45403
|
const now = Date.now();
|
|
45038
45404
|
let delay = Number.POSITIVE_INFINITY;
|
|
45039
45405
|
for (const projection of this.pendingChatProjections.values()) {
|
|
45040
|
-
if (projection.refreshSource !== false) delay =
|
|
45406
|
+
if (projection.refreshSource !== false) delay = 0;
|
|
45041
45407
|
for (const candidate of projection.notificationHints) {
|
|
45042
45408
|
const dueAtMillis = candidate.nextAttemptAtMillis ?? now + notificationRetryDelay(candidate.attempts);
|
|
45043
45409
|
delay = Math.min(delay, Math.max(0, dueAtMillis - now));
|
|
@@ -45175,26 +45541,29 @@ var ChatRealtimeService = class {
|
|
|
45175
45541
|
if (pending.length === 0) return [];
|
|
45176
45542
|
for (const [uid, projection] of pending) if (projection.refreshSource === false && this.source.cachedChatSourceByKey(`${String(session.userId)}:${uid}`) === void 0) projection.refreshSource = true;
|
|
45177
45543
|
const sourcePending = pending.filter(([, projection]) => projection.refreshSource !== false);
|
|
45178
|
-
const
|
|
45179
|
-
|
|
45180
|
-
|
|
45181
|
-
|
|
45182
|
-
|
|
45183
|
-
|
|
45184
|
-
|
|
45185
|
-
|
|
45186
|
-
|
|
45187
|
-
const
|
|
45188
|
-
|
|
45189
|
-
|
|
45544
|
+
const displayReady = (async () => {
|
|
45545
|
+
const bundles = /* @__PURE__ */ new Map();
|
|
45546
|
+
if (sourcePending.length > 0) {
|
|
45547
|
+
const sessionUids = sourcePending.map(([uid]) => uid).sort();
|
|
45548
|
+
const projectionBatchKey = sessionUids.join("|");
|
|
45549
|
+
const displayData = await this.runtime.authenticatedChatPost("/api/v1/chats/display-snapshots", { chat_session_uids: sessionUids }, session, void 0, {
|
|
45550
|
+
lane: sourcePending.some(([, projection]) => projection.notificationHints.length > 0) ? "interactive-read" : "background-read",
|
|
45551
|
+
key: `projection:display:${projectionBatchKey}`
|
|
45552
|
+
});
|
|
45553
|
+
for (const raw of listValue$18(displayData.items)) {
|
|
45554
|
+
const bundle = objectValue$2(raw);
|
|
45555
|
+
const uid = stringValue$2(objectValue$2(bundle.session).chat_session_uid).trim();
|
|
45556
|
+
if (uid !== "") bundles.set(uid, bundle);
|
|
45557
|
+
}
|
|
45190
45558
|
}
|
|
45191
|
-
|
|
45192
|
-
|
|
45193
|
-
const
|
|
45194
|
-
const
|
|
45195
|
-
|
|
45196
|
-
|
|
45197
|
-
|
|
45559
|
+
return bundles;
|
|
45560
|
+
})().catch(() => void 0);
|
|
45561
|
+
const failed = [];
|
|
45562
|
+
const notificationLanes = Array.from({ length: 3 }, () => Promise.resolve());
|
|
45563
|
+
let nextNotificationLane = 0;
|
|
45564
|
+
const project = async ([uid, projection]) => {
|
|
45565
|
+
let timelineProjection;
|
|
45566
|
+
if (projection.refreshSource !== false || projection.notificationHints.some((candidate) => candidate.resolvedNotification === void 0)) {
|
|
45198
45567
|
const cached = this.source.cachedChatSourceByKey(`${String(session.userId)}:${uid}`);
|
|
45199
45568
|
const unresolvedHints = projection.notificationHints.filter((candidate) => candidate.resolvedNotification === void 0);
|
|
45200
45569
|
const firstHintSequence = unresolvedHints.reduce((minimum, item) => Math.min(minimum, item.hint.latestSequence), projection.latestSequence);
|
|
@@ -45209,32 +45578,24 @@ var ChatRealtimeService = class {
|
|
|
45209
45578
|
lane: projection.notificationHints.length > 0 ? "interactive-read" : "background-read",
|
|
45210
45579
|
key: `projection:tail:${uid}:${String(afterSequence)}:${String(notificationAttempt)}`
|
|
45211
45580
|
});
|
|
45212
|
-
const
|
|
45581
|
+
const bundles = await displayReady;
|
|
45582
|
+
if (bundles === void 0 && projection.refreshSource !== false) throw new Error("Chat display projection failed");
|
|
45583
|
+
const bundle = bundles?.get(uid);
|
|
45213
45584
|
const sessionKind = numberValue$21(objectValue$2(bundle).session_kind ?? objectValue$2(objectValue$2(bundle).session).session_kind);
|
|
45214
45585
|
const sourceKind = sessionKind === 2 ? "group_chat" : sessionKind === 1 || sessionKind === 3 ? "private_chat" : cached?.kind === "group_chat" || cached?.kind === "private_chat" ? cached.kind : void 0;
|
|
45215
|
-
|
|
45586
|
+
timelineProjection = {
|
|
45216
45587
|
items: await this.projectionReader.chatTimelineItems(data, session, uid, sourceKind, bundle),
|
|
45217
45588
|
notificationIdentities: timelineNotificationIdentities(data)
|
|
45218
|
-
}
|
|
45219
|
-
}))).forEach((result, index) => {
|
|
45220
|
-
const uid = chunk[index]?.[0];
|
|
45221
|
-
if (uid === void 0) return;
|
|
45222
|
-
if (result.status === "fulfilled") tailItemsByUid.set(result.value[0], result.value[1]);
|
|
45223
|
-
else failedUids.add(uid);
|
|
45224
|
-
});
|
|
45225
|
-
}
|
|
45226
|
-
if (ownerGeneration !== this.attentionOwnerGeneration) return [];
|
|
45227
|
-
const updates = [];
|
|
45228
|
-
const sidebarNotificationHints = [];
|
|
45229
|
-
const notifications = [];
|
|
45230
|
-
for (const [uid, projection] of pending) {
|
|
45231
|
-
const bundle = bundles.get(uid);
|
|
45232
|
-
if (projection.refreshSource !== false && bundle === void 0 || failedUids.has(uid)) {
|
|
45233
|
-
failedUids.add(uid);
|
|
45234
|
-
continue;
|
|
45589
|
+
};
|
|
45235
45590
|
}
|
|
45591
|
+
if (ownerGeneration !== this.attentionOwnerGeneration) return [];
|
|
45592
|
+
const bundles = await displayReady;
|
|
45593
|
+
const updates = [];
|
|
45594
|
+
const sidebarNotificationHints = [];
|
|
45595
|
+
const notifications = [];
|
|
45596
|
+
const bundle = bundles?.get(uid);
|
|
45597
|
+
if (projection.refreshSource !== false && bundle === void 0) return [[uid, projection]];
|
|
45236
45598
|
const cacheKey = `${String(session.userId)}:${uid}`;
|
|
45237
|
-
const timelineProjection = tailItemsByUid.get(uid);
|
|
45238
45599
|
const timelineItems = timelineProjection?.items ?? [];
|
|
45239
45600
|
try {
|
|
45240
45601
|
const cachedSource = this.source.cachedChatSourceByKey(cacheKey);
|
|
@@ -45244,8 +45605,7 @@ var ChatRealtimeService = class {
|
|
|
45244
45605
|
...projection,
|
|
45245
45606
|
refreshSource: true
|
|
45246
45607
|
});
|
|
45247
|
-
|
|
45248
|
-
continue;
|
|
45608
|
+
return [[uid, projection]];
|
|
45249
45609
|
}
|
|
45250
45610
|
if (ownerGeneration !== this.attentionOwnerGeneration) return [];
|
|
45251
45611
|
if (projection.refreshSource !== false) this.source.setChatSourceByKey(cacheKey, source);
|
|
@@ -45335,117 +45695,137 @@ var ChatRealtimeService = class {
|
|
|
45335
45695
|
});
|
|
45336
45696
|
}
|
|
45337
45697
|
} catch {
|
|
45338
|
-
|
|
45698
|
+
return [[uid, projection]];
|
|
45339
45699
|
}
|
|
45340
|
-
|
|
45341
|
-
|
|
45342
|
-
|
|
45343
|
-
|
|
45344
|
-
|
|
45345
|
-
|
|
45346
|
-
|
|
45347
|
-
|
|
45348
|
-
|
|
45349
|
-
|
|
45350
|
-
|
|
45351
|
-
|
|
45352
|
-
|
|
45353
|
-
|
|
45354
|
-
|
|
45355
|
-
|
|
45356
|
-
|
|
45357
|
-
|
|
45358
|
-
|
|
45359
|
-
|
|
45360
|
-
|
|
45361
|
-
|
|
45362
|
-
|
|
45363
|
-
|
|
45364
|
-
|
|
45365
|
-
|
|
45366
|
-
|
|
45367
|
-
|
|
45368
|
-
|
|
45369
|
-
|
|
45370
|
-
|
|
45371
|
-
|
|
45372
|
-
|
|
45373
|
-
|
|
45374
|
-
|
|
45375
|
-
|
|
45376
|
-
|
|
45377
|
-
|
|
45378
|
-
|
|
45379
|
-
|
|
45380
|
-
|
|
45381
|
-
|
|
45382
|
-
|
|
45383
|
-
|
|
45384
|
-
|
|
45700
|
+
if (ownerGeneration !== this.attentionOwnerGeneration) return [];
|
|
45701
|
+
if (updates.length > 0) {
|
|
45702
|
+
this.source.invalidateSourceListCache(session.userId, "root");
|
|
45703
|
+
this.runtime.invalidateCalendarDates(this.runtime.requestScope(session.userId), updates.flatMap((update) => [update.source.activeAtMillis, ...update.timelineItems.map((item) => item.sendAtMillis)]));
|
|
45704
|
+
this.emitChatClientEvent({
|
|
45705
|
+
type: "sessions-delta",
|
|
45706
|
+
revision: this.nextChatClientRevision(),
|
|
45707
|
+
updates
|
|
45708
|
+
});
|
|
45709
|
+
const emittedAtMillis = Date.now();
|
|
45710
|
+
for (const { candidate } of sidebarNotificationHints) notificationDiagnostic("notification_sidebar_delta_emitted", {
|
|
45711
|
+
eventUid: candidate.hint.eventUid,
|
|
45712
|
+
connectionGeneration: candidate.connectionGeneration,
|
|
45713
|
+
attempt: candidate.attempts,
|
|
45714
|
+
emittedAtMillis,
|
|
45715
|
+
...candidate.receivedAtMillis === void 0 ? {} : { durationFromHintMillis: Math.max(0, emittedAtMillis - candidate.receivedAtMillis) }
|
|
45716
|
+
});
|
|
45717
|
+
this.refreshAttentionSummary();
|
|
45718
|
+
}
|
|
45719
|
+
if (notifications.length > 0) {
|
|
45720
|
+
const lane = nextNotificationLane++ % notificationLanes.length;
|
|
45721
|
+
notificationLanes[lane] = notificationLanes[lane].then(async () => {
|
|
45722
|
+
const deliveries = [];
|
|
45723
|
+
for (let offset = 0; offset < notifications.length; offset += 1) {
|
|
45724
|
+
const chunk = notifications.slice(offset, offset + 1);
|
|
45725
|
+
deliveries.push(...await Promise.all(chunk.map(async (entry) => {
|
|
45726
|
+
const { notification, candidate } = entry;
|
|
45727
|
+
if (ownerGeneration !== this.attentionOwnerGeneration) return {
|
|
45728
|
+
entry,
|
|
45729
|
+
fallbackToBrowser: false,
|
|
45730
|
+
outcome: "exception"
|
|
45731
|
+
};
|
|
45732
|
+
const dispatchStartedAtMillis = Date.now();
|
|
45733
|
+
try {
|
|
45734
|
+
const outcome = await this.nativeAttention.showNotification({
|
|
45735
|
+
idempotencyKey: notification.eventUid,
|
|
45736
|
+
kind: "chat.message",
|
|
45737
|
+
occurredAtMillis: notification.eventAtMillis,
|
|
45738
|
+
expiresAtMillis: notification.eventAtMillis + NOTIFICATION_EXPIRY_MILLIS,
|
|
45739
|
+
presentation: {
|
|
45740
|
+
title: notification.title,
|
|
45741
|
+
body: notification.body
|
|
45742
|
+
},
|
|
45743
|
+
activation: {
|
|
45744
|
+
kind: "chat-source",
|
|
45745
|
+
sourceRef: notification.sourceRef,
|
|
45746
|
+
sourceKey: notification.sourceKey
|
|
45747
|
+
}
|
|
45748
|
+
});
|
|
45749
|
+
const completedAtMillis = Date.now();
|
|
45750
|
+
notificationDiagnostic("notification_native_dispatch_completed", {
|
|
45751
|
+
eventUid: notification.eventUid,
|
|
45752
|
+
connectionGeneration: candidate.connectionGeneration,
|
|
45753
|
+
attempt: candidate.attempts,
|
|
45754
|
+
outcome: outcome.outcome,
|
|
45755
|
+
completedAtMillis,
|
|
45756
|
+
dispatchDurationMillis: Math.max(0, completedAtMillis - dispatchStartedAtMillis),
|
|
45757
|
+
...candidate.receivedAtMillis === void 0 ? {} : { durationFromHintMillis: Math.max(0, completedAtMillis - candidate.receivedAtMillis) }
|
|
45758
|
+
});
|
|
45759
|
+
return {
|
|
45760
|
+
entry,
|
|
45761
|
+
fallbackToBrowser: outcome.fallbackToBrowser,
|
|
45762
|
+
outcome: outcome.outcome
|
|
45763
|
+
};
|
|
45764
|
+
} catch {
|
|
45765
|
+
const completedAtMillis = Date.now();
|
|
45766
|
+
notificationDiagnostic("notification_native_dispatch_completed", {
|
|
45767
|
+
eventUid: notification.eventUid,
|
|
45768
|
+
connectionGeneration: candidate.connectionGeneration,
|
|
45769
|
+
attempt: candidate.attempts,
|
|
45770
|
+
outcome: "exception",
|
|
45771
|
+
completedAtMillis,
|
|
45772
|
+
dispatchDurationMillis: Math.max(0, completedAtMillis - dispatchStartedAtMillis),
|
|
45773
|
+
...candidate.receivedAtMillis === void 0 ? {} : { durationFromHintMillis: Math.max(0, completedAtMillis - candidate.receivedAtMillis) }
|
|
45774
|
+
});
|
|
45775
|
+
return {
|
|
45776
|
+
entry,
|
|
45777
|
+
fallbackToBrowser: false,
|
|
45778
|
+
outcome: "exception"
|
|
45779
|
+
};
|
|
45780
|
+
}
|
|
45781
|
+
})));
|
|
45782
|
+
}
|
|
45783
|
+
if (ownerGeneration !== this.attentionOwnerGeneration) return [];
|
|
45784
|
+
for (const { entry, fallbackToBrowser, outcome } of deliveries) {
|
|
45785
|
+
const { notification, candidate, uid, latestSequence } = entry;
|
|
45786
|
+
if (outcome === "native-failed" || outcome === "rate-limited" || outcome === "exception") {
|
|
45787
|
+
if (!notificationExpired(candidate)) {
|
|
45788
|
+
const attempts = candidate.attempts + 1;
|
|
45789
|
+
this.mergePendingChatProjection(uid, {
|
|
45790
|
+
latestSequence,
|
|
45791
|
+
notificationHints: [{
|
|
45792
|
+
...candidate,
|
|
45793
|
+
attempts,
|
|
45794
|
+
nextAttemptAtMillis: Date.now() + notificationRetryDelay(attempts)
|
|
45795
|
+
}],
|
|
45796
|
+
refreshSource: false,
|
|
45797
|
+
accountUserId: session.userId,
|
|
45798
|
+
accountOwnerGeneration: ownerGeneration
|
|
45799
|
+
});
|
|
45800
|
+
}
|
|
45801
|
+
continue;
|
|
45385
45802
|
}
|
|
45386
|
-
|
|
45387
|
-
|
|
45388
|
-
|
|
45389
|
-
|
|
45390
|
-
|
|
45391
|
-
|
|
45392
|
-
|
|
45393
|
-
|
|
45394
|
-
|
|
45395
|
-
|
|
45396
|
-
|
|
45397
|
-
|
|
45398
|
-
|
|
45399
|
-
|
|
45400
|
-
|
|
45401
|
-
|
|
45803
|
+
if (!fallbackToBrowser) continue;
|
|
45804
|
+
this.emitChatClientEvent({
|
|
45805
|
+
type: "message-notification",
|
|
45806
|
+
revision: this.nextChatClientRevision(),
|
|
45807
|
+
notification
|
|
45808
|
+
});
|
|
45809
|
+
}
|
|
45810
|
+
}).then(() => void 0).catch(() => {
|
|
45811
|
+
failed.push([uid, projection]);
|
|
45812
|
+
});
|
|
45813
|
+
}
|
|
45814
|
+
return [];
|
|
45815
|
+
};
|
|
45816
|
+
let next = 0;
|
|
45817
|
+
await Promise.all(Array.from({ length: Math.min(3, pending.length) }, async () => {
|
|
45818
|
+
while (next < pending.length && ownerGeneration === this.attentionOwnerGeneration) {
|
|
45819
|
+
const entry = pending[next++];
|
|
45820
|
+
try {
|
|
45821
|
+
failed.push(...await project(entry));
|
|
45402
45822
|
} catch {
|
|
45403
|
-
|
|
45404
|
-
notificationDiagnostic("notification_native_dispatch_completed", {
|
|
45405
|
-
eventUid: notification.eventUid,
|
|
45406
|
-
connectionGeneration: candidate.connectionGeneration,
|
|
45407
|
-
attempt: candidate.attempts,
|
|
45408
|
-
outcome: "exception",
|
|
45409
|
-
completedAtMillis,
|
|
45410
|
-
dispatchDurationMillis: Math.max(0, completedAtMillis - dispatchStartedAtMillis),
|
|
45411
|
-
...candidate.receivedAtMillis === void 0 ? {} : { durationFromHintMillis: Math.max(0, completedAtMillis - candidate.receivedAtMillis) }
|
|
45412
|
-
});
|
|
45413
|
-
return {
|
|
45414
|
-
entry,
|
|
45415
|
-
fallbackToBrowser: false,
|
|
45416
|
-
outcome: "exception"
|
|
45417
|
-
};
|
|
45418
|
-
}
|
|
45419
|
-
})));
|
|
45420
|
-
}
|
|
45421
|
-
if (ownerGeneration !== this.attentionOwnerGeneration) return [];
|
|
45422
|
-
for (const { entry, fallbackToBrowser, outcome } of deliveries) {
|
|
45423
|
-
const { notification, candidate, uid, latestSequence } = entry;
|
|
45424
|
-
if (outcome === "native-failed" || outcome === "rate-limited" || outcome === "exception") {
|
|
45425
|
-
if (!notificationExpired(candidate)) {
|
|
45426
|
-
const attempts = candidate.attempts + 1;
|
|
45427
|
-
this.mergePendingChatProjection(uid, {
|
|
45428
|
-
latestSequence,
|
|
45429
|
-
notificationHints: [{
|
|
45430
|
-
...candidate,
|
|
45431
|
-
attempts,
|
|
45432
|
-
nextAttemptAtMillis: Date.now() + notificationRetryDelay(attempts)
|
|
45433
|
-
}],
|
|
45434
|
-
refreshSource: false,
|
|
45435
|
-
accountUserId: session.userId,
|
|
45436
|
-
accountOwnerGeneration: ownerGeneration
|
|
45437
|
-
});
|
|
45823
|
+
failed.push(entry);
|
|
45438
45824
|
}
|
|
45439
|
-
continue;
|
|
45440
45825
|
}
|
|
45441
|
-
|
|
45442
|
-
|
|
45443
|
-
|
|
45444
|
-
revision: this.nextChatClientRevision(),
|
|
45445
|
-
notification
|
|
45446
|
-
});
|
|
45447
|
-
}
|
|
45448
|
-
return pending.filter(([uid]) => failedUids.has(uid));
|
|
45826
|
+
}));
|
|
45827
|
+
await Promise.all(notificationLanes);
|
|
45828
|
+
return failed;
|
|
45449
45829
|
}
|
|
45450
45830
|
emitChatClientEvent(event) {
|
|
45451
45831
|
for (const listener of [...this.chatClientListeners]) listener(event);
|
|
@@ -49428,12 +49808,13 @@ var ChatService = class {
|
|
|
49428
49808
|
if (!options.signal?.aborted && cacheEpoch === (this.runtime.memberCacheEpoch?.() ?? 0)) await this.runtime.stateStore.mergeConversationMembers?.(session.userId, source.ownerRef, result).catch(() => void 0);
|
|
49429
49809
|
return result;
|
|
49430
49810
|
}
|
|
49431
|
-
async memberRead(session, group, path, body, signal) {
|
|
49811
|
+
async memberRead(session, group, path, body, signal, cacheMs) {
|
|
49432
49812
|
try {
|
|
49433
49813
|
return await this.runtime.authenticatedChatPost(path, body, session, signal, {
|
|
49434
49814
|
lane: path.endsWith("/members/page") ? "interactive-read" : "background-read",
|
|
49435
49815
|
key: `members:${this.runtime.memberCacheEpoch?.() ?? 0}:${path}:${JSON.stringify(body)}`,
|
|
49436
|
-
failureCooldownMs: 2e3
|
|
49816
|
+
failureCooldownMs: 2e3,
|
|
49817
|
+
...cacheMs === void 0 ? {} : { cacheMs }
|
|
49437
49818
|
});
|
|
49438
49819
|
} catch (error) {
|
|
49439
49820
|
if (invalidatesMemberSnapshot(error)) {
|
|
@@ -50555,6 +50936,49 @@ var ChatService = class {
|
|
|
50555
50936
|
recordOwnerUserId: reference.recordOwnerUserId
|
|
50556
50937
|
};
|
|
50557
50938
|
}
|
|
50939
|
+
async reactionTarget(sourceRef, messageActionRef) {
|
|
50940
|
+
const session = await this.runtime.requireSession();
|
|
50941
|
+
const source = await this.source.openSourceRef(sourceRef.trim(), session.userId);
|
|
50942
|
+
const reference = await this.openMessageActionRef(messageActionRef, session.userId, source);
|
|
50943
|
+
if (reference.sourceKind === "record") return {
|
|
50944
|
+
record_uid: reference.recordUid,
|
|
50945
|
+
owned: true
|
|
50946
|
+
};
|
|
50947
|
+
return {
|
|
50948
|
+
chat_session_uid: reference.chatSessionUid,
|
|
50949
|
+
rel_uid: reference.relationUid
|
|
50950
|
+
};
|
|
50951
|
+
}
|
|
50952
|
+
/** Keep viewer-owned remarks separate from group nicknames and public profiles. */
|
|
50953
|
+
async reactionActorLabels(sourceRef, userIds, signal) {
|
|
50954
|
+
const session = await this.runtime.requireSession();
|
|
50955
|
+
const source = await this.source.openSourceRef(sourceRef.trim(), session.userId);
|
|
50956
|
+
const names = /* @__PURE__ */ new Map();
|
|
50957
|
+
if (source.kind !== "group_chat" && source.kind !== "private_chat") return names;
|
|
50958
|
+
const ids = [...new Set(userIds)].sort((left, right) => left - right);
|
|
50959
|
+
for (let offset = 0; offset < ids.length; offset += 50) {
|
|
50960
|
+
signal?.throwIfAborted();
|
|
50961
|
+
const batch = ids.slice(offset, offset + 50);
|
|
50962
|
+
const data = await this.memberRead(session, source.ownerRef, "/api/v1/chats/members/by-user-ids", {
|
|
50963
|
+
chat_session_uid: source.ownerRef,
|
|
50964
|
+
user_ids: batch,
|
|
50965
|
+
active_only: true,
|
|
50966
|
+
include_stats: false
|
|
50967
|
+
}, signal, 5e3);
|
|
50968
|
+
const items = listValue$16(data.items).map(objectValue$2);
|
|
50969
|
+
if (stringValue$2(data.chat_session_uid) !== source.ownerRef || !Array.isArray(data.items) || items.length > batch.length || items.some((item) => !batch.includes(numberValue$19(item.user_id)) || numberValue$19(item.status) !== 1)) throw new ArkmePluginError("member-presentation-invalid-response", "成员资料响应无效", true, 502);
|
|
50970
|
+
for (const item of items) {
|
|
50971
|
+
const userId = numberValue$19(item.user_id);
|
|
50972
|
+
const groupNickname = source.kind === "group_chat" ? firstUsableChatMemberName([item.display_name_snapshot], userId) : "";
|
|
50973
|
+
const remark = userId === session.userId ? "" : normalizedJoinDisplayName(item.remark);
|
|
50974
|
+
names.set(userId, {
|
|
50975
|
+
remark,
|
|
50976
|
+
groupNickname
|
|
50977
|
+
});
|
|
50978
|
+
}
|
|
50979
|
+
}
|
|
50980
|
+
return names;
|
|
50981
|
+
}
|
|
50558
50982
|
async sourceMessageExtensionContext(sourceRef, messageActionRef, options = {}) {
|
|
50559
50983
|
const session = await this.runtime.requireSession();
|
|
50560
50984
|
const source = await this.source.openSourceRef(sourceRef.trim(), session.userId);
|
|
@@ -87358,7 +87782,7 @@ var ProfileService = class {
|
|
|
87358
87782
|
async userCard(userId, signal) {
|
|
87359
87783
|
const session = await this.runtime.requireSession();
|
|
87360
87784
|
if (!Number.isSafeInteger(userId) || userId <= 0) throw new ArkmePluginError("user-card-target-invalid", "用户信息参数无效", false);
|
|
87361
|
-
const profile = (await this.publicProfileSummariesByUserIds([userId], session, signal)).get(userId);
|
|
87785
|
+
const profile = (await this.publicProfileSummariesByUserIds([userId], session, signal, 0)).get(userId);
|
|
87362
87786
|
return {
|
|
87363
87787
|
displayName: profile?.displayName ?? "群成员",
|
|
87364
87788
|
...profile?.avatarUrl === void 0 ? {} : { avatarRef: await this.sealProfileImageRef(session.userId, userId) }
|
|
@@ -87508,7 +87932,7 @@ var ProfileService = class {
|
|
|
87508
87932
|
const avatarRef = profile.avatarRef.trim();
|
|
87509
87933
|
return /^https?:\/\//i.test(avatarRef) || fileAssetAvatarRef(avatarRef) !== void 0;
|
|
87510
87934
|
}
|
|
87511
|
-
async publicProfileSummariesByUserIds(userIds, session, signal) {
|
|
87935
|
+
async publicProfileSummariesByUserIds(userIds, session, signal, maxAgeMillis = PUBLIC_PROFILE_CACHE_TTL_MS) {
|
|
87512
87936
|
const normalized = [...new Set(userIds.filter((userId) => Number.isSafeInteger(userId) && userId > 0))].sort((left, right) => left - right);
|
|
87513
87937
|
const profiles = /* @__PURE__ */ new Map();
|
|
87514
87938
|
const missing = [];
|
|
@@ -87516,7 +87940,7 @@ var ProfileService = class {
|
|
|
87516
87940
|
for (const [key, cached] of this.publicProfileCache) if (cached.expiresAtMillis <= now) this.publicProfileCache.delete(key);
|
|
87517
87941
|
for (const userId of normalized) {
|
|
87518
87942
|
const cached = this.publicProfileCache.get(`${String(session.userId)}:${String(userId)}`);
|
|
87519
|
-
if (cached === void 0 || cached.expiresAtMillis <= now) {
|
|
87943
|
+
if (cached === void 0 || cached.expiresAtMillis <= now || cached.expiresAtMillis - (cached.value === null ? PUBLIC_PROFILE_NEGATIVE_CACHE_TTL_MS : PUBLIC_PROFILE_CACHE_TTL_MS) + maxAgeMillis <= now) {
|
|
87520
87944
|
missing.push(userId);
|
|
87521
87945
|
continue;
|
|
87522
87946
|
}
|
|
@@ -90219,6 +90643,68 @@ var RecordingService = class {
|
|
|
90219
90643
|
}
|
|
90220
90644
|
};
|
|
90221
90645
|
//#endregion
|
|
90646
|
+
//#region src/services/recording-import-upload-retry.ts
|
|
90647
|
+
const RECORDING_UPLOAD_RETRY_DELAYS = [
|
|
90648
|
+
1e3,
|
|
90649
|
+
2e3,
|
|
90650
|
+
4e3,
|
|
90651
|
+
8e3,
|
|
90652
|
+
16e3
|
|
90653
|
+
];
|
|
90654
|
+
const RECORDING_UPLOAD_REQUEST_TIMEOUT = 300 * 1e3;
|
|
90655
|
+
const transientStatuses = /* @__PURE__ */ new Set([
|
|
90656
|
+
408,
|
|
90657
|
+
429,
|
|
90658
|
+
500,
|
|
90659
|
+
502,
|
|
90660
|
+
503,
|
|
90661
|
+
504
|
|
90662
|
+
]);
|
|
90663
|
+
const transientNames = /* @__PURE__ */ new Set([
|
|
90664
|
+
"ResponseTimeoutError",
|
|
90665
|
+
"ConnectionTimeoutError",
|
|
90666
|
+
"SocketAssignTimeoutError"
|
|
90667
|
+
]);
|
|
90668
|
+
const transientCodes = /* @__PURE__ */ new Set([
|
|
90669
|
+
"ECONNRESET",
|
|
90670
|
+
"ECONNREFUSED",
|
|
90671
|
+
"ECONNABORTED",
|
|
90672
|
+
"ETIMEDOUT",
|
|
90673
|
+
"EPIPE",
|
|
90674
|
+
"EAI_AGAIN",
|
|
90675
|
+
"ENOTFOUND",
|
|
90676
|
+
"ENETUNREACH",
|
|
90677
|
+
"EHOSTUNREACH"
|
|
90678
|
+
]);
|
|
90679
|
+
function isRetryableRecordingUploadError(error) {
|
|
90680
|
+
if (error === null || typeof error !== "object" || error instanceof RecordingImportContractError) return false;
|
|
90681
|
+
const { status, name, code } = error;
|
|
90682
|
+
if (name === "cancel" || name === "abort" || name === "AbortError") return false;
|
|
90683
|
+
if (typeof status === "number" && status >= 400) return transientStatuses.has(status);
|
|
90684
|
+
if (typeof code === "string" && code !== "" && transientCodes.has(code)) return true;
|
|
90685
|
+
if ((status === -1 || status === -2) && code === name && typeof name === "string" && (transientNames.has(name) || name === "RequestError" || name === "ResponseError")) return true;
|
|
90686
|
+
if (typeof code === "string" && code !== "") return false;
|
|
90687
|
+
return typeof name === "string" && transientNames.has(name);
|
|
90688
|
+
}
|
|
90689
|
+
function throwIfRecordingUploadAborted(signal) {
|
|
90690
|
+
if (signal?.aborted === true) throw new RecordingImportContractError("recording-import-cancelled", "录音导入已取消");
|
|
90691
|
+
}
|
|
90692
|
+
function waitForRecordingUploadRetry(delay, signal) {
|
|
90693
|
+
return new Promise((resolve, reject) => {
|
|
90694
|
+
const abort = () => {
|
|
90695
|
+
clearTimeout(timer);
|
|
90696
|
+
signal?.removeEventListener("abort", abort);
|
|
90697
|
+
reject(new RecordingImportContractError("recording-import-cancelled", "录音导入已取消"));
|
|
90698
|
+
};
|
|
90699
|
+
const timer = setTimeout(() => {
|
|
90700
|
+
signal?.removeEventListener("abort", abort);
|
|
90701
|
+
resolve();
|
|
90702
|
+
}, Math.round(delay * (1 + Math.random() * .3)));
|
|
90703
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
90704
|
+
if (signal?.aborted === true) abort();
|
|
90705
|
+
});
|
|
90706
|
+
}
|
|
90707
|
+
//#endregion
|
|
90222
90708
|
//#region src/services/recording-import-gateway.ts
|
|
90223
90709
|
const AUDIO_SESSION_RECOVERY_LIMIT = 500;
|
|
90224
90710
|
const AUDIO_IMPORT_DUPLICATE_BATCH_SIZE = 100;
|
|
@@ -90572,6 +91058,8 @@ var AudioRecordingImportGateway = class {
|
|
|
90572
91058
|
accessKeyId: credentials.accessKeyId,
|
|
90573
91059
|
accessKeySecret: credentials.accessKeySecret,
|
|
90574
91060
|
stsToken: credentials.stsToken,
|
|
91061
|
+
timeout: RECORDING_UPLOAD_REQUEST_TIMEOUT,
|
|
91062
|
+
retryMax: 0,
|
|
90575
91063
|
refreshSTSTokenInterval: 300 * 1e3,
|
|
90576
91064
|
refreshSTSToken: async () => {
|
|
90577
91065
|
await this.requireJobSession(job);
|
|
@@ -90602,17 +91090,40 @@ var AudioRecordingImportGateway = class {
|
|
|
90602
91090
|
signal?.addEventListener("abort", abort, { once: true });
|
|
90603
91091
|
try {
|
|
90604
91092
|
const objectPath = `pc_upload/${String(job.userId)}/${job.sessionId}/${remoteFileName(job)}`;
|
|
90605
|
-
|
|
90606
|
-
|
|
90607
|
-
|
|
90608
|
-
|
|
90609
|
-
|
|
90610
|
-
|
|
90611
|
-
|
|
90612
|
-
|
|
90613
|
-
|
|
91093
|
+
let latestCheckpoint = job.uploadCheckpoint;
|
|
91094
|
+
const upload = (async () => {
|
|
91095
|
+
for (let attempt = 0;; attempt += 1) {
|
|
91096
|
+
throwIfRecordingUploadAborted(signal);
|
|
91097
|
+
if (attempt > 0) await this.requireJobSession(job);
|
|
91098
|
+
throwIfRecordingUploadAborted(signal);
|
|
91099
|
+
let progressFailed = false;
|
|
91100
|
+
try {
|
|
91101
|
+
await client.multipartUpload(objectPath, job.sourceHandle, {
|
|
91102
|
+
parallel: 1,
|
|
91103
|
+
partSize: 5 * 1024 * 1024,
|
|
91104
|
+
checkpoint: latestCheckpoint,
|
|
91105
|
+
mime: job.mimeType,
|
|
91106
|
+
progress: async (percentage, checkpoint) => {
|
|
91107
|
+
try {
|
|
91108
|
+
if (signal?.aborted === true) throw new RecordingImportContractError("recording-import-cancelled", "录音导入已取消");
|
|
91109
|
+
const bounded = Math.min(1, Math.max(0, percentage));
|
|
91110
|
+
await onProgress(Math.round(job.fileSize * bounded), checkpoint);
|
|
91111
|
+
if (checkpoint !== void 0) latestCheckpoint = checkpoint;
|
|
91112
|
+
} catch (error) {
|
|
91113
|
+
progressFailed = true;
|
|
91114
|
+
throw error;
|
|
91115
|
+
}
|
|
91116
|
+
}
|
|
91117
|
+
});
|
|
91118
|
+
return;
|
|
91119
|
+
} catch (error) {
|
|
91120
|
+
const delay = RECORDING_UPLOAD_RETRY_DELAYS[attempt];
|
|
91121
|
+
if (signal?.aborted === true || progressFailed || !isRetryableRecordingUploadError(error)) throw error;
|
|
91122
|
+
if (delay === void 0) throw new ArkmePluginError("recording-import-upload-failed", error instanceof Error ? error.message : "录音上传失败", true, 503, { cause: error });
|
|
91123
|
+
await waitForRecordingUploadRetry(delay, signal);
|
|
91124
|
+
}
|
|
90614
91125
|
}
|
|
90615
|
-
});
|
|
91126
|
+
})();
|
|
90616
91127
|
await (signal === void 0 ? upload : Promise.race([upload, aborted]));
|
|
90617
91128
|
} finally {
|
|
90618
91129
|
signal?.removeEventListener("abort", abort);
|
|
@@ -94504,6 +95015,15 @@ var WorldService = class {
|
|
|
94504
95015
|
media;
|
|
94505
95016
|
record;
|
|
94506
95017
|
viewerLabels;
|
|
95018
|
+
/** Called only for a Record-owner-authorized received-reaction response. */
|
|
95019
|
+
async reactionReference(recordUid) {
|
|
95020
|
+
const session = await this.runtime.requireSession();
|
|
95021
|
+
return this.worldRecordRef(session.userId, recordUid, { ownerUserId: session.userId });
|
|
95022
|
+
}
|
|
95023
|
+
async reactionTarget(recordRef) {
|
|
95024
|
+
const session = await this.runtime.requireSession();
|
|
95025
|
+
return { record_uid: this.openWorldRecordRef(recordRef, session.userId).recordUid };
|
|
95026
|
+
}
|
|
94507
95027
|
worldImageRefs = /* @__PURE__ */ new Map();
|
|
94508
95028
|
worldAvatarResolutionCache = /* @__PURE__ */ new Map();
|
|
94509
95029
|
extensionPublicationShareCache = /* @__PURE__ */ new Map();
|
|
@@ -96050,6 +96570,7 @@ var ArkmeService = class {
|
|
|
96050
96570
|
messageReport: true,
|
|
96051
96571
|
userBanManagement: true,
|
|
96052
96572
|
directMessageAdmission: true,
|
|
96573
|
+
reactionsV1: true,
|
|
96053
96574
|
groupOwnerGovernance: true,
|
|
96054
96575
|
...this.config.markdownQuickNotesEnabled === true ? { markdownQuickNotes: true } : {},
|
|
96055
96576
|
...this.config.markdownLongArticlesEnabled === true ? { markdownLongArticles: true } : {},
|
|
@@ -96841,6 +97362,112 @@ var ArkmeService = class {
|
|
|
96841
97362
|
async favoriteStickers(signal) {
|
|
96842
97363
|
return await this.chat.favoriteStickers(signal);
|
|
96843
97364
|
}
|
|
97365
|
+
async reactions(input, signal) {
|
|
97366
|
+
const result = await new ReactionService(this.runtime, (target) => target.worldRecordRef !== void 0 ? this.world.reactionTarget(target.worldRecordRef) : this.chat.reactionTarget(target.sourceRef, target.messageActionRef), (uid) => this.world.reactionReference(uid), (viewer, uid) => this.source.chatDirectorySourceKey(viewer, uid), async (items, signal) => {
|
|
97367
|
+
const session = await this.runtime.requireSession();
|
|
97368
|
+
const [sources, profiles] = await Promise.all([this.source.chatSourcesBySessionUids([...new Set(items.map((item) => item.sessionUID).filter(Boolean))], signal), this.profile.publicProfileSummariesByUserIds([...new Set(items.map((item) => item.senderID).filter((id) => id > 0))], session, signal, 5e3)]);
|
|
97369
|
+
const missingAvatars = [...sources].filter(([, source]) => !source.avatarRef && !source.avatarRefs?.length && !source.groupAvatar);
|
|
97370
|
+
if (missingAvatars.length > 0) try {
|
|
97371
|
+
const hydrated = await this.source.hydrateDirectoryPage(missingAvatars.map(([, source]) => source), signal ?? new AbortController().signal);
|
|
97372
|
+
missingAvatars.forEach(([uid], index) => {
|
|
97373
|
+
if (hydrated[index]) sources.set(uid, hydrated[index]);
|
|
97374
|
+
});
|
|
97375
|
+
} catch {
|
|
97376
|
+
signal?.throwIfAborted();
|
|
97377
|
+
}
|
|
97378
|
+
const selfSource = items.some((item) => item.sourceKind === "send_to_self" && item.message) ? {
|
|
97379
|
+
sourceRef: await this.source.sealSourceRef(session.userId, "send_to_self", "all", "发给自己"),
|
|
97380
|
+
kind: "send_to_self",
|
|
97381
|
+
displayName: "发给自己",
|
|
97382
|
+
activeAtMillis: 0,
|
|
97383
|
+
unreadCount: 0
|
|
97384
|
+
} : void 0;
|
|
97385
|
+
return items.map((item) => {
|
|
97386
|
+
const source = item.sourceKind === "send_to_self" ? selfSource : sources.get(item.sessionUID);
|
|
97387
|
+
return {
|
|
97388
|
+
sourceName: item.sourceKind === "send_to_self" ? "发给自己" : source?.displayName,
|
|
97389
|
+
authorName: item.senderID === session.userId ? "我" : profiles.get(item.senderID)?.displayName,
|
|
97390
|
+
...source && item.message ? { originalMessage: {
|
|
97391
|
+
source,
|
|
97392
|
+
itemUid: item.message.recordUID,
|
|
97393
|
+
recordOwnerUserId: item.message.ownerUserID,
|
|
97394
|
+
sendAtMillis: item.message.sendAtMillis
|
|
97395
|
+
} } : {},
|
|
97396
|
+
...source ? { avatar: {
|
|
97397
|
+
...source.avatarRef ? { avatarRef: source.avatarRef } : {},
|
|
97398
|
+
...source.avatarRefs ? { avatarRefs: source.avatarRefs } : {},
|
|
97399
|
+
...source.groupAvatar ? { groupAvatar: source.groupAvatar } : {}
|
|
97400
|
+
} } : {}
|
|
97401
|
+
};
|
|
97402
|
+
});
|
|
97403
|
+
}).request(input, signal);
|
|
97404
|
+
if (`${this.config.environment}:${(await this.runtime.requireSession()).userId}` !== input.accountKey || signal?.aborted) throw new ArkmePluginError("reaction-account-changed", "账号已变化", false);
|
|
97405
|
+
const actorLabels = async (sourceRef, ids) => {
|
|
97406
|
+
if (!sourceRef || !ids.length) return /* @__PURE__ */ new Map();
|
|
97407
|
+
try {
|
|
97408
|
+
return await this.chat.reactionActorLabels(sourceRef, ids, signal);
|
|
97409
|
+
} catch (error) {
|
|
97410
|
+
if (signal?.aborted) throw error;
|
|
97411
|
+
return /* @__PURE__ */ new Map();
|
|
97412
|
+
}
|
|
97413
|
+
};
|
|
97414
|
+
const actorAvatars = async (viewer, profiles) => new Map(await Promise.all([...profiles].filter(([, profile]) => profile.avatarUrl).map(async ([id]) => [id, await this.profile.sealProfileImageRef(viewer, id)])));
|
|
97415
|
+
const presentActor = (userId, profileName, label, avatarRef) => ({
|
|
97416
|
+
userId,
|
|
97417
|
+
displayName: label?.remark || profileName?.trim() || "用户",
|
|
97418
|
+
...label?.groupNickname ? { groupNickname: label.groupNickname } : {},
|
|
97419
|
+
...avatarRef ? { avatarRef } : {}
|
|
97420
|
+
});
|
|
97421
|
+
if (input.action === "query" || input.action === "groups") {
|
|
97422
|
+
const page = result;
|
|
97423
|
+
const groupPage = result;
|
|
97424
|
+
const groups = input.action === "query" ? page.items.flatMap((item) => item.actors_visible ? item.groups : []) : groupPage.items;
|
|
97425
|
+
const ids = [...new Set(groups.flatMap((group) => group.actorIds))];
|
|
97426
|
+
const session = await this.runtime.requireSession();
|
|
97427
|
+
const sourceByTarget = new Map(input.action === "query" ? input.targets.map((target) => [target.id, target.sourceRef]) : []);
|
|
97428
|
+
const sourceIds = /* @__PURE__ */ new Map();
|
|
97429
|
+
const include = (sourceRef, actorIds) => {
|
|
97430
|
+
if (!sourceRef || !actorIds.length) return;
|
|
97431
|
+
const users = sourceIds.get(sourceRef) ?? /* @__PURE__ */ new Set();
|
|
97432
|
+
actorIds.forEach((id) => users.add(id));
|
|
97433
|
+
sourceIds.set(sourceRef, users);
|
|
97434
|
+
};
|
|
97435
|
+
if (input.action === "query") {
|
|
97436
|
+
for (const item of page.items) if (item.actors_visible) include(sourceByTarget.get(item.target_id), item.groups.flatMap((group) => group.actorIds));
|
|
97437
|
+
} else include(input.target.sourceRef, ids);
|
|
97438
|
+
const [profiles, labels] = await Promise.all([this.profile.publicProfileSummariesByUserIds(ids, session, signal, 5e3), (async () => {
|
|
97439
|
+
const labels = /* @__PURE__ */ new Map();
|
|
97440
|
+
for (const [sourceRef, users] of sourceIds) labels.set(sourceRef, await actorLabels(sourceRef, [...users]));
|
|
97441
|
+
return labels;
|
|
97442
|
+
})()]);
|
|
97443
|
+
const avatars = await actorAvatars(session.userId, profiles);
|
|
97444
|
+
if (`${this.config.environment}:${(await this.runtime.requireSession()).userId}` !== input.accountKey || signal?.aborted) throw new ArkmePluginError("reaction-account-changed", "账号已变化", false);
|
|
97445
|
+
const present = ({ actorIds, ...group }, sourceRef, visible = true) => ({
|
|
97446
|
+
...group,
|
|
97447
|
+
actors: visible ? actorIds.map((userId) => presentActor(userId, profiles.get(userId)?.displayName, labels.get(sourceRef ?? "")?.get(userId), avatars.get(userId))) : []
|
|
97448
|
+
});
|
|
97449
|
+
return input.action === "query" ? {
|
|
97450
|
+
...page,
|
|
97451
|
+
items: page.items.map((item) => ({
|
|
97452
|
+
...item,
|
|
97453
|
+
groups: item.groups.map((group) => present(group, sourceByTarget.get(item.target_id), item.actors_visible))
|
|
97454
|
+
}))
|
|
97455
|
+
} : {
|
|
97456
|
+
...groupPage,
|
|
97457
|
+
items: groupPage.items.map((group) => present(group, input.target.sourceRef))
|
|
97458
|
+
};
|
|
97459
|
+
}
|
|
97460
|
+
if (input.action !== "actors") return result;
|
|
97461
|
+
const page = result;
|
|
97462
|
+
const session = await this.runtime.requireSession();
|
|
97463
|
+
const [profiles, labels] = await Promise.all([this.profile.publicProfileSummariesByUserIds(page.user_ids, session, signal, 5e3), actorLabels(input.target.sourceRef, page.user_ids)]);
|
|
97464
|
+
const avatars = await actorAvatars(session.userId, profiles);
|
|
97465
|
+
if (`${this.config.environment}:${(await this.runtime.requireSession()).userId}` !== input.accountKey || signal?.aborted) throw new ArkmePluginError("reaction-account-changed", "账号已变化", false);
|
|
97466
|
+
return {
|
|
97467
|
+
items: page.user_ids.map((userId) => presentActor(userId, profiles.get(userId)?.displayName, labels.get(userId), avatars.get(userId))),
|
|
97468
|
+
has_more: page.has_more
|
|
97469
|
+
};
|
|
97470
|
+
}
|
|
96844
97471
|
async addFavoriteSticker(item, signal) {
|
|
96845
97472
|
return await this.chat.addFavoriteSticker(item, signal);
|
|
96846
97473
|
}
|