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.
Files changed (62) hide show
  1. cli.py +1017 -0
  2. compiler.py +948 -0
  3. core/__init__.py +19 -0
  4. core/api_keys.py +523 -0
  5. core/auto_repair.py +682 -0
  6. core/base_client.py +165 -0
  7. core/consent.py +169 -0
  8. core/form_automation.py +1044 -0
  9. core/hitl_queue.py +886 -0
  10. core/onboarding.py +1457 -0
  11. core/token_store.py +223 -0
  12. healer.py +2074 -0
  13. master_mcp.py +193 -0
  14. ownapi/__init__.py +94 -0
  15. ownapi/cli.py +309 -0
  16. ownapi/sdk.py +436 -0
  17. ownapi-1.0.0.dist-info/METADATA +287 -0
  18. ownapi-1.0.0.dist-info/RECORD +62 -0
  19. ownapi-1.0.0.dist-info/WHEEL +5 -0
  20. ownapi-1.0.0.dist-info/entry_points.txt +4 -0
  21. ownapi-1.0.0.dist-info/top_level.txt +10 -0
  22. recorder.py +486 -0
  23. registry/coingecko/__init__.py +5 -0
  24. registry/coingecko/client.py +84 -0
  25. registry/coingecko/schema.json +23 -0
  26. registry/edevlet/__init__.py +5 -0
  27. registry/edevlet/client.py +54 -0
  28. registry/edevlet/schema.json +14 -0
  29. registry/enuygun/__init__.py +5 -0
  30. registry/enuygun/client.py +78 -0
  31. registry/enuygun/schema.json +13 -0
  32. registry/github_trending/__init__.py +5 -0
  33. registry/github_trending/client.py +90 -0
  34. registry/github_trending/schema.json +31 -0
  35. registry/hackernews/__init__.py +5 -0
  36. registry/hackernews/client.py +90 -0
  37. registry/hackernews/schema.json +38 -0
  38. registry/hepsiburada/__init__.py +5 -0
  39. registry/hepsiburada/client.py +32 -0
  40. registry/hepsiburada/schema.json +23 -0
  41. registry/linkedin_jobs/__init__.py +5 -0
  42. registry/linkedin_jobs/client.py +241 -0
  43. registry/linkedin_jobs/schema.json +46 -0
  44. registry/pegasus/__init__.py +5 -0
  45. registry/pegasus/client.py +141 -0
  46. registry/pegasus/schema.json +76 -0
  47. registry/thy/__init__.py +5 -0
  48. registry/thy/client.py +1796 -0
  49. registry/thy/schema.json +46 -0
  50. registry/trendyol/__init__.py +5 -0
  51. registry/trendyol/client.py +228 -0
  52. registry/trendyol/schema.json +30 -0
  53. registry/ucak_bileti/client.py +94 -0
  54. registry/ucak_bileti/schema.json +47 -0
  55. registry/wikipedia/__init__.py +5 -0
  56. registry/wikipedia/client.py +64 -0
  57. registry/wikipedia/schema.json +31 -0
  58. registry/yahoo_finance/__init__.py +5 -0
  59. registry/yahoo_finance/client.py +98 -0
  60. registry/yahoo_finance/schema.json +22 -0
  61. server.py +891 -0
  62. static/index.html +2401 -0
@@ -0,0 +1,46 @@
1
+ {
2
+ "service_name": "thy",
3
+ "target_url": "https://www.turkishairlines.com/tr-tr/",
4
+ "goal": "İstanbul'dan Ankara'ya uçuş seferlerini ve fiyatlarını sorgula",
5
+ "function_name": "search_flights",
6
+ "explanation": "Kullanıcının hedefi İstanbul'dan Ankara'ya uçuş seferlerini ve fiyatlarını sorgulamak olduğundan, uçuş müsaitlik ve fiyat bilgilerini içeren '/api/v1/availability' uç noktası en uygun seçimdir. Bu uç nokta, uçuş bilgilerini almak için gerekli olan tüm parametreleri içermektedir.",
7
+ "method": "POST",
8
+ "endpoint_url": "https://www.turkishairlines.com/api/v1/availability",
9
+ "dynamic_params": [
10
+ {
11
+ "name": "departure_date",
12
+ "original_key": "departureDate",
13
+ "location": "body",
14
+ "param_type": "str",
15
+ "default_value": "02-03-2027",
16
+ "description": "Kalkış tarihi"
17
+ },
18
+ {
19
+ "name": "return_date",
20
+ "original_key": "departureDate",
21
+ "location": "body",
22
+ "param_type": "str",
23
+ "default_value": "09-03-2027",
24
+ "description": "Dönüş tarihi"
25
+ },
26
+ {
27
+ "name": "origin_airport_code",
28
+ "original_key": "originAirportCode",
29
+ "location": "body",
30
+ "param_type": "str",
31
+ "default_value": "IST",
32
+ "description": "Kalkış havaalanı kodu"
33
+ },
34
+ {
35
+ "name": "destination_airport_code",
36
+ "original_key": "destinationAirportCode",
37
+ "location": "body",
38
+ "param_type": "str",
39
+ "default_value": "ESB",
40
+ "description": "Varış havaalanı kodu"
41
+ }
42
+ ],
43
+ "created_at": "2026-11-01T16:00:17.309297+00:00",
44
+ "validation_status": "BAŞARILI",
45
+ "validation_passed_runs": 3
46
+ }
@@ -0,0 +1,5 @@
1
+ """OwnAPI Registry Module: trendyol."""
2
+
3
+ from .client import TrendyolClient, search_products
4
+
5
+ __all__ = ["search_products", "TrendyolClient"]
@@ -0,0 +1,228 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import logging
5
+ import pathlib
6
+ import sys
7
+ import time
8
+ from typing import Any
9
+ from urllib.parse import quote_plus, urlencode
10
+
11
+ if hasattr(sys.stdout, "reconfigure"):
12
+ try:
13
+ sys.stdout.reconfigure(encoding="utf-8", errors="replace")
14
+ sys.stderr.reconfigure(encoding="utf-8", errors="replace")
15
+ except Exception:
16
+ pass
17
+
18
+ sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[2]))
19
+ sys.path.insert(0, str(pathlib.Path(__file__).resolve().parent))
20
+
21
+ from core.base_client import BaseOwnClient
22
+ from core.token_store import TokenStore
23
+
24
+ logger = logging.getLogger("own_api.trendyol")
25
+
26
+
27
+ class TrendyolClient(BaseOwnClient):
28
+ """Trendyol e-ticaret servisi için OwnAPI User-Delegated Browser Middleware istemcisi."""
29
+
30
+ def __init__(
31
+ self,
32
+ tenant_id: str = "default",
33
+ token_store: TokenStore | None = None,
34
+ mode: str = "local",
35
+ ) -> None:
36
+ super().__init__(
37
+ service_name="trendyol",
38
+ base_url="https://www.trendyol.com",
39
+ tenant_id=tenant_id,
40
+ token_store=token_store,
41
+ mode=mode,
42
+ )
43
+
44
+ def search(
45
+ self,
46
+ query: str = "laptop",
47
+ max_results: int = 10,
48
+ ) -> dict[str, Any]:
49
+ """Trendyol üzerinde ürün araması yapar ve yapılandırılmış ürün listesini döndürür."""
50
+ clean_query = str(query).strip()
51
+ encoded_query = quote_plus(clean_query)
52
+ search_path = f"/sr?q={encoded_query}"
53
+ full_url = f"{self.base_url}{search_path}"
54
+
55
+ headers = {
56
+ "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
57
+ "accept-language": "tr-TR,tr;q=0.9,en-US;q=0.8,en;q=0.7",
58
+ "referer": "https://www.trendyol.com/",
59
+ }
60
+
61
+ # İsteği doğrudan BaseShadowClient (In-Browser Fetch) üzerinden gönder
62
+ raw = self.request(
63
+ method="GET",
64
+ url=full_url,
65
+ headers=headers,
66
+ )
67
+
68
+ # Yanıt HTML metnini al
69
+ html_text = ""
70
+ if isinstance(raw, dict):
71
+ html_text = raw.get("text", "")
72
+ if not html_text and "error" in raw and not raw.get("success", True):
73
+ return raw
74
+ elif isinstance(raw, str):
75
+ html_text = raw
76
+
77
+ parsed = self._extract_products_from_html(html_text, clean_query, max_results)
78
+ return parsed
79
+
80
+ @staticmethod
81
+ def _extract_products_from_html(html_text: str, query: str, max_results: int) -> dict[str, Any]:
82
+ """Trendyol SSR HTML içindeki __single-search-result__PROPS JSON bloğunu ayrıştırır."""
83
+ prefix = 'window["__single-search-result__PROPS"]='
84
+ idx = html_text.find(prefix)
85
+ if idx == -1:
86
+ prefix_alt = 'window.__SEARCH_APP_INITIAL_STATE__='
87
+ idx = html_text.find(prefix_alt)
88
+ prefix = prefix_alt
89
+
90
+ raw_products: list[dict[str, Any]] = []
91
+ if idx != -1:
92
+ sub = html_text[idx + len(prefix):]
93
+ end_script = sub.find("</script>")
94
+ if end_script != -1:
95
+ raw_json = sub[:end_script].rstrip(";").strip()
96
+ try:
97
+ data = json.loads(raw_json)
98
+ if isinstance(data, dict):
99
+ if "data" in data and isinstance(data["data"], dict):
100
+ raw_products = data["data"].get("products", [])
101
+ elif "products" in data:
102
+ raw_products = data["products"]
103
+ except Exception as e:
104
+ logger.warning(f"Trendyol JSON parse hatası: {e}")
105
+
106
+ formatted_products: list[dict[str, Any]] = []
107
+ for p in raw_products[:max_results]:
108
+ if not isinstance(p, dict):
109
+ continue
110
+
111
+ # Fiyat ayıklama
112
+ p_price = p.get("price")
113
+ discounted_val = None
114
+ original_val = None
115
+ currency = "TL"
116
+
117
+ if isinstance(p_price, dict):
118
+ discounted_val = p_price.get("discountedPrice") or p_price.get("current")
119
+ if isinstance(discounted_val, dict):
120
+ discounted_val = discounted_val.get("value")
121
+ original_val = p_price.get("originalPrice") or p_price.get("old")
122
+ if isinstance(original_val, dict):
123
+ original_val = original_val.get("value")
124
+ currency = p_price.get("currency") or p_price.get("currencySymbol") or "TL"
125
+ elif isinstance(p_price, (int, float)):
126
+ discounted_val = float(p_price)
127
+
128
+ # Marka ayıklama
129
+ brand = p.get("brand")
130
+ brand_name = brand.get("name", "") if isinstance(brand, dict) else str(brand or "")
131
+
132
+ # Puan ayıklama
133
+ rating_score = p.get("ratingScore")
134
+ avg_rating = None
135
+ total_reviews = None
136
+ if isinstance(rating_score, dict):
137
+ avg_rating = rating_score.get("averageRating")
138
+ total_reviews = rating_score.get("totalCount")
139
+
140
+ # URL
141
+ rel_url = p.get("url", "")
142
+ full_prod_url = f"https://www.trendyol.com{rel_url}" if rel_url.startswith("/") else rel_url
143
+
144
+ formatted_products.append({
145
+ "id": p.get("id"),
146
+ "name": p.get("name", ""),
147
+ "brand": brand_name,
148
+ "price": discounted_val,
149
+ "original_price": original_val,
150
+ "currency": currency,
151
+ "rating": round(avg_rating, 2) if isinstance(avg_rating, (int, float)) else None,
152
+ "review_count": total_reviews,
153
+ "url": full_prod_url,
154
+ })
155
+
156
+ return {
157
+ "success": True,
158
+ "service": "trendyol",
159
+ "query": query,
160
+ "total_found": len(raw_products),
161
+ "returned_count": len(formatted_products),
162
+ "products": formatted_products,
163
+ }
164
+
165
+
166
+ def search_products(
167
+ query: str = "laptop",
168
+ max_results: int = 10,
169
+ tenant_id: str = "default",
170
+ mode: str = "local",
171
+ ) -> dict[str, Any]:
172
+ """
173
+ Trendyol üzerinde ürün arama ve fiyat karşılaştırması yapar.
174
+ query: Aranacak kelime (örn: 'laptop', 'iphone 15', 'kablosuz kulaklık')
175
+ max_results: Döndürülecek maksimum ürün sayısı (varsayılan: 10)
176
+ tenant_id: Oturum izolasyonu için kiracı ID'si (varsayılan: 'default')
177
+ mode: Çalışma modu ('local' yerel Chrome veya 'cloud' Docker/Remote Browser)
178
+ """
179
+ client = TrendyolClient(tenant_id=tenant_id, mode=mode)
180
+ return client.search(query=query, max_results=max_results)
181
+
182
+
183
+ def print_product_summary(data: dict[str, Any], elapsed_ms: float = 0.0) -> None:
184
+ """Ayrıştırılmış ürün sonuçlarını terminalde tablo gibi temiz bir formatta yazdırır."""
185
+ products = data.get("products", [])
186
+ query = data.get("query", "")
187
+ total = data.get("total_found", 0)
188
+
189
+ print("\n" + "=" * 80)
190
+ print(f"🛍️ Trendyol Arama Sonuçları: '{query}' | Bulunan: {total} | Gösterilen: {len(products)}")
191
+ if elapsed_ms > 0:
192
+ print(f"⏱️ Yanıt Süresi: {elapsed_ms:.2f} ms")
193
+ print("=" * 80)
194
+
195
+ if not products:
196
+ print("ℹ️ Eşleşen ürün bulunamadı.")
197
+ return
198
+
199
+ for idx, p in enumerate(products, 1):
200
+ name = p.get("name", "")
201
+ brand = p.get("brand", "")
202
+ price = p.get("price")
203
+ orig_price = p.get("original_price")
204
+ curr = p.get("currency", "TL")
205
+ rating = p.get("rating")
206
+ reviews = p.get("review_count")
207
+
208
+ price_str = f"{price:,.2f} {curr}" if isinstance(price, (int, float)) else "Belirtilmemiş"
209
+ if isinstance(orig_price, (int, float)) and orig_price > (price or 0):
210
+ price_str += f" (Eski: {orig_price:,.2f} {curr})"
211
+
212
+ rating_str = f"⭐ {rating}" if rating else ""
213
+ if reviews:
214
+ rating_str += f" ({reviews} değerlendirme)"
215
+
216
+ print(f"\n{idx:2d}. [{brand}] {name[:65]}")
217
+ print(f" Fiyat: {price_str} {rating_str}")
218
+ print(f" Link : {p.get('url')}")
219
+
220
+ print("\n" + "=" * 80 + "\n")
221
+
222
+
223
+ if __name__ == "__main__":
224
+ t0 = time.perf_counter()
225
+ query_arg = sys.argv[1] if len(sys.argv) > 1 else "laptop"
226
+ res = search_products(query=query_arg, max_results=5)
227
+ t_elapsed = (time.perf_counter() - t0) * 1000
228
+ print_product_summary(res, elapsed_ms=t_elapsed)
@@ -0,0 +1,30 @@
1
+ {
2
+ "service_name": "trendyol",
3
+ "target_url": "https://www.trendyol.com",
4
+ "goal": "Trendyol üzerinde ürün arama ve fiyat karşılaştırması yap",
5
+ "function_name": "search_products",
6
+ "explanation": "Trendyol arama motoru üzerinden verilen sorgu kelimesine göre ürünleri, markaları, güncel indirimli fiyatları ve ürün puanlarını listeler.",
7
+ "method": "GET",
8
+ "endpoint_url": "https://www.trendyol.com/sr",
9
+ "dynamic_params": [
10
+ {
11
+ "name": "query",
12
+ "original_key": "q",
13
+ "location": "query",
14
+ "param_type": "str",
15
+ "default_value": "laptop",
16
+ "description": "Aranacak ürün adı veya kategori"
17
+ },
18
+ {
19
+ "name": "max_results",
20
+ "original_key": "max_results",
21
+ "location": "query",
22
+ "param_type": "int",
23
+ "default_value": 10,
24
+ "description": "Döndürülecek maksimum ürün adedi"
25
+ }
26
+ ],
27
+ "created_at": "2026-11-03T23:00:00.000000+00:00",
28
+ "validation_status": "BAŞARILI",
29
+ "last_validated_at": "2026-11-06T19:33:43.505975+00:00"
30
+ }
@@ -0,0 +1,94 @@
1
+ from __future__ import annotations
2
+
3
+ import pathlib
4
+ import sys
5
+ import time
6
+ from typing import Any
7
+
8
+ sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[2]))
9
+ sys.path.insert(0, str(pathlib.Path(__file__).resolve().parent))
10
+
11
+ from core.base_client import BaseOwnClient
12
+ from core.token_store import TokenStore
13
+
14
+
15
+ class UcakBiletiClient(BaseOwnClient):
16
+ """Obilet / Uçak Bileti servisi için OwnAPI User-Delegated Browser Middleware + Generic Form Automation istemcisi."""
17
+
18
+ def __init__(
19
+ self,
20
+ base_url: str = "https://www.obilet.com",
21
+ tenant_id: str = "default",
22
+ token_store: TokenStore | None = None,
23
+ mode: str = "local",
24
+ ) -> None:
25
+ super().__init__(
26
+ service_name="ucak_bileti",
27
+ base_url=base_url,
28
+ tenant_id=tenant_id,
29
+ token_store=token_store,
30
+ mode=mode,
31
+ )
32
+
33
+ def search_flight_offers(
34
+ self,
35
+ origin_id: str = "250_0",
36
+ destination_id: str = "1912_170",
37
+ departure_date: str = "20270707",
38
+ return_date: str = "20270714",
39
+ tenant_id: str | None = None,
40
+ mode: str | None = None,
41
+ ) -> dict[str, Any]:
42
+ """
43
+ İki şehir ve tarih arasında uçak bileti seferlerini ve fiyatlarını
44
+ Generic Form Automation + In-Browser Fetch katmanı üzerinden sorgular.
45
+ """
46
+ url = f"{self.base_url}/json/ucuslar/async/{origin_id}-{destination_id}/{departure_date}-{return_date}/2a-1c-1s/economy"
47
+ headers = {
48
+ "accept": "application/json",
49
+ "content-type": "application/json",
50
+ "origin": "https://www.obilet.com",
51
+ "referer": f"https://www.obilet.com/ucuslar/{origin_id}-{destination_id}/{departure_date}-{return_date}/2a-1c-1s/economy/all",
52
+ }
53
+ return self.request(
54
+ method="GET",
55
+ url=url,
56
+ headers=headers,
57
+ target_url="https://www.obilet.com/ucak-bileti",
58
+ mode=mode or self.mode,
59
+ form_params={
60
+ "origin": origin_id,
61
+ "destination": destination_id,
62
+ "departure_date": departure_date,
63
+ "return_date": return_date,
64
+ },
65
+ )
66
+
67
+
68
+ def search_flight_offers(
69
+ origin_id: str = "250_0",
70
+ destination_id: str = "1912_170",
71
+ departure_date: str = "20270707",
72
+ return_date: str = "20270714",
73
+ tenant_id: str = "default",
74
+ mode: str = "local",
75
+ ) -> dict[str, Any]:
76
+ client = UcakBiletiClient(tenant_id=tenant_id, mode=mode)
77
+ return client.search_flight_offers(
78
+ origin_id=origin_id,
79
+ destination_id=destination_id,
80
+ departure_date=departure_date,
81
+ return_date=return_date,
82
+ tenant_id=tenant_id,
83
+ mode=mode,
84
+ )
85
+
86
+
87
+ if __name__ == "__main__":
88
+ client = UcakBiletiClient()
89
+ start_time = time.perf_counter()
90
+ result = client.search_flight_offers()
91
+ end_time = time.perf_counter()
92
+ duration = (end_time - start_time) * 1000
93
+ print(f"Sorgu süresi: {duration:.2f} ms")
94
+ print("Uçuş teklifleri:", result.get("data", {}).get("result", {}) if isinstance(result, dict) else result)
@@ -0,0 +1,47 @@
1
+ {
2
+ "service_name": "ucak_bileti",
3
+ "target_url": "https://www.obilet.com/ucak-bileti",
4
+ "goal": "İki şehir ve tarih arasında uçak bileti seferlerini ve fiyatlarını sorgula",
5
+ "function_name": "search_flight_offers",
6
+ "explanation": "Kullanıcının hedefi iki şehir ve tarih arasında uçak bileti seferlerini ve fiyatlarını sorgulamak olduğundan, '/json/ucuslar/async/250_0-1912_170/20270707-20270714/2a-1c-1s/economy' uç noktası bu amaca en uygun olanıdır. Bu uç nokta, belirli bir rota ve tarih aralığında uçuş araması yapmaktadır.",
7
+ "method": "POST",
8
+ "endpoint_url": "https://www.obilet.com/json/ucuslar/async/250_0-1912_170/20270707-20270714/2a-1c-1s/economy",
9
+ "dynamic_params": [
10
+ {
11
+ "name": "origin_id",
12
+ "original_key": "origin-id",
13
+ "location": "path",
14
+ "param_type": "str",
15
+ "default_value": "250_0",
16
+ "description": "Kalkış şehrinin ve havaalanının ID'si."
17
+ },
18
+ {
19
+ "name": "destination_id",
20
+ "original_key": "destination-id",
21
+ "location": "path",
22
+ "param_type": "str",
23
+ "default_value": "1912_170",
24
+ "description": "Varış şehrinin ve havaalanının ID'si."
25
+ },
26
+ {
27
+ "name": "departure_date",
28
+ "original_key": "departure",
29
+ "location": "path",
30
+ "param_type": "str",
31
+ "default_value": "20270707",
32
+ "description": "Kalkış tarihi (YYYYMMDD formatında)."
33
+ },
34
+ {
35
+ "name": "return_date",
36
+ "original_key": "return",
37
+ "location": "path",
38
+ "param_type": "str",
39
+ "default_value": "20270714",
40
+ "description": "Dönüş tarihi (YYYYMMDD formatında)."
41
+ }
42
+ ],
43
+ "created_at": "2026-10-31T22:19:17.567346+00:00",
44
+ "validation_status": "BAŞARISIZ",
45
+ "validation_passed_runs": 0,
46
+ "last_validated_at": "2026-11-06T19:34:38.312611+00:00"
47
+ }
@@ -0,0 +1,5 @@
1
+ """OwnAPI Registry Module: wikipedia."""
2
+
3
+ from .client import WikipediaClient, search_wikipedia
4
+
5
+ __all__ = ["search_wikipedia", "WikipediaClient"]
@@ -0,0 +1,64 @@
1
+ from __future__ import annotations
2
+
3
+ import pathlib
4
+ import sys
5
+ import time
6
+ from typing import Any
7
+
8
+ sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[2]))
9
+ sys.path.insert(0, str(pathlib.Path(__file__).resolve().parent))
10
+
11
+ from core.base_client import BaseOwnClient
12
+ from core.token_store import TokenStore
13
+
14
+
15
+ class WikipediaClient(BaseOwnClient):
16
+ """Wikipedia REST API için OwnAPI User-Delegated Browser Middleware istemcisi."""
17
+
18
+ def __init__(
19
+ self,
20
+ tenant_id: str = "default",
21
+ token_store: TokenStore | None = None,
22
+ mode: str = "local",
23
+ ) -> None:
24
+ super().__init__(
25
+ service_name="wikipedia",
26
+ base_url="https://en.wikipedia.org/",
27
+ tenant_id=tenant_id,
28
+ token_store=token_store,
29
+ mode=mode,
30
+ )
31
+
32
+ def search_wikipedia(self, query: str = "peter thiel", limit: int = 10) -> dict[str, Any]:
33
+ url = "https://en.wikipedia.org/w/rest.php/v1/search/title"
34
+ headers = {
35
+ "accept": "application/json",
36
+ "referer": "https://en.wikipedia.org/",
37
+ }
38
+ params = {
39
+ "q": query,
40
+ "limit": limit,
41
+ }
42
+ return self.request(method="GET", url=url, headers=headers, params=params)
43
+
44
+
45
+ def search_wikipedia(
46
+ query: str = "peter thiel",
47
+ limit: int = 10,
48
+ tenant_id: str = "default",
49
+ mode: str = "local",
50
+ ) -> dict[str, Any]:
51
+ """
52
+ Wikipedia üzerinde kavram ve kişi araması yapar (OwnAPI User-Delegated Browser Middleware).
53
+ """
54
+ client = WikipediaClient(tenant_id=tenant_id, mode=mode)
55
+ return client.search_wikipedia(query=query, limit=limit)
56
+
57
+
58
+ if __name__ == "__main__":
59
+ start_time = time.perf_counter()
60
+ results = search_wikipedia()
61
+ elapsed_time = (time.perf_counter() - start_time) * 1000
62
+ pages = results.get("pages", []) if isinstance(results, dict) else []
63
+ print(f"[OwnAPI] Arama sonuçları: {pages[:2]}... (Toplam {len(pages)} sonuç)")
64
+ print(f"[OwnAPI] Sorgu süresi: {elapsed_time:.2f} ms")
@@ -0,0 +1,31 @@
1
+ {
2
+ "service_name": "wikipedia",
3
+ "target_url": "https://en.wikipedia.org",
4
+ "goal": "Wikipedia üzerinde kavram ve kişi araması yap",
5
+ "function_name": "search_wikipedia",
6
+ "explanation": "Kullanıcının amacı Wikipedia üzerinde kavram ve kişi araması yapmak olduğundan, '/w/rest.php/v1/search/title' uç noktası arama sorgularını gerçekleştirmek için en uygun uç noktadır. Bu uç nokta, arama sorgusu ve sonuç limiti gibi dinamik parametreleri desteklemektedir.",
7
+ "method": "GET",
8
+ "endpoint_url": "https://en.wikipedia.org/w/rest.php/v1/search/title",
9
+ "dynamic_params": [
10
+ {
11
+ "name": "query",
12
+ "original_key": "q",
13
+ "location": "query",
14
+ "param_type": "str",
15
+ "default_value": "peter thiel",
16
+ "description": "Aranacak kavram veya kişi ismi."
17
+ },
18
+ {
19
+ "name": "limit",
20
+ "original_key": "limit",
21
+ "location": "query",
22
+ "param_type": "int",
23
+ "default_value": 10,
24
+ "description": "Dönen sonuçların sayısı."
25
+ }
26
+ ],
27
+ "created_at": "2026-10-31T22:06:55.412344+00:00",
28
+ "validation_status": "BAŞARILI",
29
+ "validation_passed_runs": 3,
30
+ "last_validated_at": "2026-11-05T15:27:20.325892+00:00"
31
+ }
@@ -0,0 +1,5 @@
1
+ """OwnAPI Registry Module: yahoo_finance."""
2
+
3
+ from .client import YahooFinanceClient, get_stock_quote
4
+
5
+ __all__ = ["get_stock_quote", "YahooFinanceClient"]
@@ -0,0 +1,98 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import pathlib
5
+ import sys
6
+ import time
7
+ from typing import Any
8
+ from urllib.parse import quote
9
+
10
+ sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[2]))
11
+ sys.path.insert(0, str(pathlib.Path(__file__).resolve().parent))
12
+
13
+ from core.base_client import BaseOwnClient
14
+ from core.token_store import TokenStore
15
+
16
+
17
+ class YahooFinanceClient(BaseOwnClient):
18
+ """Yahoo Finance real-time stock quote & chart client via OwnAPI User-Delegated Browser Middleware."""
19
+
20
+ def __init__(
21
+ self,
22
+ tenant_id: str = "default",
23
+ token_store: TokenStore | None = None,
24
+ mode: str = "local",
25
+ ) -> None:
26
+ super().__init__(
27
+ service_name="yahoo_finance",
28
+ base_url="https://finance.yahoo.com/",
29
+ tenant_id=tenant_id,
30
+ token_store=token_store,
31
+ mode=mode,
32
+ )
33
+
34
+ def get_stock_quote(self, symbol: str = "NVDA") -> dict[str, Any]:
35
+ clean_symbol = quote(str(symbol).strip().upper() or "NVDA")
36
+ url = f"https://query1.finance.yahoo.com/v8/finance/chart/{clean_symbol}"
37
+ headers = {
38
+ "accept": "application/json, text/plain, */*",
39
+ "referer": f"https://finance.yahoo.com/quote/{clean_symbol}/",
40
+ }
41
+ params = {
42
+ "interval": "1d",
43
+ "range": "5d",
44
+ }
45
+ raw = self.request(method="GET", url=url, headers=headers, params=params)
46
+ if not isinstance(raw, dict):
47
+ return {"success": False, "symbol": symbol, "raw": raw}
48
+
49
+ chart = raw.get("chart", {})
50
+ results = chart.get("result") or []
51
+ if results and isinstance(results[0], dict):
52
+ meta = results[0].get("meta", {})
53
+ price = meta.get("regularMarketPrice")
54
+ prev_close = meta.get("chartPreviousClose") or meta.get("previousClose")
55
+ change = round(price - prev_close, 4) if isinstance(price, (int, float)) and isinstance(prev_close, (int, float)) else None
56
+ change_pct = (
57
+ round(((price - prev_close) / prev_close) * 100.0, 2)
58
+ if change is not None and prev_close
59
+ else None
60
+ )
61
+ return {
62
+ "success": True,
63
+ "service": "yahoo_finance",
64
+ "symbol": meta.get("symbol", clean_symbol),
65
+ "short_name": meta.get("shortName") or meta.get("longName") or clean_symbol,
66
+ "currency": meta.get("currency", "USD"),
67
+ "exchange": meta.get("fullExchangeName") or meta.get("exchangeName"),
68
+ "regular_market_price": price,
69
+ "previous_close": prev_close,
70
+ "change": change,
71
+ "change_percent": change_pct,
72
+ "fifty_two_week_high": meta.get("fiftyTwoWeekHigh"),
73
+ "fifty_two_week_low": meta.get("fiftyTwoWeekLow"),
74
+ "regular_market_volume": meta.get("regularMarketVolume"),
75
+ "meta": meta,
76
+ }
77
+ return raw
78
+
79
+
80
+ def get_stock_quote(
81
+ symbol: str = "NVDA",
82
+ tenant_id: str = "default",
83
+ mode: str = "local",
84
+ ) -> dict[str, Any]:
85
+ """
86
+ Fetches real-time stock/index price, daily change, and 52-week range from Yahoo Finance
87
+ via OwnAPI User-Delegated Browser Middleware.
88
+ """
89
+ client = YahooFinanceClient(tenant_id=tenant_id, mode=mode)
90
+ return client.get_stock_quote(symbol=symbol)
91
+
92
+
93
+ if __name__ == "__main__":
94
+ t0 = time.perf_counter()
95
+ res = get_stock_quote()
96
+ elapsed = (time.perf_counter() - t0) * 1000
97
+ print(f"[OwnAPI - Yahoo Finance] ({elapsed:.1f} ms)")
98
+ print(json.dumps(res, ensure_ascii=False, indent=2)[:800])