I'm tired but maybe this works?

Why did I even make deactivate vs delete if I'm not going to implement a deactivation system?
Did I miss anything making this?
Am I going to publish it and test it right now anyways?
...yes :(
This commit is contained in:
Deltaion Lee 2023-05-26 02:39:28 -05:00
parent 92cb8429fd
commit 00834042f3
5 changed files with 212 additions and 185 deletions

View File

@ -4,9 +4,9 @@
from nintendo import nasc
from nintendo.nex import backend, friends, settings, streams
from nintendo.nex import common
import anyio, time, sqlite3, sys, traceback, secrets, requests, json, pickle
import anyio, time, sqlite3, sys, traceback
sys.path.append('../')
from api.private import SERIAL_NUMBER, MAC_ADDRESS, DEVICE_CERT, DEVICE_NAME, REGION, LANGUAGE, PID, PID_HMAC, NEX_PASSWORD, CLIENT_ID, CLIENT_SECRET, HOST
from api.private import SERIAL_NUMBER, MAC_ADDRESS, DEVICE_CERT, DEVICE_NAME, REGION, LANGUAGE, PID, PID_HMAC, NEX_PASSWORD
from api import *
from api.love2 import *
@ -18,127 +18,6 @@ since = 0
begun = time.time()
startDBTime(begun)
API_ENDPOINT:str = 'https://discord.com/api/v10'
with open('./cache/databases.dat', 'rb') as file:
t = pickle.loads(file.read())
titleDatabase = t[0]
titlesToUID = t[1]
class Session:
def retire(refresh):
with sqlite3.connect('sqlite/fcLibrary.db') as con:
cursor = con.cursor()
cursor.execute('UPDATE discord SET session = ? WHERE refresh = ?', ('', refresh))
con.commit()
def create(refresh, session):
with sqlite3.connect('sqlite/fcLibrary.db') as con:
cursor = con.cursor()
cursor.execute('UPDATE discord SET session = ? WHERE refresh = ?', (session, refresh))
con.commit()
return session
def update(session):
with sqlite3.connect('sqlite/fcLibrary.db') as con:
cursor = con.cursor()
cursor.execute('UPDATE discord SET lastAccessed = ? WHERE session = ?', (time.time(), session))
con.commit()
def updatePresence(bearer, refresh, session, lastAccessed, generationDate, userData):
if time.time() - lastAccessed >= 1000:
session = Session.retire(refresh)
elif time.time() - lastAccessed <= 30:
print('[MANUAL RATE LIMITED]')
return
data = {
'activities': [
{
'type': 0,
'application_id': CLIENT_ID,
'assets': {
},
'platform': 'desktop',
},
],
}
presence = userData['User']['Presence']
if presence:
game = presence['game']
data['activities'][0]['name'] = game['name'] + ' (3DS)'
if game['icon_url']:
data['activities'][0]['assets']['large_image'] = game['icon_url'].replace('/cdn/i/', HOST + '/cdn/i/')
data['activities'][0]['assets']['large_text'] = game['name']
if presence['gameDescription']:
data['activities'][0]['details'] = presence['gameDescription']
if userData['User']['username']:
data['activities'][0]['buttons'] = [{'label': 'Profile', 'url': HOST + '/user/' + userData['User']['friendCode']},]
if userData['User']['username'] and game['icon_url']:
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))
if session:
data['token'] = session
headers = {
'Authorization': 'Bearer %s' % bearer,
'Content-Type': 'application/json',
}
for key in list(data['activities'][0]):
if isinstance(data['activities'][0][key], str) and not 'image' in key:
if len(data['activities'][0][key]) > 128:
data['activities'][0][key] = data['activities'][0][key][:128]
r = requests.post('%s/users/@me/headless-sessions' % API_ENDPOINT, data = json.dumps(data), headers = headers)
r.raise_for_status()
Session.create(refresh, r.json()['token'])
Session.update(r.json()['token'])
def resetPresence(bearer, refresh, session, lastAccessed, generationDate):
if not session:
return
elif time.time() - lastAccessed <= 30:
print('[MANUAL RATE LIMITED]')
return
Session.update(session)
headers = {
'Authorization': 'Bearer %s' % bearer,
'Content-Type': 'application/json',
}
data = {
'token': session,
}
r = requests.post('%s/users/@me/headless-sessions/delete' % API_ENDPOINT, data = json.dumps(data), headers = headers)
r.raise_for_status()
Session.create(refresh, '') # Reset session
def refreshBearer(refresh:str, access:str, generationDate:int, ID:int):
if time.time() - generationDate < 604800 - 300:
return False
print('[REFRESH BEARER %s]' % ID)
data = {
'client_id': '%s' % CLIENT_ID,
'client_secret': '%s' % CLIENT_SECRET,
'grant_type': 'refresh_token',
'refresh_token': refresh,
}
headers = {
'Content-Type': 'application/x-www-form-urlencoded',
}
r = requests.post('%s/oauth2/token' % API_ENDPOINT, data = data, headers = headers)
r.raise_for_status()
response = r.json()
with sqlite3.connect('sqlite/fcLibrary.db') as con:
cursor = con.cursor()
cursor.execute('UPDATE discord SET refresh = ?, bearer = ?, generationDate = ? WHERE refresh = ?', (response['refresh_token'], response['access_token'], time.time(), refresh))
con.commit()
return True
def deleteDiscordUser(ID:int):
print('[DELETING %s]' % ID)
with sqlite3.connect('sqlite/fcLibrary.db') as con:
cursor = con.cursor()
cursor.execute('DELETE FROM discord WHERE ID = ?', (ID,))
cursor.execute('DELETE FROM discordFriends WHERE ID = ?', (ID,))
con.commit()
async def main():
while True:
time.sleep(1)
@ -208,17 +87,9 @@ async def main():
for remover in removeList:
cursor.execute('DELETE FROM friends WHERE friendCode = ?', (str(convertPrincipalIdtoFriendCode(remover)).zfill(12),))
cursor.execute('DELETE FROM discordFriends WHERE friendCode = ?', (str(convertPrincipalIdtoFriendCode(remover)).zfill(12),))
con.commit()
cursor.execute('SELECT * FROM discord')
di = cursor.fetchall()
for dn in di:
try:
if refreshBearer(dn[1], dn[2], dn[6], dn[0]):
time.sleep(delay)
except:
deleteDiscordUser(dn[0])
if len(t) > 0:
time.sleep(delay)
f = await friends_client.get_friend_presence([ e.unk1 for e in t ])
@ -233,60 +104,8 @@ async def main():
if not gameDescription: gameDescription = ''
joinable = bool(game.presence.join_availability_flag)
cursor.execute('SELECT * FROM discordFriends WHERE friendCode = ? AND active = ?', (str(convertPrincipalIdtoFriendCode(users[-1])).zfill(12), True))
v = cursor.fetchall()
for r in v:
cursor.execute('SELECT * FROM friends WHERE friendCode = ?', (str(convertPrincipalIdtoFriendCode(users[-1])).zfill(12),))
v2 = list(cursor.fetchone())
try:
v2[2] = int(v2[2])
except:
break
cursor.execute('SELECT * FROM discord WHERE ID = ?', (r[0],))
v3 = cursor.fetchone()
if not v2[1] or v2[2] != game.presence.game_key.title_id or not v3[3] or time.time() - v3[5] >= 60:
principalId = convertFriendCodeToPrincipalId(v2[0])
presence = {
'gameDescription': gameDescription,
'game': getTitle(game.presence.game_key.title_id, titlesToUID, titleDatabase),
}
mii = v2[8]
if mii:
mii = MiiData().mii_studio_url(mii)
print('[UPDATING %s]' % v2[0])
try:
updatePresence(v3[2], v3[1], v3[3], v3[5], v3[6], {
'User': {
'friendCode': str(convertPrincipalIdtoFriendCode(principalId)).zfill(12),
'online': bool(v2[1]),
'Presence': presence,
'username': v2[6],
'mii': mii,
'lastAccessed': v2[4],
}
})
except:
deleteDiscordUser(v3[0])
time.sleep(delay)
cursor.execute('UPDATE 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)))
for user in [ h for h in rotation if not h in users ]:
cursor.execute('SELECT * FROM discordFriends WHERE friendCode = ?', (str(convertPrincipalIdtoFriendCode(user)).zfill(12),))
v = cursor.fetchall()
for r in v:
cursor.execute('SELECT * FROM friends WHERE friendCode = ?', (str(convertPrincipalIdtoFriendCode(user)).zfill(12),))
v2 = list(cursor.fetchone())
cursor.execute('SELECT * FROM discord WHERE ID = ?', (r[0],))
v3 = cursor.fetchone()
if v2[1] or v3[3]:
print('[RESETTING %s]' % v2[0])
try:
resetPresence(v3[2], v3[1], v3[3], v3[5], v3[6])
except:
deleteDiscordUser(v3[0])
time.sleep(delay)
cursor.execute('UPDATE friends SET online = ?, titleID = ?, updID = ? WHERE friendCode = ?', (False, 0, 0, str(convertPrincipalIdtoFriendCode(user)).zfill(12)))
con.commit()

204
server/discord.py Normal file
View File

@ -0,0 +1,204 @@
import time, sqlite3, sys, secrets, requests, json, pickle
sys.path.append('../')
from api import *
from api.love2 import *
from api.private import CLIENT_ID, CLIENT_SECRET, HOST
API_ENDPOINT:str = 'https://discord.com/api/v10'
with open('./cache/databases.dat', 'rb') as file:
t = pickle.loads(file.read())
titleDatabase = t[0]
titlesToUID = t[1]
class Session():
def __init__(self, con, cursor):
self.con = con
self.cursor = cursor
def retire(self, refresh):
self.cursor.execute('UPDATE discord SET session = ? WHERE refresh = ?', ('', refresh))
self.con.commit()
def create(self, refresh, session):
self.cursor.execute('UPDATE discord SET session = ? WHERE refresh = ?', (session, refresh))
self.con.commit()
return session
def update(self, session):
self.cursor.execute('UPDATE discord SET lastAccessed = ? WHERE session = ?', (time.time(), session))
self.con.commit()
class Discord():
def __init__(self, con, cursor):
self.con = con
self.cursor = cursor
def updatePresence(self, bearer, refresh, session, lastAccessed, generationDate, userData):
if time.time() - lastAccessed >= 1000:
session = Session(self.con, self.cursor).retire(refresh)
elif time.time() - lastAccessed <= 15:
print('[MANUAL RATE LIMITED]')
return
data = {
'activities': [
{
'type': 0,
'application_id': CLIENT_ID,
'assets': {
},
'platform': 'desktop',
},
],
}
presence = userData['User']['Presence']
if presence:
game = presence['game']
data['activities'][0]['name'] = game['name'] + ' (3DS)'
if game['icon_url']:
data['activities'][0]['assets']['large_image'] = game['icon_url'].replace('/cdn/i/', HOST + '/cdn/i/')
data['activities'][0]['assets']['large_text'] = game['name']
if presence['gameDescription']:
data['activities'][0]['details'] = presence['gameDescription']
if userData['User']['username']:
data['activities'][0]['buttons'] = [{'label': 'Profile', 'url': HOST + '/user/' + userData['User']['friendCode']},]
if userData['User']['username'] and game['icon_url']:
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))
if session:
data['token'] = session
headers = {
'Authorization': 'Bearer %s' % bearer,
'Content-Type': 'application/json',
}
for key in list(data['activities'][0]):
if isinstance(data['activities'][0][key], str) and not 'image' in key:
if len(data['activities'][0][key]) > 128:
data['activities'][0][key] = data['activities'][0][key][:128]
r = requests.post('%s/users/@me/headless-sessions' % API_ENDPOINT, data = json.dumps(data), headers = headers)
r.raise_for_status()
Session(self.con, self.cursor).create(refresh, r.json()['token'])
Session(self.con, self.cursor).update(r.json()['token'])
def resetPresence(self, bearer, refresh, session, lastAccessed, generationDate):
if not session:
print('[NO SESSION TO RESET]')
return
elif time.time() - lastAccessed <= 30:
print('[MANUAL RATE LIMITED]')
return
Session(self.con, self.cursor).update(session)
headers = {
'Authorization': 'Bearer %s' % bearer,
'Content-Type': 'application/json',
}
data = {
'token': session,
}
r = requests.post('%s/users/@me/headless-sessions/delete' % API_ENDPOINT, data = json.dumps(data), headers = headers)
r.raise_for_status()
Session(self.con, self.cursor).create(refresh, '') # Reset session
def refreshBearer(self, refresh:str, access:str, generationDate:int, ID:int):
if time.time() - generationDate < 604800 - 1800: # 30 minutes before the token expires
return False
print('[REFRESH BEARER %s]' % ID)
data = {
'client_id': '%s' % CLIENT_ID,
'client_secret': '%s' % CLIENT_SECRET,
'grant_type': 'refresh_token',
'refresh_token': refresh,
}
headers = {
'Content-Type': 'application/x-www-form-urlencoded',
}
r = requests.post('%s/oauth2/token' % API_ENDPOINT, data = data, headers = headers)
r.raise_for_status()
response = r.json()
self.cursor = con.cursor()
self.cursor.execute('UPDATE discord SET refresh = ?, bearer = ?, generationDate = ? WHERE refresh = ?', (response['refresh_token'], response['access_token'], time.time(), refresh))
self.con.commit()
return True
def deleteDiscordUser(self, ID:int):
print('[DELETING %s]' % ID)
self.cursor.execute('DELETE FROM discord WHERE ID = ?', (ID,))
self.cursor.execute('DELETE FROM discordFriends WHERE ID = ?', (ID,))
self.con.commit()
def deactivateDiscordUser(self, ID:int):
print('[DEACTIVATING %s]' % ID)
self.cursor.execute('DELETE FROM discord WHERE ID = ?', (ID,))
self.cursor.execute('DELETE FROM discordFriends WHERE ID = ?', (ID,))
self.con.commit()
delay = 2
while True:
time.sleep(delay)
with sqlite3.connect('sqlite/fcLibrary.db') as con:
cursor = con.cursor()
discord = Discord(con, cursor)
cursor.execute('SELECT * FROM discord')
group = cursor.fetchall()
for dn in group:
try:
if discord.refreshBearer(dn[1], dn[2], dn[6], dn[0]):
time.sleep(delay * 2)
except:
#discord.deleteDiscordUser(dn[0])
discord.deactivateDiscordUser(dn[0])
wait = time.time()
while time.time() - wait <= 1200:
cursor.execute('SELECT * FROM discordFriends WHERE active = ?', (True,))
v = cursor.fetchall()
print('[BATCH OF %s USERS]' % len(v))
if len(v) < 1:
time.sleep(delay)
continue
for r in v:
print('[RUNNING %s - %s]' % (r[0], r[1]))
cursor.execute('SELECT * FROM friends WHERE friendCode = ?', (r[1],))
v2 = cursor.fetchone()
cursor.execute('SELECT * FROM discord WHERE ID = ?', (r[0],))
v3 = cursor.fetchone()
if time.time() - v3[5] >= 60:
print(v2[0])
principalId = convertFriendCodeToPrincipalId(v2[0])
if not v2[1]:
try:
print('[RESETTING %s]' % v2[0])
discord.resetPresence(v3[2], v3[1], v3[3], v3[5], v3[6])
except:
discord.deleteDiscordUser(v3[0])
else:
presence = {
'gameDescription': v2[10],
'game': getTitle(v2[2], titlesToUID, titleDatabase),
}
mii = v2[8]
if mii:
mii = MiiData().mii_studio_url(mii)
print('[UPDATING %s]' % v2[0])
try:
discord.updatePresence(v3[2], v3[1], v3[3], v3[5], v3[6], {
'User': {
'friendCode': str(convertPrincipalIdtoFriendCode(principalId)).zfill(12),
'online': bool(v2[1]),
'Presence': presence,
'username': v2[6],
'mii': mii,
'lastAccessed': v2[4],
}
})
except:
discord.deleteDiscordUser(v3[0])
else:
print('[WAIT]')
time.sleep(delay)

View File

@ -490,6 +490,7 @@ def cdnImage(file:str):
# Login route
@app.route('/login', methods=['POST'])
@limiter.limit(newUserLimit)
def login():
try:
fc = str(convertPrincipalIdtoFriendCode(convertFriendCodeToPrincipalId(request.form['fc']))).zfill(12)
@ -500,6 +501,7 @@ def login():
# Discord route
@app.route('/authorize')
@limiter.limit(newUserLimit)
def authorize():
if not request.args.get('code'):
return render_template('dist/404.html')

View File

@ -22,12 +22,13 @@ script.
if (x){
var fc = x.textContent;
if (document.cookie.includes('token')) {
document.getElementById('addToConsoles').text = ' (Add to consoles)';
document.getElementById('addToConsoles').innerHTML = '&nbsp;(Add to consoles)';
}
function addConsole() {
var xmlHttp = new XMLHttpRequest();
xmlHttp.open( "POST", "/api/toggle/" + fc, false );
xmlHttp.send( [token, 0] );
window.location.href = "/consoles";
}
x.textContent = '(' + (x.textContent.match(/.{1,4}/g) ?? []).join('-') + ')';
}

View File

@ -30,4 +30,5 @@ block content
var xmlHttp = new XMLHttpRequest();
xmlHttp.open( "POST", "/api/toggle/" + fc, false );
xmlHttp.send( [token, 0] );
window.location.href = "/consoles";
}