UNPKG

garmin-api-handler

Version:
243 lines (220 loc) 6.64 kB
import CookieApi from 'cookie-api-handler'; import FormData from 'form-data'; import { DefaultResponseProcessor } from 'rest-api-handler'; import { GarminConnect } from 'garmin-connect'; import GarminApiException from './exceptions/GarminApiException.js'; import responseDecoder from './helpers/responseDecoder.js'; import Activity from './models/Activity.js'; class GarminApi extends CookieApi { constructor(session) { super('https://connect.garmin.com/modern/proxy', [new DefaultResponseProcessor(GarminApiException, responseDecoder)], { 'nk': 'NT', 'Content-Type': 'application/json', 'dnt': '1', 'origin': 'https://connect.garmin.com' }); this.session = session; this.addCookies({ _gid: 'X' }); } setSession(session) { this.session = session; this.addCookies({ SESSIONID: session || '' }); return this; } getSession() { return this.session; } async login(email, password) { const GCClient = new GarminConnect(); await GCClient.login(email, password); const { cookies } = GCClient.client.cookies._jar.toJSON(); const session = cookies.find(item => item.key === 'SESSIONID' && item.domain === 'connect.garmin.com').value; const __cflb = cookies.find(item => item.key === '__cflb' && item.domain === 'connect.garmin.com').value; this.setSession(session); this.addCookies({ __cflb }); return session; // does not pass clouflare protection /* // get ticket from login form const { data } = await this.post( 'https://sso.garmin.com/sso/login?service=https%3A%2F%2Fconnect.garmin.com%2Fmodern%2F', { username: email, password, embed: false, }, CookieApi.FORMATS.URL_ENCODED, { 'Content-Type': 'application/x-www-form-urlencoded', 'Referer': 'https://sso.garmin.com/sso/signin', }, ); const ticket = /ticket=(([A-Za-z]|-|\d)*)/g.exec(data); // if ticket is no present, there is some error in html if (!ticket) { const errorMessage = /<div id="status" class="error">([A-Za-z]| |\.)*<\/div>/g.exec(data); throw new Error(errorMessage ? errorMessage[1] : 'Error in Login. Cannot find ticket.'); } // use ticket try { await this.request(`?${ticket[0]}`, 'GET', { redirect: 'manual', }); // eslint-disable-next-line no-empty } catch {} // this will load cookies await this.get('https://connect.garmin.com/modern/'); const cookies = this.getCookies(); if (!cookies) { throw new Error('Login failed. Cookies were not loaded.'); } this.setSession(cookies.SESSIONID); return cookies.SESSIONID; */ } async getActivity(id) { const { data } = await this.get(`activity-service/activity/${id}`); return Activity.fromApi(data); } async getPoints(id) { const { data } = await this.get(`activity-service/activity/${id}/details`); return data; } async getActivityGpx(id) { const { data } = await this.get(`download-service/export/gpx/activity/${id}`); return data; } async getActivityFile(id) { const { data } = await this.get(`download-service/files/activity/${id}`); return data; } async getActivities(filter) { const { startDate, endDate } = filter; const { data } = await this.get('activitylist-service/activities/search/activities', { ...filter, ...(startDate ? { starDate: startDate.toSQLDate() } : {}), ...(endDate ? { endDate: endDate.toSQLDate() } : {}) }); return data.map(activity => { return Activity.fromListApi(activity); }); } logWeight(date, kg) { return this.post('weight-service/user-weight', { gmtTimestamp: date.setZone('UTC').toISO({ includeOffset: false }), dateTimestamp: date.toISO({ includeOffset: false }), unitKey: 'kg', value: kg }); } async upload(fileContent, format) { const form = new FormData(); form.append('file', fileContent, { filename: `activity.${format}`, contentType: 'application/octet-stream' }); const headers = this.getDefaultHeaders(); this.setDefaultHeaders({ cookie: headers.cookie, nk: headers.nk }); const response = await this.request(`upload-service/upload/.${format}`, 'POST', { body: form }); this.setDefaultHeaders(headers); return response.data.detailedImportResult.successes[0].internalId; } async uploadGpx(gpx) { return this.upload(gpx, 'gpx'); } async createActivity(activity) { return this.activityUpdater(activity); } async updateActivity(activity) { return this.activityUpdater(activity); } async activityUpdater(activity) { const distance = activity.getDistance(); const averageHR = activity.getAvgHeartRate(); const maxHR = activity.getMaxHeartRate(); const { data } = await this.post(`activity-service/activity/${activity.getId() || ''}`, { activityName: activity.getTitle() || activity.getTypeId(), activityTypeDTO: { typeKey: activity.getTypeId() }, summaryDTO: { duration: activity.getDuration().as('seconds'), startTimeLocal: `${activity.getStart().toISO({ includeOffset: false, suppressMilliseconds: true })}.0`, ...(averageHR != null ? { averageHR } : {}), ...(maxHR != null ? { maxHR } : {}), ...(distance != null ? { distance: distance.toNumber('m') } : {}) }, eventTypeDTO: { typeKey: activity.getCategory() }, timeZoneUnitDTO: { unitKey: 'Europe/Prague' }, description: activity.getNotes(), ...(activity.getId() ? { activityId: activity.getId() } : {}) }, CookieApi.FORMATS.JSON, { ...(activity.getId() ? { 'x-http-method-override': 'PUT' } : {}) }); return activity.getId() ? activity : Activity.fromApi(data); } async addGear(activityId, gear) { const { data } = await this.post(`gear-service/gear/link/${gear}/activity/${activityId}`, {}, CookieApi.FORMATS.JSON, { 'x-http-method-override': 'PUT' }); return data; } async getGears(activityId) { const { data } = await this.get(`gear-service/gear/filterGear?activityId=${activityId}`); return data; } } export { GarminApi as default };