Skip to content

Commit b8fb356

Browse files
authored
Simplified plain text version, issue #9
1 parent f161951 commit b8fb356

1 file changed

Lines changed: 304 additions & 0 deletions

File tree

sracli.py

Lines changed: 304 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,304 @@
1+
import time
2+
import argparse
3+
import configparser
4+
import requests
5+
import os
6+
from datetime import datetime, timezone
7+
from pypresence import Presence
8+
from pypresence.types import ActivityType
9+
from pypresence.types import StatusDisplayType
10+
11+
appid = "1478833716725022866"
12+
13+
#trimming the rich presence message for discord in case it is too long. Temporary for now, till I find a better solution.
14+
def trimmer(text):
15+
if len(text.encode("utf_16_le")) <= 256:
16+
return text
17+
else:
18+
final = ""
19+
size = 0
20+
for i in text:
21+
size += len(i.encode("utf_16_le"))
22+
if size > 250:
23+
return final + "..."
24+
final += i
25+
26+
def get_no_cache_string():
27+
"""Generate datetime string in format ddMMyyyyHHmmss for cache busting"""
28+
now = datetime.now()
29+
return now.strftime('%d%m%Y%H%M%S')
30+
31+
def ra_data(url):
32+
try:
33+
response = requests.get(url)
34+
if response.status_code == 200:
35+
return response.json()
36+
else:
37+
return None
38+
except requests.exceptions.ConnectionError:
39+
return None
40+
41+
def clear_term():
42+
os.system('cls' if os.name == 'nt' else 'clear')
43+
44+
def switch(a):
45+
if a == "0":
46+
return "1"
47+
else:
48+
return "0"
49+
50+
def settings():
51+
config = configparser.ConfigParser()
52+
config.read('config.ini')
53+
username = config.get('RA', 'username')
54+
apikey = config.get('RA', 'apikey')
55+
bt_prof = config.get('BT', 'profile')
56+
bt_gpage = config.get('BT', 'gamepage')
57+
interval = config.get('MISC', 'interval')
58+
timeout = config.get('MISC', 'timeout')
59+
60+
while True:
61+
clear_term()
62+
print(f"1. Username: {username}")
63+
print(f"2. API Key: {apikey}")
64+
print(f"3. Update interval: {interval} seconds")
65+
print(f"4. Timeout: {timeout}{" seconds" if int(timeout) > 0 else " (disabled)"}")
66+
print(f"5. Buttons")
67+
print("")
68+
print("6. Save & Exit")
69+
print("7. Exit without saving")
70+
print("")
71+
choice = input("> ")
72+
print("")
73+
if choice == "1":
74+
clear_term()
75+
username = input("Enter your RA Username: ")
76+
elif choice == "2":
77+
clear_term()
78+
apikey = input("Enter your RA Web API Key: ")
79+
elif choice == "3":
80+
clear_term()
81+
interval = input('Set the update interval in seconds (min. 5): ')
82+
try:
83+
interval = int(interval)
84+
if interval < 5:
85+
interval = 5
86+
except ValueError:
87+
interval = 5
88+
elif choice == "4":
89+
clear_term()
90+
print('After the Rich Presence message date is older than the specified number of seconds, the script stops updating Discord rich presence.')
91+
print('For example, if the timeout is set to 300 (5 minutes), the script will stop updating Discord when the Rich Presence message is older than 5 minutes.')
92+
print('Enter 0 to disable this feature.')
93+
print('The minimum value is 130 because things on RA seem to get updated every 2 minutes after you launch into a game.')
94+
timeout = input('Set the timeout value in seconds (0 to disable): ')
95+
try:
96+
t = int(timeout)
97+
if 0 < t < 130:
98+
timeout = "130"
99+
except ValueError:
100+
timeout = "0"
101+
102+
elif choice == "5":
103+
while True:
104+
clear_term()
105+
print("Here you can enable or disable different buttons on your profile.")
106+
print("One leads to your RA profile page, the other to the page of the game you currently playing.")
107+
print("Note: You can't see them on your own profile, but others will do so don't panic.")
108+
print("")
109+
print(f"1. Current game button: {"Enabled" if bt_gpage == "1" else "Disabled"}")
110+
print(f"2. RA profile button: {"Enabled" if bt_prof == "1" else "Disabled"}")
111+
print("3. Back")
112+
print("")
113+
choice = input("> ")
114+
print("")
115+
if choice == "1":
116+
bt_gpage = switch(bt_gpage)
117+
elif choice == "2":
118+
bt_prof = switch(bt_prof)
119+
elif choice == "3":
120+
break
121+
122+
elif choice == "6":
123+
config['RA']['username'] = str(username)
124+
config['RA']['apikey'] = str(apikey)
125+
config['BT']['profile'] = str(bt_prof)
126+
config['BT']['gamepage'] = str(bt_gpage)
127+
config['MISC']['interval'] = str(interval)
128+
config['MISC']['timeout'] = str(timeout)
129+
with open('config.ini', 'w') as configfile:
130+
config.write(configfile)
131+
clear_term()
132+
break
133+
elif choice == "7":
134+
clear_term()
135+
break
136+
137+
def main():
138+
#loading config values
139+
config = configparser.ConfigParser()
140+
config.read('config.ini')
141+
data = configparser.ConfigParser()
142+
data.read('data.ini')
143+
username = config.get('RA', 'username')
144+
apikey = config.get('RA', 'apikey')
145+
bt_prof = config.get('BT', 'profile')
146+
bt_game = config.get('BT', 'gamepage')
147+
try:
148+
interval = int(config.get('MISC', 'interval'))
149+
if interval < 5:
150+
interval = 5
151+
except ValueError:
152+
interval = 5
153+
try:
154+
timeout = int(config.get('MISC', 'timeout'))
155+
if 0 < timeout < 130:
156+
timeout = 130
157+
except ValueError:
158+
timeout = 0
159+
160+
current_game = None
161+
RPC = Presence(appid)
162+
rpc_connected = False
163+
rpc_status = 0
164+
rpc_st_text = ["Inactive", "Active", "", "Error!"]
165+
login = True
166+
167+
start_time = None
168+
achi_earned = ""
169+
achi_text = ""
170+
171+
log_points = None
172+
log_game_title = None
173+
log_num_achi = None
174+
log_rpc_status = None
175+
log_rpc_text = None
176+
177+
while True:
178+
#fetching data from RA
179+
no_cache = get_no_cache_string()
180+
ra_userdata = ra_data(f"https://retroachievements.org/API/API_GetUserSummary.php?u={username}&y={apikey}&g=0&a=0&noCache={no_cache}")
181+
if ra_userdata == None:
182+
rpc_status = 3
183+
else:
184+
if login:
185+
login = False
186+
print(f"Logged in as: {username}")
187+
if ra_userdata['Motto'] != "":
188+
print(f"{ra_userdata['Motto']}")
189+
190+
rp_date_str = ra_userdata.get("RichPresenceMsgDate")
191+
rp_date = datetime.strptime(rp_date_str, "%Y-%m-%d %H:%M:%S").replace(tzinfo=timezone.utc)
192+
current_date = datetime.now(timezone.utc)
193+
time_diff = (current_date - rp_date).total_seconds()
194+
if time_diff <= timeout or timeout == 0:
195+
rpc_status = 1
196+
else:
197+
rpc_status = 0
198+
199+
if rpc_status not in [0, 3]:
200+
ra_game_data = ra_data(f"https://retroachievements.org/API/API_GetGame.php?z={username}&y={apikey}&i={ra_userdata['LastGameID']}")
201+
if ra_game_data == None:
202+
rpc_status = 3
203+
ra_game_prog = ra_data(f"https://retroachievements.org/API/API_GetUserProgress.php?y={apikey}&u={username}&i={ra_userdata['LastGameID']}")
204+
if ra_game_prog == None:
205+
rpc_status = 3
206+
else:
207+
ra_game_prog = ra_game_prog.get(f"{ra_userdata['LastGameID']}", {})
208+
209+
if rpc_status not in [0, 3]:
210+
if current_game != ra_userdata['LastGameID']:
211+
if bt_game == '1':
212+
button1 = {"label": "View on RetroAchievements",
213+
"url": f"https://retroachievements.org/game/{ra_userdata['LastGameID']}"}
214+
else:
215+
button1 = None
216+
if bt_prof == '1':
217+
button2 = {"label": f"{username}'s RA Page",
218+
"url": f"https://retroachievements.org/user/{username}"}
219+
else:
220+
button2 = None
221+
buttons = [button1, button2]
222+
if button1 == None and button2 == None:
223+
buttons_filtered = None
224+
else:
225+
buttons_filtered = [d for d in buttons if d is not None]
226+
current_game = ra_userdata['LastGameID']
227+
228+
229+
if ra_game_prog['NumAchieved'] == 0:
230+
achi_earned = 0
231+
achi_text = "None"
232+
rpc_achi = f"\uD83C\uDFC6 0/{ra_game_prog['NumPossibleAchievements']} (None)"
233+
elif ra_game_prog['NumAchievedHardcore'] < ra_game_prog['NumAchieved']:
234+
achi_earned = ra_game_prog['NumAchieved']
235+
achi_text = "Softcore"
236+
rpc_achi = f"\uD83C\uDFC6 {ra_game_prog['NumAchieved']}/{ra_game_prog['NumPossibleAchievements']} (Softcore)"
237+
else:
238+
achi_earned = ra_game_prog['NumAchievedHardcore']
239+
achi_text = "Hardcore"
240+
rpc_achi = f"\uD83C\uDFC6 {ra_game_prog['NumAchievedHardcore']}/{ra_game_prog['NumPossibleAchievements']} (Hardcore)"
241+
242+
243+
if rpc_status == 1:
244+
if not rpc_connected:
245+
RPC.connect()
246+
rpc_connected = True
247+
start_time = int(time.time())
248+
RPC.update(
249+
activity_type=ActivityType.PLAYING,
250+
status_display_type=StatusDisplayType.NAME,
251+
name=ra_game_data['GameTitle'],
252+
details=trimmer(ra_userdata["RichPresenceMsg"]),
253+
state=rpc_achi,
254+
start=start_time,
255+
large_image=f"https://media.retroachievements.org{ra_game_data['ImageIcon']}",
256+
large_text=rpc_achi,
257+
small_image=data.get('CI', str(ra_game_data['ConsoleID'])),
258+
small_text=ra_game_data['ConsoleName'],
259+
buttons=buttons_filtered
260+
)
261+
elif rpc_status in [0, 3]:
262+
if rpc_connected:
263+
RPC.clear()
264+
RPC.close()
265+
rpc_connected = False
266+
267+
if rpc_status == 1:
268+
if log_points != ra_userdata['TotalPoints']:
269+
log_points = ra_userdata['TotalPoints']
270+
print(f"Total points: {ra_userdata['TotalPoints']} ({ra_userdata['TotalTruePoints']})")
271+
272+
if log_game_title != current_game:
273+
log_game_title = current_game
274+
print(f"Playing: {ra_game_data['GameTitle']}")
275+
print(f"Console: {ra_game_data['ConsoleName']}")
276+
log_num_achi = achi_earned
277+
print(f"Achivements: {achi_earned}/{ra_game_prog['NumPossibleAchievements']} ({achi_text})")
278+
279+
if log_num_achi != achi_earned:
280+
log_num_achi = achi_earned
281+
print(f"Achivements: {achi_earned}/{ra_game_prog['NumPossibleAchievements']} ({achi_text})")
282+
283+
if log_rpc_text != ra_userdata['RichPresenceMsg']:
284+
log_rpc_text = ra_userdata['RichPresenceMsg']
285+
print(f"Details: {ra_userdata['RichPresenceMsg']}")
286+
287+
if log_rpc_status != rpc_status:
288+
log_rpc_status = rpc_status
289+
print(f"RPC Status: {rpc_st_text[rpc_status]}")
290+
291+
for i in range(interval):
292+
time.sleep(1)
293+
294+
print("Something went wrong! Check your settings (python racli.py -s) and make sure your details are correct.")
295+
296+
if __name__ == "__main__":
297+
298+
parser = argparse.ArgumentParser()
299+
parser.add_argument('-s', '--settings', action='store_true')
300+
args = parser.parse_args()
301+
if args.settings:
302+
settings()
303+
else:
304+
main()

0 commit comments

Comments
 (0)