mirror of
https://github.com/Lost-MSth/Arcaea-server.git
synced 2026-02-09 01:07:27 +08:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8a3cec05b7 | ||
|
|
e3d81436d0 | ||
|
|
941a79ccc7 | ||
|
|
62902a561c | ||
|
|
1660bc5899 | ||
|
|
d577246d28 | ||
|
|
9fd626468e | ||
|
|
c51a1b4e85 | ||
|
|
0e42123417 | ||
|
|
e216e6144b | ||
|
|
b70c435a4a | ||
|
|
eb03d2be93 | ||
|
|
fcb5d264ef | ||
|
|
d617951e1f | ||
|
|
febf931f1a | ||
|
|
981961d6a1 |
28
README.md
28
README.md
@@ -39,8 +39,9 @@ This procedure is mainly used for study and research, and shall not be used for
|
||||
- 服务器日志 Server log
|
||||
|
||||
没有以下 We don't have:
|
||||
- 角色数值 Character characteristic value
|
||||
- 服务器安全性保证 Server security assurance
|
||||
- 世界模式下的搭档升级、觉醒,以及某些奖励的获取 Partner upgrading, uncapping, and receiving some rewards in the world mode
|
||||
- 世界排名 Global rank
|
||||
|
||||
可能有问题 There may be problems:
|
||||
- Recent 30
|
||||
@@ -60,24 +61,29 @@ It is just so interesting. What it can do is under exploration.
|
||||
## 更新日志 Update log
|
||||
只保留最新版本 Only keep the latest version.
|
||||
|
||||
> 提醒:更新时请注意保留原先的数据库,以防数据丢失
|
||||
> 提醒:更新时请注意保留原先的数据库,以防数据丢失。
|
||||
>
|
||||
> Tips: When updating, please keep the original database in case of data loss.
|
||||
> Tips: When updating, please keep the original database in case of data loss.
|
||||
|
||||
### Version 2.3
|
||||
- 适用于Arcaea 3.5.2版本 For Arcaea 3.5.2
|
||||
### Version 2.4
|
||||
- 适用于Arcaea 3.6.0版本 For Arcaea 3.6.0
|
||||
- 更新了歌曲数据库 Update the song database.
|
||||
- 新增服务器日志 Add server log.
|
||||
- 优化了代码结构 Optimize the code structure.
|
||||
- 修复了一些Bug,提升服务器安全性 Fix some bugs and improve server security.
|
||||
|
||||
- 新搭档 **拉格兰(拉格朗日!)** 已解锁 Unlock the new character **Lagrange**.
|
||||
- 新增游戏版本校验 Add game version verification.
|
||||
- 新增注册时邮箱记录 Add email recording while registering.
|
||||
- 新增登录时IP地址记录 Add IP address recording at login time.
|
||||
- 新增了一些设置选项和后台功能 Add some setting options and background functions.
|
||||
- 新增数据库自动更新功能 Add database automatic update function.
|
||||
- 正在构建API接口 Building API interface.
|
||||
- 修复了一些Bug Fix some bugs.
|
||||
|
||||
## 运行环境与依赖 Running environment and requirements
|
||||
- Windows/Linux/Mac OS/Android
|
||||
- Python 3
|
||||
- Flask module
|
||||
- Charles (optional)
|
||||
- Charles, IDA, proxy app... (optional)
|
||||
|
||||
<!--
|
||||
## 环境搭建 Environment construction
|
||||
[中文](https://github.com/Lost-MSth/Arcaea-server/wiki/%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA)
|
||||
[English](https://github.com/Lost-MSth/Arcaea-server/wiki/Environment-construction)
|
||||
@@ -85,11 +91,13 @@ It is just so interesting. What it can do is under exploration.
|
||||
## 使用说明 Instruction for use
|
||||
[中文](https://github.com/Lost-MSth/Arcaea-server/wiki/%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E)
|
||||
[English](https://github.com/Lost-MSth/Arcaea-server/wiki/Instruction-for-use)
|
||||
-->
|
||||
|
||||
## 注意 Attentions
|
||||
[中文](https://github.com/Lost-MSth/Arcaea-server/wiki/%E6%B3%A8%E6%84%8F)
|
||||
[English](https://github.com/Lost-MSth/Arcaea-server/wiki/Attentions)
|
||||
|
||||
|
||||
## 鸣谢 Thanks
|
||||
歌曲数据库来自 Using song database from
|
||||
[BotArcAPI releases](https://github.com/TheSnowfield/BotArcAPI/releases)
|
||||
|
||||
119
latest version/api/api_auth.py
Normal file
119
latest version/api/api_auth.py
Normal file
@@ -0,0 +1,119 @@
|
||||
import hashlib
|
||||
import time
|
||||
from server.sql import Connect
|
||||
import functools
|
||||
from setting import Config
|
||||
from flask import jsonify
|
||||
|
||||
|
||||
class User():
|
||||
# 用户类,当数据类型用
|
||||
def __init__(self, user_id=None, role='', power=[]):
|
||||
self.user_id = user_id
|
||||
self.role = role
|
||||
self.power = power
|
||||
|
||||
|
||||
def login():
|
||||
# 登录接口
|
||||
return {'token': 1}, 0
|
||||
|
||||
|
||||
def logout():
|
||||
# 登出接口
|
||||
pass
|
||||
|
||||
|
||||
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):
|
||||
@functools.wraps(view)
|
||||
def wrapped_view(*args, **kwargs):
|
||||
|
||||
try:
|
||||
request.json # 检查请求json格式
|
||||
except:
|
||||
return jsonify({'status': 400, 'code': -1, 'data': {}, 'msg': 'Payload must be a valid json'})
|
||||
|
||||
if not 'Token' in request.headers:
|
||||
return jsonify({'status': 401, 'code': -1, 'data': {}, 'msg': 'No Token'})
|
||||
|
||||
user = User()
|
||||
if power == []:
|
||||
if Config.API_TOKEN == request.headers['Token'] and Config.API_TOKEN != '':
|
||||
user.user_id = 0
|
||||
else:
|
||||
return jsonify({'status': 403, 'code': -1, 'data': {}, 'msg': 'No permission'})
|
||||
else:
|
||||
with Connect() as c:
|
||||
user.user_id = api_token_get_id(
|
||||
c, request.headers['Token'])
|
||||
if user.user_id is None:
|
||||
return jsonify({'status': 404, 'code': -1, 'data': {}, 'msg': ''})
|
||||
|
||||
role_id = id_get_role_id(c, user.user_id)
|
||||
user.role = role_id_get_role(c, role_id)
|
||||
user.role_power = get_role_power(c, role_id)
|
||||
|
||||
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 wrapped_view
|
||||
return decorator
|
||||
15
latest version/api/api_code.py
Normal file
15
latest version/api/api_code.py
Normal file
@@ -0,0 +1,15 @@
|
||||
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',
|
||||
'-101': 'Wrong data type',
|
||||
'-102': 'Wrong query parameter',
|
||||
'-103': 'Wrong sort parameter',
|
||||
'-104': 'Wrong sort order parameter'
|
||||
}
|
||||
|
||||
return msg[str(code)]
|
||||
223
latest version/api/api_main.py
Normal file
223
latest version/api/api_main.py
Normal file
@@ -0,0 +1,223 @@
|
||||
from flask import (
|
||||
Blueprint, request, jsonify
|
||||
)
|
||||
import api.api_auth
|
||||
import api.users
|
||||
import api.songs
|
||||
from api.api_code import code_get_msg
|
||||
|
||||
|
||||
bp = Blueprint('api', __name__, url_prefix='/api/v1')
|
||||
|
||||
|
||||
def get_query_parameter(request, query_able=[], sort_able=[]):
|
||||
# 提取查询请求参数,返回四个参数和code
|
||||
|
||||
limit = -1
|
||||
offset = 0
|
||||
query = {} # {'name': 'admin'}
|
||||
sort = [] # [{'column': 'user_id', 'order': 'ASC'}, ...]
|
||||
|
||||
if 'limit' in request.json:
|
||||
try:
|
||||
limit = int(request.json['limit'])
|
||||
except:
|
||||
return -1, 0, {}, {}, -101
|
||||
if 'offset' in request.json:
|
||||
try:
|
||||
offset = int(request.json['offset'])
|
||||
except:
|
||||
return -1, 0, {}, {}, -101
|
||||
if 'query' in request.json:
|
||||
query = request.json['query']
|
||||
for i in query:
|
||||
if i not in query_able:
|
||||
return -1, 0, {}, {}, -102
|
||||
if 'sort' in request.json:
|
||||
sort = request.json['sort']
|
||||
for i in sort:
|
||||
if 'column' not in i or i['column'] not in sort_able:
|
||||
return -1, 0, {}, {}, -103
|
||||
if not 'order' in i:
|
||||
i['order'] = 'ASC'
|
||||
else:
|
||||
if i['order'] not in ['ASC', 'DESC']:
|
||||
return -1, 0, {}, {}, -104
|
||||
|
||||
return limit, offset, query, sort, 0
|
||||
|
||||
|
||||
@bp.route('/')
|
||||
def ping():
|
||||
return jsonify({'status': 200, 'code': 0, 'data': {}, 'msg': ''})
|
||||
|
||||
|
||||
@bp.route('/token', methods=['POST'])
|
||||
def token_post():
|
||||
# 登录,获取token
|
||||
|
||||
if 'auth' in request.json:
|
||||
data, code = api.api_auth.login(
|
||||
request.json['auth'], request.remote_addr)
|
||||
if code < 0:
|
||||
return jsonify({'status': 200, 'code': code, 'data': {}, 'msg': code_get_msg(code)})
|
||||
else:
|
||||
return jsonify({'status': 200, 'code': 0, 'data': data, 'msg': ''})
|
||||
else:
|
||||
return jsonify({'status': 401, 'code': -1, 'data': {}, 'msg': 'No authentication'})
|
||||
|
||||
|
||||
@bp.route('/token', methods=['GET'])
|
||||
@api.api_auth.role_required(request, ['select_me', 'select'])
|
||||
def token_get(user):
|
||||
# 判断登录有效性
|
||||
return jsonify({'status': 200, 'code': 0, 'data': {}, 'msg': ''})
|
||||
|
||||
|
||||
@bp.route('/token', methods=['DELETE'])
|
||||
@api.api_auth.role_required(request, ['change_me', 'select_me', 'select'])
|
||||
def token_delete(user):
|
||||
# 登出
|
||||
return jsonify({'status': 200, 'code': 0, 'data': {}, 'msg': ''})
|
||||
|
||||
|
||||
@bp.route('/users', methods=['GET'])
|
||||
@api.api_auth.role_required(request, ['select'])
|
||||
def users_get(user):
|
||||
# 查询全用户信息
|
||||
|
||||
limit, offset, query, sort, code = get_query_parameter(request, ['user_id', 'name', 'user_code'], [
|
||||
'user_id', 'name', 'user_code', 'join_date', 'rating_ptt', 'time_played', 'ticket'])
|
||||
if code < 0:
|
||||
return jsonify({'status': 200, 'code': code, 'data': {}, 'msg': code_get_msg(code)})
|
||||
|
||||
data = api.users.get_users(limit, offset, query, sort)
|
||||
|
||||
if not data:
|
||||
return jsonify({'status': 200, 'code': -2, 'data': {}, 'msg': code_get_msg(-2)})
|
||||
|
||||
return jsonify({'status': 200, 'code': 0, 'data': data, 'msg': ''})
|
||||
|
||||
|
||||
@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 jsonify({'status': 200, 'code': -4, 'data': {}, 'msg': code_get_msg(-4)})
|
||||
|
||||
if user_id != user.user_id and not 'select' in user.power and user.user_id != 0: # 查别人需要select权限
|
||||
return jsonify({'status': 403, 'code': -1, 'data': {}, 'msg': 'No permission'})
|
||||
|
||||
data = api.users.get_user_info(user_id)
|
||||
|
||||
if not data:
|
||||
return jsonify({'status': 200, 'code': -3, 'data': {}, 'msg': code_get_msg(-3)})
|
||||
|
||||
return jsonify({'status': 200, 'code': 0, 'data': data, 'msg': ''})
|
||||
|
||||
|
||||
@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 == 'me':
|
||||
user_id = user.user_id
|
||||
|
||||
if user_id <= 0:
|
||||
return jsonify({'status': 200, 'code': -4, 'data': {}, 'msg': code_get_msg(-4)})
|
||||
|
||||
if user_id != user.user_id and not 'select' in user.power and user.user_id != 0: # 查别人需要select权限
|
||||
return jsonify({'status': 403, 'code': -1, 'data': {}, 'msg': 'No permission'})
|
||||
|
||||
data = api.users.get_user_b30(user_id)
|
||||
|
||||
if data['data'] == []:
|
||||
return jsonify({'status': 200, 'code': -3, 'data': {}, 'msg': code_get_msg(-3)})
|
||||
|
||||
return jsonify({'status': 200, 'code': 0, 'data': data, 'msg': ''})
|
||||
|
||||
|
||||
@bp.route('/users/<int:user_id>/best', methods=['GET'])
|
||||
@api.api_auth.role_required(request, ['select', 'select_me'])
|
||||
def users_user_best_get(user, user_id):
|
||||
# 查询用户所有best成绩
|
||||
|
||||
if user_id == 'me':
|
||||
user_id = user.user_id
|
||||
|
||||
if user_id <= 0:
|
||||
return jsonify({'status': 200, 'code': -4, 'data': {}, 'msg': code_get_msg(-4)})
|
||||
|
||||
if user_id != user.user_id and not 'select' in user.power and user.user_id != 0: # 查别人需要select权限
|
||||
return jsonify({'status': 403, 'code': -1, 'data': {}, 'msg': 'No permission'})
|
||||
|
||||
limit, offset, query, sort, code = get_query_parameter(request, ['song_id', 'difficulty'], [
|
||||
'song_id', 'difficulty', 'score', 'time_played', 'rating'])
|
||||
if code < 0:
|
||||
return jsonify({'status': 200, 'code': code, 'data': {}, 'msg': code_get_msg(code)})
|
||||
|
||||
data = api.users.get_user_best(user_id, limit, offset, query, sort)
|
||||
|
||||
if data['data'] == []:
|
||||
return jsonify({'status': 200, 'code': -3, 'data': {}, 'msg': code_get_msg(-3)})
|
||||
|
||||
return jsonify({'status': 200, 'code': 0, 'data': data, 'msg': ''})
|
||||
|
||||
|
||||
@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 == 'me':
|
||||
user_id = user.user_id
|
||||
|
||||
if user_id <= 0:
|
||||
return jsonify({'status': 200, 'code': -4, 'data': {}, 'msg': code_get_msg(-4)})
|
||||
|
||||
if user_id != user.user_id and not 'select' in user.power and user.user_id != 0: # 查别人需要select权限
|
||||
return jsonify({'status': 403, 'code': -1, 'data': {}, 'msg': 'No permission'})
|
||||
|
||||
data = api.users.get_user_r30(user_id)
|
||||
|
||||
if data['data'] == []:
|
||||
return jsonify({'status': 200, 'code': -3, 'data': {}, 'msg': code_get_msg(-3)})
|
||||
|
||||
return jsonify({'status': 200, 'code': 0, 'data': data, 'msg': ''})
|
||||
|
||||
|
||||
@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 jsonify({'status': 200, 'code': -2, 'data': {}, 'msg': code_get_msg(-2)})
|
||||
|
||||
return jsonify({'status': 200, 'code': 0, 'data': data, 'msg': ''})
|
||||
|
||||
|
||||
@bp.route('/songs', methods=['GET'])
|
||||
@api.api_auth.role_required(request, ['select', 'select_song_info'])
|
||||
def songs_get(user):
|
||||
# 查询全歌曲信息
|
||||
|
||||
limit, offset, query, sort, code = 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'])
|
||||
if code < 0:
|
||||
return jsonify({'status': 200, 'code': code, 'data': {}, 'msg': code_get_msg(code)})
|
||||
|
||||
data = api.songs.get_songs(limit, offset, query, sort)
|
||||
|
||||
if not data:
|
||||
return jsonify({'status': 200, 'code': -2, 'data': {}, 'msg': code_get_msg(-2)})
|
||||
|
||||
return jsonify({'status': 200, 'code': 0, 'data': data, 'msg': ''})
|
||||
58
latest version/api/songs.py
Normal file
58
latest version/api/songs.py
Normal file
@@ -0,0 +1,58 @@
|
||||
from server.sql import Connect
|
||||
from server.sql import Sql
|
||||
import time
|
||||
|
||||
|
||||
def get_song_info(song_id):
|
||||
# 查询指定歌曲信息,返回字典
|
||||
r = {}
|
||||
|
||||
with Connect('./database/arcsong.db') as c:
|
||||
c.execute('''select * from songs where sid=:a''', {'a': song_id})
|
||||
x = c.fetchone()
|
||||
if x:
|
||||
r = {'song_id': x[0],
|
||||
'name': {'name_en': x[1],
|
||||
'name_jp': x[2]},
|
||||
'pakset': x[5],
|
||||
'artist': x[6],
|
||||
'date': x[9],
|
||||
'rating_pst': x[12]/10,
|
||||
'rating_prs': x[13]/10,
|
||||
'rating_ftr': x[14]/10,
|
||||
'rating_byn': x[15]/10,
|
||||
'difficultly_pst': x[16]/2,
|
||||
'difficultly_prs': x[17]/2,
|
||||
'difficultly_ftr': x[18]/2,
|
||||
'difficultly_byn': x[19]/2
|
||||
}
|
||||
|
||||
return r
|
||||
|
||||
|
||||
def get_songs(limit=-1, offset=0, query={}, sort=[]):
|
||||
# 查询全部歌曲信息,返回字典列表
|
||||
r = []
|
||||
|
||||
with Connect('./database/arcsong.db') as c:
|
||||
x = Sql.select(c, 'songs', [], limit, offset, query, sort)
|
||||
|
||||
if x:
|
||||
for i in x:
|
||||
r.append({'sid': i[0],
|
||||
'name': {'name_en': i[1],
|
||||
'name_jp': i[2]},
|
||||
'pakset': i[5],
|
||||
'artist': i[6],
|
||||
'date': i[9],
|
||||
'rating_pst': i[12]/10,
|
||||
'rating_prs': i[13]/10,
|
||||
'rating_ftr': i[14]/10,
|
||||
'rating_byn': i[15]/10,
|
||||
'difficultly_pst': i[16]/2,
|
||||
'difficultly_prs': i[17]/2,
|
||||
'difficultly_ftr': i[18]/2,
|
||||
'difficultly_byn': i[19]/2
|
||||
})
|
||||
|
||||
return r
|
||||
99
latest version/api/users.py
Normal file
99
latest version/api/users.py
Normal file
@@ -0,0 +1,99 @@
|
||||
from server.sql import Connect
|
||||
from server.sql import Sql
|
||||
import time
|
||||
import web.webscore
|
||||
import server.info
|
||||
|
||||
|
||||
def get_users(limit=-1, offset=0, query={}, sort=[]):
|
||||
# 获取全用户信息,返回字典列表
|
||||
|
||||
r = []
|
||||
with Connect() as c:
|
||||
x = Sql.select(c, 'user', [], limit, offset, query, sort)
|
||||
|
||||
if x:
|
||||
for i in x:
|
||||
if i[23] != -1:
|
||||
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
|
||||
|
||||
|
||||
def get_user_info(user_id):
|
||||
# 获取用户信息,返回字典,其实就是调用user/me信息
|
||||
|
||||
r = {}
|
||||
with Connect() as c:
|
||||
r = server.info.get_user_me(c, user_id)
|
||||
|
||||
return r
|
||||
|
||||
|
||||
def get_user_b30(user_id):
|
||||
# 获取用户b30信息,返回字典
|
||||
|
||||
r = []
|
||||
with Connect() as c:
|
||||
r = web.webscore.get_user_score(c, user_id, 30)
|
||||
|
||||
bestptt = 0
|
||||
for i in r:
|
||||
if i['rating']:
|
||||
bestptt += i['rating']
|
||||
if 'time_played' in i:
|
||||
i['time_played'] = int(time.mktime(time.strptime(
|
||||
i['time_played'], '%Y-%m-%d %H:%M:%S')))
|
||||
|
||||
return {'user_id': user_id, 'b30_ptt': bestptt / 30, 'data': r}
|
||||
|
||||
|
||||
def get_user_best(user_id, limit=-1, offset=0, query={}, sort=[]):
|
||||
# 获取用户b30信息,返回字典
|
||||
|
||||
r = []
|
||||
with Connect() as c:
|
||||
x = Sql.select(c, 'best_score', [], limit, offset, query, sort)
|
||||
if x:
|
||||
for i in x:
|
||||
r.append({
|
||||
"song_id": i[1],
|
||||
"difficulty": i[2],
|
||||
"score": i[3],
|
||||
"shiny_perfect_count": i[4],
|
||||
"perfect_count": i[5],
|
||||
"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):
|
||||
# 获取用户r30信息,返回字典
|
||||
|
||||
r = []
|
||||
with Connect() as c:
|
||||
r, r10_ptt = web.webscore.get_user_recent30(c, user_id)
|
||||
|
||||
return {'user_id': user_id, 'r10_ptt': r10_ptt, 'data': r}
|
||||
Binary file not shown.
Binary file not shown.
@@ -5,352 +5,430 @@ import json
|
||||
|
||||
# 数据库初始化文件,删掉arcaea_database.db文件后运行即可,谨慎使用
|
||||
|
||||
conn = sqlite3.connect('arcaea_database.db')
|
||||
c = conn.cursor()
|
||||
c.execute('''create table if not exists user(user_id int primary key,
|
||||
name text unique,
|
||||
password text not null,
|
||||
join_date char(20),
|
||||
user_code char(10),
|
||||
rating_ptt int,
|
||||
character_id int,
|
||||
is_skill_sealed int,
|
||||
is_char_uncapped int,
|
||||
is_char_uncapped_override int,
|
||||
is_hide_rating int,
|
||||
song_id text,
|
||||
difficulty int,
|
||||
score int,
|
||||
shiny_perfect_count int,
|
||||
perfect_count int,
|
||||
near_count int,
|
||||
miss_count int,
|
||||
health int,
|
||||
modifier int,
|
||||
time_played int,
|
||||
clear_type int,
|
||||
rating real,
|
||||
favorite_character int,
|
||||
max_stamina_notification_enabled int,
|
||||
current_map text,
|
||||
ticket int,
|
||||
prog_boost int
|
||||
);''')
|
||||
c.execute('''create table if not exists login(access_token text,
|
||||
user_id int,
|
||||
login_time int,
|
||||
login_ip text,
|
||||
login_device text,
|
||||
primary key(access_token, user_id)
|
||||
);''')
|
||||
c.execute('''create table if not exists friend(user_id_me int,
|
||||
user_id_other int,
|
||||
primary key (user_id_me, user_id_other)
|
||||
);''')
|
||||
c.execute('''create table if not exists best_score(user_id int,
|
||||
song_id text,
|
||||
difficulty int,
|
||||
score int,
|
||||
shiny_perfect_count int,
|
||||
perfect_count int,
|
||||
near_count int,
|
||||
miss_count int,
|
||||
health int,
|
||||
modifier int,
|
||||
time_played int,
|
||||
best_clear_type int,
|
||||
clear_type int,
|
||||
rating real,
|
||||
primary key(user_id, song_id, difficulty)
|
||||
);''')
|
||||
c.execute('''create table if not exists user_char(user_id int,
|
||||
character_id int,
|
||||
level int,
|
||||
exp real,
|
||||
level_exp real,
|
||||
frag real,
|
||||
prog real,
|
||||
overdrive real,
|
||||
skill_id text,
|
||||
skill_unlock_level int,
|
||||
skill_requires_uncap int,
|
||||
skill_id_uncap text,
|
||||
char_type int,
|
||||
is_uncapped int,
|
||||
is_uncapped_override int,
|
||||
primary key(user_id, character_id)
|
||||
);''')
|
||||
c.execute('''create table if not exists character(character_id int primary key,
|
||||
name text,
|
||||
level int,
|
||||
exp real,
|
||||
level_exp real,
|
||||
frag real,
|
||||
prog real,
|
||||
overdrive real,
|
||||
skill_id text,
|
||||
skill_unlock_level int,
|
||||
skill_requires_uncap int,
|
||||
skill_id_uncap text,
|
||||
char_type int,
|
||||
uncap_cores text,
|
||||
is_uncapped int,
|
||||
is_uncapped_override int
|
||||
);''')
|
||||
c.execute('''create table if not exists recent30(user_id int primary key,
|
||||
r0 real,
|
||||
song_id0 text,
|
||||
r1 real,
|
||||
song_id1 text,
|
||||
r2 real,
|
||||
song_id2 text,
|
||||
r3 real,
|
||||
song_id3 text,
|
||||
r4 real,
|
||||
song_id4 text,
|
||||
r5 real,
|
||||
song_id5 text,
|
||||
r6 real,
|
||||
song_id6 text,
|
||||
r7 real,
|
||||
song_id7 text,
|
||||
r8 real,
|
||||
song_id8 text,
|
||||
r9 real,
|
||||
song_id9 text,
|
||||
r10 real,
|
||||
song_id10 text,
|
||||
r11 real,
|
||||
song_id11 text,
|
||||
r12 real,
|
||||
song_id12 text,
|
||||
r13 real,
|
||||
song_id13 text,
|
||||
r14 real,
|
||||
song_id14 text,
|
||||
r15 real,
|
||||
song_id15 text,
|
||||
r16 real,
|
||||
song_id16 text,
|
||||
r17 real,
|
||||
song_id17 text,
|
||||
r18 real,
|
||||
song_id18 text,
|
||||
r19 real,
|
||||
song_id19 text,
|
||||
r20 real,
|
||||
song_id20 text,
|
||||
r21 real,
|
||||
song_id21 text,
|
||||
r22 real,
|
||||
song_id22 text,
|
||||
r23 real,
|
||||
song_id23 text,
|
||||
r24 real,
|
||||
song_id24 text,
|
||||
r25 real,
|
||||
song_id25 text,
|
||||
r26 real,
|
||||
song_id26 text,
|
||||
r27 real,
|
||||
song_id27 text,
|
||||
r28 real,
|
||||
song_id28 text,
|
||||
r29 real,
|
||||
song_id29 text
|
||||
);''')
|
||||
c.execute('''create table if not exists user_world(user_id int,
|
||||
map_id text,
|
||||
curr_position int,
|
||||
curr_capture real,
|
||||
is_locked int,
|
||||
primary key(user_id, map_id)
|
||||
);''')
|
||||
c.execute('''create table if not exists world_songplay(user_id int,
|
||||
song_id text,
|
||||
difficulty int,
|
||||
stamina_multiply int,
|
||||
fragment_multiply int,
|
||||
prog_boost_multiply int,
|
||||
primary key(user_id, song_id, difficulty)
|
||||
);''')
|
||||
c.execute('''create table if not exists download_token(user_id int,
|
||||
song_id text,
|
||||
file_name text,
|
||||
token text,
|
||||
time int,
|
||||
primary key(user_id, song_id, file_name)
|
||||
);''')
|
||||
c.execute('''create table if not exists user_download(user_id int,
|
||||
token text,
|
||||
time int,
|
||||
primary key(user_id, token, time)
|
||||
);''')
|
||||
c.execute('''create table if not exists item(item_id text,
|
||||
type text,
|
||||
is_available int,
|
||||
price int,
|
||||
orig_price int,
|
||||
discount_from int,
|
||||
discount_to int,
|
||||
_id text,
|
||||
primary key(item_id, type)
|
||||
);''')
|
||||
c.execute('''create table if not exists user_item(user_id int,
|
||||
item_id text,
|
||||
type text,
|
||||
primary key(user_id, item_id, type)
|
||||
);''')
|
||||
c.execute('''create table if not exists user_save(user_id int primary key,
|
||||
scores_data text,
|
||||
clearlamps_data text,
|
||||
clearedsongs_data text,
|
||||
unlocklist_data text,
|
||||
installid_data text,
|
||||
devicemodelname_data text,
|
||||
story_data text,
|
||||
createdAt int
|
||||
);''')
|
||||
c.execute('''create table if not exists present(present_id text primary key,
|
||||
expire_ts int,
|
||||
items text,
|
||||
description text
|
||||
);''')
|
||||
c.execute('''create table if not exists user_present(user_id int,
|
||||
present_id text,
|
||||
primary key(user_id, present_id)
|
||||
);''')
|
||||
c.execute('''create table if not exists songfile(song_id text,
|
||||
file_type int,
|
||||
md5 text,
|
||||
primary key(song_id, file_type)
|
||||
);''')
|
||||
c.execute('''create table if not exists redeem(code text primary key,
|
||||
items text,
|
||||
type int
|
||||
);''')
|
||||
c.execute('''create table if not exists user_redeem(user_id int,
|
||||
code text,
|
||||
primary key(user_id, code)
|
||||
);''')
|
||||
ARCAEA_SERVER_VERSION = 'v2.4.0'
|
||||
|
||||
|
||||
char = ['Hikari', 'Tairitsu', 'Kou', 'Sapphire', 'Lethe', '', 'Tairitsu(Axium)', 'Tairitsu(Grievous Lady)', 'Stella', 'Hikari & Fisica', 'Ilith', 'Eto', 'Luna', 'Shirabe', 'Hikari(Zero)', 'Hikari(Fracture)', 'Hikari(Summer)', 'Tairitsu(Summer)', 'Tairitsu & Trin',
|
||||
'Ayu', 'Eto & Luna', 'Yume', 'Seine & Hikari', 'Saya', 'Tairitsu & Chuni Penguin', 'Chuni Penguin', 'Haruna', 'Nono', 'MTA-XXX', 'MDA-21', 'Kanae', 'Hikari(Fantasia)', 'Tairitsu(Sonata)', 'Sia', 'DORO*C', 'Tairitsu(Tempest)', 'Brillante', 'Ilith(Summer)', 'Etude', 'Alice & Tenniel', 'Luna & Mia', 'Areus', 'Seele', 'Isabelle', 'Mir']
|
||||
|
||||
skill_id = ['gauge_easy', '', '', '', 'note_mirror', '', '', 'gauge_hard', 'frag_plus_10_pack_stellights', 'gauge_easy|frag_plus_15_pst&prs', 'gauge_hard|fail_frag_minus_100', 'frag_plus_5_side_light', 'visual_hide_hp', 'frag_plus_5_side_conflict', 'challenge_fullcombo_0gauge', 'gauge_overflow', 'gauge_easy|note_mirror', 'note_mirror', 'visual_tomato_pack_tonesphere',
|
||||
'frag_rng_ayu', 'gaugestart_30|gaugegain_70', 'combo_100-frag_1', 'audio_gcemptyhit_pack_groovecoaster', 'gauge_saya', 'gauge_chuni', 'kantandeshou', 'gauge_haruna', 'frags_nono', 'gauge_pandora', 'gauge_regulus', 'omatsuri_daynight', '', '', 'sometimes(note_mirror|frag_plus_5)', 'scoreclear_aa|visual_scoregauge', 'gauge_tempest', 'gauge_hard', 'gauge_ilith_summer', '', 'note_mirror|visual_hide_far', 'frags_ongeki', 'gauge_areus', 'gauge_seele', 'gauge_isabelle', 'gauge_exhaustion']
|
||||
|
||||
skill_id_uncap = ['', '', 'frags_kou', '', 'visual_ink', '', '', '', '', '', '', '', '', 'shirabe_entry_fee',
|
||||
'', '', '', '', '', '', '', 'frags_yume', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '']
|
||||
|
||||
for i in range(0, 45):
|
||||
if i in [0, 1, 2, 4, 13, 26, 27, 28, 29, 36, 21, 42, 43]:
|
||||
sql = 'insert into character values('+str(
|
||||
i)+',"'+char[i]+'''",30,25000,25000,90,90,90,"'''+skill_id[i]+'''",0,0,"'''+skill_id_uncap[i]+'''",0,'',1,1)'''
|
||||
c.execute(sql)
|
||||
else:
|
||||
if i != 5:
|
||||
sql = 'insert into character values('+str(
|
||||
i)+',"'+char[i]+'''",30,25000,25000,90,90,90,"'''+skill_id[i]+'''",0,0,"'''+skill_id_uncap[i]+'''",0,'',0,0)'''
|
||||
c.execute(sql)
|
||||
|
||||
|
||||
def b2int(x):
|
||||
# int与布尔值转换
|
||||
if x:
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
|
||||
|
||||
def insert_items(c, items):
|
||||
# 物品数据导入
|
||||
for i in items:
|
||||
if 'discount_from' not in i:
|
||||
discount_from = -1
|
||||
else:
|
||||
discount_from = i['discount_from']
|
||||
if 'discount_to' not in i:
|
||||
discount_to = -1
|
||||
else:
|
||||
discount_to = i['discount_to']
|
||||
for j in i['items']:
|
||||
if "_id" not in j:
|
||||
_id = ''
|
||||
else:
|
||||
_id = j['_id']
|
||||
if j['type'] != 'character':
|
||||
c.execute('''insert into item(item_id, type, is_available, price, orig_price, discount_from, discount_to, _id) values(:a,:b,:c,:d,:e,:f,:g,:h)''', {
|
||||
'a': j['id'], 'b': j['type'], 'c': b2int(j['is_available']), 'd': i['price'], 'e': i['orig_price'], 'f': discount_from, 'g': discount_to, 'h': _id})
|
||||
|
||||
|
||||
f = open('singles.json', 'r')
|
||||
singles = json.load(f)
|
||||
f.close()
|
||||
insert_items(c, singles)
|
||||
|
||||
f = open('packs.json', 'r')
|
||||
packs = json.load(f)
|
||||
f.close()
|
||||
insert_items(c, packs)
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
|
||||
def arc_register(name: str, password: str):
|
||||
def build_user_code(c):
|
||||
return '123456789'
|
||||
|
||||
def build_user_id(c):
|
||||
return 2000000
|
||||
|
||||
# def insert_user_char(c, user_id):
|
||||
# for i in range(0, 38):
|
||||
# if i in [0, 1, 2, 4, 13, 26, 27, 28, 29, 36, 21]:
|
||||
# sql = 'insert into user_char values('+str(user_id)+','+str(
|
||||
# i)+''',30,25000,25000,90,90,90,'',0,0,'',0,1,1)'''
|
||||
# c.execute(sql)
|
||||
# else:
|
||||
# if i != 5:
|
||||
# sql = 'insert into user_char values('+str(user_id)+','+str(
|
||||
# i)+''',30,25000,25000,90,90,90,'',0,0,'',0,0,0)'''
|
||||
# c.execute(sql)
|
||||
def insert_user_char(c, user_id):
|
||||
# 为用户添加所有可用角色
|
||||
c.execute('''select * from character''')
|
||||
x = c.fetchall()
|
||||
if x != []:
|
||||
for i in x:
|
||||
c.execute('''insert into user_char values(:a,:b,:c,:d,:e,:f,:g,:h,:i,:j,:k,:l,:m,:n,:o)''', {
|
||||
'a': user_id, 'b': i[0], 'c': i[2], 'd': i[3], 'e': i[4], 'f': i[5], 'g': i[6], 'h': i[7], 'i': i[8], 'j': i[9], 'k': i[10], 'l': i[11], 'm': i[12], 'n': i[14], 'o': i[15]})
|
||||
|
||||
conn = sqlite3.connect('arcaea_database.db')
|
||||
def main(path='./'):
|
||||
conn = sqlite3.connect(path+'arcaea_database.db')
|
||||
c = conn.cursor()
|
||||
hash_pwd = hashlib.sha256(password.encode("utf8")).hexdigest()
|
||||
c.execute(
|
||||
'''select exists(select * from user where name = :name)''', {'name': name})
|
||||
if c.fetchone() == (0,):
|
||||
user_code = build_user_code(c)
|
||||
user_id = build_user_id(c)
|
||||
now = int(time.time() * 1000)
|
||||
c.execute('''insert into user(user_id, name, password, join_date, user_code, rating_ptt,
|
||||
character_id, is_skill_sealed, is_char_uncapped, is_char_uncapped_override, is_hide_rating, favorite_character, max_stamina_notification_enabled, current_map, ticket)
|
||||
values(:user_id, :name, :password, :join_date, :user_code, 1250, 1, 0, 1, 0, 0, -1, 0, '', 114514)
|
||||
''', {'user_code': user_code, 'user_id': user_id, 'join_date': now, 'name': name, 'password': hash_pwd})
|
||||
c.execute('''insert into recent30(user_id) values(:user_id)''', {
|
||||
'user_id': user_id})
|
||||
c.execute('''create table if not exists config(id text primary key,
|
||||
value text
|
||||
);''')
|
||||
c.execute('''insert into config values("version", :a);''',
|
||||
{'a': ARCAEA_SERVER_VERSION})
|
||||
c.execute('''create table if not exists user(user_id int primary key,
|
||||
name text unique,
|
||||
password text not null,
|
||||
join_date char(20),
|
||||
user_code char(10),
|
||||
rating_ptt int,
|
||||
character_id int,
|
||||
is_skill_sealed int,
|
||||
is_char_uncapped int,
|
||||
is_char_uncapped_override int,
|
||||
is_hide_rating int,
|
||||
song_id text,
|
||||
difficulty int,
|
||||
score int,
|
||||
shiny_perfect_count int,
|
||||
perfect_count int,
|
||||
near_count int,
|
||||
miss_count int,
|
||||
health int,
|
||||
modifier int,
|
||||
time_played int,
|
||||
clear_type int,
|
||||
rating real,
|
||||
favorite_character int,
|
||||
max_stamina_notification_enabled int,
|
||||
current_map text,
|
||||
ticket int,
|
||||
prog_boost int,
|
||||
email text
|
||||
);''')
|
||||
c.execute('''create table if not exists login(access_token text,
|
||||
user_id int,
|
||||
login_time int,
|
||||
login_ip text,
|
||||
login_device text,
|
||||
primary key(access_token, user_id)
|
||||
);''')
|
||||
c.execute('''create table if not exists friend(user_id_me int,
|
||||
user_id_other int,
|
||||
primary key (user_id_me, user_id_other)
|
||||
);''')
|
||||
c.execute('''create table if not exists best_score(user_id int,
|
||||
song_id text,
|
||||
difficulty int,
|
||||
score int,
|
||||
shiny_perfect_count int,
|
||||
perfect_count int,
|
||||
near_count int,
|
||||
miss_count int,
|
||||
health int,
|
||||
modifier int,
|
||||
time_played int,
|
||||
best_clear_type int,
|
||||
clear_type int,
|
||||
rating real,
|
||||
primary key(user_id, song_id, difficulty)
|
||||
);''')
|
||||
c.execute('''create table if not exists user_char(user_id int,
|
||||
character_id int,
|
||||
level int,
|
||||
exp real,
|
||||
level_exp real,
|
||||
frag real,
|
||||
prog real,
|
||||
overdrive real,
|
||||
skill_id text,
|
||||
skill_unlock_level int,
|
||||
skill_requires_uncap int,
|
||||
skill_id_uncap text,
|
||||
char_type int,
|
||||
is_uncapped int,
|
||||
is_uncapped_override int,
|
||||
primary key(user_id, character_id)
|
||||
);''')
|
||||
c.execute('''create table if not exists character(character_id int primary key,
|
||||
name text,
|
||||
level int,
|
||||
exp real,
|
||||
level_exp real,
|
||||
frag real,
|
||||
prog real,
|
||||
overdrive real,
|
||||
skill_id text,
|
||||
skill_unlock_level int,
|
||||
skill_requires_uncap int,
|
||||
skill_id_uncap text,
|
||||
char_type int,
|
||||
uncap_cores text,
|
||||
is_uncapped int,
|
||||
is_uncapped_override int
|
||||
);''')
|
||||
c.execute('''create table if not exists recent30(user_id int primary key,
|
||||
r0 real,
|
||||
song_id0 text,
|
||||
r1 real,
|
||||
song_id1 text,
|
||||
r2 real,
|
||||
song_id2 text,
|
||||
r3 real,
|
||||
song_id3 text,
|
||||
r4 real,
|
||||
song_id4 text,
|
||||
r5 real,
|
||||
song_id5 text,
|
||||
r6 real,
|
||||
song_id6 text,
|
||||
r7 real,
|
||||
song_id7 text,
|
||||
r8 real,
|
||||
song_id8 text,
|
||||
r9 real,
|
||||
song_id9 text,
|
||||
r10 real,
|
||||
song_id10 text,
|
||||
r11 real,
|
||||
song_id11 text,
|
||||
r12 real,
|
||||
song_id12 text,
|
||||
r13 real,
|
||||
song_id13 text,
|
||||
r14 real,
|
||||
song_id14 text,
|
||||
r15 real,
|
||||
song_id15 text,
|
||||
r16 real,
|
||||
song_id16 text,
|
||||
r17 real,
|
||||
song_id17 text,
|
||||
r18 real,
|
||||
song_id18 text,
|
||||
r19 real,
|
||||
song_id19 text,
|
||||
r20 real,
|
||||
song_id20 text,
|
||||
r21 real,
|
||||
song_id21 text,
|
||||
r22 real,
|
||||
song_id22 text,
|
||||
r23 real,
|
||||
song_id23 text,
|
||||
r24 real,
|
||||
song_id24 text,
|
||||
r25 real,
|
||||
song_id25 text,
|
||||
r26 real,
|
||||
song_id26 text,
|
||||
r27 real,
|
||||
song_id27 text,
|
||||
r28 real,
|
||||
song_id28 text,
|
||||
r29 real,
|
||||
song_id29 text
|
||||
);''')
|
||||
c.execute('''create table if not exists user_world(user_id int,
|
||||
map_id text,
|
||||
curr_position int,
|
||||
curr_capture real,
|
||||
is_locked int,
|
||||
primary key(user_id, map_id)
|
||||
);''')
|
||||
c.execute('''create table if not exists world_songplay(user_id int,
|
||||
song_id text,
|
||||
difficulty int,
|
||||
stamina_multiply int,
|
||||
fragment_multiply int,
|
||||
prog_boost_multiply int,
|
||||
primary key(user_id, song_id, difficulty)
|
||||
);''')
|
||||
c.execute('''create table if not exists download_token(user_id int,
|
||||
song_id text,
|
||||
file_name text,
|
||||
token text,
|
||||
time int,
|
||||
primary key(user_id, song_id, file_name)
|
||||
);''')
|
||||
c.execute('''create table if not exists user_download(user_id int,
|
||||
token text,
|
||||
time int,
|
||||
primary key(user_id, token, time)
|
||||
);''')
|
||||
c.execute('''create table if not exists item(item_id text,
|
||||
type text,
|
||||
is_available int,
|
||||
price int,
|
||||
orig_price int,
|
||||
discount_from int,
|
||||
discount_to int,
|
||||
_id text,
|
||||
primary key(item_id, type)
|
||||
);''')
|
||||
c.execute('''create table if not exists user_item(user_id int,
|
||||
item_id text,
|
||||
type text,
|
||||
primary key(user_id, item_id, type)
|
||||
);''')
|
||||
c.execute('''create table if not exists user_save(user_id int primary key,
|
||||
scores_data text,
|
||||
clearlamps_data text,
|
||||
clearedsongs_data text,
|
||||
unlocklist_data text,
|
||||
installid_data text,
|
||||
devicemodelname_data text,
|
||||
story_data text,
|
||||
createdAt int
|
||||
);''')
|
||||
c.execute('''create table if not exists present(present_id text primary key,
|
||||
expire_ts int,
|
||||
items text,
|
||||
description text
|
||||
);''')
|
||||
c.execute('''create table if not exists user_present(user_id int,
|
||||
present_id text,
|
||||
primary key(user_id, present_id)
|
||||
);''')
|
||||
c.execute('''create table if not exists songfile(song_id text,
|
||||
file_type int,
|
||||
md5 text,
|
||||
primary key(song_id, file_type)
|
||||
);''')
|
||||
c.execute('''create table if not exists redeem(code text primary key,
|
||||
items text,
|
||||
type int
|
||||
);''')
|
||||
c.execute('''create table if not exists user_redeem(user_id int,
|
||||
code text,
|
||||
primary key(user_id, code)
|
||||
);''')
|
||||
|
||||
c.execute('''create table if not exists role(role_id int primary key,
|
||||
role_name text,
|
||||
caption text
|
||||
);''')
|
||||
c.execute('''create table if not exists user_role(user_id int,
|
||||
role_id int,
|
||||
primary key(user_id, role_id)
|
||||
);''')
|
||||
c.execute('''create table if not exists power(power_id int primary key,
|
||||
power_name text,
|
||||
caption text
|
||||
);''')
|
||||
c.execute('''create table if not exists role_power(role_id int,
|
||||
power_id int,
|
||||
primary key(role_id, power_id)
|
||||
);''')
|
||||
c.execute('''create table if not exists api_login(user_id int,
|
||||
token text,
|
||||
login_time int,
|
||||
login_ip text,
|
||||
primary key(user_id, token)
|
||||
);''')
|
||||
|
||||
# 搭档初始化
|
||||
char = ['Hikari', 'Tairitsu', 'Kou', 'Sapphire', 'Lethe', '', 'Tairitsu(Axium)', 'Tairitsu(Grievous Lady)', 'Stella', 'Hikari & Fisica', 'Ilith', 'Eto', 'Luna', 'Shirabe', 'Hikari(Zero)', 'Hikari(Fracture)', 'Hikari(Summer)', 'Tairitsu(Summer)', 'Tairitsu & Trin',
|
||||
'Ayu', 'Eto & Luna', 'Yume', 'Seine & Hikari', 'Saya', 'Tairitsu & Chuni Penguin', 'Chuni Penguin', 'Haruna', 'Nono', 'MTA-XXX', 'MDA-21', 'Kanae', 'Hikari(Fantasia)', 'Tairitsu(Sonata)', 'Sia', 'DORO*C', 'Tairitsu(Tempest)', 'Brillante', 'Ilith(Summer)', 'Etude', 'Alice & Tenniel', 'Luna & Mia', 'Areus', 'Seele', 'Isabelle', 'Mir', 'Lagrange']
|
||||
|
||||
skill_id = ['gauge_easy', '', '', '', 'note_mirror', '', '', 'gauge_hard', 'frag_plus_10_pack_stellights', 'gauge_easy|frag_plus_15_pst&prs', 'gauge_hard|fail_frag_minus_100', 'frag_plus_5_side_light', 'visual_hide_hp', 'frag_plus_5_side_conflict', 'challenge_fullcombo_0gauge', 'gauge_overflow', 'gauge_easy|note_mirror', 'note_mirror', 'visual_tomato_pack_tonesphere',
|
||||
'frag_rng_ayu', 'gaugestart_30|gaugegain_70', 'combo_100-frag_1', 'audio_gcemptyhit_pack_groovecoaster', 'gauge_saya', 'gauge_chuni', 'kantandeshou', 'gauge_haruna', 'frags_nono', 'gauge_pandora', 'gauge_regulus', 'omatsuri_daynight', '', '', 'sometimes(note_mirror|frag_plus_5)', 'scoreclear_aa|visual_scoregauge', 'gauge_tempest', 'gauge_hard', 'gauge_ilith_summer', '', 'note_mirror|visual_hide_far', 'frags_ongeki', 'gauge_areus', 'gauge_seele', 'gauge_isabelle', 'gauge_exhaustion', 'skill_lagrange']
|
||||
|
||||
skill_id_uncap = ['', '', 'frags_kou', '', 'visual_ink', '', '', '', '', '', '', '', '', 'shirabe_entry_fee',
|
||||
'', '', '', '', '', '', '', 'frags_yume', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '']
|
||||
|
||||
frag = [88, 90, 100, 75, 80, 0, 70, 79, 65, 40, 50, 80, 90, 92, 0, 61, 67, 92, 85, 50, 86, 62,
|
||||
65, 85, 67, 88, 74, 0.5, 105, 80, 95, 50, 80, 87, 71, 50, 95, 0, 80, 75, 50, 70, 80, 100, 65, 80]
|
||||
|
||||
prog = [71, 90, 80, 75, 100, 0, 90, 102, 84, 78, 105, 67, 63, 78, 0, 99, 80, 66, 46, 83, 40, 83,
|
||||
80, 90, 93, 50, 96, 88, 99, 108, 75, 80, 50, 64, 55, 100, 100, 110, 80, 50, 74, 90, 80, 80, 56, 80]
|
||||
|
||||
overdrive = [71, 90, 57, 75, 80, 0, 95, 79, 65, 31, 50, 59, 90, 68, 0, 78, 50, 70, 62, 49, 64,
|
||||
56, 73, 95, 67, 84, 80, 88, 79, 80, 50, 80, 80, 63, 25, 50, 82, 55, 50, 95, 55, 70, 100, 80, 90, 80]
|
||||
|
||||
char_type = [1, 0, 0, 0, 0, 0, 0, 2, 0, 1, 2, 0, 0, 0, 2, 3, 1, 0, 0, 0, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 2, 2, 2, 1, 0]
|
||||
|
||||
# print(len(char))
|
||||
# print(len(skill_id))
|
||||
# print(len(skill_id_uncap))
|
||||
# print(len(frag))
|
||||
# print(len(prog))
|
||||
# print(len(overdrive))
|
||||
# print(len(char_type))
|
||||
|
||||
for i in range(0, 46):
|
||||
if i in [0, 1, 2, 4, 13, 26, 27, 28, 29, 36, 21, 42, 43]:
|
||||
sql = '''insert into character values(:a,:b,30,25000,25000,:c,:d,:e,:f,0,0,:g,:h,'',1,1)'''
|
||||
c.execute(sql, {'a': i, 'b': char[i], 'c': frag[i], 'd': prog[i],
|
||||
'e': overdrive[i], 'f': skill_id[i], 'g': skill_id_uncap[i], 'h': char_type[i]})
|
||||
else:
|
||||
if i != 5:
|
||||
sql = '''insert into character values(:a,:b,20,10000,10000,:c,:d,:e,:f,0,0,:g,:h,'',0,0)'''
|
||||
c.execute(sql, {'a': i, 'b': char[i], 'c': frag[i], 'd': prog[i],
|
||||
'e': overdrive[i], 'f': skill_id[i], 'g': skill_id_uncap[i], 'h': char_type[i]})
|
||||
|
||||
def b2int(x):
|
||||
# int与布尔值转换
|
||||
if x:
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
|
||||
def insert_items(c, items):
|
||||
# 物品数据导入
|
||||
for i in items:
|
||||
if 'discount_from' not in i:
|
||||
discount_from = -1
|
||||
else:
|
||||
discount_from = i['discount_from']
|
||||
if 'discount_to' not in i:
|
||||
discount_to = -1
|
||||
else:
|
||||
discount_to = i['discount_to']
|
||||
for j in i['items']:
|
||||
if "_id" not in j:
|
||||
_id = ''
|
||||
else:
|
||||
_id = j['_id']
|
||||
if j['type'] != 'character':
|
||||
c.execute('''insert into item(item_id, type, is_available, price, orig_price, discount_from, discount_to, _id) values(:a,:b,:c,:d,:e,:f,:g,:h)''', {
|
||||
'a': j['id'], 'b': j['type'], 'c': b2int(j['is_available']), 'd': i['price'], 'e': i['orig_price'], 'f': discount_from, 'g': discount_to, 'h': _id})
|
||||
|
||||
# item初始化
|
||||
f = open(path+'singles.json', 'r')
|
||||
singles = json.load(f)
|
||||
f.close()
|
||||
insert_items(c, singles)
|
||||
|
||||
f = open(path+'packs.json', 'r')
|
||||
packs = json.load(f)
|
||||
f.close()
|
||||
insert_items(c, packs)
|
||||
|
||||
# api权限与权限组初始化
|
||||
role = ['admin', 'user', 'selecter']
|
||||
role_caption = ['管理员', '用户', '查询接口']
|
||||
|
||||
power = ['select', 'select_me', 'change', 'change_me', 'grant',
|
||||
'grant_inf', 'select_song_rank', 'select_song_info']
|
||||
power_caption = ['总体查询权限', '自我查询权限', '总体修改权限',
|
||||
'自我修改权限', '授权权限', '下级授权权限', '歌曲排行榜查询权限', '歌曲信息查询权限']
|
||||
|
||||
role_power = {'0': [0, 1, 3, 5, 6, 7],
|
||||
'1': [1, 3, 6, 7],
|
||||
'2': [0]
|
||||
}
|
||||
|
||||
for i in range(0, len(role)):
|
||||
c.execute('''insert into role values(:a,:b,:c)''', {
|
||||
'a': i, 'b': role[i], 'c': role_caption[i]})
|
||||
for i in range(0, len(power)):
|
||||
c.execute('''insert into power values(:a,:b,:c)''', {
|
||||
'a': i, 'b': power[i], 'c': power_caption[i]})
|
||||
for i in role_power:
|
||||
for j in role_power[i]:
|
||||
c.execute('''insert into role_power values(:a,:b)''',
|
||||
{'a': int(i), 'b': j})
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
def arc_register(name: str, password: str):
|
||||
def build_user_code(c):
|
||||
return '123456789'
|
||||
|
||||
def build_user_id(c):
|
||||
return 2000000
|
||||
|
||||
# def insert_user_char(c, user_id):
|
||||
# for i in range(0, 38):
|
||||
# if i in [0, 1, 2, 4, 13, 26, 27, 28, 29, 36, 21]:
|
||||
# sql = 'insert into user_char values('+str(user_id)+','+str(
|
||||
# i)+''',30,25000,25000,90,90,90,'',0,0,'',0,1,1)'''
|
||||
# c.execute(sql)
|
||||
# else:
|
||||
# if i != 5:
|
||||
# sql = 'insert into user_char values('+str(user_id)+','+str(
|
||||
# i)+''',30,25000,25000,90,90,90,'',0,0,'',0,0,0)'''
|
||||
# c.execute(sql)
|
||||
def insert_user_char(c, user_id):
|
||||
# 为用户添加所有可用角色
|
||||
c.execute('''select * from character''')
|
||||
x = c.fetchall()
|
||||
if x != []:
|
||||
for i in x:
|
||||
c.execute('''insert into user_char values(:a,:b,:c,:d,:e,:f,:g,:h,:i,:j,:k,:l,:m,:n,:o)''', {
|
||||
'a': user_id, 'b': i[0], 'c': i[2], 'd': i[3], 'e': i[4], 'f': i[5], 'g': i[6], 'h': i[7], 'i': i[8], 'j': i[9], 'k': i[10], 'l': i[11], 'm': i[12], 'n': i[14], 'o': i[15]})
|
||||
|
||||
conn = sqlite3.connect(path+'arcaea_database.db')
|
||||
c = conn.cursor()
|
||||
hash_pwd = hashlib.sha256(password.encode("utf8")).hexdigest()
|
||||
c.execute(
|
||||
'''insert into best_score values(2000000,'vexaria',3,10000000,100,0,0,0,100,0,1599667200,3,3,10.8)''')
|
||||
insert_user_char(c, user_id)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return None
|
||||
else:
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return None
|
||||
'''select exists(select * from user where name = :name)''', {'name': name})
|
||||
if c.fetchone() == (0,):
|
||||
user_code = build_user_code(c)
|
||||
user_id = build_user_id(c)
|
||||
now = int(time.time() * 1000)
|
||||
c.execute('''insert into user(user_id, name, password, join_date, user_code, rating_ptt,
|
||||
character_id, is_skill_sealed, is_char_uncapped, is_char_uncapped_override, is_hide_rating, favorite_character, max_stamina_notification_enabled, current_map, ticket)
|
||||
values(:user_id, :name, :password, :join_date, :user_code, 1250, 1, 0, 1, 0, 0, -1, 0, '', 114514)
|
||||
''', {'user_code': user_code, 'user_id': user_id, 'join_date': now, 'name': name, 'password': hash_pwd})
|
||||
c.execute('''insert into recent30(user_id) values(:user_id)''', {
|
||||
'user_id': user_id})
|
||||
c.execute(
|
||||
'''insert into best_score values(2000000,'vexaria',3,10000000,100,0,0,0,100,0,1599667200,3,3,10.8)''')
|
||||
insert_user_char(c, user_id)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return None
|
||||
else:
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return None
|
||||
|
||||
arc_register('admin', 'admin123')
|
||||
|
||||
|
||||
arc_register('admin', 'admin123')
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
78
latest version/database/map/byd_lostcivilization.json
Normal file
78
latest version/database/map/byd_lostcivilization.json
Normal file
@@ -0,0 +1,78 @@
|
||||
{
|
||||
"map_id": "byd_lostcivilization",
|
||||
"is_legacy": false,
|
||||
"chapter": 1001,
|
||||
"available_from": -1,
|
||||
"available_to": 9999999999999,
|
||||
"is_repeatable": false,
|
||||
"require_id": "lostcivilization2",
|
||||
"require_type": "chart_unlock",
|
||||
"coordinate": "-650,-900",
|
||||
"is_beyond": true,
|
||||
"stamina_cost": 3,
|
||||
"beyond_health": 250,
|
||||
"character_affinity": [0, 1],
|
||||
"affinity_multiplier": [1.9, 1.9],
|
||||
"step_count": 7,
|
||||
"custom_bg": "",
|
||||
"curr_position": 0,
|
||||
"curr_capture": 0,
|
||||
"is_locked": false,
|
||||
"steps": [{
|
||||
"map_id": "byd_lostcivilization",
|
||||
"position": 0,
|
||||
"capture": 85
|
||||
}, {
|
||||
"map_id": "byd_lostcivilization",
|
||||
"position": 1,
|
||||
"capture": 5,
|
||||
"items": [{
|
||||
"type": "core",
|
||||
"id": "core_generic",
|
||||
"amount": 1
|
||||
}]
|
||||
}, {
|
||||
"map_id": "byd_lostcivilization",
|
||||
"position": 2,
|
||||
"capture": 5,
|
||||
"items": [{
|
||||
"type": "core",
|
||||
"id": "core_generic",
|
||||
"amount": 1
|
||||
}]
|
||||
}, {
|
||||
"map_id": "byd_lostcivilization",
|
||||
"position": 3,
|
||||
"capture": 5,
|
||||
"items": [{
|
||||
"type": "core",
|
||||
"id": "core_generic",
|
||||
"amount": 1
|
||||
}]
|
||||
}, {
|
||||
"map_id": "byd_lostcivilization",
|
||||
"position": 4,
|
||||
"capture": 100,
|
||||
"items": [{
|
||||
"type": "core",
|
||||
"id": "core_generic",
|
||||
"amount": 1
|
||||
}]
|
||||
}, {
|
||||
"map_id": "byd_lostcivilization",
|
||||
"position": 5,
|
||||
"capture": 50,
|
||||
"items": [{
|
||||
"type": "fragment",
|
||||
"amount": 525
|
||||
}]
|
||||
}, {
|
||||
"map_id": "byd_lostcivilization",
|
||||
"position": 6,
|
||||
"capture": 0,
|
||||
"items": [{
|
||||
"id": "lostcivilization3",
|
||||
"type": "world_song"
|
||||
}]
|
||||
}]
|
||||
}
|
||||
@@ -3,10 +3,9 @@
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "core",
|
||||
"_id": "6046bab736651a07ebc537db",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 400,
|
||||
"price": 500,
|
||||
"orig_price": 500,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
@@ -15,15 +14,13 @@
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "shiawase",
|
||||
"_id": "6046bab736651a07ebc53823",
|
||||
"is_available": true
|
||||
}, {
|
||||
"type": "character",
|
||||
"id": "kou",
|
||||
"_id": "6046bab736651a07ebc53822",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 250,
|
||||
"price": 500,
|
||||
"orig_price": 500,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
@@ -32,15 +29,13 @@
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "dynamix",
|
||||
"_id": "6046bab736651a07ebc53827",
|
||||
"is_available": true
|
||||
}, {
|
||||
"type": "character",
|
||||
"id": "sapphire",
|
||||
"_id": "6046bab736651a07ebc53826",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 250,
|
||||
"price": 500,
|
||||
"orig_price": 500,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
@@ -49,15 +44,13 @@
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "mirai",
|
||||
"_id": "6046bab736651a07ebc5381d",
|
||||
"is_available": true
|
||||
}, {
|
||||
"type": "character",
|
||||
"id": "lethe",
|
||||
"_id": "6046bab736651a07ebc5381c",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 250,
|
||||
"price": 500,
|
||||
"orig_price": 500,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
@@ -66,10 +59,9 @@
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "yugamu",
|
||||
"_id": "6046bab736651a07ebc537dc",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 400,
|
||||
"price": 500,
|
||||
"orig_price": 500,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
@@ -78,10 +70,9 @@
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "lanota",
|
||||
"_id": "6046bab736651a07ebc537e8",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 250,
|
||||
"price": 500,
|
||||
"orig_price": 500,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
@@ -90,10 +81,9 @@
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "nijuusei",
|
||||
"_id": "6046bab736651a07ebc537dd",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 250,
|
||||
"price": 500,
|
||||
"orig_price": 500,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
@@ -102,10 +92,9 @@
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "rei",
|
||||
"_id": "6046bab736651a07ebc537f6",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 400,
|
||||
"price": 500,
|
||||
"orig_price": 500,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
@@ -114,10 +103,9 @@
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "tonesphere",
|
||||
"_id": "6046bab736651a07ebc537ea",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 250,
|
||||
"price": 500,
|
||||
"orig_price": 500,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
@@ -126,10 +114,9 @@
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "groovecoaster",
|
||||
"_id": "6046bab736651a07ebc53812",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 250,
|
||||
"price": 500,
|
||||
"orig_price": 500,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
@@ -138,10 +125,9 @@
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "zettai",
|
||||
"_id": "6046bab736651a07ebc537f8",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 250,
|
||||
"price": 500,
|
||||
"orig_price": 500,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
@@ -150,7 +136,6 @@
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "chunithm",
|
||||
"_id": "6046bab736651a07ebc53806",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 300,
|
||||
@@ -160,10 +145,9 @@
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "prelude",
|
||||
"_id": "6046bab736651a07ebc53813",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 250,
|
||||
"price": 400,
|
||||
"orig_price": 400,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
@@ -172,10 +156,9 @@
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "omatsuri",
|
||||
"_id": "6046bab736651a07ebc537f9",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 250,
|
||||
"price": 500,
|
||||
"orig_price": 500,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
@@ -184,10 +167,9 @@
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "vs",
|
||||
"_id": "6046bab736651a07ebc53809",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 400,
|
||||
"price": 500,
|
||||
"orig_price": 500,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
@@ -196,10 +178,9 @@
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "extend",
|
||||
"_id": "6046bab736651a07ebc53816",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 500,
|
||||
"price": 700,
|
||||
"orig_price": 700,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
@@ -208,7 +189,6 @@
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "alice",
|
||||
"_id": "6046bab736651a07ebc537fe",
|
||||
"is_available": true
|
||||
}],
|
||||
"orig_price": 500,
|
||||
@@ -218,7 +198,6 @@
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "alice_append_1",
|
||||
"_id": "6046bab736651a07ebc537e6",
|
||||
"is_available": true
|
||||
}],
|
||||
"orig_price": 300,
|
||||
@@ -228,7 +207,6 @@
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "ongeki",
|
||||
"_id": "6046bab736651a07ebc5380c",
|
||||
"is_available": true
|
||||
}],
|
||||
"orig_price": 400,
|
||||
@@ -238,7 +216,6 @@
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "maimai",
|
||||
"_id": "6046bab736651a07ebc53819",
|
||||
"is_available": true
|
||||
}],
|
||||
"orig_price": 400,
|
||||
@@ -248,9 +225,26 @@
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "chunithm_append_1",
|
||||
"_id": "6046bab736651a07ebc537f2",
|
||||
"is_available": true
|
||||
}],
|
||||
"orig_price": 300,
|
||||
"price": 300
|
||||
}, {
|
||||
"name": "observer_append_1",
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "observer_append_1",
|
||||
"is_available": true
|
||||
}],
|
||||
"orig_price": 300,
|
||||
"price": 300
|
||||
}, {
|
||||
"name": "observer",
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "observer",
|
||||
"is_available": true
|
||||
}],
|
||||
"orig_price": 500,
|
||||
"price": 500
|
||||
}]
|
||||
@@ -12,10 +12,9 @@
|
||||
"items": [{
|
||||
"id": "dataerror",
|
||||
"type": "single",
|
||||
"_id": "6046bab736651a07ebc537f3",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 50,
|
||||
"price": 100,
|
||||
"orig_price": 100,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
@@ -24,10 +23,9 @@
|
||||
"items": [{
|
||||
"id": "yourvoiceso",
|
||||
"type": "single",
|
||||
"_id": "6046bab736651a07ebc53800",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 50,
|
||||
"price": 100,
|
||||
"orig_price": 100,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
@@ -36,10 +34,9 @@
|
||||
"items": [{
|
||||
"id": "crosssoul",
|
||||
"type": "single",
|
||||
"_id": "6046bab736651a07ebc5380d",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 50,
|
||||
"price": 100,
|
||||
"orig_price": 100,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
@@ -48,10 +45,9 @@
|
||||
"items": [{
|
||||
"type": "single",
|
||||
"id": "impurebird",
|
||||
"_id": "6046bab736651a07ebc53801",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 50,
|
||||
"price": 100,
|
||||
"orig_price": 100,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
@@ -60,7 +56,6 @@
|
||||
"items": [{
|
||||
"type": "single",
|
||||
"id": "auxesia",
|
||||
"_id": "6046bab736651a07ebc5380e",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 100,
|
||||
@@ -72,10 +67,9 @@
|
||||
"items": [{
|
||||
"type": "single",
|
||||
"id": "modelista",
|
||||
"_id": "6046bab736651a07ebc537f4",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 50,
|
||||
"price": 100,
|
||||
"orig_price": 100,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
@@ -84,10 +78,9 @@
|
||||
"items": [{
|
||||
"type": "single",
|
||||
"id": "yozakurafubuki",
|
||||
"_id": "6046bab736651a07ebc53802",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 50,
|
||||
"price": 100,
|
||||
"orig_price": 100,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
@@ -96,10 +89,9 @@
|
||||
"items": [{
|
||||
"type": "single",
|
||||
"id": "surrender",
|
||||
"_id": "6046bab736651a07ebc5380f",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 50,
|
||||
"price": 100,
|
||||
"orig_price": 100,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
@@ -108,7 +100,6 @@
|
||||
"items": [{
|
||||
"type": "single",
|
||||
"id": "metallicpunisher",
|
||||
"_id": "6046bab736651a07ebc537f5",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 100,
|
||||
@@ -118,7 +109,6 @@
|
||||
"items": [{
|
||||
"type": "single",
|
||||
"id": "carminescythe",
|
||||
"_id": "6046bab736651a07ebc53803",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 100,
|
||||
@@ -128,10 +118,9 @@
|
||||
"items": [{
|
||||
"type": "single",
|
||||
"id": "bethere",
|
||||
"_id": "6046bab736651a07ebc53810",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 50,
|
||||
"price": 100,
|
||||
"orig_price": 100,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
@@ -140,10 +129,9 @@
|
||||
"items": [{
|
||||
"type": "single",
|
||||
"id": "callmyname",
|
||||
"_id": "6046bab736651a07ebc537e9",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 50,
|
||||
"price": 100,
|
||||
"orig_price": 100,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
@@ -152,10 +140,9 @@
|
||||
"items": [{
|
||||
"type": "single",
|
||||
"id": "fallensquare",
|
||||
"_id": "6046bab736651a07ebc537de",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 50,
|
||||
"price": 100,
|
||||
"orig_price": 100,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
@@ -164,10 +151,9 @@
|
||||
"items": [{
|
||||
"type": "single",
|
||||
"id": "dropdead",
|
||||
"_id": "6046bab736651a07ebc53804",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 50,
|
||||
"price": 100,
|
||||
"orig_price": 100,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
@@ -176,10 +162,9 @@
|
||||
"items": [{
|
||||
"type": "single",
|
||||
"id": "alexandrite",
|
||||
"_id": "6046bab736651a07ebc53811",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 50,
|
||||
"price": 100,
|
||||
"orig_price": 100,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
@@ -188,10 +173,9 @@
|
||||
"items": [{
|
||||
"type": "single",
|
||||
"id": "astraltale",
|
||||
"_id": "6046bab736651a07ebc537f7",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 50,
|
||||
"price": 100,
|
||||
"orig_price": 100,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
@@ -200,7 +184,6 @@
|
||||
"items": [{
|
||||
"type": "single",
|
||||
"id": "phantasia",
|
||||
"_id": "6046bab736651a07ebc537df",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 100,
|
||||
@@ -210,7 +193,6 @@
|
||||
"items": [{
|
||||
"type": "single",
|
||||
"id": "empireofwinter",
|
||||
"_id": "6046bab736651a07ebc53805",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 100,
|
||||
@@ -220,10 +202,9 @@
|
||||
"items": [{
|
||||
"type": "single",
|
||||
"id": "libertas",
|
||||
"_id": "6046bab736651a07ebc537eb",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 50,
|
||||
"price": 100,
|
||||
"orig_price": 100,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
@@ -232,10 +213,9 @@
|
||||
"items": [{
|
||||
"type": "single",
|
||||
"id": "dottodot",
|
||||
"_id": "6046bab736651a07ebc537e0",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 50,
|
||||
"price": 100,
|
||||
"orig_price": 100,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
@@ -244,10 +224,9 @@
|
||||
"items": [{
|
||||
"type": "single",
|
||||
"id": "dreadnought",
|
||||
"_id": "6046bab736651a07ebc537ec",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 50,
|
||||
"price": 100,
|
||||
"orig_price": 100,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
@@ -256,10 +235,9 @@
|
||||
"items": [{
|
||||
"type": "single",
|
||||
"id": "mirzam",
|
||||
"_id": "6046bab736651a07ebc537e1",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 50,
|
||||
"price": 100,
|
||||
"orig_price": 100,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
@@ -268,10 +246,9 @@
|
||||
"items": [{
|
||||
"type": "single",
|
||||
"id": "heavenlycaress",
|
||||
"_id": "6046bab736651a07ebc53807",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 50,
|
||||
"price": 100,
|
||||
"orig_price": 100,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
@@ -280,10 +257,9 @@
|
||||
"items": [{
|
||||
"type": "single",
|
||||
"id": "filament",
|
||||
"_id": "6046bab736651a07ebc53814",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 50,
|
||||
"price": 100,
|
||||
"orig_price": 100,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
@@ -292,7 +268,6 @@
|
||||
"items": [{
|
||||
"type": "single",
|
||||
"id": "avantraze",
|
||||
"_id": "6046bab736651a07ebc537ed",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 100,
|
||||
@@ -302,10 +277,9 @@
|
||||
"items": [{
|
||||
"type": "single",
|
||||
"id": "battlenoone",
|
||||
"_id": "6046bab736651a07ebc537fa",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 50,
|
||||
"price": 100,
|
||||
"orig_price": 100,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
@@ -314,7 +288,6 @@
|
||||
"items": [{
|
||||
"type": "single",
|
||||
"id": "saikyostronger",
|
||||
"_id": "6046bab736651a07ebc537e2",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 100,
|
||||
@@ -324,10 +297,9 @@
|
||||
"items": [{
|
||||
"type": "single",
|
||||
"id": "izana",
|
||||
"_id": "6046bab736651a07ebc53808",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 50,
|
||||
"price": 100,
|
||||
"orig_price": 100,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
@@ -336,10 +308,9 @@
|
||||
"items": [{
|
||||
"type": "single",
|
||||
"id": "einherjar",
|
||||
"_id": "6046bab736651a07ebc53815",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 50,
|
||||
"price": 100,
|
||||
"orig_price": 100,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
@@ -348,10 +319,9 @@
|
||||
"items": [{
|
||||
"type": "single",
|
||||
"id": "laqryma",
|
||||
"_id": "6046bab736651a07ebc537ee",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 50,
|
||||
"price": 100,
|
||||
"orig_price": 100,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
@@ -360,10 +330,9 @@
|
||||
"items": [{
|
||||
"type": "single",
|
||||
"id": "amygdata",
|
||||
"_id": "6046bab736651a07ebc537fb",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 50,
|
||||
"price": 100,
|
||||
"orig_price": 100,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
@@ -372,10 +341,9 @@
|
||||
"items": [{
|
||||
"type": "single",
|
||||
"id": "altale",
|
||||
"_id": "6046bab736651a07ebc537e3",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 50,
|
||||
"price": 100,
|
||||
"orig_price": 100,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
@@ -384,10 +352,9 @@
|
||||
"items": [{
|
||||
"type": "single",
|
||||
"id": "feelssoright",
|
||||
"_id": "6046bab736651a07ebc537ef",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 50,
|
||||
"price": 100,
|
||||
"orig_price": 100,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
@@ -396,7 +363,6 @@
|
||||
"items": [{
|
||||
"type": "single",
|
||||
"id": "scarletcage",
|
||||
"_id": "6046bab736651a07ebc537fc",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 100,
|
||||
@@ -406,7 +372,6 @@
|
||||
"items": [{
|
||||
"type": "single",
|
||||
"id": "teriqma",
|
||||
"_id": "6046bab736651a07ebc537e4",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 100,
|
||||
@@ -416,10 +381,9 @@
|
||||
"items": [{
|
||||
"type": "single",
|
||||
"id": "mahoroba",
|
||||
"_id": "6046bab736651a07ebc5380a",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 50,
|
||||
"price": 100,
|
||||
"orig_price": 100,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
@@ -428,10 +392,9 @@
|
||||
"items": [{
|
||||
"type": "single",
|
||||
"id": "badtek",
|
||||
"_id": "6046bab736651a07ebc537f0",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 50,
|
||||
"price": 100,
|
||||
"orig_price": 100,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
@@ -440,7 +403,6 @@
|
||||
"items": [{
|
||||
"type": "single",
|
||||
"id": "maliciousmischance",
|
||||
"_id": "6046bab736651a07ebc53817",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 100,
|
||||
@@ -450,7 +412,6 @@
|
||||
"items": [{
|
||||
"type": "single",
|
||||
"id": "buchigireberserker",
|
||||
"_id": "6046bab736651a07ebc537e5",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 100,
|
||||
@@ -460,10 +421,9 @@
|
||||
"items": [{
|
||||
"type": "single",
|
||||
"id": "galaxyfriends",
|
||||
"_id": "6046bab736651a07ebc5380b",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 50,
|
||||
"price": 100,
|
||||
"orig_price": 100,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
@@ -472,7 +432,6 @@
|
||||
"items": [{
|
||||
"type": "single",
|
||||
"id": "xeraphinite",
|
||||
"_id": "6046bab736651a07ebc53818",
|
||||
"is_available": true
|
||||
}],
|
||||
"orig_price": 100,
|
||||
@@ -482,7 +441,6 @@
|
||||
"items": [{
|
||||
"type": "single",
|
||||
"id": "xanatos",
|
||||
"_id": "6046bab736651a07ebc537f1",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 100,
|
||||
@@ -492,7 +450,6 @@
|
||||
"items": [{
|
||||
"type": "single",
|
||||
"id": "attraqtia",
|
||||
"_id": "6046bab736651a07ebc537ff",
|
||||
"is_available": true
|
||||
}],
|
||||
"orig_price": 100,
|
||||
@@ -502,7 +459,15 @@
|
||||
"items": [{
|
||||
"type": "single",
|
||||
"id": "gimmedablood",
|
||||
"_id": "6046bab736651a07ebc537e7",
|
||||
"is_available": true
|
||||
}],
|
||||
"orig_price": 100,
|
||||
"price": 100
|
||||
}, {
|
||||
"name": "bassline",
|
||||
"items": [{
|
||||
"type": "single",
|
||||
"id": "bassline",
|
||||
"is_available": true
|
||||
}],
|
||||
"orig_price": 100,
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
|
||||
@@ -10,15 +10,107 @@ import server.setme
|
||||
import server.arcscore
|
||||
import web.login
|
||||
import web.index
|
||||
import api.api_main
|
||||
import server.arcworld
|
||||
import server.arcdownload
|
||||
import server.arcpurchase
|
||||
import server.init
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
app = Flask(__name__)
|
||||
wsgi_app = app.wsgi_app
|
||||
|
||||
os.chdir(sys.path[0]) # 更改工作路径,以便于愉快使用相对路径
|
||||
|
||||
app.config.from_mapping(SECRET_KEY=Config.SECRET_KEY)
|
||||
app.config['SESSION_TYPE'] = 'filesystem'
|
||||
app.register_blueprint(web.login.bp)
|
||||
app.register_blueprint(web.index.bp)
|
||||
app.register_blueprint(api.api_main.bp)
|
||||
|
||||
log_dict = {
|
||||
'version': 1,
|
||||
'root': {
|
||||
'level': 'INFO',
|
||||
'handlers': ['wsgi', 'error_file']
|
||||
},
|
||||
'handlers': {
|
||||
'wsgi': {
|
||||
'class': 'logging.StreamHandler',
|
||||
'stream': 'ext://flask.logging.wsgi_errors_stream',
|
||||
'formatter': 'default'
|
||||
},
|
||||
"error_file": {
|
||||
"class": "logging.handlers.RotatingFileHandler",
|
||||
"maxBytes": 1024 * 1024,
|
||||
"backupCount": 1,
|
||||
"encoding": "utf-8",
|
||||
"level": "ERROR",
|
||||
"formatter": "default",
|
||||
"filename": "./log/error.log"
|
||||
}
|
||||
},
|
||||
'formatters': {
|
||||
'default': {
|
||||
'format': '[%(asctime)s] %(levelname)s in %(module)s: %(message)s'
|
||||
}
|
||||
}
|
||||
}
|
||||
if Config.ALLOW_LOG_INFO:
|
||||
log_dict['root']['handlers'] = ['wsgi', 'info_file', 'error_file']
|
||||
log_dict['handlers']['info_file'] = {
|
||||
"class": "logging.handlers.RotatingFileHandler",
|
||||
"maxBytes": 1024 * 1024,
|
||||
"backupCount": 1,
|
||||
"encoding": "utf-8",
|
||||
"level": "INFO",
|
||||
"formatter": "default",
|
||||
"filename": "./log/info.log"
|
||||
}
|
||||
|
||||
dictConfig(log_dict)
|
||||
|
||||
if not server.init.check_before_run(app):
|
||||
app.logger.error('Something wrong. The server will not run.')
|
||||
input('Press ENTER key to exit.')
|
||||
sys.exit()
|
||||
|
||||
app.logger.info("Start to initialize data in 'songfile' table...")
|
||||
try:
|
||||
error = server.arcdownload.initialize_songfile()
|
||||
except:
|
||||
error = 'Something wrong.'
|
||||
if error:
|
||||
app.logger.warning(error)
|
||||
else:
|
||||
app.logger.info('Complete!')
|
||||
|
||||
|
||||
def add_url_prefix(url, strange_flag=False):
|
||||
# 给url加前缀,返回字符串
|
||||
if not url or not Config.GAME_API_PREFIX:
|
||||
return Config.GAME_API_PREFIX + url
|
||||
|
||||
prefix = Config.GAME_API_PREFIX
|
||||
if prefix[0] != '/':
|
||||
prefix = '/' + prefix
|
||||
if prefix[-1] == '/':
|
||||
prefix = prefix[:-1]
|
||||
|
||||
if url[0] != '/':
|
||||
r = '/' + url
|
||||
else:
|
||||
r = url
|
||||
|
||||
if strange_flag and prefix.count('/') >= 1: # 为了方便处理双斜杠
|
||||
t = prefix[::-1]
|
||||
t = t[t.find('/')+1:]
|
||||
prefix = t[::-1]
|
||||
|
||||
return prefix + r
|
||||
|
||||
|
||||
def error_return(error_code): # 错误返回
|
||||
# -7 处理交易时发生了错误
|
||||
@@ -72,6 +164,7 @@ def error_return(error_code): # 错误返回
|
||||
@app.route('/')
|
||||
def hello():
|
||||
return "Hello World!"
|
||||
# 自定义路径
|
||||
|
||||
|
||||
@app.route('/favicon.ico', methods=['GET']) # 图标
|
||||
@@ -83,14 +176,24 @@ def favicon():
|
||||
return app.send_static_file('favicon.ico')
|
||||
|
||||
|
||||
@app.route('/latte/13/auth/login', methods=['POST']) # 登录接口
|
||||
@app.route(add_url_prefix('/auth/login'), methods=['POST']) # 登录接口
|
||||
def login():
|
||||
if 'AppVersion' in request.headers: # 版本检查
|
||||
if Config.ALLOW_APPVERSION:
|
||||
if request.headers['AppVersion'] not in Config.ALLOW_APPVERSION:
|
||||
return jsonify({"success": False, "error_code": 5})
|
||||
|
||||
headers = request.headers
|
||||
id_pwd = headers['Authorization']
|
||||
id_pwd = base64.b64decode(id_pwd[6:]).decode()
|
||||
name, password = id_pwd.split(':', 1)
|
||||
device_id = headers['DeviceId']
|
||||
token, error_code = server.auth.arc_login(name, password, device_id)
|
||||
if 'DeviceId' in headers:
|
||||
device_id = headers['DeviceId']
|
||||
else:
|
||||
device_id = 'low_version'
|
||||
|
||||
token, error_code = server.auth.arc_login(
|
||||
name, password, device_id, request.remote_addr)
|
||||
if not error_code:
|
||||
r = {"success": True, "token_type": "Bearer"}
|
||||
r['access_token'] = token
|
||||
@@ -99,23 +202,33 @@ def login():
|
||||
return error_return(error_code)
|
||||
|
||||
|
||||
@app.route('/latte/13/user/', methods=['POST']) # 注册接口
|
||||
@app.route(add_url_prefix('/user/'), methods=['POST']) # 注册接口
|
||||
def register():
|
||||
if 'AppVersion' in request.headers: # 版本检查
|
||||
if Config.ALLOW_APPVERSION:
|
||||
if request.headers['AppVersion'] not in Config.ALLOW_APPVERSION:
|
||||
return jsonify({"success": False, "error_code": 5})
|
||||
|
||||
name = request.form['name']
|
||||
password = request.form['password']
|
||||
device_id = request.form['device_id']
|
||||
email = request.form['email']
|
||||
if 'device_id' in request.form:
|
||||
device_id = request.form['device_id']
|
||||
else:
|
||||
device_id = 'low_version'
|
||||
|
||||
user_id, token, error_code = server.auth.arc_register(
|
||||
name, password, device_id)
|
||||
name, password, device_id, email, request.remote_addr)
|
||||
if user_id is not None:
|
||||
r = {"success": True, "value": {
|
||||
'user_id': user_id, 'access_token': token}}
|
||||
return jsonify(r)
|
||||
else:
|
||||
return error_return(error_code) # 应该是101,用户名被占用,毕竟电子邮箱没记录
|
||||
return error_return(error_code)
|
||||
|
||||
|
||||
# 集成式请求,没想到什么好办法处理,就先这样写着
|
||||
@app.route('/latte/13/compose/aggregate', methods=['GET'])
|
||||
@app.route(add_url_prefix('/compose/aggregate'), methods=['GET'])
|
||||
@server.auth.auth_required(request)
|
||||
def aggregate(user_id):
|
||||
calls = request.args.get('calls')
|
||||
@@ -126,7 +239,7 @@ def aggregate(user_id):
|
||||
return jsonify(r)
|
||||
|
||||
|
||||
@app.route('/latte/13/user/me/character', methods=['POST']) # 角色切换
|
||||
@app.route(add_url_prefix('/user/me/character'), methods=['POST']) # 角色切换
|
||||
@server.auth.auth_required(request)
|
||||
def character_change(user_id):
|
||||
character_id = request.form['character']
|
||||
@@ -145,7 +258,8 @@ def character_change(user_id):
|
||||
return error_return(108)
|
||||
|
||||
|
||||
@app.route('/latte/<path:path>/toggle_uncap', methods=['POST']) # 角色觉醒切换
|
||||
# 角色觉醒切换
|
||||
@app.route(add_url_prefix('/<path:path>/toggle_uncap', True), methods=['POST'])
|
||||
@server.auth.auth_required(request)
|
||||
def character_uncap(user_id, path):
|
||||
while '//' in path:
|
||||
@@ -164,7 +278,7 @@ def character_uncap(user_id, path):
|
||||
return error_return(108)
|
||||
|
||||
|
||||
@app.route('/latte/13/friend/me/add', methods=['POST']) # 加好友
|
||||
@app.route(add_url_prefix('/friend/me/add'), methods=['POST']) # 加好友
|
||||
@server.auth.auth_required(request)
|
||||
def add_friend(user_id):
|
||||
friend_code = request.form['friend_code']
|
||||
@@ -190,7 +304,7 @@ def add_friend(user_id):
|
||||
return error_return(401)
|
||||
|
||||
|
||||
@app.route('/latte/13/friend/me/delete', methods=['POST']) # 删好友
|
||||
@app.route(add_url_prefix('/friend/me/delete'), methods=['POST']) # 删好友
|
||||
@server.auth.auth_required(request)
|
||||
def delete_friend(user_id):
|
||||
friend_id = int(request.form['friend_id'])
|
||||
@@ -212,7 +326,8 @@ def delete_friend(user_id):
|
||||
return error_return(401)
|
||||
|
||||
|
||||
@app.route('/latte/13/score/song/friend', methods=['GET']) # 好友排名,默认最多50
|
||||
# 好友排名,默认最多50
|
||||
@app.route(add_url_prefix('/score/song/friend'), methods=['GET'])
|
||||
@server.auth.auth_required(request)
|
||||
def song_score_friend(user_id):
|
||||
song_id = request.args.get('song_id')
|
||||
@@ -227,7 +342,7 @@ def song_score_friend(user_id):
|
||||
return error_return(108)
|
||||
|
||||
|
||||
@app.route('/latte/13/score/song/me', methods=['GET']) # 我的排名,默认最多20
|
||||
@app.route(add_url_prefix('/score/song/me'), methods=['GET']) # 我的排名,默认最多20
|
||||
@server.auth.auth_required(request)
|
||||
def song_score_me(user_id):
|
||||
song_id = request.args.get('song_id')
|
||||
@@ -242,7 +357,7 @@ def song_score_me(user_id):
|
||||
return error_return(108)
|
||||
|
||||
|
||||
@app.route('/latte/13/score/song', methods=['GET']) # TOP20
|
||||
@app.route(add_url_prefix('/score/song'), methods=['GET']) # TOP20
|
||||
@server.auth.auth_required(request)
|
||||
def song_score_top(user_id):
|
||||
song_id = request.args.get('song_id')
|
||||
@@ -257,7 +372,7 @@ def song_score_top(user_id):
|
||||
return error_return(108)
|
||||
|
||||
|
||||
@app.route('/latte/13/score/song', methods=['POST']) # 成绩上传
|
||||
@app.route(add_url_prefix('/score/song'), methods=['POST']) # 成绩上传
|
||||
@server.auth.auth_required(request)
|
||||
def song_score_post(user_id):
|
||||
song_token = request.form['song_token']
|
||||
@@ -296,7 +411,8 @@ def song_score_post(user_id):
|
||||
return error_return(108)
|
||||
|
||||
|
||||
@app.route('/latte/13/score/token', methods=['GET']) # 成绩上传所需的token,显然我不想验证
|
||||
# 成绩上传所需的token,显然我不想验证
|
||||
@app.route(add_url_prefix('/score/token'), methods=['GET'])
|
||||
def score_token():
|
||||
return jsonify({
|
||||
"success": True,
|
||||
@@ -307,7 +423,7 @@ def score_token():
|
||||
|
||||
|
||||
# 世界模式成绩上传所需的token,无验证
|
||||
@app.route('/latte/13/score/token/world', methods=['GET'])
|
||||
@app.route(add_url_prefix('/score/token/world'), methods=['GET'])
|
||||
@server.auth.auth_required(request)
|
||||
def score_token_world(user_id):
|
||||
args = request.args
|
||||
@@ -322,7 +438,7 @@ def score_token_world(user_id):
|
||||
})
|
||||
|
||||
|
||||
@app.route('/latte/13/user/me/save', methods=['GET']) # 从云端同步
|
||||
@app.route(add_url_prefix('/user/me/save'), methods=['GET']) # 从云端同步
|
||||
@server.auth.auth_required(request)
|
||||
def cloud_get(user_id):
|
||||
r = server.arcscore.arc_all_get(user_id)
|
||||
@@ -335,7 +451,7 @@ def cloud_get(user_id):
|
||||
return error_return(108)
|
||||
|
||||
|
||||
@app.route('/latte/13/user/me/save', methods=['POST']) # 向云端同步
|
||||
@app.route(add_url_prefix('/user/me/save'), methods=['POST']) # 向云端同步
|
||||
@server.auth.auth_required(request)
|
||||
def cloud_post(user_id):
|
||||
scores_data = request.form['scores_data']
|
||||
@@ -356,7 +472,7 @@ def cloud_post(user_id):
|
||||
})
|
||||
|
||||
|
||||
@app.route('/latte/13/purchase/me/redeem', methods=['POST']) # 兑换码
|
||||
@app.route(add_url_prefix('/purchase/me/redeem'), methods=['POST']) # 兑换码
|
||||
@server.auth.auth_required(request)
|
||||
def redeem(user_id):
|
||||
code = request.form['code']
|
||||
@@ -378,7 +494,7 @@ def redeem(user_id):
|
||||
|
||||
|
||||
# 礼物确认
|
||||
@app.route('/latte/13/present/me/claim/<present_id>', methods=['POST'])
|
||||
@app.route(add_url_prefix('/present/me/claim/<present_id>'), methods=['POST'])
|
||||
@server.auth.auth_required(request)
|
||||
def claim_present(user_id, present_id):
|
||||
flag = server.arcpurchase.claim_user_present(user_id, present_id)
|
||||
@@ -390,7 +506,8 @@ def claim_present(user_id, present_id):
|
||||
return error_return(108)
|
||||
|
||||
|
||||
@app.route('/latte/13/purchase/me/item', methods=['POST']) # 购买,world模式boost
|
||||
# 购买,world模式boost
|
||||
@app.route(add_url_prefix('/purchase/me/item'), methods=['POST'])
|
||||
@server.auth.auth_required(request)
|
||||
def prog_boost(user_id):
|
||||
re = {"success": False}
|
||||
@@ -407,20 +524,19 @@ def prog_boost(user_id):
|
||||
return jsonify(re)
|
||||
|
||||
|
||||
@app.route('/latte/13/purchase/me/pack', methods=['POST']) # 曲包和单曲购买
|
||||
@app.route(add_url_prefix('/purchase/me/pack'), methods=['POST']) # 曲包和单曲购买
|
||||
@server.auth.auth_required(request)
|
||||
def pack(user_id):
|
||||
if 'pack_id' in request.form:
|
||||
return jsonify(server.arcpurchase.buy_pack(user_id, request.form['pack_id']))
|
||||
return jsonify(server.arcpurchase.buy_thing(user_id, request.form['pack_id'], 'pack'))
|
||||
if 'single_id' in request.form:
|
||||
return jsonify(server.arcpurchase.buy_single(user_id, request.form['single_id']))
|
||||
return jsonify(server.arcpurchase.buy_thing(user_id, request.form['single_id'], 'single'))
|
||||
|
||||
return jsonify({
|
||||
"success": True
|
||||
})
|
||||
return jsonify({"success": True})
|
||||
|
||||
|
||||
@app.route('/latte/13/purchase/bundle/single', methods=['GET']) # 单曲购买信息获取
|
||||
# 单曲购买信息获取
|
||||
@app.route(add_url_prefix('/purchase/bundle/single'), methods=['GET'])
|
||||
def single():
|
||||
return jsonify({
|
||||
"success": True,
|
||||
@@ -428,7 +544,7 @@ def single():
|
||||
})
|
||||
|
||||
|
||||
@app.route('/latte/13/world/map/me', methods=['GET']) # 获得世界模式信息,所有地图
|
||||
@app.route(add_url_prefix('/world/map/me'), methods=['GET']) # 获得世界模式信息,所有地图
|
||||
@server.auth.auth_required(request)
|
||||
def world_all(user_id):
|
||||
return jsonify({
|
||||
@@ -441,7 +557,7 @@ def world_all(user_id):
|
||||
})
|
||||
|
||||
|
||||
@app.route('/latte/13/world/map/me/', methods=['POST']) # 进入地图
|
||||
@app.route(add_url_prefix('/world/map/me/'), methods=['POST']) # 进入地图
|
||||
@server.auth.auth_required(request)
|
||||
def world_in(user_id):
|
||||
map_id = request.form['map_id']
|
||||
@@ -451,7 +567,8 @@ def world_in(user_id):
|
||||
})
|
||||
|
||||
|
||||
@app.route('/latte/13/world/map/me/<map_id>', methods=['GET']) # 获得单个地图完整信息
|
||||
# 获得单个地图完整信息
|
||||
@app.route(add_url_prefix('/world/map/me/<map_id>'), methods=['GET'])
|
||||
@server.auth.auth_required(request)
|
||||
def world_one(user_id, map_id):
|
||||
server.arcworld.change_user_current_map(user_id, map_id)
|
||||
@@ -465,7 +582,7 @@ def world_one(user_id, map_id):
|
||||
})
|
||||
|
||||
|
||||
@app.route('/latte/13/serve/download/me/song', methods=['GET']) # 歌曲下载
|
||||
@app.route(add_url_prefix('/serve/download/me/song'), methods=['GET']) # 歌曲下载
|
||||
@server.auth.auth_required(request)
|
||||
def download_song(user_id):
|
||||
song_ids = request.args.getlist('sid')
|
||||
@@ -501,7 +618,8 @@ def download(file_path):
|
||||
return error_return(108)
|
||||
|
||||
|
||||
@app.route('/latte/<path:path>', methods=['POST']) # 三个设置,写在最后降低优先级
|
||||
# 三个设置,写在最后降低优先级
|
||||
@app.route(add_url_prefix('/<path:path>', True), methods=['POST'])
|
||||
@server.auth.auth_required(request)
|
||||
def sys_set(user_id, path):
|
||||
set_arg = path[10:]
|
||||
@@ -513,64 +631,11 @@ def sys_set(user_id, path):
|
||||
|
||||
|
||||
def main():
|
||||
app.config.from_mapping(SECRET_KEY=Config.SECRET_KEY)
|
||||
app.config['SESSION_TYPE'] = 'filesystem'
|
||||
app.register_blueprint(web.login.bp)
|
||||
app.register_blueprint(web.index.bp)
|
||||
|
||||
log_dict = {
|
||||
'version': 1,
|
||||
'root': {
|
||||
'level': 'INFO',
|
||||
'handlers': ['wsgi', 'error_file']
|
||||
},
|
||||
'handlers': {
|
||||
'wsgi': {
|
||||
'class': 'logging.StreamHandler',
|
||||
'stream': 'ext://flask.logging.wsgi_errors_stream',
|
||||
'formatter': 'default'
|
||||
},
|
||||
"error_file": {
|
||||
"class": "logging.handlers.RotatingFileHandler",
|
||||
"maxBytes": 1024 * 1024,
|
||||
"backupCount": 1,
|
||||
"encoding": "utf-8",
|
||||
"level": "ERROR",
|
||||
"formatter": "default",
|
||||
"filename": "./log/error.log"
|
||||
}
|
||||
},
|
||||
'formatters': {
|
||||
'default': {
|
||||
'format': '[%(asctime)s] %(levelname)s in %(module)s: %(message)s'
|
||||
}
|
||||
}
|
||||
}
|
||||
if Config.ALLOW_LOG_INFO:
|
||||
log_dict['root']['handlers'] = ['wsgi', 'info_file', 'error_file']
|
||||
log_dict['handlers']['info_file'] = {
|
||||
"class": "logging.handlers.RotatingFileHandler",
|
||||
"maxBytes": 1024 * 1024,
|
||||
"backupCount": 1,
|
||||
"encoding": "utf-8",
|
||||
"level": "INFO",
|
||||
"formatter": "default",
|
||||
"filename": "./log/info.log"
|
||||
}
|
||||
|
||||
dictConfig(log_dict)
|
||||
|
||||
app.logger.info("Start to initialize data in 'songfile' table...")
|
||||
try:
|
||||
error = server.arcdownload.initialize_songfile()
|
||||
except:
|
||||
error = 'Something wrong.'
|
||||
if error:
|
||||
app.logger.warning(error)
|
||||
if Config.SSL_CERT and Config.SSL_KEY:
|
||||
app.run(Config.HOST, Config.PORT, ssl_context=(
|
||||
Config.SSL_CERT, Config.SSL_KEY))
|
||||
else:
|
||||
app.logger.info('Complete!')
|
||||
|
||||
app.run(Config.HOST, Config.PORT)
|
||||
app.run(Config.HOST, Config.PORT)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -25,7 +25,24 @@ def get_file_md5(file_path):
|
||||
return myhash.hexdigest()
|
||||
|
||||
|
||||
def get_one_song(c, user_id, song_id, file_dir='./database/songs'):
|
||||
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 = {}
|
||||
@@ -37,7 +54,6 @@ def get_one_song(c, user_id, song_id, file_dir='./database/songs'):
|
||||
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']:
|
||||
token = hashlib.md5(
|
||||
(str(user_id) + song_id + i + str(now)).encode(encoding='UTF-8')).hexdigest()
|
||||
token = token[:8]
|
||||
|
||||
if i == 'base.ogg':
|
||||
c.execute(
|
||||
@@ -48,8 +64,12 @@ def get_one_song(c, user_id, song_id, file_dir='./database/songs'):
|
||||
else:
|
||||
checksum = get_file_md5(os.path.join(
|
||||
file_dir, song_id, 'base.ogg'))
|
||||
re['audio'] = {"checksum": checksum,
|
||||
"url": url_for('download', file_path=song_id+'/base.ogg', t=token, _external=True)}
|
||||
|
||||
if url_flag:
|
||||
re['audio'] = {"checksum": checksum, "url": get_url(
|
||||
file_path=song_id+'/base.ogg', t=token)}
|
||||
else:
|
||||
re['audio'] = {"checksum": checksum}
|
||||
else:
|
||||
if 'chart' not in re:
|
||||
re['chart'] = {}
|
||||
@@ -60,23 +80,28 @@ def get_one_song(c, user_id, song_id, file_dir='./database/songs'):
|
||||
checksum = x[0]
|
||||
else:
|
||||
checksum = get_file_md5(os.path.join(file_dir, song_id, i))
|
||||
re['chart'][i[0]] = {"checksum": checksum,
|
||||
"url": url_for('download', file_path=song_id+'/'+i, t=token, _external=True)}
|
||||
|
||||
c.execute('''insert into download_token values(:a,:b,:c,:d,:e)''', {
|
||||
'a': user_id, 'b': song_id, 'c': i, 'd': token, 'e': now})
|
||||
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'):
|
||||
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))
|
||||
re.update(get_one_song(c, user_id, i, file_dir, url_flag))
|
||||
|
||||
return re
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from server.sql import Connect
|
||||
import server.info
|
||||
import time
|
||||
import json
|
||||
|
||||
@@ -67,49 +68,55 @@ def buy_item(c, user_id, price):
|
||||
return True, ticket - price
|
||||
|
||||
|
||||
def buy_pack(user_id, pack_id):
|
||||
# 曲包购买,返回字典
|
||||
re = {"success": False}
|
||||
with Connect() as c:
|
||||
c.execute('''select price from item where item_id = :a''',
|
||||
{'a': pack_id})
|
||||
price = c.fetchone()
|
||||
if price:
|
||||
price = price[0]
|
||||
else:
|
||||
price = 0
|
||||
def buy_thing(user_id, item_id, item_type):
|
||||
# 购买物品接口,返回字典
|
||||
success_flag = False
|
||||
ticket = 0
|
||||
packs = []
|
||||
singles = []
|
||||
characters = []
|
||||
|
||||
flag, ticket = buy_item(c, user_id, price)
|
||||
with Connect() as c:
|
||||
c.execute('''select is_available, price, orig_price, discount_from, discount_to from item where item_id=:a and type=:b''',
|
||||
{'a': item_id, 'b': item_type})
|
||||
x = c.fetchone()
|
||||
price = 0
|
||||
flag = False
|
||||
if x:
|
||||
is_available = x[0]
|
||||
price = x[1]
|
||||
orig_price = x[2]
|
||||
discount_from = x[3]
|
||||
discount_to = x[4]
|
||||
|
||||
if not is_available:
|
||||
return False
|
||||
|
||||
now = int(time.time() * 1000)
|
||||
if not(discount_from <= now <= discount_to):
|
||||
price = orig_price
|
||||
|
||||
flag, ticket = buy_item(c, user_id, price)
|
||||
|
||||
if flag:
|
||||
c.execute('''insert into user_item values(:a,:b,'pack')''',
|
||||
{'a': user_id, 'b': pack_id})
|
||||
c.execute('''insert into user_item values(:a,:b,:c)''',
|
||||
{'a': user_id, 'b': item_id, 'c': item_type})
|
||||
|
||||
re = {"success": True}
|
||||
success_flag = True
|
||||
|
||||
return re
|
||||
packs = server.info.get_user_packs(c, user_id)
|
||||
singles = server.info.get_user_singles(c, user_id)
|
||||
characters = server.info.get_user_characters(c, user_id)
|
||||
|
||||
|
||||
def buy_single(user_id, single_id):
|
||||
# 单曲购买,返回字典
|
||||
re = {"success": False}
|
||||
with Connect() as c:
|
||||
c.execute('''select price from item where item_id = :a''',
|
||||
{'a': single_id})
|
||||
price = c.fetchone()
|
||||
if price:
|
||||
price = price[0]
|
||||
else:
|
||||
price = 0
|
||||
|
||||
flag, ticket = buy_item(c, user_id, price)
|
||||
|
||||
if flag:
|
||||
c.execute('''insert into user_item values(:a,:b,'single')''',
|
||||
{'a': user_id, 'b': single_id})
|
||||
re = {"success": True}
|
||||
|
||||
return re
|
||||
return {
|
||||
"success": success_flag,
|
||||
"value": {'user_id': user_id,
|
||||
'ticket': ticket,
|
||||
'packs': packs,
|
||||
'singles': singles,
|
||||
'characters': characters
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def get_prog_boost(user_id):
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,12 +1,12 @@
|
||||
import hashlib
|
||||
import time
|
||||
import server.arcworld
|
||||
from server.sql import Connect
|
||||
import functools
|
||||
from setting import Config
|
||||
from flask import jsonify
|
||||
|
||||
|
||||
def arc_login(name: str, password: str, device_id: str): # 登录判断
|
||||
def arc_login(name: str, password: str, device_id: str, ip: str): # 登录判断
|
||||
# 查询数据库中的user表,验证账号密码,返回并记录token,多返回个error code
|
||||
# token采用user_id和时间戳连接后hash生成(真的是瞎想的,没用bear)
|
||||
# 密码和token的加密方式为 SHA-256
|
||||
@@ -43,8 +43,8 @@ def arc_login(name: str, password: str, device_id: str): # 登录判断
|
||||
|
||||
if not Config.ALLOW_LOGIN_SAME_DEVICE:
|
||||
if device_id in device_list: # 对相同设备进行删除
|
||||
c.execute('''delete from login where login_device=:a''', {
|
||||
'a': device_id})
|
||||
c.execute('''delete from login where login_device=:a and user_id=:b''', {
|
||||
'a': device_id, 'b': user_id})
|
||||
should_delete_num = len(
|
||||
device_list) + 1 - device_list.count(device_id) - Config.LOGIN_DEVICE_NUMBER_LIMIT
|
||||
|
||||
@@ -52,8 +52,8 @@ def arc_login(name: str, password: str, device_id: str): # 登录判断
|
||||
c.execute('''delete from login where rowid in (select rowid from login where user_id=:user_id limit :a);''',
|
||||
{'user_id': user_id, 'a': int(should_delete_num)})
|
||||
|
||||
c.execute('''insert into login(access_token, user_id, login_device) values(:access_token, :user_id, :device_id)''', {
|
||||
'user_id': user_id, 'access_token': token, 'device_id': device_id})
|
||||
c.execute('''insert into login values(:access_token, :user_id, :time, :ip, :device_id)''', {
|
||||
'user_id': user_id, 'access_token': token, 'device_id': device_id, 'time': now, 'ip': ip})
|
||||
error_code = None
|
||||
else:
|
||||
# 密码错误
|
||||
@@ -65,8 +65,8 @@ def arc_login(name: str, password: str, device_id: str): # 登录判断
|
||||
return token, error_code
|
||||
|
||||
|
||||
def arc_register(name: str, password: str, device_id: str): # 注册
|
||||
# 账号注册,只记录hash密码和用户名,生成user_id和user_code,自动登录返回token
|
||||
def arc_register(name: str, password: str, device_id: str, email: str, ip: str): # 注册
|
||||
# 账号注册,记录hash密码、用户名和邮箱,生成user_id和user_code,自动登录返回token
|
||||
# token和密码的处理同登录部分
|
||||
|
||||
def build_user_code(c):
|
||||
@@ -107,23 +107,28 @@ def arc_register(name: str, password: str, device_id: str): # 注册
|
||||
c.execute(
|
||||
'''select exists(select * from user where name = :name)''', {'name': name})
|
||||
if c.fetchone() == (0,):
|
||||
user_code = build_user_code(c)
|
||||
user_id = build_user_id(c)
|
||||
now = int(time.time() * 1000)
|
||||
c.execute('''insert into user(user_id, name, password, join_date, user_code, rating_ptt,
|
||||
character_id, is_skill_sealed, is_char_uncapped, is_char_uncapped_override, is_hide_rating, favorite_character, max_stamina_notification_enabled, current_map, ticket)
|
||||
values(:user_id, :name, :password, :join_date, :user_code, 0, 0, 0, 0, 0, 0, -1, 0, '', 0)
|
||||
''', {'user_code': user_code, 'user_id': user_id, 'join_date': now, 'name': name, 'password': hash_pwd})
|
||||
c.execute('''insert into recent30(user_id) values(:user_id)''', {
|
||||
'user_id': user_id})
|
||||
c.execute(
|
||||
'''select exists(select * from user where email = :email)''', {'email': email})
|
||||
if c.fetchone() == (0,):
|
||||
user_code = build_user_code(c)
|
||||
user_id = build_user_id(c)
|
||||
now = int(time.time() * 1000)
|
||||
c.execute('''insert into user(user_id, name, password, join_date, user_code, rating_ptt,
|
||||
character_id, is_skill_sealed, is_char_uncapped, is_char_uncapped_override, is_hide_rating, favorite_character, max_stamina_notification_enabled, current_map, ticket, prog_boost, email)
|
||||
values(:user_id, :name, :password, :join_date, :user_code, 0, 0, 0, 0, 0, 0, -1, 0, '', :memories, 0, :email)
|
||||
''', {'user_code': user_code, 'user_id': user_id, 'join_date': now, 'name': name, 'password': hash_pwd, 'memories': Config.DEFAULT_MEMORIES, 'email': email})
|
||||
c.execute('''insert into recent30(user_id) values(:user_id)''', {
|
||||
'user_id': user_id})
|
||||
|
||||
token = hashlib.sha256(
|
||||
(str(user_id) + str(now)).encode("utf8")).hexdigest()
|
||||
c.execute('''insert into login(access_token, user_id, login_device) values(:access_token, :user_id, :device_id)''', {
|
||||
'user_id': user_id, 'access_token': token, 'device_id': device_id})
|
||||
token = hashlib.sha256(
|
||||
(str(user_id) + str(now)).encode("utf8")).hexdigest()
|
||||
c.execute('''insert into login values(:access_token, :user_id, :time, :ip, :device_id)''', {
|
||||
'user_id': user_id, 'access_token': token, 'device_id': device_id, 'time': now, 'ip': ip})
|
||||
|
||||
insert_user_char(c, user_id)
|
||||
error_code = 0
|
||||
insert_user_char(c, user_id)
|
||||
error_code = 0
|
||||
else:
|
||||
error_code = 102
|
||||
else:
|
||||
error_code = 101
|
||||
|
||||
@@ -166,8 +171,14 @@ def auth_required(request):
|
||||
def wrapped_view(*args, **kwargs):
|
||||
|
||||
user_id = None
|
||||
with Connect() as c:
|
||||
headers = request.headers
|
||||
headers = request.headers
|
||||
|
||||
if 'AppVersion' in headers: # 版本检查
|
||||
if Config.ALLOW_APPVERSION:
|
||||
if headers['AppVersion'] not in Config.ALLOW_APPVERSION:
|
||||
return jsonify({"success": False, "error_code": 5})
|
||||
|
||||
if 'Authorization' in headers:
|
||||
token = headers['Authorization']
|
||||
token = token[7:]
|
||||
user_id = token_get_id(token)
|
||||
@@ -175,7 +186,7 @@ def auth_required(request):
|
||||
if user_id is not None:
|
||||
return view(user_id, *args, **kwargs)
|
||||
else:
|
||||
return '''{"success":false,"error_code":108}'''
|
||||
return jsonify({"success": False, "error_code": 108})
|
||||
|
||||
return wrapped_view
|
||||
return decorator
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
from server.sql import Connect
|
||||
import server.arcworld
|
||||
import server.arcpurchase
|
||||
import server.arcdownload
|
||||
import time
|
||||
from setting import Config
|
||||
|
||||
|
||||
def int2b(x):
|
||||
@@ -116,12 +118,16 @@ def get_user_friend(c, user_id):
|
||||
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": y[5],
|
||||
"rating": rating,
|
||||
"join_date": int(y[3]),
|
||||
"character": character,
|
||||
"recent_score": get_recent_score(c, i[0]),
|
||||
@@ -160,21 +166,39 @@ def get_user_packs(c, user_id):
|
||||
return re
|
||||
|
||||
|
||||
def get_value_0(c, user_id):
|
||||
# 构造value id=0的数据,返回字典
|
||||
def get_user_characters(c, user_id):
|
||||
# 获取用户所拥有角色,返回列表
|
||||
|
||||
c.execute('''select character_id from user_char where user_id = :user_id''',
|
||||
{'user_id': user_id})
|
||||
|
||||
x = c.fetchall()
|
||||
characters = []
|
||||
|
||||
if x:
|
||||
for i in x:
|
||||
characters.append(i[0])
|
||||
|
||||
return characters
|
||||
|
||||
|
||||
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 = get_user_character(c, user_id)
|
||||
# 下面没有使用get_user_characters函数是为了节省一次查询
|
||||
characters = []
|
||||
for i in user_character:
|
||||
characters.append(i['character_id'])
|
||||
|
||||
prog_boost = 0
|
||||
if x[27] and x[27] != 0:
|
||||
prog_boost = 300
|
||||
|
||||
r = {"is_aprilfools": False,
|
||||
r = {"is_aprilfools": Config.IS_APRILFOOLS,
|
||||
"curr_available_maps": [],
|
||||
"character_stats": user_character,
|
||||
"friends": get_user_friend(c, user_id),
|
||||
@@ -197,7 +221,7 @@ def get_value_0(c, user_id):
|
||||
"max_stamina_ts": 1586274871917,
|
||||
"stamina": 12,
|
||||
"world_unlocks": ["scenery_chap1", "scenery_chap2", "scenery_chap3", "scenery_chap4", "scenery_chap5"],
|
||||
"world_songs": ["babaroque", "shadesoflight", "kanagawa", "lucifer", "anokumene", "ignotus", "rabbitintheblackroom", "qualia", "redandblue", "bookmaker", "darakunosono", "espebranch", "blacklotus", "givemeanightmare", "vividtheory", "onefr", "gekka", "vexaria3", "infinityheaven3", "fairytale3", "goodtek3", "suomi", "rugie", "faintlight", "harutopia", "goodtek", "dreaminattraction", "syro", "diode", "freefall", "grimheart", "blaster", "cyberneciacatharsis", "monochromeprincess", "revixy", "vector", "supernova", "nhelv", "purgatorium3", "dement3", "crossover", "guardina", "axiumcrisis", "worldvanquisher", "sheriruth", "pragmatism", "gloryroad", "etherstrike", "corpssansorganes", "lostdesire", "blrink", "essenceoftwilight", "lapis", "solitarydream", "lumia3", "purpleverse", "moonheart3", "glow", "enchantedlove", "take"],
|
||||
"world_songs": ["babaroque", "shadesoflight", "kanagawa", "lucifer", "anokumene", "ignotus", "rabbitintheblackroom", "qualia", "redandblue", "bookmaker", "darakunosono", "espebranch", "blacklotus", "givemeanightmare", "vividtheory", "onefr", "gekka", "vexaria3", "infinityheaven3", "fairytale3", "goodtek3", "suomi", "rugie", "faintlight", "harutopia", "goodtek", "dreaminattraction", "syro", "diode", "freefall", "grimheart", "blaster", "cyberneciacatharsis", "monochromeprincess", "revixy", "vector", "supernova", "nhelv", "purgatorium3", "dement3", "crossover", "guardina", "axiumcrisis", "worldvanquisher", "sheriruth", "pragmatism", "gloryroad", "etherstrike", "corpssansorganes", "lostdesire", "blrink", "essenceoftwilight", "lapis", "solitarydream", "lumia3", "purpleverse", "moonheart3", "glow", "enchantedlove", "take", "lifeispiano", "vandalism", "nexttoyou3", "lostcivilization3"],
|
||||
"singles": get_user_singles(c, user_id),
|
||||
"packs": get_user_packs(c, user_id),
|
||||
"characters": characters,
|
||||
@@ -205,7 +229,8 @@ def get_value_0(c, user_id):
|
||||
"recent_score": get_recent_score(c, user_id),
|
||||
"max_friend": 50,
|
||||
"rating": x[5],
|
||||
"join_date": int(x[3])
|
||||
"join_date": int(x[3]),
|
||||
"global_rank": 114514
|
||||
}
|
||||
|
||||
return r
|
||||
@@ -218,26 +243,26 @@ def arc_aggregate_small(user_id):
|
||||
r = {"success": True,
|
||||
"value": [{
|
||||
"id": 0,
|
||||
"value": get_value_0(c, user_id)
|
||||
"value": get_user_me(c, user_id)
|
||||
}]}
|
||||
|
||||
return r
|
||||
|
||||
|
||||
def arc_aggregate_big(user_id):
|
||||
# 返回用户数据和地图歌曲信息
|
||||
# 返回比较全的用户数据
|
||||
r = {"success": False}
|
||||
with Connect() as c:
|
||||
r = {"success": True,
|
||||
"value": [{
|
||||
"id": 0,
|
||||
"value": get_value_0(c, user_id)
|
||||
"value": get_user_me(c, user_id)
|
||||
}, {
|
||||
"id": 1,
|
||||
"value": server.arcpurchase.get_item(c, 'pack')
|
||||
}, {
|
||||
"id": 2,
|
||||
"value": {}
|
||||
"value": server.arcdownload.get_all_songs(user_id, url_flag=False)
|
||||
}, {
|
||||
"id": 3,
|
||||
"value": {
|
||||
|
||||
98
latest version/server/init.py
Normal file
98
latest version/server/init.py
Normal file
@@ -0,0 +1,98 @@
|
||||
import os
|
||||
from shutil import copy, copy2
|
||||
from server.sql import Connect
|
||||
from database.database_initialize import main, ARCAEA_SERVER_VERSION
|
||||
from web.system import update_database
|
||||
|
||||
|
||||
def try_rename(path, new_path):
|
||||
# 尝试重命名文件,并尝试避免命名冲突,返回最终路径
|
||||
final_path = new_path
|
||||
if os.path.exists(new_path):
|
||||
i = 1
|
||||
while os.path.exists(new_path + str(i)):
|
||||
i += 1
|
||||
os.rename(path, new_path + str(i))
|
||||
final_path = new_path + str(i)
|
||||
else:
|
||||
os.rename(path, new_path)
|
||||
|
||||
return final_path
|
||||
|
||||
|
||||
def check_before_run(app):
|
||||
# 运行前检查关键文件,返回布尔值,其实是因为有人经常忘了
|
||||
|
||||
f = True
|
||||
|
||||
if not os.path.exists('setting.py'):
|
||||
app.logger.warning('File `setting.py` is missing.')
|
||||
f = False
|
||||
|
||||
if not os.path.exists('database'):
|
||||
app.logger.warning('Folder `database` is missing.')
|
||||
f = False
|
||||
|
||||
if not os.path.exists('database/songs'):
|
||||
app.logger.warning('Folder `database/songs` is missing.')
|
||||
f = False
|
||||
|
||||
if not os.path.exists('database/arcaea_database.db'):
|
||||
app.logger.warning('File `database/arcaea_database.db` is missing.')
|
||||
f = False
|
||||
try:
|
||||
app.logger.info(
|
||||
'Try to new the file `database/arcaea_database.db`.')
|
||||
main('./database/')
|
||||
app.logger.info(
|
||||
'Success to new the file `database/arcaea_database.db`.')
|
||||
f = True
|
||||
except:
|
||||
app.logger.warning(
|
||||
'Fail to new the file `database/arcaea_database.db`.')
|
||||
|
||||
else:
|
||||
with Connect() as c:
|
||||
try:
|
||||
c.execute('''select value from config where id="version"''')
|
||||
x = c.fetchone()
|
||||
except:
|
||||
x = None
|
||||
|
||||
# 数据库自动更新,不强求
|
||||
if not x or x[0] != ARCAEA_SERVER_VERSION:
|
||||
app.logger.warning(
|
||||
'Maybe the file `database/arcaea_database.db` is an old version.')
|
||||
try:
|
||||
app.logger.info(
|
||||
'Try to update the file `database/arcaea_database.db`.')
|
||||
|
||||
path = try_rename('database/arcaea_database.db',
|
||||
'database/arcaea_database.db.bak')
|
||||
|
||||
try:
|
||||
copy2(path, 'database/arcaea_database.db')
|
||||
except:
|
||||
copy(path, 'database/arcaea_database.db')
|
||||
|
||||
if os.path.isfile("database/old_arcaea_database.db"):
|
||||
os.remove('database/old_arcaea_database.db')
|
||||
|
||||
try_rename('database/arcaea_database.db',
|
||||
'database/old_arcaea_database.db')
|
||||
|
||||
main('./database/')
|
||||
update_database()
|
||||
|
||||
app.logger.info(
|
||||
'Success to update the file `database/arcaea_database.db`.')
|
||||
|
||||
except:
|
||||
app.logger.warning(
|
||||
'Fail to update the file `database/arcaea_database.db`.')
|
||||
|
||||
if not os.path.exists('database/arcsong.db'):
|
||||
app.logger.warning('File `database/arcsong.db` is missing.')
|
||||
f = False
|
||||
|
||||
return f
|
||||
@@ -29,3 +29,52 @@ class Connect():
|
||||
traceback.format_exception(exc_type, exc_val, exc_tb))
|
||||
|
||||
return True
|
||||
|
||||
class Sql():
|
||||
|
||||
@staticmethod
|
||||
def select(c, table_name, target_column=[], limit=-1, offset=0, query={}, sort=[]):
|
||||
# 执行查询单句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 = []
|
||||
for i in query:
|
||||
where_field.append(i)
|
||||
where_value.append(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 sort:
|
||||
sql += ' order by ' + sort[0]['column'] + ' ' + sort[0]['order']
|
||||
if len(sort) >= 2:
|
||||
for i in range(1, len(sort)):
|
||||
sql += ', ' + sort[i]['column'] + ' ' + sort[i]['order']
|
||||
|
||||
if limit >= 0:
|
||||
sql += ' limit :limit offset :offset'
|
||||
sql_dict['limit'] = limit
|
||||
sql_dict['offset'] = offset
|
||||
|
||||
c.execute(sql, sql_dict)
|
||||
|
||||
return c.fetchall()
|
||||
|
||||
@@ -8,12 +8,57 @@ class Config():
|
||||
主机的地址和端口号
|
||||
Host and port of your server
|
||||
'''
|
||||
HOST = '192.168.1.105'
|
||||
HOST = '192.168.1.101'
|
||||
PORT = '80'
|
||||
'''
|
||||
--------------------
|
||||
'''
|
||||
|
||||
'''
|
||||
--------------------
|
||||
游戏API地址前缀
|
||||
Game API's URL prefix
|
||||
'''
|
||||
GAME_API_PREFIX = '/blockchain/14'
|
||||
'''
|
||||
--------------------
|
||||
'''
|
||||
|
||||
'''
|
||||
--------------------
|
||||
允许使用的游戏版本,若为空,则默认全部允许
|
||||
Allowed game versions
|
||||
If it is blank, all are allowed.
|
||||
'''
|
||||
ALLOW_APPVERSION = ['3.5.3', '3.5.3c',
|
||||
'3.6.0', '3.6.0c', '3.6.1', '3.6.1c']
|
||||
'''
|
||||
--------------------
|
||||
'''
|
||||
|
||||
'''
|
||||
--------------------
|
||||
SSL证书路径
|
||||
留空则使用HTTP
|
||||
SSL certificate path
|
||||
If left blank, use HTTP.
|
||||
'''
|
||||
SSL_CERT = '' # *.pem
|
||||
SSL_KEY = '' # *.key
|
||||
'''
|
||||
--------------------
|
||||
'''
|
||||
|
||||
'''
|
||||
--------------------
|
||||
愚人节模式开关
|
||||
Switch of April Fool's Day
|
||||
'''
|
||||
IS_APRILFOOLS = True
|
||||
'''
|
||||
--------------------
|
||||
'''
|
||||
|
||||
'''
|
||||
--------------------
|
||||
Web后台管理页面的用户名和密码
|
||||
@@ -36,6 +81,28 @@ class Config():
|
||||
--------------------
|
||||
'''
|
||||
|
||||
'''
|
||||
--------------------
|
||||
API接口完全控制权限Token,留空则不使用
|
||||
API interface full control permission Token
|
||||
If you don't want to use it, leave it blank.
|
||||
'''
|
||||
API_TOKEN = ''
|
||||
'''
|
||||
--------------------
|
||||
'''
|
||||
|
||||
'''
|
||||
--------------------
|
||||
歌曲下载地址前缀,留空则自动获取
|
||||
Song download address prefix
|
||||
If left blank, it will be obtained automatically.
|
||||
'''
|
||||
DOWNLOAD_LINK_PREFIX = '' # http://***.com/download/
|
||||
'''
|
||||
--------------------
|
||||
'''
|
||||
|
||||
'''
|
||||
--------------------
|
||||
玩家歌曲下载的24小时次数限制,每个文件算一次
|
||||
@@ -75,3 +142,23 @@ class Config():
|
||||
'''
|
||||
--------------------
|
||||
'''
|
||||
|
||||
'''
|
||||
--------------------
|
||||
用户注册时的默认记忆源点数量
|
||||
The default amount of memories at the time of user registration
|
||||
'''
|
||||
DEFAULT_MEMORIES = 0
|
||||
'''
|
||||
--------------------
|
||||
'''
|
||||
|
||||
'''
|
||||
--------------------
|
||||
是否强制使用全解锁云端存档
|
||||
If forcing full unlocked cloud save is enabled
|
||||
'''
|
||||
SAVE_FULL_UNLOCK = False
|
||||
'''
|
||||
--------------------
|
||||
'''
|
||||
|
||||
@@ -20,4 +20,4 @@
|
||||
{% endfor %} {% block content %}{% endblock %}
|
||||
</section>
|
||||
|
||||
<footer id="footer" class="footer">Made by Lost@2020</footer>
|
||||
<footer id="footer" class="footer">Made by Lost@2020-2021</footer>
|
||||
@@ -19,7 +19,7 @@
|
||||
<input name="rating_ftr" id="rating_ftr" required>
|
||||
<label for="rating_byd">Beyond chart const</label>
|
||||
<input name="rating_byd" id="rating_byd" required>
|
||||
<div class="content">如果没有某个铺面,应该填入-1。</div>
|
||||
<div class="content">如果没有某个谱面,应该填入-1。</div>
|
||||
<div class="content">If there is no some chart, fill in -1 please.</div>
|
||||
<input type="submit" value="Add">
|
||||
</form>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<a href="{{ url_for('index.single_player_score') }}">单个玩家成绩查询 Single player score</a></br></br>
|
||||
<a href="{{ url_for('index.single_player_ptt') }}">单个玩家PTT详情查询 Single player ptt</a></br></br>
|
||||
<a href="{{ url_for('index.all_player') }}">所有玩家信息查询 All players</a></br></br>
|
||||
<a href="{{ url_for('index.all_song') }}">铺面信息查询 All songs</a></br></br>
|
||||
<a href="{{ url_for('index.all_song') }}">谱面信息查询 All songs</a></br></br>
|
||||
<a href="{{ url_for('index.all_character') }}">角色信息查询 All characters</a></br></br>
|
||||
<a href="{{ url_for('index.all_item') }}">购买信息查询 All items</a></br></br>
|
||||
<a href="{{ url_for('index.all_present') }}">奖励信息查询 All presents</a></br></br>
|
||||
|
||||
@@ -18,4 +18,26 @@
|
||||
Uploadable files: arcaea_database.db & arcsong.db<br />
|
||||
Data that does not exist in the new database will be added and the existing duplicate data will also be changed.
|
||||
</div>
|
||||
<br />
|
||||
<hr />
|
||||
<form action="/web/updatedatabase/refreshsonghash" method="post">
|
||||
<div class="title">Refresh songs' hash</div>
|
||||
<br />
|
||||
<input type="submit" value="Refresh">
|
||||
|
||||
<div class="content">这里可以刷新储存在数据库中的songs文件夹下所有文件的Hash值。目的是应对歌曲谱面文件的更新。</div>
|
||||
<div class="content">Here you can refresh the hash values of all the files stored in the songs folder in the
|
||||
database. The purpose is to deal with the updating of songs' files.</div>
|
||||
</form>
|
||||
<br />
|
||||
<hr />
|
||||
<form action="/web/updatedatabase/refreshsongrating" method="post">
|
||||
<div class="title">Refresh songs' rating</div>
|
||||
<br />
|
||||
<input type="submit" value="Refresh">
|
||||
|
||||
<div class="content">这里可以刷新数据库中所有成绩的评分。目的是应对歌曲谱面定数的更新。</div>
|
||||
<div class="content">Here you can refresh the ratings of all scores in the database. The purpose is to deal
|
||||
with the updating of songs' chart consts.</div>
|
||||
</form>
|
||||
{% endblock %}
|
||||
@@ -10,6 +10,7 @@ import time
|
||||
import server.arcscore
|
||||
import os
|
||||
import json
|
||||
from server.arcdownload import initialize_songfile
|
||||
|
||||
UPLOAD_FOLDER = 'database'
|
||||
ALLOWED_EXTENSIONS = {'db'}
|
||||
@@ -277,6 +278,30 @@ def update_database():
|
||||
return render_template('web/updatedatabase.html')
|
||||
|
||||
|
||||
@bp.route('/updatedatabase/refreshsonghash', methods=['POST'])
|
||||
@login_required
|
||||
def update_song_hash():
|
||||
# 更新数据库内谱面文件hash值
|
||||
error = initialize_songfile()
|
||||
if error:
|
||||
flash(error)
|
||||
else:
|
||||
flash('数据刷新成功 Success refresh data.')
|
||||
return render_template('web/updatedatabase.html')
|
||||
|
||||
|
||||
@bp.route('/updatedatabase/refreshsongrating', methods=['POST'])
|
||||
@login_required
|
||||
def update_song_rating():
|
||||
# 更新所有分数的rating
|
||||
error = server.arcscore.refresh_all_score_rating()
|
||||
if error:
|
||||
flash(error)
|
||||
else:
|
||||
flash('数据刷新成功 Success refresh data.')
|
||||
return render_template('web/updatedatabase.html')
|
||||
|
||||
|
||||
@bp.route('/changesong', methods=['GET'])
|
||||
@login_required
|
||||
def change_song():
|
||||
@@ -386,7 +411,7 @@ def all_character():
|
||||
def change_character():
|
||||
# 修改角色数据
|
||||
skill_ids = ['No_skill', 'gauge_easy', 'note_mirror', 'gauge_hard', 'frag_plus_10_pack_stellights', 'gauge_easy|frag_plus_15_pst&prs', 'gauge_hard|fail_frag_minus_100', 'frag_plus_5_side_light', 'visual_hide_hp', 'frag_plus_5_side_conflict', 'challenge_fullcombo_0gauge', 'gauge_overflow', 'gauge_easy|note_mirror', 'note_mirror', 'visual_tomato_pack_tonesphere',
|
||||
'frag_rng_ayu', 'gaugestart_30|gaugegain_70', 'combo_100-frag_1', 'audio_gcemptyhit_pack_groovecoaster', 'gauge_saya', 'gauge_chuni', 'kantandeshou', 'gauge_haruna', 'frags_nono', 'gauge_pandora', 'gauge_regulus', 'omatsuri_daynight', 'sometimes(note_mirror|frag_plus_5)', 'scoreclear_aa|visual_scoregauge', 'gauge_tempest', 'gauge_hard', 'gauge_ilith_summer', 'frags_kou', 'visual_ink', 'shirabe_entry_fee', 'frags_yume', 'note_mirror|visual_hide_far', 'frags_ongeki', 'gauge_areus', 'gauge_seele', 'gauge_isabelle', 'gauge_exhaustion']
|
||||
'frag_rng_ayu', 'gaugestart_30|gaugegain_70', 'combo_100-frag_1', 'audio_gcemptyhit_pack_groovecoaster', 'gauge_saya', 'gauge_chuni', 'kantandeshou', 'gauge_haruna', 'frags_nono', 'gauge_pandora', 'gauge_regulus', 'omatsuri_daynight', 'sometimes(note_mirror|frag_plus_5)', 'scoreclear_aa|visual_scoregauge', 'gauge_tempest', 'gauge_hard', 'gauge_ilith_summer', 'frags_kou', 'visual_ink', 'shirabe_entry_fee', 'frags_yume', 'note_mirror|visual_hide_far', 'frags_ongeki', 'gauge_areus', 'gauge_seele', 'gauge_isabelle', 'gauge_exhaustion', 'skill_lagrange']
|
||||
return render_template('web/changechar.html', skill_ids=skill_ids)
|
||||
|
||||
|
||||
|
||||
@@ -195,6 +195,11 @@ def update_database():
|
||||
update_one_table(c1, c2, 'user_present')
|
||||
update_one_table(c1, c2, 'redeem')
|
||||
update_one_table(c1, c2, 'user_redeem')
|
||||
update_one_table(c1, c2, 'role')
|
||||
update_one_table(c1, c2, 'user_role')
|
||||
update_one_table(c1, c2, 'power')
|
||||
update_one_table(c1, c2, 'role_power')
|
||||
update_one_table(c1, c2, 'api_auth')
|
||||
|
||||
update_user_char(c2)
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import time
|
||||
|
||||
|
||||
def get_user_score(c, user_id, limit=-1):
|
||||
def get_user_score(c, user_id, limit=-1, offset=0):
|
||||
# 返回用户的所有歌曲数据,带排名,返回字典列表
|
||||
if limit >= 0:
|
||||
c.execute('''select * from best_score where user_id =:a order by rating DESC limit :b''',
|
||||
{'a': user_id, 'b': limit})
|
||||
c.execute('''select * from best_score where user_id =:a order by rating DESC limit :b offset :c''',
|
||||
{'a': user_id, 'b': limit, 'c': offset})
|
||||
else:
|
||||
c.execute(
|
||||
'''select * from best_score where user_id =:a order by rating DESC''', {'a': user_id})
|
||||
@@ -77,7 +77,7 @@ def get_user(c, user_id):
|
||||
|
||||
|
||||
def get_user_recent30(c, user_id):
|
||||
# 获取玩家recent30信息并计算这一部分的ptt,返回字典列表和一个值
|
||||
# 获取玩家recent30信息并计算recent10的ptt,返回字典列表和一个值
|
||||
c.execute('''select * from recent30 where user_id=:a''', {'a': user_id})
|
||||
sumr = 0
|
||||
x = c.fetchone()
|
||||
|
||||
Reference in New Issue
Block a user