Waiting for updating

+ new character
+ new byd map
+ new items
+ some new configs
+ two new operations in background
+ record email while registering
+ record ip while logging in
+ checking something before running and updating database automatically

building something about API

fix bugs:
about purchasing system
about hiding ptt
about login different accounts with same device
about some details

This is only a pre updating. Many things have been changed. It takes time to find some ways.
This commit is contained in:
Lost-MSth
2021-05-12 00:45:13 +08:00
parent 941a79ccc7
commit e3d81436d0
24 changed files with 1770 additions and 798 deletions

View 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

View 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)]

View 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': ''})

View 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

View 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}

View File

@@ -5,372 +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'
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']
def main(path='./'):
conn = sqlite3.connect(path+'arcaea_database.db')
c = conn.cursor()
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)
);''')
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']
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)
);''')
skill_id_uncap = ['', '', 'frags_kou', '', 'visual_ink', '', '', '', '', '', '', '', '', 'shirabe_entry_fee',
'', '', '', '', '', '', '', 'frags_yume', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '']
# 搭档初始化
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']
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]
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']
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]
skill_id_uncap = ['', '', 'frags_kou', '', 'visual_ink', '', '', '', '', '', '', '', '', 'shirabe_entry_fee',
'', '', '', '', '', '', '', 'frags_yume', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '']
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]
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, 90]
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]
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, 90]
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))
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, 90]
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(: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)'''
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]})
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
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:
discount_to = i['discount_to']
for j in i['items']:
if "_id" not in j:
_id = ''
return 0
def insert_items(c, items):
# 物品数据导入
for i in items:
if 'discount_from' not in i:
discount_from = -1
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})
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('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)
f = open('packs.json', 'r')
packs = json.load(f)
f.close()
insert_items(c, packs)
# api权限与权限组初始化
role = ['admin', 'user', 'selecter']
role_caption = ['管理员', '用户', '查询接口']
conn.commit()
conn.close()
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]
}
def arc_register(name: str, password: str):
def build_user_code(c):
return '123456789'
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})
def build_user_id(c):
return 2000000
conn.commit()
conn.close()
# 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]})
def arc_register(name: str, password: str):
def build_user_code(c):
return '123456789'
conn = sqlite3.connect('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})
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()

View 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"
}]
}]
}

View File

@@ -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
}]

View File

@@ -12,7 +12,6 @@
"items": [{
"id": "dataerror",
"type": "single",
"_id": "606f9f5636651a07ebc541f3",
"is_available": true
}],
"price": 100,
@@ -24,7 +23,6 @@
"items": [{
"id": "yourvoiceso",
"type": "single",
"_id": "606f9f5636651a07ebc54201",
"is_available": true
}],
"price": 100,
@@ -36,7 +34,6 @@
"items": [{
"id": "crosssoul",
"type": "single",
"_id": "606f9f5636651a07ebc5420e",
"is_available": true
}],
"price": 100,
@@ -48,7 +45,6 @@
"items": [{
"type": "single",
"id": "impurebird",
"_id": "606f9f5636651a07ebc541f4",
"is_available": true
}],
"price": 100,
@@ -60,7 +56,6 @@
"items": [{
"type": "single",
"id": "auxesia",
"_id": "606f9f5636651a07ebc5420f",
"is_available": true
}],
"price": 100,
@@ -72,7 +67,6 @@
"items": [{
"type": "single",
"id": "modelista",
"_id": "606f9f5636651a07ebc54202",
"is_available": true
}],
"price": 100,
@@ -84,7 +78,6 @@
"items": [{
"type": "single",
"id": "yozakurafubuki",
"_id": "606f9f5636651a07ebc541f5",
"is_available": true
}],
"price": 100,
@@ -96,7 +89,6 @@
"items": [{
"type": "single",
"id": "surrender",
"_id": "606f9f5636651a07ebc54210",
"is_available": true
}],
"price": 100,
@@ -108,7 +100,6 @@
"items": [{
"type": "single",
"id": "metallicpunisher",
"_id": "606f9f5636651a07ebc54203",
"is_available": true
}],
"price": 100,
@@ -118,7 +109,6 @@
"items": [{
"type": "single",
"id": "carminescythe",
"_id": "606f9f5636651a07ebc541f6",
"is_available": true
}],
"price": 100,
@@ -128,7 +118,6 @@
"items": [{
"type": "single",
"id": "bethere",
"_id": "606f9f5636651a07ebc54211",
"is_available": true
}],
"price": 100,
@@ -140,7 +129,6 @@
"items": [{
"type": "single",
"id": "callmyname",
"_id": "606f9f5636651a07ebc5421c",
"is_available": true
}],
"price": 100,
@@ -152,7 +140,6 @@
"items": [{
"type": "single",
"id": "fallensquare",
"_id": "606f9f5636651a07ebc541e9",
"is_available": true
}],
"price": 100,
@@ -164,7 +151,6 @@
"items": [{
"type": "single",
"id": "dropdead",
"_id": "606f9f5636651a07ebc541f7",
"is_available": true
}],
"price": 100,
@@ -176,7 +162,6 @@
"items": [{
"type": "single",
"id": "alexandrite",
"_id": "606f9f5636651a07ebc54212",
"is_available": true
}],
"price": 100,
@@ -188,7 +173,6 @@
"items": [{
"type": "single",
"id": "astraltale",
"_id": "606f9f5636651a07ebc54205",
"is_available": true
}],
"price": 100,
@@ -200,7 +184,6 @@
"items": [{
"type": "single",
"id": "phantasia",
"_id": "606f9f5636651a07ebc541ea",
"is_available": true
}],
"price": 100,
@@ -210,7 +193,6 @@
"items": [{
"type": "single",
"id": "empireofwinter",
"_id": "606f9f5636651a07ebc541f8",
"is_available": true
}],
"price": 100,
@@ -220,7 +202,6 @@
"items": [{
"type": "single",
"id": "libertas",
"_id": "606f9f5636651a07ebc5421e",
"is_available": true
}],
"price": 100,
@@ -232,7 +213,6 @@
"items": [{
"type": "single",
"id": "dottodot",
"_id": "606f9f5636651a07ebc541eb",
"is_available": true
}],
"price": 100,
@@ -244,7 +224,6 @@
"items": [{
"type": "single",
"id": "dreadnought",
"_id": "606f9f5636651a07ebc5421f",
"is_available": true
}],
"price": 100,
@@ -256,7 +235,6 @@
"items": [{
"type": "single",
"id": "mirzam",
"_id": "606f9f5636651a07ebc541ec",
"is_available": true
}],
"price": 100,
@@ -268,7 +246,6 @@
"items": [{
"type": "single",
"id": "heavenlycaress",
"_id": "606f9f5636651a07ebc541fa",
"is_available": true
}],
"price": 100,
@@ -280,7 +257,6 @@
"items": [{
"type": "single",
"id": "filament",
"_id": "606f9f5636651a07ebc54215",
"is_available": true
}],
"price": 100,
@@ -292,7 +268,6 @@
"items": [{
"type": "single",
"id": "avantraze",
"_id": "606f9f5636651a07ebc54220",
"is_available": true
}],
"price": 100,
@@ -302,7 +277,6 @@
"items": [{
"type": "single",
"id": "battlenoone",
"_id": "606f9f5636651a07ebc54208",
"is_available": true
}],
"price": 100,
@@ -314,7 +288,6 @@
"items": [{
"type": "single",
"id": "saikyostronger",
"_id": "606f9f5636651a07ebc541ed",
"is_available": true
}],
"price": 100,
@@ -324,7 +297,6 @@
"items": [{
"type": "single",
"id": "izana",
"_id": "606f9f5636651a07ebc541fb",
"is_available": true
}],
"price": 100,
@@ -336,7 +308,6 @@
"items": [{
"type": "single",
"id": "einherjar",
"_id": "606f9f5636651a07ebc54216",
"is_available": true
}],
"price": 100,
@@ -348,7 +319,6 @@
"items": [{
"type": "single",
"id": "laqryma",
"_id": "606f9f5636651a07ebc54221",
"is_available": true
}],
"price": 100,
@@ -360,7 +330,6 @@
"items": [{
"type": "single",
"id": "amygdata",
"_id": "606f9f5636651a07ebc54209",
"is_available": true
}],
"price": 100,
@@ -372,7 +341,6 @@
"items": [{
"type": "single",
"id": "altale",
"_id": "606f9f5636651a07ebc541ee",
"is_available": true
}],
"price": 100,
@@ -384,7 +352,6 @@
"items": [{
"type": "single",
"id": "feelssoright",
"_id": "606f9f5636651a07ebc54222",
"is_available": true
}],
"price": 100,
@@ -396,7 +363,6 @@
"items": [{
"type": "single",
"id": "scarletcage",
"_id": "606f9f5636651a07ebc5420a",
"is_available": true
}],
"price": 100,
@@ -406,7 +372,6 @@
"items": [{
"type": "single",
"id": "teriqma",
"_id": "606f9f5636651a07ebc541ef",
"is_available": true
}],
"price": 100,
@@ -416,7 +381,6 @@
"items": [{
"type": "single",
"id": "mahoroba",
"_id": "606f9f5636651a07ebc541fd",
"is_available": true
}],
"price": 100,
@@ -428,7 +392,6 @@
"items": [{
"type": "single",
"id": "badtek",
"_id": "606f9f5636651a07ebc54223",
"is_available": true
}],
"price": 100,
@@ -440,7 +403,6 @@
"items": [{
"type": "single",
"id": "maliciousmischance",
"_id": "606f9f5636651a07ebc54218",
"is_available": true
}],
"price": 100,
@@ -450,7 +412,6 @@
"items": [{
"type": "single",
"id": "buchigireberserker",
"_id": "606f9f5636651a07ebc541f0",
"is_available": true
}],
"price": 100,
@@ -460,7 +421,6 @@
"items": [{
"type": "single",
"id": "galaxyfriends",
"_id": "606f9f5636651a07ebc541fe",
"is_available": true
}],
"price": 100,
@@ -472,7 +432,6 @@
"items": [{
"type": "single",
"id": "xeraphinite",
"_id": "606f9f5636651a07ebc54219",
"is_available": true
}],
"orig_price": 100,
@@ -482,7 +441,6 @@
"items": [{
"type": "single",
"id": "xanatos",
"_id": "606f9f5636651a07ebc54224",
"is_available": true
}],
"price": 100,
@@ -492,7 +450,6 @@
"items": [{
"type": "single",
"id": "attraqtia",
"_id": "606f9f5636651a07ebc5420d",
"is_available": true
}],
"orig_price": 100,
@@ -502,7 +459,6 @@
"items": [{
"type": "single",
"id": "gimmedablood",
"_id": "606f9f5636651a07ebc541f2",
"is_available": true
}],
"orig_price": 100,
@@ -512,7 +468,6 @@
"items": [{
"type": "single",
"id": "bassline",
"_id": "606f9f5636651a07ebc54200",
"is_available": true
}],
"orig_price": 100,

View File

@@ -10,6 +10,7 @@ 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
@@ -21,6 +22,95 @@ 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 处理交易时发生了错误
@@ -74,6 +164,7 @@ def error_return(error_code): # 错误返回
@app.route('/')
def hello():
return "Hello World!"
# 自定义路径
@app.route('/favicon.ico', methods=['GET']) # 图标
@@ -85,7 +176,7 @@ 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():
headers = request.headers
id_pwd = headers['Authorization']
@@ -96,7 +187,8 @@ def login():
else:
device_id = 'low_version'
token, error_code = server.auth.arc_login(name, password, device_id)
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
@@ -105,27 +197,28 @@ def login():
return error_return(error_code)
@app.route('/latte/13/user/', methods=['POST']) # 注册接口
@app.route(add_url_prefix('/user/'), methods=['POST']) # 注册接口
def register():
name = request.form['name']
password = request.form['password']
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')
@@ -136,7 +229,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']
@@ -155,7 +248,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:
@@ -174,7 +268,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']
@@ -200,7 +294,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'])
@@ -222,7 +316,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')
@@ -237,7 +332,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')
@@ -252,7 +347,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')
@@ -267,7 +362,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']
@@ -306,7 +401,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,
@@ -317,7 +413,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
@@ -332,7 +428,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)
@@ -345,7 +441,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']
@@ -366,7 +462,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']
@@ -388,7 +484,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)
@@ -400,7 +496,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}
@@ -417,20 +514,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,
@@ -438,7 +534,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({
@@ -451,7 +547,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']
@@ -461,7 +557,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)
@@ -475,7 +572,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')
@@ -511,7 +608,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:]
@@ -523,69 +621,6 @@ def sys_set(user_id, path):
def main():
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)
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!')
if Config.SSL_CERT and Config.SSL_KEY:
app.run(Config.HOST, Config.PORT, ssl_context=(
Config.SSL_CERT, Config.SSL_KEY))

View File

@@ -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):

View File

@@ -188,8 +188,22 @@ def arc_score_me(user_id, song_id, difficulty, limit=20):
return r
def calculate_rating(defnum, score):
# 计算rating
if score >= 10000000:
ptt = defnum + 2
elif score < 9800000:
ptt = defnum + (score-9500000) / 300000
if ptt < 0 and defnum != -10:
ptt = 0
else:
ptt = defnum + 1 + (score-9800000) / 200000
return ptt
def get_one_ptt(song_id, difficulty, score: int) -> float:
# 单曲ptt计算ptt为负说明没面定数数据
# 单曲ptt计算ptt为负说明没面定数数据
ptt = -10
with Connect('./database/arcsong.db') as c:
if difficulty == 0:
@@ -212,14 +226,7 @@ def get_one_ptt(song_id, difficulty, score: int) -> float:
if defnum <= 0:
defnum = -10 # 缺少难度的当做定数-10
if score >= 10000000:
ptt = defnum + 2
elif score < 9800000:
ptt = defnum + (score-9500000) / 300000
if ptt < 0 and defnum != -10:
ptt = 0
else:
ptt = defnum + 1 + (score-9800000) / 200000
ptt = calculate_rating(defnum, score)
return ptt
@@ -299,7 +306,7 @@ def get_user_ptt(c, user_id) -> int:
def update_recent30(c, user_id, song_id, rating, is_protected):
# 刷新r30这里的判断方法存疑
# 刷新r30这里的判断方法存疑这里的song_id结尾包含难度数字
def insert_r30table(c, user_id, a, b):
# 更新r30表
c.execute('''delete from recent30 where user_id = :a''',
@@ -587,6 +594,49 @@ def arc_score_check(user_id, song_id, difficulty, score, shiny_perfect_count, pe
return True
def refresh_all_score_rating():
# 刷新所有best成绩的rating
error = 'Unknown error.'
song_data = []
with Connect('./database/arcsong.db') as c:
with Connect() as c2:
c.execute(
'''select sid, rating_pst, rating_prs, rating_ftr, rating_byn from songs''')
x = c.fetchall()
if x:
song_list = [i[0] for i in song_data]
with Connect() as c:
c.execute('''update best_score set rating=0 where song_id not in ({0})'''.format(
','.join(['?']*len(song_list))), tuple(song_list))
for i in x:
for j in range(0, 4):
defnum = -10 # 没在库里的全部当做定数-10
if i is not None:
defnum = float(i[j+1]) / 10
if defnum <= 0:
defnum = -10 # 缺少难度的当做定数-10
c.execute('''select user_id, score from best_score where song_id=:a and difficulty=:b''', {
'a': i[0], 'b': j})
y = c.fetchall()
if y:
for k in y:
ptt = calculate_rating(defnum, k[1])
if ptt < 0:
ptt = 0
c.execute('''update best_score set rating=:a where user_id=:b and song_id=:c and difficulty=:d''', {
'a': ptt, 'b': k[0], 'c': i[0], 'd': j})
error = None
else:
error = 'No song data.'
return error
def arc_all_post(user_id, scores_data, clearlamps_data, clearedsongs_data, unlocklist_data, installid_data, devicemodelname_data, story_data):
# 向云端同步,无返回
with Connect() as c:
@@ -882,6 +932,36 @@ def arc_all_get(user_id):
"mi": 6,
"c": True,
"r": True
}, {
"ma": 9,
"mi": 1,
"c": True,
"r": True
}, {
"ma": 9,
"mi": 2,
"c": True,
"r": True
}, {
"ma": 9,
"mi": 3,
"c": True,
"r": True
}, {
"ma": 9,
"mi": 4,
"c": True,
"r": True
}, {
"ma": 9,
"mi": 5,
"c": True,
"r": True
}, {
"ma": 9,
"mi": 6,
"c": True,
"r": True
}, {
"ma": 6,
"mi": 1,
@@ -944,9 +1024,6 @@ def arc_all_get(user_id):
"r": True
}]
unlocklist_data = [{
"unlock_key": "worldvanquisher|2|0",
"complete": 1
}, {
"unlock_key": "worldvanquisher|1|0",
"complete": 1
}, {
@@ -955,42 +1032,48 @@ def arc_all_get(user_id):
}, {
"unlock_key": "viyellastears|2|0",
"complete": 1
}, {
"unlock_key": "viyellastears|1|0",
"complete": 1
}, {
"unlock_key": "viciousheroism|2|0",
"complete": 1
}, {
"unlock_key": "loschen|2|0",
"complete": 1
}, {
"unlock_key": "vector|2|0",
"complete": 1
}, {
"unlock_key": "corpssansorganes|2|0",
"complete": 1
}, {
"unlock_key": "valhallazero|2|0",
"complete": 1
}, {
"unlock_key": "tiferet|1|0",
"complete": 1
}, {
"unlock_key": "ouroboros|2|0",
"complete": 1
}, {
"unlock_key": "tiemedowngently|1|0",
"complete": 1
}, {
"unlock_key": "aegleseeker|2|3|crystalgravity|2",
"complete": 10
}, {
"unlock_key": "tempestissimo|0|101",
"complete": 100
}, {
"unlock_key": "syro|2|0",
"complete": 1
}, {
"unlock_key": "ringedgenesis|2|0",
"complete": 1
}, {
"unlock_key": "suomi|1|0",
"complete": 1
}, {
"unlock_key": "solitarydream|2|0",
"complete": 1
}, {
"unlock_key": "snowwhite|2|0",
"complete": 1
}, {
"unlock_key": "sheriruth|2|0",
"complete": 1
}, {
"unlock_key": "senkyou|2|0",
"complete": 1
@@ -1000,9 +1083,15 @@ def arc_all_get(user_id):
}, {
"unlock_key": "scarletlance|2|0",
"complete": 1
}, {
"unlock_key": "toaliceliddell|1|0",
"complete": 1
}, {
"unlock_key": "scarletlance|1|0",
"complete": 1
}, {
"unlock_key": "aterlbus|1|0",
"complete": 1
}, {
"unlock_key": "rugie|2|0",
"complete": 1
@@ -1018,24 +1107,33 @@ def arc_all_get(user_id):
}, {
"unlock_key": "reinvent|1|0",
"complete": 1
}, {
"unlock_key": "essenceoftwilight|1|0",
"complete": 1
}, {
"unlock_key": "redandblue|2|0",
"complete": 1
}, {
"unlock_key": "espebranch|1|0",
"complete": 1
}, {
"unlock_key": "redandblue|1|0",
"complete": 1
}, {
"unlock_key": "lostcivilization|2|0",
"complete": 1
}, {
"unlock_key": "supernova|1|0",
"complete": 1
}, {
"unlock_key": "rabbitintheblackroom|2|0",
"complete": 1
}, {
"unlock_key": "pragmatism|1|0",
"complete": 1
}, {
"unlock_key": "rabbitintheblackroom|1|0",
"complete": 1
}, {
"unlock_key": "worldexecuteme|1|0",
"complete": 1
}, {
"unlock_key": "ringedgenesis|2|0",
"complete": 1
}, {
"unlock_key": "quon|1|0",
"complete": 1
@@ -1048,8 +1146,17 @@ def arc_all_get(user_id):
}, {
"unlock_key": "supernova|2|0",
"complete": 1
}, {
"unlock_key": "halcyon|2|0",
"complete": 1
}, {
"unlock_key": "saikyostronger|2|3|einherjar|2",
"complete": 3
}, {
"unlock_key": "cyanine|2|0",
"complete": 1
}, {
"unlock_key": "saikyostronger|2|3|laqryma|2",
"complete": 1
}, {
"unlock_key": "purgatorium|1|0",
@@ -1058,17 +1165,23 @@ def arc_all_get(user_id):
"unlock_key": "pragmatism|2|0",
"complete": 1
}, {
"unlock_key": "ouroboros|2|0",
"unlock_key": "ringedgenesis|1|0",
"complete": 1
}, {
"unlock_key": "ouroboros|1|0",
"complete": 1
}, {
"unlock_key": "partyvinyl|2|0",
"complete": 1
}, {
"unlock_key": "oracle|1|0",
"complete": 1
}, {
"unlock_key": "onelastdrive|2|0",
"complete": 1
}, {
"unlock_key": "nirvluce|1|0",
"complete": 1
}, {
"unlock_key": "onelastdrive|1|0",
"complete": 1
@@ -1076,10 +1189,13 @@ def arc_all_get(user_id):
"unlock_key": "omegafour|1|0",
"complete": 1
}, {
"unlock_key": "oblivia|2|0",
"unlock_key": "harutopia|1|0",
"complete": 1
}, {
"unlock_key": "pragmatism|1|0",
"unlock_key": "anokumene|1|0",
"complete": 1
}, {
"unlock_key": "oblivia|2|0",
"complete": 1
}, {
"unlock_key": "nhelv|2|0",
@@ -1090,9 +1206,6 @@ def arc_all_get(user_id):
}, {
"unlock_key": "melodyoflove|2|0",
"complete": 1
}, {
"unlock_key": "saikyostronger|2|3|laqryma|2",
"complete": 1
}, {
"unlock_key": "omegafour|2|0",
"complete": 1
@@ -1102,6 +1215,12 @@ def arc_all_get(user_id):
}, {
"unlock_key": "lucifer|2|0",
"complete": 1
}, {
"unlock_key": "gloryroad|1|0",
"complete": 1
}, {
"unlock_key": "babaroque|2|0",
"complete": 1
}, {
"unlock_key": "lucifer|1|0",
"complete": 1
@@ -1111,21 +1230,36 @@ def arc_all_get(user_id):
}, {
"unlock_key": "tiemedowngently|2|0",
"complete": 1
}, {
"unlock_key": "solitarydream|1|0",
"complete": 1
}, {
"unlock_key": "lostdesire|1|0",
"complete": 1
}, {
"unlock_key": "viciousheroism|1|0",
"unlock_key": "lostcivilization|1|0",
"complete": 1
}, {
"unlock_key": "flyburg|1|0",
"unlock_key": "loschen|1|0",
"complete": 1
}, {
"unlock_key": "lostcivilization|2|0",
"unlock_key": "shadesoflight|1|0",
"complete": 1
}, {
"unlock_key": "kanagawa|2|0",
"complete": 1
}, {
"unlock_key": "grievouslady|1|101",
"complete": 100
}, {
"unlock_key": "infinityheaven|1|0",
"complete": 1
}, {
"unlock_key": "worldvanquisher|2|0",
"complete": 1
}, {
"unlock_key": "bookmaker|2|0",
"complete": 1
}, {
"unlock_key": "ignotus|2|0",
"complete": 1
@@ -1135,39 +1269,39 @@ def arc_all_get(user_id):
}, {
"unlock_key": "partyvinyl|1|0",
"complete": 1
}, {
"unlock_key": "viciousheroism|1|0",
"complete": 1
}, {
"unlock_key": "gloryroad|0|0",
"complete": 1
}, {
"unlock_key": "axiumcrisis|1|0",
"complete": 1
}, {
"unlock_key": "kanagawa|1|0",
"complete": 1
}, {
"unlock_key": "ifi|2|0",
"complete": 1
}, {
"unlock_key": "jump|2|0",
"complete": 1
}, {
"unlock_key": "nhelv|1|0",
"complete": 1
}, {
"unlock_key": "harutopia|2|0",
"complete": 1
}, {
"unlock_key": "revixy|1|0",
"complete": 1
}, {
"unlock_key": "aterlbus|1|0",
"complete": 1
}, {
"unlock_key": "linearaccelerator|2|0",
"complete": 1
}, {
"unlock_key": "guardina|2|0",
"complete": 1
}, {
"unlock_key": "corpssansorganes|2|0",
"complete": 1
}, {
"unlock_key": "linearaccelerator|1|0",
"complete": 1
}, {
"unlock_key": "guardina|1|0",
"complete": 1
}, {
"unlock_key": "lethaeus|1|0",
"complete": 1
}, {
"unlock_key": "saikyostronger|2|0",
"complete": 1
@@ -1180,21 +1314,18 @@ def arc_all_get(user_id):
}, {
"unlock_key": "oshamascramble|2|0",
"complete": 1
}, {
"unlock_key": "aiueoon|2|0",
"complete": 1
}, {
"unlock_key": "blaster|2|0",
"complete": 1
}, {
"unlock_key": "grievouslady|2|101",
"complete": 100
}, {
"unlock_key": "partyvinyl|2|0",
"complete": 1
}, {
"unlock_key": "darakunosono|1|0",
"complete": 1
}, {
"unlock_key": "grievouslady|1|101",
"complete": 100
}, {
"unlock_key": "valhallazero|1|0",
"complete": 1
@@ -1207,9 +1338,15 @@ def arc_all_get(user_id):
}, {
"unlock_key": "gothiveofra|1|0",
"complete": 1
}, {
"unlock_key": "cyanine|1|0",
"complete": 1
}, {
"unlock_key": "tempestissimo|3|101",
"complete": 100
}, {
"unlock_key": "overwhelm|2|0",
"complete": 1
}, {
"unlock_key": "chronostasis|2|0",
"complete": 1
@@ -1217,19 +1354,13 @@ def arc_all_get(user_id):
"unlock_key": "gloryroad|2|0",
"complete": 1
}, {
"unlock_key": "supernova|1|0",
"unlock_key": "etherstrike|1|0",
"complete": 1
}, {
"unlock_key": "singularity|2|0",
"complete": 1
}, {
"unlock_key": "gloryroad|0|0",
"complete": 1
}, {
"unlock_key": "shadesoflight|1|0",
"complete": 1
}, {
"unlock_key": "kanagawa|2|0",
"unlock_key": "buchigireberserker|2|3|gothiveofra|2",
"complete": 1
}, {
"unlock_key": "genesis|1|0",
@@ -1240,9 +1371,15 @@ def arc_all_get(user_id):
}, {
"unlock_key": "freefall|2|0",
"complete": 1
}, {
"unlock_key": "revixy|1|0",
"complete": 1
}, {
"unlock_key": "fractureray|2|101",
"complete": 100
}, {
"unlock_key": "goodtek|1|0",
"complete": 1
}, {
"unlock_key": "monochromeprincess|2|0",
"complete": 1
@@ -1258,29 +1395,29 @@ def arc_all_get(user_id):
}, {
"unlock_key": "clotho|2|0",
"complete": 1
}, {
"unlock_key": "quon|2|0",
"complete": 1
}, {
"unlock_key": "gou|2|0",
"complete": 1
}, {
"unlock_key": "felis|2|0",
"complete": 1
}, {
"unlock_key": "cyaegha|2|0",
"complete": 1
}, {
"unlock_key": "qualia|1|0",
"complete": 1
}, {
"unlock_key": "buchigireberserker|2|0",
"complete": 1
}, {
"unlock_key": "etherstrike|2|0",
"complete": 1
}, {
"unlock_key": "etherstrike|1|0",
"complete": 1
}, {
"unlock_key": "syro|1|0",
"complete": 1
}, {
"unlock_key": "anokumene|2|0",
"complete": 1
}, {
"unlock_key": "essenceoftwilight|2|0",
"unlock_key": "flyburg|1|0",
"complete": 1
}, {
"unlock_key": "shadesoflight|2|0",
@@ -1289,26 +1426,23 @@ def arc_all_get(user_id):
"unlock_key": "espebranch|2|0",
"complete": 1
}, {
"unlock_key": "tempestissimo|1|101",
"complete": 100
}, {
"unlock_key": "nhelv|1|0",
"unlock_key": "aterlbus|2|0",
"complete": 1
}, {
"unlock_key": "conflict|1|0",
"complete": 1
}, {
"unlock_key": "espebranch|1|0",
"complete": 1
}, {
"unlock_key": "lostcivilization|1|0",
"unlock_key": "dreaminattraction|2|0",
"complete": 1
}, {
"unlock_key": "goodtek|2|0",
"complete": 1
}, {
"unlock_key": "infinityheaven|2|0",
"complete": 1
}, {
"unlock_key": "dandelion|2|0",
"complete": 1
}, {
"unlock_key": "blrink|2|0",
"complete": 1
}, {
"unlock_key": "suomi|2|0",
"complete": 1
@@ -1321,104 +1455,104 @@ def arc_all_get(user_id):
}, {
"unlock_key": "cyberneciacatharsis|1|0",
"complete": 1
}, {
"unlock_key": "quon|2|0",
"complete": 1
}, {
"unlock_key": "chronostasis|1|0",
"complete": 1
}, {
"unlock_key": "bookmaker|2|0",
"complete": 1
}, {
"unlock_key": "heavensdoor|1|0",
"complete": 1
}, {
"unlock_key": "tempestissimo|2|101",
"complete": 100
}, {
"unlock_key": "cyaegha|2|0",
"complete": 1
}, {
"unlock_key": "axiumcrisis|2|0",
"complete": 1
}, {
"unlock_key": "blrink|2|0",
"complete": 1
}, {
"unlock_key": "rise|1|0",
"complete": 1
}, {
"unlock_key": "cyanine|1|0",
"complete": 1
}, {
"unlock_key": "corpssansorganes|0|0",
"complete": 1
}, {
"unlock_key": "vector|1|0",
"complete": 1
}, {
"unlock_key": "infinityheaven|2|0",
"complete": 1
}, {
"unlock_key": "essenceoftwilight|1|0",
"complete": 1
}, {
"unlock_key": "conflict|2|0",
"complete": 1
}, {
"unlock_key": "singularity|1|0",
"complete": 1
}, {
"unlock_key": "harutopia|1|0",
"complete": 1
}, {
"unlock_key": "cyberneciacatharsis|2|0",
"complete": 1
}, {
"unlock_key": "ignotus|1|0",
"complete": 1
}, {
"unlock_key": "nirvluce|1|0",
"complete": 1
}, {
"unlock_key": "monochromeprincess|1|0",
"complete": 1
}, {
"unlock_key": "lethaeus|1|0",
"complete": 1
}, {
"unlock_key": "clotho|1|0",
"complete": 1
}, {
"unlock_key": "aterlbus|2|0",
"unlock_key": "heavensdoor|1|0",
"complete": 1
}, {
"unlock_key": "dreaminattraction|2|0",
"unlock_key": "chronostasis|1|0",
"complete": 1
}, {
"unlock_key": "solitarydream|1|0",
"unlock_key": "lethaeus|2|0",
"complete": 1
}, {
"unlock_key": "ringedgenesis|1|0",
"unlock_key": "amazingmightyyyy|1|0",
"complete": 1
}, {
"unlock_key": "aegleseeker|0|103",
"complete": 1
}, {
"unlock_key": "buchigireberserker|2|3|ouroboros|2",
"complete": 1
}, {
"unlock_key": "oshamascramble|1|0",
"complete": 1
}, {
"unlock_key": "gothiveofra|2|0",
"complete": 1
}, {
"unlock_key": "linearaccelerator|2|0",
"complete": 1
}, {
"unlock_key": "corpssansorganes|1|0",
"complete": 1
}, {
"unlock_key": "buchigireberserker|2|0",
"unlock_key": "worldexecuteme|1|0",
"complete": 1
}, {
"unlock_key": "bookmaker|1|0",
"complete": 1
}, {
"unlock_key": "heavensdoor|2|0",
"unlock_key": "fractureray|0|101",
"complete": 100
}, {
"unlock_key": "singularity|1|0",
"complete": 1
}, {
"unlock_key": "genesis|2|0",
"unlock_key": "blaster|1|0",
"complete": 1
}, {
"unlock_key": "halcyon|2|0",
"unlock_key": "rise|1|0",
"complete": 1
}, {
"unlock_key": "axiumcrisis|2|0",
"complete": 1
}, {
"unlock_key": "tempestissimo|2|101",
"complete": 100
}, {
"unlock_key": "solitarydream|2|0",
"complete": 1
}, {
"unlock_key": "aegleseeker|1|103",
"complete": 103
}, {
"unlock_key": "amazingmightyyyy|2|0",
"complete": 1
}, {
"unlock_key": "cyaegha|1|0",
"complete": 1
}, {
"unlock_key": "linearaccelerator|1|0",
"complete": 1
}, {
"unlock_key": "tempestissimo|1|101",
"complete": 100
}, {
"unlock_key": "conflict|1|0",
"complete": 1
}, {
"unlock_key": "grievouslady|0|101",
"complete": 100
}, {
"unlock_key": "dreaminattraction|1|0",
"complete": 1
}, {
"unlock_key": "saikyostronger|2|3|izana|2",
@@ -1432,6 +1566,9 @@ def arc_all_get(user_id):
}, {
"unlock_key": "felis|1|0",
"complete": 1
}, {
"unlock_key": "overwhelm|1|0",
"complete": 1
}, {
"unlock_key": "toaliceliddell|2|0",
"complete": 1
@@ -1439,50 +1576,17 @@ def arc_all_get(user_id):
"unlock_key": "blrink|1|0",
"complete": 1
}, {
"unlock_key": "grievouslady|0|101",
"complete": 100
}, {
"unlock_key": "buchigireberserker|2|3|gothiveofra|2",
"unlock_key": "heavensdoor|2|0",
"complete": 1
}, {
"unlock_key": "kanagawa|1|0",
"unlock_key": "genesis|2|0",
"complete": 1
}, {
"unlock_key": "darakunosono|2|0",
"unlock_key": "viyellastears|1|0",
"complete": 1
}, {
"unlock_key": "freefall|1|0",
"complete": 1
}, {
"unlock_key": "nirvluce|2|0",
"complete": 1
}, {
"unlock_key": "cyanine|2|0",
"complete": 1
}, {
"unlock_key": "goodtek|1|0",
"complete": 1
}, {
"unlock_key": "buchigireberserker|2|3|ouroboros|2",
"complete": 1
}, {
"unlock_key": "fractureray|0|101",
"complete": 100
}, {
"unlock_key": "blaster|1|0",
"complete": 1
}, {
"unlock_key": "dreaminattraction|1|0",
"complete": 1
}, {
"unlock_key": "toaliceliddell|1|0",
"complete": 1
}, {
"unlock_key": "oshamascramble|1|0",
"complete": 1
}, {
"unlock_key": "gothiveofra|2|0",
"complete": 1
"unlock_key": "aegleseeker|2|103",
"complete": 103
}, {
"unlock_key": "tiferet|2|0",
"complete": 1
@@ -1490,37 +1594,46 @@ def arc_all_get(user_id):
"unlock_key": "grimheart|2|0",
"complete": 1
}, {
"unlock_key": "amazingmightyyyy|1|0",
"complete": 1
}, {
"unlock_key": "lethaeus|2|0",
"complete": 1
"unlock_key": "aegleseeker|2|3|farawaylight|2",
"complete": 10
}, {
"unlock_key": "rugie|1|0",
"complete": 1
}, {
"unlock_key": "gou|1|0",
"complete": 1
}, {
"unlock_key": "sheriruth|2|0",
"complete": 1
}, {
"unlock_key": "aegleseeker|2|3|paperwitch|2",
"complete": 10
}, {
"unlock_key": "sheriruth|1|0",
"complete": 1
}, {
"unlock_key": "babaroque|2|0",
"unlock_key": "aegleseeker|2|3|loschen|2",
"complete": 10
}, {
"unlock_key": "cyberneciacatharsis|2|0",
"complete": 1
}, {
"unlock_key": "aiueoon|2|0",
"unlock_key": "essenceoftwilight|2|0",
"complete": 1
}, {
"unlock_key": "gloryroad|1|0",
"unlock_key": "syro|1|0",
"complete": 1
}, {
"unlock_key": "cyaegha|1|0",
"unlock_key": "anokumene|2|0",
"complete": 1
}, {
"unlock_key": "amazingmightyyyy|2|0",
"unlock_key": "freefall|1|0",
"complete": 1
}, {
"unlock_key": "anokumene|1|0",
"unlock_key": "darakunosono|2|0",
"complete": 1
}, {
"unlock_key": "nirvluce|2|0",
"complete": 1
}]

View File

@@ -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, prog_boost)
values(:user_id, :name, :password, :join_date, :user_code, 0, 0, 0, 0, 0, 0, -1, 0, '', :memories, 0)
''', {'user_code': user_code, 'user_id': user_id, 'join_date': now, 'name': name, 'password': hash_pwd, 'memories': Config.DEFAULT_MEMORIES})
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,17 +171,16 @@ def auth_required(request):
def wrapped_view(*args, **kwargs):
user_id = None
with Connect() as c:
headers = request.headers
if 'Authorization' in headers:
token = headers['Authorization']
token = token[7:]
user_id = token_get_id(token)
headers = request.headers
if 'Authorization' in headers:
token = headers['Authorization']
token = token[7:]
user_id = token_get_id(token)
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

View File

@@ -118,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]),
@@ -162,16 +166,34 @@ 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
@@ -199,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", "lifeispiano"],
"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,
@@ -207,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
@@ -220,7 +243,7 @@ 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
@@ -233,7 +256,7 @@ def arc_aggregate_big(user_id):
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')

View File

@@ -1,4 +1,23 @@
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):
@@ -21,6 +40,56 @@ def check_before_run(app):
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.')

View File

@@ -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()

View File

@@ -14,6 +14,16 @@ class Config():
--------------------
'''
'''
--------------------
游戏API地址前缀
Game API's URL prefix
'''
GAME_API_PREFIX = '/blockchain/14'
'''
--------------------
'''
'''
--------------------
SSL证书路径
@@ -59,6 +69,17 @@ class Config():
--------------------
'''
'''
--------------------
API接口完全控制权限Token留空则不使用
API interface full control permission Token
If you don't want to use it, leave it blank.
'''
API_TOKEN = ''
'''
--------------------
'''
'''
--------------------
歌曲下载地址前缀,留空则自动获取

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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 %}

View File

@@ -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():

View File

@@ -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)

View File

@@ -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()