@mapier/imsg-sdk 0.6.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of @mapier/imsg-sdk might be problematic. Click here for more details.
- package/dist/gateway/fake.d.ts +8 -2
- package/dist/gateway/fake.js +72 -3
- package/dist/gateway/fake.js.map +1 -1
- package/dist/gateway/imsg.d.ts +7 -1
- package/dist/gateway/imsg.js +38 -2
- package/dist/gateway/imsg.js.map +1 -1
- package/dist/gateway/multipart.d.ts +38 -0
- package/dist/gateway/multipart.js +216 -0
- package/dist/gateway/multipart.js.map +1 -0
- package/dist/gateway/sticker.d.ts +13 -0
- package/dist/gateway/sticker.js +115 -0
- package/dist/gateway/sticker.js.map +1 -0
- package/dist/gateway/types.d.ts +30 -0
- package/dist/gateway/types.js +5 -0
- package/dist/gateway/types.js.map +1 -1
- package/dist/imsg/rpc.d.ts +6 -3
- package/dist/imsg/rpc.js +18 -0
- package/dist/imsg/rpc.js.map +1 -1
- package/dist/imsg/status.js +21 -0
- package/dist/imsg/status.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/docs/api.md +1 -1
- package/docs/capability-matrix.md +2 -0
- package/docs/gateway-contract.md +272 -2
- package/package.json +1 -1
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { GatewayCapabilities, MultipartPart, MultipartResult, NumericTextFormatRange } from './types.js';
|
|
2
|
+
export declare const MULTIPART_MAX_PARTS = 100;
|
|
3
|
+
export declare const MULTIPART_MAX_FILE_PARTS = 40;
|
|
4
|
+
export declare const MULTIPART_PROVEN_REJECTIONS: readonly string[];
|
|
5
|
+
export declare function classifyMultipartFireFailure(err: unknown): MultipartResult;
|
|
6
|
+
export type MultipartWirePart = {
|
|
7
|
+
type: 'text';
|
|
8
|
+
text: string;
|
|
9
|
+
text_formatting?: NumericTextFormatRange[];
|
|
10
|
+
} | {
|
|
11
|
+
type: 'file';
|
|
12
|
+
file: string;
|
|
13
|
+
};
|
|
14
|
+
export interface MultipartWireParams {
|
|
15
|
+
chat_id: number;
|
|
16
|
+
parts: MultipartWirePart[];
|
|
17
|
+
reply_to?: string;
|
|
18
|
+
effect?: string;
|
|
19
|
+
subject?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface MultipartSendResponse {
|
|
22
|
+
ok: boolean;
|
|
23
|
+
guid?: string;
|
|
24
|
+
message_id?: string;
|
|
25
|
+
parts_count?: number;
|
|
26
|
+
}
|
|
27
|
+
export interface MultipartSendOptions {
|
|
28
|
+
replyToGuid?: string;
|
|
29
|
+
effect?: string;
|
|
30
|
+
subject?: string;
|
|
31
|
+
}
|
|
32
|
+
export declare function multipartRefusalReason(parts: readonly MultipartPart[]): string | null;
|
|
33
|
+
export declare function multipartPreflight(capabilities: Pick<GatewayCapabilities, 'multipartSend' | 'mentionFormatting'>, parts: readonly MultipartPart[]): {
|
|
34
|
+
result: MultipartResult;
|
|
35
|
+
reason: string;
|
|
36
|
+
} | null;
|
|
37
|
+
export declare function multipartWireParams(chatId: number, parts: readonly MultipartPart[], opts?: MultipartSendOptions): MultipartWireParams;
|
|
38
|
+
export declare function multipartResultFromResponse(response: MultipartSendResponse): MultipartResult;
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { bridgeEffectStarted } from './chat-background.js';
|
|
2
|
+
import { hasMentionRange, multipartFailure, resolveTextFormatting, textFormattingRefusal } from './types.js';
|
|
3
|
+
// What a Mac will ever accept in one multipart message, mirroring the native
|
|
4
|
+
// caps exactly (`MultipartLimits` in MultipartParts.swift and
|
|
5
|
+
// `kMaxMultipartParts` / `kMaxMultipartFileParts` in IMsgInjected.m). Checked
|
|
6
|
+
// here so an over-long list is a proven no-op instead of a round trip.
|
|
7
|
+
//
|
|
8
|
+
// The native side also caps the file parts' TOTAL BYTES at 100 MiB
|
|
9
|
+
// (`kMaxMultipartFileBytes`). That one is deliberately NOT mirrored: this SDK
|
|
10
|
+
// never stats a file — it passes the caller's path through, exactly as
|
|
11
|
+
// `sendAttachment` does — so the byte cap stays the Mac's refusal, classified
|
|
12
|
+
// through MULTIPART_PROVEN_REJECTIONS below.
|
|
13
|
+
export const MULTIPART_MAX_PARTS = 100;
|
|
14
|
+
export const MULTIPART_MAX_FILE_PARTS = 40;
|
|
15
|
+
// handleSendMultipart's `errorResponse` returns BEFORE `dispatchAttempted`
|
|
16
|
+
// (IMsgInjected.m), plus the formatting refusals it forwards from
|
|
17
|
+
// `buildFormattedAttributed` (IMsgMentionFormatting.m), matched against
|
|
18
|
+
// `error.data` exactly as the other bridge verbs match theirs. Several are
|
|
19
|
+
// formatted with the part index in the middle ("Part 3 file not found: …"), so
|
|
20
|
+
// the needles are the stable fragments rather than prefixes.
|
|
21
|
+
//
|
|
22
|
+
// Deliberately absent: `send-multipart failed:` — that wraps an NSException
|
|
23
|
+
// caught around the whole build-and-dispatch block, so the text cannot prove
|
|
24
|
+
// which side of `dispatchIMMessageInChat` it was raised on and the result stays
|
|
25
|
+
// ambiguous.
|
|
26
|
+
export const MULTIPART_PROVEN_REJECTIONS = [
|
|
27
|
+
'Missing chatGuid',
|
|
28
|
+
'Missing or empty parts array',
|
|
29
|
+
'Too many parts',
|
|
30
|
+
'Too many file parts',
|
|
31
|
+
'File parts total too many bytes',
|
|
32
|
+
'isAudioMessage is not supported on a multipart message',
|
|
33
|
+
// Formatted with the guid appended, so the match has to be a fragment.
|
|
34
|
+
'Chat not found',
|
|
35
|
+
'is not an object',
|
|
36
|
+
'has both text and filePath',
|
|
37
|
+
'has neither text nor filePath',
|
|
38
|
+
'file not found',
|
|
39
|
+
'symlinked attachment paths are not allowed',
|
|
40
|
+
'attachment path traverses a symlink',
|
|
41
|
+
'transfer registered without guid',
|
|
42
|
+
'Could not register attachment transfer',
|
|
43
|
+
'Could not construct multipart IMMessage',
|
|
44
|
+
// Pass 1 validates every text part's formatting against the live chat before
|
|
45
|
+
// a single transfer is prepared, so these are pre-staging refusals here even
|
|
46
|
+
// though sendRich meets them at its own fire time.
|
|
47
|
+
'Invalid text formatting',
|
|
48
|
+
'Mention identifier',
|
|
49
|
+
'Mention range',
|
|
50
|
+
'Mention formatting requires',
|
|
51
|
+
'Confirmed mention formatting is unavailable',
|
|
52
|
+
];
|
|
53
|
+
// The send itself threw. `refused` only when the error proves the helper
|
|
54
|
+
// stopped before IMChat was invoked; everything else — a bridge timeout, an
|
|
55
|
+
// exception raised after the dispatch, a dead rpc child — may already have put
|
|
56
|
+
// the message in the thread.
|
|
57
|
+
export function classifyMultipartFireFailure(err) {
|
|
58
|
+
return multipartFailure(bridgeEffectStarted(err, MULTIPART_PROVEN_REJECTIONS) ? 'fire-failed' : 'refused');
|
|
59
|
+
}
|
|
60
|
+
// The declared types do not run at runtime, so the list is validated the way
|
|
61
|
+
// the native handler reads it — as values of unknown shape — rather than as the
|
|
62
|
+
// union TypeScript promises. An untyped JS caller can send a null entry, a
|
|
63
|
+
// `textFormatting` on a file part, or a single range object where an array
|
|
64
|
+
// belongs, and each of those must refuse rather than throw.
|
|
65
|
+
function isObject(value) {
|
|
66
|
+
return value !== null && typeof value === 'object';
|
|
67
|
+
}
|
|
68
|
+
// The native side reads an empty string as "absent" on both part keys
|
|
69
|
+
// (`.nonEmpty`), so "carries both halves" means both are really there.
|
|
70
|
+
function isNonEmptyString(value) {
|
|
71
|
+
return typeof value === 'string' && value !== '';
|
|
72
|
+
}
|
|
73
|
+
// True when any part asks for a confirmed mention, tolerating parts this is
|
|
74
|
+
// asked about before they have been validated.
|
|
75
|
+
function mentionsSomeone(parts) {
|
|
76
|
+
return parts.some((part) => {
|
|
77
|
+
if (!isObject(part))
|
|
78
|
+
return false;
|
|
79
|
+
const ranges = part.textFormatting;
|
|
80
|
+
return Array.isArray(ranges) && hasMentionRange(ranges);
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
// Every rule the native side enforces, checked here so a bad list is a proven
|
|
84
|
+
// no-op instead of a round trip. Returns the line to log, or null.
|
|
85
|
+
//
|
|
86
|
+
// One native check is deliberately NOT mirrored: that each file part's path
|
|
87
|
+
// exists, is not a symlink and is inside the byte budget. This SDK never
|
|
88
|
+
// touches the filesystem — the fake runs where the Mac's files are not, and the
|
|
89
|
+
// real gateway's path is the MAC's path — so a local `existsSync` would make
|
|
90
|
+
// the fake and the real path disagree in both directions. A missing file is the
|
|
91
|
+
// Mac's refusal, classified through MULTIPART_PROVEN_REJECTIONS.
|
|
92
|
+
export function multipartRefusalReason(parts) {
|
|
93
|
+
const rawParts = parts;
|
|
94
|
+
if (!Array.isArray(rawParts))
|
|
95
|
+
return 'parts must be an array';
|
|
96
|
+
if (rawParts.length === 0)
|
|
97
|
+
return 'parts must not be empty';
|
|
98
|
+
if (rawParts.length > MULTIPART_MAX_PARTS) {
|
|
99
|
+
return `too many parts: ${rawParts.length} (max ${MULTIPART_MAX_PARTS})`;
|
|
100
|
+
}
|
|
101
|
+
const fileParts = rawParts.filter((part) => isObject(part) && part.kind === 'file').length;
|
|
102
|
+
if (fileParts > MULTIPART_MAX_FILE_PARTS) {
|
|
103
|
+
return `too many file parts: ${fileParts} (max ${MULTIPART_MAX_FILE_PARTS})`;
|
|
104
|
+
}
|
|
105
|
+
for (const [index, part] of rawParts.entries()) {
|
|
106
|
+
if (!isObject(part))
|
|
107
|
+
return `part ${index} must be an object`;
|
|
108
|
+
const kind = part.kind;
|
|
109
|
+
// A part carrying BOTH halves is refused before its `kind` is consulted,
|
|
110
|
+
// never resolved by the label: whichever branch ran would drop the other
|
|
111
|
+
// key without a word, and losing part content in silence is the one failure
|
|
112
|
+
// this verb exists to remove — it must not come back in through a caller
|
|
113
|
+
// that labels a part wrong. The native handler refuses it by index for the
|
|
114
|
+
// same reason.
|
|
115
|
+
if (isNonEmptyString(part.text) && isNonEmptyString(part.filePath)) {
|
|
116
|
+
return `part ${index}: has both text and filePath`;
|
|
117
|
+
}
|
|
118
|
+
if (kind === 'file') {
|
|
119
|
+
// Formatting on a file part is an error on the native side rather than
|
|
120
|
+
// something dropped, because a dropped one would look like a send that
|
|
121
|
+
// did what was asked.
|
|
122
|
+
if (part.textFormatting !== undefined)
|
|
123
|
+
return `part ${index}: a file part cannot carry textFormatting`;
|
|
124
|
+
const filePath = part.filePath;
|
|
125
|
+
if (typeof filePath !== 'string' || filePath === '')
|
|
126
|
+
return `part ${index}: a file part needs a filePath`;
|
|
127
|
+
// The path crosses a line-delimited JSON-RPC protocol on its way to the
|
|
128
|
+
// Mac and is then handed to the filesystem, so neither a line break nor a
|
|
129
|
+
// NUL may ride along as if it were part of a name.
|
|
130
|
+
if (filePath.includes('\n') || filePath.includes('\r') || filePath.includes('\0')) {
|
|
131
|
+
return `part ${index}: filePath must not contain newlines or NUL`;
|
|
132
|
+
}
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
if (kind !== 'text')
|
|
136
|
+
return `part ${index}: unknown kind ${String(kind)}`;
|
|
137
|
+
const text = part.text;
|
|
138
|
+
if (typeof text !== 'string' || text === '')
|
|
139
|
+
return `part ${index}: a text part needs text`;
|
|
140
|
+
const ranges = part.textFormatting;
|
|
141
|
+
if (ranges === undefined)
|
|
142
|
+
continue;
|
|
143
|
+
if (!Array.isArray(ranges))
|
|
144
|
+
return `part ${index}: textFormatting must be an array of ranges`;
|
|
145
|
+
if (ranges.length === 0)
|
|
146
|
+
continue;
|
|
147
|
+
// The SAME validator sendRich uses, per part: the helper builds each part's
|
|
148
|
+
// attributed run on its own and silently DROPS a style range with bad
|
|
149
|
+
// offsets while sending the rest, which a caller cannot detect because
|
|
150
|
+
// formatting is write-only.
|
|
151
|
+
const refusal = textFormattingRefusal(text, ranges);
|
|
152
|
+
if (refusal)
|
|
153
|
+
return `part ${index}: ${refusal.reason}`;
|
|
154
|
+
}
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
// The whole sendMultipart preflight, shared by both gateways so parity is
|
|
158
|
+
// structural: the capability gates first, then the list rules. Returns the
|
|
159
|
+
// result to answer with plus a line to log, or null to fire. Nothing has been
|
|
160
|
+
// sent at any exit here, so every one of them is `effectStarted:false`.
|
|
161
|
+
export function multipartPreflight(capabilities, parts) {
|
|
162
|
+
if (!capabilities.multipartSend) {
|
|
163
|
+
return { result: multipartFailure('unsupported'), reason: 'bridge does not advertise multipartSend' };
|
|
164
|
+
}
|
|
165
|
+
const rawParts = parts;
|
|
166
|
+
if (!Array.isArray(rawParts)) {
|
|
167
|
+
return { result: multipartFailure('refused'), reason: 'parts must be an array' };
|
|
168
|
+
}
|
|
169
|
+
// A confirmed mention needs the helper's runtime-resolved attribute key, the
|
|
170
|
+
// same gate sendRich applies — without it the helper fails the whole send
|
|
171
|
+
// rather than degrading to plain text. `unsupported`, not `refused`, because
|
|
172
|
+
// no build on this host can do it (the extensionCardUpdate precedent).
|
|
173
|
+
if (mentionsSomeone(rawParts) && !capabilities.mentionFormatting) {
|
|
174
|
+
return { result: multipartFailure('unsupported'), reason: 'bridge does not advertise mentionFormatting' };
|
|
175
|
+
}
|
|
176
|
+
const refusal = multipartRefusalReason(parts);
|
|
177
|
+
return refusal ? { result: multipartFailure('refused'), reason: refusal } : null;
|
|
178
|
+
}
|
|
179
|
+
export function multipartWireParams(chatId, parts, opts = {}) {
|
|
180
|
+
return {
|
|
181
|
+
chat_id: chatId,
|
|
182
|
+
parts: parts.map((part) => part.kind === 'file'
|
|
183
|
+
? { type: 'file', file: part.filePath }
|
|
184
|
+
: {
|
|
185
|
+
type: 'text',
|
|
186
|
+
text: part.text,
|
|
187
|
+
// Effect names resolved to Apple's ids, the same shape send.rich
|
|
188
|
+
// takes; an empty array carries nothing and is left off.
|
|
189
|
+
...(part.textFormatting?.length ? { text_formatting: resolveTextFormatting(part.textFormatting) } : {}),
|
|
190
|
+
}),
|
|
191
|
+
// Message-wide, and read by the native handler exactly as send.rich reads
|
|
192
|
+
// them (`rpcReplyToParam`, `rpcExpressiveEffectParam`, `subject`).
|
|
193
|
+
...(opts.subject !== undefined ? { subject: opts.subject } : {}),
|
|
194
|
+
...(opts.effect ? { effect: opts.effect } : {}),
|
|
195
|
+
...(opts.replyToGuid ? { reply_to: opts.replyToGuid } : {}),
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
// Build the caller's result out of the host's response. Shared by both
|
|
199
|
+
// gateways, so neither can invent a field the other cannot report.
|
|
200
|
+
export function multipartResultFromResponse(response) {
|
|
201
|
+
// The native handler answers `ok:true` on every path that reaches a result
|
|
202
|
+
// and raises a JSON-RPC error otherwise, so a falsy `ok` is a host the SDK
|
|
203
|
+
// cannot read. It was invoked, so it is ambiguous, not a refusal.
|
|
204
|
+
if (!response.ok)
|
|
205
|
+
return multipartFailure('fire-failed');
|
|
206
|
+
const guid = response.guid || response.message_id || undefined;
|
|
207
|
+
return {
|
|
208
|
+
ok: true,
|
|
209
|
+
...(guid ? { guid } : {}),
|
|
210
|
+
// The host's own count of the parts it built, never the caller's list
|
|
211
|
+
// length: a count shorter than the list is how a helper that dropped file
|
|
212
|
+
// parts shows up, and inventing the number would hide exactly that.
|
|
213
|
+
...(typeof response.parts_count === 'number' ? { partCount: response.parts_count } : {}),
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
//# sourceMappingURL=multipart.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"multipart.js","sourceRoot":"","sources":["../../src/gateway/multipart.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { StickerSendResponse } from '../imsg/rpc.js';
|
|
2
|
+
import type { SendResult } from '../types.js';
|
|
3
|
+
import type { GatewayCapabilities, StickerPlacement } from './types.js';
|
|
4
|
+
export declare function stickerResultFromResponse(response: StickerSendResponse): SendResult;
|
|
5
|
+
export declare function stickerPlacementFields(placement: StickerPlacement | undefined): StickerPlacement | undefined;
|
|
6
|
+
export declare function stickerPreflight(capabilities: Pick<GatewayCapabilities, 'stickerPlacement'>, opts: {
|
|
7
|
+
attachToGuid?: string;
|
|
8
|
+
partIndex?: number;
|
|
9
|
+
placement?: StickerPlacement;
|
|
10
|
+
} | undefined): {
|
|
11
|
+
result: SendResult;
|
|
12
|
+
reason: string;
|
|
13
|
+
} | null;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
// Build the caller's result out of the host's response. Shared by both
|
|
2
|
+
// gateways, so neither can invent a field the other cannot report.
|
|
3
|
+
export function stickerResultFromResponse(response) {
|
|
4
|
+
// The native handler answers `ok:true` on every path that reaches a result
|
|
5
|
+
// and raises a JSON-RPC error otherwise, so a falsy `ok` is a host the SDK
|
|
6
|
+
// cannot read. It was invoked, so nothing here claims the send was refused.
|
|
7
|
+
if (!response.ok)
|
|
8
|
+
return { ok: false };
|
|
9
|
+
// `guid` and `message_id` carry the same MESSAGE guid, so `message_id` is the
|
|
10
|
+
// fallback when only it is reported. `transfer_guid` is deliberately NOT read:
|
|
11
|
+
// it names the upload, not the row, and no consumer asks for it.
|
|
12
|
+
const guid = response.guid || response.message_id || undefined;
|
|
13
|
+
// There is deliberately no numeric `id`: the native result sets `message_id`
|
|
14
|
+
// to the message GUID, so no rowid exists on the real path — the same reason
|
|
15
|
+
// `sendMultipart` has none. A guid Messages never exposed is left absent
|
|
16
|
+
// rather than invented; the sticker still went.
|
|
17
|
+
return { ok: true, ...(guid ? { guid } : {}) };
|
|
18
|
+
}
|
|
19
|
+
// The four placement knobs, in the order the contract lists them.
|
|
20
|
+
const PLACEMENT_FIELDS = ['x', 'y', 'scale', 'rotation'];
|
|
21
|
+
// SANITY bounds, deliberately far wider than any sensible placement and equal
|
|
22
|
+
// to the helper's own. The PRODUCT range (x,y -5…6, scale 0.1…5, rotation
|
|
23
|
+
// ±180) belongs at the `/v1` door, which is one service and can be loosened
|
|
24
|
+
// without a fleet-wide Mac release; these exist only so a number that could
|
|
25
|
+
// never have been meant — a pixel count, a radian mistaken for a degree, a
|
|
26
|
+
// stray exponent — is refused before it reaches a Mac. Out of bounds is
|
|
27
|
+
// REFUSED, never clamped: a silently moved sticker is indistinguishable from
|
|
28
|
+
// one the caller placed, and the phone has no clamp of its own to imitate.
|
|
29
|
+
const PLACEMENT_BOUNDS = {
|
|
30
|
+
x: [-100, 100],
|
|
31
|
+
y: [-100, 100],
|
|
32
|
+
scale: [0.01, 20],
|
|
33
|
+
rotation: [-3600, 3600],
|
|
34
|
+
};
|
|
35
|
+
function isPlacementField(key) {
|
|
36
|
+
return PLACEMENT_FIELDS.includes(key);
|
|
37
|
+
}
|
|
38
|
+
// Only the fields the caller actually gave, or `undefined` when none were —
|
|
39
|
+
// which is also what an empty `placement` object means. An omitted field is
|
|
40
|
+
// NOT defaulted here: the helper owns the defaults (centre, normal size, no
|
|
41
|
+
// rotation), so a frame carrying only what was asked for is the one thing that
|
|
42
|
+
// keeps "no placement" byte-identical to the request every deployed helper has
|
|
43
|
+
// always answered.
|
|
44
|
+
export function stickerPlacementFields(placement) {
|
|
45
|
+
if (!placement)
|
|
46
|
+
return undefined;
|
|
47
|
+
const given = {};
|
|
48
|
+
for (const field of PLACEMENT_FIELDS) {
|
|
49
|
+
const value = placement[field];
|
|
50
|
+
if (value !== undefined)
|
|
51
|
+
given[field] = value;
|
|
52
|
+
}
|
|
53
|
+
return Object.keys(given).length > 0 ? given : undefined;
|
|
54
|
+
}
|
|
55
|
+
function refuse(refused, reason) {
|
|
56
|
+
return { result: { ok: false, refused }, reason };
|
|
57
|
+
}
|
|
58
|
+
// The whole sendSticker preflight, shared by both gateways. Returns the result
|
|
59
|
+
// to answer with plus a line to log, or null to fire. Nothing has been sent at
|
|
60
|
+
// any exit here, and every one of them carries a typed `refused` so a consumer
|
|
61
|
+
// can tell a proven no-op from a send that failed in flight.
|
|
62
|
+
//
|
|
63
|
+
// Order: the placement object has to be READABLE before we can tell whether a
|
|
64
|
+
// placement was asked for at all, so malformed shape is caught first; after
|
|
65
|
+
// that the capability gate comes before the value rules, the way
|
|
66
|
+
// `multipartPreflight` puts its gates first.
|
|
67
|
+
export function stickerPreflight(capabilities, opts) {
|
|
68
|
+
const raw = opts?.placement;
|
|
69
|
+
if (raw === undefined)
|
|
70
|
+
return null;
|
|
71
|
+
if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {
|
|
72
|
+
return refuse('sticker-placement-shape', 'placement must be an object');
|
|
73
|
+
}
|
|
74
|
+
// An unknown key is refused rather than dropped, and BEFORE the empty check:
|
|
75
|
+
// `{rotate: 15}` is a caller who meant to place the sticker, and quietly
|
|
76
|
+
// sending it centred would look exactly like a placement that worked.
|
|
77
|
+
const unknown = Object.keys(raw).filter((key) => !isPlacementField(key));
|
|
78
|
+
if (unknown.length > 0) {
|
|
79
|
+
return refuse('sticker-placement-shape', `placement has unknown field(s): ${unknown.join(', ')}`);
|
|
80
|
+
}
|
|
81
|
+
const given = stickerPlacementFields(raw);
|
|
82
|
+
// `{}` — or a placement whose every field is undefined — asks for nothing, so
|
|
83
|
+
// it is not a placed send at all: no gate, no target rule, no wire key.
|
|
84
|
+
if (given === undefined)
|
|
85
|
+
return null;
|
|
86
|
+
// The marker gates a loud rejection, not a silent loss (unlike `tapbackPart`
|
|
87
|
+
// and `multipartSend`): a helper without it answers `-32602` and sends
|
|
88
|
+
// nothing. Refusing here turns that round trip into a typed answer, and lets
|
|
89
|
+
// a host announce placement support to its own backend.
|
|
90
|
+
if (!capabilities.stickerPlacement) {
|
|
91
|
+
return refuse('sticker-placement-capability', 'bridge does not advertise stickerPlacement');
|
|
92
|
+
}
|
|
93
|
+
// Every sticker a phone produces is tied to a message and a part — the six
|
|
94
|
+
// placed by hand on a real phone included, and including the ones dropped in
|
|
95
|
+
// empty space well off a bubble. "Off the bubble" is an offset from a target,
|
|
96
|
+
// never the absence of one, so a placement with nothing to measure against is
|
|
97
|
+
// a caller error rather than a free-standing sticker.
|
|
98
|
+
if (opts?.attachToGuid === undefined || opts.attachToGuid === '') {
|
|
99
|
+
return refuse('sticker-placement-target', 'placement needs the message it sits on (attachToGuid)');
|
|
100
|
+
}
|
|
101
|
+
for (const field of PLACEMENT_FIELDS) {
|
|
102
|
+
const value = given[field];
|
|
103
|
+
if (value === undefined)
|
|
104
|
+
continue;
|
|
105
|
+
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
|
106
|
+
return refuse('sticker-placement-shape', `placement.${field} must be a finite number`);
|
|
107
|
+
}
|
|
108
|
+
const [min, max] = PLACEMENT_BOUNDS[field];
|
|
109
|
+
if (value < min || value > max) {
|
|
110
|
+
return refuse('sticker-placement-range', `placement.${field} ${value} is outside ${min}…${max}`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=sticker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sticker.js","sourceRoot":"","sources":["../../src/gateway/sticker.ts"],"names":[],"mappings":"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"}
|
package/dist/gateway/types.d.ts
CHANGED
|
@@ -74,6 +74,14 @@ export interface GatewayCapabilities {
|
|
|
74
74
|
locationRequest: boolean;
|
|
75
75
|
extensionCardSend: boolean;
|
|
76
76
|
extensionCardUpdate: boolean;
|
|
77
|
+
multipartSend: boolean;
|
|
78
|
+
stickerPlacement: boolean;
|
|
79
|
+
}
|
|
80
|
+
export interface StickerPlacement {
|
|
81
|
+
x?: number;
|
|
82
|
+
y?: number;
|
|
83
|
+
scale?: number;
|
|
84
|
+
rotation?: number;
|
|
77
85
|
}
|
|
78
86
|
export declare const PATCHED_GATEWAY_CAPABILITIES: GatewayCapabilities;
|
|
79
87
|
export interface SharedLocation {
|
|
@@ -162,6 +170,22 @@ export interface ExtensionCardResult {
|
|
|
162
170
|
reason?: GroupMutationFailure;
|
|
163
171
|
}
|
|
164
172
|
export declare function extensionCardFailure(reason: GroupMutationFailure): ExtensionCardResult;
|
|
173
|
+
export type MultipartPart = {
|
|
174
|
+
kind: 'text';
|
|
175
|
+
text: string;
|
|
176
|
+
textFormatting?: TextFormatRange[];
|
|
177
|
+
} | {
|
|
178
|
+
kind: 'file';
|
|
179
|
+
filePath: string;
|
|
180
|
+
};
|
|
181
|
+
export interface MultipartResult {
|
|
182
|
+
ok: boolean;
|
|
183
|
+
guid?: string;
|
|
184
|
+
partCount?: number;
|
|
185
|
+
effectStarted?: boolean;
|
|
186
|
+
reason?: GroupMutationFailure;
|
|
187
|
+
}
|
|
188
|
+
export declare function multipartFailure(reason: GroupMutationFailure): MultipartResult;
|
|
165
189
|
export declare const GROUP_MIN_MEMBERS = 3;
|
|
166
190
|
export declare function removalWouldBreakGroupMinimum(participantCount: number): boolean;
|
|
167
191
|
export declare const TIER2_HISTORY_SCAN_WINDOW = 30;
|
|
@@ -273,6 +297,7 @@ export interface Gateway {
|
|
|
273
297
|
sendSticker(chatId: number, filePath: string, opts?: {
|
|
274
298
|
attachToGuid?: string;
|
|
275
299
|
partIndex?: number;
|
|
300
|
+
placement?: StickerPlacement;
|
|
276
301
|
}): Promise<SendResult>;
|
|
277
302
|
sendPoll(chatId: number, question: string, options: string[]): Promise<SendResult>;
|
|
278
303
|
votePoll(chatId: number, pollGuid: string, optionId: string): Promise<ReactResult>;
|
|
@@ -280,6 +305,11 @@ export interface Gateway {
|
|
|
280
305
|
sendExtensionCard(chatId: number, card: ExtensionCard, opts?: {
|
|
281
306
|
update?: ExtensionCardHandle;
|
|
282
307
|
}): Promise<ExtensionCardResult>;
|
|
308
|
+
sendMultipart(chatId: number, parts: readonly MultipartPart[], opts?: {
|
|
309
|
+
replyToGuid?: string;
|
|
310
|
+
effect?: string;
|
|
311
|
+
subject?: string;
|
|
312
|
+
}): Promise<MultipartResult>;
|
|
283
313
|
editMessage(chatId: number, targetGuid: string, text: string): Promise<EditMessageResult>;
|
|
284
314
|
unsendMessage(chatId: number, targetGuid: string): Promise<MessageRemovalResult>;
|
|
285
315
|
deleteMessage(chatId: number, targetGuid: string): Promise<MessageRemovalResult>;
|
package/dist/gateway/types.js
CHANGED
|
@@ -22,6 +22,8 @@ export const PATCHED_GATEWAY_CAPABILITIES = {
|
|
|
22
22
|
locationRequest: true,
|
|
23
23
|
extensionCardSend: true,
|
|
24
24
|
extensionCardUpdate: true,
|
|
25
|
+
multipartSend: true,
|
|
26
|
+
stickerPlacement: true,
|
|
25
27
|
};
|
|
26
28
|
export const LOCATION_SHARE_EVENTS = ['location_update', 'share_started', 'share_ended'];
|
|
27
29
|
// The only preset the native Set pipeline supports today (Mapier-Labs/imsg#12,
|
|
@@ -54,6 +56,9 @@ export function messageRemovalFailure(reason) {
|
|
|
54
56
|
export function extensionCardFailure(reason) {
|
|
55
57
|
return { ok: false, effectStarted: GROUP_MUTATION_EFFECT_STARTED[reason], reason };
|
|
56
58
|
}
|
|
59
|
+
export function multipartFailure(reason) {
|
|
60
|
+
return { ok: false, effectStarted: GROUP_MUTATION_EFFECT_STARTED[reason], reason };
|
|
61
|
+
}
|
|
57
62
|
// Apple keeps a group conversation at three or more members, the local user
|
|
58
63
|
// included: a removal that would drop it below that is silently refused —
|
|
59
64
|
// IMCore's `removeParticipants:reason:` returns normally and the membership
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/gateway/types.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIhD,iEAAiE;AACjE,6EAA6E;AAC7E,6CAA6C;AAC7C,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,CAAU,CAAC;AAG/F,MAAM,eAAe,GAAG,IAAI,GAAG,CAAS,SAAS,CAAC,CAAC;AAEnD,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,OAAO,CAAC,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAwB,CAAC;AAC9E,CAAC;
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/gateway/types.ts"],"names":[],"mappings":"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"}
|
package/dist/imsg/rpc.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ExtensionCardSendResponse, ExtensionCardWireParams } from '../gateway/extension-card.js';
|
|
2
|
+
import type { MultipartSendResponse, MultipartWireParams } from '../gateway/multipart.js';
|
|
2
3
|
import { type LocationsReadWire } from '../gateway/locations.js';
|
|
3
|
-
import type { LocationUpdate, MessageService, NumericTextFormatRange, Reaction } from '../gateway/types.js';
|
|
4
|
+
import type { LocationUpdate, MessageService, NumericTextFormatRange, Reaction, StickerPlacement } from '../gateway/types.js';
|
|
4
5
|
import type { ImsgMessage, MessagePoll, SendResult } from '../types.js';
|
|
5
6
|
export declare class ImsgRpcError extends Error {
|
|
6
7
|
readonly code: number;
|
|
@@ -62,9 +63,9 @@ export interface HandleCheckResponse {
|
|
|
62
63
|
}
|
|
63
64
|
export interface StickerSendResponse {
|
|
64
65
|
ok: boolean;
|
|
65
|
-
transfer_guid: string;
|
|
66
66
|
guid?: string;
|
|
67
|
-
message_id?:
|
|
67
|
+
message_id?: string;
|
|
68
|
+
transfer_guid?: string;
|
|
68
69
|
}
|
|
69
70
|
export interface CreateChatResponse {
|
|
70
71
|
ok: boolean;
|
|
@@ -164,6 +165,7 @@ export declare class ImsgRpc {
|
|
|
164
165
|
audio?: boolean;
|
|
165
166
|
replyToGuid?: string;
|
|
166
167
|
}): Promise<SendResult>;
|
|
168
|
+
sendMultipart(params: MultipartWireParams): Promise<MultipartSendResponse>;
|
|
167
169
|
messageSendStatus(guid: string): Promise<SendStatusResponse>;
|
|
168
170
|
checkHandle(address: string, opts?: {
|
|
169
171
|
aliasType?: 'phone' | 'email';
|
|
@@ -171,6 +173,7 @@ export declare class ImsgRpc {
|
|
|
171
173
|
sendSticker(chatId: number, filePath: string, opts?: {
|
|
172
174
|
attachToGuid?: string;
|
|
173
175
|
partIndex?: number;
|
|
176
|
+
placement?: StickerPlacement;
|
|
174
177
|
}): Promise<StickerSendResponse>;
|
|
175
178
|
sendPoll(chatId: number, question: string, options: string[]): Promise<PollSendResponse>;
|
|
176
179
|
votePoll(chatId: number, pollGuid: string, optionId: string): Promise<{
|
package/dist/imsg/rpc.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
2
|
import { createInterface } from 'node:readline';
|
|
3
3
|
import { locationUpdateFromWire } from '../gateway/locations.js';
|
|
4
|
+
import { stickerPlacementFields } from '../gateway/sticker.js';
|
|
4
5
|
import { reportRuntimeIssue } from '../runtime-report.js';
|
|
5
6
|
import { imsgBinary } from './binary.js';
|
|
6
7
|
export class ImsgRpcError extends Error {
|
|
@@ -238,6 +239,14 @@ export class ImsgRpc {
|
|
|
238
239
|
...(opts.replyToGuid ? { reply_to: opts.replyToGuid } : {}),
|
|
239
240
|
});
|
|
240
241
|
}
|
|
242
|
+
// `send.multipart` (RPCServer+MultipartHandler.swift): one message built from
|
|
243
|
+
// the parts in the order given, with the message-wide effect/subject/reply
|
|
244
|
+
// read exactly as `send.rich` reads them. The params are already in the
|
|
245
|
+
// native spelling — see `multipartWireParams` for why `type` is explicit on
|
|
246
|
+
// every part.
|
|
247
|
+
sendMultipart(params) {
|
|
248
|
+
return this.request('send.multipart', params);
|
|
249
|
+
}
|
|
241
250
|
// Wire shape transcribed from the Mapier fork's docs/rpc.md
|
|
242
251
|
// `message.send_status` response (mapier/macos26-tier2).
|
|
243
252
|
messageSendStatus(guid) {
|
|
@@ -254,12 +263,21 @@ export class ImsgRpc {
|
|
|
254
263
|
// Wire shape transcribed from the Mapier fork's docs/rpc.md
|
|
255
264
|
// `send.sticker` response (mapier/macos26-tier2). part_index is invalid
|
|
256
265
|
// without attach_to, so it is omitted unless both are supplied.
|
|
266
|
+
//
|
|
267
|
+
// `placement` carries only the fields the caller gave, as plain JSON numbers
|
|
268
|
+
// in the public units (M2B-56); the helper fills in the rest and converts to
|
|
269
|
+
// Apple's own. A send with no placement — or an empty one — omits the key
|
|
270
|
+
// ENTIRELY rather than sending `{}`: the helper's param allowlist is strict,
|
|
271
|
+
// so an unexpected key is a rejected request, and the frame must stay
|
|
272
|
+
// byte-identical to the one every deployed helper already answers.
|
|
257
273
|
sendSticker(chatId, filePath, opts = {}) {
|
|
274
|
+
const placement = stickerPlacementFields(opts.placement);
|
|
258
275
|
return this.request('send.sticker', {
|
|
259
276
|
chat_id: chatId,
|
|
260
277
|
file: filePath,
|
|
261
278
|
...(opts.attachToGuid ? { attach_to: opts.attachToGuid } : {}),
|
|
262
279
|
...(opts.attachToGuid && opts.partIndex !== undefined ? { part_index: opts.partIndex } : {}),
|
|
280
|
+
...(placement ? { placement } : {}),
|
|
263
281
|
});
|
|
264
282
|
}
|
|
265
283
|
// Verified against openclaw/imsg 0.13.x source: `poll.send`
|