ownapi 1.0.0__py3-none-any.whl
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.
- cli.py +1017 -0
- compiler.py +948 -0
- core/__init__.py +19 -0
- core/api_keys.py +523 -0
- core/auto_repair.py +682 -0
- core/base_client.py +165 -0
- core/consent.py +169 -0
- core/form_automation.py +1044 -0
- core/hitl_queue.py +886 -0
- core/onboarding.py +1457 -0
- core/token_store.py +223 -0
- healer.py +2074 -0
- master_mcp.py +193 -0
- ownapi/__init__.py +94 -0
- ownapi/cli.py +309 -0
- ownapi/sdk.py +436 -0
- ownapi-1.0.0.dist-info/METADATA +287 -0
- ownapi-1.0.0.dist-info/RECORD +62 -0
- ownapi-1.0.0.dist-info/WHEEL +5 -0
- ownapi-1.0.0.dist-info/entry_points.txt +4 -0
- ownapi-1.0.0.dist-info/top_level.txt +10 -0
- recorder.py +486 -0
- registry/coingecko/__init__.py +5 -0
- registry/coingecko/client.py +84 -0
- registry/coingecko/schema.json +23 -0
- registry/edevlet/__init__.py +5 -0
- registry/edevlet/client.py +54 -0
- registry/edevlet/schema.json +14 -0
- registry/enuygun/__init__.py +5 -0
- registry/enuygun/client.py +78 -0
- registry/enuygun/schema.json +13 -0
- registry/github_trending/__init__.py +5 -0
- registry/github_trending/client.py +90 -0
- registry/github_trending/schema.json +31 -0
- registry/hackernews/__init__.py +5 -0
- registry/hackernews/client.py +90 -0
- registry/hackernews/schema.json +38 -0
- registry/hepsiburada/__init__.py +5 -0
- registry/hepsiburada/client.py +32 -0
- registry/hepsiburada/schema.json +23 -0
- registry/linkedin_jobs/__init__.py +5 -0
- registry/linkedin_jobs/client.py +241 -0
- registry/linkedin_jobs/schema.json +46 -0
- registry/pegasus/__init__.py +5 -0
- registry/pegasus/client.py +141 -0
- registry/pegasus/schema.json +76 -0
- registry/thy/__init__.py +5 -0
- registry/thy/client.py +1796 -0
- registry/thy/schema.json +46 -0
- registry/trendyol/__init__.py +5 -0
- registry/trendyol/client.py +228 -0
- registry/trendyol/schema.json +30 -0
- registry/ucak_bileti/client.py +94 -0
- registry/ucak_bileti/schema.json +47 -0
- registry/wikipedia/__init__.py +5 -0
- registry/wikipedia/client.py +64 -0
- registry/wikipedia/schema.json +31 -0
- registry/yahoo_finance/__init__.py +5 -0
- registry/yahoo_finance/client.py +98 -0
- registry/yahoo_finance/schema.json +22 -0
- server.py +891 -0
- static/index.html +2401 -0
core/token_store.py
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import threading
|
|
5
|
+
from datetime import datetime, timezone
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class TokenStore:
|
|
11
|
+
"""
|
|
12
|
+
Çok kiracılı (multi-tenant) oturum ve çerez izolasyonunu sağlayan merkezi token deposu.
|
|
13
|
+
Her kiracı (tenant_id) ve servis (service) için oturum çerezlerini izole olarak
|
|
14
|
+
`.sessions/{tenant_id}/{service}_tokens.json` dosya yolunda saklar.
|
|
15
|
+
Geriye dönük uyumluluk için kök dizindeki `session_tokens.json` dosyasını otomatik okur ve günceller.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(self, sessions_dir: Path | str = Path(".sessions"), legacy_tokens_path: Path | str = Path("session_tokens.json")) -> None:
|
|
19
|
+
self.sessions_dir = Path(sessions_dir)
|
|
20
|
+
self.legacy_tokens_path = Path(legacy_tokens_path)
|
|
21
|
+
self._lock = threading.Lock()
|
|
22
|
+
|
|
23
|
+
def get_session_path(self, tenant_id: str = "default", service: str = "thy") -> Path:
|
|
24
|
+
"""Belirtilen kiracı ve servis için özel session_tokens dosya yolunu döndürür."""
|
|
25
|
+
clean_tenant = str(tenant_id).strip().replace("/", "_").replace("\\", "_") or "default"
|
|
26
|
+
clean_service = str(service).strip().lower().replace("/", "_").replace("\\", "_") or "default"
|
|
27
|
+
tenant_dir = self.sessions_dir / clean_tenant
|
|
28
|
+
return tenant_dir / f"{clean_service}_tokens.json"
|
|
29
|
+
|
|
30
|
+
def get_session(self, tenant_id: str = "default", service: str = "thy") -> dict[str, Any]:
|
|
31
|
+
"""
|
|
32
|
+
Kiracı ve servise ait güncel oturum demetini (cookie, user-agent, headers vb.) döndürür.
|
|
33
|
+
Eğer tenant_id='default' ve henüz izole dosya oluşmamışsa, geriye dönük uyumluluk
|
|
34
|
+
olarak kök dizindeki `session_tokens.json` dosyasını okur.
|
|
35
|
+
"""
|
|
36
|
+
with self._lock:
|
|
37
|
+
session_path = self.get_session_path(tenant_id, service)
|
|
38
|
+
if session_path.exists():
|
|
39
|
+
try:
|
|
40
|
+
data = json.loads(session_path.read_text(encoding="utf-8"))
|
|
41
|
+
if isinstance(data, dict):
|
|
42
|
+
return data
|
|
43
|
+
except Exception:
|
|
44
|
+
pass
|
|
45
|
+
|
|
46
|
+
# Geriye dönük uyumluluk: default tenant için legacy session_tokens.json kontrolü
|
|
47
|
+
if tenant_id == "default" and self.legacy_tokens_path.exists():
|
|
48
|
+
try:
|
|
49
|
+
data = json.loads(self.legacy_tokens_path.read_text(encoding="utf-8"))
|
|
50
|
+
if isinstance(data, dict):
|
|
51
|
+
return data
|
|
52
|
+
except Exception:
|
|
53
|
+
pass
|
|
54
|
+
|
|
55
|
+
return {}
|
|
56
|
+
|
|
57
|
+
def save_session(
|
|
58
|
+
self,
|
|
59
|
+
tenant_id: str = "default",
|
|
60
|
+
service: str = "thy",
|
|
61
|
+
cookies: dict[str, str] | list[dict[str, Any]] | str = "",
|
|
62
|
+
user_agent: str = "",
|
|
63
|
+
metadata: dict[str, Any] | None = None,
|
|
64
|
+
) -> Path:
|
|
65
|
+
"""
|
|
66
|
+
Kiracı ve servise ait güncel oturumu atomik ve thread-safe olarak izole dosyaya kaydeder.
|
|
67
|
+
cookies parametresi:
|
|
68
|
+
- dict: {"_px3": "abc", "bm_sz": "123"}
|
|
69
|
+
- list: Playwright context.cookies() formatı ([{"name": "_px3", "value": "abc"}, ...])
|
|
70
|
+
- str: "_px3=abc; bm_sz=123"
|
|
71
|
+
"""
|
|
72
|
+
cookie_str = ""
|
|
73
|
+
cookie_dict: dict[str, str] = {}
|
|
74
|
+
playwright_cookies: list[dict[str, Any]] = []
|
|
75
|
+
|
|
76
|
+
if isinstance(cookies, dict):
|
|
77
|
+
cookie_dict = {str(k).strip(): str(v).strip() for k, v in cookies.items() if k}
|
|
78
|
+
cookie_str = "; ".join(f"{k}={v}" for k, v in cookie_dict.items())
|
|
79
|
+
elif isinstance(cookies, list):
|
|
80
|
+
# Playwright cookies listesi
|
|
81
|
+
clean_parts = []
|
|
82
|
+
for c in cookies:
|
|
83
|
+
if isinstance(c, dict) and c.get("name"):
|
|
84
|
+
k, v = str(c["name"]).strip(), str(c.get("value", "")).strip()
|
|
85
|
+
cookie_dict[k] = v
|
|
86
|
+
clean_parts.append(f"{k}={v}")
|
|
87
|
+
playwright_cookies.append(c)
|
|
88
|
+
cookie_str = "; ".join(clean_parts)
|
|
89
|
+
elif isinstance(cookies, str):
|
|
90
|
+
cookie_str = cookies.strip()
|
|
91
|
+
for part in cookie_str.split(";"):
|
|
92
|
+
if "=" in part:
|
|
93
|
+
k, v = part.split("=", 1)
|
|
94
|
+
k_clean, v_clean = k.strip(), v.strip()
|
|
95
|
+
if k_clean:
|
|
96
|
+
cookie_dict[k_clean] = v_clean
|
|
97
|
+
|
|
98
|
+
meta = dict(metadata or {})
|
|
99
|
+
target_url = meta.get("target_url") or ""
|
|
100
|
+
|
|
101
|
+
token_bundle: dict[str, Any] = {
|
|
102
|
+
"tenant_id": tenant_id,
|
|
103
|
+
"service": service,
|
|
104
|
+
"saved_at": datetime.now(timezone.utc).isoformat(),
|
|
105
|
+
"target_url": target_url,
|
|
106
|
+
"cookie": cookie_str,
|
|
107
|
+
"cookie_dict": cookie_dict,
|
|
108
|
+
"playwright_cookies": playwright_cookies,
|
|
109
|
+
"user-agent": user_agent or meta.get("user-agent", ""),
|
|
110
|
+
"headers": {
|
|
111
|
+
"cookie": cookie_str,
|
|
112
|
+
"user-agent": user_agent or meta.get("user-agent", ""),
|
|
113
|
+
"accept": "application/json",
|
|
114
|
+
"content-type": "application/json",
|
|
115
|
+
},
|
|
116
|
+
"metadata": meta,
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
# Mevcut ekstra başlıkları koru
|
|
120
|
+
if "headers" in meta and isinstance(meta["headers"], dict):
|
|
121
|
+
token_bundle["headers"].update(meta["headers"])
|
|
122
|
+
if cookie_str:
|
|
123
|
+
token_bundle["headers"]["cookie"] = cookie_str
|
|
124
|
+
if user_agent:
|
|
125
|
+
token_bundle["headers"]["user-agent"] = user_agent
|
|
126
|
+
|
|
127
|
+
with self._lock:
|
|
128
|
+
session_path = self.get_session_path(tenant_id, service)
|
|
129
|
+
session_path.parent.mkdir(parents=True, exist_ok=True)
|
|
130
|
+
session_path.write_text(json.dumps(token_bundle, ensure_ascii=False, indent=2), encoding="utf-8")
|
|
131
|
+
|
|
132
|
+
# Geriye dönük uyumluluk: default kiracı ise kök dizindeki session_tokens.json'ı da güncelle
|
|
133
|
+
if tenant_id == "default":
|
|
134
|
+
try:
|
|
135
|
+
self.legacy_tokens_path.write_text(json.dumps(token_bundle, ensure_ascii=False, indent=2), encoding="utf-8")
|
|
136
|
+
except Exception:
|
|
137
|
+
pass
|
|
138
|
+
|
|
139
|
+
return session_path
|
|
140
|
+
|
|
141
|
+
def clear_session(self, tenant_id: str = "default", service: str = "thy") -> bool:
|
|
142
|
+
"""Belirtilen kiracı ve servisin oturum dosyasını güvenle temizler."""
|
|
143
|
+
with self._lock:
|
|
144
|
+
session_path = self.get_session_path(tenant_id, service)
|
|
145
|
+
if session_path.exists():
|
|
146
|
+
try:
|
|
147
|
+
session_path.unlink()
|
|
148
|
+
return True
|
|
149
|
+
except Exception:
|
|
150
|
+
return False
|
|
151
|
+
return False
|
|
152
|
+
|
|
153
|
+
def list_tenants(self) -> list[str]:
|
|
154
|
+
"""Kayıtlı tüm kiracıları listeler."""
|
|
155
|
+
if not self.sessions_dir.exists():
|
|
156
|
+
return ["default"]
|
|
157
|
+
tenants = [p.name for p in self.sessions_dir.iterdir() if p.is_dir()]
|
|
158
|
+
if "default" not in tenants:
|
|
159
|
+
tenants.insert(0, "default")
|
|
160
|
+
return sorted(tenants)
|
|
161
|
+
|
|
162
|
+
def list_sessions(self, tenant_id: str = "default") -> list[str]:
|
|
163
|
+
"""Belirtilen kiracıya ait kayıtlı servis isimlerini listeler."""
|
|
164
|
+
clean_tenant = str(tenant_id).strip() or "default"
|
|
165
|
+
tenant_dir = self.sessions_dir / clean_tenant
|
|
166
|
+
if not tenant_dir.exists():
|
|
167
|
+
return []
|
|
168
|
+
services = []
|
|
169
|
+
for p in tenant_dir.glob("*_tokens.json"):
|
|
170
|
+
svc = p.name.replace("_tokens.json", "")
|
|
171
|
+
services.append(svc)
|
|
172
|
+
return sorted(services)
|
|
173
|
+
|
|
174
|
+
def get_playwright_cookies(
|
|
175
|
+
self,
|
|
176
|
+
tenant_id: str = "default",
|
|
177
|
+
service: str = "thy",
|
|
178
|
+
default_url: str = "",
|
|
179
|
+
) -> list[dict[str, Any]]:
|
|
180
|
+
"""
|
|
181
|
+
Kayıtlı oturumdaki çerezleri Playwright context.add_cookies() formatına dönüştürür.
|
|
182
|
+
"""
|
|
183
|
+
session = self.get_session(tenant_id=tenant_id, service=service)
|
|
184
|
+
if not session:
|
|
185
|
+
return []
|
|
186
|
+
|
|
187
|
+
pw_cookies = session.get("playwright_cookies")
|
|
188
|
+
if isinstance(pw_cookies, list) and pw_cookies:
|
|
189
|
+
valid = []
|
|
190
|
+
for c in pw_cookies:
|
|
191
|
+
if isinstance(c, dict) and c.get("name") and c.get("value") is not None:
|
|
192
|
+
if c.get("domain") or c.get("url"):
|
|
193
|
+
valid.append(c)
|
|
194
|
+
elif default_url:
|
|
195
|
+
c_copy = dict(c)
|
|
196
|
+
c_copy["url"] = default_url
|
|
197
|
+
valid.append(c_copy)
|
|
198
|
+
if valid:
|
|
199
|
+
return valid
|
|
200
|
+
|
|
201
|
+
target_url = default_url or session.get("target_url") or ""
|
|
202
|
+
if not target_url:
|
|
203
|
+
return []
|
|
204
|
+
|
|
205
|
+
cookie_dict = session.get("cookie_dict")
|
|
206
|
+
if not isinstance(cookie_dict, dict) or not cookie_dict:
|
|
207
|
+
raw_cookie = session.get("cookie", "")
|
|
208
|
+
cookie_dict = {}
|
|
209
|
+
for part in str(raw_cookie).split(";"):
|
|
210
|
+
if "=" in part:
|
|
211
|
+
k, v = part.split("=", 1)
|
|
212
|
+
if k.strip():
|
|
213
|
+
cookie_dict[k.strip()] = v.strip()
|
|
214
|
+
|
|
215
|
+
result = []
|
|
216
|
+
for k, v in cookie_dict.items():
|
|
217
|
+
if k and v is not None:
|
|
218
|
+
result.append({
|
|
219
|
+
"name": str(k).strip(),
|
|
220
|
+
"value": str(v).strip(),
|
|
221
|
+
"url": target_url,
|
|
222
|
+
})
|
|
223
|
+
return result
|