openclaw-extension-typex 1.0.19 → 1.0.21
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/README.md +8 -0
- package/dist/agent-tools-send.d.ts +50 -0
- package/dist/agent-tools-send.js +353 -0
- package/dist/client/accounts.d.ts +1 -1
- package/dist/client/accounts.js +10 -10
- package/dist/client/client.d.ts +48 -8
- package/dist/client/client.js +303 -115
- package/dist/client/config.d.ts +3 -2
- package/dist/client/domain.d.ts +7 -0
- package/dist/client/domain.js +9 -0
- package/dist/client/message.d.ts +2 -1
- package/dist/client/message.js +203 -61
- package/dist/client/monitor.d.ts +2 -1
- package/dist/client/monitor.js +36 -7
- package/dist/client/outbound.d.ts +1 -1
- package/dist/client/outbound.js +49 -4
- package/dist/client/runtime.d.ts +1 -1
- package/dist/client/send.d.ts +8 -0
- package/dist/client/send.js +11 -1
- package/dist/client/types.d.ts +17 -1
- package/dist/config-schema.d.ts +4 -2
- package/dist/config-schema.js +4 -4
- package/dist/directory.d.ts +24 -0
- package/dist/directory.js +94 -0
- package/dist/index.d.ts +178 -130
- package/dist/index.js +10 -9
- package/dist/normalize.js +7 -2
- package/dist/onboarding.d.ts +2 -2
- package/dist/onboarding.js +2 -2
- package/dist/plugin.d.ts +65 -22
- package/dist/plugin.js +81 -18
- package/dist/setup-entry.d.ts +181 -0
- package/dist/setup-entry.js +5 -0
- package/dist/types.d.ts +1 -1
- package/openclaw.plugin.json +4 -3
- package/package.json +26 -6
package/dist/client/client.js
CHANGED
|
@@ -2,10 +2,37 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.TypeXClient = void 0;
|
|
4
4
|
exports.getTypeXClient = getTypeXClient;
|
|
5
|
+
const domain_js_1 = require("./domain.js");
|
|
5
6
|
const types_js_1 = require("./types.js");
|
|
6
|
-
const TYPEX_DOMAIN = "https://api-coco.typex.im";
|
|
7
|
-
// const TYPEX_DOMAIN = "https://api-tx.bossjob.net.cn";
|
|
8
7
|
let prompter;
|
|
8
|
+
function isSessionAuthFailure(status, bodyText, resJson) {
|
|
9
|
+
const combined = `${bodyText} ${resJson?.msg ?? ""} ${resJson?.message ?? ""}`.toLowerCase();
|
|
10
|
+
return status === 401 || combined.includes("session auth error");
|
|
11
|
+
}
|
|
12
|
+
function summarizeInvalidResponse(status, bodyText) {
|
|
13
|
+
const normalized = bodyText.replace(/\s+/g, " ").trim();
|
|
14
|
+
const htmlTitle = normalized.match(/<title>(.*?)<\/title>/i)?.[1]?.trim();
|
|
15
|
+
const htmlHeading = normalized.match(/<h1>(.*?)<\/h1>/i)?.[1]?.trim();
|
|
16
|
+
const summary = htmlTitle || htmlHeading || normalized.slice(0, 120) || "unexpected response body";
|
|
17
|
+
return `HTTP ${status} - ${summary}`;
|
|
18
|
+
}
|
|
19
|
+
function buildTextContent(content) {
|
|
20
|
+
return typeof content === "string" ? { text: content } : content;
|
|
21
|
+
}
|
|
22
|
+
function escapeHtml(value) {
|
|
23
|
+
return value
|
|
24
|
+
.replace(/&/g, "&")
|
|
25
|
+
.replace(/</g, "<")
|
|
26
|
+
.replace(/>/g, ">")
|
|
27
|
+
.replace(/"/g, """);
|
|
28
|
+
}
|
|
29
|
+
function buildMentionRichText(text, mentions) {
|
|
30
|
+
const mentionMarkup = mentions
|
|
31
|
+
.map((mention) => `<at userid="${escapeHtml(mention.id)}">${escapeHtml(mention.name)}</at>`)
|
|
32
|
+
.join(" ");
|
|
33
|
+
const suffix = text.trim() ? ` ${escapeHtml(text.trim())}` : "";
|
|
34
|
+
return `<p>${mentionMarkup}${suffix}</p>`;
|
|
35
|
+
}
|
|
9
36
|
class TypeXClient {
|
|
10
37
|
options;
|
|
11
38
|
accessToken;
|
|
@@ -25,8 +52,41 @@ class TypeXClient {
|
|
|
25
52
|
async getCurUserId() {
|
|
26
53
|
return this.userId ?? "";
|
|
27
54
|
}
|
|
55
|
+
getAuthHeaders(extraHeaders = {}) {
|
|
56
|
+
if (!this.accessToken) {
|
|
57
|
+
throw new Error("TypeXClient: Not authenticated.");
|
|
58
|
+
}
|
|
59
|
+
return this.mode === "bot"
|
|
60
|
+
? { Authorization: `Bearer ${this.accessToken}`, ...extraHeaders }
|
|
61
|
+
: { Cookie: this.accessToken, ...extraHeaders };
|
|
62
|
+
}
|
|
63
|
+
async postJson(endpoint, payload) {
|
|
64
|
+
const response = await fetch(`${domain_js_1.TYPEX_DOMAIN}${endpoint}`, {
|
|
65
|
+
method: "POST",
|
|
66
|
+
headers: this.getAuthHeaders({ "Content-Type": "application/json" }),
|
|
67
|
+
body: JSON.stringify(payload),
|
|
68
|
+
});
|
|
69
|
+
const bodyText = await response.text();
|
|
70
|
+
let resJson;
|
|
71
|
+
try {
|
|
72
|
+
resJson = JSON.parse(bodyText);
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
if (isSessionAuthFailure(response.status, bodyText)) {
|
|
76
|
+
throw new Error("TypeX 用户登录态已失效,请在 OpenClaw 中重新扫码登录 TypeX user 账号后再试。");
|
|
77
|
+
}
|
|
78
|
+
throw new Error(`TypeX API ${endpoint} returned invalid JSON: ${summarizeInvalidResponse(response.status, bodyText)}`);
|
|
79
|
+
}
|
|
80
|
+
if (!response.ok || resJson.code !== 0) {
|
|
81
|
+
if (isSessionAuthFailure(response.status, bodyText, resJson)) {
|
|
82
|
+
throw new Error("TypeX 用户登录态已失效,请在 OpenClaw 中重新扫码登录 TypeX user 账号后再试。");
|
|
83
|
+
}
|
|
84
|
+
throw new Error(`TypeX API ${endpoint} failed: [${resJson.code ?? response.status}] ${resJson.msg || resJson.message || "unknown error"}`);
|
|
85
|
+
}
|
|
86
|
+
return (resJson.data ?? []);
|
|
87
|
+
}
|
|
28
88
|
async fetchQrcodeUrl() {
|
|
29
|
-
const qrResponse = await fetch(`${TYPEX_DOMAIN}/user/qrcode?login_type=open`, {
|
|
89
|
+
const qrResponse = await fetch(`${domain_js_1.TYPEX_DOMAIN}/user/qrcode?login_type=open`, {
|
|
30
90
|
method: "POST",
|
|
31
91
|
headers: { "Content-Type": "application/json" },
|
|
32
92
|
body: JSON.stringify({}),
|
|
@@ -41,7 +101,7 @@ class TypeXClient {
|
|
|
41
101
|
return qrResult.data;
|
|
42
102
|
}
|
|
43
103
|
async checkLoginStatus(qrcodeId) {
|
|
44
|
-
const checkRes = await fetch(`${TYPEX_DOMAIN}/open/qrcode/check_auth`, {
|
|
104
|
+
const checkRes = await fetch(`${domain_js_1.TYPEX_DOMAIN}/open/qrcode/check_auth`, {
|
|
45
105
|
method: "POST",
|
|
46
106
|
headers: { "Content-Type": "application/json" },
|
|
47
107
|
body: JSON.stringify({ qr_code_id: qrcodeId }),
|
|
@@ -59,66 +119,15 @@ class TypeXClient {
|
|
|
59
119
|
}
|
|
60
120
|
return false;
|
|
61
121
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
* @param to chat_id to send to
|
|
65
|
-
* @param content message text or object
|
|
66
|
-
*/
|
|
67
|
-
async sendMessage(to, content, msgType = 0, options = {}) {
|
|
68
|
-
const token = this.accessToken;
|
|
69
|
-
if (!token)
|
|
122
|
+
async executeSendMessage(endpoint, to, payload, preview) {
|
|
123
|
+
if (!this.accessToken)
|
|
70
124
|
throw new Error("TypeXClient: Not authenticated.");
|
|
71
|
-
let finalContent = content;
|
|
72
|
-
if (typeof content === "object") {
|
|
73
|
-
try {
|
|
74
|
-
finalContent = JSON.stringify(content);
|
|
75
|
-
}
|
|
76
|
-
catch {
|
|
77
|
-
finalContent = String(content);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
125
|
if (prompter)
|
|
81
|
-
prompter.note(`TypeXClient sending to ${to}: ${
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
const isBot = this.mode === "bot";
|
|
85
|
-
const endpoint = isBot ? "/open/robot/send_message" : "/open/claw/send_message";
|
|
86
|
-
let payloadStr;
|
|
87
|
-
if (isBot) {
|
|
88
|
-
let botContentObj;
|
|
89
|
-
if (msgType === types_js_1.TypeXMessageEnum.text || msgType === types_js_1.TypeXMessageEnum.richText) {
|
|
90
|
-
// According to docs, text type content format: {"text":"test"}
|
|
91
|
-
// Assuming content or finalContent holds the actual string text.
|
|
92
|
-
botContentObj = {
|
|
93
|
-
text: typeof content === "string" ? content : (typeof finalContent === "string" ? finalContent : JSON.stringify(content))
|
|
94
|
-
};
|
|
95
|
-
// Ensure msgType is 0 when sending to `/open/robot/send_message` since 8 might not be supported natively by robot API
|
|
96
|
-
msgType = types_js_1.TypeXMessageEnum.text;
|
|
97
|
-
}
|
|
98
|
-
else {
|
|
99
|
-
// Image or File object payload for bot
|
|
100
|
-
botContentObj = typeof finalContent === "string" ? { text: finalContent } : content;
|
|
101
|
-
}
|
|
102
|
-
payloadStr = JSON.stringify({
|
|
103
|
-
chat_id: to,
|
|
104
|
-
content: botContentObj,
|
|
105
|
-
msg_type: msgType,
|
|
106
|
-
reply_msg_id: options.replyMsgId || "0",
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
else {
|
|
110
|
-
payloadStr = JSON.stringify({
|
|
111
|
-
chat_id: to,
|
|
112
|
-
content: { text: finalContent },
|
|
113
|
-
msg_type: msgType,
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
const response = await fetch(`${TYPEX_DOMAIN}${endpoint}`, {
|
|
126
|
+
prompter.note(`TypeXClient sending to ${to}: ${preview.slice(0, 80)}`);
|
|
127
|
+
const payloadStr = JSON.stringify(payload);
|
|
128
|
+
const response = await fetch(`${domain_js_1.TYPEX_DOMAIN}${endpoint}`, {
|
|
117
129
|
method: "POST",
|
|
118
|
-
headers: {
|
|
119
|
-
"Content-Type": "application/json",
|
|
120
|
-
...(isBot ? { Authorization: `Bearer ${token}` } : { Cookie: token }),
|
|
121
|
-
},
|
|
130
|
+
headers: this.getAuthHeaders({ "Content-Type": "application/json" }),
|
|
122
131
|
body: payloadStr,
|
|
123
132
|
});
|
|
124
133
|
const bodyText = await response.text();
|
|
@@ -127,13 +136,104 @@ class TypeXClient {
|
|
|
127
136
|
resJson = JSON.parse(bodyText);
|
|
128
137
|
}
|
|
129
138
|
catch (e) {
|
|
130
|
-
|
|
139
|
+
if (isSessionAuthFailure(response.status, bodyText)) {
|
|
140
|
+
throw new Error("TypeX 用户登录态已失效,请在 OpenClaw 中重新扫码登录 TypeX user 账号后再试。");
|
|
141
|
+
}
|
|
142
|
+
throw new Error(`Send message failed (invalid JSON): ${summarizeInvalidResponse(response.status, bodyText)}`);
|
|
131
143
|
}
|
|
132
144
|
if (resJson.code !== 0) {
|
|
145
|
+
if (isSessionAuthFailure(response.status, bodyText, resJson)) {
|
|
146
|
+
throw new Error("TypeX 用户登录态已失效,请在 OpenClaw 中重新扫码登录 TypeX user 账号后再试。");
|
|
147
|
+
}
|
|
133
148
|
throw new Error(`Send message failed: [${resJson.code}] ${resJson.msg || resJson.message}`);
|
|
134
149
|
}
|
|
135
150
|
return resJson.data || { message_id: `msg_${Date.now()}` };
|
|
136
151
|
}
|
|
152
|
+
async sendUserChatMessage(chatId, content, msgType = types_js_1.TypeXMessageEnum.text) {
|
|
153
|
+
return this.executeSendMessage("/open/claw/send_message", chatId, {
|
|
154
|
+
chat_id: chatId,
|
|
155
|
+
content: buildTextContent(content),
|
|
156
|
+
msg_type: msgType,
|
|
157
|
+
}, typeof content === "string" ? content : JSON.stringify(content));
|
|
158
|
+
}
|
|
159
|
+
async sendDelegatedChatMessage(chatId, content, msgType = types_js_1.TypeXMessageEnum.text) {
|
|
160
|
+
return this.executeSendMessage("/open/claw/send_message", chatId, {
|
|
161
|
+
chat_id: chatId,
|
|
162
|
+
content: buildTextContent(content),
|
|
163
|
+
msg_type: msgType,
|
|
164
|
+
is_delegate: true,
|
|
165
|
+
}, typeof content === "string" ? content : JSON.stringify(content));
|
|
166
|
+
}
|
|
167
|
+
async sendDelegatedContactMessage(receiverId, content, msgType = types_js_1.TypeXMessageEnum.text) {
|
|
168
|
+
return this.executeSendMessage("/open/claw/send_message", receiverId, {
|
|
169
|
+
receiver_id: receiverId,
|
|
170
|
+
content: buildTextContent(content),
|
|
171
|
+
msg_type: msgType,
|
|
172
|
+
is_delegate: true,
|
|
173
|
+
}, typeof content === "string" ? content : JSON.stringify(content));
|
|
174
|
+
}
|
|
175
|
+
async sendBotGroupMessage(chatId, content, msgType = types_js_1.TypeXMessageEnum.text, options = {}) {
|
|
176
|
+
const mentionIds = Array.isArray(options.atUserIds) && options.atUserIds.length > 0 ? options.atUserIds : undefined;
|
|
177
|
+
const mentionEntries = Array.isArray(options.atMentions) && options.atMentions.length > 0 ? options.atMentions : undefined;
|
|
178
|
+
const normalizedMsgType = msgType === types_js_1.TypeXMessageEnum.text || msgType === types_js_1.TypeXMessageEnum.richText
|
|
179
|
+
? types_js_1.TypeXMessageEnum.text
|
|
180
|
+
: msgType;
|
|
181
|
+
const normalizedContent = normalizedMsgType === types_js_1.TypeXMessageEnum.text
|
|
182
|
+
? {
|
|
183
|
+
text: mentionEntries && mentionEntries.length > 0
|
|
184
|
+
? buildMentionRichText(typeof content === "string" ? content : JSON.stringify(content), mentionEntries)
|
|
185
|
+
: typeof content === "string"
|
|
186
|
+
? content
|
|
187
|
+
: JSON.stringify(content),
|
|
188
|
+
at_user_ids: mentionIds,
|
|
189
|
+
}
|
|
190
|
+
: typeof content === "object" && content !== null
|
|
191
|
+
? {
|
|
192
|
+
...content,
|
|
193
|
+
at_user_ids: mentionIds,
|
|
194
|
+
}
|
|
195
|
+
: content;
|
|
196
|
+
return this.executeSendMessage("/open/robot/send_message", chatId, {
|
|
197
|
+
chat_id: chatId,
|
|
198
|
+
content: normalizedContent,
|
|
199
|
+
msg_type: normalizedMsgType,
|
|
200
|
+
reply_msg_id: options.replyMsgId || "0",
|
|
201
|
+
}, typeof content === "string" ? content : JSON.stringify(content));
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Compatibility wrapper. Prefer the explicit methods above for new call sites.
|
|
205
|
+
*/
|
|
206
|
+
async sendMessage(to, content, msgType = types_js_1.TypeXMessageEnum.text, options = {}) {
|
|
207
|
+
if (this.mode === "bot") {
|
|
208
|
+
return this.sendBotGroupMessage(to, content, msgType, {
|
|
209
|
+
replyMsgId: options.replyMsgId,
|
|
210
|
+
atUserIds: options.atUserIds,
|
|
211
|
+
atMentions: options.atMentions,
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
if (options.isDelegate && options.receiverId) {
|
|
215
|
+
return this.sendDelegatedContactMessage(options.receiverId, content, msgType);
|
|
216
|
+
}
|
|
217
|
+
if (options.isDelegate) {
|
|
218
|
+
return this.sendDelegatedChatMessage(to, content, msgType);
|
|
219
|
+
}
|
|
220
|
+
return this.sendUserChatMessage(to, content, msgType);
|
|
221
|
+
}
|
|
222
|
+
async searchFeedsByName(name) {
|
|
223
|
+
if (!name.trim())
|
|
224
|
+
return [];
|
|
225
|
+
return this.postJson("/open/claw/feeds_by_name", { name });
|
|
226
|
+
}
|
|
227
|
+
async searchContactsByName(name) {
|
|
228
|
+
if (!name.trim())
|
|
229
|
+
return [];
|
|
230
|
+
return this.postJson("/open/claw/contacts_by_name", { name });
|
|
231
|
+
}
|
|
232
|
+
async listGroupMembers(chatId) {
|
|
233
|
+
if (!chatId.trim() || this.mode !== "bot")
|
|
234
|
+
return [];
|
|
235
|
+
return this.postJson("/open/robot/group_members", { chatid: chatId });
|
|
236
|
+
}
|
|
137
237
|
/**
|
|
138
238
|
* Upload resource for the robot to send.
|
|
139
239
|
* @param fileName Name of the file
|
|
@@ -154,10 +254,10 @@ class TypeXClient {
|
|
|
154
254
|
// By providing a Blob we ensure it correctly adds boundaries and content types per form part.
|
|
155
255
|
const blob = fileContent instanceof Buffer ? new Blob([fileContent]) : fileContent;
|
|
156
256
|
formData.append("file_content", blob, fileName);
|
|
157
|
-
const response = await fetch(`${TYPEX_DOMAIN}/open/robot/upload`, {
|
|
257
|
+
const response = await fetch(`${domain_js_1.TYPEX_DOMAIN}/open/robot/upload`, {
|
|
158
258
|
method: "POST",
|
|
159
259
|
headers: {
|
|
160
|
-
Authorization: `Bearer ${this.accessToken}
|
|
260
|
+
Authorization: `Bearer ${this.accessToken}`,
|
|
161
261
|
// Note: fetch will automatically set the Content-Type boundary
|
|
162
262
|
},
|
|
163
263
|
body: formData,
|
|
@@ -168,6 +268,42 @@ class TypeXClient {
|
|
|
168
268
|
}
|
|
169
269
|
return resJson.data;
|
|
170
270
|
}
|
|
271
|
+
async uploadUserResource(fileName, fileType, fileContent, chatId) {
|
|
272
|
+
if (this.mode !== "user" || !this.accessToken) {
|
|
273
|
+
throw new Error("TypeXClient: uploadUserResource requires user mode and a session token.");
|
|
274
|
+
}
|
|
275
|
+
const formData = new FormData();
|
|
276
|
+
formData.append("chat_id", chatId);
|
|
277
|
+
formData.append("file_name", fileName);
|
|
278
|
+
formData.append("file_type", fileType);
|
|
279
|
+
const blob = fileContent instanceof Buffer ? new Blob([fileContent]) : fileContent;
|
|
280
|
+
formData.append("file_content", blob, fileName);
|
|
281
|
+
const response = await fetch(`${domain_js_1.TYPEX_DOMAIN}/open/upload`, {
|
|
282
|
+
method: "POST",
|
|
283
|
+
headers: {
|
|
284
|
+
Cookie: this.accessToken,
|
|
285
|
+
},
|
|
286
|
+
body: formData,
|
|
287
|
+
});
|
|
288
|
+
const bodyText = await response.text();
|
|
289
|
+
let resJson;
|
|
290
|
+
try {
|
|
291
|
+
resJson = JSON.parse(bodyText);
|
|
292
|
+
}
|
|
293
|
+
catch {
|
|
294
|
+
if (isSessionAuthFailure(response.status, bodyText)) {
|
|
295
|
+
throw new Error("TypeX 用户登录态已失效,请在 OpenClaw 中重新扫码登录 TypeX user 账号后再试。");
|
|
296
|
+
}
|
|
297
|
+
throw new Error(`Upload user resource failed (invalid JSON): ${summarizeInvalidResponse(response.status, bodyText)}`);
|
|
298
|
+
}
|
|
299
|
+
if (!response.ok || resJson.code !== 0) {
|
|
300
|
+
if (isSessionAuthFailure(response.status, bodyText, resJson)) {
|
|
301
|
+
throw new Error("TypeX 用户登录态已失效,请在 OpenClaw 中重新扫码登录 TypeX user 账号后再试。");
|
|
302
|
+
}
|
|
303
|
+
throw new Error(`Upload user resource failed: [${resJson.code ?? response.status}] ${resJson.msg || resJson.message || "unknown error"}`);
|
|
304
|
+
}
|
|
305
|
+
return resJson.data;
|
|
306
|
+
}
|
|
171
307
|
/**
|
|
172
308
|
* Fetch messages. Dispatches to user or bot endpoint based on mode.
|
|
173
309
|
*/
|
|
@@ -180,64 +316,47 @@ class TypeXClient {
|
|
|
180
316
|
async fetchUserMessages(pos) {
|
|
181
317
|
if (!this.accessToken)
|
|
182
318
|
return [];
|
|
319
|
+
const response = await fetch(`${domain_js_1.TYPEX_DOMAIN}/open/claw/message`, {
|
|
320
|
+
method: "POST",
|
|
321
|
+
headers: { Cookie: this.accessToken, "Content-Type": "application/json" },
|
|
322
|
+
body: JSON.stringify({ pos }),
|
|
323
|
+
});
|
|
324
|
+
const bodyText = await response.text();
|
|
325
|
+
let resJson;
|
|
183
326
|
try {
|
|
184
|
-
|
|
185
|
-
method: "POST",
|
|
186
|
-
headers: { Cookie: this.accessToken, "Content-Type": "application/json" },
|
|
187
|
-
body: JSON.stringify({ pos }),
|
|
188
|
-
});
|
|
189
|
-
if (response.status === 401) {
|
|
190
|
-
throw new Error("Unauthorized: 401 Token is invalid or expired.");
|
|
191
|
-
}
|
|
192
|
-
if (!response.ok) {
|
|
193
|
-
throw new Error(`HTTP Error: ${response.status}`);
|
|
194
|
-
}
|
|
195
|
-
const resJson = await response.json();
|
|
196
|
-
if (resJson.code !== 0) {
|
|
197
|
-
throw new Error(`API Error: code ${resJson.code}, message: ${resJson.msg}`);
|
|
198
|
-
}
|
|
199
|
-
return Array.isArray(resJson.data) ? resJson.data : [];
|
|
327
|
+
resJson = JSON.parse(bodyText);
|
|
200
328
|
}
|
|
201
|
-
catch
|
|
202
|
-
|
|
203
|
-
throw e;
|
|
204
|
-
}
|
|
205
|
-
console.log(`Fetch messages error: ${e}`);
|
|
206
|
-
return [];
|
|
329
|
+
catch {
|
|
330
|
+
throw new Error(`TypeX user poll returned non-JSON response: ${summarizeInvalidResponse(response.status, bodyText)}`);
|
|
207
331
|
}
|
|
332
|
+
if (!response.ok || resJson.code !== 0) {
|
|
333
|
+
throw new Error(`TypeX user poll failed: HTTP ${response.status} - ${resJson.msg || resJson.message || "unknown error"}`);
|
|
334
|
+
}
|
|
335
|
+
return Array.isArray(resJson.data) ? resJson.data : [];
|
|
208
336
|
}
|
|
209
337
|
/**
|
|
210
338
|
* Pull messages for a bot account (Bearer token auth).
|
|
211
|
-
* TODO: replace /open/bot/message with the actual endpoint path once confirmed.
|
|
212
339
|
*/
|
|
213
340
|
async fetchBotMessages() {
|
|
214
341
|
if (!this.accessToken)
|
|
215
342
|
return [];
|
|
343
|
+
const response = await fetch(`${domain_js_1.TYPEX_DOMAIN}/open/robot/message/pull`, {
|
|
344
|
+
method: "POST",
|
|
345
|
+
headers: { Authorization: `Bearer ${this.accessToken}`, "Content-Type": "application/json" },
|
|
346
|
+
body: JSON.stringify({ limit: 5 }),
|
|
347
|
+
});
|
|
348
|
+
const bodyText = await response.text();
|
|
349
|
+
let resJson;
|
|
216
350
|
try {
|
|
217
|
-
|
|
218
|
-
method: "POST",
|
|
219
|
-
headers: { Authorization: `Bearer ${this.accessToken}`, "Content-Type": "application/json" },
|
|
220
|
-
body: JSON.stringify({ limit: 5 }),
|
|
221
|
-
});
|
|
222
|
-
if (response.status === 401) {
|
|
223
|
-
throw new Error("Unauthorized: 401 Bot Token is invalid or expired.");
|
|
224
|
-
}
|
|
225
|
-
if (!response.ok) {
|
|
226
|
-
throw new Error(`HTTP Error: ${response.status}`);
|
|
227
|
-
}
|
|
228
|
-
const resJson = await response.json();
|
|
229
|
-
if (resJson.code !== 0) {
|
|
230
|
-
return [];
|
|
231
|
-
}
|
|
232
|
-
return Array.isArray(resJson.data?.messages) ? resJson.data.messages : [];
|
|
351
|
+
resJson = JSON.parse(bodyText);
|
|
233
352
|
}
|
|
234
|
-
catch
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
return [];
|
|
353
|
+
catch {
|
|
354
|
+
throw new Error(`TypeX bot poll returned non-JSON response: ${summarizeInvalidResponse(response.status, bodyText)}`);
|
|
355
|
+
}
|
|
356
|
+
if (!response.ok || resJson.code !== 0) {
|
|
357
|
+
throw new Error(`TypeX bot poll failed: HTTP ${response.status} - ${resJson.msg || resJson.message || "unknown error"}`);
|
|
240
358
|
}
|
|
359
|
+
return Array.isArray(resJson.data?.messages) ? resJson.data.messages : [];
|
|
241
360
|
}
|
|
242
361
|
/**
|
|
243
362
|
* Fetch a single message by ID (used to resolve quoted/parent messages).
|
|
@@ -247,7 +366,7 @@ class TypeXClient {
|
|
|
247
366
|
return null;
|
|
248
367
|
try {
|
|
249
368
|
const isBot = this.mode === "bot";
|
|
250
|
-
const response = await fetch(`${TYPEX_DOMAIN}/open/claw/message/${messageId}`, {
|
|
369
|
+
const response = await fetch(`${domain_js_1.TYPEX_DOMAIN}/open/claw/message/${messageId}`, {
|
|
251
370
|
method: "GET",
|
|
252
371
|
headers: isBot
|
|
253
372
|
? { Authorization: `Bearer ${this.accessToken}`, "Content-Type": "application/json" }
|
|
@@ -265,19 +384,22 @@ class TypeXClient {
|
|
|
265
384
|
* Requires Bot Token authentication.
|
|
266
385
|
*/
|
|
267
386
|
async fetchFileBuffer(objectKey, size) {
|
|
268
|
-
if (!this.accessToken
|
|
387
|
+
if (!this.accessToken)
|
|
269
388
|
return null;
|
|
270
389
|
try {
|
|
271
390
|
const query = new URLSearchParams({ object_key: objectKey });
|
|
272
391
|
if (size)
|
|
273
392
|
query.append("size", size);
|
|
274
|
-
const
|
|
393
|
+
const isBot = this.mode === "bot";
|
|
394
|
+
const url = isBot
|
|
395
|
+
? `${domain_js_1.TYPEX_DOMAIN}/open/robot/chat/file?${query.toString()}`
|
|
396
|
+
: `${domain_js_1.TYPEX_DOMAIN}/open/file?${query.toString()}`;
|
|
275
397
|
const response = await fetch(url, {
|
|
276
398
|
method: "GET",
|
|
277
|
-
headers:
|
|
399
|
+
headers: this.getAuthHeaders(),
|
|
278
400
|
});
|
|
279
401
|
if (!response.ok) {
|
|
280
|
-
console.
|
|
402
|
+
console.warn(`fetchFileBuffer failed with status: ${response.status} ${response.statusText} for url: ${url}`);
|
|
281
403
|
return null;
|
|
282
404
|
}
|
|
283
405
|
const arrayBuffer = await response.arrayBuffer();
|
|
@@ -286,10 +408,76 @@ class TypeXClient {
|
|
|
286
408
|
return { buffer, mimeType };
|
|
287
409
|
}
|
|
288
410
|
catch (e) {
|
|
289
|
-
console.
|
|
411
|
+
console.error(`fetchFileBuffer error: ${e}`);
|
|
290
412
|
return null;
|
|
291
413
|
}
|
|
292
414
|
}
|
|
415
|
+
/**
|
|
416
|
+
* Search feeds by name (User mode)
|
|
417
|
+
*/
|
|
418
|
+
async fetchFeedsByName(name) {
|
|
419
|
+
if (!this.accessToken || this.mode !== "user")
|
|
420
|
+
return [];
|
|
421
|
+
try {
|
|
422
|
+
const response = await fetch(`${domain_js_1.TYPEX_DOMAIN}/open/claw/feeds_by_name`, {
|
|
423
|
+
method: "POST",
|
|
424
|
+
headers: { Cookie: this.accessToken, "Content-Type": "application/json" },
|
|
425
|
+
body: JSON.stringify({ name }),
|
|
426
|
+
});
|
|
427
|
+
const resJson = await response.json();
|
|
428
|
+
if (resJson.code !== 0)
|
|
429
|
+
return [];
|
|
430
|
+
return Array.isArray(resJson.data) ? resJson.data : [];
|
|
431
|
+
}
|
|
432
|
+
catch (e) {
|
|
433
|
+
console.error(`fetchFeedsByName error: ${e}`);
|
|
434
|
+
return [];
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* Search contacts by name (User mode)
|
|
439
|
+
*/
|
|
440
|
+
async fetchContactsByName(name) {
|
|
441
|
+
if (!this.accessToken || this.mode !== "user")
|
|
442
|
+
return [];
|
|
443
|
+
try {
|
|
444
|
+
const response = await fetch(`${domain_js_1.TYPEX_DOMAIN}/open/claw/contacts_by_name`, {
|
|
445
|
+
method: "POST",
|
|
446
|
+
headers: { Cookie: this.accessToken, "Content-Type": "application/json" },
|
|
447
|
+
body: JSON.stringify({ name }),
|
|
448
|
+
});
|
|
449
|
+
const resJson = await response.json();
|
|
450
|
+
if (resJson.code !== 0)
|
|
451
|
+
return [];
|
|
452
|
+
return Array.isArray(resJson.data) ? resJson.data : [];
|
|
453
|
+
}
|
|
454
|
+
catch (e) {
|
|
455
|
+
console.error(`fetchContactsByName error: ${e}`);
|
|
456
|
+
return [];
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* Search group members by name (Bot mode)
|
|
461
|
+
*/
|
|
462
|
+
async fetchGroupMembersByName(name) {
|
|
463
|
+
if (!this.accessToken || this.mode !== "bot")
|
|
464
|
+
return [];
|
|
465
|
+
try {
|
|
466
|
+
const response = await fetch(`${domain_js_1.TYPEX_DOMAIN}/open/claw/group_members`, {
|
|
467
|
+
method: "POST",
|
|
468
|
+
headers: { Authorization: `Bearer ${this.accessToken}`, "Content-Type": "application/json" },
|
|
469
|
+
body: JSON.stringify({ name }),
|
|
470
|
+
});
|
|
471
|
+
const resJson = await response.json();
|
|
472
|
+
if (resJson.code !== 0)
|
|
473
|
+
return [];
|
|
474
|
+
return Array.isArray(resJson.data) ? resJson.data : [];
|
|
475
|
+
}
|
|
476
|
+
catch (e) {
|
|
477
|
+
console.error(`fetchGroupMembersByName error: ${e}`);
|
|
478
|
+
return [];
|
|
479
|
+
}
|
|
480
|
+
}
|
|
293
481
|
}
|
|
294
482
|
exports.TypeXClient = TypeXClient;
|
|
295
483
|
function getTypeXClient(accountId, manualOptions) {
|
package/dist/client/config.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type { OpenClawConfig
|
|
2
|
-
import type {
|
|
1
|
+
import type { OpenClawConfig } from "openclaw/plugin-sdk/channel-core";
|
|
2
|
+
import type { DmPolicy, GroupPolicy } from "openclaw/plugin-sdk/setup";
|
|
3
|
+
import type { TypeXGroupConfig } from "../types.js";
|
|
3
4
|
export type ResolvedTypeXConfig = {
|
|
4
5
|
enabled: boolean;
|
|
5
6
|
dmPolicy: DmPolicy;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const TYPEX_DOMAINS: {
|
|
2
|
+
readonly prod: "https://api-coco.typex.im";
|
|
3
|
+
readonly test: "https://api-tx.typex-test.cn";
|
|
4
|
+
};
|
|
5
|
+
export type TypeXDomainTarget = keyof typeof TYPEX_DOMAINS;
|
|
6
|
+
export declare const ACTIVE_TYPEX_DOMAIN_TARGET: TypeXDomainTarget;
|
|
7
|
+
export declare const TYPEX_DOMAIN: "https://api-coco.typex.im";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TYPEX_DOMAIN = exports.ACTIVE_TYPEX_DOMAIN_TARGET = exports.TYPEX_DOMAINS = void 0;
|
|
4
|
+
exports.TYPEX_DOMAINS = {
|
|
5
|
+
prod: "https://api-coco.typex.im",
|
|
6
|
+
test: "https://api-tx.typex-test.cn",
|
|
7
|
+
};
|
|
8
|
+
exports.ACTIVE_TYPEX_DOMAIN_TARGET = "prod";
|
|
9
|
+
exports.TYPEX_DOMAIN = exports.TYPEX_DOMAINS[exports.ACTIVE_TYPEX_DOMAIN_TARGET];
|
package/dist/client/message.d.ts
CHANGED
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
* Main message dispatch orchestrator for the TypeX standalone plugin.
|
|
5
5
|
* Pure helpers live in ./message-helpers.ts.
|
|
6
6
|
*/
|
|
7
|
-
import type {
|
|
7
|
+
import type { OpenClawConfig } from "openclaw/plugin-sdk/channel-core";
|
|
8
|
+
import { type HistoryEntry } from "openclaw/plugin-sdk/reply-history";
|
|
8
9
|
import type { TypeXClient } from "./client.js";
|
|
9
10
|
import { type TypeXMessageEntry } from "./types.js";
|
|
10
11
|
export type ProcessTypeXMessageOptions = {
|