mirror of
https://github.com/MCMi460/3DS-RPC.git
synced 2025-06-18 21:45:37 -04:00

commit431529e50b
Author: Loganh4005 <60761520+Preloading@users.noreply.github.com> Date: Sun Jun 2 09:44:49 2024 -0600 Update copyright & requirements.txt commit40bfcc8a4a
Author: Loganh4005 <60761520+Preloading@users.noreply.github.com> Date: Sun May 19 15:14:06 2024 -0600 Fix the downloadable client's API, and missing variables commita209e537ff
Author: Loganh4005 <60761520+Preloading@users.noreply.github.com> Date: Sun May 12 11:24:19 2024 -0600 Add a quick note regarding my hacky fix. commit045b32e6fc
Author: Loganh4005 <60761520+Preloading@users.noreply.github.com> Date: Sun May 12 11:11:31 2024 -0600 this is actually horrifying, but it fixes pretendo finally. commit909fbeb472
Author: Loganh4005 <60761520+Preloading@users.noreply.github.com> Date: Fri May 3 15:28:09 2024 -0600 Add default to some values in CREATE.sql commit788ed5dc41
Author: Loganh4005 <60761520+Preloading@users.noreply.github.com> Date: Fri May 3 10:33:36 2024 -0600 Resolve some SQL syntax errors (thanks @spotlightishere) commit0eee7ab448
Author: Loganh4005 <60761520+Preloading@users.noreply.github.com> Date: Fri May 3 10:01:18 2024 -0600 Move NetworkIDsToName and other functions to new file + change it to IntEnum commit5a96a68269
Author: Loganh4005 <60761520+Preloading@users.noreply.github.com> Date: Sun Apr 28 13:48:38 2024 -0600 Hot... Change?: Add the ability to use -n and --network instead of just --network commit8325f523bc
Author: Loganh4005 <60761520+Preloading@users.noreply.github.com> Date: Sun Apr 28 13:36:33 2024 -0600 Fix @HotaruBlaze (Phoenix)'s code commit25da282ba1
Merge:51d10c7
4918ffb
Author: Logan <60761520+Preloading@users.noreply.github.com> Date: Sun Apr 28 13:25:36 2024 -0600 Merge pull request #1 from HotaruBlaze/Preloading/main [Cleanup] Rework __main__: Add cleaner command line & Remove unneeded function. commit4918ffb61c
Author: Phoenix / Hotaru <DeriousHD@gmail.com> Date: Sun Apr 28 20:19:25 2024 +0100 [Cleanup] Rework __main__: Add cleaner command line & Remove unneeded function. commit51d10c75bb
Author: Loganh4005 <60761520+Preloading@users.noreply.github.com> Date: Sun Apr 28 12:28:25 2024 -0600 Fix mistake where i mixed == with != somehow commit1b70062fa3
Author: Loganh4005 <60761520+Preloading@users.noreply.github.com> Date: Sun Apr 28 12:09:38 2024 -0600 Uncomment temporary commenting on getPresence (thanks phoenix) commitbea621dfd4
Author: Logan <60761520+Preloading@users.noreply.github.com> Date: Sun Apr 28 08:22:52 2024 -0600 Remove debug print line commit12bb21e7e1
Author: Loganh4005 <60761520+Preloading@users.noreply.github.com> Date: Sat Apr 27 19:31:20 2024 -0600 Fix API commands to be compatible with before commitf2f9c7a444
Author: Loganh4005 <60761520+Preloading@users.noreply.github.com> Date: Sat Apr 27 19:03:30 2024 -0600 Add a sign of what network you are on, on the discord rpc commit6f26c3cd11
Author: Loganh4005 <60761520+Preloading@users.noreply.github.com> Date: Sat Apr 27 18:48:16 2024 -0600 Show what network on the user's page commitf4e569f4b2
Author: Loganh4005 <60761520+Preloading@users.noreply.github.com> Date: Sat Apr 27 17:47:20 2024 -0600 Possibly finish off discord.py (open to adding something saying what network you are on later) commitec164ee361
Author: Loganh4005 <60761520+Preloading@users.noreply.github.com> Date: Sat Apr 27 16:27:32 2024 -0600 Make discord.py not break and rename some variables in server.py commit998c0896dc
Author: Loganh4005 <60761520+Preloading@users.noreply.github.com> Date: Sat Apr 27 15:49:44 2024 -0600 Update template.private.py to be accurate with my private file commit9145e04dae
Author: Loganh4005 <60761520+Preloading@users.noreply.github.com> Date: Sat Apr 27 15:01:37 2024 -0600 Fix mobile support for the Select Network page commitbeb800f310
Author: Loganh4005 <60761520+Preloading@users.noreply.github.com> Date: Sat Apr 27 13:28:01 2024 -0600 Done web backend! commitdb89c6f032
Author: Loganh4005 <60761520+Preloading@users.noreply.github.com> Date: Sat Apr 27 12:46:49 2024 -0600 Finish sidebar, Fix index forgetting about pretendo, and another thing commit681ffffafa
Author: Loganh4005 <60761520+Preloading@users.noreply.github.com> Date: Sat Apr 27 10:10:30 2024 -0600 Make the try profile button work with pretendo commit107bb05ac4
Author: Loganh4005 <60761520+Preloading@users.noreply.github.com> Date: Sat Apr 27 10:04:23 2024 -0600 Fix console profile redirect commit628c5eccbe
Author: Loganh4005 <60761520+Preloading@users.noreply.github.com> Date: Sat Apr 27 09:59:08 2024 -0600 Add proper pretendo redirection to the new users and active users (maybe to active users) commitb8e44b9806
Author: Loganh4005 <60761520+Preloading@users.noreply.github.com> Date: Sat Apr 27 08:38:14 2024 -0600 Well, the most inelegent way of fixing pretendo support (i guess) commitd6e7facda4
Author: Loganh4005 <60761520+Preloading@users.noreply.github.com> Date: Mon Apr 22 00:18:08 2024 -0600 Add small fixes allowing selecting and deleting consoles commit6f2987edae
Author: Loganh4005 <60761520+Preloading@users.noreply.github.com> Date: Sat Apr 20 10:24:03 2024 -0600 try to work around missing function on pretendo, currently hangs at random spots commit8ae5da0921
Author: Loganh4005 <60761520+Preloading@users.noreply.github.com> Date: Sat Apr 20 08:47:12 2024 -0600 You can now add consoles from the menu + maybe fixing the backend for pretendo (thanks DaniElectra on discord) commit397a4a7a3a
Author: Loganh4005 <60761520+Preloading@users.noreply.github.com> Date: Fri Apr 19 23:14:51 2024 -0600 Mostly finish up registering UI and site backend (actually registering todo) commit5fd14c3292
Author: Loganh4005 <60761520+Preloading@users.noreply.github.com> Date: Fri Apr 19 21:42:04 2024 -0600 Hotfix: correct and implement some feedback commitfa60f948a9
Author: Loganh4005 <60761520+Preloading@users.noreply.github.com> Date: Fri Apr 19 21:28:12 2024 -0600 Created the basic "select the network" page commit232d317120
Author: Loganh4005 <60761520+Preloading@users.noreply.github.com> Date: Thu Apr 18 00:16:52 2024 -0600 fix consoles page commit1ae808a5c5
Author: Loganh4005 <60761520+Preloading@users.noreply.github.com> Date: Wed Apr 17 23:53:26 2024 -0600 fix homepage, roster, and active commit1da19cab4d
Author: Loganh4005 <60761520+Preloading@users.noreply.github.com> Date: Wed Apr 17 20:37:46 2024 -0600 Add pretendo support to the backend uptime counter commitfd3ab39ccf
Author: Loganh4005 <60761520+Preloading@users.noreply.github.com> Date: Wed Apr 17 19:31:47 2024 -0600 Add broken pretendo backend support, waiting for help on a datetime bug in something. commit8c70cb40cd
Author: Loganh4005 <60761520+Preloading@users.noreply.github.com> Date: Mon Apr 15 23:07:54 2024 -0600 Hotfix: Fix SQL being odd commitc05e2fd270
Author: Loganh4005 <60761520+Preloading@users.noreply.github.com> Date: Mon Apr 15 23:00:19 2024 -0600 changes to the migration script & small change to backend commitc39d1253f3
Author: Loganh4005 <60761520+Preloading@users.noreply.github.com> Date: Mon Apr 15 22:52:55 2024 -0600 Add args to choose between nintendo & pretendo commite1e85c4783
Author: Loganh4005 <60761520+Preloading@users.noreply.github.com> Date: Mon Apr 15 20:49:28 2024 -0600 realizing that my ideas are stupid, and changing it from a flag to a table commitb467277f99
Author: Loganh4005 <60761520+Preloading@users.noreply.github.com> Date: Sun Apr 14 20:28:54 2024 -0600 left my note to added friends oops commitcc7c79e71c
Author: Loganh4005 <60761520+Preloading@users.noreply.github.com> Date: Sun Apr 14 20:23:36 2024 -0600 Update creation script and add Migration script to support multiple networks commit87a1589677
Author: Loganh4005 <60761520+Preloading@users.noreply.github.com> Date: Sun Apr 14 19:33:38 2024 -0600 Updated code to use the latest NintendoClients version (+ a few other things)
324 lines
12 KiB
Python
324 lines
12 KiB
Python
# Created by Deltaion Lee (MCMi460) on Github
|
|
|
|
import os, sys
|
|
import sqlite3
|
|
import time
|
|
import threading
|
|
import traceback
|
|
if os.name == 'nt':
|
|
import pyreadline3
|
|
else:
|
|
try:
|
|
import readline
|
|
except:
|
|
pass
|
|
import typing
|
|
from .love3 import *
|
|
|
|
def getAppPath(): # Credit to @HotaruBlaze
|
|
applicationPath = os.path.expanduser('~/Documents/3DS-RPC')
|
|
# Windows allows you to move your UserProfile subfolders, Such as Documents, Videos, Music etc.
|
|
# However os.path.expanduser does not actually check and assumes it's in the default location.
|
|
# This tries to correctly resolve the Documents path and fallbacks to default if it fails.
|
|
if os.name == 'nt':
|
|
try:
|
|
import ctypes.wintypes
|
|
CSIDL_PERSONAL = 5 # My Documents
|
|
SHGFP_TYPE_CURRENT = 0 # Get current, not default value
|
|
buf=ctypes.create_unicode_buffer(ctypes.wintypes.MAX_PATH)
|
|
ctypes.windll.shell32.SHGetFolderPathW(None, CSIDL_PERSONAL, None, SHGFP_TYPE_CURRENT, buf)
|
|
applicationPath = os.path.join(buf.value, '3DS-RPC')
|
|
except:pass
|
|
return applicationPath
|
|
|
|
def getPath(path):
|
|
try:
|
|
root = sys._MEIPASS
|
|
except Exception:
|
|
root = os.path.abspath('.')
|
|
|
|
return os.path.join(root, path)
|
|
|
|
def startDBTime(time, network):
|
|
with sqlite3.connect('sqlite/fcLibrary.db') as con:
|
|
cursor = con.cursor()
|
|
cursor.execute('DELETE FROM config WHERE network=' + str(network)) # doing this isn't the most intelegent of ideas but oh well (i hope you don't need to ever add another config :D)
|
|
cursor.execute('INSERT INTO config (BACKEND_UPTIME, NETWORK) VALUES (%s, %s)' % (time, network,))
|
|
con.commit()
|
|
|
|
try:
|
|
terminalSize = os.get_terminal_size(0).columns - 2
|
|
except OSError:
|
|
terminalSize = 40
|
|
|
|
os.system('')
|
|
class Color:
|
|
DEFAULT = '\033[0m'
|
|
RED = '\033[91m'
|
|
PURPLE = '\033[0;35m'
|
|
YELLOW = '\033[93m'
|
|
BLUE = '\033[94m'
|
|
GREEN = '\033[92m'
|
|
|
|
class ProgressBar(): # Written with help from https://stackoverflow.com/a/3160819/11042767
|
|
def __init__(self, width:int = terminalSize):
|
|
self.width = width
|
|
sys.stdout.write('[%s]' % (' ' * self.width))
|
|
sys.stdout.flush()
|
|
sys.stdout.write('\r[')
|
|
|
|
self.progress = 0
|
|
self.close = True
|
|
|
|
def update(self, fraction:float):
|
|
fraction = int(fraction * self.width)
|
|
self.progress += fraction
|
|
def loop(self):
|
|
for n in range(fraction):
|
|
self.close = False
|
|
sys.stdout.write('#')
|
|
sys.stdout.flush()
|
|
time.sleep(0.1)
|
|
self.close = False
|
|
self.close = True
|
|
threading.Thread(target = loop, args = (self,)).start()
|
|
|
|
def end(self): # Can take up time on main thread to finish
|
|
for n in range(self.width - self.progress):
|
|
sys.stdout.write('#')
|
|
sys.stdout.flush()
|
|
for i in range(10):
|
|
while not self.close:
|
|
time.sleep(0.2)
|
|
sys.stdout.write(']\n')
|
|
|
|
# Get image url from title ID
|
|
def getTitle(titleID, titlesToUID, titleDatabase):
|
|
_pass = None
|
|
|
|
uid = None
|
|
tid = hex(int(titleID))[2:].zfill(16).upper()
|
|
_template = {
|
|
'name': 'Unknown 3DS App',
|
|
'icon_url': '',
|
|
'banner_url': '',
|
|
'publisher': {
|
|
'name': 'Unknown',
|
|
},
|
|
'star_rating_info': {
|
|
'score': '??',
|
|
},
|
|
'display_genre': '??',
|
|
'price_on_retail': '$??.??',
|
|
'release_date_on_eshop': '????-??-??',
|
|
'@id': tid,
|
|
}
|
|
for game in titlesToUID:
|
|
if game['TitleID'] == tid:
|
|
uid = game['UID']
|
|
break
|
|
if not uid:
|
|
if tid == ''.zfill(16):
|
|
_pass = _template
|
|
_pass['name'] = 'Home Screen'
|
|
else:
|
|
_pass = _template
|
|
# raise TitleIDMatchError('unknown title id: %s' % tid)
|
|
|
|
game = None
|
|
for region in titleDatabase:
|
|
for title in region['eshop']['contents']['content']:
|
|
if title['title']['@id'] == uid:
|
|
game = title['title']
|
|
break
|
|
if game:
|
|
break
|
|
if not game:
|
|
_pass = _template
|
|
# raise GameMatchError('unknown game: %s' % uid)
|
|
if _pass:
|
|
game = _pass
|
|
|
|
for key in _template.keys():
|
|
if not key in game.keys():
|
|
game[key] = _template[key]
|
|
|
|
if game == _template:
|
|
response = getTitleInfo(titleID)
|
|
if response:
|
|
game['name'] = response['short']
|
|
game['publisher']['name'] = response['publisher']
|
|
game['icon_url'] = '/cdn/l/' + response['imageID']
|
|
game['banner_url'] = '/cdn/l/' + response['imageID']
|
|
|
|
# Support browsers' security stuff
|
|
game['icon_url'] = game['icon_url'].replace('https://kanzashi-ctr.cdn.nintendo.net/i/', '/cdn/i/')
|
|
game['banner_url'] = game['banner_url'].replace('https://kanzashi-ctr.cdn.nintendo.net/i/', '/cdn/i/')
|
|
|
|
return game
|
|
|
|
class Console():
|
|
def __init__(self, client: object, *, prefix:str = '/'):
|
|
self.prefix = prefix
|
|
self.client = client
|
|
self.commands = {}
|
|
for func in dir(self):
|
|
if callable(getattr(self, func)) and not func.startswith('_'):
|
|
function = getattr(self, func)
|
|
self.commands[func] = {
|
|
'function': function,
|
|
'docstring': str(function.__doc__).strip(),
|
|
}
|
|
|
|
self.tip = ('Type \'help\' to view the configuration menu', Color.YELLOW)
|
|
|
|
def _main(self):
|
|
self._log(*self.tip)
|
|
while True:
|
|
userInput = input(Color.DEFAULT + '> ' + Color.PURPLE).strip().lower()
|
|
args = userInput.split(' ')
|
|
|
|
try:
|
|
self.commands[args[0]]['function'](*args[1:])
|
|
except KeyError:
|
|
self._missingCommand(userInput)
|
|
except AssertionError:
|
|
self._missingSubcommand(args)
|
|
except:
|
|
self._log(traceback.format_exc().strip(), Color.RED)
|
|
|
|
def _log(self, text:str, color:str = Color.DEFAULT):
|
|
text = color + str(text)
|
|
print(text)
|
|
return text
|
|
|
|
def _missingCommand(self, command:str):
|
|
return self._log('\'%s\' is not a real command!' % command, Color.RED), self._log(*self.tip)
|
|
|
|
def _missingSubcommand(self, args:list):
|
|
return self._log('\'%s\' is not a supported subcommand of \'%s\'!' % (args[1], args[0]), Color.RED)
|
|
|
|
def exit(self):
|
|
"""
|
|
Quits application
|
|
"""
|
|
self._log('Exiting...', Color.RED)
|
|
return os._exit(0)
|
|
|
|
def help(self, command:str = None):
|
|
"""
|
|
Shows a formatted list of all available commands
|
|
self, command:str
|
|
"""
|
|
if not command:
|
|
return self._log('\n'.join(( '%s: %s' % (key, self.commands[key]['docstring']) for key in self.commands.keys())), Color.YELLOW)
|
|
assert command in self.commands.keys()
|
|
return self._log(( '%s: %s' % (command, self.commands[command]['docstring'])), Color.YELLOW)
|
|
|
|
def clear(self):
|
|
"""
|
|
Clears the console
|
|
"""
|
|
return print('\033[H\033[J', end = '')
|
|
|
|
def status(self):
|
|
"""
|
|
Shows your current status
|
|
"""
|
|
text = [
|
|
'%s: %s' % ('Exception', self.client.userData['Exception']),
|
|
'%s: %s' % ('Friend Code', self.client.userData['User']['friendCode']),
|
|
'%s: %s' % ('Online', self.client.userData['User']['online']),
|
|
'%s: %s' % ('Message', self.client.userData['User']['message']),
|
|
]
|
|
if self.client.userData['User']['username']:
|
|
text.append('%s: %s' % ('Username', self.client.userData['User']['username']))
|
|
text.append('%s: %s' % ('Mii', self.client.userData['User']['mii']['face']))
|
|
if self.client.userData['User']['online']:
|
|
text.append('%s: %s' % ('Game', self.client.userData['User']['Presence']['game']['name']))
|
|
return self._log('\n'.join(text), Color.BLUE)
|
|
|
|
def discord(self, command:typing.Literal['connect', 'disconnect'] = 'connect', pipe:int = '0'):
|
|
"""
|
|
Utility for connecting to Discord
|
|
self, command:['connect', 'disconnect'] = 'connect', pipe:int = 0
|
|
Pipe (0 - 9) refers to which Discord client to connect to. May be rather fickle.
|
|
"""
|
|
assert command in typing.get_args(self.discord.__annotations__['command'])
|
|
if command == 'connect':
|
|
self._log('Attempting to connect to Discord...', Color.YELLOW)
|
|
self.client.connect(pipe)
|
|
else:
|
|
self._log('Attempting to disconnect from Discord...', Color.YELLOW)
|
|
self.client.disconnect()
|
|
return self._log('Done', Color.BLUE)
|
|
|
|
def config(self, command:typing.Literal['help', 'profilebutton', 'elapsedtime', 'smallimage', 'fetchtime'] = None, setVar = None):
|
|
"""
|
|
Allows configuration of various things (use 'config help' to see more)
|
|
self, command:['help', 'profilebutton', 'elapsedtime', 'smallimage', 'fetchtime'] = None, setVar = None
|
|
"""
|
|
if not command:
|
|
command = 'help'
|
|
assert command in typing.get_args(self.config.__annotations__['command'])
|
|
self.dict = {
|
|
'help': 'Shows configuration help',
|
|
'profilebutton': '(on/off) Toggle the profile button on Discord',
|
|
'elapsedtime': '(on/off) Toggle whether elapsed time is shown on Discord',
|
|
'smallimage': '(on/off) Toggle whether a small image with the user\'s mii is shown on Discord',
|
|
'fetchtime': '(any number) Set time between user-fetches. Minimum is 20 seconds to prevent rate-limimting',
|
|
}
|
|
if self.dict[command].startswith('(on/off)') and setVar:
|
|
if setVar in ('yes', 'on', 'true', 'enable'):
|
|
setVar = True
|
|
else:
|
|
setVar = False
|
|
if setVar == None:
|
|
setVar = ''
|
|
if command == 'help':
|
|
return self._log('\n'.join(( '%s%s:%s %s' % (Color.RED, key, Color.YELLOW, self.dict[key]) for key in self.dict.keys())), Color.YELLOW)
|
|
elif command == 'profilebutton':
|
|
if setVar == '':
|
|
return self._log('Currently: ' + str(self.client.showProfileButton), Color.BLUE)
|
|
self.client.showProfileButton = setVar
|
|
elif command == 'elapsedtime':
|
|
if setVar == '':
|
|
return self._log('Currently: ' + str(self.client.showElapsed), Color.BLUE)
|
|
self.client.showElapsed = setVar
|
|
elif command == 'smallimage':
|
|
if setVar == '':
|
|
return self._log('Currently: ' + str(self.client.showSmallImage), Color.BLUE)
|
|
self.client.showSmallImage = setVar
|
|
elif command == 'fetchtime':
|
|
if setVar == '':
|
|
return self._log('Currently: ' + str(self.client.fetchTime), Color.BLUE)
|
|
if int(setVar) < 20:
|
|
setVar = 20
|
|
self.client.fetchTime = int(setVar)
|
|
self.client.reflectConfig()
|
|
return self._log('Done', Color.BLUE)
|
|
|
|
def log(self):
|
|
"""
|
|
Shows activity log
|
|
"""
|
|
return self._log('\n'.join(self.client.gameLog), Color.BLUE)
|
|
|
|
# Exception handling
|
|
def APIExcept(r):
|
|
text = r.text
|
|
if '429' in r.text:
|
|
text = 'You have reached your rate-limit for this resource.'
|
|
elif '502' in r.text:
|
|
text = 'The frontend is offline. Please try again later.'
|
|
raise APIException(text)
|
|
|
|
class APIException(Exception):
|
|
pass
|
|
|
|
class TitleIDMatchError(Exception):
|
|
pass
|
|
|
|
class GameMatchError(Exception):
|
|
pass
|