nodejs-insta-private-api-mqtt 1.0.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/README.md +1650 -0
- package/dist/constants/constants.js +280 -0
- package/dist/constants/index.js +41 -0
- package/dist/core/client.js +243 -0
- package/dist/core/repository.js +7 -0
- package/dist/core/request.js +212 -0
- package/dist/core/state.js +1456 -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 +30 -0
- package/dist/errors/index.js.map +1 -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 +179 -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 +39 -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 +120 -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 +56 -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 +21 -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 +186 -0
- package/dist/realtime/commands/direct.commands.js.map +1 -0
- package/dist/realtime/commands/enhanced.direct.commands.js +987 -0
- package/dist/realtime/commands/index.d.ts +2 -0
- package/dist/realtime/commands/index.js +19 -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 +73 -0
- package/dist/realtime/features/gap-handler.js +61 -0
- package/dist/realtime/features/presence.manager.js +66 -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 +381 -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 +55 -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 +449 -0
- package/dist/realtime/realtime.service.js +462 -0
- package/dist/realtime/reconnect.manager.js +94 -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 +261 -0
- package/dist/repositories/direct-thread.repository.js +247 -0
- package/dist/repositories/direct.repository.js +153 -0
- package/dist/repositories/feed.repository.js +233 -0
- package/dist/repositories/friendship.repository.js +190 -0
- package/dist/repositories/hashtag.repository.js +101 -0
- package/dist/repositories/highlights.repository.js +127 -0
- package/dist/repositories/location.repository.js +84 -0
- package/dist/repositories/media.repository.js +165 -0
- package/dist/repositories/story.repository.js +156 -0
- package/dist/repositories/upload.repository.js +167 -0
- package/dist/repositories/user.repository.js +94 -0
- package/dist/sendmedia/index.js +11 -0
- package/dist/sendmedia/sendFile.js +154 -0
- package/dist/sendmedia/sendPhoto.js +145 -0
- package/dist/sendmedia/uploadPhoto.js +175 -0
- package/dist/sendmedia/uploadfFile.js +264 -0
- package/dist/services/live.service.js +147 -0
- package/dist/services/search.service.js +116 -0
- package/dist/shared/index.js +35 -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 +437 -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/examples/listen-to-messages.js +86 -0
- package/package.json +79 -0
|
@@ -0,0 +1,247 @@
|
|
|
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
|
+
retries < this.maxRetries;
|
|
25
|
+
|
|
26
|
+
if (shouldRetry) {
|
|
27
|
+
const delay = 1000 * (retries + 1);
|
|
28
|
+
if (process.env.DEBUG) console.log(`[DEBUG] Retrying after ${delay}ms due to ${error.data?.error_type}`);
|
|
29
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
30
|
+
return this.requestWithRetry(requestFn, retries + 1);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
throw error;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Send a text message to a group thread
|
|
39
|
+
* @param {Object} options - { threadId, message }
|
|
40
|
+
*/
|
|
41
|
+
async sendToGroup(options) {
|
|
42
|
+
const { threadId, message } = options;
|
|
43
|
+
if (!threadId || !message) throw new Error('threadId and message are required');
|
|
44
|
+
|
|
45
|
+
return this.broadcast({
|
|
46
|
+
threadIds: [threadId],
|
|
47
|
+
item: 'text',
|
|
48
|
+
form: { text: message },
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Fetch a specific thread by its ID
|
|
54
|
+
* @param {string} threadId
|
|
55
|
+
*/
|
|
56
|
+
async getThread(threadId) {
|
|
57
|
+
return this.requestWithRetry(async () => {
|
|
58
|
+
const response = await this.client.request.send({
|
|
59
|
+
method: 'GET',
|
|
60
|
+
url: `/api/v1/direct_v2/threads/${threadId}/`,
|
|
61
|
+
});
|
|
62
|
+
return response.body;
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Fetch threads by participants
|
|
68
|
+
* @param {Array} recipientUsers
|
|
69
|
+
*/
|
|
70
|
+
async getByParticipants(recipientUsers) {
|
|
71
|
+
return this.requestWithRetry(async () => {
|
|
72
|
+
const response = await this.client.request.send({
|
|
73
|
+
method: 'GET',
|
|
74
|
+
url: '/api/v1/direct_v2/threads/get_by_participants/',
|
|
75
|
+
qs: { recipient_users: JSON.stringify(recipientUsers) },
|
|
76
|
+
});
|
|
77
|
+
return response.body;
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Broadcast a message to multiple threads or users
|
|
83
|
+
*/
|
|
84
|
+
async broadcast(options) {
|
|
85
|
+
const mutationToken = new Chance().guid();
|
|
86
|
+
const recipients = options.threadIds || options.userIds;
|
|
87
|
+
const recipientsType = options.threadIds ? 'thread_ids' : 'recipient_users';
|
|
88
|
+
const recipientsIds = Array.isArray(recipients) ? recipients : [recipients];
|
|
89
|
+
|
|
90
|
+
const form = {
|
|
91
|
+
action: 'send_item',
|
|
92
|
+
[recipientsType]: JSON.stringify(recipientsIds),
|
|
93
|
+
client_context: mutationToken,
|
|
94
|
+
_csrftoken: this.client.state.cookieCsrfToken,
|
|
95
|
+
device_id: this.client.state.deviceId,
|
|
96
|
+
mutation_token: mutationToken,
|
|
97
|
+
_uuid: this.client.state.uuid,
|
|
98
|
+
...options.form,
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
return this.requestWithRetry(async () => {
|
|
102
|
+
const response = await this.client.request.send({
|
|
103
|
+
url: `/api/v1/direct_v2/threads/broadcast/${options.item}/`,
|
|
104
|
+
method: 'POST',
|
|
105
|
+
form: options.signed ? this.client.request.sign(form) : form,
|
|
106
|
+
qs: options.qs,
|
|
107
|
+
});
|
|
108
|
+
return response.body;
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Mark a specific item in a thread as seen
|
|
114
|
+
*/
|
|
115
|
+
async markItemSeen(threadId, threadItemId) {
|
|
116
|
+
return this.requestWithRetry(async () => {
|
|
117
|
+
const response = await this.client.request.send({
|
|
118
|
+
url: `/api/v1/direct_v2/threads/${threadId}/items/${threadItemId}/seen/`,
|
|
119
|
+
method: 'POST',
|
|
120
|
+
form: {
|
|
121
|
+
_csrftoken: this.client.state.cookieCsrfToken,
|
|
122
|
+
_uuid: this.client.state.uuid,
|
|
123
|
+
use_unified_inbox: true,
|
|
124
|
+
action: 'mark_seen',
|
|
125
|
+
thread_id: threadId,
|
|
126
|
+
item_id: threadItemId,
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
return response.body;
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Delete an item from a thread
|
|
135
|
+
*/
|
|
136
|
+
async deleteItem(threadId, itemId) {
|
|
137
|
+
return this.requestWithRetry(async () => {
|
|
138
|
+
const response = await this.client.request.send({
|
|
139
|
+
url: `/api/v1/direct_v2/threads/${threadId}/items/${itemId}/delete/`,
|
|
140
|
+
method: 'POST',
|
|
141
|
+
form: { _csrftoken: this.client.state.cookieCsrfToken, _uuid: this.client.state.uuid },
|
|
142
|
+
});
|
|
143
|
+
return response.body;
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Approve a pending thread
|
|
149
|
+
*/
|
|
150
|
+
async approve(threadId) {
|
|
151
|
+
return this.requestWithRetry(async () => {
|
|
152
|
+
const response = await this.client.request.send({
|
|
153
|
+
url: `/api/v1/direct_v2/threads/${threadId}/approve/`,
|
|
154
|
+
method: 'POST',
|
|
155
|
+
form: { _csrftoken: this.client.state.cookieCsrfToken, _uuid: this.client.state.uuid },
|
|
156
|
+
});
|
|
157
|
+
return response.body;
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Decline a pending thread
|
|
163
|
+
*/
|
|
164
|
+
async decline(threadId) {
|
|
165
|
+
return this.requestWithRetry(async () => {
|
|
166
|
+
const response = await this.client.request.send({
|
|
167
|
+
url: `/api/v1/direct_v2/threads/${threadId}/decline/`,
|
|
168
|
+
method: 'POST',
|
|
169
|
+
form: { _csrftoken: this.client.state.cookieCsrfToken, _uuid: this.client.state.uuid },
|
|
170
|
+
});
|
|
171
|
+
return response.body;
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Mute a thread
|
|
177
|
+
*/
|
|
178
|
+
async mute(threadId) {
|
|
179
|
+
return this.requestWithRetry(async () => {
|
|
180
|
+
const response = await this.client.request.send({
|
|
181
|
+
url: `/api/v1/direct_v2/threads/${threadId}/mute/`,
|
|
182
|
+
method: 'POST',
|
|
183
|
+
form: { _csrftoken: this.client.state.cookieCsrfToken, _uuid: this.client.state.uuid },
|
|
184
|
+
});
|
|
185
|
+
return response.body;
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Unmute a thread
|
|
191
|
+
*/
|
|
192
|
+
async unmute(threadId) {
|
|
193
|
+
return this.requestWithRetry(async () => {
|
|
194
|
+
const response = await this.client.request.send({
|
|
195
|
+
url: `/api/v1/direct_v2/threads/${threadId}/unmute/`,
|
|
196
|
+
method: 'POST',
|
|
197
|
+
form: { _csrftoken: this.client.state.cookieCsrfToken, _uuid: this.client.state.uuid },
|
|
198
|
+
});
|
|
199
|
+
return response.body;
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Add users to a thread
|
|
205
|
+
*/
|
|
206
|
+
async addUser(threadId, userIds) {
|
|
207
|
+
if (!Array.isArray(userIds)) throw new Error('userIds must be an array');
|
|
208
|
+
return this.requestWithRetry(async () => {
|
|
209
|
+
const response = await this.client.request.send({
|
|
210
|
+
url: `/api/v1/direct_v2/threads/${threadId}/add_user/`,
|
|
211
|
+
method: 'POST',
|
|
212
|
+
form: { _csrftoken: this.client.state.cookieCsrfToken, user_ids: JSON.stringify(userIds), _uuid: this.client.state.uuid },
|
|
213
|
+
});
|
|
214
|
+
return response.body;
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Leave a thread
|
|
220
|
+
*/
|
|
221
|
+
async leave(threadId) {
|
|
222
|
+
return this.requestWithRetry(async () => {
|
|
223
|
+
const response = await this.client.request.send({
|
|
224
|
+
url: `/api/v1/direct_v2/threads/${threadId}/leave/`,
|
|
225
|
+
method: 'POST',
|
|
226
|
+
form: { _csrftoken: this.client.state.cookieCsrfToken, _uuid: this.client.state.uuid },
|
|
227
|
+
});
|
|
228
|
+
return response.body;
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Update thread title
|
|
234
|
+
*/
|
|
235
|
+
async updateTitle(threadId, title) {
|
|
236
|
+
return this.requestWithRetry(async () => {
|
|
237
|
+
const response = await this.client.request.send({
|
|
238
|
+
url: `/api/v1/direct_v2/threads/${threadId}/update_title/`,
|
|
239
|
+
method: 'POST',
|
|
240
|
+
form: { _csrftoken: this.client.state.cookieCsrfToken, _uuid: this.client.state.uuid, title },
|
|
241
|
+
});
|
|
242
|
+
return response.body;
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
module.exports = DirectThreadRepository;
|
|
@@ -0,0 +1,153 @@
|
|
|
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; // maximum retries for failed 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
|
+
retries < this.maxRetries;
|
|
25
|
+
|
|
26
|
+
if (shouldRetry) {
|
|
27
|
+
const delay = 1000 * (retries + 1);
|
|
28
|
+
if (process.env.DEBUG) console.log(`[DEBUG] Retrying after ${delay}ms due to ${error.data?.error_type}`);
|
|
29
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
30
|
+
return this.requestWithRetry(requestFn, retries + 1);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
throw error;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Send a text message to a user
|
|
39
|
+
*/
|
|
40
|
+
async send(options) {
|
|
41
|
+
const { to, message } = options;
|
|
42
|
+
if (!to || !message) throw new Error('Recipient (to) and message are required');
|
|
43
|
+
|
|
44
|
+
return this.requestWithRetry(async () => {
|
|
45
|
+
const user = await this.client.user.infoByUsername(to);
|
|
46
|
+
const thread = await this.client.directThread.getByParticipants([user.pk]);
|
|
47
|
+
return this.client.directThread.broadcast({
|
|
48
|
+
threadIds: [thread.thread_id],
|
|
49
|
+
item: 'text',
|
|
50
|
+
form: { text: message },
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Send an image to a user
|
|
57
|
+
*/
|
|
58
|
+
async sendImage(options) {
|
|
59
|
+
const { to, imagePath } = options;
|
|
60
|
+
if (!to || !imagePath) throw new Error('Recipient (to) and imagePath are required');
|
|
61
|
+
|
|
62
|
+
return this.requestWithRetry(async () => {
|
|
63
|
+
const imageBuffer = fs.readFileSync(imagePath);
|
|
64
|
+
const uploadResult = await this.client.upload.photo({ file: imageBuffer, uploadId: Date.now() });
|
|
65
|
+
const user = await this.client.user.infoByUsername(to);
|
|
66
|
+
const thread = await this.client.directThread.getByParticipants([user.pk]);
|
|
67
|
+
return this.client.directThread.broadcast({
|
|
68
|
+
threadIds: [thread.thread_id],
|
|
69
|
+
item: 'configure_photo',
|
|
70
|
+
form: { upload_id: uploadResult.upload_id, allow_full_aspect_ratio: true },
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Send a video to a user
|
|
77
|
+
*/
|
|
78
|
+
async sendVideo(options) {
|
|
79
|
+
const { to, videoPath } = options;
|
|
80
|
+
if (!to || !videoPath) throw new Error('Recipient (to) and videoPath are required');
|
|
81
|
+
|
|
82
|
+
return this.requestWithRetry(async () => {
|
|
83
|
+
const videoBuffer = fs.readFileSync(videoPath);
|
|
84
|
+
const uploadResult = await this.client.upload.video({ video: videoBuffer, uploadId: Date.now() });
|
|
85
|
+
const user = await this.client.user.infoByUsername(to);
|
|
86
|
+
const thread = await this.client.directThread.getByParticipants([user.pk]);
|
|
87
|
+
return this.client.directThread.broadcast({
|
|
88
|
+
threadIds: [thread.thread_id],
|
|
89
|
+
item: 'configure_video',
|
|
90
|
+
form: { upload_id: uploadResult.upload_id, video_result: 'deprecated' },
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Get inbox threads with optional pagination cursor
|
|
97
|
+
*/
|
|
98
|
+
async getInbox(cursor = null) {
|
|
99
|
+
return this.requestWithRetry(async () => {
|
|
100
|
+
const qs = cursor ? { cursor } : {};
|
|
101
|
+
const response = await this.client.request.send({ method: 'GET', url: '/api/v1/direct_v2/inbox/', qs });
|
|
102
|
+
return response.body;
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Create a group thread
|
|
108
|
+
*/
|
|
109
|
+
async createGroupThread(recipientUsers, threadTitle) {
|
|
110
|
+
if (!Array.isArray(recipientUsers) || !threadTitle) throw new Error('recipientUsers must be array and threadTitle required');
|
|
111
|
+
|
|
112
|
+
return this.requestWithRetry(async () => {
|
|
113
|
+
const response = await this.client.request.send({
|
|
114
|
+
method: 'POST',
|
|
115
|
+
url: '/api/v1/direct_v2/create_group_thread/',
|
|
116
|
+
form: this.client.request.sign({
|
|
117
|
+
_csrftoken: this.client.state.cookieCsrfToken,
|
|
118
|
+
_uuid: this.client.state.uuid,
|
|
119
|
+
_uid: this.client.state.cookieUserId,
|
|
120
|
+
recipient_users: JSON.stringify(recipientUsers),
|
|
121
|
+
thread_title: threadTitle,
|
|
122
|
+
}),
|
|
123
|
+
});
|
|
124
|
+
return response.body;
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Get ranked recipients (suggested users to send DMs)
|
|
130
|
+
*/
|
|
131
|
+
async rankedRecipients(mode = 'raven', query = '') {
|
|
132
|
+
return this.requestWithRetry(async () => {
|
|
133
|
+
const response = await this.client.request.send({
|
|
134
|
+
method: 'GET',
|
|
135
|
+
url: '/api/v1/direct_v2/ranked_recipients/',
|
|
136
|
+
qs: { mode, query, show_threads: true },
|
|
137
|
+
});
|
|
138
|
+
return response.body;
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Get online presence
|
|
144
|
+
*/
|
|
145
|
+
async getPresence() {
|
|
146
|
+
return this.requestWithRetry(async () => {
|
|
147
|
+
const response = await this.client.request.send({ method: 'GET', url: '/api/v1/direct_v2/get_presence/' });
|
|
148
|
+
return response.body;
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
module.exports = DirectRepository;
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
const Repository = require('../core/repository');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
|
|
4
|
+
class FeedRepository extends Repository {
|
|
5
|
+
async upload(options) {
|
|
6
|
+
const { imagePath, caption } = options;
|
|
7
|
+
|
|
8
|
+
// Read image file
|
|
9
|
+
const imageBuffer = fs.readFileSync(imagePath);
|
|
10
|
+
|
|
11
|
+
// Upload image first
|
|
12
|
+
const uploadResult = await this.client.upload.photo({
|
|
13
|
+
file: imageBuffer,
|
|
14
|
+
uploadId: Date.now()
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
// Configure as feed post
|
|
18
|
+
const configureResult = await this.client.upload.configurePhoto({
|
|
19
|
+
uploadId: uploadResult.upload_id,
|
|
20
|
+
caption: caption || '',
|
|
21
|
+
source_type: '4',
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
return configureResult;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async uploadVideo(options) {
|
|
28
|
+
const { videoPath, caption } = options;
|
|
29
|
+
|
|
30
|
+
// Read video file
|
|
31
|
+
const videoBuffer = fs.readFileSync(videoPath);
|
|
32
|
+
|
|
33
|
+
// Upload video first
|
|
34
|
+
const uploadResult = await this.client.upload.video({
|
|
35
|
+
video: videoBuffer,
|
|
36
|
+
uploadId: Date.now(),
|
|
37
|
+
duration_ms: options.duration_ms || 15000,
|
|
38
|
+
width: options.width || 720,
|
|
39
|
+
height: options.height || 1280,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// Configure as feed post
|
|
43
|
+
const configureResult = await this.client.upload.configureVideo({
|
|
44
|
+
uploadId: uploadResult.upload_id,
|
|
45
|
+
caption: caption || '',
|
|
46
|
+
source_type: '4',
|
|
47
|
+
length: options.duration_ms || 15000,
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
return configureResult;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async getFeed(maxId = null) {
|
|
54
|
+
const qs = {};
|
|
55
|
+
if (maxId) {
|
|
56
|
+
qs.max_id = maxId;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const response = await this.client.request.send({
|
|
60
|
+
method: 'GET',
|
|
61
|
+
url: '/api/v1/feed/timeline/',
|
|
62
|
+
qs
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
return response.body;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async getUserFeed(userId, maxId = null) {
|
|
69
|
+
const qs = {};
|
|
70
|
+
if (maxId) {
|
|
71
|
+
qs.max_id = maxId;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const response = await this.client.request.send({
|
|
75
|
+
method: 'GET',
|
|
76
|
+
url: `/api/v1/feed/user/${userId}/`,
|
|
77
|
+
qs
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
return response.body;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async getTag(tag, maxId = null) {
|
|
84
|
+
const qs = {
|
|
85
|
+
rank_token: this.client.state.uuid
|
|
86
|
+
};
|
|
87
|
+
if (maxId) {
|
|
88
|
+
qs.max_id = maxId;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const response = await this.client.request.send({
|
|
92
|
+
method: 'GET',
|
|
93
|
+
url: `/api/v1/feed/tag/${tag}/`,
|
|
94
|
+
qs
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
return response.body;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async getLiked(maxId = null) {
|
|
101
|
+
const qs = {};
|
|
102
|
+
if (maxId) {
|
|
103
|
+
qs.max_id = maxId;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const response = await this.client.request.send({
|
|
107
|
+
method: 'GET',
|
|
108
|
+
url: '/api/v1/feed/liked/',
|
|
109
|
+
qs
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
return response.body;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async getSaved(maxId = null) {
|
|
116
|
+
const qs = {};
|
|
117
|
+
if (maxId) {
|
|
118
|
+
qs.max_id = maxId;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const response = await this.client.request.send({
|
|
122
|
+
method: 'GET',
|
|
123
|
+
url: '/api/v1/feed/saved/',
|
|
124
|
+
qs
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
return response.body;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async getLocation(locationId, maxId = null) {
|
|
131
|
+
const qs = {
|
|
132
|
+
rank_token: this.client.state.uuid
|
|
133
|
+
};
|
|
134
|
+
if (maxId) {
|
|
135
|
+
qs.max_id = maxId;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const response = await this.client.request.send({
|
|
139
|
+
method: 'GET',
|
|
140
|
+
url: `/api/v1/feed/location/${locationId}/`,
|
|
141
|
+
qs
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
return response.body;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async getExploreFeed(maxId = null) {
|
|
148
|
+
const qs = {
|
|
149
|
+
is_prefetch: false,
|
|
150
|
+
is_from_promote: false,
|
|
151
|
+
timezone_offset: this.client.state.timezoneOffset,
|
|
152
|
+
session_id: this.client.state.clientSessionId,
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
if (maxId) {
|
|
156
|
+
qs.max_id = maxId;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const response = await this.client.request.send({
|
|
160
|
+
method: 'GET',
|
|
161
|
+
url: '/api/v1/discover/explore/',
|
|
162
|
+
qs
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
return response.body;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
async getReelsFeed(maxId = null) {
|
|
169
|
+
const qs = {};
|
|
170
|
+
if (maxId) {
|
|
171
|
+
qs.max_id = maxId;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const response = await this.client.request.send({
|
|
175
|
+
method: 'GET',
|
|
176
|
+
url: '/api/v1/clips/browse/',
|
|
177
|
+
qs
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
return response.body;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
async uploadCarousel(options) {
|
|
184
|
+
const { items, caption } = options;
|
|
185
|
+
const uploadIds = [];
|
|
186
|
+
|
|
187
|
+
// Upload all items first
|
|
188
|
+
for (const item of items) {
|
|
189
|
+
if (item.type === 'photo') {
|
|
190
|
+
const imageBuffer = fs.readFileSync(item.path);
|
|
191
|
+
const uploadResult = await this.client.upload.photo({
|
|
192
|
+
file: imageBuffer,
|
|
193
|
+
uploadId: Date.now() + Math.random() * 1000
|
|
194
|
+
});
|
|
195
|
+
uploadIds.push({
|
|
196
|
+
upload_id: uploadResult.upload_id,
|
|
197
|
+
source_type: '4'
|
|
198
|
+
});
|
|
199
|
+
} else if (item.type === 'video') {
|
|
200
|
+
const videoBuffer = fs.readFileSync(item.path);
|
|
201
|
+
const uploadResult = await this.client.upload.video({
|
|
202
|
+
video: videoBuffer,
|
|
203
|
+
uploadId: Date.now() + Math.random() * 1000,
|
|
204
|
+
duration_ms: item.duration_ms || 15000,
|
|
205
|
+
width: item.width || 720,
|
|
206
|
+
height: item.height || 1280,
|
|
207
|
+
});
|
|
208
|
+
uploadIds.push({
|
|
209
|
+
upload_id: uploadResult.upload_id,
|
|
210
|
+
source_type: '4'
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Configure carousel
|
|
216
|
+
const response = await this.client.request.send({
|
|
217
|
+
url: '/api/v1/media/configure_sidecar/',
|
|
218
|
+
method: 'POST',
|
|
219
|
+
form: this.client.request.sign({
|
|
220
|
+
caption: caption || '',
|
|
221
|
+
client_sidecar_id: Date.now(),
|
|
222
|
+
children_metadata: uploadIds,
|
|
223
|
+
_csrftoken: this.client.state.cookieCsrfToken,
|
|
224
|
+
_uid: this.client.state.cookieUserId,
|
|
225
|
+
_uuid: this.client.state.uuid,
|
|
226
|
+
}),
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
return response.body;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
module.exports = FeedRepository;
|