@danhachuel/thunderbolt 0.6.16 → 0.6.18

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.
@@ -8,6 +8,7 @@ import streamlit as st
8
8
  from hermes_ui.domain import create_channel, update_channel
9
9
  from hermes_ui.influencers import InfluencerBackendError, STANDALONE_CONTENT_INFLUENCER_ID, get_repository
10
10
  from hermes_ui.storage import read_json, write_json
11
+ from hermes_ui.languages import LANGUAGE_CODES, language_code, language_label
11
12
  from integrations.instagram_public import fetch_public_instagram_profile
12
13
  from integrations.meta_social import test_facebook_pages_api_card, test_instagram_api_card
13
14
 
@@ -25,6 +26,11 @@ def _metric(value: Any) -> str:
25
26
  return _clean(value) or "—"
26
27
 
27
28
 
29
+ def _language_index(value: Any) -> int:
30
+ code = language_code(value)
31
+ return list(LANGUAGE_CODES).index(code) if code in LANGUAGE_CODES else list(LANGUAGE_CODES).index("pt")
32
+
33
+
28
34
  def _api_card_defaults(kind: str) -> tuple[str, list[str], Callable[[Mapping[str, Any]], dict[str, Any]]]:
29
35
  if kind == "instagram":
30
36
  return "instagram_api_cards", ["id", "label", "account_id", "access_token"], test_instagram_api_card
@@ -208,6 +214,9 @@ def _render_instagram_card(profile: dict[str, Any], characters: list[dict[str, A
208
214
  with header_cols[1]:
209
215
  st.write(f"**{_clean(profile.get('name')) or 'Sem nome'}**")
210
216
  st.caption(f"{_clean(profile.get('handle')) or _clean(profile.get('url')) or 'sem handler'}")
217
+ bio = _clean(profile.get("bio"))
218
+ if bio:
219
+ st.caption(f"Bio: {bio}")
211
220
  st.caption(f"{_clean(profile.get('country')) or 'País não definido'} · {_clean(profile.get('language')) or 'Idioma não definido'}")
212
221
  with header_cols[2]:
213
222
  st.metric("posts", _metric(profile.get("post_count")))
@@ -240,14 +249,14 @@ def _render_instagram_card(profile: dict[str, Any], characters: list[dict[str, A
240
249
  name = st.text_input("Nome", value=_clean(profile.get("name")), key=f"instagram_edit_name_{profile_id}")
241
250
  handle = st.text_input("handler", value=_clean(profile.get("handle")), key=f"instagram_edit_handle_{profile_id}")
242
251
  country = st.text_input("País", value=_clean(profile.get("country")), key=f"instagram_edit_country_{profile_id}")
243
- language = st.text_input("Idioma", value=_clean(profile.get("language")), key=f"instagram_edit_language_{profile_id}")
252
+ language = st.selectbox("Idioma", list(LANGUAGE_CODES), index=_language_index(profile.get("language")), format_func=language_label, key=f"instagram_edit_language_{profile_id}")
244
253
  with edit_cols[1]:
245
254
  posts = st.number_input("posts", min_value=0, value=int(profile.get("post_count") or 0), key=f"instagram_edit_posts_{profile_id}")
246
255
  followers = st.number_input("Seguidores", min_value=0, value=int(profile.get("subscriber_count") or 0), key=f"instagram_edit_followers_{profile_id}")
247
256
  following = st.number_input("seguindo", min_value=0, value=int(profile.get("following_count") or 0), key=f"instagram_edit_following_{profile_id}")
248
257
  save_edit = st.form_submit_button("Guardar alterações", type="primary", use_container_width=True)
249
258
  if save_edit:
250
- update_channel(profile_id, {"name": name.strip(), "handle": handle.strip(), "country": country.strip(), "language": language.strip(), "post_count": int(posts), "subscriber_count": int(followers), "following_count": int(following)})
259
+ update_channel(profile_id, {"name": name.strip(), "handle": handle.strip(), "country": country.strip(), "language": language, "post_count": int(posts), "subscriber_count": int(followers), "following_count": int(following)})
251
260
  st.session_state.pop(edit_key, None)
252
261
  st.success("Conta Instagram actualizada.")
253
262
  st.rerun()
@@ -317,7 +326,7 @@ def render_social_networks(settings: dict[str, Any]) -> None:
317
326
  with form_cols[0]:
318
327
  name = st.text_input("Nome", value=_clean(data.get("name")), key="social_instagram_result_name")
319
328
  country = st.text_input("País", value=_clean(data.get("country")), key="social_instagram_result_country")
320
- language = st.text_input("Idioma", value=_clean(data.get("language")), key="social_instagram_result_language")
329
+ language = st.selectbox("Idioma", list(LANGUAGE_CODES), index=_language_index(data.get("language")), format_func=language_label, key="social_instagram_result_language")
321
330
  with form_cols[1]:
322
331
  posts = st.number_input("posts", min_value=0, value=int(data.get("post_count") or 0), key="social_instagram_result_posts")
323
332
  followers = st.number_input("Seguidores", min_value=0, value=int(data.get("subscriber_count") or 0), key="social_instagram_result_followers")
@@ -50,10 +50,11 @@ def _meta(document: str, name: str) -> str:
50
50
 
51
51
  def _structured_metric(document: str, *keys: str) -> int | None:
52
52
  """Read numeric Instagram counters embedded in the public HTML payload."""
53
+ document = unescape(document)
53
54
  for key in keys:
54
55
  patterns = (
55
- rf'"{re.escape(key)}"\s*:\s*\{{\s*"count"\s*:\s*(\d+)',
56
- rf'"{re.escape(key)}"\s*:\s*(\d+)',
56
+ rf'\\?["\']{re.escape(key)}\\?["\']\s*:\s*\{{[^}}]{{0,240}}?\\?["\']count\\?["\']\s*:\s*(\d+)',
57
+ rf'\\?["\']{re.escape(key)}\\?["\']\s*:\s*(\d+)',
57
58
  )
58
59
  for pattern in patterns:
59
60
  found = re.search(pattern, document, flags=re.IGNORECASE)
@@ -91,7 +92,7 @@ def fetch_public_instagram_profile(source: str) -> IntegrationResult:
91
92
  return None
92
93
 
93
94
  followers = metric((r'([\d,.]+)\s*(?:mil\s+)?(?:followers|seguidores)',)) or _structured_metric(response.text, 'edge_followed_by', 'followers', 'follower_count')
94
- following = metric((r'([\d,.]+)\s*(?:mil\s+)?(?:following|seguindo)',)) or _structured_metric(response.text, 'edge_follow', 'following', 'following_count')
95
+ following = metric((r'([\d,.]+)\s*(?:mil\s+)?(?:following|seguindo)',)) or _structured_metric(response.text, 'edge_follow', 'follows', 'following', 'following_count', 'followingCount')
95
96
  posts = metric((r'([\d,.]+)\s*(?:mil\s+)?(?:posts|publicações|publications)',)) or _structured_metric(response.text, 'edge_owner_to_timeline_media', 'posts', 'post_count')
96
97
  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()}
97
98
  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.16",
3
+ "version": "0.6.18",
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",