@danhachuel/thunderbolt 0.6.19 → 0.6.20

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.
@@ -29,6 +29,14 @@ def _metric(value: Any) -> str:
29
29
  return _clean(value) or "—"
30
30
 
31
31
 
32
+ def _profile_metric(profile: Mapping[str, Any], *keys: str) -> Any:
33
+ for key in keys:
34
+ value = profile.get(key)
35
+ if value not in (None, ""):
36
+ return value
37
+ return None
38
+
39
+
32
40
  def _language_index(value: Any) -> int:
33
41
  code = language_code(value)
34
42
  return list(LANGUAGE_CODES).index(code) if code in LANGUAGE_CODES else list(LANGUAGE_CODES).index("pt")
@@ -297,16 +305,20 @@ def _render_instagram_card(profile: dict[str, Any], characters: list[dict[str, A
297
305
  with header_cols[2]:
298
306
  st.metric("posts", _metric(profile.get("post_count")))
299
307
  with header_cols[3]:
300
- st.metric("Seguidores", _metric(profile.get("subscriber_count")))
308
+ st.metric("Seguidores", _metric(_profile_metric(profile, "subscriber_count", "followers_count", "follower_count")))
301
309
  with header_cols[4]:
302
- st.metric("seguindo", _metric(profile.get("following_count")))
310
+ st.metric("seguindo", _metric(_profile_metric(profile, "following_count", "following", "follows")))
303
311
  with header_cols[5]:
304
312
  refresh_col, edit_col = st.columns(2)
305
313
  with refresh_col:
306
314
  if st.button("↻", help="Actualizar posts, seguidores e seguindo", key=f"refresh_instagram_{profile_id}"):
307
315
  result = fetch_public_instagram_profile(_clean(profile.get("url")) or _clean(profile.get("handle")))
308
316
  if result.ok:
309
- update_channel(profile_id, {**result.data, "platform": "instagram", "country": profile.get("country", ""), "language": profile.get("language", ""), "character_id": profile.get("character_id", "")})
317
+ refreshed = dict(result.data)
318
+ for field in ("subscriber_count", "following_count", "post_count", "bio", "avatar_url"):
319
+ if refreshed.get(field) in (None, "") and profile.get(field) not in (None, ""):
320
+ refreshed[field] = profile[field]
321
+ update_channel(profile_id, {**refreshed, "platform": "instagram", "country": profile.get("country", ""), "language": profile.get("language", ""), "character_id": profile.get("character_id", "")})
310
322
  st.success("Métricas Instagram actualizadas.")
311
323
  st.rerun()
312
324
  st.warning(result.message)
@@ -67,6 +67,24 @@ def _structured_metric(document: str, *keys: str) -> int | None:
67
67
  return None
68
68
 
69
69
 
70
+ def _structured_metric_from_json(document: str, *keys: str) -> int | None:
71
+ wanted = {key.casefold() for key in keys}
72
+ for parsed in _embedded_json_documents(document):
73
+ for node in _walk_json(parsed):
74
+ if not isinstance(node, dict):
75
+ continue
76
+ for key, value in node.items():
77
+ if str(key).casefold() not in wanted:
78
+ continue
79
+ if isinstance(value, dict):
80
+ value = value.get('count') or value.get('value')
81
+ try:
82
+ return int(value)
83
+ except (TypeError, ValueError):
84
+ continue
85
+ return None
86
+
87
+
70
88
  def fetch_public_instagram_profile(source: str) -> IntegrationResult:
71
89
  try:
72
90
  reference = normalize_instagram_reference(source)
@@ -92,8 +110,8 @@ def fetch_public_instagram_profile(source: str) -> IntegrationResult:
92
110
  continue
93
111
  return None
94
112
 
95
- followers = metric((r'([\d,.]+)\s*(?:mil\s+)?(?:followers|seguidores)',)) or _structured_metric(response.text, 'edge_followed_by', 'followers', 'follower_count')
96
- following = metric((r'([\d,.]+)\s*(?:mil\s+)?(?:following|seguindo)',)) or _structured_metric(response.text, 'edge_follow', 'follows', 'following', 'following_count', 'followingCount')
113
+ followers = metric((r'([\d,.]+)\s*(?:mil\s+)?(?:followers|seguidores)',)) or _structured_metric(response.text, 'edge_followed_by', 'followers', 'follower_count') or _structured_metric_from_json(response.text, 'edge_followed_by', 'followers', 'follower_count', 'followerCount')
114
+ following = metric((r'([\d,.]+)\s*(?:mil\s+)?(?:following|seguindo)',)) or _structured_metric(response.text, 'edge_follow', 'follows', 'following', 'following_count', 'followingCount') or _structured_metric_from_json(response.text, 'edge_follow', 'follows', 'following', 'following_count', 'followingCount')
97
115
  posts = metric((r'([\d,.]+)\s*(?:mil\s+)?(?:posts|publicações|publications)',)) or _structured_metric(response.text, 'edge_owner_to_timeline_media', 'posts', 'post_count')
98
116
  data = {'id': f"instagram_{reference['username']}", **reference, 'name': title.split('(')[0].strip() or reference['username'], 'bio': description, 'avatar_url': avatar_url, 'subscriber_count': followers, 'following_count': following, 'post_count': posts, 'public_lookup': True, 'metrics_source': 'instagram_public_page', 'last_public_lookup_at': datetime.now(timezone.utc).isoformat()}
99
117
  return IntegrationResult(True, 'Perfil Instagram encontrado publicamente.', data)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danhachuel/thunderbolt",
3
- "version": "0.6.19",
3
+ "version": "0.6.20",
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",