@danhachuel/thunderbolt 0.6.22 → 0.6.23
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,9 +8,10 @@ from typing import Any, Callable, Mapping
|
|
|
8
8
|
import requests
|
|
9
9
|
import streamlit as st
|
|
10
10
|
|
|
11
|
-
from hermes_ui.domain import create_channel, update_channel
|
|
11
|
+
from hermes_ui.domain import create_channel, delete_channel, update_channel
|
|
12
12
|
from hermes_ui.influencers import InfluencerBackendError, STANDALONE_CONTENT_INFLUENCER_ID, get_repository
|
|
13
13
|
from hermes_ui.storage import read_json, write_json
|
|
14
|
+
from hermes_ui.countries import COUNTRY_OPTIONS
|
|
14
15
|
from hermes_ui.languages import LANGUAGE_CODES, language_code, language_label
|
|
15
16
|
from integrations.instagram_public import fetch_public_instagram_posts, fetch_public_instagram_profile, normalize_instagram_bio
|
|
16
17
|
from integrations.meta_social import test_facebook_pages_api_card, test_instagram_api_card
|
|
@@ -42,6 +43,20 @@ def _language_index(value: Any) -> int:
|
|
|
42
43
|
return list(LANGUAGE_CODES).index(code) if code in LANGUAGE_CODES else list(LANGUAGE_CODES).index("pt")
|
|
43
44
|
|
|
44
45
|
|
|
46
|
+
def _country_options() -> tuple[str, ...]:
|
|
47
|
+
return ("",) + COUNTRY_OPTIONS
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _country_index(value: Any) -> int:
|
|
51
|
+
options = _country_options()
|
|
52
|
+
value = _clean(value)
|
|
53
|
+
return options.index(value) if value in options else 0
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _country_label(value: str) -> str:
|
|
57
|
+
return value or "Não definido"
|
|
58
|
+
|
|
59
|
+
|
|
45
60
|
def _instagram_posts_key(profile_id: str) -> str:
|
|
46
61
|
return f"instagram_posts_{profile_id}"
|
|
47
62
|
|
|
@@ -326,17 +341,34 @@ def _render_instagram_card(profile: dict[str, Any], characters: list[dict[str, A
|
|
|
326
341
|
if st.button("Editar", key=f"edit_instagram_button_{profile_id}", use_container_width=True):
|
|
327
342
|
st.session_state[edit_key] = True
|
|
328
343
|
st.rerun()
|
|
344
|
+
if st.button("Apagar", key=f"delete_instagram_button_{profile_id}", use_container_width=True):
|
|
345
|
+
st.session_state[f"confirm_delete_instagram_{profile_id}"] = True
|
|
346
|
+
st.rerun()
|
|
329
347
|
url = _clean(profile.get("url"))
|
|
330
348
|
if url:
|
|
331
349
|
st.link_button("Abrir Instagram", url, use_container_width=True)
|
|
332
350
|
|
|
351
|
+
if st.session_state.get(f"confirm_delete_instagram_{profile_id}"):
|
|
352
|
+
st.warning("Apagar este card remove o cadastro da conta Instagram, mas não os ficheiros associados.")
|
|
353
|
+
confirm_cols = st.columns(2)
|
|
354
|
+
with confirm_cols[0]:
|
|
355
|
+
if st.button("Confirmar apagar", type="primary", key=f"confirm_delete_instagram_button_{profile_id}", use_container_width=True):
|
|
356
|
+
delete_channel(profile_id)
|
|
357
|
+
st.session_state.pop(f"confirm_delete_instagram_{profile_id}", None)
|
|
358
|
+
st.success("Conta Instagram apagada.")
|
|
359
|
+
st.rerun()
|
|
360
|
+
with confirm_cols[1]:
|
|
361
|
+
if st.button("Cancelar", key=f"cancel_delete_instagram_button_{profile_id}", use_container_width=True):
|
|
362
|
+
st.session_state.pop(f"confirm_delete_instagram_{profile_id}", None)
|
|
363
|
+
st.rerun()
|
|
364
|
+
|
|
333
365
|
if st.session_state.get(edit_key):
|
|
334
366
|
with st.form(f"edit_instagram_profile_form_{profile_id}"):
|
|
335
367
|
edit_cols = st.columns(2)
|
|
336
368
|
with edit_cols[0]:
|
|
337
369
|
name = st.text_input("Nome", value=_clean(profile.get("name")), key=f"instagram_edit_name_{profile_id}")
|
|
338
370
|
handle = st.text_input("handler", value=_clean(profile.get("handle")), key=f"instagram_edit_handle_{profile_id}")
|
|
339
|
-
country = st.
|
|
371
|
+
country = st.selectbox("País", _country_options(), index=_country_index(profile.get("country")), format_func=_country_label, key=f"instagram_edit_country_{profile_id}")
|
|
340
372
|
language = st.selectbox("Idioma", list(LANGUAGE_CODES), index=_language_index(profile.get("language")), format_func=language_label, key=f"instagram_edit_language_{profile_id}")
|
|
341
373
|
with edit_cols[1]:
|
|
342
374
|
posts = st.number_input("posts", min_value=0, value=int(profile.get("post_count") or 0), key=f"instagram_edit_posts_{profile_id}")
|
|
@@ -404,6 +436,9 @@ def render_social_networks(settings: dict[str, Any]) -> None:
|
|
|
404
436
|
with preview_cols[1]:
|
|
405
437
|
st.write(f"**{_clean(data.get('name')) or _clean(data.get('username'))}**")
|
|
406
438
|
st.caption(_clean(data.get("handle")))
|
|
439
|
+
bio = normalize_instagram_bio(data.get("bio"))
|
|
440
|
+
if bio:
|
|
441
|
+
st.caption(f"Bio: {bio}")
|
|
407
442
|
with preview_cols[2]:
|
|
408
443
|
st.metric("posts", _metric(data.get("post_count")))
|
|
409
444
|
with preview_cols[3]:
|
|
@@ -414,7 +449,7 @@ def render_social_networks(settings: dict[str, Any]) -> None:
|
|
|
414
449
|
form_cols = st.columns(2)
|
|
415
450
|
with form_cols[0]:
|
|
416
451
|
name = st.text_input("Nome", value=_clean(data.get("name")), key="social_instagram_result_name")
|
|
417
|
-
country = st.
|
|
452
|
+
country = st.selectbox("País", _country_options(), index=_country_index(data.get("country")), format_func=_country_label, key="social_instagram_result_country")
|
|
418
453
|
language = st.selectbox("Idioma", list(LANGUAGE_CODES), index=_language_index(data.get("language")), format_func=language_label, key="social_instagram_result_language")
|
|
419
454
|
with form_cols[1]:
|
|
420
455
|
posts = st.number_input("posts", min_value=0, value=int(data.get("post_count") or 0), key="social_instagram_result_posts")
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""Canonical country catalog for social-account metadata."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
COUNTRY_OPTIONS: tuple[str, ...] = (
|
|
6
|
+
"Abecásia", "Afeganistão", "África do Sul", "Albânia", "Alemanha", "Andorra", "Angola",
|
|
7
|
+
"Antígua e Barbuda", "Arábia Saudita", "Argélia", "Argentina", "Armênia", "Austrália", "Áustria",
|
|
8
|
+
"Azerbaijão", "Bahamas", "Bahrein", "Bangladesh", "Barbados", "Bélgica", "Belize", "Benim",
|
|
9
|
+
"Bielorrússia", "Bolívia", "Bósnia e Herzegovina", "Botsuana", "Brasil", "Brunei", "Bulgária",
|
|
10
|
+
"Burquina Fasso", "Burundi", "Butão", "Cabo Verde", "Camarões", "Camboja", "Canadá", "Catar",
|
|
11
|
+
"Cazaquistão", "Chade", "Chile", "China", "Chipre", "Chipre do Norte", "Cingapura", "Colômbia",
|
|
12
|
+
"Comores", "Congo", "Coreia do Norte", "Coreia do Sul", "Costa do Marfim", "Costa Rica", "Croácia",
|
|
13
|
+
"Cuba", "Dinamarca", "Djibouti", "Dominica", "República Dominicana", "Egito", "El Salvador",
|
|
14
|
+
"Emirados Árabes Unidos", "Equador", "Eritreia", "Eslováquia", "Eslovênia", "Espanha",
|
|
15
|
+
"Estados Federados da Micronésia", "Estados Unidos", "Estônia", "Essuatíni (Suazilândia)", "Etiópia",
|
|
16
|
+
"Fiji", "Filipinas", "Finlândia", "França", "Gabão", "Gâmbia", "Gana", "Geórgia", "Granada",
|
|
17
|
+
"Grécia", "Guatemala", "Guiana", "Guiné", "Guiné-Bissau", "Guiné Equatorial", "Haiti", "Honduras",
|
|
18
|
+
"Hungria", "Iêmen", "Ilhas Cook", "Índia", "Indonésia", "Irã", "Irlanda", "Islândia", "Israel",
|
|
19
|
+
"Itália", "Jamaica", "Japão", "Jordânia", "Kiribati", "Kosovo", "Kuwait", "Laos", "Lesoto",
|
|
20
|
+
"Letônia", "Líbano", "Libéria", "Líbia", "Liechtenstein", "Lituânia", "Luxemburgo", "Macedônia do Norte",
|
|
21
|
+
"Madagascar", "Malásia", "Malawi", "Maldivas", "Mali", "Malta", "Marrocos", "Ilhas Marshall",
|
|
22
|
+
"Maurício", "Mauritânia", "México", "Mianmar", "Moçambique", "Moldávia", "Mônaco", "Mongólia",
|
|
23
|
+
"Montenegro", "Namíbia", "Nauru", "Nepal", "Nicarágua", "Níger", "Nigéria", "Niue", "Noruega",
|
|
24
|
+
"Nova Zelândia", "Omã", "Ossétia do Sul", "Países Baixos", "Palau", "Palestina", "Panamá",
|
|
25
|
+
"Papua-Nova Guiné", "Paquistão", "Paraguai", "Peru", "Polônia", "Portugal", "Quênia", "Quirguistão",
|
|
26
|
+
"Reino Unido", "República Centro-Africana", "República Checa", "República Democrática do Congo", "Romênia",
|
|
27
|
+
"Ruanda", "Rússia", "Saara Ocidental (República Árabe Saharaui Democrática)", "Samoa", "San Marino",
|
|
28
|
+
"Santa Lúcia", "São Cristóvão e Nevis", "São Tomé e Príncipe", "São Vicente e Granadinas", "Senegal",
|
|
29
|
+
"Serra Leoa", "Sérvia", "Seicheles", "Somália", "Somalilândia", "Sri Lanka", "Sudão", "Sudão do Sul",
|
|
30
|
+
"Suécia", "Suíça", "Suriname", "Síria", "Tajiquistão", "Tailândia", "Taiwan", "Tanzânia",
|
|
31
|
+
"Timor-Leste", "Togo", "Tonga", "Transnístria", "Trinidad e Tobago", "Tunísia", "Turcomenistão",
|
|
32
|
+
"Turquia", "Tuvalu", "Ucrânia", "Uganda", "Uruguai", "Uzbequistão", "Vanuatu", "Vaticano (Santa Sé)",
|
|
33
|
+
"Venezuela", "Vietnã", "Zâmbia", "Zimbábue",
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def country_index(value: object) -> int:
|
|
38
|
+
"""Return the selector index while keeping legacy/empty values usable."""
|
|
39
|
+
value = str(value or "").strip()
|
|
40
|
+
return COUNTRY_OPTIONS.index(value) if value in COUNTRY_OPTIONS else 0
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
__all__ = ["COUNTRY_OPTIONS", "country_index"]
|
|
@@ -117,8 +117,10 @@ 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 = _direct_profile_metric(user, 'edge_followed_by', 'followers', 'follower_count', 'followerCount')
|
|
121
|
+
following = _direct_profile_metric(user, 'edge_follow', 'follows', 'following', 'following_count', 'followingCount')
|
|
122
|
+
followers = followers if followers is not None else _structured_metric_from_json(json.dumps(user), 'edge_followed_by', 'followers', 'follower_count', 'followerCount')
|
|
123
|
+
following = following if following is not None else _structured_metric_from_json(json.dumps(user), 'edge_follow', 'follows', 'following', 'following_count', 'followingCount')
|
|
122
124
|
media = ((user.get('edge_owner_to_timeline_media') or {}).get('count') if isinstance(user.get('edge_owner_to_timeline_media'), dict) else None)
|
|
123
125
|
return {
|
|
124
126
|
'id': f"instagram_{reference['username']}",
|
|
@@ -136,6 +138,21 @@ def _profile_data_from_api(user: Mapping[str, Any], reference: Mapping[str, str]
|
|
|
136
138
|
}
|
|
137
139
|
|
|
138
140
|
|
|
141
|
+
def _direct_profile_metric(user: Mapping[str, Any], *keys: str) -> int | None:
|
|
142
|
+
"""Read common profile counters before falling back to recursive JSON parsing."""
|
|
143
|
+
for key in keys:
|
|
144
|
+
value = user.get(key)
|
|
145
|
+
if isinstance(value, Mapping):
|
|
146
|
+
value = value.get('count') or value.get('value')
|
|
147
|
+
if value in (None, ''):
|
|
148
|
+
continue
|
|
149
|
+
try:
|
|
150
|
+
return int(value)
|
|
151
|
+
except (TypeError, ValueError):
|
|
152
|
+
continue
|
|
153
|
+
return None
|
|
154
|
+
|
|
155
|
+
|
|
139
156
|
def fetch_public_instagram_profile(source: str) -> IntegrationResult:
|
|
140
157
|
try:
|
|
141
158
|
reference = normalize_instagram_reference(source)
|
package/package.json
CHANGED