@gooin/garmin-connect 1.7.6 → 1.8.1
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/dist/common/FileMFASessionStorage.js +1 -247
- package/dist/common/HttpClient copy.js +1 -361
- package/dist/common/HttpClient.d.ts +1 -0
- package/dist/common/HttpClient.js +1 -612
- package/dist/common/HttpClient.js.map +1 -1
- package/dist/common/HttpClientOLD.js +1 -361
- package/dist/common/HttpClientOLDV1.js +1 -361
- package/dist/common/HttpClientV1.js +1 -577
- package/dist/common/MFAManager.js +1 -126
- package/dist/common/MFASessionStorage.js +1 -3
- package/dist/common/RedisMFASessionStorage.js +1 -281
- package/dist/common/RedisMFASessionStorage.js.map +1 -1
- package/dist/garmin/GarminConnect.d.ts +243 -66
- package/dist/garmin/GarminConnect.js +1 -592
- package/dist/garmin/GarminConnect.js.map +1 -1
- package/dist/garmin/UrlClass.d.ts +21 -0
- package/dist/garmin/UrlClass.js +1 -149
- package/dist/garmin/UrlClass.js.map +1 -1
- package/dist/garmin/common/DateUtils.d.ts +1 -0
- package/dist/garmin/common/DateUtils.js +1 -38
- package/dist/garmin/common/DateUtils.js.map +1 -1
- package/dist/garmin/common/HydrationUtils.js +1 -16
- package/dist/garmin/common/WeightUtils.js +1 -9
- package/dist/garmin/modules/activity/base.d.ts +39 -0
- package/dist/garmin/modules/activity/base.js +1 -0
- package/dist/garmin/modules/activity/base.js.map +1 -0
- package/dist/garmin/modules/activity/cycling.d.ts +16 -0
- package/dist/garmin/modules/activity/cycling.js +1 -0
- package/dist/garmin/modules/activity/cycling.js.map +1 -0
- package/dist/garmin/modules/activity/running.d.ts +15 -0
- package/dist/garmin/modules/activity/running.js +1 -0
- package/dist/garmin/modules/activity/running.js.map +1 -0
- package/dist/garmin/modules/activity/training-status.d.ts +13 -0
- package/dist/garmin/modules/activity/training-status.js +1 -0
- package/dist/garmin/modules/activity/training-status.js.map +1 -0
- package/dist/garmin/modules/coach/activity-summary.d.ts +20 -0
- package/dist/garmin/modules/coach/activity-summary.js +1 -0
- package/dist/garmin/modules/coach/activity-summary.js.map +1 -0
- package/dist/garmin/modules/coach/helpers.d.ts +157 -0
- package/dist/garmin/modules/coach/helpers.js +1 -0
- package/dist/garmin/modules/coach/helpers.js.map +1 -0
- package/dist/garmin/modules/coach/training-overview.d.ts +5 -0
- package/dist/garmin/modules/coach/training-overview.js +1 -0
- package/dist/garmin/modules/coach/training-overview.js.map +1 -0
- package/dist/garmin/modules/coach/wellness-overview.d.ts +5 -0
- package/dist/garmin/modules/coach/wellness-overview.js +1 -0
- package/dist/garmin/modules/coach/wellness-overview.js.map +1 -0
- package/dist/garmin/modules/coach/wellness-summary.d.ts +4 -0
- package/dist/garmin/modules/coach/wellness-summary.js +1 -0
- package/dist/garmin/modules/coach/wellness-summary.js.map +1 -0
- package/dist/garmin/modules/coach.d.ts +60 -0
- package/dist/garmin/modules/coach.js +1 -0
- package/dist/garmin/modules/coach.js.map +1 -0
- package/dist/garmin/modules/course.d.ts +27 -0
- package/dist/garmin/modules/course.js +1 -0
- package/dist/garmin/modules/course.js.map +1 -0
- package/dist/garmin/modules/device.d.ts +11 -0
- package/dist/garmin/modules/device.js +1 -0
- package/dist/garmin/modules/device.js.map +1 -0
- package/dist/garmin/modules/misc.d.ts +18 -0
- package/dist/garmin/modules/misc.js +1 -0
- package/dist/garmin/modules/misc.js.map +1 -0
- package/dist/garmin/modules/types.d.ts +10 -0
- package/dist/garmin/modules/types.js +1 -0
- package/dist/garmin/modules/types.js.map +1 -0
- package/dist/garmin/modules/user.d.ts +18 -0
- package/dist/garmin/modules/user.js +1 -0
- package/dist/garmin/modules/user.js.map +1 -0
- package/dist/garmin/modules/wellness/body-battery.d.ts +11 -0
- package/dist/garmin/modules/wellness/body-battery.js +1 -0
- package/dist/garmin/modules/wellness/body-battery.js.map +1 -0
- package/dist/garmin/modules/wellness/heart-rate.d.ts +11 -0
- package/dist/garmin/modules/wellness/heart-rate.js +1 -0
- package/dist/garmin/modules/wellness/heart-rate.js.map +1 -0
- package/dist/garmin/modules/wellness/hrv.d.ts +12 -0
- package/dist/garmin/modules/wellness/hrv.js +1 -0
- package/dist/garmin/modules/wellness/hrv.js.map +1 -0
- package/dist/garmin/modules/wellness/hydration.d.ts +12 -0
- package/dist/garmin/modules/wellness/hydration.js +1 -0
- package/dist/garmin/modules/wellness/hydration.js.map +1 -0
- package/dist/garmin/modules/wellness/sleep.d.ts +16 -0
- package/dist/garmin/modules/wellness/sleep.js +1 -0
- package/dist/garmin/modules/wellness/sleep.js.map +1 -0
- package/dist/garmin/modules/wellness/weight.d.ts +20 -0
- package/dist/garmin/modules/wellness/weight.js +1 -0
- package/dist/garmin/modules/wellness/weight.js.map +1 -0
- package/dist/garmin/modules/workout.d.ts +23 -0
- package/dist/garmin/modules/workout.js +1 -0
- package/dist/garmin/modules/workout.js.map +1 -0
- package/dist/garmin/types/activity-stats.d.ts +21 -0
- package/dist/garmin/types/activity-stats.js +1 -0
- package/dist/garmin/types/activity-stats.js.map +1 -0
- package/dist/garmin/types/activity.js +1 -27
- package/dist/garmin/types/body-battery.d.ts +7 -0
- package/dist/garmin/types/body-battery.js +1 -0
- package/dist/garmin/types/body-battery.js.map +1 -0
- package/dist/garmin/types/coach.d.ts +533 -0
- package/dist/garmin/types/coach.js +1 -0
- package/dist/garmin/types/coach.js.map +1 -0
- package/dist/garmin/types/course.d.ts +3 -11
- package/dist/garmin/types/course.js +1 -3
- package/dist/garmin/types/cycling.d.ts +70 -0
- package/dist/garmin/types/cycling.js +1 -0
- package/dist/garmin/types/cycling.js.map +1 -0
- package/dist/garmin/types/device.d.ts +32 -0
- package/dist/garmin/types/device.js +1 -0
- package/dist/garmin/types/device.js.map +1 -0
- package/dist/garmin/types/golf.d.ts +4 -4
- package/dist/garmin/types/golf.js +1 -3
- package/dist/garmin/types/golf.js.map +1 -1
- package/dist/garmin/types/heartrate.d.ts +3 -3
- package/dist/garmin/types/heartrate.js +1 -2
- package/dist/garmin/types/hrv.d.ts +3 -0
- package/dist/garmin/types/hrv.js +1 -3
- package/dist/garmin/types/hydration.d.ts +3 -3
- package/dist/garmin/types/hydration.js +1 -2
- package/dist/garmin/types/index.d.ts +15 -0
- package/dist/garmin/types/index.js +1 -36
- package/dist/garmin/types/index.js.map +1 -1
- package/dist/garmin/types/personal-info.js +1 -3
- package/dist/garmin/types/race-prediction.d.ts +50 -0
- package/dist/garmin/types/race-prediction.js +1 -0
- package/dist/garmin/types/race-prediction.js.map +1 -0
- package/dist/garmin/types/sleep.js +1 -3
- package/dist/garmin/types/training-status.js +1 -3
- package/dist/garmin/types/weight.d.ts +46 -4
- package/dist/garmin/types/weight.js +1 -3
- package/dist/garmin/workouts/Running.js +1 -47
- package/dist/garmin/workouts/templates/RunningTemplate.js +1 -78
- package/dist/index.js +1 -11
- package/dist/utils.js +1 -37
- package/package.json +4 -3
|
@@ -1,612 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.HttpClient = void 0;
|
|
7
|
-
const axios_1 = __importDefault(require("axios"));
|
|
8
|
-
const lodash_1 = __importDefault(require("lodash"));
|
|
9
|
-
const luxon_1 = require("luxon");
|
|
10
|
-
const oauth_1_0a_1 = __importDefault(require("oauth-1.0a"));
|
|
11
|
-
const qs_1 = __importDefault(require("qs"));
|
|
12
|
-
const MFAManager_1 = require("./MFAManager");
|
|
13
|
-
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
14
|
-
const tough_cookie_1 = require("tough-cookie");
|
|
15
|
-
const axios_cookiejar_support_1 = require("axios-cookiejar-support");
|
|
16
|
-
// 正则表达式常量
|
|
17
|
-
const CSRF_RE = new RegExp('name="_csrf"\\s+value="(.+?)"');
|
|
18
|
-
const TICKET_RE = new RegExp('ticket=([^"]+)"');
|
|
19
|
-
const ACCOUNT_LOCKED_RE = new RegExp('var statuss*=s*"([^"]*)"');
|
|
20
|
-
const PAGE_TITLE_RE = new RegExp('<title>([^<]*)</title>');
|
|
21
|
-
// 用户代理常量
|
|
22
|
-
const USER_AGENT_CONNECTMOBILE = 'com.garmin.android.apps.connectmobile';
|
|
23
|
-
const USER_AGENT_BROWSER = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36';
|
|
24
|
-
const USER_AGENT_BROWSER_MAC = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36';
|
|
25
|
-
// URL常量
|
|
26
|
-
const OAUTH_CONSUMER_URL = 'https://thegarth.s3.amazonaws.com/oauth_consumer.json';
|
|
27
|
-
// HTTP状态码常量
|
|
28
|
-
const HTTP_STATUS = {
|
|
29
|
-
UNAUTHORIZED: 401
|
|
30
|
-
};
|
|
31
|
-
// 全局变量
|
|
32
|
-
let tokenRefreshPromise = null;
|
|
33
|
-
let refreshSubscribers = [];
|
|
34
|
-
class HttpClient {
|
|
35
|
-
constructor(url, config) {
|
|
36
|
-
var _a, _b;
|
|
37
|
-
const jar = new tough_cookie_1.CookieJar();
|
|
38
|
-
this.url = url;
|
|
39
|
-
this.client = (0, axios_cookiejar_support_1.wrapper)(axios_1.default.create({
|
|
40
|
-
timeout: (_a = config === null || config === void 0 ? void 0 : config.timeout) !== null && _a !== void 0 ? _a : 5000,
|
|
41
|
-
timeoutErrorMessage: `Request Timeout: > ${(_b = config === null || config === void 0 ? void 0 : config.timeout) !== null && _b !== void 0 ? _b : 5000} ms`,
|
|
42
|
-
maxRedirects: 10,
|
|
43
|
-
validateStatus: function (status) {
|
|
44
|
-
return status >= 200 && status < 400;
|
|
45
|
-
},
|
|
46
|
-
withCredentials: true,
|
|
47
|
-
jar: jar
|
|
48
|
-
}));
|
|
49
|
-
this.config = config;
|
|
50
|
-
// 使用新的MFA配置初始化MFAManager
|
|
51
|
-
const mfaConfig = config.mfa || {
|
|
52
|
-
type: 'file',
|
|
53
|
-
dir: config.mfaStorageDir || '/tmp'
|
|
54
|
-
};
|
|
55
|
-
this.mfaManager = MFAManager_1.MFAManager.getInstance(mfaConfig);
|
|
56
|
-
this.setupInterceptors();
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* 设置请求和响应拦截器
|
|
60
|
-
*/
|
|
61
|
-
setupInterceptors() {
|
|
62
|
-
// 响应拦截器
|
|
63
|
-
this.client.interceptors.response.use((response) => {
|
|
64
|
-
// this.logResponseTracking(response);
|
|
65
|
-
return response;
|
|
66
|
-
}, async (error) => {
|
|
67
|
-
return this.handleResponseError(error);
|
|
68
|
-
});
|
|
69
|
-
// 请求拦截器
|
|
70
|
-
this.client.interceptors.request.use(async (config) => {
|
|
71
|
-
if (this.oauth2Token) {
|
|
72
|
-
config.headers.Authorization =
|
|
73
|
-
'Bearer ' + this.oauth2Token.access_token;
|
|
74
|
-
}
|
|
75
|
-
return config;
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* 记录响应跟踪信息
|
|
80
|
-
*/
|
|
81
|
-
logResponseTracking(response) {
|
|
82
|
-
var _a, _b, _c, _d;
|
|
83
|
-
if (((_a = response.config.url) === null || _a === void 0 ? void 0 : _a.includes('signin')) ||
|
|
84
|
-
((_b = response.config.url) === null || _b === void 0 ? void 0 : _b.includes('verifyMFA'))) {
|
|
85
|
-
console.log('> 响应跟踪 - URL:', response.config.url);
|
|
86
|
-
console.log('响应跟踪 - 状态码:', response.status);
|
|
87
|
-
console.log('响应跟踪 - 最终URL:', ((_c = response.request) === null || _c === void 0 ? void 0 : _c.responseURL) || response.config.url);
|
|
88
|
-
console.log('响应跟踪 - 重定向次数:', ((_d = response.request) === null || _d === void 0 ? void 0 : _d.redirectCount) || 0);
|
|
89
|
-
if (response.headers.location) {
|
|
90
|
-
console.log('响应跟踪 - Location头:', response.headers.location);
|
|
91
|
-
}
|
|
92
|
-
if (response.status >= 300 && response.status < 400) {
|
|
93
|
-
console.log('响应跟踪 - 检测到重定向状态码:', response.status);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
/**
|
|
98
|
-
* 处理响应错误
|
|
99
|
-
*/
|
|
100
|
-
async handleResponseError(error) {
|
|
101
|
-
var _a;
|
|
102
|
-
if (axios_1.default.isAxiosError(error) && error.code === 'ECONNABORTED') {
|
|
103
|
-
throw new Error(error.message || 'Request Timeout');
|
|
104
|
-
}
|
|
105
|
-
const originalRequest = error.config;
|
|
106
|
-
if (((_a = error === null || error === void 0 ? void 0 : error.response) === null || _a === void 0 ? void 0 : _a.status) === HTTP_STATUS.UNAUTHORIZED &&
|
|
107
|
-
!(originalRequest === null || originalRequest === void 0 ? void 0 : originalRequest._retry)) {
|
|
108
|
-
if (!this.oauth2Token) {
|
|
109
|
-
throw new Error('No OAuth2 token available');
|
|
110
|
-
}
|
|
111
|
-
originalRequest._retry = true;
|
|
112
|
-
try {
|
|
113
|
-
if (!tokenRefreshPromise) {
|
|
114
|
-
tokenRefreshPromise = this.refreshOauth2Token().finally(() => {
|
|
115
|
-
tokenRefreshPromise = null;
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
await tokenRefreshPromise;
|
|
119
|
-
originalRequest.headers.Authorization = `Bearer ${this.oauth2Token.access_token}`;
|
|
120
|
-
return this.client(originalRequest);
|
|
121
|
-
}
|
|
122
|
-
catch (err) {
|
|
123
|
-
console.error('Token refresh failed:', err);
|
|
124
|
-
throw err;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
if (axios_1.default.isAxiosError(error) && error.response) {
|
|
128
|
-
this.handleError(error.response);
|
|
129
|
-
}
|
|
130
|
-
else {
|
|
131
|
-
throw new Error('Network error or unknown error occurred');
|
|
132
|
-
}
|
|
133
|
-
throw error;
|
|
134
|
-
}
|
|
135
|
-
/**
|
|
136
|
-
* 获取OAuth消费者信息
|
|
137
|
-
*/
|
|
138
|
-
async fetchOauthConsumer() {
|
|
139
|
-
const response = await axios_1.default.get(OAUTH_CONSUMER_URL);
|
|
140
|
-
this.OAUTH_CONSUMER = {
|
|
141
|
-
key: response.data.consumer_key,
|
|
142
|
-
secret: response.data.consumer_secret
|
|
143
|
-
};
|
|
144
|
-
}
|
|
145
|
-
/**
|
|
146
|
-
* 检查令牌有效性
|
|
147
|
-
*/
|
|
148
|
-
async checkTokenVaild() {
|
|
149
|
-
if (this.oauth2Token) {
|
|
150
|
-
if (this.oauth2Token.expires_at < luxon_1.DateTime.now().toSeconds()) {
|
|
151
|
-
console.error('Token expired!');
|
|
152
|
-
await this.refreshOauth2Token();
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
/**
|
|
157
|
-
* GET请求
|
|
158
|
-
*/
|
|
159
|
-
async get(url, config) {
|
|
160
|
-
const response = await this.client.get(url, config);
|
|
161
|
-
return response === null || response === void 0 ? void 0 : response.data;
|
|
162
|
-
}
|
|
163
|
-
/**
|
|
164
|
-
* POST请求
|
|
165
|
-
*/
|
|
166
|
-
async post(url, data, config) {
|
|
167
|
-
const response = await this.client.post(url, data, config);
|
|
168
|
-
return response === null || response === void 0 ? void 0 : response.data;
|
|
169
|
-
}
|
|
170
|
-
/**
|
|
171
|
-
* PUT请求
|
|
172
|
-
*/
|
|
173
|
-
async put(url, data, config) {
|
|
174
|
-
const response = await this.client.put(url, data, config);
|
|
175
|
-
return response === null || response === void 0 ? void 0 : response.data;
|
|
176
|
-
}
|
|
177
|
-
/**
|
|
178
|
-
* DELETE请求
|
|
179
|
-
*/
|
|
180
|
-
async delete(url, config) {
|
|
181
|
-
const response = await this.client.post(url, null, {
|
|
182
|
-
...config,
|
|
183
|
-
headers: {
|
|
184
|
-
...config === null || config === void 0 ? void 0 : config.headers,
|
|
185
|
-
'X-Http-Method-Override': 'DELETE'
|
|
186
|
-
}
|
|
187
|
-
});
|
|
188
|
-
return response === null || response === void 0 ? void 0 : response.data;
|
|
189
|
-
}
|
|
190
|
-
/**
|
|
191
|
-
* 设置通用请求头
|
|
192
|
-
*/
|
|
193
|
-
setCommonHeader(headers) {
|
|
194
|
-
lodash_1.default.each(headers, (headerValue, key) => {
|
|
195
|
-
this.client.defaults.headers.common[key] = headerValue;
|
|
196
|
-
});
|
|
197
|
-
}
|
|
198
|
-
/**
|
|
199
|
-
* 处理错误
|
|
200
|
-
*/
|
|
201
|
-
handleError(response) {
|
|
202
|
-
this.handleHttpError(response);
|
|
203
|
-
}
|
|
204
|
-
/**
|
|
205
|
-
* 处理HTTP错误
|
|
206
|
-
*/
|
|
207
|
-
handleHttpError(response) {
|
|
208
|
-
const { status, statusText, data } = response;
|
|
209
|
-
const errorMessage = {
|
|
210
|
-
status,
|
|
211
|
-
statusText,
|
|
212
|
-
data: typeof data === 'object' ? JSON.stringify(data) : data
|
|
213
|
-
};
|
|
214
|
-
console.error('HTTP Error:', errorMessage);
|
|
215
|
-
throw new Error(`HTTP Error (${status}): ${statusText}`);
|
|
216
|
-
}
|
|
217
|
-
/**
|
|
218
|
-
* 登录到Garmin Connect
|
|
219
|
-
* @param username 用户名
|
|
220
|
-
* @param password 密码
|
|
221
|
-
* @param mfaCallback MFA验证回调函数
|
|
222
|
-
* @param sessionId 会话ID,用于分步登录
|
|
223
|
-
* @returns Promise<HttpClient>
|
|
224
|
-
*/
|
|
225
|
-
async login(username, password, sessionId) {
|
|
226
|
-
try {
|
|
227
|
-
// 准备登录
|
|
228
|
-
await this.fetchOauthConsumer();
|
|
229
|
-
// 获取登录票据
|
|
230
|
-
const ticket = await this.getLoginTicket(username, password, sessionId);
|
|
231
|
-
// 获取OAuth1令牌
|
|
232
|
-
const oauth1 = await this.getOauth1Token(ticket);
|
|
233
|
-
// 交换OAuth2令牌
|
|
234
|
-
await this.exchange(oauth1);
|
|
235
|
-
return this;
|
|
236
|
-
}
|
|
237
|
-
catch (error) {
|
|
238
|
-
console.error('Login failed:', error);
|
|
239
|
-
throw error;
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
/**
|
|
243
|
-
* 获取登录票据
|
|
244
|
-
* @param username 用户名
|
|
245
|
-
* @param password 密码
|
|
246
|
-
* @param mfaCallback MFA验证回调函数
|
|
247
|
-
* @param sessionId 会话ID,用于分步登录
|
|
248
|
-
* @returns 登录票据
|
|
249
|
-
*/
|
|
250
|
-
async getLoginTicket(username, password, sessionId) {
|
|
251
|
-
// 准备登录参数
|
|
252
|
-
const loginParams = this.prepareLoginParams();
|
|
253
|
-
// 步骤1: 设置cookie
|
|
254
|
-
await this.performLoginStep1(loginParams.step1Params);
|
|
255
|
-
// 步骤2: 获取CSRF令牌
|
|
256
|
-
const csrfToken = await this.performLoginStep2(loginParams.step2Params);
|
|
257
|
-
// 步骤3: 提交凭据
|
|
258
|
-
let signinResult = await this.performLoginStep3(username, password, csrfToken, loginParams.step3Params);
|
|
259
|
-
// 检查账户锁定状态
|
|
260
|
-
this.handleAccountLocked(signinResult);
|
|
261
|
-
// 检查页面标题,判断是否需要MFA
|
|
262
|
-
const pageTitle = this.handlePageTitle(signinResult);
|
|
263
|
-
// 如果需要MFA,执行MFA验证
|
|
264
|
-
if (this.isMFARequired(pageTitle)) {
|
|
265
|
-
// 如果提供了sessionId,则使用分步登录模式
|
|
266
|
-
if (sessionId) {
|
|
267
|
-
// 等待外部提供验证码
|
|
268
|
-
const mfaCode = await this.mfaManager.waitForMFACode(sessionId);
|
|
269
|
-
// 使用获取到的验证码完成MFA验证
|
|
270
|
-
signinResult = await this.handleMFAWithCode(signinResult, loginParams.step3Params, mfaCode);
|
|
271
|
-
}
|
|
272
|
-
else {
|
|
273
|
-
throw new Error('需要MFA验证,但未提供验证码获取方式');
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
// 提取票据
|
|
277
|
-
const ticket = this.extractTicket(signinResult);
|
|
278
|
-
if (!ticket) {
|
|
279
|
-
throw new Error('登录失败(未找到票据或MFA验证失败),请检查用户名和密码');
|
|
280
|
-
}
|
|
281
|
-
return ticket;
|
|
282
|
-
}
|
|
283
|
-
/**
|
|
284
|
-
* 准备登录参数
|
|
285
|
-
*/
|
|
286
|
-
prepareLoginParams() {
|
|
287
|
-
return {
|
|
288
|
-
step1Params: {
|
|
289
|
-
clientId: 'GarminConnect',
|
|
290
|
-
locale: 'en',
|
|
291
|
-
service: this.url.GC_MODERN
|
|
292
|
-
},
|
|
293
|
-
step2Params: {
|
|
294
|
-
id: 'gauth-widget',
|
|
295
|
-
embedWidget: true,
|
|
296
|
-
locale: 'en',
|
|
297
|
-
gauthHost: this.url.GARMIN_SSO_EMBED
|
|
298
|
-
},
|
|
299
|
-
step3Params: {
|
|
300
|
-
id: 'gauth-widget',
|
|
301
|
-
embedWidget: true,
|
|
302
|
-
clientId: 'GarminConnect',
|
|
303
|
-
locale: 'en',
|
|
304
|
-
gauthHost: this.url.GARMIN_SSO_EMBED,
|
|
305
|
-
service: this.url.GARMIN_SSO_EMBED,
|
|
306
|
-
source: this.url.GARMIN_SSO_EMBED,
|
|
307
|
-
redirectAfterAccountLoginUrl: this.url.GARMIN_SSO_EMBED,
|
|
308
|
-
redirectAfterAccountCreationUrl: this.url.GARMIN_SSO_EMBED
|
|
309
|
-
}
|
|
310
|
-
};
|
|
311
|
-
}
|
|
312
|
-
/**
|
|
313
|
-
* 执行登录步骤1:设置cookie
|
|
314
|
-
*/
|
|
315
|
-
async performLoginStep1(step1Params) {
|
|
316
|
-
const step1Url = `${this.url.GARMIN_SSO_EMBED}?${qs_1.default.stringify(step1Params)}`;
|
|
317
|
-
await this.client.get(step1Url);
|
|
318
|
-
}
|
|
319
|
-
/**
|
|
320
|
-
* 执行登录步骤2:获取CSRF令牌
|
|
321
|
-
*/
|
|
322
|
-
async performLoginStep2(step2Params) {
|
|
323
|
-
const step2Url = `${this.url.SIGNIN_URL}?${qs_1.default.stringify(step2Params)}`;
|
|
324
|
-
const step2Result = await this.get(step2Url);
|
|
325
|
-
const csrfToken = this.extractCsrfToken(step2Result);
|
|
326
|
-
if (!csrfToken) {
|
|
327
|
-
throw new Error('登录 - 未找到CSRF令牌');
|
|
328
|
-
}
|
|
329
|
-
return csrfToken;
|
|
330
|
-
}
|
|
331
|
-
/**
|
|
332
|
-
* 执行登录步骤3:提交凭据
|
|
333
|
-
*/
|
|
334
|
-
async performLoginStep3(username, password, csrfToken, step3Params) {
|
|
335
|
-
const step3Url = `${this.url.SIGNIN_URL}?${qs_1.default.stringify(step3Params)}`;
|
|
336
|
-
// console.log('🚀 - getLoginTicket - step3Url:', step3Url);
|
|
337
|
-
const step3Form = new URLSearchParams();
|
|
338
|
-
step3Form.append('username', username);
|
|
339
|
-
step3Form.append('password', password);
|
|
340
|
-
step3Form.append('embed', 'true');
|
|
341
|
-
step3Form.append('_csrf', csrfToken);
|
|
342
|
-
return this.post(step3Url, step3Form, {
|
|
343
|
-
headers: {
|
|
344
|
-
'Content-Type': 'application/x-www-form-urlencoded',
|
|
345
|
-
Dnt: 1,
|
|
346
|
-
Origin: this.url.GARMIN_SSO_ORIGIN,
|
|
347
|
-
Referer: this.url.SIGNIN_URL,
|
|
348
|
-
'User-Agent': USER_AGENT_CONNECTMOBILE
|
|
349
|
-
}
|
|
350
|
-
});
|
|
351
|
-
}
|
|
352
|
-
/**
|
|
353
|
-
* 判断是否需要MFA验证
|
|
354
|
-
*/
|
|
355
|
-
isMFARequired(pageTitle) {
|
|
356
|
-
return pageTitle.toLowerCase().includes('mfa');
|
|
357
|
-
}
|
|
358
|
-
/**
|
|
359
|
-
* 从响应中提取票据
|
|
360
|
-
*/
|
|
361
|
-
extractTicket(signinResult) {
|
|
362
|
-
const ticketRegResult = TICKET_RE.exec(signinResult);
|
|
363
|
-
return ticketRegResult ? ticketRegResult[1] : null;
|
|
364
|
-
}
|
|
365
|
-
/**
|
|
366
|
-
* 处理MFA验证(使用直接提供的验证码)
|
|
367
|
-
* @param htmlStr HTML响应字符串
|
|
368
|
-
* @param signinParams 登录参数
|
|
369
|
-
* @param mfaCode MFA验证码
|
|
370
|
-
* @returns MFA验证后的响应字符串
|
|
371
|
-
*/
|
|
372
|
-
async handleMFAWithCode(htmlStr, signinParams, mfaCode) {
|
|
373
|
-
try {
|
|
374
|
-
// 提取CSRF令牌
|
|
375
|
-
const csrfToken = this.extractCsrfToken(htmlStr);
|
|
376
|
-
if (!csrfToken) {
|
|
377
|
-
throw new Error('MFA验证 - 未找到CSRF令牌');
|
|
378
|
-
}
|
|
379
|
-
// 提交MFA验证码
|
|
380
|
-
const mfaResult = await this.submitMFACode(csrfToken, mfaCode, signinParams);
|
|
381
|
-
// 验证MFA结果
|
|
382
|
-
return this.validateMFAResult(mfaResult);
|
|
383
|
-
}
|
|
384
|
-
catch (error) {
|
|
385
|
-
console.error('MFA验证失败:', error);
|
|
386
|
-
throw new Error(`MFA验证失败: ${error}`);
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
/**
|
|
390
|
-
* 处理MFA验证
|
|
391
|
-
* @param htmlStr HTML响应字符串
|
|
392
|
-
* @param signinParams 登录参数
|
|
393
|
-
* @param mfaCallback MFA验证回调函数
|
|
394
|
-
* @returns MFA验证后的响应字符串
|
|
395
|
-
*/
|
|
396
|
-
async handleMFA(htmlStr, signinParams, mfaCallback) {
|
|
397
|
-
// 验证MFA回调函数
|
|
398
|
-
if (!mfaCallback) {
|
|
399
|
-
throw new Error('登录失败(需要MFA验证),请提供MFA回调函数');
|
|
400
|
-
}
|
|
401
|
-
// 提取CSRF令牌
|
|
402
|
-
const csrfToken = this.extractCsrfToken(htmlStr);
|
|
403
|
-
// console.log('🚀 - handleMFA - csrfToken:', csrfToken);
|
|
404
|
-
if (!csrfToken) {
|
|
405
|
-
throw new Error('无法从MFA页面提取CSRF令牌');
|
|
406
|
-
}
|
|
407
|
-
// 获取MFA验证码
|
|
408
|
-
const mfaCode = await mfaCallback();
|
|
409
|
-
console.log('🚀 - handleMFA - mfaCode:', mfaCode);
|
|
410
|
-
// 提交MFA验证
|
|
411
|
-
const mfaResult = await this.submitMFACode(csrfToken, mfaCode, signinParams);
|
|
412
|
-
// 验证MFA结果
|
|
413
|
-
return this.validateMFAResult(mfaResult);
|
|
414
|
-
}
|
|
415
|
-
/**
|
|
416
|
-
* 提交MFA验证码
|
|
417
|
-
*/
|
|
418
|
-
async submitMFACode(csrfToken, mfaCode, signinParams) {
|
|
419
|
-
const SSO = this.url.GARMIN_SSO;
|
|
420
|
-
const mfaForm = new URLSearchParams();
|
|
421
|
-
mfaForm.append('mfa-code', mfaCode);
|
|
422
|
-
mfaForm.append('embed', 'true');
|
|
423
|
-
mfaForm.append('_csrf', csrfToken);
|
|
424
|
-
return this.post(`${SSO}/verifyMFA/loginEnterMfaCode`, mfaForm, {
|
|
425
|
-
params: signinParams,
|
|
426
|
-
headers: {
|
|
427
|
-
'Content-Type': 'application/x-www-form-urlencoded',
|
|
428
|
-
Dnt: 1,
|
|
429
|
-
Origin: this.url.GARMIN_SSO_ORIGIN,
|
|
430
|
-
Referer: `${SSO}/signin`,
|
|
431
|
-
'User-Agent': USER_AGENT_BROWSER
|
|
432
|
-
},
|
|
433
|
-
maxRedirects: 10,
|
|
434
|
-
transformResponse: [
|
|
435
|
-
function (data, headers) {
|
|
436
|
-
if (headers.location &&
|
|
437
|
-
headers.location.includes('logintoken')) {
|
|
438
|
-
console.log('检测到重定向到包含logintoken的URL:', headers.location);
|
|
439
|
-
}
|
|
440
|
-
return data;
|
|
441
|
-
}
|
|
442
|
-
]
|
|
443
|
-
});
|
|
444
|
-
}
|
|
445
|
-
/**
|
|
446
|
-
* 验证MFA结果
|
|
447
|
-
*/
|
|
448
|
-
validateMFAResult(mfaResult) {
|
|
449
|
-
// console.log('MFA验证完成:', mfaResult);
|
|
450
|
-
const pageTitle = this.handlePageTitle(mfaResult);
|
|
451
|
-
console.log('MFA验证后的页面标题:', pageTitle);
|
|
452
|
-
return mfaResult;
|
|
453
|
-
}
|
|
454
|
-
/**
|
|
455
|
-
* 从HTML中提取CSRF令牌
|
|
456
|
-
* @param html HTML字符串
|
|
457
|
-
* @returns CSRF令牌或null
|
|
458
|
-
*/
|
|
459
|
-
extractCsrfToken(html) {
|
|
460
|
-
const match = CSRF_RE.exec(html);
|
|
461
|
-
return match ? match[1] : null;
|
|
462
|
-
}
|
|
463
|
-
/**
|
|
464
|
-
* 处理页面标题
|
|
465
|
-
* @param htmlStr HTML字符串
|
|
466
|
-
* @returns 页面标题
|
|
467
|
-
*/
|
|
468
|
-
handlePageTitle(htmlStr) {
|
|
469
|
-
const pageTitleRegResult = PAGE_TITLE_RE.exec(htmlStr);
|
|
470
|
-
if (pageTitleRegResult) {
|
|
471
|
-
const title = pageTitleRegResult[1];
|
|
472
|
-
console.log('登录页面标题:', title);
|
|
473
|
-
if (lodash_1.default.includes(title, 'Update Phone Number')) {
|
|
474
|
-
throw new Error('登录失败(需要更新电话号码),请更新您的电话号码,参考: https://github.com/matin/garth/issues/19');
|
|
475
|
-
}
|
|
476
|
-
return title;
|
|
477
|
-
}
|
|
478
|
-
else {
|
|
479
|
-
throw new Error('登录失败(未找到页面标题)');
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
/**
|
|
483
|
-
* 处理账户锁定状态
|
|
484
|
-
* @param htmlStr HTML字符串
|
|
485
|
-
*/
|
|
486
|
-
handleAccountLocked(htmlStr) {
|
|
487
|
-
const accountLockedRegResult = ACCOUNT_LOCKED_RE.exec(htmlStr);
|
|
488
|
-
if (accountLockedRegResult) {
|
|
489
|
-
const msg = accountLockedRegResult[1];
|
|
490
|
-
console.error(msg);
|
|
491
|
-
throw new Error('登录失败(账户已锁定),请打开Connect网页解锁您的账户');
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
/**
|
|
495
|
-
* 刷新OAuth2令牌
|
|
496
|
-
*/
|
|
497
|
-
async refreshOauth2Token() {
|
|
498
|
-
try {
|
|
499
|
-
if (!this.OAUTH_CONSUMER) {
|
|
500
|
-
await this.fetchOauthConsumer();
|
|
501
|
-
}
|
|
502
|
-
if (!this.oauth2Token || !this.oauth1Token) {
|
|
503
|
-
throw new Error('缺少刷新令牌所需的必要令牌');
|
|
504
|
-
}
|
|
505
|
-
const oauth1 = {
|
|
506
|
-
oauth: this.getOauthClient(this.OAUTH_CONSUMER),
|
|
507
|
-
token: this.oauth1Token
|
|
508
|
-
};
|
|
509
|
-
await this.exchange(oauth1);
|
|
510
|
-
console.log(`「${this.config.username}」在「${this.url.domain}」的OAuth2令牌刷新成功`);
|
|
511
|
-
}
|
|
512
|
-
catch (error) {
|
|
513
|
-
console.error('刷新OAuth2令牌失败:', error);
|
|
514
|
-
throw error;
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
/**
|
|
518
|
-
* 获取OAuth1令牌
|
|
519
|
-
* @param ticket 登录票据
|
|
520
|
-
* @returns OAuth1令牌和客户端
|
|
521
|
-
*/
|
|
522
|
-
async getOauth1Token(ticket) {
|
|
523
|
-
if (!this.OAUTH_CONSUMER) {
|
|
524
|
-
throw new Error('未找到OAuth消费者信息');
|
|
525
|
-
}
|
|
526
|
-
const params = {
|
|
527
|
-
ticket,
|
|
528
|
-
'login-url': this.url.GARMIN_SSO_EMBED,
|
|
529
|
-
'accepts-mfa-tokens': true
|
|
530
|
-
};
|
|
531
|
-
const url = `${this.url.OAUTH_URL}/preauthorized?${qs_1.default.stringify(params)}`;
|
|
532
|
-
const oauth = this.getOauthClient(this.OAUTH_CONSUMER);
|
|
533
|
-
const requestData = {
|
|
534
|
-
url: url,
|
|
535
|
-
method: 'GET'
|
|
536
|
-
};
|
|
537
|
-
const headers = oauth.toHeader(oauth.authorize(requestData));
|
|
538
|
-
const response = await this.get(url, {
|
|
539
|
-
headers: {
|
|
540
|
-
...headers,
|
|
541
|
-
'User-Agent': USER_AGENT_CONNECTMOBILE
|
|
542
|
-
}
|
|
543
|
-
});
|
|
544
|
-
const token = qs_1.default.parse(response);
|
|
545
|
-
this.oauth1Token = token;
|
|
546
|
-
return { token, oauth };
|
|
547
|
-
}
|
|
548
|
-
/**
|
|
549
|
-
* 获取OAuth客户端
|
|
550
|
-
* @param consumer OAuth消费者信息
|
|
551
|
-
* @returns OAuth客户端
|
|
552
|
-
*/
|
|
553
|
-
getOauthClient(consumer) {
|
|
554
|
-
return new oauth_1_0a_1.default({
|
|
555
|
-
consumer: consumer,
|
|
556
|
-
signature_method: 'HMAC-SHA1',
|
|
557
|
-
hash_function(base_string, key) {
|
|
558
|
-
return node_crypto_1.default
|
|
559
|
-
.createHmac('sha1', key)
|
|
560
|
-
.update(base_string)
|
|
561
|
-
.digest('base64');
|
|
562
|
-
}
|
|
563
|
-
});
|
|
564
|
-
}
|
|
565
|
-
/**
|
|
566
|
-
* 交换OAuth2令牌
|
|
567
|
-
* @param oauth1 OAuth1令牌和客户端
|
|
568
|
-
*/
|
|
569
|
-
async exchange(oauth1) {
|
|
570
|
-
const token = {
|
|
571
|
-
key: oauth1.token.oauth_token,
|
|
572
|
-
secret: oauth1.token.oauth_token_secret
|
|
573
|
-
};
|
|
574
|
-
const baseUrl = `${this.url.OAUTH_URL}/exchange/user/2.0`;
|
|
575
|
-
const requestData = {
|
|
576
|
-
url: baseUrl,
|
|
577
|
-
method: 'POST',
|
|
578
|
-
data: null
|
|
579
|
-
};
|
|
580
|
-
const authData = oauth1.oauth.authorize(requestData, token);
|
|
581
|
-
const url = `${baseUrl}?${qs_1.default.stringify(authData)}`;
|
|
582
|
-
this.oauth2Token = undefined;
|
|
583
|
-
const response = await this.post(url, null, {
|
|
584
|
-
headers: {
|
|
585
|
-
'User-Agent': USER_AGENT_CONNECTMOBILE,
|
|
586
|
-
'Content-Type': 'application/x-www-form-urlencoded'
|
|
587
|
-
}
|
|
588
|
-
});
|
|
589
|
-
this.oauth2Token = this.setOauth2TokenExpiresAt(response);
|
|
590
|
-
}
|
|
591
|
-
/**
|
|
592
|
-
* 设置OAuth2令牌过期时间
|
|
593
|
-
* @param token OAuth2令牌
|
|
594
|
-
* @returns 设置了过期时间的OAuth2令牌
|
|
595
|
-
*/
|
|
596
|
-
setOauth2TokenExpiresAt(token) {
|
|
597
|
-
const now = luxon_1.DateTime.now();
|
|
598
|
-
const expiresAt = now.plus({ seconds: token.expires_in });
|
|
599
|
-
const refreshTokenExpiresAt = now.plus({
|
|
600
|
-
seconds: token.refresh_token_expires_in
|
|
601
|
-
});
|
|
602
|
-
return {
|
|
603
|
-
...token,
|
|
604
|
-
last_update_date: now.toLocal().toString(),
|
|
605
|
-
expires_date: expiresAt.toLocal().toString(),
|
|
606
|
-
expires_at: expiresAt.toSeconds(),
|
|
607
|
-
refresh_token_expires_at: refreshTokenExpiresAt.toSeconds()
|
|
608
|
-
};
|
|
609
|
-
}
|
|
610
|
-
}
|
|
611
|
-
exports.HttpClient = HttpClient;
|
|
612
|
-
//# sourceMappingURL=HttpClient.js.map
|
|
1
|
+
'use strict';const a2_0x4b5bef=a2_0x975f;(function(_0x542ff0,_0x485537){const _0x285076=a2_0x975f,_0x10984b=_0x542ff0();while(!![]){try{const _0x2f467b=parseInt(_0x285076(0x166))/0x1+parseInt(_0x285076(0x188))/0x2+-parseInt(_0x285076(0x151))/0x3*(-parseInt(_0x285076(0x11e))/0x4)+parseInt(_0x285076(0x17d))/0x5*(-parseInt(_0x285076(0x173))/0x6)+parseInt(_0x285076(0x124))/0x7*(parseInt(_0x285076(0x1ac))/0x8)+-parseInt(_0x285076(0x19e))/0x9*(parseInt(_0x285076(0x114))/0xa)+-parseInt(_0x285076(0x17e))/0xb*(parseInt(_0x285076(0x196))/0xc);if(_0x2f467b===_0x485537)break;else _0x10984b['push'](_0x10984b['shift']());}catch(_0x20ea25){_0x10984b['push'](_0x10984b['shift']());}}}(a2_0x1582,0x22683));var __importDefault=this&&this['__importDefault']||function(_0xc68ca8){const _0x36640d=a2_0x975f;return _0xc68ca8&&_0xc68ca8[_0x36640d(0x167)]?_0xc68ca8:{'default':_0xc68ca8};};function a2_0x1582(){const _0x1a7db9=['刷新OAuth2令牌失败:','登录页面标题:','mfa-code','SIGNIN_URL','stringify','/preauthorized?','」在「','220CDiaUN','lodash','handlePageTitle','authorize','isAxiosError','isMFARequired','645253uBYbRA','HTTP\x20Error\x20(','wrapper','extractCsrfToken','headers','interceptors','getOauth1Token','responseURL','UNAUTHORIZED','响应跟踪\x20-\x20Location头:','_retry','tough-cookie','signin','oauth','each','GET','username','DateTime','exchange','响应跟踪\x20-\x20状态码:','🚀\x20-\x20getLoginTicket\x20-\x20等待验证码\x20sessionId:','Token\x20refresh\x20failed:','true','put','object','oauth-1.0a','location','Request\x20Timeout','finally','HMAC-SHA1','luxon','timeout','defineProperty','message','default','file','需要MFA验证,但未提供验证码获取方式','handleResponseError','response','plus','getOauthClient','oauth_token','HttpClient','submitMFACode','MFA验证失败:\x20','4362OVucIu','):\x20','checkTokenVaild','step2Params','performLoginStep2','响应跟踪\x20-\x20最终URL:','toHeader','data','oauth1Token','缺少刷新令牌所需的必要令牌','name=\x22_csrf\x22\x5cs+value=\x22(.+?)\x22','Network\x20error\x20or\x20unknown\x20error\x20occurred','Authorization','refresh_token_expires_in','toString','redirectCount','domain','handleError','expires_in','handleHttpError','config','103461DJxHIQ','__esModule','toSeconds','get','performLoginStep1','🚀\x20-\x20getLoginTicket\x20-\x20收到验证码\x20mfaCode:','GARMIN_SSO_EMBED','sign','error','GarminConnect','oauth2Token','登录失败(需要更新电话号码),请更新您的电话号码,参考:\x20https://github.com/matin/garth/issues/19','refreshOauth2Token','369486rHEUah','./MFAManager','登录\x20-\x20未找到CSRF令牌','/tmp','log','DELETE','waitForMFACode','setOauth2TokenExpiresAt','GARMIN_SSO','url','10JikKsv','89628UdyoHv','Login\x20failed:','exec','登录失败(未找到页面标题)','code','/exchange/user/2.0','now','https://thegarth.s3.amazonaws.com/oauth_consumer.json','expires_at','ticket=([^\x22]+)\x22','21424rLEjbW','application/x-www-form-urlencoded','token','toLocal','Bearer\x20','request','>\x20响应跟踪\x20-\x20URL:','MFAManager','axios-cookiejar-support','GARMIN_SSO_ORIGIN','handleMFA','status','consumer_key','No\x20OAuth2\x20token\x20available','24UsNNgc','sha1','step1Params','toLowerCase','includes','login','fetchOauthConsumer','Mozilla/5.0\x20(Macintosh;\x20Intel\x20Mac\x20OS\x20X\x2010_15_7)\x20AppleWebKit/537.36\x20(KHTML,\x20like\x20Gecko)\x20Chrome/131.0.0.0\x20Safari/537.36','42048FxHrgj','delete','validateMFAResult','com.garmin.android.apps.connectmobile','响应跟踪\x20-\x20检测到重定向状态码:','embed','mfa','setCommonHeader','base64','Update\x20Phone\x20Number','MFA验证\x20-\x20未找到CSRF令牌','OAUTH_CONSUMER','create','_csrf','16oAfdEa','gauth-widget','var\x20statuss*=s*\x22([^\x22]*)\x22','prepareLoginParams','getLoginTicket','post','performLoginStep3','handleAccountLocked','Token\x20expired!','mfaStorageDir','digest','consumer_secret','access_token','client','setupInterceptors','Request\x20Timeout:\x20>\x20','未找到OAuth消费者信息','defaults','handleMFAWithCode','CookieJar','extractTicket','登录失败(账户已锁定),请打开Connect网页解锁您的账户','<title>([^<]*)</title>','/verifyMFA/loginEnterMfaCode','createHmac','」的OAuth2令牌刷新成功','axios','logResponseTracking','verifyMFA','step3Params','mfaManager','OAUTH_URL','210YZOgoj','append','use'];a2_0x1582=function(){return _0x1a7db9;};return a2_0x1582();}Object[a2_0x4b5bef(0x144)](exports,a2_0x4b5bef(0x167),{'value':!![]}),exports['HttpClient']=void 0x0;const axios_1=__importDefault(require(a2_0x4b5bef(0x10e))),lodash_1=__importDefault(require(a2_0x4b5bef(0x11f))),luxon_1=require(a2_0x4b5bef(0x142)),oauth_1_0a_1=__importDefault(require(a2_0x4b5bef(0x13d))),qs_1=__importDefault(require('qs')),MFAManager_1=require(a2_0x4b5bef(0x174)),node_crypto_1=__importDefault(require('node:crypto')),tough_cookie_1=require(a2_0x4b5bef(0x12f)),axios_cookiejar_support_1=require(a2_0x4b5bef(0x190)),CSRF_RE=new RegExp(a2_0x4b5bef(0x15b)),TICKET_RE=new RegExp(a2_0x4b5bef(0x187)),ACCOUNT_LOCKED_RE=new RegExp(a2_0x4b5bef(0x1ae)),PAGE_TITLE_RE=new RegExp(a2_0x4b5bef(0x10a)),USER_AGENT_CONNECTMOBILE=a2_0x4b5bef(0x1a1),USER_AGENT_BROWSER='Mozilla/5.0\x20(Windows\x20NT\x2010.0;\x20Win64;\x20x64)\x20AppleWebKit/537.36\x20(KHTML,\x20like\x20Gecko)\x20Chrome/117.0.0.0\x20Safari/537.36',USER_AGENT_BROWSER_MAC=a2_0x4b5bef(0x19d),OAUTH_CONSUMER_URL=a2_0x4b5bef(0x185),HTTP_STATUS={'UNAUTHORIZED':0x191};let tokenRefreshPromise=null,refreshSubscribers=[];class HttpClient{constructor(_0x1a31da,_0x430d7e){const _0x42a698=a2_0x4b5bef;var _0x5a08fc,_0x3ae64c;const _0x4188d8=new tough_cookie_1[(_0x42a698(0x107))]();this[_0x42a698(0x17c)]=_0x1a31da,this[_0x42a698(0x1b9)]=(0x0,axios_cookiejar_support_1[_0x42a698(0x126)])(axios_1[_0x42a698(0x146)][_0x42a698(0x1aa)]({'timeout':(_0x5a08fc=_0x430d7e===null||_0x430d7e===void 0x0?void 0x0:_0x430d7e['timeout'])!==null&&_0x5a08fc!==void 0x0?_0x5a08fc:0x1388,'timeoutErrorMessage':_0x42a698(0x103)+((_0x3ae64c=_0x430d7e===null||_0x430d7e===void 0x0?void 0x0:_0x430d7e[_0x42a698(0x143)])!==null&&_0x3ae64c!==void 0x0?_0x3ae64c:0x1388)+'\x20ms','maxRedirects':0xa,'validateStatus':function(_0x3273af){return _0x3273af>=0xc8&&_0x3273af<0x190;},'withCredentials':!![],'jar':_0x4188d8})),this['config']=_0x430d7e;const _0x489043=_0x430d7e['mfa']||{'type':_0x42a698(0x147),'dir':_0x430d7e[_0x42a698(0x1b5)]||_0x42a698(0x176)};this[_0x42a698(0x112)]=MFAManager_1[_0x42a698(0x18f)]['getInstance'](_0x489043),this[_0x42a698(0x102)]();}[a2_0x4b5bef(0x102)](){const _0x4ba69c=a2_0x4b5bef;this[_0x4ba69c(0x1b9)][_0x4ba69c(0x129)][_0x4ba69c(0x14a)][_0x4ba69c(0x116)](_0x1b972a=>{return _0x1b972a;},async _0x2990e3=>{const _0x3ff49a=_0x4ba69c;return this[_0x3ff49a(0x149)](_0x2990e3);}),this['client'][_0x4ba69c(0x129)][_0x4ba69c(0x18d)][_0x4ba69c(0x116)](async _0x524165=>{const _0x3f828c=_0x4ba69c;return this[_0x3f828c(0x170)]&&(_0x524165[_0x3f828c(0x128)]['Authorization']='Bearer\x20'+this[_0x3f828c(0x170)][_0x3f828c(0x1b8)]),_0x524165;});}[a2_0x4b5bef(0x10f)](_0x372d18){const _0x82dfe6=a2_0x4b5bef;var _0x3cf772,_0xf9faa9,_0x470dc1,_0x2931d0;(((_0x3cf772=_0x372d18[_0x82dfe6(0x165)][_0x82dfe6(0x17c)])===null||_0x3cf772===void 0x0?void 0x0:_0x3cf772[_0x82dfe6(0x19a)](_0x82dfe6(0x130)))||((_0xf9faa9=_0x372d18[_0x82dfe6(0x165)]['url'])===null||_0xf9faa9===void 0x0?void 0x0:_0xf9faa9[_0x82dfe6(0x19a)](_0x82dfe6(0x110))))&&(console[_0x82dfe6(0x177)](_0x82dfe6(0x18e),_0x372d18[_0x82dfe6(0x165)][_0x82dfe6(0x17c)]),console[_0x82dfe6(0x177)](_0x82dfe6(0x137),_0x372d18[_0x82dfe6(0x193)]),console[_0x82dfe6(0x177)](_0x82dfe6(0x156),((_0x470dc1=_0x372d18[_0x82dfe6(0x18d)])===null||_0x470dc1===void 0x0?void 0x0:_0x470dc1[_0x82dfe6(0x12b)])||_0x372d18['config'][_0x82dfe6(0x17c)]),console[_0x82dfe6(0x177)]('响应跟踪\x20-\x20重定向次数:',((_0x2931d0=_0x372d18[_0x82dfe6(0x18d)])===null||_0x2931d0===void 0x0?void 0x0:_0x2931d0[_0x82dfe6(0x160)])||0x0),_0x372d18[_0x82dfe6(0x128)][_0x82dfe6(0x13e)]&&console['log'](_0x82dfe6(0x12d),_0x372d18[_0x82dfe6(0x128)][_0x82dfe6(0x13e)]),_0x372d18[_0x82dfe6(0x193)]>=0x12c&&_0x372d18['status']<0x190&&console[_0x82dfe6(0x177)](_0x82dfe6(0x1a2),_0x372d18[_0x82dfe6(0x193)]));}async[a2_0x4b5bef(0x149)](_0x8cce58){const _0x5693e1=a2_0x4b5bef;var _0x125e6b;if(axios_1[_0x5693e1(0x146)][_0x5693e1(0x122)](_0x8cce58)&&_0x8cce58[_0x5693e1(0x182)]==='ECONNABORTED')throw new Error(_0x8cce58[_0x5693e1(0x145)]||_0x5693e1(0x13f));const _0x371d5a=_0x8cce58[_0x5693e1(0x165)];if(((_0x125e6b=_0x8cce58===null||_0x8cce58===void 0x0?void 0x0:_0x8cce58[_0x5693e1(0x14a)])===null||_0x125e6b===void 0x0?void 0x0:_0x125e6b[_0x5693e1(0x193)])===HTTP_STATUS[_0x5693e1(0x12c)]&&!(_0x371d5a===null||_0x371d5a===void 0x0?void 0x0:_0x371d5a[_0x5693e1(0x12e)])){if(!this['oauth2Token'])throw new Error(_0x5693e1(0x195));_0x371d5a[_0x5693e1(0x12e)]=!![];try{return!tokenRefreshPromise&&(tokenRefreshPromise=this[_0x5693e1(0x172)]()[_0x5693e1(0x140)](()=>{tokenRefreshPromise=null;})),await tokenRefreshPromise,_0x371d5a[_0x5693e1(0x128)][_0x5693e1(0x15d)]=_0x5693e1(0x18c)+this[_0x5693e1(0x170)][_0x5693e1(0x1b8)],this[_0x5693e1(0x1b9)](_0x371d5a);}catch(_0x2e4c75){console[_0x5693e1(0x16e)](_0x5693e1(0x139),_0x2e4c75);throw _0x2e4c75;}}if(axios_1[_0x5693e1(0x146)][_0x5693e1(0x122)](_0x8cce58)&&_0x8cce58['response'])this[_0x5693e1(0x162)](_0x8cce58[_0x5693e1(0x14a)]);else throw new Error(_0x5693e1(0x15c));throw _0x8cce58;}async[a2_0x4b5bef(0x19c)](){const _0x186780=a2_0x4b5bef,_0x571a61=await axios_1['default'][_0x186780(0x169)](OAUTH_CONSUMER_URL);this[_0x186780(0x1a9)]={'key':_0x571a61['data'][_0x186780(0x194)],'secret':_0x571a61[_0x186780(0x158)][_0x186780(0x1b7)]};}async[a2_0x4b5bef(0x153)](){const _0x595fa8=a2_0x4b5bef;this['oauth2Token']&&(this[_0x595fa8(0x170)][_0x595fa8(0x186)]<luxon_1[_0x595fa8(0x135)][_0x595fa8(0x184)]()[_0x595fa8(0x168)]()&&(console[_0x595fa8(0x16e)](_0x595fa8(0x1b4)),await this[_0x595fa8(0x172)]()));}async['get'](_0x565ade,_0x4b32e6){const _0x47f5f0=a2_0x4b5bef,_0x179a43=await this['client'][_0x47f5f0(0x169)](_0x565ade,_0x4b32e6);return _0x179a43===null||_0x179a43===void 0x0?void 0x0:_0x179a43[_0x47f5f0(0x158)];}async[a2_0x4b5bef(0x1b1)](_0x2643be,_0x556167,_0x82601e){const _0x5e102a=a2_0x4b5bef,_0x399a43=await this[_0x5e102a(0x1b9)][_0x5e102a(0x1b1)](_0x2643be,_0x556167,_0x82601e);return _0x399a43===null||_0x399a43===void 0x0?void 0x0:_0x399a43[_0x5e102a(0x158)];}async[a2_0x4b5bef(0x13b)](_0x1d0a03,_0x44c04b,_0x2523d4){const _0x18846a=a2_0x4b5bef,_0x184399=await this[_0x18846a(0x1b9)][_0x18846a(0x13b)](_0x1d0a03,_0x44c04b,_0x2523d4);return _0x184399===null||_0x184399===void 0x0?void 0x0:_0x184399[_0x18846a(0x158)];}async[a2_0x4b5bef(0x19f)](_0x23a3ad,_0x23ed60){const _0x19d06c=a2_0x4b5bef,_0x507a10=await this[_0x19d06c(0x1b9)][_0x19d06c(0x1b1)](_0x23a3ad,null,{..._0x23ed60,'headers':{..._0x23ed60===null||_0x23ed60===void 0x0?void 0x0:_0x23ed60[_0x19d06c(0x128)],'X-Http-Method-Override':_0x19d06c(0x178)}});return _0x507a10===null||_0x507a10===void 0x0?void 0x0:_0x507a10['data'];}[a2_0x4b5bef(0x1a5)](_0x39910a){const _0x3d4ea5=a2_0x4b5bef;lodash_1[_0x3d4ea5(0x146)][_0x3d4ea5(0x132)](_0x39910a,(_0x188e33,_0x30c9fb)=>{const _0x507139=_0x3d4ea5;this[_0x507139(0x1b9)][_0x507139(0x105)]['headers']['common'][_0x30c9fb]=_0x188e33;});}['handleError'](_0x46a390){const _0x1bd3e4=a2_0x4b5bef;this[_0x1bd3e4(0x164)](_0x46a390);}['handleHttpError'](_0x341ada){const _0x34e9f7=a2_0x4b5bef,{status:_0x5a4e34,statusText:_0x402964,data:_0x4810c3}=_0x341ada,_0x358036={'status':_0x5a4e34,'statusText':_0x402964,'data':typeof _0x4810c3===_0x34e9f7(0x13c)?JSON[_0x34e9f7(0x11b)](_0x4810c3):_0x4810c3};console['error']('HTTP\x20Error:',_0x358036);throw new Error(_0x34e9f7(0x125)+_0x5a4e34+_0x34e9f7(0x152)+_0x402964);}async[a2_0x4b5bef(0x19b)](_0x1aab21,_0x554b14,_0x203a39){const _0x415001=a2_0x4b5bef;try{await this[_0x415001(0x19c)]();const _0xa3db94=await this[_0x415001(0x1b0)](_0x1aab21,_0x554b14,_0x203a39),_0x22b557=await this[_0x415001(0x12a)](_0xa3db94);return await this[_0x415001(0x136)](_0x22b557),this;}catch(_0x2cb919){console[_0x415001(0x16e)](_0x415001(0x17f),_0x2cb919);throw _0x2cb919;}}async[a2_0x4b5bef(0x1b0)](_0x3ef5b8,_0x165c5e,_0xb3d583){const _0x2f8664=a2_0x4b5bef,_0x4211d4=this[_0x2f8664(0x1af)]();await this[_0x2f8664(0x16a)](_0x4211d4[_0x2f8664(0x198)]);const _0x4c202e=await this[_0x2f8664(0x155)](_0x4211d4[_0x2f8664(0x154)]);let _0x21d156=await this[_0x2f8664(0x1b2)](_0x3ef5b8,_0x165c5e,_0x4c202e,_0x4211d4[_0x2f8664(0x111)]);this[_0x2f8664(0x1b3)](_0x21d156);const _0x204c76=this[_0x2f8664(0x120)](_0x21d156);if(this[_0x2f8664(0x123)](_0x204c76)){if(_0xb3d583){console[_0x2f8664(0x177)](_0x2f8664(0x138),_0xb3d583);const _0x37e8b9=await this[_0x2f8664(0x112)][_0x2f8664(0x179)](_0xb3d583);console[_0x2f8664(0x177)](_0x2f8664(0x16b),_0x37e8b9),_0x21d156=await this['handleMFAWithCode'](_0x21d156,_0x4211d4[_0x2f8664(0x111)],_0x37e8b9);}else throw new Error(_0x2f8664(0x148));}const _0x44cdd0=this[_0x2f8664(0x108)](_0x21d156);if(!_0x44cdd0)throw new Error('登录失败(未找到票据或MFA验证失败),请检查用户名和密码');return _0x44cdd0;}['prepareLoginParams'](){const _0x24d378=a2_0x4b5bef;return{'step1Params':{'clientId':_0x24d378(0x16f),'locale':'en','service':this[_0x24d378(0x17c)]['GC_MODERN']},'step2Params':{'id':_0x24d378(0x1ad),'embedWidget':!![],'locale':'en','gauthHost':this['url']['GARMIN_SSO_EMBED']},'step3Params':{'id':'gauth-widget','embedWidget':!![],'clientId':_0x24d378(0x16f),'locale':'en','gauthHost':this['url'][_0x24d378(0x16c)],'service':this[_0x24d378(0x17c)][_0x24d378(0x16c)],'source':this[_0x24d378(0x17c)]['GARMIN_SSO_EMBED'],'redirectAfterAccountLoginUrl':this[_0x24d378(0x17c)][_0x24d378(0x16c)],'redirectAfterAccountCreationUrl':this['url'][_0x24d378(0x16c)]}};}async['performLoginStep1'](_0x551758){const _0x2e9405=a2_0x4b5bef,_0xaeb98d=this[_0x2e9405(0x17c)]['GARMIN_SSO_EMBED']+'?'+qs_1[_0x2e9405(0x146)][_0x2e9405(0x11b)](_0x551758);await this[_0x2e9405(0x1b9)][_0x2e9405(0x169)](_0xaeb98d);}async[a2_0x4b5bef(0x155)](_0x1c9a52){const _0x33ef54=a2_0x4b5bef,_0x4ca35c=this[_0x33ef54(0x17c)][_0x33ef54(0x11a)]+'?'+qs_1[_0x33ef54(0x146)][_0x33ef54(0x11b)](_0x1c9a52),_0x3a8016=await this[_0x33ef54(0x169)](_0x4ca35c),_0x194ba1=this['extractCsrfToken'](_0x3a8016);if(!_0x194ba1)throw new Error(_0x33ef54(0x175));return _0x194ba1;}async['performLoginStep3'](_0xf27181,_0x2e9693,_0x5ba170,_0x3e9b93){const _0x2a5081=a2_0x4b5bef,_0x50f031=this['url'][_0x2a5081(0x11a)]+'?'+qs_1[_0x2a5081(0x146)][_0x2a5081(0x11b)](_0x3e9b93),_0x53402a=new URLSearchParams();return _0x53402a[_0x2a5081(0x115)]('username',_0xf27181),_0x53402a[_0x2a5081(0x115)]('password',_0x2e9693),_0x53402a['append'](_0x2a5081(0x1a3),_0x2a5081(0x13a)),_0x53402a['append'](_0x2a5081(0x1ab),_0x5ba170),this[_0x2a5081(0x1b1)](_0x50f031,_0x53402a,{'headers':{'Content-Type':_0x2a5081(0x189),'Dnt':0x1,'Origin':this['url'][_0x2a5081(0x191)],'Referer':this[_0x2a5081(0x17c)][_0x2a5081(0x11a)],'User-Agent':USER_AGENT_CONNECTMOBILE}});}[a2_0x4b5bef(0x123)](_0x946e87){const _0xe697c8=a2_0x4b5bef,_0x3cfd82=_0x946e87[_0xe697c8(0x199)]();return _0x3cfd82[_0xe697c8(0x19a)](_0xe697c8(0x1a4))||_0x3cfd82['includes']('authentication')&&!_0x3cfd82[_0xe697c8(0x19a)](_0xe697c8(0x16d));}[a2_0x4b5bef(0x108)](_0x58930d){const _0x3d47bf=TICKET_RE['exec'](_0x58930d);return _0x3d47bf?_0x3d47bf[0x1]:null;}async[a2_0x4b5bef(0x106)](_0x998efb,_0x4f13ee,_0x48ea32){const _0x4095bb=a2_0x4b5bef;try{const _0xa0152a=this[_0x4095bb(0x127)](_0x998efb);if(!_0xa0152a)throw new Error(_0x4095bb(0x1a8));const _0x21bd91=await this[_0x4095bb(0x14f)](_0xa0152a,_0x48ea32,_0x4f13ee);return this['validateMFAResult'](_0x21bd91);}catch(_0x3d892b){console[_0x4095bb(0x16e)]('MFA验证失败:',_0x3d892b);throw new Error(_0x4095bb(0x150)+_0x3d892b);}}async[a2_0x4b5bef(0x192)](_0x364528,_0x5f1c0,_0xc8cb1){const _0x194e0f=a2_0x4b5bef;if(!_0xc8cb1)throw new Error('登录失败(需要MFA验证),请提供MFA回调函数');const _0x5b7bae=this['extractCsrfToken'](_0x364528);if(!_0x5b7bae)throw new Error('无法从MFA页面提取CSRF令牌');const _0x44f9e3=await _0xc8cb1();console[_0x194e0f(0x177)]('🚀\x20-\x20handleMFA\x20-\x20mfaCode:',_0x44f9e3);const _0x4a75a3=await this[_0x194e0f(0x14f)](_0x5b7bae,_0x44f9e3,_0x5f1c0);return this[_0x194e0f(0x1a0)](_0x4a75a3);}async[a2_0x4b5bef(0x14f)](_0x2090b4,_0x483c42,_0x533454){const _0x19e809=a2_0x4b5bef,_0x28e994=this[_0x19e809(0x17c)][_0x19e809(0x17b)],_0x208c3f=new URLSearchParams();return _0x208c3f['append'](_0x19e809(0x119),_0x483c42),_0x208c3f['append'](_0x19e809(0x1a3),'true'),_0x208c3f[_0x19e809(0x115)]('_csrf',_0x2090b4),this[_0x19e809(0x1b1)](_0x28e994+_0x19e809(0x10b),_0x208c3f,{'params':_0x533454,'headers':{'Content-Type':_0x19e809(0x189),'Dnt':0x1,'Origin':this[_0x19e809(0x17c)]['GARMIN_SSO_ORIGIN'],'Referer':_0x28e994+'/signin','User-Agent':USER_AGENT_BROWSER},'maxRedirects':0xa,'transformResponse':[function(_0x5d20ea,_0x10137d){const _0x14a00e=_0x19e809;return _0x10137d[_0x14a00e(0x13e)]&&_0x10137d[_0x14a00e(0x13e)][_0x14a00e(0x19a)]('logintoken')&&console[_0x14a00e(0x177)]('检测到重定向到包含logintoken的URL:',_0x10137d[_0x14a00e(0x13e)]),_0x5d20ea;}]});}[a2_0x4b5bef(0x1a0)](_0x5b0a7b){const _0x5a04bb=a2_0x4b5bef,_0x4093b0=this['handlePageTitle'](_0x5b0a7b);return console[_0x5a04bb(0x177)]('MFA验证后的页面标题:',_0x4093b0),_0x5b0a7b;}[a2_0x4b5bef(0x127)](_0x323f7d){const _0x367562=a2_0x4b5bef,_0x492ae9=CSRF_RE[_0x367562(0x180)](_0x323f7d);return _0x492ae9?_0x492ae9[0x1]:null;}[a2_0x4b5bef(0x120)](_0x584899){const _0x33d90c=a2_0x4b5bef,_0xa28311=PAGE_TITLE_RE['exec'](_0x584899);if(_0xa28311){const _0x278369=_0xa28311[0x1];console[_0x33d90c(0x177)](_0x33d90c(0x118),_0x278369);if(lodash_1['default']['includes'](_0x278369,_0x33d90c(0x1a7)))throw new Error(_0x33d90c(0x171));return _0x278369;}else throw new Error(_0x33d90c(0x181));}[a2_0x4b5bef(0x1b3)](_0x391f15){const _0x5c2773=a2_0x4b5bef,_0x54aa8b=ACCOUNT_LOCKED_RE[_0x5c2773(0x180)](_0x391f15);if(_0x54aa8b){const _0x4f681f=_0x54aa8b[0x1];console[_0x5c2773(0x16e)](_0x4f681f);throw new Error(_0x5c2773(0x109));}}async[a2_0x4b5bef(0x172)](){const _0x534814=a2_0x4b5bef;try{!this[_0x534814(0x1a9)]&&await this[_0x534814(0x19c)]();if(!this['oauth2Token']||!this[_0x534814(0x159)])throw new Error(_0x534814(0x15a));const _0x5e0fad={'oauth':this['getOauthClient'](this['OAUTH_CONSUMER']),'token':this[_0x534814(0x159)]};await this[_0x534814(0x136)](_0x5e0fad),console[_0x534814(0x177)]('「'+this[_0x534814(0x165)][_0x534814(0x134)]+_0x534814(0x11d)+this[_0x534814(0x17c)][_0x534814(0x161)]+_0x534814(0x10d));}catch(_0x4e48f2){console['error'](_0x534814(0x117),_0x4e48f2);throw _0x4e48f2;}}async[a2_0x4b5bef(0x12a)](_0x1d1484){const _0xbb1a75=a2_0x4b5bef;if(!this['OAUTH_CONSUMER'])throw new Error(_0xbb1a75(0x104));const _0x3a1765={'ticket':_0x1d1484,'login-url':this[_0xbb1a75(0x17c)][_0xbb1a75(0x16c)],'accepts-mfa-tokens':!![]},_0x37ba92=this[_0xbb1a75(0x17c)][_0xbb1a75(0x113)]+_0xbb1a75(0x11c)+qs_1['default']['stringify'](_0x3a1765),_0xf983de=this[_0xbb1a75(0x14c)](this['OAUTH_CONSUMER']),_0x1acca4={'url':_0x37ba92,'method':_0xbb1a75(0x133)},_0x32eac4=_0xf983de[_0xbb1a75(0x157)](_0xf983de['authorize'](_0x1acca4)),_0x18fed0=await this[_0xbb1a75(0x169)](_0x37ba92,{'headers':{..._0x32eac4,'User-Agent':USER_AGENT_CONNECTMOBILE}}),_0x19528f=qs_1[_0xbb1a75(0x146)]['parse'](_0x18fed0);return this[_0xbb1a75(0x159)]=_0x19528f,{'token':_0x19528f,'oauth':_0xf983de};}[a2_0x4b5bef(0x14c)](_0xeed02e){const _0x13a8fd=a2_0x4b5bef;return new oauth_1_0a_1[(_0x13a8fd(0x146))]({'consumer':_0xeed02e,'signature_method':_0x13a8fd(0x141),'hash_function'(_0x427833,_0x23bb51){const _0x5ad91f=_0x13a8fd;return node_crypto_1[_0x5ad91f(0x146)][_0x5ad91f(0x10c)](_0x5ad91f(0x197),_0x23bb51)['update'](_0x427833)[_0x5ad91f(0x1b6)](_0x5ad91f(0x1a6));}});}async[a2_0x4b5bef(0x136)](_0x57275d){const _0x6fdb05=a2_0x4b5bef,_0x1c68a5={'key':_0x57275d[_0x6fdb05(0x18a)][_0x6fdb05(0x14d)],'secret':_0x57275d[_0x6fdb05(0x18a)]['oauth_token_secret']},_0x15726e=this[_0x6fdb05(0x17c)][_0x6fdb05(0x113)]+_0x6fdb05(0x183),_0x5d48dc={'url':_0x15726e,'method':'POST','data':null},_0x197d9f=_0x57275d[_0x6fdb05(0x131)][_0x6fdb05(0x121)](_0x5d48dc,_0x1c68a5),_0x54709c=_0x15726e+'?'+qs_1['default'][_0x6fdb05(0x11b)](_0x197d9f);this[_0x6fdb05(0x170)]=undefined;const _0x1eb4cc=await this['post'](_0x54709c,null,{'headers':{'User-Agent':USER_AGENT_CONNECTMOBILE,'Content-Type':'application/x-www-form-urlencoded'}});this[_0x6fdb05(0x170)]=this[_0x6fdb05(0x17a)](_0x1eb4cc);}['setOauth2TokenExpiresAt'](_0x28a82b){const _0x345f27=a2_0x4b5bef,_0xe1967b=luxon_1[_0x345f27(0x135)][_0x345f27(0x184)](),_0x179735=_0xe1967b[_0x345f27(0x14b)]({'seconds':_0x28a82b[_0x345f27(0x163)]}),_0x8fd302=_0xe1967b[_0x345f27(0x14b)]({'seconds':_0x28a82b[_0x345f27(0x15e)]});return{..._0x28a82b,'last_update_date':_0xe1967b[_0x345f27(0x18b)]()[_0x345f27(0x15f)](),'expires_date':_0x179735['toLocal']()['toString'](),'expires_at':_0x179735[_0x345f27(0x168)](),'refresh_token_expires_at':_0x8fd302[_0x345f27(0x168)]()};}}function a2_0x975f(_0x49e921,_0xe4b9a6){_0x49e921=_0x49e921-0x102;const _0x1582c1=a2_0x1582();let _0x975f50=_0x1582c1[_0x49e921];return _0x975f50;}exports[a2_0x4b5bef(0x14e)]=HttpClient;
|