@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,
|
|
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 =
|
|
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
|
-
|
|
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":
|
|
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":
|
|
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
|
-
"""
|
|
82
|
-
return str(value or '')
|
|
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
|
-
|
|
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
|
-
|
|
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((
|
|
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':
|
|
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':
|
|
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