naystack 1.8.12 → 1.8.14
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/README.md +189 -31
- package/dist/auth/index.cjs.js +158 -18
- package/dist/auth/index.d.mts +3 -1
- package/dist/auth/index.d.ts +3 -1
- package/dist/auth/index.esm.js +158 -18
- package/dist/auth/instagram/index.cjs.js +158 -18
- package/dist/auth/instagram/index.d.mts +6 -1
- package/dist/auth/instagram/index.d.ts +6 -1
- package/dist/auth/instagram/index.esm.js +158 -18
- package/dist/auth/instagram/route.cjs.js +158 -18
- package/dist/auth/instagram/route.d.mts +3 -1
- package/dist/auth/instagram/route.d.ts +3 -1
- package/dist/auth/instagram/route.esm.js +158 -18
- package/dist/auth/instagram/utils.cjs.js +2 -2
- package/dist/auth/instagram/utils.d.mts +13 -2
- package/dist/auth/instagram/utils.d.ts +13 -2
- package/dist/auth/instagram/utils.esm.js +2 -2
- package/dist/file/setup.d.mts +13 -5
- package/dist/file/setup.d.ts +13 -5
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/socials/index.cjs.js +298 -72
- package/dist/socials/index.d.mts +6 -4
- package/dist/socials/index.d.ts +6 -4
- package/dist/socials/index.esm.js +296 -72
- package/dist/socials/instagram/getters.cjs.js +76 -25
- package/dist/socials/instagram/getters.d.mts +56 -6
- package/dist/socials/instagram/getters.d.ts +56 -6
- package/dist/socials/instagram/getters.esm.js +74 -25
- package/dist/socials/instagram/setters.cjs.js +192 -14
- package/dist/socials/instagram/setters.d.mts +43 -3
- package/dist/socials/instagram/setters.d.ts +43 -3
- package/dist/socials/instagram/setters.esm.js +191 -14
- package/dist/socials/instagram/types.d.mts +48 -11
- package/dist/socials/instagram/types.d.ts +48 -11
- package/dist/socials/instagram/utils.cjs.js +28 -15
- package/dist/socials/instagram/utils.d.mts +7 -18
- package/dist/socials/instagram/utils.d.ts +7 -18
- package/dist/socials/instagram/utils.esm.js +26 -14
- package/dist/socials/instagram/webhook.cjs.js +1 -1
- package/dist/socials/instagram/webhook.esm.js +1 -1
- package/dist/socials/meta/container.cjs.js +82 -0
- package/dist/socials/meta/container.d.mts +38 -0
- package/dist/socials/meta/container.d.ts +38 -0
- package/dist/socials/meta/container.esm.js +57 -0
- package/dist/socials/meta/request.cjs.js +60 -0
- package/dist/socials/meta/request.d.mts +50 -0
- package/dist/socials/meta/request.d.ts +50 -0
- package/dist/socials/meta/request.esm.js +34 -0
- package/dist/socials/meta/types.cjs.js +34 -0
- package/dist/socials/meta/types.d.mts +51 -0
- package/dist/socials/meta/types.d.ts +51 -0
- package/dist/socials/meta/types.esm.js +9 -0
- package/dist/socials/{meta-webhook.cjs.js → meta/webhook.cjs.js} +4 -4
- package/dist/socials/{meta-webhook.esm.js → meta/webhook.esm.js} +1 -1
- package/dist/socials/threads/getters.cjs.js +36 -11
- package/dist/socials/threads/getters.d.mts +23 -2
- package/dist/socials/threads/getters.d.ts +23 -2
- package/dist/socials/threads/getters.esm.js +35 -11
- package/dist/socials/threads/setters.cjs.js +150 -44
- package/dist/socials/threads/setters.d.mts +25 -34
- package/dist/socials/threads/setters.d.ts +25 -34
- package/dist/socials/threads/setters.esm.js +149 -41
- package/dist/socials/threads/types.d.mts +40 -1
- package/dist/socials/threads/types.d.ts +40 -1
- package/dist/socials/threads/utils.cjs.js +29 -11
- package/dist/socials/threads/utils.d.mts +8 -17
- package/dist/socials/threads/utils.d.ts +8 -17
- package/dist/socials/threads/utils.esm.js +27 -10
- package/dist/socials/threads/webhook.cjs.js +1 -1
- package/dist/socials/threads/webhook.esm.js +1 -1
- package/package.json +1 -1
- /package/dist/socials/{meta-webhook.d.mts → meta/webhook.d.mts} +0 -0
- /package/dist/socials/{meta-webhook.d.ts → meta/webhook.d.ts} +0 -0
|
@@ -1,27 +1,204 @@
|
|
|
1
|
+
// src/socials/meta/request.ts
|
|
2
|
+
function createGraphClient(baseURL) {
|
|
3
|
+
const buildURL = (token, path, params = {}) => {
|
|
4
|
+
const search = new URLSearchParams();
|
|
5
|
+
for (const [key, value] of Object.entries(params)) {
|
|
6
|
+
if (value !== void 0) search.set(key, String(value));
|
|
7
|
+
}
|
|
8
|
+
search.set("access_token", token);
|
|
9
|
+
return `${baseURL}/${path}?${search.toString()}`;
|
|
10
|
+
};
|
|
11
|
+
const request = async (token, path, options = {}) => {
|
|
12
|
+
const response = await fetch(buildURL(token, path, options.params), {
|
|
13
|
+
method: options.method || (options.body ? "POST" : "GET"),
|
|
14
|
+
headers: {
|
|
15
|
+
Authorization: `Bearer ${token}`,
|
|
16
|
+
...options.body ? { "Content-Type": "application/json" } : {}
|
|
17
|
+
},
|
|
18
|
+
...options.body ? { body: JSON.stringify(options.body) } : {}
|
|
19
|
+
});
|
|
20
|
+
return response.json().catch(() => null);
|
|
21
|
+
};
|
|
22
|
+
return { buildURL, request };
|
|
23
|
+
}
|
|
24
|
+
function readGraphID(label, result) {
|
|
25
|
+
if (result?.error) {
|
|
26
|
+
console.error(`[naystack] ${label} failed: ${result.error.message}`);
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
return result?.id || null;
|
|
30
|
+
}
|
|
31
|
+
|
|
1
32
|
// src/socials/instagram/utils.ts
|
|
2
|
-
|
|
3
|
-
|
|
33
|
+
var client = createGraphClient("https://graph.instagram.com/v23.0");
|
|
34
|
+
var getInstagramData = client.request;
|
|
35
|
+
|
|
36
|
+
// src/socials/instagram/getters.ts
|
|
37
|
+
var getInstagramContainerStatus = async (token, id) => {
|
|
38
|
+
const result = await getInstagramData(token, id, { params: { fields: "status_code,status" } });
|
|
39
|
+
if (!result?.status_code) return null;
|
|
40
|
+
return {
|
|
41
|
+
status: result.status_code,
|
|
42
|
+
error: result.status_code === "ERROR" ? result.status : void 0
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// src/socials/meta/container.ts
|
|
47
|
+
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
48
|
+
async function waitForContainer(getState, { intervalMS = 5e3, timeoutMS = 3e5 } = {}) {
|
|
49
|
+
const deadline = Date.now() + timeoutMS;
|
|
50
|
+
let state = await getState();
|
|
51
|
+
if (!state) {
|
|
52
|
+
await sleep(2e3);
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
while (state?.status === "IN_PROGRESS" && Date.now() < deadline) {
|
|
56
|
+
await sleep(intervalMS);
|
|
57
|
+
state = await getState();
|
|
58
|
+
}
|
|
59
|
+
return state;
|
|
4
60
|
}
|
|
5
|
-
function
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
61
|
+
function createPublisher(platform) {
|
|
62
|
+
const isReady = async (token, id, label, wait) => {
|
|
63
|
+
const state = await waitForContainer(
|
|
64
|
+
() => platform.getStatus(token, id),
|
|
65
|
+
wait
|
|
66
|
+
);
|
|
67
|
+
if (state && state.status !== "FINISHED" && state.status !== "PUBLISHED") {
|
|
68
|
+
console.error(
|
|
69
|
+
`[naystack] ${platform.name} ${label} ${id} is ${state.status}${state.error ? `: ${state.error}` : ""}`
|
|
70
|
+
);
|
|
71
|
+
return false;
|
|
12
72
|
}
|
|
13
|
-
|
|
73
|
+
return true;
|
|
74
|
+
};
|
|
75
|
+
return {
|
|
76
|
+
/** Creates a container, waits for it to finish processing, then publishes it. */
|
|
77
|
+
publish: async (token, params, wait) => {
|
|
78
|
+
const containerID = await platform.createContainer(token, params);
|
|
79
|
+
if (!containerID) return null;
|
|
80
|
+
if (!await isReady(token, containerID, "container", wait)) return null;
|
|
81
|
+
return platform.publish(token, containerID);
|
|
82
|
+
},
|
|
83
|
+
/** Creates and awaits every carousel child, resolving to their ids — or `null` if any failed. */
|
|
84
|
+
createChildren: async (token, items, wait) => {
|
|
85
|
+
const children = [];
|
|
86
|
+
for (const item of items) {
|
|
87
|
+
const childID = await platform.createContainer(token, {
|
|
88
|
+
...item,
|
|
89
|
+
is_carousel_item: true
|
|
90
|
+
});
|
|
91
|
+
if (!childID) return null;
|
|
92
|
+
if (!await isReady(token, childID, "carousel item", wait))
|
|
93
|
+
return null;
|
|
94
|
+
children.push(childID);
|
|
95
|
+
}
|
|
96
|
+
return children;
|
|
97
|
+
}
|
|
98
|
+
};
|
|
14
99
|
}
|
|
15
100
|
|
|
16
101
|
// src/socials/instagram/setters.ts
|
|
102
|
+
var publisher = createPublisher({
|
|
103
|
+
name: "Instagram",
|
|
104
|
+
getStatus: getInstagramContainerStatus,
|
|
105
|
+
createContainer: async (token, params) => readGraphID(
|
|
106
|
+
"Instagram media container",
|
|
107
|
+
await getInstagramData(token, "me/media", {
|
|
108
|
+
params,
|
|
109
|
+
method: "POST"
|
|
110
|
+
})
|
|
111
|
+
),
|
|
112
|
+
publish: async (token, creationID) => readGraphID(
|
|
113
|
+
"Instagram publish",
|
|
114
|
+
await getInstagramData(token, "me/media_publish", {
|
|
115
|
+
params: { creation_id: creationID },
|
|
116
|
+
method: "POST"
|
|
117
|
+
})
|
|
118
|
+
)
|
|
119
|
+
});
|
|
120
|
+
var mediaParams = (media) => ({
|
|
121
|
+
media_type: media.type === "video" /* Video */ ? "VIDEO" : void 0,
|
|
122
|
+
image_url: media.type === "photo" /* Photo */ ? media.url : void 0,
|
|
123
|
+
video_url: media.type === "video" /* Video */ ? media.url : void 0,
|
|
124
|
+
// Instagram accepts alt text on images only.
|
|
125
|
+
alt_text: media.type === "photo" /* Photo */ ? media.altText : void 0
|
|
126
|
+
});
|
|
127
|
+
var createInstagramPost = async (token, input) => {
|
|
128
|
+
const media = [input.media].flat();
|
|
129
|
+
const first = media[0];
|
|
130
|
+
if (!first) {
|
|
131
|
+
console.error(
|
|
132
|
+
"[naystack] Instagram posts need at least one image or video"
|
|
133
|
+
);
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
if (input.story) {
|
|
137
|
+
return publisher.publish(
|
|
138
|
+
token,
|
|
139
|
+
{
|
|
140
|
+
...mediaParams(first),
|
|
141
|
+
media_type: "STORIES",
|
|
142
|
+
alt_text: void 0
|
|
143
|
+
},
|
|
144
|
+
input.wait
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
if (media.length > 1) {
|
|
148
|
+
const children = await publisher.createChildren(
|
|
149
|
+
token,
|
|
150
|
+
media.map(mediaParams),
|
|
151
|
+
input.wait
|
|
152
|
+
);
|
|
153
|
+
if (!children) return null;
|
|
154
|
+
return publisher.publish(
|
|
155
|
+
token,
|
|
156
|
+
{
|
|
157
|
+
media_type: "CAROUSEL",
|
|
158
|
+
children: children.join(","),
|
|
159
|
+
caption: input.caption
|
|
160
|
+
},
|
|
161
|
+
input.wait
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
if (first.type === "video" /* Video */) {
|
|
165
|
+
return publisher.publish(
|
|
166
|
+
token,
|
|
167
|
+
{
|
|
168
|
+
media_type: "REELS",
|
|
169
|
+
video_url: first.url,
|
|
170
|
+
caption: input.caption,
|
|
171
|
+
cover_url: input.coverURL,
|
|
172
|
+
thumb_offset: input.thumbOffset,
|
|
173
|
+
share_to_feed: input.shareToFeed,
|
|
174
|
+
audio_name: input.audioName,
|
|
175
|
+
collaborators: input.collaborators && JSON.stringify(input.collaborators)
|
|
176
|
+
},
|
|
177
|
+
input.wait
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
return publisher.publish(
|
|
181
|
+
token,
|
|
182
|
+
{
|
|
183
|
+
image_url: first.url,
|
|
184
|
+
caption: input.caption,
|
|
185
|
+
alt_text: first.altText,
|
|
186
|
+
location_id: input.locationID
|
|
187
|
+
},
|
|
188
|
+
input.wait
|
|
189
|
+
);
|
|
190
|
+
};
|
|
17
191
|
var sendInstagramMessage = (token, to, text) => {
|
|
18
|
-
return getInstagramData(token, "me/messages",
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
192
|
+
return getInstagramData(token, "me/messages", {
|
|
193
|
+
body: {
|
|
194
|
+
recipient: { id: to },
|
|
195
|
+
message: {
|
|
196
|
+
text
|
|
197
|
+
}
|
|
22
198
|
}
|
|
23
199
|
});
|
|
24
200
|
};
|
|
25
201
|
export {
|
|
202
|
+
createInstagramPost,
|
|
26
203
|
sendInstagramMessage
|
|
27
204
|
};
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { WaitForContainerOptions } from '../meta/container.mjs';
|
|
2
|
+
import { MetaMediaType } from '../meta/types.mjs';
|
|
3
|
+
|
|
1
4
|
/**
|
|
2
5
|
* Instagram Messaging API message shape.
|
|
3
6
|
*
|
|
@@ -84,20 +87,54 @@ type InstagramConversation = {
|
|
|
84
87
|
};
|
|
85
88
|
};
|
|
86
89
|
/**
|
|
87
|
-
*
|
|
90
|
+
* One image or video to publish. The URL must be publicly reachable — Instagram
|
|
91
|
+
* downloads it server-side.
|
|
88
92
|
*
|
|
89
|
-
* @property
|
|
93
|
+
* @property url - Public URL. Images must be **JPEG**.
|
|
94
|
+
* @property type - `"image"` or `"video"`.
|
|
95
|
+
* @property altText - Accessibility description, up to 1000 characters. Images only.
|
|
90
96
|
*
|
|
91
97
|
* @category Socials
|
|
92
98
|
*/
|
|
93
|
-
type
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
99
|
+
type InstagramPostMedia = {
|
|
100
|
+
url: string;
|
|
101
|
+
type: MetaMediaType;
|
|
102
|
+
altText?: string;
|
|
103
|
+
};
|
|
104
|
+
/**
|
|
105
|
+
* Input for {@link createInstagramPost}. What gets published follows from `media`:
|
|
106
|
+
*
|
|
107
|
+
* | `media` | Result |
|
|
108
|
+
* | --- | --- |
|
|
109
|
+
* | one image | feed image |
|
|
110
|
+
* | one video | reel |
|
|
111
|
+
* | 2–10 items | carousel (counts as one post) |
|
|
112
|
+
* | any, with `story: true` | story, from the first item |
|
|
113
|
+
*
|
|
114
|
+
* @property media - One item or an array. Images are cropped to the first item's aspect ratio in a carousel.
|
|
115
|
+
* @property caption - Post caption.
|
|
116
|
+
* @property story - Publish as a 24-hour story instead of to the feed. Uses the first media item only.
|
|
117
|
+
* @property locationID - Facebook Page id to tag as the location. Single images only.
|
|
118
|
+
* @property coverURL - Reels: public URL of a cover image. Takes precedence over `thumbOffset`.
|
|
119
|
+
* @property thumbOffset - Reels: frame to use as the thumbnail, in milliseconds.
|
|
120
|
+
* @property shareToFeed - Reels: also show the reel in the feed, not only the Reels tab.
|
|
121
|
+
* @property audioName - Reels: name for the audio track. Can only be set once.
|
|
122
|
+
* @property collaborators - Reels: up to 3 public usernames to invite as collaborators.
|
|
123
|
+
* @property wait - Container polling settings.
|
|
124
|
+
*
|
|
125
|
+
* @category Socials
|
|
126
|
+
*/
|
|
127
|
+
type InstagramPostInput = {
|
|
128
|
+
media: InstagramPostMedia | InstagramPostMedia[];
|
|
129
|
+
caption?: string;
|
|
130
|
+
story?: boolean;
|
|
131
|
+
locationID?: string;
|
|
132
|
+
coverURL?: string;
|
|
133
|
+
thumbOffset?: number;
|
|
134
|
+
shareToFeed?: boolean;
|
|
135
|
+
audioName?: string;
|
|
136
|
+
collaborators?: string[];
|
|
137
|
+
wait?: WaitForContainerOptions;
|
|
101
138
|
};
|
|
102
139
|
|
|
103
|
-
export type { InstagramConversation,
|
|
140
|
+
export type { InstagramConversation, InstagramMedia, InstagramMessage, InstagramPostInput, InstagramPostMedia, InstagramUser };
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { WaitForContainerOptions } from '../meta/container.js';
|
|
2
|
+
import { MetaMediaType } from '../meta/types.js';
|
|
3
|
+
|
|
1
4
|
/**
|
|
2
5
|
* Instagram Messaging API message shape.
|
|
3
6
|
*
|
|
@@ -84,20 +87,54 @@ type InstagramConversation = {
|
|
|
84
87
|
};
|
|
85
88
|
};
|
|
86
89
|
/**
|
|
87
|
-
*
|
|
90
|
+
* One image or video to publish. The URL must be publicly reachable — Instagram
|
|
91
|
+
* downloads it server-side.
|
|
88
92
|
*
|
|
89
|
-
* @property
|
|
93
|
+
* @property url - Public URL. Images must be **JPEG**.
|
|
94
|
+
* @property type - `"image"` or `"video"`.
|
|
95
|
+
* @property altText - Accessibility description, up to 1000 characters. Images only.
|
|
90
96
|
*
|
|
91
97
|
* @category Socials
|
|
92
98
|
*/
|
|
93
|
-
type
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
99
|
+
type InstagramPostMedia = {
|
|
100
|
+
url: string;
|
|
101
|
+
type: MetaMediaType;
|
|
102
|
+
altText?: string;
|
|
103
|
+
};
|
|
104
|
+
/**
|
|
105
|
+
* Input for {@link createInstagramPost}. What gets published follows from `media`:
|
|
106
|
+
*
|
|
107
|
+
* | `media` | Result |
|
|
108
|
+
* | --- | --- |
|
|
109
|
+
* | one image | feed image |
|
|
110
|
+
* | one video | reel |
|
|
111
|
+
* | 2–10 items | carousel (counts as one post) |
|
|
112
|
+
* | any, with `story: true` | story, from the first item |
|
|
113
|
+
*
|
|
114
|
+
* @property media - One item or an array. Images are cropped to the first item's aspect ratio in a carousel.
|
|
115
|
+
* @property caption - Post caption.
|
|
116
|
+
* @property story - Publish as a 24-hour story instead of to the feed. Uses the first media item only.
|
|
117
|
+
* @property locationID - Facebook Page id to tag as the location. Single images only.
|
|
118
|
+
* @property coverURL - Reels: public URL of a cover image. Takes precedence over `thumbOffset`.
|
|
119
|
+
* @property thumbOffset - Reels: frame to use as the thumbnail, in milliseconds.
|
|
120
|
+
* @property shareToFeed - Reels: also show the reel in the feed, not only the Reels tab.
|
|
121
|
+
* @property audioName - Reels: name for the audio track. Can only be set once.
|
|
122
|
+
* @property collaborators - Reels: up to 3 public usernames to invite as collaborators.
|
|
123
|
+
* @property wait - Container polling settings.
|
|
124
|
+
*
|
|
125
|
+
* @category Socials
|
|
126
|
+
*/
|
|
127
|
+
type InstagramPostInput = {
|
|
128
|
+
media: InstagramPostMedia | InstagramPostMedia[];
|
|
129
|
+
caption?: string;
|
|
130
|
+
story?: boolean;
|
|
131
|
+
locationID?: string;
|
|
132
|
+
coverURL?: string;
|
|
133
|
+
thumbOffset?: number;
|
|
134
|
+
shareToFeed?: boolean;
|
|
135
|
+
audioName?: string;
|
|
136
|
+
collaborators?: string[];
|
|
137
|
+
wait?: WaitForContainerOptions;
|
|
101
138
|
};
|
|
102
139
|
|
|
103
|
-
export type { InstagramConversation,
|
|
140
|
+
export type { InstagramConversation, InstagramMedia, InstagramMessage, InstagramPostInput, InstagramPostMedia, InstagramUser };
|
|
@@ -20,25 +20,38 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/socials/instagram/utils.ts
|
|
21
21
|
var utils_exports = {};
|
|
22
22
|
__export(utils_exports, {
|
|
23
|
-
getInstagramData: () => getInstagramData
|
|
24
|
-
getInstagramURL: () => getInstagramURL
|
|
23
|
+
getInstagramData: () => getInstagramData
|
|
25
24
|
});
|
|
26
25
|
module.exports = __toCommonJS(utils_exports);
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
headers: {
|
|
35
|
-
Authorization: `Bearer ${token}`,
|
|
36
|
-
"Content-Type": "application/json"
|
|
26
|
+
|
|
27
|
+
// src/socials/meta/request.ts
|
|
28
|
+
function createGraphClient(baseURL) {
|
|
29
|
+
const buildURL = (token, path, params = {}) => {
|
|
30
|
+
const search = new URLSearchParams();
|
|
31
|
+
for (const [key, value] of Object.entries(params)) {
|
|
32
|
+
if (value !== void 0) search.set(key, String(value));
|
|
37
33
|
}
|
|
38
|
-
|
|
34
|
+
search.set("access_token", token);
|
|
35
|
+
return `${baseURL}/${path}?${search.toString()}`;
|
|
36
|
+
};
|
|
37
|
+
const request = async (token, path, options = {}) => {
|
|
38
|
+
const response = await fetch(buildURL(token, path, options.params), {
|
|
39
|
+
method: options.method || (options.body ? "POST" : "GET"),
|
|
40
|
+
headers: {
|
|
41
|
+
Authorization: `Bearer ${token}`,
|
|
42
|
+
...options.body ? { "Content-Type": "application/json" } : {}
|
|
43
|
+
},
|
|
44
|
+
...options.body ? { body: JSON.stringify(options.body) } : {}
|
|
45
|
+
});
|
|
46
|
+
return response.json().catch(() => null);
|
|
47
|
+
};
|
|
48
|
+
return { buildURL, request };
|
|
39
49
|
}
|
|
50
|
+
|
|
51
|
+
// src/socials/instagram/utils.ts
|
|
52
|
+
var client = createGraphClient("https://graph.instagram.com/v23.0");
|
|
53
|
+
var getInstagramData = client.request;
|
|
40
54
|
// Annotate the CommonJS export names for ESM import in node:
|
|
41
55
|
0 && (module.exports = {
|
|
42
|
-
getInstagramData
|
|
43
|
-
getInstagramURL
|
|
56
|
+
getInstagramData
|
|
44
57
|
});
|
|
@@ -1,25 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { GraphError } from '../meta/types.mjs';
|
|
2
|
+
import { GraphRequestOptions } from '../meta/request.mjs';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* @param token - Instagram access token.
|
|
7
|
-
* @param path - API path (e.g. `"me"`, `"me/media"`, `"me/conversations"`).
|
|
8
|
-
* @param params - Additional query parameters.
|
|
9
|
-
* @returns Full URL string.
|
|
10
|
-
* @category Socials
|
|
11
|
-
*/
|
|
12
|
-
declare function getInstagramURL(token: string, path: string, params: Record<string, string>): string;
|
|
13
|
-
/**
|
|
14
|
-
* Fetches JSON from the Instagram Graph API (GET or POST depending on whether `postData` is provided).
|
|
5
|
+
* Fetches JSON from the Instagram Graph API.
|
|
15
6
|
*
|
|
16
7
|
* @param token - Instagram access token.
|
|
17
8
|
* @param path - API path.
|
|
18
|
-
* @param
|
|
19
|
-
* @
|
|
20
|
-
* @returns Promise of `(T & InstagramError) | null`.
|
|
21
|
-
* @category Socials
|
|
9
|
+
* @param options - `params` (query), `method`, `body`.
|
|
10
|
+
* @returns Promise of `(T & GraphError) | null`.
|
|
22
11
|
*/
|
|
23
|
-
declare
|
|
12
|
+
declare const getInstagramData: <T>(token: string, path: string, options?: GraphRequestOptions) => Promise<(T & GraphError) | null>;
|
|
24
13
|
|
|
25
|
-
export { getInstagramData
|
|
14
|
+
export { getInstagramData };
|
|
@@ -1,25 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { GraphError } from '../meta/types.js';
|
|
2
|
+
import { GraphRequestOptions } from '../meta/request.js';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* @param token - Instagram access token.
|
|
7
|
-
* @param path - API path (e.g. `"me"`, `"me/media"`, `"me/conversations"`).
|
|
8
|
-
* @param params - Additional query parameters.
|
|
9
|
-
* @returns Full URL string.
|
|
10
|
-
* @category Socials
|
|
11
|
-
*/
|
|
12
|
-
declare function getInstagramURL(token: string, path: string, params: Record<string, string>): string;
|
|
13
|
-
/**
|
|
14
|
-
* Fetches JSON from the Instagram Graph API (GET or POST depending on whether `postData` is provided).
|
|
5
|
+
* Fetches JSON from the Instagram Graph API.
|
|
15
6
|
*
|
|
16
7
|
* @param token - Instagram access token.
|
|
17
8
|
* @param path - API path.
|
|
18
|
-
* @param
|
|
19
|
-
* @
|
|
20
|
-
* @returns Promise of `(T & InstagramError) | null`.
|
|
21
|
-
* @category Socials
|
|
9
|
+
* @param options - `params` (query), `method`, `body`.
|
|
10
|
+
* @returns Promise of `(T & GraphError) | null`.
|
|
22
11
|
*/
|
|
23
|
-
declare
|
|
12
|
+
declare const getInstagramData: <T>(token: string, path: string, options?: GraphRequestOptions) => Promise<(T & GraphError) | null>;
|
|
24
13
|
|
|
25
|
-
export { getInstagramData
|
|
14
|
+
export { getInstagramData };
|
|
@@ -1,18 +1,30 @@
|
|
|
1
|
-
// src/socials/
|
|
2
|
-
function
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
method: postData ? "POST" : "GET",
|
|
8
|
-
body: JSON.stringify(postData),
|
|
9
|
-
headers: {
|
|
10
|
-
Authorization: `Bearer ${token}`,
|
|
11
|
-
"Content-Type": "application/json"
|
|
1
|
+
// src/socials/meta/request.ts
|
|
2
|
+
function createGraphClient(baseURL) {
|
|
3
|
+
const buildURL = (token, path, params = {}) => {
|
|
4
|
+
const search = new URLSearchParams();
|
|
5
|
+
for (const [key, value] of Object.entries(params)) {
|
|
6
|
+
if (value !== void 0) search.set(key, String(value));
|
|
12
7
|
}
|
|
13
|
-
|
|
8
|
+
search.set("access_token", token);
|
|
9
|
+
return `${baseURL}/${path}?${search.toString()}`;
|
|
10
|
+
};
|
|
11
|
+
const request = async (token, path, options = {}) => {
|
|
12
|
+
const response = await fetch(buildURL(token, path, options.params), {
|
|
13
|
+
method: options.method || (options.body ? "POST" : "GET"),
|
|
14
|
+
headers: {
|
|
15
|
+
Authorization: `Bearer ${token}`,
|
|
16
|
+
...options.body ? { "Content-Type": "application/json" } : {}
|
|
17
|
+
},
|
|
18
|
+
...options.body ? { body: JSON.stringify(options.body) } : {}
|
|
19
|
+
});
|
|
20
|
+
return response.json().catch(() => null);
|
|
21
|
+
};
|
|
22
|
+
return { buildURL, request };
|
|
14
23
|
}
|
|
24
|
+
|
|
25
|
+
// src/socials/instagram/utils.ts
|
|
26
|
+
var client = createGraphClient("https://graph.instagram.com/v23.0");
|
|
27
|
+
var getInstagramData = client.request;
|
|
15
28
|
export {
|
|
16
|
-
getInstagramData
|
|
17
|
-
getInstagramURL
|
|
29
|
+
getInstagramData
|
|
18
30
|
};
|
|
@@ -24,7 +24,7 @@ __export(webhook_exports, {
|
|
|
24
24
|
});
|
|
25
25
|
module.exports = __toCommonJS(webhook_exports);
|
|
26
26
|
|
|
27
|
-
// src/socials/meta
|
|
27
|
+
// src/socials/meta/webhook.ts
|
|
28
28
|
var import_server = require("next/server");
|
|
29
29
|
var verifyWebhook = (secret) => (req) => {
|
|
30
30
|
const params = req.nextUrl.searchParams;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/socials/meta/container.ts
|
|
21
|
+
var container_exports = {};
|
|
22
|
+
__export(container_exports, {
|
|
23
|
+
createPublisher: () => createPublisher
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(container_exports);
|
|
26
|
+
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
27
|
+
async function waitForContainer(getState, { intervalMS = 5e3, timeoutMS = 3e5 } = {}) {
|
|
28
|
+
const deadline = Date.now() + timeoutMS;
|
|
29
|
+
let state = await getState();
|
|
30
|
+
if (!state) {
|
|
31
|
+
await sleep(2e3);
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
while (state?.status === "IN_PROGRESS" && Date.now() < deadline) {
|
|
35
|
+
await sleep(intervalMS);
|
|
36
|
+
state = await getState();
|
|
37
|
+
}
|
|
38
|
+
return state;
|
|
39
|
+
}
|
|
40
|
+
function createPublisher(platform) {
|
|
41
|
+
const isReady = async (token, id, label, wait) => {
|
|
42
|
+
const state = await waitForContainer(
|
|
43
|
+
() => platform.getStatus(token, id),
|
|
44
|
+
wait
|
|
45
|
+
);
|
|
46
|
+
if (state && state.status !== "FINISHED" && state.status !== "PUBLISHED") {
|
|
47
|
+
console.error(
|
|
48
|
+
`[naystack] ${platform.name} ${label} ${id} is ${state.status}${state.error ? `: ${state.error}` : ""}`
|
|
49
|
+
);
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
return true;
|
|
53
|
+
};
|
|
54
|
+
return {
|
|
55
|
+
/** Creates a container, waits for it to finish processing, then publishes it. */
|
|
56
|
+
publish: async (token, params, wait) => {
|
|
57
|
+
const containerID = await platform.createContainer(token, params);
|
|
58
|
+
if (!containerID) return null;
|
|
59
|
+
if (!await isReady(token, containerID, "container", wait)) return null;
|
|
60
|
+
return platform.publish(token, containerID);
|
|
61
|
+
},
|
|
62
|
+
/** Creates and awaits every carousel child, resolving to their ids — or `null` if any failed. */
|
|
63
|
+
createChildren: async (token, items, wait) => {
|
|
64
|
+
const children = [];
|
|
65
|
+
for (const item of items) {
|
|
66
|
+
const childID = await platform.createContainer(token, {
|
|
67
|
+
...item,
|
|
68
|
+
is_carousel_item: true
|
|
69
|
+
});
|
|
70
|
+
if (!childID) return null;
|
|
71
|
+
if (!await isReady(token, childID, "carousel item", wait))
|
|
72
|
+
return null;
|
|
73
|
+
children.push(childID);
|
|
74
|
+
}
|
|
75
|
+
return children;
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
80
|
+
0 && (module.exports = {
|
|
81
|
+
createPublisher
|
|
82
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { GraphParams, ContainerState } from './types.mjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Polling settings for a media container.
|
|
5
|
+
*
|
|
6
|
+
* @property intervalMS - Delay between status checks. Default: `5000`.
|
|
7
|
+
* @property timeoutMS - Give up after this long. Default: `300000` (Meta stops processing after ~5 min).
|
|
8
|
+
*
|
|
9
|
+
* @category Socials
|
|
10
|
+
*/
|
|
11
|
+
type WaitForContainerOptions = {
|
|
12
|
+
intervalMS?: number;
|
|
13
|
+
timeoutMS?: number;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Wires up the create → wait → publish flow Meta platforms share. Instagram and
|
|
17
|
+
* Threads differ only in their endpoints, so each builds one publisher and every
|
|
18
|
+
* post shape goes through it.
|
|
19
|
+
*
|
|
20
|
+
* @param platform.name - Used in error logs, e.g. `"Instagram"`.
|
|
21
|
+
* @param platform.createContainer - Creates a container, resolving to its id.
|
|
22
|
+
* @param platform.getStatus - Reads a container's state by id.
|
|
23
|
+
* @param platform.publish - Publishes a finished container, resolving to the post id.
|
|
24
|
+
* @returns `{ publish, createChildren }`.
|
|
25
|
+
*/
|
|
26
|
+
declare function createPublisher(platform: {
|
|
27
|
+
name: string;
|
|
28
|
+
createContainer: (token: string, params: GraphParams) => Promise<string | null>;
|
|
29
|
+
getStatus: (token: string, id: string) => Promise<ContainerState | null>;
|
|
30
|
+
publish: (token: string, creationID: string) => Promise<string | null>;
|
|
31
|
+
}): {
|
|
32
|
+
/** Creates a container, waits for it to finish processing, then publishes it. */
|
|
33
|
+
publish: (token: string, params: GraphParams, wait?: WaitForContainerOptions) => Promise<string | null>;
|
|
34
|
+
/** Creates and awaits every carousel child, resolving to their ids — or `null` if any failed. */
|
|
35
|
+
createChildren: (token: string, items: GraphParams[], wait?: WaitForContainerOptions) => Promise<string[] | null>;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export { type WaitForContainerOptions, createPublisher };
|