Skip to content

Commit adb8cc6

Browse files
Merge pull request #2 from PogoDigitalism/pre_stable_1
Pre stable 1
2 parents 6c91384 + f642f2d commit adb8cc6

File tree

7 files changed

+215
-39
lines changed

7 files changed

+215
-39
lines changed

src/handler.py

+14-6
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,18 @@
88
from data import DataManager
99
from utils import SyncInAsync
1010
from win_comms import ToastManager
11-
from http_comms import HttpManager
11+
from http_comms import HttpManager, InboundData
1212
from threading import Thread
1313

1414
INBOUND_COOLDOWN = 5
15-
TOAST_DISPLAY_TIME = 6
15+
TOAST_DISPLAY_TIME = 5
1616

1717
class Handler:
1818
def __init__(self) -> None:
1919
Log.warning("------------- BOOT -------------")
2020

2121

22-
self._toast_queue = list()
22+
self._toast_queue: list[InboundData] = list()
2323
# DATA INITIALIZATION
2424
self.data_manager = DataManager()
2525

@@ -62,8 +62,12 @@ async def _handle_toast(self):
6262

6363
while True:
6464
if self._toast_queue:
65-
inbound = self._toast_queue[0]
66-
self.toast_manager.create_toast(message=f"{inbound.username} | Trade inbound", title=f"{inbound.username} | Trade inbound")
65+
inbound = self._toast_queue[0]
66+
67+
message = f"you: {inbound.give_value} value & {inbound.give_robux} Robux\n{inbound.username}: {inbound.receive_value} value & {inbound.receive_robux} Robux"
68+
title = f"{inbound.username} | Trade Inbound"
69+
70+
self.toast_manager.create_toast(message=message, title=title)
6771
del self._toast_queue[0]
6872

6973
await asyncio.sleep(TOAST_DISPLAY_TIME)
@@ -72,7 +76,11 @@ async def _handle_toast(self):
7276
await asyncio.sleep(0.01) # have to add this to not block the event loop
7377

7478
async def check_inbounds(self):
75-
self._loop.create_task(self._handle_toast())
79+
self._loop.create_task(self.http_manager.refresh_valuelist()) # initialize Rolimons valuelist refresher
80+
self._loop.create_task(self._handle_toast()) # initialize Toast (queue) handler
81+
82+
await asyncio.sleep(2) # add a start-up little delay for the tasks.
83+
7684
while True:
7785
inbound_data = await self.http_manager.check_inbound_trades()
7886
self._toast_queue.extend(inbound_data)

src/http_comms/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
from .http_manager import *
22

3-
__all__ = ('HttpManager')
3+
__all__ = ('HttpManager','InboundData')

src/http_comms/http_manager.py

+163-27
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import asyncio
22
import typing
3-
import time
3+
import sys
44
import json
5+
import time
56
import urllib.request as request
67
import urllib.parse as parse
78
import urllib.response as response
@@ -11,30 +12,39 @@
1112
from concurrent.futures import ThreadPoolExecutor
1213
from dataclasses import dataclass
1314

15+
from win_comms import ToastManager
1416
from data import DataManager
15-
from utils import SyncInAsync
17+
from utils import SyncInAsync, parse_query_params
1618
from log_comms import Log
1719

1820
Success: typing.TypeAlias = bool
1921

2022
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+
2125
XCSRF_URL = "https://auth.roblox.com/v2/logout"
26+
VALUE_LIST_URL = "https://api.rolimons.com/items/v1/itemdetails"
27+
2228
XCSRF_REFRESH_TIME = 25*60
29+
ROLIVALUES_REFRESH_TIME = 1.5*60
2330

2431
@dataclass
2532
class InboundData:
2633
username: str
2734
user_id: int
2835

29-
offer_items: list[int] = None
30-
request_items: list[int] = None
36+
give_value: int = None
37+
receive_value: int = None
3138

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
3444

3545

3646
class HttpManager:
37-
def __init__(self) -> None:
47+
def __init__(self):
3848
self._loop = asyncio.get_event_loop()
3949
self.sync_in_async = SyncInAsync(POOL=ThreadPoolExecutor())
4050

@@ -44,9 +54,10 @@ def __init__(self) -> None:
4454

4555
self._opener = request.build_opener(request.HTTPCookieProcessor(self._jar))
4656

57+
self._value_list = dict()
4758
self._xcsrf = None # created in __init__ and not initialize_session due to cookie validation
4859

49-
def initialize_session(self):
60+
def initialize_session(self) -> None:
5061
self._refresh_task = self._loop.create_task(self._refresh_xcsrf())
5162
self._cached_inbound_id_list: list[int] = list()
5263

@@ -72,7 +83,7 @@ def _get_xcsrf(self) -> Success:
7283
case _:
7384
return False
7485

75-
async def _refresh_xcsrf(self):
86+
async def _refresh_xcsrf(self) -> None:
7687
while True:
7788
if not self._xcsrf: # to avoid too many calls if a validation has been done recently.
7889
result = await self.sync_in_async.Call(self._get_xcsrf)
@@ -83,7 +94,80 @@ async def _refresh_xcsrf(self):
8394
await asyncio.sleep(XCSRF_REFRESH_TIME)
8495
self._xcsrf = None
8596

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:
87171
request_struct = request.Request(url=TRADE_URL, method="GET")
88172
request_struct.add_header("Cookie", f".ROBLOSECURITY={self._cookie_str}")
89173
request_struct.add_header("x-csrf-token", self._xcsrf)
@@ -96,6 +180,10 @@ def _get_inbound_trades(self):
96180
case 403:
97181
self._get_xcsrf()
98182

183+
case 401:
184+
toast = ToastManager.spawn_toast("Cookie has been invalided. Please restart your app.", "Cookie invalidated", 5)
185+
sys.exit()
186+
99187
case _:
100188
...
101189

@@ -109,32 +197,80 @@ def _get_inbound_trades(self):
109197
async def check_inbound_trades(self) -> list[InboundData] | None:
110198
inbound_data_list: list[InboundData] = list()
111199
inbound_trades_list = await self.sync_in_async.Call(self._get_inbound_trades)
200+
if not inbound_trades_list:
201+
return inbound_data_list
112202

113203
new_inbounds_list = []
114204

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)
126244

127245
## 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)
132264

133265
return inbound_data_list
134266

135-
def quit_session(self):
267+
def quit_session(self) -> None:
136268
self._opener.close()
137269
self._refresh_task.cancel()
138270

139-
def validate_cookie(self):
271+
def validate_cookie(self) -> bool:
140272
return self._get_xcsrf()
273+
274+
@property
275+
def value_list(self):
276+
return self._value_list

src/log_comms/log.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -112,14 +112,14 @@ def wrap_http(func: typing.Callable, request_struct , success_codes: list[int],
112112
try:
113113
resp = func(request_struct, *args, **kwargs)
114114

115-
Log.info(f"Opened URL: {resp.url} with code {resp.code}")
115+
Log.info(f"Opened URL: {request_struct.get_method()} {resp.url} with code {resp.code}")
116116

117117
return resp
118118
except error.HTTPError as resp:
119119
if resp.code not in success_codes:
120-
Log.critical(f"HTTPError at {func.__name__}: [{resp.code}] | {resp.read()}")
120+
Log.critical(f"HTTPError for {request_struct.get_method()} {resp.url}: [{resp.code}] | {resp.read()}")
121121
else:
122-
Log.info(f"Opened URL: {resp.url} with code {resp.code}")
122+
Log.info(f"Opened URL: {request_struct.get_method()} {resp.url} with code {resp.code}")
123123
raise resp
124124

125125
@staticmethod

src/utils/__init__.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from .encoders import *
22
from .syncinasync import *
3+
from .url_parser import *
34

4-
__all__ = ('SyncInAsync')
5+
__all__ = ('SyncInAsync', 'parse_query_params')

src/utils/url_parser.py

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
def parse_query_params(url: str, params: dict[str, str | int | float]):
2+
for name, value in params.items():
3+
url = url.replace(f"${name}$", str(value))
4+
5+
return url

0 commit comments

Comments
 (0)