redzedbot 1.0.0__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.
- redzedbot/__init__.py +9 -0
- redzedbot/auth.py +248 -0
- redzedbot/client.py +326 -0
- redzedbot/config.py +53 -0
- redzedbot/connection.py +167 -0
- redzedbot/guest.py +37 -0
- redzedbot/message.py +51 -0
- redzedbot/proto/ParaHex_pb2.py +54 -0
- redzedbot/proto/__init__.py +7 -0
- redzedbot/utils.py +191 -0
- redzedbot-1.0.0.dist-info/METADATA +246 -0
- redzedbot-1.0.0.dist-info/RECORD +15 -0
- redzedbot-1.0.0.dist-info/WHEEL +5 -0
- redzedbot-1.0.0.dist-info/licenses/LICENSE.txt +21 -0
- redzedbot-1.0.0.dist-info/top_level.txt +1 -0
redzedbot/connection.py
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"""
|
|
2
|
+
TCP connection management for online and chat
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
from typing import Callable, Optional, Dict, Any
|
|
7
|
+
from .proto import ParaHex_pb2
|
|
8
|
+
from .utils import create_message_packet, decode_whisper_message
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class OnlineConnection:
|
|
12
|
+
"""Manages the online/presence TCP connection"""
|
|
13
|
+
|
|
14
|
+
def __init__(self, ip: str, port: str, key: bytes, iv: bytes):
|
|
15
|
+
self.ip = ip
|
|
16
|
+
self.port = int(port)
|
|
17
|
+
self.key = key
|
|
18
|
+
self.iv = iv
|
|
19
|
+
self.is_connected = False
|
|
20
|
+
self._writer = None
|
|
21
|
+
self._reader = None
|
|
22
|
+
|
|
23
|
+
async def connect(self, auth_packet: str, reconnect_delay: float = 0.5):
|
|
24
|
+
"""Connect and maintain online connection"""
|
|
25
|
+
while True:
|
|
26
|
+
try:
|
|
27
|
+
self._reader, self._writer = await asyncio.open_connection(self.ip, self.port)
|
|
28
|
+
|
|
29
|
+
auth = bytes.fromhex(auth_packet)
|
|
30
|
+
self._writer.write(auth)
|
|
31
|
+
await self._writer.drain()
|
|
32
|
+
|
|
33
|
+
self.is_connected = True
|
|
34
|
+
|
|
35
|
+
while True:
|
|
36
|
+
data = await self._reader.read(9999)
|
|
37
|
+
if not data:
|
|
38
|
+
break
|
|
39
|
+
|
|
40
|
+
except Exception as e:
|
|
41
|
+
print(f"Online connection error: {e}")
|
|
42
|
+
self.is_connected = False
|
|
43
|
+
|
|
44
|
+
finally:
|
|
45
|
+
await self.close()
|
|
46
|
+
await asyncio.sleep(reconnect_delay)
|
|
47
|
+
|
|
48
|
+
async def send_packet(self, packet: bytes):
|
|
49
|
+
"""Send packet through online connection"""
|
|
50
|
+
if self._writer and not self._writer.is_closing():
|
|
51
|
+
self._writer.write(packet)
|
|
52
|
+
await self._writer.drain()
|
|
53
|
+
|
|
54
|
+
async def close(self):
|
|
55
|
+
"""Close connection"""
|
|
56
|
+
if self._writer and not self._writer.is_closing():
|
|
57
|
+
self._writer.close()
|
|
58
|
+
await self._writer.wait_closed()
|
|
59
|
+
|
|
60
|
+
self.is_connected = False
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class ChatConnection:
|
|
64
|
+
"""Manages the chat TCP connection"""
|
|
65
|
+
|
|
66
|
+
def __init__(self, ip: str, port: str, key: bytes, iv: bytes, message_callback: Callable):
|
|
67
|
+
self.ip = ip
|
|
68
|
+
self.port = int(port)
|
|
69
|
+
self.key = key
|
|
70
|
+
self.iv = iv
|
|
71
|
+
self.is_connected = False
|
|
72
|
+
self._writer = None
|
|
73
|
+
self._reader = None
|
|
74
|
+
self._message_callback = message_callback
|
|
75
|
+
|
|
76
|
+
async def connect(self, auth_packet: str, reconnect_delay: float = 0.5):
|
|
77
|
+
"""Connect and handle chat messages"""
|
|
78
|
+
while True:
|
|
79
|
+
try:
|
|
80
|
+
self._reader, self._writer = await asyncio.open_connection(self.ip, self.port)
|
|
81
|
+
|
|
82
|
+
auth = bytes.fromhex(auth_packet)
|
|
83
|
+
self._writer.write(auth)
|
|
84
|
+
await self._writer.drain()
|
|
85
|
+
|
|
86
|
+
self.is_connected = True
|
|
87
|
+
|
|
88
|
+
while True:
|
|
89
|
+
data = await self._reader.read(9999)
|
|
90
|
+
if not data:
|
|
91
|
+
break
|
|
92
|
+
|
|
93
|
+
await self._process_packet(data)
|
|
94
|
+
|
|
95
|
+
except Exception as e:
|
|
96
|
+
print(f"Chat connection error: {e}")
|
|
97
|
+
self.is_connected = False
|
|
98
|
+
|
|
99
|
+
finally:
|
|
100
|
+
await self.close()
|
|
101
|
+
await asyncio.sleep(reconnect_delay)
|
|
102
|
+
|
|
103
|
+
async def _process_packet(self, data: bytes):
|
|
104
|
+
"""Process incoming packet"""
|
|
105
|
+
try:
|
|
106
|
+
hex_data = data.hex()
|
|
107
|
+
|
|
108
|
+
if hex_data.startswith("120000"):
|
|
109
|
+
message_data = await decode_whisper_message(hex_data[10:])
|
|
110
|
+
|
|
111
|
+
if message_data:
|
|
112
|
+
await self._message_callback(message_data)
|
|
113
|
+
|
|
114
|
+
except Exception as e:
|
|
115
|
+
print(f"Packet processing error: {e}")
|
|
116
|
+
|
|
117
|
+
async def send_packet(self, packet: bytes):
|
|
118
|
+
"""Send packet through chat connection"""
|
|
119
|
+
if self._writer and not self._writer.is_closing():
|
|
120
|
+
self._writer.write(packet)
|
|
121
|
+
await self._writer.drain()
|
|
122
|
+
|
|
123
|
+
async def send_dm(self, message: str, user_id: int, key: bytes, iv: bytes):
|
|
124
|
+
packet = await create_message_packet(
|
|
125
|
+
message=message,
|
|
126
|
+
chat_type=2,
|
|
127
|
+
target_id=user_id,
|
|
128
|
+
chat_id=user_id,
|
|
129
|
+
key=key,
|
|
130
|
+
iv=iv
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
await self.send_packet(packet)
|
|
134
|
+
|
|
135
|
+
async def send_squad(self, message: str, chat_id: int, key: bytes, iv: bytes):
|
|
136
|
+
"""Send squad message"""
|
|
137
|
+
packet = await create_message_packet(
|
|
138
|
+
message=message,
|
|
139
|
+
chat_type=0,
|
|
140
|
+
target_id=chat_id,
|
|
141
|
+
chat_id=chat_id,
|
|
142
|
+
key=key,
|
|
143
|
+
iv=iv
|
|
144
|
+
)
|
|
145
|
+
await self.send_packet(packet)
|
|
146
|
+
|
|
147
|
+
async def send_guild(self, message: str, chat_id: int, key: bytes, iv: bytes):
|
|
148
|
+
"""Send guild message"""
|
|
149
|
+
packet = await create_message_packet(
|
|
150
|
+
message=message,
|
|
151
|
+
chat_type=1,
|
|
152
|
+
target_id=chat_id,
|
|
153
|
+
chat_id=chat_id,
|
|
154
|
+
key=key,
|
|
155
|
+
iv=iv
|
|
156
|
+
)
|
|
157
|
+
await self.send_packet(packet)
|
|
158
|
+
|
|
159
|
+
async def close(self):
|
|
160
|
+
"""Close connection"""
|
|
161
|
+
if self._writer and not self._writer.is_closing():
|
|
162
|
+
try:
|
|
163
|
+
self._writer.close()
|
|
164
|
+
await self._writer.wait_closed()
|
|
165
|
+
except:
|
|
166
|
+
pass
|
|
167
|
+
self.is_connected = False
|
redzedbot/guest.py
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Guest login module for redzedbot
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
from .client import redzedbot
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
async def login(uid: str, password: str) -> redzedbot:
|
|
10
|
+
"""
|
|
11
|
+
Login as a guest user and return a redzedbot instance.
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
uid: User ID
|
|
15
|
+
password: User password
|
|
16
|
+
|
|
17
|
+
Returns:
|
|
18
|
+
redzedbot instance if successful, None if login failed
|
|
19
|
+
|
|
20
|
+
Example:
|
|
21
|
+
>>> bot = await guest.login('4243232438', 'x7aMa-HGQXIFXLNx7m')
|
|
22
|
+
>>> if bot:
|
|
23
|
+
>>> await bot.online_loop()
|
|
24
|
+
"""
|
|
25
|
+
try:
|
|
26
|
+
bot = redzedbot(uid, password)
|
|
27
|
+
success = await bot._authenticate()
|
|
28
|
+
|
|
29
|
+
if success:
|
|
30
|
+
return bot
|
|
31
|
+
else:
|
|
32
|
+
print("Login failed: Invalid credentials or account banned")
|
|
33
|
+
return None
|
|
34
|
+
|
|
35
|
+
except Exception as e:
|
|
36
|
+
print(f"Login error: {e}")
|
|
37
|
+
return None
|
redzedbot/message.py
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Message class for handling incoming messages
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Optional, Dict, Any
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Message:
|
|
9
|
+
"""Represents an incoming message"""
|
|
10
|
+
|
|
11
|
+
def __init__(self, data: Dict[str, Any]):
|
|
12
|
+
self._data = data
|
|
13
|
+
|
|
14
|
+
self.user_id = data.get('uid')
|
|
15
|
+
self.chat_id = data.get('chat_id')
|
|
16
|
+
self.chat_type = data.get('chat_type')
|
|
17
|
+
self.text = data.get('message', '')
|
|
18
|
+
|
|
19
|
+
self.nickname = data.get('nickname')
|
|
20
|
+
self.profile_pic = data.get('profile_pic')
|
|
21
|
+
|
|
22
|
+
self._determine_type()
|
|
23
|
+
|
|
24
|
+
def _determine_type(self):
|
|
25
|
+
"""Determine message type from chat_type"""
|
|
26
|
+
if self.chat_type == 0:
|
|
27
|
+
self.type = 'squad'
|
|
28
|
+
elif self.chat_type == 1:
|
|
29
|
+
self.type = 'guild'
|
|
30
|
+
elif self.chat_type == 2:
|
|
31
|
+
self.type = 'dm'
|
|
32
|
+
else:
|
|
33
|
+
self.type = 'unknown'
|
|
34
|
+
|
|
35
|
+
@property
|
|
36
|
+
def is_dm(self) -> bool:
|
|
37
|
+
"""Check if message is a direct message"""
|
|
38
|
+
return self.type == 'dm'
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def is_squad(self) -> bool:
|
|
42
|
+
"""Check if message is from squad chat"""
|
|
43
|
+
return self.type == 'squad'
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def is_guild(self) -> bool:
|
|
47
|
+
"""Check if message is from guild chat"""
|
|
48
|
+
return self.type == 'guild'
|
|
49
|
+
|
|
50
|
+
def __repr__(self):
|
|
51
|
+
return f"Message(user_id={self.user_id}, type={self.type}, text='{self.text}')"
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
3
|
+
# NO CHECKED-IN PROTOBUF GENCODE
|
|
4
|
+
# source: ParaHex.proto
|
|
5
|
+
# Protobuf Python Version: 6.33.0
|
|
6
|
+
"""Generated protocol buffer code."""
|
|
7
|
+
from google.protobuf import descriptor as _descriptor
|
|
8
|
+
from google.protobuf import descriptor_pool as _descriptor_pool
|
|
9
|
+
from google.protobuf import runtime_version as _runtime_version
|
|
10
|
+
from google.protobuf import symbol_database as _symbol_database
|
|
11
|
+
from google.protobuf.internal import builder as _builder
|
|
12
|
+
_runtime_version.ValidateProtobufRuntimeVersion(
|
|
13
|
+
_runtime_version.Domain.PUBLIC,
|
|
14
|
+
6,
|
|
15
|
+
33,
|
|
16
|
+
0,
|
|
17
|
+
'',
|
|
18
|
+
'ParaHex.proto'
|
|
19
|
+
)
|
|
20
|
+
# @@protoc_insertion_point(imports)
|
|
21
|
+
|
|
22
|
+
_sym_db = _symbol_database.Default()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\rParaHex.proto\"5\n\x0cGameSecurity\x12\x0f\n\x07version\x18\x06 \x01(\r\x12\x14\n\x0chidden_value\x18\x08 \x01(\x04\"\xfa\n\n\nMajorLogin\x12\x12\n\nevent_time\x18\x03 \x01(\t\x12\x11\n\tgame_name\x18\x04 \x01(\t\x12\x13\n\x0bplatform_id\x18\x05 \x01(\x05\x12\x16\n\x0e\x63lient_version\x18\x07 \x01(\t\x12\x17\n\x0fsystem_software\x18\x08 \x01(\t\x12\x17\n\x0fsystem_hardware\x18\t \x01(\t\x12\x18\n\x10telecom_operator\x18\n \x01(\t\x12\x14\n\x0cnetwork_type\x18\x0b \x01(\t\x12\x14\n\x0cscreen_width\x18\x0c \x01(\r\x12\x15\n\rscreen_height\x18\r \x01(\r\x12\x12\n\nscreen_dpi\x18\x0e \x01(\t\x12\x19\n\x11processor_details\x18\x0f \x01(\t\x12\x0e\n\x06memory\x18\x10 \x01(\r\x12\x14\n\x0cgpu_renderer\x18\x11 \x01(\t\x12\x13\n\x0bgpu_version\x18\x12 \x01(\t\x12\x18\n\x10unique_device_id\x18\x13 \x01(\t\x12\x11\n\tclient_ip\x18\x14 \x01(\t\x12\x10\n\x08language\x18\x15 \x01(\t\x12\x0f\n\x07open_id\x18\x16 \x01(\t\x12\x14\n\x0copen_id_type\x18\x17 \x01(\t\x12\x13\n\x0b\x64\x65vice_type\x18\x18 \x01(\t\x12\'\n\x10memory_available\x18\x19 \x01(\x0b\x32\r.GameSecurity\x12\x14\n\x0c\x61\x63\x63\x65ss_token\x18\x1d \x01(\t\x12\x17\n\x0fplatform_sdk_id\x18\x1e \x01(\x05\x12\x1a\n\x12network_operator_a\x18) \x01(\t\x12\x16\n\x0enetwork_type_a\x18* \x01(\t\x12\x1c\n\x14\x63lient_using_version\x18\x39 \x01(\t\x12\x1e\n\x16\x65xternal_storage_total\x18< \x01(\x05\x12\"\n\x1a\x65xternal_storage_available\x18= \x01(\x05\x12\x1e\n\x16internal_storage_total\x18> \x01(\x05\x12\"\n\x1ainternal_storage_available\x18? \x01(\x05\x12#\n\x1bgame_disk_storage_available\x18@ \x01(\x05\x12\x1f\n\x17game_disk_storage_total\x18\x41 \x01(\x05\x12%\n\x1d\x65xternal_sdcard_avail_storage\x18\x42 \x01(\x05\x12%\n\x1d\x65xternal_sdcard_total_storage\x18\x43 \x01(\x05\x12\x10\n\x08login_by\x18I \x01(\x05\x12\x14\n\x0clibrary_path\x18J \x01(\t\x12\x12\n\nreg_avatar\x18L \x01(\x05\x12\x15\n\rlibrary_token\x18M \x01(\t\x12\x14\n\x0c\x63hannel_type\x18N \x01(\x05\x12\x10\n\x08\x63pu_type\x18O \x01(\x05\x12\x18\n\x10\x63pu_architecture\x18Q \x01(\t\x12\x1b\n\x13\x63lient_version_code\x18S \x01(\t\x12\x14\n\x0cgraphics_api\x18V \x01(\t\x12\x1d\n\x15supported_astc_bitset\x18W \x01(\r\x12\x1a\n\x12login_open_id_type\x18X \x01(\x05\x12\x18\n\x10\x61nalytics_detail\x18Y \x01(\x0c\x12\x14\n\x0cloading_time\x18\\ \x01(\r\x12\x17\n\x0frelease_channel\x18] \x01(\t\x12\x12\n\nextra_info\x18^ \x01(\t\x12 \n\x18\x61ndroid_engine_init_flag\x18_ \x01(\r\x12\x0f\n\x07if_push\x18\x61 \x01(\x05\x12\x0e\n\x06is_vpn\x18\x62 \x01(\x05\x12\x1c\n\x14origin_platform_type\x18\x63 \x01(\t\x12\x1d\n\x15primary_platform_type\x18\x64 \x01(\t\"|\n\rMajorLoginRes\x12\x13\n\x0b\x61\x63\x63ount_uid\x18\x01 \x01(\x04\x12\x0e\n\x06region\x18\x02 \x01(\t\x12\r\n\x05token\x18\x08 \x01(\t\x12\x0b\n\x03url\x18\n \x01(\t\x12\x11\n\ttimestamp\x18\x15 \x01(\x03\x12\x0b\n\x03key\x18\x16 \x01(\x0c\x12\n\n\x02iv\x18\x17 \x01(\x0c\"\xa4\x01\n\x0cGetLoginData\x12\x12\n\nAccountUID\x18\x01 \x01(\x04\x12\x0e\n\x06Region\x18\x03 \x01(\t\x12\x13\n\x0b\x41\x63\x63ountName\x18\x04 \x01(\t\x12\x16\n\x0eOnline_IP_Port\x18\x0e \x01(\t\x12\x0f\n\x07\x43lan_ID\x18\x14 \x01(\x03\x12\x16\n\x0e\x41\x63\x63ountIP_Port\x18 \x01(\t\x12\x1a\n\x12\x43lan_Compiled_Data\x18\x37 \x01(\x0c\"\xa8\x02\n\rDecodeWhisper\x12$\n\x04\x44\x61ta\x18\x05 \x01(\x0b\x32\x16.DecodeWhisper.Nested1\x1a\xf0\x01\n\x07Nested1\x12\x0b\n\x03uid\x18\x01 \x01(\x04\x12\x0f\n\x07\x43hat_ID\x18\x02 \x01(\x04\x12\x11\n\tchat_type\x18\x03 \x01(\x05\x12\x0b\n\x03msg\x18\x04 \x01(\t\x12/\n\x07\x44\x65tails\x18\t \x01(\x0b\x32\x1e.DecodeWhisper.Nested1.Nested2\x12\x35\n\rPlatform_Info\x18\r \x01(\x0b\x32\x1e.DecodeWhisper.Nested1.Nested3\x1a\x1b\n\x07Nested2\x12\x10\n\x08Nickname\x18\x01 \x01(\t\x1a\"\n\x07Nested3\x12\x17\n\x0fprofile_pic_url\x18\x01 \x01(\t\"\x80\x01\n\rrecieved_chat\x12\x13\n\x0bpacket_type\x18\x04 \x01(\x03\x12&\n\x07\x64\x65tails\x18\x05 \x01(\x0b\x32\x15.recieved_chat.nested\x1a\x32\n\x06nested\x12\x12\n\nplayer_uid\x18\x01 \x01(\x03\x12\x14\n\x0cteam_session\x18\x0e \x01(\x0c\x62\x06proto3')
|
|
28
|
+
|
|
29
|
+
_globals = globals()
|
|
30
|
+
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
|
31
|
+
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'ParaHex_pb2', _globals)
|
|
32
|
+
if not _descriptor._USE_C_DESCRIPTORS:
|
|
33
|
+
DESCRIPTOR._loaded_options = None
|
|
34
|
+
_globals['_GAMESECURITY']._serialized_start=17
|
|
35
|
+
_globals['_GAMESECURITY']._serialized_end=70
|
|
36
|
+
_globals['_MAJORLOGIN']._serialized_start=73
|
|
37
|
+
_globals['_MAJORLOGIN']._serialized_end=1475
|
|
38
|
+
_globals['_MAJORLOGINRES']._serialized_start=1477
|
|
39
|
+
_globals['_MAJORLOGINRES']._serialized_end=1601
|
|
40
|
+
_globals['_GETLOGINDATA']._serialized_start=1604
|
|
41
|
+
_globals['_GETLOGINDATA']._serialized_end=1768
|
|
42
|
+
_globals['_DECODEWHISPER']._serialized_start=1771
|
|
43
|
+
_globals['_DECODEWHISPER']._serialized_end=2067
|
|
44
|
+
_globals['_DECODEWHISPER_NESTED1']._serialized_start=1827
|
|
45
|
+
_globals['_DECODEWHISPER_NESTED1']._serialized_end=2067
|
|
46
|
+
_globals['_DECODEWHISPER_NESTED1_NESTED2']._serialized_start=2004
|
|
47
|
+
_globals['_DECODEWHISPER_NESTED1_NESTED2']._serialized_end=2031
|
|
48
|
+
_globals['_DECODEWHISPER_NESTED1_NESTED3']._serialized_start=2033
|
|
49
|
+
_globals['_DECODEWHISPER_NESTED1_NESTED3']._serialized_end=2067
|
|
50
|
+
_globals['_RECIEVED_CHAT']._serialized_start=2070
|
|
51
|
+
_globals['_RECIEVED_CHAT']._serialized_end=2198
|
|
52
|
+
_globals['_RECIEVED_CHAT_NESTED']._serialized_start=2148
|
|
53
|
+
_globals['_RECIEVED_CHAT_NESTED']._serialized_end=2198
|
|
54
|
+
# @@protoc_insertion_point(module_scope)
|
redzedbot/utils.py
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Utility functions for ParaHex
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import random
|
|
6
|
+
from typing import Dict, Any, Optional
|
|
7
|
+
from Crypto.Cipher import AES
|
|
8
|
+
from Crypto.Util.Padding import pad
|
|
9
|
+
from .proto import ParaHex_pb2
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
async def get_random_user_agent() -> str:
|
|
13
|
+
"""Generate random user agent"""
|
|
14
|
+
versions = [
|
|
15
|
+
'4.0.18P6', '4.0.19P7', '4.0.20P1', '4.1.0P3', '4.1.5P2', '4.2.1P8',
|
|
16
|
+
'4.2.3P1', '5.0.1B2', '5.0.2P4', '5.1.0P1', '5.2.0B1', '5.2.5P3',
|
|
17
|
+
'5.3.0B1', '5.3.2P2', '5.4.0P1', '5.4.3B2', '5.5.0P1', '5.5.2P3'
|
|
18
|
+
]
|
|
19
|
+
models = [
|
|
20
|
+
'SM-A125F', 'SM-A225F', 'SM-A325M', 'SM-A515F', 'SM-A725F', 'SM-M215F', 'SM-M325FV',
|
|
21
|
+
'Redmi 9A', 'Redmi 9C', 'POCO M3', 'POCO M4 Pro', 'RMX2185', 'RMX3085',
|
|
22
|
+
'moto g(9) play', 'CPH2239', 'V2027', 'OnePlus Nord', 'ASUS_Z01QD',
|
|
23
|
+
]
|
|
24
|
+
android_versions = ['9', '10', '11', '12', '13', '14']
|
|
25
|
+
languages = ['en-US', 'es-MX', 'pt-BR', 'id-ID', 'ru-RU', 'hi-IN']
|
|
26
|
+
countries = ['USA', 'MEX', 'BRA', 'IDN', 'RUS', 'IND']
|
|
27
|
+
|
|
28
|
+
version = random.choice(versions)
|
|
29
|
+
model = random.choice(models)
|
|
30
|
+
android = random.choice(android_versions)
|
|
31
|
+
lang = random.choice(languages)
|
|
32
|
+
country = random.choice(countries)
|
|
33
|
+
|
|
34
|
+
return f"GarenaMSDK/{version}({model};Android {android};{lang};{country};)"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
async def get_random_banner() -> int:
|
|
38
|
+
"""Get random banner ID"""
|
|
39
|
+
banners = [
|
|
40
|
+
902000306, 902000305, 902000003, 902000016, 902000017, 902000019,
|
|
41
|
+
902031010, 902043025, 902043024, 902000020, 902000021, 902000023,
|
|
42
|
+
902000070, 902000087, 902000108, 902000011, 902049020, 902049018,
|
|
43
|
+
902049017, 902049016, 902049015, 902049003, 902033016, 902033017,
|
|
44
|
+
902033018, 902048018, 902000306, 902000305, 902000079
|
|
45
|
+
]
|
|
46
|
+
return random.choice(banners)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
async def encrypt_proto(data: bytes) -> bytes:
|
|
50
|
+
"""Encrypt protobuf data"""
|
|
51
|
+
key = b'Yg&tc%DEuh6%Zc^8'
|
|
52
|
+
iv = b'6oyZDr22E3ychjM%'
|
|
53
|
+
cipher = AES.new(key, AES.MODE_CBC, iv)
|
|
54
|
+
padded_message = pad(data, AES.block_size)
|
|
55
|
+
encrypted_payload = cipher.encrypt(padded_message)
|
|
56
|
+
return encrypted_payload
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
async def encrypt_packet(hex_data: str, key: bytes, iv: bytes) -> str:
|
|
60
|
+
"""Encrypt packet data"""
|
|
61
|
+
cipher = AES.new(key, AES.MODE_CBC, iv)
|
|
62
|
+
encrypted = cipher.encrypt(pad(bytes.fromhex(hex_data), 16))
|
|
63
|
+
return encrypted.hex()
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
async def encode_hex(value: int) -> str:
|
|
67
|
+
"""Encode integer to hex"""
|
|
68
|
+
result = hex(value)[2:]
|
|
69
|
+
if len(result) == 1:
|
|
70
|
+
result = "0" + result
|
|
71
|
+
return result
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
async def encode_uid(uid: int) -> str:
|
|
75
|
+
"""Encode UID for protobuf"""
|
|
76
|
+
encoded = []
|
|
77
|
+
while uid:
|
|
78
|
+
encoded.append((uid & 0x7F) | (0x80 if uid > 0x7F else 0))
|
|
79
|
+
uid >>= 7
|
|
80
|
+
return bytes(encoded).hex()
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
async def encode_varint(value: int) -> bytes:
|
|
84
|
+
"""Encode varint"""
|
|
85
|
+
if value < 0:
|
|
86
|
+
return b''
|
|
87
|
+
|
|
88
|
+
result = []
|
|
89
|
+
while True:
|
|
90
|
+
byte = value & 0x7F
|
|
91
|
+
value >>= 7
|
|
92
|
+
if value:
|
|
93
|
+
byte |= 0x80
|
|
94
|
+
result.append(byte)
|
|
95
|
+
if not value:
|
|
96
|
+
break
|
|
97
|
+
|
|
98
|
+
return bytes(result)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
async def create_variant_field(field_number: int, value: int) -> bytes:
|
|
102
|
+
"""Create variant field"""
|
|
103
|
+
field_header = (field_number << 3) | 0
|
|
104
|
+
return await encode_varint(field_header) + await encode_varint(value)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
async def create_length_field(field_number: int, value) -> bytes:
|
|
108
|
+
"""Create length-delimited field"""
|
|
109
|
+
field_header = (field_number << 3) | 2
|
|
110
|
+
encoded_value = value.encode() if isinstance(value, str) else value
|
|
111
|
+
return await encode_varint(field_header) + await encode_varint(len(encoded_value)) + encoded_value
|
|
112
|
+
|
|
113
|
+
async def E_AEs(Pc):
|
|
114
|
+
Z = bytes.fromhex(Pc)
|
|
115
|
+
key = bytes([89, 103, 38, 116, 99, 37, 68, 69, 117, 104, 54, 37, 90, 99, 94, 56])
|
|
116
|
+
iv = bytes([54, 111, 121, 90, 68, 114, 50, 50, 69, 51, 121, 99, 104, 106, 77, 37])
|
|
117
|
+
K = AES.new(key , AES.MODE_CBC , iv)
|
|
118
|
+
R = K.encrypt(pad(Z , AES.block_size))
|
|
119
|
+
return bytes.fromhex(R.hex())
|
|
120
|
+
|
|
121
|
+
async def create_proto(fields: Dict) -> bytearray:
|
|
122
|
+
"""Create protobuf packet from fields"""
|
|
123
|
+
packet = bytearray()
|
|
124
|
+
|
|
125
|
+
for field, value in fields.items():
|
|
126
|
+
if isinstance(value, dict):
|
|
127
|
+
nested_packet = await create_proto(value)
|
|
128
|
+
packet.extend(await create_length_field(field, nested_packet))
|
|
129
|
+
elif isinstance(value, int):
|
|
130
|
+
packet.extend(await create_variant_field(field, value))
|
|
131
|
+
elif isinstance(value, str) or isinstance(value, bytes):
|
|
132
|
+
packet.extend(await create_length_field(field, value))
|
|
133
|
+
|
|
134
|
+
return packet
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
async def generate_packet(packet_hex: str, header: str, key: bytes, iv: bytes) -> bytes:
|
|
138
|
+
"""Generate final packet with header"""
|
|
139
|
+
encrypted = await encrypt_packet(packet_hex, key, iv)
|
|
140
|
+
length_hex = await encode_hex(len(encrypted) // 2)
|
|
141
|
+
|
|
142
|
+
if len(length_hex) == 2:
|
|
143
|
+
header_prefix = header + "000000"
|
|
144
|
+
elif len(length_hex) == 3:
|
|
145
|
+
header_prefix = header + "00000"
|
|
146
|
+
elif len(length_hex) == 4:
|
|
147
|
+
header_prefix = header + "0000"
|
|
148
|
+
elif len(length_hex) == 5:
|
|
149
|
+
header_prefix = header + "000"
|
|
150
|
+
else:
|
|
151
|
+
header_prefix = header + "00"
|
|
152
|
+
|
|
153
|
+
return bytes.fromhex(header_prefix + length_hex + encrypted)
|
|
154
|
+
|
|
155
|
+
async def xBanner():
|
|
156
|
+
bN = [902000306 , 902000305 , 902000003 , 902000016 , 902000017 , 902000019 , 902031010 , 902043025 , 902043024 , 902000020 , 902000021 , 902000023 , 902000070 , 902000087 , 902000108 , 902000011 , 902049020 , 902049018 , 902049017 , 902049016 , 902049015 , 902049003 , 902033016 , 902033017 , 902033018 , 902048018 , 902000306 , 902000305 , 902000079]
|
|
157
|
+
return random.choice(bN)
|
|
158
|
+
|
|
159
|
+
async def create_message_packet(message: str, chat_type: int, target_id: int, chat_id: int, key: bytes, iv: bytes) -> bytes:
|
|
160
|
+
"""Create message packet"""
|
|
161
|
+
try:
|
|
162
|
+
fields = {1: target_id , 2: target_id , 3: 2, 4: message, 5: 1735129800, 7: 2, 9: {1: "RedZedTOP1", 2: int(await xBanner()), 3: 901048018, 4: 330, 5: 909034009, 8: "RedZedTOP1", 10: 1, 11: 1, 13: {1: 2}, 14: {1: 12484827014, 2: 8, 3: "\u0010\u0015\b\n\u000b\u0013\f\u000f\u0011\u0004\u0007\u0002\u0003\r\u000e\u0012\u0001\u0005\u0006"}, 12: 0}, 10: "en", 13: {3: 1}}
|
|
163
|
+
|
|
164
|
+
packet = (await create_proto(fields)).hex()
|
|
165
|
+
packet = "080112" + await encode_uid(len(packet) // 2) + packet
|
|
166
|
+
|
|
167
|
+
return await generate_packet(packet, '1215', key, iv)
|
|
168
|
+
except Exception as e:
|
|
169
|
+
print(e)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
async def decode_whisper_message(hex_packet: str) -> Optional[Dict[str, Any]]:
|
|
175
|
+
"""Decode whisper message"""
|
|
176
|
+
try:
|
|
177
|
+
packet = bytes.fromhex(hex_packet)
|
|
178
|
+
proto = ParaHex_pb2.DecodeWhisper()
|
|
179
|
+
proto.ParseFromString(packet)
|
|
180
|
+
|
|
181
|
+
return {
|
|
182
|
+
'uid': proto.Data.uid,
|
|
183
|
+
'chat_id': proto.Data.Chat_ID,
|
|
184
|
+
'chat_type': proto.Data.chat_type,
|
|
185
|
+
'message': proto.Data.msg,
|
|
186
|
+
'nickname': proto.Data.Details.Nickname if proto.Data.HasField('Details') else None,
|
|
187
|
+
'profile_pic': proto.Data.Platform_Info.profile_pic_url if proto.Data.HasField('Platform_Info') else None
|
|
188
|
+
}
|
|
189
|
+
except Exception as e:
|
|
190
|
+
print(f"Decode error: {e}")
|
|
191
|
+
return None
|