xync-db 0.5.5.dev23__tar.gz → 0.5.5.dev26__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.
Files changed (40) hide show
  1. {xync_db-0.5.5.dev23 → xync_db-0.5.5.dev26}/PKG-INFO +1 -1
  2. {xync_db-0.5.5.dev23 → xync_db-0.5.5.dev26}/src/xync_db/login_store.py +1 -1
  3. {xync_db-0.5.5.dev23 → xync_db-0.5.5.dev26}/src/xync_db/models/__init__.py +39 -10
  4. {xync_db-0.5.5.dev23 → xync_db-0.5.5.dev26}/.gitignore +0 -0
  5. {xync_db-0.5.5.dev23 → xync_db-0.5.5.dev26}/README.md +0 -0
  6. {xync_db-0.5.5.dev23 → xync_db-0.5.5.dev26}/pyproject.toml +0 -0
  7. {xync_db-0.5.5.dev23 → xync_db-0.5.5.dev26}/src/xync_db/__init__.py +0 -0
  8. {xync_db-0.5.5.dev23 → xync_db-0.5.5.dev26}/src/xync_db/config.py +0 -0
  9. {xync_db-0.5.5.dev23 → xync_db-0.5.5.dev26}/src/xync_db/enums.py +0 -0
  10. {xync_db-0.5.5.dev23 → xync_db-0.5.5.dev26}/src/xync_db/exceptions.py +0 -0
  11. {xync_db-0.5.5.dev23 → xync_db-0.5.5.dev26}/src/xync_db/graph.py +0 -0
  12. {xync_db-0.5.5.dev23 → xync_db-0.5.5.dev26}/src/xync_db/i18n.py +0 -0
  13. {xync_db-0.5.5.dev23 → xync_db-0.5.5.dev26}/src/xync_db/logo.png +0 -0
  14. {xync_db-0.5.5.dev23 → xync_db-0.5.5.dev26}/src/xync_db/migrations/CLAUDE.md +0 -0
  15. {xync_db-0.5.5.dev23 → xync_db-0.5.5.dev26}/src/xync_db/migrations/__init__.py +0 -0
  16. {xync_db-0.5.5.dev23 → xync_db-0.5.5.dev26}/src/xync_db/migrations/models/0001_initial.py +0 -0
  17. {xync_db-0.5.5.dev23 → xync_db-0.5.5.dev26}/src/xync_db/migrations/models/0002_triggers.py +0 -0
  18. {xync_db-0.5.5.dev23 → xync_db-0.5.5.dev26}/src/xync_db/migrations/models/0003_seed.py +0 -0
  19. {xync_db-0.5.5.dev23 → xync_db-0.5.5.dev26}/src/xync_db/migrations/models/__init__.py +0 -0
  20. {xync_db-0.5.5.dev23 → xync_db-0.5.5.dev26}/src/xync_db/migrations/models/seed/__init__.py +0 -0
  21. {xync_db-0.5.5.dev23 → xync_db-0.5.5.dev26}/src/xync_db/migrations/models/seed/a_actor_seed.py +0 -0
  22. {xync_db-0.5.5.dev23 → xync_db-0.5.5.dev26}/src/xync_db/migrations/models/seed/a_bin_seed.py +0 -0
  23. {xync_db-0.5.5.dev23 → xync_db-0.5.5.dev26}/src/xync_db/migrations/models/seed/a_country_seed.py +0 -0
  24. {xync_db-0.5.5.dev23 → xync_db-0.5.5.dev26}/src/xync_db/migrations/models/seed/a_tg_seed.py +0 -0
  25. {xync_db-0.5.5.dev23 → xync_db-0.5.5.dev26}/src/xync_db/migrations/models/seed/b_htx_seed.py +0 -0
  26. {xync_db-0.5.5.dev23 → xync_db-0.5.5.dev26}/src/xync_db/migrations/models/seed/c_net_seed.py +0 -0
  27. {xync_db-0.5.5.dev23 → xync_db-0.5.5.dev26}/src/xync_db/migrations/models/seed/countries.json +0 -0
  28. {xync_db-0.5.5.dev23 → xync_db-0.5.5.dev26}/src/xync_db/migrations/models/seed/ranges.csv +0 -0
  29. {xync_db-0.5.5.dev23 → xync_db-0.5.5.dev26}/src/xync_db/migrations/models/seed/xync_files.sql +0 -0
  30. {xync_db-0.5.5.dev23 → xync_db-0.5.5.dev26}/src/xync_db/models/CLAUDE.md +0 -0
  31. {xync_db-0.5.5.dev23 → xync_db-0.5.5.dev26}/src/xync_db/py.typed +0 -0
  32. {xync_db-0.5.5.dev23 → xync_db-0.5.5.dev26}/src/xync_db/shared.py +0 -0
  33. {xync_db-0.5.5.dev23 → xync_db-0.5.5.dev26}/src/xync_db/typs/__init__.py +0 -0
  34. {xync_db-0.5.5.dev23 → xync_db-0.5.5.dev26}/src/xync_db/typs/db/__init__.py +0 -0
  35. {xync_db-0.5.5.dev23 → xync_db-0.5.5.dev26}/src/xync_db/typs/db/actor.py +0 -0
  36. {xync_db-0.5.5.dev23 → xync_db-0.5.5.dev26}/src/xync_db/typs/db/ad.py +0 -0
  37. {xync_db-0.5.5.dev23 → xync_db-0.5.5.dev26}/src/xync_db/typs/db/common.py +0 -0
  38. {xync_db-0.5.5.dev23 → xync_db-0.5.5.dev26}/src/xync_db/typs/db/cred.py +0 -0
  39. {xync_db-0.5.5.dev23 → xync_db-0.5.5.dev26}/src/xync_db/typs/db/order.py +0 -0
  40. {xync_db-0.5.5.dev23 → xync_db-0.5.5.dev26}/src/xync_db/typs/db/pm.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: xync-db
3
- Version: 0.5.5.dev23
3
+ Version: 0.5.5.dev26
4
4
  Summary: Xync DB schema: Tortoise ORM models, enums, pydantic DTOs.
5
5
  Author-email: Mike Artemiev <mixartemev@gmail.com>
6
6
  License: LicenseRef-EULA
@@ -88,7 +88,7 @@ async def login_fulfil(token: str, payload: dict[str, Any]) -> bool:
88
88
  return True
89
89
 
90
90
 
91
- async def login_take(token: str) -> dict[str, Any] | None | object:
91
+ async def login_take(token: str) -> dict[str, Any] | object | None:
92
92
  """Poll a token. Returns `MISSING` if it never existed / expired, `None` if it exists but
93
93
  Start wasn't tapped yet, or the payload dict once ready (consumed: the token is deleted)."""
94
94
  if _redis is not None:
@@ -490,6 +490,16 @@ class User(TgUser, TsTrait):
490
490
  # self.pm_clients = {pma.pm_id: pma.client() for pma in self.pm_agents}
491
491
  # self.agent_clients = {a.id: a.client() for a in self.person.actor.agents}
492
492
 
493
+ @property
494
+ def handle(self) -> str | None:
495
+ """«@username» из Telegram либо None (ждёт prefetched `username`)."""
496
+ return self.username.username and "@" + self.username.username
497
+
498
+ @property
499
+ def title(self) -> str:
500
+ """Отображаемое имя: first_name → @handle → id:N (ждёт prefetched `username`)."""
501
+ return self.first_name or self.handle or f"id:{self.id}"
502
+
493
503
  @classmethod
494
504
  async def mark_membership(
495
505
  cls, username_id: int, *, in_channel: bool | None = None, in_chat: bool | None = None
@@ -687,13 +697,7 @@ class User(TgUser, TsTrait):
687
697
  )
688
698
  c["amount"] += a["free"] / 10**scale
689
699
  users = await cls.filter(id__in=[uid for uid in ss if uid]).prefetch_related("username")
690
- names = {
691
- u.id: (
692
- u.first_name or (u.username and u.username.username and "@" + u.username.username) or f"id:{u.id}",
693
- u.username and u.username.username and "@" + u.username.username,
694
- )
695
- for u in users
696
- }
700
+ names = {u.id: (u.title, u.handle) for u in users}
697
701
  return cls._assemble_rich(ss, cur_meta, usdt_rate, coins_by_user, names)
698
702
 
699
703
  async def balance(self, cur_id: int, ts: int | None = None) -> int:
@@ -1245,9 +1249,25 @@ class Agent(Model, TsTrait):
1245
1249
 
1246
1250
  @classmethod
1247
1251
  async def upsert(
1248
- cls, exid: int, host: str, auth: dict, exp: int | None, rname: str, nick: str, email: str | None = None
1252
+ cls,
1253
+ exid: int,
1254
+ host: str,
1255
+ auth: dict,
1256
+ exp: int | None,
1257
+ rname: str | None,
1258
+ nick: str,
1259
+ email: str | None = None,
1260
+ user: User | None = None,
1249
1261
  ) -> tuple[int, Agent, bool]:
1250
- """Единый писатель агента (за `/public/set-agent`). Bootstrap-персону нового актора резолвим
1262
+ """Единый писатель агента (за `/public/set-agent` и OAuth «Connect Bybit»). `exp=None` — срок
1263
+ web-сессии неизвестен (Htx/Mexc, голые API-ключи из OAuth) → `expire_at` не трогаем. У OAuth-ключей
1264
+ и имени нет (`rname=None`, `nick=""`) — их дозаполнит `Ex.person_save`/`actor_save` по данным биржи.
1265
+
1266
+ `user` — владелец УЖЕ известен (OAuth из приложения залогиненного юзера): актор сразу
1267
+ привязывается к нему `Actor.set_user` (тот же мердж персон, что по ответу админа), без
1268
+ USER_DEF-вопроса «чей это агент?». Актор чужого юзера → PermissionError, агента не трогаем.
1269
+
1270
+ Bootstrap-персону нового актора резолвим
1251
1271
  ПО `note` (`"{ex_id}:{exid}"`, уникальному по актору) — тем же ключом, что клиентский
1252
1272
  `Ex.person_save`, чтобы у Person был ОДИН путь создания (D-035). По НЕуникальному `name` НЕ
1253
1273
  ищем: именно `get_or_none(name=rname)` ронял прод `MultipleObjectsReturned` на тёзках без юзера.
@@ -1265,6 +1285,8 @@ class Agent(Model, TsTrait):
1265
1285
  note = f"{ex.id}:{exid}" # ключ bootstrap-персоны актора (как в Ex.person_save)
1266
1286
  if person := await Person.get_or_none(note=note):
1267
1287
  pass # актор уже знаком — берём его bootstrap-персону
1288
+ elif user: # владелец известен — персона без USER_DEF (админа не зовём), к юзеру ниже set_user'ом
1289
+ person = await Person.create(name=rname, note=note)
1268
1290
  else:
1269
1291
  # gmail-автолинк: замаскированная биржей почта, однозначно (ровно 1 gmail по LIKE,
1270
1292
  # где `*`→`_`) указывающая на юзера, сразу привязывает актора к его персоне — без
@@ -1286,9 +1308,16 @@ class Agent(Model, TsTrait):
1286
1308
  person = await Person.create(name=rname, note=note, status=PersonStatus.USER_DEF)
1287
1309
  err = 1 # ждём привязки к юзеру через def_user
1288
1310
  actor = await Actor.create(ex=ex, exid=exid, name=nick, person=person)
1311
+ if user:
1312
+ await actor.fetch_related("person")
1313
+ if actor.person.user_id not in (None, user.id):
1314
+ raise PermissionError(f"Actor#{actor.id} ({ex.name}:{exid}) belongs to user#{actor.person.user_id}")
1315
+ if not actor.person.user_id:
1316
+ await actor.set_user(user.username_id)
1289
1317
  if ag := await Agent.get_or_none(actor=actor):
1290
1318
  ag.auth |= auth
1291
- ag.expire_at = exp
1319
+ if exp is not None:
1320
+ ag.expire_at = exp
1292
1321
  await ag.save(update_fields=["auth", "expire_at"])
1293
1322
  created = False
1294
1323
  else:
File without changes
File without changes