Migrate to enum-oriented functions

By migrating information such as the bot's friend codes and database column names, a significant amount of boilerplate can be reduced.
This commit is contained in:
Spotlight 2024-07-21 00:04:30 -05:00
parent a23380e0cd
commit f478bd3fc5
No known key found for this signature in database
GPG Key ID: 874AA355B3209BDC
4 changed files with 116 additions and 87 deletions

View File

@ -1,24 +1,44 @@
from enum import IntEnum
from api.public import nintendoBotFC, pretendoBotFC
# Selectable networks
class NetworkIDsToName(IntEnum):
nintendo = 0
pretendo = 1
def getBotFriendCodeFromNetworkId(network:int):
match network:
case 0:
return nintendoBotFC
case 1:
return pretendoBotFC
def nameToNetworkId(network:int):
if network == None:
network = 0
else:
try:
network = NetworkIDsToName[network].value
except:
network = 0
return network
class InvalidNetworkError(Exception):
pass
class NetworkType(IntEnum):
"""Selectable network types."""
NINTENDO = 0
PRETENDO = 1
def friend_code(self) -> str:
"""Returns the configured friend code for this network type."""
match self:
case self.NINTENDO:
return nintendoBotFC
case self.PRETENDO:
return pretendoBotFC
def column_name(self) -> str:
"""Returns the database column name for this network type."""
match self:
case self.NINTENDO:
return "nintendo_friends"
case self.PRETENDO:
return "pretendo_friends"
def lower_name(self) -> str:
"""Returns a lowercase name of this enum member's name for API compatibility."""
return self.name.lower()
def nameToNetworkType(network_name: str) -> NetworkType:
# Assume Nintendo Network as a fallback.
if network_name is None:
return NetworkType.NINTENDO
try:
# All enum members are uppercase, so ensure we are, too.
return NetworkType[network_name.upper()]
except:
return NetworkType.NINTENDO

View File

@ -10,7 +10,7 @@ sys.path.append('../')
from api.private import SERIAL_NUMBER, MAC_ADDRESS, DEVICE_CERT, DEVICE_NAME, REGION, LANGUAGE, NINTENDO_PID, PRETENDO_PID, PID_HMAC, NINTENDO_NEX_PASSWORD, PRETENDO_NEX_PASSWORD
from api import *
from api.love2 import *
from api.networks import NetworkIDsToName
from api.networks import NetworkType, InvalidNetworkError
import logging
logging.basicConfig(level=logging.INFO)
@ -21,7 +21,7 @@ quicker = 15
begun = time.time()
scrape_only = False
network:int = 0
network: NetworkType = NetworkType.NINTENDO
async def main():
while True:
@ -29,7 +29,7 @@ async def main():
print('Grabbing new friends...')
with sqlite3.connect('sqlite/fcLibrary.db') as con:
cursor = con.cursor()
cursor.execute('SELECT friendCode, lastAccessed FROM ' + NetworkIDsToName(network).name + "_friends")
cursor.execute('SELECT friendCode, lastAccessed FROM ' + network.column_name())
result = cursor.fetchall()
if not result:
continue
@ -45,19 +45,17 @@ async def main():
client.set_title(0x0004013000003202, 20) # to be honest, this should be seperate between networks, so if one eg, gets banned, you still get to keep the friend code for the other network, but i'm lazy.
client.set_locale(REGION, LANGUAGE)
if network == 0: # Nintendo Network
if network == NetworkType.NINTENDO:
client.set_url("nasc.nintendowifi.net")
PID = NINTENDO_PID
NEX_PASSWORD = NINTENDO_NEX_PASSWORD
elif network == 1:
elif network == NetworkType.PRETENDO:
client.set_url("nasc.pretendo.cc")
client.context.set_authority(None)
PID = PRETENDO_PID
NEX_PASSWORD = PRETENDO_NEX_PASSWORD
else:
raise Exception(NetworkIDsToName(network).name + " is not a valid network")
raise InvalidNetworkError(f"Network type {network} is not configured for querying")
client.set_device(SERIAL_NUMBER, MAC_ADDRESS, DEVICE_CERT, DEVICE_NAME)
client.set_user(PID , PID_HMAC)
@ -88,10 +86,13 @@ async def main():
removeList = []
cleanUp = []
if network == 1:
# The add_friend_by_principal_ids method is not yet
# implemented on Pretendo, so this is a fix for now.
if network == NetworkType.PRETENDO:
for friend_pid in rotation:
time.sleep(delay / quicker)
await friends_client.add_friend_by_principal_id(0, friend_pid) # the add_friend_by_principal_ids hasn't been implemented yet on pretendo, so this is a fix for now.
await friends_client.add_friend_by_principal_id(0, friend_pid)
else:
time.sleep(delay)
await friends_client.add_friend_by_principal_ids(0, rotation)
@ -112,7 +113,8 @@ async def main():
cleanUp.append(t1.pid)
for remover in removeList:
cursor.execute('DELETE FROM ' + NetworkIDsToName(network).name + "_friends" + ' WHERE friendCode = ?', (str(convertPrincipalIdtoFriendCode(remover)).zfill(12),))
column_name = network.column_name()
cursor.execute('DELETE FROM ' + column_name + ' WHERE friendCode = ?', (str(convertPrincipalIdtoFriendCode(remover)).zfill(12),))
cursor.execute('DELETE FROM discordFriends WHERE friendCode = ? AND network = ?', (str(convertPrincipalIdtoFriendCode(remover)).zfill(12), str(network)))
con.commit()
@ -132,9 +134,9 @@ async def main():
if not gameDescription: gameDescription = ''
joinable = bool(game.presence.join_availability_flag)
cursor.execute('UPDATE ' + NetworkIDsToName(network).name + "_friends" + ' SET online = ?, titleID = ?, updID = ?, joinable = ?, gameDescription = ?, lastOnline = ? WHERE friendCode = ?', (True, game.presence.game_key.title_id, game.presence.game_key.title_version, joinable, gameDescription, time.time(), str(convertPrincipalIdtoFriendCode(users[-1])).zfill(12)))
cursor.execute('UPDATE ' + network.column_name() + ' SET online = ?, titleID = ?, updID = ?, joinable = ?, gameDescription = ?, lastOnline = ? WHERE friendCode = ?', (True, game.presence.game_key.title_id, game.presence.game_key.title_version, joinable, gameDescription, time.time(), str(convertPrincipalIdtoFriendCode(users[-1])).zfill(12)))
for user in [ h for h in rotation if not h in users ]:
cursor.execute('UPDATE ' + NetworkIDsToName(network).name + "_friends" + ' SET online = ?, titleID = ?, updID = ? WHERE friendCode = ?', (False, 0, 0, str(convertPrincipalIdtoFriendCode(user)).zfill(12)))
cursor.execute('UPDATE ' + network.column_name() + ' SET online = ?, titleID = ?, updID = ? WHERE friendCode = ?', (False, 0, 0, str(convertPrincipalIdtoFriendCode(user)).zfill(12)))
con.commit()
@ -176,7 +178,7 @@ async def main():
jeuFavori = j1[0].game_key.title_id
else:
comment = ''
cursor.execute('UPDATE ' + NetworkIDsToName(network).name + "_friends" + ' SET username = ?, message = ?, mii = ?, jeuFavori = ? WHERE friendCode = ?', (username, comment, face, jeuFavori, str(convertPrincipalIdtoFriendCode(ti.pid)).zfill(12)))
cursor.execute('UPDATE ' + network.column_name() + ' SET username = ?, message = ?, mii = ?, jeuFavori = ? WHERE friendCode = ?', (username, comment, face, jeuFavori, str(convertPrincipalIdtoFriendCode(ti.pid)).zfill(12)))
con.commit()
for friend in rotation + cleanUp:
@ -194,10 +196,10 @@ async def main():
if __name__ == '__main__':
try:
parser = argparse.ArgumentParser()
parser.add_argument('-n', '--network', choices=[member.name.lower() for member in NetworkIDsToName], required=True)
parser.add_argument('-n', '--network', choices=[member.lower_name() for member in NetworkType], required=True)
args = parser.parse_args()
network = NetworkIDsToName[args.network.lower()].value
network = NetworkType[args.network.upper()]
startDBTime(begun, network)
anyio.run(main)
except (KeyboardInterrupt, Exception) as e:

View File

@ -4,7 +4,7 @@ sys.path.append('../')
from api import *
from api.love2 import *
from api.private import CLIENT_ID, CLIENT_SECRET, HOST
from api.networks import NetworkIDsToName, nameToNetworkId
from api.networks import NetworkType
API_ENDPOINT:str = 'https://discord.com/api/v10'
@ -63,10 +63,10 @@ class Discord():
if presence['gameDescription']:
data['activities'][0]['details'] = presence['gameDescription']
if userData['User']['username'] and bool(config[0]):
data['activities'][0]['buttons'] = [{'label': 'Profile', 'url': HOST + '/user/' + userData['User']['friendCode'] + '/?network=' + NetworkIDsToName(network).name},]
data['activities'][0]['buttons'] = [{'label': 'Profile', 'url': HOST + '/user/' + userData['User']['friendCode'] + '/?network=' + NetworkType(network).name},]
if userData['User']['username'] and game['icon_url'] and bool(config[1]):
data['activities'][0]['assets']['small_image'] = userData['User']['mii']['face']
data['activities'][0]['assets']['small_text'] = '-'.join(userData['User']['friendCode'][i:i+4] for i in range(0, 12, 4)) + ' on ' + NetworkIDsToName(network).name.capitalize()
data['activities'][0]['assets']['small_text'] = '-'.join(userData['User']['friendCode'][i:i+4] for i in range(0, 12, 4)) + ' on ' + NetworkType(network).name.capitalize()
if session:
data['token'] = session
headers = {
@ -193,7 +193,7 @@ while True:
continue
for r in discordFriends:
print('[RUNNING %s - %s]' % (r[0], r[1]))
cursor.execute('SELECT * FROM ' + NetworkIDsToName(r[2]).name + '_friends WHERE friendCode = ?', (r[1],))
cursor.execute('SELECT * FROM ' + NetworkType(r[2]).column_name() + ' WHERE friendCode = ?', (r[1],))
v2 = cursor.fetchone()
cursor.execute('SELECT * FROM discord WHERE ID = ?', (r[0],))
v3 = cursor.fetchone()

View File

@ -10,7 +10,7 @@ from api import *
from api.love2 import *
from api.private import CLIENT_ID, CLIENT_SECRET, HOST
from api.public import pretendoBotFC, nintendoBotFC
from api.networks import NetworkIDsToName, nameToNetworkId, getBotFriendCodeFromNetworkId
from api.networks import NetworkType, nameToNetworkType
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.abspath('sqlite/fcLibrary.db')
@ -94,7 +94,7 @@ def cacheTitles():
print('[Saved database to file]')
# Create entry in database with friendCode
def createUser(friendCode:int, network:int, addNewInstance:bool = False):
def createUser(friendCode:int, network:NetworkType, addNewInstance:bool = False):
if int(friendCode) == int(pretendoBotFC):
raise Exception('invalid FC')
if int(friendCode) == int(nintendoBotFC):
@ -103,11 +103,11 @@ def createUser(friendCode:int, network:int, addNewInstance:bool = False):
convertFriendCodeToPrincipalId(friendCode)
if not addNewInstance:
raise Exception('UNIQUE constraint failed: friends.friendCode')
db.session.execute('INSERT INTO ' + NetworkIDsToName(network).name + '_friends (friendCode, online, titleID, updID, lastAccessed, accountCreation, lastOnline, jeuFavori) VALUES (\'%s\', %s, %s, %s, %s, %s, %s, %s)' % (str(friendCode).zfill(12), False, '0', '0', time.time() + 300, time.time(), time.time(), 0))
db.session.execute('INSERT INTO ' + network.column_name() + ' (friendCode, online, titleID, updID, lastAccessed, accountCreation, lastOnline, jeuFavori) VALUES (\'%s\', %s, %s, %s, %s, %s, %s, %s)' % (str(friendCode).zfill(12), False, '0', '0', time.time() + 300, time.time(), time.time(), 0))
db.session.commit()
except Exception as e:
if 'UNIQUE constraint failed: friends.friendCode' in str(e):
db.session.execute('UPDATE ' + NetworkIDsToName(network).name + '_friends SET lastAccessed = %s WHERE friendCode = \'%s\'' % (time.time(), str(friendCode).zfill(12)))
db.session.execute('UPDATE ' + network.column_name() + ' SET lastAccessed = %s WHERE friendCode = \'%s\'' % (time.time(), str(friendCode).zfill(12)))
db.session.commit()
def fetchBearerToken(code:str):
@ -212,7 +212,7 @@ def userAgentCheck():
except:
raise Exception('this client is invalid')
def getPresence(friendCode:int, network:int, *, createAccount:bool = True, ignoreUserAgent = False, ignoreBackend = False):
def getPresence(friendCode:int, network:NetworkType, *, createAccount:bool = True, ignoreUserAgent = False, ignoreBackend = False):
try:
if not ignoreUserAgent:
userAgentCheck()
@ -225,7 +225,7 @@ def getPresence(friendCode:int, network:int, *, createAccount:bool = True, ignor
if createAccount:
createUser(friendCode, network, False)
principalId = convertFriendCodeToPrincipalId(friendCode)
result = db.session.execute('SELECT * FROM ' + NetworkIDsToName(network).name + '_friends WHERE friendCode = \'%s\'' % friendCode)
result = db.session.execute('SELECT * FROM ' + network.column_name() + ' WHERE friendCode = \'%s\'' % friendCode)
result = result.fetchone()
if not result:
raise Exception('friendCode not recognized\nHint: You may not have added the bot as a friend')
@ -273,7 +273,7 @@ def getPresence(friendCode:int, network:int, *, createAccount:bool = True, ignor
# Index page
@app.route('/')
def index():
results = db.session.execute(' UNION '.join([f'SELECT *, "{network.name}" FROM {network.name}_friends WHERE online = True AND username != ""' for network in NetworkIDsToName]) + ' ORDER BY lastAccessed DESC')
results = db.session.execute(' UNION '.join([f'SELECT *, "{network.lower_name()}" FROM {network.column_name()} WHERE online = True AND username != ""' for network in NetworkType]) + ' ORDER BY lastAccessed DESC')
results = results.fetchall()
num = len(results)
data = sidenav()
@ -286,11 +286,12 @@ def index():
'game': getTitle(user[2], titlesToUID, titleDatabase),
'friendCode': str(user[0]).zfill(12),
'joinable': bool(user[9]),
# Adjust for uppercase enum naming.
'network': str(user[13]),
}) for user in results if user[6] ]
data['active'] = data['active'][:2]
results = db.session.execute(' UNION '.join([f'SELECT *, "{network.name}" FROM {network.name}_friends WHERE username != ""' for network in NetworkIDsToName]) + ' ORDER BY accountCreation DESC LIMIT 6')
results = db.session.execute(' UNION '.join([f'SELECT *, "{network.lower_name()}" FROM {network.column_name()} WHERE username != ""' for network in NetworkType]) + ' ORDER BY accountCreation DESC LIMIT 6')
results = results.fetchall()
data['new'] = [ ({
'mii':MiiData().mii_studio_url(user[8]),
@ -351,7 +352,7 @@ def settingsRedirect():
# Roster page
@app.route('/roster')
def roster():
results = db.session.execute(' UNION '.join([f'SELECT *, "{network.name}" AS network FROM {network.name}_friends WHERE username != ""' for network in NetworkIDsToName]) + ' ORDER BY accountCreation DESC LIMIT 8')
results = db.session.execute(' UNION '.join([f'SELECT *, "{network.lower_name()}" AS network FROM {network.column_name()} WHERE username != ""' for network in NetworkType]) + ' ORDER BY accountCreation DESC LIMIT 8')
results = results.fetchall()
data = sidenav()
data['title'] = 'New Users'
@ -370,7 +371,7 @@ def roster():
# Active page
@app.route('/active')
def active():
results = db.session.execute(' UNION '.join([f'SELECT *, "{network.name}" AS network FROM {network.name}_friends WHERE online = True AND username != ""' for network in NetworkIDsToName]) + ' ORDER BY lastAccessed DESC')
results = db.session.execute(' UNION '.join([f'SELECT *, "{network.lower_name()}" AS network FROM {network.column_name()} WHERE online = True AND username != ""' for network in NetworkType]) + ' ORDER BY lastAccessed DESC')
results = results.fetchall()
data = sidenav()
data['title'] = 'Active Users'
@ -392,16 +393,17 @@ def active():
def register():
network = request.args.get('network')
if network == None:
response = make_response(render_template('dist/registerselectnetwork.html'))
else:
try:
network = NetworkIDsToName[network].value
response = make_response(render_template('dist/register.html', data = {'botFC':'-'.join(getBotFriendCodeFromNetworkId(network)[i:i+4] for i in range(0, len(getBotFriendCodeFromNetworkId(network)), 4)), 'network':network}))
except:
network = 0
response = make_response(render_template('dist/registerselectnetwork.html'))
return response
if network is None:
return make_response(render_template('dist/registerselectnetwork.html'))
try:
network = NetworkType[network.upper()]
response = make_response(render_template('dist/register.html', data = {
'botFC': '-'.join(network.friend_code()[i:i+4] for i in range(0, len(network.friend_code()), 4)),
'network': network
}))
except:
return make_response(render_template('dist/registerselectnetwork.html'))
# Register page redirect
@app.route('/register')
@ -450,14 +452,15 @@ def consoles():
response.set_cookie('pfp', '', expires = 0)
return response
return redirect('/')
for console, active, network in getConnectedConsoles(id):
result = db.session.execute('SELECT * FROM ' + NetworkIDsToName(network).name + '_friends WHERE friendCode = \'%s\'' % console)
for console, active, network_type in getConnectedConsoles(id):
network = NetworkType(network_type)
result = db.session.execute('SELECT * FROM ' + network.column_name() + ' WHERE friendCode = \'%s\'' % console)
result = result.fetchone()
data['consoles'].append({
'fc': '-'.join(console[i:i+4] for i in range(0, 12, 4)),
'username': result[6],
'active': active,
'network': NetworkIDsToName(network).name
'network': network.lower_name()
})
data.update(sidenav())
response = render_template('dist/consoles.html', data = data)
@ -465,18 +468,21 @@ def consoles():
@app.route('/user/<string:friendCode>/')
def userPage(friendCode:str):
network: NetworkType
try:
network = nameToNetworkId(request.args.get('network'))
network = nameToNetworkType(request.args.get('network'))
userData = getPresence(int(friendCode.replace('-', '')), network, createAccount= False, ignoreUserAgent = True, ignoreBackend = True)
if userData['Exception'] or not userData['User']['username']:
raise Exception(userData['Exception'])
except:
return render_template('dist/404.html')
if not userData['User']['online'] or not userData['User']['Presence']:
userData['User']['Presence']['game'] = None
userData['User']['favoriteGame'] = getTitle(userData['User']['favoriteGame'], titlesToUID, titleDatabase)
userData['User']['network'] = NetworkIDsToName(nameToNetworkId(request.args.get('network'))).name
userData['User']['network'] = network.lower_name()
if userData['User']['favoriteGame']['name'] == 'Home Screen':
userData['User']['favoriteGame'] = None
for i in ('accountCreation','lastAccessed','lastOnline'):
@ -510,9 +516,11 @@ def newUser(friendCode:int, network:int=-1, userCheck:bool = True):
if userCheck:
userAgentCheck()
if network == -1:
network = 0
if request.data.decode('utf-8').split(',')[0].isnumeric():
network = NetworkIDsToName(request.data.decode('utf-8').split(',')[0]).value
network = NetworkType.NINTENDO
request_arg = request.data.decode('utf-8').split(',')[0]
if request_arg.isnumeric():
network = NetworkType(request_arg.upper())
createUser(friendCode, network, True)
return {
'Exception': False,
@ -528,7 +536,7 @@ def newUser(friendCode:int, network:int=-1, userCheck:bool = True):
@app.route('/api/user/<int:friendCode>/', methods=['GET'])
@limiter.limit(userPresenceLimit)
def userPresence(friendCode:int, network:str="nintendo", *, createAccount:bool = True, ignoreUserAgent = False, ignoreBackend = False):
return getPresence(friendCode, nameToNetworkId(network), createAccount=createAccount, ignoreUserAgent = ignoreUserAgent, ignoreBackend = ignoreBackend)
return getPresence(friendCode, nameToNetworkType(network), createAccount=createAccount, ignoreUserAgent = ignoreUserAgent, ignoreBackend = ignoreBackend)
# Alias
@app.route('/api/u/<int:friendCode>/', methods=['GET'])
@ -536,48 +544,48 @@ def userPresence(friendCode:int, network:str="nintendo", *, createAccount:bool =
def userAlias(friendCode:int):
network = 0
if request.args.get('network') != None:
network = nameToNetworkId(request.args.get('network'))
network = nameToNetworkType(request.args.get('network'))
return userPresence(friendCode, network)
# Alias
@app.route('/api/u/c/<int:friendCode>/', methods=['POST'])
@limiter.limit(newUserLimit)
def newAlias1(friendCode:int):
network = 0
network = NetworkType.NINTENDO
if (request.data.decode('utf-8').split(','))[0] != None:
network = nameToNetworkId((request.data.decode('utf-8').split(','))[0])
network = nameToNetworkType((request.data.decode('utf-8').split(','))[0])
return newUser(friendCode, network)
# Alias
@app.route('/api/user/c/<int:friendCode>/', methods=['POST'])
@limiter.limit(newUserLimit)
def newAlias2(friendCode:int):
network = 0
network = NetworkType.NINTENDO
if (request.data.decode('utf-8').split(','))[0] != None:
network = nameToNetworkId((request.data.decode('utf-8').split(','))[0])
network = nameToNetworkType((request.data.decode('utf-8').split(','))[0])
return newUser(friendCode, network)
# Alias
@app.route('/api/u/create/<int:friendCode>/', methods=['POST'])
@limiter.limit(newUserLimit)
def newAlias3(friendCode:int):
network = 0
network = NetworkType.NINTENDO
if (request.data.decode('utf-8').split(','))[0] != None:
network = nameToNetworkId((request.data.decode('utf-8').split(','))[0])
network = nameToNetworkType((request.data.decode('utf-8').split(','))[0])
return newUser(friendCode, network)
# Toggle
@app.route('/api/toggle/<int:friendCode>/', methods=['POST'])
@limiter.limit(togglerLimit)
def toggler(friendCode:int):
network = 0
network = NetworkType.NINTENDO
if request.data.decode('utf-8').split(',')[2] != None:
network = nameToNetworkId(request.data.decode('utf-8').split(',')[2])
network = nameToNetworkType(request.data.decode('utf-8').split(',')[2])
try:
fc = str(convertPrincipalIdtoFriendCode(convertFriendCodeToPrincipalId(friendCode))).zfill(12)
except:
return 'failure!\nthat is not a real friendCode!'
result = db.session.execute('SELECT * FROM ' + NetworkIDsToName(network).name + '_friends WHERE friendCode = \'%s\'' % fc)
result = db.session.execute('SELECT * FROM ' + NetworkType(network).column_name() + ' WHERE friendCode = \'%s\'' % fc)
result = result.fetchone()
if not result:
return 'failure!\nthat is not an existing friendCode!'
@ -618,7 +626,7 @@ def deleter(friendCode:int):
data = request.data.decode('utf-8').split(',')
token = data[0]
network = nameToNetworkId(data[1])
network = nameToNetworkType(data[1])
id = userFromToken(token)[0]
db.session.execute('DELETE FROM discordFriends WHERE friendCode = \'%s\' AND ID = %s AND network = %s' % (fc, id, network))
db.session.commit()
@ -626,7 +634,7 @@ def deleter(friendCode:int):
result = db.session.execute('SELECT * FROM discordFriends WHERE friendCode = \'%s\' AND network = %s' % (fc, network))
result = result.fetchone()
if result == None:
db.session.execute('DELETE FROM ' + NetworkIDsToName(network).name + '_friends WHERE friendCode = \'%s\'' % (fc))
db.session.execute('DELETE FROM ' + network.column_name() + ' WHERE friendCode = \'%s\'' % (fc))
db.session.commit()
# end of optional
@ -671,15 +679,14 @@ def localImageCdn(file:str):
def login():
try:
fc = str(convertPrincipalIdtoFriendCode(convertFriendCodeToPrincipalId(request.form['fc']))).zfill(12)
if request.form['network'] == None:
networkName = NetworkIDsToName(0).name
if request.form['network'] is None:
network = NetworkType.NINTENDO
else:
networkName = NetworkIDsToName(int(request.form['network'])).name
networkId = nameToNetworkId(networkName)
newUser(fc, networkId, False)
network = NetworkType(int(request.form['network']))
newUser(fc, network, False)
except:
return redirect('/failure.html')
return redirect(f'/success.html?fc={fc}&network={networkName}')
return redirect(f'/success.html?fc={fc}&network={network}')
# Discord route
@app.route('/authorize')