mirror of
https://github.com/Lost-MSth/Arcaea-server.git
synced 2026-02-10 18:07:28 +08:00
Update to v2.8.5
This commit is contained in:
@@ -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)
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
66
latest version/server/func.py
Normal file
66
latest version/server/func.py
Normal 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
|
||||
})
|
||||
73
latest version/server/user.py
Normal file
73
latest version/server/user.py
Normal 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()
|
||||
Reference in New Issue
Block a user