44import customtkinter as ctk
55import os
66import configparser
7+ from datetime import datetime , timezone
78
89
10+ # Path for config.ini
911roaming_path = os .path .join (os .getenv ('APPDATA' ), "VATSIM-Discord-RPC" )
12+ # If path does not exist, then creates it
1013if not os .path .exists (roaming_path ):
1114 os .makedirs (roaming_path )
1215
16+ # Stores path for config file
1317ini_file_path = os .path .join (roaming_path , 'config.ini' )
1418
19+
20+ # If the config file does not exist, creates it with needed info
1521if not os .path .exists (ini_file_path ):
1622 with open (ini_file_path , 'w' ) as ini_file :
1723 ini_file .write ('[Settings]\n ' )
1824 ini_file .write ('cid=\n ' )
1925
26+ # Config parser for reading and writing
2027config = configparser .ConfigParser ()
2128config .read (ini_file_path )
2229
30+ # API for Vatsim data
2331vatsim_api = "https://data.vatsim.net/v3/vatsim-data.json"
2432
33+ '''Returns pilot data for given CID'''
2534def get_pilot_info (user_cid ):
2635 response = requests .get (vatsim_api )
2736 if response .status_code == 200 :
@@ -32,48 +41,71 @@ def get_pilot_info(user_cid):
3241 return pilot
3342 return None
3443
44+
45+ '''Returns parsed flight data for given CID'''
3546def get_data (user_cid ):
3647 try :
3748 pilot = get_pilot_info (user_cid )
49+ print (pilot )
3850 call_sign = pilot .get ("callsign" )
39- flight_plan = pilot .get ("flight_plan" , {})
40- departure = flight_plan .get ("departure" )
41- arrival = flight_plan .get ("arrival" )
4251 altitude = pilot .get ("altitude" )
4352 hdg = pilot .get ("heading" )
44- logon_time = pilot .get ("logon_time" )
4553
46- flight_rule_letter = flight_plan .get ("flight_rules" )
47- if (flight_rule_letter == 'I' ):
48- flight_rule = "IFR"
49- elif (flight_rule_letter == 'V' ):
50- flight_rule = "VFR"
54+ flight_plan = pilot .get ("flight_plan" , {})
55+
56+ # Gets data from flight plan if it exists
57+ if flight_plan != None :
58+ departure = flight_plan .get ("departure" )
59+ arrival = flight_plan .get ("arrival" )
60+ flight_rule_letter = flight_plan .get ("flight_rules" )
61+
62+ if (flight_rule_letter == 'I' ):
63+ flight_rule = "IFR"
64+ elif (flight_rule_letter == 'V' ):
65+ flight_rule = "VFR"
66+ else :
67+ flight_rule = None
68+
69+ aircraft_type = flight_plan .get ("aircraft_short" )
70+ # If no flight plan is filed, sets everything to None
5171 else :
52- flight_rule = None
72+ departure = arrival = flight_rule = aircraft_type = None
5373
54- aircraft_type = flight_plan .get ("aircraft_short" )
74+ # How long user has been logged into network for
75+ logon_time = (pilot .get ("logon_time" ))
76+
77+ # Thank you ChatGPT for this line. Line saver
78+ dt = datetime .fromisoformat (logon_time .rstrip ('Z' )).replace (tzinfo = timezone .utc )
5579
80+ # Get epoch time
81+ epoch_time = int (dt .timestamp ())
82+
83+ # If sucessfully found user on network, return parsed data
5684 if (pilot != None ):
57- return (call_sign , departure , arrival , altitude , flight_rule , aircraft_type , hdg )
85+ return (call_sign , departure , arrival , altitude , flight_rule , aircraft_type , hdg , epoch_time )
86+ # If no user was found return None
5887 else :
5988 return None
6089 except :
6190 return None
6291
6392
6493
94+ # Discord dev client id
6595client_id = "1344534564244160662"
6696RPC = Presence (client_id )
6797
68-
98+ # UI setup
6999root = ctk .CTk ()
70100root .title ("VATSIM Discord Rich Presence" )
71101root .geometry ("450x200" )
72102
103+ # cid and checkbox values
73104cid_var = tk .StringVar ()
74105checkbox_var = ctk .BooleanVar (value = False )
75106cid = 0
76107
108+ '''Runs after clicking submit. Obtains and updates RPC data and .ini if needed'''
77109def submit ():
78110 global cid
79111 try :
@@ -88,6 +120,7 @@ def submit():
88120 status_label .configure (text = "Invalid CID!" )
89121
90122
123+ # Checkbox for making cid default
91124checkbox = ctk .CTkCheckBox (
92125 root ,
93126 text = "Make default" ,
@@ -97,68 +130,94 @@ def submit():
97130)
98131
99132
133+ # CID entry area
100134cid_label = ctk .CTkLabel (root , text = 'CID' , font = ('arial ' ,10 , 'bold' ))
101-
102135cid_entry = ctk .CTkEntry (root ,textvariable = cid_var , font = ('arial ' ,10 ,'normal' ))
136+
137+ # Submit button
103138sub_btn = ctk .CTkButton (root ,text = 'Submit' , command = submit )
104139
140+ # Adds elements to window
105141cid_label .pack (anchor = "center" )
106142cid_entry .pack (anchor = "center" )
107143checkbox .pack (pady = 20 )
108144
109145sub_btn .pack (anchor = "center" )
110146
147+ # Default text when RPC is not connected
111148status_label = ctk .CTkLabel (root , text = "Please open discord" )
112149status_label .pack (anchor = 'center' )
113150
114151
115152def connect_to_discord ():
153+ # Trys to connect to discord, and catches it if fails (such as discord not being opened)
116154 try :
117- status_label .configure (text = "RPC connected. Waiting for CID..." )
118155 RPC .connect ()
156+ status_label .configure (text = "RPC connected. Waiting for CID..." )
157+ # Sets cid from config.ini if it exists
119158 if config .get ("Settings" , "cid" ) != "" and config .get ("Settings" , "cid" ) != None :
120159 global cid
121160 cid = int (config .get ("Settings" , "cid" ))
122161 cid_entry .insert (0 ,str (cid ))
162+ # Updates based on default cid if it is entered
123163 update_presence ()
164+ # Runs if RPC wrapper can't make connection with discord
124165 except :
125166 status_label .configure (text = "Please open discord" )
167+ # Checks connection again after 5 seconds
126168 root .after (5000 , connect_to_discord )
127169
128170
171+ '''Updates UI and discord RPC to display flight details'''
129172def update_presence ():
173+ # Gets data for current cid
130174 data = get_data (cid )
175+
176+ # Makes sure pilot is online and exists
131177 if (data != None ):
132178 formated_string = ""
179+
180+ # Sets depature airport
133181 if data [1 ]:
134182 formated_string += f"{ data [1 ]} "
183+ # Sets arrival airport
135184 if data [2 ]:
136185 formated_string += f"➜{ data [2 ]} | "
137186 # If no arrival airport, but has depature
138187 elif data [1 ]:
139188 formated_string += f" | "
189+ # Sets callsign
140190 if data [0 ]:
141191 formated_string += f"Callsign: { data [0 ]} | "
192+ # Sets flight rules
142193 if data [4 ]:
143194 formated_string += f"{ data [4 ]} | "
195+ # Sets altitude
144196 if data [3 ]:
145197 formated_string += f"Alt: { data [3 ]} ft | "
198+ # Sets heading
146199 if data [6 ]:
147200 formated_string += f"Hdg: { data [6 ]} ° | "
201+ # Sets aircraft type
148202 if data [5 ]:
149203 formated_string += f"{ data [5 ]} "
150-
204+
205+
206+ # Updates UI to display what is displayed on discord
151207 status_label .configure (text = formated_string )
152- RPC .update (pid = 1 , details = formated_string )
208+ RPC .update (pid = 1 , details = formated_string , start = data [7 ])
209+ # If there is no pilot data, means CID is wrong or user is offline
153210 else :
154- status_label .configure (text = "ID is offline" )
211+ status_label .configure (text = "User is offline or invalid" )
212+
213+ # Clears RPC
155214 RPC .clear (1 )
156-
215+
216+ # Reupdates every 15 seconds
157217 root .after (15000 , update_presence )
158218
159-
160-
161-
219+ # RPC connection
162220connect_to_discord ()
221+ # Main loop for UI
163222root .mainloop ()
164223
0 commit comments