magaza-mcp 0.3.0 → 0.4.0
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.
- package/AGENTS.md +13 -5
- package/README.md +23 -14
- package/README.tr.md +25 -15
- package/dist/src/araclar/alim-gucu.js +50 -0
- package/dist/src/araclar/appstore-yazma.js +194 -10
- package/dist/src/araclar/dispatch.js +9 -3
- package/dist/src/araclar/ortak.js +5 -5
- package/dist/src/araclar/play-yazma.js +692 -0
- package/dist/src/araclar/play.js +146 -98
- package/dist/src/index.js +2 -1
- package/dist/src/istemci/play.js +46 -10
- package/dist/src/katalog/yukle.js +7 -0
- package/package.json +1 -1
package/AGENTS.md
CHANGED
|
@@ -157,8 +157,8 @@ npx -y magaza-mcp kaydet --magazalar appstore,play --salt-okunur
|
|
|
157
157
|
```
|
|
158
158
|
|
|
159
159
|
Bu modda yazma yapan araçlar (`appstore__yorum_yanitla`,
|
|
160
|
-
`appstore__metin_guncelle`, fiyat/abonelik/yaş derecelendirme araçları,
|
|
161
|
-
`play__yorum_yanitla`) listeye hiç girmez;
|
|
160
|
+
`appstore__metin_guncelle`, fiyat/abonelik/yaş derecelendirme araçları, Play yayın araçları,
|
|
161
|
+
`play__yorum_yanitla`) listeye hiç girmez; 39 araç 26'ya düşer. `magaza__cagir` listede kalır — katalogdaki okuma uçlarına da o
|
|
162
162
|
araçtan gidiliyor — ama veri değiştiren bir operasyon istendiğinde reddeder.
|
|
163
163
|
|
|
164
164
|
Komut mevcut ayarlara dokunmaz: yalnızca `magaza-mcp` girdisini ekler veya
|
|
@@ -227,15 +227,23 @@ npx magaza-mcp kur
|
|
|
227
227
|
|
|
228
228
|
### Yüklü araçlar günlük işi görür
|
|
229
229
|
|
|
230
|
-
İki mağaza da açıkken
|
|
230
|
+
İki mağaza da açıkken 39 araç yüklenir: 17 tane `appstore__`, 16 tane `play__`,
|
|
231
231
|
3 tane iki mağazayı birden sorgulayan `magaza__`, 3 tane de aşağıdaki erişim
|
|
232
232
|
aracı. Tek mağazalı kurulumda çapraz araçlar hiç oluşmaz: yalnızca App Store'da
|
|
233
|
-
|
|
234
|
-
yaklaşık **
|
|
233
|
+
20, yalnızca Play'de 19 araç yüklenir. Araç tanımlarının tamamı bağlamda
|
|
234
|
+
yaklaşık **8.500 token** yer kaplar.
|
|
235
235
|
|
|
236
236
|
Abonelik ve IAP fiyatı, abonelik kurulumu, satış ülkeleri ve yaş
|
|
237
237
|
derecelendirmesi için hazır `appstore__` araçları var. Bunları
|
|
238
238
|
`magaza__cagir` ile ülke ülke yapma: hazır araç 175 ülkeyi tek istekte yazar.
|
|
239
|
+
Kullanıcı "ülkelere göre fiyatlandır" derse önce `appstore__fiyat_analizi` ile
|
|
240
|
+
öneri al, kullanıcıya göster, onaylarsa çıktıdaki `ozel_fiyatlar`'ı
|
|
241
|
+
`appstore__abonelik_fiyat_ayarla`'ya ver. Play'de aynısı `play__fiyat_analizi` →
|
|
242
|
+
`play__abonelik_fiyat_ayarla`.
|
|
243
|
+
|
|
244
|
+
Play'de sürüm yayınlama (AAB yükleme dahil), rollout yüzdesi, kanallar arası
|
|
245
|
+
taşıma, mağaza metni ve görsel için hazır `play__` araçları var; bunları
|
|
246
|
+
`magaza__cagir` ile edit oturumu açıp kapatarak yapma.
|
|
239
247
|
|
|
240
248
|
Hangi araçların yüklü olduğunu `npx -y magaza-mcp araclar` ile görebilirsin.
|
|
241
249
|
|
package/README.md
CHANGED
|
@@ -62,8 +62,8 @@ directly to Apple and Google, and your keys never leave your Keychain.
|
|
|
62
62
|
| [🧑💻 Manual setup](#-manual-setup) | If you're not using an assistant |
|
|
63
63
|
| [🧩 Do the two stores mix up?](#-do-the-two-stores-mix-up) | No — here's why |
|
|
64
64
|
| [💬 What you can ask](#-what-you-can-ask) | Example prompts |
|
|
65
|
-
| [🧰 Tools](#-tools) | Full list of all
|
|
66
|
-
| [🌐 Full API access](#-full-api-access) | 1,440 operations, ~
|
|
65
|
+
| [🧰 Tools](#-tools) | Full list of all 39 tools |
|
|
66
|
+
| [🌐 Full API access](#-full-api-access) | 1,440 operations, ~8,500 tokens |
|
|
67
67
|
| [🔑 Required permissions](#-required-permissions) | What Apple and Google require |
|
|
68
68
|
| [🔒 Security](#-security) | Keys, approval gates, telemetry |
|
|
69
69
|
| [🩺 Troubleshooting](#-troubleshooting) | Common errors |
|
|
@@ -274,10 +274,10 @@ model, and cost zero tokens.
|
|
|
274
274
|
|
|
275
275
|
| Setup | Tools loaded |
|
|
276
276
|
| --- | --- |
|
|
277
|
-
| Both stores |
|
|
278
|
-
| App Store only |
|
|
279
|
-
| Play only |
|
|
280
|
-
| Read-only (both stores) |
|
|
277
|
+
| Both stores | 39 |
|
|
278
|
+
| App Store only | 20 |
|
|
279
|
+
| Play only | 19 |
|
|
280
|
+
| Read-only (both stores) | 26 |
|
|
281
281
|
|
|
282
282
|
**3. Cross-store tools only exist when both stores are enabled.** In a
|
|
283
283
|
single-store setup they're never created at all — they'd have no meaning.
|
|
@@ -336,7 +336,7 @@ These tools only load when both stores are connected.
|
|
|
336
336
|
</details>
|
|
337
337
|
|
|
338
338
|
<details>
|
|
339
|
-
<summary><b>App Store Connect —
|
|
339
|
+
<summary><b>App Store Connect — 17 tools</b> (<code>appstore__</code>)</summary>
|
|
340
340
|
|
|
341
341
|
<br>
|
|
342
342
|
|
|
@@ -353,7 +353,8 @@ These tools only load when both stores are connected.
|
|
|
353
353
|
| `appstore__testflight_gruplari` | Shows TestFlight beta groups, how many testers are in each, public invite links, and quotas. |
|
|
354
354
|
| `appstore__metin_guncelle` ✎ | Updates the store text of the version in preparation: release notes, description, keywords, promotional text. |
|
|
355
355
|
| `appstore__satis_raporu` | Downloads the sales/download report as TSV: daily, weekly, monthly, or yearly; sales, pre-order, installs, subscriptions, subscription events, subscribers, and offer code reports. |
|
|
356
|
-
| `appstore__abonelik_fiyat_ayarla` ✎ | Sets a subscription's price in **every country at once**: give one price (e.g. USA 9.99), Apple's equalization table fills in all ~175 countries and they're written in a single request. |
|
|
356
|
+
| `appstore__abonelik_fiyat_ayarla` ✎ | Sets a subscription's price in **every country at once**: give one price (e.g. USA 9.99), Apple's equalization table fills in all ~175 countries and they're written in a single request. Add country-specific prices on top (`{"TUR": "349.99"}`), or change only those countries and leave the rest alone. |
|
|
357
|
+
| `appstore__fiyat_analizi` | Regional pricing suggestion, writes nothing: starts from Apple's equalized price and applies a softened discount based on each country's purchasing power (World Bank). Never raises a price, never cuts more than half, leaves rich countries alone. The output plugs straight into `abonelik_fiyat_ayarla`. |
|
|
357
358
|
| `appstore__iap_fiyat_ayarla` ✎ | Sets a one-time purchase's price from a single base country; Apple fills in the rest. |
|
|
358
359
|
| `appstore__abonelik_ulkeleri` ✎ | Makes a subscription available in every country (or the ones you list) in one request. |
|
|
359
360
|
| `appstore__abonelik_kur` ✎ | Creates a subscription from scratch behind one approval: group → subscription → localized names → availability → prices in all countries. |
|
|
@@ -362,7 +363,7 @@ These tools only load when both stores are connected.
|
|
|
362
363
|
</details>
|
|
363
364
|
|
|
364
365
|
<details>
|
|
365
|
-
<summary><b>Google Play —
|
|
366
|
+
<summary><b>Google Play — 16 tools</b> (<code>play__</code>)</summary>
|
|
366
367
|
|
|
367
368
|
<br>
|
|
368
369
|
|
|
@@ -377,7 +378,13 @@ These tools only load when both stores are connected.
|
|
|
377
378
|
| `play__urunler` | Lists one-time products; tries both the new (`oneTimeProducts`) and legacy (`inappproducts`) models. |
|
|
378
379
|
| `play__satin_alma_dogrula` | Verifies a purchase token: subscription status, acknowledgment status, expiry date, whether it's a test purchase. |
|
|
379
380
|
| `play__iade_edilenler` | Lists cancelled, refunded, or charged-back purchases; turns the refund reason and source into something readable. Google only returns the last 30 days. |
|
|
380
|
-
| `play__cokme_orani` |
|
|
381
|
+
| `play__cokme_orani` | Android vitals: crash and ANR rates (including the user-perceived rates Play uses for its bad-behavior thresholds), whether a threshold is exceeded, and the most frequent crash clusters with stack traces. Can be narrowed to one version code. |
|
|
382
|
+
| `play__surum_yayinla` ✎ | Uploads an AAB/APK and releases it to a track in one approval: upload → assign to track → release notes → commit. Give a percentage for a staged rollout (e.g. production 10%). If anything fails, nothing is committed. |
|
|
383
|
+
| `play__yayin_yonet` ✎ | Manages a track: change the rollout %, halt, resume, complete, promote a release to another track (internal → production), or update only the release notes. The preview shows the track before and after. |
|
|
384
|
+
| `play__listeleme_guncelle` ✎ | Reads or updates store listing text (title, short and full description, video) for every language in one edit. |
|
|
385
|
+
| `play__gorsel_yukle` ✎ | Uploads screenshots, the icon, or the feature graphic, optionally replacing the existing ones. |
|
|
386
|
+
| `play__abonelik_fiyat_ayarla` ✎ | Sets a base plan's price in every country at once from a USD price (Google's own conversion), with optional country-specific prices on top, or changes only the countries you list. |
|
|
387
|
+
| `play__fiyat_analizi` | Regional pricing suggestion for Play, writes nothing: same purchasing-power logic as the App Store version, rounded to natural-looking local prices. |
|
|
381
388
|
|
|
382
389
|
</details>
|
|
383
390
|
|
|
@@ -425,7 +432,7 @@ any of them.
|
|
|
425
432
|
|
|
426
433
|
| | Tools | Paid on every message |
|
|
427
434
|
| --- | --- | --- |
|
|
428
|
-
| **magaza-mcp** |
|
|
435
|
+
| **magaza-mcp** | 39 | **~8,500 tokens** |
|
|
429
436
|
| If all were separate tools | 1,440 | ~210,000 tokens |
|
|
430
437
|
|
|
431
438
|
> [!NOTE]
|
|
@@ -482,7 +489,7 @@ some analytics reports need higher privileges.
|
|
|
482
489
|
the client's approval UI.
|
|
483
490
|
- **Read-only mode.** `--salt-okunur` (or `SALT_OKUNUR=1`) removes every
|
|
484
491
|
write-capable tool from the list entirely: the model can't see them or call
|
|
485
|
-
them.
|
|
492
|
+
them. 39 tools become 26. `magaza__cagir` stays in the list because it's
|
|
486
493
|
also the single gateway for read endpoints in the catalog — but it refuses
|
|
487
494
|
write operations when read-only mode is on.
|
|
488
495
|
- **Path parameters are validated.** IDs passed to tools are encoded before
|
|
@@ -500,8 +507,10 @@ some analytics reports need higher privileges.
|
|
|
500
507
|
- **No telemetry.** No analytics, error reports, or usage data are sent.
|
|
501
508
|
Network traffic only goes to `api.appstoreconnect.apple.com`,
|
|
502
509
|
`androidpublisher.googleapis.com`, `playdeveloperreporting.googleapis.com`,
|
|
503
|
-
and `oauth2.googleapis.com` for tokens.
|
|
504
|
-
|
|
510
|
+
and `oauth2.googleapis.com` for tokens. The one exception is
|
|
511
|
+
the two `fiyat_analizi` tools, which read public purchasing-power figures from
|
|
512
|
+
`api.worldbank.org` (no key, nothing of yours is sent). There's no server in
|
|
513
|
+
the middle; data flows directly between your machine and Apple/Google.
|
|
505
514
|
|
|
506
515
|
---
|
|
507
516
|
|
package/README.tr.md
CHANGED
|
@@ -62,8 +62,8 @@ Google ile konuşur, anahtarın Anahtar Zinciri'nden dışarı çıkmaz.
|
|
|
62
62
|
| [🧑💻 Elle kurulum](#-elle-kurulum) | Asistan kullanmıyorsan |
|
|
63
63
|
| [🧩 İki mağaza karışır mı?](#-iki-mağaza-karışır-mı) | Hayır — nedeni burada |
|
|
64
64
|
| [💬 Ne sorabilirsin](#-ne-sorabilirsin) | Örnek komutlar |
|
|
65
|
-
| [🧰 Araçlar](#-araçlar) |
|
|
66
|
-
| [🌐 Tam API erişimi](#-tam-api-erişimi) | 1440 operasyon, ~
|
|
65
|
+
| [🧰 Araçlar](#-araçlar) | 39 aracın tam listesi |
|
|
66
|
+
| [🌐 Tam API erişimi](#-tam-api-erişimi) | 1440 operasyon, ~8.500 token |
|
|
67
67
|
| [🔑 Gereken izinler](#-gereken-izinler) | Apple ve Google tarafında ne şart |
|
|
68
68
|
| [🔒 Güvenlik](#-güvenlik) | Anahtarlar, onay kapıları, telemetri |
|
|
69
69
|
| [🩺 Sorun giderme](#-sorun-giderme) | Sık karşılaşılan hatalar |
|
|
@@ -270,10 +270,10 @@ belleğe bile alınmaz, modele gösterilmez, token harcamaz.
|
|
|
270
270
|
|
|
271
271
|
| Kurulum | Yüklenen araç |
|
|
272
272
|
| --- | --- |
|
|
273
|
-
| İki mağaza |
|
|
274
|
-
| Yalnızca App Store |
|
|
275
|
-
| Yalnızca Play |
|
|
276
|
-
| Salt-okunur (iki mağaza) |
|
|
273
|
+
| İki mağaza | 39 |
|
|
274
|
+
| Yalnızca App Store | 20 |
|
|
275
|
+
| Yalnızca Play | 19 |
|
|
276
|
+
| Salt-okunur (iki mağaza) | 26 |
|
|
277
277
|
|
|
278
278
|
**3. Çapraz araçlar yalnızca iki mağaza da açıkken var olur.** Tek mağazalı
|
|
279
279
|
kurulumda hiç oluşturulmazlar — anlamları olmadığı için.
|
|
@@ -333,7 +333,7 @@ Bu araçlar yalnızca iki mağaza da bağlıyken yüklenir.
|
|
|
333
333
|
</details>
|
|
334
334
|
|
|
335
335
|
<details>
|
|
336
|
-
<summary><b>App Store Connect —
|
|
336
|
+
<summary><b>App Store Connect — 17 araç</b> (<code>appstore__</code>)</summary>
|
|
337
337
|
|
|
338
338
|
<br>
|
|
339
339
|
|
|
@@ -350,7 +350,8 @@ Bu araçlar yalnızca iki mağaza da bağlıyken yüklenir.
|
|
|
350
350
|
| `appstore__testflight_gruplari` | TestFlight beta gruplarını, her gruptaki test kullanıcı sayısını, genel davet linklerini ve kotalarını gösterir. |
|
|
351
351
|
| `appstore__metin_guncelle` ✎ | Hazırlanmakta olan sürümün mağaza metinlerini günceller: sürüm notları, açıklama, anahtar kelimeler, tanıtım metni. |
|
|
352
352
|
| `appstore__satis_raporu` | Satış/indirme raporunu TSV olarak indirir: günlük, haftalık, aylık veya yıllık; satış, ön sipariş, yükleme, abonelik, abonelik olayı, abone ve teklif kodu raporları. |
|
|
353
|
-
| `appstore__abonelik_fiyat_ayarla` ✎ | Aboneliğin fiyatını **bütün ülkelerde tek seferde** ayarlar: bir fiyat verirsin (örn. USA 9.99), Apple'ın eşitleme tablosu ~175 ülkenin karşılığını çıkarır, hepsi tek istekte yazılır. |
|
|
353
|
+
| `appstore__abonelik_fiyat_ayarla` ✎ | Aboneliğin fiyatını **bütün ülkelerde tek seferde** ayarlar: bir fiyat verirsin (örn. USA 9.99), Apple'ın eşitleme tablosu ~175 ülkenin karşılığını çıkarır, hepsi tek istekte yazılır. Üstüne ülkeye özel fiyat ekleyebilirsin (`{"TUR": "349.99"}`) ya da yalnızca o ülkeleri değiştirip gerisine dokunmazsın. |
|
|
354
|
+
| `appstore__fiyat_analizi` | Bölgesel fiyat önerir, hiçbir şey yazmaz: Apple'ın eşit fiyatından yola çıkıp ülkenin alım gücüne (Dünya Bankası) göre yumuşatılmış indirim uygular. Fiyat asla artmaz, yarıdan fazla düşmez, zengin ülkelere dokunulmaz. Çıktısı doğrudan `abonelik_fiyat_ayarla`'ya verilir. |
|
|
354
355
|
| `appstore__iap_fiyat_ayarla` ✎ | Tek seferlik satın almanın fiyatını tek bir taban ülkeden ayarlar; gerisini Apple doldurur. |
|
|
355
356
|
| `appstore__abonelik_ulkeleri` ✎ | Aboneliği bütün ülkelerde (ya da verdiklerinde) tek istekte satışa açar. |
|
|
356
357
|
| `appstore__abonelik_kur` ✎ | Sıfırdan abonelik kurar, tek onayla: grup → abonelik → dil metinleri → satış ülkeleri → bütün ülkelerde fiyat. |
|
|
@@ -359,7 +360,7 @@ Bu araçlar yalnızca iki mağaza da bağlıyken yüklenir.
|
|
|
359
360
|
</details>
|
|
360
361
|
|
|
361
362
|
<details>
|
|
362
|
-
<summary><b>Google Play —
|
|
363
|
+
<summary><b>Google Play — 16 araç</b> (<code>play__</code>)</summary>
|
|
363
364
|
|
|
364
365
|
<br>
|
|
365
366
|
|
|
@@ -374,7 +375,13 @@ Bu araçlar yalnızca iki mağaza da bağlıyken yüklenir.
|
|
|
374
375
|
| `play__urunler` | Tek seferlik ürünleri listeler; yeni (`oneTimeProducts`) ve eski (`inappproducts`) modelin ikisini de dener. |
|
|
375
376
|
| `play__satin_alma_dogrula` | Bir satın alma token'ını doğrular: abonelik durumu, onay durumu, bitiş tarihi, test satın alması mı. |
|
|
376
377
|
| `play__iade_edilenler` | İptal edilmiş, iade edilmiş veya geri alınmış satın almaları listeler; iade sebebini ve kaynağını okunur hale getirir. Google yalnızca son 30 günü verir. |
|
|
377
|
-
| `play__cokme_orani` | Android vitals çökme
|
|
378
|
+
| `play__cokme_orani` | Android vitals: çökme ve ANR oranları (Play'in kötü davranış eşiğinde baktığı "algılanan" oranlar dahil), eşik aşıldı mı, en sık çökme kümeleri ve stack trace'leri. Tek bir sürüm koduna daraltılabilir. |
|
|
379
|
+
| `play__surum_yayinla` ✎ | AAB/APK'yı yükleyip bir kanala yayınlar, tek onayla: yükle → kanala ata → sürüm notları → commit. Yüzde verirsen kademeli yayın (örn. production %10). Bir adım düşerse hiçbir şey commit edilmez. |
|
|
380
|
+
| `play__yayin_yonet` ✎ | Kanalı yönetir: rollout yüzdesini değiştir, durdur, sürdür, tamamla, sürümü başka kanala taşı (internal → production) ya da yalnızca sürüm notlarını güncelle. Önizlemede kanalın önceki ve sonraki hali görünür. |
|
|
381
|
+
| `play__listeleme_guncelle` ✎ | Mağaza metinlerini (başlık, kısa ve tam açıklama, video) okur ya da bütün dillerde tek oturumda günceller. |
|
|
382
|
+
| `play__gorsel_yukle` ✎ | Ekran görüntüsü, ikon veya öne çıkan görsel yükler; istersen eskilerini önce siler. |
|
|
383
|
+
| `play__abonelik_fiyat_ayarla` ✎ | Bir temel planın fiyatını USD fiyattan bütün ülkelerde tek seferde ayarlar (Google'ın kendi çevirisiyle); üstüne ülkeye özel fiyat eklenebilir ya da yalnızca verdiğin ülkeler değişir. |
|
|
384
|
+
| `play__fiyat_analizi` | Play için bölgesel fiyat önerir, hiçbir şey yazmaz: App Store'dakiyle aynı alım gücü mantığı, mağazada doğal görünen yerel fiyatlara yuvarlanır. |
|
|
378
385
|
|
|
379
386
|
</details>
|
|
380
387
|
|
|
@@ -420,7 +427,7 @@ hiçbirini kullanmasan bile her mesajda yeniden ödenir.
|
|
|
420
427
|
|
|
421
428
|
| | Araç | Her mesajda ödenen |
|
|
422
429
|
| --- | --- | --- |
|
|
423
|
-
| **magaza-mcp** |
|
|
430
|
+
| **magaza-mcp** | 39 | **~8.500 token** |
|
|
424
431
|
| Hepsi ayrı araç olsaydı | 1440 | ~210.000 token |
|
|
425
432
|
|
|
426
433
|
> [!NOTE]
|
|
@@ -472,8 +479,8 @@ analitik raporlar daha yüksek yetki ister.
|
|
|
472
479
|
onayladıktan sonra `onayla=true` ile tekrarlandığında yürür. Bu **sunucu
|
|
473
480
|
tarafında** uygulanır — istemcinin onay arayüzüne bağlı değildir.
|
|
474
481
|
- **Salt-okunur mod.** `--salt-okunur` (veya `SALT_OKUNUR=1`) yazma yapan
|
|
475
|
-
araçları listeden tamamen çıkarır: model onları göremez, çağıramaz.
|
|
476
|
-
|
|
482
|
+
araçları listeden tamamen çıkarır: model onları göremez, çağıramaz. 39 araç
|
|
483
|
+
26'ya düşer. `magaza__cagir` listede kalır çünkü okuma uçlarının da tek
|
|
477
484
|
kapısıdır — ama yazma operasyonu istendiğinde reddeder.
|
|
478
485
|
- **Yol parametreleri doğrulanır.** Araçlara verilen kimlikler URL'e girmeden
|
|
479
486
|
önce kodlanır; `.` ve `..` gibi yol gezinme denemeleri reddedilir. Mutlak
|
|
@@ -490,8 +497,11 @@ analitik raporlar daha yüksek yetki ister.
|
|
|
490
497
|
- **Telemetri yok.** Hiçbir analitik, hata raporu veya kullanım verisi
|
|
491
498
|
gönderilmez. Ağ trafiği yalnızca `api.appstoreconnect.apple.com`,
|
|
492
499
|
`androidpublisher.googleapis.com`, `playdeveloperreporting.googleapis.com` ve
|
|
493
|
-
token için `oauth2.googleapis.com` adreslerine gider.
|
|
494
|
-
|
|
500
|
+
token için `oauth2.googleapis.com` adreslerine gider. Tek istisna iki
|
|
501
|
+
`fiyat_analizi` aracı: herkese açık alım gücü verisini
|
|
502
|
+
`api.worldbank.org`'dan okur (anahtarsız, senden hiçbir veri gitmez). Araya
|
|
503
|
+
giren bir sunucu yoktur; veri doğrudan senin makinenle Apple ve Google
|
|
504
|
+
arasında akar.
|
|
495
505
|
|
|
496
506
|
---
|
|
497
507
|
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ülkelerin fiyat seviyesi — bölgesel fiyat önerileri için.
|
|
3
|
+
*
|
|
4
|
+
* App Store ve Play analiz araçları ortak kullanır.
|
|
5
|
+
*/
|
|
6
|
+
let _alimGucu;
|
|
7
|
+
/**
|
|
8
|
+
* Dünya Bankası'ndan ülkelerin fiyat seviyesi verisini çeker.
|
|
9
|
+
*
|
|
10
|
+
* Hane tüketimi PPP çevrim katsayısı / resmi kur: ABD = 1, Türkiye ≈ 0.45,
|
|
11
|
+
* Hindistan ≈ 0.23. Anahtarsız, herkese açık veri; bizden hiçbir şey gitmez.
|
|
12
|
+
*/
|
|
13
|
+
export function alimGucuVerisi() {
|
|
14
|
+
const getir = async (gosterge) => {
|
|
15
|
+
const yanit = await fetch(`https://api.worldbank.org/v2/country/all/indicator/${gosterge}?format=json&mrnev=1&per_page=500`, { signal: AbortSignal.timeout(10_000) });
|
|
16
|
+
const [, veri] = (await yanit.json());
|
|
17
|
+
if (!Array.isArray(veri))
|
|
18
|
+
throw new Error(`Dünya Bankası ${gosterge} verisi gelmedi.`);
|
|
19
|
+
return new Map(veri
|
|
20
|
+
.filter((x) => x?.value != null && x?.countryiso3code)
|
|
21
|
+
// Apple ülkeleri 3 harfle (TUR), Play 2 harfle (TR) yazıyor; ikisini de anahtar yapıyoruz.
|
|
22
|
+
.flatMap((x) => {
|
|
23
|
+
const kayit = { deger: Number(x.value), yil: String(x.date) };
|
|
24
|
+
return [[x.countryiso3code, kayit], [x.country?.id, kayit]];
|
|
25
|
+
})
|
|
26
|
+
.filter(([u]) => !!u));
|
|
27
|
+
};
|
|
28
|
+
_alimGucu ??= Promise.all([getir("PA.NUS.PRVT.PP"), getir("PA.NUS.FCRF")])
|
|
29
|
+
.then(([ppp, kur]) => ({ ppp, kur }))
|
|
30
|
+
.catch((hata) => {
|
|
31
|
+
_alimGucu = undefined;
|
|
32
|
+
throw new Error(`Alım gücü verisi alınamadı (api.worldbank.org): ${hata.message}`);
|
|
33
|
+
});
|
|
34
|
+
return _alimGucu;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Bir ülkenin fiyat seviyesi (ABD = 1).
|
|
38
|
+
*
|
|
39
|
+
* Mağaza avroyla satıyorsa avro kuru kullanılır: avroya yeni geçen ülkelerde
|
|
40
|
+
* (Bulgaristan 2026) PPP avroya çevrilmiş ama kur serisi eski parada kalmış,
|
|
41
|
+
* oran yarıya düşüyordu. İki seri farklı yıldansa (Arjantin'de PPP 2021, kur
|
|
42
|
+
* 2025) oran anlamsız; o ülke atlanır.
|
|
43
|
+
*/
|
|
44
|
+
export function fiyatSeviyesi(v, ulke, para) {
|
|
45
|
+
const p = v.ppp.get(ulke);
|
|
46
|
+
const k = para === "EUR" ? v.kur.get("DEU") : v.kur.get(ulke);
|
|
47
|
+
if (!p || !k || k.yil !== p.yil || !(k.deger > 0))
|
|
48
|
+
return undefined;
|
|
49
|
+
return p.deger / k.deger;
|
|
50
|
+
}
|
|
@@ -6,10 +6,14 @@
|
|
|
6
6
|
* 175 ülke × (önizleme + onay) ≈ 350 araç çağrısı. Buradaki araçlar döngüyü
|
|
7
7
|
* sunucuda koşturur; model yalnızca bir önizleme ve bir onay görür.
|
|
8
8
|
*
|
|
9
|
+
* appstore__fiyat_analizi da burada: yazmaz, ama ülkeye özel fiyat önerisini
|
|
10
|
+
* abonelik_fiyat_ayarla'nın doğrudan kabul ettiği biçimde üretir.
|
|
11
|
+
*
|
|
9
12
|
* Yollar ve gövdeler Apple OpenAPI 4.5'e karşı doğrulandı; standart
|
|
10
13
|
* aboneliğin planType'ı canlı hesapta UPFRONT okundu.
|
|
11
14
|
*/
|
|
12
15
|
import { cagir, tumunuGetir } from "../istemci/apple.js";
|
|
16
|
+
import { alimGucuVerisi, fiyatSeviyesi } from "./alim-gucu.js";
|
|
13
17
|
import { kimlik, metin, onayIste, onaySemasi, parca, ulkeKodu } from "./appstore.js";
|
|
14
18
|
/** Fiyat noktası uçları tek sayfada 8000 kayda izin verir (ABD'de ~800 nokta). */
|
|
15
19
|
const FIYAT_SAYFASI = 8000;
|
|
@@ -328,6 +332,79 @@ async function beyanOku(appInfoId) {
|
|
|
328
332
|
return yanit?.data;
|
|
329
333
|
}
|
|
330
334
|
/* ------------------------------------------------------------------ */
|
|
335
|
+
/* Ülkeye özel fiyat */
|
|
336
|
+
/* ------------------------------------------------------------------ */
|
|
337
|
+
/**
|
|
338
|
+
* Birden çok ülkenin fiyat basamaklarını getirir.
|
|
339
|
+
*
|
|
340
|
+
* `filter[territory]` liste kabul ediyor; ülke başına ~800-850 basamak var,
|
|
341
|
+
* 9 ülke tek 8000'lik sayfaya sığıyor. 120 ülke = 14 istek, altışar paralel.
|
|
342
|
+
*/
|
|
343
|
+
async function ulkeNoktalari(aboneId, ulkeler) {
|
|
344
|
+
const gruplar = [];
|
|
345
|
+
for (let i = 0; i < ulkeler.length; i += 9)
|
|
346
|
+
gruplar.push(ulkeler.slice(i, i + 9));
|
|
347
|
+
const sonuc = new Map();
|
|
348
|
+
for (let i = 0; i < gruplar.length; i += 6) {
|
|
349
|
+
const sayfalar = await Promise.all(gruplar.slice(i, i + 6).map((grup) => tumunuGetir(`/v1/subscriptions/${parca(aboneId, "abonelik_id")}/pricePoints`, {
|
|
350
|
+
"filter[territory]": grup.join(","),
|
|
351
|
+
include: "territory",
|
|
352
|
+
"fields[subscriptionPricePoints]": "customerPrice,proceeds,territory",
|
|
353
|
+
"fields[territories]": "currency",
|
|
354
|
+
}, FIYAT_SAYFASI * 2, FIYAT_SAYFASI)));
|
|
355
|
+
for (const { data, included } of sayfalar) {
|
|
356
|
+
const paralar = new Map();
|
|
357
|
+
for (const t of included)
|
|
358
|
+
if (t?.type === "territories")
|
|
359
|
+
paralar.set(t.id, t.attributes?.currency);
|
|
360
|
+
for (const n of data) {
|
|
361
|
+
const u = n.relationships?.territory?.data?.id;
|
|
362
|
+
if (!u)
|
|
363
|
+
continue;
|
|
364
|
+
if (!sonuc.has(u))
|
|
365
|
+
sonuc.set(u, []);
|
|
366
|
+
sonuc.get(u).push({
|
|
367
|
+
ulke: u,
|
|
368
|
+
nokta: n.id,
|
|
369
|
+
fiyat: n.attributes?.customerPrice,
|
|
370
|
+
gelir: n.attributes?.proceeds,
|
|
371
|
+
para: paralar.get(u),
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
return sonuc;
|
|
377
|
+
}
|
|
378
|
+
function enYakin(noktalar, hedef) {
|
|
379
|
+
return noktalar.reduce((a, b) => Math.abs(Number(b.fiyat) - hedef) < Math.abs(Number(a.fiyat) - hedef) ? b : a);
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Ülkeye özel fiyatları geçerli basamaklara oturtur.
|
|
383
|
+
*
|
|
384
|
+
* Apple keyfi fiyat kabul etmiyor ("150 TL" yok, 149.99 var); en yakın
|
|
385
|
+
* basamak seçilir ve önizlemede istenenle birlikte gösterilir.
|
|
386
|
+
*/
|
|
387
|
+
async function ozelSatirlar(aboneId, ham) {
|
|
388
|
+
if (!ham || typeof ham !== "object" || Array.isArray(ham)) {
|
|
389
|
+
throw new Error("ozel_fiyatlar bir nesne olmalı, örn. {\"TUR\": \"349.99\", \"IND\": \"499\"}.");
|
|
390
|
+
}
|
|
391
|
+
const istenen = new Map();
|
|
392
|
+
for (const [u, f] of Object.entries(ham))
|
|
393
|
+
istenen.set(ulkeKodu(u), fiyatSayisi(f, `${u} fiyatı`));
|
|
394
|
+
const noktalar = await ulkeNoktalari(aboneId, [...istenen.keys()]);
|
|
395
|
+
const satirlar = [];
|
|
396
|
+
const notlar = [];
|
|
397
|
+
for (const [u, f] of istenen) {
|
|
398
|
+
const liste = noktalar.get(u);
|
|
399
|
+
if (!liste?.length)
|
|
400
|
+
throw new Error(`${u} için fiyat basamağı bulunamadı; ülke kodu doğru mu (3 harf, örn. TUR)?`);
|
|
401
|
+
const s = enYakin(liste, f);
|
|
402
|
+
satirlar.push(s);
|
|
403
|
+
notlar.push(`${u}: ${s.fiyat} ${s.para ?? ""}`.trim() + (Number(s.fiyat) !== f ? ` (istenen ${f}, en yakın basamak)` : ""));
|
|
404
|
+
}
|
|
405
|
+
return { satirlar, notlar };
|
|
406
|
+
}
|
|
407
|
+
/* ------------------------------------------------------------------ */
|
|
331
408
|
/* Araçlar */
|
|
332
409
|
/* ------------------------------------------------------------------ */
|
|
333
410
|
// Tip birleşimi (string|number) bazı istemcilerin şema denetiminden geçmiyor;
|
|
@@ -342,17 +419,26 @@ export function appstoreYazmaAraclari() {
|
|
|
342
419
|
return [
|
|
343
420
|
{
|
|
344
421
|
ad: "appstore__abonelik_fiyat_ayarla",
|
|
345
|
-
aciklama: "Bir aboneliğin fiyatını
|
|
346
|
-
"
|
|
347
|
-
"
|
|
348
|
-
"
|
|
349
|
-
"
|
|
422
|
+
aciklama: "Bir aboneliğin fiyatını ayarlar; döngü sunucuda, tek onayla. Ülke ülke " +
|
|
423
|
+
"magaza__cagir ile subscriptionPrices yazma, bu araç onun yerine geçer.\n" +
|
|
424
|
+
"- fiyat: bir ülkedeki fiyat (varsayılan USA); Apple'ın eşitleme tablosu " +
|
|
425
|
+
"~175 ülkenin karşılığını çıkarır, hepsi yazılır.\n" +
|
|
426
|
+
"- ozel_fiyatlar: {\"TUR\": \"349.99\"} gibi ülkeye özel fiyatlar. fiyat ile " +
|
|
427
|
+
"birlikte verilirse eşitlemenin üstüne yazılır; tek başına verilirse yalnızca " +
|
|
428
|
+
"o ülkeler değişir, diğerlerine dokunulmaz.\n" +
|
|
429
|
+
"Fiyat en yakın geçerli Apple basamağına oturtulur ve önizlemede gösterilir.",
|
|
350
430
|
girdiSemasi: {
|
|
351
431
|
type: "object",
|
|
352
432
|
properties: {
|
|
353
433
|
abonelik_id: metin("appstore__abonelikler'den gelen abonelik id'si."),
|
|
354
434
|
fiyat: fiyatSemasi,
|
|
355
435
|
ulke: ulkeSemasi,
|
|
436
|
+
ozel_fiyatlar: {
|
|
437
|
+
type: "object",
|
|
438
|
+
description: "Ülke kodu → o ülkenin para biriminde fiyat, örn. {\"TUR\": \"349.99\", " +
|
|
439
|
+
"\"IND\": \"499\"}. appstore__fiyat_analizi'nin ozel_fiyatlar çıktısı olduğu gibi verilebilir.",
|
|
440
|
+
additionalProperties: { type: "string" },
|
|
441
|
+
},
|
|
356
442
|
mevcut_aboneleri_koru: {
|
|
357
443
|
type: "boolean",
|
|
358
444
|
description: "Fiyat değişikliğinde mevcut aboneler eski fiyattan devam etsin mi " +
|
|
@@ -361,19 +447,34 @@ export function appstoreYazmaAraclari() {
|
|
|
361
447
|
baslangic: baslangicSemasi,
|
|
362
448
|
onayla: onaySemasi,
|
|
363
449
|
},
|
|
364
|
-
required: ["abonelik_id"
|
|
450
|
+
required: ["abonelik_id"],
|
|
365
451
|
},
|
|
366
452
|
yazma: true,
|
|
367
453
|
async calistir(girdi) {
|
|
368
|
-
const
|
|
454
|
+
const tabanVar = girdi.fiyat !== undefined && girdi.fiyat !== "";
|
|
455
|
+
if (!tabanVar && girdi.ozel_fiyatlar === undefined) {
|
|
456
|
+
throw new Error("fiyat ya da ozel_fiyatlar vermelisin.");
|
|
457
|
+
}
|
|
458
|
+
const fiyat = tabanVar ? fiyatSayisi(girdi.fiyat) : undefined;
|
|
369
459
|
const ulke = ulkeKodu(girdi.ulke) ?? "USA";
|
|
370
460
|
const koru = girdi.mevcut_aboneleri_koru !== false;
|
|
371
461
|
const baslangic = tarih(girdi.baslangic);
|
|
372
|
-
const
|
|
462
|
+
const [esit, ozel] = await Promise.all([
|
|
463
|
+
fiyat !== undefined ? abonelikMatrisi(girdi.abonelik_id, fiyat, ulke) : [],
|
|
464
|
+
girdi.ozel_fiyatlar !== undefined
|
|
465
|
+
? ozelSatirlar(girdi.abonelik_id, girdi.ozel_fiyatlar)
|
|
466
|
+
: { satirlar: [], notlar: [] },
|
|
467
|
+
]);
|
|
468
|
+
const ozelUlkeler = new Set(ozel.satirlar.map((s) => s.ulke));
|
|
469
|
+
const matris = [...esit.filter((s) => !ozelUlkeler.has(s.ulke)), ...ozel.satirlar];
|
|
373
470
|
if (girdi.onayla !== true) {
|
|
374
|
-
return onayIste(`PATCH /v1/subscriptions/${girdi.abonelik_id} (${matris.length} ülke)`,
|
|
471
|
+
return onayIste(`PATCH /v1/subscriptions/${girdi.abonelik_id} (${matris.length} ülke)`, fiyat !== undefined
|
|
472
|
+
? `Aboneliğin fiyatını ${ulke} ${fiyat} baz alınarak ${matris.length} ülkede ayarlar` +
|
|
473
|
+
(ozel.satirlar.length ? `; ${ozel.satirlar.length} ülkede özel fiyat.` : ".")
|
|
474
|
+
: `Yalnızca ${matris.length} ülkenin fiyatını değiştirir; diğer ülkelere dokunmaz.`, {
|
|
375
475
|
ulke_sayisi: matris.length,
|
|
376
|
-
ornek_fiyatlar: ornekler(
|
|
476
|
+
ornek_fiyatlar: fiyat !== undefined ? ornekler(esit) : undefined,
|
|
477
|
+
ozel_fiyatlar: ozel.notlar.length ? ozel.notlar : undefined,
|
|
377
478
|
mevcut_aboneleri_koru: koru,
|
|
378
479
|
baslangic: baslangic ?? "hemen",
|
|
379
480
|
});
|
|
@@ -381,6 +482,89 @@ export function appstoreYazmaAraclari() {
|
|
|
381
482
|
return abonelikFiyatYaz(girdi.abonelik_id, matris, koru, baslangic);
|
|
382
483
|
},
|
|
383
484
|
},
|
|
485
|
+
{
|
|
486
|
+
ad: "appstore__fiyat_analizi",
|
|
487
|
+
aciklama: "Ülkelere özel fiyat önerir (bölgesel fiyatlandırma); hiçbir şey yazmaz. Bir " +
|
|
488
|
+
"taban fiyattan (örn. USA 9.99) Apple'ın eşitlediği yerel fiyatı alır, ülkenin " +
|
|
489
|
+
"alım gücüne (Dünya Bankası fiyat seviyesi) göre yumuşatılmış bir indirim " +
|
|
490
|
+
"hesaplar ve en yakın geçerli Apple basamağına oturtur. Fiyatı öldürmemek için " +
|
|
491
|
+
"indirim sınırlı: zengin ülkelere dokunulmaz, hiçbir ülke eşit fiyatın " +
|
|
492
|
+
"en_dusuk_oran'ının altına inmez, hiçbir fiyat artırılmaz. Öneriyi kullanıcıya " +
|
|
493
|
+
"göster, isterse ülke ülke düzelt, sonra appstore__abonelik_fiyat_ayarla'ya " +
|
|
494
|
+
"fiyat + ozel_fiyatlar olarak ver.",
|
|
495
|
+
girdiSemasi: {
|
|
496
|
+
type: "object",
|
|
497
|
+
properties: {
|
|
498
|
+
abonelik_id: metin("appstore__abonelikler'den gelen abonelik id'si."),
|
|
499
|
+
fiyat: fiyatSemasi,
|
|
500
|
+
ulke: ulkeSemasi,
|
|
501
|
+
yansitma: {
|
|
502
|
+
type: "number",
|
|
503
|
+
description: "Alım gücü farkının ne kadarı fiyata yansısın, 0-1 (varsayılan 0.4). " +
|
|
504
|
+
"0 = indirim yok, 1 = farkın tamamı (agresif). 0.4'te Türkiye ~%27, Hindistan ~%44 iner.",
|
|
505
|
+
},
|
|
506
|
+
en_dusuk_oran: {
|
|
507
|
+
type: "number",
|
|
508
|
+
description: "Hiçbir ülke eşit fiyatın bu oranının altına inmesin (varsayılan 0.5 = en fazla %50 indirim).",
|
|
509
|
+
},
|
|
510
|
+
},
|
|
511
|
+
required: ["abonelik_id", "fiyat"],
|
|
512
|
+
},
|
|
513
|
+
async calistir(girdi) {
|
|
514
|
+
const fiyat = fiyatSayisi(girdi.fiyat);
|
|
515
|
+
const ulke = ulkeKodu(girdi.ulke) ?? "USA";
|
|
516
|
+
const yansitma = Math.min(1, Math.max(0, Number(girdi.yansitma ?? 0.4) || 0));
|
|
517
|
+
const taban = Math.min(1, Math.max(0.1, Number(girdi.en_dusuk_oran ?? 0.5) || 0.5));
|
|
518
|
+
const [matris, veri] = await Promise.all([
|
|
519
|
+
abonelikMatrisi(girdi.abonelik_id, fiyat, ulke),
|
|
520
|
+
alimGucuVerisi(),
|
|
521
|
+
]);
|
|
522
|
+
const cipaOrani = fiyatSeviyesi(veri, ulke, matris[0]?.para) ?? 1;
|
|
523
|
+
// %15'ten küçük indirim gürültü: İtalya'yı %12 indirmek geliri düşürür, satışı
|
|
524
|
+
// pek artırmaz. Batı Avrupa ve Japonya eşit fiyatta kalır.
|
|
525
|
+
const adaylar = matris
|
|
526
|
+
.map((s) => {
|
|
527
|
+
const o = fiyatSeviyesi(veri, s.ulke, s.para);
|
|
528
|
+
if (!o || s.ulke === ulke)
|
|
529
|
+
return undefined;
|
|
530
|
+
const goreli = o / cipaOrani;
|
|
531
|
+
const carpan = Math.min(1, Math.max(taban, goreli ** yansitma));
|
|
532
|
+
return carpan <= 0.85 ? { s, goreli, carpan } : undefined;
|
|
533
|
+
})
|
|
534
|
+
.filter((x) => x !== undefined);
|
|
535
|
+
const noktalar = await ulkeNoktalari(girdi.abonelik_id, adaylar.map((a) => a.s.ulke));
|
|
536
|
+
const oneriler = [];
|
|
537
|
+
for (const { s, goreli, carpan } of adaylar) {
|
|
538
|
+
const liste = noktalar.get(s.ulke);
|
|
539
|
+
if (!liste?.length)
|
|
540
|
+
continue;
|
|
541
|
+
const yeni = enYakin(liste, Number(s.fiyat) * carpan);
|
|
542
|
+
const indirim = Math.round((1 - Number(yeni.fiyat) / Number(s.fiyat)) * 100);
|
|
543
|
+
if (indirim < 10)
|
|
544
|
+
continue;
|
|
545
|
+
oneriler.push({
|
|
546
|
+
ulke: s.ulke,
|
|
547
|
+
fiyat: yeni.fiyat,
|
|
548
|
+
indirim,
|
|
549
|
+
satir: `${s.ulke}: ${s.fiyat} → ${yeni.fiyat} ${s.para ?? ""} ` +
|
|
550
|
+
`(-%${indirim}, alım gücü ${goreli.toFixed(2)})`,
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
oneriler.sort((a, b) => b.indirim - a.indirim);
|
|
554
|
+
return {
|
|
555
|
+
taban: `${ulke} ${fiyat}`,
|
|
556
|
+
esit_kalan_ulke: matris.length - oneriler.length,
|
|
557
|
+
indirimli_ulke: oneriler.length,
|
|
558
|
+
oneriler: oneriler.map((o) => o.satir),
|
|
559
|
+
ozel_fiyatlar: Object.fromEntries(oneriler.map((o) => [o.ulke, o.fiyat])),
|
|
560
|
+
ayarlar: { yansitma, en_dusuk_oran: taban },
|
|
561
|
+
kaynak: "Alım gücü: Dünya Bankası, hane tüketimi PPP / resmi kur (en güncel yıl). " +
|
|
562
|
+
"Verisi olmayan ülkeler (ör. Tayvan) eşit fiyatta kalır.",
|
|
563
|
+
not: "Hiçbir şey yazılmadı. Kullanıcıya önerileri göster; onaylarsa " +
|
|
564
|
+
"appstore__abonelik_fiyat_ayarla'ya fiyat ve bu ozel_fiyatlar ile çağır.",
|
|
565
|
+
};
|
|
566
|
+
},
|
|
567
|
+
},
|
|
384
568
|
{
|
|
385
569
|
ad: "appstore__iap_fiyat_ayarla",
|
|
386
570
|
aciklama: "Tek seferlik uygulama içi satın almanın fiyatını ayarlar. Bir ülkedeki fiyatı " +
|
|
@@ -121,7 +121,7 @@ const YAZMA_YONTEMLERI = new Set(["POST", "PATCH", "PUT", "DELETE"]);
|
|
|
121
121
|
* çalışmaz. `:batchMigratePrices` gibi gerçek toplu yazma uçları bu listeye
|
|
122
122
|
* girmesin diye ek adı tam olarak eşleştiriliyor.
|
|
123
123
|
*/
|
|
124
|
-
const OKUYAN_POST_EKLERI = [":query", ":batchGet", ":search", ":lookup"];
|
|
124
|
+
const OKUYAN_POST_EKLERI = [":query", ":batchGet", ":search", ":lookup", ":convertRegionPrices"];
|
|
125
125
|
/**
|
|
126
126
|
* İkinci emniyet: ekin adı kadar operasyonun kendi açıklaması da okuma
|
|
127
127
|
* demeli. Google ileride `:batchGet` gibi görünen ama yazan bir uç eklerse
|
|
@@ -129,7 +129,7 @@ const OKUYAN_POST_EKLERI = [":query", ":batchGet", ":search", ":lookup"];
|
|
|
129
129
|
* "Reads / Queries / Searches / Gets / Lists" ile başlamıyorsa yazma sayıp
|
|
130
130
|
* onay isteriz. Bugünkü 19 ucun 19'u bu kalıba uyuyor.
|
|
131
131
|
*/
|
|
132
|
-
const OKUMA_OZETI = /^(read|quer|search|look ?up|get|list|fetch|retriev)/i;
|
|
132
|
+
const OKUMA_OZETI = /^(read|quer|search|look ?up|get|list|fetch|retriev|calculat)/i;
|
|
133
133
|
function yazmaIslemiMi(op) {
|
|
134
134
|
if (!YAZMA_YONTEMLERI.has(op.yontem))
|
|
135
135
|
return false;
|
|
@@ -184,7 +184,8 @@ export function dispatchAraclari() {
|
|
|
184
184
|
"yöntemini döndürür. Sonucu magaza__cagir ile çalıştırabilirsin. " +
|
|
185
185
|
"Örnek sorgular: 'subscription price', 'testflight tester', 'crash rate'. " +
|
|
186
186
|
"Abonelik/IAP fiyatı, abonelik kurulumu ve yaş derecelendirmesi için " +
|
|
187
|
-
"hazır appstore__
|
|
187
|
+
"hazır appstore__ araçları; Play'de sürüm yayınlama, rollout, mağaza " +
|
|
188
|
+
"metni, görsel ve abonelik fiyatı için hazır play__ araçları var. Onları tercih et.",
|
|
188
189
|
girdiSemasi: {
|
|
189
190
|
type: "object",
|
|
190
191
|
properties: {
|
|
@@ -293,6 +294,11 @@ export function dispatchAraclari() {
|
|
|
293
294
|
? `\nBunlar mı demek istedin?\n ${yakin.map((y) => y.ad).join("\n ")}`
|
|
294
295
|
: "\nmagaza__endpoint_ara ile arayabilirsin."));
|
|
295
296
|
}
|
|
297
|
+
// Katalogdaki yükleme uçları ham dosya baytı ister; JSON gövdeyle hep düşüyordu.
|
|
298
|
+
if (magaza === "play" && /\.upload[a-z]*$/i.test(op.ad)) {
|
|
299
|
+
throw new Error(`'${op.ad}' dosya yükler; magaza__cagir dosya gönderemez. AAB/APK için ` +
|
|
300
|
+
"play__surum_yayinla, ekran görüntüsü ve ikon için play__gorsel_yukle kullan.");
|
|
301
|
+
}
|
|
296
302
|
const yazma = yazmaIslemiMi(op);
|
|
297
303
|
if (yazma && magaza === "appstore")
|
|
298
304
|
govdeTamamla(op, girdi);
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { cagir as appleCagir } from "../istemci/apple.js";
|
|
9
9
|
import { cagir as playCagir } from "../istemci/play.js";
|
|
10
|
-
import { yolParcasi } from "./play.js";
|
|
10
|
+
import { paraBicimle, yolParcasi } from "./play.js";
|
|
11
11
|
import { acikMagazalar } from "./tip.js";
|
|
12
12
|
const metin = (aciklama) => ({ type: "string", description: aciklama });
|
|
13
13
|
/** Bir isteği çalıştırır; patlarsa hatayı sonuç olarak döndürür. */
|
|
@@ -254,11 +254,11 @@ export function ortakAraclar() {
|
|
|
254
254
|
plan_id: p.basePlanId,
|
|
255
255
|
isim: a.listings?.[0]?.title,
|
|
256
256
|
sure: p.autoRenewingBasePlanType?.billingPeriodDuration ??
|
|
257
|
-
p.prepaidBasePlanType?.billingPeriodDuration
|
|
257
|
+
p.prepaidBasePlanType?.billingPeriodDuration ??
|
|
258
|
+
p.installmentsBasePlanType?.billingPeriodDuration,
|
|
258
259
|
durum: p.state,
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
: undefined,
|
|
260
|
+
// toFixed(2) JPY'de kuruş uyduruyor, TND'de (3 hane) yuvarlıyordu.
|
|
261
|
+
fiyat: paraBicimle(bolge?.price),
|
|
262
262
|
};
|
|
263
263
|
}));
|
|
264
264
|
return { uygulama: uygulama.displayName, abonelikler };
|