[Enhance] Cloud save update scores & capture auth error

- Restore the feature that cloud save can be used to cover best scores
- Capture error that the request does not have `Authorization` in header
This commit is contained in:
Lost-MSth
2023-01-13 17:50:01 +08:00
parent f250701eca
commit af03a48134
9 changed files with 142 additions and 69 deletions

View File

@@ -167,7 +167,7 @@ class UserDownload:
class DownloadList(UserDownload): class DownloadList(UserDownload):
''' '''
下载列表类\ 下载列表类
properties: `user` - `User`类或子类的实例 properties: `user` - `User`类或子类的实例
''' '''

View File

@@ -1,18 +1,20 @@
from .save import SaveData
from .sql import Connect, Sql from .sql import Connect, Sql
from .score import Score from .score import Score
from .download import DownloadList from .download import DownloadList
from .user import User
class BaseOperation: class BaseOperation:
name: str = None name: str = None
def __init__(self): def __init__(self, *args, **kwargs):
pass pass
def __call__(self, *args, **kwargs): def __call__(self, *args, **kwargs) -> None:
return self.run(*args, **kwargs) return self.run(*args, **kwargs)
def run(self, *args, **kwargs): def run(self, *args, **kwargs) -> None:
raise NotImplementedError raise NotImplementedError
@@ -65,3 +67,102 @@ class RefreshSongFileCache(BaseOperation):
def run(self): def run(self):
DownloadList.clear_all_cache() DownloadList.clear_all_cache()
DownloadList.initialize_cache() DownloadList.initialize_cache()
class SaveUpdateScore(BaseOperation):
'''
云存档更新成绩,是覆盖式更新\
提供user参数时只更新该用户的成绩否则更新所有用户的成绩
'''
name = 'save_update_score'
def __init__(self, user=None):
self.user = user
def run(self, user=None):
'''
parameter:
`user` - `User`类或子类的实例
'''
if user is not None:
self.user = user
if self.user is not None and self.user.user_id is not None:
self._one_user_update()
else:
self._all_update()
def _one_user_update(self):
with Connect() as c:
save = SaveData(c)
save.select_scores(self.user)
clear_state = {f'{i["song_id"]}{i["difficulty"]}': i['clear_type']
for i in save.clearlamps_data}
song_id_1 = [i['song_id'] for i in save.scores_data]
song_id_2 = [i['song_id'] for i in save.clearlamps_data]
song_id = list(set(song_id_1 + song_id_2))
c.execute(
f'''select song_id, rating_pst, rating_prs, rating_ftr, rating_byn from chart where song_id in ({','.join(['?']*len(song_id))})''', song_id)
x = c.fetchall()
song_chart_const = {i[0]: [i[1], i[2], i[3], i[4]]
for i in x} # chart const * 10
new_scores = []
for i in save.scores_data:
rating = 0
if i['song_id'] in song_chart_const:
rating = Score.calculate_rating(
song_chart_const[i['song_id']][i['difficulty']] / 10, i['score'])
if rating < 0:
rating = 0
y = f'{i["song_id"]}{i["difficulty"]}'
if y in clear_state:
clear_type = clear_state[y]
else:
clear_type = 0
new_scores.append((self.user.user_id, i['song_id'], i['difficulty'], i['score'], i['shiny_perfect_count'], i['perfect_count'],
i['near_count'], i['miss_count'], i['health'], i['modifier'], i['time_played'], clear_type, clear_type, rating))
c.executemany(
'''insert or replace into best_score values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)''', new_scores)
def _all_update(self):
with Connect() as c:
c.execute(
f'''select song_id, rating_pst, rating_prs, rating_ftr, rating_byn from chart''')
song_chart_const = {i[0]: [i[1], i[2], i[3], i[4]]
for i in c.fetchall()} # chart const * 10
c.execute('''select user_id from user_save''')
for y in c.fetchall():
user = User()
user.user_id = y[0]
save = SaveData(c)
save.select_scores(user)
clear_state = {f'{i["song_id"]}{i["difficulty"]}': i['clear_type']
for i in save.clearlamps_data}
new_scores = []
for i in save.scores_data:
rating = 0
if i['song_id'] in song_chart_const:
rating = Score.calculate_rating(
song_chart_const[i['song_id']][i['difficulty']] / 10, i['score'])
if rating < 0:
rating = 0
y = f'{i["song_id"]}{i["difficulty"]}'
if y in clear_state:
clear_type = clear_state[y]
else:
clear_type = 0
new_scores.append((user.user_id, i['song_id'], i['difficulty'], i['score'], i['shiny_perfect_count'], i['perfect_count'],
i['near_count'], i['miss_count'], i['health'], i['modifier'], i['time_played'], clear_type, clear_type, rating))
c.executemany(
'''insert or replace into best_score values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)''', new_scores)

View File

@@ -3,7 +3,7 @@ from time import time
from .config_manager import Config from .config_manager import Config
from .constant import Constant from .constant import Constant
from .error import InputError from .error import InputError, NoData
from .util import md5 from .util import md5
@@ -54,6 +54,20 @@ class SaveData:
} }
} }
def select_scores(self, user) -> None:
'''
parameter: `user` - `User`类或子类的实例
'''
self.user = user
self.c.execute('''select scores_data, clearlamps_data from user_save where user_id=:a''',
{'a': user.user_id})
x = self.c.fetchone()
if not x:
raise NoData(f'User `{user.user_id}` has no cloud save data')
self.scores_data: list = json.loads(x[0])[""]
self.clearlamps_data: list = json.loads(x[1])[""]
def select_all(self, user) -> None: def select_all(self, user) -> None:
''' '''
parameter: `user` - `User`类或子类的实例 parameter: `user` - `User`类或子类的实例

View File

@@ -102,7 +102,7 @@ class Score:
return True return True
@staticmethod @staticmethod
def calculate_rating(defnum: int, score: int) -> float: def calculate_rating(defnum: float, score: int) -> float:
'''计算rating谱面定数小于等于0视为Unrank返回值会为-1这里的defnum = Chart const''' '''计算rating谱面定数小于等于0视为Unrank返回值会为-1这里的defnum = Chart const'''
if not defnum or defnum <= 0: if not defnum or defnum <= 0:
# 谱面没定数或者定数小于等于0被视作Unrank # 谱面没定数或者定数小于等于0被视作Unrank

View File

@@ -243,8 +243,9 @@ class UserLogin(User):
'name': self.name}) 'name': self.name})
x = self.c.fetchone() x = self.c.fetchone()
if x is None: if x is None:
raise NoData('Username does not exist.', 104) raise NoData(f'Username `{self.name}` does not exist.', 104)
self.user_id = x[0]
self.now = int(time.time() * 1000) self.now = int(time.time() * 1000)
if x[2] is not None and x[2] != '': if x[2] is not None and x[2] != '':
# 自动封号检查 # 自动封号检查
@@ -255,14 +256,14 @@ class UserLogin(User):
if x[1] == '': if x[1] == '':
# 账号封禁 # 账号封禁
raise UserBan('The account has been banned.', 106) raise UserBan(
f'The account `{self.user_id}` has been banned.', 106)
if x[1] != self.hash_pwd: if x[1] != self.hash_pwd:
raise NoAccess('Wrong password.', 104) raise NoAccess('Wrong password.', 104)
self.user_id = str(x[0])
self.token = base64.b64encode(hashlib.sha256( self.token = base64.b64encode(hashlib.sha256(
(self.user_id + str(self.now)).encode("utf8") + urandom(8)).digest()).decode() (str(self.user_id) + str(self.now)).encode("utf8") + urandom(8)).digest()).decode()
self.c.execute( self.c.execute(
'''select login_device from login where user_id = :user_id''', {"user_id": self.user_id}) '''select login_device from login where user_id = :user_id''', {"user_id": self.user_id})

View File

@@ -105,6 +105,12 @@ if Config.DEPLOY_MODE == 'waitress':
f'{request.remote_addr} - - {request.method} {request.path} {response.status_code}') f'{request.remote_addr} - - {request.method} {request.path} {response.status_code}')
return response return response
# @app.before_request
# def before_request():
# print(request.path)
# print(request.headers)
# print(request.data)
def tcp_server_run(): def tcp_server_run():
if Config.DEPLOY_MODE == 'gevent': if Config.DEPLOY_MODE == 'gevent':

View File

@@ -53,7 +53,10 @@ def auth_required(request):
with Connect() as c: with Connect() as c:
try: try:
user = UserAuth(c) user = UserAuth(c)
user.token = headers['Authorization'][7:] token = headers.get('Authorization')
if not token:
raise NoAccess('No token.', -4)
user.token = token[7:]
user_id = user.token_get_id() user_id = user.token_get_id()
g.user = user g.user = user
except ArcError as e: except ArcError as e:

View File

@@ -2,9 +2,10 @@ import os
import time import time
from core.init import FileChecker from core.init import FileChecker
from core.operation import RefreshAllScoreRating, RefreshSongFileCache from core.operation import RefreshAllScoreRating, RefreshSongFileCache, SaveUpdateScore
from core.rank import RankList from core.rank import RankList
from core.sql import Connect from core.sql import Connect
from core.user import User
from flask import Blueprint, flash, redirect, render_template, request, url_for from flask import Blueprint, flash, redirect, render_template, request, url_for
from werkzeug.utils import secure_filename from werkzeug.utils import secure_filename
@@ -937,7 +938,7 @@ def update_user_save():
# 全修改 # 全修改
if 'name' not in request.form and 'user_code' not in request.form: if 'name' not in request.form and 'user_code' not in request.form:
flag = False flag = False
web.system.update_all_save(c) SaveUpdateScore().run()
flash("全部用户存档同步成功 Successfully update all users' saves.") flash("全部用户存档同步成功 Successfully update all users' saves.")
else: else:
@@ -957,7 +958,9 @@ def update_user_save():
user_id = c.fetchone() user_id = c.fetchone()
if user_id: if user_id:
user_id = user_id[0] user_id = user_id[0]
web.system.update_one_save(c, user_id) user = User()
user.user_id = user_id
SaveUpdateScore(user).run()
flash("用户存档同步成功 Successfully update the user's saves.") flash("用户存档同步成功 Successfully update the user's saves.")
else: else:

View File

@@ -132,61 +132,6 @@ def get_all_purchase():
return re return re
def update_one_save(c, user_id):
# 同步指定用户存档
# 注意best_score表不比较直接覆盖
return
# c.execute('''select scores_data, clearlamps_data from user_save where user_id=:a''', {
# 'a': user_id})
# x = c.fetchone()
# if x:
# scores = json.loads(x[0])[""]
# clearlamps = json.loads(x[1])[""]
# clear_song_id_difficulty = []
# clear_state = []
# for i in clearlamps:
# clear_song_id_difficulty.append(i['song_id']+str(i['difficulty']))
# clear_state.append(i['clear_type'])
# for i in scores:
# rating = server.arcscore.get_one_ptt(
# i['song_id'], i['difficulty'], i['score'])
# if rating < 0:
# rating = 0
# try:
# index = clear_song_id_difficulty.index(
# i['song_id'] + str(i['difficulty']))
# except:
# index = -1
# if index != -1:
# clear_type = clear_state[index]
# else:
# clear_type = 0
# c.execute('''delete from best_score where user_id=:a and song_id=:b and difficulty=:c''', {
# 'a': user_id, 'b': i['song_id'], 'c': i['difficulty']})
# c.execute('''insert into best_score values(:a, :b, :c, :d, :e, :f, :g, :h, :i, :j, :k, :l, :m, :n)''', {
# 'a': user_id, 'b': i['song_id'], 'c': i['difficulty'], 'd': i['score'], 'e': i['shiny_perfect_count'], 'f': i['perfect_count'], 'g': i['near_count'], 'h': i['miss_count'], 'i': i['health'], 'j': i['modifier'], 'k': i['time_played'], 'l': clear_type, 'm': clear_type, 'n': rating})
# ptt = server.arcscore.get_user_ptt(c, user_id) # 更新PTT
# c.execute('''update user set rating_ptt=:a where user_id=:b''', {
# 'a': ptt, 'b': user_id})
# return
def update_all_save(c):
# 同步所有用户存档
c.execute('''select user_id from user_save''')
x = c.fetchall()
if x:
for i in x:
update_one_save(c, i[0])
return
def add_one_present(present_id, expire_ts, description, item_id, item_type, item_amount): def add_one_present(present_id, expire_ts, description, item_id, item_type, item_amount):
# 添加一个奖励 # 添加一个奖励