@dongdev/fca-unofficial 2.0.30 → 2.0.32
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/CHANGELOG.md +6 -0
- package/package.json +1 -1
- package/src/api/action/refreshFb_dtsg.js +23 -46
- package/src/api/messaging/sendMessage.js +235 -341
- package/src/api/messaging/sendMessageMqtt.js +0 -323
package/CHANGELOG.md
CHANGED
package/package.json
CHANGED
|
@@ -3,67 +3,44 @@
|
|
|
3
3
|
const log = require("npmlog");
|
|
4
4
|
const { getFrom } = require("../../utils/constants");
|
|
5
5
|
const { get } = require("../../utils/request")
|
|
6
|
-
|
|
6
|
+
const { getType } = require("../../utils/format");
|
|
7
|
+
module.exports = function (defaultFuncs, api, ctx) {
|
|
7
8
|
return function refreshFb_dtsg(obj, callback) {
|
|
8
|
-
|
|
9
|
-
const returnPromise = new Promise((resolve, reject) => {
|
|
10
|
-
resolveFunc = resolve;
|
|
11
|
-
rejectFunc = reject;
|
|
12
|
-
});
|
|
13
|
-
if (
|
|
14
|
-
getType(obj) === "Function" ||
|
|
15
|
-
getType(obj) === "AsyncFunction"
|
|
16
|
-
) {
|
|
9
|
+
if (typeof obj === "function") {
|
|
17
10
|
callback = obj;
|
|
18
11
|
obj = {};
|
|
19
12
|
}
|
|
20
13
|
if (!obj) obj = {};
|
|
21
14
|
if (getType(obj) !== "Object") {
|
|
22
|
-
throw
|
|
23
|
-
"The first parameter must be an object or a callback function"
|
|
24
|
-
);
|
|
15
|
+
throw new CustomError("The first parameter must be an object or a callback function");
|
|
25
16
|
}
|
|
17
|
+
let resolveFunc, rejectFunc;
|
|
18
|
+
const returnPromise = new Promise((resolve, reject) => {
|
|
19
|
+
resolveFunc = resolve;
|
|
20
|
+
rejectFunc = reject;
|
|
21
|
+
});
|
|
26
22
|
if (!callback) {
|
|
27
|
-
callback = (err, data) =>
|
|
23
|
+
callback = (err, data) => err ? rejectFunc(err) : resolveFunc(data);
|
|
28
24
|
}
|
|
29
25
|
if (Object.keys(obj).length === 0) {
|
|
30
|
-
get(
|
|
31
|
-
"
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
ctx
|
|
35
|
-
{
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
)
|
|
39
|
-
.then(resData => {
|
|
40
|
-
const fb_dtsg = getFrom(
|
|
41
|
-
resData.body,
|
|
42
|
-
'["DTSGInitData",[],{"token":"',
|
|
43
|
-
'","'
|
|
44
|
-
);
|
|
45
|
-
const jazoest = getFrom(resData.body, "jazoest=", '",');
|
|
46
|
-
if (!fb_dtsg) {
|
|
47
|
-
throw Error(
|
|
48
|
-
"Could not find fb_dtsg in HTML after requesting Facebook."
|
|
49
|
-
);
|
|
50
|
-
}
|
|
51
|
-
ctx.fb_dtsg = fb_dtsg;
|
|
52
|
-
ctx.jazoest = jazoest;
|
|
53
|
-
callback(null, {
|
|
54
|
-
data: { fb_dtsg, jazoest },
|
|
55
|
-
message: "Refreshed fb_dtsg and jazoest"
|
|
56
|
-
});
|
|
57
|
-
})
|
|
58
|
-
.catch(err => {
|
|
59
|
-
log.error("refreshFb_dtsg", err);
|
|
60
|
-
callback(err);
|
|
26
|
+
get("https://www.facebook.com/", ctx.jar, null, ctx.globalOptions, { noRef: true }).then(({ data }) => {
|
|
27
|
+
const fb_dtsg = getFrom(data, '["DTSGInitData",[],{"token":"', '","');
|
|
28
|
+
const jazoest = getFrom(data, "jazoest=", '",');
|
|
29
|
+
if (!fb_dtsg) throw new Error("Could not find fb_dtsg in HTML after requesting Facebook.");
|
|
30
|
+
Object.assign(ctx, { fb_dtsg, jazoest });
|
|
31
|
+
callback(null, {
|
|
32
|
+
data: { fb_dtsg, jazoest },
|
|
33
|
+
message: "Refreshed fb_dtsg and jazoest",
|
|
61
34
|
});
|
|
35
|
+
}).catch(err => {
|
|
36
|
+
console.error("refreshFb_dtsg", err);
|
|
37
|
+
callback(err);
|
|
38
|
+
});
|
|
62
39
|
} else {
|
|
63
40
|
Object.assign(ctx, obj);
|
|
64
41
|
callback(null, {
|
|
65
42
|
data: obj,
|
|
66
|
-
message:
|
|
43
|
+
message: `Refreshed ${Object.keys(obj).join(", ")}`,
|
|
67
44
|
});
|
|
68
45
|
}
|
|
69
46
|
return returnPromise;
|
|
@@ -1,379 +1,273 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Create by Donix-VN (DongDev)
|
|
3
|
+
* Don't change credit
|
|
4
|
+
* Send a message using MQTT.
|
|
5
|
+
* @param {string} text - The text of the message to send.
|
|
6
|
+
* @param {string} threadID - The ID of the thread to send the message to.
|
|
7
|
+
* @param {string} [msgReplace] - Optional. The message ID of the message to replace.
|
|
8
|
+
* @param {Array<Buffer|Stream>} [attachments] - Optional. The attachments to send with the message.
|
|
9
|
+
* @param {function} [callback] - Optional. The callback function to call when the message is sent.
|
|
10
|
+
* @returns {Promise<object>} A promise that resolves with the bodies of the sent message.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
"use strict";
|
|
4
14
|
const log = require("npmlog");
|
|
5
|
-
const allowedProperties = {
|
|
6
|
-
attachment: true,
|
|
7
|
-
url: true,
|
|
8
|
-
sticker: true,
|
|
9
|
-
emoji: true,
|
|
10
|
-
emojiSize: true,
|
|
11
|
-
body: true,
|
|
12
|
-
mentions: true,
|
|
13
|
-
location: true,
|
|
14
|
-
asPage: true
|
|
15
|
-
};
|
|
16
|
-
const { isReadableStream } = require("../../utils/constants");
|
|
17
15
|
const { parseAndCheckLogin } = require("../../utils/client");
|
|
18
|
-
const { getType
|
|
16
|
+
const { getType } = require("../../utils/format");
|
|
17
|
+
const { isReadableStream } = require("../../utils/constants");
|
|
18
|
+
const { generateOfflineThreadingID } = require("../../utils/format");
|
|
19
19
|
|
|
20
20
|
module.exports = function (defaultFuncs, api, ctx) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
if (Buffer.isBuffer(input)) return Readable.from(input);
|
|
24
|
-
if (typeof input === "string" && fs.existsSync(input) && fs.statSync(input).isFile()) return fs.createReadStream(path.resolve(input));
|
|
25
|
-
throw { error: "Unsupported attachment input. Use stream/buffer/filepath." };
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function uploadAttachment(attachments, callback) {
|
|
29
|
-
const uploads = [];
|
|
30
|
-
for (let i = 0; i < attachments.length; i++) {
|
|
31
|
-
if (!isReadableStream(attachments[i])) throw { error: "Attachment should be a readable stream and not " + getType(attachments[i]) + "." };
|
|
32
|
-
const form = { upload_1024: attachments[i], voice_clip: "true" };
|
|
33
|
-
uploads.push(
|
|
34
|
-
defaultFuncs
|
|
35
|
-
.postFormData("https://upload.facebook.com/ajax/mercury/upload.php", ctx.jar, form, {}, {})
|
|
36
|
-
.then(parseAndCheckLogin(ctx, defaultFuncs))
|
|
37
|
-
.then(resData => {
|
|
38
|
-
if (resData.error) throw resData;
|
|
39
|
-
return resData.payload.metadata[0];
|
|
40
|
-
})
|
|
41
|
-
);
|
|
42
|
-
}
|
|
43
|
-
Promise.all(uploads)
|
|
44
|
-
.then(resData => callback(null, resData))
|
|
45
|
-
.catch(err => {
|
|
46
|
-
log.error("uploadAttachment", err);
|
|
47
|
-
callback(err);
|
|
48
|
-
});
|
|
49
|
-
}
|
|
21
|
+
const hasLinks = s => typeof s === "string" && /(https?:\/\/|www\.|t\.me\/|fb\.me\/|youtu\.be\/|facebook\.com\/|youtube\.com\/)/i.test(s);
|
|
22
|
+
const emojiSizes = { small: 1, medium: 2, large: 3 };
|
|
50
23
|
|
|
51
|
-
function
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function sleep(ms) {
|
|
68
|
-
return new Promise(r => setTimeout(r, ms));
|
|
24
|
+
async function uploadAttachment(streams) {
|
|
25
|
+
const uploads = streams.map(stream => {
|
|
26
|
+
if (!isReadableStream(stream)) throw { error: "Attachment should be a readable stream and not " + getType(stream) + "." };
|
|
27
|
+
const form = { upload_1024: stream, voice_clip: "true" };
|
|
28
|
+
return defaultFuncs
|
|
29
|
+
.postFormData("https://upload.facebook.com/ajax/mercury/upload.php", ctx.jar, form, {})
|
|
30
|
+
.then(parseAndCheckLogin(ctx, defaultFuncs))
|
|
31
|
+
.then(resData => {
|
|
32
|
+
if (resData.error) throw resData;
|
|
33
|
+
return resData.payload.metadata[0];
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
return Promise.all(uploads);
|
|
69
37
|
}
|
|
70
38
|
|
|
71
|
-
|
|
72
|
-
let
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
if (
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
39
|
+
function extractIdsFromPayload(payload) {
|
|
40
|
+
let messageID = null;
|
|
41
|
+
let threadID = null;
|
|
42
|
+
function walk(n) {
|
|
43
|
+
if (Array.isArray(n)) {
|
|
44
|
+
if (n[0] === 5 && (n[1] === "replaceOptimsiticMessage" || n[1] === "replaceOptimisticMessage")) {
|
|
45
|
+
messageID = String(n[3]);
|
|
46
|
+
}
|
|
47
|
+
if (n[0] === 5 && n[1] === "writeCTAIdToThreadsTable") {
|
|
48
|
+
const a = n[2];
|
|
49
|
+
if (Array.isArray(a) && a[0] === 19) threadID = String(a[1]);
|
|
50
|
+
}
|
|
51
|
+
for (const x of n) walk(x);
|
|
83
52
|
}
|
|
84
53
|
}
|
|
85
|
-
|
|
54
|
+
walk(payload?.step);
|
|
55
|
+
return { threadID, messageID };
|
|
86
56
|
}
|
|
87
57
|
|
|
88
|
-
function
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
58
|
+
function publishWithAck(content, text, reqID, callback) {
|
|
59
|
+
const mqttClient = ctx.mqttClient;
|
|
60
|
+
return new Promise((resolve, reject) => {
|
|
61
|
+
let done = false;
|
|
62
|
+
const cleanup = () => {
|
|
63
|
+
if (done) return;
|
|
64
|
+
done = true;
|
|
65
|
+
mqttClient.removeListener("message", handleRes);
|
|
66
|
+
};
|
|
67
|
+
const handleRes = (topic, message) => {
|
|
68
|
+
if (topic !== "/ls_resp") return;
|
|
69
|
+
let jsonMsg;
|
|
70
|
+
try {
|
|
71
|
+
jsonMsg = JSON.parse(message.toString());
|
|
72
|
+
jsonMsg.payload = JSON.parse(jsonMsg.payload);
|
|
73
|
+
} catch {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (jsonMsg.request_id !== reqID) return;
|
|
77
|
+
const { threadID, messageID } = extractIdsFromPayload(jsonMsg.payload);
|
|
78
|
+
const bodies = { body: text || null, messageID, threadID };
|
|
79
|
+
cleanup();
|
|
80
|
+
callback && callback(undefined, bodies);
|
|
81
|
+
resolve(bodies);
|
|
82
|
+
};
|
|
83
|
+
mqttClient.on("message", handleRes);
|
|
84
|
+
mqttClient.publish("/ls_req", JSON.stringify(content), { qos: 1, retain: false }, err => {
|
|
85
|
+
if (err) {
|
|
86
|
+
cleanup();
|
|
87
|
+
callback && callback(err);
|
|
88
|
+
reject(err);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
setTimeout(() => {
|
|
92
|
+
if (done) return;
|
|
93
|
+
cleanup();
|
|
94
|
+
const err = { error: "Timeout waiting for ACK" };
|
|
95
|
+
callback && callback(err);
|
|
96
|
+
reject(err);
|
|
97
|
+
}, 15000);
|
|
98
|
+
});
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
function
|
|
102
|
-
if (!msg.mentions || !msg.mentions.length) return;
|
|
103
|
-
|
|
104
|
-
const
|
|
101
|
+
function buildMentionData(msg, baseBody) {
|
|
102
|
+
if (!msg.mentions || !Array.isArray(msg.mentions) || !msg.mentions.length) return null;
|
|
103
|
+
const base = typeof baseBody === "string" ? baseBody : "";
|
|
104
|
+
const ids = [];
|
|
105
|
+
const offsets = [];
|
|
106
|
+
const lengths = [];
|
|
107
|
+
const types = [];
|
|
108
|
+
let cursor = 0;
|
|
105
109
|
for (const m of msg.mentions) {
|
|
106
|
-
const
|
|
107
|
-
|
|
110
|
+
const raw = String(m.tag || "");
|
|
111
|
+
const name = raw.replace(/^@+/, "");
|
|
112
|
+
const start = Number.isInteger(m.fromIndex) ? m.fromIndex : cursor;
|
|
113
|
+
let idx = base.indexOf(raw, start);
|
|
114
|
+
let adj = 0;
|
|
115
|
+
if (idx === -1) {
|
|
116
|
+
idx = base.indexOf(name, start);
|
|
117
|
+
adj = 0;
|
|
118
|
+
} else {
|
|
119
|
+
adj = raw.length - name.length;
|
|
120
|
+
}
|
|
121
|
+
if (idx < 0) {
|
|
122
|
+
idx = 0;
|
|
123
|
+
adj = 0;
|
|
124
|
+
}
|
|
125
|
+
const off = idx + adj;
|
|
126
|
+
ids.push(String(m.id || 0));
|
|
127
|
+
offsets.push(off);
|
|
128
|
+
lengths.push(name.length);
|
|
129
|
+
types.push("p");
|
|
130
|
+
cursor = off + name.length;
|
|
108
131
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
const from = typeof m.fromIndex === "number" ? m.fromIndex : searchFrom;
|
|
116
|
-
const off = Math.max(0, body.indexOf(tag, from));
|
|
117
|
-
form[`profile_xmd[${i}][offset]`] = off + 1;
|
|
118
|
-
form[`profile_xmd[${i}][length]`] = tag.length;
|
|
119
|
-
form[`profile_xmd[${i}][id]`] = m.id || 0;
|
|
120
|
-
form[`profile_xmd[${i}][type]`] = "p";
|
|
121
|
-
searchFrom = off + tag.length;
|
|
122
|
-
});
|
|
132
|
+
return {
|
|
133
|
+
mention_ids: ids.join(","),
|
|
134
|
+
mention_offsets: offsets.join(","),
|
|
135
|
+
mention_lengths: lengths.join(","),
|
|
136
|
+
mention_types: types.join(",")
|
|
137
|
+
};
|
|
123
138
|
}
|
|
124
139
|
|
|
125
|
-
function
|
|
126
|
-
|
|
127
|
-
|
|
140
|
+
function coerceMsg(x) {
|
|
141
|
+
if (x == null) return { body: "" };
|
|
142
|
+
if (typeof x === "string") return { body: x };
|
|
143
|
+
if (typeof x === "object") return x;
|
|
144
|
+
return { body: String(x) };
|
|
128
145
|
}
|
|
129
146
|
|
|
130
|
-
function
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
if (actions && actions.length) {
|
|
136
|
-
const v = actions.find(x => x && x.message_id) || actions[0];
|
|
137
|
-
messageID = v && v.message_id ? v.message_id : null;
|
|
138
|
-
threadFBID = (v && (v.thread_fbid || v.thread_id)) || fallbackThreadID || null;
|
|
139
|
-
timestamp = v && v.timestamp ? v.timestamp : null;
|
|
147
|
+
return async function sendMessageMqtt(msg, threadID, callback, replyToMessage) {
|
|
148
|
+
if (typeof threadID === "function") return threadID({ error: "Pass a threadID as a second argument." });
|
|
149
|
+
if (typeof callback === "string" && !replyToMessage) {
|
|
150
|
+
replyToMessage = callback;
|
|
151
|
+
callback = () => { };
|
|
140
152
|
}
|
|
141
|
-
if (
|
|
142
|
-
if (!
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
function sendContent(form, threadID, isSingleUser, messageAndOTID, callback) {
|
|
149
|
-
if (getType(threadID) === "Array") {
|
|
150
|
-
for (let i = 0; i < threadID.length; i++) form["specific_to_list[" + i + "]"] = "fbid:" + threadID[i];
|
|
151
|
-
form["specific_to_list[" + threadID.length + "]"] = "fbid:" + ctx.userID;
|
|
152
|
-
form["client_thread_id"] = "root:" + messageAndOTID;
|
|
153
|
-
} else {
|
|
154
|
-
if (isSingleUser) {
|
|
155
|
-
form["specific_to_list[0]"] = "fbid:" + threadID;
|
|
156
|
-
form["specific_to_list[1]"] = "fbid:" + ctx.userID;
|
|
157
|
-
form["other_user_fbid"] = threadID;
|
|
158
|
-
} else {
|
|
159
|
-
form["thread_fbid"] = threadID;
|
|
160
|
-
}
|
|
153
|
+
if (typeof callback !== "function") callback = () => { };
|
|
154
|
+
if (!threadID) {
|
|
155
|
+
const err = { error: "threadID is required" };
|
|
156
|
+
callback(err);
|
|
157
|
+
throw err;
|
|
161
158
|
}
|
|
162
|
-
postWithRetry("https://www.facebook.com/messaging/send/", ctx.jar, form)
|
|
163
|
-
.then(resData => {
|
|
164
|
-
if (!resData) return callback({ error: "Send message failed." });
|
|
165
|
-
if (resData.error) {
|
|
166
|
-
if (resData.error === 1545012) log.warn("sendMessage", "Got error 1545012. This might mean that you're not part of the conversation " + threadID);
|
|
167
|
-
else log.error("sendMessage", resData);
|
|
168
|
-
return callback(resData);
|
|
169
|
-
}
|
|
170
|
-
const info = extractMessageInfo(resData, getType(threadID) === "Array" ? null : String(threadID));
|
|
171
|
-
if (!info) return callback({ error: "Cannot parse message info." });
|
|
172
|
-
callback(null, info);
|
|
173
|
-
})
|
|
174
|
-
.catch(err => {
|
|
175
|
-
log.error("sendMessage", err);
|
|
176
|
-
if (getType(err) === "Object" && err.error === "Not logged in.") ctx.loggedIn = false;
|
|
177
|
-
callback(err);
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
159
|
|
|
181
|
-
|
|
182
|
-
const
|
|
183
|
-
const
|
|
184
|
-
|
|
185
|
-
sendContent(form, threadID, isSingleUser, otid, (err, info) => (err ? reject(err) : resolve(info)));
|
|
186
|
-
});
|
|
187
|
-
}
|
|
160
|
+
const m = coerceMsg(msg);
|
|
161
|
+
const baseBody = m.body != null ? String(m.body) : "";
|
|
162
|
+
const reqID = Math.floor(100 + Math.random() * 900);
|
|
163
|
+
const epoch = (BigInt(Date.now()) << 22n).toString();
|
|
188
164
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
165
|
+
const payload0 = {
|
|
166
|
+
thread_id: String(threadID),
|
|
167
|
+
otid: generateOfflineThreadingID(),
|
|
168
|
+
source: 2097153,
|
|
169
|
+
send_type: 1,
|
|
170
|
+
sync_group: 1,
|
|
171
|
+
mark_thread_read: 1,
|
|
172
|
+
text: baseBody === "" ? null : baseBody,
|
|
173
|
+
initiating_source: 0,
|
|
174
|
+
skip_url_preview_gen: 0,
|
|
175
|
+
text_has_links: hasLinks(baseBody) ? 1 : 0,
|
|
176
|
+
multitab_env: 0,
|
|
177
|
+
metadata_dataclass: JSON.stringify({ media_accessibility_metadata: { alt_text: null } })
|
|
178
|
+
};
|
|
200
179
|
|
|
201
|
-
|
|
202
|
-
if (
|
|
203
|
-
form["shareable_attachment[share_type]"] = "100";
|
|
204
|
-
getUrl(msg.url, function (err, params) {
|
|
205
|
-
if (err) return callback(err);
|
|
206
|
-
form["shareable_attachment[share_params]"] = params;
|
|
207
|
-
cb();
|
|
208
|
-
});
|
|
209
|
-
} else cb();
|
|
210
|
-
}
|
|
180
|
+
const mentionData = buildMentionData(m, baseBody);
|
|
181
|
+
if (mentionData) payload0.mention_data = mentionData;
|
|
211
182
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
form["location_attachment[coordinates][latitude]"] = msg.location.latitude;
|
|
216
|
-
form["location_attachment[coordinates][longitude]"] = msg.location.longitude;
|
|
217
|
-
form["location_attachment[is_current_location]"] = !!msg.location.current;
|
|
183
|
+
if (m.sticker) {
|
|
184
|
+
payload0.send_type = 2;
|
|
185
|
+
payload0.sticker_id = m.sticker;
|
|
218
186
|
}
|
|
219
|
-
cb();
|
|
220
|
-
}
|
|
221
187
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
function handleEmoji(msg, form, callback, cb) {
|
|
228
|
-
if (msg.emojiSize != null && msg.emoji == null) return callback({ error: "emoji property is empty" });
|
|
229
|
-
if (msg.emoji) {
|
|
230
|
-
if (msg.emojiSize == null) msg.emojiSize = "medium";
|
|
231
|
-
if (msg.emojiSize !== "small" && msg.emojiSize !== "medium" && msg.emojiSize !== "large") return callback({ error: "emojiSize property is invalid" });
|
|
232
|
-
if (form["body"] != null && form["body"] !== "") return callback({ error: "body is not empty" });
|
|
233
|
-
form["body"] = msg.emoji;
|
|
234
|
-
form["tags[0]"] = "hot_emoji_size:" + msg.emojiSize;
|
|
188
|
+
if (m.emoji) {
|
|
189
|
+
const size = !isNaN(m.emojiSize) ? Number(m.emojiSize) : emojiSizes[m.emojiSize || "small"] || 1;
|
|
190
|
+
payload0.send_type = 1;
|
|
191
|
+
payload0.text = m.emoji;
|
|
192
|
+
payload0.hot_emoji_size = Math.min(3, Math.max(1, size));
|
|
235
193
|
}
|
|
236
|
-
cb();
|
|
237
|
-
}
|
|
238
194
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
continue;
|
|
247
|
-
}
|
|
248
|
-
if (a && typeof a === "object") {
|
|
249
|
-
if (a.id && a.type && /_id$/.test(a.type)) {
|
|
250
|
-
ids.push([a.type, String(a.id)]);
|
|
251
|
-
continue;
|
|
252
|
-
}
|
|
253
|
-
const k = Object.keys(a || {}).find(x => /_id$/.test(x));
|
|
254
|
-
if (k) {
|
|
255
|
-
ids.push([k, String(a[k])]);
|
|
256
|
-
continue;
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
streams.push(toReadable(a));
|
|
195
|
+
if (m.location && m.location.latitude != null && m.location.longitude != null) {
|
|
196
|
+
payload0.send_type = 1;
|
|
197
|
+
payload0.location_data = {
|
|
198
|
+
coordinates: { latitude: m.location.latitude, longitude: m.location.longitude },
|
|
199
|
+
is_current_location: !!m.location.current,
|
|
200
|
+
is_live_location: !!m.location.live
|
|
201
|
+
};
|
|
260
202
|
}
|
|
261
|
-
return { ids, streams };
|
|
262
|
-
}
|
|
263
203
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
form["image_ids"] = [];
|
|
267
|
-
form["gif_ids"] = [];
|
|
268
|
-
form["file_ids"] = [];
|
|
269
|
-
form["video_ids"] = [];
|
|
270
|
-
form["audio_ids"] = [];
|
|
271
|
-
const { ids, streams } = splitAttachments(msg.attachment);
|
|
272
|
-
for (const [type, id] of ids) form[`${type}s`].push(id);
|
|
273
|
-
if (!streams.length) return cb();
|
|
274
|
-
uploadAttachment(streams, function (err, files) {
|
|
275
|
-
if (err) return callback(err);
|
|
276
|
-
files.forEach(function (file) {
|
|
277
|
-
const type = Object.keys(file)[0];
|
|
278
|
-
form[type + "s"].push(file[type]);
|
|
279
|
-
});
|
|
280
|
-
cb();
|
|
281
|
-
});
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
function handleMention(msg, form, callback, cb) {
|
|
285
|
-
try {
|
|
286
|
-
applyMentions(msg, form);
|
|
287
|
-
cb();
|
|
288
|
-
} catch (e) {
|
|
289
|
-
callback(e);
|
|
204
|
+
if (replyToMessage) {
|
|
205
|
+
payload0.reply_metadata = { reply_source_id: replyToMessage, reply_source_type: 1, reply_type: 0 };
|
|
290
206
|
}
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
return function sendMessage(msg, threadID, callback, replyToMessage, isGroup) {
|
|
294
|
-
const isFn = v => typeof v === "function";
|
|
295
|
-
const isStr = v => typeof v === "string";
|
|
296
207
|
|
|
297
|
-
if (
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
208
|
+
if (m.attachment) {
|
|
209
|
+
payload0.send_type = 3;
|
|
210
|
+
if (payload0.text === "") payload0.text = null;
|
|
211
|
+
payload0.attachment_fbids = [];
|
|
212
|
+
let list = m.attachment;
|
|
213
|
+
if (getType(list) !== "Array") list = [list];
|
|
214
|
+
const idsFromPairs = [];
|
|
215
|
+
const streams = [];
|
|
216
|
+
for (const it of list) {
|
|
217
|
+
if (Array.isArray(it) && typeof it[0] === "string") {
|
|
218
|
+
idsFromPairs.push(String(it[1]));
|
|
219
|
+
} else if (isReadableStream(it)) {
|
|
220
|
+
streams.push(it);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (idsFromPairs.length) payload0.attachment_fbids.push(...idsFromPairs);
|
|
224
|
+
if (streams.length) {
|
|
225
|
+
try {
|
|
226
|
+
const files = await uploadAttachment(streams);
|
|
227
|
+
for (const file of files) {
|
|
228
|
+
const key = Object.keys(file)[0];
|
|
229
|
+
payload0.attachment_fbids.push(file[key]);
|
|
230
|
+
}
|
|
231
|
+
} catch (err) {
|
|
232
|
+
log.error("uploadAttachment", err);
|
|
233
|
+
callback(err);
|
|
234
|
+
throw err;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
307
237
|
}
|
|
308
238
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
timestamp_time_passed: "0",
|
|
339
|
-
is_unread: false,
|
|
340
|
-
is_cleared: false,
|
|
341
|
-
is_forward: false,
|
|
342
|
-
is_filtered_content: false,
|
|
343
|
-
is_filtered_content_bh: false,
|
|
344
|
-
is_filtered_content_account: false,
|
|
345
|
-
is_filtered_content_quasar: false,
|
|
346
|
-
is_filtered_content_invalid_app: false,
|
|
347
|
-
is_spoof_warning: false,
|
|
348
|
-
source: "source:chat:web",
|
|
349
|
-
"source_tags[0]": "source:chat",
|
|
350
|
-
body: msg.body ? msg.body.toString() : "",
|
|
351
|
-
html_body: false,
|
|
352
|
-
ui_push_phase: "V3",
|
|
353
|
-
status: "0",
|
|
354
|
-
offline_threading_id: messageAndOTID,
|
|
355
|
-
message_id: messageAndOTID,
|
|
356
|
-
threading_id: generateThreadingID(ctx.clientID),
|
|
357
|
-
ephemeral_ttl_mode: "0",
|
|
358
|
-
manual_retry_cnt: "0",
|
|
359
|
-
signatureID: getSignatureID(),
|
|
360
|
-
replied_to_message_id: replyToMessage ? replyToMessage.toString() : ""
|
|
239
|
+
const content = {
|
|
240
|
+
app_id: "2220391788200892",
|
|
241
|
+
payload: {
|
|
242
|
+
tasks: [
|
|
243
|
+
{
|
|
244
|
+
label: "46",
|
|
245
|
+
payload: payload0,
|
|
246
|
+
queue_name: String(threadID),
|
|
247
|
+
task_id: 400,
|
|
248
|
+
failure_count: null
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
label: "21",
|
|
252
|
+
payload: {
|
|
253
|
+
thread_id: String(threadID),
|
|
254
|
+
last_read_watermark_ts: Date.now(),
|
|
255
|
+
sync_group: 1
|
|
256
|
+
},
|
|
257
|
+
queue_name: String(threadID),
|
|
258
|
+
task_id: 401,
|
|
259
|
+
failure_count: null
|
|
260
|
+
}
|
|
261
|
+
],
|
|
262
|
+
epoch_id: epoch,
|
|
263
|
+
version_id: "24804310205905615",
|
|
264
|
+
data_trace_id: "#" + Buffer.from(String(Math.random())).toString("base64").replace(/=+$/g, "")
|
|
265
|
+
},
|
|
266
|
+
request_id: reqID,
|
|
267
|
+
type: 3
|
|
361
268
|
};
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
handleAttachment(msg, form, callback, () =>
|
|
366
|
-
handleUrl(msg, form, callback, () =>
|
|
367
|
-
handleEmoji(msg, form, callback, () =>
|
|
368
|
-
handleMention(msg, form, callback, () => {
|
|
369
|
-
finalizeHasAttachment(form);
|
|
370
|
-
send(form, threadID, messageAndOTID, callback, isGroup);
|
|
371
|
-
})
|
|
372
|
-
)
|
|
373
|
-
)
|
|
374
|
-
)
|
|
375
|
-
)
|
|
376
|
-
);
|
|
377
|
-
return returnPromise;
|
|
269
|
+
content.payload.tasks.forEach(t => (t.payload = JSON.stringify(t.payload)));
|
|
270
|
+
content.payload = JSON.stringify(content.payload);
|
|
271
|
+
return publishWithAck(content, baseBody, reqID, callback);
|
|
378
272
|
};
|
|
379
273
|
};
|
|
@@ -1,323 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
var log = require("npmlog");
|
|
3
|
-
var bluebird = require("bluebird");
|
|
4
|
-
const { parseAndCheckLogin } = require("../../utils/client");
|
|
5
|
-
const { getType } = require("../../utils/format");
|
|
6
|
-
const { isReadableStream } = require("../../utils/constants");
|
|
7
|
-
module.exports = function(defaultFuncs, api, ctx) {
|
|
8
|
-
function uploadAttachment(attachments, callback) {
|
|
9
|
-
callback = callback || function() {};
|
|
10
|
-
var uploads = [];
|
|
11
|
-
|
|
12
|
-
// create an array of promises
|
|
13
|
-
for (var i = 0; i < attachments.length; i++) {
|
|
14
|
-
if (!isReadableStream(attachments[i])) {
|
|
15
|
-
throw {
|
|
16
|
-
error:
|
|
17
|
-
"Attachment should be a readable stream and not " +
|
|
18
|
-
getType(attachments[i]) +
|
|
19
|
-
"."
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
var form = {
|
|
24
|
-
upload_1024: attachments[i],
|
|
25
|
-
voice_clip: "true"
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
uploads.push(
|
|
29
|
-
defaultFuncs
|
|
30
|
-
.postFormData(
|
|
31
|
-
"https://upload.facebook.com/ajax/mercury/upload.php",
|
|
32
|
-
ctx.jar,
|
|
33
|
-
form,
|
|
34
|
-
{}
|
|
35
|
-
)
|
|
36
|
-
.then(parseAndCheckLogin(ctx, defaultFuncs))
|
|
37
|
-
.then(function(resData) {
|
|
38
|
-
if (resData.error) {
|
|
39
|
-
throw resData;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// We have to return the data unformatted unless we want to change it
|
|
43
|
-
// back in sendMessage.
|
|
44
|
-
return resData.payload.metadata[0];
|
|
45
|
-
})
|
|
46
|
-
);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// resolve all promises
|
|
50
|
-
bluebird
|
|
51
|
-
.all(uploads)
|
|
52
|
-
.then(function(resData) {
|
|
53
|
-
callback(null, resData);
|
|
54
|
-
})
|
|
55
|
-
.catch(function(err) {
|
|
56
|
-
log.error("uploadAttachment", err);
|
|
57
|
-
return callback(err);
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
let variance = 0;
|
|
62
|
-
const epoch_id = () =>
|
|
63
|
-
Math.floor(Date.now() * (4194304 + (variance = (variance + 0.1) % 5)));
|
|
64
|
-
const emojiSizes = {
|
|
65
|
-
small: 1,
|
|
66
|
-
medium: 2,
|
|
67
|
-
large: 3
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
function handleEmoji(msg, form, callback, cb) {
|
|
71
|
-
if (msg.emojiSize != null && msg.emoji == null) {
|
|
72
|
-
return callback({ error: "emoji property is empty" });
|
|
73
|
-
}
|
|
74
|
-
if (msg.emoji) {
|
|
75
|
-
if (!msg.emojiSize) {
|
|
76
|
-
msg.emojiSize = "small";
|
|
77
|
-
}
|
|
78
|
-
if (
|
|
79
|
-
msg.emojiSize !== "small" &&
|
|
80
|
-
msg.emojiSize !== "medium" &&
|
|
81
|
-
msg.emojiSize !== "large" &&
|
|
82
|
-
(isNaN(msg.emojiSize) || msg.emojiSize < 1 || msg.emojiSize > 3)
|
|
83
|
-
) {
|
|
84
|
-
return callback({ error: "emojiSize property is invalid" });
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
form.payload.tasks[0].payload.send_type = 1;
|
|
88
|
-
form.payload.tasks[0].payload.text = msg.emoji;
|
|
89
|
-
form.payload.tasks[0].payload.hot_emoji_size = !isNaN(msg.emojiSize)
|
|
90
|
-
? msg.emojiSize
|
|
91
|
-
: emojiSizes[msg.emojiSize];
|
|
92
|
-
}
|
|
93
|
-
cb();
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function handleSticker(msg, form, callback, cb) {
|
|
97
|
-
if (msg.sticker) {
|
|
98
|
-
form.payload.tasks[0].payload.send_type = 2;
|
|
99
|
-
form.payload.tasks[0].payload.sticker_id = msg.sticker;
|
|
100
|
-
}
|
|
101
|
-
cb();
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
function handleAttachment(msg, form, callback, cb) {
|
|
105
|
-
if (msg.attachment) {
|
|
106
|
-
form.payload.tasks[0].payload.send_type = 3;
|
|
107
|
-
form.payload.tasks[0].payload.attachment_fbids = [];
|
|
108
|
-
if (form.payload.tasks[0].payload.text == "")
|
|
109
|
-
form.payload.tasks[0].payload.text = null;
|
|
110
|
-
if (getType(msg.attachment) !== "Array") {
|
|
111
|
-
msg.attachment = [msg.attachment];
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
uploadAttachment(msg.attachment, function(err, files) {
|
|
115
|
-
if (err) {
|
|
116
|
-
return callback(err);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
files.forEach(function(file) {
|
|
120
|
-
var key = Object.keys(file);
|
|
121
|
-
var type = key[0]; // image_id, file_id, etc
|
|
122
|
-
form.payload.tasks[0].payload.attachment_fbids.push(file[type]); // push the id
|
|
123
|
-
});
|
|
124
|
-
cb();
|
|
125
|
-
});
|
|
126
|
-
} else {
|
|
127
|
-
cb();
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
function handleMention(msg, form, callback, cb) {
|
|
132
|
-
if (msg.mentions) {
|
|
133
|
-
form.payload.tasks[0].payload.send_type = 1;
|
|
134
|
-
|
|
135
|
-
const arrayIds = [];
|
|
136
|
-
const arrayOffsets = [];
|
|
137
|
-
const arrayLengths = [];
|
|
138
|
-
const mention_types = [];
|
|
139
|
-
|
|
140
|
-
for (let i = 0; i < msg.mentions.length; i++) {
|
|
141
|
-
const mention = msg.mentions[i];
|
|
142
|
-
|
|
143
|
-
const tag = mention.tag;
|
|
144
|
-
if (typeof tag !== "string") {
|
|
145
|
-
return callback({ error: "Mention tags must be strings." });
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
const offset = msg.body.indexOf(tag, mention.fromIndex || 0);
|
|
149
|
-
|
|
150
|
-
if (offset < 0) {
|
|
151
|
-
log.warn(
|
|
152
|
-
"handleMention",
|
|
153
|
-
'Mention for "' + tag + '" not found in message string.'
|
|
154
|
-
);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
if (mention.id == null) {
|
|
158
|
-
log.warn("handleMention", "Mention id should be non-null.");
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
const id = mention.id || 0;
|
|
162
|
-
arrayIds.push(id);
|
|
163
|
-
arrayOffsets.push(offset);
|
|
164
|
-
arrayLengths.push(tag.length);
|
|
165
|
-
mention_types.push("p");
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
form.payload.tasks[0].payload.mention_data = {
|
|
169
|
-
mention_ids: arrayIds.join(","),
|
|
170
|
-
mention_offsets: arrayOffsets.join(","),
|
|
171
|
-
mention_lengths: arrayLengths.join(","),
|
|
172
|
-
mention_types: mention_types.join(",")
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
cb();
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
function handleLocation(msg, form, callback, cb) {
|
|
179
|
-
// this is not working yet
|
|
180
|
-
if (msg.location) {
|
|
181
|
-
if (msg.location.latitude == null || msg.location.longitude == null) {
|
|
182
|
-
return callback({
|
|
183
|
-
error: "location property needs both latitude and longitude"
|
|
184
|
-
});
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
form.payload.tasks[0].payload.send_type = 1;
|
|
188
|
-
form.payload.tasks[0].payload.location_data = {
|
|
189
|
-
coordinates: {
|
|
190
|
-
latitude: msg.location.latitude,
|
|
191
|
-
longitude: msg.location.longitude
|
|
192
|
-
},
|
|
193
|
-
is_current_location: !!msg.location.current,
|
|
194
|
-
is_live_location: !!msg.location.live
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
cb();
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
function send(form, threadID, callback, replyToMessage) {
|
|
202
|
-
if (replyToMessage) {
|
|
203
|
-
form.payload.tasks[0].payload.reply_metadata = {
|
|
204
|
-
reply_source_id: replyToMessage,
|
|
205
|
-
reply_source_type: 1,
|
|
206
|
-
reply_type: 0
|
|
207
|
-
};
|
|
208
|
-
}
|
|
209
|
-
const mqttClient = ctx.mqttClient;
|
|
210
|
-
form.payload.tasks.forEach(task => {
|
|
211
|
-
task.payload = JSON.stringify(task.payload);
|
|
212
|
-
});
|
|
213
|
-
form.payload = JSON.stringify(form.payload);
|
|
214
|
-
console.log(global.jsonStringifyColor(form, null, 2));
|
|
215
|
-
|
|
216
|
-
return mqttClient.publish("/ls_req", JSON.stringify(form), function(
|
|
217
|
-
err,
|
|
218
|
-
data
|
|
219
|
-
) {
|
|
220
|
-
if (err) {
|
|
221
|
-
console.error("Error publishing message: ", err);
|
|
222
|
-
callback(err);
|
|
223
|
-
} else {
|
|
224
|
-
console.log("Message published successfully with data: ", data);
|
|
225
|
-
callback(null, data);
|
|
226
|
-
}
|
|
227
|
-
});
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
return function sendMessageMqtt(msg, threadID, callback, replyToMessage) {
|
|
231
|
-
if (
|
|
232
|
-
!callback &&
|
|
233
|
-
(getType(threadID) === "Function" ||
|
|
234
|
-
getType(threadID) === "AsyncFunction")
|
|
235
|
-
) {
|
|
236
|
-
return threadID({ error: "Pass a threadID as a second argument." });
|
|
237
|
-
}
|
|
238
|
-
if (!replyToMessage && getType(callback) === "String") {
|
|
239
|
-
replyToMessage = callback;
|
|
240
|
-
callback = function() {};
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
if (!callback) {
|
|
244
|
-
callback = function(err, friendList) {};
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
var msgType = getType(msg);
|
|
248
|
-
var threadIDType = getType(threadID);
|
|
249
|
-
var messageIDType = getType(replyToMessage);
|
|
250
|
-
|
|
251
|
-
if (msgType !== "String" && msgType !== "Object") {
|
|
252
|
-
return callback({
|
|
253
|
-
error:
|
|
254
|
-
"Message should be of type string or object and not " + msgType + "."
|
|
255
|
-
});
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
if (msgType === "String") {
|
|
259
|
-
msg = { body: msg };
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
const timestamp = Date.now();
|
|
263
|
-
// get full date time
|
|
264
|
-
const epoch = timestamp << 22;
|
|
265
|
-
//const otid = epoch + 0; // TODO replace with randomInt(0, 2**22)
|
|
266
|
-
const otid = epoch + Math.floor(Math.random() * 4194304);
|
|
267
|
-
|
|
268
|
-
const form = {
|
|
269
|
-
app_id: "2220391788200892",
|
|
270
|
-
payload: {
|
|
271
|
-
tasks: [
|
|
272
|
-
{
|
|
273
|
-
label: "46",
|
|
274
|
-
payload: {
|
|
275
|
-
thread_id: threadID.toString(),
|
|
276
|
-
otid: otid.toString(),
|
|
277
|
-
source: 0,
|
|
278
|
-
send_type: 1,
|
|
279
|
-
sync_group: 1,
|
|
280
|
-
text:
|
|
281
|
-
msg.body != null && msg.body != undefined
|
|
282
|
-
? msg.body.toString()
|
|
283
|
-
: "",
|
|
284
|
-
initiating_source: 1,
|
|
285
|
-
skip_url_preview_gen: 0
|
|
286
|
-
},
|
|
287
|
-
queue_name: threadID.toString(),
|
|
288
|
-
task_id: 0,
|
|
289
|
-
failure_count: null
|
|
290
|
-
},
|
|
291
|
-
{
|
|
292
|
-
label: "21",
|
|
293
|
-
payload: {
|
|
294
|
-
thread_id: threadID.toString(),
|
|
295
|
-
last_read_watermark_ts: Date.now(),
|
|
296
|
-
sync_group: 1
|
|
297
|
-
},
|
|
298
|
-
queue_name: threadID.toString(),
|
|
299
|
-
task_id: 1,
|
|
300
|
-
failure_count: null
|
|
301
|
-
}
|
|
302
|
-
],
|
|
303
|
-
epoch_id: epoch_id(),
|
|
304
|
-
version_id: "6120284488008082",
|
|
305
|
-
data_trace_id: null
|
|
306
|
-
},
|
|
307
|
-
request_id: 1,
|
|
308
|
-
type: 3
|
|
309
|
-
};
|
|
310
|
-
|
|
311
|
-
handleEmoji(msg, form, callback, function() {
|
|
312
|
-
handleLocation(msg, form, callback, function() {
|
|
313
|
-
handleMention(msg, form, callback, function() {
|
|
314
|
-
handleSticker(msg, form, callback, function() {
|
|
315
|
-
handleAttachment(msg, form, callback, function() {
|
|
316
|
-
send(form, threadID, callback, replyToMessage);
|
|
317
|
-
});
|
|
318
|
-
});
|
|
319
|
-
});
|
|
320
|
-
});
|
|
321
|
-
});
|
|
322
|
-
};
|
|
323
|
-
};
|