triangle-api 0.1.5__tar.gz → 0.1.6__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: triangle-api
3
- Version: 0.1.5
3
+ Version: 0.1.6
4
4
  Summary: Unofficial Python wrapper for Canadian Tire Triangle Mastercard API
5
5
  Home-page: https://github.com/diogobas/triangle-api
6
6
  Author: Diogo Bastos
@@ -5,7 +5,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
5
5
 
6
6
  setup(
7
7
  name="triangle-api", # Nome que aparecerá no PyPI
8
- version="0.1.5",
8
+ version="0.1.6",
9
9
  author="Diogo Bastos",
10
10
  author_email="seu-email@exemplo.com", # Opcional
11
11
  description="Unofficial Python wrapper for Canadian Tire Triangle Mastercard API",
@@ -0,0 +1,4 @@
1
+ from .client import TriangleClient
2
+
3
+ __version__ = "0.1.6"
4
+ __all__ = ["TriangleClient"]
@@ -13,12 +13,10 @@ class TriangleClient:
13
13
  self.account_data = None
14
14
 
15
15
  def _get_csrf(self, context):
16
- """Auxiliar para extrair o token CSRF dos cookies."""
17
16
  cookies = context.cookies()
18
17
  return next((c['value'] for c in cookies if c['name'] == 'csrftoken'), "")
19
18
 
20
19
  def _fetch_account_data(self, page, csrf_token):
21
- """Faz a chamada manual do retrieveAccount com CSRF."""
22
20
  js_get_account = """
23
21
  async (csrf) => {
24
22
  const r = await fetch('/dash/v1/account/retrieveAccount', {
@@ -33,13 +31,17 @@ class TriangleClient:
33
31
  return r.ok ? await r.json() : null;
34
32
  }
35
33
  """
36
- return page.evaluate(js_get_account, csrf_token)
34
+ try:
35
+ return page.evaluate(js_get_account, csrf_token)
36
+ except:
37
+ return None
37
38
 
38
39
  def login(self, headless=True):
39
40
  with sync_playwright() as p:
40
41
  browser = p.chromium.launch(headless=headless)
41
- has_session = os.path.exists(self.session_path)
42
- storage = self.session_path if has_session else None
42
+
43
+ # SEGURANÇA: carrega se o arquivo existir
44
+ storage = self.session_path if os.path.exists(self.session_path) else None
43
45
 
44
46
  context = browser.new_context(
45
47
  viewport={'width': 1280, 'height': 800},
@@ -51,15 +53,13 @@ class TriangleClient:
51
53
  print("[API] Acessando o portal...")
52
54
  page.goto("https://www.ctfs.com/content/dash/en/private/Summary.html", timeout=60000)
53
55
 
54
- # Se cair no login, faz o processo de autenticação
55
56
  if "login" in page.url.lower():
56
57
  if not self.username or not self.password:
57
58
  browser.close()
58
59
  return "NEEDS_CREDENTIALS"
59
60
 
60
- print("[API] Sessão expirada. Autenticando...")
61
+ print("[API] Sessão expirada ou inexistente. Autenticando...")
61
62
  try:
62
- # Lida com cookies
63
63
  if page.is_visible("#onetrust-accept-btn-handler"):
64
64
  page.click("#onetrust-accept-btn-handler", timeout=5000)
65
65
 
@@ -69,7 +69,6 @@ class TriangleClient:
69
69
  page.focus("#password")
70
70
  page.keyboard.press("Enter")
71
71
 
72
- # Espera sair do login
73
72
  page.wait_for_function("() => !window.location.href.includes('login')", timeout=45000)
74
73
 
75
74
  if "challenge" in page.url or "otp" in page.url:
@@ -82,7 +81,6 @@ class TriangleClient:
82
81
  browser.close()
83
82
  return None
84
83
 
85
- # CAPTURA DE DADOS (Saldo)
86
84
  print("[API] Capturando dados financeiros...")
87
85
  try:
88
86
  page.wait_for_load_state("domcontentloaded")
@@ -91,7 +89,7 @@ class TriangleClient:
91
89
 
92
90
  if self.account_data:
93
91
  context.storage_state(path=self.session_path)
94
- print("[API] Dados capturados com sucesso.")
92
+ print("[API] Dados capturados e sessão salva.")
95
93
  except Exception as e:
96
94
  print(f"[AVISO] Erro na captura de saldo: {e}")
97
95
 
@@ -99,13 +97,15 @@ class TriangleClient:
99
97
  return self.account_data
100
98
 
101
99
  def get_transactions(self, start_date=None, headless=True):
102
- today = datetime.today()
103
100
  if not start_date:
104
- start_date = today.replace(day=1).strftime('%Y-%m-%d')
101
+ start_date = datetime.today().replace(day=1).strftime('%Y-%m-%d')
105
102
 
106
103
  with sync_playwright() as p:
107
104
  browser = p.chromium.launch(headless=headless)
108
- context = browser.new_context(storage_state=self.session_path)
105
+
106
+ # SEGURANÇA: Só carrega se o arquivo existir
107
+ storage = self.session_path if os.path.exists(self.session_path) else None
108
+ context = browser.new_context(storage_state=storage)
109
109
  page = context.new_page()
110
110
 
111
111
  print("[API] Sincronizando para transações...")
@@ -113,11 +113,11 @@ class TriangleClient:
113
113
 
114
114
  csrf = self._get_csrf(context)
115
115
 
116
- # Se o account_data sumiu da memória, pegamos de novo agora que temos o CSRF
117
116
  if not self.account_data:
118
117
  self.account_data = self._fetch_account_data(page, csrf)
119
118
 
120
119
  if not self.account_data:
120
+ print("[ERRO] Não foi possível obter dados da conta para transações.")
121
121
  browser.close()
122
122
  return []
123
123
 
@@ -147,7 +147,6 @@ class TriangleClient:
147
147
  })
148
148
  browser.close()
149
149
 
150
- # Filtro e Deduplicação
151
150
  unique_txs = {}
152
151
  for t in raw_txs:
153
152
  d = t.get("tranDate", "")
@@ -167,7 +166,7 @@ class TriangleClient:
167
166
  "external_id": ext_id, "status": "cleared" if t.get("category") in ["POSTED", "STATEMENTED"] else "uncleared",
168
167
  "notes": f"Type: {t.get('type')} | Imported via Triangle API"
169
168
  })
170
- return sorted(normalized, key=lambda x: x["date"], reverse=True)
169
+ return sorted(normalized, key=lambda x: x.get("date", ""), reverse=True)
171
170
 
172
171
  def get_balance(self):
173
172
  return self.account_data.get("currentBalanceAmt", 0.0) if self.account_data else "N/A"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: triangle-api
3
- Version: 0.1.5
3
+ Version: 0.1.6
4
4
  Summary: Unofficial Python wrapper for Canadian Tire Triangle Mastercard API
5
5
  Home-page: https://github.com/diogobas/triangle-api
6
6
  Author: Diogo Bastos
@@ -1,4 +0,0 @@
1
- from .client import TriangleClient
2
-
3
- __version__ = "0.1.5"
4
- __all__ = ["TriangleClient"]
File without changes
File without changes