iobroker.google-sharedlocations2 0.3.3 → 0.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/admin/google-sharedlocations2.png +0 -0
- package/io-package.json +15 -15
- package/package.json +8 -10
- package/src/lib/Cookie.ts +48 -24
package/README.md
CHANGED
|
@@ -27,6 +27,9 @@ Copyright and trademark of Google are property of Google.
|
|
|
27
27
|
Placeholder for the next version (at the beginning of the line):
|
|
28
28
|
### **WORK IN PROGRESS**
|
|
29
29
|
-->
|
|
30
|
+
### 0.3.4 (2026-04-22)
|
|
31
|
+
* (Garfonso) replaced axios dependency. Tried to make login more robust.
|
|
32
|
+
|
|
30
33
|
### 0.3.3 (2026-02-17)
|
|
31
34
|
* (Garfonso) if deleting cookies, also delete cookies in Browser to force login with username & password.
|
|
32
35
|
|
|
Binary file
|
package/io-package.json
CHANGED
|
@@ -1,8 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"common": {
|
|
3
3
|
"name": "google-sharedlocations2",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.4",
|
|
5
5
|
"news": {
|
|
6
|
+
"0.3.4": {
|
|
7
|
+
"en": "replaced axios dependency. Tried to make login more robust.",
|
|
8
|
+
"de": "ersetzt axios Abhängigkeit. Versuchen Sie, die Anmeldung robuster zu machen.",
|
|
9
|
+
"ru": "замена аксиозависимости. Пытался сделать логин более надежным.",
|
|
10
|
+
"pt": "substituiu a dependência axios. Tentou tornar o login mais robusto.",
|
|
11
|
+
"nl": "axios afhankelijkheid vervangen. Probeerde om login robuuster te maken.",
|
|
12
|
+
"fr": "a remplacé la dépendance aux axios. J'ai essayé de rendre la connexion plus robuste.",
|
|
13
|
+
"it": "ha sostituito la dipendenza dagli assi. Ho cercato di rendere il login più robusto.",
|
|
14
|
+
"es": "sustituyó la dependencia de axios. Traté de hacer que el login sea más robusto.",
|
|
15
|
+
"pl": "zastąpił zależność aksjońską. Próbował uczynić logowanie bardziej solidnym.",
|
|
16
|
+
"uk": "замінена аксіос залежність. Виконується, щоб зробити логін більш надійним.",
|
|
17
|
+
"zh-cn": "取代对轴的依赖。 试图使登录更坚固."
|
|
18
|
+
},
|
|
6
19
|
"0.3.3": {
|
|
7
20
|
"en": "if deleting cookies, also delete cookies in Browser to force login with username & password.",
|
|
8
21
|
"de": "wenn Sie Cookies löschen, löschen Sie auch Cookies im Browser, um die Anmeldung mit Benutzername & Passwort zu zwingen.",
|
|
@@ -80,19 +93,6 @@
|
|
|
80
93
|
"pl": "poprawa odzyskiwania z błędów logowania",
|
|
81
94
|
"uk": "поліпшення відновлення від помилок логіна",
|
|
82
95
|
"zh-cn": "改进登录错误的恢复"
|
|
83
|
-
},
|
|
84
|
-
"0.1.0": {
|
|
85
|
-
"en": "added: support for places\nadded: support for fences\ntry to prevent login as much as possible.",
|
|
86
|
-
"de": "hinzugefügt: unterstützung für places\nhinzugefügt: unterstützung für fences\nversuche, die anmeldung so weit wie möglich zu verhindern.",
|
|
87
|
-
"ru": "добавлено: поддержка мест\nдобавлено: поддержка заборов\nстарайтесь максимально предотвратить вход.",
|
|
88
|
-
"pt": "adicionado: suporte para locais\nadicionado: suporte para cercas\ntentar evitar o login o máximo possível.",
|
|
89
|
-
"nl": "toegevoegd: ondersteuning voor plaatsen\ntoegevoegd: steun voor hekken\nproberen zo veel mogelijk inloggen te voorkomen.",
|
|
90
|
-
"fr": "ajouté: soutien aux places\najouté: soutien aux clôtures\nessayer d'éviter la connexion autant que possible.",
|
|
91
|
-
"it": "aggiunto: supporto per i luoghi\naggiunto: supporto per recinzioni\ncercare di impedire il login il più possibile.",
|
|
92
|
-
"es": "añadido: apoyo a los lugares\nañadido: soporte para vallas\ntratar de prevenir la entrada tanto como sea posible.",
|
|
93
|
-
"pl": "dodane: wsparcie dla miejsc\ndodane: wsparcie dla ogrodzeń\nspróbuj zapobiec logowaniu jak najwięcej.",
|
|
94
|
-
"uk": "додано: підтримка місць\nдоданий: підтримка парканів\nнамагатися попередити логін якомога простіше.",
|
|
95
|
-
"zh-cn": "添加:对位置的支持\n添加:支持围栏\n尽量防止登录."
|
|
96
96
|
}
|
|
97
97
|
},
|
|
98
98
|
"titleLang": {
|
|
@@ -158,7 +158,7 @@
|
|
|
158
158
|
],
|
|
159
159
|
"globalDependencies": [
|
|
160
160
|
{
|
|
161
|
-
"admin": ">=7.
|
|
161
|
+
"admin": ">=7.6.17"
|
|
162
162
|
}
|
|
163
163
|
],
|
|
164
164
|
"osDependencies": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iobroker.google-sharedlocations2",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.4",
|
|
4
4
|
"description": "Share your location with iobroker via google maps.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Garfonso",
|
|
@@ -26,21 +26,19 @@
|
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"@iobroker/adapter-core": "^3.3.2",
|
|
29
|
-
"
|
|
30
|
-
"puppeteer": "^24.36.1"
|
|
29
|
+
"puppeteer": "^24.42.0"
|
|
31
30
|
},
|
|
32
31
|
"devDependencies": {
|
|
33
|
-
"@alcalzone/release-script": "^5.
|
|
34
|
-
"@alcalzone/release-script-plugin-iobroker": "^
|
|
35
|
-
"@alcalzone/release-script-plugin-license": "^
|
|
36
|
-
"@alcalzone/release-script-plugin-manual-review": "^
|
|
32
|
+
"@alcalzone/release-script": "^5.1.1",
|
|
33
|
+
"@alcalzone/release-script-plugin-iobroker": "^5.1.2",
|
|
34
|
+
"@alcalzone/release-script-plugin-license": "^5.1.1",
|
|
35
|
+
"@alcalzone/release-script-plugin-manual-review": "^5.1.1",
|
|
37
36
|
"@iobroker/adapter-dev": "^1.5.0",
|
|
38
|
-
"@iobroker/dev-server": "^0.8.0",
|
|
39
37
|
"@iobroker/eslint-config": "^2.2.0",
|
|
40
38
|
"@iobroker/testing": "^5.2.2",
|
|
41
|
-
"@iobroker/types": "^7.1.
|
|
39
|
+
"@iobroker/types": "^7.1.1",
|
|
42
40
|
"@tsconfig/node22": "^22.0.5",
|
|
43
|
-
"@types/node": "^22.19.
|
|
41
|
+
"@types/node": "^22.19.17 < 23",
|
|
44
42
|
"source-map-support": "^0.5.21",
|
|
45
43
|
"ts-node": "^10.9.2",
|
|
46
44
|
"typescript": "~5.9.3"
|
package/src/lib/Cookie.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import axios from 'axios';
|
|
2
1
|
import type { GoogleSharedlocations2 } from '../main';
|
|
3
2
|
import puppeteer from 'puppeteer';
|
|
4
|
-
import type { Browser, Page, CookieData } from 'puppeteer';
|
|
3
|
+
import type { Browser, Page, CookieData, CookiePriority, CookieSameSite } from 'puppeteer';
|
|
5
4
|
import { mkdir } from 'fs/promises';
|
|
5
|
+
import type { RequestCredentials } from 'undici-types/fetch';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Helper class to manage Google cookies.
|
|
@@ -114,18 +114,24 @@ export class Cookie {
|
|
|
114
114
|
/**
|
|
115
115
|
* Augment the current cookie with data from the 'set-cookie' header.
|
|
116
116
|
*
|
|
117
|
-
* @param
|
|
117
|
+
* @param headersObj - HTTP headers of response
|
|
118
118
|
*/
|
|
119
|
-
async augmentCookieFromHeader(
|
|
120
|
-
|
|
119
|
+
async augmentCookieFromHeader(headersObj: Headers): Promise<void> {
|
|
120
|
+
const headers = headersObj.getSetCookie();
|
|
121
|
+
if (headers.length > 0) {
|
|
121
122
|
this.log?.debug('New header received.');
|
|
122
123
|
const oldLength = this.cookies.length;
|
|
123
124
|
|
|
124
125
|
//split old cookie and new cookie. Update single values.
|
|
125
|
-
for (const header of headers
|
|
126
|
+
for (const header of headers) {
|
|
126
127
|
//console.log('Processing header cookie:', header);
|
|
127
128
|
const keyValues = header.split('; ');
|
|
128
|
-
const
|
|
129
|
+
const firstCookie = keyValues.shift();
|
|
130
|
+
if (firstCookie === undefined) {
|
|
131
|
+
this.log.debug(`Invalid cookie header: ${header}`);
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
const [name, value] = firstCookie.split('='); // first part is cookie, rest are attributes like path, secure etc.
|
|
129
135
|
const cookie = {
|
|
130
136
|
name: name.trim(),
|
|
131
137
|
value: value.trim(),
|
|
@@ -147,13 +153,13 @@ export class Cookie {
|
|
|
147
153
|
cookie.httpOnly = true;
|
|
148
154
|
break;
|
|
149
155
|
case 'samesite':
|
|
150
|
-
cookie.sameSite = v ? v.trim() : 'Lax';
|
|
156
|
+
cookie.sameSite = v ? (v.trim() as CookieSameSite) : 'Lax';
|
|
151
157
|
break;
|
|
152
158
|
case 'expires':
|
|
153
159
|
cookie.expires = new Date(v).getTime() / 1000; //puppeteer expects expires in seconds, not milliseconds
|
|
154
160
|
break;
|
|
155
161
|
case 'priority':
|
|
156
|
-
cookie.priority = v ? v.trim() : 'Medium';
|
|
162
|
+
cookie.priority = v ? (v.trim() as CookiePriority) : 'Medium';
|
|
157
163
|
break;
|
|
158
164
|
default:
|
|
159
165
|
this.log.debug(`Unknown cookie attribute: ${k}=${v}`);
|
|
@@ -189,8 +195,9 @@ export class Cookie {
|
|
|
189
195
|
*/
|
|
190
196
|
async improveCookie(): Promise<boolean> {
|
|
191
197
|
//see https://github.com/costastf/locationsharinglib/blob/master/locationsharinglib/locationsharinglib.py#L105
|
|
198
|
+
const url = 'https://myaccount.google.com/?hl=en';
|
|
192
199
|
const options = {
|
|
193
|
-
|
|
200
|
+
credentials: 'same-origin' as RequestCredentials, //or do we need 'include' here?
|
|
194
201
|
headers: {
|
|
195
202
|
Cookie: this.cookies.map(c => `${c.name}=${c.value}`).join('; '),
|
|
196
203
|
},
|
|
@@ -198,7 +205,7 @@ export class Cookie {
|
|
|
198
205
|
};
|
|
199
206
|
|
|
200
207
|
try {
|
|
201
|
-
const response = await
|
|
208
|
+
const response = await fetch(url, options);
|
|
202
209
|
|
|
203
210
|
if (response.status !== 200) {
|
|
204
211
|
this.log?.error(`Failed improving cookie: ${response.status}`);
|
|
@@ -257,30 +264,34 @@ export class Cookie {
|
|
|
257
264
|
|
|
258
265
|
//send request with current cookies
|
|
259
266
|
this.log.debug('Sending request with current cookies');
|
|
267
|
+
const url =
|
|
268
|
+
'https://www.google.com/maps/rpc/locationsharing/read?authuser=2&hl=en&gl=us&pb=!1m7!8m6!1m3!1i14!2i8413!3i5385!2i6!3x4095!2m3!1e0!2sm!3i407105169!3m7!2sen!5e1105!12m4!1e68!2m2!1sset!2sRoadmap!4e1!5m4!1e4!8m2!1e0!1e1!6m9!1e12!2i2!26m1!4b1!30m1!1f1.3953487873077393!39b1!44e1!50e0!23i4111425';
|
|
260
269
|
const options = {
|
|
261
270
|
method: 'GET',
|
|
262
|
-
|
|
271
|
+
credentials: 'same-origin' as RequestCredentials, //or do we need 'include' here?
|
|
263
272
|
headers: {
|
|
264
273
|
Cookie: this.cookies.map(c => `${c.name}=${c.value}`).join('; '),
|
|
265
274
|
},
|
|
266
|
-
params: {
|
|
275
|
+
/*params: {
|
|
267
276
|
authuser: 2,
|
|
268
277
|
hl: 'en',
|
|
269
278
|
gl: 'us',
|
|
270
279
|
//pb is place on map. Is irrelevant, set to google head quarters here.
|
|
271
280
|
pb: '!1m7!8m6!1m3!1i14!2i8413!3i5385!2i6!3x4095!2m3!1e0!2sm!3i407105169!3m7!2sen!5e1105!12m4!1e68!2m2!1sset!2sRoadmap!4e1!5m4!1e4!8m2!1e0!1e1!6m9!1e12!2i2!26m1!4b1!30m1!1f1.3953487873077393!39b1!44e1!50e0!23i4111425',
|
|
272
|
-
}
|
|
281
|
+
},*/
|
|
273
282
|
};
|
|
274
|
-
|
|
275
283
|
try {
|
|
276
|
-
const response = await
|
|
284
|
+
const response = await fetch(url, options);
|
|
277
285
|
this.log.debug(`Request successful, response code: ${response.status}`);
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
286
|
+
if (response.ok) {
|
|
287
|
+
const dataBuffer = await response.text();
|
|
288
|
+
const data = dataBuffer.split('\n').slice(1).join('\n');
|
|
289
|
+
const locationData = JSON.parse(data);
|
|
290
|
+
const locations = locationData[0];
|
|
291
|
+
if (locations && locations.length > 0) {
|
|
292
|
+
await this.augmentCookieFromHeader(response.headers);
|
|
293
|
+
return locations;
|
|
294
|
+
}
|
|
284
295
|
}
|
|
285
296
|
this.log.info('No shared locations found in the response, probably not logged in.');
|
|
286
297
|
} catch (e) {
|
|
@@ -451,8 +462,21 @@ export class Cookie {
|
|
|
451
462
|
return false;
|
|
452
463
|
}
|
|
453
464
|
|
|
454
|
-
|
|
455
|
-
|
|
465
|
+
try {
|
|
466
|
+
logDebug('Trying to click on username, if user was logged in before.');
|
|
467
|
+
const userElement = await page.$(`[data-email="${this.username}"]`);
|
|
468
|
+
if (userElement) {
|
|
469
|
+
await userElement.click();
|
|
470
|
+
} else {
|
|
471
|
+
logDebug('No user element found, filling in username.');
|
|
472
|
+
await page.locator('#identifierId').fill(this.username);
|
|
473
|
+
}
|
|
474
|
+
} catch (e: any) {
|
|
475
|
+
logDebug(`Ok, no user it seems (${e}). Let's fill in useranme`);
|
|
476
|
+
logDebug('filling in username.');
|
|
477
|
+
await page.locator('#identifierId').fill(this.username);
|
|
478
|
+
}
|
|
479
|
+
|
|
456
480
|
//is this enough, or do we need to search button in this div?
|
|
457
481
|
logDebug('clicking user next button.');
|
|
458
482
|
await page.locator('#identifierNext').click();
|