mirror of
https://github.com/Lost-MSth/Arcaea-server.git
synced 2025-12-14 08:06:23 +08:00
5
.gitignore
vendored
5
.gitignore
vendored
@@ -17,4 +17,7 @@ __pycache__/
|
||||
|
||||
# setting/config files
|
||||
latest version/config/
|
||||
latest version/config.py
|
||||
latest version/config.py
|
||||
|
||||
# song data
|
||||
latest version/database/songs/
|
||||
|
||||
20
README.md
20
README.md
@@ -73,17 +73,17 @@ It is just so interesting. What it can do is under exploration.
|
||||
>
|
||||
> Tips: When updating, please keep the original database in case of data loss.
|
||||
|
||||
### Version 2.10.2
|
||||
### Version 2.10.3
|
||||
|
||||
- 适用于Arcaea 4.1.7版本 For Arcaea 4.1.7
|
||||
- 新搭档 **红(冬日)** 已解锁 Unlock the character **Kou(Winter)**.
|
||||
- 新增记录数据库来记录全部的游玩历史分数 Add a log database to record all playing scores.
|
||||
- 新增设置选项,可选择阻止或接受unranked成绩 Add a config option that can be used to forbid unranked scores.
|
||||
- 为自定义异常添加简明的warning日志 Add brief warning logs for custom exceptions.
|
||||
- 修复flask应用启动前出现异常,日志无法正确地指出异常的问题 Fix a bug that if an exception is raised before flask app runs, logger will not work well.
|
||||
- 现在初始化文件中JSON文件可以是模块支持的其它编码格式 Now initial files can be other encoding types which are supported by JSON module.
|
||||
- `run.bat`在报错时会停下而不是一闪而过了 Make the `run.bat` script pause when meeting an error. #82
|
||||
- 新增API接口查询单谱排行 Add an API endpoint for getting the rank list of a song's chart. #81
|
||||
- 适用于Arcaea 4.2.0版本 For Arcaea 4.2.0
|
||||
- 新搭档 **拉格兰(Aria)** 已解锁 Unlock the character **Lagrange(Aria)**. (Lack of its values)
|
||||
- 新搭档 **忘却(Apophenia)** 已解锁 Unlock the character **Lethe(Apophenia)**.
|
||||
- 新增选项取消歌曲文件哈希预计算 Add an option to disable song file hash pre-calculation.
|
||||
- 新增对世界模式中地图本地限制歌曲解锁或挑战解锁以及地图中台阶上限制歌曲难度的支持 Add support for restricting songs' difficulty in the map's steps of world mode and locally restricting unlocking songs or challenges in the map of world mode.
|
||||
- 恢复使用云存档覆盖成绩的功能 Restore the feature that cloud save can be used to cover best scores.
|
||||
- 捕获`Authorization`不在请求头导致的报错 Capture error that the request does not have `Authorization` in header.
|
||||
- 修复客户端版本校验中请求头不存在`AppVersion`也能通过校验的逻辑错误 Fix a bug that headers without `AppVersion` are allowed in client version checking.
|
||||
- 新增增删改歌曲信息的API接口 Add some API endpoints, including creating, changing, deleting song info.
|
||||
|
||||
## 运行环境与依赖 Running environment and requirements
|
||||
|
||||
|
||||
@@ -48,13 +48,14 @@ def role_required(request, powers=[]):
|
||||
return decorator
|
||||
|
||||
|
||||
def request_json_handle(request, required_keys=[], optional_keys=[]):
|
||||
def request_json_handle(request, required_keys: list = [], optional_keys: list = [], must_change: bool = False):
|
||||
'''
|
||||
提取post参数,返回dict,写成了修饰器\
|
||||
parameters: \
|
||||
`request`: `Request` - 当前请求\
|
||||
`required_keys`: `list` - 必须的参数\
|
||||
`optional_keys`: `list` - 可选的参数
|
||||
`optional_keys`: `list` - 可选的参数\
|
||||
`must_change`: `bool` - 当全都是可选参数时,是否必须有至少一项修改
|
||||
'''
|
||||
|
||||
def decorator(view):
|
||||
@@ -67,8 +68,11 @@ def request_json_handle(request, required_keys=[], optional_keys=[]):
|
||||
else:
|
||||
if request.method == 'GET' and 'query' in request.args:
|
||||
# 处理axios没法GET传data的问题
|
||||
json_data = loads(
|
||||
b64decode(request.args['query']).decode())
|
||||
try:
|
||||
json_data = loads(
|
||||
b64decode(request.args['query']).decode())
|
||||
except:
|
||||
raise PostError(api_error_code=-105)
|
||||
else:
|
||||
json_data = {}
|
||||
|
||||
@@ -81,6 +85,9 @@ def request_json_handle(request, required_keys=[], optional_keys=[]):
|
||||
if key in json_data:
|
||||
data[key] = json_data[key]
|
||||
|
||||
if must_change and not data:
|
||||
return error_return(PostError('No change', api_error_code=-100))
|
||||
|
||||
return view(data, *args, **kwargs)
|
||||
|
||||
return wrapped_view
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from core.error import ArcError
|
||||
from flask import jsonify
|
||||
|
||||
from core.error import ArcError
|
||||
|
||||
default_error = ArcError('Unknown Error')
|
||||
|
||||
|
||||
@@ -9,6 +10,7 @@ CODE_MSG = {
|
||||
-1: 'See status code', # 基础错误
|
||||
-2: 'No data',
|
||||
-3: 'No data or user', # 不确定是无数据还是无用户
|
||||
-4: 'Data exist',
|
||||
-100: 'Invalid post data', # 1xx数据错误
|
||||
-101: 'Invalid data type',
|
||||
-102: 'Invalid query parameter',
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
from core.error import NoData, InputError
|
||||
from flask import Blueprint, request
|
||||
|
||||
from core.error import DataExist, InputError, NoData
|
||||
from core.rank import RankList
|
||||
from core.song import Song
|
||||
from core.sql import Connect, Query, Sql
|
||||
from flask import Blueprint, request
|
||||
|
||||
from .api_auth import api_try, request_json_handle, role_required
|
||||
from .api_code import success_return
|
||||
@@ -21,6 +22,39 @@ def songs_song_get(user, song_id):
|
||||
return success_return(s.to_dict())
|
||||
|
||||
|
||||
@bp.route('/<string:song_id>', methods=['PUT'])
|
||||
@role_required(request, ['change'])
|
||||
@request_json_handle(request, optional_keys=['name', 'charts'], must_change=True)
|
||||
@api_try
|
||||
def songs_song_put(data, user, song_id):
|
||||
'''修改歌曲信息'''
|
||||
with Connect() as c:
|
||||
s = Song(c, song_id).select()
|
||||
if 'name' in data:
|
||||
s.name = str(data['name'])
|
||||
if 'charts' in data:
|
||||
for i in data['charts']:
|
||||
if 'difficulty' in i and 'chart_const' in i:
|
||||
s.charts[i['difficulty']].defnum = round(
|
||||
i['chart_const'] * 10)
|
||||
|
||||
s.update()
|
||||
return success_return(s.to_dict())
|
||||
|
||||
|
||||
@bp.route('/<string:song_id>', methods=['DELETE'])
|
||||
@role_required(request, ['change'])
|
||||
@api_try
|
||||
def songs_song_delete(user, song_id):
|
||||
'''删除歌曲信息'''
|
||||
with Connect() as c:
|
||||
s = Song(c, song_id)
|
||||
if not s.select_exists():
|
||||
raise NoData(f'No such song: `{song_id}`')
|
||||
s.delete()
|
||||
return success_return()
|
||||
|
||||
|
||||
@bp.route('', methods=['GET'])
|
||||
@role_required(request, ['select', 'select_song_info'])
|
||||
@request_json_handle(request, optional_keys=Constant.QUERY_KEYS)
|
||||
@@ -43,6 +77,20 @@ def songs_get(data, user):
|
||||
return success_return([x.to_dict() for x in r])
|
||||
|
||||
|
||||
@bp.route('', methods=['POST'])
|
||||
@role_required(request, ['change'])
|
||||
@request_json_handle(request, ['song_id', 'charts'], ['name'])
|
||||
@api_try
|
||||
def songs_post(data, user):
|
||||
'''添加歌曲信息'''
|
||||
with Connect() as c:
|
||||
s = Song(c).from_dict(data)
|
||||
if s.select_exists():
|
||||
raise DataExist(f'Song `{s.song_id}` already exists')
|
||||
s.insert()
|
||||
return success_return(s.to_dict())
|
||||
|
||||
|
||||
@bp.route('/<string:song_id>/<int:difficulty>/rank', methods=['GET'])
|
||||
@role_required(request, ['select', 'select_song_rank', 'select_song_rank_top'])
|
||||
@request_json_handle(request, optional_keys=['limit'])
|
||||
|
||||
@@ -78,7 +78,7 @@ def users_user_get(user, user_id):
|
||||
|
||||
@bp.route('/<int:user_id>', methods=['PUT'])
|
||||
@role_required(request, ['change'])
|
||||
@request_json_handle(request, optional_keys=['name', 'password', 'user_code', 'ticket', 'email'])
|
||||
@request_json_handle(request, optional_keys=['name', 'password', 'user_code', 'ticket', 'email'], must_change=True)
|
||||
@api_try
|
||||
def users_user_put(data, user, user_id):
|
||||
'''修改一个用户'''
|
||||
@@ -103,8 +103,7 @@ def users_user_put(data, user, user_id):
|
||||
raise InputError('Ticket must be int')
|
||||
u.ticket = data['ticket']
|
||||
r['ticket'] = u.ticket
|
||||
if r:
|
||||
u.update_columns(d=r)
|
||||
u.update_columns(d=r)
|
||||
return success_return(r)
|
||||
|
||||
|
||||
|
||||
@@ -134,11 +134,12 @@ class APIUser(UserOnline):
|
||||
x = self.c.fetchone()
|
||||
if x is None:
|
||||
raise NoData('The user `%s` does not exist.' %
|
||||
self.name, api_error_code=-201)
|
||||
self.name, api_error_code=-201, status=401)
|
||||
if x[1] == '':
|
||||
raise UserBan('The user `%s` is banned.' % self.name)
|
||||
if self.hash_pwd != x[1]:
|
||||
raise NoAccess('The password is incorrect.', api_error_code=-201)
|
||||
raise NoAccess('The password is incorrect.',
|
||||
api_error_code=-201, status=401)
|
||||
|
||||
self.user_id = x[0]
|
||||
now = int(time() * 1000)
|
||||
|
||||
@@ -10,6 +10,8 @@ class Config:
|
||||
USE_PROXY_FIX = False
|
||||
USE_CORS = False
|
||||
|
||||
SONG_FILE_HASH_PRE_CALCULATE = True
|
||||
|
||||
GAME_API_PREFIX = '/join/21'
|
||||
|
||||
ALLOW_APPVERSION = [] # list[str]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from .config_manager import Config
|
||||
|
||||
ARCAEA_SERVER_VERSION = 'v2.10.2'
|
||||
ARCAEA_SERVER_VERSION = 'v2.10.3'
|
||||
|
||||
|
||||
class Constant:
|
||||
|
||||
@@ -5,6 +5,7 @@ from time import time
|
||||
|
||||
from flask import url_for
|
||||
|
||||
from .config_manager import Config
|
||||
from .constant import Constant
|
||||
from .error import NoAccess
|
||||
from .limiter import ArcLimiter
|
||||
@@ -167,7 +168,7 @@ class UserDownload:
|
||||
|
||||
class DownloadList(UserDownload):
|
||||
'''
|
||||
下载列表类\
|
||||
下载列表类
|
||||
properties: `user` - `User`类或子类的实例
|
||||
'''
|
||||
|
||||
@@ -184,10 +185,11 @@ class DownloadList(UserDownload):
|
||||
def initialize_cache(cls) -> None:
|
||||
'''初始化歌曲数据缓存,包括md5、文件目录遍历、解析songlist'''
|
||||
SonglistParser()
|
||||
x = cls()
|
||||
x.url_flag = False
|
||||
x.add_songs()
|
||||
del x
|
||||
if Config.SONG_FILE_HASH_PRE_CALCULATE:
|
||||
x = cls()
|
||||
x.url_flag = False
|
||||
x.add_songs()
|
||||
del x
|
||||
|
||||
@staticmethod
|
||||
def clear_all_cache() -> None:
|
||||
@@ -212,9 +214,10 @@ class DownloadList(UserDownload):
|
||||
def get_one_song_file_names(song_id: str) -> list:
|
||||
'''获取一个歌曲文件夹下的所有合法文件名,有lru缓存'''
|
||||
r = []
|
||||
for i in os.listdir(os.path.join(Constant.SONG_FILE_FOLDER_PATH, song_id)):
|
||||
if os.path.isfile(os.path.join(Constant.SONG_FILE_FOLDER_PATH, song_id, i)) and SonglistParser.is_available_file(song_id, i):
|
||||
r.append(i)
|
||||
for i in os.scandir(os.path.join(Constant.SONG_FILE_FOLDER_PATH, song_id)):
|
||||
file_name = i.name
|
||||
if i.is_file() and SonglistParser.is_available_file(song_id, file_name):
|
||||
r.append(file_name)
|
||||
return r
|
||||
|
||||
def add_one_song(self, song_id: str) -> None:
|
||||
@@ -265,7 +268,7 @@ class DownloadList(UserDownload):
|
||||
@lru_cache()
|
||||
def get_all_song_ids() -> list:
|
||||
'''获取全歌曲文件夹列表,有lru缓存'''
|
||||
return list(filter(lambda x: os.path.isdir(os.path.join(Constant.SONG_FILE_FOLDER_PATH, x)), os.listdir(Constant.SONG_FILE_FOLDER_PATH)))
|
||||
return [i.name for i in os.scandir(Constant.SONG_FILE_FOLDER_PATH) if i.is_dir()]
|
||||
|
||||
def add_songs(self, song_ids: list = None) -> None:
|
||||
'''添加一个或多个歌曲到下载列表,若`song_ids`为空,则添加所有歌曲'''
|
||||
|
||||
@@ -19,7 +19,9 @@ class InputError(ArcError):
|
||||
|
||||
class DataExist(ArcError):
|
||||
'''数据存在'''
|
||||
pass
|
||||
|
||||
def __init__(self, message=None, error_code=108, api_error_code=-4, extra_data=None, status=200) -> None:
|
||||
super().__init__(message, error_code, api_error_code, extra_data, status)
|
||||
|
||||
|
||||
class NoData(ArcError):
|
||||
|
||||
@@ -286,6 +286,8 @@ class FileChecker:
|
||||
self.logger.info("Start to initialize song data...")
|
||||
try:
|
||||
DownloadList.initialize_cache()
|
||||
if not Config.SONG_FILE_HASH_PRE_CALCULATE:
|
||||
self.logger.info('Song file hash pre-calculate is disabled.')
|
||||
self.logger.info('Complete!')
|
||||
except Exception as e:
|
||||
self.logger.error(format_exc())
|
||||
|
||||
@@ -1,18 +1,23 @@
|
||||
from .sql import Connect, Sql
|
||||
from .score import Score
|
||||
from .download import DownloadList
|
||||
from .save import SaveData
|
||||
from .score import Score
|
||||
from .sql import Connect, Sql
|
||||
from .user import User
|
||||
|
||||
|
||||
class BaseOperation:
|
||||
name: str = None
|
||||
_name: str = None
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
def __call__(self, *args, **kwargs) -> None:
|
||||
return self.run(*args, **kwargs)
|
||||
|
||||
def run(self, *args, **kwargs):
|
||||
def set_params(self, *args, **kwargs) -> None:
|
||||
pass
|
||||
|
||||
def run(self, *args, **kwargs) -> None:
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
@@ -20,7 +25,7 @@ class RefreshAllScoreRating(BaseOperation):
|
||||
'''
|
||||
刷新所有成绩的评分
|
||||
'''
|
||||
name = 'refresh_all_score_rating'
|
||||
_name = 'refresh_all_score_rating'
|
||||
|
||||
def run(self):
|
||||
# 追求效率,不用Song类,尽量不用对象
|
||||
@@ -59,9 +64,181 @@ class RefreshAllScoreRating(BaseOperation):
|
||||
class RefreshSongFileCache(BaseOperation):
|
||||
'''
|
||||
刷新歌曲文件缓存,包括文件hash缓存重建、文件目录重遍历、songlist重解析
|
||||
注意在设置里预先计算关闭的情况下,文件hash不会计算
|
||||
'''
|
||||
name = 'refresh_song_file_cache'
|
||||
_name = 'refresh_song_file_cache'
|
||||
|
||||
def run(self):
|
||||
DownloadList.clear_all_cache()
|
||||
DownloadList.initialize_cache()
|
||||
|
||||
|
||||
class SaveUpdateScore(BaseOperation):
|
||||
'''
|
||||
云存档更新成绩,是覆盖式更新\
|
||||
提供user参数时,只更新该用户的成绩,否则更新所有用户的成绩
|
||||
'''
|
||||
_name = 'save_update_score'
|
||||
|
||||
def __init__(self, user=None):
|
||||
self.user = user
|
||||
|
||||
def set_params(self, user_id: int = None, *args, **kwargs):
|
||||
if user_id is not None:
|
||||
self.user = User()
|
||||
self.user.user_id = int(user_id)
|
||||
|
||||
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)
|
||||
|
||||
|
||||
class UnlockUserItem(BaseOperation):
|
||||
'''
|
||||
全解锁/锁定用户物品\
|
||||
提供user参数时,只更新该用户的,否则更新所有用户的
|
||||
'''
|
||||
_name = 'unlock_user_item'
|
||||
|
||||
ALLOW_TYPES = ['single', 'pack', 'world_song',
|
||||
'course_banner', 'world_unlock']
|
||||
|
||||
def __init__(self, user=None, method: str = 'unlock', item_types: list = ['single', 'pack']):
|
||||
self.user = user
|
||||
self.set_params(method=method, item_types=item_types)
|
||||
|
||||
def set_params(self, user_id: int = None, method: str = 'unlock', item_types: list = ['single', 'pack'], *args, **kwargs):
|
||||
if user_id is not None:
|
||||
self.user = User()
|
||||
self.user.user_id = int(user_id)
|
||||
if method in ['unlock', 'lock']:
|
||||
self.method = method
|
||||
if isinstance(item_types, list) and all([i in self.ALLOW_TYPES for i in item_types]):
|
||||
self.item_types = item_types
|
||||
|
||||
def run(self):
|
||||
if self.user is not None and self.user.user_id is not None:
|
||||
if self.method == 'unlock':
|
||||
self._one_user_insert()
|
||||
else:
|
||||
self._one_user_delete()
|
||||
else:
|
||||
if self.method == 'unlock':
|
||||
self._all_insert()
|
||||
else:
|
||||
self._all_delete()
|
||||
|
||||
def _one_user_insert(self):
|
||||
with Connect() as c:
|
||||
c.execute(
|
||||
f'''select item_id, type from item where type in ({','.join(['?'] * len(self.item_types))})''', self.item_types)
|
||||
sql_list = [(self.user.user_id, i[0], i[1])
|
||||
for i in c.fetchall()]
|
||||
c.executemany(
|
||||
'''insert or ignore into user_item values (?, ?, ?, 1)''', sql_list)
|
||||
|
||||
def _all_insert(self):
|
||||
with Connect() as c:
|
||||
c.execute('''select user_id from user''')
|
||||
x = c.fetchall()
|
||||
c.execute(
|
||||
f'''select item_id, type from item where type in ({','.join(['?'] * len(self.item_types))})''', self.item_types)
|
||||
y = c.fetchall()
|
||||
sql_list = [(i[0], j[0], j[1])
|
||||
for i in x for j in y]
|
||||
c.executemany(
|
||||
'''insert or ignore into user_item values (?, ?, ?, 1)''', sql_list)
|
||||
|
||||
def _one_user_delete(self):
|
||||
with Connect() as c:
|
||||
c.execute(
|
||||
f'''delete from user_item where user_id = ? and type in ({','.join(['?'] * len(self.item_types))})''', (self.user.user_id, *self.item_types))
|
||||
|
||||
def _all_delete(self):
|
||||
with Connect() as c:
|
||||
c.execute(
|
||||
f'''delete from user_item where type in ({','.join(['?'] * len(self.item_types))})''', self.item_types)
|
||||
|
||||
@@ -22,6 +22,8 @@ class Purchase:
|
||||
|
||||
self.items: list = []
|
||||
|
||||
# TODO: "discount_reason": "extend"
|
||||
|
||||
@property
|
||||
def price_displayed(self) -> int:
|
||||
'''
|
||||
|
||||
@@ -3,7 +3,7 @@ from time import time
|
||||
|
||||
from .config_manager import Config
|
||||
from .constant import Constant
|
||||
from .error import InputError
|
||||
from .error import InputError, NoData
|
||||
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:
|
||||
'''
|
||||
parameter: `user` - `User`类或子类的实例
|
||||
|
||||
@@ -102,7 +102,7 @@ class Score:
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def calculate_rating(defnum: int, score: int) -> float:
|
||||
def calculate_rating(defnum: float, score: int) -> float:
|
||||
'''计算rating,谱面定数小于等于0视为Unrank,返回值会为-1,这里的defnum = Chart const'''
|
||||
if not defnum or defnum <= 0:
|
||||
# 谱面没定数或者定数小于等于0被视作Unrank
|
||||
|
||||
@@ -43,7 +43,7 @@ class Chart:
|
||||
|
||||
|
||||
class Song:
|
||||
def __init__(self, c=None, song_id=None) -> None:
|
||||
def __init__(self, c=None, song_id: str = None) -> None:
|
||||
self.c = c
|
||||
self.song_id: str = song_id
|
||||
self.name: str = None
|
||||
@@ -68,10 +68,39 @@ class Song:
|
||||
self.charts[3].defnum = x[5]
|
||||
return self
|
||||
|
||||
def from_dict(self, d: dict) -> 'Song':
|
||||
self.song_id = d['song_id']
|
||||
self.name = d.get('name', '')
|
||||
self.charts = [Chart(self.c, self.song_id, 0), Chart(self.c, self.song_id, 1), Chart(
|
||||
self.c, self.song_id, 2), Chart(self.c, self.song_id, 3)]
|
||||
for i in range(4):
|
||||
self.charts[i].defnum = -10
|
||||
for chart in d['charts']:
|
||||
self.charts[chart['difficulty']].defnum = round(
|
||||
chart['chart_const'] * 10)
|
||||
return self
|
||||
|
||||
def delete(self) -> None:
|
||||
self.c.execute(
|
||||
'''delete from chart where song_id=?''', (self.song_id,))
|
||||
|
||||
def update(self) -> None:
|
||||
'''全部更新'''
|
||||
self.c.execute(
|
||||
'''update chart set name=?, rating_pst=?, rating_prs=?, rating_ftr=?, rating_byn=? where song_id=?''', (self.name, self.charts[0].defnum, self.charts[1].defnum, self.charts[2].defnum, self.charts[3].defnum, self.song_id))
|
||||
|
||||
def insert(self) -> None:
|
||||
self.c.execute(
|
||||
'''insert into chart values (?,?,?,?,?,?)''', (self.song_id, self.name, self.charts[0].defnum, self.charts[1].defnum, self.charts[2].defnum, self.charts[3].defnum))
|
||||
|
||||
def select_exists(self, song_id: str = None) -> bool:
|
||||
if song_id is not None:
|
||||
self.song_id = song_id
|
||||
|
||||
self.c.execute(
|
||||
'''select exists(select * from chart where song_id=?)''', (self.song_id,))
|
||||
return bool(self.c.fetchone()[0])
|
||||
|
||||
def select(self, song_id: str = None) -> 'Song':
|
||||
if song_id is not None:
|
||||
self.song_id = song_id
|
||||
@@ -80,6 +109,6 @@ class Song:
|
||||
'a': self.song_id})
|
||||
x = self.c.fetchone()
|
||||
if x is None:
|
||||
raise NoData('The song `%s` does not exist.' % self.song_id)
|
||||
raise NoData(f'The song `{self.song_id}` does not exist.')
|
||||
|
||||
return self.from_list(x)
|
||||
|
||||
@@ -243,8 +243,9 @@ class UserLogin(User):
|
||||
'name': self.name})
|
||||
x = self.c.fetchone()
|
||||
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)
|
||||
if x[2] is not None and x[2] != '':
|
||||
# 自动封号检查
|
||||
@@ -255,14 +256,14 @@ class UserLogin(User):
|
||||
|
||||
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:
|
||||
raise NoAccess('Wrong password.', 104)
|
||||
|
||||
self.user_id = str(x[0])
|
||||
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(
|
||||
'''select login_device from login where user_id = :user_id''', {"user_id": self.user_id})
|
||||
|
||||
@@ -13,13 +13,11 @@ def md5(code: str) -> str:
|
||||
|
||||
|
||||
def get_file_md5(file_path: str) -> str:
|
||||
'''计算文件MD5'''
|
||||
if not os.path.isfile(file_path):
|
||||
return None
|
||||
'''计算文件MD5,假设是文件'''
|
||||
myhash = hashlib.md5()
|
||||
with open(file_path, 'rb') as f:
|
||||
while True:
|
||||
b = f.read(8096)
|
||||
b = f.read(8192)
|
||||
if not b:
|
||||
break
|
||||
myhash.update(b)
|
||||
|
||||
@@ -50,6 +50,7 @@ class Step:
|
||||
self.restrict_id: str = None
|
||||
self.restrict_ids: list = []
|
||||
self.restrict_type: str = None
|
||||
self.restrict_difficulty: int = None
|
||||
self.step_type: list = None
|
||||
self.speed_limit_value: int = None
|
||||
self.plus_stamina_value: int = None
|
||||
@@ -61,12 +62,14 @@ class Step:
|
||||
}
|
||||
if self.items:
|
||||
r['items'] = [i.to_dict() for i in self.items]
|
||||
if self.restrict_id:
|
||||
r['restrict_id'] = self.restrict_id
|
||||
if self.restrict_ids:
|
||||
r['restrict_ids'] = self.restrict_ids
|
||||
if self.restrict_type:
|
||||
r['restrict_type'] = self.restrict_type
|
||||
if self.restrict_id:
|
||||
r['restrict_id'] = self.restrict_id
|
||||
if self.restrict_ids:
|
||||
r['restrict_ids'] = self.restrict_ids
|
||||
if self.restrict_difficulty is not None:
|
||||
r['restrict_difficulty'] = self.restrict_difficulty
|
||||
if self.step_type:
|
||||
r['step_type'] = self.step_type
|
||||
if self.speed_limit_value:
|
||||
@@ -82,6 +85,7 @@ class Step:
|
||||
self.restrict_id = d.get('restrict_id')
|
||||
self.restrict_ids = d.get('restrict_ids')
|
||||
self.restrict_type = d.get('restrict_type')
|
||||
self.restrict_difficulty = d.get('restrict_difficulty')
|
||||
self.step_type = d.get('step_type')
|
||||
self.speed_limit_value = d.get('speed_limit_value')
|
||||
self.plus_stamina_value = d.get('plus_stamina_value')
|
||||
@@ -102,7 +106,7 @@ class Map:
|
||||
self.available_from: int = None
|
||||
self.available_to: int = None
|
||||
self.is_repeatable: bool = None
|
||||
self.require_id: str = None
|
||||
self.require_id: 'str | list[str]' = None
|
||||
self.require_type: str = None
|
||||
self.require_value: int = None
|
||||
self.coordinate: str = None
|
||||
@@ -111,6 +115,9 @@ class Map:
|
||||
self.steps: list = []
|
||||
self.__rewards: list = None
|
||||
|
||||
self.require_localunlock_songid: str = None
|
||||
self.require_localunlock_challengeid: str = None
|
||||
|
||||
@property
|
||||
def rewards(self) -> list:
|
||||
if self.__rewards is None:
|
||||
@@ -151,6 +158,8 @@ class Map:
|
||||
'custom_bg': self.custom_bg,
|
||||
'stamina_cost': self.stamina_cost,
|
||||
'step_count': self.step_count,
|
||||
'require_localunlock_songid': self.require_localunlock_songid,
|
||||
'require_localunlock_challengeid': self.require_localunlock_challengeid,
|
||||
'steps': [s.to_dict() for s in self.steps],
|
||||
}
|
||||
|
||||
@@ -170,6 +179,8 @@ class Map:
|
||||
self.coordinate = raw_dict.get('coordinate')
|
||||
self.custom_bg = raw_dict.get('custom_bg', '')
|
||||
self.stamina_cost = raw_dict.get('stamina_cost')
|
||||
self.require_localunlock_songid = raw_dict.get('require_localunlock_songid', '')
|
||||
self.require_localunlock_challengeid = raw_dict.get('require_localunlock_challengeid', '')
|
||||
self.steps = [Step().from_dict(s) for s in raw_dict.get('steps')]
|
||||
return self
|
||||
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
class InitData:
|
||||
char = ['hikari', 'tairitsu', 'kou', 'sapphire', 'lethe', 'hikari&tairitsu(reunion)', 'Tairitsu(Axium)', 'Tairitsu(Grievous Lady)', 'stella', 'Hikari & Fisica', 'ilith', 'eto', 'luna', 'shirabe', 'Hikari(Zero)', 'Hikari(Fracture)', 'Hikari(Summer)', 'Tairitsu(Summer)', 'Tairitsu & Trin',
|
||||
'ayu', 'Eto & Luna', 'yume', 'Seine & Hikari', 'saya', 'Tairitsu & Chuni Penguin', 'Chuni Penguin', 'haruna', 'nono', 'MTA-XXX', 'MDA-21', 'kanae', 'Hikari(Fantasia)', 'Tairitsu(Sonata)', 'sia', 'DORO*C', 'Tairitsu(Tempest)', 'brillante', 'Ilith(Summer)', 'etude', 'Alice & Tenniel', 'Luna & Mia', 'areus', 'seele', 'isabelle', 'mir', 'lagrange', 'linka', 'nami', 'Saya & Elizabeth', 'lily', 'kanae(midsummer)', 'alice&tenniel(minuet)', 'tairitsu(elegy)', 'marija', 'vita', 'hikari(fatalis)', 'saki', 'setsuna', 'amane', 'kou(winter)']
|
||||
'ayu', 'Eto & Luna', 'yume', 'Seine & Hikari', 'saya', 'Tairitsu & Chuni Penguin', 'Chuni Penguin', 'haruna', 'nono', 'MTA-XXX', 'MDA-21', 'kanae', 'Hikari(Fantasia)', 'Tairitsu(Sonata)', 'sia', 'DORO*C', 'Tairitsu(Tempest)', 'brillante', 'Ilith(Summer)', 'etude', 'Alice & Tenniel', 'Luna & Mia', 'areus', 'seele', 'isabelle', 'mir', 'lagrange', 'linka', 'nami', 'Saya & Elizabeth', 'lily', 'kanae(midsummer)', 'alice&tenniel(minuet)', 'tairitsu(elegy)', 'marija', 'vita', 'hikari(fatalis)', 'saki', 'setsuna', 'amane', 'kou(winter)', 'lagrange(aria)', 'lethe(apophenia)']
|
||||
|
||||
skill_id = ['gauge_easy', '', '', '', 'note_mirror', 'skill_reunion', '', 'gauge_hard', 'frag_plus_10_pack_stellights', 'gauge_easy|frag_plus_15_pst&prs', 'gauge_hard|fail_frag_minus_100', 'frag_plus_5_side_light', 'visual_hide_hp', 'frag_plus_5_side_conflict', 'challenge_fullcombo_0gauge', 'gauge_overflow', 'gauge_easy|note_mirror', 'note_mirror', 'visual_tomato_pack_tonesphere',
|
||||
'frag_rng_ayu', 'gaugestart_30|gaugegain_70', 'combo_100-frag_1', 'audio_gcemptyhit_pack_groovecoaster', 'gauge_saya', 'gauge_chuni', 'kantandeshou', 'gauge_haruna', 'frags_nono', 'gauge_pandora', 'gauge_regulus', 'omatsuri_daynight', '', '', 'sometimes(note_mirror|frag_plus_5)', 'scoreclear_aa|visual_scoregauge', 'gauge_tempest', 'gauge_hard', 'gauge_ilith_summer', '', 'note_mirror|visual_hide_far', 'frags_ongeki', 'gauge_areus', 'gauge_seele', 'gauge_isabelle', 'gauge_exhaustion', 'skill_lagrange', 'gauge_safe_10', 'frags_nami', 'skill_elizabeth', 'skill_lily', 'skill_kanae_midsummer', '', '', 'visual_ghost_skynotes', 'skill_vita', 'skill_fatalis', 'frags_ongeki_slash', 'frags_ongeki_hard', 'skill_amane', 'skill_kou_winter']
|
||||
'frag_rng_ayu', 'gaugestart_30|gaugegain_70', 'combo_100-frag_1', 'audio_gcemptyhit_pack_groovecoaster', 'gauge_saya', 'gauge_chuni', 'kantandeshou', 'gauge_haruna', 'frags_nono', 'gauge_pandora', 'gauge_regulus', 'omatsuri_daynight', '', '', 'sometimes(note_mirror|frag_plus_5)', 'scoreclear_aa|visual_scoregauge', 'gauge_tempest', 'gauge_hard', 'gauge_ilith_summer', '', 'note_mirror|visual_hide_far', 'frags_ongeki', 'gauge_areus', 'gauge_seele', 'gauge_isabelle', 'gauge_exhaustion', 'skill_lagrange', 'gauge_safe_10', 'frags_nami', 'skill_elizabeth', 'skill_lily', 'skill_kanae_midsummer', '', '', 'visual_ghost_skynotes', 'skill_vita', 'skill_fatalis', 'frags_ongeki_slash', 'frags_ongeki_hard', 'skill_amane', 'skill_kou_winter', '', 'gauge_hard|note_mirror']
|
||||
|
||||
skill_id_uncap = ['', '', 'frags_kou', '', 'visual_ink', '', '', '', '', '', '', 'eto_uncap', 'luna_uncap', 'shirabe_entry_fee',
|
||||
'', '', '', '', '', 'ayu_uncap', '', 'frags_yume', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '']
|
||||
'', '', '', '', '', 'ayu_uncap', '', 'frags_yume', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '']
|
||||
|
||||
skill_unlock_level = [0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 8, 0, 14, 0, 0, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 8, 0, 0, 0]
|
||||
0, 0, 0, 8, 0, 14, 0, 0, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 8, 0, 0, 0, 0, 0]
|
||||
|
||||
frag1 = [55, 55, 60, 50, 47, 79, 47, 57, 41, 22, 50, 54, 60, 56, 78, 42, 41, 61, 52, 50, 52, 32,
|
||||
42, 55, 45, 58, 43, 0.5, 68, 50, 62, 45, 45, 52, 44, 27, 59, 0, 45, 50, 50, 47, 47, 61, 43, 42, 38, 25, 58, 50, 61, 45, 45, 38, 34, 27, 18, 56, 47, 30]
|
||||
42, 55, 45, 58, 43, 0.5, 68, 50, 62, 45, 45, 52, 44, 27, 59, 0, 45, 50, 50, 47, 47, 61, 43, 42, 38, 25, 58, 50, 61, 45, 45, 38, 34, 27, 18, 56, 47, 30, 0, 57]
|
||||
|
||||
prog1 = [35, 55, 47, 50, 60, 70, 60, 70, 58, 45, 70, 45, 42, 46, 61, 67, 49, 44, 28, 45, 24, 46, 52,
|
||||
59, 62, 33, 58, 25, 63, 69, 50, 45, 45, 51, 34, 70, 62, 70, 45, 32, 32, 61, 47, 47, 37, 42, 50, 50, 45, 41, 61, 45, 45, 58, 50, 130, 18, 57, 55, 50]
|
||||
59, 62, 33, 58, 25, 63, 69, 50, 45, 45, 51, 34, 70, 62, 70, 45, 32, 32, 61, 47, 47, 37, 42, 50, 50, 45, 41, 61, 45, 45, 58, 50, 130, 18, 57, 55, 50, 0, 70]
|
||||
|
||||
overdrive1 = [35, 55, 25, 50, 47, 70, 72, 57, 41, 7, 10, 32, 65, 31, 61, 53, 31, 47, 38, 12, 39, 18,
|
||||
48, 65, 45, 55, 44, 25, 46, 44, 33, 45, 45, 37, 25, 27, 50, 20, 45, 63, 21, 47, 61, 47, 65, 80, 38, 30, 49, 15, 34, 45, 45, 38, 67, 120, 44, 33, 55, 50]
|
||||
48, 65, 45, 55, 44, 25, 46, 44, 33, 45, 45, 37, 25, 27, 50, 20, 45, 63, 21, 47, 61, 47, 65, 80, 38, 30, 49, 15, 34, 45, 45, 38, 67, 120, 44, 33, 55, 50, 0, 57]
|
||||
|
||||
frag20 = [78, 80, 90, 75, 70, 79, 70, 79, 65, 40, 50, 80, 90, 82, 0, 61, 67, 92, 85, 50, 86, 52,
|
||||
65, 85, 67, 88, 64, 0.5, 95, 70, 95, 50, 80, 87, 71, 50, 85, 0, 80, 75, 50, 70, 70, 90, 65, 80, 61, 50, 68, 60, 90, 67, 50, 60, 51, 50, 35, 85, 47, 50]
|
||||
@@ -64,7 +64,7 @@ class InitData:
|
||||
'core_ambivalent', 'core_scarlet', 'core_groove', 'core_generic', 'core_binary', 'core_colorful', 'core_course_skip_purchase']
|
||||
|
||||
world_songs = ["babaroque", "shadesoflight", "kanagawa", "lucifer", "anokumene", "ignotus", "rabbitintheblackroom", "qualia", "redandblue", "bookmaker", "darakunosono", "espebranch", "blacklotus", "givemeanightmare", "vividtheory", "onefr", "gekka", "vexaria3", "infinityheaven3", "fairytale3", "goodtek3", "suomi", "rugie", "faintlight", "harutopia", "goodtek", "dreaminattraction", "syro", "diode", "freefall", "grimheart", "blaster",
|
||||
"cyberneciacatharsis", "monochromeprincess", "revixy", "vector", "supernova", "nhelv", "purgatorium3", "dement3", "crossover", "guardina", "axiumcrisis", "worldvanquisher", "sheriruth", "pragmatism", "gloryroad", "etherstrike", "corpssansorganes", "lostdesire", "blrink", "essenceoftwilight", "lapis", "solitarydream", "lumia3", "purpleverse", "moonheart3", "glow", "enchantedlove", "take", "lifeispiano", "vandalism", "nexttoyou3", "lostcivilization3", "turbocharger", "bookmaker3", "laqryma3", "kyogenkigo", "hivemind", "seclusion", "quonwacca3", "bluecomet", "energysynergymatrix", "gengaozo", "lastendconductor3", "antithese3", "qualia3", "kanagawa3", "heavensdoor3", "pragmatism3", "nulctrl", "avril", "ddd", "merlin3", "omakeno3", "nekonote", "sanskia", 'altair', 'mukishitsu', 'trapcrow', 'redandblue3', 'ignotus3', 'singularity3', 'dropdead3', 'arcahv', 'freefall3', 'partyvinyl3']
|
||||
"cyberneciacatharsis", "monochromeprincess", "revixy", "vector", "supernova", "nhelv", "purgatorium3", "dement3", "crossover", "guardina", "axiumcrisis", "worldvanquisher", "sheriruth", "pragmatism", "gloryroad", "etherstrike", "corpssansorganes", "lostdesire", "blrink", "essenceoftwilight", "lapis", "solitarydream", "lumia3", "purpleverse", "moonheart3", "glow", "enchantedlove", "take", "lifeispiano", "vandalism", "nexttoyou3", "lostcivilization3", "turbocharger", "bookmaker3", "laqryma3", "kyogenkigo", "hivemind", "seclusion", "quonwacca3", "bluecomet", "energysynergymatrix", "gengaozo", "lastendconductor3", "antithese3", "qualia3", "kanagawa3", "heavensdoor3", "pragmatism3", "nulctrl", "avril", "ddd", "merlin3", "omakeno3", "nekonote", "sanskia", 'altair', 'mukishitsu', 'trapcrow', 'redandblue3', 'ignotus3', 'singularity3', 'dropdead3', 'arcahv', 'freefall3', 'partyvinyl3', 'tsukinimurakumo', 'mantis', 'worldfragments', 'astrawalkthrough', 'chronicle']
|
||||
|
||||
world_unlocks = ["scenery_chap1", "scenery_chap2",
|
||||
"scenery_chap3", "scenery_chap4", "scenery_chap5", "scenery_chap6", "scenery_chap7"]
|
||||
|
||||
@@ -327,11 +327,10 @@
|
||||
"is_available": true
|
||||
}
|
||||
],
|
||||
"price": 0,
|
||||
"price": 700,
|
||||
"orig_price": 700,
|
||||
"discount_from": 1646784000000,
|
||||
"discount_to": 1647388799000,
|
||||
"discount_reason": "extend"
|
||||
"discount_to": 1647388799000
|
||||
},
|
||||
{
|
||||
"name": "alice",
|
||||
@@ -595,6 +594,11 @@
|
||||
"id": "finale",
|
||||
"is_available": true
|
||||
},
|
||||
{
|
||||
"type": "pack",
|
||||
"id": "epilogue",
|
||||
"is_available": true
|
||||
},
|
||||
{
|
||||
"type": "core",
|
||||
"amount": 5,
|
||||
@@ -622,5 +626,25 @@
|
||||
],
|
||||
"orig_price": 500,
|
||||
"price": 500
|
||||
},
|
||||
{
|
||||
"name": "extend_2",
|
||||
"items": [
|
||||
{
|
||||
"type": "pack",
|
||||
"id": "extend_2",
|
||||
"is_available": true
|
||||
},
|
||||
{
|
||||
"type": "core",
|
||||
"amount": 7,
|
||||
"id": "core_generic",
|
||||
"is_available": true
|
||||
}
|
||||
],
|
||||
"orig_price": 700,
|
||||
"price": 700,
|
||||
"discount_from": 1646784000000,
|
||||
"discount_to": 1647388799000
|
||||
}
|
||||
]
|
||||
@@ -28,8 +28,8 @@
|
||||
],
|
||||
"price": 100,
|
||||
"orig_price": 100,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
"discount_from": 1646784000000,
|
||||
"discount_to": 1647388799000
|
||||
},
|
||||
{
|
||||
"name": "yourvoiceso",
|
||||
@@ -48,8 +48,8 @@
|
||||
],
|
||||
"price": 100,
|
||||
"orig_price": 100,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
"discount_from": 1646784000000,
|
||||
"discount_to": 1647388799000
|
||||
},
|
||||
{
|
||||
"name": "crosssoul",
|
||||
@@ -68,8 +68,8 @@
|
||||
],
|
||||
"price": 100,
|
||||
"orig_price": 100,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
"discount_from": 1646784000000,
|
||||
"discount_to": 1647388799000
|
||||
},
|
||||
{
|
||||
"name": "impurebird",
|
||||
@@ -88,8 +88,8 @@
|
||||
],
|
||||
"price": 100,
|
||||
"orig_price": 100,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
"discount_from": 1646784000000,
|
||||
"discount_to": 1647388799000
|
||||
},
|
||||
{
|
||||
"name": "auxesia",
|
||||
@@ -128,8 +128,8 @@
|
||||
],
|
||||
"price": 100,
|
||||
"orig_price": 100,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
"discount_from": 1646784000000,
|
||||
"discount_to": 1647388799000
|
||||
},
|
||||
{
|
||||
"name": "yozakurafubuki",
|
||||
@@ -148,8 +148,8 @@
|
||||
],
|
||||
"price": 100,
|
||||
"orig_price": 100,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
"discount_from": 1646784000000,
|
||||
"discount_to": 1647388799000
|
||||
},
|
||||
{
|
||||
"name": "surrender",
|
||||
@@ -168,8 +168,8 @@
|
||||
],
|
||||
"price": 100,
|
||||
"orig_price": 100,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
"discount_from": 1646784000000,
|
||||
"discount_to": 1647388799000
|
||||
},
|
||||
{
|
||||
"name": "metallicpunisher",
|
||||
@@ -224,8 +224,8 @@
|
||||
],
|
||||
"price": 100,
|
||||
"orig_price": 100,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
"discount_from": 1646784000000,
|
||||
"discount_to": 1647388799000
|
||||
},
|
||||
{
|
||||
"name": "callmyname",
|
||||
@@ -244,8 +244,8 @@
|
||||
],
|
||||
"price": 100,
|
||||
"orig_price": 100,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
"discount_from": 1646784000000,
|
||||
"discount_to": 1647388799000
|
||||
},
|
||||
{
|
||||
"name": "fallensquare",
|
||||
@@ -264,8 +264,8 @@
|
||||
],
|
||||
"price": 100,
|
||||
"orig_price": 100,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
"discount_from": 1646784000000,
|
||||
"discount_to": 1647388799000
|
||||
},
|
||||
{
|
||||
"name": "dropdead",
|
||||
@@ -304,8 +304,8 @@
|
||||
],
|
||||
"price": 100,
|
||||
"orig_price": 100,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
"discount_from": 1646784000000,
|
||||
"discount_to": 1647388799000
|
||||
},
|
||||
{
|
||||
"name": "astraltale",
|
||||
@@ -324,8 +324,8 @@
|
||||
],
|
||||
"price": 100,
|
||||
"orig_price": 100,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
"discount_from": 1646784000000,
|
||||
"discount_to": 1647388799000
|
||||
},
|
||||
{
|
||||
"name": "phantasia",
|
||||
@@ -380,8 +380,8 @@
|
||||
],
|
||||
"price": 100,
|
||||
"orig_price": 100,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
"discount_from": 1646784000000,
|
||||
"discount_to": 1647388799000
|
||||
},
|
||||
{
|
||||
"name": "dottodot",
|
||||
@@ -400,8 +400,8 @@
|
||||
],
|
||||
"price": 100,
|
||||
"orig_price": 100,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
"discount_from": 1646784000000,
|
||||
"discount_to": 1647388799000
|
||||
},
|
||||
{
|
||||
"name": "dreadnought",
|
||||
@@ -420,8 +420,8 @@
|
||||
],
|
||||
"price": 100,
|
||||
"orig_price": 100,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
"discount_from": 1646784000000,
|
||||
"discount_to": 1647388799000
|
||||
},
|
||||
{
|
||||
"name": "mirzam",
|
||||
@@ -440,8 +440,8 @@
|
||||
],
|
||||
"price": 100,
|
||||
"orig_price": 100,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
"discount_from": 1646784000000,
|
||||
"discount_to": 1647388799000
|
||||
},
|
||||
{
|
||||
"name": "heavenlycaress",
|
||||
@@ -460,8 +460,8 @@
|
||||
],
|
||||
"price": 100,
|
||||
"orig_price": 100,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
"discount_from": 1646784000000,
|
||||
"discount_to": 1647388799000
|
||||
},
|
||||
{
|
||||
"name": "filament",
|
||||
@@ -480,8 +480,8 @@
|
||||
],
|
||||
"price": 100,
|
||||
"orig_price": 100,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
"discount_from": 1646784000000,
|
||||
"discount_to": 1647388799000
|
||||
},
|
||||
{
|
||||
"name": "avantraze",
|
||||
@@ -518,8 +518,8 @@
|
||||
],
|
||||
"price": 100,
|
||||
"orig_price": 100,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
"discount_from": 1646784000000,
|
||||
"discount_to": 1647388799000
|
||||
},
|
||||
{
|
||||
"name": "saikyostronger",
|
||||
@@ -556,8 +556,8 @@
|
||||
],
|
||||
"price": 100,
|
||||
"orig_price": 100,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
"discount_from": 1646784000000,
|
||||
"discount_to": 1647388799000
|
||||
},
|
||||
{
|
||||
"name": "einherjar",
|
||||
@@ -576,8 +576,8 @@
|
||||
],
|
||||
"price": 100,
|
||||
"orig_price": 100,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
"discount_from": 1646784000000,
|
||||
"discount_to": 1647388799000
|
||||
},
|
||||
{
|
||||
"name": "laqryma",
|
||||
@@ -596,8 +596,8 @@
|
||||
],
|
||||
"price": 100,
|
||||
"orig_price": 100,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
"discount_from": 1646784000000,
|
||||
"discount_to": 1647388799000
|
||||
},
|
||||
{
|
||||
"name": "amygdata",
|
||||
@@ -616,8 +616,8 @@
|
||||
],
|
||||
"price": 100,
|
||||
"orig_price": 100,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
"discount_from": 1646784000000,
|
||||
"discount_to": 1647388799000
|
||||
},
|
||||
{
|
||||
"name": "altale",
|
||||
@@ -636,8 +636,8 @@
|
||||
],
|
||||
"price": 100,
|
||||
"orig_price": 100,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
"discount_from": 1646784000000,
|
||||
"discount_to": 1647388799000
|
||||
},
|
||||
{
|
||||
"name": "feelssoright",
|
||||
@@ -656,8 +656,8 @@
|
||||
],
|
||||
"price": 100,
|
||||
"orig_price": 100,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
"discount_from": 1646784000000,
|
||||
"discount_to": 1647388799000
|
||||
},
|
||||
{
|
||||
"name": "scarletcage",
|
||||
@@ -712,8 +712,8 @@
|
||||
],
|
||||
"price": 100,
|
||||
"orig_price": 100,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
"discount_from": 1646784000000,
|
||||
"discount_to": 1647388799000
|
||||
},
|
||||
{
|
||||
"name": "badtek",
|
||||
@@ -732,8 +732,8 @@
|
||||
],
|
||||
"price": 100,
|
||||
"orig_price": 100,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
"discount_from": 1646784000000,
|
||||
"discount_to": 1647388799000
|
||||
},
|
||||
{
|
||||
"name": "maliciousmischance",
|
||||
@@ -788,8 +788,8 @@
|
||||
],
|
||||
"price": 100,
|
||||
"orig_price": 100,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
"discount_from": 1646784000000,
|
||||
"discount_to": 1647388799000
|
||||
},
|
||||
{
|
||||
"name": "xeraphinite",
|
||||
@@ -807,7 +807,9 @@
|
||||
}
|
||||
],
|
||||
"orig_price": 100,
|
||||
"price": 100
|
||||
"price": 100,
|
||||
"discount_from": 1646784000000,
|
||||
"discount_to": 1647388799000
|
||||
},
|
||||
{
|
||||
"name": "xanatos",
|
||||
@@ -825,7 +827,9 @@
|
||||
}
|
||||
],
|
||||
"price": 100,
|
||||
"orig_price": 100
|
||||
"orig_price": 100,
|
||||
"discount_from": 1646784000000,
|
||||
"discount_to": 1647388799000
|
||||
},
|
||||
{
|
||||
"name": "attraqtia",
|
||||
@@ -843,7 +847,9 @@
|
||||
}
|
||||
],
|
||||
"orig_price": 100,
|
||||
"price": 100
|
||||
"price": 100,
|
||||
"discount_from": 1646784000000,
|
||||
"discount_to": 1647388799000
|
||||
},
|
||||
{
|
||||
"name": "gimmedablood",
|
||||
@@ -879,7 +885,9 @@
|
||||
}
|
||||
],
|
||||
"orig_price": 100,
|
||||
"price": 100
|
||||
"price": 100,
|
||||
"discount_from": 1646784000000,
|
||||
"discount_to": 1647388799000
|
||||
},
|
||||
{
|
||||
"name": "theultimacy",
|
||||
@@ -897,7 +905,9 @@
|
||||
}
|
||||
],
|
||||
"orig_price": 100,
|
||||
"price": 100
|
||||
"price": 100,
|
||||
"discount_from": 1646784000000,
|
||||
"discount_to": 1647388799000
|
||||
},
|
||||
{
|
||||
"name": "rekkaresonanc",
|
||||
@@ -945,7 +955,9 @@
|
||||
}
|
||||
],
|
||||
"orig_price": 100,
|
||||
"price": 100
|
||||
"price": 100,
|
||||
"discount_from": 1646784000000,
|
||||
"discount_to": 1647388799000
|
||||
},
|
||||
{
|
||||
"name": "eveninginscarlet",
|
||||
@@ -981,7 +993,9 @@
|
||||
}
|
||||
],
|
||||
"orig_price": 100,
|
||||
"price": 100
|
||||
"price": 100,
|
||||
"discount_from": 1646784000000,
|
||||
"discount_to": 1647388799000
|
||||
},
|
||||
{
|
||||
"name": "goldenslaughterer",
|
||||
@@ -999,7 +1013,9 @@
|
||||
}
|
||||
],
|
||||
"orig_price": 100,
|
||||
"price": 100
|
||||
"price": 100,
|
||||
"discount_from": 1646784000000,
|
||||
"discount_to": 1647388799000
|
||||
},
|
||||
{
|
||||
"name": "redolentshape",
|
||||
@@ -1017,7 +1033,9 @@
|
||||
}
|
||||
],
|
||||
"orig_price": 100,
|
||||
"price": 100
|
||||
"price": 100,
|
||||
"discount_from": 1646784000000,
|
||||
"discount_to": 1647388799000
|
||||
},
|
||||
{
|
||||
"name": "summerfireworks",
|
||||
@@ -1056,29 +1074,11 @@
|
||||
"price": 100
|
||||
},
|
||||
{
|
||||
"name": "internetoverdose",
|
||||
"name": "kissinglucifer",
|
||||
"items": [
|
||||
{
|
||||
"type": "single",
|
||||
"id": "internetoverdose",
|
||||
"is_available": true
|
||||
},
|
||||
{
|
||||
"type": "core",
|
||||
"amount": 1,
|
||||
"id": "core_generic",
|
||||
"is_available": true
|
||||
}
|
||||
],
|
||||
"orig_price": 100,
|
||||
"price": 100
|
||||
},
|
||||
{
|
||||
"name": "macromod",
|
||||
"items": [
|
||||
{
|
||||
"type": "single",
|
||||
"id": "macromod",
|
||||
"id": "kissinglucifer",
|
||||
"is_available": true
|
||||
},
|
||||
{
|
||||
@@ -1110,11 +1110,11 @@
|
||||
"price": 100
|
||||
},
|
||||
{
|
||||
"name": "kissinglucifer",
|
||||
"name": "macromod",
|
||||
"items": [
|
||||
{
|
||||
"type": "single",
|
||||
"id": "kissinglucifer",
|
||||
"id": "macromod",
|
||||
"is_available": true
|
||||
},
|
||||
{
|
||||
@@ -1128,11 +1128,11 @@
|
||||
"price": 100
|
||||
},
|
||||
{
|
||||
"name": "headbonkache",
|
||||
"name": "internetoverdose",
|
||||
"items": [
|
||||
{
|
||||
"type": "single",
|
||||
"id": "headbonkache",
|
||||
"id": "internetoverdose",
|
||||
"is_available": true
|
||||
},
|
||||
{
|
||||
@@ -1163,6 +1163,24 @@
|
||||
"orig_price": 100,
|
||||
"price": 100
|
||||
},
|
||||
{
|
||||
"name": "headbonkache",
|
||||
"items": [
|
||||
{
|
||||
"type": "single",
|
||||
"id": "headbonkache",
|
||||
"is_available": true
|
||||
},
|
||||
{
|
||||
"type": "core",
|
||||
"amount": 1,
|
||||
"id": "core_generic",
|
||||
"is_available": true
|
||||
}
|
||||
],
|
||||
"orig_price": 100,
|
||||
"price": 100
|
||||
},
|
||||
{
|
||||
"name": "picopicotranslation",
|
||||
"items": [
|
||||
@@ -1218,11 +1236,11 @@
|
||||
"price": 100
|
||||
},
|
||||
{
|
||||
"name": "freemyself",
|
||||
"name": "cocorocosmetic",
|
||||
"items": [
|
||||
{
|
||||
"type": "single",
|
||||
"id": "freemyself",
|
||||
"id": "cocorocosmetic",
|
||||
"is_available": true
|
||||
},
|
||||
{
|
||||
@@ -1236,11 +1254,11 @@
|
||||
"price": 100
|
||||
},
|
||||
{
|
||||
"name": "cocorocosmetic",
|
||||
"name": "freemyself",
|
||||
"items": [
|
||||
{
|
||||
"type": "single",
|
||||
"id": "cocorocosmetic",
|
||||
"id": "freemyself",
|
||||
"is_available": true
|
||||
},
|
||||
{
|
||||
@@ -1270,5 +1288,23 @@
|
||||
],
|
||||
"orig_price": 100,
|
||||
"price": 100
|
||||
},
|
||||
{
|
||||
"name": "nullapophenia",
|
||||
"items": [
|
||||
{
|
||||
"type": "single",
|
||||
"id": "nullapophenia",
|
||||
"is_available": true
|
||||
},
|
||||
{
|
||||
"type": "core",
|
||||
"amount": 1,
|
||||
"id": "core_generic",
|
||||
"is_available": true
|
||||
}
|
||||
],
|
||||
"orig_price": 100,
|
||||
"price": 100
|
||||
}
|
||||
]
|
||||
@@ -105,6 +105,12 @@ if Config.DEPLOY_MODE == 'waitress':
|
||||
f'{request.remote_addr} - - {request.method} {request.path} {response.status_code}')
|
||||
return response
|
||||
|
||||
# @app.before_request
|
||||
# def before_request():
|
||||
# print(request.path)
|
||||
# print(request.headers)
|
||||
# print(request.data)
|
||||
|
||||
|
||||
def tcp_server_run():
|
||||
if Config.DEPLOY_MODE == 'gevent':
|
||||
|
||||
@@ -16,10 +16,9 @@ bp = Blueprint('auth', __name__, url_prefix='/auth')
|
||||
@arc_try
|
||||
def login():
|
||||
headers = request.headers
|
||||
if 'AppVersion' in headers: # 版本检查
|
||||
if Config.ALLOW_APPVERSION:
|
||||
if headers['AppVersion'] not in Config.ALLOW_APPVERSION:
|
||||
raise NoAccess('Wrong app version.', 1203)
|
||||
if Config.ALLOW_APPVERSION: # 版本检查
|
||||
if 'AppVersion' not in headers or headers['AppVersion'] not in Config.ALLOW_APPVERSION:
|
||||
raise NoAccess('Invalid app version.', 1203)
|
||||
|
||||
request.form['grant_type']
|
||||
with Connect() as c:
|
||||
@@ -45,15 +44,17 @@ def auth_required(request):
|
||||
|
||||
headers = request.headers
|
||||
|
||||
if 'AppVersion' in headers: # 版本检查
|
||||
if Config.ALLOW_APPVERSION:
|
||||
if headers['AppVersion'] not in Config.ALLOW_APPVERSION:
|
||||
return error_return(NoAccess('Wrong app version.', 1203))
|
||||
if Config.ALLOW_APPVERSION: # 版本检查
|
||||
if 'AppVersion' not in headers or headers['AppVersion'] not in Config.ALLOW_APPVERSION:
|
||||
return error_return(NoAccess('Invalid app version.', 1203))
|
||||
|
||||
with Connect() as c:
|
||||
try:
|
||||
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()
|
||||
g.user = user
|
||||
except ArcError as e:
|
||||
|
||||
@@ -16,10 +16,10 @@ bp = Blueprint('user', __name__, url_prefix='/user')
|
||||
@bp.route('', methods=['POST']) # 注册接口
|
||||
@arc_try
|
||||
def register():
|
||||
if 'AppVersion' in request.headers: # 版本检查
|
||||
if Config.ALLOW_APPVERSION:
|
||||
if request.headers['AppVersion'] not in Config.ALLOW_APPVERSION:
|
||||
raise NoAccess('Wrong app version.', 1203)
|
||||
headers = request.headers
|
||||
if Config.ALLOW_APPVERSION: # 版本检查
|
||||
if 'AppVersion' not in headers or headers['AppVersion'] not in Config.ALLOW_APPVERSION:
|
||||
raise NoAccess('Invalid app version.', 1203)
|
||||
|
||||
with Connect() as c:
|
||||
new_user = UserRegister(c)
|
||||
|
||||
@@ -2,9 +2,10 @@ import os
|
||||
import time
|
||||
|
||||
from core.init import FileChecker
|
||||
from core.operation import RefreshAllScoreRating, RefreshSongFileCache
|
||||
from core.operation import RefreshAllScoreRating, RefreshSongFileCache, SaveUpdateScore, UnlockUserItem
|
||||
from core.rank import RankList
|
||||
from core.sql import Connect
|
||||
from core.user import User
|
||||
from flask import Blueprint, flash, redirect, render_template, request, url_for
|
||||
from werkzeug.utils import secure_filename
|
||||
|
||||
@@ -421,7 +422,7 @@ def all_character():
|
||||
def change_character():
|
||||
# 修改角色数据
|
||||
skill_ids = ['No_skill', 'gauge_easy', 'note_mirror', 'gauge_hard', 'frag_plus_10_pack_stellights', 'gauge_easy|frag_plus_15_pst&prs', 'gauge_hard|fail_frag_minus_100', 'frag_plus_5_side_light', 'visual_hide_hp', 'frag_plus_5_side_conflict', 'challenge_fullcombo_0gauge', 'gauge_overflow', 'gauge_easy|note_mirror', 'note_mirror', 'visual_tomato_pack_tonesphere',
|
||||
'frag_rng_ayu', 'gaugestart_30|gaugegain_70', 'combo_100-frag_1', 'audio_gcemptyhit_pack_groovecoaster', 'gauge_saya', 'gauge_chuni', 'kantandeshou', 'gauge_haruna', 'frags_nono', 'gauge_pandora', 'gauge_regulus', 'omatsuri_daynight', 'sometimes(note_mirror|frag_plus_5)', 'scoreclear_aa|visual_scoregauge', 'gauge_tempest', 'gauge_hard', 'gauge_ilith_summer', 'frags_kou', 'visual_ink', 'shirabe_entry_fee', 'frags_yume', 'note_mirror|visual_hide_far', 'frags_ongeki', 'gauge_areus', 'gauge_seele', 'gauge_isabelle', 'gauge_exhaustion', 'skill_lagrange', 'gauge_safe_10', 'frags_nami', 'skill_elizabeth', 'skill_lily', 'skill_kanae_midsummer', 'eto_uncap', 'luna_uncap', 'frags_preferred_song', 'visual_ghost_skynotes', 'ayu_uncap', 'skill_vita', 'skill_fatalis', 'skill_reunion', 'frags_ongeki_slash', 'frags_ongeki_hard', 'skill_amane']
|
||||
'frag_rng_ayu', 'gaugestart_30|gaugegain_70', 'combo_100-frag_1', 'audio_gcemptyhit_pack_groovecoaster', 'gauge_saya', 'gauge_chuni', 'kantandeshou', 'gauge_haruna', 'frags_nono', 'gauge_pandora', 'gauge_regulus', 'omatsuri_daynight', 'sometimes(note_mirror|frag_plus_5)', 'scoreclear_aa|visual_scoregauge', 'gauge_tempest', 'gauge_hard', 'gauge_ilith_summer', 'frags_kou', 'visual_ink', 'shirabe_entry_fee', 'frags_yume', 'note_mirror|visual_hide_far', 'frags_ongeki', 'gauge_areus', 'gauge_seele', 'gauge_isabelle', 'gauge_exhaustion', 'skill_lagrange', 'gauge_safe_10', 'frags_nami', 'skill_elizabeth', 'skill_lily', 'skill_kanae_midsummer', 'eto_uncap', 'luna_uncap', 'frags_preferred_song', 'visual_ghost_skynotes', 'ayu_uncap', 'skill_vita', 'skill_fatalis', 'skill_reunion', 'frags_ongeki_slash', 'frags_ongeki_hard', 'skill_amane', 'skill_kou_winter', 'gauge_hard|note_mirror']
|
||||
return render_template('web/changechar.html', skill_ids=skill_ids)
|
||||
|
||||
|
||||
@@ -605,7 +606,7 @@ def edit_user_purchase():
|
||||
if 'name' not in request.form and 'user_code' not in request.form:
|
||||
flag = False
|
||||
if method == '0':
|
||||
web.system.unlock_all_user_item(c)
|
||||
UnlockUserItem().run()
|
||||
else:
|
||||
c.execute(
|
||||
'''delete from user_item where type in ('pack', 'single')''')
|
||||
@@ -631,7 +632,9 @@ def edit_user_purchase():
|
||||
user_id = user_id[0]
|
||||
|
||||
if method == '0':
|
||||
web.system.unlock_user_item(c, user_id)
|
||||
x = UnlockUserItem()
|
||||
x.set_params(user_id=user_id)
|
||||
x.run()
|
||||
else:
|
||||
c.execute('''delete from user_item where type in ('pack', 'single') and user_id = :user_id''', {
|
||||
'user_id': user_id})
|
||||
@@ -937,7 +940,7 @@ def update_user_save():
|
||||
# 全修改
|
||||
if 'name' not in request.form and 'user_code' not in request.form:
|
||||
flag = False
|
||||
web.system.update_all_save(c)
|
||||
SaveUpdateScore().run()
|
||||
flash("全部用户存档同步成功 Successfully update all users' saves.")
|
||||
|
||||
else:
|
||||
@@ -957,7 +960,9 @@ def update_user_save():
|
||||
user_id = c.fetchone()
|
||||
if user_id:
|
||||
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.")
|
||||
|
||||
else:
|
||||
|
||||
@@ -40,41 +40,6 @@ def update_user_char(c):
|
||||
(j[0], i[0], i[1], exp, i[2], 0))
|
||||
|
||||
|
||||
def unlock_all_user_item(c):
|
||||
# 解锁所有用户购买
|
||||
|
||||
c.execute('''select user_id from user''')
|
||||
x = c.fetchall()
|
||||
c.execute('''select item_id, type from purchase_item''')
|
||||
y = c.fetchall()
|
||||
if x and y:
|
||||
for i in x:
|
||||
for j in y:
|
||||
c.execute('''select exists(select * from user_item where user_id=:a and item_id=:b and type=:c)''', {
|
||||
'a': i[0], 'b': j[0], 'c': j[1]})
|
||||
if c.fetchone() == (0,) and j[1] != 'character':
|
||||
c.execute('''insert into user_item values(:a,:b,:c,1)''', {
|
||||
'a': i[0], 'b': j[0], 'c': j[1]})
|
||||
|
||||
return
|
||||
|
||||
|
||||
def unlock_user_item(c, user_id):
|
||||
# 解锁用户购买
|
||||
|
||||
c.execute('''select item_id, type from purchase_item''')
|
||||
y = c.fetchall()
|
||||
|
||||
for j in y:
|
||||
c.execute('''select exists(select * from user_item where user_id=:a and item_id=:b and type=:c)''', {
|
||||
'a': user_id, 'b': j[0], 'c': j[1]})
|
||||
if c.fetchone() == (0,) and j[1] != 'character':
|
||||
c.execute('''insert into user_item values(:a,:b,:c,1)''', {
|
||||
'a': user_id, 'b': j[0], 'c': j[1]})
|
||||
|
||||
return
|
||||
|
||||
|
||||
def get_all_item():
|
||||
# 所有物品数据查询
|
||||
with Connect() as c:
|
||||
@@ -132,61 +97,6 @@ def get_all_purchase():
|
||||
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):
|
||||
# 添加一个奖励
|
||||
|
||||
|
||||
Reference in New Issue
Block a user