@onebots/adapter-instagram 0.1.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.
package/lib/types.d.ts ADDED
@@ -0,0 +1,171 @@
1
+ import type { MetaGraphCallOptions, MetaGraphMethod, MetaHttpRequest, MetaHttpResponse, MetaIngestResult } from "@onebots/meta";
2
+ export type InstagramReceiveMode = "webhook" | "manual";
3
+ export interface InstagramConfig {
4
+ account_id: string;
5
+ instagram_user_id: string;
6
+ access_token: string;
7
+ app_secret?: string;
8
+ verify_token?: string;
9
+ receive_mode?: InstagramReceiveMode;
10
+ http_path?: string;
11
+ api_version?: string;
12
+ api_origin?: string;
13
+ max_body_bytes?: number;
14
+ auto_subscribe?: boolean;
15
+ subscribed_fields?: string[];
16
+ event_types?: InstagramEventType[];
17
+ declared_permissions?: string[];
18
+ }
19
+ export interface InstagramActor {
20
+ id: string;
21
+ }
22
+ export interface InstagramAttachment {
23
+ type: string;
24
+ payload: Record<string, unknown>;
25
+ }
26
+ export interface InstagramMessage {
27
+ mid: string;
28
+ text?: string;
29
+ is_echo?: boolean;
30
+ is_deleted?: boolean;
31
+ is_self?: boolean;
32
+ is_unsupported?: boolean;
33
+ attachments?: InstagramAttachment[];
34
+ quick_reply?: {
35
+ payload: string;
36
+ };
37
+ reply_to?: Record<string, unknown>;
38
+ referral?: Record<string, unknown>;
39
+ }
40
+ export interface InstagramMessagingItem {
41
+ sender: InstagramActor;
42
+ recipient: InstagramActor;
43
+ timestamp: number;
44
+ message?: InstagramMessage;
45
+ message_edit?: Record<string, unknown>;
46
+ reaction?: Record<string, unknown>;
47
+ postback?: Record<string, unknown>;
48
+ read?: Record<string, unknown>;
49
+ referral?: Record<string, unknown>;
50
+ optin?: Record<string, unknown>;
51
+ pass_thread_control?: Record<string, unknown>;
52
+ take_thread_control?: Record<string, unknown>;
53
+ request_thread_control?: Record<string, unknown>;
54
+ raw: Record<string, unknown>;
55
+ }
56
+ export interface InstagramChange {
57
+ field: string;
58
+ value: Record<string, unknown>;
59
+ }
60
+ export interface InstagramEntry {
61
+ id: string;
62
+ time: number;
63
+ messaging: InstagramMessagingItem[];
64
+ standby: InstagramMessagingItem[];
65
+ changes: InstagramChange[];
66
+ raw: Record<string, unknown>;
67
+ }
68
+ export interface InstagramWebhookEnvelope {
69
+ object: "instagram";
70
+ entry: InstagramEntry[];
71
+ raw: Record<string, unknown>;
72
+ }
73
+ export declare const INSTAGRAM_EVENT_TYPES: readonly ["message", "message_echo", "message_deleted", "message_unsupported", "message_edit", "reaction", "postback", "read", "referral", "optin", "handover", "change", "unknown"];
74
+ export type InstagramEventType = (typeof INSTAGRAM_EVENT_TYPES)[number];
75
+ export declare const INSTAGRAM_WEBHOOK_FIELDS: readonly ["messages", "messaging_postbacks", "messaging_seen", "messaging_handover", "messaging_referral", "messaging_optins", "message_reactions", "standby", "comments", "live_comments", "mentions", "story_insights"];
76
+ export interface InstagramEvent {
77
+ event_type: InstagramEventType;
78
+ source: "messaging" | "standby" | "change";
79
+ instagram_user_id: string;
80
+ entry_time: number;
81
+ messaging?: InstagramMessagingItem;
82
+ change?: InstagramChange;
83
+ }
84
+ export interface InstagramDelivery {
85
+ id: string;
86
+ event: InstagramEvent;
87
+ rawEnvelope: InstagramWebhookEnvelope;
88
+ }
89
+ export type InstagramIngestResult = MetaIngestResult<InstagramEvent, InstagramWebhookEnvelope>;
90
+ export interface InstagramClientEvents {
91
+ event: [delivery: InstagramDelivery];
92
+ ready: [];
93
+ error: [error: Error];
94
+ stop: [];
95
+ }
96
+ export interface InstagramBusinessProfile {
97
+ id: string;
98
+ user_id?: string;
99
+ username?: string;
100
+ name?: string;
101
+ profile_picture_url?: string;
102
+ account_type?: string;
103
+ }
104
+ export interface InstagramUserProfile {
105
+ id: string;
106
+ name?: string;
107
+ username?: string;
108
+ profile_pic?: string;
109
+ follower_count?: number;
110
+ is_user_follow_business?: boolean;
111
+ is_business_follow_user?: boolean;
112
+ is_verified_user?: boolean;
113
+ }
114
+ export interface InstagramSendResponse {
115
+ recipient_id: string;
116
+ message_id: string;
117
+ }
118
+ export interface InstagramApiMessage {
119
+ id: string;
120
+ created_time: string;
121
+ from?: {
122
+ id: string;
123
+ username?: string;
124
+ };
125
+ to?: {
126
+ data: Array<{
127
+ id: string;
128
+ username?: string;
129
+ }>;
130
+ };
131
+ message?: string;
132
+ }
133
+ export interface InstagramConversation {
134
+ id: string;
135
+ updated_time?: string;
136
+ participants?: {
137
+ data: Array<{
138
+ id: string;
139
+ username?: string;
140
+ }>;
141
+ };
142
+ messages?: {
143
+ data: InstagramApiMessage[];
144
+ paging?: InstagramPaging;
145
+ };
146
+ }
147
+ export interface InstagramPaging {
148
+ cursors?: {
149
+ before?: string;
150
+ after?: string;
151
+ };
152
+ }
153
+ export interface InstagramList<T> {
154
+ data: T[];
155
+ paging?: InstagramPaging;
156
+ }
157
+ export interface InstagramOutgoingMessage extends Record<string, unknown> {
158
+ text?: string;
159
+ attachment?: Record<string, unknown>;
160
+ quick_replies?: Record<string, unknown>[];
161
+ reply_to?: {
162
+ mid: string;
163
+ };
164
+ }
165
+ export interface InstagramHttpRequest extends MetaHttpRequest {
166
+ }
167
+ export interface InstagramHttpResponse extends MetaHttpResponse {
168
+ }
169
+ export interface InstagramCallOptions extends MetaGraphCallOptions {
170
+ }
171
+ export type InstagramGraphMethod = MetaGraphMethod;
package/lib/types.js ADDED
@@ -0,0 +1,29 @@
1
+ export const INSTAGRAM_EVENT_TYPES = [
2
+ "message",
3
+ "message_echo",
4
+ "message_deleted",
5
+ "message_unsupported",
6
+ "message_edit",
7
+ "reaction",
8
+ "postback",
9
+ "read",
10
+ "referral",
11
+ "optin",
12
+ "handover",
13
+ "change",
14
+ "unknown",
15
+ ];
16
+ export const INSTAGRAM_WEBHOOK_FIELDS = [
17
+ "messages",
18
+ "messaging_postbacks",
19
+ "messaging_seen",
20
+ "messaging_handover",
21
+ "messaging_referral",
22
+ "messaging_optins",
23
+ "message_reactions",
24
+ "standby",
25
+ "comments",
26
+ "live_comments",
27
+ "mentions",
28
+ "story_insights",
29
+ ];
@@ -0,0 +1,12 @@
1
+ import { isRecord as isMetaRecord } from "@onebots/meta";
2
+ import { type InstagramConfig } from "./types.js";
3
+ export declare const isRecord: typeof isMetaRecord;
4
+ export declare function requireRecord(value: unknown, field: string): Record<string, unknown>;
5
+ export declare function requireString(value: unknown, field: string): string;
6
+ export declare function optionalString(value: unknown, field: string): string | undefined;
7
+ export declare function requireNumber(value: unknown, field: string): number;
8
+ export declare function requireArray(value: unknown, field: string): unknown[];
9
+ export declare function assertInstagramConfig(config: InstagramConfig): void;
10
+ export declare function assertMetaId(value: unknown, field: string): string;
11
+ export declare function assertNumericMetaId(value: unknown, field: string): string;
12
+ export declare function assertHttpsUrl(value: unknown, field: string): string;
@@ -0,0 +1,96 @@
1
+ import { isRecord as isMetaRecord } from "@onebots/meta";
2
+ import { InstagramError } from "./errors.js";
3
+ import { INSTAGRAM_EVENT_TYPES, INSTAGRAM_WEBHOOK_FIELDS } from "./types.js";
4
+ export const isRecord = isMetaRecord;
5
+ export function requireRecord(value, field) {
6
+ if (!isRecord(value))
7
+ throw InstagramError.invalid(`${field} 必须是对象`);
8
+ return value;
9
+ }
10
+ export function requireString(value, field) {
11
+ if (typeof value !== "string" || !value) {
12
+ throw InstagramError.invalid(`${field} 必须是非空字符串`);
13
+ }
14
+ return value;
15
+ }
16
+ export function optionalString(value, field) {
17
+ if (value === undefined || value === null)
18
+ return undefined;
19
+ return requireString(value, field);
20
+ }
21
+ export function requireNumber(value, field) {
22
+ if (typeof value !== "number" || !Number.isFinite(value)) {
23
+ throw InstagramError.invalid(`${field} 必须是有限数字`);
24
+ }
25
+ return value;
26
+ }
27
+ export function requireArray(value, field) {
28
+ if (!Array.isArray(value))
29
+ throw InstagramError.invalid(`${field} 必须是数组`);
30
+ return value;
31
+ }
32
+ export function assertInstagramConfig(config) {
33
+ requireString(config.account_id, "account_id");
34
+ assertNumericMetaId(config.instagram_user_id, "instagram_user_id");
35
+ requireString(config.access_token, "access_token");
36
+ const mode = config.receive_mode || "webhook";
37
+ if (mode !== "webhook" && mode !== "manual") {
38
+ throw InstagramError.invalid("receive_mode 必须是 webhook 或 manual");
39
+ }
40
+ if (mode === "webhook" && (!config.app_secret || !config.verify_token)) {
41
+ throw InstagramError.invalid("webhook 模式必须配置 app_secret 与 verify_token");
42
+ }
43
+ if (config.http_path && !/^\/(?!\/)[^?#\u0000-\u001f\u007f]*$/u.test(config.http_path)) {
44
+ throw InstagramError.invalid("http_path 必须是安全绝对 pathname");
45
+ }
46
+ if (config.max_body_bytes !== undefined &&
47
+ (!Number.isSafeInteger(config.max_body_bytes) ||
48
+ config.max_body_bytes <= 0 ||
49
+ config.max_body_bytes > 50 * 1024 * 1024)) {
50
+ throw InstagramError.invalid("max_body_bytes 必须是 1 到 50 MiB 的安全整数");
51
+ }
52
+ assertStringList(config.subscribed_fields, "subscribed_fields");
53
+ assertStringList(config.event_types, "event_types");
54
+ assertStringList(config.declared_permissions, "declared_permissions");
55
+ if (config.subscribed_fields?.some(field => !INSTAGRAM_WEBHOOK_FIELDS.includes(field))) {
56
+ throw InstagramError.invalid("subscribed_fields 包含当前 Instagram API 未定义字段");
57
+ }
58
+ if (config.event_types?.some(type => !INSTAGRAM_EVENT_TYPES.includes(type))) {
59
+ throw InstagramError.invalid("event_types 包含当前适配器未定义事件");
60
+ }
61
+ }
62
+ export function assertMetaId(value, field) {
63
+ const id = requireString(value, field);
64
+ if (!/^[A-Za-z0-9_.:-]+$/u.test(id)) {
65
+ throw InstagramError.invalid(`${field} 包含不安全字符`);
66
+ }
67
+ return id;
68
+ }
69
+ export function assertNumericMetaId(value, field) {
70
+ const id = requireString(value, field);
71
+ if (!/^\d+$/u.test(id)) {
72
+ throw InstagramError.invalid(`${field} 必须是十进制 Meta ID`);
73
+ }
74
+ return id;
75
+ }
76
+ export function assertHttpsUrl(value, field) {
77
+ const raw = requireString(value, field);
78
+ if (!URL.canParse(raw))
79
+ throw InstagramError.invalid(`${field} 不是有效 URL`);
80
+ const url = new URL(raw);
81
+ if (url.protocol !== "https:" || url.username || url.password) {
82
+ throw InstagramError.invalid(`${field} 必须是无凭据 HTTPS URL`);
83
+ }
84
+ return url.toString();
85
+ }
86
+ function assertStringList(value, field) {
87
+ if (value === undefined)
88
+ return;
89
+ const values = requireArray(value, field);
90
+ if (values.some(item => typeof item !== "string" || !item)) {
91
+ throw InstagramError.invalid(`${field} 必须只包含非空字符串`);
92
+ }
93
+ if (new Set(values).size !== values.length) {
94
+ throw InstagramError.invalid(`${field} 不能包含重复项`);
95
+ }
96
+ }
@@ -0,0 +1,10 @@
1
+ import type { MetaWebhookCodec, MetaWebhookDelivery } from "@onebots/meta";
2
+ import { type InstagramEvent, type InstagramEventType, type InstagramWebhookEnvelope } from "./types.js";
3
+ /** 将 Instagram webhook batch 严格展开为可独立去重和提交的事件。 */
4
+ export declare class InstagramWebhookCodec implements MetaWebhookCodec<InstagramEvent, InstagramWebhookEnvelope> {
5
+ private readonly instagramUserId;
6
+ private readonly enabled?;
7
+ constructor(instagramUserId: string, eventTypes?: readonly InstagramEventType[]);
8
+ parse(value: unknown): InstagramWebhookEnvelope;
9
+ expand(envelope: InstagramWebhookEnvelope): MetaWebhookDelivery<InstagramEvent, InstagramWebhookEnvelope>[];
10
+ }
@@ -0,0 +1,263 @@
1
+ import { sha256Json } from "onebots";
2
+ import { InstagramError } from "./errors.js";
3
+ import { INSTAGRAM_EVENT_TYPES, } from "./types.js";
4
+ import { assertNumericMetaId, optionalString, requireArray, requireNumber, requireRecord, requireString, } from "./validation.js";
5
+ const EVENT_TYPE_SET = new Set(INSTAGRAM_EVENT_TYPES);
6
+ /** 将 Instagram webhook batch 严格展开为可独立去重和提交的事件。 */
7
+ export class InstagramWebhookCodec {
8
+ instagramUserId;
9
+ enabled;
10
+ constructor(instagramUserId, eventTypes) {
11
+ this.instagramUserId = instagramUserId;
12
+ assertNumericMetaId(instagramUserId, "instagram_user_id");
13
+ if (eventTypes?.some(type => !EVENT_TYPE_SET.has(type))) {
14
+ invalid("event_types 包含未知 Instagram 事件");
15
+ }
16
+ this.enabled = eventTypes?.length ? new Set(eventTypes) : undefined;
17
+ }
18
+ parse(value) {
19
+ const root = requireRecord(value, "webhook");
20
+ if (root.object !== "instagram") {
21
+ invalid("Instagram webhook.object 必须是 instagram");
22
+ }
23
+ const entries = requireArray(root.entry, "webhook.entry").map((entry, index) => parseEntry(entry, `webhook.entry[${index}]`));
24
+ if (!entries.length)
25
+ invalid("Instagram webhook.entry 不能为空");
26
+ if (entries.some(entry => entry.id !== this.instagramUserId)) {
27
+ invalid("Instagram webhook entry.id 与配置 instagram_user_id 不匹配");
28
+ }
29
+ return { object: "instagram", entry: entries, raw: root };
30
+ }
31
+ expand(envelope) {
32
+ const deliveries = [];
33
+ for (const entry of envelope.entry) {
34
+ for (const [source, items] of [
35
+ ["messaging", entry.messaging],
36
+ ["standby", entry.standby],
37
+ ]) {
38
+ for (const item of items) {
39
+ const eventType = classify(item);
40
+ if (this.enabled && !this.enabled.has(eventType))
41
+ continue;
42
+ const event = {
43
+ event_type: eventType,
44
+ source,
45
+ instagram_user_id: entry.id,
46
+ entry_time: entry.time,
47
+ messaging: item,
48
+ };
49
+ deliveries.push({
50
+ id: deliveryIdentity(event),
51
+ event,
52
+ rawEnvelope: envelope,
53
+ });
54
+ }
55
+ }
56
+ for (const change of entry.changes) {
57
+ if (this.enabled && !this.enabled.has("change"))
58
+ continue;
59
+ const event = {
60
+ event_type: "change",
61
+ source: "change",
62
+ instagram_user_id: entry.id,
63
+ entry_time: entry.time,
64
+ change,
65
+ };
66
+ deliveries.push({
67
+ id: `change:${entry.id}:${entry.time}:${change.field}:${sha256Json(change.value)}`,
68
+ event,
69
+ rawEnvelope: envelope,
70
+ });
71
+ }
72
+ }
73
+ return deliveries;
74
+ }
75
+ }
76
+ function parseEntry(value, field) {
77
+ const raw = requireRecord(value, field);
78
+ const id = assertNumericMetaId(raw.id, `${field}.id`);
79
+ const time = requireTimestamp(raw.time, `${field}.time`);
80
+ const messaging = parseMessagingArray(raw.messaging, `${field}.messaging`);
81
+ const standby = parseMessagingArray(raw.standby, `${field}.standby`);
82
+ const changes = parseEntryChange(raw, field);
83
+ if (!messaging.length && !standby.length && !changes.length) {
84
+ invalid(`${field} 没有 messaging、standby 或 field/value 事件`);
85
+ }
86
+ return { id, time, messaging, standby, changes, raw };
87
+ }
88
+ function parseEntryChange(raw, field) {
89
+ if (raw.field === undefined && raw.value === undefined)
90
+ return [];
91
+ if (raw.field === undefined || raw.value === undefined) {
92
+ invalid(`${field}.field 与 ${field}.value 必须同时存在`);
93
+ }
94
+ return [
95
+ {
96
+ field: requireString(raw.field, `${field}.field`),
97
+ value: requireRecord(raw.value, `${field}.value`),
98
+ },
99
+ ];
100
+ }
101
+ function parseMessagingArray(value, field) {
102
+ if (value === undefined)
103
+ return [];
104
+ return requireArray(value, field).map((item, index) => parseMessagingItem(item, `${field}[${index}]`));
105
+ }
106
+ function parseMessagingItem(value, field) {
107
+ const raw = requireRecord(value, field);
108
+ const item = {
109
+ sender: parseActor(raw.sender, `${field}.sender`),
110
+ recipient: parseActor(raw.recipient, `${field}.recipient`),
111
+ timestamp: requireTimestamp(raw.timestamp, `${field}.timestamp`),
112
+ raw,
113
+ };
114
+ if (raw.message !== undefined)
115
+ item.message = parseMessage(raw.message, `${field}.message`);
116
+ for (const key of [
117
+ "message_edit",
118
+ "reaction",
119
+ "postback",
120
+ "read",
121
+ "referral",
122
+ "optin",
123
+ "pass_thread_control",
124
+ "take_thread_control",
125
+ "request_thread_control",
126
+ ]) {
127
+ if (raw[key] !== undefined)
128
+ item[key] = requireRecord(raw[key], `${field}.${key}`);
129
+ }
130
+ validateKnownPayloads(item, field);
131
+ if (classify(item) === "unknown" && Object.keys(raw).length <= 3) {
132
+ invalid(`${field} 缺少 Instagram 事件 payload`);
133
+ }
134
+ return item;
135
+ }
136
+ function parseActor(value, field) {
137
+ const actor = requireRecord(value, field);
138
+ return { id: assertNumericMetaId(actor.id, `${field}.id`) };
139
+ }
140
+ function parseMessage(value, field) {
141
+ const raw = requireRecord(value, field);
142
+ const message = {
143
+ mid: requireString(raw.mid, `${field}.mid`),
144
+ text: optionalString(raw.text, `${field}.text`),
145
+ is_echo: optionalBoolean(raw.is_echo, `${field}.is_echo`),
146
+ is_deleted: optionalBoolean(raw.is_deleted, `${field}.is_deleted`),
147
+ is_self: optionalBoolean(raw.is_self, `${field}.is_self`),
148
+ is_unsupported: optionalBoolean(raw.is_unsupported, `${field}.is_unsupported`),
149
+ };
150
+ if (raw.attachments !== undefined) {
151
+ message.attachments = requireArray(raw.attachments, `${field}.attachments`).map((item, index) => parseAttachment(item, `${field}.attachments[${index}]`));
152
+ }
153
+ if (raw.quick_reply !== undefined) {
154
+ const quickReply = requireRecord(raw.quick_reply, `${field}.quick_reply`);
155
+ message.quick_reply = {
156
+ payload: requireString(quickReply.payload, `${field}.quick_reply.payload`),
157
+ };
158
+ }
159
+ if (raw.reply_to !== undefined) {
160
+ message.reply_to = requireRecord(raw.reply_to, `${field}.reply_to`);
161
+ }
162
+ if (raw.referral !== undefined) {
163
+ message.referral = requireRecord(raw.referral, `${field}.referral`);
164
+ }
165
+ return message;
166
+ }
167
+ function parseAttachment(value, field) {
168
+ const raw = requireRecord(value, field);
169
+ return {
170
+ type: requireString(raw.type, `${field}.type`),
171
+ payload: requireRecord(raw.payload, `${field}.payload`),
172
+ };
173
+ }
174
+ function validateKnownPayloads(item, field) {
175
+ if (item.message_edit) {
176
+ requireString(item.message_edit.mid, `${field}.message_edit.mid`);
177
+ requireString(item.message_edit.text, `${field}.message_edit.text`);
178
+ const count = requireNumber(item.message_edit.num_edit, `${field}.message_edit.num_edit`);
179
+ if (!Number.isSafeInteger(count) || count < 1) {
180
+ invalid(`${field}.message_edit.num_edit 必须是正安全整数`);
181
+ }
182
+ }
183
+ if (item.reaction) {
184
+ requireString(item.reaction.mid, `${field}.reaction.mid`);
185
+ const action = requireString(item.reaction.action, `${field}.reaction.action`);
186
+ if (action !== "react" && action !== "unreact") {
187
+ invalid(`${field}.reaction.action 必须是 react 或 unreact`);
188
+ }
189
+ optionalString(item.reaction.emoji, `${field}.reaction.emoji`);
190
+ }
191
+ if (item.postback) {
192
+ requireString(item.postback.mid, `${field}.postback.mid`);
193
+ requireString(item.postback.title, `${field}.postback.title`);
194
+ requireString(item.postback.payload, `${field}.postback.payload`);
195
+ }
196
+ if (item.read)
197
+ requireString(item.read.mid, `${field}.read.mid`);
198
+ if (item.referral) {
199
+ requireString(item.referral.ref, `${field}.referral.ref`);
200
+ requireString(item.referral.source, `${field}.referral.source`);
201
+ requireString(item.referral.type, `${field}.referral.type`);
202
+ }
203
+ }
204
+ function classify(item) {
205
+ if (item.message?.is_deleted)
206
+ return "message_deleted";
207
+ if (item.message?.is_unsupported)
208
+ return "message_unsupported";
209
+ if (item.message)
210
+ return item.message.is_echo ? "message_echo" : "message";
211
+ if (item.message_edit)
212
+ return "message_edit";
213
+ if (item.reaction)
214
+ return "reaction";
215
+ if (item.postback)
216
+ return "postback";
217
+ if (item.read)
218
+ return "read";
219
+ if (item.referral)
220
+ return "referral";
221
+ if (item.optin)
222
+ return "optin";
223
+ if (item.pass_thread_control || item.take_thread_control || item.request_thread_control) {
224
+ return "handover";
225
+ }
226
+ return "unknown";
227
+ }
228
+ function deliveryIdentity(event) {
229
+ const item = event.messaging;
230
+ if (!item)
231
+ return sha256Json(event);
232
+ const prefix = `${event.instagram_user_id}:${event.source}`;
233
+ if (item.message)
234
+ return `${prefix}:message:${item.message.mid}:${event.event_type}`;
235
+ if (item.message_edit) {
236
+ return `${prefix}:edit:${item.message_edit.mid}:${item.message_edit.num_edit}`;
237
+ }
238
+ if (item.reaction) {
239
+ return `${prefix}:reaction:${item.reaction.mid}:${item.sender.id}:${item.reaction.action}`;
240
+ }
241
+ if (item.postback)
242
+ return `${prefix}:postback:${item.postback.mid}:${item.sender.id}`;
243
+ if (item.read)
244
+ return `${prefix}:read:${item.read.mid}:${item.sender.id}`;
245
+ return `${prefix}:${classify(item)}:${item.timestamp}:${sha256Json(item.raw)}`;
246
+ }
247
+ function requireTimestamp(value, field) {
248
+ const timestamp = requireNumber(value, field);
249
+ if (!Number.isSafeInteger(timestamp) || timestamp < 0) {
250
+ invalid(`${field} 必须是非负毫秒时间戳安全整数`);
251
+ }
252
+ return timestamp;
253
+ }
254
+ function optionalBoolean(value, field) {
255
+ if (value === undefined)
256
+ return undefined;
257
+ if (typeof value !== "boolean")
258
+ invalid(`${field} 必须是 boolean`);
259
+ return value;
260
+ }
261
+ function invalid(message) {
262
+ throw InstagramError.invalid(message);
263
+ }
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@onebots/adapter-instagram",
3
+ "version": "0.1.0",
4
+ "description": "OneBots Instagram Messaging 与 Instagram Graph API 适配器",
5
+ "type": "module",
6
+ "main": "lib/index.js",
7
+ "types": "lib/index.d.ts",
8
+ "keywords": [
9
+ "onebots",
10
+ "instagram",
11
+ "messaging",
12
+ "meta",
13
+ "adapter"
14
+ ],
15
+ "author": "凉菜",
16
+ "license": "MIT",
17
+ "publishConfig": {
18
+ "access": "public",
19
+ "registry": "https://registry.npmjs.org"
20
+ },
21
+ "files": [
22
+ "/lib/**/*.js",
23
+ "/lib/**/*.d.ts",
24
+ "!lib/**/*.spec.*",
25
+ "!lib/**/*.test.*",
26
+ "!lib/**/__tests__/**"
27
+ ],
28
+ "dependencies": {
29
+ "@onebots/meta": "0.1.0"
30
+ },
31
+ "devDependencies": {
32
+ "@types/node": "^24.0.0",
33
+ "tsc-alias": "latest",
34
+ "typescript": "5.9.3"
35
+ },
36
+ "peerDependencies": {
37
+ "onebots": "1.2.8"
38
+ },
39
+ "repository": {
40
+ "type": "git",
41
+ "url": "git+https://github.com/lc-cn/onebots.git"
42
+ },
43
+ "scripts": {
44
+ "build": "rm -f *.tsbuildinfo && tsc --project tsconfig.json && tsc-alias -p tsconfig.json",
45
+ "clean": "rm -rf lib *.tsbuildinfo"
46
+ }
47
+ }