reykit 1.0.0__py3-none-any.whl → 1.0.1__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.
- reydb/__init__.py +25 -0
- reydb/rall.py +17 -0
- reydb/rbuild.py +1235 -0
- reydb/rconnection.py +2276 -0
- reydb/rexecute.py +354 -0
- reydb/rfile.py +416 -0
- reydb/rinformation.py +515 -0
- reydb/rparameter.py +243 -0
- reykit/__init__.py +1 -1
- reykit/rrandom.py +40 -9
- {reykit-1.0.0.dist-info → reykit-1.0.1.dist-info}/METADATA +1 -1
- reykit-1.0.1.dist-info/RECORD +64 -0
- reyweb/__init__.py +19 -0
- reyweb/rall.py +12 -0
- reyweb/rbaidu/__init__.py +21 -0
- reyweb/rbaidu/rbaidu_base.py +186 -0
- reyweb/rbaidu/rbaidu_chat.py +299 -0
- reyweb/rbaidu/rbaidu_image.py +183 -0
- reyweb/rbaidu/rbaidu_voice.py +256 -0
- reywechat/__init__.py +32 -0
- reywechat/data/client_api.dll +0 -0
- reywechat/rall.py +20 -0
- reywechat/rclient.py +912 -0
- reywechat/rdatabase.py +1189 -0
- reywechat/rexception.py +65 -0
- reywechat/rexecute.py +201 -0
- reywechat/rlog.py +198 -0
- reywechat/rreceive.py +1232 -0
- reywechat/rschedule.py +136 -0
- reywechat/rsend.py +630 -0
- reywechat/rwechat.py +201 -0
- reyworm/__init__.py +24 -0
- reyworm/rall.py +16 -0
- reyworm/rbrowser.py +134 -0
- reyworm/rcalendar.py +159 -0
- reyworm/rnews.py +126 -0
- reyworm/rsecurity.py +239 -0
- reyworm/rtranslate.py +75 -0
- reykit-1.0.0.dist-info/RECORD +0 -30
- {reykit-1.0.0.dist-info → reykit-1.0.1.dist-info}/WHEEL +0 -0
- {reykit-1.0.0.dist-info → reykit-1.0.1.dist-info}/top_level.txt +0 -0
reywechat/rclient.py
ADDED
@@ -0,0 +1,912 @@
|
|
1
|
+
# !/usr/bin/env python
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
|
4
|
+
"""
|
5
|
+
@Time : 2023-10-17 20:27:16
|
6
|
+
@Author : Rey
|
7
|
+
@Contact : reyxbo@163.com
|
8
|
+
@Explain : Client methods.
|
9
|
+
"""
|
10
|
+
|
11
|
+
|
12
|
+
from __future__ import annotations
|
13
|
+
from typing import Any, TypedDict, Optional, Literal, Union, Final
|
14
|
+
from os.path import abspath as os_abspath
|
15
|
+
from reykit.rcomm import request as reytool_request
|
16
|
+
from reykit.rdll import inject_dll
|
17
|
+
from reykit.rexception import RError
|
18
|
+
from reykit.ros import find_relpath
|
19
|
+
from reykit.rsystem import search_process, memory_read, memory_write
|
20
|
+
from reykit.rtype import RConfigMeta
|
21
|
+
|
22
|
+
from .rwechat import RWeChat
|
23
|
+
|
24
|
+
|
25
|
+
__all__ = (
|
26
|
+
'RConfigClient',
|
27
|
+
'RClientErorr',
|
28
|
+
'RClient',
|
29
|
+
'simulate_client_version'
|
30
|
+
)
|
31
|
+
|
32
|
+
|
33
|
+
Response = TypedDict('Response', {'code': int, 'message': str, 'data': Any})
|
34
|
+
|
35
|
+
|
36
|
+
class RConfigClient(object, metaclass=RConfigMeta):
|
37
|
+
"""
|
38
|
+
Rey's `config client` type.
|
39
|
+
"""
|
40
|
+
|
41
|
+
# Set.
|
42
|
+
_client_version_memory_offsets = (
|
43
|
+
61280212,
|
44
|
+
61372636,
|
45
|
+
61474056,
|
46
|
+
61638128,
|
47
|
+
61666264,
|
48
|
+
61674264,
|
49
|
+
61675784
|
50
|
+
)
|
51
|
+
|
52
|
+
|
53
|
+
class RClientErorr(RError):
|
54
|
+
"""
|
55
|
+
Rey's `client exception` type.
|
56
|
+
"""
|
57
|
+
|
58
|
+
|
59
|
+
class RClient(object):
|
60
|
+
"""
|
61
|
+
Rey's `client` type.
|
62
|
+
"""
|
63
|
+
|
64
|
+
|
65
|
+
# Environment.
|
66
|
+
client_version: Final[str] = '3.9.5.81'
|
67
|
+
client_version_int: Final[int] = 1661535569
|
68
|
+
client_version_simulate: Final[str] = '3.10.0.1'
|
69
|
+
client_version_simulate_int: Final[int] = 1661599745
|
70
|
+
client_api_port: Final[int] = 19088
|
71
|
+
message_callback_port: Final[int] = 19089
|
72
|
+
|
73
|
+
|
74
|
+
def __init__(
|
75
|
+
self,
|
76
|
+
rwechat: RWeChat
|
77
|
+
) -> None:
|
78
|
+
"""
|
79
|
+
Build `client` attributes.
|
80
|
+
|
81
|
+
Parameters
|
82
|
+
----------
|
83
|
+
rwechat : `RWeChat` instance.
|
84
|
+
"""
|
85
|
+
|
86
|
+
# Start.
|
87
|
+
self.rwechat = rwechat
|
88
|
+
self.start_api()
|
89
|
+
|
90
|
+
# Set attribute.
|
91
|
+
self.login_info = self.get_login_info()
|
92
|
+
|
93
|
+
|
94
|
+
def start_api(self) -> None:
|
95
|
+
"""
|
96
|
+
Start client control API.
|
97
|
+
"""
|
98
|
+
|
99
|
+
# Check client.
|
100
|
+
judge = self.check_client_started()
|
101
|
+
if not judge:
|
102
|
+
raise RClientErorr('WeChat client not started')
|
103
|
+
|
104
|
+
# Check client version.
|
105
|
+
judge = self.check_client_version()
|
106
|
+
if not judge:
|
107
|
+
raise RClientErorr(f'WeChat client version failed, must be "{self.client_version}"')
|
108
|
+
|
109
|
+
# Check start.
|
110
|
+
judge = self.check_api()
|
111
|
+
if not judge:
|
112
|
+
|
113
|
+
# Inject DLL.
|
114
|
+
self.inject_dll()
|
115
|
+
|
116
|
+
# Check api.
|
117
|
+
judge = self.check_api()
|
118
|
+
if not judge:
|
119
|
+
raise RClientErorr('start WeChat client API failed')
|
120
|
+
|
121
|
+
# Report.
|
122
|
+
print('Start WeChat client API successfully, address is "127.0.0.1:19088".')
|
123
|
+
|
124
|
+
|
125
|
+
def check_client_started(self) -> bool:
|
126
|
+
"""
|
127
|
+
Check if the client is started.
|
128
|
+
|
129
|
+
Returns
|
130
|
+
-------
|
131
|
+
Check result.
|
132
|
+
"""
|
133
|
+
|
134
|
+
# Search.
|
135
|
+
processes = search_process(name='WeChat.exe')
|
136
|
+
|
137
|
+
# Check.
|
138
|
+
if processes == []:
|
139
|
+
return False
|
140
|
+
else:
|
141
|
+
return True
|
142
|
+
|
143
|
+
|
144
|
+
def check_client_version(self) -> bool:
|
145
|
+
"""
|
146
|
+
Check if the client version.
|
147
|
+
|
148
|
+
Returns
|
149
|
+
-------
|
150
|
+
Check result.
|
151
|
+
"""
|
152
|
+
|
153
|
+
# Check.
|
154
|
+
for offset in RConfigClient._client_version_memory_offsets:
|
155
|
+
value = memory_read(
|
156
|
+
'WeChat.exe',
|
157
|
+
'WeChatWin.dll',
|
158
|
+
offset
|
159
|
+
)
|
160
|
+
if value not in (
|
161
|
+
self.client_version_int,
|
162
|
+
self.client_version_simulate_int,
|
163
|
+
0
|
164
|
+
):
|
165
|
+
return False
|
166
|
+
|
167
|
+
return True
|
168
|
+
|
169
|
+
|
170
|
+
def check_api(self) -> bool:
|
171
|
+
"""
|
172
|
+
Check if the client API is started.
|
173
|
+
"""
|
174
|
+
|
175
|
+
# Search.
|
176
|
+
processes = search_process(port=self.client_api_port)
|
177
|
+
|
178
|
+
# Check.
|
179
|
+
if processes == []:
|
180
|
+
return False
|
181
|
+
process = processes[0]
|
182
|
+
with process.oneshot():
|
183
|
+
process_name = process.name()
|
184
|
+
if process_name != 'WeChat.exe':
|
185
|
+
return False
|
186
|
+
|
187
|
+
## Check request.
|
188
|
+
result = self.check_login()
|
189
|
+
if not result:
|
190
|
+
return False
|
191
|
+
|
192
|
+
return True
|
193
|
+
|
194
|
+
|
195
|
+
def inject_dll(self) -> None:
|
196
|
+
"""
|
197
|
+
Inject DLL file of start API into the WeChat client process.
|
198
|
+
"""
|
199
|
+
|
200
|
+
# Get parameter.
|
201
|
+
dll_file_relpath = './data/client_api.dll'
|
202
|
+
dll_file_path = find_relpath(__file__, dll_file_relpath)
|
203
|
+
|
204
|
+
# Inject.
|
205
|
+
processes = search_process(name='WeChat.exe')
|
206
|
+
process = processes[0]
|
207
|
+
inject_dll(
|
208
|
+
process.pid,
|
209
|
+
dll_file_path
|
210
|
+
)
|
211
|
+
|
212
|
+
|
213
|
+
def request(
|
214
|
+
self,
|
215
|
+
api: str,
|
216
|
+
data: Optional[dict] = None,
|
217
|
+
success_code: Optional[Union[int, list[int]]] = None,
|
218
|
+
fail_code: Optional[Union[int, list[int]]] = None
|
219
|
+
) -> Response:
|
220
|
+
"""
|
221
|
+
Request client API.
|
222
|
+
|
223
|
+
Parameters
|
224
|
+
----------
|
225
|
+
api : API name.
|
226
|
+
data : Request data.
|
227
|
+
success_code : Suceess code, if not within the range, throw an exception.
|
228
|
+
- `None`: Not handle.
|
229
|
+
- `Union[int, list[int]]`: Handle.
|
230
|
+
fail_code : Fail code, if within the range, throw an exception.
|
231
|
+
- `None`: Not handle.
|
232
|
+
- `Union[int, list[int]]`: Handle.
|
233
|
+
|
234
|
+
Returns
|
235
|
+
-------
|
236
|
+
Client response content dictionary.
|
237
|
+
"""
|
238
|
+
|
239
|
+
# Get parameter.
|
240
|
+
url = f'http://127.0.0.1:{self.client_api_port}/api/{api}'
|
241
|
+
if data is None:
|
242
|
+
data = {}
|
243
|
+
if success_code.__class__ == int:
|
244
|
+
success_code = [success_code]
|
245
|
+
if fail_code.__class__ == int:
|
246
|
+
fail_code = [fail_code]
|
247
|
+
|
248
|
+
# Request.
|
249
|
+
response = reytool_request(
|
250
|
+
url,
|
251
|
+
json=data,
|
252
|
+
method='post',
|
253
|
+
check=True
|
254
|
+
)
|
255
|
+
|
256
|
+
# Extract.
|
257
|
+
response_data = response.json()
|
258
|
+
response = {
|
259
|
+
'code': response_data['code'],
|
260
|
+
'message': response_data['msg'],
|
261
|
+
'data': response_data['data']
|
262
|
+
}
|
263
|
+
|
264
|
+
# Throw exception.
|
265
|
+
if (
|
266
|
+
(
|
267
|
+
success_code is not None
|
268
|
+
and response['code'] not in success_code
|
269
|
+
) or (
|
270
|
+
fail_code is not None
|
271
|
+
and response['code'] in fail_code
|
272
|
+
)
|
273
|
+
):
|
274
|
+
raise RClientErorr(f'client API "{api}" request failed', data, response)
|
275
|
+
|
276
|
+
return response
|
277
|
+
|
278
|
+
|
279
|
+
def check_login(self) -> bool:
|
280
|
+
"""
|
281
|
+
Check if the client is logged in.
|
282
|
+
|
283
|
+
Returns
|
284
|
+
-------
|
285
|
+
Check result.
|
286
|
+
"""
|
287
|
+
|
288
|
+
# Get parameter.
|
289
|
+
api = 'checkLogin'
|
290
|
+
|
291
|
+
# Request.
|
292
|
+
response = self.request(api)
|
293
|
+
|
294
|
+
# Check.
|
295
|
+
match response['code']:
|
296
|
+
case 1:
|
297
|
+
return True
|
298
|
+
case 0:
|
299
|
+
return False
|
300
|
+
|
301
|
+
|
302
|
+
def get_login_info(
|
303
|
+
self
|
304
|
+
) -> dict[
|
305
|
+
Literal[
|
306
|
+
'id',
|
307
|
+
'account',
|
308
|
+
'name',
|
309
|
+
'phone',
|
310
|
+
'signature',
|
311
|
+
'city',
|
312
|
+
'province',
|
313
|
+
'country',
|
314
|
+
'head_image',
|
315
|
+
'account_data_path',
|
316
|
+
'wechat_data_path',
|
317
|
+
'decrypt_key'
|
318
|
+
],
|
319
|
+
Optional[str]
|
320
|
+
]:
|
321
|
+
"""
|
322
|
+
Get login account information.
|
323
|
+
|
324
|
+
Returns
|
325
|
+
-------
|
326
|
+
Login user account information.
|
327
|
+
- `Key 'id'`: User ID, cannot change.
|
328
|
+
- `Key 'account'`: User account, can change.
|
329
|
+
- `Key 'name'`: User nickname.
|
330
|
+
- `Key 'phone'`: Phone number.
|
331
|
+
- `Key 'signature'`: Personal signature.
|
332
|
+
- `Key 'city'`: City.
|
333
|
+
- `Key 'province'`: Province.
|
334
|
+
- `Key 'country'`: Country.
|
335
|
+
- `Key 'head_image'`: Head image URL.
|
336
|
+
- `Key 'account_data_path'`: Current account data save path.
|
337
|
+
- `Key 'wechat_data_path'`: WeChat data save path.
|
338
|
+
- `Key 'decrypt_key'`: Database decrypt key.
|
339
|
+
"""
|
340
|
+
|
341
|
+
# Get parameter.
|
342
|
+
api = 'userInfo'
|
343
|
+
|
344
|
+
# Request.
|
345
|
+
response = self.request(api)
|
346
|
+
|
347
|
+
# Extract.
|
348
|
+
data = response['data']
|
349
|
+
info = {
|
350
|
+
'id': data['wxid'],
|
351
|
+
'account': data['account'],
|
352
|
+
'name': data['name'],
|
353
|
+
'phone': data['mobile'],
|
354
|
+
'signature': data['signature'],
|
355
|
+
'city': data['city'],
|
356
|
+
'province': data['province'],
|
357
|
+
'country': data['country'],
|
358
|
+
'head_image': data['headImage'],
|
359
|
+
'account_data_path': data['currentDataPath'],
|
360
|
+
'wechat_data_path': data['dataSavePath'],
|
361
|
+
'decrypt_key': data['dbKey']
|
362
|
+
}
|
363
|
+
info = {
|
364
|
+
key: (
|
365
|
+
None
|
366
|
+
if value == ''
|
367
|
+
else value
|
368
|
+
)
|
369
|
+
for key, value in info.items()
|
370
|
+
}
|
371
|
+
|
372
|
+
return info
|
373
|
+
|
374
|
+
|
375
|
+
def hook_message(
|
376
|
+
self,
|
377
|
+
host: str,
|
378
|
+
port: Union[str, int],
|
379
|
+
timeout: float
|
380
|
+
) -> None:
|
381
|
+
"""
|
382
|
+
Hook the message, and send the message to the TCP protocol request.
|
383
|
+
|
384
|
+
Parameters
|
385
|
+
----------
|
386
|
+
host : Request host.
|
387
|
+
port : Request port.
|
388
|
+
timeout : Request timeout seconds.
|
389
|
+
"""
|
390
|
+
|
391
|
+
# Get parameter.
|
392
|
+
api = 'hookSyncMsg'
|
393
|
+
port = str(port)
|
394
|
+
timeout_ms_str = str(int(timeout * 1000))
|
395
|
+
data = {
|
396
|
+
'ip': host,
|
397
|
+
'port': port,
|
398
|
+
'timeout': timeout_ms_str,
|
399
|
+
'enableHttp': '0'
|
400
|
+
}
|
401
|
+
|
402
|
+
# Request.
|
403
|
+
response = self.request(api, data, [0, 2])
|
404
|
+
|
405
|
+
# Retry.
|
406
|
+
if response['code'] == 2:
|
407
|
+
self.unhook_message()
|
408
|
+
self.hook_message(
|
409
|
+
host,
|
410
|
+
port,
|
411
|
+
timeout
|
412
|
+
)
|
413
|
+
|
414
|
+
# Report.
|
415
|
+
else:
|
416
|
+
print(
|
417
|
+
'Hook message successfully, address is "%s:%s".' % (
|
418
|
+
host,
|
419
|
+
port
|
420
|
+
)
|
421
|
+
)
|
422
|
+
|
423
|
+
|
424
|
+
def unhook_message(self) -> None:
|
425
|
+
"""
|
426
|
+
Unhook the message.
|
427
|
+
"""
|
428
|
+
|
429
|
+
# Get parameter.
|
430
|
+
api = 'unhookSyncMsg'
|
431
|
+
|
432
|
+
# Request.
|
433
|
+
self.request(api, success_code=0)
|
434
|
+
|
435
|
+
# Report.
|
436
|
+
print('Unhook message successfully.')
|
437
|
+
|
438
|
+
|
439
|
+
def download_file(
|
440
|
+
self,
|
441
|
+
id_: int
|
442
|
+
) -> None:
|
443
|
+
"""
|
444
|
+
Download image or video or other file.
|
445
|
+
|
446
|
+
Parameters
|
447
|
+
----------
|
448
|
+
id_ : Message ID.
|
449
|
+
"""
|
450
|
+
|
451
|
+
# Get parameter.
|
452
|
+
api = 'downloadAttach'
|
453
|
+
data = {'msgId': id_}
|
454
|
+
|
455
|
+
# Request.
|
456
|
+
self.request(api, data, [0, 1000])
|
457
|
+
|
458
|
+
|
459
|
+
def download_voice(
|
460
|
+
self,
|
461
|
+
id_: int,
|
462
|
+
dir_: str
|
463
|
+
) -> None:
|
464
|
+
"""
|
465
|
+
Download voice.
|
466
|
+
|
467
|
+
Parameters
|
468
|
+
----------
|
469
|
+
id_ : Message ID.
|
470
|
+
dir_ : Save directory.
|
471
|
+
"""
|
472
|
+
|
473
|
+
# Get parameter.
|
474
|
+
api = 'getVoiceByMsgId'
|
475
|
+
dir_ = os_abspath(dir_)
|
476
|
+
data = {
|
477
|
+
'msgId': id_,
|
478
|
+
'storeDir': dir_
|
479
|
+
}
|
480
|
+
|
481
|
+
# Request.
|
482
|
+
self.request(api, data, [0, 1])
|
483
|
+
|
484
|
+
|
485
|
+
def get_contact_table(
|
486
|
+
self,
|
487
|
+
type_: Optional[Literal['user', 'room']] = None
|
488
|
+
) -> list[dict[Literal['id', 'name'], str]]:
|
489
|
+
"""
|
490
|
+
Get contact table, include chat user and chat room.
|
491
|
+
|
492
|
+
Parameters
|
493
|
+
----------
|
494
|
+
type_ : Return contact table type.
|
495
|
+
- `None`: Return all contact table.
|
496
|
+
- `Literal['user']`: Return user contact table.
|
497
|
+
- `Literal['room']`: Return chat room contact table.
|
498
|
+
|
499
|
+
Returns
|
500
|
+
-------
|
501
|
+
Contact table.
|
502
|
+
- `Key 'id'`: User ID or chat room ID.
|
503
|
+
- `Key 'name'`: User nickname or chat room name.
|
504
|
+
"""
|
505
|
+
|
506
|
+
# Get parameter.
|
507
|
+
api = 'getContactList'
|
508
|
+
filter_names = {
|
509
|
+
'filehelper': '朋友推荐消息',
|
510
|
+
'floatbottle': '语音记事本',
|
511
|
+
'fmessage': '漂流瓶',
|
512
|
+
'medianote': '文件传输助手'
|
513
|
+
}
|
514
|
+
|
515
|
+
# Request.
|
516
|
+
response = self.request(api, success_code=1)
|
517
|
+
|
518
|
+
# Extract.
|
519
|
+
data: list[dict] = response['data']
|
520
|
+
table_user = []
|
521
|
+
table_room = []
|
522
|
+
for info in data:
|
523
|
+
id_: str = info['wxid']
|
524
|
+
|
525
|
+
# Filter system user.
|
526
|
+
if id_ in filter_names:
|
527
|
+
continue
|
528
|
+
|
529
|
+
# Split table.
|
530
|
+
row = {
|
531
|
+
'id': id_,
|
532
|
+
'name': info['nickname']
|
533
|
+
}
|
534
|
+
|
535
|
+
## Chat room table.
|
536
|
+
if id_.endswith('chatroom'):
|
537
|
+
if (
|
538
|
+
type_ in (None, 'room')
|
539
|
+
and id_[-1] == 'm'
|
540
|
+
):
|
541
|
+
table_room.append(row)
|
542
|
+
|
543
|
+
## User table.
|
544
|
+
else:
|
545
|
+
if type_ in (None, 'user'):
|
546
|
+
table_user.append(row)
|
547
|
+
|
548
|
+
# User no name.
|
549
|
+
for row in table_user[::-1]:
|
550
|
+
if row['name'] == '':
|
551
|
+
table_user.remove(row)
|
552
|
+
|
553
|
+
# Merge table.
|
554
|
+
table = table_user + table_room
|
555
|
+
|
556
|
+
return table
|
557
|
+
|
558
|
+
|
559
|
+
def get_contact_name(
|
560
|
+
self,
|
561
|
+
id_: str
|
562
|
+
) -> str:
|
563
|
+
"""
|
564
|
+
Get contact name, can be friend and chat room and chat room member.
|
565
|
+
|
566
|
+
Parameters
|
567
|
+
----------
|
568
|
+
id_ : User ID or chat room ID.
|
569
|
+
|
570
|
+
Returns
|
571
|
+
-------
|
572
|
+
User nickname or chat room name.
|
573
|
+
"""
|
574
|
+
|
575
|
+
# Get parameter.
|
576
|
+
api = 'getContactProfile'
|
577
|
+
data = {'wxid': id_}
|
578
|
+
|
579
|
+
# Request.
|
580
|
+
response = self.request(api, data, [0, 1])
|
581
|
+
|
582
|
+
# Extract.
|
583
|
+
data: Optional[dict] = response['data']
|
584
|
+
if data is None:
|
585
|
+
name = None
|
586
|
+
else:
|
587
|
+
name = data['nickname']
|
588
|
+
|
589
|
+
return name
|
590
|
+
|
591
|
+
|
592
|
+
def get_room_member_list(
|
593
|
+
self,
|
594
|
+
room_id: str
|
595
|
+
) -> list[str]:
|
596
|
+
"""
|
597
|
+
Get list of chat room member user ID.
|
598
|
+
|
599
|
+
Parameters
|
600
|
+
----------
|
601
|
+
room_id : Chat room ID.
|
602
|
+
|
603
|
+
Returns
|
604
|
+
-------
|
605
|
+
List of chat room member user ID.
|
606
|
+
"""
|
607
|
+
|
608
|
+
# Get parameter.
|
609
|
+
api = 'getMemberFromChatRoom'
|
610
|
+
data = {'chatRoomId': room_id}
|
611
|
+
|
612
|
+
# Request.
|
613
|
+
response = self.request(api, data, [0, 1])
|
614
|
+
|
615
|
+
# Convert.
|
616
|
+
data: dict = response['data']
|
617
|
+
members: str = data['members']
|
618
|
+
members_list = members.split('^G')
|
619
|
+
members_list = list(filter(
|
620
|
+
lambda member: member != '',
|
621
|
+
members_list
|
622
|
+
))
|
623
|
+
|
624
|
+
return members_list
|
625
|
+
|
626
|
+
|
627
|
+
def get_room_member_dict(
|
628
|
+
self,
|
629
|
+
room_id: str
|
630
|
+
) -> dict[str, str]:
|
631
|
+
"""
|
632
|
+
Get dictionary of chat room member user ID and user name.
|
633
|
+
|
634
|
+
Parameters
|
635
|
+
----------
|
636
|
+
room_id : Chat room ID.
|
637
|
+
|
638
|
+
Returns
|
639
|
+
-------
|
640
|
+
Table of chat room member user ID and user name.
|
641
|
+
"""
|
642
|
+
|
643
|
+
# Get member.
|
644
|
+
members = self.get_room_member_list(room_id)
|
645
|
+
|
646
|
+
# Loop.
|
647
|
+
table = {}
|
648
|
+
for member in members:
|
649
|
+
table[member] = self.get_contact_name(member)
|
650
|
+
|
651
|
+
return table
|
652
|
+
|
653
|
+
|
654
|
+
def send_text(
|
655
|
+
self,
|
656
|
+
receive_id: str,
|
657
|
+
text: str
|
658
|
+
) -> None:
|
659
|
+
"""
|
660
|
+
Send text message.
|
661
|
+
|
662
|
+
Parameters
|
663
|
+
----------
|
664
|
+
receive_id : User ID or chat room ID of receive message.
|
665
|
+
text : Message text.
|
666
|
+
"""
|
667
|
+
|
668
|
+
# Get parameter.
|
669
|
+
api = 'sendTextMsg'
|
670
|
+
data = {
|
671
|
+
'wxid': receive_id,
|
672
|
+
'msg': text
|
673
|
+
}
|
674
|
+
|
675
|
+
# Request.
|
676
|
+
self.request(api, data, 1)
|
677
|
+
|
678
|
+
|
679
|
+
def send_text_at(
|
680
|
+
self,
|
681
|
+
room_id: str,
|
682
|
+
user_id: Union[str, list[str], Literal['notify@all']],
|
683
|
+
text: str
|
684
|
+
) -> None:
|
685
|
+
"""
|
686
|
+
Send text message with `@`.
|
687
|
+
|
688
|
+
Parameters
|
689
|
+
----------
|
690
|
+
room_id : Chat room ID of receive message.
|
691
|
+
user_id : User ID of `@`.
|
692
|
+
- `str`, `@`: one user.
|
693
|
+
- `list[str]` `@`: multiple users.
|
694
|
+
- `Literal['notify@all']` `@`: all users.
|
695
|
+
text : Message text.
|
696
|
+
"""
|
697
|
+
|
698
|
+
# Get parameter.
|
699
|
+
api = 'sendAtText'
|
700
|
+
if user_id.__class__ != str:
|
701
|
+
user_id = ','.join(user_id)
|
702
|
+
data = {
|
703
|
+
'chatRoomId': room_id,
|
704
|
+
'wxids': user_id,
|
705
|
+
'msg': text
|
706
|
+
}
|
707
|
+
|
708
|
+
# Request.
|
709
|
+
self.request(api, data, fail_code=-1)
|
710
|
+
|
711
|
+
|
712
|
+
def send_file(
|
713
|
+
self,
|
714
|
+
receive_id: str,
|
715
|
+
path: str
|
716
|
+
) -> None:
|
717
|
+
"""
|
718
|
+
Send file message.
|
719
|
+
|
720
|
+
Parameters
|
721
|
+
----------
|
722
|
+
receive_id : User ID or chat room ID of receive message.
|
723
|
+
path : Message file path.
|
724
|
+
"""
|
725
|
+
|
726
|
+
# Get parameter.
|
727
|
+
api = 'sendFileMsg'
|
728
|
+
data = {
|
729
|
+
'wxid': receive_id,
|
730
|
+
'filePath': path
|
731
|
+
}
|
732
|
+
|
733
|
+
# Request.
|
734
|
+
self.request(api, data, fail_code=-1)
|
735
|
+
|
736
|
+
|
737
|
+
def send_image(
|
738
|
+
self,
|
739
|
+
receive_id: str,
|
740
|
+
path: str
|
741
|
+
) -> None:
|
742
|
+
"""
|
743
|
+
Send image message.
|
744
|
+
|
745
|
+
Parameters
|
746
|
+
----------
|
747
|
+
receive_id : User ID or chat room ID of receive message.
|
748
|
+
path : Message image file path.
|
749
|
+
"""
|
750
|
+
|
751
|
+
# Get parameter.
|
752
|
+
api = 'sendImagesMsg'
|
753
|
+
data = {
|
754
|
+
'wxid': receive_id,
|
755
|
+
'imagePath': path
|
756
|
+
}
|
757
|
+
|
758
|
+
# Request.
|
759
|
+
self.request(api, data, success_code=1)
|
760
|
+
|
761
|
+
|
762
|
+
def send_emotion(
|
763
|
+
self,
|
764
|
+
receive_id: str,
|
765
|
+
path: str
|
766
|
+
) -> None:
|
767
|
+
"""
|
768
|
+
Send emotion message.
|
769
|
+
|
770
|
+
Parameters
|
771
|
+
----------
|
772
|
+
receive_id : User ID or chat room ID of receive message.
|
773
|
+
path : Message emotion file path.
|
774
|
+
"""
|
775
|
+
|
776
|
+
# Get parameter.
|
777
|
+
api = 'sendCustomEmotion'
|
778
|
+
data = {
|
779
|
+
'wxid': receive_id,
|
780
|
+
'filePath': path
|
781
|
+
}
|
782
|
+
|
783
|
+
# Request.
|
784
|
+
self.request(api, data, success_code=1)
|
785
|
+
|
786
|
+
|
787
|
+
def send_pat(
|
788
|
+
self,
|
789
|
+
receive_id: str,
|
790
|
+
user_id: str,
|
791
|
+
) -> None:
|
792
|
+
"""
|
793
|
+
Send pat message.
|
794
|
+
|
795
|
+
Parameters
|
796
|
+
----------
|
797
|
+
receive_id : User ID or chat room ID of receive message.
|
798
|
+
user_id : User ID of pat.
|
799
|
+
"""
|
800
|
+
|
801
|
+
# Get parameter.
|
802
|
+
api = 'sendPatMsg'
|
803
|
+
data = {
|
804
|
+
'wxid': receive_id,
|
805
|
+
'receiver': user_id
|
806
|
+
}
|
807
|
+
|
808
|
+
# Request.
|
809
|
+
self.request(api, data, success_code=1)
|
810
|
+
|
811
|
+
|
812
|
+
def send_public(
|
813
|
+
self,
|
814
|
+
receive_id: str,
|
815
|
+
page_url: str,
|
816
|
+
title: str,
|
817
|
+
text: Optional[str] = None,
|
818
|
+
image_url: Optional[str] = None,
|
819
|
+
public_name: Optional[str] = None,
|
820
|
+
public_id: Optional[str] = None
|
821
|
+
) -> None:
|
822
|
+
"""
|
823
|
+
Send public account message.
|
824
|
+
|
825
|
+
Parameters
|
826
|
+
----------
|
827
|
+
receive_id : User ID or chat room ID of receive message.
|
828
|
+
page_url : Control click open page URL.
|
829
|
+
title : Control title.
|
830
|
+
text : Control text.
|
831
|
+
image_url : Control image URL.
|
832
|
+
public_name : Control public account name.
|
833
|
+
public_id : Control public account ID.
|
834
|
+
"""
|
835
|
+
|
836
|
+
# Get parameter.
|
837
|
+
if text is None:
|
838
|
+
text = ''
|
839
|
+
if image_url is None:
|
840
|
+
image_url = ''
|
841
|
+
if public_name is None:
|
842
|
+
public_name = ''
|
843
|
+
if public_id is None:
|
844
|
+
public_id = ''
|
845
|
+
api = 'forwardPublicMsg'
|
846
|
+
data = {
|
847
|
+
'wxid': receive_id,
|
848
|
+
'url': page_url,
|
849
|
+
'title': title,
|
850
|
+
'digest': text,
|
851
|
+
'thumbUrl': image_url,
|
852
|
+
'appName': public_name,
|
853
|
+
'userName': public_id
|
854
|
+
}
|
855
|
+
|
856
|
+
# Request.
|
857
|
+
self.request(api, data, success_code=1)
|
858
|
+
|
859
|
+
|
860
|
+
def send_forward(
|
861
|
+
self,
|
862
|
+
receive_id: str,
|
863
|
+
message_id: str
|
864
|
+
) -> None:
|
865
|
+
"""
|
866
|
+
Forward message.
|
867
|
+
|
868
|
+
Parameters
|
869
|
+
----------
|
870
|
+
receive_id : User ID or chat room ID of receive message.
|
871
|
+
message_id : Forward message ID.
|
872
|
+
"""
|
873
|
+
|
874
|
+
# Get parameter.
|
875
|
+
api = 'sendImagesMsg'
|
876
|
+
data = {
|
877
|
+
'wxid': receive_id,
|
878
|
+
'forwardMsg': message_id
|
879
|
+
}
|
880
|
+
|
881
|
+
# Request.
|
882
|
+
self.request(api, data, success_code=1)
|
883
|
+
|
884
|
+
|
885
|
+
def simulate_client_version() -> None:
|
886
|
+
"""
|
887
|
+
Simulate WeChat client version.
|
888
|
+
"""
|
889
|
+
|
890
|
+
# Check.
|
891
|
+
|
892
|
+
## Check client.
|
893
|
+
judge = RClient.check_client_started(RClient)
|
894
|
+
if not judge:
|
895
|
+
raise RClientErorr('WeChat client not started')
|
896
|
+
|
897
|
+
## Check client version.
|
898
|
+
judge = RClient.check_client_version(RClient)
|
899
|
+
if not judge:
|
900
|
+
raise RClientErorr(f'WeChat client version failed, must be "{RClient.client_version}"')
|
901
|
+
|
902
|
+
# Simulate.
|
903
|
+
for offset in RConfigClient._client_version_memory_offsets:
|
904
|
+
memory_write(
|
905
|
+
'WeChat.exe',
|
906
|
+
'WeChatWin.dll',
|
907
|
+
offset,
|
908
|
+
RClient.client_version_simulate_int
|
909
|
+
)
|
910
|
+
|
911
|
+
# Report.
|
912
|
+
print(f'WeChat client version simulated be "{RClient.client_version_simulate}"')
|