@danhachuel/thunderbolt 0.6.30 → 0.6.32

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.
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import io
4
+ import re
4
5
  import uuid
5
6
  import zipfile
6
7
  from typing import Any, Callable, Mapping
@@ -52,6 +53,20 @@ def _profile_bio(profile: Mapping[str, Any]) -> str:
52
53
  return normalize_instagram_bio(profile.get("bio")) or normalize_instagram_bio(profile.get("bio_raw"))
53
54
 
54
55
 
56
+ def _infer_country_from_bio(value: Any) -> str:
57
+ bio = _clean(value).casefold()
58
+ if not bio:
59
+ return ""
60
+ for country in sorted(COUNTRY_OPTIONS, key=len, reverse=True):
61
+ if re.search(rf"(?<!\w){re.escape(country.casefold())}(?!\w)", bio):
62
+ return country
63
+ return ""
64
+
65
+
66
+ def _profile_country(profile: Mapping[str, Any]) -> str:
67
+ return _clean(profile.get("country")) or _infer_country_from_bio(_profile_bio(profile))
68
+
69
+
55
70
  def _language_index(value: Any) -> int:
56
71
  code = language_code(value)
57
72
  return list(LANGUAGE_CODES).index(code) if code in LANGUAGE_CODES else list(LANGUAGE_CODES).index("pt")
@@ -295,7 +310,7 @@ def _characters(settings: Mapping[str, Any]) -> tuple[list[dict[str, Any]], Any
295
310
  def _save_public_profile(data: Mapping[str, Any], *, country: str, language: str, character_id: str = "") -> dict[str, Any]:
296
311
  name = _clean(data.get("name")) or _clean(data.get("username")) or "Conta Instagram"
297
312
  url = _clean(data.get("url"))
298
- selected_country = _clean(country) if _clean(country) in COUNTRY_OPTIONS else ""
313
+ selected_country = _clean(country) if _clean(country) in COUNTRY_OPTIONS else _infer_country_from_bio(_profile_bio(data))
299
314
  metadata = {
300
315
  **dict(data),
301
316
  "platform": "instagram",
@@ -322,6 +337,8 @@ def _merge_instagram_refresh(existing: Mapping[str, Any], refreshed: Mapping[str
322
337
  for field in ("subscriber_count", "following_count", "post_count", "bio", "bio_raw", "avatar_url"):
323
338
  if merged.get(field) in (None, "") and existing.get(field) not in (None, ""):
324
339
  merged[field] = existing[field]
340
+ if not _clean(merged.get("country")):
341
+ merged["country"] = _infer_country_from_bio(_profile_bio(merged))
325
342
  return merged
326
343
 
327
344
 
@@ -354,7 +371,7 @@ def _render_instagram_card(profile: dict[str, Any], characters: list[dict[str, A
354
371
  st.write(f"**{_clean(profile.get('name')) or 'Sem nome'}**")
355
372
  st.caption(f"{_clean(profile.get('handle')) or _clean(profile.get('url')) or 'sem handler'}")
356
373
  _render_instagram_bio(_profile_bio(profile))
357
- st.caption(f"{_clean(profile.get('country')) or 'País não definido'} · {_clean(profile.get('language')) or 'Idioma não definido'}")
374
+ st.caption(f"{_profile_country(profile) or 'País não definido'} · {_clean(profile.get('language')) or 'Idioma não definido'}")
358
375
  with header_cols[2]:
359
376
  st.metric("posts", _metric(profile.get("post_count")))
360
377
  with header_cols[3]:
@@ -372,7 +389,7 @@ def _render_instagram_card(profile: dict[str, Any], characters: list[dict[str, A
372
389
  posts_ok, _, refreshed_posts = _load_instagram_posts(refreshed, limit=max(10, len(current_posts)))
373
390
  if posts_ok:
374
391
  st.session_state[posts_key] = refreshed_posts
375
- update_channel(profile_id, {**refreshed, "platform": "instagram", "country": profile.get("country", ""), "language": profile.get("language", ""), "character_id": profile.get("character_id", "")})
392
+ update_channel(profile_id, {**refreshed, "platform": "instagram", "country": refreshed.get("country") or profile.get("country", ""), "language": profile.get("language", ""), "character_id": profile.get("character_id", "")})
376
393
  st.success("Métricas Instagram actualizadas.")
377
394
  st.rerun()
378
395
  st.warning(refreshed_message)
@@ -408,7 +425,7 @@ def _render_instagram_card(profile: dict[str, Any], characters: list[dict[str, A
408
425
  name = st.text_input("Nome", value=_clean(profile.get("name")), key=f"instagram_edit_name_{profile_id}")
409
426
  handle = st.text_input("handler", value=_clean(profile.get("handle")), key=f"instagram_edit_handle_{profile_id}")
410
427
  bio = st.text_area("Bio", value=_profile_bio(profile), key=f"instagram_edit_bio_{profile_id}")
411
- country = st.selectbox("País", _country_options(), index=_country_index(profile.get("country")), format_func=_country_label, key=f"instagram_edit_country_{profile_id}")
428
+ country = st.selectbox("País", _country_options(), index=_country_index(_profile_country(profile)), format_func=_country_label, key=f"instagram_edit_country_{profile_id}")
412
429
  language = st.selectbox("Idioma", list(LANGUAGE_CODES), index=_language_index(profile.get("language")), format_func=language_label, key=f"instagram_edit_language_{profile_id}")
413
430
  with edit_cols[1]:
414
431
  posts = st.number_input("posts", min_value=0, value=_metric_input(profile.get("post_count")), placeholder="Não encontrado", key=f"instagram_edit_posts_{profile_id}")
@@ -427,7 +444,7 @@ def _render_instagram_card(profile: dict[str, Any], characters: list[dict[str, A
427
444
  with block_cols[1]:
428
445
  st.markdown(f"**handler**\n\n{_clean(profile.get('handle')) or '—'}")
429
446
  with block_cols[2]:
430
- st.markdown(f"**País**\n\n{_clean(profile.get('country')) or '—'}")
447
+ st.markdown(f"**País**\n\n{_profile_country(profile) or '—'}")
431
448
  with block_cols[3]:
432
449
  st.markdown(f"**Idioma**\n\n{_clean(profile.get('language')) or '—'}")
433
450
 
@@ -459,7 +476,10 @@ def render_social_networks(settings: dict[str, Any]) -> None:
459
476
  st.rerun()
460
477
  if search_clicked:
461
478
  result = fetch_public_instagram_profile(source)
462
- st.session_state["social_instagram_result"] = result.data if isinstance(result.data, dict) else {}
479
+ result_data = dict(result.data) if isinstance(result.data, dict) else {}
480
+ if result_data and not _clean(result_data.get("country")):
481
+ result_data["country"] = _infer_country_from_bio(_profile_bio(result_data))
482
+ st.session_state["social_instagram_result"] = result_data
463
483
  st.session_state["social_instagram_ok"] = result.ok
464
484
  st.session_state["social_instagram_message"] = result.message
465
485
  if st.session_state.get("social_instagram_message"):
@@ -488,7 +508,7 @@ def render_social_networks(settings: dict[str, Any]) -> None:
488
508
  with form_cols[0]:
489
509
  name = st.text_input("Nome", value=_clean(data.get("name")), key="social_instagram_result_name")
490
510
  bio = st.text_area("Bio", value=_profile_bio(data), key="social_instagram_result_bio")
491
- country = st.selectbox("País", _country_options(), index=_country_index(data.get("country")), format_func=_country_label, key="social_instagram_result_country")
511
+ country = st.selectbox("País", _country_options(), index=_country_index(_profile_country(data)), format_func=_country_label, key="social_instagram_result_country")
492
512
  language = st.selectbox("Idioma", list(LANGUAGE_CODES), index=_language_index(data.get("language")), format_func=language_label, key="social_instagram_result_language")
493
513
  with form_cols[1]:
494
514
  posts = st.number_input("posts", min_value=0, value=_metric_input(data.get("post_count")), placeholder="Não encontrado", key="social_instagram_result_posts")
@@ -125,6 +125,8 @@ def _fetch_web_profile_user(username: str) -> dict[str, Any] | None:
125
125
  [curl, '-L', '--max-time', '20', '-sS', '-A', headers['User-Agent'], '-H', f"x-ig-app-id: {headers['x-ig-app-id']}", '-H', 'Accept: application/json, text/plain, */*', endpoint],
126
126
  capture_output=True,
127
127
  text=True,
128
+ encoding='utf-8',
129
+ errors='replace',
128
130
  timeout=25,
129
131
  check=False,
130
132
  )
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danhachuel/thunderbolt",
3
- "version": "0.6.30",
3
+ "version": "0.6.32",
4
4
  "description": "Thunderbolt — interface local para operação de canais faceless e motor MoneyPrinterTurbo",
5
5
  "license": "MIT",
6
6
  "main": "scripts/cli.mjs",