@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.
|
|
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
|
|
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.
|
|
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*\{{\
|
|
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