@danhachuel/thunderbolt 0.6.23 → 0.6.24
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.
- package/app/social_networks_ui.py +25 -13
- package/hermes_ui/domain.py +1 -0
- package/integrations/instagram_public.py +17 -14
- package/package.json +1 -1
|
@@ -13,7 +13,7 @@ from hermes_ui.influencers import InfluencerBackendError, STANDALONE_CONTENT_INF
|
|
|
13
13
|
from hermes_ui.storage import read_json, write_json
|
|
14
14
|
from hermes_ui.countries import COUNTRY_OPTIONS
|
|
15
15
|
from hermes_ui.languages import LANGUAGE_CODES, language_code, language_label
|
|
16
|
-
from integrations.instagram_public import fetch_public_instagram_posts, fetch_public_instagram_profile, normalize_instagram_bio
|
|
16
|
+
from integrations.instagram_public import fetch_public_instagram_posts, fetch_public_instagram_profile, normalize_instagram_bio, normalize_instagram_metric
|
|
17
17
|
from integrations.meta_social import test_facebook_pages_api_card, test_instagram_api_card
|
|
18
18
|
|
|
19
19
|
|
|
@@ -38,6 +38,10 @@ def _profile_metric(profile: Mapping[str, Any], *keys: str) -> Any:
|
|
|
38
38
|
return None
|
|
39
39
|
|
|
40
40
|
|
|
41
|
+
def _metric_input(value: Any) -> int | None:
|
|
42
|
+
return normalize_instagram_metric(value)
|
|
43
|
+
|
|
44
|
+
|
|
41
45
|
def _language_index(value: Any) -> int:
|
|
42
46
|
code = language_code(value)
|
|
43
47
|
return list(LANGUAGE_CODES).index(code) if code in LANGUAGE_CODES else list(LANGUAGE_CODES).index("pt")
|
|
@@ -288,6 +292,7 @@ def _save_public_profile(data: Mapping[str, Any], *, country: str, language: str
|
|
|
288
292
|
"name": name,
|
|
289
293
|
"url": url,
|
|
290
294
|
"handle": _clean(data.get("handle")),
|
|
295
|
+
"bio": normalize_instagram_bio(data.get("bio")),
|
|
291
296
|
"country": country.strip(),
|
|
292
297
|
"language": language.strip(),
|
|
293
298
|
"post_count": data.get("post_count"),
|
|
@@ -300,6 +305,14 @@ def _save_public_profile(data: Mapping[str, Any], *, country: str, language: str
|
|
|
300
305
|
return create_channel(name, url, metadata)
|
|
301
306
|
|
|
302
307
|
|
|
308
|
+
def _merge_instagram_refresh(existing: Mapping[str, Any], refreshed: Mapping[str, Any]) -> dict[str, Any]:
|
|
309
|
+
merged = dict(refreshed)
|
|
310
|
+
for field in ("subscriber_count", "following_count", "post_count", "bio", "avatar_url"):
|
|
311
|
+
if merged.get(field) in (None, "") and existing.get(field) not in (None, ""):
|
|
312
|
+
merged[field] = existing[field]
|
|
313
|
+
return merged
|
|
314
|
+
|
|
315
|
+
|
|
303
316
|
def _render_instagram_card(profile: dict[str, Any], characters: list[dict[str, Any]], settings: Mapping[str, Any]) -> None:
|
|
304
317
|
profile_id = _clean(profile.get("id"))
|
|
305
318
|
edit_key = f"edit_instagram_profile_{profile_id}"
|
|
@@ -329,10 +342,7 @@ def _render_instagram_card(profile: dict[str, Any], characters: list[dict[str, A
|
|
|
329
342
|
if st.button("↻", help="Actualizar posts, seguidores e seguindo", key=f"refresh_instagram_{profile_id}"):
|
|
330
343
|
result = fetch_public_instagram_profile(_clean(profile.get("url")) or _clean(profile.get("handle")))
|
|
331
344
|
if result.ok:
|
|
332
|
-
refreshed =
|
|
333
|
-
for field in ("subscriber_count", "following_count", "post_count", "bio", "avatar_url"):
|
|
334
|
-
if refreshed.get(field) in (None, "") and profile.get(field) not in (None, ""):
|
|
335
|
-
refreshed[field] = profile[field]
|
|
345
|
+
refreshed = _merge_instagram_refresh(profile, result.data)
|
|
336
346
|
update_channel(profile_id, {**refreshed, "platform": "instagram", "country": profile.get("country", ""), "language": profile.get("language", ""), "character_id": profile.get("character_id", "")})
|
|
337
347
|
st.success("Métricas Instagram actualizadas.")
|
|
338
348
|
st.rerun()
|
|
@@ -368,15 +378,16 @@ def _render_instagram_card(profile: dict[str, Any], characters: list[dict[str, A
|
|
|
368
378
|
with edit_cols[0]:
|
|
369
379
|
name = st.text_input("Nome", value=_clean(profile.get("name")), key=f"instagram_edit_name_{profile_id}")
|
|
370
380
|
handle = st.text_input("handler", value=_clean(profile.get("handle")), key=f"instagram_edit_handle_{profile_id}")
|
|
381
|
+
bio = st.text_area("Bio", value=normalize_instagram_bio(profile.get("bio")), key=f"instagram_edit_bio_{profile_id}")
|
|
371
382
|
country = st.selectbox("País", _country_options(), index=_country_index(profile.get("country")), format_func=_country_label, key=f"instagram_edit_country_{profile_id}")
|
|
372
383
|
language = st.selectbox("Idioma", list(LANGUAGE_CODES), index=_language_index(profile.get("language")), format_func=language_label, key=f"instagram_edit_language_{profile_id}")
|
|
373
384
|
with edit_cols[1]:
|
|
374
|
-
posts = st.number_input("posts", min_value=0, value=
|
|
375
|
-
followers = st.number_input("Seguidores", min_value=0, value=
|
|
376
|
-
following = st.number_input("seguindo", min_value=0, value=
|
|
385
|
+
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}")
|
|
386
|
+
followers = st.number_input("Seguidores", min_value=0, value=_metric_input(profile.get("subscriber_count")), placeholder="Não encontrado", key=f"instagram_edit_followers_{profile_id}")
|
|
387
|
+
following = st.number_input("seguindo", min_value=0, value=_metric_input(profile.get("following_count")), placeholder="Não encontrado", key=f"instagram_edit_following_{profile_id}")
|
|
377
388
|
save_edit = st.form_submit_button("Guardar alterações", type="primary", use_container_width=True)
|
|
378
389
|
if save_edit:
|
|
379
|
-
update_channel(profile_id, {"name": name.strip(), "handle": handle.strip(), "country": country.strip(), "language": language, "post_count":
|
|
390
|
+
update_channel(profile_id, {"name": name.strip(), "handle": handle.strip(), "bio": normalize_instagram_bio(bio), "country": country.strip(), "language": language, "post_count": _metric_input(posts), "subscriber_count": _metric_input(followers), "following_count": _metric_input(following)})
|
|
380
391
|
st.session_state.pop(edit_key, None)
|
|
381
392
|
st.success("Conta Instagram actualizada.")
|
|
382
393
|
st.rerun()
|
|
@@ -449,19 +460,20 @@ def render_social_networks(settings: dict[str, Any]) -> None:
|
|
|
449
460
|
form_cols = st.columns(2)
|
|
450
461
|
with form_cols[0]:
|
|
451
462
|
name = st.text_input("Nome", value=_clean(data.get("name")), key="social_instagram_result_name")
|
|
463
|
+
bio = st.text_area("Bio", value=normalize_instagram_bio(data.get("bio")), key="social_instagram_result_bio")
|
|
452
464
|
country = st.selectbox("País", _country_options(), index=_country_index(data.get("country")), format_func=_country_label, key="social_instagram_result_country")
|
|
453
465
|
language = st.selectbox("Idioma", list(LANGUAGE_CODES), index=_language_index(data.get("language")), format_func=language_label, key="social_instagram_result_language")
|
|
454
466
|
with form_cols[1]:
|
|
455
|
-
posts = st.number_input("posts", min_value=0, value=
|
|
456
|
-
followers = st.number_input("Seguidores", min_value=0, value=
|
|
457
|
-
following = st.number_input("seguindo", min_value=0, value=
|
|
467
|
+
posts = st.number_input("posts", min_value=0, value=_metric_input(data.get("post_count")), placeholder="Não encontrado", key="social_instagram_result_posts")
|
|
468
|
+
followers = st.number_input("Seguidores", min_value=0, value=_metric_input(data.get("subscriber_count")), placeholder="Não encontrado", key="social_instagram_result_followers")
|
|
469
|
+
following = st.number_input("seguindo", min_value=0, value=_metric_input(data.get("following_count")), placeholder="Não encontrado", key="social_instagram_result_following")
|
|
458
470
|
character_options = [""] + [_clean(item.get("id")) for item in characters if _clean(item.get("id"))]
|
|
459
471
|
character_labels = {"": "Sem personagem associado"} | {_clean(item.get("id")): _clean(item.get("name")) or _clean(item.get("id")) for item in characters}
|
|
460
472
|
selected_character = st.selectbox("Personagem", character_options, format_func=lambda value: character_labels.get(value, value), key="social_instagram_result_character")
|
|
461
473
|
save_profile = st.form_submit_button("Cadastrar conta Instagram", type="primary", use_container_width=True)
|
|
462
474
|
if save_profile:
|
|
463
475
|
profile_data = dict(data)
|
|
464
|
-
profile_data.update({"name": name, "post_count":
|
|
476
|
+
profile_data.update({"name": name, "bio": normalize_instagram_bio(bio), "post_count": _metric_input(posts), "subscriber_count": _metric_input(followers), "following_count": _metric_input(following)})
|
|
465
477
|
_save_public_profile(profile_data, country=country, language=language, character_id=selected_character)
|
|
466
478
|
st.success("Conta Instagram cadastrada em Redes Sociais.")
|
|
467
479
|
for key in ("social_instagram_result", "social_instagram_ok", "social_instagram_message"):
|
package/hermes_ui/domain.py
CHANGED
|
@@ -117,8 +117,8 @@ def _fetch_web_profile_user(username: str) -> dict[str, Any] | None:
|
|
|
117
117
|
|
|
118
118
|
|
|
119
119
|
def _profile_data_from_api(user: Mapping[str, Any], reference: Mapping[str, str]) -> dict[str, Any]:
|
|
120
|
-
followers =
|
|
121
|
-
following =
|
|
120
|
+
followers = _profile_metric(user, 'edge_followed_by', 'followers', 'follower_count', 'followerCount')
|
|
121
|
+
following = _profile_metric(user, 'edge_follow', 'follows', 'following', 'following_count', 'followingCount')
|
|
122
122
|
followers = followers if followers is not None else _structured_metric_from_json(json.dumps(user), 'edge_followed_by', 'followers', 'follower_count', 'followerCount')
|
|
123
123
|
following = following if following is not None else _structured_metric_from_json(json.dumps(user), 'edge_follow', 'follows', 'following', 'following_count', 'followingCount')
|
|
124
124
|
media = ((user.get('edge_owner_to_timeline_media') or {}).get('count') if isinstance(user.get('edge_owner_to_timeline_media'), dict) else None)
|
|
@@ -138,18 +138,21 @@ def _profile_data_from_api(user: Mapping[str, Any], reference: Mapping[str, str]
|
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
|
|
141
|
-
def
|
|
142
|
-
"""
|
|
141
|
+
def normalize_instagram_metric(value: Any) -> int | None:
|
|
142
|
+
"""Normalize an Instagram counter without turning an unknown value into zero."""
|
|
143
|
+
if isinstance(value, Mapping):
|
|
144
|
+
value = value.get('count') or value.get('value')
|
|
145
|
+
if value in (None, ''):
|
|
146
|
+
return None
|
|
147
|
+
digits = re.sub(r'[^0-9]', '', str(value))
|
|
148
|
+
return int(digits) if digits else None
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def _profile_metric(user: Mapping[str, Any], *keys: str) -> int | None:
|
|
143
152
|
for key in keys:
|
|
144
|
-
value = user.get(key)
|
|
145
|
-
if
|
|
146
|
-
|
|
147
|
-
if value in (None, ''):
|
|
148
|
-
continue
|
|
149
|
-
try:
|
|
150
|
-
return int(value)
|
|
151
|
-
except (TypeError, ValueError):
|
|
152
|
-
continue
|
|
153
|
+
value = normalize_instagram_metric(user.get(key))
|
|
154
|
+
if value is not None:
|
|
155
|
+
return value
|
|
153
156
|
return None
|
|
154
157
|
|
|
155
158
|
|
|
@@ -283,4 +286,4 @@ def fetch_public_instagram_posts(source: str, limit: int = 10) -> IntegrationRes
|
|
|
283
286
|
return IntegrationResult(bool(posts), 'Posts públicos encontrados.' if posts else 'Não foi possível encontrar posts públicos nesta página do Instagram.', reference | {'posts': posts[:max(1, int(limit))]})
|
|
284
287
|
|
|
285
288
|
|
|
286
|
-
__all__ = ['IntegrationResult', 'fetch_public_instagram_posts', 'fetch_public_instagram_profile', 'normalize_instagram_bio', 'normalize_instagram_reference']
|
|
289
|
+
__all__ = ['IntegrationResult', 'fetch_public_instagram_posts', 'fetch_public_instagram_profile', 'normalize_instagram_bio', 'normalize_instagram_metric', 'normalize_instagram_reference']
|
package/package.json
CHANGED