@danhachuel/thunderbolt 0.6.25 → 0.6.27

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.
@@ -42,6 +42,12 @@ def _metric_input(value: Any) -> int | None:
42
42
  return normalize_instagram_metric(value)
43
43
 
44
44
 
45
+ def _render_instagram_bio(value: Any) -> None:
46
+ bio = normalize_instagram_bio(value)
47
+ if bio:
48
+ st.markdown(bio.replace("\n", " \n"))
49
+
50
+
45
51
  def _language_index(value: Any) -> int:
46
52
  code = language_code(value)
47
53
  return list(LANGUAGE_CODES).index(code) if code in LANGUAGE_CODES else list(LANGUAGE_CODES).index("pt")
@@ -326,9 +332,7 @@ def _render_instagram_card(profile: dict[str, Any], characters: list[dict[str, A
326
332
  with header_cols[1]:
327
333
  st.write(f"**{_clean(profile.get('name')) or 'Sem nome'}**")
328
334
  st.caption(f"{_clean(profile.get('handle')) or _clean(profile.get('url')) or 'sem handler'}")
329
- bio = normalize_instagram_bio(profile.get("bio"))
330
- if bio:
331
- st.caption(f"Bio: {bio}")
335
+ _render_instagram_bio(profile.get("bio"))
332
336
  st.caption(f"{_clean(profile.get('country')) or 'País não definido'} · {_clean(profile.get('language')) or 'Idioma não definido'}")
333
337
  with header_cols[2]:
334
338
  st.metric("posts", _metric(profile.get("post_count")))
@@ -447,9 +451,7 @@ def render_social_networks(settings: dict[str, Any]) -> None:
447
451
  with preview_cols[1]:
448
452
  st.write(f"**{_clean(data.get('name')) or _clean(data.get('username'))}**")
449
453
  st.caption(_clean(data.get("handle")))
450
- bio = normalize_instagram_bio(data.get("bio"))
451
- if bio:
452
- st.caption(f"Bio: {bio}")
454
+ _render_instagram_bio(data.get("bio"))
453
455
  with preview_cols[2]:
454
456
  st.metric("posts", _metric(data.get("post_count")))
455
457
  with preview_cols[3]:
@@ -111,6 +111,23 @@ def _fetch_web_profile_user(username: str) -> dict[str, Any] | None:
111
111
  f'https://www.instagram.com/api/v1/users/web_profile_info/?username={username}',
112
112
  f'https://i.instagram.com/api/v1/users/web_profile_info/?username={username}',
113
113
  )
114
+ curl = shutil.which('curl')
115
+ if curl:
116
+ for endpoint in endpoints:
117
+ try:
118
+ completed = subprocess.run(
119
+ [curl, '-L', '--max-time', '20', '-sS', '-A', headers['User-Agent'], '-H', f"x-ig-app-id: {headers['x-ig-app-id']}", '-H', 'Accept: application/json, text/plain, */*', endpoint],
120
+ capture_output=True,
121
+ text=True,
122
+ timeout=25,
123
+ check=False,
124
+ )
125
+ payload = json.loads(completed.stdout) if completed.returncode == 0 and completed.stdout else None
126
+ user = ((payload.get('data') or {}).get('user') if isinstance(payload, dict) else None)
127
+ if isinstance(user, dict):
128
+ return user
129
+ except (OSError, subprocess.SubprocessError, ValueError):
130
+ continue
114
131
  for endpoint in endpoints:
115
132
  try:
116
133
  response = requests.get(endpoint, headers=headers, timeout=15)
@@ -122,22 +139,6 @@ def _fetch_web_profile_user(username: str) -> dict[str, Any] | None:
122
139
  user = ((payload.get('data') or {}).get('user') if isinstance(payload, dict) else None)
123
140
  if isinstance(user, dict):
124
141
  return user
125
- curl = shutil.which('curl')
126
- if curl:
127
- try:
128
- completed = subprocess.run(
129
- [curl, '-L', '--max-time', '20', '-sS', '-A', headers['User-Agent'], '-H', f"x-ig-app-id: {headers['x-ig-app-id']}", endpoint],
130
- capture_output=True,
131
- text=True,
132
- timeout=25,
133
- check=False,
134
- )
135
- payload = json.loads(completed.stdout) if completed.returncode == 0 and completed.stdout else None
136
- user = ((payload.get('data') or {}).get('user') if isinstance(payload, dict) else None)
137
- if isinstance(user, dict):
138
- return user
139
- except (OSError, subprocess.SubprocessError, ValueError):
140
- pass
141
142
  return None
142
143
 
143
144
 
@@ -156,6 +157,7 @@ def _profile_data_from_api(user: Mapping[str, Any], reference: Mapping[str, str]
156
157
  'subscriber_count': followers,
157
158
  'following_count': following,
158
159
  'post_count': media,
160
+ 'is_private': bool(user.get('is_private')),
159
161
  'public_lookup': True,
160
162
  'metrics_source': 'instagram_web_profile_info',
161
163
  'last_public_lookup_at': datetime.now(timezone.utc).isoformat(),
@@ -289,6 +291,8 @@ def fetch_public_instagram_posts(source: str, limit: int = 10) -> IntegrationRes
289
291
  api_posts.append(post)
290
292
  if api_posts:
291
293
  return IntegrationResult(True, 'Posts públicos encontrados.', reference | {'posts': api_posts[:max(1, int(limit))]})
294
+ if api_user.get('is_private'):
295
+ return IntegrationResult(False, 'Esta conta é privada. O Instagram não disponibiliza posts sem uma sessão autenticada.', reference | {'posts': [], 'is_private': True})
292
296
  try:
293
297
  response = requests.get(reference['url'], headers={'User-Agent': 'Mozilla/5.0', 'Accept-Language': 'pt-BR,pt;q=0.9,en;q=0.8'}, timeout=15)
294
298
  except requests.RequestException as exc:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danhachuel/thunderbolt",
3
- "version": "0.6.25",
3
+ "version": "0.6.27",
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",