@dongdev/fca-unofficial 3.0.30 → 3.0.31

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.
Files changed (48) hide show
  1. package/CHANGELOG.md +3 -0
  2. package/package.json +2 -1
  3. package/src/api/http/httpGet.js +2 -2
  4. package/src/api/messaging/addUserToGroup.js +1 -1
  5. package/src/api/messaging/changeGroupImage.js +1 -1
  6. package/src/api/messaging/changeNickname.js +1 -1
  7. package/src/api/messaging/changeThreadColor.js +1 -1
  8. package/src/api/messaging/editMessage.js +1 -1
  9. package/src/api/messaging/searchForThread.js +2 -1
  10. package/src/api/messaging/sendTypingIndicator.js +1 -1
  11. package/src/api/messaging/setMessageReaction.js +3 -4
  12. package/src/api/messaging/unsendMessage.js +1 -1
  13. package/src/api/threads/getThreadInfo.js +2 -1
  14. package/src/api/users/getUserInfo.js +7 -4
  15. package/src/database/helpers.js +53 -0
  16. package/src/database/models/index.js +2 -1
  17. package/src/database/threadData.js +49 -53
  18. package/src/database/userData.js +46 -37
  19. package/src/utils/format/attachment.js +357 -0
  20. package/src/utils/format/cookie.js +9 -0
  21. package/src/utils/format/date.js +50 -0
  22. package/src/utils/format/decode.js +44 -0
  23. package/src/utils/format/delta.js +194 -0
  24. package/src/utils/format/ids.js +64 -0
  25. package/src/utils/format/index.js +64 -0
  26. package/src/utils/format/message.js +88 -0
  27. package/src/utils/format/presence.js +132 -0
  28. package/src/utils/format/readTyp.js +44 -0
  29. package/src/utils/format/thread.js +42 -0
  30. package/src/utils/format/utils.js +141 -0
  31. package/src/utils/loginParser/autoLogin.js +125 -0
  32. package/src/utils/loginParser/helpers.js +43 -0
  33. package/src/utils/loginParser/index.js +10 -0
  34. package/src/utils/loginParser/parseAndCheckLogin.js +220 -0
  35. package/src/utils/loginParser/textUtils.js +28 -0
  36. package/src/utils/request/client.js +26 -0
  37. package/src/utils/request/config.js +23 -0
  38. package/src/utils/request/defaults.js +46 -0
  39. package/src/utils/request/helpers.js +46 -0
  40. package/src/utils/request/index.js +17 -0
  41. package/src/utils/request/methods.js +163 -0
  42. package/src/utils/request/proxy.js +21 -0
  43. package/src/utils/request/retry.js +77 -0
  44. package/src/utils/request/sanitize.js +49 -0
  45. package/src/utils/format.js +0 -1174
  46. package/src/utils/loginParser.js +0 -365
  47. package/src/utils/messageFormat.js +0 -1173
  48. package/src/utils/request.js +0 -332
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+
3
+ const { padZeros } = require("./utils");
4
+
5
+ function binaryToDecimal(data) {
6
+ var ret = "";
7
+ while (data !== "0") {
8
+ var end = 0;
9
+ var fullName = "";
10
+ var i = 0;
11
+ for (; i < data.length; i++) {
12
+ end = 2 * end + parseInt(data[i], 10);
13
+ if (end >= 10) {
14
+ fullName += "1";
15
+ end -= 10;
16
+ } else fullName += "0";
17
+ }
18
+ ret = end.toString() + ret;
19
+ data = fullName.slice(fullName.indexOf("1"));
20
+ }
21
+ return ret;
22
+ }
23
+
24
+ function generateOfflineThreadingID() {
25
+ var ret = Date.now();
26
+ var value = Math.floor(Math.random() * 4294967295);
27
+ var str = ("0000000000000000000000" + value.toString(2)).slice(-22);
28
+ var msgs = ret.toString(2) + str;
29
+ return binaryToDecimal(msgs);
30
+ }
31
+
32
+ function generateThreadingID(clientID) {
33
+ var k = Date.now();
34
+ var l = Math.floor(Math.random() * 4294967295);
35
+ var m = clientID;
36
+ return "<" + k + ":" + l + "-" + m + "@mail.projektitan.com>";
37
+ }
38
+
39
+ function getGUID() {
40
+ var sectionLength = Date.now();
41
+ var id = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(
42
+ /[xy]/g,
43
+ function (c) {
44
+ var r = Math.floor((sectionLength + Math.random() * 16) % 16);
45
+ sectionLength = Math.floor(sectionLength / 16);
46
+ var _guid = (c == "x" ? r : (r & 7) | 8).toString(16);
47
+ return _guid;
48
+ },
49
+ );
50
+ return id;
51
+ }
52
+
53
+ function generateTimestampRelative() {
54
+ var d = new Date();
55
+ return d.getHours() + ":" + padZeros(d.getMinutes());
56
+ }
57
+
58
+ module.exports = {
59
+ binaryToDecimal,
60
+ generateOfflineThreadingID,
61
+ generateThreadingID,
62
+ getGUID,
63
+ generateTimestampRelative,
64
+ };
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+
3
+ const utils = require("./utils");
4
+ const ids = require("./ids");
5
+ const date = require("./date");
6
+ const presence = require("./presence");
7
+ const attachment = require("./attachment");
8
+ const delta = require("./delta");
9
+ const message = require("./message");
10
+ const readTyp = require("./readTyp");
11
+ const thread = require("./thread");
12
+ const decode = require("./decode");
13
+ const cookie = require("./cookie");
14
+
15
+ module.exports = {
16
+ getType: utils.getType,
17
+ formatID: utils.formatID,
18
+ padZeros: utils.padZeros,
19
+ arrayToObject: utils.arrayToObject,
20
+ arrToForm: utils.arrToForm,
21
+ getData_Path: utils.getData_Path,
22
+ setData_Path: utils.setData_Path,
23
+ getPaths: utils.getPaths,
24
+ cleanHTML: utils.cleanHTML,
25
+ getCurrentTimestamp: utils.getCurrentTimestamp,
26
+ getSignatureID: utils.getSignatureID,
27
+
28
+ generateOfflineThreadingID: ids.generateOfflineThreadingID,
29
+ generateThreadingID: ids.generateThreadingID,
30
+ getGUID: ids.getGUID,
31
+ generateTimestampRelative: ids.generateTimestampRelative,
32
+
33
+ formatDate: date.formatDate,
34
+
35
+ presenceEncode: presence.presenceEncode,
36
+ presenceDecode: presence.presenceDecode,
37
+ generatePresence: presence.generatePresence,
38
+ generateAccessiblityCookie: presence.generateAccessiblityCookie,
39
+ formatProxyPresence: presence.formatProxyPresence,
40
+ formatPresence: presence.formatPresence,
41
+
42
+ _formatAttachment: attachment._formatAttachment,
43
+ formatAttachment: attachment.formatAttachment,
44
+
45
+ getAdminTextMessageType: delta.getAdminTextMessageType,
46
+ formatDeltaEvent: delta.formatDeltaEvent,
47
+ formatDeltaMessage: delta.formatDeltaMessage,
48
+ getMentionsFromDeltaMessage: delta.getMentionsFromDeltaMessage,
49
+ formatDeltaReadReceipt: delta.formatDeltaReadReceipt,
50
+
51
+ formatMessage: message.formatMessage,
52
+ formatEvent: message.formatEvent,
53
+ formatHistoryMessage: message.formatHistoryMessage,
54
+
55
+ formatReadReceipt: readTyp.formatReadReceipt,
56
+ formatRead: readTyp.formatRead,
57
+ formatTyp: readTyp.formatTyp,
58
+
59
+ formatThread: thread.formatThread,
60
+
61
+ decodeClientPayload: decode.decodeClientPayload,
62
+
63
+ formatCookie: cookie.formatCookie,
64
+ };
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+
3
+ const { formatID } = require("./utils");
4
+ const { formatAttachment } = require("./attachment");
5
+ const { getAdminTextMessageType } = require("./delta");
6
+
7
+ function formatMessage(m) {
8
+ var originalMessage = m.message ? m.message : m;
9
+ var body = originalMessage.body || "";
10
+ var args = body == "" ? [] : body.trim().split(/\s+/);
11
+ var obj = {
12
+ type: "message",
13
+ senderName: originalMessage.sender_name,
14
+ senderID: formatID(originalMessage.sender_fbid.toString()),
15
+ participantNames: originalMessage.group_thread_info
16
+ ? originalMessage.group_thread_info.participant_names
17
+ : [originalMessage.sender_name.split(" ")[0]],
18
+ participantIDs: originalMessage.group_thread_info
19
+ ? originalMessage.group_thread_info.participant_ids.map(function (v) {
20
+ return formatID(v.toString());
21
+ })
22
+ : [formatID(originalMessage.sender_fbid)],
23
+ body: body,
24
+ args: args,
25
+ threadID: formatID(
26
+ (
27
+ originalMessage.thread_fbid || originalMessage.other_user_fbid
28
+ ).toString(),
29
+ ),
30
+ threadName: originalMessage.group_thread_info
31
+ ? originalMessage.group_thread_info.name
32
+ : originalMessage.sender_name,
33
+ location: originalMessage.coordinates ? originalMessage.coordinates : null,
34
+ messageID: originalMessage.mid
35
+ ? originalMessage.mid.toString()
36
+ : originalMessage.message_id,
37
+ attachments: formatAttachment(
38
+ originalMessage.attachments,
39
+ originalMessage.attachmentIds,
40
+ originalMessage.attachment_map,
41
+ originalMessage.share_map,
42
+ ),
43
+ timestamp: originalMessage.timestamp,
44
+ timestampAbsolute: originalMessage.timestamp_absolute,
45
+ timestampRelative: originalMessage.timestamp_relative,
46
+ timestampDatetime: originalMessage.timestamp_datetime,
47
+ tags: originalMessage.tags,
48
+ reactions: originalMessage.reactions ? originalMessage.reactions : [],
49
+ isUnread: originalMessage.is_unread,
50
+ };
51
+ if (m.type === "pages_messaging")
52
+ obj.pageID = m.realtime_viewer_fbid.toString();
53
+ obj.isGroup = obj.participantIDs.length > 2;
54
+ return obj;
55
+ }
56
+
57
+ function formatEvent(m) {
58
+ var originalMessage = m.message ? m.message : m;
59
+ var logMessageType = originalMessage.log_message_type;
60
+ var logMessageData;
61
+ if (logMessageType === "log:generic-admin-text") {
62
+ logMessageData = originalMessage.log_message_data.untypedData;
63
+ logMessageType = getAdminTextMessageType(
64
+ originalMessage.log_message_data.message_type,
65
+ );
66
+ } else logMessageData = originalMessage.log_message_data;
67
+ return Object.assign(formatMessage(originalMessage), {
68
+ type: "event",
69
+ logMessageType: logMessageType,
70
+ logMessageData: logMessageData,
71
+ logMessageBody: originalMessage.log_message_body,
72
+ });
73
+ }
74
+
75
+ function formatHistoryMessage(m) {
76
+ switch (m.action_type) {
77
+ case "ma-type:log-message":
78
+ return formatEvent(m);
79
+ default:
80
+ return formatMessage(m);
81
+ }
82
+ }
83
+
84
+ module.exports = {
85
+ formatMessage,
86
+ formatEvent,
87
+ formatHistoryMessage,
88
+ };
@@ -0,0 +1,132 @@
1
+ "use strict";
2
+
3
+ var PRESENCE_MAP = {
4
+ _: "%",
5
+ A: "%2",
6
+ B: "000",
7
+ C: "%7d",
8
+ D: "%7b%22",
9
+ E: "%2c%22",
10
+ F: "%22%3a",
11
+ G: "%2c%22ut%22%3a1",
12
+ H: "%2c%22bls%22%3a",
13
+ I: "%2c%22n%22%3a%22%",
14
+ J: "%22%3a%7b%22i%22%3a0%7d",
15
+ K: "%2c%22pt%22%3a0%2c%22vis%22%3a",
16
+ L: "%2c%22ch%22%3a%7b%22h%22%3a%22",
17
+ M: "%7b%22v%22%3a2%2c%22time%22%3a1",
18
+ N: ".channel%22%2c%22sub%22%3a%5b",
19
+ O: "%2c%22sb%22%3a1%2c%22t%22%3a%5b",
20
+ P: "%2c%22ud%22%3a100%2c%22lc%22%3a0",
21
+ Q: "%5d%2c%22f%22%3anull%2c%22uct%22%3a",
22
+ R: ".channel%22%2c%22sub%22%3a%5b1%5d",
23
+ S: "%22%2c%22m%22%3a0%7d%2c%7b%22i%22%3a",
24
+ T: "%2c%22blc%22%3a1%2c%22snd%22%3a1%2c%22ct%22%3a",
25
+ U: "%2c%22blc%22%3a0%2c%22snd%22%3a1%2c%22ct%22%3a",
26
+ V: "%2c%22blc%22%3a0%2c%22snd%22%3a0%2c%22ct%22%3a",
27
+ W: "%2c%22s%22%3a0%2c%22blo%22%3a0%7d%2c%22bl%22%3a%7b%22ac%22%3a",
28
+ X: "%2c%22ri%22%3a0%7d%2c%22state%22%3a%7b%22p%22%3a0%2c%22ut%22%3a1",
29
+ Y: "%2c%22pt%22%3a0%2c%22vis%22%3a1%2c%22bls%22%3a0%2c%22blc%22%3a0%2c%22snd%22%3a1%2c%22ct%22%3a",
30
+ Z: "%2c%22sb%22%3a1%2c%22t%22%3a%5b%5d%2c%22f%22%3anull%2c%22uct%22%3a0%2c%22s%22%3a0%2c%22blo%22%3a0%7d%2c%22bl%22%3a%7b%22ac%22%3a",
31
+ };
32
+
33
+ var PRESENCE_REVERSE = {};
34
+ var PRESENCE_REGEX;
35
+ (function () {
36
+ var l = [];
37
+ for (var m in PRESENCE_MAP) {
38
+ PRESENCE_REVERSE[PRESENCE_MAP[m]] = m;
39
+ l.push(PRESENCE_MAP[m]);
40
+ }
41
+ l.reverse();
42
+ PRESENCE_REGEX = new RegExp(l.join("|"), "g");
43
+ })();
44
+
45
+ function presenceEncode(str) {
46
+ return encodeURIComponent(str)
47
+ .replace(/([_A-Z])|%../g, function (m, n) {
48
+ return n ? "%" + n.charCodeAt(0).toString(16) : m;
49
+ })
50
+ .toLowerCase()
51
+ .replace(PRESENCE_REGEX, function (m) {
52
+ return PRESENCE_REVERSE[m];
53
+ });
54
+ }
55
+
56
+ function presenceDecode(str) {
57
+ return decodeURIComponent(
58
+ str.replace(/[_A-Z]/g, function (m) {
59
+ return PRESENCE_MAP[m];
60
+ }),
61
+ );
62
+ }
63
+
64
+ function generatePresence(userID) {
65
+ var time = Date.now();
66
+ return (
67
+ "E" +
68
+ presenceEncode(
69
+ JSON.stringify({
70
+ v: 3,
71
+ time: parseInt(time / 1000, 10),
72
+ user: userID,
73
+ state: {
74
+ ut: 0,
75
+ t2: [],
76
+ lm2: null,
77
+ uct2: time,
78
+ tr: null,
79
+ tw: Math.floor(Math.random() * 4294967295) + 1,
80
+ at: time,
81
+ },
82
+ ch: {
83
+ ["p_" + userID]: 0,
84
+ },
85
+ }),
86
+ )
87
+ );
88
+ }
89
+
90
+ function generateAccessiblityCookie() {
91
+ var time = Date.now();
92
+ return encodeURIComponent(
93
+ JSON.stringify({
94
+ sr: 0,
95
+ "sr-ts": time,
96
+ jk: 0,
97
+ "jk-ts": time,
98
+ kb: 0,
99
+ "kb-ts": time,
100
+ hcm: 0,
101
+ "hcm-ts": time,
102
+ }),
103
+ );
104
+ }
105
+
106
+ function formatProxyPresence(presence, userID) {
107
+ if (presence.lat === undefined || presence.p === undefined) return null;
108
+ return {
109
+ type: "presence",
110
+ timestamp: presence.lat * 1000,
111
+ userID: userID || "",
112
+ statuses: presence.p,
113
+ };
114
+ }
115
+
116
+ function formatPresence(presence, userID) {
117
+ return {
118
+ type: "presence",
119
+ timestamp: presence.la * 1000,
120
+ userID: userID || "",
121
+ statuses: presence.a,
122
+ };
123
+ }
124
+
125
+ module.exports = {
126
+ presenceEncode,
127
+ presenceDecode,
128
+ generatePresence,
129
+ generateAccessiblityCookie,
130
+ formatProxyPresence,
131
+ formatPresence,
132
+ };
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+
3
+ const { formatID } = require("./utils");
4
+
5
+ function formatReadReceipt(event) {
6
+ return {
7
+ reader: event.reader.toString(),
8
+ time: event.time,
9
+ threadID: formatID((event.thread_fbid || event.reader).toString()),
10
+ type: "read_receipt",
11
+ };
12
+ }
13
+
14
+ function formatRead(event) {
15
+ return {
16
+ threadID: formatID(
17
+ (
18
+ (event.chat_ids && event.chat_ids[0]) ||
19
+ (event.thread_fbids && event.thread_fbids[0])
20
+ ).toString(),
21
+ ),
22
+ time: event.timestamp,
23
+ type: "read",
24
+ };
25
+ }
26
+
27
+ function formatTyp(event) {
28
+ return {
29
+ isTyping: !!event.st,
30
+ from: event.from.toString(),
31
+ threadID: formatID(
32
+ (event.to || event.thread_fbid || event.from).toString(),
33
+ ),
34
+ fromMobile: event.hasOwnProperty("from_mobile") ? event.from_mobile : true,
35
+ userID: (event.realtime_viewer_fbid || event.from).toString(),
36
+ type: "typ",
37
+ };
38
+ }
39
+
40
+ module.exports = {
41
+ formatReadReceipt,
42
+ formatRead,
43
+ formatTyp,
44
+ };
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+
3
+ const { formatID } = require("./utils");
4
+
5
+ function formatThread(data) {
6
+ return {
7
+ threadID: formatID(data.thread_fbid.toString()),
8
+ participants: data.participants.map(formatID),
9
+ participantIDs: data.participants.map(formatID),
10
+ name: data.name,
11
+ nicknames: data.custom_nickname,
12
+ snippet: data.snippet,
13
+ snippetAttachments: data.snippet_attachments,
14
+ snippetSender: formatID((data.snippet_sender || "").toString()),
15
+ unreadCount: data.unread_count,
16
+ messageCount: data.message_count,
17
+ imageSrc: data.image_src,
18
+ timestamp: data.timestamp,
19
+ muteUntil: data.mute_until,
20
+ isCanonicalUser: data.is_canonical_user,
21
+ isCanonical: data.is_canonical,
22
+ isSubscribed: data.is_subscribed,
23
+ folder: data.folder,
24
+ isArchived: data.is_archived,
25
+ recipientsLoadable: data.recipients_loadable,
26
+ hasEmailParticipant: data.has_email_participant,
27
+ readOnly: data.read_only,
28
+ canReply: data.can_reply,
29
+ cannotReplyReason: data.cannot_reply_reason,
30
+ lastMessageTimestamp: data.last_message_timestamp,
31
+ lastReadTimestamp: data.last_read_timestamp,
32
+ lastMessageType: data.last_message_type,
33
+ emoji: data.custom_like_icon,
34
+ color: data.custom_color,
35
+ adminIDs: data.admin_ids,
36
+ threadType: data.thread_type,
37
+ };
38
+ }
39
+
40
+ module.exports = {
41
+ formatThread,
42
+ };
@@ -0,0 +1,141 @@
1
+ "use strict";
2
+
3
+ function getType(obj) {
4
+ return Object.prototype.toString.call(obj).slice(8, -1);
5
+ }
6
+
7
+ function formatID(id) {
8
+ if (id != undefined && id != null) return id.replace(/(fb)?id[:.]/, "");
9
+ else return id;
10
+ }
11
+
12
+ function padZeros(val, len) {
13
+ val = String(val);
14
+ len = len || 2;
15
+ while (val.length < len) val = "0" + val;
16
+ return val;
17
+ }
18
+
19
+ function arrayToObject(arr, getKey, getValue) {
20
+ return arr.reduce(function (acc, val) {
21
+ acc[getKey(val)] = getValue(val);
22
+ return acc;
23
+ }, {});
24
+ }
25
+
26
+ function arrToForm(form) {
27
+ return arrayToObject(
28
+ form,
29
+ function (v) {
30
+ return v.name;
31
+ },
32
+ function (v) {
33
+ return v.val;
34
+ },
35
+ );
36
+ }
37
+
38
+ function getData_Path(Obj, Arr, Stt) {
39
+ if (Arr.length === 0 && Obj != undefined) {
40
+ return Obj;
41
+ } else if (Obj == undefined) {
42
+ return Stt;
43
+ }
44
+ const head = Arr[0];
45
+ if (head == undefined) {
46
+ return Stt;
47
+ }
48
+ const tail = Arr.slice(1);
49
+ return getData_Path(Obj[head], tail, Stt++);
50
+ }
51
+
52
+ function setData_Path(obj, path, value) {
53
+ if (!path.length) {
54
+ return obj;
55
+ }
56
+ const currentKey = path[0];
57
+ let currentObj = obj[currentKey];
58
+
59
+ if (!currentObj) {
60
+ obj[currentKey] = value;
61
+ currentObj = obj[currentKey];
62
+ }
63
+ path.shift();
64
+ if (!path.length) {
65
+ currentObj = value;
66
+ } else {
67
+ currentObj = setData_Path(currentObj, path, value);
68
+ }
69
+
70
+ return obj;
71
+ }
72
+
73
+ function getPaths(obj, parentPath = []) {
74
+ let paths = [];
75
+ for (let prop in obj) {
76
+ if (typeof obj[prop] === "object") {
77
+ paths = paths.concat(getPaths(obj[prop], [...parentPath, prop]));
78
+ } else {
79
+ paths.push([...parentPath, prop]);
80
+ }
81
+ }
82
+ return paths;
83
+ }
84
+
85
+ function cleanHTML(text) {
86
+ text = text.replace(
87
+ /(<br>)|(<\/?i>)|(<\/?em>)|(<\/?b>)|(!?~)|(&amp;)|(&#039;)|(&lt;)|(&gt;)|(&quot;)/g,
88
+ (match) => {
89
+ switch (match) {
90
+ case "<br>":
91
+ return "\n";
92
+ case "<i>":
93
+ case "<em>":
94
+ case "</i>":
95
+ case "</em>":
96
+ return "*";
97
+ case "<b>":
98
+ case "</b>":
99
+ return "**";
100
+ case "~!":
101
+ case "!~":
102
+ return "||";
103
+ case "&amp;":
104
+ return "&";
105
+ case "&#039;":
106
+ return "'";
107
+ case "&lt;":
108
+ return "<";
109
+ case "&gt;":
110
+ return ">";
111
+ case "&quot;":
112
+ return '"';
113
+ }
114
+ },
115
+ );
116
+ return text;
117
+ }
118
+
119
+ function getCurrentTimestamp() {
120
+ const date = new Date();
121
+ const unixTime = date.getTime();
122
+ return unixTime;
123
+ }
124
+
125
+ function getSignatureID() {
126
+ return Math.floor(Math.random() * 2147483648).toString(16);
127
+ }
128
+
129
+ module.exports = {
130
+ getType,
131
+ formatID,
132
+ padZeros,
133
+ arrayToObject,
134
+ arrToForm,
135
+ getData_Path,
136
+ setData_Path,
137
+ getPaths,
138
+ cleanHTML,
139
+ getCurrentTimestamp,
140
+ getSignatureID,
141
+ };
@@ -0,0 +1,125 @@
1
+ "use strict";
2
+
3
+ const logger = require("../../../func/logger");
4
+
5
+ /**
6
+ * Tạo hàm maybeAutoLogin: khi session hết hạn thì thử đăng nhập lại và retry request.
7
+ * @param {Object} ctx - Context (jar, performAutoLogin, globalOptions, ...)
8
+ * @param {Object} http - HTTP client (get, post, postFormData)
9
+ * @param {Object} helpers - { buildUrl, headerOf, formatCookie }
10
+ * @param {Function} emit - createEmit(ctx)
11
+ * @param {Function} parseAndCheckLogin - Hàm parse chính để retry sau khi auto login thành công
12
+ */
13
+ function createMaybeAutoLogin(ctx, http, helpers, emit, parseAndCheckLogin) {
14
+ const { buildUrl, headerOf, formatCookie } = helpers;
15
+
16
+ return async function maybeAutoLogin(resData, resConfig) {
17
+ if (ctx.auto_login) {
18
+ const e = new Error("Not logged in. Auto login already in progress.");
19
+ e.error = "Not logged in.";
20
+ e.res = resData;
21
+ throw e;
22
+ }
23
+ if (typeof ctx.performAutoLogin !== "function") {
24
+ const e = new Error("Not logged in. Auto login function not available.");
25
+ e.error = "Not logged in.";
26
+ e.res = resData;
27
+ throw e;
28
+ }
29
+
30
+ ctx.auto_login = true;
31
+ logger("Login session expired, attempting auto login...", "warn");
32
+ emit("sessionExpired", { res: resData });
33
+
34
+ try {
35
+ const ok = await ctx.performAutoLogin();
36
+ if (ok) {
37
+ logger("Auto login successful! Retrying request...", "info");
38
+ emit("autoLoginSuccess", { res: resData });
39
+ ctx.auto_login = false;
40
+
41
+ if (resConfig) {
42
+ const url = buildUrl(resConfig);
43
+ const method = String(resConfig?.method || "GET").toUpperCase();
44
+ const ctype = String(headerOf(resConfig?.headers, "content-type") || "").toLowerCase();
45
+ const isMultipart = ctype.includes("multipart/form-data");
46
+ const payload = resConfig?.data;
47
+ const params = resConfig?.params;
48
+
49
+ try {
50
+ let newData;
51
+ if (method === "GET") {
52
+ newData = await http.get(url, ctx.jar, params || null, ctx.globalOptions, ctx);
53
+ } else if (isMultipart) {
54
+ newData = await http.postFormData(url, ctx.jar, payload, params, ctx.globalOptions, ctx);
55
+ } else {
56
+ newData = await http.post(url, ctx.jar, payload, ctx.globalOptions, ctx);
57
+ }
58
+ return await parseAndCheckLogin(ctx, http)(newData);
59
+ } catch (retryErr) {
60
+ if (
61
+ retryErr?.code === "ERR_INVALID_CHAR" ||
62
+ (retryErr?.message && retryErr.message.includes("Invalid character in header"))
63
+ ) {
64
+ logger(
65
+ `Auto login retry failed: Invalid header detected. Error: ${retryErr.message}`,
66
+ "error"
67
+ );
68
+ const e = new Error("Not logged in. Auto login retry failed due to invalid header.");
69
+ e.error = "Not logged in.";
70
+ e.res = resData;
71
+ e.originalError = retryErr;
72
+ throw e;
73
+ }
74
+ logger(
75
+ `Auto login retry failed: ${
76
+ retryErr && retryErr.message ? retryErr.message : String(retryErr)
77
+ }`,
78
+ "error"
79
+ );
80
+ const e = new Error("Not logged in. Auto login retry failed.");
81
+ e.error = "Not logged in.";
82
+ e.res = resData;
83
+ e.originalError = retryErr;
84
+ throw e;
85
+ }
86
+ } else {
87
+ const e = new Error(
88
+ "Not logged in. Auto login successful but cannot retry request."
89
+ );
90
+ e.error = "Not logged in.";
91
+ e.res = resData;
92
+ throw e;
93
+ }
94
+ } else {
95
+ ctx.auto_login = false;
96
+ const e = new Error("Not logged in. Auto login failed.");
97
+ e.error = "Not logged in.";
98
+ e.res = resData;
99
+ emit("autoLoginFailed", { error: e, res: resData });
100
+ throw e;
101
+ }
102
+ } catch (autoLoginErr) {
103
+ ctx.auto_login = false;
104
+ if (autoLoginErr.error === "Not logged in.") {
105
+ throw autoLoginErr;
106
+ }
107
+ logger(
108
+ `Auto login error: ${
109
+ autoLoginErr && autoLoginErr.message ? autoLoginErr.message : String(autoLoginErr)
110
+ }`,
111
+ "error"
112
+ );
113
+ const e = new Error("Not logged in. Auto login error.");
114
+ e.error = "Not logged in.";
115
+ e.res = resData;
116
+ e.originalError = autoLoginErr;
117
+ emit("autoLoginFailed", { error: e, res: resData });
118
+ throw e;
119
+ }
120
+ };
121
+ }
122
+
123
+ module.exports = {
124
+ createMaybeAutoLogin
125
+ };