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/__init__.py
ADDED
redzedbot/auth.py
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Authentication and login handling
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import aiohttp
|
|
7
|
+
import json
|
|
8
|
+
import ssl
|
|
9
|
+
from typing import Dict, Optional, Tuple
|
|
10
|
+
from datetime import datetime
|
|
11
|
+
from .proto import ParaHex_pb2
|
|
12
|
+
from .utils import get_random_user_agent, encrypt_proto
|
|
13
|
+
from .config import Config
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
async def generate_access_token(uid: str, password: str) -> Tuple[Optional[str], Optional[str]]:
|
|
17
|
+
"""Generate Garena access token"""
|
|
18
|
+
url = "https://100067.connect.garena.com/oauth/guest/token/grant"
|
|
19
|
+
|
|
20
|
+
headers = {
|
|
21
|
+
"Host": "100067.connect.garena.com",
|
|
22
|
+
"User-Agent": await get_random_user_agent(),
|
|
23
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
24
|
+
"Accept-Encoding": "gzip, deflate, br",
|
|
25
|
+
"Connection": "close"
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
data = {
|
|
29
|
+
"uid": uid,
|
|
30
|
+
"password": password,
|
|
31
|
+
"response_type": "token",
|
|
32
|
+
"client_type": "2",
|
|
33
|
+
"client_secret": "2ee44819e9b4598845141067b281621874d0d5d7af9d8f7e00c1e54715b7d1e3",
|
|
34
|
+
"client_id": "100067"
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
try:
|
|
38
|
+
async with aiohttp.ClientSession() as session:
|
|
39
|
+
async with session.post(url, headers=headers, data=data) as response:
|
|
40
|
+
if response.status != 200:
|
|
41
|
+
return None, None
|
|
42
|
+
|
|
43
|
+
data = await response.json()
|
|
44
|
+
open_id = data.get("open_id")
|
|
45
|
+
access_token = data.get("access_token")
|
|
46
|
+
|
|
47
|
+
return (open_id, access_token) if open_id and access_token else (None, None)
|
|
48
|
+
|
|
49
|
+
except Exception as e:
|
|
50
|
+
print(f"Token generation error: {e}")
|
|
51
|
+
return None, None
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
async def create_payload(open_id: str, access_token: str) -> bytes:
|
|
55
|
+
"""Create encrypted protobuf payload for login"""
|
|
56
|
+
major_login = ParaHex_pb2.MajorLogin()
|
|
57
|
+
|
|
58
|
+
major_login.event_time = str(datetime.now())[:-7]
|
|
59
|
+
major_login.game_name = "free fire"
|
|
60
|
+
major_login.platform_id = 1
|
|
61
|
+
major_login.client_version = Config.VERSION
|
|
62
|
+
major_login.system_software = "Android OS 9 / API-28 (PQ3B.190801.10101846/G9650ZHU2ARC6)"
|
|
63
|
+
major_login.system_hardware = "Handheld"
|
|
64
|
+
major_login.telecom_operator = "Verizon"
|
|
65
|
+
major_login.network_type = "WIFI"
|
|
66
|
+
major_login.screen_width = 1920
|
|
67
|
+
major_login.screen_height = 1080
|
|
68
|
+
major_login.screen_dpi = "280"
|
|
69
|
+
major_login.processor_details = "ARM64 FP ASIMD AES VMH | 2865 | 4"
|
|
70
|
+
major_login.memory = 3003
|
|
71
|
+
major_login.gpu_renderer = "Adreno (TM) 640"
|
|
72
|
+
major_login.gpu_version = "OpenGL ES 3.1 v1.46"
|
|
73
|
+
major_login.unique_device_id = "Google|34a7dcdf-a7d5-4cb6-8d7e-3b0e448a0c57"
|
|
74
|
+
major_login.client_ip = "223.191.51.89"
|
|
75
|
+
major_login.language = "en"
|
|
76
|
+
major_login.open_id = open_id
|
|
77
|
+
major_login.open_id_type = "4"
|
|
78
|
+
major_login.device_type = "Handheld"
|
|
79
|
+
|
|
80
|
+
memory_available = major_login.memory_available
|
|
81
|
+
memory_available.version = 55
|
|
82
|
+
memory_available.hidden_value = 81
|
|
83
|
+
|
|
84
|
+
major_login.access_token = access_token
|
|
85
|
+
major_login.platform_sdk_id = 1
|
|
86
|
+
major_login.network_operator_a = "Verizon"
|
|
87
|
+
major_login.network_type_a = "WIFI"
|
|
88
|
+
major_login.client_using_version = "7428b253defc164018c604a1ebbfebdf"
|
|
89
|
+
major_login.external_storage_total = 36235
|
|
90
|
+
major_login.external_storage_available = 31335
|
|
91
|
+
major_login.internal_storage_total = 2519
|
|
92
|
+
major_login.internal_storage_available = 703
|
|
93
|
+
major_login.game_disk_storage_available = 25010
|
|
94
|
+
major_login.game_disk_storage_total = 26628
|
|
95
|
+
major_login.external_sdcard_avail_storage = 32992
|
|
96
|
+
major_login.external_sdcard_total_storage = 36235
|
|
97
|
+
major_login.login_by = 3
|
|
98
|
+
major_login.library_path = "/data/app/com.dts.freefireth-YPKM8jHEwAJlhpmhDhv5MQ==/lib/arm64"
|
|
99
|
+
major_login.reg_avatar = 1
|
|
100
|
+
major_login.library_token = "5b892aaabd688e571f688053118a162b|/data/app/com.dts.freefireth-YPKM8jHEwAJlhpmhDhv5MQ==/base.apk"
|
|
101
|
+
major_login.channel_type = 3
|
|
102
|
+
major_login.cpu_type = 2
|
|
103
|
+
major_login.cpu_architecture = "64"
|
|
104
|
+
major_login.client_version_code = "2019118695"
|
|
105
|
+
major_login.graphics_api = "OpenGLES2"
|
|
106
|
+
major_login.supported_astc_bitset = 16383
|
|
107
|
+
major_login.login_open_id_type = 4
|
|
108
|
+
major_login.analytics_detail = b"FwQVTgUPX1UaUllDDwcWCRBpWAUOUgsvA1snWlBaO1kFYg=="
|
|
109
|
+
major_login.loading_time = 13564
|
|
110
|
+
major_login.release_channel = "android"
|
|
111
|
+
major_login.extra_info = "KqsHTymw5/5GB23YGniUYN2/q47GATrq7eFeRatf0NkwLKEMQ0PK5BKEk72dPflAxUlEBir6Vtey83XqF593qsl8hwY="
|
|
112
|
+
major_login.android_engine_init_flag = 110009
|
|
113
|
+
major_login.if_push = 1
|
|
114
|
+
major_login.is_vpn = 1
|
|
115
|
+
major_login.origin_platform_type = "4"
|
|
116
|
+
major_login.primary_platform_type = "4"
|
|
117
|
+
|
|
118
|
+
string = major_login.SerializeToString()
|
|
119
|
+
return await encrypt_proto(string)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
async def major_login(open_id: str, access_token: str) -> Optional[bytes]:
|
|
123
|
+
"""Perform major login"""
|
|
124
|
+
if not Config._updated:
|
|
125
|
+
await Config.auto_update()
|
|
126
|
+
|
|
127
|
+
payload = await create_payload(open_id, access_token)
|
|
128
|
+
url = f"{Config.LOGIN_URL}MajorLogin"
|
|
129
|
+
|
|
130
|
+
headers = {
|
|
131
|
+
'User-Agent': await get_random_user_agent(),
|
|
132
|
+
'Connection': "Keep-Alive",
|
|
133
|
+
'Accept-Encoding': "gzip",
|
|
134
|
+
'Content-Type': "application/x-www-form-urlencoded",
|
|
135
|
+
'Expect': "100-continue",
|
|
136
|
+
'X-Unity-Version': "2018.4.11f1",
|
|
137
|
+
'X-GA': "v1 1",
|
|
138
|
+
'ReleaseVersion': Config.OB
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
ssl_context = ssl.create_default_context()
|
|
142
|
+
ssl_context.check_hostname = False
|
|
143
|
+
ssl_context.verify_mode = ssl.CERT_NONE
|
|
144
|
+
|
|
145
|
+
try:
|
|
146
|
+
async with aiohttp.ClientSession() as session:
|
|
147
|
+
async with session.post(url, data=payload, headers=headers, ssl=ssl_context) as response:
|
|
148
|
+
response_content = await response.read()
|
|
149
|
+
if response.status == 200:
|
|
150
|
+
return response_content
|
|
151
|
+
return None
|
|
152
|
+
except Exception as e:
|
|
153
|
+
print(f"Major login error: {e}")
|
|
154
|
+
return None
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
async def get_login_data(major_response: bytes, open_id: str, access_token: str) -> Optional[Dict]:
|
|
158
|
+
"""Get login data from major login response"""
|
|
159
|
+
try:
|
|
160
|
+
proto = ParaHex_pb2.MajorLoginRes()
|
|
161
|
+
proto.ParseFromString(major_response)
|
|
162
|
+
|
|
163
|
+
clientbp = proto.url
|
|
164
|
+
token = proto.token
|
|
165
|
+
account_uid = proto.account_uid
|
|
166
|
+
timestamp = proto.timestamp
|
|
167
|
+
key = proto.key
|
|
168
|
+
iv = proto.iv
|
|
169
|
+
|
|
170
|
+
payload = await create_payload(open_id, access_token)
|
|
171
|
+
login_url = f"{clientbp}/GetLoginData"
|
|
172
|
+
|
|
173
|
+
headers = {
|
|
174
|
+
'User-Agent': await get_random_user_agent(),
|
|
175
|
+
'Connection': "Keep-Alive",
|
|
176
|
+
'Accept-Encoding': "gzip",
|
|
177
|
+
'Content-Type': "application/x-www-form-urlencoded",
|
|
178
|
+
'Expect': "100-continue",
|
|
179
|
+
'X-Unity-Version': "2018.4.11f1",
|
|
180
|
+
'X-GA': "v1 1",
|
|
181
|
+
'ReleaseVersion': Config.OB,
|
|
182
|
+
'Authorization': f"Bearer {token}"
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
ssl_context = ssl.create_default_context()
|
|
186
|
+
ssl_context.check_hostname = False
|
|
187
|
+
ssl_context.verify_mode = ssl.CERT_NONE
|
|
188
|
+
|
|
189
|
+
async with aiohttp.ClientSession() as session:
|
|
190
|
+
async with session.post(login_url, data=payload, headers=headers, ssl=ssl_context) as response:
|
|
191
|
+
if response.status != 200:
|
|
192
|
+
return None
|
|
193
|
+
|
|
194
|
+
response_data = await response.read()
|
|
195
|
+
|
|
196
|
+
proto = ParaHex_pb2.GetLoginData()
|
|
197
|
+
proto.ParseFromString(response_data)
|
|
198
|
+
|
|
199
|
+
online_ports = proto.Online_IP_Port
|
|
200
|
+
chat_ports = proto.AccountIP_Port
|
|
201
|
+
online_ip, online_port = online_ports.split(":")
|
|
202
|
+
chat_ip, chat_port = chat_ports.split(":")
|
|
203
|
+
|
|
204
|
+
return {
|
|
205
|
+
'account_uid': account_uid,
|
|
206
|
+
'nickname': proto.AccountName,
|
|
207
|
+
'region': proto.Region,
|
|
208
|
+
'online_ip': online_ip,
|
|
209
|
+
'online_port': online_port,
|
|
210
|
+
'chat_ip': chat_ip,
|
|
211
|
+
'chat_port': chat_port,
|
|
212
|
+
'clan_id': proto.Clan_ID if proto.Clan_ID else None,
|
|
213
|
+
'token': token,
|
|
214
|
+
'timestamp': timestamp,
|
|
215
|
+
'key': key,
|
|
216
|
+
'iv': iv,
|
|
217
|
+
'url':clientbp
|
|
218
|
+
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
except Exception as e:
|
|
222
|
+
print(f"Login data error: {e}")
|
|
223
|
+
return None
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
async def create_auth_packet(account_uid: int, token: str, timestamp: int, key: bytes, iv: bytes) -> str:
|
|
227
|
+
"""Create authentication packet"""
|
|
228
|
+
from .utils import encode_hex, encrypt_packet
|
|
229
|
+
|
|
230
|
+
uid_hex = hex(account_uid)[2:]
|
|
231
|
+
uid_length = len(uid_hex)
|
|
232
|
+
encrypted_timestamp = await encode_hex(timestamp)
|
|
233
|
+
encrypted_account_token = token.encode().hex()
|
|
234
|
+
encrypted_packet = await encrypt_packet(encrypted_account_token, key, iv)
|
|
235
|
+
encrypted_packet_length = hex(len(encrypted_packet) // 2)[2:]
|
|
236
|
+
|
|
237
|
+
if uid_length == 9:
|
|
238
|
+
headers = '0000000'
|
|
239
|
+
elif uid_length == 8:
|
|
240
|
+
headers = '00000000'
|
|
241
|
+
elif uid_length == 10:
|
|
242
|
+
headers = '000000'
|
|
243
|
+
elif uid_length == 7:
|
|
244
|
+
headers = '000000000'
|
|
245
|
+
else:
|
|
246
|
+
headers = '0000000'
|
|
247
|
+
|
|
248
|
+
return f"0115{headers}{uid_hex}{encrypted_timestamp}00000{encrypted_packet_length}{encrypted_packet}"
|
redzedbot/client.py
ADDED
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Main redzedbot client class
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
from typing import Callable, Dict, Optional, Any
|
|
7
|
+
from .auth import generate_access_token, major_login, get_login_data, create_auth_packet
|
|
8
|
+
from .connection import OnlineConnection, ChatConnection
|
|
9
|
+
from .message import Message
|
|
10
|
+
from .utils import get_random_user_agent
|
|
11
|
+
import requests
|
|
12
|
+
from .config import Config
|
|
13
|
+
from .utils import create_proto,E_AEs
|
|
14
|
+
|
|
15
|
+
class redzedbot:
|
|
16
|
+
"""
|
|
17
|
+
Main bot client class with decorator-based event handling
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def __init__(self, uid: str, password: str):
|
|
21
|
+
self.uid = uid
|
|
22
|
+
self.password = password
|
|
23
|
+
|
|
24
|
+
self.is_connected = False
|
|
25
|
+
self.chat_connected = False
|
|
26
|
+
self.online_connected = False
|
|
27
|
+
|
|
28
|
+
self.nickname = None
|
|
29
|
+
self.account_uid = None
|
|
30
|
+
self.region = None
|
|
31
|
+
self.clan_id = None
|
|
32
|
+
|
|
33
|
+
self._key = None
|
|
34
|
+
self._iv = None
|
|
35
|
+
self._token = None
|
|
36
|
+
self._timestamp = None
|
|
37
|
+
self._auth_packet = None
|
|
38
|
+
|
|
39
|
+
self._online_conn = None
|
|
40
|
+
self._chat_conn = None
|
|
41
|
+
|
|
42
|
+
self._message_handlers = []
|
|
43
|
+
self._ready_handlers = []
|
|
44
|
+
|
|
45
|
+
self._ready_event = asyncio.Event()
|
|
46
|
+
|
|
47
|
+
async def _authenticate(self) -> bool:
|
|
48
|
+
"""Internal authentication process"""
|
|
49
|
+
try:
|
|
50
|
+
from .config import Config
|
|
51
|
+
if not Config._updated:
|
|
52
|
+
|
|
53
|
+
await Config.auto_update()
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
open_id, access_token = await generate_access_token(self.uid, self.password)
|
|
57
|
+
if not open_id or not access_token:
|
|
58
|
+
return False
|
|
59
|
+
|
|
60
|
+
print(f'LoginURL : {Config.LOGIN_URL}MajorLogin , Version {Config.VERSION} , OB: {Config.OB}')
|
|
61
|
+
major_response = await major_login(open_id, access_token)
|
|
62
|
+
if not major_response:
|
|
63
|
+
return False
|
|
64
|
+
|
|
65
|
+
login_data = await get_login_data(major_response, open_id, access_token)
|
|
66
|
+
if not login_data:
|
|
67
|
+
return False
|
|
68
|
+
|
|
69
|
+
self.account_uid = login_data['account_uid']
|
|
70
|
+
self.nickname = login_data['nickname']
|
|
71
|
+
self.region = login_data['region']
|
|
72
|
+
self.clan_id = login_data.get('clan_id')
|
|
73
|
+
self._clientbp = login_data['url']
|
|
74
|
+
self._key = login_data['key']
|
|
75
|
+
self._iv = login_data['iv']
|
|
76
|
+
self._token = login_data['token']
|
|
77
|
+
self._timestamp = login_data['timestamp']
|
|
78
|
+
self._online_conn = OnlineConnection(
|
|
79
|
+
login_data['online_ip'],
|
|
80
|
+
login_data['online_port'],
|
|
81
|
+
self._key,
|
|
82
|
+
self._iv
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
self._chat_conn = ChatConnection(
|
|
86
|
+
login_data['chat_ip'],
|
|
87
|
+
login_data['chat_port'],
|
|
88
|
+
self._key,
|
|
89
|
+
self._iv,
|
|
90
|
+
self._process_message
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
self._auth_packet = await create_auth_packet(
|
|
94
|
+
self.account_uid,
|
|
95
|
+
self._token,
|
|
96
|
+
self._timestamp,
|
|
97
|
+
self._key,
|
|
98
|
+
self._iv
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
self.is_connected = True
|
|
102
|
+
return True
|
|
103
|
+
|
|
104
|
+
except Exception as e:
|
|
105
|
+
print(f"Authentication error: {e}")
|
|
106
|
+
return False
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
async def add_friend(self, uid: int):
|
|
111
|
+
"""
|
|
112
|
+
Add Friend Via UID.
|
|
113
|
+
|
|
114
|
+
Example:
|
|
115
|
+
await bot.add_friend(6728454010)
|
|
116
|
+
"""
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
try:
|
|
120
|
+
user_id = int(uid)
|
|
121
|
+
except ValueError:
|
|
122
|
+
return "Only Uid In Numbers :/"
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
if not self._online_conn:
|
|
126
|
+
return "online connection not correctly started"
|
|
127
|
+
|
|
128
|
+
url = self._clientbp + "/RequestAddingFriend"
|
|
129
|
+
|
|
130
|
+
fields = {
|
|
131
|
+
1: self.account_uid,
|
|
132
|
+
2: user_id,
|
|
133
|
+
3: 22
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
proto = await create_proto(fields)
|
|
137
|
+
pphex = proto.hex()
|
|
138
|
+
encrypted_payload = await E_AEs(pphex)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
headers = {
|
|
142
|
+
"Accept-Encoding": "gzip",
|
|
143
|
+
"Authorization": f"Bearer {self._token}",
|
|
144
|
+
"Connection": "Keep-Alive",
|
|
145
|
+
"Content-Type": "application/octet-stream",
|
|
146
|
+
"Expect": "100-continue",
|
|
147
|
+
"Host": Config.HOST,
|
|
148
|
+
"ReleaseVersion": Config.OB,
|
|
149
|
+
"User-Agent": "Dalvik/2.1.0 (Linux; U; Android 9; ASUS_I005DA Build/PI)",
|
|
150
|
+
"X-GA": "v1 1",
|
|
151
|
+
"X-Unity-Version": "2018.4.11f1"
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
response = requests.post(
|
|
155
|
+
url,
|
|
156
|
+
headers=headers,
|
|
157
|
+
data=encrypted_payload,
|
|
158
|
+
verify=False,
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
print(response.text)
|
|
162
|
+
|
|
163
|
+
if "BR_FRIEND_ALREADY_SENT_REQUEST" in response.text:
|
|
164
|
+
return "Already Sent The Friend Request"
|
|
165
|
+
elif "BR_FRIEND_DUPLICATE" in response.text:
|
|
166
|
+
return "User Is Already A Friend"
|
|
167
|
+
else:
|
|
168
|
+
return "Sent Invite!"
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def message_handler(self, command: Optional[str] = None):
|
|
177
|
+
"""
|
|
178
|
+
Decorator for message handlers.
|
|
179
|
+
|
|
180
|
+
Args:
|
|
181
|
+
command: Optional command to match (e.g., 'Hi', 'help')
|
|
182
|
+
|
|
183
|
+
Example:
|
|
184
|
+
@bot.message_handler(command='Hi')
|
|
185
|
+
async def handle_hi(msg):
|
|
186
|
+
await bot.send_dm('Hello!', msg.user_id)
|
|
187
|
+
"""
|
|
188
|
+
def decorator(func: Callable):
|
|
189
|
+
self._message_handlers.append({
|
|
190
|
+
'command': command.lower() if command else None,
|
|
191
|
+
'handler': func
|
|
192
|
+
})
|
|
193
|
+
return func
|
|
194
|
+
return decorator
|
|
195
|
+
|
|
196
|
+
def online(self):
|
|
197
|
+
"""
|
|
198
|
+
Decorator for ready/online event handler.
|
|
199
|
+
|
|
200
|
+
Example:
|
|
201
|
+
@bot.online()
|
|
202
|
+
def on_ready(bot):
|
|
203
|
+
if bot.is_connected:
|
|
204
|
+
print(f"Logged in as {bot.nickname}")
|
|
205
|
+
"""
|
|
206
|
+
def decorator(func: Callable):
|
|
207
|
+
self._ready_handlers.append(func)
|
|
208
|
+
return func
|
|
209
|
+
return decorator
|
|
210
|
+
|
|
211
|
+
async def _process_message(self, msg_data: Dict[str, Any]):
|
|
212
|
+
"""Internal message processor"""
|
|
213
|
+
try:
|
|
214
|
+
msg = Message(msg_data)
|
|
215
|
+
|
|
216
|
+
for handler_info in self._message_handlers:
|
|
217
|
+
command = handler_info['command']
|
|
218
|
+
handler = handler_info['handler']
|
|
219
|
+
|
|
220
|
+
if command is None or msg.text.lower().startswith(command):
|
|
221
|
+
if asyncio.iscoroutinefunction(handler):
|
|
222
|
+
await handler(msg)
|
|
223
|
+
else:
|
|
224
|
+
handler(msg)
|
|
225
|
+
|
|
226
|
+
except Exception as e:
|
|
227
|
+
print(f"Message processing error: {e}")
|
|
228
|
+
|
|
229
|
+
async def _trigger_ready_handlers(self):
|
|
230
|
+
"""Trigger all ready handlers"""
|
|
231
|
+
for handler in self._ready_handlers:
|
|
232
|
+
try:
|
|
233
|
+
if asyncio.iscoroutinefunction(handler):
|
|
234
|
+
await handler(self)
|
|
235
|
+
else:
|
|
236
|
+
handler(self)
|
|
237
|
+
except Exception as e:
|
|
238
|
+
print(f"Ready handler error: {e}")
|
|
239
|
+
|
|
240
|
+
async def send_dm(self, message: str, user_id: int):
|
|
241
|
+
"""
|
|
242
|
+
Send a direct message to a user.
|
|
243
|
+
|
|
244
|
+
Args:
|
|
245
|
+
message: Message text
|
|
246
|
+
user_id: Target user ID
|
|
247
|
+
"""
|
|
248
|
+
|
|
249
|
+
if not self._chat_conn:
|
|
250
|
+
print("Chat connection not correctly started")
|
|
251
|
+
return
|
|
252
|
+
|
|
253
|
+
await self._chat_conn.send_dm(message, user_id, self._key, self._iv)
|
|
254
|
+
|
|
255
|
+
async def send_squad(self, message: str, chat_id: int):
|
|
256
|
+
"""
|
|
257
|
+
Send a message to squad chat.
|
|
258
|
+
|
|
259
|
+
Args:
|
|
260
|
+
message: Message text
|
|
261
|
+
chat_id: Squad chat ID
|
|
262
|
+
"""
|
|
263
|
+
if not self._chat_conn:
|
|
264
|
+
print("Chat connection not established")
|
|
265
|
+
return
|
|
266
|
+
|
|
267
|
+
await self._chat_conn.send_squad(message, chat_id, self._key, self._iv)
|
|
268
|
+
|
|
269
|
+
async def send_guild(self, message: str, chat_id: int):
|
|
270
|
+
"""
|
|
271
|
+
Send a message to guild chat.
|
|
272
|
+
|
|
273
|
+
Args:
|
|
274
|
+
message: Message text
|
|
275
|
+
chat_id: Guild chat ID
|
|
276
|
+
"""
|
|
277
|
+
if not self._chat_conn:
|
|
278
|
+
print("Chat connection not established")
|
|
279
|
+
return
|
|
280
|
+
|
|
281
|
+
await self._chat_conn.send_guild(message, chat_id, self._key, self._iv)
|
|
282
|
+
|
|
283
|
+
async def online_loop(self):
|
|
284
|
+
"""
|
|
285
|
+
Main event loop - keeps the bot online and processing messages.
|
|
286
|
+
This is a blocking call that runs until interrupted.
|
|
287
|
+
|
|
288
|
+
Example:
|
|
289
|
+
await bot.online_loop()
|
|
290
|
+
"""
|
|
291
|
+
try:
|
|
292
|
+
online_task = asyncio.create_task(
|
|
293
|
+
self._online_conn.connect(self._auth_packet)
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
chat_task = asyncio.create_task(
|
|
297
|
+
self._chat_conn.connect(self._auth_packet)
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
await asyncio.sleep(1)
|
|
301
|
+
|
|
302
|
+
self.online_connected = self._online_conn.is_connected
|
|
303
|
+
self.chat_connected = self._chat_conn.is_connected
|
|
304
|
+
|
|
305
|
+
await self._trigger_ready_handlers()
|
|
306
|
+
|
|
307
|
+
await asyncio.gather(online_task, chat_task)
|
|
308
|
+
|
|
309
|
+
except KeyboardInterrupt:
|
|
310
|
+
print("\nBot stopped by user")
|
|
311
|
+
except Exception as e:
|
|
312
|
+
print(f"Event loop error: {e}")
|
|
313
|
+
finally:
|
|
314
|
+
await self.disconnect()
|
|
315
|
+
|
|
316
|
+
async def disconnect(self):
|
|
317
|
+
"""Disconnect all connections"""
|
|
318
|
+
if self._online_conn:
|
|
319
|
+
await self._online_conn.close()
|
|
320
|
+
await asyncio.sleep(0.5)
|
|
321
|
+
if self._chat_conn:
|
|
322
|
+
await self._chat_conn.close()
|
|
323
|
+
|
|
324
|
+
self.is_connected = False
|
|
325
|
+
self.chat_connected = False
|
|
326
|
+
self.online_connected = False
|
redzedbot/config.py
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Configuration module - Auto-updates from Google Play Store
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import aiohttp
|
|
7
|
+
import json
|
|
8
|
+
from google_play_scraper import app
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Config:
|
|
12
|
+
"""Configuration settings - Auto-updated on first use"""
|
|
13
|
+
|
|
14
|
+
LOGIN_URL = None
|
|
15
|
+
OB = None
|
|
16
|
+
VERSION = None
|
|
17
|
+
HOST = None
|
|
18
|
+
|
|
19
|
+
AES_KEY = b'Yg&tc%DEuh6%Zc^8'
|
|
20
|
+
AES_IV = b'6oyZDr22E3ychjM%'
|
|
21
|
+
|
|
22
|
+
_updated = False
|
|
23
|
+
|
|
24
|
+
@classmethod
|
|
25
|
+
async def auto_update(cls):
|
|
26
|
+
"""Auto-update configuration from game version"""
|
|
27
|
+
if cls._updated:
|
|
28
|
+
return True
|
|
29
|
+
|
|
30
|
+
try:
|
|
31
|
+
result = app('com.dts.freefireth', lang="fr", country='fr')
|
|
32
|
+
version = result['version']
|
|
33
|
+
|
|
34
|
+
url = f'https://bdversion.ggbluefox.com/live/ver.php?version={version}&lang=ar&device=android&channel=android&appstore=googleplay®ion=ME&whitelist_version=1.3.0&whitelist_sp_version=1.0.0&device_name=google%20G011A&device_CPU=ARMv7%20VFPv3%20NEON%20VMH&device_GPU=Adreno%20(TM)%20640&device_mem=1993'
|
|
35
|
+
|
|
36
|
+
async with aiohttp.ClientSession() as session:
|
|
37
|
+
async with session.get(url) as res:
|
|
38
|
+
text = await res.text()
|
|
39
|
+
data = json.loads(text)
|
|
40
|
+
|
|
41
|
+
cls.LOGIN_URL = data['server_url']
|
|
42
|
+
cls.OB = data['latest_release_version']
|
|
43
|
+
cls.VERSION = version
|
|
44
|
+
cls.HOST = data['server_url'].split('//')[1].split('/')[0]
|
|
45
|
+
|
|
46
|
+
cls._updated = True
|
|
47
|
+
return True
|
|
48
|
+
|
|
49
|
+
except Exception as e:
|
|
50
|
+
print(f"⚠️ Auto-update failed: {e}")
|
|
51
|
+
print("Using fallback configuration...")
|
|
52
|
+
|
|
53
|
+
return False
|