Compare commits

..

3 commits

Author SHA1 Message Date
Sakimori 288edab17c
agent registration and viewing 2025-10-15 20:23:50 -04:00
Sakimori c4fd87008c
cutes 2025-10-15 14:51:10 -04:00
Sakimori dfa122cf73
initialized ignore 2025-10-15 14:50:53 -04:00
6 changed files with 323 additions and 0 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
data/*
__pycache__/*

31
api.py Normal file
View file

@ -0,0 +1,31 @@
import requests
BASE_URL = "https://api.spacetraders.io/v2"
def handleResponse(res:requests.Response):
code = res.status_code
try:
data = res.json()
except:
data = None
return code, data
def newAgent(accToken, callsign, faction='COSMIC'):
header = {
"Authorization": f"Bearer {accToken}",
"Content-Type" : "application/json"
}
params = {
"symbol" : callsign,
"faction" : faction
}
url = f"{BASE_URL}/register"
return handleResponse(requests.post(url, json=params, headers=header))
def viewAgent(agentToken):
header = {
"Authorization": f"Bearer {agentToken}",
"Content-Type" : "application/json"
}
url = f"{BASE_URL}/my/agent"
return handleResponse(requests.get(url, headers=header))

BIN
assets/babynet.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

127
mainwindow.py Normal file
View file

@ -0,0 +1,127 @@
#builtins
import tkinter as Tk
from tkinter import ttk as Ttk
from tkinter import font
from os import path
#packages
from PIL import ImageTk, Image
#custom stuff
from storage import readSave, save
import api, ui
#load icon
icoPng = Image.open(path.join("assets","babynet.png"))
#initialize root
root = Tk.Tk()
root.title("SPASESHUPS")
ico = ImageTk.PhotoImage(icoPng)
root.wm_iconphoto(False, ico)
#add the three main sections
#header contains auth keys and such
headerFrame = ui.Container(root, freezeCols=1)
headerFrame.widget.grid(row=0, column=0, sticky='new')
#requestFrame is the main body where the request buttons will be
requestFrame = ui.Container(root)
requestFrame.widget.grid(row=1, column=0, sticky='nesw')
#response frame is where the output lives
responseFrame = ui.Container(root)
responseFrame.widget.grid(row=2, column=0, sticky="snew")
#populate the header
accToken = Tk.StringVar()
accAuthBox = ui.SavedEntryBox(headerFrame.widget, 'acc_token', accToken)
Ttk.Label(headerFrame.widget, text="ACCOUNT TOKEN:").grid(row=0, column=0, sticky='e')
accAuthBox.widget.grid(row=0, column=1, columnspan=5, sticky='ew')
agentToken = Tk.StringVar()
agentAuthBox = ui.SavedEntryBox(headerFrame.widget, 'active_agent_token', agentToken)
Ttk.Label(headerFrame.widget, text="AGENT TOKEN:").grid(row=1, column=0, sticky='e')
agentAuthBox.widget.grid(row=1, column=1, columnspan=5, sticky='ew')
#freeze the label column
headerFrame.widget.columnconfigure(0, weight=0)
#populate and style the response frame
resLabel = Tk.Text(responseFrame.widget, background='grey17', foreground='white')
resLabel.grid(row=0, column=0, sticky='news')
resLabel.insert('1.0', "Awaiting call...")
resLabel['state'] = 'disabled'
resScrollbarV = Ttk.Scrollbar(responseFrame.widget, orient='vertical', command=resLabel.yview)
resLabel.configure(yscrollcommand=resScrollbarV.set)
resScrollbarV.grid(row=0, column=1, rowspan=2, sticky='ns')
def updateResponse(newText):
resLabel['state'] = 'normal'
resLabel.delete('1.0', 'end')
resLabel.insert('1.0', newText)
resLabel['state'] = 'disabled'
Ttk.Style().configure('Shaded.TFrame', background='grey17')
responseFrame.widget.configure(borderwidth=2, relief='groove', style='Shaded.TFrame')
#populate the request frame
makeFrame = ui.RequesterFrame(requestFrame.widget, 'CREATE')
makeFrame.widget.grid(row=0, column=0, sticky='nsew')
#new agent
callsignVar = Tk.StringVar()
factionVar = Tk.StringVar()
ui.FieldLabel(makeFrame.widget, 'Callsign:', 1)
ui.EntryBox(makeFrame.widget, callsignVar).widget.grid(row=1, column=2, sticky='ew')
ui.FieldLabel(makeFrame.widget, 'Faction Name:', 2)
ui.EntryBox(makeFrame.widget, factionVar).widget.grid(row=2, column=2, sticky='ew')
def newAgent(args):
resCode, resData = api.newAgent(*args)
updateResponse(ui.formatResponse(resCode, resData))
try:
agentToken.set(resData['token'])
if int(resCode) < 300:
agentAuthBox.saveValue()
except:
pass
return resCode, resData
newAgentButton = ui.ApiButton(makeFrame.widget, 'NEW AGENT', 3, newAgent, (accToken, callsignVar, factionVar))
viewFrame = ui.RequesterFrame(requestFrame.widget, 'VIEW')
viewFrame.widget.grid(row=0, column=1, sticky='nesw')
#view agent
def viewAgent(args):
resCode, resData = api.viewAgent(*args)
updateResponse(ui.formatResponse(resCode, resData))
return resCode, resData
viewAgentButton = ui.ApiButton(viewFrame.widget, 'VIEW AGENT', 2, viewAgent, (agentToken))
useFrame = ui.RequesterFrame(requestFrame.widget, 'USE')
useFrame.widget.grid(row=0, column=2, sticky='nsew')
#finalize the layout
makeFrame.finalize()
useFrame.finalize()
viewFrame.finalize()
headerFrame.finalize()
requestFrame.finalize()
#response frame finalize doesnt help much so do it manually
responseFrame.widget.grid_rowconfigure(0, weight=1)
responseFrame.widget.grid_columnconfigure(0, weight=1)
responseFrame.widget.grid_columnconfigure(1, weight=0)
for child in root.winfo_children():
child.grid_configure(padx = 5, pady = 5)
#sort out scaling for the top 3 frames
root.columnconfigure(0, weight=1)
root.rowconfigure(0, weight=0)
root.rowconfigure(1, weight=1)
root.rowconfigure(2, weight=1, minsize=300)
#aaaand go
root.mainloop()

35
storage.py Normal file
View file

@ -0,0 +1,35 @@
from os import path
import json
SAVE_FILE = path.join("data", "save.dat")
def readSave(*fields):
try:
with open(SAVE_FILE, 'r') as f:
out = {}
obj = json.load(f)
for field in fields:
try:
out[field] = obj[field]
except KeyError:
out[field] = None
return out
except FileNotFoundError:
with open(SAVE_FILE, 'w') as f:
json.dump({'acc_token': 0}, f, indent=4)
return readSave(*fields)
def save(fieldsDic):
try:
obj = None
with open(SAVE_FILE, 'r') as f:
out = {}
obj = json.load(f)
obj.update(fieldsDic)
with open(SAVE_FILE, 'w') as f:
json.dump(obj, f, indent=4)
return True
except FileNotFoundError:
with open(SAVE_FILE, 'w') as f:
json.dump(fieldsDic, f, indent=4)
return True

128
ui.py Normal file
View file

@ -0,0 +1,128 @@
import tkinter as Tk
from tkinter import ttk as Ttk
from tkinter import font
from pprint import pformat
import threading
from storage import readSave, save
class FieldLabel():
"""
Label to set alongside a text box.
"""
def __init__(self, parent, text, rowNum):
self.widget = Ttk.Label(parent, text=text)
self.widget.grid(row=rowNum, column=1, sticky='e')
class EntryBox():
"""
Basic entry box class.
"""
def __init__(self, parent, textVar=None):
if textVar is None:
self.textVar = Tk.StringVar()
else:
self.textVar = textVar
self.widget = Ttk.Entry(parent, width=12, textvariable=self.textVar)
class SavedEntryBox(EntryBox):
"""
Entry box that saves values to file with a given field name. Reads from that field on launch.
"""
def __init__(self, parent, fieldName, textVar, saveOnEnter=True, saveOnChange=False):
self.textVar = textVar
super().__init__(parent, self.textVar)
self.fieldName = fieldName
value = readSave(fieldName)[fieldName]
if value is not None:
self.textVar.set(value)
else:
self.textVar.set(fieldName)
if saveOnEnter:
self.widget.bind("<Return>", self.saveValue)
if saveOnChange:
self.textVar.trace_add("write", self.saveValue)
def saveValue(self, *args):
save({self.fieldName: self.textVar.get()})
class Container():
def __init__(self, parent, freezeCols=0):
self.widget = Ttk.Frame(parent, padding=(3))
self.freezeCols = freezeCols
def finalize(self):
cols, rows = self.widget.grid_size()
if self.freezeCols > 0:
for i in range(self.freezeCols):
self.widget.grid_columnconfigure(i, weight=0)
for i in range(self.freezeCols, cols):
self.widget.grid_columnconfigure(i, weight=1, uniform='column')
for i in range(rows):
self.widget.grid_rowconfigure(i, weight=0)
class RequesterFrame(Container):
"""
Top-level frame for classes of manipulations.
"""
def __init__(self, parent, label):
super().__init__(parent)
self.widget.configure(height=480, width=330, borderwidth=3, relief="raised")
self.widget.bind('<Enter>', lambda e: self.widget.configure(relief="sunken"))
self.widget.bind('<Leave>', lambda e: self.widget.configure(relief="raised"))
self.label = Ttk.Label(self.widget, text=label, font=font.nametofont('TkHeadingFont'))
self.label.grid(row=0, column=0, sticky='nw')
def finalize(self):
cols, rows = self.widget.grid_size()
self.widget.grid_columnconfigure(0, weight=0)
self.widget.grid_columnconfigure(1, weight=0)
self.widget.grid_columnconfigure(2, weight=1, minsize=150)
for i in range(rows):
self.widget.grid_rowconfigure(i, weight=1, minsize=5)
class ApiButton():
"""
Button which triggers an API call when clicked.
"""
def __init__(self, parent, label, rowNum, apiFunc, args):
self.call = apiFunc
self.args = args
self.row = rowNum
self.parent = parent
self.widget = Ttk.Button(parent, command=self.pressed, text=label)
self.widget.grid(row=rowNum, column=1, columnspan=2, sticky='nsew')
def pressed(self, *args):
progbar = Ttk.Progressbar(self.parent, orient='horizontal', mode='indeterminate', length=10)
progbar.grid(row=self.row, column=0)
threading.Thread(target=self.thread, args=(progbar,)).start()
def thread(self, progbar):
toPass = []
if isinstance(self.args, (list, tuple)):
for arg in self.args:
if type(arg) is Tk.StringVar:
toPass.append(arg.get())
else:
toPass.append(arg)
else:
if type(self.args) is Tk.StringVar:
toPass.append(self.args.get())
else:
toPass.append(self.args)
self.resCode, self.resData = self.call(toPass)
progbar.grid_forget()
def formatResponse(resCode, resData):
return f"""Response {resCode}:
{pformat(resData)}"""