@danhachuel/thunderbolt 0.6.21 → 0.6.22

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.
@@ -12,7 +12,7 @@ from hermes_ui.domain import create_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
14
  from hermes_ui.languages import LANGUAGE_CODES, language_code, language_label
15
- from integrations.instagram_public import fetch_public_instagram_posts, fetch_public_instagram_profile
15
+ from integrations.instagram_public import fetch_public_instagram_posts, fetch_public_instagram_profile, normalize_instagram_bio
16
16
  from integrations.meta_social import test_facebook_pages_api_card, test_instagram_api_card
17
17
 
18
18
 
@@ -298,7 +298,7 @@ def _render_instagram_card(profile: dict[str, Any], characters: list[dict[str, A
298
298
  with header_cols[1]:
299
299
  st.write(f"**{_clean(profile.get('name')) or 'Sem nome'}**")
300
300
  st.caption(f"{_clean(profile.get('handle')) or _clean(profile.get('url')) or 'sem handler'}")
301
- bio = _clean(profile.get("bio"))
301
+ bio = normalize_instagram_bio(profile.get("bio"))
302
302
  if bio:
303
303
  st.caption(f"Bio: {bio}")
304
304
  st.caption(f"{_clean(profile.get('country')) or 'País não definido'} · {_clean(profile.get('language')) or 'Idioma não definido'}")
@@ -18,6 +18,16 @@ class IntegrationResult:
18
18
  data: dict[str, Any]
19
19
 
20
20
 
21
+ def normalize_instagram_bio(value: Any) -> str:
22
+ """Keep the profile biography, never Instagram's metrics summary."""
23
+ bio = str(value or '').strip()
24
+ if not bio:
25
+ return ''
26
+ if re.search(r'\b(?:followers|following|seguidores|seguindo|posts|publicações)\b', bio, flags=re.IGNORECASE) and re.search(r'\d', bio):
27
+ return ''
28
+ return bio
29
+
30
+
21
31
  def normalize_instagram_reference(source: str) -> dict[str, str]:
22
32
  value = str(source or '').strip()
23
33
  if not value:
@@ -114,7 +124,7 @@ def _profile_data_from_api(user: Mapping[str, Any], reference: Mapping[str, str]
114
124
  'id': f"instagram_{reference['username']}",
115
125
  **reference,
116
126
  'name': str(user.get('full_name') or user.get('username') or reference['username']).strip(),
117
- 'bio': str(user.get('biography') or '').strip(),
127
+ 'bio': normalize_instagram_bio(user.get('biography')),
118
128
  'avatar_url': str(user.get('profile_pic_url_hd') or user.get('profile_pic_url') or '').strip(),
119
129
  'subscriber_count': followers,
120
130
  'following_count': following,
@@ -137,6 +147,9 @@ def fetch_public_instagram_profile(source: str) -> IntegrationResult:
137
147
  return IntegrationResult(False, f'Não foi possível consultar o perfil público do Instagram: {exc}', reference)
138
148
  if response.status_code >= 400:
139
149
  return IntegrationResult(False, f'O Instagram devolveu HTTP {response.status_code}. Confirme o @handle ou use o cadastro manual.', reference | {'status_code': response.status_code})
150
+ api_user = _fetch_web_profile_user(reference['username'])
151
+ if api_user:
152
+ return IntegrationResult(True, 'Perfil Instagram encontrado publicamente.', _profile_data_from_api(api_user, reference))
140
153
  title = _meta(response.text, 'og:title') or reference['username']
141
154
  description = _meta(response.text, 'og:description')
142
155
  avatar_url = _meta(response.text, 'og:image')
@@ -154,11 +167,7 @@ def fetch_public_instagram_profile(source: str) -> IntegrationResult:
154
167
  followers = metric((r'([\d,.]+)\s*(?:mil\s+)?(?:followers|seguidores)',)) or _structured_metric(response.text, 'edge_followed_by', 'followers', 'follower_count') or _structured_metric_from_json(response.text, 'edge_followed_by', 'followers', 'follower_count', 'followerCount')
155
168
  following = metric((r'([\d,.]+)\s*(?:mil\s+)?(?:following|seguindo)',)) or _structured_metric(response.text, 'edge_follow', 'follows', 'following', 'following_count', 'followingCount') or _structured_metric_from_json(response.text, 'edge_follow', 'follows', 'following', 'following_count', 'followingCount')
156
169
  posts = metric((r'([\d,.]+)\s*(?:mil\s+)?(?:posts|publicações|publications)',)) or _structured_metric(response.text, 'edge_owner_to_timeline_media', 'posts', 'post_count')
157
- if followers is None or following is None or not avatar_url:
158
- api_user = _fetch_web_profile_user(reference['username'])
159
- if api_user:
160
- return IntegrationResult(True, 'Perfil Instagram encontrado publicamente.', _profile_data_from_api(api_user, reference))
161
- 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()}
170
+ data = {'id': f"instagram_{reference['username']}", **reference, 'name': title.split('(')[0].strip() or reference['username'], 'bio': normalize_instagram_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()}
162
171
  return IntegrationResult(True, 'Perfil Instagram encontrado publicamente.', data)
163
172
 
164
173
 
@@ -257,4 +266,4 @@ def fetch_public_instagram_posts(source: str, limit: int = 10) -> IntegrationRes
257
266
  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))]})
258
267
 
259
268
 
260
- __all__ = ['IntegrationResult', 'fetch_public_instagram_posts', 'fetch_public_instagram_profile', 'normalize_instagram_reference']
269
+ __all__ = ['IntegrationResult', 'fetch_public_instagram_posts', 'fetch_public_instagram_profile', 'normalize_instagram_bio', 'normalize_instagram_reference']
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danhachuel/thunderbolt",
3
- "version": "0.6.21",
3
+ "version": "0.6.22",
4
4
  "description": "Thunderbolt — interface local para operação de canais faceless e motor MoneyPrinterTurbo",
5
5
  "license": "MIT",
6
6
  "main": "scripts/cli.mjs",