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
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import pathlib
|
|
5
|
+
import re
|
|
6
|
+
import sys
|
|
7
|
+
from html import unescape
|
|
8
|
+
from typing import Any
|
|
9
|
+
from urllib.parse import quote_plus
|
|
10
|
+
|
|
11
|
+
sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[2]))
|
|
12
|
+
sys.path.insert(0, str(pathlib.Path(__file__).resolve().parent))
|
|
13
|
+
|
|
14
|
+
from core.base_client import BaseOwnClient
|
|
15
|
+
from core.token_store import TokenStore
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _clean_html_text(raw: str) -> str:
|
|
19
|
+
"""HTML etiketlerini ve fazla boşlukları temizler."""
|
|
20
|
+
text = unescape(re.sub(r"<[^>]+>", " ", raw or ""))
|
|
21
|
+
return re.sub(r"\s+", " ", text).strip()
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _parse_linkedin_job_cards(raw_html_or_text: str) -> list[dict[str, Any]]:
|
|
25
|
+
"""
|
|
26
|
+
`seeMoreJobPostings/search` veya LinkedIn iş arama yanıtındaki TÜM iş ilanı kartlarını
|
|
27
|
+
(`<li>`, `.base-card`, `.job-search-card`) döngüyle ayıklar.
|
|
28
|
+
"""
|
|
29
|
+
jobs: list[dict[str, Any]] = []
|
|
30
|
+
if not raw_html_or_text:
|
|
31
|
+
return jobs
|
|
32
|
+
|
|
33
|
+
# Kart bloklarını hem <li>...</li> üzerinden hem de base-card / job-search-card bölümlerinden yakala
|
|
34
|
+
card_blocks = re.findall(r"<li\b[^>]*>(.*?)</li>", raw_html_or_text, flags=re.DOTALL | re.IGNORECASE)
|
|
35
|
+
if len(card_blocks) <= 1 and "base-search-card__title" in raw_html_or_text:
|
|
36
|
+
# <li> etiketi kapanmamış veya farklı sarmalayıcı kullanılmışsa kart başlangıçlarından böl
|
|
37
|
+
parts = re.split(r'(?=<div[^>]+class="[^"]*(?:base-card|job-search-card)[^"]*")', raw_html_or_text, flags=re.IGNORECASE)
|
|
38
|
+
card_blocks = [p for p in parts if "base-search-card__title" in p]
|
|
39
|
+
|
|
40
|
+
seen_keys: set[str] = set()
|
|
41
|
+
for block in card_blocks:
|
|
42
|
+
title_m = re.search(
|
|
43
|
+
r'class="[^"]*base-search-card__title[^"]*"[^>]*>(.*?)</h[1-6]>',
|
|
44
|
+
block,
|
|
45
|
+
flags=re.DOTALL | re.IGNORECASE,
|
|
46
|
+
)
|
|
47
|
+
if not title_m:
|
|
48
|
+
continue
|
|
49
|
+
|
|
50
|
+
company_m = re.search(
|
|
51
|
+
r'class="[^"]*base-search-card__subtitle[^"]*"[^>]*>(.*?)</h[1-6]>',
|
|
52
|
+
block,
|
|
53
|
+
flags=re.DOTALL | re.IGNORECASE,
|
|
54
|
+
)
|
|
55
|
+
loc_m = re.search(
|
|
56
|
+
r'class="[^"]*job-search-card__location[^"]*"[^>]*>(.*?)</span>',
|
|
57
|
+
block,
|
|
58
|
+
flags=re.DOTALL | re.IGNORECASE,
|
|
59
|
+
)
|
|
60
|
+
link_m = re.search(
|
|
61
|
+
r'href="(https://[^"]*linkedin\.com/jobs/view/[^"?\s]+)',
|
|
62
|
+
block,
|
|
63
|
+
flags=re.IGNORECASE,
|
|
64
|
+
)
|
|
65
|
+
urn_m = re.search(
|
|
66
|
+
r'data-entity-urn="urn:li:jobPosting:(\d+)"',
|
|
67
|
+
block,
|
|
68
|
+
flags=re.IGNORECASE,
|
|
69
|
+
)
|
|
70
|
+
date_m = re.search(r'<time[^>]*datetime="([^"]+)"', block, flags=re.IGNORECASE)
|
|
71
|
+
|
|
72
|
+
clean_title = _clean_html_text(title_m.group(1))
|
|
73
|
+
clean_company = _clean_html_text(company_m.group(1)) if company_m else ""
|
|
74
|
+
clean_loc = _clean_html_text(loc_m.group(1)) if loc_m else ""
|
|
75
|
+
job_id = urn_m.group(1).strip() if urn_m else ""
|
|
76
|
+
job_url = link_m.group(1).strip() if link_m else (f"https://www.linkedin.com/jobs/view/{job_id}/" if job_id else "")
|
|
77
|
+
listed_at = date_m.group(1).strip() if date_m else ""
|
|
78
|
+
|
|
79
|
+
if not clean_title:
|
|
80
|
+
continue
|
|
81
|
+
|
|
82
|
+
dedup_key = job_id or job_url or f"{clean_title}|{clean_company}|{clean_loc}"
|
|
83
|
+
if dedup_key in seen_keys:
|
|
84
|
+
continue
|
|
85
|
+
seen_keys.add(dedup_key)
|
|
86
|
+
|
|
87
|
+
jobs.append(
|
|
88
|
+
{
|
|
89
|
+
"job_id": job_id,
|
|
90
|
+
"title": clean_title,
|
|
91
|
+
"company": clean_company,
|
|
92
|
+
"location": clean_loc,
|
|
93
|
+
"job_url": job_url,
|
|
94
|
+
"listed_at": listed_at,
|
|
95
|
+
}
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
return jobs
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class LinkedinJobsClient(BaseOwnClient):
|
|
102
|
+
"""LinkedIn Jobs iş ilanı arama ve dinamik sayfalama istemcisi (User-Delegated Browser Middleware)."""
|
|
103
|
+
|
|
104
|
+
def __init__(
|
|
105
|
+
self,
|
|
106
|
+
tenant_id: str = "default",
|
|
107
|
+
token_store: TokenStore | None = None,
|
|
108
|
+
mode: str = "local",
|
|
109
|
+
) -> None:
|
|
110
|
+
super().__init__(
|
|
111
|
+
service_name="linkedin_jobs",
|
|
112
|
+
base_url="https://www.linkedin.com",
|
|
113
|
+
tenant_id=tenant_id,
|
|
114
|
+
token_store=token_store,
|
|
115
|
+
mode=mode,
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
def search_jobs(
|
|
119
|
+
self,
|
|
120
|
+
keywords: str = "AI Engineer",
|
|
121
|
+
location: str = "",
|
|
122
|
+
limit: int = 10,
|
|
123
|
+
start: int = 0,
|
|
124
|
+
) -> dict[str, Any]:
|
|
125
|
+
"""
|
|
126
|
+
LinkedIn üzerinde belirtilen `keywords` (örn. 'AI Engineer'), `location` ve `limit`
|
|
127
|
+
değerlerine göre iş ilanlarını sorgular.
|
|
128
|
+
Tek sayfadan dönen ilan sayısı `limit` değerinden azsa (örn. limit=15, sayfa=10),
|
|
129
|
+
`start` parametresini otomatik artırarak (`start=0`, `start=10`...) hedef `limit`
|
|
130
|
+
sayısına ulaşana kadar sayfalama yapar ve tam olarak `jobs[:limit]` döndürür.
|
|
131
|
+
"""
|
|
132
|
+
clean_keywords = (keywords or "AI Engineer").strip()
|
|
133
|
+
target_limit = max(1, min(int(limit or 10), 50))
|
|
134
|
+
current_start = max(0, int(start or 0))
|
|
135
|
+
encoded_kw = quote_plus(clean_keywords)
|
|
136
|
+
search_page_url = f"https://www.linkedin.com/jobs/search/?keywords={encoded_kw}"
|
|
137
|
+
|
|
138
|
+
headers = {
|
|
139
|
+
"accept": "application/json, text/plain, */*",
|
|
140
|
+
"referer": search_page_url,
|
|
141
|
+
"sec-ch-ua-mobile": "?0",
|
|
142
|
+
"sec-ch-ua-platform": '"Windows"',
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
collected_jobs: list[dict[str, Any]] = []
|
|
146
|
+
seen_keys: set[str] = set()
|
|
147
|
+
pages_fetched = 0
|
|
148
|
+
max_pages = max(1, (target_limit // 10) + 2)
|
|
149
|
+
last_status = 200
|
|
150
|
+
last_raw_text = ""
|
|
151
|
+
|
|
152
|
+
while len(collected_jobs) < target_limit and pages_fetched < max_pages:
|
|
153
|
+
params: dict[str, Any] = {
|
|
154
|
+
"keywords": clean_keywords,
|
|
155
|
+
"start": current_start,
|
|
156
|
+
}
|
|
157
|
+
if location and location.strip():
|
|
158
|
+
params["location"] = location.strip()
|
|
159
|
+
|
|
160
|
+
res = self.request(
|
|
161
|
+
method="GET",
|
|
162
|
+
url="https://www.linkedin.com/jobs-guest/jobs/api/seeMoreJobPostings/search",
|
|
163
|
+
headers=headers,
|
|
164
|
+
params=params,
|
|
165
|
+
target_url="https://www.linkedin.com/robots.txt",
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
raw_text = ""
|
|
169
|
+
if isinstance(res, dict):
|
|
170
|
+
last_status = int(res.get("status_code", 200))
|
|
171
|
+
raw_text = str(res.get("text") or res.get("response_sample") or "")
|
|
172
|
+
else:
|
|
173
|
+
raw_text = str(res or "")
|
|
174
|
+
|
|
175
|
+
last_raw_text = raw_text
|
|
176
|
+
page_jobs = _parse_linkedin_job_cards(raw_text)
|
|
177
|
+
pages_fetched += 1
|
|
178
|
+
|
|
179
|
+
if not page_jobs:
|
|
180
|
+
break
|
|
181
|
+
|
|
182
|
+
added_in_page = 0
|
|
183
|
+
for job in page_jobs:
|
|
184
|
+
key = job.get("job_id") or job.get("job_url") or f"{job.get('title')}|{job.get('company')}"
|
|
185
|
+
if key not in seen_keys:
|
|
186
|
+
seen_keys.add(key)
|
|
187
|
+
collected_jobs.append(job)
|
|
188
|
+
added_in_page += 1
|
|
189
|
+
if len(collected_jobs) >= target_limit:
|
|
190
|
+
break
|
|
191
|
+
|
|
192
|
+
# Bu sayfada yeni ilan gelmediyse veya hedef limite ulaşıldıysa döngüyü bitir
|
|
193
|
+
if added_in_page == 0 or len(collected_jobs) >= target_limit:
|
|
194
|
+
break
|
|
195
|
+
|
|
196
|
+
current_start += len(page_jobs)
|
|
197
|
+
|
|
198
|
+
final_jobs = collected_jobs[:target_limit]
|
|
199
|
+
|
|
200
|
+
return {
|
|
201
|
+
"service": "linkedin_jobs",
|
|
202
|
+
"keywords": clean_keywords,
|
|
203
|
+
"location": location or "Global",
|
|
204
|
+
"requested_limit": target_limit,
|
|
205
|
+
"start": int(start),
|
|
206
|
+
"pages_fetched": pages_fetched,
|
|
207
|
+
"total_jobs_returned": len(final_jobs),
|
|
208
|
+
"jobs": final_jobs,
|
|
209
|
+
"search_url": search_page_url,
|
|
210
|
+
"status_code": last_status,
|
|
211
|
+
"response_preview": (
|
|
212
|
+
f"{len(final_jobs)}/{target_limit} iş ilanı ({pages_fetched} sayfa taranarak) başarıyla getirildi."
|
|
213
|
+
if final_jobs
|
|
214
|
+
else last_raw_text[:500]
|
|
215
|
+
),
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
# Geriye dönük uyumluluk
|
|
219
|
+
def get_user_metadata(self, keywords: str = "AI Engineer") -> dict[str, Any]:
|
|
220
|
+
return self.search_jobs(keywords=keywords)
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def search_jobs(
|
|
224
|
+
keywords: str = "AI Engineer",
|
|
225
|
+
location: str = "",
|
|
226
|
+
limit: int = 10,
|
|
227
|
+
start: int = 0,
|
|
228
|
+
tenant_id: str = "default",
|
|
229
|
+
mode: str = "local",
|
|
230
|
+
) -> dict[str, Any]:
|
|
231
|
+
"""
|
|
232
|
+
LinkedIn Jobs üzerinde anahtar kelime (`keywords`), konum (`location`) ve
|
|
233
|
+
istenilen ilan sayısına (`limit`) göre iş ilanlarını arar; gerekirse otomatik sayfalama yapar.
|
|
234
|
+
"""
|
|
235
|
+
client = LinkedinJobsClient(tenant_id=tenant_id, mode=mode)
|
|
236
|
+
return client.search_jobs(keywords=keywords, location=location, limit=limit, start=start)
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
if __name__ == "__main__":
|
|
240
|
+
result = search_jobs(keywords="AI Engineer", limit=5)
|
|
241
|
+
print(json.dumps(result, ensure_ascii=False, indent=2)[:1500])
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"service_name": "linkedin_jobs",
|
|
3
|
+
"target_url": "https://www.linkedin.com/jobs/search/?keywords=AI%20Engineer",
|
|
4
|
+
"goal": "LinkedIn Jobs üzerinde anahtar kelime (keywords), konum (location) ve istenen ilan adedine (limit) göre güncel iş ilanlarını arama ve listeleme servisi",
|
|
5
|
+
"function_name": "search_jobs",
|
|
6
|
+
"explanation": "LinkedIn iş arama API uç noktası üzerinden `keywords` (örn. 'AI Engineer'), `location`, `limit` (getirilecek ilan sayısı, örn. 2, 10, 15) ve `start` parametreleriyle gerçek zamanlı iş ilanlarını otomatik sayfalamayla döndürür.",
|
|
7
|
+
"method": "GET",
|
|
8
|
+
"endpoint_url": "https://www.linkedin.com/jobs-guest/jobs/api/seeMoreJobPostings/search",
|
|
9
|
+
"dynamic_params": [
|
|
10
|
+
{
|
|
11
|
+
"name": "keywords",
|
|
12
|
+
"original_key": "keywords",
|
|
13
|
+
"location": "query",
|
|
14
|
+
"param_type": "str",
|
|
15
|
+
"default_value": "AI Engineer",
|
|
16
|
+
"description": "Aranacak iş unvanı, pozisyon veya yetenek anahtar kelimesi (örn. 'AI Engineer', 'Python Developer', 'Product Manager')."
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"name": "location",
|
|
20
|
+
"original_key": "location",
|
|
21
|
+
"location": "query",
|
|
22
|
+
"param_type": "str",
|
|
23
|
+
"default_value": "",
|
|
24
|
+
"description": "İsteğe bağlı şehir, ülke veya çalışma modeli filtresi (örn. 'Turkey', 'Istanbul', 'Remote', 'United States')."
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"name": "limit",
|
|
28
|
+
"original_key": "limit",
|
|
29
|
+
"location": "query",
|
|
30
|
+
"param_type": "int",
|
|
31
|
+
"default_value": 10,
|
|
32
|
+
"description": "Döndürülecek maksimum iş ilanı sayısı (örn. 2, 5, 10, 15). Tek sayfada yeterli ilan yoksa otomatik sayfalama yapılır."
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"name": "start",
|
|
36
|
+
"original_key": "start",
|
|
37
|
+
"location": "query",
|
|
38
|
+
"param_type": "int",
|
|
39
|
+
"default_value": 0,
|
|
40
|
+
"description": "Sayfalama başlangıç indeksi (0, 10, 20 vb.)."
|
|
41
|
+
}
|
|
42
|
+
],
|
|
43
|
+
"created_at": "2026-11-06T20:27:02.643887+00:00",
|
|
44
|
+
"validation_status": "BAŞARILI",
|
|
45
|
+
"validation_passed_runs": 3
|
|
46
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import pathlib
|
|
5
|
+
import sys
|
|
6
|
+
import time
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[2]))
|
|
10
|
+
sys.path.insert(0, str(pathlib.Path(__file__).resolve().parent))
|
|
11
|
+
|
|
12
|
+
from core.base_client import BaseOwnClient
|
|
13
|
+
from core.token_store import TokenStore
|
|
14
|
+
from healer import update_payload_deep
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class PegasusClient(BaseOwnClient):
|
|
18
|
+
"""Pegasus Hava Yolları (flypgs.com) için OwnAPI User-Delegated Browser Middleware istemcisi."""
|
|
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="pegasus",
|
|
28
|
+
base_url="https://web.flypgs.com/booking",
|
|
29
|
+
tenant_id=tenant_id,
|
|
30
|
+
token_store=token_store,
|
|
31
|
+
mode=mode,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
def get_flight_availability(
|
|
35
|
+
self,
|
|
36
|
+
departure_port: str = "SAW",
|
|
37
|
+
arrival_port: str = "ESB",
|
|
38
|
+
departure_date: str = "2027-03-03",
|
|
39
|
+
return_date: str = "2027-03-08",
|
|
40
|
+
adult_count: int = 1,
|
|
41
|
+
child_count: int = 0,
|
|
42
|
+
infant_count: int = 0,
|
|
43
|
+
currency: str = "TRY",
|
|
44
|
+
) -> dict[str, Any]:
|
|
45
|
+
params: dict[str, Any] = {}
|
|
46
|
+
headers: dict[str, str] = {
|
|
47
|
+
"accept": "application/json, text/plain, */*",
|
|
48
|
+
"content-type": "application/json",
|
|
49
|
+
"origin": "https://web.flypgs.com",
|
|
50
|
+
"referer": (
|
|
51
|
+
f"https://web.flypgs.com/booking?language=tr&adultCount={adult_count}"
|
|
52
|
+
f"&arrivalPort={arrival_port}&departurePort={departure_port}¤cy={currency}"
|
|
53
|
+
f"&dateOption=1&departureDate={departure_date}&returnDate={return_date}"
|
|
54
|
+
),
|
|
55
|
+
}
|
|
56
|
+
payload: Any = {
|
|
57
|
+
"flightSearchList": [
|
|
58
|
+
{
|
|
59
|
+
"departurePort": departure_port,
|
|
60
|
+
"arrivalPort": arrival_port,
|
|
61
|
+
"departureDate": departure_date,
|
|
62
|
+
"returnDate": return_date,
|
|
63
|
+
}
|
|
64
|
+
],
|
|
65
|
+
"adultCount": adult_count,
|
|
66
|
+
"childCount": child_count,
|
|
67
|
+
"infantCount": infant_count,
|
|
68
|
+
"currency": currency,
|
|
69
|
+
"dateOption": 1,
|
|
70
|
+
"ffRedemption": False,
|
|
71
|
+
"totalPoints": None,
|
|
72
|
+
"personnelFlightSearch": False,
|
|
73
|
+
"expatPnr": False,
|
|
74
|
+
"operationCode": "TK",
|
|
75
|
+
"affiliate": {"id": None},
|
|
76
|
+
"bookingType": "BOOKING",
|
|
77
|
+
}
|
|
78
|
+
payload = update_payload_deep(
|
|
79
|
+
payload,
|
|
80
|
+
{
|
|
81
|
+
"departurePort": departure_port,
|
|
82
|
+
"arrivalPort": arrival_port,
|
|
83
|
+
"departureDate": departure_date,
|
|
84
|
+
"returnDate": return_date,
|
|
85
|
+
"adultCount": adult_count,
|
|
86
|
+
"childCount": child_count,
|
|
87
|
+
"infantCount": infant_count,
|
|
88
|
+
"currency": currency,
|
|
89
|
+
},
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
return self.request(
|
|
93
|
+
method="POST",
|
|
94
|
+
url="https://web.flypgs.com/pegasus/availability",
|
|
95
|
+
headers=headers,
|
|
96
|
+
params=params,
|
|
97
|
+
json_payload=payload,
|
|
98
|
+
form_params={
|
|
99
|
+
"origin": departure_port,
|
|
100
|
+
"destination": arrival_port,
|
|
101
|
+
"departure_date": departure_date,
|
|
102
|
+
"return_date": return_date,
|
|
103
|
+
},
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def get_flight_availability(
|
|
108
|
+
departure_port: str = "SAW",
|
|
109
|
+
arrival_port: str = "ESB",
|
|
110
|
+
departure_date: str = "2027-03-03",
|
|
111
|
+
return_date: str = "2027-03-08",
|
|
112
|
+
adult_count: int = 1,
|
|
113
|
+
child_count: int = 0,
|
|
114
|
+
infant_count: int = 0,
|
|
115
|
+
currency: str = "TRY",
|
|
116
|
+
tenant_id: str = "default",
|
|
117
|
+
mode: str = "local",
|
|
118
|
+
) -> dict[str, Any]:
|
|
119
|
+
"""
|
|
120
|
+
Pegasus Hava Yolları uçuş seferlerini ve fiyatlarını kullanıcının gerçek tarayıcı oturumu üzerinden sorgular.
|
|
121
|
+
Hedef Endpoint: POST https://web.flypgs.com/pegasus/availability
|
|
122
|
+
"""
|
|
123
|
+
client = PegasusClient(tenant_id=tenant_id, mode=mode)
|
|
124
|
+
return client.get_flight_availability(
|
|
125
|
+
departure_port=departure_port,
|
|
126
|
+
arrival_port=arrival_port,
|
|
127
|
+
departure_date=departure_date,
|
|
128
|
+
return_date=return_date,
|
|
129
|
+
adult_count=adult_count,
|
|
130
|
+
child_count=child_count,
|
|
131
|
+
infant_count=infant_count,
|
|
132
|
+
currency=currency,
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
if __name__ == "__main__":
|
|
137
|
+
start_ns = time.perf_counter()
|
|
138
|
+
data = get_flight_availability()
|
|
139
|
+
elapsed_ms = (time.perf_counter() - start_ns) * 1000
|
|
140
|
+
print(f"[OwnAPI] Yanıt Süresi: {elapsed_ms:.2f} ms")
|
|
141
|
+
print(json.dumps(data, ensure_ascii=False, indent=2)[:800])
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"service_name": "pegasus",
|
|
3
|
+
"target_url": "https://www.flypgs.com/tr",
|
|
4
|
+
"goal": "İstanbul'dan Ankara'ya uçuş seferlerini ve fiyatlarını sorgula",
|
|
5
|
+
"function_name": "get_flight_availability",
|
|
6
|
+
"explanation": "İstanbul'dan Ankara'ya uçuş seferlerini ve fiyatlarını sorgulamak için en uygun uç nokta, uçuş arama ve fiyat bilgilerini içeren '/pegasus/availability' uç noktasıdır. Bu uç nokta, kullanıcı tarafından belirtilen kalkış ve varış noktaları ile tarihleri içeren bir arama yapar ve uçuş bilgilerini döndürür.",
|
|
7
|
+
"method": "POST",
|
|
8
|
+
"endpoint_url": "https://web.flypgs.com/pegasus/availability",
|
|
9
|
+
"dynamic_params": [
|
|
10
|
+
{
|
|
11
|
+
"name": "departure_port",
|
|
12
|
+
"original_key": "departurePort",
|
|
13
|
+
"location": "body",
|
|
14
|
+
"param_type": "str",
|
|
15
|
+
"default_value": "SAW",
|
|
16
|
+
"description": "Kalkış yapılacak havaalanı kodu."
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"name": "arrival_port",
|
|
20
|
+
"original_key": "arrivalPort",
|
|
21
|
+
"location": "body",
|
|
22
|
+
"param_type": "str",
|
|
23
|
+
"default_value": "ESB",
|
|
24
|
+
"description": "Varış yapılacak havaalanı kodu."
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"name": "departure_date",
|
|
28
|
+
"original_key": "departureDate",
|
|
29
|
+
"location": "body",
|
|
30
|
+
"param_type": "str",
|
|
31
|
+
"default_value": "2027-03-03",
|
|
32
|
+
"description": "Kalkış tarihi."
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"name": "return_date",
|
|
36
|
+
"original_key": "returnDate",
|
|
37
|
+
"location": "body",
|
|
38
|
+
"param_type": "str",
|
|
39
|
+
"default_value": "2027-03-08",
|
|
40
|
+
"description": "Dönüş tarihi."
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"name": "adult_count",
|
|
44
|
+
"original_key": "adultCount",
|
|
45
|
+
"location": "body",
|
|
46
|
+
"param_type": "int",
|
|
47
|
+
"default_value": 1,
|
|
48
|
+
"description": "Yetişkin yolcu sayısı."
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"name": "child_count",
|
|
52
|
+
"original_key": "childCount",
|
|
53
|
+
"location": "body",
|
|
54
|
+
"param_type": "int",
|
|
55
|
+
"default_value": 0,
|
|
56
|
+
"description": "Çocuk yolcu sayısı."
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"name": "infant_count",
|
|
60
|
+
"original_key": "infantCount",
|
|
61
|
+
"location": "body",
|
|
62
|
+
"param_type": "int",
|
|
63
|
+
"default_value": 0,
|
|
64
|
+
"description": "Bebek yolcu sayısı."
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"name": "currency",
|
|
68
|
+
"original_key": "currency",
|
|
69
|
+
"location": "body",
|
|
70
|
+
"param_type": "str",
|
|
71
|
+
"default_value": "TRY",
|
|
72
|
+
"description": "Para birimi."
|
|
73
|
+
}
|
|
74
|
+
],
|
|
75
|
+
"created_at": "2026-11-01T12:39:07.722926+00:00"
|
|
76
|
+
}
|