mirror of
https://github.com/Lost-MSth/Arcaea-server.git
synced 2026-02-11 18:47:26 +08:00
Code refactoring
- Code refactoring mainly for API - Delete a useless option in `setting.py` - Change some constants in Link Play mode
This commit is contained in:
@@ -0,0 +1,10 @@
|
|||||||
|
from flask import Blueprint
|
||||||
|
|
||||||
|
from . import users
|
||||||
|
from . import songs
|
||||||
|
from . import token
|
||||||
|
|
||||||
|
bp = Blueprint('api', __name__, url_prefix='/api/v1')
|
||||||
|
bp.register_blueprint(users.bp)
|
||||||
|
bp.register_blueprint(songs.bp)
|
||||||
|
bp.register_blueprint(token.bp)
|
||||||
|
|||||||
@@ -1,156 +1,75 @@
|
|||||||
import hashlib
|
|
||||||
import base64
|
|
||||||
import time
|
|
||||||
import random
|
|
||||||
from core.sql import Connect
|
|
||||||
import functools
|
import functools
|
||||||
|
|
||||||
|
from core.api_user import APIUser
|
||||||
|
from core.error import ArcError, NoAccess, PostError
|
||||||
|
from core.sql import Connect
|
||||||
from setting import Config
|
from setting import Config
|
||||||
from flask import jsonify
|
|
||||||
|
from .api_code import error_return
|
||||||
|
|
||||||
|
|
||||||
class User():
|
def role_required(request, powers=[]):
|
||||||
# 用户类,当数据类型用
|
'''api token验证,写成了修饰器'''
|
||||||
def __init__(self, user_id=None, role='', power=[]):
|
|
||||||
self.user_id = user_id
|
|
||||||
self.role = role
|
|
||||||
self.power = power
|
|
||||||
|
|
||||||
|
|
||||||
def login(auth: str, ip: str):
|
|
||||||
# 登录接口,返回字典和错误码
|
|
||||||
|
|
||||||
try:
|
|
||||||
auth_decode = bytes.decode(base64.b64decode(auth))
|
|
||||||
except:
|
|
||||||
return {}, -100
|
|
||||||
|
|
||||||
if not ':' in auth_decode:
|
|
||||||
return {}, -100
|
|
||||||
|
|
||||||
name, password = auth_decode.split(':', 1)
|
|
||||||
|
|
||||||
with Connect() as c:
|
|
||||||
hash_pwd = hashlib.sha256(password.encode("utf8")).hexdigest()
|
|
||||||
c.execute('''select user_id, password from user where name = :name''', {
|
|
||||||
'name': name})
|
|
||||||
x = c.fetchone()
|
|
||||||
if x is None:
|
|
||||||
return {}, -201
|
|
||||||
if x[1] == '':
|
|
||||||
return {}, -202
|
|
||||||
if hash_pwd != x[1]:
|
|
||||||
return {}, -201
|
|
||||||
|
|
||||||
user_id = str(x[0])
|
|
||||||
now = int(time.time() * 1000)
|
|
||||||
token = hashlib.sha256(
|
|
||||||
(user_id + str(random.randint(10000, 99999)) + str(now)).encode("utf8")).hexdigest()
|
|
||||||
c.execute('''delete from api_login where user_id=?''', (user_id,))
|
|
||||||
c.execute('''insert into api_login values(?,?,?,?)''',
|
|
||||||
(user_id, token, now, ip))
|
|
||||||
|
|
||||||
return {'token': token, 'user_id': user_id}, 0
|
|
||||||
|
|
||||||
|
|
||||||
def logout(user: User):
|
|
||||||
# 登出接口,返回错误码
|
|
||||||
code = 999
|
|
||||||
with Connect() as c:
|
|
||||||
c.execute('''delete from api_login where user_id=?''', (user.user_id,))
|
|
||||||
code = 0
|
|
||||||
|
|
||||||
return code
|
|
||||||
|
|
||||||
|
|
||||||
def id_get_role_id(c, user_id):
|
|
||||||
# user_id获取role_id
|
|
||||||
role_id = 1
|
|
||||||
c.execute('''select role_id from user_role where user_id = :a''',
|
|
||||||
{'a': user_id})
|
|
||||||
x = c.fetchone()
|
|
||||||
if x is not None:
|
|
||||||
role_id = int(x[0])
|
|
||||||
|
|
||||||
return role_id
|
|
||||||
|
|
||||||
|
|
||||||
def role_id_get_role(c, role_id):
|
|
||||||
# role_id获取role
|
|
||||||
role = ''
|
|
||||||
c.execute('''select role_name from role where role_id = :a''',
|
|
||||||
{'a': role_id})
|
|
||||||
x = c.fetchone()
|
|
||||||
if x is not None:
|
|
||||||
role = x[0]
|
|
||||||
|
|
||||||
return role
|
|
||||||
|
|
||||||
|
|
||||||
def api_token_get_id(c, token):
|
|
||||||
# api的token获取user_id
|
|
||||||
user_id = None
|
|
||||||
c.execute('''select user_id from api_login where token = :token''', {
|
|
||||||
'token': token})
|
|
||||||
x = c.fetchone()
|
|
||||||
if x is not None:
|
|
||||||
user_id = x[0]
|
|
||||||
|
|
||||||
return user_id
|
|
||||||
|
|
||||||
|
|
||||||
def get_role_power(c, role_id):
|
|
||||||
# 获取role_id对应power,返回列表
|
|
||||||
|
|
||||||
role_power = []
|
|
||||||
|
|
||||||
c.execute('''select power_name from power where power_id in (select power_id from role_power where role_id=:a)''', {
|
|
||||||
'a': role_id})
|
|
||||||
x = c.fetchall()
|
|
||||||
for i in x:
|
|
||||||
role_power.append(i[0])
|
|
||||||
|
|
||||||
return role_power
|
|
||||||
|
|
||||||
|
|
||||||
def role_required(request, power=[]):
|
|
||||||
# api token验证,写成了修饰器
|
|
||||||
def decorator(view):
|
def decorator(view):
|
||||||
@functools.wraps(view)
|
@functools.wraps(view)
|
||||||
def wrapped_view(*args, **kwargs):
|
def wrapped_view(*args, **kwargs):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
request.json # 检查请求json格式
|
request.json # 检查请求json格式
|
||||||
except:
|
except:
|
||||||
return jsonify({'status': 400, 'code': -1, 'data': {}, 'msg': 'Payload must be a valid json'})
|
return error_return(PostError('Payload must be a valid json', api_error_code=-1), 400)
|
||||||
|
|
||||||
if not 'Token' in request.headers:
|
if not 'Token' in request.headers:
|
||||||
return jsonify({'status': 401, 'code': -1, 'data': {}, 'msg': 'No token'})
|
return error_return(PostError('No token', api_error_code=-1), 401)
|
||||||
|
|
||||||
user = User()
|
user = APIUser()
|
||||||
if Config.API_TOKEN == request.headers['Token'] and Config.API_TOKEN != '':
|
if Config.API_TOKEN == request.headers['Token'] and Config.API_TOKEN != '':
|
||||||
user.user_id = 0
|
user.user_id = 0
|
||||||
elif power == []:
|
elif powers == []:
|
||||||
return jsonify({'status': 403, 'code': -1, 'data': {}, 'msg': 'No permission'})
|
# 无powers则非本地权限(API_TOKEN规定的)无法访问
|
||||||
|
return error_return(NoAccess('No permission', api_error_code=-1), 403)
|
||||||
else:
|
else:
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
user.user_id = api_token_get_id(
|
try:
|
||||||
c, request.headers['Token'])
|
user.c = c
|
||||||
if user.user_id is None:
|
user.select_user_id_from_api_token(
|
||||||
return jsonify({'status': 401, 'code': -1, 'data': {}, 'msg': 'No token'})
|
request.headers['Token'])
|
||||||
|
user.select_role_and_powers()
|
||||||
|
|
||||||
role_id = id_get_role_id(c, user.user_id)
|
if not any([y in [x.power_name for x in user.role.powers] for y in powers]):
|
||||||
user.role = role_id_get_role(c, role_id)
|
return error_return(NoAccess('No permission', api_error_code=-1), 403)
|
||||||
user.role_power = get_role_power(c, role_id)
|
except ArcError as e:
|
||||||
|
return error_return(e, 401)
|
||||||
f = False
|
|
||||||
for i in power:
|
|
||||||
if i in user.role_power:
|
|
||||||
f = True
|
|
||||||
break
|
|
||||||
if not f:
|
|
||||||
return jsonify({'status': 403, 'code': -1, 'data': {}, 'msg': 'No permission'})
|
|
||||||
|
|
||||||
return view(user, *args, **kwargs)
|
return view(user, *args, **kwargs)
|
||||||
|
|
||||||
return wrapped_view
|
return wrapped_view
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
|
def request_json_handle(request, required_keys=[], optional_keys=[]):
|
||||||
|
'''
|
||||||
|
提取post参数,返回dict,写成了修饰器\
|
||||||
|
parameters: \
|
||||||
|
`request`: `Request` - 当前请求\
|
||||||
|
`required_keys`: `list` - 必须的参数\
|
||||||
|
`optional_keys`: `list` - 可选的参数
|
||||||
|
'''
|
||||||
|
|
||||||
|
def decorator(view):
|
||||||
|
@functools.wraps(view)
|
||||||
|
def wrapped_view(*args, **kwargs):
|
||||||
|
|
||||||
|
data = {}
|
||||||
|
for key in required_keys:
|
||||||
|
if key not in request.json:
|
||||||
|
return error_return(PostError('Missing parameter: ' + key, api_error_code=-100))
|
||||||
|
data[key] = request.json[key]
|
||||||
|
|
||||||
|
for key in optional_keys:
|
||||||
|
if key in request.json:
|
||||||
|
data[key] = request.json[key]
|
||||||
|
|
||||||
|
return view(data, *args, **kwargs)
|
||||||
|
|
||||||
|
return wrapped_view
|
||||||
|
return decorator
|
||||||
|
|||||||
@@ -1,34 +1,32 @@
|
|||||||
|
from core.error import ArcError
|
||||||
from flask import jsonify
|
from flask import jsonify
|
||||||
|
|
||||||
|
default_error = ArcError('Unknown Error')
|
||||||
def code_get_msg(code):
|
|
||||||
# api接口code获取msg,返回字符串
|
|
||||||
msg = {
|
|
||||||
0: '',
|
|
||||||
-1: 'See status code',
|
|
||||||
-2: 'No data',
|
|
||||||
-3: 'No data or user',
|
|
||||||
-4: 'No user_id',
|
|
||||||
-100: 'Wrong post data',
|
|
||||||
-101: 'Wrong data type',
|
|
||||||
-102: 'Wrong query parameter',
|
|
||||||
-103: 'Wrong sort parameter',
|
|
||||||
-104: 'Wrong sort order parameter',
|
|
||||||
-201: 'Wrong username or password',
|
|
||||||
-202: 'User is banned',
|
|
||||||
-203: 'Username exists',
|
|
||||||
-204: 'Email address exists',
|
|
||||||
-999: 'Unknown error'
|
|
||||||
}
|
|
||||||
|
|
||||||
return msg[code]
|
|
||||||
|
|
||||||
|
|
||||||
def return_encode(code: int = 0, data: dict = {}, status: int = 200, msg: str = ''):
|
CODE_MSG = {
|
||||||
# 构造返回,返回jsonify处理过后的response_class
|
0: '',
|
||||||
if msg == '':
|
-1: 'See status code',
|
||||||
msg = code_get_msg(code)
|
-2: 'No data',
|
||||||
if code < 0:
|
-3: 'No data or user',
|
||||||
return jsonify({'status': status, 'code': code, 'data': {}, 'msg': msg})
|
-4: 'No user_id',
|
||||||
else:
|
-100: 'Wrong post data',
|
||||||
return jsonify({'status': status, 'code': code, 'data': data, 'msg': msg})
|
-101: 'Wrong data type',
|
||||||
|
-102: 'Wrong query parameter',
|
||||||
|
-103: 'Wrong sort parameter',
|
||||||
|
-104: 'Wrong sort order parameter',
|
||||||
|
-200: 'No permission',
|
||||||
|
-201: 'Wrong username or password',
|
||||||
|
-202: 'User is banned',
|
||||||
|
-203: 'Username exists',
|
||||||
|
-204: 'Email address exists',
|
||||||
|
-999: 'Unknown error'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def success_return(data: dict = {}, status: int = 200, msg: str = ''):
|
||||||
|
return jsonify({'code': 0, 'data': data, 'msg': msg}), status
|
||||||
|
|
||||||
|
|
||||||
|
def error_return(e: 'ArcError' = default_error, status: int = 200):
|
||||||
|
return jsonify({'code': e.api_error_code, 'data': {} if e.extra_data is None else e.extra_data, 'msg': CODE_MSG[e.api_error_code] if e.message is None else e.message}), status
|
||||||
|
|||||||
@@ -1,219 +0,0 @@
|
|||||||
from flask import (
|
|
||||||
Blueprint, request, jsonify
|
|
||||||
)
|
|
||||||
import functools
|
|
||||||
import api.api_auth
|
|
||||||
from . import users
|
|
||||||
import api.songs
|
|
||||||
from .api_code import code_get_msg, return_encode
|
|
||||||
|
|
||||||
|
|
||||||
bp = Blueprint('api', __name__, url_prefix='/api/v1')
|
|
||||||
bp.register_blueprint(users.bp)
|
|
||||||
|
|
||||||
|
|
||||||
class Query():
|
|
||||||
# 查询类,当查询附加参数的数据类型用
|
|
||||||
def __init__(self, limit=-1, offset=0, query={}, sort=[]) -> None:
|
|
||||||
self.limit = limit
|
|
||||||
self.offset = offset
|
|
||||||
self.query = query # {'name': 'admin'}
|
|
||||||
self.sort = sort # [{'column': 'user_id', 'order': 'ASC'}, ...]
|
|
||||||
|
|
||||||
|
|
||||||
def get_query_parameter(request, query_able=[], sort_able=[]):
|
|
||||||
# 提取查询请求参数,返回Query类查询参数,写成修饰器
|
|
||||||
|
|
||||||
def decorator(view):
|
|
||||||
@functools.wraps(view)
|
|
||||||
def wrapped_view(*args, **kwargs):
|
|
||||||
|
|
||||||
re = Query()
|
|
||||||
|
|
||||||
if 'limit' in request.json:
|
|
||||||
try:
|
|
||||||
re.limit = int(request.json['limit'])
|
|
||||||
except:
|
|
||||||
return jsonify({'status': 200, 'code': -101, 'data': {}, 'msg': code_get_msg(-101)})
|
|
||||||
|
|
||||||
if 'offset' in request.json:
|
|
||||||
try:
|
|
||||||
re.offset = int(request.json['offset'])
|
|
||||||
except:
|
|
||||||
return jsonify({'status': 200, 'code': -101, 'data': {}, 'msg': code_get_msg(-101)})
|
|
||||||
if 'query' in request.json:
|
|
||||||
re.query = request.json['query']
|
|
||||||
for i in re.query:
|
|
||||||
if i not in query_able:
|
|
||||||
return jsonify({'status': 200, 'code': -102, 'data': {}, 'msg': code_get_msg(-102)})
|
|
||||||
if 'sort' in request.json:
|
|
||||||
re.sort = request.json['sort']
|
|
||||||
for i in re.sort:
|
|
||||||
if 'column' not in i or i['column'] not in sort_able:
|
|
||||||
return jsonify({'status': 200, 'code': -103, 'data': {}, 'msg': code_get_msg(-103)})
|
|
||||||
if not 'order' in i:
|
|
||||||
i['order'] = 'ASC'
|
|
||||||
else:
|
|
||||||
if i['order'] not in ['ASC', 'DESC']:
|
|
||||||
return jsonify({'status': 200, 'code': -104, 'data': {}, 'msg': code_get_msg(-104)})
|
|
||||||
|
|
||||||
return view(re, *args, **kwargs)
|
|
||||||
|
|
||||||
return wrapped_view
|
|
||||||
return decorator
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/')
|
|
||||||
def ping():
|
|
||||||
return return_encode()
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/token', methods=['POST'])
|
|
||||||
def token_post():
|
|
||||||
# 登录,获取token
|
|
||||||
# {'auth': `base64(user_id:password)`}
|
|
||||||
|
|
||||||
if 'auth' in request.json:
|
|
||||||
data, code = api.api_auth.login(
|
|
||||||
str(request.json['auth']), request.remote_addr)
|
|
||||||
return return_encode(code, data)
|
|
||||||
else:
|
|
||||||
return return_encode(-1, {}, 401, 'No authentication')
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/token', methods=['GET'])
|
|
||||||
@api.api_auth.role_required(request, ['select_me', 'select'])
|
|
||||||
def token_get(user):
|
|
||||||
# 判断登录有效性
|
|
||||||
return return_encode()
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/token', methods=['DELETE'])
|
|
||||||
@api.api_auth.role_required(request, ['change_me', 'select_me', 'select'])
|
|
||||||
def token_delete(user):
|
|
||||||
# 登出
|
|
||||||
return return_encode(api.api_auth.logout(user))
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/users', methods=['GET'])
|
|
||||||
@api.api_auth.role_required(request, ['select'])
|
|
||||||
@get_query_parameter(request, ['user_id', 'name', 'user_code'], [
|
|
||||||
'user_id', 'name', 'user_code', 'join_date', 'rating_ptt', 'time_played', 'ticket', 'world_rank_score'])
|
|
||||||
def users_get(query, user):
|
|
||||||
# 查询全用户信息
|
|
||||||
|
|
||||||
data = users.get_users(query)
|
|
||||||
|
|
||||||
if not data:
|
|
||||||
return return_encode(-2)
|
|
||||||
|
|
||||||
return return_encode(0, data)
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/users/<int:user_id>', methods=['GET'])
|
|
||||||
@api.api_auth.role_required(request, ['select', 'select_me'])
|
|
||||||
def users_user_get(user, user_id):
|
|
||||||
# 查询用户信息
|
|
||||||
|
|
||||||
if user_id == 'me':
|
|
||||||
user_id = user.user_id
|
|
||||||
|
|
||||||
if user_id <= 0:
|
|
||||||
return return_encode(-4)
|
|
||||||
|
|
||||||
if user_id != user.user_id and not 'select' in user.power and user.user_id != 0: # 查别人需要select权限
|
|
||||||
return return_encode(-1, {}, 403, 'No permission')
|
|
||||||
|
|
||||||
data = users.get_user_info(user_id)
|
|
||||||
|
|
||||||
if not data:
|
|
||||||
return return_encode(-3)
|
|
||||||
|
|
||||||
return return_encode(0, data)
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/users/<int:user_id>/b30', methods=['GET'])
|
|
||||||
@api.api_auth.role_required(request, ['select', 'select_me'])
|
|
||||||
def users_user_b30_get(user, user_id):
|
|
||||||
# 查询用户b30
|
|
||||||
|
|
||||||
if user_id <= 0:
|
|
||||||
return return_encode(-4)
|
|
||||||
|
|
||||||
if user_id != user.user_id and not 'select' in user.power and user.user_id != 0: # 查别人需要select权限
|
|
||||||
return return_encode(-1, {}, 403, 'No permission')
|
|
||||||
|
|
||||||
data = users.get_user_b30(user_id)
|
|
||||||
|
|
||||||
if data['data'] == []:
|
|
||||||
return return_encode(-3)
|
|
||||||
|
|
||||||
return return_encode(0, data)
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/users/<int:user_id>/best', methods=['GET'])
|
|
||||||
@api.api_auth.role_required(request, ['select', 'select_me'])
|
|
||||||
@get_query_parameter(request, ['song_id', 'difficulty'], [
|
|
||||||
'song_id', 'difficulty', 'score', 'time_played', 'rating'])
|
|
||||||
def users_user_best_get(query, user, user_id):
|
|
||||||
# 查询用户所有best成绩
|
|
||||||
|
|
||||||
if user_id <= 0:
|
|
||||||
return return_encode(-4)
|
|
||||||
|
|
||||||
if user_id != user.user_id and not 'select' in user.power and user.user_id != 0: # 查别人需要select权限
|
|
||||||
return return_encode(-1, {}, 403, 'No permission')
|
|
||||||
|
|
||||||
data = users.get_user_best(user_id, query)
|
|
||||||
|
|
||||||
if data['data'] == []:
|
|
||||||
return return_encode(-3)
|
|
||||||
|
|
||||||
return return_encode(0, data)
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/users/<int:user_id>/r30', methods=['GET'])
|
|
||||||
@api.api_auth.role_required(request, ['select', 'select_me'])
|
|
||||||
def users_user_r30_get(user, user_id):
|
|
||||||
# 查询用户r30
|
|
||||||
|
|
||||||
if user_id <= 0:
|
|
||||||
return return_encode(-4)
|
|
||||||
|
|
||||||
if user_id != user.user_id and not 'select' in user.power and user.user_id != 0: # 查别人需要select权限
|
|
||||||
return return_encode(-1, {}, 403, 'No permission')
|
|
||||||
|
|
||||||
data = users.get_user_r30(user_id)
|
|
||||||
|
|
||||||
if data['data'] == []:
|
|
||||||
return return_encode(-3)
|
|
||||||
|
|
||||||
return return_encode(0, data)
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/songs/<string:song_id>', methods=['GET'])
|
|
||||||
@api.api_auth.role_required(request, ['select', 'select_song_info'])
|
|
||||||
def songs_song_get(user, song_id):
|
|
||||||
# 查询歌曲信息
|
|
||||||
|
|
||||||
data = api.songs.get_song_info(song_id)
|
|
||||||
|
|
||||||
if not data:
|
|
||||||
return return_encode(-2)
|
|
||||||
|
|
||||||
return return_encode(0, data)
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/songs', methods=['GET'])
|
|
||||||
@api.api_auth.role_required(request, ['select', 'select_song_info'])
|
|
||||||
@get_query_parameter(request, ['sid', 'name_en', 'name_jp', 'pakset', 'artist'], [
|
|
||||||
'sid', 'name_en', 'name_jp', 'pakset', 'artist', 'date', 'rating_pst', 'rating_prs', 'rating_ftr', 'rating_byn'])
|
|
||||||
def songs_get(query, user):
|
|
||||||
# 查询全歌曲信息
|
|
||||||
|
|
||||||
data = api.songs.get_songs(query)
|
|
||||||
|
|
||||||
if not data:
|
|
||||||
return return_encode(-2)
|
|
||||||
|
|
||||||
return return_encode(0, data)
|
|
||||||
2
latest version/api/constant.py
Normal file
2
latest version/api/constant.py
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
class Constant:
|
||||||
|
QUERY_KEYS = ['limit', 'offset', 'query', 'fuzzy_query', 'sort']
|
||||||
@@ -1,57 +1,48 @@
|
|||||||
from core.sql import Connect
|
from core.error import ArcError, NoData
|
||||||
from core.sql import Sql
|
from core.song import Song
|
||||||
|
from core.sql import Connect, Query, Sql
|
||||||
|
from flask import Blueprint, request
|
||||||
|
|
||||||
|
from .api_auth import request_json_handle, role_required
|
||||||
|
from .api_code import error_return, success_return
|
||||||
|
from .constant import Constant
|
||||||
|
|
||||||
|
bp = Blueprint('songs', __name__, url_prefix='/songs')
|
||||||
|
|
||||||
|
|
||||||
def get_song_info(song_id):
|
@bp.route('/<string:song_id>', methods=['GET'])
|
||||||
# 查询指定歌曲信息,返回字典
|
@role_required(request, ['select', 'select_song_info'])
|
||||||
r = {}
|
def songs_song_get(user, song_id):
|
||||||
|
'''查询歌曲信息'''
|
||||||
with Connect('') as c:
|
with Connect() as c:
|
||||||
c.execute('''select * from chart where song_id=:a''', {'a': song_id})
|
try:
|
||||||
x = c.fetchone()
|
s = Song(c, song_id).select()
|
||||||
if x:
|
return success_return(s.to_dict())
|
||||||
r = {'song_id': x[0],
|
except ArcError as e:
|
||||||
'name': {'name_en': x[1],
|
return error_return(e)
|
||||||
'name_jp': x[2]},
|
return error_return()
|
||||||
'pakset': x[5],
|
|
||||||
'artist': x[6],
|
|
||||||
'date': x[9],
|
|
||||||
'rating_pst': x[13]/10,
|
|
||||||
'rating_prs': x[14]/10,
|
|
||||||
'rating_ftr': x[15]/10,
|
|
||||||
'rating_byn': x[16]/10,
|
|
||||||
'difficultly_pst': x[17]/2,
|
|
||||||
'difficultly_prs': x[18]/2,
|
|
||||||
'difficultly_ftr': x[19]/2,
|
|
||||||
'difficultly_byn': x[20]/2
|
|
||||||
}
|
|
||||||
|
|
||||||
return r
|
|
||||||
|
|
||||||
|
|
||||||
def get_songs(query=None):
|
@bp.route('', methods=['GET'])
|
||||||
# 查询全部歌曲信息,返回字典列表
|
@role_required(request, ['select', 'select_song_info'])
|
||||||
r = []
|
@request_json_handle(request, optional_keys=Constant.QUERY_KEYS)
|
||||||
|
def songs_get(data, user):
|
||||||
with Connect('') as c:
|
'''查询全歌曲信息'''
|
||||||
x = Sql.select(c, 'chart', [], query)
|
A = ['song_id', 'name']
|
||||||
|
B = ['song_id', 'name', 'rating_pst',
|
||||||
if x:
|
'rating_prs', 'rating_ftr', 'rating_byn']
|
||||||
|
with Connect() as c:
|
||||||
|
try:
|
||||||
|
query = Query(A, A, B).from_data(data)
|
||||||
|
x = Sql(c).select('chart', query=query)
|
||||||
|
r = []
|
||||||
for i in x:
|
for i in x:
|
||||||
r.append({'sid': i[0],
|
r.append(Song(c).from_list(i))
|
||||||
'name': {'name_en': i[1],
|
|
||||||
'name_jp': i[2]},
|
|
||||||
'pakset': i[5],
|
|
||||||
'artist': i[6],
|
|
||||||
'date': i[9],
|
|
||||||
'rating_pst': i[13]/10,
|
|
||||||
'rating_prs': i[14]/10,
|
|
||||||
'rating_ftr': i[15]/10,
|
|
||||||
'rating_byn': i[16]/10,
|
|
||||||
'difficultly_pst': i[17]/2,
|
|
||||||
'difficultly_prs': i[18]/2,
|
|
||||||
'difficultly_ftr': i[19]/2,
|
|
||||||
'difficultly_byn': i[20]/2
|
|
||||||
})
|
|
||||||
|
|
||||||
return r
|
if not r:
|
||||||
|
raise NoData(api_error_code=-2)
|
||||||
|
|
||||||
|
return success_return([x.to_dict() for x in r])
|
||||||
|
except ArcError as e:
|
||||||
|
return error_return(e)
|
||||||
|
return error_return()
|
||||||
|
|||||||
57
latest version/api/token.py
Normal file
57
latest version/api/token.py
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
from base64 import b64decode
|
||||||
|
|
||||||
|
from core.api_user import APIUser
|
||||||
|
from core.error import ArcError, PostError
|
||||||
|
from core.sql import Connect
|
||||||
|
from flask import Blueprint, request
|
||||||
|
|
||||||
|
from .api_auth import request_json_handle, role_required
|
||||||
|
from .api_code import error_return, success_return
|
||||||
|
|
||||||
|
bp = Blueprint('token', __name__, url_prefix='/token')
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route('', methods=['POST'])
|
||||||
|
@request_json_handle(request, required_keys=['auth'])
|
||||||
|
def token_post(data):
|
||||||
|
'''
|
||||||
|
登录,获取token\
|
||||||
|
{'auth': base64('<user_id>:<password>')}
|
||||||
|
'''
|
||||||
|
try:
|
||||||
|
auth_decode = bytes.decode(b64decode(data['auth']))
|
||||||
|
except:
|
||||||
|
return error_return(PostError(api_error_code=-100))
|
||||||
|
if not ':' in auth_decode:
|
||||||
|
return error_return(PostError(api_error_code=-100))
|
||||||
|
name, password = auth_decode.split(':', 1)
|
||||||
|
|
||||||
|
with Connect() as c:
|
||||||
|
try:
|
||||||
|
user = APIUser(c)
|
||||||
|
user.login(name, password, request.remote_addr)
|
||||||
|
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'])
|
||||||
|
@role_required(request, ['select_me', 'select'])
|
||||||
|
def token_get(user):
|
||||||
|
'''判断登录有效性'''
|
||||||
|
return success_return()
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route('', methods=['DELETE'])
|
||||||
|
@role_required(request, ['change_me', 'select_me', 'select'])
|
||||||
|
def token_delete(user):
|
||||||
|
'''登出'''
|
||||||
|
with Connect() as c:
|
||||||
|
try:
|
||||||
|
user.c = c
|
||||||
|
user.logout()
|
||||||
|
return success_return()
|
||||||
|
except ArcError as e:
|
||||||
|
return error_return(e)
|
||||||
|
return error_return()
|
||||||
@@ -1,138 +1,150 @@
|
|||||||
from flask import (
|
from core.error import ArcError, InputError, NoAccess, NoData
|
||||||
Blueprint, request
|
from core.score import Potential, UserScoreList
|
||||||
)
|
from core.sql import Connect, Query, Sql
|
||||||
|
from core.user import UserInfo, UserRegister
|
||||||
|
from flask import Blueprint, request
|
||||||
|
|
||||||
from .api_code import return_encode
|
from .api_auth import request_json_handle, role_required
|
||||||
from .api_auth import role_required
|
from .api_code import error_return, success_return
|
||||||
from core.user import UserRegister
|
from .constant import Constant
|
||||||
from core.error import ArcError, PostError
|
|
||||||
from core.sql import Connect
|
|
||||||
from core.sql import Sql
|
|
||||||
import time
|
|
||||||
import web.webscore
|
|
||||||
import server.info
|
|
||||||
|
|
||||||
bp = Blueprint('users', __name__, url_prefix='/users')
|
bp = Blueprint('users', __name__, url_prefix='/users')
|
||||||
|
|
||||||
|
|
||||||
@bp.route('', methods=['POST'])
|
@bp.route('', methods=['POST'])
|
||||||
@role_required(request, ['change'])
|
@role_required(request, ['change'])
|
||||||
def users_post(user):
|
@request_json_handle(request, ['name', 'password', 'email'])
|
||||||
# 注册用户
|
def users_post(data, _):
|
||||||
|
'''注册一个用户'''
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
new_user = UserRegister(c)
|
new_user = UserRegister(c)
|
||||||
try:
|
try:
|
||||||
if 'name' in request.json:
|
new_user.set_name(data['name'])
|
||||||
new_user.set_name(request.json['name'])
|
new_user.set_password(data['password'])
|
||||||
else:
|
new_user.set_email(data['email'])
|
||||||
raise PostError('No name provided.')
|
|
||||||
|
|
||||||
if 'password' in request.json:
|
|
||||||
new_user.set_password(request.json['password'])
|
|
||||||
else:
|
|
||||||
raise PostError('No password provided.')
|
|
||||||
|
|
||||||
if 'email' in request.json:
|
|
||||||
new_user.set_email(request.json['email'])
|
|
||||||
else:
|
|
||||||
raise PostError('No email provided.')
|
|
||||||
|
|
||||||
new_user.register()
|
new_user.register()
|
||||||
|
return success_return({'user_id': new_user.user_id, 'user_code': new_user.user_code})
|
||||||
except ArcError as e:
|
except ArcError as e:
|
||||||
return return_encode(e.api_error_code)
|
return error_return(e)
|
||||||
|
return error_return()
|
||||||
return return_encode(0, {'user_id': new_user.user_id, 'user_code': new_user.user_code})
|
|
||||||
|
|
||||||
|
|
||||||
def get_users(query=None):
|
@bp.route('', methods=['GET'])
|
||||||
# 获取全用户信息,返回字典列表
|
@role_required(request, ['select'])
|
||||||
|
@request_json_handle(request, optional_keys=Constant.QUERY_KEYS)
|
||||||
r = []
|
def users_get(data, user):
|
||||||
|
'''查询全用户信息'''
|
||||||
|
A = ['user_id', 'name', 'user_code']
|
||||||
|
B = ['user_id', 'name', 'user_code', 'join_date',
|
||||||
|
'rating_ptt', 'time_played', 'ticket', 'world_rank_score']
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
x = Sql.select(c, 'user', [], query)
|
try:
|
||||||
|
query = Query(A, A, B).from_data(data)
|
||||||
if x:
|
x = Sql(c).select('user', query=query)
|
||||||
|
r = []
|
||||||
for i in x:
|
for i in x:
|
||||||
if i[23] != -1:
|
r.append(UserInfo(c).from_list(i))
|
||||||
character_id = i[23]
|
|
||||||
else:
|
|
||||||
character_id = i[6]
|
|
||||||
r.append({
|
|
||||||
'user_id': i[0],
|
|
||||||
'name': i[1],
|
|
||||||
'join_date': i[3],
|
|
||||||
'user_code': i[4],
|
|
||||||
'rating_ptt': i[5]/100,
|
|
||||||
'character_id': character_id,
|
|
||||||
'is_char_uncapped': i[8] == 1,
|
|
||||||
'is_char_uncapped_override': i[9] == 1,
|
|
||||||
'is_hide_rating': i[10],
|
|
||||||
'ticket': i[26]
|
|
||||||
})
|
|
||||||
|
|
||||||
return r
|
if not r:
|
||||||
|
raise NoData(api_error_code=-2)
|
||||||
|
|
||||||
|
return success_return([{
|
||||||
|
'user_id': x.user_id,
|
||||||
|
'name': x.name,
|
||||||
|
'join_date': x.join_date,
|
||||||
|
'user_code': x.user_code,
|
||||||
|
'rating_ptt': x.rating_ptt/100,
|
||||||
|
'character_id': x.character.character_id,
|
||||||
|
'is_char_uncapped': x.character.is_uncapped,
|
||||||
|
'is_char_uncapped_override': x.character.is_uncapped_override,
|
||||||
|
'is_hide_rating': x.is_hide_rating,
|
||||||
|
'ticket': x.ticket
|
||||||
|
} for x in r])
|
||||||
|
except ArcError as e:
|
||||||
|
return error_return(e)
|
||||||
|
return error_return()
|
||||||
|
|
||||||
|
|
||||||
def get_user_info(user_id):
|
@bp.route('/<int:user_id>', methods=['GET'])
|
||||||
# 获取用户信息,返回字典,其实就是调用user/me信息
|
@role_required(request, ['select', 'select_me'])
|
||||||
|
def users_user_get(user, user_id):
|
||||||
|
'''查询用户信息'''
|
||||||
|
if user_id <= 0:
|
||||||
|
return error_return(InputError(api_error_code=-4))
|
||||||
|
# 查别人需要select权限
|
||||||
|
if user_id != user.user_id and user.user_id != 0 and not user.role.has_power('select'):
|
||||||
|
return error_return(NoAccess('No permission', api_error_code=-1), 403)
|
||||||
|
|
||||||
r = {}
|
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
r = server.info.get_user_me(c, user_id)
|
try:
|
||||||
|
u = UserInfo(c, user_id)
|
||||||
return r
|
return success_return(u.to_dict())
|
||||||
|
except ArcError as e:
|
||||||
|
return error_return(e)
|
||||||
|
return error_return()
|
||||||
|
|
||||||
|
|
||||||
def get_user_b30(user_id):
|
@bp.route('/<int:user_id>/b30', methods=['GET'])
|
||||||
# 获取用户b30信息,返回字典
|
@role_required(request, ['select', 'select_me'])
|
||||||
|
def users_user_b30_get(user, user_id):
|
||||||
|
'''查询用户b30'''
|
||||||
|
if user_id <= 0:
|
||||||
|
return error_return(InputError(api_error_code=-4))
|
||||||
|
# 查别人需要select权限
|
||||||
|
if user_id != user.user_id and user.user_id != 0 and not user.role.has_power('select'):
|
||||||
|
return error_return(NoAccess('No permission', api_error_code=-1), 403)
|
||||||
|
|
||||||
r = []
|
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
r = web.webscore.get_user_score(c, user_id, 30)
|
try:
|
||||||
|
x = UserScoreList(c, UserInfo(c, user_id))
|
||||||
bestptt = 0
|
x.query.limit = 30
|
||||||
for i in r:
|
x.select_from_user()
|
||||||
if i['rating']:
|
r = x.to_dict_list()
|
||||||
bestptt += i['rating']
|
rating_sum = sum([i.rating for i in x.scores])
|
||||||
if 'time_played' in i:
|
return success_return({'user_id': user_id, 'b30_ptt': rating_sum / 30, 'data': r})
|
||||||
i['time_played'] = int(time.mktime(time.strptime(
|
except ArcError as e:
|
||||||
i['time_played'], '%Y-%m-%d %H:%M:%S')))
|
return error_return(e)
|
||||||
|
return error_return()
|
||||||
return {'user_id': user_id, 'b30_ptt': bestptt / 30, 'data': r}
|
|
||||||
|
|
||||||
|
|
||||||
def get_user_best(user_id, query=None):
|
@bp.route('/<int:user_id>/best', methods=['GET'])
|
||||||
# 获取用户b30信息,返回字典
|
@role_required(request, ['select', 'select_me'])
|
||||||
|
@request_json_handle(request, optional_keys=Constant.QUERY_KEYS)
|
||||||
|
def users_user_best_get(data, user, user_id):
|
||||||
|
'''查询用户所有best成绩'''
|
||||||
|
if user_id <= 0:
|
||||||
|
return error_return(InputError(api_error_code=-4))
|
||||||
|
# 查别人需要select权限
|
||||||
|
if user_id != user.user_id and user.user_id != 0 and not user.role.has_power('select'):
|
||||||
|
return error_return(NoAccess('No permission', api_error_code=-1), 403)
|
||||||
|
|
||||||
r = []
|
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
x = Sql.select(c, 'best_score', [], query)
|
try:
|
||||||
if x:
|
x = UserScoreList(c, UserInfo(c, user_id))
|
||||||
for i in x:
|
x.query.from_data(data)
|
||||||
r.append({
|
x.select_from_user()
|
||||||
"song_id": i[1],
|
r = x.to_dict_list()
|
||||||
"difficulty": i[2],
|
return success_return({'user_id': user_id, 'data': r})
|
||||||
"score": i[3],
|
except ArcError as e:
|
||||||
"shiny_perfect_count": i[4],
|
return error_return(e)
|
||||||
"perfect_count": i[5],
|
return error_return()
|
||||||
"near_count": i[6],
|
|
||||||
"miss_count": i[7],
|
|
||||||
"health": i[8],
|
|
||||||
"modifier": i[9],
|
|
||||||
"time_played": i[10],
|
|
||||||
"best_clear_type": i[11],
|
|
||||||
"clear_type": i[12],
|
|
||||||
"rating": i[13]
|
|
||||||
})
|
|
||||||
|
|
||||||
return {'user_id': user_id, 'data': r}
|
|
||||||
|
|
||||||
|
|
||||||
def get_user_r30(user_id):
|
@bp.route('/<int:user_id>/r30', methods=['GET'])
|
||||||
# 获取用户r30信息,返回字典
|
@role_required(request, ['select', 'select_me'])
|
||||||
|
def users_user_r30_get(user, user_id):
|
||||||
|
'''查询用户r30'''
|
||||||
|
|
||||||
|
if user_id <= 0:
|
||||||
|
return error_return(InputError(api_error_code=-4))
|
||||||
|
# 查别人需要select权限
|
||||||
|
if user_id != user.user_id and user.user_id != 0 and not user.role.has_power('select'):
|
||||||
|
return error_return(NoAccess('No permission', api_error_code=-1), 403)
|
||||||
|
|
||||||
r = []
|
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
r, r10_ptt = web.webscore.get_user_recent30(c, user_id)
|
try:
|
||||||
|
p = Potential(c, UserInfo(c, user_id))
|
||||||
return {'user_id': user_id, 'r10_ptt': r10_ptt, 'data': r}
|
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()
|
||||||
|
|||||||
134
latest version/core/api_user.py
Normal file
134
latest version/core/api_user.py
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
from hashlib import sha256
|
||||||
|
from os import urandom
|
||||||
|
from time import time
|
||||||
|
|
||||||
|
from .error import NoAccess, NoData, UserBan
|
||||||
|
from .user import UserOnline
|
||||||
|
|
||||||
|
|
||||||
|
class Power:
|
||||||
|
def __init__(self, c=None):
|
||||||
|
self.c = c
|
||||||
|
self.power_id: int = None
|
||||||
|
self.power_name: str = None
|
||||||
|
self.caption: str = None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, d: dict, c=None) -> 'Power':
|
||||||
|
p = cls(c)
|
||||||
|
p.power_id = d['power_id']
|
||||||
|
p.power_name = d['power_name']
|
||||||
|
p.caption = d['caption']
|
||||||
|
return p
|
||||||
|
|
||||||
|
def select_from_name(self, power_name: str) -> 'Power':
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Role:
|
||||||
|
def __init__(self, c=None):
|
||||||
|
self.c = c
|
||||||
|
self.role_id: int = None
|
||||||
|
self.role_name: str = None
|
||||||
|
self.caption: str = None
|
||||||
|
|
||||||
|
self.powers: list = None
|
||||||
|
|
||||||
|
def has_power(self, power_name: str) -> bool:
|
||||||
|
'''判断role是否有power'''
|
||||||
|
for i in self.powers:
|
||||||
|
if i.power_name == power_name:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def select_from_id(self, role_id: int = None) -> 'Role':
|
||||||
|
'''用role_id查询role'''
|
||||||
|
if role_id is not None:
|
||||||
|
self.role_id = role_id
|
||||||
|
self.c.execute('''select role_name, caption from role where role_id = :a''',
|
||||||
|
{'a': self.role_id})
|
||||||
|
x = self.c.fetchone()
|
||||||
|
if x is None:
|
||||||
|
raise NoData('The role `%s` does not exist.' %
|
||||||
|
self.role_id, api_error_code=-200)
|
||||||
|
self.role_name = x[0]
|
||||||
|
self.caption = x[1]
|
||||||
|
return self
|
||||||
|
|
||||||
|
def select_powers(self) -> None:
|
||||||
|
'''查询role的全部powers'''
|
||||||
|
self.powers = []
|
||||||
|
self.c.execute('''select * from power where power_id in (select power_id from role_power where role_id=:a)''', {
|
||||||
|
'a': self.role_id})
|
||||||
|
x = self.c.fetchall()
|
||||||
|
for i in x:
|
||||||
|
self.powers.append(Power.from_dict(
|
||||||
|
{'power_id': i[0], 'power_name': i[1], 'caption': i[2]}, self.c))
|
||||||
|
|
||||||
|
|
||||||
|
class APIUser(UserOnline):
|
||||||
|
def __init__(self, c=None, user_id=None) -> None:
|
||||||
|
super().__init__(c, user_id)
|
||||||
|
self.api_token: str = None
|
||||||
|
self.role: 'Role' = None
|
||||||
|
|
||||||
|
self.ip: str = None
|
||||||
|
|
||||||
|
def select_role(self) -> None:
|
||||||
|
'''查询user的role'''
|
||||||
|
self.c.execute('''select role_id from user_role where user_id = :a''',
|
||||||
|
{'a': self.user_id})
|
||||||
|
x = self.c.fetchone()
|
||||||
|
self.role = Role(self.c)
|
||||||
|
if x is None:
|
||||||
|
# 默认role为user
|
||||||
|
self.role.role_id = 1
|
||||||
|
else:
|
||||||
|
self.role.role_id = int(x[0])
|
||||||
|
self.role.select_from_id()
|
||||||
|
|
||||||
|
def select_role_and_powers(self) -> None:
|
||||||
|
'''查询user的role,以及role的powers'''
|
||||||
|
self.select_role()
|
||||||
|
self.role.select_powers()
|
||||||
|
|
||||||
|
def select_user_id_from_api_token(self, api_token: str = None) -> None:
|
||||||
|
if api_token is not None:
|
||||||
|
self.api_token = api_token
|
||||||
|
self.c.execute('''select user_id from api_login where token = :token''', {
|
||||||
|
'token': self.api_token})
|
||||||
|
x = self.c.fetchone()
|
||||||
|
if x is None:
|
||||||
|
raise NoAccess('No token', api_error_code=-1)
|
||||||
|
self.user_id = x[0]
|
||||||
|
|
||||||
|
def logout(self) -> None:
|
||||||
|
self.c.execute(
|
||||||
|
'''delete from api_login where user_id=?''', (self.user_id,))
|
||||||
|
|
||||||
|
def login(self, name: str = None, password: str = None, ip: str = None) -> None:
|
||||||
|
if name is not None:
|
||||||
|
self.name = name
|
||||||
|
if password is not None:
|
||||||
|
self.password = password
|
||||||
|
if ip is not None:
|
||||||
|
self.ip = ip
|
||||||
|
self.c.execute('''select user_id, password from user where name = :a''', {
|
||||||
|
'a': self.name})
|
||||||
|
x = self.c.fetchone()
|
||||||
|
if x is None:
|
||||||
|
raise NoData('The user `%s` does not exist.' %
|
||||||
|
self.name, api_error_code=-201)
|
||||||
|
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)
|
||||||
|
|
||||||
|
self.user_id = x[0]
|
||||||
|
now = int(time() * 1000)
|
||||||
|
self.token = sha256(
|
||||||
|
(str(self.user_id) + str(now)).encode("utf8") + urandom(8)).hexdigest()
|
||||||
|
|
||||||
|
self.logout()
|
||||||
|
self.c.execute('''insert into api_login values(?,?,?,?)''',
|
||||||
|
(self.user_id, self.token, now, self.ip))
|
||||||
@@ -120,7 +120,6 @@ class Character:
|
|||||||
def skill_id_displayed(self) -> str:
|
def skill_id_displayed(self) -> str:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@property
|
|
||||||
def uncap_cores_to_dict(self):
|
def uncap_cores_to_dict(self):
|
||||||
return [x.to_dict() for x in self.uncap_cores]
|
return [x.to_dict() for x in self.uncap_cores]
|
||||||
|
|
||||||
@@ -210,13 +209,12 @@ class UserCharacter(Character):
|
|||||||
|
|
||||||
self.select_character_core()
|
self.select_character_core()
|
||||||
|
|
||||||
@property
|
def to_dict(self) -> dict:
|
||||||
def to_dict(self):
|
|
||||||
if self.char_type is None:
|
if self.char_type is None:
|
||||||
self.select_character_info(self.user)
|
self.select_character_info(self.user)
|
||||||
r = {"is_uncapped_override": self.is_uncapped_override,
|
r = {"is_uncapped_override": self.is_uncapped_override,
|
||||||
"is_uncapped": self.is_uncapped,
|
"is_uncapped": self.is_uncapped,
|
||||||
"uncap_cores": self.uncap_cores_to_dict,
|
"uncap_cores": self.uncap_cores_to_dict(),
|
||||||
"char_type": self.char_type,
|
"char_type": self.char_type,
|
||||||
"skill_id_uncap": self.skill.skill_id_uncap,
|
"skill_id_uncap": self.skill.skill_id_uncap,
|
||||||
"skill_requires_uncap": self.skill.skill_requires_uncap,
|
"skill_requires_uncap": self.skill.skill_requires_uncap,
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ class Constant:
|
|||||||
|
|
||||||
WORLD_MAP_FOLDER_PATH = './database/map/'
|
WORLD_MAP_FOLDER_PATH = './database/map/'
|
||||||
SONG_FILE_FOLDER_PATH = './database/songs/'
|
SONG_FILE_FOLDER_PATH = './database/songs/'
|
||||||
|
SQLITE_DATABASE_PATH = './database/arcaea_database.db'
|
||||||
|
|
||||||
DOWNLOAD_TIMES_LIMIT = Config.DOWNLOAD_TIMES_LIMIT
|
DOWNLOAD_TIMES_LIMIT = Config.DOWNLOAD_TIMES_LIMIT
|
||||||
DOWNLOAD_TIME_GAP_LIMIT = Config.DOWNLOAD_TIME_GAP_LIMIT
|
DOWNLOAD_TIME_GAP_LIMIT = Config.DOWNLOAD_TIME_GAP_LIMIT
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ class PostError(ArcError):
|
|||||||
|
|
||||||
class UserBan(ArcError):
|
class UserBan(ArcError):
|
||||||
# 用户封禁
|
# 用户封禁
|
||||||
def __init__(self, message=None, error_code=121, api_error_code=-999, extra_data=None) -> None:
|
def __init__(self, message=None, error_code=121, api_error_code=-202, extra_data=None) -> None:
|
||||||
super().__init__(message, error_code, api_error_code, extra_data)
|
super().__init__(message, error_code, api_error_code, extra_data)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ from .user import UserInfo
|
|||||||
def get_song_unlock(client_song_map: dict) -> bytes:
|
def get_song_unlock(client_song_map: dict) -> bytes:
|
||||||
'''处理可用歌曲bit,返回bytes'''
|
'''处理可用歌曲bit,返回bytes'''
|
||||||
|
|
||||||
user_song_unlock = [0] * 512
|
user_song_unlock = [0] * Constant.LINK_PLAY_UNLOCK_LENGTH
|
||||||
for i in range(0, 1024, 2):
|
for i in range(0, Constant.LINK_PLAY_UNLOCK_LENGTH*2, 2):
|
||||||
x = 0
|
x = 0
|
||||||
y = 0
|
y = 0
|
||||||
if str(i) in client_song_map:
|
if str(i) in client_song_map:
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ class UserPresentList:
|
|||||||
|
|
||||||
self.presents: list = None
|
self.presents: list = None
|
||||||
|
|
||||||
def to_dict(self) -> list:
|
def to_dict_list(self) -> list:
|
||||||
return [x.to_dict() for x in self.presents]
|
return [x.to_dict() for x in self.presents]
|
||||||
|
|
||||||
def select_user_presents(self) -> None:
|
def select_user_presents(self) -> None:
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ class Purchase:
|
|||||||
return self.price
|
return self.price
|
||||||
return self.orig_price
|
return self.orig_price
|
||||||
|
|
||||||
@property
|
|
||||||
def to_dict(self) -> dict:
|
def to_dict(self) -> dict:
|
||||||
price = self.price_displayed
|
price = self.price_displayed
|
||||||
r = {
|
r = {
|
||||||
@@ -143,9 +142,8 @@ class PurchaseList:
|
|||||||
self.user = user
|
self.user = user
|
||||||
self.purchases: list = []
|
self.purchases: list = []
|
||||||
|
|
||||||
@property
|
def to_dict_list(self) -> list:
|
||||||
def to_dict(self) -> list:
|
return [x.to_dict() for x in self.purchases]
|
||||||
return [x.to_dict for x in self.purchases]
|
|
||||||
|
|
||||||
def select_from_type(self, item_type: str) -> 'PurchaseList':
|
def select_from_type(self, item_type: str) -> 'PurchaseList':
|
||||||
self.c.execute('''select purchase_name from purchase_item where type = :a''', {
|
self.c.execute('''select purchase_name from purchase_item where type = :a''', {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
from .user import UserInfo
|
|
||||||
from .song import Chart
|
|
||||||
from .score import UserScore
|
|
||||||
from .constant import Constant
|
from .constant import Constant
|
||||||
|
from .score import UserScore
|
||||||
|
from .song import Chart
|
||||||
|
from .user import UserInfo
|
||||||
|
|
||||||
|
|
||||||
class RankList:
|
class RankList:
|
||||||
@@ -18,9 +18,8 @@ class RankList:
|
|||||||
self.limit: int = 20
|
self.limit: int = 20
|
||||||
self.user = None
|
self.user = None
|
||||||
|
|
||||||
@property
|
|
||||||
def to_dict_list(self) -> list:
|
def to_dict_list(self) -> list:
|
||||||
return [x.to_dict for x in self.list]
|
return [x.to_dict() for x in self.list]
|
||||||
|
|
||||||
def select_top(self) -> None:
|
def select_top(self) -> None:
|
||||||
'''
|
'''
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ class SaveData:
|
|||||||
self.story_data = []
|
self.story_data = []
|
||||||
self.createdAt = 0
|
self.createdAt = 0
|
||||||
|
|
||||||
@property
|
|
||||||
def to_dict(self):
|
def to_dict(self):
|
||||||
return {
|
return {
|
||||||
"user_id": self.user.user_id,
|
"user_id": self.user.user_id,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ from time import time
|
|||||||
from .constant import Constant
|
from .constant import Constant
|
||||||
from .error import NoData, StaminaNotEnough
|
from .error import NoData, StaminaNotEnough
|
||||||
from .song import Chart
|
from .song import Chart
|
||||||
|
from .sql import Query, Sql
|
||||||
from .util import md5
|
from .util import md5
|
||||||
from .world import WorldPlay
|
from .world import WorldPlay
|
||||||
|
|
||||||
@@ -122,7 +123,6 @@ class Score:
|
|||||||
self.rating = self.calculate_rating(self.song.chart_const, self.score)
|
self.rating = self.calculate_rating(self.song.chart_const, self.score)
|
||||||
return self.rating
|
return self.rating
|
||||||
|
|
||||||
@property
|
|
||||||
def to_dict(self) -> dict:
|
def to_dict(self) -> dict:
|
||||||
return {
|
return {
|
||||||
"rating": self.rating,
|
"rating": self.rating,
|
||||||
@@ -158,20 +158,29 @@ class UserScore(Score):
|
|||||||
raise NoData('No score data.')
|
raise NoData('No score data.')
|
||||||
self.user.select_user_about_character()
|
self.user.select_user_about_character()
|
||||||
|
|
||||||
|
self.from_list(x)
|
||||||
|
|
||||||
|
def from_list(self, x: list) -> 'UserScore':
|
||||||
|
if self.song.song_id is None:
|
||||||
|
self.song.song_id = x[1]
|
||||||
|
if self.song.difficulty is None:
|
||||||
|
self.song.difficulty = x[2]
|
||||||
self.set_score(x[3], x[4], x[5], x[6], x[7], x[8], x[9], x[10], x[12])
|
self.set_score(x[3], x[4], x[5], x[6], x[7], x[8], x[9], x[10], x[12])
|
||||||
self.best_clear_type = int(x[11])
|
self.best_clear_type = int(x[11])
|
||||||
self.rating = float(x[13])
|
self.rating = float(x[13])
|
||||||
|
|
||||||
@property
|
return self
|
||||||
def to_dict(self) -> dict:
|
|
||||||
r = super().to_dict
|
def to_dict(self, has_user_info: bool = True) -> dict:
|
||||||
r['user_id'] = self.user.user_id
|
r = super().to_dict()
|
||||||
r['name'] = self.user.name
|
|
||||||
r['best_clear_type'] = self.best_clear_type
|
r['best_clear_type'] = self.best_clear_type
|
||||||
r['is_skill_sealed'] = self.user.is_skill_sealed
|
if has_user_info:
|
||||||
character = self.user.character_displayed
|
r['user_id'] = self.user.user_id
|
||||||
r['is_char_uncapped'] = character.is_uncapped_displayed
|
r['name'] = self.user.name
|
||||||
r['character'] = character.character_id
|
r['is_skill_sealed'] = self.user.is_skill_sealed
|
||||||
|
character = self.user.character_displayed
|
||||||
|
r['is_char_uncapped'] = character.is_uncapped_displayed
|
||||||
|
r['character'] = character.character_id
|
||||||
if self.rank:
|
if self.rank:
|
||||||
r['rank'] = self.rank
|
r['rank'] = self.rank
|
||||||
return r
|
return r
|
||||||
@@ -196,14 +205,13 @@ class UserPlay(UserScore):
|
|||||||
self.ptt: Potential = None # 临时用来计算用户ptt的
|
self.ptt: Potential = None # 临时用来计算用户ptt的
|
||||||
self.world_play: 'WorldPlay' = None
|
self.world_play: 'WorldPlay' = None
|
||||||
|
|
||||||
@property
|
|
||||||
def to_dict(self) -> dict:
|
def to_dict(self) -> dict:
|
||||||
if self.is_world_mode is None:
|
if self.is_world_mode is None:
|
||||||
return {}
|
return {}
|
||||||
elif not self.is_world_mode:
|
elif not self.is_world_mode:
|
||||||
return {'global_rank': self.user.global_rank, 'user_rating': self.user.rating_ptt}
|
return {'global_rank': self.user.global_rank, 'user_rating': self.user.rating_ptt}
|
||||||
else:
|
else:
|
||||||
r = self.world_play.to_dict
|
r = self.world_play.to_dict()
|
||||||
r['user_rating'] = self.user.rating_ptt
|
r['user_rating'] = self.user.rating_ptt
|
||||||
r['global_rank'] = self.user.global_rank
|
r['global_rank'] = self.user.global_rank
|
||||||
return r
|
return r
|
||||||
@@ -384,6 +392,8 @@ class Potential:
|
|||||||
self.s30: list = None
|
self.s30: list = None
|
||||||
self.songs_selected: list = None
|
self.songs_selected: list = None
|
||||||
|
|
||||||
|
self.b30: list = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def value(self) -> float:
|
def value(self) -> float:
|
||||||
'''计算用户潜力值'''
|
'''计算用户潜力值'''
|
||||||
@@ -432,6 +442,15 @@ class Potential:
|
|||||||
i += 1
|
i += 1
|
||||||
return rating_sum
|
return rating_sum
|
||||||
|
|
||||||
|
def recent_30_to_dict_list(self) -> list:
|
||||||
|
if self.r30 is None:
|
||||||
|
self.select_recent_30()
|
||||||
|
return [{
|
||||||
|
'song_id': self.s30[i][:-1],
|
||||||
|
'difficulty': int(self.s30[i][-1]),
|
||||||
|
'rating': self.r30[i]
|
||||||
|
} for i in range(len(self.r30))]
|
||||||
|
|
||||||
def recent_30_update(self, pop_index: int, rating: float, song_id_difficulty: str) -> None:
|
def recent_30_update(self, pop_index: int, rating: float, song_id_difficulty: str) -> None:
|
||||||
self.r30.pop(pop_index)
|
self.r30.pop(pop_index)
|
||||||
self.s30.pop(pop_index)
|
self.s30.pop(pop_index)
|
||||||
@@ -449,3 +468,32 @@ class Potential:
|
|||||||
sql_list.append(self.user.user_id)
|
sql_list.append(self.user.user_id)
|
||||||
|
|
||||||
self.c.execute(sql, sql_list)
|
self.c.execute(sql, sql_list)
|
||||||
|
|
||||||
|
|
||||||
|
class UserScoreList:
|
||||||
|
'''
|
||||||
|
用户分数查询类\
|
||||||
|
properties: `user` - `User`类或子类的实例
|
||||||
|
'''
|
||||||
|
|
||||||
|
def __init__(self, c=None, user=None):
|
||||||
|
self.c = c
|
||||||
|
self.user = user
|
||||||
|
self.scores: list = None
|
||||||
|
self.query: 'Query' = Query(['user_id', 'song_id', 'difficulty'], ['song_id'], [
|
||||||
|
'rating', 'difficulty', 'song_id', 'score', 'time_played'])
|
||||||
|
|
||||||
|
def to_dict_list(self) -> list:
|
||||||
|
return [x.to_dict(has_user_info=False) for x in self.scores]
|
||||||
|
|
||||||
|
def select_from_user(self, user=None) -> None:
|
||||||
|
'''获取用户的best_score数据'''
|
||||||
|
if user is not None:
|
||||||
|
self.user = user
|
||||||
|
|
||||||
|
self.query.query_append({'user_id': self.user.user_id})
|
||||||
|
self.query.sort += [{'column': 'rating', 'order': 'DESC'}]
|
||||||
|
print(self.query.sort)
|
||||||
|
x = Sql(self.c).select('best_score', query=self.query)
|
||||||
|
|
||||||
|
self.scores = [UserScore(self.c, self.user).from_list(i) for i in x]
|
||||||
|
|||||||
@@ -9,6 +9,12 @@ class Chart:
|
|||||||
self.set_chart(song_id, difficulty)
|
self.set_chart(song_id, difficulty)
|
||||||
self.defnum: int = None
|
self.defnum: int = None
|
||||||
|
|
||||||
|
def to_dict(self) -> dict:
|
||||||
|
return {
|
||||||
|
'difficulty': self.difficulty,
|
||||||
|
'chart_const': self.chart_const
|
||||||
|
}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def chart_const(self) -> float:
|
def chart_const(self) -> float:
|
||||||
return self.defnum / 10 if self.defnum else -1
|
return self.defnum / 10 if self.defnum else -1
|
||||||
@@ -37,6 +43,37 @@ class Song:
|
|||||||
self.name: str = None
|
self.name: str = None
|
||||||
self.charts: dict = None
|
self.charts: dict = None
|
||||||
|
|
||||||
|
def to_dict(self) -> dict:
|
||||||
|
return {
|
||||||
|
'song_id': self.song_id,
|
||||||
|
'name': self.name,
|
||||||
|
'charts': [chart.to_dict() for chart in self.charts]
|
||||||
|
}
|
||||||
|
|
||||||
|
def from_list(self, x: list) -> 'Song':
|
||||||
|
if self.song_id is None:
|
||||||
|
self.song_id = x[0]
|
||||||
|
self.name = x[1]
|
||||||
|
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)]
|
||||||
|
self.charts[0].defnum = x[2]
|
||||||
|
self.charts[1].defnum = x[3]
|
||||||
|
self.charts[2].defnum = x[4]
|
||||||
|
self.charts[3].defnum = x[5]
|
||||||
|
return self
|
||||||
|
|
||||||
def insert(self) -> None:
|
def insert(self) -> None:
|
||||||
self.c.execute(
|
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))
|
'''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(self, song_id: str = None) -> 'Song':
|
||||||
|
if song_id is not None:
|
||||||
|
self.song_id = song_id
|
||||||
|
|
||||||
|
self.c.execute('''select * from chart where song_id=:a''', {
|
||||||
|
'a': self.song_id})
|
||||||
|
x = self.c.fetchone()
|
||||||
|
if x is None:
|
||||||
|
raise NoData('The song `%s` does not exist.' % self.song_id)
|
||||||
|
|
||||||
|
return self.from_list(x)
|
||||||
|
|||||||
@@ -1,12 +1,17 @@
|
|||||||
import sqlite3
|
import sqlite3
|
||||||
from flask import current_app
|
|
||||||
import traceback
|
import traceback
|
||||||
|
from certifi import where
|
||||||
|
|
||||||
|
from flask import current_app
|
||||||
|
|
||||||
|
from .constant import Constant
|
||||||
|
from .error import InputError
|
||||||
|
|
||||||
|
|
||||||
class Connect():
|
class Connect:
|
||||||
# 数据库连接类,上下文管理
|
# 数据库连接类,上下文管理
|
||||||
|
|
||||||
def __init__(self, file_path='./database/arcaea_database.db'):
|
def __init__(self, file_path=Constant.SQLITE_DATABASE_PATH):
|
||||||
"""
|
"""
|
||||||
数据库连接,默认连接arcaea_database.db\
|
数据库连接,默认连接arcaea_database.db\
|
||||||
接受:文件路径\
|
接受:文件路径\
|
||||||
@@ -34,15 +39,126 @@ class Connect():
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
class Sql():
|
class Query:
|
||||||
|
'''查询参数类'''
|
||||||
|
|
||||||
@staticmethod
|
def __init__(self, query_able: list = None, quzzy_query_able: list = None, sort_able: list = None) -> None:
|
||||||
def select(c, table_name, target_column=[], query=None):
|
self.query_able: list = query_able
|
||||||
# 执行查询单句sql语句,返回fetchall数据
|
self.quzzy_query_able: list = quzzy_query_able
|
||||||
# 使用准确查询,且在单表内
|
self.sort_able: list = sort_able
|
||||||
|
|
||||||
|
self.__limit: int = -1
|
||||||
|
self.__offset: int = 0
|
||||||
|
self.__query: dict = {} # {'name': 'admin'}
|
||||||
|
self.__fuzzy_query: dict = {} # {'name': 'dmi'}
|
||||||
|
# [{'column': 'user_id', 'order': 'ASC'}, ...]
|
||||||
|
self.__sort: list = []
|
||||||
|
|
||||||
|
@property
|
||||||
|
def limit(self) -> int:
|
||||||
|
return self.__limit
|
||||||
|
|
||||||
|
@limit.setter
|
||||||
|
def limit(self, limit: int) -> None:
|
||||||
|
if not isinstance(limit, int):
|
||||||
|
raise InputError(api_error_code=-101)
|
||||||
|
self.__limit = limit
|
||||||
|
|
||||||
|
@property
|
||||||
|
def offset(self) -> int:
|
||||||
|
return self.__offset
|
||||||
|
|
||||||
|
@offset.setter
|
||||||
|
def offset(self, offset: int) -> None:
|
||||||
|
if not isinstance(offset, int):
|
||||||
|
raise InputError(api_error_code=-101)
|
||||||
|
self.__offset = offset
|
||||||
|
|
||||||
|
@property
|
||||||
|
def query(self) -> dict:
|
||||||
|
return self.__query
|
||||||
|
|
||||||
|
@query.setter
|
||||||
|
def query(self, query: dict) -> None:
|
||||||
|
self.__query = {}
|
||||||
|
self.query_append(query)
|
||||||
|
|
||||||
|
def query_append(self, query: dict) -> None:
|
||||||
|
if not isinstance(query, dict):
|
||||||
|
raise InputError(api_error_code=-101)
|
||||||
|
if self.query_able is not None and query and not set(query).issubset(set(self.query_able)):
|
||||||
|
raise InputError(api_error_code=-102)
|
||||||
|
if not self.__query:
|
||||||
|
self.__query = query
|
||||||
|
else:
|
||||||
|
self.__query.update(query)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def fuzzy_query(self) -> dict:
|
||||||
|
return self.__fuzzy_query
|
||||||
|
|
||||||
|
@fuzzy_query.setter
|
||||||
|
def fuzzy_query(self, fuzzy_query: dict) -> None:
|
||||||
|
self.__fuzzy_query = {}
|
||||||
|
self.fuzzy_query_append(fuzzy_query)
|
||||||
|
|
||||||
|
def fuzzy_query_append(self, fuzzy_query: dict) -> None:
|
||||||
|
if not isinstance(fuzzy_query, dict):
|
||||||
|
raise InputError(api_error_code=-101)
|
||||||
|
if self.quzzy_query_able is not None and fuzzy_query and not set(fuzzy_query).issubset(set(self.quzzy_query_able)):
|
||||||
|
raise InputError(api_error_code=-102)
|
||||||
|
if not self.__fuzzy_query:
|
||||||
|
self.__fuzzy_query = fuzzy_query
|
||||||
|
else:
|
||||||
|
self.__fuzzy_query.update(fuzzy_query)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def sort(self) -> list:
|
||||||
|
return self.__sort
|
||||||
|
|
||||||
|
@sort.setter
|
||||||
|
def sort(self, sort: list) -> None:
|
||||||
|
if not isinstance(sort, list):
|
||||||
|
raise InputError(api_error_code=-101)
|
||||||
|
if self.sort_able is not None and sort:
|
||||||
|
for x in sort:
|
||||||
|
if not isinstance(x, dict):
|
||||||
|
raise InputError(api_error_code=-101)
|
||||||
|
if 'column' not in x or x['column'] not in self.sort_able:
|
||||||
|
raise InputError(api_error_code=-103)
|
||||||
|
if 'order' not in x:
|
||||||
|
x['order'] = 'ASC'
|
||||||
|
else:
|
||||||
|
if x['order'] not in ['ASC', 'DESC']:
|
||||||
|
raise InputError(api_error_code=-104)
|
||||||
|
self.__sort = sort
|
||||||
|
|
||||||
|
def set_value(self, limit=-1, offset=0, query={}, fuzzy_query={}, sort=[]) -> None:
|
||||||
|
self.limit = limit
|
||||||
|
self.offset = offset
|
||||||
|
self.query = query
|
||||||
|
self.fuzzy_query = fuzzy_query
|
||||||
|
self.sort = sort
|
||||||
|
|
||||||
|
def from_data(self, d: dict) -> 'Query':
|
||||||
|
self.set_value(d.get('limit', -1), d.get('offset', 0),
|
||||||
|
d.get('query', {}), d.get('fuzzy_query', {}), d.get('sort', []))
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
class Sql:
|
||||||
|
'''
|
||||||
|
数据库增查删改类
|
||||||
|
'''
|
||||||
|
|
||||||
|
def __init__(self, c=None) -> None:
|
||||||
|
self.c = c
|
||||||
|
|
||||||
|
def select(self, table_name: str, target_column: 'list' = [], query: 'Query' = None) -> list:
|
||||||
|
'''单表内行查询单句sql语句,返回fetchall数据'''
|
||||||
|
|
||||||
sql = 'select '
|
sql = 'select '
|
||||||
sql_dict = {}
|
sql_list = []
|
||||||
if len(target_column) >= 2:
|
if len(target_column) >= 2:
|
||||||
sql += target_column[0]
|
sql += target_column[0]
|
||||||
for i in range(1, len(target_column)):
|
for i in range(1, len(target_column)):
|
||||||
@@ -53,34 +169,48 @@ class Sql():
|
|||||||
else:
|
else:
|
||||||
sql += '* from ' + table_name
|
sql += '* from ' + table_name
|
||||||
|
|
||||||
where_field = []
|
if query is None:
|
||||||
where_value = []
|
self.c.execute(sql)
|
||||||
if query:
|
return self.c.fetchall()
|
||||||
for i in query.query:
|
|
||||||
where_field.append(i)
|
|
||||||
where_value.append(query.query[i])
|
|
||||||
|
|
||||||
if where_field and where_value:
|
where_key = []
|
||||||
|
where_like_key = []
|
||||||
|
for i in query.query:
|
||||||
|
where_key.append(i)
|
||||||
|
sql_list.append(query.query[i])
|
||||||
|
|
||||||
|
for i in query.fuzzy_query:
|
||||||
|
where_like_key.append(i)
|
||||||
|
sql_list.append('%' + query.fuzzy_query[i] + '%')
|
||||||
|
|
||||||
|
if where_key or where_like_key:
|
||||||
sql += ' where '
|
sql += ' where '
|
||||||
sql += where_field[0] + '=:' + where_field[0]
|
if where_key:
|
||||||
sql_dict[where_field[0]] = where_value[0]
|
sql += where_key[0] + '=?'
|
||||||
if len(where_field) >= 2:
|
if len(where_key) >= 2:
|
||||||
for i in range(1, len(where_field)):
|
for i in range(1, len(where_key)):
|
||||||
sql_dict[where_field[i]] = where_value[i]
|
sql += ' and ' + where_key[i] + '=?'
|
||||||
sql += ' and ' + where_field[i] + '=:' + where_field[i]
|
if where_like_key:
|
||||||
|
for i in where_like_key:
|
||||||
|
sql += ' and ' + i + ' like ?'
|
||||||
|
else:
|
||||||
|
sql += where_like_key[0] + ' like ?'
|
||||||
|
|
||||||
if query and query.sort:
|
if len(where_like_key) >= 2:
|
||||||
|
for i in range(1, len(where_key)):
|
||||||
|
sql += ' and ' + where_key[i] + ' like ?'
|
||||||
|
|
||||||
|
if query.sort:
|
||||||
sql += ' order by ' + \
|
sql += ' order by ' + \
|
||||||
query.sort[0]['column'] + ' ' + query.sort[0]['order']
|
query.sort[0]['column'] + ' ' + query.sort[0]['order']
|
||||||
for i in range(1, len(query.sort)):
|
for i in range(1, len(query.sort)):
|
||||||
sql += ', ' + query.sort[i]['column'] + \
|
sql += ', ' + query.sort[i]['column'] + \
|
||||||
' ' + query.sort[i]['order']
|
' ' + query.sort[i]['order']
|
||||||
|
|
||||||
if query and query.limit >= 0:
|
if query.limit >= 0:
|
||||||
sql += ' limit :limit offset :offset'
|
sql += ' limit ? offset ?'
|
||||||
sql_dict['limit'] = query.limit
|
sql_list.append(query.limit)
|
||||||
sql_dict['offset'] = query.offset
|
sql_list.append(query.offset)
|
||||||
|
|
||||||
c.execute(sql, sql_dict)
|
self.c.execute(sql, sql_list)
|
||||||
|
return self.c.fetchall()
|
||||||
return c.fetchall()
|
|
||||||
|
|||||||
@@ -45,12 +45,16 @@ class User:
|
|||||||
self.world_rank_score = None
|
self.world_rank_score = None
|
||||||
self.ban_flag = None
|
self.ban_flag = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def hash_pwd(self) -> str:
|
||||||
|
'''`password`的SHA-256值'''
|
||||||
|
return hashlib.sha256(self.password.encode("utf8")).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
class UserRegister(User):
|
class UserRegister(User):
|
||||||
def __init__(self, c) -> None:
|
def __init__(self, c) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.c = c
|
self.c = c
|
||||||
self.hash_pwd = None
|
|
||||||
|
|
||||||
def set_name(self, name: str):
|
def set_name(self, name: str):
|
||||||
if 3 <= len(name) <= 16:
|
if 3 <= len(name) <= 16:
|
||||||
@@ -67,7 +71,6 @@ class UserRegister(User):
|
|||||||
def set_password(self, password: str):
|
def set_password(self, password: str):
|
||||||
if 8 <= len(password) <= 32:
|
if 8 <= len(password) <= 32:
|
||||||
self.password = password
|
self.password = password
|
||||||
self.hash_pwd = hashlib.sha256(password.encode("utf8")).hexdigest()
|
|
||||||
else:
|
else:
|
||||||
raise InputError('Password is invalid.')
|
raise InputError('Password is invalid.')
|
||||||
|
|
||||||
@@ -146,7 +149,6 @@ class UserLogin(User):
|
|||||||
self.c = c
|
self.c = c
|
||||||
self.device_id = None
|
self.device_id = None
|
||||||
self.ip = None
|
self.ip = None
|
||||||
self.hash_pwd = None
|
|
||||||
self.token = None
|
self.token = None
|
||||||
self.now = 0
|
self.now = 0
|
||||||
|
|
||||||
@@ -155,7 +157,6 @@ class UserLogin(User):
|
|||||||
|
|
||||||
def set_password(self, password: str):
|
def set_password(self, password: str):
|
||||||
self.password = password
|
self.password = password
|
||||||
self.hash_pwd = hashlib.sha256(password.encode("utf8")).hexdigest()
|
|
||||||
|
|
||||||
def set_device_id(self, device_id: str):
|
def set_device_id(self, device_id: str):
|
||||||
self.device_id = device_id
|
self.device_id = device_id
|
||||||
@@ -282,7 +283,7 @@ class UserInfo(User):
|
|||||||
self.is_skill_sealed = False
|
self.is_skill_sealed = False
|
||||||
self.is_hide_rating = False
|
self.is_hide_rating = False
|
||||||
self.recent_score = Score()
|
self.recent_score = Score()
|
||||||
self.favorite_character = -1
|
self.favorite_character = None
|
||||||
self.max_stamina_notification_enabled = False
|
self.max_stamina_notification_enabled = False
|
||||||
self.prog_boost = 0
|
self.prog_boost = 0
|
||||||
|
|
||||||
@@ -411,7 +412,7 @@ class UserInfo(User):
|
|||||||
y = self.c.fetchone()
|
y = self.c.fetchone()
|
||||||
best_clear_type = y[0] if y is not None else self.recent_score.clear_type
|
best_clear_type = y[0] if y is not None else self.recent_score.clear_type
|
||||||
|
|
||||||
r = self.recent_score.to_dict
|
r = self.recent_score.to_dict()
|
||||||
r["best_clear_type"] = best_clear_type
|
r["best_clear_type"] = best_clear_type
|
||||||
return [r]
|
return [r]
|
||||||
|
|
||||||
@@ -440,7 +441,7 @@ class UserInfo(User):
|
|||||||
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,
|
||||||
"character_stats": [x.to_dict for x in self.characters.characters],
|
"character_stats": [x.to_dict() for x in self.characters.characters],
|
||||||
"friends": self.friends,
|
"friends": self.friends,
|
||||||
"settings": {
|
"settings": {
|
||||||
"favorite_character": favorite_character_id,
|
"favorite_character": favorite_character_id,
|
||||||
@@ -473,14 +474,12 @@ class UserInfo(User):
|
|||||||
"global_rank": self.global_rank
|
"global_rank": self.global_rank
|
||||||
}
|
}
|
||||||
|
|
||||||
def select_user(self) -> None:
|
def from_list(self, x: list) -> 'UserInfo':
|
||||||
# 查user表所有信息
|
'''从数据库user表全部数据获取信息'''
|
||||||
self.c.execute(
|
|
||||||
'''select * from user where user_id = :x''', {'x': self.user_id})
|
|
||||||
x = self.c.fetchone()
|
|
||||||
if not x:
|
if not x:
|
||||||
raise NoData('No user.', 108, -3)
|
return None
|
||||||
|
if self.user_id is None:
|
||||||
|
self.user_id = x[0]
|
||||||
self.name = x[1]
|
self.name = x[1]
|
||||||
self.join_date = int(x[3])
|
self.join_date = int(x[3])
|
||||||
self.user_code = x[4]
|
self.user_code = x[4]
|
||||||
@@ -512,6 +511,18 @@ class UserInfo(User):
|
|||||||
self.stamina = UserStamina(self.c, self)
|
self.stamina = UserStamina(self.c, self)
|
||||||
self.stamina.set_value(x[32], x[33])
|
self.stamina.set_value(x[32], x[33])
|
||||||
|
|
||||||
|
return self
|
||||||
|
|
||||||
|
def select_user(self) -> None:
|
||||||
|
# 查user表所有信息
|
||||||
|
self.c.execute(
|
||||||
|
'''select * from user where user_id = :x''', {'x': self.user_id})
|
||||||
|
x = self.c.fetchone()
|
||||||
|
if not x:
|
||||||
|
raise NoData('No user.', 108, -3)
|
||||||
|
|
||||||
|
self.from_list(x)
|
||||||
|
|
||||||
def select_user_about_current_map(self) -> None:
|
def select_user_about_current_map(self) -> None:
|
||||||
self.c.execute('''select current_map from user where user_id = :a''',
|
self.c.execute('''select current_map from user where user_id = :a''',
|
||||||
{'a': self.user_id})
|
{'a': self.user_id})
|
||||||
|
|||||||
@@ -54,7 +54,6 @@ class Step:
|
|||||||
self.speed_limit_value: int = None
|
self.speed_limit_value: int = None
|
||||||
self.plus_stamina_value: int = None
|
self.plus_stamina_value: int = None
|
||||||
|
|
||||||
@property
|
|
||||||
def to_dict(self) -> dict:
|
def to_dict(self) -> dict:
|
||||||
r = {
|
r = {
|
||||||
'position': self.position,
|
'position': self.position,
|
||||||
@@ -152,7 +151,7 @@ class Map:
|
|||||||
'custom_bg': self.custom_bg,
|
'custom_bg': self.custom_bg,
|
||||||
'stamina_cost': self.stamina_cost,
|
'stamina_cost': self.stamina_cost,
|
||||||
'step_count': self.step_count,
|
'step_count': self.step_count,
|
||||||
'steps': [s.to_dict for s in self.steps],
|
'steps': [s.to_dict() for s in self.steps],
|
||||||
}
|
}
|
||||||
|
|
||||||
def from_dict(self, raw_dict: dict) -> 'Map':
|
def from_dict(self, raw_dict: dict) -> 'Map':
|
||||||
@@ -208,7 +207,6 @@ class UserMap(Map):
|
|||||||
|
|
||||||
return rewards
|
return rewards
|
||||||
|
|
||||||
@property
|
|
||||||
def rewards_for_climbing_to_dict(self) -> list:
|
def rewards_for_climbing_to_dict(self) -> list:
|
||||||
rewards = []
|
rewards = []
|
||||||
for i in range(self.prev_position, self.curr_position+1):
|
for i in range(self.prev_position, self.curr_position+1):
|
||||||
@@ -441,11 +439,10 @@ class WorldPlay:
|
|||||||
self.overdrive_extra: float = None
|
self.overdrive_extra: float = None
|
||||||
self.character_bonus_progress: float = None
|
self.character_bonus_progress: float = None
|
||||||
|
|
||||||
@property
|
|
||||||
def to_dict(self) -> dict:
|
def to_dict(self) -> dict:
|
||||||
arcmap: 'UserMap' = self.user.current_map
|
arcmap: 'UserMap' = self.user.current_map
|
||||||
r = {
|
r = {
|
||||||
"rewards": arcmap.rewards_for_climbing_to_dict,
|
"rewards": arcmap.rewards_for_climbing_to_dict(),
|
||||||
"exp": self.character_used.level.exp,
|
"exp": self.character_used.level.exp,
|
||||||
"level": self.character_used.level.level,
|
"level": self.character_used.level.level,
|
||||||
"base_progress": self.base_step_value,
|
"base_progress": self.base_step_value,
|
||||||
@@ -484,7 +481,7 @@ class WorldPlay:
|
|||||||
|
|
||||||
if self.user_play.beyond_gauge == 0:
|
if self.user_play.beyond_gauge == 0:
|
||||||
r["user_map"]["steps"] = [
|
r["user_map"]["steps"] = [
|
||||||
x.to_dict for x in arcmap.steps_for_climbing]
|
x.to_dict() for x in arcmap.steps_for_climbing]
|
||||||
else:
|
else:
|
||||||
r["user_map"]["steps"] = len(arcmap.steps_for_climbing)
|
r["user_map"]["steps"] = len(arcmap.steps_for_climbing)
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ from multiprocessing import Process, set_start_method
|
|||||||
|
|
||||||
from flask import Flask, request, send_from_directory
|
from flask import Flask, request, send_from_directory
|
||||||
|
|
||||||
import api.api_main
|
import api
|
||||||
import server
|
import server
|
||||||
import server.init
|
import server.init
|
||||||
import web.index
|
import web.index
|
||||||
@@ -28,7 +28,7 @@ app.config.from_mapping(SECRET_KEY=Config.SECRET_KEY)
|
|||||||
app.config['SESSION_TYPE'] = 'filesystem'
|
app.config['SESSION_TYPE'] = 'filesystem'
|
||||||
app.register_blueprint(web.login.bp)
|
app.register_blueprint(web.login.bp)
|
||||||
app.register_blueprint(web.index.bp)
|
app.register_blueprint(web.index.bp)
|
||||||
app.register_blueprint(api.api_main.bp)
|
app.register_blueprint(api.bp)
|
||||||
app.register_blueprint(server.bp)
|
app.register_blueprint(server.bp)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,224 +0,0 @@
|
|||||||
import os
|
|
||||||
import hashlib
|
|
||||||
from flask import url_for
|
|
||||||
import sqlite3
|
|
||||||
from server.sql import Connect
|
|
||||||
import time
|
|
||||||
from setting import Config
|
|
||||||
|
|
||||||
time_limit = Config.DOWNLOAD_TIMES_LIMIT # 每个玩家24小时下载次数限制
|
|
||||||
time_gap_limit = Config.DOWNLOAD_TIME_GAP_LIMIT # 下载链接有效秒数
|
|
||||||
|
|
||||||
|
|
||||||
def get_file_md5(file_path):
|
|
||||||
# 计算文件MD5
|
|
||||||
if not os.path.isfile(file_path):
|
|
||||||
return None
|
|
||||||
myhash = hashlib.md5()
|
|
||||||
with open(file_path, 'rb') as f:
|
|
||||||
while True:
|
|
||||||
b = f.read(8096)
|
|
||||||
if not b:
|
|
||||||
break
|
|
||||||
myhash.update(b)
|
|
||||||
|
|
||||||
return myhash.hexdigest()
|
|
||||||
|
|
||||||
|
|
||||||
def get_url(file_path, **kwargs):
|
|
||||||
# 获取下载地址
|
|
||||||
|
|
||||||
t = ''
|
|
||||||
if 't' in kwargs:
|
|
||||||
t = kwargs['t']
|
|
||||||
|
|
||||||
if Config.DOWNLOAD_LINK_PREFIX:
|
|
||||||
prefix = Config.DOWNLOAD_LINK_PREFIX
|
|
||||||
if prefix[-1] != '/':
|
|
||||||
prefix += '/'
|
|
||||||
|
|
||||||
return prefix + file_path + '?t=' + t
|
|
||||||
else:
|
|
||||||
return url_for('download', file_path=file_path, t=t, _external=True)
|
|
||||||
|
|
||||||
|
|
||||||
def get_one_song(c, user_id, song_id, file_dir='./database/songs', url_flag=True):
|
|
||||||
# 获取一首歌的下载链接,返回字典
|
|
||||||
dir_list = os.listdir(os.path.join(file_dir, song_id))
|
|
||||||
re = {}
|
|
||||||
now = int(time.time())
|
|
||||||
c.execute('''delete from download_token where user_id=:a and song_id=:b''', {
|
|
||||||
'a': user_id, 'b': song_id})
|
|
||||||
|
|
||||||
for i in dir_list:
|
|
||||||
if os.path.isfile(os.path.join(file_dir, song_id, i)) and i in ['0.aff', '1.aff', '2.aff', '3.aff', 'base.ogg', '3.ogg']:
|
|
||||||
token = hashlib.md5(
|
|
||||||
(str(user_id) + song_id + i + str(now)).encode(encoding='UTF-8')).hexdigest()
|
|
||||||
|
|
||||||
if i == 'base.ogg':
|
|
||||||
if 'audio' not in re:
|
|
||||||
re['audio'] = {}
|
|
||||||
|
|
||||||
c.execute(
|
|
||||||
'''select md5 from songfile where song_id=:a and file_type=-1''', {'a': song_id})
|
|
||||||
x = c.fetchone()
|
|
||||||
if x:
|
|
||||||
checksum = x[0]
|
|
||||||
else:
|
|
||||||
checksum = get_file_md5(os.path.join(
|
|
||||||
file_dir, song_id, 'base.ogg'))
|
|
||||||
|
|
||||||
re['audio']["checksum"] = checksum
|
|
||||||
if url_flag:
|
|
||||||
re['audio']["url"] = get_url(
|
|
||||||
file_path=song_id+'/base.ogg', t=token)
|
|
||||||
|
|
||||||
elif i == '3.ogg':
|
|
||||||
if 'audio' not in re:
|
|
||||||
re['audio'] = {}
|
|
||||||
|
|
||||||
c.execute(
|
|
||||||
'''select md5 from songfile where song_id=:a and file_type=-2''', {'a': song_id})
|
|
||||||
x = c.fetchone()
|
|
||||||
if x:
|
|
||||||
checksum = x[0]
|
|
||||||
else:
|
|
||||||
checksum = get_file_md5(os.path.join(
|
|
||||||
file_dir, song_id, '3.ogg'))
|
|
||||||
|
|
||||||
if url_flag:
|
|
||||||
re['audio']['3'] = {"checksum": checksum, "url": get_url(
|
|
||||||
file_path=song_id+'/3.ogg', t=token)}
|
|
||||||
else:
|
|
||||||
re['audio']['3'] = {"checksum": checksum}
|
|
||||||
else:
|
|
||||||
if 'chart' not in re:
|
|
||||||
re['chart'] = {}
|
|
||||||
c.execute(
|
|
||||||
'''select md5 from songfile where song_id=:a and file_type=:b''', {'a': song_id, 'b': int(i[0])})
|
|
||||||
x = c.fetchone()
|
|
||||||
if x:
|
|
||||||
checksum = x[0]
|
|
||||||
else:
|
|
||||||
checksum = get_file_md5(os.path.join(file_dir, song_id, i))
|
|
||||||
|
|
||||||
if url_flag:
|
|
||||||
re['chart'][i[0]] = {"checksum": checksum, "url": get_url(
|
|
||||||
file_path=song_id+'/'+i, t=token)}
|
|
||||||
else:
|
|
||||||
re['chart'][i[0]] = {"checksum": checksum}
|
|
||||||
|
|
||||||
if url_flag:
|
|
||||||
c.execute('''insert into download_token values(:a,:b,:c,:d,:e)''', {
|
|
||||||
'a': user_id, 'b': song_id, 'c': i, 'd': token, 'e': now})
|
|
||||||
|
|
||||||
return {song_id: re}
|
|
||||||
|
|
||||||
|
|
||||||
def get_all_songs(user_id, file_dir='./database/songs', url_flag=True):
|
|
||||||
# 获取所有歌的下载链接,返回字典
|
|
||||||
dir_list = os.listdir(file_dir)
|
|
||||||
re = {}
|
|
||||||
with Connect() as c:
|
|
||||||
for i in dir_list:
|
|
||||||
if os.path.isdir(os.path.join(file_dir, i)):
|
|
||||||
re.update(get_one_song(c, user_id, i, file_dir, url_flag))
|
|
||||||
|
|
||||||
return re
|
|
||||||
|
|
||||||
|
|
||||||
def get_some_songs(user_id, song_ids):
|
|
||||||
# 获取一些歌的下载链接,返回字典
|
|
||||||
re = {}
|
|
||||||
with Connect() as c:
|
|
||||||
for song_id in song_ids:
|
|
||||||
re.update(get_one_song(c, user_id, song_id))
|
|
||||||
|
|
||||||
return re
|
|
||||||
|
|
||||||
|
|
||||||
def is_token_able_download(t, path):
|
|
||||||
# token是否可以下载,返回错误码,0即可以
|
|
||||||
errorcode = 108
|
|
||||||
with Connect() as c:
|
|
||||||
c.execute('''select * from download_token where token = :t limit 1''',
|
|
||||||
{'t': t})
|
|
||||||
x = c.fetchone()
|
|
||||||
now = int(time.time())
|
|
||||||
if x and now - x[4] <= time_gap_limit and x[1]+'/'+x[2] == path:
|
|
||||||
c.execute(
|
|
||||||
'''select count(*) from user_download where user_id = :a''', {'a': x[0]})
|
|
||||||
y = c.fetchone()
|
|
||||||
if y and y[0] <= time_limit:
|
|
||||||
c.execute('''insert into user_download values(:a,:b,:c)''', {
|
|
||||||
'a': x[0], 'b': x[3], 'c': now})
|
|
||||||
errorcode = 0
|
|
||||||
else:
|
|
||||||
errorcode = 903
|
|
||||||
else:
|
|
||||||
errorcode = 108
|
|
||||||
|
|
||||||
return errorcode
|
|
||||||
|
|
||||||
|
|
||||||
def is_able_download(user_id):
|
|
||||||
# 是否可以下载,返回布尔值
|
|
||||||
f = False
|
|
||||||
with Connect() as c:
|
|
||||||
now = int(time.time())
|
|
||||||
c.execute(
|
|
||||||
'''delete from user_download where user_id = :a and time <= :b''', {'a': user_id, 'b': now - 24*3600})
|
|
||||||
c.execute(
|
|
||||||
'''select count(*) from user_download where user_id = :a''', {'a': user_id})
|
|
||||||
y = c.fetchone()
|
|
||||||
if y and y[0] <= time_limit:
|
|
||||||
f = True
|
|
||||||
else:
|
|
||||||
f = False
|
|
||||||
|
|
||||||
return f
|
|
||||||
|
|
||||||
|
|
||||||
def initialize_one_songfile(c, song_id, file_dir='./database/songs'):
|
|
||||||
# 计算并添加歌曲md5到表中,无返回
|
|
||||||
dir_list = os.listdir(os.path.join(file_dir, song_id))
|
|
||||||
for i in dir_list:
|
|
||||||
if os.path.isfile(os.path.join(file_dir, song_id, i)) and i in ['0.aff', '1.aff', '2.aff', '3.aff', 'base.ogg', '3.ogg']:
|
|
||||||
if i == 'base.ogg':
|
|
||||||
c.execute('''insert into songfile values(:a,-1,:c)''', {
|
|
||||||
'a': song_id, 'c': get_file_md5(os.path.join(file_dir, song_id, 'base.ogg'))})
|
|
||||||
elif i == '3.ogg':
|
|
||||||
c.execute('''insert into songfile values(:a,-2,:c)''', {
|
|
||||||
'a': song_id, 'c': get_file_md5(os.path.join(file_dir, song_id, '3.ogg'))})
|
|
||||||
else:
|
|
||||||
c.execute('''insert into songfile values(:a,:b,:c)''', {
|
|
||||||
'a': song_id, 'b': int(i[0]), 'c': get_file_md5(os.path.join(file_dir, song_id, i))})
|
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
def initialize_songfile(file_dir='./database/songs'):
|
|
||||||
# 初始化歌曲数据的md5表,返回错误信息
|
|
||||||
error = None
|
|
||||||
try:
|
|
||||||
dir_list = os.listdir(file_dir)
|
|
||||||
except:
|
|
||||||
error = 'OS error!'
|
|
||||||
return error
|
|
||||||
try:
|
|
||||||
conn = sqlite3.connect('./database/arcaea_database.db')
|
|
||||||
c = conn.cursor()
|
|
||||||
except:
|
|
||||||
error = 'Database error!'
|
|
||||||
return error
|
|
||||||
try:
|
|
||||||
c.execute('''delete from songfile''')
|
|
||||||
for i in dir_list:
|
|
||||||
if os.path.isdir(os.path.join(file_dir, i)):
|
|
||||||
initialize_one_songfile(c, i, file_dir)
|
|
||||||
except:
|
|
||||||
error = 'Initialization error!'
|
|
||||||
finally:
|
|
||||||
conn.commit()
|
|
||||||
conn.close()
|
|
||||||
return error
|
|
||||||
@@ -1,125 +0,0 @@
|
|||||||
from .sql import Connect
|
|
||||||
import base64
|
|
||||||
|
|
||||||
|
|
||||||
def get_song_unlock(client_song_map):
|
|
||||||
# 处理可用歌曲bit,返回bytes
|
|
||||||
|
|
||||||
user_song_unlock = [0] * 512
|
|
||||||
for i in range(0, 1024, 2):
|
|
||||||
x = 0
|
|
||||||
y = 0
|
|
||||||
if str(i) in client_song_map:
|
|
||||||
if client_song_map[str(i)][0]:
|
|
||||||
x += 1
|
|
||||||
if client_song_map[str(i)][1]:
|
|
||||||
x += 2
|
|
||||||
if client_song_map[str(i)][2]:
|
|
||||||
x += 4
|
|
||||||
if client_song_map[str(i)][3]:
|
|
||||||
x += 8
|
|
||||||
if str(i+1) in client_song_map:
|
|
||||||
if client_song_map[str(i+1)][0]:
|
|
||||||
y += 1
|
|
||||||
if client_song_map[str(i+1)][1]:
|
|
||||||
y += 2
|
|
||||||
if client_song_map[str(i+1)][2]:
|
|
||||||
y += 4
|
|
||||||
if client_song_map[str(i+1)][3]:
|
|
||||||
y += 8
|
|
||||||
|
|
||||||
user_song_unlock[i // 2] = y*16 + x
|
|
||||||
|
|
||||||
return bytes(user_song_unlock)
|
|
||||||
|
|
||||||
|
|
||||||
def create_room(conn, user_id, client_song_map):
|
|
||||||
# 创建房间,返回错误码和房间与用户信息
|
|
||||||
error_code = 108
|
|
||||||
|
|
||||||
with Connect() as c:
|
|
||||||
c.execute('''select name from user where user_id=?''', (user_id,))
|
|
||||||
x = c.fetchone()
|
|
||||||
if x is not None:
|
|
||||||
name = x[0]
|
|
||||||
|
|
||||||
song_unlock = get_song_unlock(client_song_map)
|
|
||||||
|
|
||||||
conn.send((1, name, song_unlock))
|
|
||||||
if conn.poll(10):
|
|
||||||
data = conn.recv()
|
|
||||||
else:
|
|
||||||
data = (-1,)
|
|
||||||
|
|
||||||
if data[0] == 0:
|
|
||||||
error_code = 0
|
|
||||||
return error_code, {'roomCode': data[1],
|
|
||||||
'roomId': str(data[2]),
|
|
||||||
'token': str(data[3]),
|
|
||||||
'key': (base64.b64encode(data[4])).decode(),
|
|
||||||
'playerId': str(data[5]),
|
|
||||||
'userId': user_id,
|
|
||||||
'orderedAllowedSongs': (base64.b64encode(song_unlock)).decode()
|
|
||||||
}
|
|
||||||
|
|
||||||
return error_code, None
|
|
||||||
|
|
||||||
|
|
||||||
def join_room(conn, user_id, client_song_map, room_code):
|
|
||||||
# 加入房间,返回错误码和房间与用户信息
|
|
||||||
error_code = 108
|
|
||||||
|
|
||||||
with Connect() as c:
|
|
||||||
c.execute('''select name from user where user_id=?''', (user_id,))
|
|
||||||
x = c.fetchone()
|
|
||||||
if x is not None:
|
|
||||||
name = x[0]
|
|
||||||
|
|
||||||
song_unlock = get_song_unlock(client_song_map)
|
|
||||||
|
|
||||||
conn.send((2, name, song_unlock, room_code))
|
|
||||||
if conn.poll(10):
|
|
||||||
data = conn.recv()
|
|
||||||
else:
|
|
||||||
data = (-1,)
|
|
||||||
|
|
||||||
if data[0] == 0:
|
|
||||||
error_code = 0
|
|
||||||
return error_code, {'roomCode': data[1],
|
|
||||||
'roomId': str(data[2]),
|
|
||||||
'token': str(data[3]),
|
|
||||||
'key': (base64.b64encode(data[4])).decode(),
|
|
||||||
'playerId': str(data[5]),
|
|
||||||
'userId': user_id,
|
|
||||||
'orderedAllowedSongs': (base64.b64encode(data[6])).decode()
|
|
||||||
}
|
|
||||||
else:
|
|
||||||
error_code = data[0]
|
|
||||||
|
|
||||||
return error_code, None
|
|
||||||
|
|
||||||
|
|
||||||
def update_room(conn, user_id, token):
|
|
||||||
# 更新房间,返回错误码和房间与用户信息
|
|
||||||
error_code = 108
|
|
||||||
|
|
||||||
conn.send((3, int(token)))
|
|
||||||
if conn.poll(10):
|
|
||||||
data = conn.recv()
|
|
||||||
else:
|
|
||||||
data = (-1,)
|
|
||||||
|
|
||||||
if data[0] == 0:
|
|
||||||
error_code = 0
|
|
||||||
return error_code, {'roomCode': data[1],
|
|
||||||
'roomId': str(data[2]),
|
|
||||||
'token': token,
|
|
||||||
'key': (base64.b64encode(data[3])).decode(),
|
|
||||||
'playerId': str(data[4]),
|
|
||||||
'userId': user_id,
|
|
||||||
'orderedAllowedSongs': (base64.b64encode(data[5])).decode()
|
|
||||||
}
|
|
||||||
else:
|
|
||||||
error_code = data[0]
|
|
||||||
|
|
||||||
return error_code, None
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
def buy_item(c, user_id, price):
|
|
||||||
# 购买接口,返回成功与否标识符和剩余源点数
|
|
||||||
c.execute('''select ticket from user where user_id = :a''',
|
|
||||||
{'a': user_id})
|
|
||||||
ticket = c.fetchone()
|
|
||||||
if ticket:
|
|
||||||
ticket = ticket[0]
|
|
||||||
else:
|
|
||||||
ticket = 0
|
|
||||||
|
|
||||||
if ticket < price:
|
|
||||||
return False, ticket
|
|
||||||
|
|
||||||
c.execute('''update user set ticket = :b where user_id = :a''',
|
|
||||||
{'a': user_id, 'b': ticket-price})
|
|
||||||
|
|
||||||
return True, ticket - price
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,98 +1,4 @@
|
|||||||
from core.sql import Connect
|
from core.sql import Connect
|
||||||
from setting import Config
|
|
||||||
|
|
||||||
|
|
||||||
def b2int(x):
|
|
||||||
# int与布尔值转换
|
|
||||||
if x:
|
|
||||||
return 1
|
|
||||||
else:
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
def int2b(x):
|
|
||||||
# int与布尔值转换
|
|
||||||
if x is None or x == 0:
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def get_score(c, user_id, song_id, difficulty):
|
|
||||||
# 根据user_id、song_id、难度得到该曲目最好成绩,返回字典
|
|
||||||
c.execute('''select * from best_score where user_id = :a and song_id = :b and difficulty = :c''',
|
|
||||||
{'a': user_id, 'b': song_id, 'c': difficulty})
|
|
||||||
x = c.fetchone()
|
|
||||||
if x is not None:
|
|
||||||
c.execute('''select name, character_id, is_skill_sealed, is_char_uncapped, is_char_uncapped_override, favorite_character from user where user_id = :a''', {
|
|
||||||
'a': user_id})
|
|
||||||
y = c.fetchone()
|
|
||||||
if y is not None:
|
|
||||||
character = y[1]
|
|
||||||
is_char_uncapped = int2b(y[3])
|
|
||||||
if y[5] != -1:
|
|
||||||
character = y[5]
|
|
||||||
if not Config.CHARACTER_FULL_UNLOCK:
|
|
||||||
c.execute('''select is_uncapped, is_uncapped_override from user_char where user_id=:a and character_id=:b''', {
|
|
||||||
'a': user_id, 'b': character})
|
|
||||||
else:
|
|
||||||
c.execute('''select is_uncapped, is_uncapped_override from user_char_full where user_id=:a and character_id=:b''', {
|
|
||||||
'a': user_id, 'b': character})
|
|
||||||
z = c.fetchone()
|
|
||||||
if z:
|
|
||||||
if z[1] == 0:
|
|
||||||
is_char_uncapped = int2b(z[0])
|
|
||||||
else:
|
|
||||||
is_char_uncapped = False
|
|
||||||
else:
|
|
||||||
if y[4] == 1:
|
|
||||||
is_char_uncapped = False
|
|
||||||
|
|
||||||
return {
|
|
||||||
"user_id": x[0],
|
|
||||||
"song_id": x[1],
|
|
||||||
"difficulty": x[2],
|
|
||||||
"score": x[3],
|
|
||||||
"shiny_perfect_count": x[4],
|
|
||||||
"perfect_count": x[5],
|
|
||||||
"near_count": x[6],
|
|
||||||
"miss_count": x[7],
|
|
||||||
"health": x[8],
|
|
||||||
"modifier": x[9],
|
|
||||||
"time_played": x[10],
|
|
||||||
"best_clear_type": x[11],
|
|
||||||
"clear_type": x[12],
|
|
||||||
"name": y[0],
|
|
||||||
"character": character,
|
|
||||||
"is_skill_sealed": int2b(y[2]),
|
|
||||||
"is_char_uncapped": is_char_uncapped
|
|
||||||
}
|
|
||||||
else:
|
|
||||||
return {}
|
|
||||||
else:
|
|
||||||
return {}
|
|
||||||
|
|
||||||
|
|
||||||
def arc_score_top(song_id, difficulty, limit=20):
|
|
||||||
# 得到top分数表,默认最多20个,如果是负数则全部查询
|
|
||||||
r = []
|
|
||||||
with Connect() as c:
|
|
||||||
if limit >= 0:
|
|
||||||
c.execute('''select user_id from best_score where song_id = :song_id and difficulty = :difficulty order by score DESC, time_played DESC limit :limit''', {
|
|
||||||
'song_id': song_id, 'difficulty': difficulty, 'limit': limit})
|
|
||||||
else:
|
|
||||||
c.execute('''select user_id from best_score where song_id = :song_id and difficulty = :difficulty order by score DESC, time_played DESC''', {
|
|
||||||
'song_id': song_id, 'difficulty': difficulty})
|
|
||||||
x = c.fetchall()
|
|
||||||
if x != []:
|
|
||||||
rank = 0
|
|
||||||
for i in x:
|
|
||||||
rank += 1
|
|
||||||
y = get_score(c, i[0], song_id, difficulty)
|
|
||||||
y['rank'] = rank
|
|
||||||
r.append(y)
|
|
||||||
|
|
||||||
return r
|
|
||||||
|
|
||||||
|
|
||||||
def calculate_rating(defnum, score):
|
def calculate_rating(defnum, score):
|
||||||
@@ -113,7 +19,7 @@ def refresh_all_score_rating():
|
|||||||
# 刷新所有best成绩的rating
|
# 刷新所有best成绩的rating
|
||||||
error = 'Unknown error.'
|
error = 'Unknown error.'
|
||||||
|
|
||||||
with Connect('') as c:
|
with Connect() as c:
|
||||||
c.execute(
|
c.execute(
|
||||||
'''select song_id, rating_pst, rating_prs, rating_ftr, rating_byn from chart''')
|
'''select song_id, rating_pst, rating_prs, rating_ftr, rating_byn from chart''')
|
||||||
x = c.fetchall()
|
x = c.fetchall()
|
||||||
|
|||||||
@@ -1,56 +0,0 @@
|
|||||||
import json
|
|
||||||
from .config import Constant
|
|
||||||
from setting import Config
|
|
||||||
import time
|
|
||||||
|
|
||||||
|
|
||||||
def int2b(x):
|
|
||||||
# int与布尔值转换
|
|
||||||
if x is None or x == 0:
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def calc_stamina(max_stamina_ts, curr_stamina):
|
|
||||||
# 计算体力,返回剩余体力数值
|
|
||||||
|
|
||||||
stamina = int(Constant.MAX_STAMINA - (max_stamina_ts -
|
|
||||||
int(time.time()*1000)) / Constant.STAMINA_RECOVER_TICK)
|
|
||||||
|
|
||||||
if stamina >= Constant.MAX_STAMINA:
|
|
||||||
if curr_stamina >= Constant.MAX_STAMINA:
|
|
||||||
stamina = curr_stamina
|
|
||||||
else:
|
|
||||||
stamina = Constant.MAX_STAMINA
|
|
||||||
if stamina < 0:
|
|
||||||
stamina = 0
|
|
||||||
|
|
||||||
return stamina
|
|
||||||
|
|
||||||
|
|
||||||
def get_world_info(map_id):
|
|
||||||
# 读取json文件内容,返回字典
|
|
||||||
world_info = {}
|
|
||||||
with open('./database/map/'+map_id+'.json', 'r') as f:
|
|
||||||
world_info = json.load(f)
|
|
||||||
|
|
||||||
return world_info
|
|
||||||
|
|
||||||
|
|
||||||
def get_available_maps():
|
|
||||||
# 获取当前可用图(用户设定的),返回字典列表
|
|
||||||
re = []
|
|
||||||
for i in Config.AVAILABLE_MAP:
|
|
||||||
info = get_world_info(i)
|
|
||||||
del info['steps']
|
|
||||||
try:
|
|
||||||
del info['is_locked']
|
|
||||||
del info['curr_position']
|
|
||||||
del info['curr_capture']
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
re.append(info)
|
|
||||||
|
|
||||||
return re
|
|
||||||
|
|
||||||
@@ -1,131 +0,0 @@
|
|||||||
from setting import Config
|
|
||||||
from .config import Constant
|
|
||||||
|
|
||||||
|
|
||||||
def int2b(x):
|
|
||||||
# int与布尔值转换
|
|
||||||
if x is None or x == 0:
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def calc_char_value(level, value1, value20, value30):
|
|
||||||
# 计算搭档数值的核心函数,返回浮点数
|
|
||||||
|
|
||||||
def calc_char_value_20(level, stata, statb, lva=1, lvb=20):
|
|
||||||
# 计算1~20级搭档数值的核心函数,返回浮点数,来自https://redive.estertion.win/arcaea/calc/
|
|
||||||
|
|
||||||
n = [0, 0, 0.0005831753900000081, 0.004665403120000065, 0.015745735529959858, 0.03732322495992008, 0.07289692374980007, 0.12596588423968, 0.2000291587694801, 0.29858579967923987, 0.42513485930893946,
|
|
||||||
0.5748651406910605, 0.7014142003207574, 0.7999708412305152, 0.8740341157603029, 0.9271030762501818, 0.962676775040091, 0.9842542644700301, 0.9953345968799998, 0.9994168246100001, 1]
|
|
||||||
e = n[lva] - n[lvb]
|
|
||||||
a = stata - statb
|
|
||||||
r = a / e
|
|
||||||
d = stata - n[lva] * r
|
|
||||||
|
|
||||||
return d + r * n[level]
|
|
||||||
|
|
||||||
def calc_char_value_30(level, stata, statb, lva=20, lvb=30):
|
|
||||||
# 计算21~30级搭档数值,返回浮点数
|
|
||||||
|
|
||||||
return (level - lva) * (statb - stata) / (lvb - lva) + stata
|
|
||||||
|
|
||||||
if level < 1 or level > 30:
|
|
||||||
return 0
|
|
||||||
elif 1 <= level <= 20:
|
|
||||||
return calc_char_value_20(level, value1, value20)
|
|
||||||
else:
|
|
||||||
return calc_char_value_30(level, value20, value30)
|
|
||||||
|
|
||||||
|
|
||||||
def get_char_core(c, character_id):
|
|
||||||
# 得到对应角色觉醒所需的核心,返回字典列表
|
|
||||||
r = []
|
|
||||||
c.execute(
|
|
||||||
'''select item_id, amount from char_item where character_id = ? and type="core"''', (character_id,))
|
|
||||||
x = c.fetchall()
|
|
||||||
if x:
|
|
||||||
for i in x:
|
|
||||||
r.append({'core_type': i[0], 'amount': i[1]})
|
|
||||||
return r
|
|
||||||
|
|
||||||
|
|
||||||
def get_user_character(c, user_id):
|
|
||||||
# 得到用户拥有的角色列表,返回列表
|
|
||||||
|
|
||||||
if Config.CHARACTER_FULL_UNLOCK:
|
|
||||||
c.execute('''select * from user_char_full a,character b where a.user_id = :user_id and a.character_id=b.character_id''',
|
|
||||||
{'user_id': user_id})
|
|
||||||
else:
|
|
||||||
c.execute('''select * from user_char a,character b where a.user_id = :user_id and a.character_id=b.character_id''',
|
|
||||||
{'user_id': user_id})
|
|
||||||
x = c.fetchall()
|
|
||||||
if not x and not Config.CHARACTER_FULL_UNLOCK:
|
|
||||||
# 添加初始角色
|
|
||||||
c.execute('''insert into user_char values(?,?,?,?,?,?)''',
|
|
||||||
(user_id, 0, 1, 0, 0, 0))
|
|
||||||
c.execute('''insert into user_char values(?,?,?,?,?,?)''',
|
|
||||||
(user_id, 1, 1, 0, 0, 0))
|
|
||||||
c.execute('''select * from user_char a,character b where a.user_id = :user_id and a.character_id=b.character_id''',
|
|
||||||
{'user_id': user_id})
|
|
||||||
x = c.fetchall()
|
|
||||||
|
|
||||||
if not x:
|
|
||||||
return []
|
|
||||||
r = []
|
|
||||||
for i in x:
|
|
||||||
|
|
||||||
char = {
|
|
||||||
"is_uncapped_override": int2b(i[5]),
|
|
||||||
"is_uncapped": int2b(i[4]),
|
|
||||||
"uncap_cores": get_char_core(c, i[1]),
|
|
||||||
"char_type": i[22],
|
|
||||||
"skill_id_uncap": i[21],
|
|
||||||
"skill_requires_uncap": int2b(i[20]),
|
|
||||||
"skill_unlock_level": i[19],
|
|
||||||
"skill_id": i[18],
|
|
||||||
"overdrive": calc_char_value(i[2], i[11], i[14], i[17]),
|
|
||||||
"prog": calc_char_value(i[2], i[10], i[13], i[16]),
|
|
||||||
"frag": calc_char_value(i[2], i[9], i[12], i[15]),
|
|
||||||
"level_exp": Constant.LEVEL_STEPS[i[2]],
|
|
||||||
"exp": i[3],
|
|
||||||
"level": i[2],
|
|
||||||
"name": i[7],
|
|
||||||
"character_id": i[1]
|
|
||||||
}
|
|
||||||
if i[1] == 21:
|
|
||||||
char["voice"] = [0, 1, 2, 3, 100, 1000, 1001]
|
|
||||||
r.append(char)
|
|
||||||
|
|
||||||
return r
|
|
||||||
|
|
||||||
|
|
||||||
def calc_level_up(c, user_id, character_id, exp, exp_addition):
|
|
||||||
# 计算角色升级,返回当前经验和等级
|
|
||||||
|
|
||||||
exp += exp_addition
|
|
||||||
|
|
||||||
if exp >= Constant.LEVEL_STEPS[20]: # 未觉醒溢出
|
|
||||||
c.execute('''select is_uncapped from user_char where user_id=? and character_id=?''',
|
|
||||||
(user_id, character_id))
|
|
||||||
x = c.fetchone()
|
|
||||||
if x and x[0] == 0:
|
|
||||||
return Constant.LEVEL_STEPS[20], 20
|
|
||||||
|
|
||||||
a = []
|
|
||||||
b = []
|
|
||||||
for i in Constant.LEVEL_STEPS:
|
|
||||||
a.append(i)
|
|
||||||
b.append(Constant.LEVEL_STEPS[i])
|
|
||||||
|
|
||||||
if exp >= b[-1]: # 溢出
|
|
||||||
return b[-1], a[-1]
|
|
||||||
|
|
||||||
if exp < b[0]: # 向下溢出,是异常状态
|
|
||||||
return 0, 1
|
|
||||||
|
|
||||||
i = len(a) - 1
|
|
||||||
while exp < b[i]:
|
|
||||||
i -= 1
|
|
||||||
|
|
||||||
return exp, a[i]
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
class Constant:
|
|
||||||
|
|
||||||
MAX_STAMINA = 12
|
|
||||||
|
|
||||||
STAMINA_RECOVER_TICK = 1800000
|
|
||||||
|
|
||||||
CORE_EXP = 250
|
|
||||||
|
|
||||||
LEVEL_STEPS = {1: 0, 2: 50, 3: 100, 4: 150, 5: 200, 6: 300, 7: 450, 8: 650, 9: 900, 10: 1200, 11: 1600, 12: 2100, 13: 2700, 14: 3400, 15: 4200, 16: 5100,
|
|
||||||
17: 6100, 18: 7200, 19: 8500, 20: 10000, 21: 11500, 22: 13000, 23: 14500, 24: 16000, 25: 17500, 26: 19000, 27: 20500, 28: 22000, 29: 23500, 30: 25000}
|
|
||||||
|
|
||||||
ETO_UNCAP_BONUS_PROGRESS = 7
|
|
||||||
LUNA_UNCAP_BONUS_PROGRESS = 7
|
|
||||||
AYU_UNCAP_BONUS_PROGRESS = 5
|
|
||||||
@@ -1,195 +0,0 @@
|
|||||||
import server.arcworld
|
|
||||||
import server.arcpurchase
|
|
||||||
import server.character
|
|
||||||
import server.item
|
|
||||||
from setting import Config
|
|
||||||
|
|
||||||
|
|
||||||
def int2b(x):
|
|
||||||
# int与布尔值转换
|
|
||||||
if x is None or x == 0:
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def get_recent_score(c, user_id):
|
|
||||||
# 得到用户最近一次的成绩,返回列表
|
|
||||||
c.execute('''select * from user where user_id = :x''', {'x': user_id})
|
|
||||||
x = c.fetchone()
|
|
||||||
if x is not None:
|
|
||||||
if x[11] is not None:
|
|
||||||
c.execute('''select best_clear_type from best_score where user_id=:u and song_id=:s and difficulty=:d''', {
|
|
||||||
'u': user_id, 's': x[11], 'd': x[12]})
|
|
||||||
y = c.fetchone()
|
|
||||||
if y is not None:
|
|
||||||
best_clear_type = y[0]
|
|
||||||
else:
|
|
||||||
best_clear_type = x[21]
|
|
||||||
|
|
||||||
return [{
|
|
||||||
"rating": x[22],
|
|
||||||
"modifier": x[19],
|
|
||||||
"time_played": x[20],
|
|
||||||
"health": x[18],
|
|
||||||
"best_clear_type": best_clear_type,
|
|
||||||
"clear_type": x[21],
|
|
||||||
"miss_count": x[17],
|
|
||||||
"near_count": x[16],
|
|
||||||
"perfect_count": x[15],
|
|
||||||
"shiny_perfect_count": x[14],
|
|
||||||
"score": x[13],
|
|
||||||
"difficulty": x[12],
|
|
||||||
"song_id": x[11]
|
|
||||||
}]
|
|
||||||
return []
|
|
||||||
|
|
||||||
|
|
||||||
def get_user_friend(c, user_id):
|
|
||||||
# 得到用户的朋友列表,返回列表
|
|
||||||
c.execute('''select user_id_other from friend where user_id_me = :user_id''', {
|
|
||||||
'user_id': user_id})
|
|
||||||
x = c.fetchall()
|
|
||||||
s = []
|
|
||||||
if x != [] and x[0][0] is not None:
|
|
||||||
|
|
||||||
for i in x:
|
|
||||||
c.execute('''select exists(select * from friend where user_id_me = :x and user_id_other = :y)''',
|
|
||||||
{'x': i[0], 'y': user_id})
|
|
||||||
if c.fetchone() == (1,):
|
|
||||||
is_mutual = True
|
|
||||||
else:
|
|
||||||
is_mutual = False
|
|
||||||
|
|
||||||
c.execute('''select * from user where user_id = :x''', {'x': i[0]})
|
|
||||||
y = c.fetchone()
|
|
||||||
if y is not None:
|
|
||||||
character = y[6]
|
|
||||||
is_char_uncapped = int2b(y[8])
|
|
||||||
is_char_uncapped_override = int2b(y[9])
|
|
||||||
if y[23] != -1:
|
|
||||||
character = y[23]
|
|
||||||
if not Config.CHARACTER_FULL_UNLOCK:
|
|
||||||
c.execute('''select is_uncapped, is_uncapped_override from user_char where user_id=:a and character_id=:b''', {
|
|
||||||
'a': i[0], 'b': character})
|
|
||||||
else:
|
|
||||||
c.execute('''select is_uncapped, is_uncapped_override from user_char_full where user_id=:a and character_id=:b''', {
|
|
||||||
'a': i[0], 'b': character})
|
|
||||||
z = c.fetchone()
|
|
||||||
if z:
|
|
||||||
is_char_uncapped = int2b(z[0])
|
|
||||||
is_char_uncapped_override = int2b(z[1])
|
|
||||||
|
|
||||||
rating = y[5]
|
|
||||||
if int2b(y[10]):
|
|
||||||
rating = -1
|
|
||||||
|
|
||||||
s.append({
|
|
||||||
"is_mutual": is_mutual,
|
|
||||||
"is_char_uncapped_override": is_char_uncapped_override,
|
|
||||||
"is_char_uncapped": is_char_uncapped,
|
|
||||||
"is_skill_sealed": int2b(y[7]),
|
|
||||||
"rating": rating,
|
|
||||||
"join_date": int(y[3]),
|
|
||||||
"character": character,
|
|
||||||
"recent_score": get_recent_score(c, i[0]),
|
|
||||||
"name": y[1],
|
|
||||||
"user_id": i[0]
|
|
||||||
})
|
|
||||||
|
|
||||||
s.sort(key=lambda item: item["recent_score"][0]["time_played"] if len(
|
|
||||||
item["recent_score"]) > 0 else 0, reverse=True)
|
|
||||||
return s
|
|
||||||
|
|
||||||
|
|
||||||
def get_user_me(c, user_id):
|
|
||||||
# 构造user/me的数据,返回字典
|
|
||||||
c.execute('''select * from user where user_id = :x''', {'x': user_id})
|
|
||||||
x = c.fetchone()
|
|
||||||
r = {}
|
|
||||||
if x is not None:
|
|
||||||
user_character = server.character.get_user_character(c, user_id)
|
|
||||||
# 下面没有使用get_user_characters函数是为了节省一次查询
|
|
||||||
characters = []
|
|
||||||
for i in user_character:
|
|
||||||
characters.append(i['character_id'])
|
|
||||||
|
|
||||||
character_id = x[6]
|
|
||||||
if character_id not in characters:
|
|
||||||
character_id = 0
|
|
||||||
c.execute(
|
|
||||||
'''update user set character_id=0 where user_id=?''', (user_id,))
|
|
||||||
|
|
||||||
favorite_character_id = x[23]
|
|
||||||
if favorite_character_id not in characters: # 这是考虑有可能favourite_character设置了用户未拥有的角色
|
|
||||||
favorite_character_id = -1
|
|
||||||
c.execute(
|
|
||||||
'''update user set favorite_character=-1 where user_id=?''', (user_id,))
|
|
||||||
|
|
||||||
# 计算世界排名
|
|
||||||
c.execute(
|
|
||||||
'''select world_rank_score from user where user_id=?''', (user_id,))
|
|
||||||
y = c.fetchone()
|
|
||||||
if y and y[0]:
|
|
||||||
c.execute(
|
|
||||||
'''select count(*) from user where world_rank_score > ?''', (y[0],))
|
|
||||||
y = c.fetchone()
|
|
||||||
if y and y[0] + 1 <= Config.WORLD_RANK_MAX:
|
|
||||||
world_rank = y[0] + 1
|
|
||||||
else:
|
|
||||||
world_rank = 0
|
|
||||||
else:
|
|
||||||
world_rank = 0
|
|
||||||
|
|
||||||
# 源点强化
|
|
||||||
prog_boost = 0
|
|
||||||
if x[27] and x[27] != 0:
|
|
||||||
prog_boost = 300
|
|
||||||
|
|
||||||
# 体力计算
|
|
||||||
next_fragstam_ts = -1
|
|
||||||
max_stamina_ts = 1586274871917
|
|
||||||
stamina = 12
|
|
||||||
if x[31]:
|
|
||||||
next_fragstam_ts = x[31]
|
|
||||||
if x[32]:
|
|
||||||
max_stamina_ts = x[32]
|
|
||||||
if x[33]:
|
|
||||||
stamina = x[33]
|
|
||||||
|
|
||||||
r = {"is_aprilfools": Config.IS_APRILFOOLS,
|
|
||||||
"curr_available_maps": server.arcworld.get_available_maps(),
|
|
||||||
"character_stats": user_character,
|
|
||||||
"friends": get_user_friend(c, user_id),
|
|
||||||
"settings": {
|
|
||||||
"favorite_character": favorite_character_id,
|
|
||||||
"is_hide_rating": int2b(x[10]),
|
|
||||||
"max_stamina_notification_enabled": int2b(x[24])
|
|
||||||
},
|
|
||||||
"user_id": user_id,
|
|
||||||
"name": x[1],
|
|
||||||
"user_code": x[4],
|
|
||||||
"display_name": x[1],
|
|
||||||
"ticket": x[26],
|
|
||||||
"character": character_id,
|
|
||||||
"is_locked_name_duplicate": False,
|
|
||||||
"is_skill_sealed": int2b(x[7]),
|
|
||||||
"current_map": x[25],
|
|
||||||
"prog_boost": prog_boost,
|
|
||||||
"next_fragstam_ts": next_fragstam_ts,
|
|
||||||
"max_stamina_ts": max_stamina_ts,
|
|
||||||
"stamina": server.arcworld.calc_stamina(max_stamina_ts, stamina),
|
|
||||||
"world_unlocks": server.item.get_user_items(c, user_id, 'world_unlock'),
|
|
||||||
"world_songs": server.item.get_user_items(c, user_id, 'world_song'),
|
|
||||||
"singles": server.item.get_user_items(c, user_id, 'single'),
|
|
||||||
"packs": server.item.get_user_items(c, user_id, 'pack'),
|
|
||||||
"characters": characters,
|
|
||||||
"cores": server.item.get_user_cores(c, user_id),
|
|
||||||
"recent_score": get_recent_score(c, user_id),
|
|
||||||
"max_friend": 50,
|
|
||||||
"rating": x[5],
|
|
||||||
"join_date": int(x[3]),
|
|
||||||
"global_rank": world_rank
|
|
||||||
}
|
|
||||||
|
|
||||||
return r
|
|
||||||
@@ -1,106 +0,0 @@
|
|||||||
from setting import Config
|
|
||||||
|
|
||||||
|
|
||||||
def get_user_items(c, user_id, item_type):
|
|
||||||
# 得到用户的物品,返回列表,不包含数量信息
|
|
||||||
|
|
||||||
if item_type == 'world_song' and Config.WORLD_SONG_FULL_UNLOCK or item_type == 'world_unlock' and Config.WORLD_SCENERY_FULL_UNLOCK:
|
|
||||||
c.execute('''select item_id from item where type=?''', (item_type,))
|
|
||||||
else:
|
|
||||||
c.execute('''select item_id from user_item where user_id = :user_id and type = :item_type''',
|
|
||||||
{'user_id': user_id, 'item_type': item_type})
|
|
||||||
x = c.fetchall()
|
|
||||||
if not x:
|
|
||||||
return []
|
|
||||||
|
|
||||||
re = []
|
|
||||||
for i in x:
|
|
||||||
re.append(i[0])
|
|
||||||
return re
|
|
||||||
|
|
||||||
|
|
||||||
def get_user_cores(c, user_id):
|
|
||||||
# 得到用户的core,返回字典列表
|
|
||||||
r = []
|
|
||||||
c.execute(
|
|
||||||
'''select item_id, amount from user_item where user_id = ? and type="core"''', (user_id,))
|
|
||||||
x = c.fetchall()
|
|
||||||
if x:
|
|
||||||
for i in x:
|
|
||||||
if i[1]:
|
|
||||||
amount = i[1]
|
|
||||||
else:
|
|
||||||
amount = 0
|
|
||||||
r.append({'core_type': i[0], 'amount': amount})
|
|
||||||
|
|
||||||
return r
|
|
||||||
|
|
||||||
|
|
||||||
def claim_user_item(c, user_id, item_id, item_type, amount=1):
|
|
||||||
# 处理用户物品,包括添加和删除操作,返回成功与否布尔值
|
|
||||||
# 这个接口允许memory变成负数,所以不能用来购买
|
|
||||||
|
|
||||||
try:
|
|
||||||
amount = int(amount)
|
|
||||||
except:
|
|
||||||
return False
|
|
||||||
|
|
||||||
if item_type not in ['memory', 'character']:
|
|
||||||
c.execute('''select is_available from item where item_id=? and type=?''',
|
|
||||||
(item_id, item_type))
|
|
||||||
x = c.fetchone()
|
|
||||||
if x:
|
|
||||||
if x[0] == 0:
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
if item_type in ['core', 'anni5tix']:
|
|
||||||
c.execute(
|
|
||||||
'''select amount from user_item where user_id=? and item_id=? and type=?''', (user_id, item_id, item_type))
|
|
||||||
x = c.fetchone()
|
|
||||||
if x:
|
|
||||||
if x[0] + amount < 0: # 数量不足
|
|
||||||
return False
|
|
||||||
c.execute('''update user_item set amount=? where user_id=? and item_id=? and type=?''',
|
|
||||||
(x[0]+amount, user_id, item_id, item_type))
|
|
||||||
else:
|
|
||||||
if amount < 0: # 添加数量错误
|
|
||||||
return False
|
|
||||||
c.execute('''insert into user_item values(?,?,?,?)''',
|
|
||||||
(user_id, item_id, item_type, amount))
|
|
||||||
|
|
||||||
elif item_type == 'memory':
|
|
||||||
c.execute('''select ticket from user where user_id=?''', (user_id,))
|
|
||||||
x = c.fetchone()
|
|
||||||
if x is not None:
|
|
||||||
c.execute('''update user set ticket=? where user_id=?''',
|
|
||||||
(x[0]+amount, user_id))
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
elif item_type == 'character':
|
|
||||||
if not item_id.isdigit():
|
|
||||||
c.execute(
|
|
||||||
'''select character_id from character where name=?''', (item_id,))
|
|
||||||
x = c.fetchone()
|
|
||||||
if x:
|
|
||||||
character_id = x[0]
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
character_id = int(item_id)
|
|
||||||
c.execute(
|
|
||||||
'''select exists(select * from user_char where user_id=? and character_id=?)''', (user_id, character_id))
|
|
||||||
if c.fetchone() == (0,):
|
|
||||||
c.execute('''insert into user_char values(?,?,1,0,0,0)''',
|
|
||||||
(user_id, character_id))
|
|
||||||
|
|
||||||
elif item_type in ['world_song', 'world_unlock', 'single', 'pack']:
|
|
||||||
c.execute('''select exists(select * from user_item where user_id=? and item_id=? and type=?)''',
|
|
||||||
(user_id, item_id, item_type))
|
|
||||||
if c.fetchone() == (0,):
|
|
||||||
c.execute('''insert into user_item values(:a,:b,:c,1)''',
|
|
||||||
{'a': user_id, 'b': item_id, 'c': item_type})
|
|
||||||
|
|
||||||
return True
|
|
||||||
@@ -18,7 +18,7 @@ def present_info(user_id):
|
|||||||
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())
|
return success_return(x.to_dict_list())
|
||||||
except ArcError as e:
|
except ArcError as e:
|
||||||
return error_return(e)
|
return error_return(e)
|
||||||
return error_return()
|
return error_return()
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ def bundle_pack(user_id):
|
|||||||
try:
|
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)
|
return success_return(x.to_dict_list())
|
||||||
except ArcError as e:
|
except ArcError as e:
|
||||||
return error_return(e)
|
return error_return(e)
|
||||||
return error_return()
|
return error_return()
|
||||||
@@ -34,7 +34,7 @@ def get_single(user_id):
|
|||||||
try:
|
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)
|
return success_return(x.to_dict_list())
|
||||||
except ArcError as e:
|
except ArcError as e:
|
||||||
return error_return(e)
|
return error_return(e)
|
||||||
return error_return()
|
return error_return()
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ def song_score_post(user_id):
|
|||||||
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:
|
except ArcError as e:
|
||||||
return error_return(e)
|
return error_return(e)
|
||||||
return error_return()
|
return error_return()
|
||||||
@@ -78,7 +78,7 @@ def song_score_top(user_id):
|
|||||||
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:
|
except ArcError as e:
|
||||||
return error_return(e)
|
return error_return(e)
|
||||||
return error_return()
|
return error_return()
|
||||||
@@ -93,7 +93,7 @@ def song_score_me(user_id):
|
|||||||
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:
|
except ArcError as e:
|
||||||
return error_return(e)
|
return error_return(e)
|
||||||
return error_return()
|
return error_return()
|
||||||
@@ -108,7 +108,7 @@ def song_score_friend(user_id):
|
|||||||
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:
|
except ArcError as e:
|
||||||
return error_return(e)
|
return error_return(e)
|
||||||
return error_return()
|
return error_return()
|
||||||
|
|||||||
@@ -1,160 +0,0 @@
|
|||||||
from server.sql import Connect
|
|
||||||
from setting import Config
|
|
||||||
from .config import Constant
|
|
||||||
import server.info
|
|
||||||
import server.character
|
|
||||||
|
|
||||||
|
|
||||||
def b2int(x):
|
|
||||||
# int与布尔值转换
|
|
||||||
if x:
|
|
||||||
return 1
|
|
||||||
else:
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
def int2b(x):
|
|
||||||
# int与布尔值转换
|
|
||||||
if x is None or x == 0:
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def change_char(user_id, character_id, skill_sealed):
|
|
||||||
# 角色改变,包括技能封印的改变,返回成功与否的布尔值
|
|
||||||
re = False
|
|
||||||
|
|
||||||
with Connect() as c:
|
|
||||||
if Config.CHARACTER_FULL_UNLOCK:
|
|
||||||
c.execute('''select is_uncapped, is_uncapped_override from user_char_full where user_id = :a and character_id = :b''',
|
|
||||||
{'a': user_id, 'b': character_id})
|
|
||||||
else:
|
|
||||||
c.execute('''select is_uncapped, is_uncapped_override from user_char where user_id = :a and character_id = :b''',
|
|
||||||
{'a': user_id, 'b': character_id})
|
|
||||||
x = c.fetchone()
|
|
||||||
if x:
|
|
||||||
is_uncapped = x[0]
|
|
||||||
is_uncapped_override = x[1]
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
if skill_sealed == 'false':
|
|
||||||
skill_sealed = False
|
|
||||||
else:
|
|
||||||
skill_sealed = True
|
|
||||||
c.execute('''update user set is_skill_sealed = :a, character_id = :b, is_char_uncapped = :c, is_char_uncapped_override = :d where user_id = :e''', {
|
|
||||||
'a': b2int(skill_sealed), 'b': character_id, 'c': is_uncapped, 'd': is_uncapped_override, 'e': user_id})
|
|
||||||
|
|
||||||
re = True
|
|
||||||
|
|
||||||
return re
|
|
||||||
|
|
||||||
|
|
||||||
def change_char_uncap(user_id, character_id):
|
|
||||||
# 角色觉醒改变,返回字典
|
|
||||||
r = None
|
|
||||||
with Connect() as c:
|
|
||||||
if not Config.CHARACTER_FULL_UNLOCK:
|
|
||||||
c.execute('''select is_uncapped, is_uncapped_override from user_char where user_id = :a and character_id = :b''',
|
|
||||||
{'a': user_id, 'b': character_id})
|
|
||||||
else:
|
|
||||||
c.execute('''select is_uncapped, is_uncapped_override from user_char_full where user_id = :a and character_id = :b''',
|
|
||||||
{'a': user_id, 'b': character_id})
|
|
||||||
x = c.fetchone()
|
|
||||||
|
|
||||||
if x is not None and x[0] == 1:
|
|
||||||
c.execute('''update user set is_char_uncapped_override = :a where user_id = :b''', {
|
|
||||||
'a': b2int(x[1] == 0), 'b': user_id})
|
|
||||||
|
|
||||||
if not Config.CHARACTER_FULL_UNLOCK:
|
|
||||||
c.execute('''update user_char set is_uncapped_override = :a where user_id = :b and character_id = :c''', {
|
|
||||||
'a': b2int(x[1] == 0), 'b': user_id, 'c': character_id})
|
|
||||||
c.execute(
|
|
||||||
'''select * from user_char a,character b where a.user_id=? and a.character_id=b.character_id and a.character_id=?''', (user_id, character_id))
|
|
||||||
else:
|
|
||||||
c.execute('''update user_char_full set is_uncapped_override = :a where user_id = :b and character_id = :c''', {
|
|
||||||
'a': b2int(x[1] == 0), 'b': user_id, 'c': character_id})
|
|
||||||
c.execute(
|
|
||||||
'''select * from user_char_full a,character b where a.user_id=? and a.character_id=b.character_id and a.character_id=?''', (user_id, character_id))
|
|
||||||
y = c.fetchone()
|
|
||||||
if y is not None:
|
|
||||||
r = {
|
|
||||||
"is_uncapped_override": int2b(y[5]),
|
|
||||||
"is_uncapped": int2b(y[4]),
|
|
||||||
"uncap_cores": server.character.get_char_core(c, y[1]),
|
|
||||||
"char_type": y[22],
|
|
||||||
"skill_id_uncap": y[21],
|
|
||||||
"skill_requires_uncap": int2b(y[20]),
|
|
||||||
"skill_unlock_level": y[19],
|
|
||||||
"skill_id": y[18],
|
|
||||||
"overdrive": server.character.calc_char_value(y[2], y[11], y[14], y[17]),
|
|
||||||
"prog": server.character.calc_char_value(y[2], y[10], y[13], y[16]),
|
|
||||||
"frag": server.character.calc_char_value(y[2], y[9], y[12], y[15]),
|
|
||||||
"level_exp": Constant.LEVEL_STEPS[y[2]],
|
|
||||||
"exp": y[3],
|
|
||||||
"level": y[2],
|
|
||||||
"name": y[7],
|
|
||||||
"character_id": y[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
return r
|
|
||||||
|
|
||||||
|
|
||||||
def arc_sys_set(user_id, value, set_arg):
|
|
||||||
# 三个设置,PTT隐藏、体力满通知、最爱角色,无返回
|
|
||||||
with Connect() as c:
|
|
||||||
if 'favorite_character' in set_arg:
|
|
||||||
value = int(value)
|
|
||||||
c.execute('''update user set favorite_character = :a where user_id = :b''', {
|
|
||||||
'a': value, 'b': user_id})
|
|
||||||
|
|
||||||
else:
|
|
||||||
if value == 'false':
|
|
||||||
value = False
|
|
||||||
else:
|
|
||||||
value = True
|
|
||||||
|
|
||||||
if 'is_hide_rating' in set_arg:
|
|
||||||
c.execute('''update user set is_hide_rating = :a where user_id = :b''', {
|
|
||||||
'a': b2int(value), 'b': user_id})
|
|
||||||
if 'max_stamina_notification_enabled' in set_arg:
|
|
||||||
c.execute('''update user set max_stamina_notification_enabled = :a where user_id = :b''', {
|
|
||||||
'a': b2int(value), 'b': user_id})
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def arc_add_friend(user_id, friend_id):
|
|
||||||
# 加好友,返回好友列表,或者是错误码602、604
|
|
||||||
if user_id == friend_id:
|
|
||||||
return 604
|
|
||||||
|
|
||||||
r = None
|
|
||||||
with Connect() as c:
|
|
||||||
c.execute('''select exists(select * from friend where user_id_me = :x and user_id_other = :y)''',
|
|
||||||
{'x': user_id, 'y': friend_id})
|
|
||||||
if c.fetchone() == (0,):
|
|
||||||
c.execute('''insert into friend values(:a, :b)''',
|
|
||||||
{'a': user_id, 'b': friend_id})
|
|
||||||
r = server.info.get_user_friend(c, user_id)
|
|
||||||
else:
|
|
||||||
r = 602
|
|
||||||
|
|
||||||
return r
|
|
||||||
|
|
||||||
|
|
||||||
def arc_delete_friend(user_id, friend_id):
|
|
||||||
# 删好友,返回好友列表
|
|
||||||
r = None
|
|
||||||
with Connect() as c:
|
|
||||||
|
|
||||||
c.execute('''select exists(select * from friend where user_id_me = :x and user_id_other = :y)''',
|
|
||||||
{'x': user_id, 'y': friend_id})
|
|
||||||
if c.fetchone() == (1,):
|
|
||||||
c.execute('''delete from friend where user_id_me = :x and user_id_other = :y''',
|
|
||||||
{'x': user_id, 'y': friend_id})
|
|
||||||
|
|
||||||
r = server.info.get_user_friend(c, user_id)
|
|
||||||
|
|
||||||
return r
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
import sqlite3
|
|
||||||
from flask import current_app
|
|
||||||
import traceback
|
|
||||||
|
|
||||||
|
|
||||||
class Connect():
|
|
||||||
# 数据库连接类,上下文管理
|
|
||||||
|
|
||||||
def __init__(self, file_path='./database/arcaea_database.db'):
|
|
||||||
"""
|
|
||||||
数据库连接,默认连接arcaea_database.db
|
|
||||||
接受:文件路径
|
|
||||||
返回:sqlite3连接操作对象
|
|
||||||
"""
|
|
||||||
self.file_path = file_path
|
|
||||||
|
|
||||||
def __enter__(self):
|
|
||||||
self.conn = sqlite3.connect(self.file_path)
|
|
||||||
self.c = self.conn.cursor()
|
|
||||||
return self.c
|
|
||||||
|
|
||||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
||||||
if self.conn:
|
|
||||||
self.conn.commit()
|
|
||||||
self.conn.close()
|
|
||||||
|
|
||||||
if exc_type is not None:
|
|
||||||
current_app.logger.error(
|
|
||||||
traceback.format_exception(exc_type, exc_val, exc_tb))
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
class Sql():
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def select(c, table_name, target_column=[], query=None):
|
|
||||||
# 执行查询单句sql语句,返回fetchall数据
|
|
||||||
# 使用准确查询,且在单表内
|
|
||||||
|
|
||||||
sql = 'select '
|
|
||||||
sql_dict = {}
|
|
||||||
if len(target_column) >= 2:
|
|
||||||
sql += target_column[0]
|
|
||||||
for i in range(1, len(target_column)):
|
|
||||||
sql += ',' + target_column[i]
|
|
||||||
sql += ' from ' + table_name
|
|
||||||
elif len(target_column) == 1:
|
|
||||||
sql += target_column[0] + ' from ' + table_name
|
|
||||||
else:
|
|
||||||
sql += '* from ' + table_name
|
|
||||||
|
|
||||||
where_field = []
|
|
||||||
where_value = []
|
|
||||||
if query:
|
|
||||||
for i in query.query:
|
|
||||||
where_field.append(i)
|
|
||||||
where_value.append(query.query[i])
|
|
||||||
|
|
||||||
if where_field and where_value:
|
|
||||||
sql += ' where '
|
|
||||||
sql += where_field[0] + '=:' + where_field[0]
|
|
||||||
sql_dict[where_field[0]] = where_value[0]
|
|
||||||
if len(where_field) >= 2:
|
|
||||||
for i in range(1, len(where_field)):
|
|
||||||
sql_dict[where_field[i]] = where_value[i]
|
|
||||||
sql += ' and ' + where_field[i] + '=:' + where_field[i]
|
|
||||||
|
|
||||||
if query and query.sort:
|
|
||||||
sql += ' order by ' + \
|
|
||||||
query.sort[0]['column'] + ' ' + query.sort[0]['order']
|
|
||||||
for i in range(1, len(query.sort)):
|
|
||||||
sql += ', ' + query.sort[i]['column'] + \
|
|
||||||
' ' + query.sort[i]['order']
|
|
||||||
|
|
||||||
if query and query.limit >= 0:
|
|
||||||
sql += ' limit :limit offset :offset'
|
|
||||||
sql_dict['limit'] = query.limit
|
|
||||||
sql_dict['offset'] = query.offset
|
|
||||||
|
|
||||||
c.execute(sql, sql_dict)
|
|
||||||
|
|
||||||
return c.fetchall()
|
|
||||||
@@ -80,7 +80,7 @@ def toggle_uncap(user_id, character_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:
|
except ArcError as e:
|
||||||
return error_return(e)
|
return error_return(e)
|
||||||
return error_return()
|
return error_return()
|
||||||
@@ -96,7 +96,7 @@ def character_first_uncap(user_id, character_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:
|
except ArcError as e:
|
||||||
return error_return(e)
|
return error_return(e)
|
||||||
return error_return()
|
return error_return()
|
||||||
@@ -130,7 +130,7 @@ def cloud_get(user_id):
|
|||||||
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:
|
except ArcError as e:
|
||||||
return error_return(e)
|
return error_return(e)
|
||||||
return error_return()
|
return error_return()
|
||||||
|
|||||||
@@ -252,13 +252,3 @@ class Config():
|
|||||||
'''
|
'''
|
||||||
--------------------
|
--------------------
|
||||||
'''
|
'''
|
||||||
|
|
||||||
'''
|
|
||||||
--------------------
|
|
||||||
是否使用最好的 10 条记录(而不是最近的 30 条记录中较好的 10 条)来计算 PTT
|
|
||||||
Calculate PTT with best 10 instead of recent best 10
|
|
||||||
'''
|
|
||||||
USE_B10_AS_R10 = False
|
|
||||||
'''
|
|
||||||
--------------------
|
|
||||||
'''
|
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
from .udp_config import Config
|
||||||
|
|
||||||
|
|
||||||
def b(value, length=1):
|
def b(value, length=1):
|
||||||
return value.to_bytes(length=length, byteorder='little')
|
return value.to_bytes(length=length, byteorder='little')
|
||||||
|
|
||||||
@@ -35,7 +38,7 @@ class Player:
|
|||||||
self.last_timestamp = 0
|
self.last_timestamp = 0
|
||||||
self.extra_command_queue = []
|
self.extra_command_queue = []
|
||||||
|
|
||||||
self.song_unlock = b'\x00' * 512
|
self.song_unlock = b'\x00' * Config.LINK_PLAY_UNLOCK_LENGTH
|
||||||
|
|
||||||
self.start_command_num = 0
|
self.start_command_num = 0
|
||||||
|
|
||||||
@@ -58,7 +61,7 @@ class Room:
|
|||||||
self.song_idx = 0xffff
|
self.song_idx = 0xffff
|
||||||
self.last_song_idx = 0xffff
|
self.last_song_idx = 0xffff
|
||||||
|
|
||||||
self.song_unlock = b'\x00' * 512
|
self.song_unlock = b'\x00' * Config.LINK_PLAY_UNLOCK_LENGTH
|
||||||
|
|
||||||
self.host_id = 0
|
self.host_id = 0
|
||||||
self.players = [Player(), Player(), Player(), Player()]
|
self.players = [Player(), Player(), Player(), Player()]
|
||||||
@@ -120,12 +123,12 @@ class Room:
|
|||||||
|
|
||||||
def update_song_unlock(self):
|
def update_song_unlock(self):
|
||||||
# 更新房间可用歌曲
|
# 更新房间可用歌曲
|
||||||
r = bi(b'\xff' * 512)
|
r = bi(b'\xff' * Config.LINK_PLAY_UNLOCK_LENGTH)
|
||||||
for i in self.players:
|
for i in self.players:
|
||||||
if i.player_id != 0:
|
if i.player_id != 0:
|
||||||
r &= bi(i.song_unlock)
|
r &= bi(i.song_unlock)
|
||||||
|
|
||||||
self.song_unlock = b(r, 512)
|
self.song_unlock = b(r, Config.LINK_PLAY_UNLOCK_LENGTH)
|
||||||
|
|
||||||
def is_ready(self, old_state: int, player_state: int):
|
def is_ready(self, old_state: int, player_state: int):
|
||||||
# 是否全部准备就绪
|
# 是否全部准备就绪
|
||||||
|
|||||||
@@ -5,4 +5,7 @@ class Config:
|
|||||||
|
|
||||||
COUNTDOWM_TIME = 3999
|
COUNTDOWM_TIME = 3999
|
||||||
|
|
||||||
|
PLAYER_PRE_TIMEOUT = 3000000
|
||||||
PLAYER_TIMEOUT = 20000000
|
PLAYER_TIMEOUT = 20000000
|
||||||
|
|
||||||
|
LINK_PLAY_UNLOCK_LENGTH = 512
|
||||||
@@ -176,7 +176,7 @@ class CommandParser:
|
|||||||
t = self.room.players[i]
|
t = self.room.players[i]
|
||||||
if t.player_id != 0:
|
if t.player_id != 0:
|
||||||
if t.last_timestamp != 0:
|
if t.last_timestamp != 0:
|
||||||
if t.online == 1 and x.timestamp - t.last_timestamp >= 3000000:
|
if t.online == 1 and x.timestamp - t.last_timestamp >= Config.PLAYER_PRE_TIMEOUT:
|
||||||
t.online = 0
|
t.online = 0
|
||||||
self.room.command_queue_length += 1
|
self.room.command_queue_length += 1
|
||||||
self.room.command_queue.append(x.command_12(i))
|
self.room.command_queue.append(x.command_12(i))
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import time
|
|||||||
|
|
||||||
import server.arcscore
|
import server.arcscore
|
||||||
from core.download import initialize_songfile
|
from core.download import initialize_songfile
|
||||||
|
from core.rank import RankList
|
||||||
from core.sql import Connect
|
from core.sql import Connect
|
||||||
from flask import Blueprint, flash, redirect, render_template, request, url_for
|
from flask import Blueprint, flash, redirect, render_template, request, url_for
|
||||||
from werkzeug.utils import secure_filename
|
from werkzeug.utils import secure_filename
|
||||||
@@ -215,25 +216,30 @@ def single_chart_top():
|
|||||||
difficulty = request.form['difficulty']
|
difficulty = request.form['difficulty']
|
||||||
if difficulty.isdigit():
|
if difficulty.isdigit():
|
||||||
difficulty = int(difficulty)
|
difficulty = int(difficulty)
|
||||||
|
else:
|
||||||
|
difficulty = 0
|
||||||
error = None
|
error = None
|
||||||
x = None
|
x = None
|
||||||
with Connect('') as c:
|
with Connect() as c:
|
||||||
song_name = '%'+song_name+'%'
|
song_name = '%'+song_name+'%'
|
||||||
c.execute('''select song_id, name from chart where song_id like :a limit 1''',
|
c.execute('''select song_id, name from chart where song_id like :a limit 1''',
|
||||||
{'a': song_name})
|
{'a': song_name})
|
||||||
x = c.fetchone()
|
x = c.fetchone()
|
||||||
|
|
||||||
if x:
|
if x:
|
||||||
song_id = x[0]
|
y = RankList(c)
|
||||||
posts = server.arcscore.arc_score_top(song_id, difficulty, -1)
|
y.song.set_chart(x[0], difficulty)
|
||||||
for i in posts:
|
y.limit = -1
|
||||||
i['time_played'] = time.strftime('%Y-%m-%d %H:%M:%S',
|
y.select_top()
|
||||||
time.localtime(i['time_played']))
|
posts = y.to_dict_list()
|
||||||
else:
|
for i in posts:
|
||||||
error = '查询为空 No song.'
|
i['time_played'] = time.strftime('%Y-%m-%d %H:%M:%S',
|
||||||
|
time.localtime(i['time_played']))
|
||||||
|
else:
|
||||||
|
error = '查询为空 No song.'
|
||||||
|
|
||||||
if not error:
|
if not error:
|
||||||
return render_template('web/singlecharttop.html', posts=posts, song_name_en=x[1], song_id=song_id, difficulty=difficulty)
|
return render_template('web/singlecharttop.html', posts=posts, song_name_en=x[1], song_id=x[0], difficulty=difficulty)
|
||||||
else:
|
else:
|
||||||
flash(error)
|
flash(error)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user