@open-apime/sdk 0.4.1 → 0.6.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.
@@ -0,0 +1,179 @@
1
+ /**
2
+ * The 12 event types the apime delivers. `ignore` is internal and never
3
+ * reaches a consumer, so it is not part of the union.
4
+ */
5
+ type WebhookEventType = "message" | "receipt" | "presence" | "chat_presence" | "reaction" | "contact_update" | "connected" | "disconnected" | "temporary_ban" | "restriction_lifted" | "contact_reachout_locked" | "unknown";
6
+ interface WebhookEnvelope<T extends WebhookEventType = WebhookEventType, P = unknown> {
7
+ id: string;
8
+ instanceId: string;
9
+ type: T;
10
+ payload: P;
11
+ createdAt: string;
12
+ }
13
+ /** Button of an interactive message. `call`, `copy`, `reply` and `url` are button kinds, not events. */
14
+ interface MessageButton {
15
+ id: string;
16
+ label: string;
17
+ type: "reply" | "url" | "copy" | "call";
18
+ url?: string;
19
+ code?: string;
20
+ phone?: string;
21
+ }
22
+ /** Row of a list section. */
23
+ interface MessageListRow {
24
+ id: string;
25
+ label: string;
26
+ description: string;
27
+ }
28
+ /** Section of a list message. */
29
+ interface MessageListSection {
30
+ title: string;
31
+ rows: MessageListRow[];
32
+ }
33
+ /**
34
+ * Interactive content of a received message. `kind` says how to render it:
35
+ * `buttons` carries `buttons`, `list` carries `sections`.
36
+ */
37
+ interface MessageInteractive {
38
+ kind: "buttons" | "list";
39
+ text?: string;
40
+ footer?: string;
41
+ buttons?: MessageButton[];
42
+ sections?: MessageListSection[];
43
+ }
44
+ /**
45
+ * What the contact picked when tapping a button or list row. `selectedId` and
46
+ * `selectedLabel` are the useful pair; `name` and `paramsJson` are the raw
47
+ * NativeFlow fields, kept for anything the normalizer did not extract.
48
+ */
49
+ interface MessageInteractiveReply {
50
+ selectedId?: string;
51
+ selectedLabel?: string;
52
+ name?: string;
53
+ paramsJson?: string;
54
+ }
55
+ interface MessagePayload {
56
+ from: string;
57
+ to: string;
58
+ /** Chat the message belongs to. Equals `to`, resolved to a phone number when possible. */
59
+ chatJID?: string;
60
+ /**
61
+ * Stable identity of the contact, emitted whenever known, even when the phone
62
+ * number was resolved. Use it to unify a contact instead of relying on the
63
+ * number, which changes.
64
+ */
65
+ lid?: string;
66
+ /** How the chat is addressed by WhatsApp: by phone number or by LID. */
67
+ addressingMode?: string;
68
+ isFromMe: boolean;
69
+ isGroup: boolean;
70
+ messageId: string;
71
+ timestamp: number;
72
+ pushName?: string;
73
+ /** Group only: who sent it inside the group. Both carry the same value. */
74
+ participant?: string;
75
+ author?: string;
76
+ /** Business accounts only, from the verified name certificate. */
77
+ verifiedName?: string;
78
+ issuer?: string;
79
+ text?: string;
80
+ mentionedJids?: string[];
81
+ mediaType?: "image" | "video" | "audio" | "document" | "sticker" | "location" | "contact";
82
+ mediaUrl?: string;
83
+ mimetype?: string;
84
+ caption?: string;
85
+ fileName?: string;
86
+ /** Size in bytes. */
87
+ fileSize?: number;
88
+ /** Audio and video: length in seconds. */
89
+ duration?: number;
90
+ /** Audio recorded as a voice note rather than an attached file. */
91
+ ptt?: boolean;
92
+ /** Video sent as a round "video note". */
93
+ isPtv?: boolean;
94
+ /** Location messages. */
95
+ latitude?: number;
96
+ longitude?: number;
97
+ address?: string;
98
+ /** Contact messages. `contactNumber` is the raw vCard, not a bare number. */
99
+ contactName?: string;
100
+ contactNumber?: string;
101
+ buttons?: MessageButton[];
102
+ /** Present when the message itself carries buttons or a list. */
103
+ interactive?: MessageInteractive;
104
+ /** Present when the message IS the contact's answer to a button or list. */
105
+ interactiveReply?: MessageInteractiveReply;
106
+ /**
107
+ * Present only when this event is an edit: the id of the message that was
108
+ * edited, and its new text. They always travel together, so checking one is
109
+ * enough to know it is an edit.
110
+ */
111
+ editedMessageId?: string;
112
+ editedText?: string;
113
+ }
114
+ /**
115
+ * A reaction added to, or removed from, a message. An empty `reactionEmoji`
116
+ * means the contact removed their reaction.
117
+ */
118
+ interface ReactionPayload {
119
+ from: string;
120
+ chatJID: string;
121
+ isFromMe: boolean;
122
+ isGroup: boolean;
123
+ timestamp: number;
124
+ pushName?: string;
125
+ /** Group only: who reacted. Both carry the same value. */
126
+ participant?: string;
127
+ author?: string;
128
+ /** The emoji, or an empty string when the reaction was removed. */
129
+ reactionEmoji: string;
130
+ /** Id of the message that was reacted to. */
131
+ reactionMessageId?: string;
132
+ }
133
+ interface ReceiptPayload {
134
+ messageIds: string[];
135
+ timestamp: number;
136
+ chat: string;
137
+ status: "read" | "delivered" | "played";
138
+ }
139
+ interface PresencePayload {
140
+ from: string;
141
+ unavailable: boolean;
142
+ lastSeen?: number;
143
+ }
144
+ interface ChatPresencePayload {
145
+ from: string;
146
+ chatJID: string;
147
+ state: "composing" | "paused";
148
+ media?: "audio";
149
+ }
150
+ interface ContactUpdatePayload {
151
+ jid: string;
152
+ username: string;
153
+ }
154
+ interface DisconnectedPayload {
155
+ reason?: string;
156
+ }
157
+ /** Same shape for the account ban and for the reach-out timelock. */
158
+ interface TemporaryBanPayload {
159
+ reason?: string;
160
+ code?: number;
161
+ active: boolean;
162
+ restrictedUntil?: string;
163
+ enforcementType?: string;
164
+ }
165
+ interface RestrictionLiftedPayload {
166
+ active: false;
167
+ enforcementType?: string;
168
+ restrictedUntil?: string;
169
+ }
170
+ interface ContactReachoutLockedPayload {
171
+ to: string;
172
+ reason: string;
173
+ detail: string;
174
+ code: number;
175
+ }
176
+ /** Discriminated by `type`, so a switch narrows the payload. */
177
+ type WebhookEvent = WebhookEnvelope<"message", MessagePayload> | WebhookEnvelope<"receipt", ReceiptPayload> | WebhookEnvelope<"presence", PresencePayload> | WebhookEnvelope<"chat_presence", ChatPresencePayload> | WebhookEnvelope<"reaction", ReactionPayload> | WebhookEnvelope<"contact_update", ContactUpdatePayload> | WebhookEnvelope<"connected", Record<string, unknown>> | WebhookEnvelope<"disconnected", DisconnectedPayload> | WebhookEnvelope<"temporary_ban", TemporaryBanPayload> | WebhookEnvelope<"restriction_lifted", RestrictionLiftedPayload> | WebhookEnvelope<"contact_reachout_locked", ContactReachoutLockedPayload> | WebhookEnvelope<"unknown", Record<string, unknown>>;
178
+
179
+ export type { ChatPresencePayload as C, DisconnectedPayload as D, MessageButton as M, PresencePayload as P, ReactionPayload as R, TemporaryBanPayload as T, WebhookEnvelope as W, ContactReachoutLockedPayload as a, ContactUpdatePayload as b, MessageInteractive as c, MessageInteractiveReply as d, MessageListRow as e, MessageListSection as f, MessagePayload as g, ReceiptPayload as h, RestrictionLiftedPayload as i, WebhookEvent as j, WebhookEventType as k };
@@ -0,0 +1,179 @@
1
+ /**
2
+ * The 12 event types the apime delivers. `ignore` is internal and never
3
+ * reaches a consumer, so it is not part of the union.
4
+ */
5
+ type WebhookEventType = "message" | "receipt" | "presence" | "chat_presence" | "reaction" | "contact_update" | "connected" | "disconnected" | "temporary_ban" | "restriction_lifted" | "contact_reachout_locked" | "unknown";
6
+ interface WebhookEnvelope<T extends WebhookEventType = WebhookEventType, P = unknown> {
7
+ id: string;
8
+ instanceId: string;
9
+ type: T;
10
+ payload: P;
11
+ createdAt: string;
12
+ }
13
+ /** Button of an interactive message. `call`, `copy`, `reply` and `url` are button kinds, not events. */
14
+ interface MessageButton {
15
+ id: string;
16
+ label: string;
17
+ type: "reply" | "url" | "copy" | "call";
18
+ url?: string;
19
+ code?: string;
20
+ phone?: string;
21
+ }
22
+ /** Row of a list section. */
23
+ interface MessageListRow {
24
+ id: string;
25
+ label: string;
26
+ description: string;
27
+ }
28
+ /** Section of a list message. */
29
+ interface MessageListSection {
30
+ title: string;
31
+ rows: MessageListRow[];
32
+ }
33
+ /**
34
+ * Interactive content of a received message. `kind` says how to render it:
35
+ * `buttons` carries `buttons`, `list` carries `sections`.
36
+ */
37
+ interface MessageInteractive {
38
+ kind: "buttons" | "list";
39
+ text?: string;
40
+ footer?: string;
41
+ buttons?: MessageButton[];
42
+ sections?: MessageListSection[];
43
+ }
44
+ /**
45
+ * What the contact picked when tapping a button or list row. `selectedId` and
46
+ * `selectedLabel` are the useful pair; `name` and `paramsJson` are the raw
47
+ * NativeFlow fields, kept for anything the normalizer did not extract.
48
+ */
49
+ interface MessageInteractiveReply {
50
+ selectedId?: string;
51
+ selectedLabel?: string;
52
+ name?: string;
53
+ paramsJson?: string;
54
+ }
55
+ interface MessagePayload {
56
+ from: string;
57
+ to: string;
58
+ /** Chat the message belongs to. Equals `to`, resolved to a phone number when possible. */
59
+ chatJID?: string;
60
+ /**
61
+ * Stable identity of the contact, emitted whenever known, even when the phone
62
+ * number was resolved. Use it to unify a contact instead of relying on the
63
+ * number, which changes.
64
+ */
65
+ lid?: string;
66
+ /** How the chat is addressed by WhatsApp: by phone number or by LID. */
67
+ addressingMode?: string;
68
+ isFromMe: boolean;
69
+ isGroup: boolean;
70
+ messageId: string;
71
+ timestamp: number;
72
+ pushName?: string;
73
+ /** Group only: who sent it inside the group. Both carry the same value. */
74
+ participant?: string;
75
+ author?: string;
76
+ /** Business accounts only, from the verified name certificate. */
77
+ verifiedName?: string;
78
+ issuer?: string;
79
+ text?: string;
80
+ mentionedJids?: string[];
81
+ mediaType?: "image" | "video" | "audio" | "document" | "sticker" | "location" | "contact";
82
+ mediaUrl?: string;
83
+ mimetype?: string;
84
+ caption?: string;
85
+ fileName?: string;
86
+ /** Size in bytes. */
87
+ fileSize?: number;
88
+ /** Audio and video: length in seconds. */
89
+ duration?: number;
90
+ /** Audio recorded as a voice note rather than an attached file. */
91
+ ptt?: boolean;
92
+ /** Video sent as a round "video note". */
93
+ isPtv?: boolean;
94
+ /** Location messages. */
95
+ latitude?: number;
96
+ longitude?: number;
97
+ address?: string;
98
+ /** Contact messages. `contactNumber` is the raw vCard, not a bare number. */
99
+ contactName?: string;
100
+ contactNumber?: string;
101
+ buttons?: MessageButton[];
102
+ /** Present when the message itself carries buttons or a list. */
103
+ interactive?: MessageInteractive;
104
+ /** Present when the message IS the contact's answer to a button or list. */
105
+ interactiveReply?: MessageInteractiveReply;
106
+ /**
107
+ * Present only when this event is an edit: the id of the message that was
108
+ * edited, and its new text. They always travel together, so checking one is
109
+ * enough to know it is an edit.
110
+ */
111
+ editedMessageId?: string;
112
+ editedText?: string;
113
+ }
114
+ /**
115
+ * A reaction added to, or removed from, a message. An empty `reactionEmoji`
116
+ * means the contact removed their reaction.
117
+ */
118
+ interface ReactionPayload {
119
+ from: string;
120
+ chatJID: string;
121
+ isFromMe: boolean;
122
+ isGroup: boolean;
123
+ timestamp: number;
124
+ pushName?: string;
125
+ /** Group only: who reacted. Both carry the same value. */
126
+ participant?: string;
127
+ author?: string;
128
+ /** The emoji, or an empty string when the reaction was removed. */
129
+ reactionEmoji: string;
130
+ /** Id of the message that was reacted to. */
131
+ reactionMessageId?: string;
132
+ }
133
+ interface ReceiptPayload {
134
+ messageIds: string[];
135
+ timestamp: number;
136
+ chat: string;
137
+ status: "read" | "delivered" | "played";
138
+ }
139
+ interface PresencePayload {
140
+ from: string;
141
+ unavailable: boolean;
142
+ lastSeen?: number;
143
+ }
144
+ interface ChatPresencePayload {
145
+ from: string;
146
+ chatJID: string;
147
+ state: "composing" | "paused";
148
+ media?: "audio";
149
+ }
150
+ interface ContactUpdatePayload {
151
+ jid: string;
152
+ username: string;
153
+ }
154
+ interface DisconnectedPayload {
155
+ reason?: string;
156
+ }
157
+ /** Same shape for the account ban and for the reach-out timelock. */
158
+ interface TemporaryBanPayload {
159
+ reason?: string;
160
+ code?: number;
161
+ active: boolean;
162
+ restrictedUntil?: string;
163
+ enforcementType?: string;
164
+ }
165
+ interface RestrictionLiftedPayload {
166
+ active: false;
167
+ enforcementType?: string;
168
+ restrictedUntil?: string;
169
+ }
170
+ interface ContactReachoutLockedPayload {
171
+ to: string;
172
+ reason: string;
173
+ detail: string;
174
+ code: number;
175
+ }
176
+ /** Discriminated by `type`, so a switch narrows the payload. */
177
+ type WebhookEvent = WebhookEnvelope<"message", MessagePayload> | WebhookEnvelope<"receipt", ReceiptPayload> | WebhookEnvelope<"presence", PresencePayload> | WebhookEnvelope<"chat_presence", ChatPresencePayload> | WebhookEnvelope<"reaction", ReactionPayload> | WebhookEnvelope<"contact_update", ContactUpdatePayload> | WebhookEnvelope<"connected", Record<string, unknown>> | WebhookEnvelope<"disconnected", DisconnectedPayload> | WebhookEnvelope<"temporary_ban", TemporaryBanPayload> | WebhookEnvelope<"restriction_lifted", RestrictionLiftedPayload> | WebhookEnvelope<"contact_reachout_locked", ContactReachoutLockedPayload> | WebhookEnvelope<"unknown", Record<string, unknown>>;
178
+
179
+ export type { ChatPresencePayload as C, DisconnectedPayload as D, MessageButton as M, PresencePayload as P, ReactionPayload as R, TemporaryBanPayload as T, WebhookEnvelope as W, ContactReachoutLockedPayload as a, ContactUpdatePayload as b, MessageInteractive as c, MessageInteractiveReply as d, MessageListRow as e, MessageListSection as f, MessagePayload as g, ReceiptPayload as h, RestrictionLiftedPayload as i, WebhookEvent as j, WebhookEventType as k };
@@ -1,4 +1,4 @@
1
- import { e as WebhookEvent } from './events-BV5UVoFu.cjs';
1
+ import { j as WebhookEvent } from './events-BUqVWlyS.cjs';
2
2
 
3
3
  /**
4
4
  * One class per condition, so callers branch on the error instead of parsing
@@ -1,4 +1,4 @@
1
- import { e as WebhookEvent } from './events-BV5UVoFu.js';
1
+ import { j as WebhookEvent } from './events-BUqVWlyS.js';
2
2
 
3
3
  /**
4
4
  * One class per condition, so callers branch on the error instead of parsing
package/dist/index.cjs CHANGED
@@ -490,6 +490,18 @@ var MessagesResource = class {
490
490
  form.set("file", input.file, input.filename ?? fileName(input.file, "media"));
491
491
  return this.http.request({ method: "POST", path: `${this.base}/media`, body: form, ...options ? { options } : {} });
492
492
  }
493
+ sendGif(input, options) {
494
+ const form = toForm(input, ["file", "filename"]);
495
+ form.set("type", "gif");
496
+ form.set("file", input.file, input.filename ?? fileName(input.file, "animation.mp4"));
497
+ return this.http.request({ method: "POST", path: `${this.base}/media`, body: form, ...options ? { options } : {} });
498
+ }
499
+ sendSticker(input, options) {
500
+ const form = toForm(input, ["file", "filename"]);
501
+ form.set("type", "sticker");
502
+ form.set("file", input.file, input.filename ?? fileName(input.file, "sticker.webp"));
503
+ return this.http.request({ method: "POST", path: `${this.base}/media`, body: form, ...options ? { options } : {} });
504
+ }
493
505
  sendAudio(input, options) {
494
506
  const form = toForm(input, ["file", "filename"]);
495
507
  form.set("file", input.file, input.filename ?? fileName(input.file, "audio.ogg"));
package/dist/index.d.cts CHANGED
@@ -1,7 +1,7 @@
1
- import { A as ApimeError } from './index-37ITjlwO.cjs';
2
- export { a as ApiError, b as AuthenticationError, C as ConfigurationError, c as ConflictError, d as ConnectionError, I as InvalidRequestError, e as InvalidSignatureError, N as NotFoundError, P as PermissionError, R as RateLimitError, S as SessionUnavailableError, T as TimeoutError, U as UnprocessableError, f as constructEvent, v as verifyWebhookSignature } from './index-37ITjlwO.cjs';
1
+ import { A as ApimeError } from './index-CJP98yfj.cjs';
2
+ export { a as ApiError, b as AuthenticationError, C as ConfigurationError, c as ConflictError, d as ConnectionError, I as InvalidRequestError, e as InvalidSignatureError, N as NotFoundError, P as PermissionError, R as RateLimitError, S as SessionUnavailableError, T as TimeoutError, U as UnprocessableError, f as constructEvent, v as verifyWebhookSignature } from './index-CJP98yfj.cjs';
3
3
  import { Instance, QrCode, EventLogEntry, InstanceInfo, Profile, SentMessage } from './types/index.cjs';
4
- export { C as ChatPresencePayload, a as ContactReachoutLockedPayload, b as ContactUpdatePayload, D as DisconnectedPayload, M as MessageButton, c as MessagePayload, P as PresencePayload, R as ReceiptPayload, d as RestrictionLiftedPayload, T as TemporaryBanPayload, W as WebhookEnvelope, e as WebhookEvent, f as WebhookEventType } from './events-BV5UVoFu.cjs';
4
+ export { C as ChatPresencePayload, a as ContactReachoutLockedPayload, b as ContactUpdatePayload, D as DisconnectedPayload, M as MessageButton, c as MessageInteractive, d as MessageInteractiveReply, e as MessageListRow, f as MessageListSection, g as MessagePayload, P as PresencePayload, R as ReactionPayload, h as ReceiptPayload, i as RestrictionLiftedPayload, T as TemporaryBanPayload, W as WebhookEnvelope, j as WebhookEvent, k as WebhookEventType } from './events-BUqVWlyS.cjs';
5
5
 
6
6
  /**
7
7
  * The apime has three credentials and they do not overlap: 46 routes only
@@ -230,6 +230,32 @@ interface SendMediaInput extends QuoteInput, MarkReadInput {
230
230
  filename?: string;
231
231
  caption?: string;
232
232
  }
233
+ /**
234
+ * A GIF on WhatsApp is an MP4 VIDEO carrying the gif-playback flag, which is what makes the client
235
+ * loop it with no controls. Sending a real `.gif` file arrives as a still image, so it gets a method
236
+ * of its own rather than a `type` on sendMedia: the name is what warns the caller at the call site.
237
+ */
238
+ interface SendGifInput extends QuoteInput, MarkReadInput {
239
+ to: string;
240
+ /** MP4 bytes. A `.gif` file is NOT what WhatsApp expects here. */
241
+ file: Blob | File;
242
+ filename?: string;
243
+ caption?: string;
244
+ }
245
+ /**
246
+ * WhatsApp takes stickers as WebP 512x512, up to 500 KB, transparent background; animated ones are
247
+ * animated WebP. The server validates and refuses outside that, because a sticker off-spec is
248
+ * accepted by the wire and then fails to render, with nothing reporting it.
249
+ *
250
+ * There is no caption: the protocol has no such field on a sticker, so one would be dropped in
251
+ * silence. That is why this does not reuse `SendMediaInput`.
252
+ */
253
+ interface SendStickerInput extends QuoteInput, MarkReadInput {
254
+ to: string;
255
+ /** WebP bytes, 512x512, up to 500 KB. */
256
+ file: Blob | File;
257
+ filename?: string;
258
+ }
233
259
  interface SendAudioInput extends QuoteInput, MarkReadInput {
234
260
  to: string;
235
261
  file: Blob | File;
@@ -275,6 +301,8 @@ declare class MessagesResource {
275
301
  private get base();
276
302
  sendText(input: SendTextInput, options?: RequestOptions): Promise<SentMessage>;
277
303
  sendMedia(input: SendMediaInput, options?: RequestOptions): Promise<SentMessage>;
304
+ sendGif(input: SendGifInput, options?: RequestOptions): Promise<SentMessage>;
305
+ sendSticker(input: SendStickerInput, options?: RequestOptions): Promise<SentMessage>;
278
306
  sendAudio(input: SendAudioInput, options?: RequestOptions): Promise<SentMessage>;
279
307
  sendDocument(input: SendDocumentInput, options?: RequestOptions): Promise<SentMessage>;
280
308
  sendContact(input: SendContactInput, options?: RequestOptions): Promise<SentMessage>;
@@ -535,4 +563,4 @@ interface HealthResult {
535
563
  declare const DEFAULT_HEALTH_TIMEOUT_MS = 10000;
536
564
  declare function checkHealth(baseUrl: string, options?: HealthOptions): Promise<HealthResult>;
537
565
 
538
- export { type ApiToken, type ApiTokenAuth, Apime, ApimeError, ApimeInstanceClient, ApimeUserClient, type Auth, type AuthKind, type CheckNumberResult, type ClientOptions, type ContactEntry, type CreateInstanceInput, DEFAULT_HEALTH_TIMEOUT_MS, EventLogEntry, type FailureInfo, type GroupInfo, type GroupParticipant, type HealthOptions, type HealthResult, Instance, InstanceInfo, type InstanceTokenAuth, type IpFamily, type JoinRequestAction, type MarkReadInput, type Observer, type ParticipantAction, type PresenceState, Profile, QrCode, type QuoteInput, type RequestInfo, type RequestOptions, type SendAudioInput, type SendContactInput, type SendDocumentInput, type SendLocationInput, type SendMediaInput, type SendTextInput, SentMessage, type SuccessInfo, type UpdateInstanceInput, type User, type UserAuth, type UserJwtAuth, checkHealth, hasSentry };
566
+ export { type ApiToken, type ApiTokenAuth, Apime, ApimeError, ApimeInstanceClient, ApimeUserClient, type Auth, type AuthKind, type CheckNumberResult, type ClientOptions, type ContactEntry, type CreateInstanceInput, DEFAULT_HEALTH_TIMEOUT_MS, EventLogEntry, type FailureInfo, type GroupInfo, type GroupParticipant, type HealthOptions, type HealthResult, Instance, InstanceInfo, type InstanceTokenAuth, type IpFamily, type JoinRequestAction, type MarkReadInput, type Observer, type ParticipantAction, type PresenceState, Profile, QrCode, type QuoteInput, type RequestInfo, type RequestOptions, type SendAudioInput, type SendContactInput, type SendDocumentInput, type SendGifInput, type SendLocationInput, type SendMediaInput, type SendStickerInput, type SendTextInput, SentMessage, type SuccessInfo, type UpdateInstanceInput, type User, type UserAuth, type UserJwtAuth, checkHealth, hasSentry };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- import { A as ApimeError } from './index-TqYrYke0.js';
2
- export { a as ApiError, b as AuthenticationError, C as ConfigurationError, c as ConflictError, d as ConnectionError, I as InvalidRequestError, e as InvalidSignatureError, N as NotFoundError, P as PermissionError, R as RateLimitError, S as SessionUnavailableError, T as TimeoutError, U as UnprocessableError, f as constructEvent, v as verifyWebhookSignature } from './index-TqYrYke0.js';
1
+ import { A as ApimeError } from './index-CauhD-7n.js';
2
+ export { a as ApiError, b as AuthenticationError, C as ConfigurationError, c as ConflictError, d as ConnectionError, I as InvalidRequestError, e as InvalidSignatureError, N as NotFoundError, P as PermissionError, R as RateLimitError, S as SessionUnavailableError, T as TimeoutError, U as UnprocessableError, f as constructEvent, v as verifyWebhookSignature } from './index-CauhD-7n.js';
3
3
  import { Instance, QrCode, EventLogEntry, InstanceInfo, Profile, SentMessage } from './types/index.js';
4
- export { C as ChatPresencePayload, a as ContactReachoutLockedPayload, b as ContactUpdatePayload, D as DisconnectedPayload, M as MessageButton, c as MessagePayload, P as PresencePayload, R as ReceiptPayload, d as RestrictionLiftedPayload, T as TemporaryBanPayload, W as WebhookEnvelope, e as WebhookEvent, f as WebhookEventType } from './events-BV5UVoFu.js';
4
+ export { C as ChatPresencePayload, a as ContactReachoutLockedPayload, b as ContactUpdatePayload, D as DisconnectedPayload, M as MessageButton, c as MessageInteractive, d as MessageInteractiveReply, e as MessageListRow, f as MessageListSection, g as MessagePayload, P as PresencePayload, R as ReactionPayload, h as ReceiptPayload, i as RestrictionLiftedPayload, T as TemporaryBanPayload, W as WebhookEnvelope, j as WebhookEvent, k as WebhookEventType } from './events-BUqVWlyS.js';
5
5
 
6
6
  /**
7
7
  * The apime has three credentials and they do not overlap: 46 routes only
@@ -230,6 +230,32 @@ interface SendMediaInput extends QuoteInput, MarkReadInput {
230
230
  filename?: string;
231
231
  caption?: string;
232
232
  }
233
+ /**
234
+ * A GIF on WhatsApp is an MP4 VIDEO carrying the gif-playback flag, which is what makes the client
235
+ * loop it with no controls. Sending a real `.gif` file arrives as a still image, so it gets a method
236
+ * of its own rather than a `type` on sendMedia: the name is what warns the caller at the call site.
237
+ */
238
+ interface SendGifInput extends QuoteInput, MarkReadInput {
239
+ to: string;
240
+ /** MP4 bytes. A `.gif` file is NOT what WhatsApp expects here. */
241
+ file: Blob | File;
242
+ filename?: string;
243
+ caption?: string;
244
+ }
245
+ /**
246
+ * WhatsApp takes stickers as WebP 512x512, up to 500 KB, transparent background; animated ones are
247
+ * animated WebP. The server validates and refuses outside that, because a sticker off-spec is
248
+ * accepted by the wire and then fails to render, with nothing reporting it.
249
+ *
250
+ * There is no caption: the protocol has no such field on a sticker, so one would be dropped in
251
+ * silence. That is why this does not reuse `SendMediaInput`.
252
+ */
253
+ interface SendStickerInput extends QuoteInput, MarkReadInput {
254
+ to: string;
255
+ /** WebP bytes, 512x512, up to 500 KB. */
256
+ file: Blob | File;
257
+ filename?: string;
258
+ }
233
259
  interface SendAudioInput extends QuoteInput, MarkReadInput {
234
260
  to: string;
235
261
  file: Blob | File;
@@ -275,6 +301,8 @@ declare class MessagesResource {
275
301
  private get base();
276
302
  sendText(input: SendTextInput, options?: RequestOptions): Promise<SentMessage>;
277
303
  sendMedia(input: SendMediaInput, options?: RequestOptions): Promise<SentMessage>;
304
+ sendGif(input: SendGifInput, options?: RequestOptions): Promise<SentMessage>;
305
+ sendSticker(input: SendStickerInput, options?: RequestOptions): Promise<SentMessage>;
278
306
  sendAudio(input: SendAudioInput, options?: RequestOptions): Promise<SentMessage>;
279
307
  sendDocument(input: SendDocumentInput, options?: RequestOptions): Promise<SentMessage>;
280
308
  sendContact(input: SendContactInput, options?: RequestOptions): Promise<SentMessage>;
@@ -535,4 +563,4 @@ interface HealthResult {
535
563
  declare const DEFAULT_HEALTH_TIMEOUT_MS = 10000;
536
564
  declare function checkHealth(baseUrl: string, options?: HealthOptions): Promise<HealthResult>;
537
565
 
538
- export { type ApiToken, type ApiTokenAuth, Apime, ApimeError, ApimeInstanceClient, ApimeUserClient, type Auth, type AuthKind, type CheckNumberResult, type ClientOptions, type ContactEntry, type CreateInstanceInput, DEFAULT_HEALTH_TIMEOUT_MS, EventLogEntry, type FailureInfo, type GroupInfo, type GroupParticipant, type HealthOptions, type HealthResult, Instance, InstanceInfo, type InstanceTokenAuth, type IpFamily, type JoinRequestAction, type MarkReadInput, type Observer, type ParticipantAction, type PresenceState, Profile, QrCode, type QuoteInput, type RequestInfo, type RequestOptions, type SendAudioInput, type SendContactInput, type SendDocumentInput, type SendLocationInput, type SendMediaInput, type SendTextInput, SentMessage, type SuccessInfo, type UpdateInstanceInput, type User, type UserAuth, type UserJwtAuth, checkHealth, hasSentry };
566
+ export { type ApiToken, type ApiTokenAuth, Apime, ApimeError, ApimeInstanceClient, ApimeUserClient, type Auth, type AuthKind, type CheckNumberResult, type ClientOptions, type ContactEntry, type CreateInstanceInput, DEFAULT_HEALTH_TIMEOUT_MS, EventLogEntry, type FailureInfo, type GroupInfo, type GroupParticipant, type HealthOptions, type HealthResult, Instance, InstanceInfo, type InstanceTokenAuth, type IpFamily, type JoinRequestAction, type MarkReadInput, type Observer, type ParticipantAction, type PresenceState, Profile, QrCode, type QuoteInput, type RequestInfo, type RequestOptions, type SendAudioInput, type SendContactInput, type SendDocumentInput, type SendGifInput, type SendLocationInput, type SendMediaInput, type SendStickerInput, type SendTextInput, SentMessage, type SuccessInfo, type UpdateInstanceInput, type User, type UserAuth, type UserJwtAuth, checkHealth, hasSentry };
package/dist/index.js CHANGED
@@ -419,6 +419,18 @@ var MessagesResource = class {
419
419
  form.set("file", input.file, input.filename ?? fileName(input.file, "media"));
420
420
  return this.http.request({ method: "POST", path: `${this.base}/media`, body: form, ...options ? { options } : {} });
421
421
  }
422
+ sendGif(input, options) {
423
+ const form = toForm(input, ["file", "filename"]);
424
+ form.set("type", "gif");
425
+ form.set("file", input.file, input.filename ?? fileName(input.file, "animation.mp4"));
426
+ return this.http.request({ method: "POST", path: `${this.base}/media`, body: form, ...options ? { options } : {} });
427
+ }
428
+ sendSticker(input, options) {
429
+ const form = toForm(input, ["file", "filename"]);
430
+ form.set("type", "sticker");
431
+ form.set("file", input.file, input.filename ?? fileName(input.file, "sticker.webp"));
432
+ return this.http.request({ method: "POST", path: `${this.base}/media`, body: form, ...options ? { options } : {} });
433
+ }
422
434
  sendAudio(input, options) {
423
435
  const form = toForm(input, ["file", "filename"]);
424
436
  form.set("file", input.file, input.filename ?? fileName(input.file, "audio.ogg"));
@@ -1,4 +1,4 @@
1
- export { C as ChatPresencePayload, a as ContactReachoutLockedPayload, b as ContactUpdatePayload, D as DisconnectedPayload, M as MessageButton, c as MessagePayload, P as PresencePayload, R as ReceiptPayload, d as RestrictionLiftedPayload, T as TemporaryBanPayload, W as WebhookEnvelope, e as WebhookEvent, f as WebhookEventType } from '../events-BV5UVoFu.cjs';
1
+ export { C as ChatPresencePayload, a as ContactReachoutLockedPayload, b as ContactUpdatePayload, D as DisconnectedPayload, M as MessageButton, c as MessageInteractive, d as MessageInteractiveReply, e as MessageListRow, f as MessageListSection, g as MessagePayload, P as PresencePayload, R as ReactionPayload, h as ReceiptPayload, i as RestrictionLiftedPayload, T as TemporaryBanPayload, W as WebhookEnvelope, j as WebhookEvent, k as WebhookEventType } from '../events-BUqVWlyS.cjs';
2
2
 
3
3
  /** Shapes the apime returns. Field names are the ones on the wire. */
4
4
  interface Instance {
@@ -1,4 +1,4 @@
1
- export { C as ChatPresencePayload, a as ContactReachoutLockedPayload, b as ContactUpdatePayload, D as DisconnectedPayload, M as MessageButton, c as MessagePayload, P as PresencePayload, R as ReceiptPayload, d as RestrictionLiftedPayload, T as TemporaryBanPayload, W as WebhookEnvelope, e as WebhookEvent, f as WebhookEventType } from '../events-BV5UVoFu.js';
1
+ export { C as ChatPresencePayload, a as ContactReachoutLockedPayload, b as ContactUpdatePayload, D as DisconnectedPayload, M as MessageButton, c as MessageInteractive, d as MessageInteractiveReply, e as MessageListRow, f as MessageListSection, g as MessagePayload, P as PresencePayload, R as ReactionPayload, h as ReceiptPayload, i as RestrictionLiftedPayload, T as TemporaryBanPayload, W as WebhookEnvelope, j as WebhookEvent, k as WebhookEventType } from '../events-BUqVWlyS.js';
2
2
 
3
3
  /** Shapes the apime returns. Field names are the ones on the wire. */
4
4
  interface Instance {
@@ -1,2 +1,2 @@
1
- export { e as InvalidSignatureError, g as SIGNATURE_HEADER, f as constructEvent, v as verifyWebhookSignature } from '../index-37ITjlwO.cjs';
2
- export { e as WebhookEvent } from '../events-BV5UVoFu.cjs';
1
+ export { e as InvalidSignatureError, g as SIGNATURE_HEADER, f as constructEvent, v as verifyWebhookSignature } from '../index-CJP98yfj.cjs';
2
+ export { j as WebhookEvent } from '../events-BUqVWlyS.cjs';
@@ -1,2 +1,2 @@
1
- export { e as InvalidSignatureError, g as SIGNATURE_HEADER, f as constructEvent, v as verifyWebhookSignature } from '../index-TqYrYke0.js';
2
- export { e as WebhookEvent } from '../events-BV5UVoFu.js';
1
+ export { e as InvalidSignatureError, g as SIGNATURE_HEADER, f as constructEvent, v as verifyWebhookSignature } from '../index-CauhD-7n.js';
2
+ export { j as WebhookEvent } from '../events-BUqVWlyS.js';
package/docs/guia.md CHANGED
@@ -48,6 +48,20 @@ await conexao.messages.sendMedia(
48
48
  { idempotencyKey: mensagem.id },
49
49
  );
50
50
 
51
+ // GIF é MP4 com reprodução em laço, não arquivo .gif: mandar um .gif de verdade
52
+ // faz a mensagem chegar como imagem parada.
53
+ await conexao.messages.sendGif(
54
+ { to: "5511999999999", file: mp4Blob },
55
+ { idempotencyKey: mensagem.id },
56
+ );
57
+
58
+ // Figurinha exige WebP 512x512 até 500 KB (animada ou não), e não tem legenda:
59
+ // o protocolo não traz esse campo. Fora da especificação, o apime recusa com 400.
60
+ await conexao.messages.sendSticker(
61
+ { to: "5511999999999", file: webpBlob },
62
+ { idempotencyKey: mensagem.id },
63
+ );
64
+
51
65
  await conexao.whatsapp.groups.updateParticipants(grupoJid, {
52
66
  action: "add",
53
67
  participants: ["5511999999999@s.whatsapp.net"],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-apime/sdk",
3
- "version": "0.4.1",
3
+ "version": "0.6.0",
4
4
  "description": "Cliente TypeScript da API do apime: WhatsApp, instâncias e webhooks",
5
5
  "license": "MIT",
6
6
  "keywords": [
@@ -1,90 +0,0 @@
1
- /**
2
- * The 12 event types the apime delivers. `ignore` is internal and never
3
- * reaches a consumer, so it is not part of the union.
4
- */
5
- type WebhookEventType = "message" | "receipt" | "presence" | "chat_presence" | "reaction" | "contact_update" | "connected" | "disconnected" | "temporary_ban" | "restriction_lifted" | "contact_reachout_locked" | "unknown";
6
- interface WebhookEnvelope<T extends WebhookEventType = WebhookEventType, P = unknown> {
7
- id: string;
8
- instanceId: string;
9
- type: T;
10
- payload: P;
11
- createdAt: string;
12
- }
13
- /** Button of an interactive message. `call`, `copy`, `reply` and `url` are button kinds, not events. */
14
- interface MessageButton {
15
- id: string;
16
- label: string;
17
- type: "reply" | "url" | "copy" | "call";
18
- url?: string;
19
- code?: string;
20
- phone?: string;
21
- }
22
- interface MessagePayload {
23
- from: string;
24
- to: string;
25
- isFromMe: boolean;
26
- isGroup: boolean;
27
- messageId: string;
28
- timestamp: number;
29
- pushName?: string;
30
- text?: string;
31
- mediaType?: "image" | "video" | "audio" | "document" | "sticker" | "location" | "contact";
32
- mediaUrl?: string;
33
- mimetype?: string;
34
- caption?: string;
35
- buttons?: MessageButton[];
36
- /**
37
- * Present only when this event is an edit: the id of the message that was
38
- * edited, and its new text. They always travel together, so checking one is
39
- * enough to know it is an edit.
40
- */
41
- editedMessageId?: string;
42
- editedText?: string;
43
- }
44
- interface ReceiptPayload {
45
- messageIds: string[];
46
- timestamp: number;
47
- chat: string;
48
- status: "read" | "delivered" | "played";
49
- }
50
- interface PresencePayload {
51
- from: string;
52
- unavailable: boolean;
53
- lastSeen?: number;
54
- }
55
- interface ChatPresencePayload {
56
- from: string;
57
- chatJID: string;
58
- state: "composing" | "paused";
59
- media?: "audio";
60
- }
61
- interface ContactUpdatePayload {
62
- jid: string;
63
- username: string;
64
- }
65
- interface DisconnectedPayload {
66
- reason?: string;
67
- }
68
- /** Same shape for the account ban and for the reach-out timelock. */
69
- interface TemporaryBanPayload {
70
- reason?: string;
71
- code?: number;
72
- active: boolean;
73
- restrictedUntil?: string;
74
- enforcementType?: string;
75
- }
76
- interface RestrictionLiftedPayload {
77
- active: false;
78
- enforcementType?: string;
79
- restrictedUntil?: string;
80
- }
81
- interface ContactReachoutLockedPayload {
82
- to: string;
83
- reason: string;
84
- detail: string;
85
- code: number;
86
- }
87
- /** Discriminated by `type`, so a switch narrows the payload. */
88
- type WebhookEvent = WebhookEnvelope<"message", MessagePayload> | WebhookEnvelope<"receipt", ReceiptPayload> | WebhookEnvelope<"presence", PresencePayload> | WebhookEnvelope<"chat_presence", ChatPresencePayload> | WebhookEnvelope<"reaction", Record<string, unknown>> | WebhookEnvelope<"contact_update", ContactUpdatePayload> | WebhookEnvelope<"connected", Record<string, unknown>> | WebhookEnvelope<"disconnected", DisconnectedPayload> | WebhookEnvelope<"temporary_ban", TemporaryBanPayload> | WebhookEnvelope<"restriction_lifted", RestrictionLiftedPayload> | WebhookEnvelope<"contact_reachout_locked", ContactReachoutLockedPayload> | WebhookEnvelope<"unknown", Record<string, unknown>>;
89
-
90
- export type { ChatPresencePayload as C, DisconnectedPayload as D, MessageButton as M, PresencePayload as P, ReceiptPayload as R, TemporaryBanPayload as T, WebhookEnvelope as W, ContactReachoutLockedPayload as a, ContactUpdatePayload as b, MessagePayload as c, RestrictionLiftedPayload as d, WebhookEvent as e, WebhookEventType as f };
@@ -1,90 +0,0 @@
1
- /**
2
- * The 12 event types the apime delivers. `ignore` is internal and never
3
- * reaches a consumer, so it is not part of the union.
4
- */
5
- type WebhookEventType = "message" | "receipt" | "presence" | "chat_presence" | "reaction" | "contact_update" | "connected" | "disconnected" | "temporary_ban" | "restriction_lifted" | "contact_reachout_locked" | "unknown";
6
- interface WebhookEnvelope<T extends WebhookEventType = WebhookEventType, P = unknown> {
7
- id: string;
8
- instanceId: string;
9
- type: T;
10
- payload: P;
11
- createdAt: string;
12
- }
13
- /** Button of an interactive message. `call`, `copy`, `reply` and `url` are button kinds, not events. */
14
- interface MessageButton {
15
- id: string;
16
- label: string;
17
- type: "reply" | "url" | "copy" | "call";
18
- url?: string;
19
- code?: string;
20
- phone?: string;
21
- }
22
- interface MessagePayload {
23
- from: string;
24
- to: string;
25
- isFromMe: boolean;
26
- isGroup: boolean;
27
- messageId: string;
28
- timestamp: number;
29
- pushName?: string;
30
- text?: string;
31
- mediaType?: "image" | "video" | "audio" | "document" | "sticker" | "location" | "contact";
32
- mediaUrl?: string;
33
- mimetype?: string;
34
- caption?: string;
35
- buttons?: MessageButton[];
36
- /**
37
- * Present only when this event is an edit: the id of the message that was
38
- * edited, and its new text. They always travel together, so checking one is
39
- * enough to know it is an edit.
40
- */
41
- editedMessageId?: string;
42
- editedText?: string;
43
- }
44
- interface ReceiptPayload {
45
- messageIds: string[];
46
- timestamp: number;
47
- chat: string;
48
- status: "read" | "delivered" | "played";
49
- }
50
- interface PresencePayload {
51
- from: string;
52
- unavailable: boolean;
53
- lastSeen?: number;
54
- }
55
- interface ChatPresencePayload {
56
- from: string;
57
- chatJID: string;
58
- state: "composing" | "paused";
59
- media?: "audio";
60
- }
61
- interface ContactUpdatePayload {
62
- jid: string;
63
- username: string;
64
- }
65
- interface DisconnectedPayload {
66
- reason?: string;
67
- }
68
- /** Same shape for the account ban and for the reach-out timelock. */
69
- interface TemporaryBanPayload {
70
- reason?: string;
71
- code?: number;
72
- active: boolean;
73
- restrictedUntil?: string;
74
- enforcementType?: string;
75
- }
76
- interface RestrictionLiftedPayload {
77
- active: false;
78
- enforcementType?: string;
79
- restrictedUntil?: string;
80
- }
81
- interface ContactReachoutLockedPayload {
82
- to: string;
83
- reason: string;
84
- detail: string;
85
- code: number;
86
- }
87
- /** Discriminated by `type`, so a switch narrows the payload. */
88
- type WebhookEvent = WebhookEnvelope<"message", MessagePayload> | WebhookEnvelope<"receipt", ReceiptPayload> | WebhookEnvelope<"presence", PresencePayload> | WebhookEnvelope<"chat_presence", ChatPresencePayload> | WebhookEnvelope<"reaction", Record<string, unknown>> | WebhookEnvelope<"contact_update", ContactUpdatePayload> | WebhookEnvelope<"connected", Record<string, unknown>> | WebhookEnvelope<"disconnected", DisconnectedPayload> | WebhookEnvelope<"temporary_ban", TemporaryBanPayload> | WebhookEnvelope<"restriction_lifted", RestrictionLiftedPayload> | WebhookEnvelope<"contact_reachout_locked", ContactReachoutLockedPayload> | WebhookEnvelope<"unknown", Record<string, unknown>>;
89
-
90
- export type { ChatPresencePayload as C, DisconnectedPayload as D, MessageButton as M, PresencePayload as P, ReceiptPayload as R, TemporaryBanPayload as T, WebhookEnvelope as W, ContactReachoutLockedPayload as a, ContactUpdatePayload as b, MessagePayload as c, RestrictionLiftedPayload as d, WebhookEvent as e, WebhookEventType as f };