funora 0.0.1.dev2__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.
Files changed (87) hide show
  1. funora/__init__.py +265 -0
  2. funora/_account.py +661 -0
  3. funora/_aclient.py +1484 -0
  4. funora/_budget.py +579 -0
  5. funora/_calc.py +182 -0
  6. funora/_canonical.py +235 -0
  7. funora/_catalog.py +473 -0
  8. funora/_chat_history.py +367 -0
  9. funora/_chats.py +392 -0
  10. funora/_chips.py +403 -0
  11. funora/_classify.py +466 -0
  12. funora/_client.py +1490 -0
  13. funora/_currency_switch.py +130 -0
  14. funora/_cursor.py +120 -0
  15. funora/_delivered.py +233 -0
  16. funora/_diff.py +752 -0
  17. funora/_engine.py +4795 -0
  18. funora/_extract.py +124 -0
  19. funora/_field_schema.py +112 -0
  20. funora/_fileio.py +58 -0
  21. funora/_gate.py +69 -0
  22. funora/_hops.py +101 -0
  23. funora/_host.py +120 -0
  24. funora/_identity.py +284 -0
  25. funora/_json.py +31 -0
  26. funora/_listen.py +312 -0
  27. funora/_lot_form.py +331 -0
  28. funora/_market.py +406 -0
  29. funora/_matching.py +147 -0
  30. funora/_money.py +279 -0
  31. funora/_monitoring.py +396 -0
  32. funora/_observed.py +237 -0
  33. funora/_order.py +552 -0
  34. funora/_order_details.py +281 -0
  35. funora/_orders.py +807 -0
  36. funora/_outbound.py +453 -0
  37. funora/_own_lots.py +301 -0
  38. funora/_poll.py +410 -0
  39. funora/_price_audit.py +281 -0
  40. funora/_proxies.py +232 -0
  41. funora/_raise.py +150 -0
  42. funora/_refund.py +102 -0
  43. funora/_result.py +157 -0
  44. funora/_retry.py +213 -0
  45. funora/_review_write.py +138 -0
  46. funora/_reviews.py +584 -0
  47. funora/_runner.py +651 -0
  48. funora/_secret.py +385 -0
  49. funora/_showcase.py +362 -0
  50. funora/_signals.py +375 -0
  51. funora/_skeleton.py +752 -0
  52. funora/_snapshot.py +276 -0
  53. funora/_state.py +279 -0
  54. funora/_stock.py +32 -0
  55. funora/_thread.py +574 -0
  56. funora/_transport.py +1023 -0
  57. funora/_updates.py +292 -0
  58. funora/_verdicts.py +91 -0
  59. funora/_viewing.py +143 -0
  60. funora/_watch.py +825 -0
  61. funora/_watch_state.py +237 -0
  62. funora/_whoami.py +546 -0
  63. funora/bot/__init__.py +52 -0
  64. funora/bot/_delivery.py +341 -0
  65. funora/bot/_outbox.py +261 -0
  66. funora/bot/_runtime.py +447 -0
  67. funora/bot/_spool.py +534 -0
  68. funora/budget.py +311 -0
  69. funora/capabilities.py +297 -0
  70. funora/conformance.py +758 -0
  71. funora/contract.py +73 -0
  72. funora/errors.py +996 -0
  73. funora/events.py +189 -0
  74. funora/extraction.py +420 -0
  75. funora/observe.py +560 -0
  76. funora/operations.py +671 -0
  77. funora/py.typed +0 -0
  78. funora/reconciliation.py +51 -0
  79. funora/response_classes.py +155 -0
  80. funora/retry.py +238 -0
  81. funora/send_outcome.py +78 -0
  82. funora/skeleton_format.py +77 -0
  83. funora-0.0.1.dev2.dist-info/METADATA +294 -0
  84. funora-0.0.1.dev2.dist-info/RECORD +87 -0
  85. funora-0.0.1.dev2.dist-info/WHEEL +4 -0
  86. funora-0.0.1.dev2.dist-info/entry_points.txt +2 -0
  87. funora-0.0.1.dev2.dist-info/licenses/LICENSE +201 -0
funora/operations.py ADDED
@@ -0,0 +1,671 @@
1
+ r"""Операции служб и их свойства.
2
+
3
+ Файл порождён из спецификации, править его руками нельзя: правка исчезнет при
4
+ следующей сборке. Источник - spec/services/*.yaml в репозитории Funora-spec.
5
+ Перестроить: .venv\Scripts\python.exe tools/codegen.py
6
+
7
+ Безопасность операции - половина нормативного входа решения о повторе.
8
+ Вторая половина, класс ошибки, порождается из errors.yaml. Пока эта
9
+ половина была рукописной, смена безопасности в спецификации не
10
+ отражалась нигде - ни в коде, ни в проверке.
11
+
12
+ Повторить небезопасную операцию значит выполнить её дважды: отправить
13
+ покупателю второе сообщение, поднять лот второй раз, списать деньги
14
+ второй раз.
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ from dataclasses import dataclass
20
+ from enum import StrEnum
21
+ from typing import Final
22
+
23
+ __all__ = ["Safety", "Operation", "OPERATIONS"]
24
+
25
+
26
+ class Safety(StrEnum):
27
+ """Безопасность операции при повторе.
28
+
29
+ safe - повтор ничего не меняет: операция только читает.
30
+ idempotent - повтор с тем же ключом идемпотентности даёт тот же
31
+ результат.
32
+ unsafe - повтор выполняет действие дважды.
33
+ """
34
+
35
+ IDEMPOTENT = "idempotent"
36
+ SAFE = "safe"
37
+ UNSAFE = "unsafe"
38
+
39
+
40
+ @dataclass(frozen=True, slots=True)
41
+ class Operation:
42
+ """Свойства одной операции службы.
43
+
44
+ Attributes:
45
+ name (str): Идентификатор операции.
46
+ capability (str): Возможность, которой операция требует.
47
+ safety (Safety): Безопасность при повторе.
48
+ request_class (str): Класс запроса для бюджета.
49
+ returns (str): Тип результата, как объявлен спецификацией.
50
+ errors (tuple[str, ...]): Устойчивые идентификаторы ошибок,
51
+ которыми операция вправе завершиться. Ровно то, что
52
+ вызывающий выписывает в except.
53
+
54
+ Перечень объявлен спецификацией на каждую операцию и до
55
+ сих пор до пакета не доходил: генератор принимал ключ
56
+ errors и выбрасывал его. Расхождение между обещанным и
57
+ возбуждаемым не ловило ничто, и вызывающий, выписавший
58
+ except по контракту, ловил не всё.
59
+ audit (str): Что операция обязана сохранить до того, как
60
+ выполнится. Пустая строка означает, что аудита ей не
61
+ предписано.
62
+
63
+ Ключ принимался и выбрасывался: спецификация требовала
64
+ аудита, пакет о требовании не знал, и связать отказ
65
+ операции с объявлением было нечем.
66
+ audit_fail_closed (bool): Отказывает ли операция, когда
67
+ сохранять некуда. Ложь означает либо отсутствие аудита,
68
+ либо аудит, которым разрешено пренебречь.
69
+ request_provenance (str): Своими ли глазами мы видели тот
70
+ запрос, который операция отправляет.
71
+
72
+ Ось отдельная от возможности, и путать их нельзя.
73
+ Возможность говорит о ПЛОЩАДКЕ - есть ли у аккаунта
74
+ право. Происхождение говорит о НАС.
75
+
76
+ Пустая строка означает, что операция стоит целиком на
77
+ нашем наблюдении. Значение third_party_report означает,
78
+ что часть запроса известна от независимой реализации
79
+ того же протокола, а нашего наблюдения на ней нет.
80
+ provenance_source (str): Кто именно сообщил. Обязателен при
81
+ third_party_report: без имени сообщение неотличимо от
82
+ выдумки, и проверить его нечем.
83
+ provenance_rests_on (str): Какая ИМЕННО часть запроса не
84
+ проверена нами. Без этого читающий переносит недоверие
85
+ либо на всё сразу, либо ни на что.
86
+ """
87
+
88
+ name: str
89
+ capability: str
90
+ safety: Safety
91
+ request_class: str
92
+ returns: str
93
+ errors: tuple[str, ...]
94
+ audit: str = ""
95
+ audit_fail_closed: bool = False
96
+ request_provenance: str = ""
97
+ provenance_source: str = ""
98
+ provenance_rests_on: str = ""
99
+ cache_ttl_ms: int = 0
100
+ transport_lane: str = "authenticated"
101
+ cost_hint: int = 0
102
+ cache_invalidate_on: tuple[str, ...] = ()
103
+
104
+
105
+ #: Операции служб по идентификатору.
106
+ OPERATIONS: Final[dict[str, Operation]] = {
107
+ "account.balance": Operation(
108
+ name="account.balance",
109
+ capability="account.balance",
110
+ safety=Safety.SAFE,
111
+ request_class="interactive",
112
+ returns="BalancePage",
113
+ errors=(
114
+ "funora.auth.session_expired",
115
+ "funora.protocol.changed",
116
+ "funora.transport",
117
+ ),
118
+ ),
119
+ "account.get": Operation(
120
+ name="account.get",
121
+ capability="account.profile",
122
+ safety=Safety.SAFE,
123
+ request_class="interactive",
124
+ returns="Account",
125
+ errors=(
126
+ "funora.auth.session_expired",
127
+ "funora.protocol.changed",
128
+ "funora.transport",
129
+ ),
130
+ ),
131
+ "account.refresh": Operation(
132
+ name="account.refresh",
133
+ capability="account.profile",
134
+ safety=Safety.SAFE,
135
+ request_class="interactive",
136
+ returns="Account",
137
+ errors=(
138
+ "funora.auth.session_expired",
139
+ "funora.protocol.changed",
140
+ "funora.transport",
141
+ ),
142
+ ),
143
+ "account.switch_currency": Operation(
144
+ name="account.switch_currency",
145
+ capability="account.switch_currency",
146
+ safety=Safety.IDEMPOTENT,
147
+ request_class="automation",
148
+ returns="CurrencySwitch",
149
+ errors=(
150
+ "funora.capability.unsupported",
151
+ "funora.auth.session_expired",
152
+ "funora.protocol.changed",
153
+ "funora.transport",
154
+ ),
155
+ request_provenance="third_party_report",
156
+ provenance_source="FunPayAPI (бот FunPayCardinal), account.py, get_exchange_rate.",
157
+ provenance_rests_on=(
158
+ "Адрес, имена полей запроса и ключи ответа. НАБЛЮДЁН НАМИ только сам переключатель в "
159
+ "шапке: пункты его несут код ISO 4217 в data-cy, и перечисляют они ПРОЧИЕ валюты, не "
160
+ "текущую. То есть мы видели, какие коды бывают, и не видели, как их отправить."
161
+ ),
162
+ ),
163
+ "capabilities": Operation(
164
+ name="capabilities",
165
+ capability="account.profile",
166
+ safety=Safety.SAFE,
167
+ request_class="poll",
168
+ returns="CapabilityProfile",
169
+ errors=(),
170
+ ),
171
+ "catalog.categories": Operation(
172
+ name="catalog.categories",
173
+ capability="catalog.categories",
174
+ safety=Safety.SAFE,
175
+ request_class="interactive",
176
+ returns="CatalogPage",
177
+ cache_ttl_ms=86400000,
178
+ cache_invalidate_on=("adapter_version_change", "protocol_changed", "session_change"),
179
+ errors=(
180
+ "funora.protocol.changed",
181
+ "funora.transport",
182
+ ),
183
+ ),
184
+ "catalog.field_schema": Operation(
185
+ name="catalog.field_schema",
186
+ capability="catalog.field_schema",
187
+ safety=Safety.SAFE,
188
+ request_class="interactive",
189
+ returns="FieldSchema",
190
+ errors=(
191
+ "funora.capability.unsupported",
192
+ "funora.protocol.changed",
193
+ "funora.transport",
194
+ ),
195
+ ),
196
+ "catalog.search": Operation(
197
+ name="catalog.search",
198
+ capability="catalog.search",
199
+ safety=Safety.SAFE,
200
+ request_class="interactive",
201
+ returns="CatalogPage",
202
+ transport_lane="public_read",
203
+ errors=(
204
+ "funora.validation",
205
+ "funora.protocol.changed",
206
+ "funora.transport",
207
+ ),
208
+ ),
209
+ "chats.buyer_viewing": Operation(
210
+ name="chats.buyer_viewing",
211
+ capability="chats.buyer_viewing",
212
+ safety=Safety.SAFE,
213
+ request_class="poll",
214
+ returns="BuyerViewing[]",
215
+ errors=(
216
+ "funora.capability.unsupported",
217
+ "funora.auth.session_expired",
218
+ "funora.protocol.changed",
219
+ "funora.transport",
220
+ ),
221
+ request_provenance="third_party_report",
222
+ provenance_source=(
223
+ "FunPayAPI (бот FunPayCardinal), account.py, get_buyers_viewing и "
224
+ "__parse_buyer_viewing."
225
+ ),
226
+ provenance_rests_on=(
227
+ "ТОЛЬКО ОТВЕТ. Подписка наблюдена НАМИ - объект вида c-p-u лежит в семи наших записях "
228
+ "канала, и состав его известен: признак, идентификатор из восьми цифр, метка из "
229
+ "восьми знаков.\nНе наблюдено, ЧТО приходит в ответ на такую подписку. Известно от "
230
+ "сторонней реализации: при пустом признаке покупатель не смотрит ничего, иначе внутри "
231
+ "лежит разметка со ссылкой на лот."
232
+ ),
233
+ ),
234
+ "chats.history": Operation(
235
+ name="chats.history",
236
+ capability="chats.history",
237
+ safety=Safety.SAFE,
238
+ request_class="interactive",
239
+ returns="Thread",
240
+ errors=(
241
+ "funora.domain.not_found",
242
+ "funora.auth.session_expired",
243
+ "funora.protocol.changed",
244
+ "funora.protocol.unexpected_response",
245
+ "funora.transport",
246
+ ),
247
+ ),
248
+ "chats.history_before": Operation(
249
+ name="chats.history_before",
250
+ capability="chats.history_pagination",
251
+ safety=Safety.SAFE,
252
+ request_class="interactive",
253
+ returns="ChatHistory",
254
+ errors=(
255
+ "funora.validation",
256
+ "funora.capability.unsupported",
257
+ "funora.state.cursor_incompatible",
258
+ "funora.protocol.unexpected_response",
259
+ "funora.transport",
260
+ ),
261
+ request_provenance="third_party_report",
262
+ provenance_source=(
263
+ "FunPayAPI (бот FunPayCardinal), account.py, get_chat_history - адрес chat/history, "
264
+ "параметры node и last_message, заголовок x-requested-with, и разбор ответа по ключам "
265
+ "chat.messages с полями id, author, html."
266
+ ),
267
+ provenance_rests_on=(
268
+ "ВЕСЬ ЗАПРОС ЦЕЛИКОМ, а не вывод о нём: этой точки мы не наблюдали ни разу. Наш "
269
+ "собственный способ прочитать переписку - страница /chat/?node=, и она отдаёт только "
270
+ "то, что площадка показала сразу. Чужое здесь и адрес, и имена обоих параметров, и "
271
+ "форма ответа.\nОтдельно чужим остаётся НАПРАВЛЕНИЕ: что курсор отдаёт сообщения "
272
+ "СТАРШЕ него, а не младше. Именно это утверждение операция проверяет сама - см. "
273
+ "примечание о сверке."
274
+ ),
275
+ ),
276
+ "chats.list": Operation(
277
+ name="chats.list",
278
+ capability="chats.list",
279
+ safety=Safety.SAFE,
280
+ request_class="poll",
281
+ returns="ChatsPage",
282
+ errors=(
283
+ "funora.auth.session_expired",
284
+ "funora.protocol.changed",
285
+ "funora.protocol.unexpected_response",
286
+ "funora.transport",
287
+ ),
288
+ ),
289
+ "chats.mark_read": Operation(
290
+ name="chats.mark_read",
291
+ capability="chats.mark_read",
292
+ safety=Safety.IDEMPOTENT,
293
+ request_class="automation",
294
+ returns="void",
295
+ errors=(
296
+ "funora.capability.unsupported",
297
+ "funora.transport",
298
+ ),
299
+ request_provenance="third_party_report",
300
+ provenance_source=(
301
+ "FunPayAPI (бот FunPayCardinal), account.py, send_message - флаг leave_as_unread "
302
+ "убирает диалог из подписки, и это единственное, чем он отличается."
303
+ ),
304
+ provenance_rests_on=(
305
+ "НЕ форма запроса, а ВЫВОД о его действии. Форма наша: наши записи показывают, что "
306
+ "страница при открытом диалоге опрашивает канал с подпиской на него. Чужое - "
307
+ "утверждение, что именно подписка снимает пометку непрочитанного."
308
+ ),
309
+ ),
310
+ "chats.send_image": Operation(
311
+ name="chats.send_image",
312
+ capability="chats.send_image",
313
+ safety=Safety.UNSAFE,
314
+ request_class="interactive",
315
+ returns="SendResult",
316
+ errors=(
317
+ "funora.capability.unsupported",
318
+ "funora.budget.exhausted",
319
+ "funora.transport",
320
+ ),
321
+ ),
322
+ "chats.send_text": Operation(
323
+ name="chats.send_text",
324
+ capability="chats.send_text",
325
+ safety=Safety.UNSAFE,
326
+ request_class="interactive",
327
+ returns="SendResult",
328
+ errors=(
329
+ "funora.domain.not_found",
330
+ "funora.budget.exhausted",
331
+ "funora.auth.session_expired",
332
+ "funora.transport.timeout",
333
+ "funora.transport",
334
+ ),
335
+ ),
336
+ "chips.calculate_prices": Operation(
337
+ name="chips.calculate_prices",
338
+ capability="chips.calculate_prices",
339
+ safety=Safety.SAFE,
340
+ request_class="interactive",
341
+ returns="PriceCalculation",
342
+ errors=(
343
+ "funora.capability.unsupported",
344
+ "funora.auth.session_expired",
345
+ "funora.protocol.changed",
346
+ "funora.transport",
347
+ ),
348
+ request_provenance="third_party_report",
349
+ provenance_source="FunPayAPI (бот FunPayCardinal), account.py, calc.",
350
+ provenance_rests_on=(
351
+ "Адрес, имена полей запроса и ключи ответа. НАБЛЮДЁН НАМИ только итог расчёта на "
352
+ "странице правки лота - таблица .table-buyers-prices, которую эта же точка и "
353
+ "перерисовывает - но на странице ЛОТА, не чипов. Здесь непроверено и то, и другое."
354
+ ),
355
+ ),
356
+ "chips.offers": Operation(
357
+ name="chips.offers",
358
+ capability="chips.offers",
359
+ safety=Safety.SAFE,
360
+ request_class="monitoring",
361
+ returns="ChipsPage",
362
+ transport_lane="public_read",
363
+ cost_hint=1,
364
+ errors=(
365
+ "funora.capability.unsupported",
366
+ "funora.protocol.changed",
367
+ "funora.transport.rate_limited",
368
+ "funora.transport",
369
+ ),
370
+ ),
371
+ "lots.activate": Operation(
372
+ name="lots.activate",
373
+ capability="lots.activate",
374
+ safety=Safety.IDEMPOTENT,
375
+ request_class="automation",
376
+ returns="LotForm",
377
+ errors=(
378
+ "funora.capability.unsupported",
379
+ "funora.domain.not_found",
380
+ "funora.auth.session_expired",
381
+ "funora.transport",
382
+ ),
383
+ request_provenance="third_party_report",
384
+ provenance_source="FunPayAPI (бот FunPayCardinal), types.py, LotFields.renew_fields.",
385
+ provenance_rests_on=(
386
+ "Вид запроса при СНЯТОМ флажке active. Оба наших снимка сохранения сняты с "
387
+ "отмеченным; сторонний источник шлёт поле всегда, при выключенном лоте - пустой "
388
+ "строкой, тогда как наше рассуждение говорило, что снятый флажок не уходит вовсе."
389
+ ),
390
+ ),
391
+ "lots.calculate_prices": Operation(
392
+ name="lots.calculate_prices",
393
+ capability="lots.calculate_prices",
394
+ safety=Safety.SAFE,
395
+ request_class="interactive",
396
+ returns="PriceCalculation",
397
+ errors=(
398
+ "funora.capability.unsupported",
399
+ "funora.auth.session_expired",
400
+ "funora.protocol.changed",
401
+ "funora.transport",
402
+ ),
403
+ request_provenance="third_party_report",
404
+ provenance_source="FunPayAPI (бот FunPayCardinal), account.py, calc.",
405
+ provenance_rests_on=(
406
+ "Адрес, имена полей запроса и ключи ответа. НАБЛЮДЁН НАМИ только итог расчёта на "
407
+ "странице правки лота - таблица .table-buyers-prices, которую эта же точка и "
408
+ "перерисовывает. То есть мы видели, ЧТО она возвращает, и не видели, КАК её спросить."
409
+ ),
410
+ ),
411
+ "lots.deactivate": Operation(
412
+ name="lots.deactivate",
413
+ capability="lots.deactivate",
414
+ safety=Safety.IDEMPOTENT,
415
+ request_class="automation",
416
+ returns="LotForm",
417
+ errors=(
418
+ "funora.capability.unsupported",
419
+ "funora.domain.not_found",
420
+ "funora.auth.session_expired",
421
+ "funora.transport",
422
+ ),
423
+ request_provenance="third_party_report",
424
+ provenance_source="FunPayAPI (бот FunPayCardinal), types.py, LotFields.renew_fields.",
425
+ provenance_rests_on=(
426
+ "Вид запроса при СНЯТОМ флажке active. Оба наших снимка сохранения сняты с "
427
+ "отмеченным; сторонний источник шлёт поле всегда, при выключенном лоте - пустой "
428
+ "строкой, тогда как наше рассуждение говорило, что снятый флажок не уходит вовсе."
429
+ ),
430
+ ),
431
+ "lots.form": Operation(
432
+ name="lots.form",
433
+ capability="lots.form",
434
+ safety=Safety.SAFE,
435
+ request_class="automation",
436
+ returns="LotForm",
437
+ cost_hint=1,
438
+ errors=(
439
+ "funora.capability.unsupported",
440
+ "funora.domain.not_found",
441
+ "funora.auth.session_expired",
442
+ "funora.protocol.changed",
443
+ "funora.transport",
444
+ ),
445
+ ),
446
+ "lots.list_own": Operation(
447
+ name="lots.list_own",
448
+ capability="lots.list_own",
449
+ safety=Safety.SAFE,
450
+ request_class="interactive",
451
+ returns="OwnLotsPage",
452
+ errors=(
453
+ "funora.auth.session_expired",
454
+ "funora.protocol.changed",
455
+ "funora.protocol.unexpected_response",
456
+ "funora.transport",
457
+ ),
458
+ ),
459
+ "lots.promote": Operation(
460
+ name="lots.promote",
461
+ capability="lots.promote",
462
+ safety=Safety.UNSAFE,
463
+ request_class="automation",
464
+ returns="RaiseResult",
465
+ errors=(
466
+ "funora.capability.unsupported",
467
+ "funora.transport.timeout",
468
+ "funora.transport",
469
+ ),
470
+ ),
471
+ "lots.showcase": Operation(
472
+ name="lots.showcase",
473
+ capability="lots.showcase",
474
+ safety=Safety.SAFE,
475
+ request_class="interactive",
476
+ returns="ShowcasePage",
477
+ errors=(
478
+ "funora.auth.session_expired",
479
+ "funora.protocol.changed",
480
+ "funora.transport",
481
+ ),
482
+ ),
483
+ "lots.update_price": Operation(
484
+ name="lots.update_price",
485
+ capability="lots.update_price",
486
+ safety=Safety.IDEMPOTENT,
487
+ request_class="automation",
488
+ returns="LotForm",
489
+ errors=(
490
+ "funora.capability.unsupported",
491
+ "funora.domain.precondition_failed",
492
+ "funora.domain.not_found",
493
+ "funora.auth.session_expired",
494
+ "funora.transport",
495
+ ),
496
+ audit="before_state",
497
+ audit_fail_closed=True,
498
+ ),
499
+ "market.offers": Operation(
500
+ name="market.offers",
501
+ capability="market.offers",
502
+ safety=Safety.SAFE,
503
+ request_class="monitoring",
504
+ returns="MarketPage",
505
+ transport_lane="public_read",
506
+ cost_hint=1,
507
+ errors=(
508
+ "funora.protocol.changed",
509
+ "funora.transport.rate_limited",
510
+ "funora.transport",
511
+ ),
512
+ ),
513
+ "market.snapshot": Operation(
514
+ name="market.snapshot",
515
+ capability="market.snapshot",
516
+ safety=Safety.SAFE,
517
+ request_class="monitoring",
518
+ returns="MarketSnapshot",
519
+ transport_lane="public_read",
520
+ cost_hint=1,
521
+ errors=(
522
+ "funora.protocol.changed",
523
+ "funora.transport.rate_limited",
524
+ "funora.transport",
525
+ ),
526
+ ),
527
+ "orders.details": Operation(
528
+ name="orders.details",
529
+ capability="orders.details",
530
+ safety=Safety.SAFE,
531
+ request_class="interactive",
532
+ returns="OrderDetailsBatch",
533
+ errors=(
534
+ "funora.capability.unsupported",
535
+ "funora.auth.session_expired",
536
+ "funora.protocol.changed",
537
+ "funora.transport",
538
+ ),
539
+ request_provenance="third_party_report",
540
+ provenance_source="FunPayAPI (бот FunPayCardinal), account.py, get_orders_by_ids.",
541
+ provenance_rests_on=(
542
+ "ВСЁ. Ни адреса, ни полей запроса, ни ключей ответа мы не наблюдали ни разу - ни в "
543
+ "снимках, ни в записях запросов, ни в пробах.\nЭто отличает точку от прочих на "
544
+ "вторичном источнике: у поднятия наблюдён запрос, у отзыва - два поля из четырёх, у "
545
+ "расчёта цены - итог на странице. Здесь не наблюдено ничего."
546
+ ),
547
+ ),
548
+ "orders.get": Operation(
549
+ name="orders.get",
550
+ capability="orders.get",
551
+ safety=Safety.SAFE,
552
+ request_class="interactive",
553
+ returns="OrderView",
554
+ errors=(
555
+ "funora.domain.not_found",
556
+ "funora.auth.session_expired",
557
+ "funora.protocol.changed",
558
+ "funora.transport",
559
+ ),
560
+ ),
561
+ "orders.list": Operation(
562
+ name="orders.list",
563
+ capability="orders.list",
564
+ safety=Safety.SAFE,
565
+ request_class="interactive",
566
+ returns="OrdersPage",
567
+ errors=(
568
+ "funora.validation",
569
+ "funora.auth.session_expired",
570
+ "funora.protocol.changed",
571
+ "funora.protocol.unexpected_response",
572
+ "funora.transport",
573
+ ),
574
+ ),
575
+ "orders.refund": Operation(
576
+ name="orders.refund",
577
+ capability="orders.refund",
578
+ safety=Safety.UNSAFE,
579
+ request_class="automation",
580
+ returns="RefundResult",
581
+ errors=(
582
+ "funora.capability.unsupported",
583
+ "funora.domain.precondition_failed",
584
+ "funora.domain.not_found",
585
+ "funora.auth.session_expired",
586
+ "funora.transport",
587
+ ),
588
+ request_provenance="third_party_report",
589
+ provenance_source="FunPayAPI (бот FunPayCardinal), account.py, refund.",
590
+ provenance_rests_on=(
591
+ "НЕ ЗАПРОС, А ЧТЕНИЕ ОТВЕТА. Оба поля запроса наблюдены НАМИ: форма возврата лежит на "
592
+ "странице заказа, и в ней csrf_token и id. Адрес - в её же атрибуте action.\nНе "
593
+ "наблюдено, ЧЕМ площадка отвечает. Известно от независимой реализации: объект с "
594
+ "признаком отказа error и сообщением msg."
595
+ ),
596
+ ),
597
+ "reviews.get": Operation(
598
+ name="reviews.get",
599
+ capability="reviews.get",
600
+ safety=Safety.SAFE,
601
+ request_class="interactive",
602
+ returns="ReviewsPage",
603
+ errors=(
604
+ "funora.state.cursor_incompatible",
605
+ "funora.validation",
606
+ "funora.auth.session_expired",
607
+ "funora.protocol.changed",
608
+ "funora.transport",
609
+ ),
610
+ ),
611
+ "reviews.leave": Operation(
612
+ name="reviews.leave",
613
+ capability="reviews.leave",
614
+ safety=Safety.IDEMPOTENT,
615
+ request_class="automation",
616
+ returns="ReviewResult",
617
+ errors=(
618
+ "funora.capability.unsupported",
619
+ "funora.domain.not_found",
620
+ "funora.auth.session_expired",
621
+ "funora.transport",
622
+ ),
623
+ request_provenance="third_party_report",
624
+ provenance_source=(
625
+ "FunPayAPI (бот FunPayCardinal), account.py, send_review и delete_review."
626
+ ),
627
+ provenance_rests_on=(
628
+ "Состав полей запроса и форма ответа. Два поля из четырёх - номер заказа и "
629
+ "идентификатор автора - наблюдены НАМИ: они лежат атрибутами data-order и data-author "
630
+ "на странице заказа и уже читаются разбором. Не наблюдены имена полей запроса, адрес "
631
+ "и то, что приходит в ответ."
632
+ ),
633
+ ),
634
+ "reviews.remove": Operation(
635
+ name="reviews.remove",
636
+ capability="reviews.remove",
637
+ safety=Safety.IDEMPOTENT,
638
+ request_class="automation",
639
+ returns="ReviewResult",
640
+ errors=(
641
+ "funora.capability.unsupported",
642
+ "funora.domain.not_found",
643
+ "funora.auth.session_expired",
644
+ "funora.transport",
645
+ ),
646
+ request_provenance="third_party_report",
647
+ provenance_source=(
648
+ "FunPayAPI (бот FunPayCardinal), account.py, send_review и delete_review."
649
+ ),
650
+ provenance_rests_on=(
651
+ "Состав полей запроса и форма ответа. Два поля из четырёх - номер заказа и "
652
+ "идентификатор автора - наблюдены НАМИ: они лежат атрибутами data-order и data-author "
653
+ "на странице заказа и уже читаются разбором. Не наблюдены имена полей запроса, адрес "
654
+ "и то, что приходит в ответ."
655
+ ),
656
+ ),
657
+ "session.health": Operation(
658
+ name="session.health",
659
+ capability="account.profile",
660
+ safety=Safety.SAFE,
661
+ request_class="poll",
662
+ returns="SessionHealth",
663
+ cost_hint=1,
664
+ errors=(
665
+ "funora.auth.session_expired",
666
+ "funora.auth.access_blocked",
667
+ "funora.auth.challenge_required",
668
+ "funora.transport",
669
+ ),
670
+ ),
671
+ }
funora/py.typed ADDED
File without changes