Skip to content

Commit

Permalink
Merge pull request #99 from Lost-MSth/dev
Browse files Browse the repository at this point in the history
Update to v2.11.1
  • Loading branch information
Lost-MSth authored Mar 24, 2023
2 parents 70d27be + 6f82e9b commit 1c58aeb
Show file tree
Hide file tree
Showing 48 changed files with 655 additions and 404 deletions.
18 changes: 6 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,18 +73,12 @@ It is just so interesting. What it can do is under exploration.
>
> Tips: When updating, please keep the original database in case of data loss.
### Version 2.11.0

- 适用于Arcaea 4.3.0版本 For Arcaea 4.3.0
- 新搭档 **霞玛(大~宇~宙)****米露可(大~宇~宙)****紫黑****百合咲美香** 已解锁 Unlock the character **Shama(UNiVERSE)**, **Milk(UNiVERSE)**, **Shikoku**, **Mika Yurisaki**.
- 搭档 **依莉丝** 已觉醒 Uncap the character **Ilith**.
- 为觉醒 **依莉丝** 以及 **百合咲美香** 的技能提供支持 Add support for the skills of uncapped **Ilith** and **Mika Yurisaki**.
- 为 Beyond 图倍增提供支持 Add support for beyond gauge boost.
- 为 Beyond 连锁图提供支持 Add support for beyond chain maps.
- 修复联机时无人房间仍可进入的问题 Fix a logic bug that the room without anyone can be entered in multiplayer.
- 对一些数值的算法进行了更改 Some changes in some values' algorithms.
- 小重构 Link Play 子程序 Refactor simply for Link Play subprogram.
- 新增增删改兑换码、购买项目、登陆奖励、物品的API接口 Add some API endpoints, including creating, changing, deleting about redeem, purchase, login present and item.
### Version 2.11.1

- 适用于Arcaea 4.4.0版本 For Arcaea 4.4.0
- 新搭档 **密特拉·泰尔塞拉****不来方斗亚** 已解锁 Unlock the character **Mithra Tercera** and **Toa Kozukata**.
-**密特拉·泰尔塞拉** 的技能提供支持 Add support for the skill of **Mithra Tercera**.
- 新增修改搭档的API接口 Add some API endpoints about characters.

## 运行环境与依赖 Running environment and requirements

Expand Down
3 changes: 2 additions & 1 deletion latest version/api/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from flask import Blueprint

from . import (users, songs, token, system, items,
purchases, presents, redeems)
purchases, presents, redeems, characters)

bp = Blueprint('api', __name__, url_prefix='/api/v1')
bp.register_blueprint(users.bp)
Expand All @@ -12,3 +12,4 @@
bp.register_blueprint(purchases.bp)
bp.register_blueprint(presents.bp)
bp.register_blueprint(redeems.bp)
bp.register_blueprint(characters.bp)
14 changes: 7 additions & 7 deletions latest version/api/api_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,12 @@ def wrapped_view(*args, **kwargs):

def request_json_handle(request, required_keys: list = [], optional_keys: list = [], must_change: bool = False, is_batch: bool = False):
'''
提取post参数,返回dict,写成了修饰器\
parameters: \
`request`: `Request` - 当前请求\
`required_keys`: `list` - 必须的参数\
`optional_keys`: `list` - 可选的参数\
提取post参数,返回dict,写成了修饰器
parameters:
`request`: `Request` - 当前请求
`required_keys`: `list` - 必须的参数
`optional_keys`: `list` - 可选的参数
`must_change`: `bool` - 当全都是可选参数时,是否必须有至少一项修改
'''

Expand Down Expand Up @@ -113,8 +114,7 @@ def wrapped_view(*args, **kwargs):
data = view(*args, **kwargs)
if data is None:
return error_return()
else:
return data
return data
except ArcError as e:
if Config.ALLOW_WARNING_LOG:
current_app.logger.warning(format_exc())
Expand Down
2 changes: 2 additions & 0 deletions latest version/api/api_code.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
-122: 'Item already exists',
-123: 'The collection already has this item',
-124: 'The collection does not have this item',
-130: 'No such character',
-131: 'Invalid skill ID',
-200: 'No permission', # 2xx用户相关错误
-201: 'Wrong username or password',
-202: 'User is banned',
Expand Down
134 changes: 134 additions & 0 deletions latest version/api/characters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
from flask import Blueprint, request

from core.error import InputError, NoData
from core.item import ItemFactory
from core.character import Character
from core.sql import Connect, Query, Sql

from .api_auth import api_try, request_json_handle, role_required
from .api_code import success_return
from .constant import Constant


bp = Blueprint('characters', __name__, url_prefix='/characters')


@bp.route('', methods=['GET'])
@role_required(request, ['select'])
@request_json_handle(request, optional_keys=Constant.QUERY_KEYS)
@api_try
def characters_get(data, user):
'''查询全角色信息'''
A = ['character_id', 'name', 'skill_id',
'skill_id_uncap', 'char_type', 'is_uncapped']
B = ['name', 'skill_id', 'skill_id_uncap']
C = ['name', 'frag1', 'prog1', 'overdrive1', 'frag20',
'prog20', 'overdrive20', 'frag30', 'prog30', 'overdrive30']
with Connect() as c:
query = Query(A, B, C).from_dict(data)
x = Sql(c).select('character', query=query)
r = [Character().from_list(i) for i in x]

if not r:
raise NoData(api_error_code=-2)

return success_return([x.to_dict() for x in r])


@bp.route('/<int:character_id>', methods=['GET'])
@role_required(request, ['select'])
@api_try
def characters_character_get(user, character_id: int):
# 包含core
with Connect() as c:
c = Character(c).select(character_id)
c.select_character_core()
return success_return(c.to_dict(has_cores=True))


@bp.route('/<int:character_id>', methods=['PUT'])
@role_required(request, ['change'])
@request_json_handle(request, optional_keys=['max_level', 'skill_id', 'skill_id_uncap', 'skill_unlock_level', 'skill_requires_uncap', 'char_type', 'is_uncapped', 'frag1', 'prog1', 'overdrive1', 'frag20', 'prog20', 'overdrive20', 'frag30', 'prog30', 'overdrive30'], must_change=True)
@api_try
def characters_character_put(data, user, character_id: int):
'''修改角色信息'''
if ('skill_id' in data and data['skill_id'] != '' and data['skill_id'] not in Constant.SKILL_IDS) or ('skill_id_uncap' in data and data['skill_id_uncap'] != '' and data['skill_id_uncap'] not in Constant.SKILL_IDS):
raise InputError('Invalid skill_id', api_error_code=-131)
with Connect() as c:
c = Character(c).select(character_id)
try:
if 'max_level' in data:
c.max_level = int(data['max_level'])
if 'skill_id' in data:
c.skill_id = data['skill_id']
if 'skill_id_uncap' in data:
c.skill_id_uncap = data['skill_id_uncap']
if 'skill_unlock_level' in data:
c.skill_unlock_level = int(data['skill_unlock_level'])
if 'skill_requires_uncap' in data:
c.skill_requires_uncap = data['skill_requires_uncap'] == 1
if 'char_type' in data:
c.char_type = int(data['char_type'])
if 'is_uncapped' in data:
c.is_uncapped = data['is_uncapped'] == 1
t = ['frag1', 'prog1', 'overdrive1', 'frag20', 'prog20',
'overdrive20', 'frag30', 'prog30', 'overdrive30']
for i in t:
if i not in data:
continue
if i.endswith('1'):
x = getattr(c, i[:-1])
x.start = float(data[i])
elif i.endswith('20'):
x = getattr(c, i[:-2])
x.mid = float(data[i])
else:
x = getattr(c, i[:-2])
x.end = float(data[i])
except ValueError as e:
raise InputError('Invalid input', api_error_code=-101) from e
c.update()
return success_return(c.to_dict())


@bp.route('/<int:character_id>/cores', methods=['GET'])
@role_required(request, ['select'])
@api_try
def characters_character_cores_get(user, character_id: int):
with Connect() as c:
c = Character(c)
c.character_id = character_id
c.select_character_core()
return success_return(c.uncap_cores_to_dict())


@bp.route('/<int:character_id>/cores', methods=['PATCH'])
@role_required(request, ['change'])
@request_json_handle(request, is_batch=True)
@api_try
def characters_character_cores_patch(data, user, character_id: int):
'''修改角色觉醒cores'''
def force_type_core(x: dict) -> dict:
x['item_type'] = 'core'
x['type'] = 'core'
return x

with Connect() as c:
ch = Character(c)
ch.character_id = character_id
ch.select_character_core()
ch.remove_items([ItemFactory.from_dict(x, c=c)
for x in map(force_type_core, data.get('remove', []))])
ch.add_items([ItemFactory.from_dict(x, c=c)
for x in map(force_type_core, data.get('create', []))])
updates = list(map(force_type_core, data.get('update', [])))
for x in updates:
if 'amount' not in x:
raise InputError('`amount` is required in `update`')
if not isinstance(x['amount'], int) or x['amount'] <= 0:
raise InputError(
'`amount` must be a positive integer', api_error_code=-101)

ch.update_items(
[ItemFactory.from_dict(x, c=c) for x in updates])
return success_return(ch.uncap_cores_to_dict())
3 changes: 3 additions & 0 deletions latest version/api/constant.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@ class Constant:
QUERY_KEYS = ['limit', 'offset', 'query', 'fuzzy_query', 'sort']

PATCH_KEYS = ['create', 'update', 'remove']

SKILL_IDS = ['gauge_easy', 'note_mirror', 'gauge_hard', 'frag_plus_10_pack_stellights', 'gauge_easy|frag_plus_15_pst&prs', 'gauge_hard|fail_frag_minus_100', 'frag_plus_5_side_light', 'visual_hide_hp', 'frag_plus_5_side_conflict', 'challenge_fullcombo_0gauge', 'gauge_overflow', 'gauge_easy|note_mirror', 'note_mirror', 'visual_tomato_pack_tonesphere', 'frag_rng_ayu', 'gaugestart_30|gaugegain_70', 'combo_100-frag_1', 'audio_gcemptyhit_pack_groovecoaster', 'gauge_saya', 'gauge_chuni', 'kantandeshou', 'gauge_haruna', 'frags_nono', 'gauge_pandora', 'gauge_regulus', 'omatsuri_daynight',
'sometimes(note_mirror|frag_plus_5)', 'scoreclear_aa|visual_scoregauge', 'gauge_tempest', 'gauge_hard', 'gauge_ilith_summer', 'frags_kou', 'visual_ink', 'shirabe_entry_fee', 'frags_yume', 'note_mirror|visual_hide_far', 'frags_ongeki', 'gauge_areus', 'gauge_seele', 'gauge_isabelle', 'gauge_exhaustion', 'skill_lagrange', 'gauge_safe_10', 'frags_nami', 'skill_elizabeth', 'skill_lily', 'skill_kanae_midsummer', 'eto_uncap', 'luna_uncap', 'frags_preferred_song', 'visual_ghost_skynotes', 'ayu_uncap', 'skill_vita', 'skill_fatalis', 'skill_reunion', 'frags_ongeki_slash', 'frags_ongeki_hard', 'skill_amane', 'skill_kou_winter', 'gauge_hard|note_mirror', 'skill_shama', 'skill_milk', 'skill_shikoku', 'skill_mika', 'ilith_awakened_skill', 'skill_mithra', 'skill_toa']
11 changes: 6 additions & 5 deletions latest version/api/token.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,22 @@
@api_try
def token_post(data):
'''
登录,获取token\
登录,获取token
{'auth': base64('<user_id>:<password>')}
'''
try:
auth_decode = bytes.decode(b64decode(data['auth']))
except:
raise PostError(api_error_code=-100)
if not ':' in auth_decode:
except Exception as e:
raise PostError(api_error_code=-100) from e
if ':' not in auth_decode:
raise PostError(api_error_code=-100)
name, password = auth_decode.split(':', 1)

with Connect() as c:
user = APIUser(c)
user.login(name, password, request.remote_addr)
return success_return({'token': user.token, 'user_id': user.user_id})
return success_return({'token': user.api_token, 'user_id': user.user_id})


@bp.route('', methods=['GET'])
Expand Down
2 changes: 1 addition & 1 deletion latest version/api/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ def users_user_b30_get(user, user_id):
f'No best30 data of user `{user_id}`', api_error_code=-3)
x.select_song_name()
r = x.to_dict_list()
rating_sum = sum([i.rating for i in x.scores])
rating_sum = sum(i.rating for i in x.scores)
return success_return({'user_id': user_id, 'b30_ptt': rating_sum / 30, 'data': r})


Expand Down
14 changes: 7 additions & 7 deletions latest version/core/api_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ def select_from_id(self, role_id: int = None) -> 'Role':
{'a': self.role_id})
x = self.c.fetchone()
if x is None:
raise NoData('The role `%s` does not exist.' %
self.role_id, api_error_code=-200)
raise NoData(
f'The role `{self.role_id}` does not exist.', api_error_code=-200)
self.caption = x[0]
return self

Expand Down Expand Up @@ -133,19 +133,19 @@ def login(self, name: str = None, password: str = None, ip: str = None) -> None:
'a': self.name})
x = self.c.fetchone()
if x is None:
raise NoData('The user `%s` does not exist.' %
self.name, api_error_code=-201, status=401)
raise NoData(
f'The user `{self.name}` does not exist.', api_error_code=-201, status=401)
if x[1] == '':
raise UserBan('The user `%s` is banned.' % self.name)
raise UserBan(f'The user `{self.name}` is banned.')
if self.hash_pwd != x[1]:
raise NoAccess('The password is incorrect.',
api_error_code=-201, status=401)

self.user_id = x[0]
now = int(time() * 1000)
self.token = sha256(
self.api_token = sha256(
(str(self.user_id) + str(now)).encode("utf8") + urandom(8)).hexdigest()

self.logout()
self.c.execute('''insert into api_login values(?,?,?,?)''',
(self.user_id, self.token, now, self.ip))
(self.user_id, self.api_token, now, self.ip))
Loading

0 comments on commit 1c58aeb

Please sign in to comment.