aioqzone 1.7.5.dev1__tar.gz → 1.8.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.
Files changed (52) hide show
  1. {aioqzone-1.7.5.dev1 → aioqzone-1.8.1.dev1}/PKG-INFO +4 -6
  2. {aioqzone-1.7.5.dev1 → aioqzone-1.8.1.dev1}/pyproject.toml +3 -5
  3. {aioqzone-1.7.5.dev1 → aioqzone-1.8.1.dev1}/src/aioqzone/api/h5/model.py +43 -86
  4. {aioqzone-1.7.5.dev1 → aioqzone-1.8.1.dev1}/src/aioqzone/model/api/__init__.py +30 -11
  5. {aioqzone-1.7.5.dev1 → aioqzone-1.8.1.dev1}/src/aioqzone/model/api/request.py +10 -4
  6. {aioqzone-1.7.5.dev1 → aioqzone-1.8.1.dev1}/src/aioqzone/model/api/response.py +3 -6
  7. {aioqzone-1.7.5.dev1 → aioqzone-1.8.1.dev1}/LICENSE +0 -0
  8. {aioqzone-1.7.5.dev1 → aioqzone-1.8.1.dev1}/README.md +0 -0
  9. {aioqzone-1.7.5.dev1 → aioqzone-1.8.1.dev1}/src/aioqzone/__init__.py +0 -0
  10. {aioqzone-1.7.5.dev1 → aioqzone-1.8.1.dev1}/src/aioqzone/api/__init__.py +0 -0
  11. {aioqzone-1.7.5.dev1 → aioqzone-1.8.1.dev1}/src/aioqzone/api/h5/__init__.py +0 -0
  12. {aioqzone-1.7.5.dev1 → aioqzone-1.8.1.dev1}/src/aioqzone/api/login/__init__.py +0 -0
  13. {aioqzone-1.7.5.dev1 → aioqzone-1.8.1.dev1}/src/aioqzone/api/login/_base.py +0 -0
  14. {aioqzone-1.7.5.dev1 → aioqzone-1.8.1.dev1}/src/aioqzone/exception.py +0 -0
  15. {aioqzone-1.7.5.dev1 → aioqzone-1.8.1.dev1}/src/aioqzone/message.py +0 -0
  16. {aioqzone-1.7.5.dev1 → aioqzone-1.8.1.dev1}/src/aioqzone/model/__init__.py +0 -0
  17. {aioqzone-1.7.5.dev1 → aioqzone-1.8.1.dev1}/src/aioqzone/model/api/feed.py +0 -0
  18. {aioqzone-1.7.5.dev1 → aioqzone-1.8.1.dev1}/src/aioqzone/model/api/profile.py +0 -0
  19. {aioqzone-1.7.5.dev1 → aioqzone-1.8.1.dev1}/src/aioqzone/model/protocol/__init__.py +0 -0
  20. {aioqzone-1.7.5.dev1 → aioqzone-1.8.1.dev1}/src/aioqzone/model/protocol/config.py +0 -0
  21. {aioqzone-1.7.5.dev1 → aioqzone-1.8.1.dev1}/src/aioqzone/model/protocol/entity.py +0 -0
  22. {aioqzone-1.7.5.dev1 → aioqzone-1.8.1.dev1}/src/aioqzone/utils/__init__.py +0 -0
  23. {aioqzone-1.7.5.dev1 → aioqzone-1.8.1.dev1}/src/aioqzone/utils/entity.py +0 -0
  24. {aioqzone-1.7.5.dev1 → aioqzone-1.8.1.dev1}/src/aioqzone/utils/regex.py +0 -0
  25. {aioqzone-1.7.5.dev1 → aioqzone-1.8.1.dev1}/src/aioqzone/utils/retry.py +0 -0
  26. {aioqzone-1.7.5.dev1 → aioqzone-1.8.1.dev1}/src/aioqzone/utils/time.py +0 -0
  27. {aioqzone-1.7.5.dev1 → aioqzone-1.8.1.dev1}/src/qqqr/__init__.py +0 -0
  28. {aioqzone-1.7.5.dev1 → aioqzone-1.8.1.dev1}/src/qqqr/base.py +0 -0
  29. {aioqzone-1.7.5.dev1 → aioqzone-1.8.1.dev1}/src/qqqr/constant.py +0 -0
  30. {aioqzone-1.7.5.dev1 → aioqzone-1.8.1.dev1}/src/qqqr/exception.py +0 -0
  31. {aioqzone-1.7.5.dev1 → aioqzone-1.8.1.dev1}/src/qqqr/message.py +0 -0
  32. {aioqzone-1.7.5.dev1 → aioqzone-1.8.1.dev1}/src/qqqr/py.typed +0 -0
  33. {aioqzone-1.7.5.dev1 → aioqzone-1.8.1.dev1}/src/qqqr/qr/__init__.py +0 -0
  34. {aioqzone-1.7.5.dev1 → aioqzone-1.8.1.dev1}/src/qqqr/qr/type.py +0 -0
  35. {aioqzone-1.7.5.dev1 → aioqzone-1.8.1.dev1}/src/qqqr/type.py +0 -0
  36. {aioqzone-1.7.5.dev1 → aioqzone-1.8.1.dev1}/src/qqqr/up/__init__.py +0 -0
  37. {aioqzone-1.7.5.dev1 → aioqzone-1.8.1.dev1}/src/qqqr/up/_model.py +0 -0
  38. {aioqzone-1.7.5.dev1 → aioqzone-1.8.1.dev1}/src/qqqr/up/captcha/__init__.py +0 -0
  39. {aioqzone-1.7.5.dev1 → aioqzone-1.8.1.dev1}/src/qqqr/up/captcha/_model.py +0 -0
  40. {aioqzone-1.7.5.dev1 → aioqzone-1.8.1.dev1}/src/qqqr/up/captcha/capsess.py +0 -0
  41. {aioqzone-1.7.5.dev1 → aioqzone-1.8.1.dev1}/src/qqqr/up/captcha/pil_utils.py +0 -0
  42. {aioqzone-1.7.5.dev1 → aioqzone-1.8.1.dev1}/src/qqqr/up/captcha/select/__init__.py +0 -0
  43. {aioqzone-1.7.5.dev1 → aioqzone-1.8.1.dev1}/src/qqqr/up/captcha/select/_types.py +0 -0
  44. {aioqzone-1.7.5.dev1 → aioqzone-1.8.1.dev1}/src/qqqr/up/captcha/slide/__init__.py +0 -0
  45. {aioqzone-1.7.5.dev1 → aioqzone-1.8.1.dev1}/src/qqqr/up/captcha/slide/_types.py +0 -0
  46. {aioqzone-1.7.5.dev1 → aioqzone-1.8.1.dev1}/src/qqqr/up/encrypt.py +0 -0
  47. {aioqzone-1.7.5.dev1 → aioqzone-1.8.1.dev1}/src/qqqr/up/h5.py +0 -0
  48. {aioqzone-1.7.5.dev1 → aioqzone-1.8.1.dev1}/src/qqqr/up/web.py +0 -0
  49. {aioqzone-1.7.5.dev1 → aioqzone-1.8.1.dev1}/src/qqqr/utils/encrypt.py +0 -0
  50. {aioqzone-1.7.5.dev1 → aioqzone-1.8.1.dev1}/src/qqqr/utils/iter.py +0 -0
  51. {aioqzone-1.7.5.dev1 → aioqzone-1.8.1.dev1}/src/qqqr/utils/jsjson.py +0 -0
  52. {aioqzone-1.7.5.dev1 → aioqzone-1.8.1.dev1}/src/qqqr/utils/net.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: aioqzone
3
- Version: 1.7.5.dev1
3
+ Version: 1.8.1.dev1
4
4
  Summary: A Python wrapper for Qzone login and H5 APIs.
5
5
  Home-page: https://github.com/aioqzone/aioqzone
6
6
  License: AGPL-3.0
@@ -19,17 +19,15 @@ Classifier: Programming Language :: Python :: 3.11
19
19
  Classifier: Programming Language :: Python :: 3.12
20
20
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
21
  Classifier: Typing :: Typed
22
- Provides-Extra: captcha
22
+ Provides-Extra: slide-captcha
23
23
  Requires-Dist: aiohttp (>=3.8.5,<4.0.0)
24
- Requires-Dist: cssselect (>=1.1.0,<2.0.0)
25
24
  Requires-Dist: exceptiongroup (>=1.1.1,<2.0.0)
26
- Requires-Dist: lxml (>=4.9.1,<5.0.0)
27
25
  Requires-Dist: pillow (>=10.0.1,<11.0.0)
28
- Requires-Dist: pychaosvm (>=0.3.2,<0.4.0)
26
+ Requires-Dist: pychaosvm (>=0.3.3,<0.4.0)
29
27
  Requires-Dist: pydantic (>=2.0.3,<3.0.0)
30
28
  Requires-Dist: pydantic-settings (>=2.0.2,<3.0.0)
31
29
  Requires-Dist: rsa (>=4.8,<5.0)
32
- Requires-Dist: slide-tc (>=0.1.1,<0.2.0) ; extra == "captcha"
30
+ Requires-Dist: slide-tc (>=0.1.1,<0.2.0) ; extra == "slide-captcha"
33
31
  Requires-Dist: tenacity (>=8.2.3,<9.0.0)
34
32
  Requires-Dist: tylisten (>=2.1.3,<3.0.0)
35
33
  Project-URL: Bug Tracker, https://github.com/aioqzone/aioqzone/issues
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "aioqzone"
3
- version = "1.7.5.dev1"
3
+ version = "1.8.1.dev1"
4
4
  description = "A Python wrapper for Qzone login and H5 APIs."
5
5
  authors = ["aioqzone <zzzzss990315@gmail.com>"]
6
6
  license = "AGPL-3.0"
@@ -32,17 +32,15 @@ aiohttp = "^3.8.5"
32
32
  pydantic = "^2.0.3"
33
33
  pydantic-settings = "^2.0.2"
34
34
  rsa = "^4.8"
35
- lxml = "^4.9.1"
36
- cssselect = "^1.1.0"
37
35
  tenacity = "^8.2.3"
38
36
  exceptiongroup = "^1.1.1"
39
37
  tylisten = "^2.1.3"
40
38
  pillow = "^10.0.1"
41
- pychaosvm = { version = "~0.3.2", source = "aioqzone-index" }
39
+ pychaosvm = { version = "~0.3.3", source = "aioqzone-index" }
42
40
  slide-tc = {version = "~0.1.1", optional = true, source = "aioqzone-index" }
43
41
 
44
42
  [tool.poetry.extras]
45
- captcha = ["slide-tc"]
43
+ slide-captcha = ["slide-tc"]
46
44
 
47
45
  # dependency groups
48
46
  [tool.poetry.group.test]
@@ -95,7 +95,7 @@ class QzoneH5API:
95
95
  :raise `RuntimeError`: if any failure occurs in data parsing.
96
96
  """
97
97
 
98
- r = await self.call(IndexPageApi(response=IndexPageResp, attach_token=False))
98
+ r = await self.call(IndexPageApi())
99
99
  self.qzone_tokens[self.login.uin] = r.qzonetoken
100
100
  log.debug(f"qzonetoken[{self.login.uin}] = {r.qzonetoken}")
101
101
  return r
@@ -106,12 +106,7 @@ class QzoneH5API:
106
106
  :param hostuin: uin of the user
107
107
  :param start_time: timestamp in seconds, default as current time.
108
108
  """
109
- r = await self.call(
110
- UserProfileApi(
111
- params=ProfileParams(hostuin=hostuin, starttime=int(1e3 * start_time)),
112
- response=ProfilePagePesp,
113
- )
114
- )
109
+ r = await self.call(UserProfileApi(params=ProfileParams.model_validate(locals())))
115
110
  self.qzone_tokens[hostuin] = r.qzonetoken
116
111
  log.debug(f"qzonetoken[{hostuin}] = {r.qzonetoken}")
117
112
  return r
@@ -127,90 +122,57 @@ class QzoneH5API:
127
122
  if not self.qzone_tokens.get(self.login.uin) or not attach_info:
128
123
  return await self.index()
129
124
 
130
- return await self.call(
131
- FeedPageApi(
132
- params=ActiveFeedsParams(attach_info=attach_info),
133
- response=FeedPageResp,
134
- )
135
- )
125
+ return await self.call(FeedPageApi(params=ActiveFeedsParams.model_validate(locals())))
136
126
 
137
- async def get_feeds(self, uin: int, attach_info: t.Optional[str] = None) -> ProfileResp:
127
+ async def get_feeds(self, hostuin: int, attach_info: t.Optional[str] = None) -> ProfileResp:
138
128
  """Get next page of the given :obj:`uin`.
139
129
  If :obj:`.qzone_tokens` has not cached qzonetoken of given :obj:`uin` or :obj:`attach_info` is empty,
140
130
  it will call :meth:`.profile` and return its :obj:`~ProfileResp.feedpage` field.
141
131
 
142
- :param uin: uin of the user
132
+ :param hostuin: uin of the user
143
133
  :param attach_info: The ``attach_info`` field from last call.
144
134
  Pass an empty string equals to call :meth:`.index`.
145
135
  :return: If success, the ``data`` field of the response.
146
136
  """
147
- if not self.qzone_tokens.get(uin) or not attach_info:
148
- return (await self.profile(uin)).feedpage
137
+ if not self.qzone_tokens.get(hostuin) or not attach_info:
138
+ return (await self.profile(hostuin)).feedpage
149
139
 
150
- return await self.call(
151
- GetFeedsApi(
152
- params=GetFeedsParams(
153
- hostuin=uin,
154
- res_attach=attach_info,
155
- ),
156
- response=ProfileResp,
157
- )
158
- )
140
+ return await self.call(GetFeedsApi(params=GetFeedsParams.model_validate(locals())))
159
141
 
160
142
  async def shuoshuo(self, fid: str, uin: int, appid=311, busi_param: str = "") -> DetailResp:
161
143
  """This can be used to get the detailed summary of a feed.
162
144
 
163
- :param fid: aka. ``cellid``
164
- :param hostuin: uin of the owner of the given feed
165
- :param appid: appid of the given feed, default as 311
145
+ :param fid: :term:`fid`
146
+ :param uin: uin of the owner of the given feed
147
+ :param appid: :term:`appid`
166
148
  :param busi_param: optional encoded params
167
149
  """
168
- return await self.call(
169
- ShuoshuoApi(
170
- params=ShuoshuoParams(fid=fid, uin=uin, appid=appid, busi_param=busi_param),
171
- response=DetailResp,
172
- )
173
- )
150
+ return await self.call(ShuoshuoApi(params=ShuoshuoParams.model_validate(locals())))
174
151
 
175
152
  async def mfeeds_get_count(self) -> FeedCount:
176
153
  """Get new feeds count. This is also the "keep-alive" signal of the cookie."""
177
- return await self.call(
178
- GetCountApi(params=GetCountParams(), response=FeedCount),
179
- )
154
+ return await self.call(GetCountApi())
180
155
 
181
156
  async def internal_dolike_app(
182
157
  self, appid: int, unikey: str, curkey: str, like=True
183
158
  ) -> SingleReturnResp:
184
159
  """Like or unlike."""
185
- if like:
186
- path = "/proxy/domain/w.qzone.qq.com/cgi-bin/likes/internal_dolike_app"
187
- else:
188
- path = "/proxy/domain/w.qzone.qq.com/cgi-bin/likes/internal_unlike_app"
160
+ cls = LikeApi if like else UnlikeApi
189
161
 
190
- return await self.call(
191
- DoLikeApi(
192
- path=path,
193
- params=DolikeParam(appid=appid, unikey=unikey, curkey=curkey),
194
- response=SingleReturnResp,
195
- )
196
- )
162
+ return await self.call(cls(params=DolikeParam.model_validate(locals())))
197
163
 
198
164
  async def add_comment(
199
- self, owner_uin: int, fid: str, appid: int, content: str, private=False
165
+ self, ownuin: int, fid: str, appid: int, content: str, private=False
200
166
  ) -> AddCommentResp:
201
- """Comment a feed."""
202
- return await self.call(
203
- AddCommentApi(
204
- params=AddCommentParams(
205
- ownuin=owner_uin,
206
- fid=fid,
207
- private=private,
208
- content=content,
209
- appid=appid,
210
- ),
211
- response=AddCommentResp,
212
- )
213
- )
167
+ """Comment a feed.
168
+
169
+ :param ownuin: Feed owner uin
170
+ :param fid: :term:`fid`
171
+ :param appid: :term:`appid`
172
+ :param content: comment content
173
+ :param private: is private comment
174
+ """
175
+ return await self.call(AddCommentApi(params=AddCommentParams.model_validate(locals())))
214
176
 
215
177
  async def publish_mood(
216
178
  self,
@@ -219,17 +181,15 @@ class QzoneH5API:
219
181
  sync_weibo=False,
220
182
  ugc_right: UgcRight = UgcRight.all,
221
183
  ) -> PublishMoodResp:
222
- return await self.call(
223
- PublishMoodApi(
224
- params=PublishMoodParams(
225
- content=content,
226
- photos=photos or [],
227
- issyncweibo=sync_weibo,
228
- ugc_right=ugc_right,
229
- ),
230
- response=PublishMoodResp,
231
- )
232
- )
184
+ """Publish a feed.
185
+
186
+ :param content: feed content
187
+ :param photos:
188
+ :param sync_weibo: sync to weibo, default to false
189
+ :param ugc_right: access right, default to "Available to Everyone".
190
+ """
191
+ photos = photos or []
192
+ return await self.call(PublishMoodApi(params=PublishMoodParams.model_validate(locals())))
233
193
 
234
194
  async def upload_pic(
235
195
  self, picture: bytes, width: int, height: int, quality: int
@@ -242,29 +202,26 @@ class QzoneH5API:
242
202
  hd_height=height,
243
203
  hd_quality=quality,
244
204
  ),
245
- response=UploadPicResponse,
246
205
  )
247
206
  )
248
207
 
249
208
  async def preupload_photos(
250
- self, photos: t.List[UploadPicResponse], cur_num=0, hd=False
209
+ self, upload_pics: t.List[UploadPicResponse], cur_num=0, upload_hd=False
251
210
  ) -> PhotosPreuploadResponse:
252
- assert photos
211
+ assert upload_pics
253
212
  return await self.call(
254
- PhotosPreuploadApi(
255
- params=PhotosPreuploadParams(
256
- upload_pics=photos,
257
- currnum=cur_num,
258
- upload_hd=int(hd),
259
- ),
260
- response=PhotosPreuploadResponse,
261
- )
213
+ PhotosPreuploadApi(params=PhotosPreuploadParams.model_validate(locals()))
262
214
  )
263
215
 
264
216
  async def delete_ugc(self, fid: str, appid: int) -> DeleteUgcResp:
217
+ """Delete a feed.
218
+
219
+ :param fid: :term:`fid`
220
+ :param appid: :term:`appid`
221
+ """
265
222
  return await self.call(
266
223
  AddOperationApi(
267
- params=DeleteUgcParams(fid=fid, appid=appid),
224
+ params=DeleteUgcParams.model_validate(locals()),
268
225
  response=DeleteUgcResp,
269
226
  )
270
227
  )
@@ -14,15 +14,15 @@ TyHttpMethod = t.Union[t.Literal["GET"], t.Literal["POST"]]
14
14
  class QzoneApi(BaseModel, t.Generic[TyRequest, TyResponse]):
15
15
  host: t.ClassVar[str] = "https://h5.qzone.qq.com"
16
16
  http_method: t.ClassVar[TyHttpMethod]
17
- path: str
17
+ path: t.ClassVar[str]
18
18
 
19
- keep_alive: bool = True
20
- accept: t.Optional[str] = None
19
+ keep_alive: t.ClassVar[bool] = True
20
+ accept: t.ClassVar[t.Optional[str]] = None
21
21
  referer: str = "https://h5.qzone.qq.com/"
22
22
 
23
- attach_token: bool = True
23
+ attach_token: t.ClassVar[bool] = True
24
24
  params: TyRequest = Field(default_factory=QzoneRequestParams)
25
- response: t.Type[TyResponse]
25
+ response: t.ClassVar[t.Type[TyResponse]] # type: ignore
26
26
 
27
27
  @property
28
28
  def url(self) -> URL:
@@ -30,47 +30,62 @@ class QzoneApi(BaseModel, t.Generic[TyRequest, TyResponse]):
30
30
 
31
31
 
32
32
  class IndexPageApi(QzoneApi[QzoneRequestParams, IndexPageResp]):
33
+ response: t.ClassVar = IndexPageResp
33
34
  http_method: t.ClassVar[TyHttpMethod] = "GET"
34
35
  path: t.ClassVar[str] = "/mqzone/index"
35
- keep_alive: bool = False
36
- attach_token: bool = False
36
+ keep_alive: t.ClassVar[bool] = False
37
+ attach_token: t.ClassVar[bool] = False
37
38
 
38
39
 
39
40
  class UserProfileApi(QzoneApi[ProfileParams, ProfilePagePesp]):
41
+ response: t.ClassVar = ProfilePagePesp
40
42
  http_method: t.ClassVar[TyHttpMethod] = "GET"
41
43
  path: t.ClassVar[str] = "/mqzone/profile"
42
- keep_alive: bool = False
43
- attach_token: bool = False
44
+ keep_alive: t.ClassVar[bool] = False
45
+ attach_token: t.ClassVar[bool] = False
44
46
 
45
47
 
46
48
  class FeedPageApi(QzoneApi[ActiveFeedsParams, FeedPageResp]):
49
+ response: t.ClassVar = FeedPageResp
47
50
  http_method: t.ClassVar[TyHttpMethod] = "GET"
48
51
  path: t.ClassVar[str] = "/webapp/json/mqzone_feeds/getActiveFeeds"
49
52
 
50
53
 
51
54
  class ShuoshuoApi(QzoneApi[ShuoshuoParams, DetailResp]):
55
+ response: t.ClassVar = DetailResp
52
56
  http_method: t.ClassVar[TyHttpMethod] = "GET"
53
57
  path: t.ClassVar[str] = "/webapp/json/mqzone_detail/shuoshuo"
54
58
 
55
59
 
56
60
  class GetFeedsApi(QzoneApi[GetFeedsParams, ProfileResp]):
61
+ response: t.ClassVar = ProfileResp
57
62
  http_method: t.ClassVar[TyHttpMethod] = "GET"
58
63
  host: t.ClassVar[str] = "https://mobile.qzone.qq.com"
59
64
  path: t.ClassVar[str] = "/get_feeds"
60
65
 
61
66
 
62
67
  class GetCountApi(QzoneApi[GetCountParams, FeedCount]):
68
+ response: t.ClassVar = FeedCount
69
+ params: GetCountParams = Field(default_factory=GetCountParams)
70
+
63
71
  http_method: t.ClassVar[TyHttpMethod] = "GET"
64
72
  host: t.ClassVar[str] = "https://mobile.qzone.qq.com"
65
73
  path: t.ClassVar[str] = "/feeds/mfeeds_get_count"
66
- accept: str = "application/json"
74
+ accept: t.ClassVar[str] = "application/json"
67
75
 
68
76
 
69
- class DoLikeApi(QzoneApi[DolikeParam, SingleReturnResp]):
77
+ class LikeApi(QzoneApi[DolikeParam, SingleReturnResp]):
78
+ response: t.ClassVar = SingleReturnResp
70
79
  http_method: t.ClassVar[TyHttpMethod] = "POST"
80
+ path: t.ClassVar[str] = "/proxy/domain/w.qzone.qq.com/cgi-bin/likes/internal_dolike_app"
81
+
82
+
83
+ class UnlikeApi(LikeApi):
84
+ path: t.ClassVar[str] = "/proxy/domain/w.qzone.qq.com/cgi-bin/likes/internal_unlike_app"
71
85
 
72
86
 
73
87
  class AddCommentApi(QzoneApi[AddCommentParams, AddCommentResp]):
88
+ response: t.ClassVar = AddCommentResp
74
89
  http_method: t.ClassVar[TyHttpMethod] = "POST"
75
90
  path: t.ClassVar[str] = "/webapp/json/qzoneOperation/addComment"
76
91
 
@@ -82,24 +97,28 @@ class ListFriendApi(QzoneApi):
82
97
 
83
98
 
84
99
  class PublishMoodApi(QzoneApi[PublishMoodParams, PublishMoodResp]):
100
+ response: t.ClassVar = PublishMoodResp
85
101
  http_method: t.ClassVar[TyHttpMethod] = "POST"
86
102
  host: t.ClassVar[str] = "https://mobile.qzone.qq.com"
87
103
  path: t.ClassVar[str] = "/mood/publish_mood"
88
104
 
89
105
 
90
106
  class AddOperationApi(QzoneApi):
107
+ response: t.Type[QzoneResponse]
91
108
  http_method: t.ClassVar[TyHttpMethod] = "POST"
92
109
  host: t.ClassVar[str] = "https://mobile.qzone.qq.com"
93
110
  path: t.ClassVar[str] = "operation/operation_add"
94
111
 
95
112
 
96
113
  class UploadPicApi(QzoneApi[UploadPicParams, UploadPicResponse]):
114
+ response: t.ClassVar = UploadPicResponse
97
115
  http_method: t.ClassVar[TyHttpMethod] = "POST"
98
116
  host: t.ClassVar[str] = "https://mobile.qzone.qq.com"
99
117
  path: t.ClassVar[str] = "/up/cgi-bin/upload/cgi_upload_pic_v2"
100
118
 
101
119
 
102
120
  class PhotosPreuploadApi(QzoneApi[PhotosPreuploadParams, PhotosPreuploadResponse]):
121
+ response: t.ClassVar = PhotosPreuploadResponse
103
122
  http_method: t.ClassVar[TyHttpMethod] = "POST"
104
123
  host: t.ClassVar[str] = "https://mobile.qzone.qq.com"
105
124
  path: t.ClassVar[str] = "/up/cgi-bin/upload/cgi_upload_pic_v2"
@@ -48,7 +48,7 @@ class ActiveFeedsParams(QzoneRequestParams):
48
48
 
49
49
  class GetFeedsParams(QzoneRequestParams):
50
50
  hostuin: int
51
- res_attach: str = ""
51
+ attach_info: str = Field(default="", serialization_alias="res_attach")
52
52
 
53
53
  res_type: int = 2
54
54
  refresh_type: int = 2
@@ -57,9 +57,13 @@ class GetFeedsParams(QzoneRequestParams):
57
57
 
58
58
  class ProfileParams(QzoneRequestParams):
59
59
  hostuin: int
60
- starttime: int = 0
60
+ start_time: float = Field(default=0, serialization_alias="starttime")
61
61
  ts_fields = ("starttime",)
62
62
 
63
+ @field_serializer("start_time")
64
+ def ms(self, start_time: float):
65
+ return int(1e3 * start_time)
66
+
63
67
 
64
68
  class ShuoshuoParams(QzoneRequestParams):
65
69
  fid: str = Field(serialization_alias="cellid")
@@ -132,7 +136,9 @@ class PublishMoodParams(QzoneRequestParams):
132
136
  uin_fields = ("res_uin",)
133
137
  content: str = Field(min_length=1, max_length=2000)
134
138
  photos: t.List[PhotoData] = Field(default_factory=list, serialization_alias="richval")
135
- issyncweibo: int = Field(default=False, validate_default=True)
139
+ sync_weibo: int = Field(
140
+ default=False, validate_default=True, serialization_alias="issyncweibo"
141
+ )
136
142
  ugc_right: UgcRight = UgcRight.all
137
143
 
138
144
  opr_type: str = "publish_shuoshuo"
@@ -179,7 +185,7 @@ class PhotosPreuploadParams(QzoneRequestParams):
179
185
  uin_fields = ("uin",)
180
186
  upload_pics: t.List[UploadPicResponse] = Field(exclude=True)
181
187
 
182
- currnum: int = 0
188
+ cur_num: int = Field(default=0, serialization_alias="currnum")
183
189
  upload_hd: int = 0
184
190
 
185
191
  # defaults
@@ -11,11 +11,13 @@ from typing_extensions import Self
11
11
  from aioqzone.exception import QzoneError
12
12
  from aioqzone.utils.regex import entire_closing, response_callback
13
13
  from qqqr.utils.iter import firstn
14
- from qqqr.utils.jsjson import json_loads
14
+ from qqqr.utils.jsjson import JsonValue, json_loads
15
15
 
16
16
  from .feed import FeedData
17
17
  from .profile import ProfileFeedData, QzoneProfile
18
18
 
19
+ StrDict = t.Dict[str, JsonValue]
20
+
19
21
  __all__ = [
20
22
  "QzoneResponse",
21
23
  "FeedPageResp",
@@ -34,11 +36,6 @@ __all__ = [
34
36
  "ProfileFeedData",
35
37
  ]
36
38
 
37
- if t.TYPE_CHECKING:
38
- from qqqr.utils.jsjson import JsonValue
39
-
40
- StrDict = t.Dict[str, JsonValue]
41
-
42
39
 
43
40
  class QzoneResponse(BaseModel):
44
41
  _errno_key: t.ClassVar[t.Union[str, AliasPath, AliasChoices, None]] = AliasChoices(
File without changes
File without changes