mirror of
https://github.com/Lost-MSth/Arcaea-server.git
synced 2025-12-14 08:06:23 +08:00
[Enhance][Bug fix] Improve setting file & ...
- Fix a small bug that `best30` of API cannot have scores whose songs are not in database - At present the setting file can be a module or a file with some of options - Limiter can have multiple rules together now
This commit is contained in:
16
.gitignore
vendored
16
.gitignore
vendored
@@ -1,5 +1,19 @@
|
|||||||
|
# log files
|
||||||
*.log
|
*.log
|
||||||
|
|
||||||
|
# SSL cert
|
||||||
|
*.pem
|
||||||
|
*.key
|
||||||
|
|
||||||
|
# sqlite3 database
|
||||||
|
*.db
|
||||||
|
*.db.bak*
|
||||||
|
|
||||||
# Byte-compiled / optimized / DLL files
|
# Byte-compiled / optimized / DLL files
|
||||||
__pycache__/
|
__pycache__/
|
||||||
*.py[cod]
|
*.py[cod]
|
||||||
*$py.class
|
*$py.class
|
||||||
|
|
||||||
|
# setting/config files
|
||||||
|
latest version/config/
|
||||||
|
latest version/config.py
|
||||||
@@ -2,10 +2,10 @@ from functools import wraps
|
|||||||
from traceback import format_exc
|
from traceback import format_exc
|
||||||
|
|
||||||
from core.api_user import APIUser
|
from core.api_user import APIUser
|
||||||
|
from core.config_manager import Config
|
||||||
from core.error import ArcError, NoAccess, PostError
|
from core.error import ArcError, NoAccess, PostError
|
||||||
from core.sql import Connect
|
from core.sql import Connect
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
from setting import Config
|
|
||||||
|
|
||||||
from .api_code import error_return
|
from .api_code import error_return
|
||||||
|
|
||||||
|
|||||||
259
latest version/config.example.py
Normal file
259
latest version/config.example.py
Normal file
@@ -0,0 +1,259 @@
|
|||||||
|
class Config():
|
||||||
|
'''
|
||||||
|
This is the example setting file.
|
||||||
|
The user's setting file's name is `config.py`.
|
||||||
|
'''
|
||||||
|
|
||||||
|
'''
|
||||||
|
--------------------
|
||||||
|
主机的地址和端口号
|
||||||
|
Host and port of your server
|
||||||
|
'''
|
||||||
|
HOST = '0.0.0.0'
|
||||||
|
PORT = 80
|
||||||
|
'''
|
||||||
|
--------------------
|
||||||
|
'''
|
||||||
|
|
||||||
|
'''
|
||||||
|
--------------------
|
||||||
|
游戏API地址前缀
|
||||||
|
Game API's URL prefix
|
||||||
|
'''
|
||||||
|
GAME_API_PREFIX = '/join/21'
|
||||||
|
'''
|
||||||
|
--------------------
|
||||||
|
'''
|
||||||
|
|
||||||
|
'''
|
||||||
|
--------------------
|
||||||
|
允许使用的游戏版本,若为空,则默认全部允许
|
||||||
|
Allowed game versions
|
||||||
|
If it is blank, all are allowed.
|
||||||
|
'''
|
||||||
|
ALLOW_APPVERSION = ['3.12.6', '3.12.6c',
|
||||||
|
'4.0.256', '4.0.256c', '4.1.0', '4.1.0c']
|
||||||
|
'''
|
||||||
|
--------------------
|
||||||
|
'''
|
||||||
|
|
||||||
|
'''
|
||||||
|
--------------------
|
||||||
|
联机功能相关设置,请确保与Link Play服务器端的设置一致
|
||||||
|
Setting of your link play server
|
||||||
|
Please ensure that the settings on the side of Link Play server are consistent.
|
||||||
|
'''
|
||||||
|
# SET_LINKPLAY_SERVER_AS_SUB_PROCESS: 是否同时在本地启动Link Play服务器
|
||||||
|
# SET_LINKPLAY_SERVER_AS_SUB_PROCESS: If it is `True`, the link play server will run with the main server locally at the same time.
|
||||||
|
SET_LINKPLAY_SERVER_AS_SUB_PROCESS = True
|
||||||
|
# LINKPLAY_HOST: 对主服务器来说的Link Play服务器的地址
|
||||||
|
# LINKPLAY_HOST: The address of the linkplay server based on the main server. If it is blank, the link play feature will be disabled.
|
||||||
|
LINKPLAY_HOST = '0.0.0.0'
|
||||||
|
LINKPLAY_UDP_PORT = 10900
|
||||||
|
LINKPLAY_TCP_PORT = 10901
|
||||||
|
LINKPLAY_AUTHENTICATION = 'my_link_play_server'
|
||||||
|
# LINKPLAY_DISPLAY_HOST: 对客户端来说的Link Play服务器地址,如果为空,则自动获取
|
||||||
|
# LINKPLAY_DISPLAY_HOST: The address of the linkplay server based on the client. If it is blank, the host of link play server for the client will be obtained automatically.
|
||||||
|
LINKPLAY_DISPLAY_HOST = ''
|
||||||
|
'''
|
||||||
|
--------------------
|
||||||
|
'''
|
||||||
|
|
||||||
|
'''
|
||||||
|
--------------------
|
||||||
|
SSL证书路径
|
||||||
|
留空则使用HTTP
|
||||||
|
SSL certificate path
|
||||||
|
If left blank, use HTTP.
|
||||||
|
'''
|
||||||
|
SSL_CERT = '' # *.pem
|
||||||
|
SSL_KEY = '' # *.key
|
||||||
|
'''
|
||||||
|
--------------------
|
||||||
|
'''
|
||||||
|
|
||||||
|
'''
|
||||||
|
--------------------
|
||||||
|
愚人节模式开关
|
||||||
|
Switch of April Fool's Day
|
||||||
|
'''
|
||||||
|
IS_APRILFOOLS = True
|
||||||
|
'''
|
||||||
|
--------------------
|
||||||
|
'''
|
||||||
|
|
||||||
|
'''
|
||||||
|
--------------------
|
||||||
|
世界排名的最大显示数量
|
||||||
|
The largest number of global rank
|
||||||
|
'''
|
||||||
|
WORLD_RANK_MAX = 200
|
||||||
|
'''
|
||||||
|
--------------------
|
||||||
|
'''
|
||||||
|
|
||||||
|
'''
|
||||||
|
--------------------
|
||||||
|
世界模式当前活动图设置
|
||||||
|
Current available maps in world mode
|
||||||
|
'''
|
||||||
|
AVAILABLE_MAP = [] # Ex. ['test', 'test2']
|
||||||
|
'''
|
||||||
|
--------------------
|
||||||
|
'''
|
||||||
|
|
||||||
|
'''
|
||||||
|
--------------------
|
||||||
|
Web后台管理页面的用户名和密码
|
||||||
|
Username and password of web background management page
|
||||||
|
'''
|
||||||
|
USERNAME = 'admin'
|
||||||
|
PASSWORD = 'admin'
|
||||||
|
'''
|
||||||
|
--------------------
|
||||||
|
'''
|
||||||
|
|
||||||
|
'''
|
||||||
|
--------------------
|
||||||
|
Web后台管理页面的session秘钥,如果不知道是什么,请不要修改
|
||||||
|
Session key of web background management page
|
||||||
|
If you don't know what it is, please don't modify it.
|
||||||
|
'''
|
||||||
|
SECRET_KEY = '1145141919810'
|
||||||
|
'''
|
||||||
|
--------------------
|
||||||
|
'''
|
||||||
|
|
||||||
|
'''
|
||||||
|
--------------------
|
||||||
|
API接口完全控制权限Token,留空则不使用
|
||||||
|
API interface full control permission Token
|
||||||
|
If you don't want to use it, leave it blank.
|
||||||
|
'''
|
||||||
|
API_TOKEN = ''
|
||||||
|
'''
|
||||||
|
--------------------
|
||||||
|
'''
|
||||||
|
|
||||||
|
'''
|
||||||
|
--------------------
|
||||||
|
歌曲下载地址前缀,留空则自动获取
|
||||||
|
Song download address prefix
|
||||||
|
If left blank, it will be obtained automatically.
|
||||||
|
'''
|
||||||
|
DOWNLOAD_LINK_PREFIX = '' # http://***.com/download/
|
||||||
|
'''
|
||||||
|
--------------------
|
||||||
|
'''
|
||||||
|
|
||||||
|
'''
|
||||||
|
--------------------
|
||||||
|
玩家歌曲下载的24小时次数限制,每个文件算一次
|
||||||
|
Player's song download limit times in 24 hours, once per file
|
||||||
|
'''
|
||||||
|
DOWNLOAD_TIMES_LIMIT = 3000
|
||||||
|
'''
|
||||||
|
歌曲下载链接的有效时长,单位:秒
|
||||||
|
Effective duration of song download link, unit: seconds
|
||||||
|
'''
|
||||||
|
DOWNLOAD_TIME_GAP_LIMIT = 1000
|
||||||
|
'''
|
||||||
|
--------------------
|
||||||
|
'''
|
||||||
|
|
||||||
|
'''
|
||||||
|
--------------------
|
||||||
|
Arcaea登录的最大允许设备数量,最小值为1
|
||||||
|
The maximum number of devices allowed to log in Arcaea, minimum: 1
|
||||||
|
'''
|
||||||
|
LOGIN_DEVICE_NUMBER_LIMIT = 1
|
||||||
|
'''
|
||||||
|
是否允许同设备多应用共存登录
|
||||||
|
请注意,这个选项设置为True时,下一个选项将自动变为False
|
||||||
|
If logging in from multiple applications on the same device is allowed
|
||||||
|
Note that when this option is set to True, the next option automatically becomes False
|
||||||
|
'''
|
||||||
|
ALLOW_LOGIN_SAME_DEVICE = False
|
||||||
|
'''
|
||||||
|
24小时内登陆设备数超过最大允许设备数量时,是否自动封号(1天、3天、7天、15天、31天)
|
||||||
|
When the number of login devices exceeds the maximum number of devices allowed to log in Arcaea within 24 hours, whether the account will be automatically banned (1 day, 3 days, 7 days, 15 days, 31 days)
|
||||||
|
'''
|
||||||
|
ALLOW_BAN_MULTIDEVICE_USER_AUTO = True
|
||||||
|
'''
|
||||||
|
--------------------
|
||||||
|
'''
|
||||||
|
|
||||||
|
'''
|
||||||
|
--------------------
|
||||||
|
是否记录详细的服务器日志
|
||||||
|
If recording detailed server logs is enabled
|
||||||
|
'''
|
||||||
|
ALLOW_INFO_LOG = False
|
||||||
|
ALLOW_WARNING_LOG = False
|
||||||
|
'''
|
||||||
|
--------------------
|
||||||
|
'''
|
||||||
|
|
||||||
|
'''
|
||||||
|
--------------------
|
||||||
|
用户注册时的默认记忆源点数量
|
||||||
|
The default amount of memories at the time of user registration
|
||||||
|
'''
|
||||||
|
DEFAULT_MEMORIES = 0
|
||||||
|
'''
|
||||||
|
--------------------
|
||||||
|
'''
|
||||||
|
|
||||||
|
'''
|
||||||
|
--------------------
|
||||||
|
数据库更新时,是否采用最新的角色数据,如果你想采用最新的官方角色数据
|
||||||
|
注意:如果是,旧的数据将丢失;如果否,某些角色的数据变动将无法同步
|
||||||
|
If using the latest character data when updating database. If you want to only keep newest official character data, please set it `True`.
|
||||||
|
Note: If `True`, the old data will be lost; If `False`, the data changes of some characters will not be synchronized.
|
||||||
|
'''
|
||||||
|
UPDATE_WITH_NEW_CHARACTER_DATA = True
|
||||||
|
'''
|
||||||
|
--------------------
|
||||||
|
'''
|
||||||
|
|
||||||
|
'''
|
||||||
|
--------------------
|
||||||
|
是否全解锁搭档
|
||||||
|
If unlocking all partners is enabled
|
||||||
|
'''
|
||||||
|
CHARACTER_FULL_UNLOCK = True
|
||||||
|
'''
|
||||||
|
--------------------
|
||||||
|
'''
|
||||||
|
|
||||||
|
'''
|
||||||
|
--------------------
|
||||||
|
是否全解锁世界歌曲
|
||||||
|
If unlocking all world songs is enabled
|
||||||
|
'''
|
||||||
|
WORLD_SONG_FULL_UNLOCK = True
|
||||||
|
'''
|
||||||
|
--------------------
|
||||||
|
'''
|
||||||
|
|
||||||
|
'''
|
||||||
|
--------------------
|
||||||
|
是否全解锁世界场景
|
||||||
|
If unlocking all world sceneries is enabled
|
||||||
|
'''
|
||||||
|
WORLD_SCENERY_FULL_UNLOCK = True
|
||||||
|
'''
|
||||||
|
--------------------
|
||||||
|
'''
|
||||||
|
|
||||||
|
'''
|
||||||
|
--------------------
|
||||||
|
是否强制使用全解锁云端存档
|
||||||
|
If forcing full unlocked cloud save is enabled
|
||||||
|
请注意,当前对于最终结局的判定为固定在`Testify`解锁之前
|
||||||
|
Please note that the current setting of the finale state is before the unlock of `Testify`
|
||||||
|
'''
|
||||||
|
SAVE_FULL_UNLOCK = False
|
||||||
|
'''
|
||||||
|
--------------------
|
||||||
|
'''
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
from setting import Config
|
from .config_manager import Config
|
||||||
from .error import ArcError, InputError, NoData, ItemNotEnough
|
|
||||||
from .constant import Constant
|
from .constant import Constant
|
||||||
|
from .error import ArcError, InputError, ItemNotEnough, NoData
|
||||||
from .item import Item, ItemCore
|
from .item import Item, ItemCore
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
81
latest version/core/config_manager.py
Normal file
81
latest version/core/config_manager.py
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
class Config:
|
||||||
|
'''
|
||||||
|
Default config
|
||||||
|
'''
|
||||||
|
|
||||||
|
HOST = '0.0.0.0'
|
||||||
|
PORT = 80
|
||||||
|
|
||||||
|
GAME_API_PREFIX = '/join/21'
|
||||||
|
|
||||||
|
ALLOW_APPVERSION = [] # list[str]
|
||||||
|
|
||||||
|
SET_LINKPLAY_SERVER_AS_SUB_PROCESS = True
|
||||||
|
|
||||||
|
LINKPLAY_HOST = '0.0.0.0'
|
||||||
|
LINKPLAY_UDP_PORT = 10900
|
||||||
|
LINKPLAY_TCP_PORT = 10901
|
||||||
|
LINKPLAY_AUTHENTICATION = 'my_link_play_server'
|
||||||
|
LINKPLAY_DISPLAY_HOST = ''
|
||||||
|
|
||||||
|
SSL_CERT = ''
|
||||||
|
SSL_KEY = ''
|
||||||
|
|
||||||
|
IS_APRILFOOLS = True
|
||||||
|
|
||||||
|
WORLD_RANK_MAX = 200
|
||||||
|
|
||||||
|
AVAILABLE_MAP = [] # list[str]
|
||||||
|
|
||||||
|
USERNAME = 'admin'
|
||||||
|
PASSWORD = 'admin'
|
||||||
|
|
||||||
|
SECRET_KEY = '1145141919810'
|
||||||
|
|
||||||
|
API_TOKEN = ''
|
||||||
|
|
||||||
|
DOWNLOAD_LINK_PREFIX = ''
|
||||||
|
|
||||||
|
DOWNLOAD_TIMES_LIMIT = 3000
|
||||||
|
DOWNLOAD_TIME_GAP_LIMIT = 1000
|
||||||
|
|
||||||
|
LOGIN_DEVICE_NUMBER_LIMIT = 1
|
||||||
|
ALLOW_LOGIN_SAME_DEVICE = False
|
||||||
|
ALLOW_BAN_MULTIDEVICE_USER_AUTO = True
|
||||||
|
|
||||||
|
ALLOW_INFO_LOG = False
|
||||||
|
ALLOW_WARNING_LOG = False
|
||||||
|
|
||||||
|
DEFAULT_MEMORIES = 0
|
||||||
|
|
||||||
|
UPDATE_WITH_NEW_CHARACTER_DATA = True
|
||||||
|
|
||||||
|
CHARACTER_FULL_UNLOCK = True
|
||||||
|
WORLD_SONG_FULL_UNLOCK = True
|
||||||
|
WORLD_SCENERY_FULL_UNLOCK = True
|
||||||
|
|
||||||
|
SAVE_FULL_UNLOCK = False
|
||||||
|
|
||||||
|
# ------------------------------------------
|
||||||
|
|
||||||
|
# You can change this to make another PTT mechanism.
|
||||||
|
BEST30_WEIGHT = 1 / 40
|
||||||
|
RECENT10_WEIGHT = 1 / 40
|
||||||
|
|
||||||
|
MAX_FRIEND_COUNT = 50
|
||||||
|
|
||||||
|
WORLD_MAP_FOLDER_PATH = './database/map/'
|
||||||
|
SONG_FILE_FOLDER_PATH = './database/songs/'
|
||||||
|
SONGLIST_FILE_PATH = './database/songs/songlist'
|
||||||
|
SQLITE_DATABASE_PATH = './database/arcaea_database.db'
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigManager:
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def load(config):
|
||||||
|
for k, v in config.__dict__.items():
|
||||||
|
if k.startswith('__') or k.endswith('__'):
|
||||||
|
continue
|
||||||
|
if hasattr(Config, k):
|
||||||
|
setattr(Config, k, v)
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
from setting import Config
|
from .config_manager import Config
|
||||||
|
|
||||||
|
|
||||||
class Constant:
|
class Constant:
|
||||||
@@ -9,6 +9,8 @@ class Constant:
|
|||||||
|
|
||||||
STAMINA_RECOVER_TICK = 1800000
|
STAMINA_RECOVER_TICK = 1800000
|
||||||
|
|
||||||
|
COURSE_STAMINA_COST = 4
|
||||||
|
|
||||||
CORE_EXP = 250
|
CORE_EXP = 250
|
||||||
|
|
||||||
LEVEL_STEPS = {1: 0, 2: 50, 3: 100, 4: 150, 5: 200, 6: 300, 7: 450, 8: 650, 9: 900, 10: 1200, 11: 1600, 12: 2100, 13: 2700, 14: 3400, 15: 4200, 16: 5100,
|
LEVEL_STEPS = {1: 0, 2: 50, 3: 100, 4: 150, 5: 200, 6: 300, 7: 450, 8: 650, 9: 900, 10: 1200, 11: 1600, 12: 2100, 13: 2700, 14: 3400, 15: 4200, 16: 5100,
|
||||||
@@ -19,19 +21,18 @@ class Constant:
|
|||||||
AYU_UNCAP_BONUS_PROGRESS = 5
|
AYU_UNCAP_BONUS_PROGRESS = 5
|
||||||
SKILL_FATALIS_WORLD_LOCKED_TIME = 3600000
|
SKILL_FATALIS_WORLD_LOCKED_TIME = 3600000
|
||||||
|
|
||||||
MAX_FRIEND_COUNT = 50
|
MAX_FRIEND_COUNT = Config.MAX_FRIEND_COUNT
|
||||||
|
|
||||||
MY_RANK_MAX_LOCAL_POSITION = 5
|
MY_RANK_MAX_LOCAL_POSITION = 5
|
||||||
MY_RANK_MAX_GLOBAL_POSITION = 9999
|
MY_RANK_MAX_GLOBAL_POSITION = 9999
|
||||||
|
|
||||||
# You can change this to make another PTT mechanism.
|
BEST30_WEIGHT = Config.BEST30_WEIGHT
|
||||||
BEST30_WEIGHT = 1 / 40
|
RECENT10_WEIGHT = Config.RECENT10_WEIGHT
|
||||||
RECENT10_WEIGHT = 1 / 40
|
|
||||||
|
|
||||||
WORLD_MAP_FOLDER_PATH = './database/map/'
|
WORLD_MAP_FOLDER_PATH = Config.WORLD_MAP_FOLDER_PATH
|
||||||
SONG_FILE_FOLDER_PATH = './database/songs/'
|
SONG_FILE_FOLDER_PATH = Config.SONG_FILE_FOLDER_PATH
|
||||||
SONGLIST_FILE_PATH = './database/songs/songlist'
|
SONGLIST_FILE_PATH = Config.SONGLIST_FILE_PATH
|
||||||
SQLITE_DATABASE_PATH = './database/arcaea_database.db'
|
SQLITE_DATABASE_PATH = Config.SQLITE_DATABASE_PATH
|
||||||
|
|
||||||
DOWNLOAD_TIMES_LIMIT = Config.DOWNLOAD_TIMES_LIMIT
|
DOWNLOAD_TIMES_LIMIT = Config.DOWNLOAD_TIMES_LIMIT
|
||||||
DOWNLOAD_TIME_GAP_LIMIT = Config.DOWNLOAD_TIME_GAP_LIMIT
|
DOWNLOAD_TIME_GAP_LIMIT = Config.DOWNLOAD_TIME_GAP_LIMIT
|
||||||
@@ -45,8 +46,6 @@ class Constant:
|
|||||||
LINKPLAY_UDP_PORT = Config.LINKPLAY_UDP_PORT
|
LINKPLAY_UDP_PORT = Config.LINKPLAY_UDP_PORT
|
||||||
LINKPLAY_AUTHENTICATION = Config.LINKPLAY_AUTHENTICATION
|
LINKPLAY_AUTHENTICATION = Config.LINKPLAY_AUTHENTICATION
|
||||||
|
|
||||||
COURSE_STAMINA_COST = 4
|
|
||||||
|
|
||||||
# Well, I can't say a word when I see this.
|
# Well, I can't say a word when I see this.
|
||||||
FINALE_SWITCH = [
|
FINALE_SWITCH = [
|
||||||
(0x0015F0, 0x00B032), (0x014C9A, 0x014408), (0x062585, 0x02783B),
|
(0x0015F0, 0x00B032), (0x014C9A, 0x014408), (0x062585, 0x02783B),
|
||||||
|
|||||||
@@ -1,91 +1,111 @@
|
|||||||
class ArcError(Exception):
|
class ArcError(Exception):
|
||||||
def __init__(self, message=None, error_code=108, api_error_code=-999, extra_data=None, status=200) -> None:
|
def __init__(self, message=None, error_code=108, api_error_code=-999, extra_data=None, status=200) -> None:
|
||||||
self.message = message
|
self.message: str = message
|
||||||
self.error_code = error_code
|
self.error_code: int = error_code
|
||||||
self.api_error_code = api_error_code
|
self.api_error_code: int = api_error_code
|
||||||
self.extra_data = extra_data
|
self.extra_data = extra_data
|
||||||
self.status = status
|
self.status: int = status
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return repr(self.message)
|
return repr(self.message)
|
||||||
|
|
||||||
|
|
||||||
class InputError(ArcError):
|
class InputError(ArcError):
|
||||||
# 输入类型错误
|
'''输入类型错误'''
|
||||||
|
|
||||||
def __init__(self, message=None, error_code=108, api_error_code=-100, extra_data=None, status=200) -> None:
|
def __init__(self, message=None, error_code=108, api_error_code=-100, extra_data=None, status=200) -> None:
|
||||||
super().__init__(message, error_code, api_error_code, extra_data, status)
|
super().__init__(message, error_code, api_error_code, extra_data, status)
|
||||||
|
|
||||||
|
|
||||||
class DataExist(ArcError):
|
class DataExist(ArcError):
|
||||||
# 数据存在
|
'''数据存在'''
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class NoData(ArcError):
|
class NoData(ArcError):
|
||||||
# 数据不存在
|
'''数据不存在'''
|
||||||
|
|
||||||
def __init__(self, message=None, error_code=108, api_error_code=-2, extra_data=None, status=200) -> None:
|
def __init__(self, message=None, error_code=108, api_error_code=-2, extra_data=None, status=200) -> None:
|
||||||
super().__init__(message, error_code, api_error_code, extra_data, status)
|
super().__init__(message, error_code, api_error_code, extra_data, status)
|
||||||
|
|
||||||
|
|
||||||
class PostError(ArcError):
|
class PostError(ArcError):
|
||||||
# 缺少输入
|
'''缺少输入'''
|
||||||
|
|
||||||
def __init__(self, message=None, error_code=108, api_error_code=-100, extra_data=None, status=200) -> None:
|
def __init__(self, message=None, error_code=108, api_error_code=-100, extra_data=None, status=200) -> None:
|
||||||
super().__init__(message, error_code, api_error_code, extra_data, status)
|
super().__init__(message, error_code, api_error_code, extra_data, status)
|
||||||
|
|
||||||
|
|
||||||
class UserBan(ArcError):
|
class UserBan(ArcError):
|
||||||
# 用户封禁
|
'''用户封禁'''
|
||||||
|
|
||||||
def __init__(self, message=None, error_code=121, api_error_code=-202, extra_data=None, status=200) -> None:
|
def __init__(self, message=None, error_code=121, api_error_code=-202, extra_data=None, status=200) -> None:
|
||||||
super().__init__(message, error_code, api_error_code, extra_data, status)
|
super().__init__(message, error_code, api_error_code, extra_data, status)
|
||||||
|
|
||||||
|
|
||||||
class ItemNotEnough(ArcError):
|
class ItemNotEnough(ArcError):
|
||||||
# 物品数量不足
|
'''物品数量不足'''
|
||||||
|
|
||||||
def __init__(self, message=None, error_code=-6, api_error_code=-999, extra_data=None, status=200) -> None:
|
def __init__(self, message=None, error_code=-6, api_error_code=-999, extra_data=None, status=200) -> None:
|
||||||
super().__init__(message, error_code, api_error_code, extra_data, status)
|
super().__init__(message, error_code, api_error_code, extra_data, status)
|
||||||
|
|
||||||
|
|
||||||
class ItemUnavailable(ArcError):
|
class ItemUnavailable(ArcError):
|
||||||
# 物品不可用
|
'''物品不可用'''
|
||||||
|
|
||||||
def __init__(self, message=None, error_code=-6, api_error_code=-999, extra_data=None, status=200) -> None:
|
def __init__(self, message=None, error_code=-6, api_error_code=-999, extra_data=None, status=200) -> None:
|
||||||
super().__init__(message, error_code, api_error_code, extra_data, status)
|
super().__init__(message, error_code, api_error_code, extra_data, status)
|
||||||
|
|
||||||
|
|
||||||
class RedeemUnavailable(ArcError):
|
class RedeemUnavailable(ArcError):
|
||||||
# 兑换码不可用
|
'''兑换码不可用'''
|
||||||
|
|
||||||
def __init__(self, message=None, error_code=505, api_error_code=-999, extra_data=None, status=200) -> None:
|
def __init__(self, message=None, error_code=505, api_error_code=-999, extra_data=None, status=200) -> None:
|
||||||
super().__init__(message, error_code, api_error_code, extra_data, status)
|
super().__init__(message, error_code, api_error_code, extra_data, status)
|
||||||
|
|
||||||
|
|
||||||
class MapLocked(ArcError):
|
class MapLocked(ArcError):
|
||||||
# 地图锁定
|
'''地图锁定'''
|
||||||
|
|
||||||
def __init__(self, message=None, error_code=108, api_error_code=-999, extra_data=None, status=200) -> None:
|
def __init__(self, message=None, error_code=108, api_error_code=-999, extra_data=None, status=200) -> None:
|
||||||
super().__init__(message, error_code, api_error_code, extra_data, status)
|
super().__init__(message, error_code, api_error_code, extra_data, status)
|
||||||
|
|
||||||
|
|
||||||
class StaminaNotEnough(ArcError):
|
class StaminaNotEnough(ArcError):
|
||||||
# 体力不足
|
'''体力不足'''
|
||||||
|
|
||||||
def __init__(self, message=None, error_code=107, api_error_code=-999, extra_data=None, status=200) -> None:
|
def __init__(self, message=None, error_code=107, api_error_code=-999, extra_data=None, status=200) -> None:
|
||||||
super().__init__(message, error_code, api_error_code, extra_data, status)
|
super().__init__(message, error_code, api_error_code, extra_data, status)
|
||||||
|
|
||||||
|
|
||||||
class TicketNotEnough(ArcError):
|
class TicketNotEnough(ArcError):
|
||||||
# 记忆源点不足
|
'''记忆源点不足'''
|
||||||
|
|
||||||
def __init__(self, message=None, error_code=-6, api_error_code=-999, extra_data=None, status=200) -> None:
|
def __init__(self, message=None, error_code=-6, api_error_code=-999, extra_data=None, status=200) -> None:
|
||||||
super().__init__(message, error_code, api_error_code, extra_data, status)
|
super().__init__(message, error_code, api_error_code, extra_data, status)
|
||||||
|
|
||||||
|
|
||||||
class FriendError(ArcError):
|
class FriendError(ArcError):
|
||||||
# 好友系统出错
|
'''好友系统出错'''
|
||||||
|
|
||||||
def __init__(self, message=None, error_code=108, api_error_code=-999, extra_data=None, status=200) -> None:
|
def __init__(self, message=None, error_code=108, api_error_code=-999, extra_data=None, status=200) -> None:
|
||||||
super().__init__(message, error_code, api_error_code, extra_data, status)
|
super().__init__(message, error_code, api_error_code, extra_data, status)
|
||||||
|
|
||||||
|
|
||||||
class NoAccess(ArcError):
|
class NoAccess(ArcError):
|
||||||
# 无权限
|
'''无权限'''
|
||||||
pass
|
|
||||||
|
def __init__(self, message=None, error_code=108, api_error_code=-999, extra_data=None, status=403) -> None:
|
||||||
|
super().__init__(message, error_code, api_error_code, extra_data, status)
|
||||||
|
|
||||||
|
|
||||||
class Timeout(ArcError):
|
class Timeout(ArcError):
|
||||||
# 超时
|
'''超时'''
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class RateLimit(ArcError):
|
||||||
|
'''频率达到限制'''
|
||||||
|
|
||||||
|
def __init__(self, message=None, error_code=108, api_error_code=-999, extra_data=None, status=429) -> None:
|
||||||
|
super().__init__(message, error_code, api_error_code, extra_data, status)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
|
from .config_manager import Config
|
||||||
from .error import InputError, ItemNotEnough, ItemUnavailable, NoData
|
from .error import InputError, ItemNotEnough, ItemUnavailable, NoData
|
||||||
from setting import Config
|
|
||||||
|
|
||||||
|
|
||||||
class Item:
|
class Item:
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from limits import parse, strategies
|
from limits import parse_many, strategies
|
||||||
from limits.storage import storage_from_string
|
from limits.storage import storage_from_string
|
||||||
|
|
||||||
|
|
||||||
@@ -6,23 +6,26 @@ class ArcLimiter:
|
|||||||
storage = storage_from_string("memory://")
|
storage = storage_from_string("memory://")
|
||||||
strategy = strategies.FixedWindowRateLimiter(storage)
|
strategy = strategies.FixedWindowRateLimiter(storage)
|
||||||
|
|
||||||
def __init__(self, limit: str = None, namespace: str = None):
|
def __init__(self, limit_str: str = None, namespace: str = None):
|
||||||
self._limit = None
|
self._limits = None
|
||||||
self.limit = limit
|
self.limits = limit_str
|
||||||
self.namespace = namespace
|
self.namespace = namespace
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def limit(self):
|
def limits(self):
|
||||||
return self._limit
|
return self._limits
|
||||||
|
|
||||||
@limit.setter
|
@limits.setter
|
||||||
def limit(self, value):
|
def limits(self, value: str):
|
||||||
if value is None:
|
if value is None:
|
||||||
return
|
return
|
||||||
self._limit = parse(value)
|
self._limits = parse_many(value)
|
||||||
|
|
||||||
def hit(self, key: str, cost: int = 1) -> bool:
|
def hit(self, key: str, cost: int = 1) -> bool:
|
||||||
return self.strategy.hit(self.limit, self.namespace, key, cost=cost)
|
flag = True
|
||||||
|
for limit in self.limits:
|
||||||
|
flag &= self.strategy.hit(limit, self.namespace, key, cost)
|
||||||
|
return flag
|
||||||
|
|
||||||
def test(self, key: str) -> bool:
|
def test(self, key: str) -> bool:
|
||||||
return self.strategy.test(self.limit, self.namespace, key)
|
return all(self.strategy.test(limit, self.namespace, key) for limit in self.limits)
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
import json
|
import json
|
||||||
from time import time
|
from time import time
|
||||||
|
|
||||||
from setting import Config
|
from .config_manager import Config
|
||||||
|
from .constant import Constant
|
||||||
from core.constant import Constant
|
|
||||||
|
|
||||||
from .error import InputError
|
from .error import InputError
|
||||||
from .util import md5
|
from .util import md5
|
||||||
|
|
||||||
|
|||||||
@@ -580,4 +580,5 @@ class UserScoreList:
|
|||||||
for score in self.scores:
|
for score in self.scores:
|
||||||
self.c.execute(
|
self.c.execute(
|
||||||
'''select name from chart where song_id = ?''', (score.song.song_id,))
|
'''select name from chart where song_id = ?''', (score.song.song_id,))
|
||||||
score.song.song_name = self.c.fetchone()[0]
|
x = self.c.fetchone()
|
||||||
|
score.song.song_name = x[0] if x else ''
|
||||||
|
|||||||
@@ -3,9 +3,8 @@ import hashlib
|
|||||||
import time
|
import time
|
||||||
from os import urandom
|
from os import urandom
|
||||||
|
|
||||||
from setting import Config
|
|
||||||
|
|
||||||
from .character import UserCharacter, UserCharacterList
|
from .character import UserCharacter, UserCharacterList
|
||||||
|
from .config_manager import Config
|
||||||
from .constant import Constant
|
from .constant import Constant
|
||||||
from .error import (ArcError, DataExist, FriendError, InputError, NoAccess,
|
from .error import (ArcError, DataExist, FriendError, InputError, NoAccess,
|
||||||
NoData, UserBan)
|
NoData, UserBan)
|
||||||
|
|||||||
@@ -207,11 +207,6 @@ def main(path='./'):
|
|||||||
time int,
|
time int,
|
||||||
primary key(user_id, song_id, file_name)
|
primary key(user_id, song_id, file_name)
|
||||||
);''')
|
);''')
|
||||||
# c.execute('''create table if not exists user_download(user_id int,
|
|
||||||
# time int,
|
|
||||||
# token text,
|
|
||||||
# primary key(user_id, time, token)
|
|
||||||
# );''')
|
|
||||||
c.execute('''create table if not exists item(item_id text,
|
c.execute('''create table if not exists item(item_id text,
|
||||||
type text,
|
type text,
|
||||||
is_available int,
|
is_available int,
|
||||||
|
|||||||
@@ -2,12 +2,19 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
from importlib import import_module
|
||||||
from logging.config import dictConfig
|
from logging.config import dictConfig
|
||||||
from multiprocessing import Process, set_start_method
|
from multiprocessing import Process, set_start_method
|
||||||
from traceback import format_exc
|
from traceback import format_exc
|
||||||
|
|
||||||
from flask import Flask, make_response, request, send_from_directory
|
from flask import Flask, make_response, request, send_from_directory
|
||||||
|
|
||||||
|
from core.config_manager import Config, ConfigManager
|
||||||
|
|
||||||
|
if os.path.exists('config.py') or os.path.exists('config'):
|
||||||
|
ConfigManager.load(import_module('config').Config)
|
||||||
|
|
||||||
|
|
||||||
import api
|
import api
|
||||||
import server
|
import server
|
||||||
import server.init
|
import server.init
|
||||||
@@ -16,10 +23,9 @@ import web.login
|
|||||||
from core.constant import Constant
|
from core.constant import Constant
|
||||||
from core.download import (UserDownload, get_only_3_song_ids,
|
from core.download import (UserDownload, get_only_3_song_ids,
|
||||||
initialize_songfile)
|
initialize_songfile)
|
||||||
from core.error import ArcError
|
from core.error import ArcError, NoAccess, RateLimit
|
||||||
from core.sql import Connect
|
from core.sql import Connect
|
||||||
from server.func import error_return
|
from server.func import error_return
|
||||||
from setting import Config
|
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
||||||
@@ -31,6 +37,7 @@ app = Flask(__name__)
|
|||||||
|
|
||||||
os.chdir(sys.path[0]) # 更改工作路径,以便于愉快使用相对路径
|
os.chdir(sys.path[0]) # 更改工作路径,以便于愉快使用相对路径
|
||||||
|
|
||||||
|
|
||||||
app.config.from_mapping(SECRET_KEY=Config.SECRET_KEY)
|
app.config.from_mapping(SECRET_KEY=Config.SECRET_KEY)
|
||||||
app.config['SESSION_TYPE'] = 'filesystem'
|
app.config['SESSION_TYPE'] = 'filesystem'
|
||||||
app.register_blueprint(web.login.bp)
|
app.register_blueprint(web.login.bp)
|
||||||
@@ -62,10 +69,9 @@ def download(file_path):
|
|||||||
x.song_id, x.file_name = file_path.split('/', 1)
|
x.song_id, x.file_name = file_path.split('/', 1)
|
||||||
x.select_for_check()
|
x.select_for_check()
|
||||||
if x.is_limited:
|
if x.is_limited:
|
||||||
raise ArcError(
|
raise RateLimit('You have reached the download limit.', 903)
|
||||||
'You have reached the download limit.', 903, status=429)
|
|
||||||
if not x.is_valid:
|
if not x.is_valid:
|
||||||
raise ArcError('Expired token.', status=403)
|
raise NoAccess('Expired token.')
|
||||||
x.download_hit()
|
x.download_hit()
|
||||||
# response = make_response()
|
# response = make_response()
|
||||||
# response.headers['Content-Type'] = 'application/octet-stream'
|
# response.headers['Content-Type'] = 'application/octet-stream'
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
|
from core.config_manager import Config
|
||||||
from flask import Blueprint
|
from flask import Blueprint
|
||||||
from setting import Config
|
|
||||||
|
|
||||||
from . import (auth, course, friend, multiplayer, others, present, purchase,
|
from . import (auth, course, friend, multiplayer, others, present, purchase,
|
||||||
score, user, world)
|
score, user, world)
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import base64
|
import base64
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
|
||||||
|
from core.config_manager import Config
|
||||||
from core.error import ArcError, NoAccess
|
from core.error import ArcError, NoAccess
|
||||||
from core.sql import Connect
|
from core.sql import Connect
|
||||||
from core.user import UserAuth, UserLogin
|
from core.user import UserAuth, UserLogin
|
||||||
from flask import Blueprint, jsonify, request
|
from flask import Blueprint, jsonify, request
|
||||||
from setting import Config
|
|
||||||
|
|
||||||
from .func import arc_try, error_return
|
from .func import arc_try, error_return
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
from functools import wraps
|
from functools import wraps
|
||||||
from traceback import format_exc
|
from traceback import format_exc
|
||||||
|
|
||||||
|
from core.config_manager import Config
|
||||||
from core.error import ArcError
|
from core.error import ArcError
|
||||||
from flask import current_app, jsonify
|
from flask import current_app, jsonify
|
||||||
from setting import Config
|
|
||||||
|
|
||||||
default_error = ArcError('Unknown Error', status=500)
|
default_error = ArcError('Unknown Error', status=500)
|
||||||
|
|
||||||
|
|||||||
@@ -25,10 +25,6 @@ def check_before_run(app):
|
|||||||
|
|
||||||
f = True
|
f = True
|
||||||
|
|
||||||
if not os.path.exists('setting.py'):
|
|
||||||
app.logger.warning('File `setting.py` is missing.')
|
|
||||||
f = False
|
|
||||||
|
|
||||||
if not os.path.exists('database'):
|
if not os.path.exists('database'):
|
||||||
app.logger.warning('Folder `database` is missing.')
|
app.logger.warning('Folder `database` is missing.')
|
||||||
f = False
|
f = False
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
|
from core.config_manager import Config
|
||||||
from core.error import ArcError
|
from core.error import ArcError
|
||||||
from core.linkplay import Player, RemoteMultiPlayer, Room
|
from core.linkplay import Player, RemoteMultiPlayer, Room
|
||||||
from core.sql import Connect
|
from core.sql import Connect
|
||||||
from flask import Blueprint, request
|
from flask import Blueprint, request
|
||||||
from setting import Config
|
|
||||||
|
|
||||||
from .auth import auth_required
|
from .auth import auth_required
|
||||||
from .func import arc_try, success_return
|
from .func import arc_try, success_return
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import json
|
|||||||
from urllib.parse import parse_qs, urlparse
|
from urllib.parse import parse_qs, urlparse
|
||||||
|
|
||||||
from core.download import DownloadList
|
from core.download import DownloadList
|
||||||
from core.error import ArcError
|
from core.error import RateLimit
|
||||||
from core.sql import Connect
|
from core.sql import Connect
|
||||||
from core.system import GameInfo
|
from core.system import GameInfo
|
||||||
from core.user import UserOnline
|
from core.user import UserOnline
|
||||||
@@ -34,7 +34,7 @@ def download_song(user_id):
|
|||||||
x.song_ids = request.args.getlist('sid')
|
x.song_ids = request.args.getlist('sid')
|
||||||
x.url_flag = json.loads(request.args.get('url', 'true'))
|
x.url_flag = json.loads(request.args.get('url', 'true'))
|
||||||
if x.url_flag and x.is_limited:
|
if x.url_flag and x.is_limited:
|
||||||
raise ArcError('You have reached the download limit.', 903)
|
raise RateLimit('You have reached the download limit.', 903)
|
||||||
|
|
||||||
x.add_songs()
|
x.add_songs()
|
||||||
return success_return(x.urls)
|
return success_return(x.urls)
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
from core.character import UserCharacter
|
from core.character import UserCharacter
|
||||||
|
from core.config_manager import Config
|
||||||
from core.error import ArcError, NoAccess
|
from core.error import ArcError, NoAccess
|
||||||
from core.item import ItemCore
|
from core.item import ItemCore
|
||||||
from core.save import SaveData
|
from core.save import SaveData
|
||||||
from core.sql import Connect
|
from core.sql import Connect
|
||||||
from core.user import User, UserLogin, UserOnline, UserRegister
|
from core.user import User, UserLogin, UserOnline, UserRegister
|
||||||
from flask import Blueprint, request
|
from flask import Blueprint, request
|
||||||
from setting import Config
|
|
||||||
|
|
||||||
from .auth import auth_required
|
from .auth import auth_required
|
||||||
from .func import arc_try, success_return
|
from .func import arc_try, success_return
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
#import sqlite3
|
|
||||||
from flask import (Blueprint, flash, g, redirect,
|
|
||||||
render_template, request, session, url_for)
|
|
||||||
import functools
|
import functools
|
||||||
from setting import Config
|
|
||||||
import hashlib
|
import hashlib
|
||||||
|
|
||||||
|
from core.config_manager import Config
|
||||||
|
from flask import (Blueprint, flash, g, redirect, render_template, request,
|
||||||
|
session, url_for)
|
||||||
|
|
||||||
bp = Blueprint('login', __name__, url_prefix='/web')
|
bp = Blueprint('login', __name__, url_prefix='/web')
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import os
|
|
||||||
from core.sql import Connect
|
|
||||||
import time
|
|
||||||
import hashlib
|
import hashlib
|
||||||
|
import os
|
||||||
|
import time
|
||||||
from random import Random
|
from random import Random
|
||||||
from setting import Config
|
|
||||||
|
from core.config_manager import Config
|
||||||
|
from core.sql import Connect
|
||||||
|
|
||||||
|
|
||||||
def int2b(x):
|
def int2b(x):
|
||||||
@@ -216,10 +217,10 @@ def update_database():
|
|||||||
update_one_table(c1, c2, 'api_login')
|
update_one_table(c1, c2, 'api_login')
|
||||||
update_one_table(c1, c2, 'chart')
|
update_one_table(c1, c2, 'chart')
|
||||||
update_one_table(c1, c2, 'user_course')
|
update_one_table(c1, c2, 'user_course')
|
||||||
update_one_table(c1, c2, 'course')
|
# update_one_table(c1, c2, 'course')
|
||||||
update_one_table(c1, c2, 'course_item')
|
# update_one_table(c1, c2, 'course_item')
|
||||||
update_one_table(c1, c2, 'course_chart')
|
# update_one_table(c1, c2, 'course_chart')
|
||||||
update_one_table(c1, c2, 'course_requirement')
|
# update_one_table(c1, c2, 'course_requirement')
|
||||||
|
|
||||||
update_one_table(c1, c2, 'user_char')
|
update_one_table(c1, c2, 'user_char')
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user