@danhachuel/thunderbolt 0.6.15 → 0.6.17
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
|
|
@@ -152,7 +158,16 @@ def render_meta_api_cards(settings: dict[str, Any], kind: str) -> None:
|
|
|
152
158
|
|
|
153
159
|
def _instagram_profiles() -> list[dict[str, Any]]:
|
|
154
160
|
records = read_json("channels.json", [])
|
|
155
|
-
|
|
161
|
+
profiles: list[dict[str, Any]] = []
|
|
162
|
+
for item in records:
|
|
163
|
+
if not isinstance(item, dict):
|
|
164
|
+
continue
|
|
165
|
+
platform = _clean(item.get("platform")).lower()
|
|
166
|
+
network = _clean(item.get("social_network")).lower()
|
|
167
|
+
url = _clean(item.get("url")).lower()
|
|
168
|
+
if platform == "instagram" or network == "instagram" or "instagram.com/" in url:
|
|
169
|
+
profiles.append(item)
|
|
170
|
+
return profiles
|
|
156
171
|
|
|
157
172
|
|
|
158
173
|
def _characters(settings: Mapping[str, Any]) -> tuple[list[dict[str, Any]], Any | None]:
|
|
@@ -231,14 +246,14 @@ def _render_instagram_card(profile: dict[str, Any], characters: list[dict[str, A
|
|
|
231
246
|
name = st.text_input("Nome", value=_clean(profile.get("name")), key=f"instagram_edit_name_{profile_id}")
|
|
232
247
|
handle = st.text_input("handler", value=_clean(profile.get("handle")), key=f"instagram_edit_handle_{profile_id}")
|
|
233
248
|
country = st.text_input("País", value=_clean(profile.get("country")), key=f"instagram_edit_country_{profile_id}")
|
|
234
|
-
language = st.
|
|
249
|
+
language = st.selectbox("Idioma", list(LANGUAGE_CODES), index=_language_index(profile.get("language")), format_func=language_label, key=f"instagram_edit_language_{profile_id}")
|
|
235
250
|
with edit_cols[1]:
|
|
236
251
|
posts = st.number_input("posts", min_value=0, value=int(profile.get("post_count") or 0), key=f"instagram_edit_posts_{profile_id}")
|
|
237
252
|
followers = st.number_input("Seguidores", min_value=0, value=int(profile.get("subscriber_count") or 0), key=f"instagram_edit_followers_{profile_id}")
|
|
238
253
|
following = st.number_input("seguindo", min_value=0, value=int(profile.get("following_count") or 0), key=f"instagram_edit_following_{profile_id}")
|
|
239
254
|
save_edit = st.form_submit_button("Guardar alterações", type="primary", use_container_width=True)
|
|
240
255
|
if save_edit:
|
|
241
|
-
update_channel(profile_id, {"name": name.strip(), "handle": handle.strip(), "country": country.strip(), "language": language
|
|
256
|
+
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)})
|
|
242
257
|
st.session_state.pop(edit_key, None)
|
|
243
258
|
st.success("Conta Instagram actualizada.")
|
|
244
259
|
st.rerun()
|
|
@@ -308,7 +323,7 @@ def render_social_networks(settings: dict[str, Any]) -> None:
|
|
|
308
323
|
with form_cols[0]:
|
|
309
324
|
name = st.text_input("Nome", value=_clean(data.get("name")), key="social_instagram_result_name")
|
|
310
325
|
country = st.text_input("País", value=_clean(data.get("country")), key="social_instagram_result_country")
|
|
311
|
-
language = st.
|
|
326
|
+
language = st.selectbox("Idioma", list(LANGUAGE_CODES), index=_language_index(data.get("language")), format_func=language_label, key="social_instagram_result_language")
|
|
312
327
|
with form_cols[1]:
|
|
313
328
|
posts = st.number_input("posts", min_value=0, value=int(data.get("post_count") or 0), key="social_instagram_result_posts")
|
|
314
329
|
followers = st.number_input("Seguidores", min_value=0, value=int(data.get("subscriber_count") or 0), key="social_instagram_result_followers")
|
package/hermes_ui/domain.py
CHANGED
|
@@ -33,6 +33,8 @@ def create_channel(name: str, url: str = "", metadata: dict[str, Any] | None = N
|
|
|
33
33
|
"google_account_email": "",
|
|
34
34
|
"name": name.strip(),
|
|
35
35
|
"url": url.strip(),
|
|
36
|
+
"platform": "youtube",
|
|
37
|
+
"social_network": "YouTube",
|
|
36
38
|
"handle": "",
|
|
37
39
|
"description": "",
|
|
38
40
|
"description_source": "",
|
|
@@ -41,9 +43,15 @@ def create_channel(name: str, url: str = "", metadata: dict[str, Any] | None = N
|
|
|
41
43
|
"thumbnail_url": "",
|
|
42
44
|
"avatar_url": "",
|
|
43
45
|
"subscriber_count": None,
|
|
46
|
+
"post_count": None,
|
|
47
|
+
"following_count": None,
|
|
44
48
|
"video_count": None,
|
|
45
49
|
"view_count": None,
|
|
46
50
|
"metrics_source": "manual",
|
|
51
|
+
"public_lookup": False,
|
|
52
|
+
"last_public_lookup_at": None,
|
|
53
|
+
"country": "",
|
|
54
|
+
"character_id": "",
|
|
47
55
|
"last_youtube_sync": None,
|
|
48
56
|
"language": "Português",
|
|
49
57
|
"blueprint_id": "",
|
|
@@ -48,6 +48,24 @@ def _meta(document: str, name: str) -> str:
|
|
|
48
48
|
return ''
|
|
49
49
|
|
|
50
50
|
|
|
51
|
+
def _structured_metric(document: str, *keys: str) -> int | None:
|
|
52
|
+
"""Read numeric Instagram counters embedded in the public HTML payload."""
|
|
53
|
+
document = unescape(document)
|
|
54
|
+
for key in keys:
|
|
55
|
+
patterns = (
|
|
56
|
+
rf'\\?["\']{re.escape(key)}\\?["\']\s*:\s*\{{[^}}]{{0,240}}?\\?["\']count\\?["\']\s*:\s*(\d+)',
|
|
57
|
+
rf'\\?["\']{re.escape(key)}\\?["\']\s*:\s*(\d+)',
|
|
58
|
+
)
|
|
59
|
+
for pattern in patterns:
|
|
60
|
+
found = re.search(pattern, document, flags=re.IGNORECASE)
|
|
61
|
+
if found:
|
|
62
|
+
try:
|
|
63
|
+
return int(found.group(1))
|
|
64
|
+
except ValueError:
|
|
65
|
+
continue
|
|
66
|
+
return None
|
|
67
|
+
|
|
68
|
+
|
|
51
69
|
def fetch_public_instagram_profile(source: str) -> IntegrationResult:
|
|
52
70
|
try:
|
|
53
71
|
reference = normalize_instagram_reference(source)
|
|
@@ -73,9 +91,9 @@ def fetch_public_instagram_profile(source: str) -> IntegrationResult:
|
|
|
73
91
|
continue
|
|
74
92
|
return None
|
|
75
93
|
|
|
76
|
-
followers = metric((r'([\d,.]+)\s*(?:mil\s+)?(?:followers|seguidores)',))
|
|
77
|
-
following = metric((r'([\d,.]+)\s*(?:mil\s+)?(?:following|seguindo)',))
|
|
78
|
-
posts = metric((r'([\d,.]+)\s*(?:mil\s+)?(?:posts|publicações|publications)',))
|
|
94
|
+
followers = metric((r'([\d,.]+)\s*(?:mil\s+)?(?:followers|seguidores)',)) or _structured_metric(response.text, 'edge_followed_by', 'followers', 'follower_count')
|
|
95
|
+
following = metric((r'([\d,.]+)\s*(?:mil\s+)?(?:following|seguindo)',)) or _structured_metric(response.text, 'edge_follow', 'follows', 'following', 'following_count', 'followingCount')
|
|
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')
|
|
79
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()}
|
|
80
98
|
return IntegrationResult(True, 'Perfil Instagram encontrado publicamente.', data)
|
|
81
99
|
|
package/package.json
CHANGED