xync-db 0.5.6__tar.gz → 0.6.1.dev1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {xync_db-0.5.6 → xync_db-0.6.1.dev1}/PKG-INFO +1 -1
- {xync_db-0.5.6 → xync_db-0.6.1.dev1}/src/xync_db/enums.py +2 -3
- {xync_db-0.5.6 → xync_db-0.6.1.dev1}/src/xync_db/migrations/CLAUDE.md +2 -2
- {xync_db-0.5.6 → xync_db-0.6.1.dev1}/src/xync_db/migrations/models/0001_initial.py +45 -6
- {xync_db-0.5.6 → xync_db-0.6.1.dev1}/src/xync_db/migrations/models/0003_seed.py +6 -3
- xync_db-0.6.1.dev1/src/xync_db/migrations/models/seed/a_city_seed.py +23 -0
- xync_db-0.6.1.dev1/src/xync_db/migrations/models/seed/cities.json +337 -0
- {xync_db-0.5.6 → xync_db-0.6.1.dev1}/src/xync_db/models/CLAUDE.md +7 -5
- {xync_db-0.5.6 → xync_db-0.6.1.dev1}/src/xync_db/models/__init__.py +24 -9
- {xync_db-0.5.6 → xync_db-0.6.1.dev1}/.gitignore +0 -0
- {xync_db-0.5.6 → xync_db-0.6.1.dev1}/README.md +0 -0
- {xync_db-0.5.6 → xync_db-0.6.1.dev1}/pyproject.toml +0 -0
- {xync_db-0.5.6 → xync_db-0.6.1.dev1}/src/xync_db/__init__.py +0 -0
- {xync_db-0.5.6 → xync_db-0.6.1.dev1}/src/xync_db/config.py +0 -0
- {xync_db-0.5.6 → xync_db-0.6.1.dev1}/src/xync_db/exceptions.py +0 -0
- {xync_db-0.5.6 → xync_db-0.6.1.dev1}/src/xync_db/graph.py +0 -0
- {xync_db-0.5.6 → xync_db-0.6.1.dev1}/src/xync_db/i18n.py +0 -0
- {xync_db-0.5.6 → xync_db-0.6.1.dev1}/src/xync_db/login_store.py +0 -0
- {xync_db-0.5.6 → xync_db-0.6.1.dev1}/src/xync_db/logo.png +0 -0
- {xync_db-0.5.6 → xync_db-0.6.1.dev1}/src/xync_db/migrations/__init__.py +0 -0
- {xync_db-0.5.6 → xync_db-0.6.1.dev1}/src/xync_db/migrations/models/0002_triggers.py +0 -0
- {xync_db-0.5.6 → xync_db-0.6.1.dev1}/src/xync_db/migrations/models/__init__.py +0 -0
- {xync_db-0.5.6 → xync_db-0.6.1.dev1}/src/xync_db/migrations/models/seed/__init__.py +0 -0
- {xync_db-0.5.6 → xync_db-0.6.1.dev1}/src/xync_db/migrations/models/seed/a_actor_seed.py +0 -0
- {xync_db-0.5.6 → xync_db-0.6.1.dev1}/src/xync_db/migrations/models/seed/a_bin_seed.py +0 -0
- {xync_db-0.5.6 → xync_db-0.6.1.dev1}/src/xync_db/migrations/models/seed/a_country_seed.py +0 -0
- {xync_db-0.5.6 → xync_db-0.6.1.dev1}/src/xync_db/migrations/models/seed/a_tg_seed.py +0 -0
- {xync_db-0.5.6 → xync_db-0.6.1.dev1}/src/xync_db/migrations/models/seed/b_htx_seed.py +0 -0
- {xync_db-0.5.6 → xync_db-0.6.1.dev1}/src/xync_db/migrations/models/seed/c_net_seed.py +0 -0
- {xync_db-0.5.6 → xync_db-0.6.1.dev1}/src/xync_db/migrations/models/seed/countries.json +0 -0
- {xync_db-0.5.6 → xync_db-0.6.1.dev1}/src/xync_db/migrations/models/seed/ranges.csv +0 -0
- {xync_db-0.5.6 → xync_db-0.6.1.dev1}/src/xync_db/migrations/models/seed/xync_files.sql +0 -0
- {xync_db-0.5.6 → xync_db-0.6.1.dev1}/src/xync_db/py.typed +0 -0
- {xync_db-0.5.6 → xync_db-0.6.1.dev1}/src/xync_db/shared.py +0 -0
- {xync_db-0.5.6 → xync_db-0.6.1.dev1}/src/xync_db/typs/__init__.py +0 -0
- {xync_db-0.5.6 → xync_db-0.6.1.dev1}/src/xync_db/typs/db/__init__.py +0 -0
- {xync_db-0.5.6 → xync_db-0.6.1.dev1}/src/xync_db/typs/db/actor.py +0 -0
- {xync_db-0.5.6 → xync_db-0.6.1.dev1}/src/xync_db/typs/db/ad.py +0 -0
- {xync_db-0.5.6 → xync_db-0.6.1.dev1}/src/xync_db/typs/db/common.py +0 -0
- {xync_db-0.5.6 → xync_db-0.6.1.dev1}/src/xync_db/typs/db/cred.py +0 -0
- {xync_db-0.5.6 → xync_db-0.6.1.dev1}/src/xync_db/typs/db/order.py +0 -0
- {xync_db-0.5.6 → xync_db-0.6.1.dev1}/src/xync_db/typs/db/pm.py +0 -0
|
@@ -202,12 +202,12 @@ class Boundary(IntEnum):
|
|
|
202
202
|
class SbpStrict(IntEnum):
|
|
203
203
|
sbp = 1
|
|
204
204
|
card = 2
|
|
205
|
+
atm_qr = 3 # наличные через банкомат по QR-коду: «отправляю в чат фото qr-кода с банкомата»
|
|
205
206
|
|
|
206
207
|
|
|
207
|
-
class
|
|
208
|
+
class PartyType(IntEnum): # чей счёт в платеже: и from_party, и to_party у CondParsed
|
|
208
209
|
fst = 0
|
|
209
210
|
fam = 1
|
|
210
|
-
lk = 2
|
|
211
211
|
trd = 3
|
|
212
212
|
|
|
213
213
|
|
|
@@ -221,7 +221,6 @@ class Slip(IntEnum):
|
|
|
221
221
|
class AbuserType(IntEnum):
|
|
222
222
|
rating = 1 # рейтинг: жмет "Оплачено" сразу, "Отмена" по апелляции
|
|
223
223
|
fake = 2 # реклама
|
|
224
|
-
cash = 3 # типа кэш обменник, но хз
|
|
225
224
|
long = 4 # ждать дольше 15 мин (оплату/отпуск)
|
|
226
225
|
add_vrf = 5 # сверх чека — видео из лк/выписка и т.п.
|
|
227
226
|
fee = 6 # доплата/комиссия сверх суммы ордера
|
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
`user_new`.
|
|
12
12
|
3. **`0003_seed`** — засев по зависимостям: файлы PM-логотипов → `a_actor_seed` (биржи, 56 базовых валют, монеты,
|
|
13
13
|
XyncPay, эл. кошельки, origin user id=0) → `a_tg_seed` (DC/App, `pm_rep`) → `a_country_seed` (страны+валюты
|
|
14
|
-
из `seed/countries.json`) → `
|
|
15
|
-
HTX) → `c_net_seed` (web3-сети).
|
|
14
|
+
из `seed/countries.json`) → `a_city_seed` (города из `seed/cities.json`) → `a_bin_seed` (BIN/банки из
|
|
15
|
+
`seed/ranges.csv`) → `b_htx_seed` (валюты/монеты/пары HTX) → `c_net_seed` (web3-сети).
|
|
16
16
|
|
|
17
17
|
Засев, зависящий от Pm (`set_pms` с логотипами, `refine_pms`), — в `xync-script`, не здесь: миграции не
|
|
18
18
|
импортируют `pyro_client`.
|
|
@@ -25,11 +25,11 @@ from xync_db.enums import (
|
|
|
25
25
|
ExStatus,
|
|
26
26
|
ExType,
|
|
27
27
|
FileType,
|
|
28
|
-
FromParty,
|
|
29
28
|
GeminiModel,
|
|
30
29
|
GeminiRes,
|
|
31
30
|
HotStatus,
|
|
32
31
|
OrderStatus,
|
|
32
|
+
PartyType,
|
|
33
33
|
PersonStatus,
|
|
34
34
|
PmType,
|
|
35
35
|
SbpStrict,
|
|
@@ -228,6 +228,26 @@ class Migration(migrations.Migration):
|
|
|
228
228
|
options={"table": "country", "app": "models", "pk_attr": "id"},
|
|
229
229
|
bases=["Country"],
|
|
230
230
|
),
|
|
231
|
+
ops.CreateModel(
|
|
232
|
+
name="City",
|
|
233
|
+
fields=[
|
|
234
|
+
("id", fields.IntField(generated=True, primary_key=True, unique=True, db_index=True)),
|
|
235
|
+
("name", fields.CharField(unique=True, max_length=63)),
|
|
236
|
+
(
|
|
237
|
+
"country",
|
|
238
|
+
fields.ForeignKeyField(
|
|
239
|
+
"models.Country",
|
|
240
|
+
source_field="country_id",
|
|
241
|
+
db_constraint=True,
|
|
242
|
+
to_field="id",
|
|
243
|
+
related_name="cities",
|
|
244
|
+
on_delete=OnDelete.CASCADE,
|
|
245
|
+
),
|
|
246
|
+
),
|
|
247
|
+
],
|
|
248
|
+
options={"table": "city", "app": "models", "pk_attr": "id"},
|
|
249
|
+
bases=["Model"],
|
|
250
|
+
),
|
|
231
251
|
ops.CreateModel(
|
|
232
252
|
name="Dc",
|
|
233
253
|
fields=[
|
|
@@ -712,15 +732,16 @@ class Migration(migrations.Migration):
|
|
|
712
732
|
(
|
|
713
733
|
"from_party",
|
|
714
734
|
fields.IntEnumField(
|
|
715
|
-
null=True, description="fst: 0\nfam: 1\
|
|
735
|
+
null=True, description="fst: 0\nfam: 1\ntrd: 3", enum_type=PartyType, generated=False
|
|
716
736
|
),
|
|
717
737
|
),
|
|
718
738
|
(
|
|
719
739
|
"to_party",
|
|
720
740
|
fields.IntEnumField(
|
|
721
|
-
null=True, description="fst: 0\nfam: 1\
|
|
741
|
+
null=True, description="fst: 0\nfam: 1\ntrd: 3", enum_type=PartyType, generated=False
|
|
722
742
|
),
|
|
723
743
|
),
|
|
744
|
+
("lk", fields.BooleanField(null=True)),
|
|
724
745
|
("ppo", UInt1Field(null=True)),
|
|
725
746
|
(
|
|
726
747
|
"slip",
|
|
@@ -732,16 +753,32 @@ class Migration(migrations.Migration):
|
|
|
732
753
|
"abuser",
|
|
733
754
|
fields.IntEnumField(
|
|
734
755
|
null=True,
|
|
735
|
-
description="rating: 1\nfake: 2\
|
|
756
|
+
description="rating: 1\nfake: 2\nlong: 4\nadd_vrf: 5\nfee: 6\nrate: 7",
|
|
736
757
|
enum_type=AbuserType,
|
|
737
758
|
generated=False,
|
|
738
759
|
),
|
|
739
760
|
),
|
|
761
|
+
(
|
|
762
|
+
"cash_cities",
|
|
763
|
+
fields.ManyToManyField(
|
|
764
|
+
"models.City",
|
|
765
|
+
unique=True,
|
|
766
|
+
db_constraint=True,
|
|
767
|
+
through="cond_cities",
|
|
768
|
+
forward_key="city_id",
|
|
769
|
+
backward_key="cond_parsed_id",
|
|
770
|
+
related_name="conds",
|
|
771
|
+
on_delete=OnDelete.CASCADE,
|
|
772
|
+
),
|
|
773
|
+
),
|
|
740
774
|
("slavic", fields.BooleanField(null=True)),
|
|
741
775
|
(
|
|
742
776
|
"cred",
|
|
743
777
|
fields.IntEnumField(
|
|
744
|
-
null=True,
|
|
778
|
+
null=True,
|
|
779
|
+
description="no_llc: 1\nrus_num: 2\nforeign: 3",
|
|
780
|
+
enum_type=CredType,
|
|
781
|
+
generated=False,
|
|
745
782
|
),
|
|
746
783
|
),
|
|
747
784
|
("mtl_like", fields.BooleanField(null=True)),
|
|
@@ -749,7 +786,9 @@ class Migration(migrations.Migration):
|
|
|
749
786
|
("bank_side", fields.BooleanField(null=True)),
|
|
750
787
|
(
|
|
751
788
|
"sbp_strict",
|
|
752
|
-
fields.IntEnumField(
|
|
789
|
+
fields.IntEnumField(
|
|
790
|
+
null=True, description="sbp: 1\ncard: 2\natm_qr: 3", enum_type=SbpStrict, generated=False
|
|
791
|
+
),
|
|
753
792
|
),
|
|
754
793
|
("contact", fields.CharField(null=True, max_length=127)),
|
|
755
794
|
("is_intermediary", fields.BooleanField(null=True)),
|
|
@@ -7,9 +7,10 @@
|
|
|
7
7
|
2. a_actor_seed — биржи, 56 базовых валют, монеты, XyncPay PM, эл. кошельки ЮMoney/ЦУПИС/Pyypl/Alipay (Bank+Pm+RUB), origin user
|
|
8
8
|
3. a_tg_seed — Telegram DC/App, нормализация имён PM (`pm_rep`)
|
|
9
9
|
4. a_country_seed — ВСЕ страны + их валюты из mledoze (`seed/countries.json`), ДО биржевых
|
|
10
|
-
5.
|
|
11
|
-
6.
|
|
12
|
-
7.
|
|
10
|
+
5. a_city_seed — города из `seed/cities.json`, ПОСЛЕ стран (линк по alpha-2)
|
|
11
|
+
6. a_bin_seed — BIN-диапазоны + банки из binlist/data (`seed/ranges.csv`), ПОСЛЕ стран (линк по alpha-2)
|
|
12
|
+
7. b_htx_seed — HTX-специфика: валюты (CurEx), монеты, пары
|
|
13
|
+
8. c_net_seed — web3-сети (Net) + недостающие нативные монеты
|
|
13
14
|
|
|
14
15
|
NB (D-09): PM-зависимый засев (`set_pms` + `refine_pms`) живёт в `packages/xync-script`, НЕ здесь —
|
|
15
16
|
он тянет `pyro_client` (FileClient), а миграции не должны импортировать `pyro_client`.
|
|
@@ -24,6 +25,7 @@ from tortoise.migrations.operations import RunPython
|
|
|
24
25
|
from xync_db.migrations.models.seed import (
|
|
25
26
|
a_actor_seed,
|
|
26
27
|
a_bin_seed,
|
|
28
|
+
a_city_seed,
|
|
27
29
|
a_country_seed,
|
|
28
30
|
a_tg_seed,
|
|
29
31
|
b_htx_seed,
|
|
@@ -42,6 +44,7 @@ async def seed(apps, schema_editor):
|
|
|
42
44
|
sql = await a_actor_seed.actor_seed(db)
|
|
43
45
|
sql += await a_tg_seed.tg_seed(db)
|
|
44
46
|
sql += await a_country_seed.country_seed(db) # страны+валюты из mledoze, ДО биржевых сидеров
|
|
47
|
+
sql += await a_city_seed.city_seed(db) # города из sky, ПОСЛЕ стран (линк по alpha-2)
|
|
45
48
|
sql += await a_bin_seed.bin_seed(db) # BIN-диапазоны+банки из binlist/data, ПОСЛЕ стран (линк по alpha-2)
|
|
46
49
|
sql += await b_htx_seed.htx_seed(db)
|
|
47
50
|
sql += await c_net_seed.net_seed(db)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""Сидер городов — ПОСЛЕ стран (`a_country_seed`, линк по alpha-2).
|
|
2
|
+
|
|
3
|
+
Данные — статический `cities.json` рядом: `{alpha-2: [имя, …]}` (выгружен из `sky.city`). Идемпотентен:
|
|
4
|
+
заводит только имена, которых ещё нет.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
from tortoise import BaseDBAsyncClient
|
|
11
|
+
|
|
12
|
+
from xync_db.models import City, Country
|
|
13
|
+
|
|
14
|
+
DATA = Path(__file__).with_name("cities.json")
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
async def city_seed(_: BaseDBAsyncClient) -> str:
|
|
18
|
+
data: dict[str, list[str]] = json.loads(DATA.read_text(encoding="utf-8"))
|
|
19
|
+
have = set(await City.all().values_list("name", flat=True))
|
|
20
|
+
for short, names in data.items():
|
|
21
|
+
country = await Country.get(short=short)
|
|
22
|
+
await City.bulk_create([City(name=n, country=country) for n in names if n not in have])
|
|
23
|
+
return "select 1;"
|
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
{
|
|
2
|
+
"RU": [
|
|
3
|
+
"Abakan",
|
|
4
|
+
"Achayvajam",
|
|
5
|
+
"Achinsk",
|
|
6
|
+
"Adamovka",
|
|
7
|
+
"Adler/Sochi",
|
|
8
|
+
"Agzu",
|
|
9
|
+
"Ajan",
|
|
10
|
+
"Ajanka",
|
|
11
|
+
"Aldan",
|
|
12
|
+
"Amderma",
|
|
13
|
+
"Amgu",
|
|
14
|
+
"Amurzet",
|
|
15
|
+
"Anadyr",
|
|
16
|
+
"Anapa",
|
|
17
|
+
"Apuka",
|
|
18
|
+
"Arkhangelsk",
|
|
19
|
+
"Arkhyz",
|
|
20
|
+
"Astrakhan",
|
|
21
|
+
"Bajkit",
|
|
22
|
+
"Balakovo",
|
|
23
|
+
"Barnaul",
|
|
24
|
+
"Batagay",
|
|
25
|
+
"Batagay-Alyta",
|
|
26
|
+
"Belaya Gora",
|
|
27
|
+
"Belgorod",
|
|
28
|
+
"Belokurikha",
|
|
29
|
+
"Beloreck",
|
|
30
|
+
"Beloyarsky",
|
|
31
|
+
"Berezovo",
|
|
32
|
+
"Beringovskiy",
|
|
33
|
+
"Blagoveschensk",
|
|
34
|
+
"Bodaybo",
|
|
35
|
+
"Bogorodskoye",
|
|
36
|
+
"Boguchany",
|
|
37
|
+
"Bolshoy Larjak",
|
|
38
|
+
"Bor",
|
|
39
|
+
"Bovanenkovo",
|
|
40
|
+
"Bratsk",
|
|
41
|
+
"Bryansk",
|
|
42
|
+
"Bugulma",
|
|
43
|
+
"Buguruslan",
|
|
44
|
+
"Burnyj",
|
|
45
|
+
"Bylino",
|
|
46
|
+
"Chara",
|
|
47
|
+
"Cheboksary",
|
|
48
|
+
"Chegdomyn",
|
|
49
|
+
"Chekhlomej",
|
|
50
|
+
"Chelyabinsk",
|
|
51
|
+
"Chemdalsk",
|
|
52
|
+
"Cherepovets",
|
|
53
|
+
"Cherskiy",
|
|
54
|
+
"Chirinda",
|
|
55
|
+
"Chita",
|
|
56
|
+
"Chkalovsky",
|
|
57
|
+
"Chokurdah",
|
|
58
|
+
"Chumikan",
|
|
59
|
+
"Dalnegorsk",
|
|
60
|
+
"Dalnerechensk",
|
|
61
|
+
"Deputatsky",
|
|
62
|
+
"Dikson",
|
|
63
|
+
"Dombay",
|
|
64
|
+
"Edinka",
|
|
65
|
+
"Egvekinot",
|
|
66
|
+
"Ekonda",
|
|
67
|
+
"Elbrus",
|
|
68
|
+
"Elista",
|
|
69
|
+
"Eniseysk",
|
|
70
|
+
"Erbogachen",
|
|
71
|
+
"Essentuki",
|
|
72
|
+
"Essey",
|
|
73
|
+
"Evensk",
|
|
74
|
+
"Gelendzik",
|
|
75
|
+
"Glade Cheget",
|
|
76
|
+
"Gorno-Altaysk",
|
|
77
|
+
"Groznyy",
|
|
78
|
+
"Hatanga",
|
|
79
|
+
"Igarka",
|
|
80
|
+
"Igrim",
|
|
81
|
+
"Inta",
|
|
82
|
+
"Irkutsk",
|
|
83
|
+
"Iturup",
|
|
84
|
+
"Iturup Island",
|
|
85
|
+
"Ivanovo",
|
|
86
|
+
"Izhevsk",
|
|
87
|
+
"Jukta",
|
|
88
|
+
"Kaliningrad",
|
|
89
|
+
"Kaluga",
|
|
90
|
+
"Kamenskoe",
|
|
91
|
+
"Kavalerovo",
|
|
92
|
+
"Kazachinskoye",
|
|
93
|
+
"Kazan",
|
|
94
|
+
"Kedrovy",
|
|
95
|
+
"Kemerovo",
|
|
96
|
+
"Keperveyem",
|
|
97
|
+
"Khabarovsk",
|
|
98
|
+
"Khailino",
|
|
99
|
+
"Khanty-Mansiysk",
|
|
100
|
+
"Kharuta",
|
|
101
|
+
"Kherpuchi",
|
|
102
|
+
"Khonu",
|
|
103
|
+
"Khuzhir",
|
|
104
|
+
"Kirensk",
|
|
105
|
+
"Kirov",
|
|
106
|
+
"Kirovsk",
|
|
107
|
+
"Kislokan",
|
|
108
|
+
"Kislovodsk",
|
|
109
|
+
"Kodinsk",
|
|
110
|
+
"Kogalym",
|
|
111
|
+
"Kolekjegan",
|
|
112
|
+
"Komsomolsk Na Amure",
|
|
113
|
+
"Korliki",
|
|
114
|
+
"Koslan",
|
|
115
|
+
"Kostroma",
|
|
116
|
+
"Kotlas",
|
|
117
|
+
"Krabozavodskoye",
|
|
118
|
+
"Krasnodar",
|
|
119
|
+
"Krasnojarsk",
|
|
120
|
+
"Krasnokamensk",
|
|
121
|
+
"Krasnoselkup",
|
|
122
|
+
"Krasny Kut",
|
|
123
|
+
"Krasnyiy Chikoy",
|
|
124
|
+
"Kujumba",
|
|
125
|
+
"Kunashir Island",
|
|
126
|
+
"Kurgan",
|
|
127
|
+
"Kursk",
|
|
128
|
+
"Kuskurgul",
|
|
129
|
+
"Kuzmovka",
|
|
130
|
+
"Kyra",
|
|
131
|
+
"Kyzyl",
|
|
132
|
+
"Laitamak",
|
|
133
|
+
"Larjak",
|
|
134
|
+
"Lensk",
|
|
135
|
+
"Leshukonskoye",
|
|
136
|
+
"Lipetsk",
|
|
137
|
+
"Magadan",
|
|
138
|
+
"Magan",
|
|
139
|
+
"Magas",
|
|
140
|
+
"Magdagachi",
|
|
141
|
+
"Magnitogorsk",
|
|
142
|
+
"Makhachkala",
|
|
143
|
+
"Mama",
|
|
144
|
+
"Manily",
|
|
145
|
+
"Markovo",
|
|
146
|
+
"Maximovka",
|
|
147
|
+
"Mezen",
|
|
148
|
+
"Mineralnye Vody",
|
|
149
|
+
"Miriuga",
|
|
150
|
+
"Mirnyj",
|
|
151
|
+
"Moscow",
|
|
152
|
+
"Motygino",
|
|
153
|
+
"Murmansk",
|
|
154
|
+
"Muzhi",
|
|
155
|
+
"Mys-Kamenny",
|
|
156
|
+
"Naberezhnye Chelny",
|
|
157
|
+
"Nadym",
|
|
158
|
+
"Nalchik",
|
|
159
|
+
"Naryan-Mar",
|
|
160
|
+
"Neftekamsk",
|
|
161
|
+
"Nefteyugansk",
|
|
162
|
+
"Nelkan",
|
|
163
|
+
"Neryungri",
|
|
164
|
+
"Nikolaevsk-na-Amure",
|
|
165
|
+
"Nikolskoe",
|
|
166
|
+
"Nizhneangarsk",
|
|
167
|
+
"Nizhnevartovsk",
|
|
168
|
+
"Nizhniy Novgorod",
|
|
169
|
+
"Nogliki",
|
|
170
|
+
"Nojabrxsk",
|
|
171
|
+
"Noril'sk",
|
|
172
|
+
"Novgorod",
|
|
173
|
+
"Novokurovka",
|
|
174
|
+
"Novokuznetsk",
|
|
175
|
+
"Novorossijsk",
|
|
176
|
+
"Novosibirsk",
|
|
177
|
+
"Novyj Urengoj",
|
|
178
|
+
"Nyagan",
|
|
179
|
+
"Nyurba",
|
|
180
|
+
"Okha",
|
|
181
|
+
"Okhotsk",
|
|
182
|
+
"Oktiabrskij",
|
|
183
|
+
"Olenyok",
|
|
184
|
+
"Olokminsk",
|
|
185
|
+
"Omolon",
|
|
186
|
+
"Omsk",
|
|
187
|
+
"Omsukchan",
|
|
188
|
+
"Orel",
|
|
189
|
+
"Orenburg",
|
|
190
|
+
"Orsk",
|
|
191
|
+
"Osharovo",
|
|
192
|
+
"Ossora",
|
|
193
|
+
"Ozernaya",
|
|
194
|
+
"Pakhachi",
|
|
195
|
+
"Palana",
|
|
196
|
+
"Pechora",
|
|
197
|
+
"Penza",
|
|
198
|
+
"Perm",
|
|
199
|
+
"Petropavlovsk-Kamchatsky",
|
|
200
|
+
"Petrozavodsk",
|
|
201
|
+
"Pevek",
|
|
202
|
+
"Plastun",
|
|
203
|
+
"Pobeda",
|
|
204
|
+
"Podvolochnoe",
|
|
205
|
+
"Pokur",
|
|
206
|
+
"Poligus",
|
|
207
|
+
"Poliny Osipenko",
|
|
208
|
+
"Poronaisk",
|
|
209
|
+
"Preobrazheniye",
|
|
210
|
+
"Provideniya",
|
|
211
|
+
"Pskov",
|
|
212
|
+
"Pug-Jug",
|
|
213
|
+
"Raduzhnyi",
|
|
214
|
+
"Romantik",
|
|
215
|
+
"Rostov",
|
|
216
|
+
"Ryazan",
|
|
217
|
+
"Rybinsk",
|
|
218
|
+
"Sabetta",
|
|
219
|
+
"Saint Petersburg",
|
|
220
|
+
"Salekhard",
|
|
221
|
+
"Samara",
|
|
222
|
+
"Samarga",
|
|
223
|
+
"Sangar",
|
|
224
|
+
"Saransk",
|
|
225
|
+
"Saratov",
|
|
226
|
+
"Saskylakh",
|
|
227
|
+
"Severo-Eniseyskiy",
|
|
228
|
+
"Severo-Kurilsk",
|
|
229
|
+
"Seymchan",
|
|
230
|
+
"Shakhtyorsk",
|
|
231
|
+
"Sheregesh",
|
|
232
|
+
"Shushenskoe",
|
|
233
|
+
"Sinegorye",
|
|
234
|
+
"Slautnoe",
|
|
235
|
+
"Smirnykh",
|
|
236
|
+
"Smolensk",
|
|
237
|
+
"Sobolevo",
|
|
238
|
+
"Solovetsky",
|
|
239
|
+
"Sosnovy Bor",
|
|
240
|
+
"Sovetskaya Gavan",
|
|
241
|
+
"Sovetsky",
|
|
242
|
+
"Srednekolymsk",
|
|
243
|
+
"Srednie Pakhachi",
|
|
244
|
+
"Stary Oskol",
|
|
245
|
+
"Stavropol",
|
|
246
|
+
"Strelka-Chunya",
|
|
247
|
+
"Strezhevoy",
|
|
248
|
+
"Sulomay",
|
|
249
|
+
"Suntar",
|
|
250
|
+
"Surgut",
|
|
251
|
+
"Surinda",
|
|
252
|
+
"Susuman",
|
|
253
|
+
"Svetlaya",
|
|
254
|
+
"Svetlogorsk",
|
|
255
|
+
"Svetly",
|
|
256
|
+
"Syktyvkar",
|
|
257
|
+
"Taganrog",
|
|
258
|
+
"Taksimo",
|
|
259
|
+
"Talakan",
|
|
260
|
+
"Talovka",
|
|
261
|
+
"Tambov",
|
|
262
|
+
"Tarko-Sale",
|
|
263
|
+
"Terney",
|
|
264
|
+
"Tigil",
|
|
265
|
+
"Tiksi",
|
|
266
|
+
"Tilichiki",
|
|
267
|
+
"Tobolsk",
|
|
268
|
+
"Tolka",
|
|
269
|
+
"Tomsk",
|
|
270
|
+
"Troitsko-Pechorsk",
|
|
271
|
+
"Tugur",
|
|
272
|
+
"Tula",
|
|
273
|
+
"Tura",
|
|
274
|
+
"Turukhansk",
|
|
275
|
+
"Tutonchany",
|
|
276
|
+
"Tver",
|
|
277
|
+
"Tynda",
|
|
278
|
+
"Tyumen",
|
|
279
|
+
"Uchami",
|
|
280
|
+
"Udachnyj",
|
|
281
|
+
"Udskoe",
|
|
282
|
+
"Ufa",
|
|
283
|
+
"Ukhta",
|
|
284
|
+
"Ulan-Ude",
|
|
285
|
+
"Ulyanovsk",
|
|
286
|
+
"Uraj",
|
|
287
|
+
"Urengoy",
|
|
288
|
+
"Usinsk",
|
|
289
|
+
"Ust'-Koksa",
|
|
290
|
+
"Ust-Ilimsk",
|
|
291
|
+
"Ust-Kamchatsk",
|
|
292
|
+
"Ust-Khayryuzovo",
|
|
293
|
+
"Ust-Kolikjegan",
|
|
294
|
+
"Ust-Kuiga",
|
|
295
|
+
"Ust-Kut",
|
|
296
|
+
"Ust-Maya",
|
|
297
|
+
"Ust-Nera",
|
|
298
|
+
"Ust-Omchug",
|
|
299
|
+
"Ust-Tsilma",
|
|
300
|
+
"Vagay",
|
|
301
|
+
"Vampugolsk",
|
|
302
|
+
"Vanavara",
|
|
303
|
+
"Varandey",
|
|
304
|
+
"Velikij Ustyug",
|
|
305
|
+
"Velikiye Luki",
|
|
306
|
+
"Verhnevilyuisk",
|
|
307
|
+
"Vershinskie",
|
|
308
|
+
"Vilyuisk",
|
|
309
|
+
"Vladikavkaz",
|
|
310
|
+
"Vladimir",
|
|
311
|
+
"Vladivostok",
|
|
312
|
+
"Volgodonsk",
|
|
313
|
+
"Volgograd",
|
|
314
|
+
"Vologda",
|
|
315
|
+
"Vorkuta",
|
|
316
|
+
"Voronezh",
|
|
317
|
+
"Vuktyl",
|
|
318
|
+
"Yagodnoe",
|
|
319
|
+
"Yakutsk",
|
|
320
|
+
"Yamburg",
|
|
321
|
+
"Yaroslavl",
|
|
322
|
+
"Yartsevo",
|
|
323
|
+
"Yekaterinburg",
|
|
324
|
+
"Yeysk",
|
|
325
|
+
"Yoshkar-Ola",
|
|
326
|
+
"Yuzhno-Sakhalinsk",
|
|
327
|
+
"Zaliv Lavrentija",
|
|
328
|
+
"Zeja",
|
|
329
|
+
"Zheleznogorsk",
|
|
330
|
+
"Zheleznovodsk",
|
|
331
|
+
"Zhigansk",
|
|
332
|
+
"Zonalnoye",
|
|
333
|
+
"Zyryanka",
|
|
334
|
+
"azau",
|
|
335
|
+
"terskol"
|
|
336
|
+
]
|
|
337
|
+
}
|
|
@@ -75,14 +75,16 @@ Re-exported models from `x_auth`: `Username`, `Proxy`, `Dc`, `Fcm`, `App`, `Sess
|
|
|
75
75
|
`Cond` -> `CondParsed`, `Synonym`, `CondSim`, `CondPrompt`
|
|
76
76
|
|
|
77
77
|
- `Cond.raw_txt` = текст условия (до 4095); условие — одной стороны, без объяв не живёт (D-18)
|
|
78
|
-
- `CondParsed` = structured parse result: `from_party` / `to_party` (`
|
|
79
|
-
покупателя), `
|
|
80
|
-
|
|
78
|
+
- `CondParsed` = structured parse result: `from_party` / `to_party` (`PartyType`: с чьего / на чей счёт идёт платёж
|
|
79
|
+
покупателя), `lk` (лк банка на руках: True — требование к тейкеру, False — есть у мейкера), `cash_cities` (`City`
|
|
80
|
+
кэш-обменника / встречи — m2m `cond_cities`, их может быть несколько), `ppo`, `slip` (`Slip`) — требует/шлёт понятно из направления объявы, abuse flags,
|
|
81
|
+
`is_intermediary` (мейкер-посредник «треугол»), linked bank `Pm`s
|
|
81
82
|
— все поля nullable, дефолт `None` (= «в условии не сказано»; никаких `*.no`-заглушек). Единый writer
|
|
82
|
-
`CondParsed.upsert(cond_id, bank_ids=None, **fields)` (бот-разметка /cond и разбор нейронками, D-19); ответ админа
|
|
83
|
+
`CondParsed.upsert(cond_id, bank_ids=None, city_ids=None, **fields)` (бот-разметка /cond и разбор нейронками, D-19); ответ админа
|
|
83
84
|
на «сохранить разбор?» — эмиттер `CondParsed.answer(cond_id, remark=None)` → pg-канал `cond_ai_answer`
|
|
84
85
|
`[cond_id, remark]` (null — «да», текст — «нет» с замечанием, "" — «нет», замечание пишется, true — «🔄»: объяву
|
|
85
|
-
условия заново с биржи — реальный exid замаскированного мейкера + свежий `raw_txt`, затем вопрос
|
|
86
|
+
условия заново с биржи — реальный exid замаскированного мейкера + свежий `raw_txt`, затем вопрос заново; false —
|
|
87
|
+
«другая модель»: разбор следующей по кругу моделью, затем вопрос заново); возвращает, слушает
|
|
86
88
|
ли канал хоть один прогон (pg_stat_activity) — иначе ответ пропал бы молча
|
|
87
89
|
- `CondPrompt` = версии промпта разбора: системного промпта Gemini и (`is_jev`) вопросов JEV JSON-ом — у каждого
|
|
88
90
|
действует последняя (`id` max); пишет только `CondAi` (засев исходной + правки Claude по замечаниям админа, `cond` —
|
|
@@ -82,13 +82,13 @@ from xync_db.enums import (
|
|
|
82
82
|
ExAction,
|
|
83
83
|
ExStatus,
|
|
84
84
|
ExType,
|
|
85
|
-
FromParty,
|
|
86
85
|
GeminiModel,
|
|
87
86
|
GeminiRes,
|
|
88
87
|
HotStatus,
|
|
89
88
|
LinkType,
|
|
90
89
|
OrderErr,
|
|
91
90
|
OrderStatus,
|
|
91
|
+
PartyType,
|
|
92
92
|
PersonStatus,
|
|
93
93
|
PmType,
|
|
94
94
|
SbpStrict,
|
|
@@ -128,6 +128,12 @@ class Country(BaseCountry):
|
|
|
128
128
|
# fiats: BackwardFKRelation[Fiat]
|
|
129
129
|
|
|
130
130
|
|
|
131
|
+
class City(Model):
|
|
132
|
+
id: int = IntField(True)
|
|
133
|
+
name: str = CharField(63, unique=True)
|
|
134
|
+
country: ForeignKeyRelation[Country] = ForeignKeyField("models.Country", "cities", on_update=CASCADE)
|
|
135
|
+
|
|
136
|
+
|
|
131
137
|
class Cur(Model):
|
|
132
138
|
id = UInt1Field(True)
|
|
133
139
|
ticker: str = CharField(3, unique=True)
|
|
@@ -1464,11 +1470,16 @@ class Pm(Model):
|
|
|
1464
1470
|
class CondParsed(Model, TsTrait):
|
|
1465
1471
|
cond: OneToOneNullableRelation[Cond] = OneToOneField("models.Cond", "parsed", on_update=CASCADE, null=True)
|
|
1466
1472
|
cond_id: int | None # new
|
|
1467
|
-
from_party:
|
|
1468
|
-
to_party:
|
|
1473
|
+
from_party: PartyType | None = IntEnumField(PartyType, null=True) # с чьего счёта платят
|
|
1474
|
+
to_party: PartyType | None = IntEnumField(PartyType, null=True) # на чей счёт платят
|
|
1475
|
+
lk: bool | None = BooleanField(null=True) # лк на руках: True — требует от тейкера, False — есть у мейкера
|
|
1469
1476
|
ppo: int = UInt1Field(null=True) # Payments per order
|
|
1470
1477
|
slip: Slip = IntEnumField(Slip, null=True)
|
|
1471
1478
|
abuser: AbuserType | None = IntEnumField(AbuserType, null=True)
|
|
1479
|
+
# кэш-обменник / встреча за наличные в этих городах
|
|
1480
|
+
cash_cities: ManyToManyRelation[City] = ManyToManyField(
|
|
1481
|
+
"models.City", "cond_cities", related_name="conds", on_update=CASCADE
|
|
1482
|
+
)
|
|
1472
1483
|
slavic: bool = BooleanField(null=True)
|
|
1473
1484
|
cred: CredType | None = IntEnumField(
|
|
1474
1485
|
CredType, null=True
|
|
@@ -1487,21 +1498,25 @@ class CondParsed(Model, TsTrait):
|
|
|
1487
1498
|
table = "cond_parsed"
|
|
1488
1499
|
|
|
1489
1500
|
@classmethod
|
|
1490
|
-
async def upsert(
|
|
1501
|
+
async def upsert(
|
|
1502
|
+
cls, cond_id: int, bank_ids: list[int] | None = None, city_ids: list[int] | None = None, **fields
|
|
1503
|
+
) -> CondParsed:
|
|
1491
1504
|
"""ЕДИНСТВЕННЫЙ writer разбора условия: бот-разметка /cond (по полю) и Gemini (lens.parse_conds_admin,
|
|
1492
|
-
целиком). bank_ids: None —
|
|
1505
|
+
целиком). bank_ids / city_ids: None — не трогаем, список — заменяем им cond_banks / cond_cities."""
|
|
1493
1506
|
async with in_transaction():
|
|
1494
1507
|
cp, _ = await cls.update_or_create(fields, cond_id=cond_id)
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1508
|
+
for rel, model, ids in ((cp.banks, Pm, bank_ids), (cp.cash_cities, City, city_ids)):
|
|
1509
|
+
if ids is not None:
|
|
1510
|
+
await rel.clear()
|
|
1511
|
+
await rel.add(*await model.filter(id__in=ids))
|
|
1498
1512
|
return cp
|
|
1499
1513
|
|
|
1500
1514
|
@classmethod
|
|
1501
1515
|
async def answer(cls, cond_id: int, remark: str | bool | None = None) -> bool:
|
|
1502
1516
|
"""Ответ админа на вопрос «сохранить разбор Gemini?» (бот, routers/cond): `remark` None — «да», текст —
|
|
1503
1517
|
«нет» с замечанием, что не так (по нему Claude правит промпт — `CondAi.fix`), "" — «нет», замечание пишется,
|
|
1504
|
-
True — «🔄»: объяву условия заново с биржи (реальный exid мейкера, свежий текст), затем вопрос
|
|
1518
|
+
True — «🔄»: объяву условия заново с биржи (реальный exid мейкера, свежий текст), затем вопрос заново, False —
|
|
1519
|
+
«другая модель»: разбор следующей моделью (`CondAi.reparse`), затем вопрос заново.
|
|
1505
1520
|
Ждёт его прогон lens.parse_conds_admin в ДРУГОМ процессе (скрипт/крон), а ответ ловит бот api — поэтому через
|
|
1506
1521
|
pg_notify, как Agent.confirm_trade; на «да» разбор пишет сам ждущий (у него распарсенные поля).
|
|
1507
1522
|
→ ждёт ли кто-то: без слушателя pg_notify молча пропадает (прогон упал/закончился) — бот скажет это админу.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|