@danhachuel/thunderbolt 0.6.60 → 0.6.62

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.
@@ -15,7 +15,7 @@ from hermes_ui.influencers import InfluencerBackendError, STANDALONE_CONTENT_INF
15
15
  from hermes_ui.storage import read_json, write_json
16
16
  from hermes_ui.countries import COUNTRY_OPTIONS
17
17
  from hermes_ui.languages import LANGUAGE_CODES, language_code, language_label
18
- from integrations.instagram_public import fetch_public_instagram_posts, fetch_public_instagram_profile, normalize_instagram_bio, normalize_instagram_metric
18
+ from integrations.instagram_public import fetch_public_instagram_posts, fetch_public_instagram_profile, normalize_instagram_metric
19
19
  from integrations.meta_social import test_facebook_pages_api_card, test_instagram_api_card
20
20
 
21
21
 
@@ -46,17 +46,13 @@ def _metric_input(value: Any) -> int | None:
46
46
 
47
47
 
48
48
  def _render_instagram_bio(value: Any) -> None:
49
- bio = normalize_instagram_bio(value)
49
+ bio = str(value or "")
50
50
  if bio:
51
51
  st.text(bio)
52
52
 
53
53
 
54
54
  def _profile_bio(profile: Mapping[str, Any]) -> str:
55
- for key in ("bio", "bio_raw", "biography", "description"):
56
- bio = normalize_instagram_bio(profile.get(key))
57
- if bio:
58
- return bio
59
- return ""
55
+ return str(profile.get("bio") or profile.get("biography", ""))
60
56
 
61
57
 
62
58
  def _country_key(value: Any) -> str:
@@ -539,7 +535,7 @@ def _render_instagram_card(profile: dict[str, Any], characters: list[dict[str, A
539
535
  following = st.number_input("seguindo", min_value=0, value=_metric_input(_profile_metric(profile, "following_count", "following", "follows", "followingCount", "edge_follow")), placeholder="Não encontrado", key=f"instagram_edit_following_{profile_id}")
540
536
  save_edit = st.form_submit_button("Guardar alterações", type="primary", use_container_width=True)
541
537
  if save_edit:
542
- update_channel(profile_id, {"name": name.strip(), "handle": handle.strip(), "bio": normalize_instagram_bio(bio), "bio_raw": bio, "country": _normalise_country(country), "language": language, "post_count": _metric_input(posts), "subscriber_count": _metric_input(followers), "following_count": _metric_input(following)})
538
+ update_channel(profile_id, {"name": name.strip(), "handle": handle.strip(), "bio": bio, "bio_raw": bio, "country": _normalise_country(country), "language": language, "post_count": _metric_input(posts), "subscriber_count": _metric_input(followers), "following_count": _metric_input(following)})
543
539
  st.session_state.pop(edit_key, None)
544
540
  st.success("Conta Instagram actualizada.")
545
541
  st.rerun()
@@ -625,7 +621,7 @@ def render_social_networks(settings: dict[str, Any]) -> None:
625
621
  save_profile = st.form_submit_button("Cadastrar conta Instagram", type="primary", use_container_width=True)
626
622
  if save_profile:
627
623
  profile_data = dict(data)
628
- profile_data.update({"name": name, "bio": normalize_instagram_bio(bio), "bio_raw": bio, "post_count": _metric_input(posts), "subscriber_count": _metric_input(followers), "following_count": _metric_input(following)})
624
+ profile_data.update({"name": name, "bio": bio, "bio_raw": bio, "post_count": _metric_input(posts), "subscriber_count": _metric_input(followers), "following_count": _metric_input(following)})
629
625
  profile_data["country"] = _normalise_country(country)
630
626
  _save_public_profile(profile_data, country=profile_data["country"], language=language, character_id=selected_character)
631
627
  st.success("Conta Instagram cadastrada em Redes Sociais.")
@@ -78,8 +78,8 @@ class IntegrationResult:
78
78
 
79
79
 
80
80
  def normalize_instagram_bio(value: Any) -> str:
81
- """Preserve the complete biography returned by Instagram."""
82
- return str(value or '').strip()
81
+ """Deprecated compatibility helper; Bio values are now passed through unchanged."""
82
+ return str(value or '')
83
83
 
84
84
 
85
85
  def normalize_instagram_reference(source: str) -> dict[str, str]:
@@ -185,23 +185,45 @@ def _metric_text_value(value: Any) -> int | None:
185
185
  return int(digits) * multiplier
186
186
 
187
187
 
188
+ def _extract_bio_from_html(document: str) -> str:
189
+ """Extract only the JSON biography value; never use an SEO description as Bio."""
190
+ patterns = (
191
+ r'"biography"\s*:\s*"((?:\\.|[^"\\])*)"',
192
+ r"'biography'\s*:\s*'((?:\\.|[^'\\])*)'",
193
+ )
194
+ for pattern in patterns:
195
+ for match in re.finditer(pattern, document, flags=re.IGNORECASE | re.DOTALL):
196
+ raw_value = match.group(1)
197
+ try:
198
+ decoded = json.loads(f'"{raw_value}"')
199
+ except (TypeError, ValueError, json.JSONDecodeError):
200
+ decoded = bytes(raw_value, 'utf-8').decode('unicode_escape')
201
+ if str(decoded or '').strip():
202
+ return str(decoded).strip()
203
+ return ''
204
+
205
+
188
206
  def _embedded_profile_user(document: str, username: str) -> Mapping[str, Any]:
189
- wanted = username.casefold()
207
+ wanted = str(username or '').strip().lstrip('@').casefold()
190
208
  for payload in _embedded_json_documents(document):
191
209
  for node in _walk_json(payload):
192
210
  if not isinstance(node, Mapping):
193
211
  continue
194
- node_username = str(node.get('username') or node.get('handle') or '').lstrip('@').casefold()
195
- if node_username == wanted and any(key in node for key in ('biography', 'bio', 'edge_followed_by', 'edge_follow', 'followers', 'following')):
212
+ node_username = str(node.get('username') or node.get('handle') or '').strip().lstrip('@').casefold()
213
+ has_profile_fields = any(key in node for key in ('biography', 'bio', 'edge_followed_by', 'edge_follow', 'followers', 'following'))
214
+ if node_username == wanted and has_profile_fields:
196
215
  return node
197
216
  return {}
198
217
 
199
218
 
200
219
  def _extract_profile_user_from_html(document: str, username: str) -> dict[str, Any]:
201
220
  embedded_user = _embedded_profile_user(document, username)
221
+ regex_biography = _extract_bio_from_html(document)
202
222
  seo_description = _meta(document, 'og:description')
203
- biography = str(embedded_user.get('biography') or embedded_user.get('bio') or '').strip()
204
- description = biography or seo_description
223
+ biography = str(embedded_user.get('biography') or embedded_user.get('bio') or regex_biography or '').strip()
224
+ print(f'embedded_user for @{username}: {embedded_user}')
225
+ print(f'biography extracted for @{username}: {biography!r}')
226
+ description = biography
205
227
  title = str(embedded_user.get('full_name') or embedded_user.get('name') or _meta(document, 'og:title') or username).split('(')[0].strip() or username
206
228
  avatar_url = str(embedded_user.get('profile_pic_url_hd') or embedded_user.get('profile_pic_url') or _meta(document, 'og:image')).strip()
207
229
 
@@ -235,7 +257,7 @@ def _extract_profile_user_from_html(document: str, username: str) -> dict[str, A
235
257
  'profile_pic_url': avatar_url,
236
258
  '_html_posts': posts,
237
259
  }
238
- return user if any((description, followers, following, post_count, country, posts)) else {}
260
+ return user if any((biography, followers, following, post_count, country, posts)) else {}
239
261
 
240
262
 
241
263
  def _fetch_web_profile_user(username: str) -> dict[str, Any] | None:
@@ -382,7 +404,7 @@ def _profile_data_from_api(user: Mapping[str, Any], reference: Mapping[str, str]
382
404
  'id': f"instagram_{reference['username']}",
383
405
  **reference,
384
406
  'name': str(user.get('full_name') or user.get('username') or reference['username']).strip(),
385
- 'bio': normalize_instagram_bio(biography),
407
+ 'bio': biography,
386
408
  'bio_raw': biography,
387
409
  'country': country,
388
410
  'avatar_url': str(user.get('profile_pic_url_hd') or user.get('profile_pic_url') or '').strip(),
@@ -492,7 +514,7 @@ def fetch_public_instagram_profile(source: str) -> IntegrationResult:
492
514
  embedded_country = extract_public_instagram_country(document)
493
515
  if embedded_country:
494
516
  break
495
- data = {'id': f"instagram_{reference['username']}", **reference, 'name': title.split('(')[0].strip() or reference['username'], 'bio': normalize_instagram_bio(description), 'bio_raw': description, 'country': embedded_country, '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()}
517
+ data = {'id': f"instagram_{reference['username']}", **reference, 'name': title.split('(')[0].strip() or reference['username'], 'bio': description, 'bio_raw': description, 'country': embedded_country, '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()}
496
518
  return IntegrationResult(True, 'Perfil Instagram encontrado publicamente.', data)
497
519
 
498
520
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danhachuel/thunderbolt",
3
- "version": "0.6.60",
3
+ "version": "0.6.62",
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",