Fix a bug and add a new thing

- Add support for logging Arcaea's errors
- Fix a bug when world maps' data don't have some unnecessary parts the client of iOS may break down
This commit is contained in:
Lost-MSth
2022-07-16 19:50:07 +08:00
parent 6801af197f
commit 47f05cdf1e
23 changed files with 549 additions and 613 deletions

View File

@@ -1,8 +1,10 @@
import functools from functools import wraps
from traceback import format_exc
from core.api_user import APIUser from core.api_user import APIUser
from core.error import ArcError, NoAccess, PostError from core.error import ArcError, NoAccess, PostError
from core.sql import Connect from core.sql import Connect
from flask import current_app
from setting import Config from setting import Config
from .api_code import error_return from .api_code import error_return
@@ -11,7 +13,7 @@ from .api_code import error_return
def role_required(request, powers=[]): def role_required(request, powers=[]):
'''api token验证写成了修饰器''' '''api token验证写成了修饰器'''
def decorator(view): def decorator(view):
@functools.wraps(view) @wraps(view)
def wrapped_view(*args, **kwargs): def wrapped_view(*args, **kwargs):
try: try:
request.json # 检查请求json格式 request.json # 检查请求json格式
@@ -56,7 +58,7 @@ def request_json_handle(request, required_keys=[], optional_keys=[]):
''' '''
def decorator(view): def decorator(view):
@functools.wraps(view) @wraps(view)
def wrapped_view(*args, **kwargs): def wrapped_view(*args, **kwargs):
data = {} data = {}
@@ -73,3 +75,21 @@ def request_json_handle(request, required_keys=[], optional_keys=[]):
return wrapped_view return wrapped_view
return decorator return decorator
def api_try(view):
'''替代try/except记录`ArcError`为warning'''
@wraps(view)
def wrapped_view(*args, **kwargs):
try:
data = view(*args, **kwargs)
if data is None:
return error_return()
else:
return data
except ArcError as e:
if Config.ALLOW_WARNING_LOG:
current_app.logger.warning(format_exc())
return error_return(e, e.status)
return wrapped_view

View File

@@ -1,10 +1,10 @@
from core.error import ArcError, NoData from core.error import NoData
from core.song import Song from core.song import Song
from core.sql import Connect, Query, Sql from core.sql import Connect, Query, Sql
from flask import Blueprint, request from flask import Blueprint, request
from .api_auth import request_json_handle, role_required from .api_auth import api_try, request_json_handle, role_required
from .api_code import error_return, success_return from .api_code import success_return
from .constant import Constant from .constant import Constant
bp = Blueprint('songs', __name__, url_prefix='/songs') bp = Blueprint('songs', __name__, url_prefix='/songs')
@@ -12,37 +12,31 @@ bp = Blueprint('songs', __name__, url_prefix='/songs')
@bp.route('/<string:song_id>', methods=['GET']) @bp.route('/<string:song_id>', methods=['GET'])
@role_required(request, ['select', 'select_song_info']) @role_required(request, ['select', 'select_song_info'])
@api_try
def songs_song_get(user, song_id): def songs_song_get(user, song_id):
'''查询歌曲信息''' '''查询歌曲信息'''
with Connect() as c: with Connect() as c:
try: s = Song(c, song_id).select()
s = Song(c, song_id).select() return success_return(s.to_dict())
return success_return(s.to_dict())
except ArcError as e:
return error_return(e)
return error_return()
@bp.route('', methods=['GET']) @bp.route('', methods=['GET'])
@role_required(request, ['select', 'select_song_info']) @role_required(request, ['select', 'select_song_info'])
@request_json_handle(request, optional_keys=Constant.QUERY_KEYS) @request_json_handle(request, optional_keys=Constant.QUERY_KEYS)
@api_try
def songs_get(data, user): def songs_get(data, user):
'''查询全歌曲信息''' '''查询全歌曲信息'''
A = ['song_id', 'name'] A = ['song_id', 'name']
B = ['song_id', 'name', 'rating_pst', B = ['song_id', 'name', 'rating_pst',
'rating_prs', 'rating_ftr', 'rating_byn'] 'rating_prs', 'rating_ftr', 'rating_byn']
with Connect() as c: with Connect() as c:
try: query = Query(A, A, B).from_data(data)
query = Query(A, A, B).from_data(data) x = Sql(c).select('chart', query=query)
x = Sql(c).select('chart', query=query) r = []
r = [] for i in x:
for i in x: r.append(Song(c).from_list(i))
r.append(Song(c).from_list(i))
if not r: if not r:
raise NoData(api_error_code=-2) raise NoData(api_error_code=-2)
return success_return([x.to_dict() for x in r]) return success_return([x.to_dict() for x in r])
except ArcError as e:
return error_return(e)
return error_return()

View File

@@ -1,11 +1,11 @@
from base64 import b64decode from base64 import b64decode
from core.api_user import APIUser from core.api_user import APIUser
from core.error import ArcError, PostError from core.error import PostError
from core.sql import Connect from core.sql import Connect
from flask import Blueprint, request from flask import Blueprint, request
from .api_auth import request_json_handle, role_required from .api_auth import api_try, request_json_handle, role_required
from .api_code import error_return, success_return from .api_code import error_return, success_return
bp = Blueprint('token', __name__, url_prefix='/token') bp = Blueprint('token', __name__, url_prefix='/token')
@@ -13,6 +13,7 @@ bp = Blueprint('token', __name__, url_prefix='/token')
@bp.route('', methods=['POST']) @bp.route('', methods=['POST'])
@request_json_handle(request, required_keys=['auth']) @request_json_handle(request, required_keys=['auth'])
@api_try
def token_post(data): def token_post(data):
''' '''
登录获取token\ 登录获取token\
@@ -27,17 +28,14 @@ def token_post(data):
name, password = auth_decode.split(':', 1) name, password = auth_decode.split(':', 1)
with Connect() as c: with Connect() as c:
try: user = APIUser(c)
user = APIUser(c) user.login(name, password, request.remote_addr)
user.login(name, password, request.remote_addr) return success_return({'token': user.token, 'user_id': user.user_id})
return success_return({'token': user.token, 'user_id': user.user_id})
except ArcError as e:
return error_return(e)
return error_return()
@bp.route('', methods=['GET']) @bp.route('', methods=['GET'])
@role_required(request, ['select_me', 'select']) @role_required(request, ['select_me', 'select'])
@api_try
def token_get(user): def token_get(user):
'''判断登录有效性''' '''判断登录有效性'''
return success_return() return success_return()
@@ -45,13 +43,10 @@ def token_get(user):
@bp.route('', methods=['DELETE']) @bp.route('', methods=['DELETE'])
@role_required(request, ['change_me', 'select_me', 'select']) @role_required(request, ['change_me', 'select_me', 'select'])
@api_try
def token_delete(user): def token_delete(user):
'''登出''' '''登出'''
with Connect() as c: with Connect() as c:
try: user.c = c
user.c = c user.logout()
user.logout() return success_return()
return success_return()
except ArcError as e:
return error_return(e)
return error_return()

View File

@@ -1,10 +1,10 @@
from core.error import ArcError, InputError, NoAccess, NoData from core.error import InputError, NoAccess, NoData
from core.score import Potential, UserScoreList from core.score import Potential, UserScoreList
from core.sql import Connect, Query, Sql from core.sql import Connect, Query, Sql
from core.user import UserInfo, UserRegister from core.user import UserInfo, UserRegister
from flask import Blueprint, request from flask import Blueprint, request
from .api_auth import request_json_handle, role_required from .api_auth import api_try, request_json_handle, role_required
from .api_code import error_return, success_return from .api_code import error_return, success_return
from .constant import Constant from .constant import Constant
@@ -14,59 +14,54 @@ bp = Blueprint('users', __name__, url_prefix='/users')
@bp.route('', methods=['POST']) @bp.route('', methods=['POST'])
@role_required(request, ['change']) @role_required(request, ['change'])
@request_json_handle(request, ['name', 'password', 'email']) @request_json_handle(request, ['name', 'password', 'email'])
@api_try
def users_post(data, _): def users_post(data, _):
'''注册一个用户''' '''注册一个用户'''
with Connect() as c: with Connect() as c:
new_user = UserRegister(c) new_user = UserRegister(c)
try: new_user.set_name(data['name'])
new_user.set_name(data['name']) new_user.set_password(data['password'])
new_user.set_password(data['password']) new_user.set_email(data['email'])
new_user.set_email(data['email']) new_user.register()
new_user.register() return success_return({'user_id': new_user.user_id, 'user_code': new_user.user_code})
return success_return({'user_id': new_user.user_id, 'user_code': new_user.user_code})
except ArcError as e:
return error_return(e)
return error_return()
@bp.route('', methods=['GET']) @bp.route('', methods=['GET'])
@role_required(request, ['select']) @role_required(request, ['select'])
@request_json_handle(request, optional_keys=Constant.QUERY_KEYS) @request_json_handle(request, optional_keys=Constant.QUERY_KEYS)
@api_try
def users_get(data, user): def users_get(data, user):
'''查询全用户信息''' '''查询全用户信息'''
A = ['user_id', 'name', 'user_code'] A = ['user_id', 'name', 'user_code']
B = ['user_id', 'name', 'user_code', 'join_date', B = ['user_id', 'name', 'user_code', 'join_date',
'rating_ptt', 'time_played', 'ticket', 'world_rank_score'] 'rating_ptt', 'time_played', 'ticket', 'world_rank_score']
with Connect() as c: with Connect() as c:
try: query = Query(A, A, B).from_data(data)
query = Query(A, A, B).from_data(data) x = Sql(c).select('user', query=query)
x = Sql(c).select('user', query=query) r = []
r = [] for i in x:
for i in x: r.append(UserInfo(c).from_list(i))
r.append(UserInfo(c).from_list(i))
if not r: if not r:
raise NoData(api_error_code=-2) raise NoData(api_error_code=-2)
return success_return([{ return success_return([{
'user_id': x.user_id, 'user_id': x.user_id,
'name': x.name, 'name': x.name,
'join_date': x.join_date, 'join_date': x.join_date,
'user_code': x.user_code, 'user_code': x.user_code,
'rating_ptt': x.rating_ptt/100, 'rating_ptt': x.rating_ptt/100,
'character_id': x.character.character_id, 'character_id': x.character.character_id,
'is_char_uncapped': x.character.is_uncapped, 'is_char_uncapped': x.character.is_uncapped,
'is_char_uncapped_override': x.character.is_uncapped_override, 'is_char_uncapped_override': x.character.is_uncapped_override,
'is_hide_rating': x.is_hide_rating, 'is_hide_rating': x.is_hide_rating,
'ticket': x.ticket 'ticket': x.ticket
} for x in r]) } for x in r])
except ArcError as e:
return error_return(e)
return error_return()
@bp.route('/<int:user_id>', methods=['GET']) @bp.route('/<int:user_id>', methods=['GET'])
@role_required(request, ['select', 'select_me']) @role_required(request, ['select', 'select_me'])
@api_try
def users_user_get(user, user_id): def users_user_get(user, user_id):
'''查询用户信息''' '''查询用户信息'''
if user_id <= 0: if user_id <= 0:
@@ -76,16 +71,13 @@ def users_user_get(user, user_id):
return error_return(NoAccess('No permission', api_error_code=-1), 403) return error_return(NoAccess('No permission', api_error_code=-1), 403)
with Connect() as c: with Connect() as c:
try: u = UserInfo(c, user_id)
u = UserInfo(c, user_id) return success_return(u.to_dict())
return success_return(u.to_dict())
except ArcError as e:
return error_return(e)
return error_return()
@bp.route('/<int:user_id>/b30', methods=['GET']) @bp.route('/<int:user_id>/b30', methods=['GET'])
@role_required(request, ['select', 'select_me']) @role_required(request, ['select', 'select_me'])
@api_try
def users_user_b30_get(user, user_id): def users_user_b30_get(user, user_id):
'''查询用户b30''' '''查询用户b30'''
if user_id <= 0: if user_id <= 0:
@@ -95,21 +87,18 @@ def users_user_b30_get(user, user_id):
return error_return(NoAccess('No permission', api_error_code=-1), 403) return error_return(NoAccess('No permission', api_error_code=-1), 403)
with Connect() as c: with Connect() as c:
try: x = UserScoreList(c, UserInfo(c, user_id))
x = UserScoreList(c, UserInfo(c, user_id)) x.query.limit = 30
x.query.limit = 30 x.select_from_user()
x.select_from_user() r = x.to_dict_list()
r = x.to_dict_list() rating_sum = sum([i.rating for i in x.scores])
rating_sum = sum([i.rating for i in x.scores]) return success_return({'user_id': user_id, 'b30_ptt': rating_sum / 30, 'data': r})
return success_return({'user_id': user_id, 'b30_ptt': rating_sum / 30, 'data': r})
except ArcError as e:
return error_return(e)
return error_return()
@bp.route('/<int:user_id>/best', methods=['GET']) @bp.route('/<int:user_id>/best', methods=['GET'])
@role_required(request, ['select', 'select_me']) @role_required(request, ['select', 'select_me'])
@request_json_handle(request, optional_keys=Constant.QUERY_KEYS) @request_json_handle(request, optional_keys=Constant.QUERY_KEYS)
@api_try
def users_user_best_get(data, user, user_id): def users_user_best_get(data, user, user_id):
'''查询用户所有best成绩''' '''查询用户所有best成绩'''
if user_id <= 0: if user_id <= 0:
@@ -119,19 +108,16 @@ def users_user_best_get(data, user, user_id):
return error_return(NoAccess('No permission', api_error_code=-1), 403) return error_return(NoAccess('No permission', api_error_code=-1), 403)
with Connect() as c: with Connect() as c:
try: x = UserScoreList(c, UserInfo(c, user_id))
x = UserScoreList(c, UserInfo(c, user_id)) x.query.from_data(data)
x.query.from_data(data) x.select_from_user()
x.select_from_user() r = x.to_dict_list()
r = x.to_dict_list() return success_return({'user_id': user_id, 'data': r})
return success_return({'user_id': user_id, 'data': r})
except ArcError as e:
return error_return(e)
return error_return()
@bp.route('/<int:user_id>/r30', methods=['GET']) @bp.route('/<int:user_id>/r30', methods=['GET'])
@role_required(request, ['select', 'select_me']) @role_required(request, ['select', 'select_me'])
@api_try
def users_user_r30_get(user, user_id): def users_user_r30_get(user, user_id):
'''查询用户r30''' '''查询用户r30'''
@@ -142,9 +128,5 @@ def users_user_r30_get(user, user_id):
return error_return(NoAccess('No permission', api_error_code=-1), 403) return error_return(NoAccess('No permission', api_error_code=-1), 403)
with Connect() as c: with Connect() as c:
try: p = Potential(c, UserInfo(c, user_id))
p = Potential(c, UserInfo(c, user_id)) return success_return({'user_id': user_id, 'r10_ptt': p.recent_10 / 10, 'data': p.recent_30_to_dict_list()})
return success_return({'user_id': user_id, 'r10_ptt': p.recent_10 / 10, 'data': p.recent_30_to_dict_list()})
except ArcError as e:
return error_return(e)
return error_return()

View File

@@ -177,10 +177,12 @@ class UserCharacter(Character):
x = self.c.fetchone() x = self.c.fetchone()
if not x: if not x:
raise NoData('The character of the user does not exist.') self.is_uncapped = False
self.is_uncapped_override = False
self.is_uncapped = x[0] == 1 # raise NoData('The character of the user does not exist.')
self.is_uncapped_override = x[1] == 1 else:
self.is_uncapped = x[0] == 1
self.is_uncapped_override = x[1] == 1
def select_character_info(self, user=None): def select_character_info(self, user=None):
# parameter: user - User类或子类的实例 # parameter: user - User类或子类的实例

View File

@@ -1,9 +1,10 @@
class ArcError(Exception): class ArcError(Exception):
def __init__(self, message=None, error_code=108, api_error_code=-999, extra_data=None) -> None: def __init__(self, message=None, error_code=108, api_error_code=-999, extra_data=None, status=200) -> None:
self.message = message self.message = message
self.error_code = error_code self.error_code = error_code
self.api_error_code = api_error_code self.api_error_code = api_error_code
self.extra_data = extra_data self.extra_data = extra_data
self.status = status
def __str__(self) -> str: def __str__(self) -> str:
return repr(self.message) return repr(self.message)
@@ -11,8 +12,8 @@ class ArcError(Exception):
class InputError(ArcError): class InputError(ArcError):
# 输入类型错误 # 输入类型错误
def __init__(self, message=None, error_code=108, api_error_code=-100, extra_data=None) -> None: def __init__(self, message=None, error_code=108, api_error_code=-100, extra_data=None, status=200) -> None:
super().__init__(message, error_code, api_error_code, extra_data) super().__init__(message, error_code, api_error_code, extra_data, status)
class DataExist(ArcError): class DataExist(ArcError):
@@ -22,62 +23,62 @@ class DataExist(ArcError):
class NoData(ArcError): class NoData(ArcError):
# 数据不存在 # 数据不存在
def __init__(self, message=None, error_code=108, api_error_code=-2, extra_data=None) -> None: def __init__(self, message=None, error_code=108, api_error_code=-2, extra_data=None, status=200) -> None:
super().__init__(message, error_code, api_error_code, extra_data) super().__init__(message, error_code, api_error_code, extra_data, status)
class PostError(ArcError): class PostError(ArcError):
# 缺少输入 # 缺少输入
def __init__(self, message=None, error_code=108, api_error_code=-100, extra_data=None) -> None: def __init__(self, message=None, error_code=108, api_error_code=-100, extra_data=None, status=200) -> None:
super().__init__(message, error_code, api_error_code, extra_data) super().__init__(message, error_code, api_error_code, extra_data, status)
class UserBan(ArcError): class UserBan(ArcError):
# 用户封禁 # 用户封禁
def __init__(self, message=None, error_code=121, api_error_code=-202, extra_data=None) -> None: def __init__(self, message=None, error_code=121, api_error_code=-202, extra_data=None, status=200) -> None:
super().__init__(message, error_code, api_error_code, extra_data) super().__init__(message, error_code, api_error_code, extra_data, status)
class ItemNotEnough(ArcError): class ItemNotEnough(ArcError):
# 物品数量不足 # 物品数量不足
def __init__(self, message=None, error_code=-6, api_error_code=-999, extra_data=None) -> None: def __init__(self, message=None, error_code=-6, api_error_code=-999, extra_data=None, status=200) -> None:
super().__init__(message, error_code, api_error_code, extra_data) super().__init__(message, error_code, api_error_code, extra_data, status)
class ItemUnavailable(ArcError): class ItemUnavailable(ArcError):
# 物品不可用 # 物品不可用
def __init__(self, message=None, error_code=-6, api_error_code=-999, extra_data=None) -> None: def __init__(self, message=None, error_code=-6, api_error_code=-999, extra_data=None, status=200) -> None:
super().__init__(message, error_code, api_error_code, extra_data) super().__init__(message, error_code, api_error_code, extra_data, status)
class RedeemUnavailable(ArcError): class RedeemUnavailable(ArcError):
# 兑换码不可用 # 兑换码不可用
def __init__(self, message=None, error_code=505, api_error_code=-999, extra_data=None) -> None: def __init__(self, message=None, error_code=505, api_error_code=-999, extra_data=None, status=200) -> None:
super().__init__(message, error_code, api_error_code, extra_data) super().__init__(message, error_code, api_error_code, extra_data, status)
class MapLocked(ArcError): class MapLocked(ArcError):
# 地图锁定 # 地图锁定
def __init__(self, message=None, error_code=108, api_error_code=-999, extra_data=None) -> None: def __init__(self, message=None, error_code=108, api_error_code=-999, extra_data=None, status=200) -> None:
super().__init__(message, error_code, api_error_code, extra_data) super().__init__(message, error_code, api_error_code, extra_data, status)
class StaminaNotEnough(ArcError): class StaminaNotEnough(ArcError):
# 体力不足 # 体力不足
def __init__(self, message=None, error_code=107, api_error_code=-999, extra_data=None) -> None: def __init__(self, message=None, error_code=107, api_error_code=-999, extra_data=None, status=200) -> None:
super().__init__(message, error_code, api_error_code, extra_data) super().__init__(message, error_code, api_error_code, extra_data, status)
class TicketNotEnough(ArcError): class TicketNotEnough(ArcError):
# 记忆源点不足 # 记忆源点不足
def __init__(self, message=None, error_code=-6, api_error_code=-999, extra_data=None) -> None: def __init__(self, message=None, error_code=-6, api_error_code=-999, extra_data=None, status=200) -> None:
super().__init__(message, error_code, api_error_code, extra_data) super().__init__(message, error_code, api_error_code, extra_data, status)
class FriendError(ArcError): class FriendError(ArcError):
# 好友系统出错 # 好友系统出错
def __init__(self, message=None, error_code=108, api_error_code=-999, extra_data=None) -> None: def __init__(self, message=None, error_code=108, api_error_code=-999, extra_data=None, status=200) -> None:
super().__init__(message, error_code, api_error_code, extra_data) super().__init__(message, error_code, api_error_code, extra_data, status)
class NoAccess(ArcError): class NoAccess(ArcError):

View File

@@ -98,10 +98,11 @@ class LocalMultiPlayer:
if self.conn.poll(Constant.LINK_PLAY_TIMEOUT): if self.conn.poll(Constant.LINK_PLAY_TIMEOUT):
self.data_recv = self.conn.recv() self.data_recv = self.conn.recv()
if self.data_recv[0] != 0: if self.data_recv[0] != 0:
raise ArcError('Link Play error.', self.data_recv[0]) raise ArcError('Link Play error.',
self.data_recv[0], status=400)
else: else:
raise Timeout( raise Timeout(
'Timeout when waiting for data from local udp server.') 'Timeout when waiting for data from local udp server.', status=400)
def create_room(self, user: 'Player' = None) -> None: def create_room(self, user: 'Player' = None) -> None:
'''创建房间''' '''创建房间'''

View File

@@ -4,7 +4,7 @@ import traceback
from flask import current_app from flask import current_app
from .constant import Constant from .constant import Constant
from .error import InputError from .error import ArcError, InputError
class Connect: class Connect:
@@ -24,18 +24,22 @@ class Connect:
return self.c return self.c
def __exit__(self, exc_type, exc_val, exc_tb) -> bool: def __exit__(self, exc_type, exc_val, exc_tb) -> bool:
flag = True
if exc_type is not None: if exc_type is not None:
if self.conn: if issubclass(exc_type, ArcError):
self.conn.rollback() flag = False
else:
if self.conn:
self.conn.rollback()
current_app.logger.error( current_app.logger.error(
traceback.format_exception(exc_type, exc_val, exc_tb)) traceback.format_exception(exc_type, exc_val, exc_tb))
if self.conn: if self.conn:
self.conn.commit() self.conn.commit()
self.conn.close() self.conn.close()
return True return flag
class Query: class Query:

View File

@@ -450,6 +450,10 @@ class UserInfo(User):
favorite_character_id = self.favorite_character.character_id favorite_character_id = self.favorite_character.character_id
else: else:
favorite_character_id = -1 favorite_character_id = -1
if self.character.character_id not in character_list:
self.character.character_id = 0
return { return {
"is_aprilfools": Config.IS_APRILFOOLS, "is_aprilfools": Config.IS_APRILFOOLS,
"curr_available_maps": self.curr_available_maps_list, "curr_available_maps": self.curr_available_maps_list,
@@ -477,7 +481,7 @@ class UserInfo(User):
"world_songs": self.world_songs, "world_songs": self.world_songs,
"singles": self.singles, "singles": self.singles,
"packs": self.packs, "packs": self.packs,
"characters": self.characters_list, "characters": character_list,
"cores": self.cores, "cores": self.cores,
"recent_score": self.recent_score_list, "recent_score": self.recent_score_list,
"max_friend": Constant.MAX_FRIEND_COUNT, "max_friend": Constant.MAX_FRIEND_COUNT,

View File

@@ -158,17 +158,17 @@ class Map:
self.is_legacy = raw_dict.get('is_legacy') self.is_legacy = raw_dict.get('is_legacy')
self.is_beyond = raw_dict.get('is_beyond') self.is_beyond = raw_dict.get('is_beyond')
self.beyond_health = raw_dict.get('beyond_health') self.beyond_health = raw_dict.get('beyond_health')
self.character_affinity = raw_dict.get('character_affinity') self.character_affinity = raw_dict.get('character_affinity', [])
self.affinity_multiplier = raw_dict.get('affinity_multiplier') self.affinity_multiplier = raw_dict.get('affinity_multiplier', [])
self.chapter = raw_dict.get('chapter') self.chapter = raw_dict.get('chapter')
self.available_from = raw_dict.get('available_from') self.available_from = raw_dict.get('available_from', -1)
self.available_to = raw_dict.get('available_to') self.available_to = raw_dict.get('available_to', 9999999999999)
self.is_repeatable = raw_dict.get('is_repeatable') self.is_repeatable = raw_dict.get('is_repeatable')
self.require_id = raw_dict.get('require_id') self.require_id = raw_dict.get('require_id', '')
self.require_type = raw_dict.get('require_type') self.require_type = raw_dict.get('require_type', '')
self.require_value = raw_dict.get('require_value') self.require_value = raw_dict.get('require_value', 1)
self.coordinate = raw_dict.get('coordinate') self.coordinate = raw_dict.get('coordinate')
self.custom_bg = raw_dict.get('custom_bg') self.custom_bg = raw_dict.get('custom_bg', '')
self.stamina_cost = raw_dict.get('stamina_cost') self.stamina_cost = raw_dict.get('stamina_cost')
self.steps = [Step().from_dict(s) for s in raw_dict.get('steps')] self.steps = [Step().from_dict(s) for s in raw_dict.get('steps')]
return self return self

View File

@@ -4,6 +4,7 @@ import os
import sys import sys
from logging.config import dictConfig from logging.config import dictConfig
from multiprocessing import Process, set_start_method from multiprocessing import Process, set_start_method
from traceback import format_exc
from flask import Flask, request, send_from_directory from flask import Flask, request, send_from_directory
@@ -59,6 +60,7 @@ def download(file_path):
x.insert_user_download() x.insert_user_download()
return send_from_directory(Constant.SONG_FILE_FOLDER_PATH, file_path, as_attachment=True) return send_from_directory(Constant.SONG_FILE_FOLDER_PATH, file_path, as_attachment=True)
except ArcError as e: except ArcError as e:
app.logger.warning(format_exc())
return error_return(e) return error_return(e)
return error_return() return error_return()
@@ -100,8 +102,8 @@ def main():
} }
} }
} }
if Config.ALLOW_LOG_INFO: if Config.ALLOW_INFO_LOG:
log_dict['root']['handlers'] = ['wsgi', 'info_file', 'error_file'] log_dict['root']['handlers'].append('info_file')
log_dict['handlers']['info_file'] = { log_dict['handlers']['info_file'] = {
"class": "logging.handlers.RotatingFileHandler", "class": "logging.handlers.RotatingFileHandler",
"maxBytes": 1024 * 1024, "maxBytes": 1024 * 1024,
@@ -111,6 +113,17 @@ def main():
"formatter": "default", "formatter": "default",
"filename": "./log/info.log" "filename": "./log/info.log"
} }
if Config.ALLOW_WARNING_LOG:
log_dict['root']['handlers'].append('warning_file')
log_dict['handlers']['warning_file'] = {
"class": "logging.handlers.RotatingFileHandler",
"maxBytes": 1024 * 1024,
"backupCount": 1,
"encoding": "utf-8",
"level": "WARNING",
"formatter": "default",
"filename": "./log/warning.log"
}
dictConfig(log_dict) dictConfig(log_dict)

View File

@@ -1,5 +1,5 @@
import base64 import base64
import functools from functools import wraps
from core.error import ArcError, NoAccess from core.error import ArcError, NoAccess
from core.sql import Connect from core.sql import Connect
@@ -7,44 +7,40 @@ from core.user import UserAuth, UserLogin
from flask import Blueprint, jsonify, request from flask import Blueprint, jsonify, request
from setting import Config from setting import Config
from .func import error_return from .func import arc_try, error_return
bp = Blueprint('auth', __name__, url_prefix='/auth') bp = Blueprint('auth', __name__, url_prefix='/auth')
@bp.route('/login', methods=['POST']) # 登录接口 @bp.route('/login', methods=['POST']) # 登录接口
@arc_try
def login(): def login():
if 'AppVersion' in request.headers: # 版本检查 if 'AppVersion' in request.headers: # 版本检查
if Config.ALLOW_APPVERSION: if Config.ALLOW_APPVERSION:
if request.headers['AppVersion'] not in Config.ALLOW_APPVERSION: if request.headers['AppVersion'] not in Config.ALLOW_APPVERSION:
return error_return(NoAccess('Wrong app version.', 1203)) raise NoAccess('Wrong app version.', 1203)
headers = request.headers headers = request.headers
request.form['grant_type'] request.form['grant_type']
with Connect() as c: with Connect() as c:
try: id_pwd = headers['Authorization']
id_pwd = headers['Authorization'] id_pwd = base64.b64decode(id_pwd[6:]).decode()
id_pwd = base64.b64decode(id_pwd[6:]).decode() name, password = id_pwd.split(':', 1)
name, password = id_pwd.split(':', 1) if 'DeviceId' in headers:
if 'DeviceId' in headers: device_id = headers['DeviceId']
device_id = headers['DeviceId'] else:
else: device_id = 'low_version'
device_id = 'low_version'
user = UserLogin(c) user = UserLogin(c)
user.login(name, password, device_id, request.remote_addr) user.login(name, password, device_id, request.remote_addr)
return jsonify({"success": True, "token_type": "Bearer", 'user_id': user.user_id, 'access_token': user.token}) return jsonify({"success": True, "token_type": "Bearer", 'user_id': user.user_id, 'access_token': user.token})
except ArcError as e:
return error_return(e)
return error_return()
def auth_required(request): def auth_required(request):
# arcaea登录验证写成了修饰器 # arcaea登录验证写成了修饰器
def decorator(view): def decorator(view):
@functools.wraps(view) @wraps(view)
def wrapped_view(*args, **kwargs): def wrapped_view(*args, **kwargs):
headers = request.headers headers = request.headers

View File

@@ -7,27 +7,24 @@ from core.user import UserOnline
from flask import Blueprint, request from flask import Blueprint, request
from .auth import auth_required from .auth import auth_required
from .func import error_return, success_return from .func import arc_try, success_return
bp = Blueprint('course', __name__, url_prefix='/course') bp = Blueprint('course', __name__, url_prefix='/course')
@bp.route('/me', methods=['GET']) @bp.route('/me', methods=['GET'])
@auth_required(request) @auth_required(request)
@arc_try
def course_me(user_id): def course_me(user_id):
with Connect() as c: with Connect() as c:
try: user = UserOnline(c, user_id)
user = UserOnline(c, user_id) core = ItemCore(c)
core = ItemCore(c) core.item_id = 'core_course_skip_purchase'
core.item_id = 'core_course_skip_purchase' core.select(user)
core.select(user) x = UserCourseList(c, user)
x = UserCourseList(c, user) x.select_all()
x.select_all() return success_return({
return success_return({ 'courses': x.to_dict_list(),
'courses': x.to_dict_list(), "stamina_cost": Constant.COURSE_STAMINA_COST,
"stamina_cost": Constant.COURSE_STAMINA_COST, "course_skip_purchase_ticket": core.amount
"course_skip_purchase_ticket": core.amount })
})
except ArcError as e:
return error_return(e)
return error_return()

View File

@@ -1,49 +1,43 @@
from flask import Blueprint, request
from core.sql import Connect from core.sql import Connect
from core.error import ArcError
from core.user import UserOnline, code_get_id from core.user import UserOnline, code_get_id
from .func import error_return, success_return from flask import Blueprint, request
from .auth import auth_required from .auth import auth_required
from .func import arc_try, success_return
bp = Blueprint('friend', __name__, url_prefix='/friend') bp = Blueprint('friend', __name__, url_prefix='/friend')
@bp.route('/me/add', methods=['POST']) # 加好友 @bp.route('/me/add', methods=['POST']) # 加好友
@auth_required(request) @auth_required(request)
@arc_try
def add_friend(user_id): def add_friend(user_id):
with Connect() as c: with Connect() as c:
try: friend_code = request.form['friend_code']
friend_code = request.form['friend_code'] friend_id = code_get_id(c, friend_code)
friend_id = code_get_id(c, friend_code) user = UserOnline(c, user_id)
user = UserOnline(c, user_id) user.add_friend(friend_id)
user.add_friend(friend_id)
return success_return({ return success_return({
"user_id": user.user_id, "user_id": user.user_id,
"updatedAt": "2020-09-07T07:32:12.740Z", "updatedAt": "2020-09-07T07:32:12.740Z",
"createdAt": "2020-09-06T10:05:18.471Z", "createdAt": "2020-09-06T10:05:18.471Z",
"friends": user.friends "friends": user.friends
}) })
except ArcError as e:
return error_return(e)
return error_return()
@bp.route('/me/delete', methods=['POST']) # 删好友 @bp.route('/me/delete', methods=['POST']) # 删好友
@auth_required(request) @auth_required(request)
@arc_try
def delete_friend(user_id): def delete_friend(user_id):
with Connect() as c: with Connect() as c:
try: friend_id = int(request.form['friend_id'])
friend_id = int(request.form['friend_id']) user = UserOnline(c, user_id)
user = UserOnline(c, user_id) user.delete_friend(friend_id)
user.delete_friend(friend_id)
return success_return({ return success_return({
"user_id": user.user_id, "user_id": user.user_id,
"updatedAt": "2020-09-07T07:32:12.740Z", "updatedAt": "2020-09-07T07:32:12.740Z",
"createdAt": "2020-09-06T10:05:18.471Z", "createdAt": "2020-09-06T10:05:18.471Z",
"friends": user.friends "friends": user.friends
}) })
except ArcError as e:
return error_return(e)
return error_return()

View File

@@ -1,5 +1,9 @@
from flask import jsonify from functools import wraps
from traceback import format_exc
from core.error import ArcError from core.error import ArcError
from flask import current_app, jsonify
from setting import Config
default_error = ArcError('Unknown Error') default_error = ArcError('Unknown Error')
@@ -56,7 +60,7 @@ def error_return(e: ArcError = default_error): # 错误返回
if e.extra_data: if e.extra_data:
r['extra'] = e.extra_data r['extra'] = e.extra_data
return jsonify(r) return jsonify(r), e.status
def success_return(value=None): def success_return(value=None):
@@ -64,3 +68,21 @@ def success_return(value=None):
if value is not None: if value is not None:
r['value'] = value r['value'] = value
return jsonify(r) return jsonify(r)
def arc_try(view):
'''替代try/except记录`ArcError`为warning'''
@wraps(view)
def wrapped_view(*args, **kwargs):
try:
data = view(*args, **kwargs)
if data is None:
return error_return()
else:
return data
except ArcError as e:
if Config.ALLOW_WARNING_LOG:
current_app.logger.warning(format_exc())
return error_return(e)
return wrapped_view

View File

@@ -7,7 +7,7 @@ from flask import Blueprint, request
from setting import Config from setting import Config
from .auth import auth_required from .auth import auth_required
from .func import error_return, success_return from .func import arc_try, success_return
bp = Blueprint('multiplayer', __name__, url_prefix='/multiplayer') bp = Blueprint('multiplayer', __name__, url_prefix='/multiplayer')
@@ -16,66 +16,57 @@ conn1, conn2 = Pipe()
@bp.route('/me/room/create', methods=['POST']) # 创建房间 @bp.route('/me/room/create', methods=['POST']) # 创建房间
@auth_required(request) @auth_required(request)
@arc_try
def room_create(user_id): def room_create(user_id):
if not Config.UDP_PORT or Config.UDP_PORT == '': if not Config.UDP_PORT or Config.UDP_PORT == '':
return error_return(ArcError('The local udp server is down.', 151)), 404 raise ArcError('The local udp server is down.', 151, status=404)
with Connect() as c: with Connect() as c:
try: x = LocalMultiPlayer(conn1)
x = LocalMultiPlayer(conn1) user = Player(c, user_id)
user = Player(c, user_id) user.get_song_unlock(request.json['clientSongMap'])
user.get_song_unlock(request.json['clientSongMap']) x.create_room(user)
x.create_room(user) r = x.to_dict()
r = x.to_dict() r['endPoint'] = request.host.split(
r['endPoint'] = request.host.split( ':')[0] if Config.LINK_PLAY_HOST == '' else Config.LINK_PLAY_HOST
':')[0] if Config.LINK_PLAY_HOST == '' else Config.LINK_PLAY_HOST r['port'] = int(Config.UDP_PORT)
r['port'] = int(Config.UDP_PORT) return success_return(r)
return success_return(r)
except ArcError as e:
return error_return(e), 400
return error_return()
@bp.route('/me/room/join/<room_code>', methods=['POST']) # 加入房间 @bp.route('/me/room/join/<room_code>', methods=['POST']) # 加入房间
@auth_required(request) @auth_required(request)
@arc_try
def room_join(user_id, room_code): def room_join(user_id, room_code):
if not Config.UDP_PORT or Config.UDP_PORT == '': if not Config.UDP_PORT or Config.UDP_PORT == '':
return error_return(ArcError('The local udp server is down.', 151)), 404 raise ArcError('The local udp server is down.', 151, status=404)
with Connect() as c: with Connect() as c:
try: x = LocalMultiPlayer(conn1)
x = LocalMultiPlayer(conn1) user = Player(c, user_id)
user = Player(c, user_id) user.get_song_unlock(request.json['clientSongMap'])
user.get_song_unlock(request.json['clientSongMap']) room = Room()
room = Room() room.room_code = room_code
room.room_code = room_code x.join_room(room, user)
x.join_room(room, user) r = x.to_dict()
r = x.to_dict() r['endPoint'] = request.host.split(
r['endPoint'] = request.host.split( ':')[0] if Config.LINK_PLAY_HOST == '' else Config.LINK_PLAY_HOST
':')[0] if Config.LINK_PLAY_HOST == '' else Config.LINK_PLAY_HOST r['port'] = int(Config.UDP_PORT)
r['port'] = int(Config.UDP_PORT) return success_return(r)
return success_return(r)
except ArcError as e:
return error_return(e), 400
return error_return()
@bp.route('/me/update', methods=['POST']) # 更新房间 @bp.route('/me/update', methods=['POST']) # 更新房间
@auth_required(request) @auth_required(request)
@arc_try
def multiplayer_update(user_id): def multiplayer_update(user_id):
if not Config.UDP_PORT or Config.UDP_PORT == '': if not Config.UDP_PORT or Config.UDP_PORT == '':
return error_return(ArcError('The local udp server is down.', 151)), 404 raise ArcError('The local udp server is down.', 151, status=404)
with Connect() as c: with Connect() as c:
try: x = LocalMultiPlayer(conn1)
x = LocalMultiPlayer(conn1) user = Player(c, user_id)
user = Player(c, user_id) user.token = int(request.json['token'])
user.token = int(request.json['token']) x.update_room(user)
x.update_room(user) r = x.to_dict()
r = x.to_dict() r['endPoint'] = request.host.split(
r['endPoint'] = request.host.split( ':')[0] if Config.LINK_PLAY_HOST == '' else Config.LINK_PLAY_HOST
':')[0] if Config.LINK_PLAY_HOST == '' else Config.LINK_PLAY_HOST r['port'] = int(Config.UDP_PORT)
r['port'] = int(Config.UDP_PORT) return success_return(r)
return success_return(r)
except ArcError as e:
return error_return(e), 400
return error_return()

View File

@@ -10,7 +10,7 @@ from flask import Blueprint, jsonify, request
from werkzeug.datastructures import ImmutableMultiDict from werkzeug.datastructures import ImmutableMultiDict
from .auth import auth_required from .auth import auth_required
from .func import error_return, success_return from .func import arc_try, error_return, success_return
from .present import present_info from .present import present_info
from .purchase import bundle_pack, bundle_bundle from .purchase import bundle_pack, bundle_bundle
from .score import song_score_friend from .score import song_score_friend
@@ -27,21 +27,18 @@ def game_info():
@bp.route('/serve/download/me/song', methods=['GET']) # 歌曲下载 @bp.route('/serve/download/me/song', methods=['GET']) # 歌曲下载
@auth_required(request) @auth_required(request)
@arc_try
def download_song(user_id): def download_song(user_id):
with Connect() as c: with Connect() as c:
try: x = DownloadList(c, UserOnline(c, user_id))
x = DownloadList(c, UserOnline(c, user_id)) x.song_ids = request.args.getlist('sid')
x.song_ids = request.args.getlist('sid') x.url_flag = json.loads(request.args.get('url', 'true'))
x.url_flag = json.loads(request.args.get('url', 'true')) x.clear_user_download()
x.clear_user_download() if x.is_limited and x.url_flag:
if x.is_limited and x.url_flag: raise ArcError('You have reached the download limit.', 903)
raise ArcError('You have reached the download limit.', 903)
x.add_songs() x.add_songs()
return success_return(x.urls) return success_return(x.urls)
except ArcError as e:
return error_return(e)
return error_return()
@bp.route('/finale/progress', methods=['GET']) @bp.route('/finale/progress', methods=['GET'])

View File

@@ -5,34 +5,28 @@ from core.user import UserOnline
from flask import Blueprint, request from flask import Blueprint, request
from .auth import auth_required from .auth import auth_required
from .func import error_return, success_return from .func import arc_try, error_return, success_return
bp = Blueprint('present', __name__, url_prefix='/present') bp = Blueprint('present', __name__, url_prefix='/present')
@bp.route('/me', methods=['GET']) # 用户奖励信息 @bp.route('/me', methods=['GET']) # 用户奖励信息
@auth_required(request) @auth_required(request)
@arc_try
def present_info(user_id): def present_info(user_id):
with Connect() as c: with Connect() as c:
try: x = UserPresentList(c, UserOnline(c, user_id))
x = UserPresentList(c, UserOnline(c, user_id)) x.select_user_presents()
x.select_user_presents()
return success_return(x.to_dict_list()) return success_return(x.to_dict_list())
except ArcError as e:
return error_return(e)
return error_return()
@bp.route('/me/claim/<present_id>', methods=['POST']) # 礼物确认 @bp.route('/me/claim/<present_id>', methods=['POST']) # 礼物确认
@auth_required(request) @auth_required(request)
@arc_try
def claim_present(user_id, present_id): def claim_present(user_id, present_id):
with Connect() as c: with Connect() as c:
try: x = UserPresent(c, UserOnline(c, user_id))
x = UserPresent(c, UserOnline(c, user_id)) x.claim_user_present(present_id)
x.claim_user_present(present_id)
return success_return() return success_return()
except ArcError as e:
return error_return(e)
return error_return()

View File

@@ -1,6 +1,6 @@
from time import time from time import time
from core.error import ArcError, ItemUnavailable from core.error import InputError, ItemUnavailable, PostError
from core.item import ItemFactory, Stamina6 from core.item import ItemFactory, Stamina6
from core.purchase import Purchase, PurchaseList from core.purchase import Purchase, PurchaseList
from core.redeem import UserRedeem from core.redeem import UserRedeem
@@ -9,35 +9,29 @@ from core.user import UserOnline
from flask import Blueprint, request from flask import Blueprint, request
from .auth import auth_required from .auth import auth_required
from .func import error_return, success_return from .func import arc_try, success_return
bp = Blueprint('purchase', __name__, url_prefix='/purchase') bp = Blueprint('purchase', __name__, url_prefix='/purchase')
@bp.route('/bundle/pack', methods=['GET']) # 曲包信息 @bp.route('/bundle/pack', methods=['GET']) # 曲包信息
@auth_required(request) @auth_required(request)
@arc_try
def bundle_pack(user_id): def bundle_pack(user_id):
with Connect() as c: with Connect() as c:
try: x = PurchaseList(c, UserOnline(c, user_id)
x = PurchaseList(c, UserOnline(c, user_id) ).select_from_type('pack')
).select_from_type('pack') return success_return(x.to_dict_list())
return success_return(x.to_dict_list())
except ArcError as e:
return error_return(e)
return error_return()
@bp.route('/bundle/single', methods=['GET']) # 单曲购买信息获取 @bp.route('/bundle/single', methods=['GET']) # 单曲购买信息获取
@auth_required(request) @auth_required(request)
@arc_try
def get_single(user_id): def get_single(user_id):
with Connect() as c: with Connect() as c:
try: x = PurchaseList(c, UserOnline(c, user_id)
x = PurchaseList(c, UserOnline(c, user_id) ).select_from_type('single')
).select_from_type('single') return success_return(x.to_dict_list())
return success_return(x.to_dict_list())
except ArcError as e:
return error_return(e)
return error_return()
@bp.route('/bundle/bundle', methods=['GET']) # 捆绑包 @bp.route('/bundle/bundle', methods=['GET']) # 捆绑包
@@ -72,99 +66,87 @@ def bundle_bundle():
@bp.route('/me/pack', methods=['POST']) # 曲包和单曲购买 @bp.route('/me/pack', methods=['POST']) # 曲包和单曲购买
@auth_required(request) @auth_required(request)
@arc_try
def buy_pack_or_single(user_id): def buy_pack_or_single(user_id):
with Connect() as c: with Connect() as c:
try: if 'pack_id' in request.form:
if 'pack_id' in request.form: purchase_name = request.form['pack_id']
purchase_name = request.form['pack_id'] elif 'single_id' in request.form:
elif 'single_id' in request.form: purchase_name = request.form['single_id']
purchase_name = request.form['single_id'] else:
else: return success_return()
return success_return()
x = Purchase(c, UserOnline(c, user_id)).select(purchase_name) x = Purchase(c, UserOnline(c, user_id)).select(purchase_name)
x.buy() x.buy()
return success_return({ return success_return({
'user_id': x.user.user_id, 'user_id': x.user.user_id,
'ticket': x.user.ticket, 'ticket': x.user.ticket,
'packs': x.user.packs, 'packs': x.user.packs,
'singles': x.user.singles, 'singles': x.user.singles,
'characters': x.user.characters_list 'characters': x.user.characters_list
}) })
except ArcError as e:
return error_return(e)
return error_return()
@bp.route('/me/item', methods=['POST']) # 特殊购买world模式boost和stamina @bp.route('/me/item', methods=['POST']) # 特殊购买world模式boost和stamina
@auth_required(request) @auth_required(request)
@arc_try
def buy_special(user_id): def buy_special(user_id):
with Connect() as c: with Connect() as c:
try: if 'item_id' not in request.form:
if 'item_id' not in request.form: raise PostError('`item_id` is required')
return error_return() item_id = request.form['item_id']
item_id = request.form['item_id']
x = Purchase(c, UserOnline(c, user_id)) x = Purchase(c, UserOnline(c, user_id))
x.purchase_name = item_id x.purchase_name = item_id
x.price = 50 x.price = 50
x.orig_price = 50 x.orig_price = 50
x.discount_from = -1 x.discount_from = -1
x.discount_to = -1 x.discount_to = -1
x.items = [ItemFactory(c).get_item(item_id)] x.items = [ItemFactory(c).get_item(item_id)]
x.buy() x.buy()
r = {'user_id': x.user.user_id, 'ticket': x.user.ticket} r = {'user_id': x.user.user_id, 'ticket': x.user.ticket}
if item_id == 'stamina6': if item_id == 'stamina6':
r['stamina'] = x.user.stamina.stamina r['stamina'] = x.user.stamina.stamina
r['max_stamina_ts'] = x.user.stamina.max_stamina_ts r['max_stamina_ts'] = x.user.stamina.max_stamina_ts
r['world_mode_locked_end_ts'] = -1 r['world_mode_locked_end_ts'] = -1
return success_return(r) return success_return(r)
except ArcError as e:
return error_return(e)
return error_return()
@bp.route('/me/stamina/<buy_stamina_type>', methods=['POST']) # 购买体力 @bp.route('/me/stamina/<buy_stamina_type>', methods=['POST']) # 购买体力
@auth_required(request) @auth_required(request)
@arc_try
def purchase_stamina(user_id, buy_stamina_type): def purchase_stamina(user_id, buy_stamina_type):
with Connect() as c: with Connect() as c:
try: if buy_stamina_type != 'fragment':
if buy_stamina_type != 'fragment': raise InputError('Invalid type of buying stamina')
return error_return()
user = UserOnline(c, user_id) user = UserOnline(c, user_id)
user.select_user_one_column('next_fragstam_ts', -1) user.select_user_one_column('next_fragstam_ts', -1)
now = int(time()*1000) now = int(time()*1000)
if user.next_fragstam_ts > now: if user.next_fragstam_ts > now:
return ItemUnavailable('Buying stamina by fragment is not available yet.', 905) return ItemUnavailable('Buying stamina by fragment is not available yet.', 905)
user.update_user_one_column( user.update_user_one_column(
'next_fragstam_ts', now + 24 * 3600 * 1000) 'next_fragstam_ts', now + 24 * 3600 * 1000)
s = Stamina6(c) s = Stamina6(c)
s.user_claim_item(user) s.user_claim_item(user)
return success_return({ return success_return({
"user_id": user.user_id, "user_id": user.user_id,
"stamina": user.stamina.stamina, "stamina": user.stamina.stamina,
"max_stamina_ts": user.stamina.max_stamina_ts, "max_stamina_ts": user.stamina.max_stamina_ts,
"next_fragstam_ts": user.next_fragstam_ts, "next_fragstam_ts": user.next_fragstam_ts,
'world_mode_locked_end_ts': -1 'world_mode_locked_end_ts': -1
}) })
except ArcError as e:
return error_return(e)
return error_return()
@bp.route('/me/redeem', methods=['POST']) # 兑换码 @bp.route('/me/redeem', methods=['POST']) # 兑换码
@auth_required(request) @auth_required(request)
@arc_try
def redeem(user_id): def redeem(user_id):
with Connect() as c: with Connect() as c:
try: x = UserRedeem(c, UserOnline(c, user_id))
x = UserRedeem(c, UserOnline(c, user_id)) x.claim_user_redeem(request.form['code'])
x.claim_user_redeem(request.form['code'])
return success_return({"coupon": "fragment" + str(x.fragment) if x.fragment > 0 else ""}) return success_return({"coupon": "fragment" + str(x.fragment) if x.fragment > 0 else ""})
except ArcError as e:
return error_return(e)
return error_return()

View File

@@ -1,7 +1,7 @@
from time import time from time import time
from core.course import CoursePlay from core.course import CoursePlay
from core.error import ArcError, InputError from core.error import InputError
from core.rank import RankList from core.rank import RankList
from core.score import UserPlay from core.score import UserPlay
from core.sql import Connect from core.sql import Connect
@@ -9,7 +9,7 @@ from core.user import UserOnline
from flask import Blueprint, request from flask import Blueprint, request
from .auth import auth_required from .auth import auth_required
from .func import error_return, success_return from .func import arc_try, success_return
bp = Blueprint('score', __name__, url_prefix='/score') bp = Blueprint('score', __name__, url_prefix='/score')
@@ -21,6 +21,7 @@ def score_token():
@bp.route('/token/world', methods=['GET']) # 世界模式成绩上传所需的token @bp.route('/token/world', methods=['GET']) # 世界模式成绩上传所需的token
@auth_required(request) @auth_required(request)
@arc_try
def score_token_world(user_id): def score_token_world(user_id):
stamina_multiply = int( stamina_multiply = int(
@@ -30,127 +31,108 @@ def score_token_world(user_id):
prog_boost_multiply = int( prog_boost_multiply = int(
request.args['prog_boost_multiply']) if 'prog_boost_multiply' in request.args else 0 request.args['prog_boost_multiply']) if 'prog_boost_multiply' in request.args else 0
with Connect() as c: with Connect() as c:
try: x = UserPlay(c, UserOnline(c, user_id))
x = UserPlay(c, UserOnline(c, user_id)) x.song.set_chart(request.args['song_id'], int(
x.song.set_chart(request.args['song_id'], int( request.args['difficulty']))
request.args['difficulty'])) x.set_play_state_for_world(stamina_multiply,
x.set_play_state_for_world(stamina_multiply, fragment_multiply, prog_boost_multiply)
fragment_multiply, prog_boost_multiply) return success_return({
return success_return({ "stamina": x.user.stamina.stamina,
"stamina": x.user.stamina.stamina, "max_stamina_ts": x.user.stamina.max_stamina_ts,
"max_stamina_ts": x.user.stamina.max_stamina_ts, "token": x.song_token
"token": x.song_token }
} )
)
except ArcError as e:
return error_return(e)
return error_return()
@bp.route('/token/course', methods=['GET']) # 课题模式成绩上传所需的token @bp.route('/token/course', methods=['GET']) # 课题模式成绩上传所需的token
@auth_required(request) @auth_required(request)
@arc_try
def score_token_course(user_id): def score_token_course(user_id):
with Connect() as c: with Connect() as c:
try: use_course_skip_purchase = request.args.get(
use_course_skip_purchase = request.args.get( 'use_course_skip_purchase', 'false') == 'true'
'use_course_skip_purchase', 'false') == 'true'
user = UserOnline(c, user_id) user = UserOnline(c, user_id)
user_play = UserPlay(c, user) user_play = UserPlay(c, user)
user_play.song_token = request.args.get('previous_token', None) user_play.song_token = request.args.get('previous_token', None)
user_play.get_play_state() user_play.get_play_state()
status = 'created' status = 'created'
if user_play.course_play_state == -1: if user_play.course_play_state == -1:
# 没有token课题模式刚开始 # 没有token课题模式刚开始
course_play = CoursePlay(c, user, user_play) course_play = CoursePlay(c, user, user_play)
course_play.course_id = request.args['course_id'] course_play.course_id = request.args['course_id']
user_play.course_play = course_play user_play.course_play = course_play
user_play.set_play_state_for_course( user_play.set_play_state_for_course(
use_course_skip_purchase) use_course_skip_purchase)
elif 0 <= user_play.course_play_state <= 3: elif 0 <= user_play.course_play_state <= 3:
# 验证token # 验证token
user_play.update_token_for_course() user_play.update_token_for_course()
else: else:
# 课题模式已经结束 # 课题模式已经结束
user_play.clear_play_state() user_play.clear_play_state()
user.select_user_about_stamina() user.select_user_about_stamina()
status = 'cleared' if user_play.course_play_state == 4 else 'failed' status = 'cleared' if user_play.course_play_state == 4 else 'failed'
return success_return({ return success_return({
"stamina": user.stamina.stamina, "stamina": user.stamina.stamina,
"max_stamina_ts": user.stamina.max_stamina_ts, "max_stamina_ts": user.stamina.max_stamina_ts,
"token": user_play.song_token, "token": user_play.song_token,
'status': status 'status': status
}) })
except ArcError as e:
return error_return(e)
return error_return()
@bp.route('/song', methods=['POST']) # 成绩上传 @bp.route('/song', methods=['POST']) # 成绩上传
@auth_required(request) @auth_required(request)
@arc_try
def song_score_post(user_id): def song_score_post(user_id):
with Connect() as c: with Connect() as c:
try: x = UserPlay(c, UserOnline(c, user_id))
x = UserPlay(c, UserOnline(c, user_id)) x.song_token = request.form['song_token']
x.song_token = request.form['song_token'] x.song_hash = request.form['song_hash']
x.song_hash = request.form['song_hash'] x.song.set_chart(
x.song.set_chart( request.form['song_id'], request.form['difficulty'])
request.form['song_id'], request.form['difficulty']) x.set_score(request.form['score'], request.form['shiny_perfect_count'], request.form['perfect_count'], request.form['near_count'],
x.set_score(request.form['score'], request.form['shiny_perfect_count'], request.form['perfect_count'], request.form['near_count'], request.form['miss_count'], request.form['health'], request.form['modifier'], int(time() * 1000), request.form['clear_type'])
request.form['miss_count'], request.form['health'], request.form['modifier'], int(time() * 1000), request.form['clear_type']) x.beyond_gauge = int(request.form['beyond_gauge'])
x.beyond_gauge = int(request.form['beyond_gauge']) x.submission_hash = request.form['submission_hash']
x.submission_hash = request.form['submission_hash'] if not x.is_valid:
if not x.is_valid: raise InputError('Invalid score.', 107)
raise InputError('Invalid score.', 107) x.upload_score()
x.upload_score() return success_return(x.to_dict())
return success_return(x.to_dict())
except ArcError as e:
return error_return(e)
return error_return()
@bp.route('/song', methods=['GET']) # TOP20 @bp.route('/song', methods=['GET']) # TOP20
@auth_required(request) @auth_required(request)
@arc_try
def song_score_top(user_id): def song_score_top(user_id):
with Connect() as c: with Connect() as c:
try: rank_list = RankList(c)
rank_list = RankList(c) rank_list.song.set_chart(request.args.get(
rank_list.song.set_chart(request.args.get( 'song_id'), request.args.get('difficulty'))
'song_id'), request.args.get('difficulty')) rank_list.select_top()
rank_list.select_top() return success_return(rank_list.to_dict_list())
return success_return(rank_list.to_dict_list())
except ArcError as e:
return error_return(e)
return error_return()
@bp.route('/song/me', methods=['GET']) # 我的排名默认最多20 @bp.route('/song/me', methods=['GET']) # 我的排名默认最多20
@auth_required(request) @auth_required(request)
@arc_try
def song_score_me(user_id): def song_score_me(user_id):
with Connect() as c: with Connect() as c:
try: rank_list = RankList(c)
rank_list = RankList(c) rank_list.song.set_chart(request.args.get(
rank_list.song.set_chart(request.args.get( 'song_id'), request.args.get('difficulty'))
'song_id'), request.args.get('difficulty')) rank_list.select_me(UserOnline(c, user_id))
rank_list.select_me(UserOnline(c, user_id)) return success_return(rank_list.to_dict_list())
return success_return(rank_list.to_dict_list())
except ArcError as e:
return error_return(e)
return error_return()
@bp.route('/song/friend', methods=['GET']) # 好友排名默认最多50 @bp.route('/song/friend', methods=['GET']) # 好友排名默认最多50
@auth_required(request) @auth_required(request)
@arc_try
def song_score_friend(user_id): def song_score_friend(user_id):
with Connect() as c: with Connect() as c:
try: rank_list = RankList(c)
rank_list = RankList(c) rank_list.song.set_chart(request.args.get(
rank_list.song.set_chart(request.args.get( 'song_id'), request.args.get('difficulty'))
'song_id'), request.args.get('difficulty')) rank_list.select_friend(UserOnline(c, user_id))
rank_list.select_friend(UserOnline(c, user_id)) return success_return(rank_list.to_dict_list())
return success_return(rank_list.to_dict_list())
except ArcError as e:
return error_return(e)
return error_return()

View File

@@ -8,186 +8,160 @@ from flask import Blueprint, request
from setting import Config from setting import Config
from .auth import auth_required from .auth import auth_required
from .func import error_return, success_return from .func import arc_try, success_return
bp = Blueprint('user', __name__, url_prefix='/user') bp = Blueprint('user', __name__, url_prefix='/user')
@bp.route('', methods=['POST']) # 注册接口 @bp.route('', methods=['POST']) # 注册接口
@arc_try
def register(): def register():
if 'AppVersion' in request.headers: # 版本检查 if 'AppVersion' in request.headers: # 版本检查
if Config.ALLOW_APPVERSION: if Config.ALLOW_APPVERSION:
if request.headers['AppVersion'] not in Config.ALLOW_APPVERSION: if request.headers['AppVersion'] not in Config.ALLOW_APPVERSION:
return error_return(NoAccess('Wrong app version.', 1203)) raise NoAccess('Wrong app version.', 1203)
with Connect() as c: with Connect() as c:
try: new_user = UserRegister(c)
new_user = UserRegister(c) new_user.set_name(request.form['name'])
new_user.set_name(request.form['name']) new_user.set_password(request.form['password'])
new_user.set_password(request.form['password']) new_user.set_email(request.form['email'])
new_user.set_email(request.form['email']) if 'device_id' in request.form:
if 'device_id' in request.form: device_id = request.form['device_id']
device_id = request.form['device_id'] else:
else: device_id = 'low_version'
device_id = 'low_version'
new_user.register() new_user.register()
# 注册后自动登录 # 注册后自动登录
user = UserLogin(c) user = UserLogin(c)
user.login(new_user.name, new_user.password, user.login(new_user.name, new_user.password,
device_id, request.remote_addr) device_id, request.remote_addr)
return success_return({'user_id': user.user_id, 'access_token': user.token}) return success_return({'user_id': user.user_id, 'access_token': user.token})
except ArcError as e:
return error_return(e)
return error_return()
@bp.route('/me', methods=['GET']) # 用户信息 @bp.route('/me', methods=['GET']) # 用户信息
@auth_required(request) @auth_required(request)
@arc_try
def user_me(user_id): def user_me(user_id):
with Connect() as c: with Connect() as c:
try: return success_return(UserOnline(c, user_id).to_dict())
return success_return(UserOnline(c, user_id).to_dict())
except ArcError as e:
return error_return(e)
return error_return()
@bp.route('/me/character', methods=['POST']) # 角色切换 @bp.route('/me/character', methods=['POST']) # 角色切换
@auth_required(request) @auth_required(request)
@arc_try
def character_change(user_id): def character_change(user_id):
with Connect() as c: with Connect() as c:
try: user = UserOnline(c, user_id)
user = UserOnline(c, user_id) user.change_character(
user.change_character( int(request.form['character']), request.form['skill_sealed'] == 'true')
int(request.form['character']), request.form['skill_sealed'] == 'true')
return success_return({'user_id': user.user_id, 'character': user.character.character_id}) return success_return({'user_id': user.user_id, 'character': user.character.character_id})
except ArcError as e:
return error_return(e)
return error_return()
# 角色觉醒切换 # 角色觉醒切换
@bp.route('/me/character/<int:character_id>/toggle_uncap', methods=['POST']) @bp.route('/me/character/<int:character_id>/toggle_uncap', methods=['POST'])
@auth_required(request) @auth_required(request)
@arc_try
def toggle_uncap(user_id, character_id): def toggle_uncap(user_id, character_id):
with Connect() as c: with Connect() as c:
try: user = User()
user = User() user.user_id = user_id
user.user_id = user_id character = UserCharacter(c, character_id)
character = UserCharacter(c, character_id) character.change_uncap_override(user)
character.change_uncap_override(user) character.select_character_info(user)
character.select_character_info(user) return success_return({'user_id': user.user_id, 'character': [character.to_dict()]})
return success_return({'user_id': user.user_id, 'character': [character.to_dict()]})
except ArcError as e:
return error_return(e)
return error_return()
# 角色觉醒 # 角色觉醒
@bp.route('/me/character/<int:character_id>/uncap', methods=['POST']) @bp.route('/me/character/<int:character_id>/uncap', methods=['POST'])
@auth_required(request) @auth_required(request)
@arc_try
def character_first_uncap(user_id, character_id): def character_first_uncap(user_id, character_id):
with Connect() as c: with Connect() as c:
try: user = UserOnline(c, user_id)
user = UserOnline(c, user_id) character = UserCharacter(c, character_id)
character = UserCharacter(c, character_id) character.select_character_info(user)
character.select_character_info(user) character.character_uncap(user)
character.character_uncap(user) return success_return({'user_id': user.user_id, 'character': [character.to_dict()], 'cores': user.cores})
return success_return({'user_id': user.user_id, 'character': [character.to_dict()], 'cores': user.cores})
except ArcError as e:
return error_return(e)
return error_return()
# 角色使用以太之滴 # 角色使用以太之滴
@bp.route('/me/character/<int:character_id>/exp', methods=['POST']) @bp.route('/me/character/<int:character_id>/exp', methods=['POST'])
@auth_required(request) @auth_required(request)
@arc_try
def character_exp(user_id, character_id): def character_exp(user_id, character_id):
with Connect() as c: with Connect() as c:
try: user = UserOnline(c, user_id)
user = UserOnline(c, user_id) character = UserCharacter(c, character_id)
character = UserCharacter(c, character_id) character.select_character_info(user)
character.select_character_info(user) core = ItemCore(c)
core = ItemCore(c) core.amount = - int(request.form['amount'])
core.amount = - int(request.form['amount']) core.item_id = 'core_generic'
core.item_id = 'core_generic' character.upgrade_by_core(user, core)
character.upgrade_by_core(user, core) return success_return({'user_id': user.user_id, 'character': [character.to_dict], 'cores': user.cores})
return success_return({'user_id': user.user_id, 'character': [character.to_dict], 'cores': user.cores})
except ArcError as e:
return error_return(e)
return error_return()
@bp.route('/me/save', methods=['GET']) # 从云端同步 @bp.route('/me/save', methods=['GET']) # 从云端同步
@auth_required(request) @auth_required(request)
@arc_try
def cloud_get(user_id): def cloud_get(user_id):
with Connect() as c: with Connect() as c:
try: user = User()
user = User() user.user_id = user_id
user.user_id = user_id save = SaveData(c)
save = SaveData(c) save.select_all(user)
save.select_all(user) return success_return(save.to_dict())
return success_return(save.to_dict())
except ArcError as e:
return error_return(e)
return error_return()
@bp.route('/me/save', methods=['POST']) # 向云端同步 @bp.route('/me/save', methods=['POST']) # 向云端同步
@auth_required(request) @auth_required(request)
@arc_try
def cloud_post(user_id): def cloud_post(user_id):
with Connect() as c: with Connect() as c:
try: user = User()
user = User() user.user_id = user_id
user.user_id = user_id save = SaveData(c)
save = SaveData(c) save.set_value(
save.set_value( 'scores_data', request.form['scores_data'], request.form['scores_checksum'])
'scores_data', request.form['scores_data'], request.form['scores_checksum']) save.set_value(
save.set_value( 'clearlamps_data', request.form['clearlamps_data'], request.form['clearlamps_checksum'])
'clearlamps_data', request.form['clearlamps_data'], request.form['clearlamps_checksum']) save.set_value(
save.set_value( 'clearedsongs_data', request.form['clearedsongs_data'], request.form['clearedsongs_checksum'])
'clearedsongs_data', request.form['clearedsongs_data'], request.form['clearedsongs_checksum']) save.set_value(
save.set_value( 'unlocklist_data', request.form['unlocklist_data'], request.form['unlocklist_checksum'])
'unlocklist_data', request.form['unlocklist_data'], request.form['unlocklist_checksum']) save.set_value(
save.set_value( 'installid_data', request.form['installid_data'], request.form['installid_checksum'])
'installid_data', request.form['installid_data'], request.form['installid_checksum']) save.set_value('devicemodelname_data',
save.set_value('devicemodelname_data', request.form['devicemodelname_data'], request.form['devicemodelname_checksum'])
request.form['devicemodelname_data'], request.form['devicemodelname_checksum']) save.set_value(
save.set_value( 'story_data', request.form['story_data'], request.form['story_checksum'])
'story_data', request.form['story_data'], request.form['story_checksum']) save.set_value(
save.set_value( 'finalestate_data', request.form['finalestate_data'], request.form['finalestate_checksum'])
'finalestate_data', request.form['finalestate_data'], request.form['finalestate_checksum'])
save.update_all(user) save.update_all(user)
return success_return({'user_id': user.user_id}) return success_return({'user_id': user.user_id})
except ArcError as e:
return error_return(e)
return error_return()
@bp.route('/me/setting/<set_arg>', methods=['POST']) # 三个设置 @bp.route('/me/setting/<set_arg>', methods=['POST']) # 三个设置
@auth_required(request) @auth_required(request)
@arc_try
def sys_set(user_id, set_arg): def sys_set(user_id, set_arg):
with Connect() as c: with Connect() as c:
try: value = request.form['value']
value = request.form['value'] user = UserOnline(c, user_id)
user = UserOnline(c, user_id) if 'favorite_character' == set_arg:
if 'favorite_character' == set_arg: user.change_favorite_character(int(value))
user.change_favorite_character(int(value)) else:
else: value = 'true' == value
value = 'true' == value if 'is_hide_rating' == set_arg or 'max_stamina_notification_enabled' == set_arg:
if 'is_hide_rating' == set_arg or 'max_stamina_notification_enabled' == set_arg: user.update_user_one_column(set_arg, value)
user.update_user_one_column(set_arg, value) return success_return(user.to_dict())
return success_return(user.to_dict())
except ArcError as e:
return error_return(e)
return error_return()
@bp.route('/me/request_delete', methods=['POST']) # 删除账号 @bp.route('/me/request_delete', methods=['POST']) # 删除账号
@auth_required(request) @auth_required(request)
@arc_try
def user_delete(user_id): def user_delete(user_id):
return error_return(ArcError('Cannot delete the account.', 151)), 404 raise ArcError('Cannot delete the account.', 151, status=404)

View File

@@ -1,57 +1,47 @@
from core.error import ArcError
from core.sql import Connect from core.sql import Connect
from core.user import UserOnline from core.user import UserOnline
from core.world import UserMap, get_world_all from core.world import UserMap, get_world_all
from flask import Blueprint, request from flask import Blueprint, request
from .auth import auth_required from .auth import auth_required
from .func import error_return, success_return from .func import arc_try, success_return
bp = Blueprint('world', __name__, url_prefix='/world') bp = Blueprint('world', __name__, url_prefix='/world')
@bp.route('/map/me', methods=['GET']) # 获得世界模式信息,所有地图 @bp.route('/map/me', methods=['GET']) # 获得世界模式信息,所有地图
@auth_required(request) @auth_required(request)
@arc_try
def world_all(user_id): def world_all(user_id):
with Connect() as c: with Connect() as c:
try: user = UserOnline(c, user_id)
user = UserOnline(c, user_id) user.select_user_about_current_map()
user.select_user_about_current_map() return success_return({
return success_return({ "current_map": user.current_map.map_id,
"current_map": user.current_map.map_id, "user_id": user_id,
"user_id": user_id, "maps": [x.to_dict(has_map_info=True, has_rewards=True) for x in get_world_all(c, user)]
"maps": [x.to_dict(has_map_info=True, has_rewards=True) for x in get_world_all(c, user)] })
})
except ArcError as e:
return error_return(e)
return error_return()
@bp.route('/map/me', methods=['POST']) # 进入地图 @bp.route('/map/me', methods=['POST']) # 进入地图
@auth_required(request) @auth_required(request)
@arc_try
def world_in(user_id): def world_in(user_id):
with Connect() as c: with Connect() as c:
try: arcmap = UserMap(c, request.form['map_id'], UserOnline(c, user_id))
arcmap = UserMap(c, request.form['map_id'], UserOnline(c, user_id)) if arcmap.unlock():
if arcmap.unlock(): return success_return(arcmap.to_dict())
return success_return(arcmap.to_dict())
except ArcError as e:
return error_return(e)
return error_return()
@bp.route('/map/me/<map_id>', methods=['GET']) # 获得单个地图完整信息 @bp.route('/map/me/<map_id>', methods=['GET']) # 获得单个地图完整信息
@auth_required(request) @auth_required(request)
@arc_try
def world_one(user_id, map_id): def world_one(user_id, map_id):
with Connect() as c: with Connect() as c:
try: arcmap = UserMap(c, map_id, UserOnline(c, user_id))
arcmap = UserMap(c, map_id, UserOnline(c, user_id)) arcmap.change_user_current_map()
arcmap.change_user_current_map() return success_return({
return success_return({ "user_id": user_id,
"user_id": user_id, "current_map": map_id,
"current_map": map_id, "maps": [arcmap.to_dict(has_map_info=True, has_steps=True)]
"maps": [arcmap.to_dict(has_map_info=True, has_steps=True)] })
})
except ArcError as e:
return error_return(e)
return error_return()

View File

@@ -186,7 +186,8 @@ class Config():
是否记录详细的服务器日志 是否记录详细的服务器日志
If recording detailed server logs is enabled If recording detailed server logs is enabled
''' '''
ALLOW_LOG_INFO = False ALLOW_INFO_LOG = False
ALLOW_WARNING_LOG = False
''' '''
-------------------- --------------------
''' '''