Skip to content

Commit 37d9311

Browse files
author
Rohan Srivastava
committed
Reply Approvals Postman Update
1 parent 7c92f16 commit 37d9311

6 files changed

Lines changed: 287 additions & 32 deletions

File tree

postman/threads-api.postman_collection.json

Lines changed: 158 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,18 @@
449449
"value": "{\"gif_id\":\"16185153483944881729\",\"provider\":\"TENOR\"}",
450450
"description": "Optional field to attach a GIF to your text posts.",
451451
"disabled": true
452+
},
453+
{
454+
"key": "is_ghost_post",
455+
"value": "true",
456+
"description": "When this optional flag is passed, a ghost post is created which will expire after 24 hours.",
457+
"disabled": true
458+
459+
},
460+
{
461+
"key": "enable_reply_approvals",
462+
"value": "true",
463+
"description": "Optional field to enable reply approvals on Threads Posts"
452464
}
453465
]
454466
},
@@ -2341,6 +2353,88 @@
23412353
}
23422354
]
23432355
},
2356+
{
2357+
"name": "Get Threads Pending Replies",
2358+
"request": {
2359+
"method": "GET",
2360+
"header": [],
2361+
"url": {
2362+
"raw": "{{api_host}}/{{thread_id}}/pending_replies?fields={{fields_replies}}&reverse=false&approval_status=pending",
2363+
"host": [
2364+
"{{api_host}}"
2365+
],
2366+
"path": [
2367+
"{{thread_id}}",
2368+
"pending_replies"
2369+
],
2370+
"query": [
2371+
{
2372+
"key": "fields",
2373+
"value": "{{fields_replies}}",
2374+
"description": "A comma-separated list of fields for replies on Threads."
2375+
},
2376+
{
2377+
"key": "reverse",
2378+
"value": "false",
2379+
"description": "Whether or not replies should be sorted in reverse chronological order. The default is true if not specified."
2380+
},
2381+
{
2382+
"key": "approval_status",
2383+
"value": "pending",
2384+
"description": "Set to \"pending\" to only fetch pending replies, set to \"ignored\" to only fetch ignored replies"
2385+
}
2386+
]
2387+
},
2388+
"description": "Use `GET` /pending_replies [endpoint](https://developers.facebook.com/docs/threads/reply-management#retrieve-pending-replies) to fetch a paginated list of all pending replies."
2389+
},
2390+
"response": [
2391+
{
2392+
"name": "Get Threads Pending Replies",
2393+
"originalRequest": {
2394+
"method": "GET",
2395+
"header": [],
2396+
"url": {
2397+
"raw": "{{api_host}}/{{thread_id}}/pending_replies?fields={{fields_replies}}&reverse=false",
2398+
"host": [
2399+
"{{api_host}}"
2400+
],
2401+
"path": [
2402+
"{{thread_id}}",
2403+
"replies"
2404+
],
2405+
"query": [
2406+
{
2407+
"key": "fields",
2408+
"value": "{{fields_replies}}",
2409+
"description": "A comma-separated list of fields for replies on Threads."
2410+
},
2411+
{
2412+
"key": "reverse",
2413+
"value": "false",
2414+
"description": "Whether or not replies should be sorted in reverse chronological order. The default is true if not specified."
2415+
},
2416+
{
2417+
"key": "approval_status",
2418+
"value": "pending",
2419+
"description": "Set to \"pending\" to only fetch pending replies, set to \"ignored\" to only fetch ignored replies"
2420+
}
2421+
]
2422+
}
2423+
},
2424+
"_postman_previewlanguage": "json",
2425+
"header": [
2426+
{
2427+
"key": "Content-Type",
2428+
"value": "application/json",
2429+
"description": "",
2430+
"type": "text"
2431+
}
2432+
],
2433+
"cookie": [],
2434+
"body": "{\n \"data\": [\n {\n \"id\": \"string\",\n \"text\": \"reply\",\n \"timestamp\": \"2024-09-17T20:54:47+0000\",\n \"media_product_type\": \"THREADS\",\n \"media_type\": \"TEXT_POST\",\n \"permalink\": \"string\",\n \"shortcode\": \"string\",\n \"username\": \"string\",\n \"is_quote_post\": false,\n \"has_replies\": false,\n \"is_reply\": true,\n \"is_reply_owned_by_me\": false,\n \"root_post\": {\n \"id\": \"string\"\n },\n \"replied_to\": {\n \"id\": \"string\"\n }\n }\n ],\n \"paging\": {\n \"cursors\": {\n \"before\": \"string\",\n \"after\": \"string\"\n }\n }\n}"
2435+
}
2436+
]
2437+
},
23442438
{
23452439
"name": "Get Threads Conversations",
23462440
"request": {
@@ -2475,6 +2569,68 @@
24752569
}
24762570
]
24772571
},
2572+
{
2573+
"name": "Manage Pending Replies",
2574+
"request": {
2575+
"method": "POST",
2576+
"header": [],
2577+
"url": {
2578+
"raw": "{{api_host}}/{{reply_thread_id}}/manage_pending_reply?approve=true",
2579+
"host": [
2580+
"{{api_host}}"
2581+
],
2582+
"path": [
2583+
"{{reply_thread_id}}",
2584+
"manage_pending_reply"
2585+
],
2586+
"query": [
2587+
{
2588+
"key": "approve",
2589+
"value": "true",
2590+
"description": "Set to true to approve a reply and set to false to ignore a reply."
2591+
}
2592+
]
2593+
},
2594+
"description": "Use `POST` /manage_pending_reply [endpoint](https://developers.facebook.com/docs/threads/reply-management#manage-pending-replies) to approve or ignore pending replies."
2595+
},
2596+
"response": [
2597+
{
2598+
"name": "Manage Pending Replies",
2599+
"originalRequest": {
2600+
"method": "POST",
2601+
"header": [],
2602+
"url": {
2603+
"raw": "{{api_host}}/{{reply_thread_id}}/manage_pending_reply?approve=true",
2604+
"host": [
2605+
"{{api_host}}"
2606+
],
2607+
"path": [
2608+
"{{reply_thread_id}}",
2609+
"manage_pending_reply"
2610+
],
2611+
"query": [
2612+
{
2613+
"key": "approve",
2614+
"value": "true",
2615+
"description": "Set to true to approve a reply and set to false to ignore a reply."
2616+
}
2617+
]
2618+
}
2619+
},
2620+
"_postman_previewlanguage": "json",
2621+
"header": [
2622+
{
2623+
"key": "Content-Type",
2624+
"value": "application/json",
2625+
"description": "",
2626+
"type": "text"
2627+
}
2628+
],
2629+
"cookie": [],
2630+
"body": "{\n \"success\": true\n}"
2631+
}
2632+
]
2633+
},
24782634
{
24792635
"name": "Respond to Replies",
24802636
"request": {
@@ -3585,11 +3741,11 @@
35853741
},
35863742
{
35873743
"key": "fields_threads",
3588-
"value": "id,media_product_type,media_type,media_url,gif_url,permalink,owner,username,text,timestamp,shortcode,thumbnail_url,children,is_quote_post,quoted_post,reposted_post,has_replies,alt_text,link_attachment_url,poll_attachment{option_a,option_b,option_c,option_d,option_a_votes_percentage,option_b_votes_percentage,option_c_votes_percentage,option_d_votes_percentage,expiration_timestamp},location_id,topic_tag"
3744+
"value": "id,media_product_type,media_type,media_url,gif_url,permalink,owner,username,text,timestamp,shortcode,thumbnail_url,children,is_quote_post,quoted_post,reposted_post,has_replies,alt_text,link_attachment_url,poll_attachment{option_a,option_b,option_c,option_d,option_a_votes_percentage,option_b_votes_percentage,option_c_votes_percentage,option_d_votes_percentage,expiration_timestamp},location_id,topic_tag,is_verified,profile_picture_url"
35893745
},
35903746
{
35913747
"key": "fields_replies",
3592-
"value": "id,text,timestamp,media_product_type,media_type,media_url,gif_url,permalink,shortcode,thumbnail_url,username,children,is_quote_post,quoted_post,reposted_post,alt_text,link_attachment_url,has_replies,is_reply,is_reply_owned_by_me,root_post,replied_to,hide_status,reply_audience,poll_attachment{option_a,option_b,option_c,option_d,option_a_votes_percentage,option_b_votes_percentage,option_c_votes_percentage,option_d_votes_percentage,expiration_timestamp},location_id,topic_tag"
3748+
"value": "id,text,timestamp,media_product_type,media_type,media_url,gif_url,permalink,shortcode,thumbnail_url,username,children,is_quote_post,quoted_post,reposted_post,alt_text,link_attachment_url,has_replies,is_reply,is_reply_owned_by_me,root_post,replied_to,hide_status,reply_audience,poll_attachment{option_a,option_b,option_c,option_d,option_a_votes_percentage,option_b_votes_percentage,option_c_votes_percentage,option_d_votes_percentage,expiration_timestamp},location_id,topic_tag,is_verified,profile_picture_url,reply_approval_status"
35933749
},
35943750
{
35953751
"key": "fields_profile",

public/img/ghost_post.png

8.48 KB
Loading

public/scripts/upload.js

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,28 @@
88
async function updateMediaType(attachmentsCount, attachmentListElem) {
99
const mediaTypeElem = document.getElementById('media-type');
1010
const spoilerMediaControl = document.querySelector('.spoiler-media-control');
11+
const ghostPostMediaControl = document.querySelector('.ghost-post-media-control');
12+
const autoPublishTextMediaControl = document.querySelector('.auto-publish-text-media-control');
1113

1214
let mediaTypeDesc;
1315
if (attachmentsCount === 0) {
1416
mediaTypeDesc = 'Text 📝';
1517
spoilerMediaControl.style.display = 'none';
18+
ghostPostMediaControl.style.display = 'block';
19+
autoPublishTextMediaControl.style.display = 'block';
1620
} else if (attachmentsCount === 1) {
1721
const singleAttachmentType =
1822
attachmentListElem.querySelector('select').value;
1923
if (singleAttachmentType === 'Image') mediaTypeDesc = 'Image 🖼️';
2024
else mediaTypeDesc = 'Video 🎬';
2125
spoilerMediaControl.style.display = 'block';
26+
ghostPostMediaControl.style.display = 'none';
27+
autoPublishTextMediaControl.style.display = 'none';
2228
} else {
2329
mediaTypeDesc = 'Carousel 🎠';
2430
spoilerMediaControl.style.display = 'block';
31+
ghostPostMediaControl.style.display = 'none';
32+
autoPublishTextMediaControl.style.display = 'none';
2533
}
2634

2735
mediaTypeElem.innerText = mediaTypeDesc;
@@ -72,10 +80,56 @@ document.addEventListener('DOMContentLoaded', async () => {
7280
const pollAttachmentOptions = document.getElementById(
7381
'poll-attachment-options'
7482
);
83+
const ghostPostMediaControl = document.querySelector('.ghost-post-media-control');
7584
if (pollAttachmentOptions.style.display === 'none') {
7685
pollAttachmentOptions.style.display = 'block';
86+
ghostPostMediaControl.style.display = 'none';
7787
} else {
7888
pollAttachmentOptions.style.display = 'none';
89+
ghostPostMediaControl.style.display = 'block';
7990
}
8091
});
92+
93+
const ghostPostCheckbox = document.querySelector('input[name="ghostPostMedia"]');
94+
const topicTagMediaControl = document.querySelector('.topic-tag-media-control');
95+
const linkAttachmentMediaControl = document.querySelector('.link-attachment-media-control');
96+
const attachImageMediaControl = document.querySelector('.attach-image-media-control');
97+
const replyOptionsMediaControl = document.querySelector('.reply-options-media-control');
98+
99+
const topicTagInput = document.getElementById('topic-tag');
100+
const linkAttachmentInput = document.getElementById('link-attachment');
101+
const replyControlSelect = document.getElementById('reply-control');
102+
const attachmentsList = document.getElementById('attachments-list');
103+
const pollAttachmentOptions = document.getElementById('poll-attachment-options');
104+
105+
if (ghostPostCheckbox) {
106+
ghostPostCheckbox.addEventListener('change', async () => {
107+
// When ghost post is checked, disable fields that are not relevant to ghost posts
108+
if (ghostPostCheckbox.checked) {
109+
topicTagMediaControl.style.display = 'none';
110+
linkAttachmentMediaControl.style.display = 'none';
111+
attachPollButton.style.display = 'none';
112+
attachImageMediaControl.style.display = 'none';
113+
replyOptionsMediaControl.style.display = 'none';
114+
115+
if (topicTagInput) topicTagInput.value = '';
116+
if (linkAttachmentInput) linkAttachmentInput.value = '';
117+
if (replyControlSelect) replyControlSelect.value = '';
118+
if (attachmentsList) attachmentsList.innerHTML = '';
119+
if (pollAttachmentOptions) {
120+
pollAttachmentOptions.style.display = 'none';
121+
const pollInputs = pollAttachmentOptions.querySelectorAll('input[type="text"]');
122+
pollInputs.forEach(input => input.value = '');
123+
}
124+
125+
await updateMediaType(0, null);
126+
} else { // When the checkbox is unchecked, enable all fields
127+
topicTagMediaControl.style.display = 'block';
128+
linkAttachmentMediaControl.style.display = 'block';
129+
attachPollButton.style.display = 'block';
130+
attachImageMediaControl.style.display = 'block';
131+
replyOptionsMediaControl.style.display = 'block';
132+
}
133+
});
134+
}
81135
});

src/index.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ const FIELD__USERNAME = 'username';
6262
const FIELD__VIEWS = 'views';
6363
const FIELD_IS_SPOILER_MEDIA = 'is_spoiler_media';
6464
const FIELD_TEXT_ENTITES = 'text_entities';
65+
const FIELD_GHOST_POST_STATUS = 'ghost_post_status';
6566

6667
const MEDIA_TYPE__CAROUSEL = 'CAROUSEL';
6768
const MEDIA_TYPE__IMAGE = 'IMAGE';
@@ -100,6 +101,7 @@ const PARAMS__TOPIC_TAG = 'topic_tag';
100101
const PARAMS__USERNAME = 'username';
101102
const PARAMS_IS_SPOILER_MEDIA = 'is_spoiler_media';
102103
const PARAMS_TEXT_ENTITES = 'text_entities';
104+
const PARAMS_IS_GHOST_POST = 'is_ghost_post';
103105

104106
// Read variables from environment
105107
require('dotenv').config();
@@ -449,6 +451,7 @@ app.post('/upload', upload.array(), async (req, res) => {
449451
pollOptionD,
450452
quotePostId,
451453
spoilerMedia,
454+
ghostPostMedia,
452455
} = req.body;
453456

454457
const params = {
@@ -479,6 +482,10 @@ app.post('/upload', upload.array(), async (req, res) => {
479482
params[PARAMS_IS_SPOILER_MEDIA] = true;
480483
}
481484

485+
if (ghostPostMedia) {
486+
params[PARAMS_IS_GHOST_POST] = true;
487+
}
488+
482489
if (pollOptionA && pollOptionB) {
483490
const pollAttachment = JSON.stringify({
484491
option_a: pollOptionA,
@@ -679,6 +686,7 @@ app.get('/threads/:threadId', loggedInUserChecker, async (req, res) => {
679686
FIELD__REPOSTED_POST,
680687
FIELD_IS_SPOILER_MEDIA,
681688
FIELD_TEXT_ENTITES,
689+
FIELD_GHOST_POST_STATUS,
682690
].join(','),
683691
},
684692
req.session.access_token
@@ -688,7 +696,7 @@ app.get('/threads/:threadId', loggedInUserChecker, async (req, res) => {
688696
const queryResponse = await axios.get(queryThreadUrl, {
689697
httpsAgent: agent,
690698
});
691-
const { poll_attachment, ...rest } = queryResponse.data;
699+
const { poll_attachment, ghost_post_status, ...rest } = queryResponse.data;
692700
data = rest;
693701

694702
if (poll_attachment) {
@@ -697,6 +705,12 @@ app.get('/threads/:threadId', loggedInUserChecker, async (req, res) => {
697705
...poll_attachment,
698706
};
699707
}
708+
709+
const is_ghost_post = ghost_post_status === 'ACTIVE';
710+
data = {
711+
...data,
712+
is_ghost_post,
713+
};
700714
} catch (e) {
701715
console.error(e?.response?.data?.error?.message ?? e.message);
702716
}

views/thread.pug

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ block content
3939
tr
4040
td Is Spoiler Media Post
4141
td #{is_spoiler_media}
42+
tr
43+
td Is Ghost Post
44+
td #{is_ghost_post}
4245
tr
4346
td GIF URL
4447
td

0 commit comments

Comments
 (0)