@ascegu/teamily 1.0.24 → 1.0.25

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ascegu/teamily",
3
- "version": "1.0.24",
3
+ "version": "1.0.25",
4
4
  "description": "OpenClaw Teamily channel plugin - Team instant messaging server integration",
5
5
  "keywords": [
6
6
  "channel",
package/src/channel.ts CHANGED
@@ -395,6 +395,7 @@ export const teamilyPlugin: ChannelPlugin<ResolvedTeamilyAccount> = {
395
395
 
396
396
  // Send media attachments (first with caption, rest without).
397
397
  const mediaUrls = resolveOutboundMediaUrls(payload as Record<string, unknown>);
398
+ let captionSent = false;
398
399
  const sentMedia = await sendMediaWithLeadingCaption({
399
400
  mediaUrls,
400
401
  caption: replyText,
@@ -403,6 +404,7 @@ export const teamilyPlugin: ChannelPlugin<ResolvedTeamilyAccount> = {
403
404
  // OpenIM doesn't support inline captions on media; send separately.
404
405
  if (caption) {
405
406
  await monitor.sendText(target, caption);
407
+ captionSent = true;
406
408
  }
407
409
  },
408
410
  onError: (error) => {
@@ -410,8 +412,8 @@ export const teamilyPlugin: ChannelPlugin<ResolvedTeamilyAccount> = {
410
412
  },
411
413
  });
412
414
 
413
- // If no media was sent, send text only.
414
- if (!sentMedia && replyText) {
415
+ // If no media was sent (or media failed), send text as a plain message.
416
+ if ((!sentMedia || !captionSent) && replyText) {
415
417
  log?.info?.(
416
418
  `[${accountId}] Sending reply to: ${replyTarget} (isGroup=${isGroup})`,
417
419
  );
package/src/monitor.ts CHANGED
@@ -20,6 +20,36 @@ export interface TeamilyMonitorOptions {
20
20
  type SdkModule = typeof import("@openim/client-sdk");
21
21
  type SdkInstance = ReturnType<SdkModule["getSDK"]>;
22
22
 
23
+ // The OpenIM SDK uses FileReader internally for file upload (MD5 hashing of
24
+ // chunks). FileReader is a browser-only API not available in Node.js, so we
25
+ // provide a minimal polyfill that delegates to Blob.prototype.arrayBuffer().
26
+ if (typeof globalThis.FileReader === "undefined") {
27
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
28
+ (globalThis as any).FileReader = class FileReader {
29
+ result: ArrayBuffer | null = null;
30
+ error: unknown = null;
31
+ readyState = 0; // 0=EMPTY, 1=LOADING, 2=DONE
32
+ onload: ((ev: { target: FileReader }) => void) | null = null;
33
+ onerror: ((ev: unknown) => void) | null = null;
34
+
35
+ readAsArrayBuffer(blob: Blob): void {
36
+ this.readyState = 1;
37
+ blob
38
+ .arrayBuffer()
39
+ .then((buffer) => {
40
+ this.result = buffer;
41
+ this.readyState = 2;
42
+ this.onload?.({ target: this });
43
+ })
44
+ .catch((err) => {
45
+ this.error = err;
46
+ this.readyState = 2;
47
+ this.onerror?.(err);
48
+ });
49
+ }
50
+ };
51
+ }
52
+
23
53
  // Lazy-loaded SDK to avoid top-level dynamic import issues
24
54
  let sdkModule: SdkModule | null = null;
25
55
  async function loadSDK() {
@@ -217,8 +247,9 @@ export class TeamilyMonitor {
217
247
  ): Promise<string> {
218
248
  const sdk = this.requireSdk();
219
249
  const file = new File([new Uint8Array(buffer)], fileName, { type: contentType });
250
+ const uuid = crypto.randomUUID();
220
251
  const picInfo = {
221
- uuid: "",
252
+ uuid,
222
253
  type: contentType,
223
254
  width: 0,
224
255
  height: 0,
@@ -256,10 +287,10 @@ export class TeamilyMonitor {
256
287
  duration: 0,
257
288
  videoType: contentType,
258
289
  snapshotPath: "",
259
- videoUUID: "",
290
+ videoUUID: crypto.randomUUID(),
260
291
  videoUrl: "",
261
292
  videoSize: buffer.length,
262
- snapshotUUID: "",
293
+ snapshotUUID: crypto.randomUUID(),
263
294
  snapshotSize: 0,
264
295
  snapshotUrl: "",
265
296
  snapshotWidth: 0,
@@ -285,7 +316,7 @@ export class TeamilyMonitor {
285
316
  const sdk = this.requireSdk();
286
317
  const file = new File([new Uint8Array(buffer)], fileName, { type: contentType });
287
318
  const created = await sdk.createSoundMessageByFile({
288
- uuid: "",
319
+ uuid: crypto.randomUUID(),
289
320
  soundPath: "",
290
321
  sourceUrl: "",
291
322
  dataSize: buffer.length,
@@ -312,7 +343,7 @@ export class TeamilyMonitor {
312
343
  const created = await sdk.createFileMessageByFile({
313
344
  filePath: "",
314
345
  fileName,
315
- uuid: "",
346
+ uuid: crypto.randomUUID(),
316
347
  sourceUrl: "",
317
348
  fileSize: buffer.length,
318
349
  file,