xync-db 0.5.5.dev21__tar.gz → 0.5.5.dev25__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.5.dev21 → xync_db-0.5.5.dev25}/PKG-INFO +1 -1
- {xync_db-0.5.5.dev21 → xync_db-0.5.5.dev25}/src/xync_db/migrations/models/0001_initial.py +1 -0
- {xync_db-0.5.5.dev21 → xync_db-0.5.5.dev25}/src/xync_db/models/__init__.py +54 -5
- {xync_db-0.5.5.dev21 → xync_db-0.5.5.dev25}/src/xync_db/typs/__init__.py +3 -0
- {xync_db-0.5.5.dev21 → xync_db-0.5.5.dev25}/.gitignore +0 -0
- {xync_db-0.5.5.dev21 → xync_db-0.5.5.dev25}/README.md +0 -0
- {xync_db-0.5.5.dev21 → xync_db-0.5.5.dev25}/pyproject.toml +0 -0
- {xync_db-0.5.5.dev21 → xync_db-0.5.5.dev25}/src/xync_db/__init__.py +0 -0
- {xync_db-0.5.5.dev21 → xync_db-0.5.5.dev25}/src/xync_db/config.py +0 -0
- {xync_db-0.5.5.dev21 → xync_db-0.5.5.dev25}/src/xync_db/enums.py +0 -0
- {xync_db-0.5.5.dev21 → xync_db-0.5.5.dev25}/src/xync_db/exceptions.py +0 -0
- {xync_db-0.5.5.dev21 → xync_db-0.5.5.dev25}/src/xync_db/graph.py +0 -0
- {xync_db-0.5.5.dev21 → xync_db-0.5.5.dev25}/src/xync_db/i18n.py +0 -0
- {xync_db-0.5.5.dev21 → xync_db-0.5.5.dev25}/src/xync_db/login_store.py +0 -0
- {xync_db-0.5.5.dev21 → xync_db-0.5.5.dev25}/src/xync_db/logo.png +0 -0
- {xync_db-0.5.5.dev21 → xync_db-0.5.5.dev25}/src/xync_db/migrations/CLAUDE.md +0 -0
- {xync_db-0.5.5.dev21 → xync_db-0.5.5.dev25}/src/xync_db/migrations/__init__.py +0 -0
- {xync_db-0.5.5.dev21 → xync_db-0.5.5.dev25}/src/xync_db/migrations/models/0002_triggers.py +0 -0
- {xync_db-0.5.5.dev21 → xync_db-0.5.5.dev25}/src/xync_db/migrations/models/0003_seed.py +0 -0
- {xync_db-0.5.5.dev21 → xync_db-0.5.5.dev25}/src/xync_db/migrations/models/__init__.py +0 -0
- {xync_db-0.5.5.dev21 → xync_db-0.5.5.dev25}/src/xync_db/migrations/models/seed/__init__.py +0 -0
- {xync_db-0.5.5.dev21 → xync_db-0.5.5.dev25}/src/xync_db/migrations/models/seed/a_actor_seed.py +0 -0
- {xync_db-0.5.5.dev21 → xync_db-0.5.5.dev25}/src/xync_db/migrations/models/seed/a_bin_seed.py +0 -0
- {xync_db-0.5.5.dev21 → xync_db-0.5.5.dev25}/src/xync_db/migrations/models/seed/a_country_seed.py +0 -0
- {xync_db-0.5.5.dev21 → xync_db-0.5.5.dev25}/src/xync_db/migrations/models/seed/a_tg_seed.py +0 -0
- {xync_db-0.5.5.dev21 → xync_db-0.5.5.dev25}/src/xync_db/migrations/models/seed/b_htx_seed.py +0 -0
- {xync_db-0.5.5.dev21 → xync_db-0.5.5.dev25}/src/xync_db/migrations/models/seed/c_net_seed.py +0 -0
- {xync_db-0.5.5.dev21 → xync_db-0.5.5.dev25}/src/xync_db/migrations/models/seed/countries.json +0 -0
- {xync_db-0.5.5.dev21 → xync_db-0.5.5.dev25}/src/xync_db/migrations/models/seed/ranges.csv +0 -0
- {xync_db-0.5.5.dev21 → xync_db-0.5.5.dev25}/src/xync_db/migrations/models/seed/xync_files.sql +0 -0
- {xync_db-0.5.5.dev21 → xync_db-0.5.5.dev25}/src/xync_db/models/CLAUDE.md +0 -0
- {xync_db-0.5.5.dev21 → xync_db-0.5.5.dev25}/src/xync_db/py.typed +0 -0
- {xync_db-0.5.5.dev21 → xync_db-0.5.5.dev25}/src/xync_db/shared.py +0 -0
- {xync_db-0.5.5.dev21 → xync_db-0.5.5.dev25}/src/xync_db/typs/db/__init__.py +0 -0
- {xync_db-0.5.5.dev21 → xync_db-0.5.5.dev25}/src/xync_db/typs/db/actor.py +0 -0
- {xync_db-0.5.5.dev21 → xync_db-0.5.5.dev25}/src/xync_db/typs/db/ad.py +0 -0
- {xync_db-0.5.5.dev21 → xync_db-0.5.5.dev25}/src/xync_db/typs/db/common.py +0 -0
- {xync_db-0.5.5.dev21 → xync_db-0.5.5.dev25}/src/xync_db/typs/db/cred.py +0 -0
- {xync_db-0.5.5.dev21 → xync_db-0.5.5.dev25}/src/xync_db/typs/db/order.py +0 -0
- {xync_db-0.5.5.dev21 → xync_db-0.5.5.dev25}/src/xync_db/typs/db/pm.py +0 -0
|
@@ -1069,6 +1069,7 @@ class Migration(migrations.Migration):
|
|
|
1069
1069
|
("tz", fields.SmallIntField(db_default=3)),
|
|
1070
1070
|
("prv", UniqBinaryField(null=True, unique=True)),
|
|
1071
1071
|
("pub", UniqBinaryField(null=True, unique=True)),
|
|
1072
|
+
("review_anon", fields.BooleanField(db_default=True)),
|
|
1072
1073
|
],
|
|
1073
1074
|
options={"table": "user", "app": "models", "pk_attr": "id"},
|
|
1074
1075
|
bases=["User", "TsTrait"],
|
|
@@ -107,6 +107,11 @@ from xync_db.typs import HotAd, HotAds, OrderResult
|
|
|
107
107
|
# место смены — на случай добавления других share-бирж.
|
|
108
108
|
SHARE_HEX_EX_IDS = [4]
|
|
109
109
|
|
|
110
|
+
# ex_id бирж, где по ПРОДАЖНОЙ hot-объяве монету отпускаем сразу по факту внутренней транзакции оплаты
|
|
111
|
+
# (XyncPay-перевод прошёл), НЕ дожидаясь, пока покупатель отметит оплату на бирже (status→paid).
|
|
112
|
+
# На остальных биржах действует общее правило: релиз только после paid (см. Order.hot_process).
|
|
113
|
+
INSTANT_RELEASE_EX_IDS = [4] # Bybit
|
|
114
|
+
|
|
110
115
|
|
|
111
116
|
class Country(BaseCountry):
|
|
112
117
|
ccn3: int | None = UInt2Field(null=True) # числовой ISO 3166-1 (базовый `code` — телефонный)
|
|
@@ -453,6 +458,9 @@ class User(TgUser, TsTrait):
|
|
|
453
458
|
# (api get_chat_member) и live — ботом-админом через chat_member-апдейты. None = не состоит / неизвестно.
|
|
454
459
|
in_channel_at: datetime | None = DatetimeSecField(null=True)
|
|
455
460
|
in_chat_at: datetime | None = DatetimeSecField(null=True)
|
|
461
|
+
# взаимный отзыв, который наш мейкер зеркалит контрагенту по hot-ордеру этого юзера, оставлять
|
|
462
|
+
# анонимно (чекбокс под списком hot-объяв в боте и вебе; по умолчанию включён)
|
|
463
|
+
review_anon: bool = BooleanField(db_default=True)
|
|
456
464
|
|
|
457
465
|
# actors: BackwardFKRelation[Actor]
|
|
458
466
|
# borrows: BackwardFKRelation[Credit]
|
|
@@ -498,6 +506,15 @@ class User(TgUser, TsTrait):
|
|
|
498
506
|
if fields:
|
|
499
507
|
await cls.filter(username_id=username_id).update(**fields)
|
|
500
508
|
|
|
509
|
+
async def set_review_anon(self, anon: bool) -> bool:
|
|
510
|
+
"""ЕДИНСТВЕННЫЙ writer User.review_anon (чекбокс «Взаимный отзыв анонимно» под списком
|
|
511
|
+
hot-объяв): пишут и веб (HotCtr.review_anon), и бот (routers.hot.filter_toggle, dim="r").
|
|
512
|
+
Возвращает записанное значение."""
|
|
513
|
+
if self.review_anon != anon:
|
|
514
|
+
self.review_anon = anon
|
|
515
|
+
await self.save(update_fields=["review_anon"])
|
|
516
|
+
return self.review_anon
|
|
517
|
+
|
|
501
518
|
async def free_assets(self):
|
|
502
519
|
assets = await Asset.filter(addr__actor__person__user__id=self.id).values("free", "addr__coin__rate")
|
|
503
520
|
return sum(asset["free"] * asset["addr__coin__rate"] for asset in assets)
|
|
@@ -1020,7 +1037,13 @@ class User(TgUser, TsTrait):
|
|
|
1020
1037
|
shared_at=sell_ad.shared_at,
|
|
1021
1038
|
)
|
|
1022
1039
|
)
|
|
1023
|
-
return HotAds(
|
|
1040
|
+
return HotAds(
|
|
1041
|
+
sells=take_sales,
|
|
1042
|
+
buys=take_buys,
|
|
1043
|
+
balance=balances,
|
|
1044
|
+
coin_balance=coin_balances,
|
|
1045
|
+
review_anon=self.review_anon,
|
|
1046
|
+
)
|
|
1024
1047
|
|
|
1025
1048
|
async def get_last_hot(self, force_create: bool = False) -> Hot:
|
|
1026
1049
|
if not force_create and (
|
|
@@ -1222,9 +1245,24 @@ class Agent(Model, TsTrait):
|
|
|
1222
1245
|
|
|
1223
1246
|
@classmethod
|
|
1224
1247
|
async def upsert(
|
|
1225
|
-
cls,
|
|
1248
|
+
cls,
|
|
1249
|
+
exid: int,
|
|
1250
|
+
host: str,
|
|
1251
|
+
auth: dict,
|
|
1252
|
+
exp: int | None,
|
|
1253
|
+
rname: str,
|
|
1254
|
+
nick: str,
|
|
1255
|
+
email: str | None = None,
|
|
1256
|
+
user: User | None = None,
|
|
1226
1257
|
) -> tuple[int, Agent, bool]:
|
|
1227
|
-
"""Единый писатель агента (за `/public/set-agent`).
|
|
1258
|
+
"""Единый писатель агента (за `/public/set-agent` и OAuth «Connect Bybit»). `exp=None` — срок
|
|
1259
|
+
web-сессии неизвестен (Htx/Mexc, голые API-ключи из OAuth) → `expire_at` не трогаем.
|
|
1260
|
+
|
|
1261
|
+
`user` — владелец УЖЕ известен (OAuth из приложения залогиненного юзера): актор сразу
|
|
1262
|
+
привязывается к нему `Actor.set_user` (тот же мердж персон, что по ответу админа), без
|
|
1263
|
+
USER_DEF-вопроса «чей это агент?». Актор чужого юзера → PermissionError, агента не трогаем.
|
|
1264
|
+
|
|
1265
|
+
Bootstrap-персону нового актора резолвим
|
|
1228
1266
|
ПО `note` (`"{ex_id}:{exid}"`, уникальному по актору) — тем же ключом, что клиентский
|
|
1229
1267
|
`Ex.person_save`, чтобы у Person был ОДИН путь создания (D-035). По НЕуникальному `name` НЕ
|
|
1230
1268
|
ищем: именно `get_or_none(name=rname)` ронял прод `MultipleObjectsReturned` на тёзках без юзера.
|
|
@@ -1242,6 +1280,8 @@ class Agent(Model, TsTrait):
|
|
|
1242
1280
|
note = f"{ex.id}:{exid}" # ключ bootstrap-персоны актора (как в Ex.person_save)
|
|
1243
1281
|
if person := await Person.get_or_none(note=note):
|
|
1244
1282
|
pass # актор уже знаком — берём его bootstrap-персону
|
|
1283
|
+
elif user: # владелец известен — персона без USER_DEF (админа не зовём), к юзеру ниже set_user'ом
|
|
1284
|
+
person = await Person.create(name=rname, note=note)
|
|
1245
1285
|
else:
|
|
1246
1286
|
# gmail-автолинк: замаскированная биржей почта, однозначно (ровно 1 gmail по LIKE,
|
|
1247
1287
|
# где `*`→`_`) указывающая на юзера, сразу привязывает актора к его персоне — без
|
|
@@ -1263,9 +1303,16 @@ class Agent(Model, TsTrait):
|
|
|
1263
1303
|
person = await Person.create(name=rname, note=note, status=PersonStatus.USER_DEF)
|
|
1264
1304
|
err = 1 # ждём привязки к юзеру через def_user
|
|
1265
1305
|
actor = await Actor.create(ex=ex, exid=exid, name=nick, person=person)
|
|
1306
|
+
if user:
|
|
1307
|
+
await actor.fetch_related("person")
|
|
1308
|
+
if actor.person.user_id not in (None, user.id):
|
|
1309
|
+
raise PermissionError(f"Actor#{actor.id} ({ex.name}:{exid}) belongs to user#{actor.person.user_id}")
|
|
1310
|
+
if not actor.person.user_id:
|
|
1311
|
+
await actor.set_user(user.username_id)
|
|
1266
1312
|
if ag := await Agent.get_or_none(actor=actor):
|
|
1267
1313
|
ag.auth |= auth
|
|
1268
|
-
|
|
1314
|
+
if exp is not None:
|
|
1315
|
+
ag.expire_at = exp
|
|
1269
1316
|
await ag.save(update_fields=["auth", "expire_at"])
|
|
1270
1317
|
created = False
|
|
1271
1318
|
else:
|
|
@@ -2404,7 +2451,9 @@ class Order(Model, TsTrait):
|
|
|
2404
2451
|
# продажная объява: монету отпускаем только ПОСЛЕ того, как покупатель отметит оплату на # noqa: RUF003
|
|
2405
2452
|
# бирже (status→paid). Пока ордер created — не рассчитываемся (XyncPay) и не релизим, чтобы
|
|
2406
2453
|
# не отдать монету за неоплаченный ордер; релиз дожмёт hot_order_upd на переходе в paid.
|
|
2407
|
-
|
|
2454
|
+
# Исключение — INSTANT_RELEASE_EX_IDS (Bybit): там отпускаем прямо на created, как только
|
|
2455
|
+
# прошла внутренняя транзакция оплаты (pay() ниже), не дожидаясь paid от покупателя.
|
|
2456
|
+
if self.ad.pair_side.is_sell and self.ad.maker.ex_id not in INSTANT_RELEASE_EX_IDS:
|
|
2408
2457
|
res.err = OrderErr.i_wait_for_payment
|
|
2409
2458
|
res.data = OrderResult.Order.load(self)
|
|
2410
2459
|
return res
|
|
@@ -50,3 +50,6 @@ class HotAds(Xs):
|
|
|
50
50
|
balance: dict[int, float] # остаток по каждой валюте (cur_id → сумма)
|
|
51
51
|
# остаток монет у акторов персон юзера: ex_id → coin_id → сумма asset.free
|
|
52
52
|
coin_balance: dict[int, dict[int, int]]
|
|
53
|
+
# состояние чекбокса «Взаимный отзыв анонимно» под списком объяв (User.review_anon) — его
|
|
54
|
+
# рисуют оба клиента (бот hot_kb, web HotPage), поэтому едет тем же payload'ом, что и объявы
|
|
55
|
+
review_anon: bool = True
|
|
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
|
{xync_db-0.5.5.dev21 → xync_db-0.5.5.dev25}/src/xync_db/migrations/models/seed/a_actor_seed.py
RENAMED
|
File without changes
|
{xync_db-0.5.5.dev21 → xync_db-0.5.5.dev25}/src/xync_db/migrations/models/seed/a_bin_seed.py
RENAMED
|
File without changes
|
{xync_db-0.5.5.dev21 → xync_db-0.5.5.dev25}/src/xync_db/migrations/models/seed/a_country_seed.py
RENAMED
|
File without changes
|
|
File without changes
|
{xync_db-0.5.5.dev21 → xync_db-0.5.5.dev25}/src/xync_db/migrations/models/seed/b_htx_seed.py
RENAMED
|
File without changes
|
{xync_db-0.5.5.dev21 → xync_db-0.5.5.dev25}/src/xync_db/migrations/models/seed/c_net_seed.py
RENAMED
|
File without changes
|
{xync_db-0.5.5.dev21 → xync_db-0.5.5.dev25}/src/xync_db/migrations/models/seed/countries.json
RENAMED
|
File without changes
|
|
File without changes
|
{xync_db-0.5.5.dev21 → xync_db-0.5.5.dev25}/src/xync_db/migrations/models/seed/xync_files.sql
RENAMED
|
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
|