nodejs-insta-private-api-mqt 1.3.70
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/LICENSE +21 -0
- package/README.md +3677 -0
- package/dist/constants/constants.js +342 -0
- package/dist/constants/index.js +58 -0
- package/dist/core/client.js +419 -0
- package/dist/core/nav-chain.js +282 -0
- package/dist/core/repository.js +7 -0
- package/dist/core/request.js +390 -0
- package/dist/core/state.js +1473 -0
- package/dist/core/utils.js +786 -0
- package/dist/downloadMedia.js +381 -0
- package/dist/errors/index.d.ts +16 -0
- package/dist/errors/index.js +38 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/extend.js +167 -0
- package/dist/fbns/fbns.client.d.ts +32 -0
- package/dist/fbns/fbns.client.events.d.ts +41 -0
- package/dist/fbns/fbns.client.events.js +3 -0
- package/dist/fbns/fbns.client.events.js.map +1 -0
- package/dist/fbns/fbns.client.js +252 -0
- package/dist/fbns/fbns.client.js.map +1 -0
- package/dist/fbns/fbns.device-auth.d.ts +17 -0
- package/dist/fbns/fbns.device-auth.js +54 -0
- package/dist/fbns/fbns.device-auth.js.map +1 -0
- package/dist/fbns/fbns.types.d.ts +83 -0
- package/dist/fbns/fbns.types.js +3 -0
- package/dist/fbns/fbns.types.js.map +1 -0
- package/dist/fbns/fbns.utilities.d.ts +2 -0
- package/dist/fbns/fbns.utilities.js +79 -0
- package/dist/fbns/fbns.utilities.js.map +1 -0
- package/dist/fbns/index.d.ts +4 -0
- package/dist/fbns/index.js +21 -0
- package/dist/fbns/index.js.map +1 -0
- package/dist/index.js +139 -0
- package/dist/mqtt-shim.d.ts +96 -0
- package/dist/mqtt-shim.js +15 -0
- package/dist/mqttot/index.d.ts +4 -0
- package/dist/mqttot/index.js +21 -0
- package/dist/mqttot/index.js.map +1 -0
- package/dist/mqttot/mqttot.client.d.ts +39 -0
- package/dist/mqttot/mqttot.client.js +318 -0
- package/dist/mqttot/mqttot.client.js.map +1 -0
- package/dist/mqttot/mqttot.connect.request.packet.d.ts +7 -0
- package/dist/mqttot/mqttot.connect.request.packet.js +9 -0
- package/dist/mqttot/mqttot.connect.request.packet.js.map +1 -0
- package/dist/mqttot/mqttot.connect.response.packet.d.ts +7 -0
- package/dist/mqttot/mqttot.connect.response.packet.js +24 -0
- package/dist/mqttot/mqttot.connect.response.packet.js.map +1 -0
- package/dist/mqttot/mqttot.connection.d.ts +57 -0
- package/dist/mqttot/mqttot.connection.js +79 -0
- package/dist/mqttot/mqttot.connection.js.map +1 -0
- package/dist/package.json +59 -0
- package/dist/realtime/commands/commands.d.ts +15 -0
- package/dist/realtime/commands/commands.js +71 -0
- package/dist/realtime/commands/commands.js.map +1 -0
- package/dist/realtime/commands/direct.commands.d.ts +75 -0
- package/dist/realtime/commands/direct.commands.js +417 -0
- package/dist/realtime/commands/direct.commands.js.map +1 -0
- package/dist/realtime/commands/enhanced.direct.commands.js +1731 -0
- package/dist/realtime/commands/enhanced.direct.commands.js.bak +967 -0
- package/dist/realtime/commands/index.d.ts +2 -0
- package/dist/realtime/commands/index.js +20 -0
- package/dist/realtime/commands/index.js.map +1 -0
- package/dist/realtime/delta-sync.manager.js +293 -0
- package/dist/realtime/features/dm-sender.js +88 -0
- package/dist/realtime/features/error-handler.js +185 -0
- package/dist/realtime/features/gap-handler.js +61 -0
- package/dist/realtime/features/persistent-logger.js +186 -0
- package/dist/realtime/features/presence.manager.js +66 -0
- package/dist/realtime/features/session-health-monitor.js +345 -0
- package/dist/realtime/index.js +30 -0
- package/dist/realtime/messages/app-presence.event.d.ts +9 -0
- package/dist/realtime/messages/app-presence.event.js +3 -0
- package/dist/realtime/messages/app-presence.event.js.map +1 -0
- package/dist/realtime/messages/index.d.ts +3 -0
- package/dist/realtime/messages/index.js +20 -0
- package/dist/realtime/messages/index.js.map +1 -0
- package/dist/realtime/messages/message-sync.message.d.ts +222 -0
- package/dist/realtime/messages/message-sync.message.js +43 -0
- package/dist/realtime/messages/message-sync.message.js.map +1 -0
- package/dist/realtime/messages/realtime-sub.direct.data.d.ts +11 -0
- package/dist/realtime/messages/realtime-sub.direct.data.js +3 -0
- package/dist/realtime/messages/realtime-sub.direct.data.js.map +1 -0
- package/dist/realtime/messages/thread-update.message.d.ts +68 -0
- package/dist/realtime/messages/thread-update.message.js +3 -0
- package/dist/realtime/messages/thread-update.message.js.map +1 -0
- package/dist/realtime/mixins/index.d.ts +3 -0
- package/dist/realtime/mixins/index.js +20 -0
- package/dist/realtime/mixins/index.js.map +1 -0
- package/dist/realtime/mixins/message-sync.mixin.d.ts +8 -0
- package/dist/realtime/mixins/message-sync.mixin.js +596 -0
- package/dist/realtime/mixins/message-sync.mixin.js.map +1 -0
- package/dist/realtime/mixins/mixin.d.ts +19 -0
- package/dist/realtime/mixins/mixin.js +41 -0
- package/dist/realtime/mixins/mixin.js.map +1 -0
- package/dist/realtime/mixins/presence-typing.mixin.js +33 -0
- package/dist/realtime/mixins/realtime-sub.mixin.d.ts +8 -0
- package/dist/realtime/mixins/realtime-sub.mixin.js +181 -0
- package/dist/realtime/mixins/realtime-sub.mixin.js.map +1 -0
- package/dist/realtime/parsers/graphql-parser.js +43 -0
- package/dist/realtime/parsers/graphql.parser.d.ts +15 -0
- package/dist/realtime/parsers/graphql.parser.js +22 -0
- package/dist/realtime/parsers/graphql.parser.js.map +1 -0
- package/dist/realtime/parsers/index.d.ts +6 -0
- package/dist/realtime/parsers/index.js +23 -0
- package/dist/realtime/parsers/index.js.map +1 -0
- package/dist/realtime/parsers/iris-parser.js +43 -0
- package/dist/realtime/parsers/iris.parser.d.ts +17 -0
- package/dist/realtime/parsers/iris.parser.js +10 -0
- package/dist/realtime/parsers/iris.parser.js.map +1 -0
- package/dist/realtime/parsers/json-parser.js +43 -0
- package/dist/realtime/parsers/json.parser.d.ts +6 -0
- package/dist/realtime/parsers/json.parser.js +10 -0
- package/dist/realtime/parsers/json.parser.js.map +1 -0
- package/dist/realtime/parsers/parser.d.ts +9 -0
- package/dist/realtime/parsers/parser.js +3 -0
- package/dist/realtime/parsers/parser.js.map +1 -0
- package/dist/realtime/parsers/region-hint-parser.js +43 -0
- package/dist/realtime/parsers/region-hint.parser.d.ts +12 -0
- package/dist/realtime/parsers/region-hint.parser.js +15 -0
- package/dist/realtime/parsers/region-hint.parser.js.map +1 -0
- package/dist/realtime/parsers/skywalker-parser.js +43 -0
- package/dist/realtime/parsers/skywalker.parser.d.ts +12 -0
- package/dist/realtime/parsers/skywalker.parser.js +15 -0
- package/dist/realtime/parsers/skywalker.parser.js.map +1 -0
- package/dist/realtime/parsers-advanced.js +158 -0
- package/dist/realtime/proto/common.proto +38 -0
- package/dist/realtime/proto/direct.proto +65 -0
- package/dist/realtime/proto/ig-messages.proto +83 -0
- package/dist/realtime/proto/iris.proto +188 -0
- package/dist/realtime/proto-parser.js +195 -0
- package/dist/realtime/protocols/iris.handshake.js +74 -0
- package/dist/realtime/protocols/proto-definitions.js +80 -0
- package/dist/realtime/protocols/skywalker.protocol.js +91 -0
- package/dist/realtime/realtime.client.events.js +3 -0
- package/dist/realtime/realtime.client.js +1915 -0
- package/dist/realtime/realtime.service.js +462 -0
- package/dist/realtime/reconnect.manager.js +88 -0
- package/dist/realtime/session.manager.js +121 -0
- package/dist/realtime/subscriptions/graphql.subscription.d.ts +47 -0
- package/dist/realtime/subscriptions/graphql.subscription.js +99 -0
- package/dist/realtime/subscriptions/graphql.subscription.js.map +1 -0
- package/dist/realtime/subscriptions/index.d.ts +2 -0
- package/dist/realtime/subscriptions/index.js +19 -0
- package/dist/realtime/subscriptions/index.js.map +1 -0
- package/dist/realtime/subscriptions/skywalker.subscription.d.ts +4 -0
- package/dist/realtime/subscriptions/skywalker.subscription.js +13 -0
- package/dist/realtime/subscriptions/skywalker.subscription.js.map +1 -0
- package/dist/realtime/topic-map.js +71 -0
- package/dist/realtime/topic.js +80 -0
- package/dist/repositories/account.repository.js +575 -0
- package/dist/repositories/bloks.repository.js +70 -0
- package/dist/repositories/captcha.repository.js +44 -0
- package/dist/repositories/challenge.repository.js +120 -0
- package/dist/repositories/clip.repository.js +165 -0
- package/dist/repositories/close-friends.repository.js +46 -0
- package/dist/repositories/collection.repository.js +68 -0
- package/dist/repositories/direct-thread.repository.js +446 -0
- package/dist/repositories/direct.repository.js +232 -0
- package/dist/repositories/explore.repository.js +70 -0
- package/dist/repositories/fbsearch.repository.js +140 -0
- package/dist/repositories/feed.repository.js +245 -0
- package/dist/repositories/friendship.repository.js +296 -0
- package/dist/repositories/fundraiser.repository.js +49 -0
- package/dist/repositories/hashtag.repository.js +99 -0
- package/dist/repositories/highlights.repository.js +121 -0
- package/dist/repositories/insights.repository.js +82 -0
- package/dist/repositories/location.repository.js +84 -0
- package/dist/repositories/media.repository.js +395 -0
- package/dist/repositories/multiple-accounts.repository.js +41 -0
- package/dist/repositories/news.repository.js +35 -0
- package/dist/repositories/note.repository.js +57 -0
- package/dist/repositories/notification.repository.js +79 -0
- package/dist/repositories/share.repository.js +35 -0
- package/dist/repositories/signup.repository.js +218 -0
- package/dist/repositories/story.repository.js +290 -0
- package/dist/repositories/timeline.repository.js +60 -0
- package/dist/repositories/totp.repository.js +139 -0
- package/dist/repositories/track.repository.js +53 -0
- package/dist/repositories/upload.repository.js +204 -0
- package/dist/repositories/user.repository.js +360 -0
- package/dist/sendmedia/index.js +27 -0
- package/dist/sendmedia/sendFile.js +72 -0
- package/dist/sendmedia/sendPhoto.js +142 -0
- package/dist/sendmedia/sendRavenPhoto.js +153 -0
- package/dist/sendmedia/sendRavenVideo.js +158 -0
- package/dist/sendmedia/uploadPhoto.js +107 -0
- package/dist/sendmedia/uploadfFile.js +130 -0
- package/dist/services/live.service.js +139 -0
- package/dist/services/search.service.js +115 -0
- package/dist/shared/index.js +96 -0
- package/dist/shared/shared.js +86 -0
- package/dist/thrift/index.d.ts +3 -0
- package/dist/thrift/index.js +20 -0
- package/dist/thrift/index.js.map +1 -0
- package/dist/thrift/thrift.d.ts +59 -0
- package/dist/thrift/thrift.js +101 -0
- package/dist/thrift/thrift.js.map +1 -0
- package/dist/thrift/thrift.reading.d.ts +41 -0
- package/dist/thrift/thrift.reading.js +327 -0
- package/dist/thrift/thrift.reading.js.map +1 -0
- package/dist/thrift/thrift.writing.d.ts +44 -0
- package/dist/thrift/thrift.writing.js +342 -0
- package/dist/thrift/thrift.writing.js.map +1 -0
- package/dist/types/index.js +285 -0
- package/dist/useMultiFileAuthState.js +1768 -0
- package/dist/utils/helper-1.js +1 -0
- package/dist/utils/helper-10.js +1 -0
- package/dist/utils/helper-11.js +1 -0
- package/dist/utils/helper-12.js +1 -0
- package/dist/utils/helper-13.js +1 -0
- package/dist/utils/helper-14.js +1 -0
- package/dist/utils/helper-15.js +1 -0
- package/dist/utils/helper-16.js +1 -0
- package/dist/utils/helper-17.js +1 -0
- package/dist/utils/helper-18.js +1 -0
- package/dist/utils/helper-19.js +1 -0
- package/dist/utils/helper-2.js +1 -0
- package/dist/utils/helper-20.js +1 -0
- package/dist/utils/helper-21.js +1 -0
- package/dist/utils/helper-22.js +1 -0
- package/dist/utils/helper-23.js +1 -0
- package/dist/utils/helper-24.js +1 -0
- package/dist/utils/helper-25.js +1 -0
- package/dist/utils/helper-26.js +1 -0
- package/dist/utils/helper-27.js +1 -0
- package/dist/utils/helper-28.js +1 -0
- package/dist/utils/helper-29.js +1 -0
- package/dist/utils/helper-3.js +1 -0
- package/dist/utils/helper-30.js +1 -0
- package/dist/utils/helper-4.js +1 -0
- package/dist/utils/helper-5.js +1 -0
- package/dist/utils/helper-6.js +1 -0
- package/dist/utils/helper-7.js +1 -0
- package/dist/utils/helper-8.js +1 -0
- package/dist/utils/helper-9.js +1 -0
- package/dist/utils/index.js +280 -0
- package/dist/utils/insta-mqtt-helper.js +128 -0
- package/examples/listen-to-messages.js +86 -0
- package/package.json +82 -0
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
const Repository = require('../core/repository');
|
|
2
|
+
const Chance = require('chance');
|
|
3
|
+
|
|
4
|
+
class DirectThreadRepository extends Repository {
|
|
5
|
+
constructor(client) {
|
|
6
|
+
super(client);
|
|
7
|
+
this.maxRetries = 3; // default max retries for requests
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Generic request wrapper with retry and debug logging
|
|
12
|
+
* @param {Function} requestFn - async function performing request
|
|
13
|
+
* @param {number} retries - current retry count
|
|
14
|
+
*/
|
|
15
|
+
async requestWithRetry(requestFn, retries = 0) {
|
|
16
|
+
try {
|
|
17
|
+
if (process.env.DEBUG) console.log(`[DEBUG] Attempt #${retries + 1}`);
|
|
18
|
+
const result = await requestFn();
|
|
19
|
+
return result;
|
|
20
|
+
} catch (error) {
|
|
21
|
+
const shouldRetry =
|
|
22
|
+
(error.data?.error_type === 'server_error' ||
|
|
23
|
+
error.data?.error_type === 'rate_limited' ||
|
|
24
|
+
error.name === 'IgActionSpamError' ||
|
|
25
|
+
error.status === 503 ||
|
|
26
|
+
error.status === 429) &&
|
|
27
|
+
retries < this.maxRetries;
|
|
28
|
+
|
|
29
|
+
if (shouldRetry) {
|
|
30
|
+
const delay = 1000 * (retries + 1);
|
|
31
|
+
if (process.env.DEBUG) console.log(`[DEBUG] Retrying after ${delay}ms due to ${error.data?.error_type || error.message || error.name || error.status}`);
|
|
32
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
33
|
+
return this.requestWithRetry(requestFn, retries + 1);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
throw error;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Send a text message to a group thread
|
|
42
|
+
* @param {Object} options - { threadId, message }
|
|
43
|
+
*/
|
|
44
|
+
async sendToGroup(options) {
|
|
45
|
+
const { threadId, message } = options;
|
|
46
|
+
if (!threadId || !message) throw new Error('threadId and message are required');
|
|
47
|
+
|
|
48
|
+
return this.broadcast({
|
|
49
|
+
threadIds: [threadId],
|
|
50
|
+
item: 'text',
|
|
51
|
+
form: { text: message },
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Fetch a specific thread by its ID
|
|
57
|
+
* @param {string} threadId
|
|
58
|
+
*/
|
|
59
|
+
async getThread(threadId) {
|
|
60
|
+
return this.requestWithRetry(async () => {
|
|
61
|
+
const response = await this.client.request.send({
|
|
62
|
+
method: 'GET',
|
|
63
|
+
url: `/api/v1/direct_v2/threads/${threadId}/`,
|
|
64
|
+
});
|
|
65
|
+
return response.body || response.data || response;
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Fetch threads by participants
|
|
71
|
+
* @param {Array} recipientUsers
|
|
72
|
+
*/
|
|
73
|
+
async getByParticipants(recipientUsers) {
|
|
74
|
+
return this.requestWithRetry(async () => {
|
|
75
|
+
const response = await this.client.request.send({
|
|
76
|
+
method: 'GET',
|
|
77
|
+
url: '/api/v1/direct_v2/threads/get_by_participants/',
|
|
78
|
+
qs: { recipient_users: JSON.stringify(recipientUsers) },
|
|
79
|
+
});
|
|
80
|
+
return response.body || response.data || response;
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Broadcast a message to multiple threads or users
|
|
86
|
+
*/
|
|
87
|
+
async broadcast(options) {
|
|
88
|
+
const mutationToken = new Chance().guid();
|
|
89
|
+
const recipients = options.threadIds || options.userIds;
|
|
90
|
+
const recipientsType = options.threadIds ? 'thread_ids' : 'recipient_users';
|
|
91
|
+
const recipientsIds = Array.isArray(recipients) ? recipients : [recipients];
|
|
92
|
+
const recipientsValue = recipientsType === 'thread_ids'
|
|
93
|
+
? JSON.stringify(recipientsIds)
|
|
94
|
+
: JSON.stringify([recipientsIds]);
|
|
95
|
+
|
|
96
|
+
const form = {
|
|
97
|
+
action: 'send_item',
|
|
98
|
+
[recipientsType]: recipientsValue,
|
|
99
|
+
client_context: mutationToken,
|
|
100
|
+
_csrftoken: this.client.state.cookieCsrfToken,
|
|
101
|
+
device_id: this.client.state.deviceId,
|
|
102
|
+
mutation_token: mutationToken,
|
|
103
|
+
_uuid: this.client.state.uuid,
|
|
104
|
+
...options.form,
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
return this.requestWithRetry(async () => {
|
|
108
|
+
const payloadForm = options.signed && this.client.request && typeof this.client.request.sign === 'function'
|
|
109
|
+
? this.client.request.sign(form)
|
|
110
|
+
: form;
|
|
111
|
+
|
|
112
|
+
const response = await this.client.request.send({
|
|
113
|
+
url: `/api/v1/direct_v2/threads/broadcast/${options.item}/`,
|
|
114
|
+
method: 'POST',
|
|
115
|
+
form: payloadForm,
|
|
116
|
+
qs: options.qs,
|
|
117
|
+
});
|
|
118
|
+
return response.body || response.data || response;
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Broadcast a photo to one or more threads (uses REST configure_photo)
|
|
124
|
+
* Options:
|
|
125
|
+
* - uploadId (required) : upload_id returned from rupload
|
|
126
|
+
* - threadIds or threadId (required) : target thread id(s)
|
|
127
|
+
* - caption (optional) : caption/text to attach
|
|
128
|
+
* - signed (optional, default true) : whether to sign the form (if client.request.sign available)
|
|
129
|
+
*/
|
|
130
|
+
async broadcastPhoto(options) {
|
|
131
|
+
// normalize inputs
|
|
132
|
+
const uploadId = options.uploadId || options.upload_id || options.uploadIdStr;
|
|
133
|
+
const threadIds = options.threadIds || (options.threadId ? [options.threadId] : []);
|
|
134
|
+
const caption = options.caption || options.text || '';
|
|
135
|
+
const signed = (options.signed === undefined) ? true : !!options.signed; // default to true for media
|
|
136
|
+
const mutationToken = new Chance().guid();
|
|
137
|
+
const clientContext = mutationToken;
|
|
138
|
+
|
|
139
|
+
if (!uploadId) throw new Error('broadcastPhoto: uploadId is required');
|
|
140
|
+
if (!threadIds || !Array.isArray(threadIds) || threadIds.length === 0) throw new Error('broadcastPhoto: at least one threadId is required');
|
|
141
|
+
|
|
142
|
+
const form = {
|
|
143
|
+
action: 'send_item',
|
|
144
|
+
upload_id: uploadId.toString(),
|
|
145
|
+
thread_ids: JSON.stringify(threadIds),
|
|
146
|
+
client_context: clientContext,
|
|
147
|
+
_csrftoken: this.client.state.cookieCsrfToken,
|
|
148
|
+
mutation_token: mutationToken,
|
|
149
|
+
offline_threading_id: clientContext,
|
|
150
|
+
device_id: this.client.state.deviceId,
|
|
151
|
+
_uuid: this.client.state.uuid,
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
if (caption) {
|
|
155
|
+
// Instagram often expects 'text' for the message body
|
|
156
|
+
form.text = caption;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// perform request with retry wrapper
|
|
160
|
+
return this.requestWithRetry(async () => {
|
|
161
|
+
const payloadForm = (signed && this.client.request && typeof this.client.request.sign === 'function')
|
|
162
|
+
? this.client.request.sign(form)
|
|
163
|
+
: form;
|
|
164
|
+
|
|
165
|
+
const response = await this.client.request.send({
|
|
166
|
+
url: `/api/v1/direct_v2/threads/broadcast/configure_photo/`,
|
|
167
|
+
method: 'POST',
|
|
168
|
+
form: payloadForm,
|
|
169
|
+
qs: {
|
|
170
|
+
use_unified_inbox: true,
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
// normalize: some wrappers return { body } or axios response
|
|
175
|
+
const body = response && (response.body || response.data || response);
|
|
176
|
+
|
|
177
|
+
if (!body) {
|
|
178
|
+
const err = new Error('broadcastPhoto: empty response');
|
|
179
|
+
throw err;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// parse if string
|
|
183
|
+
let parsed = null;
|
|
184
|
+
if (typeof body === 'string') {
|
|
185
|
+
try {
|
|
186
|
+
parsed = JSON.parse(body);
|
|
187
|
+
} catch (e) {
|
|
188
|
+
parsed = null;
|
|
189
|
+
}
|
|
190
|
+
} else if (typeof body === 'object') {
|
|
191
|
+
parsed = body;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Typical success: parsed.status === 'ok' OR parsed.media/parsed.result/payload present
|
|
195
|
+
const ok = parsed && (parsed.status === 'ok' || parsed.media || parsed.result || parsed.payload || parsed.items || parsed.thread);
|
|
196
|
+
if (ok) return parsed;
|
|
197
|
+
|
|
198
|
+
// If we reach here, treat as error to trigger retry logic
|
|
199
|
+
const error = new Error('broadcastPhoto: Request failed');
|
|
200
|
+
error.response = response;
|
|
201
|
+
if (parsed) error.data = parsed;
|
|
202
|
+
throw error;
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Broadcast a raven (view-once/ephemeral) attachment to one or more threads via REST.
|
|
208
|
+
* IMPORTANT: Media must be uploaded to rupload.facebook.com/messenger_image/ first!
|
|
209
|
+
* Uses endpoint: /api/v1/direct_v2/threads/broadcast/raven_attachment/
|
|
210
|
+
*
|
|
211
|
+
* Options:
|
|
212
|
+
* - uploadId (required): upload_id from messenger_image rupload
|
|
213
|
+
* - attachmentFbid (optional): media_id from messenger_image upload response
|
|
214
|
+
* - threadIds or threadId (required): target thread id(s)
|
|
215
|
+
* - viewMode (optional, default 'replayable'): 'once' or 'replayable'
|
|
216
|
+
* - mediaType (optional, default '1'): '1' = photo, '2' = video
|
|
217
|
+
*/
|
|
218
|
+
async broadcastRaven(options) {
|
|
219
|
+
const uploadId = options.uploadId || options.upload_id;
|
|
220
|
+
const threadIds = options.threadIds || (options.threadId ? [options.threadId] : []);
|
|
221
|
+
const viewMode = options.viewMode || 'replayable';
|
|
222
|
+
const mediaType = String(options.mediaType || '1');
|
|
223
|
+
const attachmentFbid = options.attachmentFbid || options.attachment_fbid || null;
|
|
224
|
+
|
|
225
|
+
if (!uploadId) throw new Error('broadcastRaven: uploadId is required');
|
|
226
|
+
if (!threadIds || !Array.isArray(threadIds) || threadIds.length === 0) throw new Error('broadcastRaven: at least one threadId is required');
|
|
227
|
+
|
|
228
|
+
const { v4: uuidv4 } = require('uuid');
|
|
229
|
+
const clientContext = BigInt(Math.floor(Math.random() * 2**62)).toString();
|
|
230
|
+
const now = Math.floor(Date.now() / 1000);
|
|
231
|
+
|
|
232
|
+
const form = {
|
|
233
|
+
allow_multi_configures: '1',
|
|
234
|
+
recipient_users: '[]',
|
|
235
|
+
view_mode: viewMode,
|
|
236
|
+
is_shh_mode: '0',
|
|
237
|
+
camera_entry_point: '3',
|
|
238
|
+
thread_ids: JSON.stringify(threadIds.map(String)),
|
|
239
|
+
reshare_mode: 'allow_reshare',
|
|
240
|
+
original_media_type: mediaType,
|
|
241
|
+
send_attribution: 'direct_thread_camera',
|
|
242
|
+
client_context: clientContext,
|
|
243
|
+
camera_session_id: uuidv4(),
|
|
244
|
+
include_e2ee_mentioned_user_list: '1',
|
|
245
|
+
hide_from_profile_grid: 'false',
|
|
246
|
+
scene_capture_type: '',
|
|
247
|
+
timezone_offset: String(new Date().getTimezoneOffset() * -60),
|
|
248
|
+
client_shared_at: String(now),
|
|
249
|
+
configure_mode: '2',
|
|
250
|
+
source_type: '4',
|
|
251
|
+
camera_position: 'unknown',
|
|
252
|
+
_uid: String(this.client.state.cookieUserId || this.client.state.igUserId),
|
|
253
|
+
device_id: this.client.state.deviceId,
|
|
254
|
+
composition_id: uuidv4(),
|
|
255
|
+
mutation_token: clientContext,
|
|
256
|
+
_uuid: this.client.state.uuid,
|
|
257
|
+
creation_tool_info: '[]',
|
|
258
|
+
creation_surface: 'camera',
|
|
259
|
+
capture_type: 'normal',
|
|
260
|
+
audience: 'default',
|
|
261
|
+
upload_id: uploadId.toString(),
|
|
262
|
+
client_timestamp: String(now),
|
|
263
|
+
sampled: 'true',
|
|
264
|
+
media_transformation_info: JSON.stringify({
|
|
265
|
+
width: '720', height: '1280',
|
|
266
|
+
x_transform: '0', y_transform: '0',
|
|
267
|
+
zoom: '1.0', rotation: '0.0', background_coverage: '0.0',
|
|
268
|
+
}),
|
|
269
|
+
edits: JSON.stringify({ filter_type: 0, filter_strength: 0.5, crop_original_size: [720.0, 1280.0] }),
|
|
270
|
+
extra: JSON.stringify({ source_width: 720, source_height: 1280 }),
|
|
271
|
+
device: JSON.stringify({
|
|
272
|
+
manufacturer: this.client.state.devicePayload?.manufacturer || 'samsung',
|
|
273
|
+
model: this.client.state.devicePayload?.model || 'SM-S938B',
|
|
274
|
+
android_version: this.client.state.devicePayload?.android_version || 35,
|
|
275
|
+
android_release: this.client.state.devicePayload?.android_release || '15',
|
|
276
|
+
}),
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
if (attachmentFbid) {
|
|
280
|
+
form.attachment_fbid = String(attachmentFbid);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return this.requestWithRetry(async () => {
|
|
284
|
+
const payloadForm = (this.client.request && typeof this.client.request.sign === 'function')
|
|
285
|
+
? this.client.request.sign(form)
|
|
286
|
+
: form;
|
|
287
|
+
|
|
288
|
+
const response = await this.client.request.send({
|
|
289
|
+
url: `/api/v1/direct_v2/threads/broadcast/raven_attachment/`,
|
|
290
|
+
method: 'POST',
|
|
291
|
+
form: payloadForm,
|
|
292
|
+
qs: {
|
|
293
|
+
use_unified_inbox: true,
|
|
294
|
+
},
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
const body = response && (response.body || response.data || response);
|
|
298
|
+
if (!body) {
|
|
299
|
+
throw new Error('broadcastRaven: empty response');
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
let parsed = null;
|
|
303
|
+
if (typeof body === 'string') {
|
|
304
|
+
try { parsed = JSON.parse(body); } catch (e) { parsed = null; }
|
|
305
|
+
} else if (typeof body === 'object') {
|
|
306
|
+
parsed = body;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const ok = parsed && (parsed.status === 'ok' || parsed.media || parsed.result || parsed.payload || parsed.items || parsed.thread);
|
|
310
|
+
if (ok) return parsed;
|
|
311
|
+
|
|
312
|
+
const error = new Error('broadcastRaven: Request failed');
|
|
313
|
+
error.response = response;
|
|
314
|
+
if (parsed) error.data = parsed;
|
|
315
|
+
throw error;
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Mark a specific item in a thread as seen
|
|
321
|
+
*/
|
|
322
|
+
async markItemSeen(threadId, threadItemId) {
|
|
323
|
+
return this.requestWithRetry(async () => {
|
|
324
|
+
const response = await this.client.request.send({
|
|
325
|
+
url: `/api/v1/direct_v2/threads/${threadId}/items/${threadItemId}/seen/`,
|
|
326
|
+
method: 'POST',
|
|
327
|
+
form: {
|
|
328
|
+
_uuid: this.client.state.uuid,
|
|
329
|
+
use_unified_inbox: true,
|
|
330
|
+
action: 'mark_seen',
|
|
331
|
+
thread_id: threadId,
|
|
332
|
+
item_id: threadItemId,
|
|
333
|
+
},
|
|
334
|
+
});
|
|
335
|
+
return response.body || response.data || response;
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Delete an item from a thread
|
|
341
|
+
*/
|
|
342
|
+
async deleteItem(threadId, itemId) {
|
|
343
|
+
return this.requestWithRetry(async () => {
|
|
344
|
+
const response = await this.client.request.send({
|
|
345
|
+
url: `/api/v1/direct_v2/threads/${threadId}/items/${itemId}/delete/`,
|
|
346
|
+
method: 'POST',
|
|
347
|
+
});
|
|
348
|
+
return response.body || response.data || response;
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Approve a pending thread
|
|
354
|
+
*/
|
|
355
|
+
async approve(threadId) {
|
|
356
|
+
return this.requestWithRetry(async () => {
|
|
357
|
+
const response = await this.client.request.send({
|
|
358
|
+
url: `/api/v1/direct_v2/threads/${threadId}/approve/`,
|
|
359
|
+
method: 'POST',
|
|
360
|
+
});
|
|
361
|
+
return response.body || response.data || response;
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Decline a pending thread
|
|
367
|
+
*/
|
|
368
|
+
async decline(threadId) {
|
|
369
|
+
return this.requestWithRetry(async () => {
|
|
370
|
+
const response = await this.client.request.send({
|
|
371
|
+
url: `/api/v1/direct_v2/threads/${threadId}/decline/`,
|
|
372
|
+
method: 'POST',
|
|
373
|
+
});
|
|
374
|
+
return response.body || response.data || response;
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Mute a thread
|
|
380
|
+
*/
|
|
381
|
+
async mute(threadId) {
|
|
382
|
+
return this.requestWithRetry(async () => {
|
|
383
|
+
const response = await this.client.request.send({
|
|
384
|
+
url: `/api/v1/direct_v2/threads/${threadId}/mute/`,
|
|
385
|
+
method: 'POST',
|
|
386
|
+
});
|
|
387
|
+
return response.body || response.data || response;
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Unmute a thread
|
|
393
|
+
*/
|
|
394
|
+
async unmute(threadId) {
|
|
395
|
+
return this.requestWithRetry(async () => {
|
|
396
|
+
const response = await this.client.request.send({
|
|
397
|
+
url: `/api/v1/direct_v2/threads/${threadId}/unmute/`,
|
|
398
|
+
method: 'POST',
|
|
399
|
+
});
|
|
400
|
+
return response.body || response.data || response;
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Add users to a thread
|
|
406
|
+
*/
|
|
407
|
+
async addUser(threadId, userIds) {
|
|
408
|
+
if (!Array.isArray(userIds)) throw new Error('userIds must be an array');
|
|
409
|
+
return this.requestWithRetry(async () => {
|
|
410
|
+
const response = await this.client.request.send({
|
|
411
|
+
url: `/api/v1/direct_v2/threads/${threadId}/add_user/`,
|
|
412
|
+
method: 'POST',
|
|
413
|
+
});
|
|
414
|
+
return response.body || response.data || response;
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* Leave a thread
|
|
420
|
+
*/
|
|
421
|
+
async leave(threadId) {
|
|
422
|
+
return this.requestWithRetry(async () => {
|
|
423
|
+
const response = await this.client.request.send({
|
|
424
|
+
url: `/api/v1/direct_v2/threads/${threadId}/leave/`,
|
|
425
|
+
method: 'POST',
|
|
426
|
+
});
|
|
427
|
+
return response.body || response.data || response;
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Update thread title
|
|
433
|
+
*/
|
|
434
|
+
async updateTitle(threadId, title) {
|
|
435
|
+
return this.requestWithRetry(async () => {
|
|
436
|
+
const response = await this.client.request.send({
|
|
437
|
+
url: `/api/v1/direct_v2/threads/${threadId}/update_title/`,
|
|
438
|
+
method: 'POST',
|
|
439
|
+
});
|
|
440
|
+
return response.body || response.data || response;
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
module.exports = DirectThreadRepository;
|
|
446
|
+
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
const Repository = require('../core/repository');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
|
|
4
|
+
class DirectRepository extends Repository {
|
|
5
|
+
constructor(client) {
|
|
6
|
+
super(client);
|
|
7
|
+
this.maxRetries = 3;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
async requestWithRetry(requestFn, retries = 0) {
|
|
11
|
+
try {
|
|
12
|
+
const result = await requestFn();
|
|
13
|
+
return result;
|
|
14
|
+
} catch (error) {
|
|
15
|
+
const shouldRetry =
|
|
16
|
+
(error.data?.error_type === 'server_error' ||
|
|
17
|
+
error.data?.error_type === 'rate_limited') &&
|
|
18
|
+
retries < this.maxRetries;
|
|
19
|
+
if (shouldRetry) {
|
|
20
|
+
const delay = 1000 * (retries + 1);
|
|
21
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
22
|
+
return this.requestWithRetry(requestFn, retries + 1);
|
|
23
|
+
}
|
|
24
|
+
throw error;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async send(options) {
|
|
29
|
+
const { to, message } = options;
|
|
30
|
+
if (!to || !message) throw new Error('Recipient (to) and message are required');
|
|
31
|
+
|
|
32
|
+
return this.requestWithRetry(async () => {
|
|
33
|
+
const user = await this.client.user.infoByUsername(to);
|
|
34
|
+
const thread = await this.client.directThread.getByParticipants([user.pk]);
|
|
35
|
+
return this.client.directThread.broadcast({
|
|
36
|
+
threadIds: [thread.thread_id],
|
|
37
|
+
item: 'text',
|
|
38
|
+
form: { text: message },
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async sendToUserId(userId, message) {
|
|
44
|
+
return this.requestWithRetry(async () => {
|
|
45
|
+
return this.client.directThread.broadcast({
|
|
46
|
+
userIds: [userId],
|
|
47
|
+
item: 'text',
|
|
48
|
+
form: { text: message },
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async sendImage(options) {
|
|
54
|
+
const { to, imagePath } = options;
|
|
55
|
+
if (!to || !imagePath) throw new Error('Recipient (to) and imagePath are required');
|
|
56
|
+
|
|
57
|
+
return this.requestWithRetry(async () => {
|
|
58
|
+
const imageBuffer = fs.readFileSync(imagePath);
|
|
59
|
+
const uploadResult = await this.client.upload.photo({ file: imageBuffer, uploadId: Date.now() });
|
|
60
|
+
const user = await this.client.user.infoByUsername(to);
|
|
61
|
+
const thread = await this.client.directThread.getByParticipants([user.pk]);
|
|
62
|
+
return this.client.directThread.broadcast({
|
|
63
|
+
threadIds: [thread.thread_id],
|
|
64
|
+
item: 'configure_photo',
|
|
65
|
+
form: { upload_id: uploadResult.upload_id, allow_full_aspect_ratio: true },
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async sendVideo(options) {
|
|
71
|
+
const { to, videoPath } = options;
|
|
72
|
+
if (!to || !videoPath) throw new Error('Recipient (to) and videoPath are required');
|
|
73
|
+
|
|
74
|
+
return this.requestWithRetry(async () => {
|
|
75
|
+
const videoBuffer = fs.readFileSync(videoPath);
|
|
76
|
+
const uploadResult = await this.client.upload.video({ video: videoBuffer, uploadId: Date.now() });
|
|
77
|
+
const user = await this.client.user.infoByUsername(to);
|
|
78
|
+
const thread = await this.client.directThread.getByParticipants([user.pk]);
|
|
79
|
+
return this.client.directThread.broadcast({
|
|
80
|
+
threadIds: [thread.thread_id],
|
|
81
|
+
item: 'configure_video',
|
|
82
|
+
form: { upload_id: uploadResult.upload_id, video_result: 'deprecated' },
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async sendLink(options) {
|
|
88
|
+
const { to, text, urls } = options;
|
|
89
|
+
return this.requestWithRetry(async () => {
|
|
90
|
+
const user = await this.client.user.infoByUsername(to);
|
|
91
|
+
const thread = await this.client.directThread.getByParticipants([user.pk]);
|
|
92
|
+
return this.client.directThread.broadcast({
|
|
93
|
+
threadIds: [thread.thread_id],
|
|
94
|
+
item: 'link',
|
|
95
|
+
form: {
|
|
96
|
+
link_text: text || '',
|
|
97
|
+
link_urls: JSON.stringify(urls || []),
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async sendMediaShare(options) {
|
|
104
|
+
const { to, mediaId } = options;
|
|
105
|
+
return this.requestWithRetry(async () => {
|
|
106
|
+
const user = await this.client.user.infoByUsername(to);
|
|
107
|
+
const thread = await this.client.directThread.getByParticipants([user.pk]);
|
|
108
|
+
return this.client.directThread.broadcast({
|
|
109
|
+
threadIds: [thread.thread_id],
|
|
110
|
+
item: 'media_share',
|
|
111
|
+
form: { media_id: mediaId },
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async sendProfile(options) {
|
|
117
|
+
const { to, profileUserId } = options;
|
|
118
|
+
return this.requestWithRetry(async () => {
|
|
119
|
+
const user = await this.client.user.infoByUsername(to);
|
|
120
|
+
const thread = await this.client.directThread.getByParticipants([user.pk]);
|
|
121
|
+
return this.client.directThread.broadcast({
|
|
122
|
+
threadIds: [thread.thread_id],
|
|
123
|
+
item: 'profile',
|
|
124
|
+
form: { profile_user_id: profileUserId },
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async sendHashtag(options) {
|
|
130
|
+
const { to, hashtag, text } = options;
|
|
131
|
+
return this.requestWithRetry(async () => {
|
|
132
|
+
const user = await this.client.user.infoByUsername(to);
|
|
133
|
+
const thread = await this.client.directThread.getByParticipants([user.pk]);
|
|
134
|
+
return this.client.directThread.broadcast({
|
|
135
|
+
threadIds: [thread.thread_id],
|
|
136
|
+
item: 'hashtag',
|
|
137
|
+
form: {
|
|
138
|
+
hashtag,
|
|
139
|
+
text: text || '',
|
|
140
|
+
},
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async sendLocation(options) {
|
|
146
|
+
const { to, locationId, text } = options;
|
|
147
|
+
return this.requestWithRetry(async () => {
|
|
148
|
+
const user = await this.client.user.infoByUsername(to);
|
|
149
|
+
const thread = await this.client.directThread.getByParticipants([user.pk]);
|
|
150
|
+
return this.client.directThread.broadcast({
|
|
151
|
+
threadIds: [thread.thread_id],
|
|
152
|
+
item: 'location',
|
|
153
|
+
form: {
|
|
154
|
+
venue_id: locationId,
|
|
155
|
+
text: text || '',
|
|
156
|
+
},
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
async getInbox(cursor = null, limit = 20) {
|
|
162
|
+
return this.requestWithRetry(async () => {
|
|
163
|
+
const qs = { persistentBadging: true, limit };
|
|
164
|
+
if (cursor) qs.cursor = cursor;
|
|
165
|
+
const response = await this.client.request.send({ method: 'GET', url: '/api/v1/direct_v2/inbox/', qs });
|
|
166
|
+
return response.body;
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
async getPendingInbox(cursor = null) {
|
|
171
|
+
return this.requestWithRetry(async () => {
|
|
172
|
+
const qs = cursor ? { cursor } : {};
|
|
173
|
+
const response = await this.client.request.send({ method: 'GET', url: '/api/v1/direct_v2/pending_inbox/', qs });
|
|
174
|
+
return response.body;
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
async createGroupThread(recipientUsers, threadTitle) {
|
|
179
|
+
if (!Array.isArray(recipientUsers) || !threadTitle) throw new Error('recipientUsers must be array and threadTitle required');
|
|
180
|
+
|
|
181
|
+
return this.requestWithRetry(async () => {
|
|
182
|
+
const response = await this.client.request.send({
|
|
183
|
+
method: 'POST',
|
|
184
|
+
url: '/api/v1/direct_v2/create_group_thread/',
|
|
185
|
+
form: this.client.request.sign({
|
|
186
|
+
_uuid: this.client.state.uuid,
|
|
187
|
+
_uid: this.client.state.cookieUserId,
|
|
188
|
+
recipient_users: JSON.stringify(recipientUsers),
|
|
189
|
+
thread_title: threadTitle,
|
|
190
|
+
}),
|
|
191
|
+
});
|
|
192
|
+
return response.body;
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
async rankedRecipients(mode = 'raven', query = '') {
|
|
197
|
+
return this.requestWithRetry(async () => {
|
|
198
|
+
const response = await this.client.request.send({
|
|
199
|
+
method: 'GET',
|
|
200
|
+
url: '/api/v1/direct_v2/ranked_recipients/',
|
|
201
|
+
qs: { mode, query, show_threads: true },
|
|
202
|
+
});
|
|
203
|
+
return response.body;
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
async getPresence() {
|
|
208
|
+
return this.requestWithRetry(async () => {
|
|
209
|
+
const response = await this.client.request.send({ method: 'GET', url: '/api/v1/direct_v2/get_presence/' });
|
|
210
|
+
return response.body;
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
async markAsSeen(threadId, itemId) {
|
|
215
|
+
return this.client.directThread.markItemSeen(threadId, itemId);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
async hideThread(threadId) {
|
|
219
|
+
return this.requestWithRetry(async () => {
|
|
220
|
+
const response = await this.client.request.send({
|
|
221
|
+
method: 'POST',
|
|
222
|
+
url: `/api/v1/direct_v2/threads/${threadId}/hide/`,
|
|
223
|
+
form: this.client.request.sign({
|
|
224
|
+
_uuid: this.client.state.uuid,
|
|
225
|
+
}),
|
|
226
|
+
});
|
|
227
|
+
return response.body;
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
module.exports = DirectRepository;
|