Skip to content

Commit d4728d7

Browse files
authored
feat(square): send image / video / audio to a SquareChat (OpenChat) (#206)
* fix(obs): let uploadObjTalk carry the real audio/video duration uploadObjTalk hardcoded the obs `duration` param to "1919" for audio and video. LINE uses this param verbatim as the length shown in the client (it does not recompute it from the uploaded m4a/mp4), so every voice/video message was displayed as ~1.9s regardless of its real length. Add an optional `durationMs` argument and use it, keeping 1919 as the fallback so existing callers are unaffected. * feat(square): add SquareChat sendImage/sendVideo/sendAudio helpers OpenChat rejects the "reserve an empty IMAGE/AUDIO/VIDEO message via sendMessage, then attach the obs object to its id" pattern with ILLEGAL_ARGUMENT. Uploading the obs object with no oid ("reqseq" mode) makes the server create the message itself; a consequence is that such a message can't carry a relatedMessageId, so media sent this way is never threaded as a reply. Depends on #205 for the durationMs argument that carries the real audio/video length.
1 parent 0123866 commit d4728d7

2 files changed

Lines changed: 61 additions & 1 deletion

File tree

packages/linejs/base/obs/mod.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ export class LineObs {
157157
data: Blob,
158158
oid?: string,
159159
filename?: string,
160+
durationMs?: number,
160161
): Promise<{
161162
objId: string;
162163
objHash: string;
@@ -195,7 +196,10 @@ export class LineObs {
195196
param.cat = "original";
196197
param.type = "image";
197198
} else if (type === "audio" || type === "video") {
198-
param.duration = "1919"; // 810
199+
// LINE uses this obs param verbatim as the displayed length (it does not
200+
// recompute it from the uploaded file), so a caller-supplied real duration
201+
// must be honoured; keep the historical value as the fallback.
202+
param.duration = (durationMs ?? 1919).toString();
199203
}
200204
const toType: "talk" | "g2" = to[0] === "m" || to[0] === "t"
201205
? "g2"

packages/linejs/client/features/square/mod.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type {
55
import type * as LINETypes from "@evex/linejs-types";
66
import type { Client } from "../../mod.ts";
77
import { continueRequest } from "../../../base/mod.ts";
8+
import type { ObjType } from "../../../base/obs/mod.ts";
89
import { SquareMessage } from "../message/mod.ts";
910
import { TypedEventEmitter } from "../../../base/core/typed-event-emitter/index.ts";
1011

@@ -109,6 +110,61 @@ export class SquareChat extends TypedEventEmitter<SquareChatEvents> {
109110
});
110111
}
111112

113+
/**
114+
* Sends an image to this OpenChat.
115+
*
116+
* OpenChat rejects the "reserve an empty IMAGE/AUDIO/VIDEO message via
117+
* `sendMessage`, then attach the obs object to its id" pattern with
118+
* ILLEGAL_ARGUMENT. Uploading the obs object with no `oid` ("reqseq" mode)
119+
* instead makes the server create the message itself. A consequence is that
120+
* such a message can't carry a `relatedMessageId`, so media sent this way is
121+
* never threaded as a reply.
122+
*/
123+
async sendImage(
124+
data: Blob,
125+
filename = "image",
126+
): Promise<{ objId: string; objHash: string }> {
127+
return await this.#sendMedia("image", data, filename);
128+
}
129+
130+
/** Sends a video to this OpenChat. See {@link sendImage} for the caveats. */
131+
async sendVideo(
132+
data: Blob,
133+
filename = "video",
134+
durationMs?: number,
135+
): Promise<{ objId: string; objHash: string }> {
136+
return await this.#sendMedia("video", data, filename, durationMs);
137+
}
138+
139+
/**
140+
* Sends a voice/audio message to this OpenChat.
141+
* See {@link sendImage} for the caveats.
142+
*/
143+
async sendAudio(
144+
data: Blob,
145+
filename = "voice.m4a",
146+
durationMs?: number,
147+
): Promise<{ objId: string; objHash: string }> {
148+
return await this.#sendMedia("audio", data, filename, durationMs);
149+
}
150+
151+
async #sendMedia(
152+
type: ObjType,
153+
data: Blob,
154+
filename: string,
155+
durationMs?: number,
156+
): Promise<{ objId: string; objHash: string }> {
157+
const { objId, objHash } = await this.#client.base.obs.uploadObjTalk(
158+
this.raw.squareChatMid,
159+
type,
160+
data,
161+
undefined, // oid unset => reqseq mode => the server creates the message
162+
filename,
163+
durationMs,
164+
);
165+
return { objId, objHash };
166+
}
167+
112168
async updateSquareChat(
113169
input: {
114170
updatedAttrs: LINETypes.SquareChatAttribute[];

0 commit comments

Comments
 (0)