Update to v2.8.5

This commit is contained in:
Lost-MSth
2022-04-28 18:29:26 +08:00
parent aa77a4c903
commit 96fbd26342
19 changed files with 708 additions and 330 deletions

View File

@@ -0,0 +1,8 @@
from flask import Blueprint
from setting import Config
from . import user
from . import auth
bp = Blueprint('server', __name__, url_prefix=Config.GAME_API_PREFIX)
bp.register_blueprint(user.bp)
bp.register_blueprint(auth.bp)

View File

@@ -1,187 +1,43 @@
import hashlib
import time
from server.sql import Connect
from flask import Blueprint, request, jsonify
import functools
import base64
from core.error import ArcError, NoAccess
from core.user import UserAuth, UserLogin
from core.sql import Connect
from .func import error_return
from setting import Config
from flask import jsonify
BAN_TIME = [1, 3, 7, 15, 31]
def arc_login(name: str, password: str, device_id: str, ip: str): # 登录判断
# 查询数据库中的user表验证账号密码返回并记录token和user_id多返回个error code和extra
# token采用user_id和时间戳连接后hash生成真的是瞎想的没用bear
# 密码和token的加密方式为 SHA-256
bp = Blueprint('auth', __name__, url_prefix='/auth')
error_code = 108
token = None
user_id = None
@bp.route('/login', methods=['POST']) # 登录接口
def login():
if 'AppVersion' in request.headers: # 版本检查
if Config.ALLOW_APPVERSION:
if request.headers['AppVersion'] not in Config.ALLOW_APPVERSION:
return error_return(NoAccess('Wrong app version.', 1203))
headers = request.headers
request.form['grant_type']
with Connect() as c:
hash_pwd = hashlib.sha256(password.encode("utf8")).hexdigest()
c.execute('''select user_id, password, ban_flag from user where name = :name''', {
'name': name})
x = c.fetchone()
if x is not None:
now = int(time.time() * 1000)
if x[2] is not None and x[2] != '':
# 自动封号检查
ban_timestamp = int(x[2].split(':', 1)[1])
if ban_timestamp > now:
return None, 105, {'remaining_ts': ban_timestamp-now}
if x[1] == '':
# 账号封禁
error_code = 106
elif x[1] == hash_pwd:
user_id = str(x[0])
token = hashlib.sha256(
(user_id + str(now)).encode("utf8")).hexdigest()
c.execute(
'''select login_device from login where user_id = :user_id''', {"user_id": user_id})
y = c.fetchall()
if y:
device_list = []
for i in y:
if i[0]:
device_list.append(i[0])
else:
device_list.append('')
should_delete_num = len(
device_list) + 1 - Config.LOGIN_DEVICE_NUMBER_LIMIT
if not Config.ALLOW_LOGIN_SAME_DEVICE:
if device_id in device_list: # 对相同设备进行删除
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
if should_delete_num >= 1: # 删掉多余token
if not Config.ALLOW_LOGIN_SAME_DEVICE and Config.ALLOW_BAN_MULTIDEVICE_USER_AUTO: # 自动封号检查
c.execute(
'''select count(*) from login where user_id=? and login_time>?''', (user_id, now-86400000))
if c.fetchone()[0] >= Config.LOGIN_DEVICE_NUMBER_LIMIT:
remaining_ts = arc_auto_ban(c, user_id, now)
return None, None, 105, {'remaining_ts': remaining_ts}
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 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
try:
id_pwd = headers['Authorization']
id_pwd = base64.b64decode(id_pwd[6:]).decode()
name, password = id_pwd.split(':', 1)
if 'DeviceId' in headers:
device_id = headers['DeviceId']
else:
# 密码错误
error_code = 104
else:
# 用户名错误
error_code = 104
device_id = 'low_version'
return token, user_id, error_code, None
user = UserLogin(c)
user.login(name, password, device_id, request.remote_addr)
return jsonify({"success": True, "token_type": "Bearer", 'user_id': user.user_id, 'access_token': user.token})
except ArcError as e:
return error_return(e)
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):
# 生成9位的user_code用的自然是随机
import random
flag = True
while flag:
user_code = ''.join([str(random.randint(0, 9)) for i in range(9)])
c.execute('''select exists(select * from user where user_code = :user_code)''',
{'user_code': user_code})
if c.fetchone() == (0,):
flag = False
return user_code
def build_user_id(c):
# 生成user_id往后加1
c.execute('''select max(user_id) from user''')
x = c.fetchone()
if x[0] is not None:
return x[0] + 1
else:
return 2000001
def insert_user_char(c, user_id):
# 为用户添加初始角色
c.execute('''insert into user_char values(?,?,?,?,?,?)''',
(user_id, 0, 1, 0, 0, 0))
c.execute('''insert into user_char values(?,?,?,?,?,?)''',
(user_id, 1, 1, 0, 0, 0))
c.execute('''select character_id, max_level, is_uncapped from character''')
x = c.fetchall()
if x:
for i in x:
exp = 25000 if i[1] == 30 else 10000
c.execute('''insert into user_char_full values(?,?,?,?,?,?)''',
(user_id, i[0], i[1], exp, i[2], 0))
user_id = None
token = None
error_code = 108
with Connect() as c:
hash_pwd = hashlib.sha256(password.encode("utf8")).hexdigest()
c.execute(
'''select exists(select * from user where name = :name)''', {'name': name})
if c.fetchone() == (0,):
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 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
else:
error_code = 102
else:
error_code = 101
return user_id, token, error_code
def token_get_id(token: str):
# 用token获取id没有考虑不同用户token相同情况说不定会有bug
user_id = None
with Connect() as c:
c.execute('''select user_id from login where access_token = :token''', {
'token': token})
x = c.fetchone()
if x is not None:
user_id = x[0]
return user_id
def code_get_id(user_code):
# 用user_code获取id
user_id = None
with Connect() as c:
c.execute('''select user_id from user where user_code = :a''',
{'a': user_code})
x = c.fetchone()
if x is not None:
user_id = x[0]
return user_id
return error_return()
def auth_required(request):
@@ -190,44 +46,22 @@ def auth_required(request):
@functools.wraps(view)
def wrapped_view(*args, **kwargs):
user_id = None
headers = request.headers
if 'AppVersion' in headers: # 版本检查
if Config.ALLOW_APPVERSION:
if headers['AppVersion'] not in Config.ALLOW_APPVERSION:
return jsonify({"success": False, "error_code": 1203})
return error_return(NoAccess('Wrong app version.', 1203))
if 'Authorization' in headers:
token = headers['Authorization']
token = token[7:]
user_id = token_get_id(token)
with Connect() as c:
try:
user = UserAuth(c)
user.token = headers['Authorization'][7:]
return view(user.token_get_id(), *args, **kwargs)
except ArcError as e:
return error_return(e)
if user_id is not None:
return view(user_id, *args, **kwargs)
else:
return jsonify({"success": False, "error_code": 108})
return error_return()
return wrapped_view
return decorator
def arc_auto_ban(c, user_id, now):
# 多设备自动封号机制,返回封号时长
c.execute('''delete from login where user_id=?''', (user_id, ))
c.execute('''select ban_flag from user where user_id=?''', (user_id,))
x = c.fetchone()
if x and x[0] != '' and x[0] is not None:
last_ban_time = int(x[0].split(':', 1)[0])
i = 0
while i < len(BAN_TIME) - 1 and BAN_TIME[i] <= last_ban_time:
i += 1
ban_time = BAN_TIME[i]
else:
ban_time = BAN_TIME[0]
ban_flag = ':'.join((str(ban_time), str(now + ban_time*24*60*60*1000)))
c.execute('''update user set ban_flag=? where user_id=?''',
(ban_flag, user_id))
return ban_time*24*60*60*1000

View File

@@ -636,6 +636,9 @@ class Constant:
}, {
"unlock_key": "nhelv|2|0",
"complete": 1
}, {
"unlock_key": "nekonote|2|0",
"complete": 1
}, {
"unlock_key": "memoryforest|1|0",
"complete": 1
@@ -681,6 +684,9 @@ class Constant:
}, {
"unlock_key": "lostdesire|1|0",
"complete": 1
}, {
"unlock_key": "nekonote|1|0",
"complete": 1
}, {
"unlock_key": "lostcivilization|1|0",
"complete": 1
@@ -777,12 +783,6 @@ class Constant:
}, {
"unlock_key": "gothiveofra|1|0",
"complete": 1
}, {
"unlock_key": "etherstrike|1|0",
"complete": 1
}, {
"unlock_key": "singularity|2|0",
"complete": 1
}, {
"unlock_key": "lightningscrew|1|0",
"complete": 1
@@ -801,6 +801,9 @@ class Constant:
}, {
"unlock_key": "pragmatism|2|0",
"complete": 1
}, {
"unlock_key": "stasis|2|0",
"complete": 1
}, {
"unlock_key": "oracle|2|0",
"complete": 1
@@ -861,6 +864,12 @@ class Constant:
}, {
"unlock_key": "etherstrike|2|0",
"complete": 1
}, {
"unlock_key": "singularity|2|0",
"complete": 1
}, {
"unlock_key": "etherstrike|1|0",
"complete": 1
}, {
"unlock_key": "flyburg|1|0",
"complete": 1
@@ -1125,6 +1134,9 @@ class Constant:
}, {
"unlock_key": "vector|2|0",
"complete": 1
}, {
"unlock_key": "stasis|1|0",
"complete": 1
}, {
"unlock_key": "loschen|2|0",
"complete": 1

View File

@@ -0,0 +1,66 @@
from flask import jsonify
from core.error import ArcError
default_error = ArcError('Unknown Error')
def error_return(e: ArcError = default_error): # 错误返回
# -7 处理交易时发生了错误
# -5 所有的曲目都已经下载完毕
# -4 您的账号已在别处登录
# -3 无法连接至服务器
# 2 Arcaea服务器正在维护
# 9 新版本请等待几分钟
# 100 无法在此ip地址下登录游戏
# 101 用户名占用
# 102 电子邮箱已注册
# 103 已有一个账号由此设备创建
# 104 用户名密码错误
# 105 24小时内登入两台设备
# 106 121 账户冻结
# 107 你没有足够的体力
# 113 活动已结束
# 114 该活动已结束,您的成绩不会提交
# 115 请输入有效的电子邮箱地址
# 120 封号警告
# 122 账户暂时冻结
# 123 账户被限制
# 124 你今天不能再使用这个IP地址创建新的账号
# 150 非常抱歉您已被限制使用此功能
# 151 目前无法使用此功能
# 401 用户不存在
# 403 无法连接至服务器
# 501 502 -6 此物品目前无法获取
# 504 无效的序列码
# 505 此序列码已被使用
# 506 你已拥有了此物品
# 601 好友列表已满
# 602 此用户已是好友
# 604 你不能加自己为好友
# 903 下载量超过了限制请24小时后重试
# 905 请在再次使用此功能前等待24小时
# 1001 设备数量达到上限
# 1002 此设备已使用过此功能
# 1201 房间已满
# 1202 房间号码无效
# 1203 请将Arcaea更新至最新版本
# 1205 此房间目前无法加入
# 9801 下载歌曲时发生问题,请再试一次
# 9802 保存歌曲时发生问题,请检查设备空间容量
# 9803 下载已取消
# 9905 没有在云端发现任何数据
# 9907 更新数据时发生了问题
# 9908 服务器只支持最新的版本请更新Arcaea
# 其它 发生未知错误
r = {"success": False, "error_code": e.error_code}
if e.extra_data:
r['extra'] = e.extra_data
return jsonify(r)
def success_return(value):
return jsonify({
"success": True,
"value": value
})

View File

@@ -0,0 +1,73 @@
from flask import Blueprint, request
from core.error import ArcError, NoAccess
from core.sql import Connect
from core.user import UserRegister, UserLogin, User, UserOnline
from core.character import UserCharacter
from .func import error_return, success_return
from .auth import auth_required
from setting import Config
bp = Blueprint('user', __name__, url_prefix='/user')
@bp.route('', methods=['POST']) # 注册接口
def register():
if 'AppVersion' in request.headers: # 版本检查
if Config.ALLOW_APPVERSION:
if request.headers['AppVersion'] not in Config.ALLOW_APPVERSION:
return error_return(NoAccess('Wrong app version.', 1203))
with Connect() as c:
try:
new_user = UserRegister(c)
new_user.set_name(request.form['name'])
new_user.set_password(request.form['password'])
new_user.set_email(request.form['email'])
if 'device_id' in request.form:
device_id = request.form['device_id']
else:
device_id = 'low_version'
new_user.register()
# 注册后自动登录
user = UserLogin(c)
user.login(new_user.name, new_user.password,
device_id, request.remote_addr)
return success_return({'user_id': user.user_id, 'access_token': user.token})
except ArcError as e:
return error_return(e)
return error_return()
@bp.route('/me/character', methods=['POST']) # 角色切换
@auth_required(request)
def character_change(user_id):
with Connect() as c:
try:
user = UserOnline(c, user_id)
user.change_character(
int(request.form['character']), request.form['skill_sealed'] == 'true')
return success_return({'user_id': user.user_id, 'character': user.character.character_id})
except ArcError as e:
return error_return(e)
return error_return()
# 角色觉醒切换
@bp.route('/me/character/<int:character_id>/toggle_uncap', methods=['POST'])
@auth_required(request)
def toggle_uncap(user_id, character_id):
with Connect() as c:
try:
user = User()
user.user_id = user_id
character = UserCharacter(c)
character.character_id = character_id
character.change_uncap_override(user)
character.select_character_info(user)
return success_return({'user_id': user.user_id, 'character': [character.to_dict]})
except ArcError as e:
return error_return(e)
return error_return()