@danhachuel/thunderbolt 0.6.48 → 0.6.50
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/app/social_networks_ui.py +2 -2
- package/integrations/instagram_public.py +134 -115
- package/package.json +1 -1
|
@@ -216,8 +216,8 @@ def _render_instagram_posts(profile: Mapping[str, Any]) -> None:
|
|
|
216
216
|
if load_clicked or refresh_clicked:
|
|
217
217
|
target = max(10, len(posts)) if refresh_clicked else 10
|
|
218
218
|
posts_ok, posts_message, loaded_posts = _load_instagram_posts(profile, limit=target)
|
|
219
|
-
#
|
|
220
|
-
|
|
219
|
+
# DEBUG WINDOWS INSTAGRAM: manter até a validação final do carregamento público.
|
|
220
|
+
st.write({"instagram_posts_ok": posts_ok, "instagram_posts_message": posts_message, "instagram_posts_count": len(loaded_posts)})
|
|
221
221
|
if posts_ok:
|
|
222
222
|
posts = loaded_posts
|
|
223
223
|
_persist_instagram_posts(profile, posts)
|
|
@@ -4,6 +4,7 @@ import re
|
|
|
4
4
|
import json
|
|
5
5
|
import logging
|
|
6
6
|
import os
|
|
7
|
+
import platform
|
|
7
8
|
import sys
|
|
8
9
|
import shutil
|
|
9
10
|
import subprocess
|
|
@@ -131,18 +132,8 @@ class IntegrationResult:
|
|
|
131
132
|
|
|
132
133
|
|
|
133
134
|
def normalize_instagram_bio(value: Any) -> str:
|
|
134
|
-
"""Keep the
|
|
135
|
+
"""Keep the complete public biography exactly as returned by Instagram."""
|
|
135
136
|
bio = str(value or '').strip()
|
|
136
|
-
if not bio:
|
|
137
|
-
return ''
|
|
138
|
-
metric_words = r'followers|following|seguidores|seguindo|posts|publicações|publications'
|
|
139
|
-
metric_hits = re.findall(
|
|
140
|
-
rf'(?:\d[\d.,]*\s*(?:{metric_words})\b|\b(?:{metric_words})\s*\d[\d.,]*)',
|
|
141
|
-
bio,
|
|
142
|
-
flags=re.IGNORECASE,
|
|
143
|
-
)
|
|
144
|
-
if len(metric_hits) >= 2:
|
|
145
|
-
return ''
|
|
146
137
|
return bio
|
|
147
138
|
|
|
148
139
|
|
|
@@ -213,48 +204,92 @@ def _structured_metric_from_json(document: str, *keys: str) -> int | None:
|
|
|
213
204
|
return None
|
|
214
205
|
|
|
215
206
|
|
|
207
|
+
def _profile_quality(user: Mapping[str, Any]) -> int:
|
|
208
|
+
groups = (
|
|
209
|
+
('biography', 'bio', 'description'),
|
|
210
|
+
('edge_follow', 'following', 'following_count', 'followingCount', 'follows'),
|
|
211
|
+
('edge_followed_by', 'followers', 'follower_count', 'followers_count', 'followerCount'),
|
|
212
|
+
('edge_owner_to_timeline_media', 'posts', 'post_count', 'media_count'),
|
|
213
|
+
('username', 'user_name', 'handle'),
|
|
214
|
+
)
|
|
215
|
+
return sum(any(user.get(key) not in (None, '', {}, []) for key in group) for group in groups)
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def _merge_profile(base: dict[str, Any] | None, candidate: Mapping[str, Any]) -> dict[str, Any]:
|
|
219
|
+
merged = dict(base or {})
|
|
220
|
+
for key, value in candidate.items():
|
|
221
|
+
if value not in (None, '', [], {}):
|
|
222
|
+
if isinstance(value, Mapping) and isinstance(merged.get(key), Mapping):
|
|
223
|
+
merged[key] = {**merged[key], **value}
|
|
224
|
+
else:
|
|
225
|
+
merged[key] = value
|
|
226
|
+
return merged
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def _extract_profile_user(payload: Any) -> Mapping[str, Any] | None:
|
|
230
|
+
if not isinstance(payload, Mapping):
|
|
231
|
+
return None
|
|
232
|
+
candidates = [
|
|
233
|
+
payload.get('user'),
|
|
234
|
+
(payload.get('data') or {}).get('user') if isinstance(payload.get('data'), Mapping) else None,
|
|
235
|
+
(payload.get('graphql') or {}).get('user') if isinstance(payload.get('graphql'), Mapping) else None,
|
|
236
|
+
(payload.get('data') or {}).get('profile') if isinstance(payload.get('data'), Mapping) else None,
|
|
237
|
+
]
|
|
238
|
+
return next((candidate for candidate in candidates if isinstance(candidate, Mapping)), None)
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def _fetch_profile_with_playwright(username: str, headers: Mapping[str, str], cookies: Mapping[str, str]) -> dict[str, Any] | None:
|
|
242
|
+
# CORREÇÃO WINDOWS: este helper é o único caminho de rede usado pelo perfil no Windows.
|
|
243
|
+
endpoints = (
|
|
244
|
+
f'https://www.instagram.com/api/v1/users/web_profile_info/?username={username}',
|
|
245
|
+
f'https://i.instagram.com/api/v1/users/web_profile_info/?username={username}',
|
|
246
|
+
)
|
|
247
|
+
best_user: dict[str, Any] | None = None
|
|
248
|
+
try:
|
|
249
|
+
from playwright.sync_api import sync_playwright
|
|
250
|
+
with sync_playwright() as playwright:
|
|
251
|
+
browser = playwright.chromium.launch(headless=True)
|
|
252
|
+
try:
|
|
253
|
+
page = browser.new_page(
|
|
254
|
+
user_agent=headers['User-Agent'],
|
|
255
|
+
extra_http_headers={'x-ig-app-id': headers['x-ig-app-id'], 'Accept-Language': 'en-US,en;q=0.9'},
|
|
256
|
+
)
|
|
257
|
+
if cookies:
|
|
258
|
+
page.context.add_cookies([{'name': name, 'value': value, 'domain': '.instagram.com', 'path': '/'} for name, value in cookies.items()])
|
|
259
|
+
for endpoint in endpoints:
|
|
260
|
+
try:
|
|
261
|
+
response = page.goto(endpoint, wait_until='domcontentloaded', timeout=30000)
|
|
262
|
+
LOGGER.debug('Instagram Playwright profile URL=%s status=%s', endpoint, response.status if response else None)
|
|
263
|
+
if response is None or response.status >= 400:
|
|
264
|
+
continue
|
|
265
|
+
payload = json.loads(page.locator('body').inner_text(timeout=5000))
|
|
266
|
+
user = _extract_profile_user(payload)
|
|
267
|
+
if isinstance(user, dict):
|
|
268
|
+
best_user = _merge_profile(best_user, user)
|
|
269
|
+
if _profile_quality(best_user) >= 4:
|
|
270
|
+
return best_user
|
|
271
|
+
except (ValueError, TimeoutError) as exc:
|
|
272
|
+
LOGGER.warning('Falha ao interpretar resposta Playwright do perfil URL=%s: %s', endpoint, exc)
|
|
273
|
+
finally:
|
|
274
|
+
browser.close()
|
|
275
|
+
except Exception as exc:
|
|
276
|
+
LOGGER.warning('Falha no Playwright do perfil Instagram: %s', exc, exc_info=True)
|
|
277
|
+
_streamlit_message('error', f'Playwright não conseguiu consultar o perfil Instagram: {exc}', 'profile_playwright_error')
|
|
278
|
+
return best_user
|
|
279
|
+
|
|
280
|
+
|
|
216
281
|
def _fetch_web_profile_user(username: str) -> dict[str, Any] | None:
|
|
217
282
|
_ensure_windows_playwright()
|
|
218
283
|
cookies = _instagram_cookies()
|
|
219
284
|
headers = _instagram_headers()
|
|
220
285
|
headers['User-Agent'] = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/131.0 Safari/537.36'
|
|
221
|
-
# The www host is the current public web endpoint. The legacy i host is
|
|
222
|
-
# retained as a fallback because Instagram rate-limits the two hosts
|
|
223
|
-
# independently and their availability can vary by region.
|
|
224
286
|
endpoints = (
|
|
225
287
|
f'https://www.instagram.com/api/v1/users/web_profile_info/?username={username}',
|
|
226
288
|
f'https://i.instagram.com/api/v1/users/web_profile_info/?username={username}',
|
|
227
289
|
)
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
('edge_follow', 'following', 'following_count', 'followingCount', 'follows'),
|
|
232
|
-
('edge_followed_by', 'followers', 'follower_count', 'followers_count', 'followerCount'),
|
|
233
|
-
('edge_owner_to_timeline_media', 'posts', 'post_count', 'media_count'),
|
|
234
|
-
('username', 'user_name', 'handle'),
|
|
235
|
-
)
|
|
236
|
-
return sum(any(user.get(key) not in (None, '', {}, []) for key in group) for group in groups)
|
|
237
|
-
|
|
238
|
-
def merge_profile(base: dict[str, Any] | None, candidate: Mapping[str, Any]) -> dict[str, Any]:
|
|
239
|
-
merged = dict(base or {})
|
|
240
|
-
for key, value in candidate.items():
|
|
241
|
-
if value not in (None, '', [], {}):
|
|
242
|
-
if isinstance(value, Mapping) and isinstance(merged.get(key), Mapping):
|
|
243
|
-
merged[key] = {**merged[key], **value}
|
|
244
|
-
else:
|
|
245
|
-
merged[key] = value
|
|
246
|
-
return merged
|
|
247
|
-
|
|
248
|
-
def extract_user(payload: Any) -> Mapping[str, Any] | None:
|
|
249
|
-
if not isinstance(payload, Mapping):
|
|
250
|
-
return None
|
|
251
|
-
candidates = [
|
|
252
|
-
payload.get('user'),
|
|
253
|
-
(payload.get('data') or {}).get('user') if isinstance(payload.get('data'), Mapping) else None,
|
|
254
|
-
(payload.get('graphql') or {}).get('user') if isinstance(payload.get('graphql'), Mapping) else None,
|
|
255
|
-
(payload.get('data') or {}).get('profile') if isinstance(payload.get('data'), Mapping) else None,
|
|
256
|
-
]
|
|
257
|
-
return next((candidate for candidate in candidates if isinstance(candidate, Mapping)), None)
|
|
290
|
+
if platform.system() == 'Windows':
|
|
291
|
+
LOGGER.debug('Windows detectado: perfil Instagram exclusivamente via Playwright.')
|
|
292
|
+
return _fetch_profile_with_playwright(username, headers, cookies)
|
|
258
293
|
|
|
259
294
|
request_headers = [
|
|
260
295
|
headers,
|
|
@@ -276,10 +311,10 @@ def _fetch_web_profile_user(username: str) -> dict[str, Any] | None:
|
|
|
276
311
|
if best_user:
|
|
277
312
|
return best_user
|
|
278
313
|
continue
|
|
279
|
-
user =
|
|
314
|
+
user = _extract_profile_user(payload)
|
|
280
315
|
if isinstance(user, dict):
|
|
281
|
-
best_user =
|
|
282
|
-
if
|
|
316
|
+
best_user = _merge_profile(best_user, user)
|
|
317
|
+
if _profile_quality(best_user) >= 4:
|
|
283
318
|
return best_user
|
|
284
319
|
|
|
285
320
|
curl = shutil.which('curl')
|
|
@@ -298,10 +333,10 @@ def _fetch_web_profile_user(username: str) -> dict[str, Any] | None:
|
|
|
298
333
|
)
|
|
299
334
|
payload = json.loads(completed.stdout) if completed.returncode == 0 and completed.stdout else None
|
|
300
335
|
LOGGER.debug('Instagram curl URL=%s returncode=%s stdout_bytes=%s stderr=%s', endpoint, completed.returncode, len(completed.stdout or ''), (completed.stderr or '').strip()[-500:])
|
|
301
|
-
user =
|
|
336
|
+
user = _extract_profile_user(payload)
|
|
302
337
|
if isinstance(user, dict):
|
|
303
|
-
best_user =
|
|
304
|
-
if
|
|
338
|
+
best_user = _merge_profile(best_user, user)
|
|
339
|
+
if _profile_quality(best_user) >= 4:
|
|
305
340
|
return best_user
|
|
306
341
|
except (OSError, subprocess.SubprocessError, ValueError):
|
|
307
342
|
LOGGER.warning('Falha no fallback curl do perfil Instagram URL=%s', endpoint, exc_info=True)
|
|
@@ -310,39 +345,7 @@ def _fetch_web_profile_user(username: str) -> dict[str, Any] | None:
|
|
|
310
345
|
LOGGER.warning('curl não está disponível no PATH; a tentar Playwright.')
|
|
311
346
|
_streamlit_message('warning', 'curl não foi encontrado no PATH do Windows; será tentado o fallback Playwright.', 'curl_missing')
|
|
312
347
|
|
|
313
|
-
|
|
314
|
-
# clientes HTTP, embora entregue o JSON completo a um navegador Chromium.
|
|
315
|
-
# O fallback é opcional: a aplicação continua funcional sem Playwright.
|
|
316
|
-
try:
|
|
317
|
-
from playwright.sync_api import sync_playwright
|
|
318
|
-
with sync_playwright() as playwright:
|
|
319
|
-
browser = playwright.chromium.launch(headless=True)
|
|
320
|
-
try:
|
|
321
|
-
page = browser.new_page(
|
|
322
|
-
user_agent=headers['User-Agent'],
|
|
323
|
-
extra_http_headers={'x-ig-app-id': headers['x-ig-app-id'], 'Accept-Language': 'en-US,en;q=0.9'},
|
|
324
|
-
)
|
|
325
|
-
if cookies:
|
|
326
|
-
page.context.add_cookies([{'name': name, 'value': value, 'domain': '.instagram.com', 'path': '/'} for name, value in cookies.items()])
|
|
327
|
-
for endpoint in endpoints:
|
|
328
|
-
try:
|
|
329
|
-
response = page.goto(endpoint, wait_until='domcontentloaded', timeout=30000)
|
|
330
|
-
if response is None or response.status >= 400:
|
|
331
|
-
continue
|
|
332
|
-
payload = json.loads(page.locator('body').inner_text(timeout=5000))
|
|
333
|
-
user = extract_user(payload)
|
|
334
|
-
if isinstance(user, dict):
|
|
335
|
-
best_user = merge_profile(best_user, user)
|
|
336
|
-
if profile_quality(best_user) >= 4:
|
|
337
|
-
return best_user
|
|
338
|
-
except (ValueError, TimeoutError):
|
|
339
|
-
continue
|
|
340
|
-
finally:
|
|
341
|
-
browser.close()
|
|
342
|
-
except Exception as exc:
|
|
343
|
-
LOGGER.warning('Falha no fallback Playwright do perfil Instagram: %s', exc, exc_info=True)
|
|
344
|
-
_streamlit_message('error', f'Fallback Playwright do perfil Instagram falhou: {exc}. Execute: python -m playwright install chromium', 'profile_playwright_error')
|
|
345
|
-
return best_user
|
|
348
|
+
return _fetch_profile_with_playwright(username, headers, cookies) or best_user
|
|
346
349
|
|
|
347
350
|
|
|
348
351
|
def _country_from_bloks(value: Any) -> str:
|
|
@@ -417,7 +420,9 @@ def _country_from_bio_fallback(bio: str) -> str:
|
|
|
417
420
|
|
|
418
421
|
def _profile_data_from_api(user: Mapping[str, Any], reference: Mapping[str, str]) -> dict[str, Any]:
|
|
419
422
|
followers = _profile_metric(user, 'edge_followed_by', 'followers', 'follower_count', 'followerCount')
|
|
420
|
-
|
|
423
|
+
following_node = user.get('edge_follow')
|
|
424
|
+
following = normalize_instagram_metric(following_node) if isinstance(following_node, Mapping) else None
|
|
425
|
+
following = following if following is not None else _profile_metric(user, 'follows', 'following', 'following_count', 'followingCount')
|
|
421
426
|
followers = followers if followers is not None else _structured_metric_from_json(json.dumps(user), 'edge_followed_by', 'followers', 'follower_count', 'followerCount')
|
|
422
427
|
following = following if following is not None else _structured_metric_from_json(json.dumps(user), 'edge_follow', 'follows', 'following', 'following_count', 'followingCount')
|
|
423
428
|
media = ((user.get('edge_owner_to_timeline_media') or {}).get('count') if isinstance(user.get('edge_owner_to_timeline_media'), dict) else None)
|
|
@@ -425,7 +430,7 @@ def _profile_data_from_api(user: Mapping[str, Any], reference: Mapping[str, str]
|
|
|
425
430
|
biography = str(user.get('biography') or user.get('bio') or user.get('description') or '').strip()
|
|
426
431
|
country = extract_public_instagram_country(user) or _fetch_instagram_about_country(user.get('id') or user.get('pk'))
|
|
427
432
|
country = country or _country_from_bio_fallback(biography)
|
|
428
|
-
LOGGER.debug('Instagram profile extracted bio=%s followers=%s following=%s posts=%s country=%s', bool(biography), followers, following, media, country or '—')
|
|
433
|
+
LOGGER.debug('Instagram profile extracted keys=%s edge_follow=%r bio=%s followers=%s following=%s posts=%s country=%s', sorted(str(key) for key in user.keys()), following_node, bool(biography), followers, following, media, country or '—')
|
|
429
434
|
return {
|
|
430
435
|
'id': f"instagram_{reference['username']}",
|
|
431
436
|
**reference,
|
|
@@ -604,6 +609,45 @@ def _post_from_node(node: dict[str, Any]) -> dict[str, Any] | None:
|
|
|
604
609
|
}
|
|
605
610
|
|
|
606
611
|
|
|
612
|
+
def _fetch_posts_with_playwright(reference: Mapping[str, str], limit: int, cookies: Mapping[str, str]) -> list[dict[str, Any]]:
|
|
613
|
+
# CORREÇÃO WINDOWS: posts são extraídos exclusivamente do JSON/HTML obtido pelo Playwright.
|
|
614
|
+
posts: list[dict[str, Any]] = []
|
|
615
|
+
seen: set[str] = set()
|
|
616
|
+
try:
|
|
617
|
+
from playwright.sync_api import sync_playwright
|
|
618
|
+
with sync_playwright() as playwright:
|
|
619
|
+
browser = playwright.chromium.launch(headless=True)
|
|
620
|
+
try:
|
|
621
|
+
page = browser.new_page(
|
|
622
|
+
user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/131.0 Safari/537.36',
|
|
623
|
+
extra_http_headers={'x-ig-app-id': '936619743392459', 'Accept-Language': 'en-US,en;q=0.9'},
|
|
624
|
+
)
|
|
625
|
+
if cookies:
|
|
626
|
+
page.context.add_cookies([{'name': name, 'value': value, 'domain': '.instagram.com', 'path': '/'} for name, value in cookies.items()])
|
|
627
|
+
response = page.goto(reference['url'], wait_until='domcontentloaded', timeout=30000)
|
|
628
|
+
LOGGER.debug('Instagram Playwright posts URL=%s status=%s', reference['url'], response.status if response else None)
|
|
629
|
+
if response is None or response.status >= 400:
|
|
630
|
+
return []
|
|
631
|
+
browser_document = page.content()
|
|
632
|
+
LOGGER.debug('Instagram Playwright posts document_bytes=%s', len(browser_document))
|
|
633
|
+
for document in _embedded_json_documents(browser_document):
|
|
634
|
+
for node in _walk_json(document):
|
|
635
|
+
post = _post_from_node(node)
|
|
636
|
+
if not post or post['id'] in seen:
|
|
637
|
+
continue
|
|
638
|
+
seen.add(post['id'])
|
|
639
|
+
posts.append(post)
|
|
640
|
+
LOGGER.debug('Instagram Playwright post extracted id=%s', post['id'])
|
|
641
|
+
if len(posts) >= max(1, int(limit)):
|
|
642
|
+
return posts
|
|
643
|
+
finally:
|
|
644
|
+
browser.close()
|
|
645
|
+
except Exception as exc:
|
|
646
|
+
LOGGER.warning('Falha no Playwright dos posts Instagram: %s', exc, exc_info=True)
|
|
647
|
+
_streamlit_message('error', f'Playwright não conseguiu consultar os posts Instagram: {exc}', 'posts_playwright_error')
|
|
648
|
+
return posts
|
|
649
|
+
|
|
650
|
+
|
|
607
651
|
def fetch_public_instagram_posts(source: str, limit: int = 10) -> IntegrationResult:
|
|
608
652
|
LOGGER.debug('fetch_public_instagram_posts source=%s limit=%s', source, limit)
|
|
609
653
|
_ensure_windows_playwright()
|
|
@@ -611,6 +655,10 @@ def fetch_public_instagram_posts(source: str, limit: int = 10) -> IntegrationRes
|
|
|
611
655
|
reference = normalize_instagram_reference(source)
|
|
612
656
|
except ValueError as exc:
|
|
613
657
|
return IntegrationResult(False, str(exc), {})
|
|
658
|
+
if platform.system() == 'Windows':
|
|
659
|
+
LOGGER.debug('Windows detectado: posts Instagram exclusivamente via Playwright.')
|
|
660
|
+
posts = _fetch_posts_with_playwright(reference, limit, _instagram_cookies())
|
|
661
|
+
return IntegrationResult(bool(posts), 'Posts públicos encontrados.' if posts else 'Não foi possível encontrar posts públicos nesta página do Instagram.', reference | {'posts': posts[:max(1, int(limit))]})
|
|
614
662
|
api_user = _fetch_web_profile_user(reference['username'])
|
|
615
663
|
if api_user:
|
|
616
664
|
api_posts: list[dict[str, Any]] = []
|
|
@@ -647,36 +695,7 @@ def fetch_public_instagram_posts(source: str, limit: int = 10) -> IntegrationRes
|
|
|
647
695
|
if len(posts) >= max(1, int(limit)):
|
|
648
696
|
break
|
|
649
697
|
if not posts:
|
|
650
|
-
|
|
651
|
-
from playwright.sync_api import sync_playwright
|
|
652
|
-
with sync_playwright() as playwright:
|
|
653
|
-
browser = playwright.chromium.launch(headless=True)
|
|
654
|
-
try:
|
|
655
|
-
page = browser.new_page(
|
|
656
|
-
user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/131.0 Safari/537.36',
|
|
657
|
-
extra_http_headers={'x-ig-app-id': '936619743392459', 'Accept-Language': 'en-US,en;q=0.9'},
|
|
658
|
-
)
|
|
659
|
-
cookies = _instagram_cookies()
|
|
660
|
-
if cookies:
|
|
661
|
-
page.context.add_cookies([{'name': name, 'value': value, 'domain': '.instagram.com', 'path': '/'} for name, value in cookies.items()])
|
|
662
|
-
page.goto(reference['url'], wait_until='domcontentloaded', timeout=30000)
|
|
663
|
-
browser_document = page.content()
|
|
664
|
-
for document in _embedded_json_documents(browser_document):
|
|
665
|
-
for node in _walk_json(document):
|
|
666
|
-
post = _post_from_node(node)
|
|
667
|
-
if not post or post['id'] in seen:
|
|
668
|
-
continue
|
|
669
|
-
seen.add(post['id'])
|
|
670
|
-
posts.append(post)
|
|
671
|
-
if len(posts) >= max(1, int(limit)):
|
|
672
|
-
break
|
|
673
|
-
if len(posts) >= max(1, int(limit)):
|
|
674
|
-
break
|
|
675
|
-
finally:
|
|
676
|
-
browser.close()
|
|
677
|
-
except Exception as exc:
|
|
678
|
-
LOGGER.warning('Falha no fallback Playwright dos posts Instagram: %s', exc, exc_info=True)
|
|
679
|
-
_streamlit_message('error', f'Fallback Playwright dos posts Instagram falhou: {exc}. Execute: python -m playwright install chromium', 'posts_playwright_error')
|
|
698
|
+
posts = _fetch_posts_with_playwright(reference, limit, _instagram_cookies())
|
|
680
699
|
LOGGER.debug('Instagram posts extracted=%s limit=%s', len(posts), limit)
|
|
681
700
|
return IntegrationResult(bool(posts), 'Posts públicos encontrados.' if posts else 'Não foi possível encontrar posts públicos nesta página do Instagram.', reference | {'posts': posts[:max(1, int(limit))]})
|
|
682
701
|
|
package/package.json
CHANGED