1
1
import asyncio
2
2
import typing
3
- import time
3
+ import sys
4
4
import json
5
+ import time
5
6
import urllib .request as request
6
7
import urllib .parse as parse
7
8
import urllib .response as response
11
12
from concurrent .futures import ThreadPoolExecutor
12
13
from dataclasses import dataclass
13
14
15
+ from win_comms import ToastManager
14
16
from data import DataManager
15
- from utils import SyncInAsync
17
+ from utils import SyncInAsync , parse_query_params
16
18
from log_comms import Log
17
19
18
20
Success : typing .TypeAlias = bool
19
21
20
22
TRADE_URL = "https://trades.roblox.com/v1/trades/inbound?cursor=&limit=25&sortOrder=Desc"
23
+ TRADE_INFO_URL = f"https://trades.roblox.com/v1/trades/$id$"
24
+
21
25
XCSRF_URL = "https://auth.roblox.com/v2/logout"
26
+ VALUE_LIST_URL = "https://api.rolimons.com/items/v1/itemdetails"
27
+
22
28
XCSRF_REFRESH_TIME = 25 * 60
29
+ ROLIVALUES_REFRESH_TIME = 1.5 * 60
23
30
24
31
@dataclass
25
32
class InboundData :
26
33
username : str
27
34
user_id : int
28
35
29
- offer_items : list [ int ] = None
30
- request_items : list [ int ] = None
36
+ give_value : int = None
37
+ receive_value : int = None
31
38
32
- offer_robux : int = None
33
- request_robux : int = None
39
+ give_items : list [int ] = None
40
+ receive_items : list [int ] = None
41
+
42
+ give_robux : int = None
43
+ receive_robux : int = None
34
44
35
45
36
46
class HttpManager :
37
- def __init__ (self ) -> None :
47
+ def __init__ (self ):
38
48
self ._loop = asyncio .get_event_loop ()
39
49
self .sync_in_async = SyncInAsync (POOL = ThreadPoolExecutor ())
40
50
@@ -44,9 +54,10 @@ def __init__(self) -> None:
44
54
45
55
self ._opener = request .build_opener (request .HTTPCookieProcessor (self ._jar ))
46
56
57
+ self ._value_list = dict ()
47
58
self ._xcsrf = None # created in __init__ and not initialize_session due to cookie validation
48
59
49
- def initialize_session (self ):
60
+ def initialize_session (self ) -> None :
50
61
self ._refresh_task = self ._loop .create_task (self ._refresh_xcsrf ())
51
62
self ._cached_inbound_id_list : list [int ] = list ()
52
63
@@ -72,7 +83,7 @@ def _get_xcsrf(self) -> Success:
72
83
case _:
73
84
return False
74
85
75
- async def _refresh_xcsrf (self ):
86
+ async def _refresh_xcsrf (self ) -> None :
76
87
while True :
77
88
if not self ._xcsrf : # to avoid too many calls if a validation has been done recently.
78
89
result = await self .sync_in_async .Call (self ._get_xcsrf )
@@ -83,7 +94,80 @@ async def _refresh_xcsrf(self):
83
94
await asyncio .sleep (XCSRF_REFRESH_TIME )
84
95
self ._xcsrf = None
85
96
86
- def _get_inbound_trades (self ):
97
+ def _get_value_list (self ) -> Success :
98
+ request_struct = request .Request (url = VALUE_LIST_URL , method = "GET" )
99
+
100
+ try :
101
+ resp = Log .wrap_http (self ._opener .open , request_struct = request_struct , success_codes = [])
102
+ except error .HTTPError as resp :
103
+ match resp .code :
104
+ case _:
105
+ return False
106
+ else :
107
+ response_content = resp .read ()
108
+ json_response = json .loads (response_content .decode ('utf-8' ))
109
+
110
+ if json_response ["success" ]:
111
+ self ._value_list = json_response ["items" ]
112
+ return True
113
+ else :
114
+ return False
115
+
116
+ async def refresh_valuelist (self ):
117
+ while True :
118
+ success = await self .sync_in_async .Call (self ._get_value_list )
119
+
120
+ await asyncio .sleep (ROLIVALUES_REFRESH_TIME )
121
+
122
+ def _items_to_value (self , trade_info : dict ) -> tuple [int , int ] | tuple [str , str ]:
123
+ if self ._value_list :
124
+ give_list = trade_info ["offers" ][0 ]["userAssets" ]
125
+ receive_list = trade_info ["offers" ][1 ]["userAssets" ]
126
+
127
+ give_value = 0
128
+ for asset_data in give_list :
129
+ asset_value = self ._value_list [str (asset_data ["assetId" ])][4 ]
130
+ give_value += asset_value
131
+
132
+ receive_value = 0
133
+ for asset_data in receive_list :
134
+ asset_value = self ._value_list [str (asset_data ["assetId" ])][4 ]
135
+ receive_value += asset_value
136
+
137
+ return (give_value , receive_value )
138
+ else :
139
+ return ("-" , "-" )
140
+
141
+ def _get_trade_info (self , id : int ) -> dict | bool :
142
+ parsed_url = parse_query_params (TRADE_INFO_URL , {"id" : id })
143
+
144
+ request_struct = request .Request (url = parsed_url , method = "GET" )
145
+ request_struct .add_header ("Cookie" , f".ROBLOSECURITY={ self ._cookie_str } " )
146
+ request_struct .add_header ("x-csrf-token" , self ._xcsrf )
147
+ request_struct .add_header ("content-type" , "application/json" )
148
+
149
+ try :
150
+ resp = Log .wrap_http (self ._opener .open , request_struct = request_struct , success_codes = [200 , 201 ])
151
+ except error .HTTPError as resp :
152
+ match resp .code :
153
+ case 403 :
154
+ self ._get_xcsrf ()
155
+
156
+ case 401 :
157
+ toast = ToastManager .spawn_toast ("Cookie has been invalided. Please restart your app." , "Cookie invalidated" , 5 )
158
+ sys .exit ()
159
+
160
+ case _:
161
+ ...
162
+
163
+ return False
164
+ else :
165
+ response_content = resp .read ()
166
+ json_response = json .loads (response_content .decode ('utf-8' ))
167
+
168
+ return json_response
169
+
170
+ def _get_inbound_trades (self ) -> dict | bool :
87
171
request_struct = request .Request (url = TRADE_URL , method = "GET" )
88
172
request_struct .add_header ("Cookie" , f".ROBLOSECURITY={ self ._cookie_str } " )
89
173
request_struct .add_header ("x-csrf-token" , self ._xcsrf )
@@ -96,6 +180,10 @@ def _get_inbound_trades(self):
96
180
case 403 :
97
181
self ._get_xcsrf ()
98
182
183
+ case 401 :
184
+ toast = ToastManager .spawn_toast ("Cookie has been invalided. Please restart your app." , "Cookie invalidated" , 5 )
185
+ sys .exit ()
186
+
99
187
case _:
100
188
...
101
189
@@ -109,32 +197,80 @@ def _get_inbound_trades(self):
109
197
async def check_inbound_trades (self ) -> list [InboundData ] | None :
110
198
inbound_data_list : list [InboundData ] = list ()
111
199
inbound_trades_list = await self .sync_in_async .Call (self ._get_inbound_trades )
200
+ if not inbound_trades_list :
201
+ return inbound_data_list
112
202
113
203
new_inbounds_list = []
114
204
115
- inbound_id_list = [inbound ["id" ] for inbound in inbound_trades_list ["data" ]]
116
- if self ._cached_inbound_id_list and any (new_inbounds := [inbound for inbound in inbound_trades_list ["data" ] if not inbound ["id" ] in self ._cached_inbound_id_list ]):
117
- new_inbounds_list = [inbound ['id' ] for inbound in new_inbounds ]
118
- Log .info (f"New inbounds detected: { new_inbounds_list } " )
119
-
120
- for inbound in new_inbounds :
121
- inbound_data = InboundData (username = inbound ["user" ]["name" ],
122
- user_id = inbound ["user" ]["id" ])
123
- inbound_data_list .append (inbound_data )
124
-
125
- self ._cached_inbound_id_list .extend (new_inbounds_list ) if self ._cached_inbound_id_list else self ._cached_inbound_id_list .extend (inbound_id_list )
205
+ # inbound_id_list = [inbound["id"] for inbound in inbound_trades_list["data"]]
206
+ # if self._cached_inbound_id_list and any(new_inbounds := [inbound for inbound in inbound_trades_list["data"] if not inbound["id"] in self._cached_inbound_id_list]):
207
+ # new_inbounds_list = [inbound['id'] for inbound in new_inbounds]
208
+
209
+ # Log.info(f"New inbounds detected: {new_inbounds_list}")
210
+
211
+ # for inbound in new_inbounds:
212
+ # inbound_trade_info = await self.sync_in_async.Call(self._get_trade_info, id=inbound["id"])
213
+ # if inbound_trade_info:
214
+ # give_value, receive_value = self._items_to_value(trade_info=inbound_trade_info)
215
+
216
+ # inbound_data = InboundData(
217
+ # username=inbound["user"]["name"],
218
+ # user_id=inbound["user"]["id"],
219
+
220
+ # give_items=inbound_trade_info["offers"][0]["userAssets"],
221
+ # give_value=give_value,
222
+ # give_robux=inbound_trade_info["offers"][0]["robux"],
223
+
224
+ # receive_items=inbound_trade_info["offers"][1]["userAssets"],
225
+ # receive_value=receive_value,
226
+ # receive_robux=inbound_trade_info["offers"][1]["robux"],
227
+ # )
228
+ # else:
229
+ # inbound_data = InboundData(
230
+ # username=inbound["user"]["name"],
231
+ # user_id=inbound["user"]["id"],
232
+
233
+ # give_items=[],
234
+ # give_value="-",
235
+ # give_robux="-",
236
+
237
+ # receive_items=[],
238
+ # receive_value="-",
239
+ # receive_robux="-",
240
+ # )
241
+ # inbound_data_list.append(inbound_data)
242
+
243
+ # self._cached_inbound_id_list.extend(new_inbounds_list) if self._cached_inbound_id_list else self._cached_inbound_id_list.extend(inbound_id_list)
126
244
127
245
## for testing purposes
128
- # for inbound in inbound_trades_list["data"]:
129
- # inbound_data = InboundData(username=inbound["user"]["name"],
130
- # user_id=inbound["user"]["id"])
131
- # inbound_data_list.append(inbound_data)
246
+ for inbound in inbound_trades_list ["data" ][:1 ]:
247
+ inbound_trade_info = await self .sync_in_async .Call (self ._get_trade_info , id = inbound ["id" ])
248
+ if inbound_trade_info :
249
+ give_value , receive_value = self ._items_to_value (trade_info = inbound_trade_info )
250
+
251
+ inbound_data = InboundData (
252
+ username = inbound ["user" ]["name" ],
253
+ user_id = inbound ["user" ]["id" ],
254
+
255
+ give_items = inbound_trade_info ["offers" ][0 ]["userAssets" ],
256
+ give_value = give_value ,
257
+ give_robux = inbound_trade_info ["offers" ][0 ]["robux" ],
258
+
259
+ receive_items = inbound_trade_info ["offers" ][1 ]["userAssets" ],
260
+ receive_value = receive_value ,
261
+ receive_robux = inbound_trade_info ["offers" ][1 ]["robux" ],
262
+ )
263
+ inbound_data_list .append (inbound_data )
132
264
133
265
return inbound_data_list
134
266
135
- def quit_session (self ):
267
+ def quit_session (self ) -> None :
136
268
self ._opener .close ()
137
269
self ._refresh_task .cancel ()
138
270
139
- def validate_cookie (self ):
271
+ def validate_cookie (self ) -> bool :
140
272
return self ._get_xcsrf ()
273
+
274
+ @property
275
+ def value_list (self ):
276
+ return self ._value_list
0 commit comments