mai2: add present support
This commit is contained in:
parent
fef527d61f
commit
4446ff1f21
41
core/data/alembic/versions/5ea363686347_mai2_presents.py
Normal file
41
core/data/alembic/versions/5ea363686347_mai2_presents.py
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
"""mai2_presents
|
||||||
|
|
||||||
|
Revision ID: 5ea363686347
|
||||||
|
Revises: 680789dabab3
|
||||||
|
Create Date: 2024-06-28 14:49:07.666879
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
from sqlalchemy.dialects import mysql
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '5ea363686347'
|
||||||
|
down_revision = '680789dabab3'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.create_table('mai2_item_present',
|
||||||
|
sa.Column('id', sa.BIGINT(), nullable=False),
|
||||||
|
sa.Column('version', sa.INTEGER(), nullable=True),
|
||||||
|
sa.Column('user', sa.Integer(), nullable=True),
|
||||||
|
sa.Column('itemKind', sa.INTEGER(), nullable=False),
|
||||||
|
sa.Column('itemId', sa.INTEGER(), nullable=False),
|
||||||
|
sa.Column('stock', sa.INTEGER(), server_default='1', nullable=False),
|
||||||
|
sa.Column('startDate', sa.TIMESTAMP(), nullable=True),
|
||||||
|
sa.Column('endDate', sa.TIMESTAMP(), nullable=True),
|
||||||
|
sa.ForeignKeyConstraint(['user'], ['aime_user.id'], onupdate='cascade', ondelete='cascade'),
|
||||||
|
sa.PrimaryKeyConstraint('id'),
|
||||||
|
sa.UniqueConstraint('version', 'user', 'itemKind', 'itemId', name='mai2_item_present_uk'),
|
||||||
|
mysql_charset='utf8mb4'
|
||||||
|
)
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.drop_table('mai2_item_present')
|
||||||
|
# ### end Alembic commands ###
|
@ -182,6 +182,14 @@ Config file is located in `config/cxb.yaml`.
|
|||||||
|
|
||||||
## maimai DX
|
## maimai DX
|
||||||
|
|
||||||
|
### Presents
|
||||||
|
Presents are items given to the user when they login, with a little animation (for example, the KOP song was given to the finalists as a present). To add a present, you must insert it into the `mai2_item_present` table. In that table, a NULL version means any version, a NULL user means any user, a NULL start date means always open, and a NULL end date means it never expires. Below is a list of presents one might wish to add:
|
||||||
|
|
||||||
|
| Game Version | Item ID | Item Kind | Item Description | Present Description |
|
||||||
|
|--------------|---------|-----------|-------------------------------------------------|------------------------------------------------|
|
||||||
|
| BUDDiES (21) | 409505 | Icon (3) | 旅行スタンプ(月面基地) (Travel Stamp - Moon Base) | Officially obtained on the webui with a serial |
|
||||||
|
| | | | | number, for project raputa |
|
||||||
|
|
||||||
### Versions
|
### Versions
|
||||||
|
|
||||||
| Game Code | Version ID | Version Name |
|
| Game Code | Version ID | Version Name |
|
||||||
|
@ -471,7 +471,25 @@ class Mai2Base:
|
|||||||
}
|
}
|
||||||
|
|
||||||
async def handle_get_user_present_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_present_api_request(self, data: Dict) -> Dict:
|
||||||
return { "userId": data.get("userId", 0), "length": 0, "userPresentList": []}
|
items: List[Dict[str, Any]] = []
|
||||||
|
user_pres_list = await self.data.item.get_presents_by_version_user(self.version, data["userId"])
|
||||||
|
if user_pres_list:
|
||||||
|
for present in user_pres_list:
|
||||||
|
if (present['startDate'] and present['startDate'].timestamp() > datetime.now().timestamp()):
|
||||||
|
self.logger.debug(f"Present {present['id']} distribution hasn't started yet (begins {present['startDate']})")
|
||||||
|
continue # present period hasn't started yet, move onto the next one
|
||||||
|
|
||||||
|
if (present['endDate'] and present['endDate'].timestamp() < datetime.now().timestamp()):
|
||||||
|
self.logger.warn(f"Present {present['id']} ended on {present['endDate']} and should be removed")
|
||||||
|
continue # present period ended, move onto the next one
|
||||||
|
|
||||||
|
test = await self.data.item.get_item(data["userId"], present['itemKind'], present['itemId'])
|
||||||
|
if not test: # Don't send presents for items the user already has
|
||||||
|
items.append({"itemId": present['itemId'], "itemKind": present['itemKind'], "stock": present['stock'], "isValid": True})
|
||||||
|
self.logger.info(f"Give user {data['userId']} {present['stock']}x item {present['itemId']} (kind {present['itemKind']}) as present")
|
||||||
|
await self.data.item.put_item(data["userId"], present['itemKind'], present['itemId'], present['stock'], True)
|
||||||
|
|
||||||
|
return { "userId": data.get("userId", 0), "length": len(items), "userPresentList": items}
|
||||||
|
|
||||||
async def handle_get_transfer_friend_api_request(self, data: Dict) -> Dict:
|
async def handle_get_transfer_friend_api_request(self, data: Dict) -> Dict:
|
||||||
return {}
|
return {}
|
||||||
|
@ -327,16 +327,35 @@ class Mai2DX(Mai2Base):
|
|||||||
async def handle_get_user_item_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_item_api_request(self, data: Dict) -> Dict:
|
||||||
kind = int(data["nextIndex"] / 10000000000)
|
kind = int(data["nextIndex"] / 10000000000)
|
||||||
next_idx = int(data["nextIndex"] % 10000000000)
|
next_idx = int(data["nextIndex"] % 10000000000)
|
||||||
user_item_list = await self.data.item.get_items(data["userId"], kind)
|
|
||||||
|
|
||||||
items: List[Dict[str, Any]] = []
|
items: List[Dict[str, Any]] = []
|
||||||
for i in range(next_idx, len(user_item_list)):
|
|
||||||
tmp = user_item_list[i]._asdict()
|
if kind == 4: # presents
|
||||||
tmp.pop("user")
|
user_pres_list = await self.data.item.get_presents_by_version_user(self.version, data["userId"])
|
||||||
tmp.pop("id")
|
if user_pres_list:
|
||||||
items.append(tmp)
|
for present in user_pres_list:
|
||||||
if len(items) >= int(data["maxCount"]):
|
if (present['startDate'] and present['startDate'].timestamp() > datetime.now().timestamp()):
|
||||||
break
|
self.logger.debug(f"Present {present['id']} distribution hasn't started yet (begins {present['startDate']})")
|
||||||
|
continue # present period hasn't started yet, move onto the next one
|
||||||
|
|
||||||
|
if (present['endDate'] and present['endDate'].timestamp() < datetime.now().timestamp()):
|
||||||
|
self.logger.warn(f"Present {present['id']} ended on {present['endDate']} and should be removed")
|
||||||
|
continue # present period ended, move onto the next one
|
||||||
|
|
||||||
|
test = await self.data.item.get_item(data["userId"], present['itemKind'], present['itemId'])
|
||||||
|
if not test: # Don't send presents for items the user already has
|
||||||
|
items.append({"itemId": present['itemId'], "itemKind": present['itemKind'], "stock": present['stock'], "isValid": True})
|
||||||
|
self.logger.info(f"Give user {data['userId']} {present['stock']}x item {present['itemId']} (kind {present['itemKind']}) as present")
|
||||||
|
await self.data.item.put_item(data["userId"], present['itemKind'], present['itemId'], present['stock'], True)
|
||||||
|
|
||||||
|
else:
|
||||||
|
user_item_list = await self.data.item.get_items(data["userId"], kind)
|
||||||
|
for i in range(next_idx, len(user_item_list)):
|
||||||
|
tmp = user_item_list[i]._asdict()
|
||||||
|
tmp.pop("user")
|
||||||
|
tmp.pop("id")
|
||||||
|
items.append(tmp)
|
||||||
|
if len(items) >= int(data["maxCount"]):
|
||||||
|
break
|
||||||
|
|
||||||
xout = kind * 10000000000 + next_idx + len(items)
|
xout = kind * 10000000000 + next_idx + len(items)
|
||||||
|
|
||||||
|
@ -2,8 +2,8 @@ from core.data.schema import BaseData, metadata
|
|||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Optional, Dict, List
|
from typing import Optional, Dict, List
|
||||||
from sqlalchemy import Table, Column, UniqueConstraint, PrimaryKeyConstraint, and_
|
from sqlalchemy import Table, Column, UniqueConstraint, PrimaryKeyConstraint, and_, or_
|
||||||
from sqlalchemy.types import Integer, String, TIMESTAMP, Boolean, JSON
|
from sqlalchemy.types import Integer, String, TIMESTAMP, Boolean, JSON, BIGINT, INTEGER
|
||||||
from sqlalchemy.schema import ForeignKey
|
from sqlalchemy.schema import ForeignKey
|
||||||
from sqlalchemy.sql import func, select
|
from sqlalchemy.sql import func, select
|
||||||
from sqlalchemy.dialects.mysql import insert
|
from sqlalchemy.dialects.mysql import insert
|
||||||
@ -198,6 +198,20 @@ print_detail = Table(
|
|||||||
mysql_charset="utf8mb4",
|
mysql_charset="utf8mb4",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
present = Table(
|
||||||
|
"mai2_item_present",
|
||||||
|
metadata,
|
||||||
|
Column('id', BIGINT, primary_key=True, nullable=False),
|
||||||
|
Column('version', INTEGER),
|
||||||
|
Column("user", Integer, ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")),
|
||||||
|
Column("itemKind", INTEGER, nullable=False),
|
||||||
|
Column("itemId", INTEGER, nullable=False),
|
||||||
|
Column("stock", INTEGER, nullable=False, server_default="1"),
|
||||||
|
Column("startDate", TIMESTAMP),
|
||||||
|
Column("endDate", TIMESTAMP),
|
||||||
|
UniqueConstraint("version", "user", "itemKind", "itemId", name="mai2_item_present_uk"),
|
||||||
|
mysql_charset="utf8mb4",
|
||||||
|
)
|
||||||
|
|
||||||
class Mai2ItemData(BaseData):
|
class Mai2ItemData(BaseData):
|
||||||
async def put_item(
|
async def put_item(
|
||||||
@ -476,7 +490,7 @@ class Mai2ItemData(BaseData):
|
|||||||
musicId = music_id
|
musicId = music_id
|
||||||
)
|
)
|
||||||
|
|
||||||
conflict = sql.on_duplicate_key_do_nothing()
|
conflict = sql.on_duplicate_key_update(musicId = music_id)
|
||||||
|
|
||||||
result = await self.execute(conflict)
|
result = await self.execute(conflict)
|
||||||
if result:
|
if result:
|
||||||
@ -586,3 +600,49 @@ class Mai2ItemData(BaseData):
|
|||||||
)
|
)
|
||||||
return None
|
return None
|
||||||
return result.lastrowid
|
return result.lastrowid
|
||||||
|
|
||||||
|
async def put_present(self, item_kind: int, item_id: int, version: int = None, user_id: int = None, start_date: datetime = None, end_date: datetime = None) -> Optional[int]:
|
||||||
|
sql = insert(present).values(
|
||||||
|
version = version,
|
||||||
|
user = user_id,
|
||||||
|
itemKind = item_kind,
|
||||||
|
itemId = item_id,
|
||||||
|
startDate = start_date,
|
||||||
|
endDate = end_date
|
||||||
|
)
|
||||||
|
|
||||||
|
conflict = sql.on_duplicate_key_update(
|
||||||
|
startDate = start_date,
|
||||||
|
endDate = end_date
|
||||||
|
)
|
||||||
|
|
||||||
|
result = await self.execute(conflict)
|
||||||
|
if result:
|
||||||
|
return result.lastrowid
|
||||||
|
|
||||||
|
self.logger.error(f"Failed to add present item {item_id}!")
|
||||||
|
|
||||||
|
async def get_presents_by_user(self, user_id: int = None) -> Optional[List[Row]]:
|
||||||
|
result = await self.execute(present.select(or_(present.c.user == user_id, present.c.user is None)))
|
||||||
|
if result:
|
||||||
|
return result.fetchall()
|
||||||
|
|
||||||
|
async def get_presents_by_version(self, ver: int = None) -> Optional[List[Row]]:
|
||||||
|
result = await self.execute(present.select(or_(present.c.version == ver, present.c.version is None)))
|
||||||
|
if result:
|
||||||
|
return result.fetchall()
|
||||||
|
|
||||||
|
async def get_presents_by_version_user(self, ver: int = None, user_id: int = None) -> Optional[List[Row]]:
|
||||||
|
result = await self.execute(present.select(
|
||||||
|
and_(
|
||||||
|
or_(present.c.user == user_id, present.c.user is None)),
|
||||||
|
or_(present.c.version == ver, present.c.version is None)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if result:
|
||||||
|
return result.fetchall()
|
||||||
|
|
||||||
|
async def get_present_by_id(self, present_id: int) -> Optional[Row]:
|
||||||
|
result = await self.execute(present.select(present.c.id == present_id))
|
||||||
|
if result:
|
||||||
|
return result.fetchone()
|
||||||
|
@ -892,7 +892,7 @@ class Mai2ProfileData(BaseData):
|
|||||||
rival = rival_id
|
rival = rival_id
|
||||||
)
|
)
|
||||||
|
|
||||||
conflict = sql.on_duplicate_key_do_nothing()
|
conflict = sql.on_duplicate_key_update(rival = rival_id)
|
||||||
|
|
||||||
result = await self.execute(conflict)
|
result = await self.execute(conflict)
|
||||||
if result:
|
if result:
|
||||||
|
Loading…
Reference in New Issue
Block a user