xync-client 0.0.155__py3-none-any.whl → 0.0.162__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.
@@ -1,59 +1,44 @@
1
- """
2
- MEXC P2P OpenAPI v1.2 Async Client
3
- """
1
+ from enum import IntEnum
2
+ from typing import Optional, Literal
4
3
 
5
- import hmac
6
- import hashlib
7
- import time
8
- from typing import Optional, List, Literal
9
- from decimal import Decimal
10
-
11
- import aiohttp
12
4
  from pydantic import BaseModel, Field
13
5
 
14
-
15
6
  # ============ Enums ============
7
+ Side = Literal["BUY", "SELL"]
16
8
 
17
9
 
18
- class Side(str):
19
- BUY = "BUY"
20
- SELL = "SELL"
21
-
10
+ class AdvStatus(IntEnum):
11
+ CLOSE = 0
12
+ OPEN = 1
13
+ DELETE = 2
14
+ LOW_STOCK = 3
22
15
 
23
- class AdvStatus(str):
24
- CLOSE = "CLOSE"
25
- OPEN = "OPEN"
26
- DELETE = "DELETE"
27
- LOW_STOCK = "LOW_STOCK"
28
16
 
17
+ class OrderDealState(IntEnum):
18
+ NOT_PAID = 0
19
+ PAID = 1
20
+ WAIT_PROCESS = 2
21
+ PROCESSING = 3
22
+ DONE = 4
23
+ CANCEL = 5
24
+ INVALID = 6
25
+ REFUSE = 7
26
+ TIMEOUT = 8
29
27
 
30
- class OrderDealState(str):
31
- NOT_PAID = "NOT_PAID"
32
- PAID = "PAID"
33
- WAIT_PROCESS = "WAIT_PROCESS"
34
- PROCESSING = "PROCESSING"
35
- DONE = "DONE"
36
- CANCEL = "CANCEL"
37
- INVALID = "INVALID"
38
- REFUSE = "REFUSE"
39
- TIMEOUT = "TIMEOUT"
40
28
 
41
-
42
- class NotifyType(str):
43
- SMS = "SMS"
44
- MAIL = "MAIL"
45
- GA = "GA"
29
+ class NotifyType(IntEnum):
30
+ SMS = 0
31
+ MAIL = 1
32
+ GA = 2
46
33
 
47
34
 
48
35
  # ============ Request Models ============
49
-
50
-
51
36
  class CreateUpdateAdRequest(BaseModel):
52
37
  advNo: Optional[str] = None
53
38
  payTimeLimit: int
54
- initQuantity: Decimal
55
- supplyQuantity: Optional[Decimal] = None
56
- price: Decimal
39
+ initQuantity: float
40
+ supplyQuantity: Optional[float] = None
41
+ price: float
57
42
  coinId: str
58
43
  countryCode: Optional[str] = None
59
44
  side: str
@@ -63,8 +48,8 @@ class CreateUpdateAdRequest(BaseModel):
63
48
  payMethod: str
64
49
  autoReplyMsg: Optional[str] = None
65
50
  tradeTerms: Optional[str] = None
66
- minSingleTransAmount: Decimal
67
- maxSingleTransAmount: Decimal
51
+ minSingleTransAmount: float
52
+ maxSingleTransAmount: float
68
53
  kycLevel: Optional[str] = None
69
54
  requireMobile: Optional[bool] = None
70
55
  userAllTradeCountMin: int
@@ -72,15 +57,15 @@ class CreateUpdateAdRequest(BaseModel):
72
57
  exchangeCount: Optional[int] = None
73
58
  maxPayLimit: Optional[int] = None
74
59
  buyerRegDaysLimit: Optional[int] = None
75
- creditAmount: Optional[Decimal] = None
60
+ creditAmount: Optional[float] = None
76
61
  blockTrade: Optional[bool] = None
77
62
  deviceId: Optional[str] = None
78
63
 
79
64
 
80
65
  class CreateOrderRequest(BaseModel):
81
66
  advNo: str
82
- amount: Optional[Decimal] = None
83
- tradableQuantity: Optional[Decimal] = None
67
+ amount: Optional[float] = None
68
+ tradableQuantity: Optional[float] = None
84
69
  userConfirmPaymentId: int
85
70
  userConfirmPayMethodId: Optional[int] = None
86
71
  deviceId: Optional[str] = None
@@ -102,8 +87,6 @@ class ServiceSwitchRequest(BaseModel):
102
87
 
103
88
 
104
89
  # ============ Response Models ============
105
-
106
-
107
90
  class BaseResponse(BaseModel):
108
91
  code: int
109
92
  msg: str
@@ -112,11 +95,11 @@ class BaseResponse(BaseModel):
112
95
  class PaymentInfo(BaseModel):
113
96
  id: int
114
97
  payMethod: int
115
- bankName: str
116
- account: str
117
- bankAddress: str
118
- payee: str
119
- extend: str
98
+ bankName: str = None
99
+ account: str = None
100
+ bankAddress: str = None
101
+ payee: str = None
102
+ extend: str = None
120
103
 
121
104
 
122
105
  class MerchantInfo(BaseModel):
@@ -138,38 +121,41 @@ class MerchantStatistics(BaseModel):
138
121
  totalBuyCount: int
139
122
  totalSellCount: int
140
123
  doneLastMonthCount: int
141
- avgBuyHandleTime: int
142
- avgSellHandleTime: int
124
+ avgBuyHandleTime: float
125
+ avgSellHandleTime: float
143
126
  lastMonthCompleteRate: str
144
127
  completeRate: str
145
- avgHandleTime: int
128
+ avgHandleTime: float
129
+
130
+
131
+ 2
146
132
 
147
133
 
148
134
  class Advertisement(BaseModel):
149
135
  advNo: str
150
136
  payTimeLimit: int
151
- quantity: int
152
- price: Decimal
153
- initAmount: Decimal
154
- frozenQuantity: Decimal
155
- availableQuantity: Decimal
156
- coinId: str
137
+ quantity: int = None
138
+ price: float
139
+ initAmount: float = None
140
+ frozenQuantity: float = None
141
+ availableQuantity: float
142
+ coinId: str = None
157
143
  coinName: str
158
144
  countryCode: str
159
- commissionRate: Decimal
160
- advStatus: str
145
+ commissionRate: float = None
146
+ advStatus: str = None
161
147
  side: str
162
- createTime: int
148
+ createTime: int = None
163
149
  updateTime: int
164
150
  fiatUnit: str
165
- feeType: int
151
+ feeType: int = None
166
152
  autoReplyMsg: str
167
153
  tradeTerms: str
168
154
  payMethod: str
169
- paymentInfo: List[PaymentInfo]
170
- minSingleTransAmount: Decimal
171
- maxSingleTransAmount: Decimal
172
- kycLevel: int
155
+ paymentInfo: list[PaymentInfo]
156
+ minSingleTransAmount: float
157
+ maxSingleTransAmount: float
158
+ kycLevel: Literal["PRIMARY", "ADVANCED"]
173
159
  requireMobile: bool
174
160
  userAllTradeCountMax: int
175
161
  userAllTradeCountMin: int
@@ -200,30 +186,30 @@ class UserInfo(BaseModel):
200
186
  class Order(BaseModel):
201
187
  advNo: str
202
188
  advOrderNo: str
203
- tradableQuantity: Decimal
204
- price: Decimal
205
- amount: Decimal
189
+ tradableQuantity: float
190
+ price: float
191
+ amount: float
206
192
  coinName: str
207
- state: int
193
+ state: Literal["DONE", "PAID", "NOT_PAID", "PROCESSING"]
208
194
  payTimeLimit: int
209
- side: str
195
+ side: Side
210
196
  fiatUnit: str
211
197
  createTime: int
212
198
  updateTime: int
213
199
  userInfo: UserInfo
214
200
  complained: bool
215
- blockUser: bool
201
+ blockUser: bool = None
216
202
  unreadCount: int
217
203
  complainId: Optional[str] = None
218
204
 
219
205
 
220
206
  class OrderDetail(Order):
221
- paymentInfo: List[PaymentInfo]
222
- allowComplainTime: int
207
+ paymentInfo: list[PaymentInfo]
208
+ allowComplainTime: int = None
223
209
  confirmPaymentInfo: PaymentInfo
224
210
  userInfo: dict
225
- userFiatStatistics: dict
226
- spotCount: int
211
+ userFiatStatistics: dict = None
212
+ spotCount: int = None
227
213
 
228
214
 
229
215
  class CreateAdResponse(BaseResponse):
@@ -231,12 +217,12 @@ class CreateAdResponse(BaseResponse):
231
217
 
232
218
 
233
219
  class AdListResponse(BaseResponse):
234
- data: List[Advertisement]
220
+ data: list[Advertisement]
235
221
  page: PageInfo
236
222
 
237
223
 
238
224
  class MarketAdListResponse(BaseResponse):
239
- data: List[MarketAdvertisement]
225
+ data: list[MarketAdvertisement]
240
226
  page: PageInfo
241
227
 
242
228
 
@@ -245,7 +231,7 @@ class CreateOrderResponse(BaseResponse):
245
231
 
246
232
 
247
233
  class OrderListResponse(BaseResponse):
248
- data: List[Order]
234
+ data: list[Order]
249
235
  page: PageInfo
250
236
 
251
237
 
@@ -253,7 +239,7 @@ class OrderDetailResponse(BaseResponse):
253
239
  data: OrderDetail
254
240
 
255
241
 
256
- class ListenKeyResponse(BaseResponse):
242
+ class ListenKeyResponse(BaseModel):
257
243
  listenKey: str
258
244
 
259
245
 
@@ -284,356 +270,85 @@ class UploadFileResponse(BaseResponse):
284
270
  data: dict
285
271
 
286
272
 
287
- # ============ Client ============
288
-
289
-
290
- class MEXCP2PClient:
291
- """Асинхронный клиент для MEXC P2P API v1.2"""
292
-
293
- BASE_URL = "https://api.mexc.com"
294
-
295
- def __init__(self, api_key: str, api_secret: str):
296
- self.api_key = api_key
297
- self.api_secret = api_secret
298
- self.session: Optional[aiohttp.ClientSession] = None
299
-
300
- async def __aenter__(self):
301
- self.session = aiohttp.ClientSession()
302
- return self
303
-
304
- async def __aexit__(self, exc_type, exc_val, exc_tb):
305
- if self.session:
306
- await self.session.close()
307
-
308
- def _generate_signature(self, query_string: str) -> str:
309
- """Генерация HMAC SHA256 подписи"""
310
- return hmac.new(self.api_secret.encode("utf-8"), query_string.encode("utf-8"), hashlib.sha256).hexdigest()
311
-
312
- def _get_timestamp(self) -> int:
313
- """Получение текущего timestamp в миллисекундах"""
314
- return int(time.time() * 1000)
315
-
316
- async def _request(
317
- self, method: str, endpoint: str, params: Optional[dict] = None, data: Optional[BaseModel] = None
318
- ) -> dict:
319
- """Базовый метод для HTTP запросов"""
320
- if not self.session:
321
- raise RuntimeError("Client not initialized. Use async context manager.")
322
-
323
- timestamp = self._get_timestamp()
324
-
325
- # Формирование query string для подписи
326
- query_params = params.copy() if params else {}
327
- query_params["timestamp"] = timestamp
328
-
329
- query_string = "&".join(f"{k}={v}" for k, v in sorted(query_params.items()))
330
- signature = self._generate_signature(query_string)
331
-
332
- query_params["signature"] = signature
333
-
334
- headers = {"X-MEXC-APIKEY": self.api_key, "Content-Type": "application/json"}
335
-
336
- url = f"{self.BASE_URL}{endpoint}"
337
-
338
- json_data = data.model_dump(exclude_none=True) if data else None
339
-
340
- async with self.session.request(method, url, params=query_params, json=json_data, headers=headers) as response:
341
- return await response.json()
342
-
343
- # ============ Advertisement Methods ============
344
-
345
- async def create_or_update_ad(self, request: CreateUpdateAdRequest) -> CreateAdResponse:
346
- """Создание или обновление объявления"""
347
- result = await self._request("POST", "/api/v3/fiat/merchant/ads/save_or_update", data=request)
348
- return CreateAdResponse(**result)
349
-
350
- async def get_my_ads(
351
- self,
352
- coin_id: Optional[str] = None,
353
- adv_status: Optional[str] = None,
354
- merchant_id: Optional[str] = None,
355
- fiat_unit: Optional[str] = None,
356
- side: Optional[str] = None,
357
- kyc_level: Optional[str] = None,
358
- start_time: Optional[int] = None,
359
- end_time: Optional[int] = None,
360
- page: int = 1,
361
- limit: int = 10,
362
- ) -> AdListResponse:
363
- """Получение списка моих объявлений с пагинацией"""
364
- params = {"page": page, "limit": limit}
365
-
366
- if coin_id:
367
- params["coinId"] = coin_id
368
- if adv_status:
369
- params["advStatus"] = adv_status
370
- if merchant_id:
371
- params["merchantId"] = merchant_id
372
- if fiat_unit:
373
- params["fiatUnit"] = fiat_unit
374
- if side:
375
- params["side"] = side
376
- if kyc_level:
377
- params["kycLevel"] = kyc_level
378
- if start_time:
379
- params["startTime"] = start_time
380
- if end_time:
381
- params["endTime"] = end_time
382
-
383
- result = await self._request("GET", "/api/v3/fiat/merchant/ads/pagination", params=params)
384
- return AdListResponse(**result)
385
-
386
- async def get_market_ads(
387
- self,
388
- fiat_unit: str,
389
- coin_id: str,
390
- country_code: Optional[str] = None,
391
- side: Optional[str] = None,
392
- amount: Optional[Decimal] = None,
393
- quantity: Optional[Decimal] = None,
394
- pay_method: Optional[str] = None,
395
- block_trade: Optional[bool] = None,
396
- allow_trade: Optional[bool] = None,
397
- have_trade: Optional[bool] = None,
398
- follow: Optional[bool] = None,
399
- page: int = 1,
400
- ) -> MarketAdListResponse:
401
- """Получение рыночных объявлений"""
402
- params = {"fiatUnit": fiat_unit, "coinId": coin_id, "page": page}
403
-
404
- if country_code:
405
- params["countryCode"] = country_code
406
- if side:
407
- params["side"] = side
408
- if amount:
409
- params["amount"] = str(amount)
410
- if quantity:
411
- params["quantity"] = str(quantity)
412
- if pay_method:
413
- params["payMethod"] = pay_method
414
- if block_trade is not None:
415
- params["blockTrade"] = block_trade
416
- if allow_trade is not None:
417
- params["allowTrade"] = allow_trade
418
- if have_trade is not None:
419
- params["haveTrade"] = have_trade
420
- if follow is not None:
421
- params["follow"] = follow
422
-
423
- result = await self._request("GET", "/api/v3/fiat/market/ads/pagination", params=params)
424
- return MarketAdListResponse(**result)
425
-
426
- # ============ Order Methods ============
427
-
428
- async def create_order(self, request: CreateOrderRequest) -> CreateOrderResponse:
429
- """Создание ордера (захват объявления)"""
430
- result = await self._request("POST", "/api/v3/fiat/merchant/order/deal", data=request)
431
- return CreateOrderResponse(**result)
432
-
433
- async def get_my_orders(
434
- self,
435
- start_time: int,
436
- end_time: int,
437
- coin_id: Optional[str] = None,
438
- adv_order_no: Optional[str] = None,
439
- side: Optional[str] = None,
440
- order_deal_state: Optional[str] = None,
441
- page: int = 1,
442
- limit: int = 10,
443
- ) -> OrderListResponse:
444
- """Получение моих ордеров (только как maker)"""
445
- params = {"startTime": start_time, "endTime": end_time, "page": page, "limit": limit}
446
-
447
- if coin_id:
448
- params["coinId"] = coin_id
449
- if adv_order_no:
450
- params["advOrderNo"] = adv_order_no
451
- if side:
452
- params["side"] = side
453
- if order_deal_state:
454
- params["orderDealState"] = order_deal_state
455
-
456
- result = await self._request("GET", "/api/v3/fiat/merchant/order/pagination", params=params)
457
- return OrderListResponse(**result)
458
-
459
- async def get_market_orders(
460
- self,
461
- coin_id: Optional[str] = None,
462
- adv_order_no: Optional[str] = None,
463
- side: Optional[str] = None,
464
- order_deal_state: Optional[str] = None,
465
- start_time: Optional[int] = None,
466
- end_time: Optional[int] = None,
467
- page: int = 1,
468
- limit: int = 10,
469
- ) -> OrderListResponse:
470
- """Получение всех ордеров (как maker и taker)"""
471
- params = {"page": page, "limit": limit}
472
-
473
- if coin_id:
474
- params["coinId"] = coin_id
475
- if adv_order_no:
476
- params["advOrderNo"] = adv_order_no
477
- if side:
478
- params["side"] = side
479
- if order_deal_state:
480
- params["orderDealState"] = order_deal_state
481
- if start_time:
482
- params["startTime"] = start_time
483
- if end_time:
484
- params["endTime"] = end_time
485
-
486
- result = await self._request("GET", "/api/v3/fiat/market/order/pagination", params=params)
487
- return OrderListResponse(**result)
488
-
489
- async def confirm_paid(self, request: ConfirmPaidRequest) -> BaseResponse:
490
- """Подтверждение оплаты"""
491
- result = await self._request("POST", "/api/v3/fiat/confirm_paid", data=request)
492
- return BaseResponse(**result)
493
-
494
- async def release_coin(self, request: ReleaseCoinRequest) -> BaseResponse:
495
- """Релиз криптовалюты"""
496
- result = await self._request("POST", "/api/v3/fiat/release_coin", data=request)
497
- return BaseResponse(**result)
498
-
499
- async def get_order_detail(self, adv_order_no: str) -> OrderDetailResponse:
500
- """Получение деталей ордера"""
501
- params = {"advOrderNo": adv_order_no}
502
-
503
- result = await self._request("GET", "/api/v3/fiat/order/detail", params=params)
504
- return OrderDetailResponse(**result)
505
-
506
- # ============ Service Methods ============
507
-
508
- async def switch_service(self, request: ServiceSwitchRequest) -> BaseResponse:
509
- """Открытие/закрытие торговли"""
510
- result = await self._request("POST", "/api/v3/fiat/merchant/service/switch", data=request)
511
- return BaseResponse(**result)
512
-
513
- # ============ WebSocket Methods ============
514
-
515
- async def generate_listen_key(self) -> ListenKeyResponse:
516
- """Генерация listenKey для WebSocket"""
517
- result = await self._request("POST", "/api/v3/userDataStream")
518
- return ListenKeyResponse(**result)
519
-
520
- async def get_listen_key(self) -> ListenKeyResponse:
521
- """Получение существующего listenKey"""
522
- result = await self._request("GET", "/api/v3/userDataStream")
523
- return ListenKeyResponse(**result)
524
-
525
- # ============ Chat Methods ============
526
-
527
- async def get_chat_conversation(self, order_no: str) -> ConversationResponse:
528
- """Получение ID чат-сессии для ордера"""
529
- params = {"orderNo": order_no}
530
-
531
- result = await self._request("GET", "/api/v3/fiat/retrieveChatConversation", params=params)
532
- return ConversationResponse(**result)
533
-
534
- async def get_chat_messages(
535
- self,
536
- conversation_id: int,
537
- page: int = 1,
538
- limit: int = 20,
539
- chat_message_type: Optional[str] = None,
540
- message_id: Optional[int] = None,
541
- sort: Literal["DESC", "ASC"] = "DESC",
542
- ) -> ChatMessagesResponse:
543
- """Получение истории чата с пагинацией"""
544
- params = {"conversationId": conversation_id, "page": page, "limit": limit, "sort": sort}
545
-
546
- if chat_message_type:
547
- params["chatMessageType"] = chat_message_type
548
- if message_id:
549
- params["id"] = message_id
550
-
551
- result = await self._request("GET", "/api/v3/fiat/retrieveChatMessageWithPagination", params=params)
552
- return ChatMessagesResponse(**result)
553
-
554
- async def upload_file(self, file_data: bytes, filename: str) -> UploadFileResponse:
555
- """Загрузка файла"""
556
- if not self.session:
557
- raise RuntimeError("Client not initialized.")
558
-
559
- timestamp = self._get_timestamp()
560
- query_string = f"timestamp={timestamp}"
561
- signature = self._generate_signature(query_string)
562
-
563
- url = f"{self.BASE_URL}/api/v3/fiat/uploadFile"
564
- params = {"timestamp": timestamp, "signature": signature}
565
-
566
- headers = {"X-MEXC-APIKEY": self.api_key}
567
-
568
- form = aiohttp.FormData()
569
- form.add_field("file", file_data, filename=filename)
273
+ # ============ WebSocket Message Models ============
274
+ class ChatMessageType(IntEnum):
275
+ TEXT = 1
276
+ IMAGE = 2
277
+ VIDEO = 3
278
+ FILE = 4
570
279
 
571
- async with self.session.post(url, params=params, data=form, headers=headers) as response:
572
- result = await response.json()
573
280
 
574
- return UploadFileResponse(**result)
281
+ class WSMethod(str):
282
+ SUBSCRIPTION = "SUBSCRIPTION"
283
+ UNSUBSCRIPTION = "UNSUBSCRIPTION"
284
+ SEND_MESSAGE = "SEND_MESSAGE"
285
+ RECEIVE_MESSAGE = "RECEIVE_MESSAGE"
286
+ PING = "PING"
575
287
 
576
- async def download_file(self, file_id: str) -> dict:
577
- """Скачивание файла"""
578
- params = {"fileId": file_id}
579
288
 
580
- result = await self._request("GET", "/api/v3/fiat/downloadFile", params=params)
581
- return result
289
+ class SendTextMessage(BaseModel):
290
+ """Отправка текстового сообщения"""
582
291
 
292
+ content: str
293
+ conversationId: int
294
+ type: int = ChatMessageType.TEXT
583
295
 
584
- # ============ Usage Example ============
585
296
 
297
+ class SendImageMessage(BaseModel):
298
+ """Отправка изображения"""
586
299
 
587
- async def main():
588
- """Пример использования клиента"""
300
+ imageUrl: str
301
+ imageThumbUrl: str
302
+ conversationId: int
303
+ type: int = ChatMessageType.IMAGE
589
304
 
590
- api_key = "your_api_key"
591
- api_secret = "your_api_secret"
592
305
 
593
- async with MEXCP2PClient(api_key, api_secret) as client:
594
- # Создание объявления
595
- ad_request = CreateUpdateAdRequest(
596
- payTimeLimit=15,
597
- initQuantity=Decimal("100"),
598
- price=Decimal("70000"),
599
- coinId="5989b56ba96a43599dbeeca5bb053f43",
600
- side=Side.BUY,
601
- fiatUnit="USD",
602
- payMethod="1",
603
- minSingleTransAmount=Decimal("10"),
604
- maxSingleTransAmount=Decimal("1000"),
605
- userAllTradeCountMin=0,
606
- userAllTradeCountMax=100,
607
- )
306
+ class SendVideoMessage(BaseModel):
307
+ """Отправка видео"""
608
308
 
609
- ad_response = await client.create_or_update_ad(ad_request)
610
- print(f"Created ad: {ad_response.data}")
309
+ videoUrl: str
310
+ imageThumbUrl: str # превью видео
311
+ conversationId: int
312
+ type: int = ChatMessageType.VIDEO
611
313
 
612
- # Получение рыночных объявлений
613
- market_ads = await client.get_market_ads(
614
- fiat_unit="USD", coin_id="5989b56ba96a43599dbeeca5bb053f43", side=Side.SELL, page=1
615
- )
616
314
 
617
- print(f"Found {len(market_ads.data)} ads")
315
+ class SendFileMessage(BaseModel):
316
+ """Отправка файла"""
618
317
 
619
- # Создание ордера
620
- if market_ads.data:
621
- first_ad = market_ads.data[0]
622
- order_request = CreateOrderRequest(advNo=first_ad.advNo, amount=Decimal("100"), userConfirmPaymentId=123)
318
+ fileUrl: str
319
+ conversationId: int
320
+ type: int = ChatMessageType.FILE
623
321
 
624
- order_response = await client.create_order(order_request)
625
- print(f"Created order: {order_response.data}")
626
322
 
627
- # Получение деталей ордера
628
- order_detail = await client.get_order_detail("order_id_here")
629
- print(f"Order state: {order_detail.data.state}")
323
+ class WSRequest(BaseModel):
324
+ """Базовая структура WebSocket запроса"""
630
325
 
631
- # Генерация listenKey для WebSocket
632
- listen_key = await client.generate_listen_key()
633
- print(f"ListenKey: {listen_key.listenKey}")
326
+ method: str
327
+ params: dict | list[str] | None = None
328
+ id: int = None
634
329
 
635
330
 
636
- if __name__ == "__main__":
637
- import asyncio
331
+ class WSBaseResponse(BaseModel):
332
+ """Базовый ответ WebSocket"""
638
333
 
639
- asyncio.run(main())
334
+ success: bool
335
+ method: str
336
+ msg: str
337
+ data: Optional[str] = None
338
+
339
+
340
+ class ReceivedChatMessage(BaseModel):
341
+ """Полученное сообщение из чата"""
342
+
343
+ id: int
344
+ content: Optional[str] = None
345
+ conversationId: int
346
+ type: int
347
+ imageUrl: Optional[str] = None
348
+ imageThumbUrl: Optional[str] = None
349
+ videoUrl: Optional[str] = None
350
+ fileUrl: Optional[str] = None
351
+ createTime: str
352
+ self_: bool = Field(alias="self")
353
+ fromUserId: str
354
+ fromNickName: str