5 Commits
v1.4 ... v1.7

Author SHA1 Message Date
Lost-MSth
dfcbb6f9c0 Update to v1.7
close #1
close #2
2020-11-27 21:35:35 +08:00
Lost-MSth
30690b2fbf Update to v1.6 2020-11-19 22:15:33 +08:00
Lost-MSth
56ae7047fc Update to v1.5
A small update.
2020-11-02 19:02:50 +08:00
Lost-MSth
f5d2589971 Update README.md 2020-10-31 15:03:46 +08:00
Lost-MSth
8bb5c28663 Update README.md 2020-10-30 20:49:25 +08:00
30 changed files with 2197 additions and 363 deletions

View File

@@ -8,7 +8,7 @@ This is a small local Arcaea server based on Python and Flask, which can simulat
> It looks stupid, but it works! > It looks stupid, but it works!
## 特性 Features ## 特性 Features
有 We have 以下 We have
- 登录、注册 Login and registration - 登录、注册 Login and registration
- 成绩上传 Score upload - 成绩上传 Score upload
- PTT - PTT
@@ -17,42 +17,53 @@ This is a small local Arcaea server based on Python and Flask, which can simulat
- 数据同步 Data synchronization - 数据同步 Data synchronization
- 爬梯 Climbing steps - 爬梯 Climbing steps
- 自定义世界模式 Customizable World Mode - 自定义世界模式 Customizable World Mode
- 自定义歌曲下载 Customizable songs download
- 单曲和曲包购买(没啥用) Single songs and song packs purchase(useless)
- 全角色立绘 All character drawings - 全角色立绘 All character drawings
- 全剧情解锁 Unlock all the storys - 角色技能 Character skills
- 后台查分 Background search scores - 自定义角色属性 Customizable characters attributes
- 后台自定义歌曲定数 Customize chart consts in the background - 全剧情解锁 Unlock all the stories
- 后台查询 Background search
- 后台自定义信息 Customize some things in the background
- 成绩校验 Score check
- 下载校验 Download check
没有以下 We don't have 没有以下 We don't have
- 角色特性 Character characteristics - 角色数值 Character characteristic value
- 购买 Purchase - 数据同步的时间和设备记录 The record of time and device for data synchronization
- 歌曲解锁、曲包解锁 Songs unlocking and music packs unlocking
- 反作弊系统 Anti cheating system
- 服务器安全性保证 Server security assurance - 服务器安全性保证 Server security assurance
可能有问题 There may be problems 可能有问题 There may be problems
- Recent 30 - Recent 30
- 一些歌曲的解锁 Some Songs' unlocking
## 说明 Statement ## 说明 Statement
只是很有趣,用处探索中。 只是很有趣,用处探索中。
It is just so interesting. What it can do is under exploration. It is just so interesting. What it can do is under exploration.
进行了一下项目改进,可能目录有所变化
Some project improvements have been made and the catalog may have changed.
## 下载 Download ## 下载 Download
[这里 Here](https://github.com/Lost-MSth/Arcaea-server/releases) [这里 Here](https://github.com/Lost-MSth/Arcaea-server/releases)
[Arcaea](https://konmai.cn/#arcaea)
## 更新日志 Update log ## 更新日志 Update log
只保留最新版本 Only keep the latest version. 只保留最新版本 Only keep the latest version.
### Version 1.3
- 适用于Arcaea 3.2.1版本 For Arcaea 3.2.1 > 提醒:更新时请注意保留原先的数据库,以防数据丢失
>
> Tips: When updating, please keep the original database in case of data loss.
### Version 1.7
- 仍然适用于Arcaea 3.2.3版本 Still for Arcaea 3.2.3
- 更新了歌曲数据库 Update the song database. - 更新了歌曲数据库 Update the song database.
- 新增了可自定义的世界模式 Add customizable World Mode. - 新增了购买系统,包括单曲购买和曲包购买 Add purchase system, including single purchase and pack purchase.
- 对于官方版本,解锁几乎所有歌曲 For the official version, unlock almost all songs. - 后台查询与修改项目增多 Increase background query and modification items.
- 新增用户下载量限制和下载校验 Add user download limit and download verification.
> 更新说明本次更新无法从旧版本同步数据请使用游戏内数据同步功能上传best_score数据 - 新增了**梦**的语音 Add the voice of **Yume**.
- 新增了网站图标 Add a favicon.
> Update note: In this update you cannot synchronize data from an older version. Please use the data synchronization function in game to upload best_score data. - 尝试解锁了所有的场景 Try to unlock all the scenes.
- 修复了一些Bug Fix some bugs.
## 运行环境与依赖 Running environment and requirements ## 运行环境与依赖 Running environment and requirements
- Windows操作系统 Windows operating system - Windows操作系统 Windows operating system
@@ -76,6 +87,8 @@ Some project improvements have been made and the catalog may have changed.
歌曲数据库来自 Using song database from 歌曲数据库来自 Using song database from
[BotArcAPI releases](https://github.com/TheSnowfield/BotArcAPI/releases) [BotArcAPI releases](https://github.com/TheSnowfield/BotArcAPI/releases)
网站图标来自 Using favicon from [black fate - てんてん - pixiv](https://www.pixiv.net/artworks/82374369)
## 联系方式 Contact ## 联系方式 Contact
如有必要,可以联系本人 Contact me if necessary 如有必要,可以联系本人 Contact me if necessary
邮箱 Emailth84292@foxmail.com 邮箱 Emailth84292@foxmail.com

Binary file not shown.

View File

@@ -1,6 +1,7 @@
import sqlite3 import sqlite3
import hashlib import hashlib
import time import time
import json
# 数据库初始化文件删掉arcaea_database.db文件后运行即可谨慎使用 # 数据库初始化文件删掉arcaea_database.db文件后运行即可谨慎使用
@@ -31,7 +32,8 @@ clear_type int,
rating real, rating real,
favorite_character int, favorite_character int,
max_stamina_notification_enabled int, max_stamina_notification_enabled int,
current_map text current_map text,
ticket int
);''') );''')
c.execute('''create table if not exists login(access_token text, c.execute('''create table if not exists login(access_token text,
user_id int, user_id int,
@@ -170,30 +172,98 @@ fragment_multiply int,
prog_boost_multiply int, prog_boost_multiply int,
primary key(user_id, song_id, difficulty) primary key(user_id, song_id, difficulty)
);''') );''')
c.execute('''create table if not exists download_token(user_id int,
song_id text,
file_name text,
token text,
time int,
primary key(user_id, song_id, file_name)
);''')
c.execute('''create table if not exists user_download(user_id int,
token text,
time int,
primary key(user_id, token, time)
);''')
c.execute('''create table if not exists item(item_id text,
type text,
is_available int,
price int,
orig_price int,
discount_from int,
discount_to int,
_id text,
primary key(item_id, type)
);''')
c.execute('''create table if not exists user_item(user_id int,
item_id text,
type text,
primary key(user_id, item_id, type)
);''')
char = ['Hikari','Tairitsu','Kou','Sapphire','Lethe','','Tairitsu(Axium)' char = ['Hikari', 'Tairitsu', 'Kou', 'Sapphire', 'Lethe', '', 'Tairitsu(Axium)', 'Tairitsu(Grievous Lady)', 'Stella', 'Hikari & Fisica', 'Ilith', 'Eto', 'Luna', 'Shirabe', 'Hikari(Zero)', 'Hikari(Fracture)', 'Hikari(Summer)', 'Tairitsu(Summer)', 'Tairitsu&Trin',
,'Tairitsu(Grievous Lady)','Stella','Hikari & Fisica','Ilith','Eto','Luna' 'Ayu', 'Eto&Luna', 'Yume', 'Seine & Hikari', 'Saya', 'Tairitsu & Chuni Penguin', 'Chuni Penguin', 'Haruna', 'Nono', 'MTA-XXX', 'MDA-21', 'Kanae', 'Hikari(Fantasia)', 'Tairitsu(Sonata)', 'Sia', 'DORO*C', 'Tairitsu(Tempest)', 'Brillante', 'Ilith(Summer)', 'Etude']
,'Shirabe','Hikari(Zero)','Hikari(Fracture)','Hikari(Summer)','Tairitsu(Summer)'
,'Tairitsu&Trin','Ayu','Eto&Luna','Yume','Seine & Hikari','Saya','Tairitsu & Chuni Penguin' skill_id = ['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',
,'Chuni Penguin','Haruna','Nono','MTA-XXX','MDA-21','Kanae','Hikari(Fantasia)','Tairitsu(Sonata)','Sia','DORO*C' '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', '']
,'Tairitsu(Tempest)','Brillante','Ilith(Summer)','Etude']
skill_id_uncap = ['', '', 'frags_kou', '', 'visual_ink', '', '', '', '', '', '', '', '', 'shirabe_entry_fee',
'', '', '', '', '', '', '', 'frags_yume', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '']
for i in range(0, 39): for i in range(0, 39):
if i in [0, 1, 2, 4, 13, 26, 27, 28, 29, 36, 21]: if i in [0, 1, 2, 4, 13, 26, 27, 28, 29, 36, 21]:
sql = 'insert into character values('+str(i)+',"'+char[i]+'''",30,25000,25000,90,90,90,'',0,0,'',0,'',1,1)''' sql = 'insert into character values('+str(
i)+',"'+char[i]+'''",30,25000,25000,90,90,90,"'''+skill_id[i]+'''",0,0,"'''+skill_id_uncap[i]+'''",0,'',1,1)'''
c.execute(sql) c.execute(sql)
else: else:
if i != 5: if i != 5:
sql = 'insert into character values('+str(i)+',"'+char[i]+'''",30,25000,25000,90,90,90,'',0,0,'',0,'',0,0)''' sql = 'insert into character values('+str(
i)+',"'+char[i]+'''",30,25000,25000,90,90,90,"'''+skill_id[i]+'''",0,0,"'''+skill_id_uncap[i]+'''",0,'',0,0)'''
c.execute(sql) c.execute(sql)
def b2int(x):
# int与布尔值转换
if x:
return 1
else:
return 0
def insert_items(c, items):
# 物品数据导入
for i in items:
if 'discount_from' not in i:
discount_from = -1
else:
discount_from = i['discount_from']
if 'discount_to' not in i:
discount_to = -1
else:
discount_to = i['discount_to']
for j in i['items']:
if "_id" not in j:
_id = ''
else:
_id = j['_id']
if j['type'] != 'character':
c.execute('''insert into item(item_id, type, is_available, price, orig_price, discount_from, discount_to, _id) values(:a,:b,:c,:d,:e,:f,:g,:h)''', {
'a': j['id'], 'b': j['type'], 'c': b2int(j['is_available']), 'd': i['price'], 'e': i['orig_price'], 'f': discount_from, 'g': discount_to, 'h': _id})
f = open('singles.json', 'r')
singles = json.load(f)
f.close()
insert_items(c, singles)
f = open('packs.json', 'r')
packs = json.load(f)
f.close()
insert_items(c, packs)
conn.commit() conn.commit()
conn.close() conn.close()
def arc_register(name: str, password: str): def arc_register(name: str, password: str):
def build_user_code(c): def build_user_code(c):
return '123456789' return '123456789'
@@ -201,17 +271,17 @@ def arc_register(name: str, password: str):
def build_user_id(c): def build_user_id(c):
return 2000000 return 2000000
## def insert_user_char(c, user_id): # def insert_user_char(c, user_id):
## for i in range(0, 38): # for i in range(0, 38):
## if i in [0, 1, 2, 4, 13, 26, 27, 28, 29, 36, 21]: # if i in [0, 1, 2, 4, 13, 26, 27, 28, 29, 36, 21]:
## sql = 'insert into user_char values('+str(user_id)+','+str( # sql = 'insert into user_char values('+str(user_id)+','+str(
## i)+''',30,25000,25000,90,90,90,'',0,0,'',0,1,1)''' # i)+''',30,25000,25000,90,90,90,'',0,0,'',0,1,1)'''
## c.execute(sql) # c.execute(sql)
## else: # else:
## if i != 5: # if i != 5:
## sql = 'insert into user_char values('+str(user_id)+','+str( # sql = 'insert into user_char values('+str(user_id)+','+str(
## i)+''',30,25000,25000,90,90,90,'',0,0,'',0,0,0)''' # i)+''',30,25000,25000,90,90,90,'',0,0,'',0,0,0)'''
## c.execute(sql) # c.execute(sql)
def insert_user_char(c, user_id): def insert_user_char(c, user_id):
# 为用户添加所有可用角色 # 为用户添加所有可用角色
c.execute('''select * from character''') c.execute('''select * from character''')
@@ -221,7 +291,6 @@ def arc_register(name: str, password: str):
c.execute('''insert into user_char values(:a,:b,:c,:d,:e,:f,:g,:h,:i,:j,:k,:l,:m,:n,:o)''', { c.execute('''insert into user_char values(:a,:b,:c,:d,:e,:f,:g,:h,:i,:j,:k,:l,:m,:n,:o)''', {
'a': user_id, 'b': i[0], 'c': i[2], 'd': i[3], 'e': i[4], 'f': i[5], 'g': i[6], 'h': i[7], 'i': i[8], 'j': i[9], 'k': i[10], 'l': i[11], 'm': i[12], 'n': i[14], 'o': i[15]}) 'a': user_id, 'b': i[0], 'c': i[2], 'd': i[3], 'e': i[4], 'f': i[5], 'g': i[6], 'h': i[7], 'i': i[8], 'j': i[9], 'k': i[10], 'l': i[11], 'm': i[12], 'n': i[14], 'o': i[15]})
conn = sqlite3.connect('arcaea_database.db') conn = sqlite3.connect('arcaea_database.db')
c = conn.cursor() c = conn.cursor()
hash_pwd = hashlib.sha256(password.encode("utf8")).hexdigest() hash_pwd = hashlib.sha256(password.encode("utf8")).hexdigest()
@@ -232,12 +301,13 @@ def arc_register(name: str, password: str):
user_id = build_user_id(c) user_id = build_user_id(c)
now = int(time.time() * 1000) now = int(time.time() * 1000)
c.execute('''insert into user(user_id, name, password, join_date, user_code, rating_ptt, 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) character_id, is_skill_sealed, is_char_uncapped, is_char_uncapped_override, is_hide_rating, favorite_character, max_stamina_notification_enabled, current_map, ticket)
values(:user_id, :name, :password, :join_date, :user_code, 1250, 1, 0, 1, 0, 0, -1, 0, '') values(:user_id, :name, :password, :join_date, :user_code, 1250, 1, 0, 1, 0, 0, -1, 0, '', 114514)
''', {'user_code': user_code, 'user_id': user_id, 'join_date': now, 'name': name, 'password': hash_pwd}) ''', {'user_code': user_code, 'user_id': user_id, 'join_date': now, 'name': name, 'password': hash_pwd})
c.execute('''insert into recent30(user_id) values(:user_id)''', { c.execute('''insert into recent30(user_id) values(:user_id)''', {
'user_id': user_id}) 'user_id': user_id})
c.execute('''insert into best_score values(2000000,'vexaria',3,10000000,100,0,0,0,100,0,1599667200,3,3,10.8)''') c.execute(
'''insert into best_score values(2000000,'vexaria',3,10000000,100,0,0,0,100,0,1599667200,3,3,10.8)''')
insert_user_char(c, user_id) insert_user_char(c, user_id)
conn.commit() conn.commit()
conn.close() conn.close()

View File

@@ -0,0 +1,70 @@
{
"map_id": "byd_lumia",
"is_legacy": false,
"chapter": 1001,
"available_from": -1,
"available_to": 9999999999999,
"is_repeatable": false,
"require_id": "lumia2",
"require_type": "chart_unlock",
"coordinate": "650,-650",
"is_beyond": true,
"stamina_cost": 3,
"beyond_health": 150,
"character_affinity": [0, 15, 14, 16],
"affinity_multiplier": [2.4, 1.9, 2.4, 2.9],
"step_count": 6,
"custom_bg": "",
"curr_position": 0,
"curr_capture": 0,
"is_locked": false,
"steps": [{
"map_id": "byd_lumia",
"position": 0,
"capture": 10,
"restrict_id": "core",
"restrict_type": "pack_id"
}, {
"map_id": "byd_lumia",
"position": 1,
"capture": 20,
"items": [{
"type": "fragment",
"amount": 200
}]
}, {
"map_id": "byd_lumia",
"position": 2,
"capture": 30,
"items": [{
"type": "core",
"id": "core_generic",
"amount": 1
}]
}, {
"map_id": "byd_lumia",
"position": 3,
"capture": 40,
"items": [{
"type": "fragment",
"amount": 250
}]
}, {
"map_id": "byd_lumia",
"position": 4,
"capture": 50,
"items": [{
"type": "core",
"id": "core_generic",
"amount": 1
}]
}, {
"map_id": "byd_lumia",
"position": 5,
"capture": 0,
"items": [{
"id": "lumia3",
"type": "world_song"
}]
}]
}

View File

@@ -0,0 +1,198 @@
[{
"name": "core",
"items": [{
"type": "pack",
"id": "core",
"_id": "5fb5b68a68273a03de60f205",
"is_available": true
}],
"price": 500,
"orig_price": 500,
"discount_from": 1583712000000,
"discount_to": 1584316799000
}, {
"name": "shiawase",
"items": [{
"type": "pack",
"id": "shiawase",
"_id": "5fb5b68a68273a03de60f234",
"is_available": true
}, {
"type": "character",
"id": "kou",
"_id": "5fb5b68a68273a03de60f233",
"is_available": true
}],
"price": 500,
"orig_price": 500,
"discount_from": 1552089600000,
"discount_to": 1552694399000
}, {
"name": "dynamix",
"items": [{
"type": "pack",
"id": "dynamix",
"_id": "5fb5b68a68273a03de60f238",
"is_available": true
}, {
"type": "character",
"id": "sapphire",
"_id": "5fb5b68a68273a03de60f237",
"is_available": true
}],
"price": 500,
"orig_price": 500,
"discount_from": 1583712000000,
"discount_to": 1584316799000
}, {
"name": "mirai",
"items": [{
"type": "pack",
"id": "mirai",
"_id": "5fb5b68a68273a03de60f23e",
"is_available": true
}, {
"type": "character",
"id": "lethe",
"_id": "5fb5b68a68273a03de60f23d",
"is_available": true
}],
"price": 500,
"orig_price": 500,
"discount_from": 1552089600000,
"discount_to": 1552694399000
}, {
"name": "yugamu",
"items": [{
"type": "pack",
"id": "yugamu",
"_id": "5fb5b68a68273a03de60f206",
"is_available": true
}],
"price": 500,
"orig_price": 500,
"discount_from": 1583712000000,
"discount_to": 1584316799000
}, {
"name": "lanota",
"items": [{
"type": "pack",
"id": "lanota",
"_id": "5fb5b68a68273a03de60f211",
"is_available": true
}],
"price": 500,
"orig_price": 500,
"discount_from": 1583712000000,
"discount_to": 1584316799000
}, {
"name": "nijuusei",
"items": [{
"type": "pack",
"id": "nijuusei",
"_id": "5fb5b68a68273a03de60f207",
"is_available": true
}],
"price": 500,
"orig_price": 500,
"discount_from": 1583712000000,
"discount_to": 1584316799000
}, {
"name": "rei",
"items": [{
"type": "pack",
"id": "rei",
"_id": "5fb5b68a68273a03de60f1fd",
"is_available": true
}],
"price": 500,
"orig_price": 500,
"discount_from": 1583712000000,
"discount_to": 1584316799000
}, {
"name": "tonesphere",
"items": [{
"type": "pack",
"id": "tonesphere",
"_id": "5fb5b68a68273a03de60f213",
"is_available": true
}],
"price": 500,
"orig_price": 500,
"discount_from": 1583712000000,
"discount_to": 1584316799000
}, {
"name": "groovecoaster",
"items": [{
"type": "pack",
"id": "groovecoaster",
"_id": "5fb5b68a68273a03de60f22c",
"is_available": true
}],
"price": 500,
"orig_price": 500,
"discount_from": 1583712000000,
"discount_to": 1584316799000
}, {
"name": "zettai",
"items": [{
"type": "pack",
"id": "zettai",
"_id": "5fb5b68a68273a03de60f1ff",
"is_available": true
}],
"price": 500,
"orig_price": 500,
"discount_from": 1583712000000,
"discount_to": 1584316799000
}, {
"name": "chunithm",
"items": [{
"type": "pack",
"id": "chunithm",
"_id": "5fb5b68a68273a03de60f221",
"is_available": true
}],
"price": 300,
"orig_price": 300
}, {
"name": "prelude",
"items": [{
"type": "pack",
"id": "prelude",
"_id": "5fb5b68a68273a03de60f22d",
"is_available": true
}],
"price": 400,
"orig_price": 400
}, {
"name": "omatsuri",
"items": [{
"type": "pack",
"id": "omatsuri",
"_id": "5fb5b68a68273a03de60f200",
"is_available": true
}],
"price": 500,
"orig_price": 500
}, {
"name": "vs",
"items": [{
"type": "pack",
"id": "vs",
"_id": "5fb5b68a68273a03de60f224",
"is_available": true
}],
"price": 500,
"orig_price": 500
}, {
"name": "extend",
"items": [{
"type": "pack",
"id": "extend",
"_id": "5fb5b68a68273a03de60f230",
"is_available": true
}],
"price": 700,
"orig_price": 700
}]

View File

@@ -0,0 +1,466 @@
[{
"name": "testsingle",
"items": [{
"id": "testsingle",
"type": "single",
"is_available": false
}],
"price": 100,
"orig_price": 100
}, {
"name": "dataerror",
"items": [{
"id": "dataerror",
"type": "single",
"_id": "5fb5b68a68273a03de60f210",
"is_available": true
}],
"price": 100,
"orig_price": 100,
"discount_from": 1583712000000,
"discount_to": 1584316799000
}, {
"name": "yourvoiceso",
"items": [{
"id": "yourvoiceso",
"type": "single",
"_id": "5fb5b68a68273a03de60f227",
"is_available": true
}],
"price": 100,
"orig_price": 100,
"discount_from": 1583712000000,
"discount_to": 1584316799000
}, {
"name": "crosssoul",
"items": [{
"id": "crosssoul",
"type": "single",
"_id": "5fb5b68a68273a03de60f21b",
"is_available": true
}],
"price": 100,
"orig_price": 100,
"discount_from": 1583712000000,
"discount_to": 1584316799000
}, {
"name": "impurebird",
"items": [{
"type": "single",
"id": "impurebird",
"_id": "5fb5b68a68273a03de60f21c",
"is_available": true
}],
"price": 100,
"orig_price": 100,
"discount_from": 1583712000000,
"discount_to": 1584316799000
}, {
"name": "auxesia",
"items": [{
"type": "single",
"id": "auxesia",
"_id": "5fb5b68a68273a03de60f228",
"is_available": true
}],
"price": 100,
"orig_price": 100,
"discount_from": 1520413239000,
"discount_to": 1521072000000
}, {
"name": "modelista",
"items": [{
"type": "single",
"id": "modelista",
"_id": "5fb5b68a68273a03de60f1fb",
"is_available": true
}],
"price": 100,
"orig_price": 100,
"discount_from": 1583712000000,
"discount_to": 1584316799000
}, {
"name": "yozakurafubuki",
"items": [{
"type": "single",
"id": "yozakurafubuki",
"_id": "5fb5b68a68273a03de60f21d",
"is_available": true
}],
"price": 100,
"orig_price": 100,
"discount_from": 1583712000000,
"discount_to": 1584316799000
}, {
"name": "surrender",
"items": [{
"type": "single",
"id": "surrender",
"_id": "5fb5b68a68273a03de60f229",
"is_available": true
}],
"price": 100,
"orig_price": 100,
"discount_from": 1583712000000,
"discount_to": 1584316799000
}, {
"name": "metallicpunisher",
"items": [{
"type": "single",
"id": "metallicpunisher",
"_id": "5fb5b68a68273a03de60f1fc",
"is_available": true
}],
"price": 100,
"orig_price": 100
}, {
"name": "carminescythe",
"items": [{
"type": "single",
"id": "carminescythe",
"_id": "5fb5b68a68273a03de60f21e",
"is_available": true
}],
"price": 100,
"orig_price": 100
}, {
"name": "bethere",
"items": [{
"type": "single",
"id": "bethere",
"_id": "5fb5b68a68273a03de60f22a",
"is_available": true
}],
"price": 100,
"orig_price": 100,
"discount_from": 1583712000000,
"discount_to": 1584316799000
}, {
"name": "callmyname",
"items": [{
"type": "single",
"id": "callmyname",
"_id": "5fb5b68a68273a03de60f212",
"is_available": true
}],
"price": 100,
"orig_price": 100,
"discount_from": 1583712000000,
"discount_to": 1584316799000
}, {
"name": "fallensquare",
"items": [{
"type": "single",
"id": "fallensquare",
"_id": "5fb5b68a68273a03de60f208",
"is_available": true
}],
"price": 100,
"orig_price": 100,
"discount_from": 1583712000000,
"discount_to": 1584316799000
}, {
"name": "dropdead",
"items": [{
"type": "single",
"id": "dropdead",
"_id": "5fb5b68a68273a03de60f21f",
"is_available": true
}],
"price": 100,
"orig_price": 100,
"discount_from": 1583712000000,
"discount_to": 1584316799000
}, {
"name": "alexandrite",
"items": [{
"type": "single",
"id": "alexandrite",
"_id": "5fb5b68a68273a03de60f22b",
"is_available": true
}],
"price": 100,
"orig_price": 100,
"discount_from": 1583712000000,
"discount_to": 1584316799000
}, {
"name": "astraltale",
"items": [{
"type": "single",
"id": "astraltale",
"_id": "5fb5b68a68273a03de60f1fe",
"is_available": true
}],
"price": 100,
"orig_price": 100,
"discount_from": 1583712000000,
"discount_to": 1584316799000
}, {
"name": "phantasia",
"items": [{
"type": "single",
"id": "phantasia",
"_id": "5fb5b68a68273a03de60f209",
"is_available": true
}],
"price": 100,
"orig_price": 100
}, {
"name": "empireofwinter",
"items": [{
"type": "single",
"id": "empireofwinter",
"_id": "5fb5b68a68273a03de60f220",
"is_available": true
}],
"price": 100,
"orig_price": 100
}, {
"name": "libertas",
"items": [{
"type": "single",
"id": "libertas",
"_id": "5fb5b68a68273a03de60f214",
"is_available": true
}],
"price": 100,
"orig_price": 100,
"discount_from": 1583712000000,
"discount_to": 1584316799000
}, {
"name": "dottodot",
"items": [{
"type": "single",
"id": "dottodot",
"_id": "5fb5b68a68273a03de60f20a",
"is_available": true
}],
"price": 100,
"orig_price": 100,
"discount_from": 1583712000000,
"discount_to": 1584316799000
}, {
"name": "dreadnought",
"items": [{
"type": "single",
"id": "dreadnought",
"_id": "5fb5b68a68273a03de60f215",
"is_available": true
}],
"price": 100,
"orig_price": 100,
"discount_from": 1583712000000,
"discount_to": 1584316799000
}, {
"name": "mirzam",
"items": [{
"type": "single",
"id": "mirzam",
"_id": "5fb5b68a68273a03de60f20b",
"is_available": true
}],
"price": 100,
"orig_price": 100,
"discount_from": 1583712000000,
"discount_to": 1584316799000
}, {
"name": "heavenlycaress",
"items": [{
"type": "single",
"id": "heavenlycaress",
"_id": "5fb5b68a68273a03de60f222",
"is_available": true
}],
"price": 100,
"orig_price": 100
}, {
"name": "filament",
"items": [{
"type": "single",
"id": "filament",
"_id": "5fb5b68a68273a03de60f22e",
"is_available": true
}],
"price": 100,
"orig_price": 100
}, {
"name": "avantraze",
"items": [{
"type": "single",
"id": "avantraze",
"_id": "5fb5b68a68273a03de60f216",
"is_available": true
}],
"price": 100,
"orig_price": 100
}, {
"name": "battlenoone",
"items": [{
"type": "single",
"id": "battlenoone",
"_id": "5fb5b68a68273a03de60f201",
"is_available": true
}],
"price": 100,
"orig_price": 100
}, {
"name": "saikyostronger",
"items": [{
"type": "single",
"id": "saikyostronger",
"_id": "5fb5b68a68273a03de60f20c",
"is_available": true
}],
"price": 100,
"orig_price": 100
}, {
"name": "izana",
"items": [{
"type": "single",
"id": "izana",
"_id": "5fb5b68a68273a03de60f223",
"is_available": true
}],
"price": 100,
"orig_price": 100
}, {
"name": "einherjar",
"items": [{
"type": "single",
"id": "einherjar",
"_id": "5fb5b68a68273a03de60f22f",
"is_available": true
}],
"price": 100,
"orig_price": 100
}, {
"name": "laqryma",
"items": [{
"type": "single",
"id": "laqryma",
"_id": "5fb5b68a68273a03de60f217",
"is_available": true
}],
"price": 100,
"orig_price": 100
}, {
"name": "amygdata",
"items": [{
"type": "single",
"id": "amygdata",
"_id": "5fb5b68a68273a03de60f202",
"is_available": true
}],
"price": 100,
"orig_price": 100
}, {
"name": "altale",
"items": [{
"type": "single",
"id": "altale",
"_id": "5fb5b68a68273a03de60f20d",
"is_available": true
}],
"price": 100,
"orig_price": 100
}, {
"name": "feelssoright",
"items": [{
"type": "single",
"id": "feelssoright",
"_id": "5fb5b68a68273a03de60f218",
"is_available": true
}],
"price": 100,
"orig_price": 100
}, {
"name": "scarletcage",
"items": [{
"type": "single",
"id": "scarletcage",
"_id": "5fb5b68a68273a03de60f203",
"is_available": true
}],
"price": 100,
"orig_price": 100
}, {
"name": "teriqma",
"items": [{
"type": "single",
"id": "teriqma",
"_id": "5fb5b68a68273a03de60f20e",
"is_available": true
}],
"price": 100,
"orig_price": 100
}, {
"name": "mahoroba",
"items": [{
"type": "single",
"id": "mahoroba",
"_id": "5fb5b68a68273a03de60f225",
"is_available": true
}],
"price": 100,
"orig_price": 100
}, {
"name": "badtek",
"items": [{
"type": "single",
"id": "badtek",
"_id": "5fb5b68a68273a03de60f219",
"is_available": true
}],
"price": 100,
"orig_price": 100
}, {
"name": "maliciousmischance",
"items": [{
"type": "single",
"id": "maliciousmischance",
"_id": "5fb5b68a68273a03de60f231",
"is_available": true
}],
"price": 100,
"orig_price": 100
}, {
"name": "buchigireberserker",
"items": [{
"type": "single",
"id": "buchigireberserker",
"_id": "5fb5b68a68273a03de60f20f",
"is_available": true
}],
"price": 100,
"orig_price": 100
}, {
"name": "galaxyfriends",
"items": [{
"type": "single",
"id": "galaxyfriends",
"_id": "5fb5b68a68273a03de60f226",
"is_available": true
}],
"price": 100,
"orig_price": 100
}, {
"name": "xeraphinite",
"items": [{
"type": "single",
"id": "xeraphinite",
"_id": "5fb5b68a68273a03de60f232",
"is_available": true
}],
"orig_price": 100,
"price": 100
}, {
"name": "xanatos",
"items": [{
"type": "single",
"id": "xanatos",
"_id": "5fb5b68a68273a03de60f21a",
"is_available": true
}],
"price": 100,
"orig_price": 100
}]

View File

@@ -2,6 +2,7 @@ AudioOffset:408
- -
timing(0,210.00,4.00); timing(0,210.00,4.00);
(1142,4); (1142,4);
flick(1142,0.50,0.50,0.00,1.00);
(1571,2); (1571,2);
(2857,1); (2857,1);
(3142,2); (3142,2);

View File

@@ -9,13 +9,21 @@ import web.login
import web.index import web.index
import server.arcworld import server.arcworld
import server.arcdownload import server.arcdownload
import server.arcpurchase
import os import os
app = Flask(__name__) app = Flask(__name__)
wsgi_app = app.wsgi_app wsgi_app = app.wsgi_app
def error_return(error_code): # 错误返回 def error_return(error_code): # 错误返回
# -7 处理交易时发生了错误
# -5 所有的曲目都已经下载完毕
# -4 您的账号已在别处登录
# -3 无法连接至服务器
# 2 Arcaea服务器正在维护
# 5 请更新Arcaea到最新版本
# 100 无法在此ip地址下登录游戏 # 100 无法在此ip地址下登录游戏
# 101 用户名占用 # 101 用户名占用
# 102 电子邮箱已注册 # 102 电子邮箱已注册
@@ -30,19 +38,27 @@ def error_return(error_code): # 错误返回
# 121 账户冻结 # 121 账户冻结
# 122 账户暂时冻结 # 122 账户暂时冻结
# 123 账户被限制 # 123 账户被限制
# 124 你今天不能再使用这个IP地址创建新的账号
# 150 非常抱歉您已被限制使用此功能 # 150 非常抱歉您已被限制使用此功能
# 151 目前无法使用此功能 # 151 目前无法使用此功能
# 401 用户不存在 # 401 用户不存在
# 403 无法连接至服务器 # 403 无法连接至服务器
# 501 502 此物品目前无法获取 # 501 502 -6 此物品目前无法获取
# 504 无效的序列码 # 504 无效的序列码
# 505 此序列码已被使用 # 505 此序列码已被使用
# 506 你已拥有了此物品 # 506 你已拥有了此物品
# 601 好友列表已满 # 601 好友列表已满
# 602 此用户已是好友 # 602 此用户已是好友
# 604 你不能加自己为好友 # 604 你不能加自己为好友
# 903 下载量超过了限制请24小时后重试
# 905 请在再次使用此功能前等待24小时
# 1001 设备数量达到上限 # 1001 设备数量达到上限
# 1002 设备已使用过功能 # 1002 设备已使用过功能
# 9801 下载歌曲时发生问题,请再试一次
# 9802 保存歌曲时发生问题,请检查设备空间容量
# 9905 没有在云端发现任何数据
# 9907 更新数据时发生了问题
# 9908 服务器只支持最新的版本请更新Arcaea
# 其它 发生未知错误 # 其它 发生未知错误
return jsonify({ return jsonify({
"success": False, "success": False,
@@ -55,6 +71,15 @@ def hello():
return "Hello World!" return "Hello World!"
@app.route('/favicon.ico', methods=['GET']) # 图标
def favicon():
# Pixiv ID: 82374369
# 我觉得这张图虽然并不是那么精细,但很有感觉,色彩的强烈对比下给人带来一种惊艳
# 然后在压缩之下什么也看不清了:(
return app.send_static_file('favicon.ico')
@app.route('/coffee/12/auth/login', methods=['POST']) # 登录接口 @app.route('/coffee/12/auth/login', methods=['POST']) # 登录接口
def login(): def login():
headers = request.headers headers = request.headers
@@ -90,7 +115,8 @@ def register():
return error_return(108) return error_return(108)
@app.route('/coffee/12/compose/aggregate', methods=['GET']) # 用户信息获取 # 集成式请求,没想到什么好办法处理,就先这样写着
@app.route('/coffee/12/compose/aggregate', methods=['GET'])
def aggregate(): def aggregate():
calls = request.args.get('calls') calls = request.args.get('calls')
headers = request.headers headers = request.headers
@@ -307,6 +333,8 @@ def song_score_post():
headers = request.headers headers = request.headers
token = headers['Authorization'] token = headers['Authorization']
token = token[7:] token = token[7:]
song_token = request.form['song_token']
song_hash = request.form['song_hash']
song_id = request.form['song_id'] song_id = request.form['song_id']
difficulty = int(request.form['difficulty']) difficulty = int(request.form['difficulty'])
score = int(request.form['score']) score = int(request.form['score'])
@@ -318,10 +346,15 @@ def song_score_post():
modifier = int(request.form['modifier']) modifier = int(request.form['modifier'])
beyond_gauge = int(request.form['beyond_gauge']) beyond_gauge = int(request.form['beyond_gauge'])
clear_type = int(request.form['clear_type']) clear_type = int(request.form['clear_type'])
submission_hash = request.form['submission_hash']
try: try:
user_id = server.auth.token_get_id(token) user_id = server.auth.token_get_id(token)
if user_id is not None: if user_id is not None:
# 增加成绩校验
if not server.arcscore.arc_score_check(user_id, song_id, difficulty, score, shiny_perfect_count, perfect_count, near_count, miss_count, health, modifier, beyond_gauge, clear_type, song_token, song_hash, submission_hash):
return error_return(107)
r, re = server.arcscore.arc_score_post(user_id, song_id, difficulty, score, shiny_perfect_count, r, re = server.arcscore.arc_score_post(user_id, song_id, difficulty, score, shiny_perfect_count,
perfect_count, near_count, miss_count, health, modifier, beyond_gauge, clear_type) perfect_count, near_count, miss_count, health, modifier, beyond_gauge, clear_type)
if r: if r:
@@ -423,12 +456,20 @@ def cloud_post():
return error_return(108) return error_return(108)
@app.route('/coffee/12/purchase/me/redeem', methods=['POST']) # 兑换码,然没有用 @app.route('/coffee/12/purchase/me/redeem', methods=['POST']) # 兑换码,然没有用
def redeem(): def redeem():
return error_return(504) return error_return(504)
# 购买自然没有用只是为了world模式boost一下 # 礼物确认
@app.route('/coffee/12/present/me/claim/<present_id>', methods=['POST'])
def claim_present(present_id):
return jsonify({
"success": True
})
# 购买为了world模式boost一下
@app.route('/coffee/12/purchase/me/item', methods=['POST']) @app.route('/coffee/12/purchase/me/item', methods=['POST'])
def item(): def item():
return jsonify({ return jsonify({
@@ -436,27 +477,32 @@ def item():
}) })
@app.route('/coffee/12/purchase/me/pack', methods=['POST']) # 购买,自然没有用 @app.route('/coffee/12/purchase/me/pack', methods=['POST']) # 曲包和单曲购买
def pack(): def pack():
headers = request.headers
token = headers['Authorization']
token = token[7:]
try:
user_id = server.auth.token_get_id(token)
if user_id:
if 'pack_id' in request.form:
return jsonify(server.arcpurchase.buy_pack(user_id, request.form['pack_id']))
if 'single_id' in request.form:
return jsonify(server.arcpurchase.buy_single(user_id, request.form['single_id']))
else:
return error_return(108)
except:
return error_return(108)
return jsonify({ return jsonify({
"success": True "success": True
}) })
@app.route('/coffee/12/purchase/bundle/single', methods=['GET']) # 单曲购买,自然没有用 @app.route('/coffee/12/purchase/bundle/single', methods=['GET']) # 单曲购买信息获取
def single(): def single():
return jsonify({ return jsonify({
"success": True, "success": True,
"value": [{ "value": server.arcpurchase.get_single_purchase()
"name": "testsingle",
"items": [{
"id": "testsingle",
"type": "single",
"is_available": False
}],
"price": 100,
"orig_price": 100
}]
}) })
@@ -466,6 +512,7 @@ def world_all():
token = headers['Authorization'] token = headers['Authorization']
token = token[7:] token = token[7:]
try: try:
user_id = server.auth.token_get_id(token) user_id = server.auth.token_get_id(token)
if user_id: if user_id:
return jsonify({ return jsonify({
@@ -530,21 +577,22 @@ def download_song():
token = headers['Authorization'] token = headers['Authorization']
token = token[7:] token = token[7:]
song_ids = request.args.getlist('sid') song_ids = request.args.getlist('sid')
try: try:
user_id = server.auth.token_get_id(token) user_id = server.auth.token_get_id(token)
if user_id: if user_id:
if server.arcdownload.is_able_download(user_id):
re = {} re = {}
if not song_ids: if not song_ids:
re = server.arcdownload.get_all_songs() re = server.arcdownload.get_all_songs(user_id)
else: else:
for song_id in song_ids: re = server.arcdownload.get_some_songs(user_id, song_ids)
re.update(server.arcdownload.get_one_song(song_id))
return jsonify({ return jsonify({
"success": True, "success": True,
"value": re "value": re
}) })
else:
return error_return(903)
else: else:
return error_return(108) return error_return(108)
except: except:
@@ -553,12 +601,17 @@ def download_song():
@app.route('/download/<path:file_path>', methods=['GET']) # 下载 @app.route('/download/<path:file_path>', methods=['GET']) # 下载
def download(file_path): def download(file_path):
t = request.args.get('t')
try: try:
message = server.arcdownload.is_token_able_download(t)
if message == 0:
path = os.path.join('./database/songs', file_path) path = os.path.join('./database/songs', file_path)
if os.path.isfile(path) and not('../' in path or '..\\' in path): if os.path.isfile(path) and not('../' in path or '..\\' in path):
return send_from_directory('./database/songs', file_path, as_attachment=True) return send_from_directory('./database/songs', file_path, as_attachment=True)
else: else:
return error_return(109) return error_return(109)
else:
return error_return(message)
except: except:
return error_return(108) return error_return(108)

View File

@@ -1,6 +1,11 @@
import os import os
import hashlib import hashlib
from flask import url_for from flask import url_for
import sqlite3
import time
time_limit = 3000 # 每个玩家24小时下载次数限制
time_gap_limit = 1000 # 下载链接有效秒数
def get_file_md5(file_path): def get_file_md5(file_path):
@@ -18,31 +23,105 @@ def get_file_md5(file_path):
return myhash.hexdigest() return myhash.hexdigest()
def get_one_song(song_id, file_dir='./database/songs'): def get_one_song(c, user_id, song_id, file_dir='./database/songs'):
# 获取一首歌的下载链接,返回字典 # 获取一首歌的下载链接,返回字典
dir_list = os.listdir(os.path.join(file_dir, song_id)) dir_list = os.listdir(os.path.join(file_dir, song_id))
re = {} re = {}
now = int(time.time())
c.execute('''delete from download_token where user_id=:a and song_id=:b''', {
'a': user_id, 'b': song_id})
for i in dir_list: for i in dir_list:
if os.path.isfile(os.path.join(file_dir, song_id, i)) and i in ['0.aff', '1.aff', '2.aff', '3.aff', 'base.ogg']: if os.path.isfile(os.path.join(file_dir, song_id, i)) and i in ['0.aff', '1.aff', '2.aff', '3.aff', 'base.ogg']:
token = hashlib.md5(
(str(user_id) + song_id + i + str(now)).encode(encoding='UTF-8')).hexdigest()
token = token[:8]
if i == 'base.ogg': if i == 'base.ogg':
re['audio'] = {"checksum": get_file_md5(os.path.join(file_dir, song_id, 'base.ogg')), re['audio'] = {"checksum": get_file_md5(os.path.join(file_dir, song_id, 'base.ogg')),
"url": url_for('download', file_path=song_id+'/base.ogg', _external=True)} "url": url_for('download', file_path=song_id+'/base.ogg', t=token, _external=True)}
else: else:
if 'chart' not in re: if 'chart' not in re:
re['chart'] = {} re['chart'] = {}
re['chart'][i[0]] = {"checksum": get_file_md5(os.path.join(file_dir, song_id, i)), re['chart'][i[0]] = {"checksum": get_file_md5(os.path.join(file_dir, song_id, i)),
"url": url_for('download', file_path=song_id+'/'+i, _external=True)} "url": url_for('download', file_path=song_id+'/'+i, t=token, _external=True)}
c.execute('''insert into download_token values(:a,:b,:c,:d,:e)''', {
'a': user_id, 'b': song_id, 'c': i, 'd': token, 'e': now})
return {song_id: re} return {song_id: re}
def get_all_songs(file_dir='./database/songs'): def get_all_songs(user_id, file_dir='./database/songs'):
# 获取所有歌的下载链接,返回字典 # 获取所有歌的下载链接,返回字典
dir_list = os.listdir(file_dir) dir_list = os.listdir(file_dir)
re = {} re = {}
conn = sqlite3.connect('./database/arcaea_database.db')
c = conn.cursor()
for i in dir_list: for i in dir_list:
if os.path.isdir(os.path.join(file_dir, i)): if os.path.isdir(os.path.join(file_dir, i)):
re.update(get_one_song(i)) re.update(get_one_song(c, user_id, i))
conn.commit()
conn.close()
return re return re
def get_some_songs(user_id, song_ids):
# 获取一些歌的下载链接,返回字典
re = {}
conn = sqlite3.connect('./database/arcaea_database.db')
c = conn.cursor()
for song_id in song_ids:
re.update(get_one_song(c, user_id, song_id))
conn.commit()
conn.close()
return re
def is_token_able_download(t):
# token是否可以下载返回错误码0即可以
errorcode = 0
conn = sqlite3.connect('./database/arcaea_database.db')
c = conn.cursor()
c.execute('''select * from download_token where token = :t limit 1''',
{'t': t})
x = c.fetchone()
now = int(time.time())
if x and now - x[4] <= time_gap_limit:
c.execute(
'''select count(*) from user_download where user_id = :a''', {'a': x[0]})
y = c.fetchone()
if y and y[0] <= time_limit:
c.execute('''insert into user_download values(:a,:b,:c)''', {
'a': x[0], 'b': x[3], 'c': now})
else:
errorcode = 903
else:
errorcode = 108
conn.commit()
conn.close()
return errorcode
def is_able_download(user_id):
# 是否可以下载,返回布尔值
f = True
conn = sqlite3.connect('./database/arcaea_database.db')
c = conn.cursor()
now = int(time.time())
c.execute(
'''delete from user_download where user_id = :a and time <= :b''', {'a': user_id, 'b': now - 24*3600})
c.execute(
'''select count(*) from user_download where user_id = :a''', {'a': user_id})
y = c.fetchone()
if y and y[0] <= time_limit:
pass
else:
f = False
conn.commit()
conn.close()
return f

View File

@@ -0,0 +1,122 @@
import sqlite3
def int2b(x):
# int与布尔值转换
if x is None or x == 0:
return False
else:
return True
def get_item(c, type='pack'):
# 读取packs内容返回字典列表
c.execute('''select * from item where type = :a''', {'a': type})
x = c.fetchall()
if not x:
return []
re = []
for i in x:
r = {"name": i[0],
"items": [{
"type": i[1],
"id": i[0],
"is_available": int2b(i[2])
}],
"price": i[3],
"orig_price": i[4]}
if i[5] > 0:
r['discount_from'] = i[5]
if i[6] > 0:
r['discount_to'] = i[6]
re.append(r)
return re
def get_single_purchase():
# main里面没开数据库这里写一下代替
conn = sqlite3.connect('./database/arcaea_database.db')
c = conn.cursor()
re = get_item(c, type='single')
conn.commit()
conn.close()
return re
def buy_pack(user_id, pack_id):
# 曲包购买,返回字典
conn = sqlite3.connect('./database/arcaea_database.db')
c = conn.cursor()
c.execute('''select price from item where item_id = :a''', {'a': pack_id})
price = c.fetchone()
if price:
price = price[0]
else:
price = 0
c.execute('''select ticket from user where user_id = :a''', {'a': user_id})
ticket = c.fetchone()
if ticket:
ticket = ticket[0]
else:
ticket = 0
if ticket < price:
conn.commit()
conn.close()
return {
"success": False
}
c.execute('''update user set ticket = :b where user_id = :a''',
{'a': user_id, 'b': ticket-price})
c.execute('''insert into user_item values(:a,:b,'pack')''',
{'a': user_id, 'b': pack_id})
conn.commit()
conn.close()
return {
"success": True
}
def buy_single(user_id, single_id):
# 单曲购买,返回字典
conn = sqlite3.connect('./database/arcaea_database.db')
c = conn.cursor()
c.execute('''select price from item where item_id = :a''',
{'a': single_id})
price = c.fetchone()
if price:
price = price[0]
else:
price = 0
c.execute('''select ticket from user where user_id = :a''', {'a': user_id})
ticket = c.fetchone()
if ticket:
ticket = ticket[0]
else:
ticket = 0
if ticket < price:
conn.commit()
conn.close()
return {
"success": False
}
c.execute('''update user set ticket = :b where user_id = :a''',
{'a': user_id, 'b': ticket-price})
c.execute('''insert into user_item values(:a,:b,'single')''',
{'a': user_id, 'b': single_id})
conn.commit()
conn.close()
return {
"success": True
}

View File

@@ -2,6 +2,7 @@ import sqlite3
import time import time
import json import json
import server.arcworld import server.arcworld
import hashlib
def b2int(x): def b2int(x):
@@ -20,6 +21,16 @@ def int2b(x):
return True return True
def md5(code):
# md5加密算法
code = code.encode()
md5s = hashlib.md5()
md5s.update(code)
codes = md5s.hexdigest()
return codes
def get_score(c, user_id, song_id, difficulty): def get_score(c, user_id, song_id, difficulty):
# 根据user_id、song_id、难度得到该曲目最好成绩返回字典 # 根据user_id、song_id、难度得到该曲目最好成绩返回字典
c.execute('''select * from best_score where user_id = :a and song_id = :b and difficulty = :c''', c.execute('''select * from best_score where user_id = :a and song_id = :b and difficulty = :c''',
@@ -480,6 +491,29 @@ def arc_score_post(user_id, song_id, difficulty, score, shiny_perfect_count, per
return ptt, re return ptt, re
def arc_score_check(user_id, song_id, difficulty, score, shiny_perfect_count, perfect_count, near_count, miss_count, health, modifier, beyond_gauge, clear_type, song_token, song_hash, submission_hash):
# 分数校验,返回布尔值
if shiny_perfect_count < 0 or perfect_count < 0 or near_count < 0 or miss_count < 0 or score < 0:
return False
if difficulty not in [0, 1, 2, 3]:
return False
all_note = perfect_count + near_count + miss_count
ascore = 10000000 / all_note * \
(perfect_count + near_count/2) + shiny_perfect_count
if abs(ascore - score) >= 5:
return False
x = song_token + song_hash + song_id + str(difficulty) + str(score) + str(shiny_perfect_count) + str(
perfect_count) + str(near_count) + str(miss_count) + str(health) + str(modifier) + str(clear_type)
y = str(user_id) + song_hash
checksum = md5(x+md5(y))
if checksum != submission_hash:
return False
return True
def arc_all_post(user_id, scores_data, clearlamps_data): def arc_all_post(user_id, scores_data, clearlamps_data):
# 向云端同步,无返回 # 向云端同步,无返回
# 注意best_score表不比较直接覆盖 # 注意best_score表不比较直接覆盖
@@ -1356,7 +1390,18 @@ def arc_all_get(user_id):
}, { }, {
"complete": 1, "complete": 1,
"unlock_key": "dreaminattraction|1|0" "unlock_key": "dreaminattraction|1|0"
}] }, {
"complete": 1,
"unlock_key": "buchigireberserker|2|0"
}, {
"complete": 1,
"unlock_key": "gothiveofra|2|0"
}, {
"complete": 1,
"unlock_key": "gothiveofra|1|0"
}
]
}, "clearedsongs": { }, "clearedsongs": {
"": song_1 "": song_1
}, },

View File

@@ -62,18 +62,6 @@ def arc_register(name: str, password: str): # 注册
else: else:
return 2000001 return 2000001
# def insert_user_char(c, user_id):
# # 为用户添加所有可用角色
# for i in range(0, 38):
# if i in [0, 1, 2, 4, 13, 26, 27, 28, 29, 36, 21]:
# sql = 'insert into user_char values('+str(user_id)+','+str(
# i)+''',30,25000,25000,90,90,90,'',0,0,'',0,1,1)'''
# c.execute(sql)
# else:
# if i != 5:
# sql = 'insert into user_char values('+str(user_id)+','+str(
# i)+''',30,25000,25000,90,90,90,'',0,0,'',0,0,0)'''
# c.execute(sql)
def insert_user_char(c, user_id): def insert_user_char(c, user_id):
# 为用户添加所有可用角色 # 为用户添加所有可用角色
c.execute('''select * from character''') c.execute('''select * from character''')
@@ -93,8 +81,8 @@ def arc_register(name: str, password: str): # 注册
user_id = build_user_id(c) user_id = build_user_id(c)
now = int(time.time() * 1000) now = int(time.time() * 1000)
c.execute('''insert into user(user_id, name, password, join_date, user_code, rating_ptt, 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) character_id, is_skill_sealed, is_char_uncapped, is_char_uncapped_override, is_hide_rating, favorite_character, max_stamina_notification_enabled, current_map, ticket)
values(:user_id, :name, :password, :join_date, :user_code, 0, 0, 0, 0, 0, 0, -1, 0, '') values(:user_id, :name, :password, :join_date, :user_code, 0, 0, 0, 0, 0, 0, -1, 0, '', 114514)
''', {'user_code': user_code, 'user_id': user_id, 'join_date': now, 'name': name, 'password': hash_pwd}) ''', {'user_code': user_code, 'user_id': user_id, 'join_date': now, 'name': name, 'password': hash_pwd})
c.execute('''insert into recent30(user_id) values(:user_id)''', { c.execute('''insert into recent30(user_id) values(:user_id)''', {
'user_id': user_id}) 'user_id': user_id})

View File

@@ -1,5 +1,7 @@
import sqlite3 import sqlite3
import server.arcworld import server.arcworld
import server.arcpurchase
import time
def int2b(x): def int2b(x):
@@ -56,7 +58,7 @@ def get_user_character(c, user_id):
y = c.fetchone() y = c.fetchone()
if y is not None: if y is not None:
char_name = y[0] char_name = y[0]
s.append({ char = {
"is_uncapped_override": int2b(i[14]), "is_uncapped_override": int2b(i[14]),
"is_uncapped": int2b(i[13]), "is_uncapped": int2b(i[13]),
"uncap_cores": [], "uncap_cores": [],
@@ -73,7 +75,10 @@ def get_user_character(c, user_id):
"level": i[2], "level": i[2],
"name": char_name, "name": char_name,
"character_id": i[1] "character_id": i[1]
}) }
if i[1] == 21:
char["voice"] = [0, 1, 2, 3, 100, 1000, 1001]
s.append(char)
return s return s
else: else:
@@ -115,6 +120,34 @@ def get_user_friend(c, user_id):
return s return s
def get_user_singles(c, user_id):
# 得到用户的单曲,返回列表
c.execute('''select * from user_item where user_id = :user_id and type = "single"''',
{'user_id': user_id})
x = c.fetchall()
if not x:
return []
re = []
for i in x:
re.append(i[1])
return re
def get_user_packs(c, user_id):
# 得到用户的曲包,返回列表
c.execute('''select * from user_item where user_id = :user_id and type = "pack"''',
{'user_id': user_id})
x = c.fetchall()
if not x:
return []
re = []
for i in x:
re.append(i[1])
return re
def get_value_0(c, user_id): def get_value_0(c, user_id):
# 构造value id=0的数据返回字典 # 构造value id=0的数据返回字典
c.execute('''select * from user where user_id = :x''', {'x': user_id}) c.execute('''select * from user where user_id = :x''', {'x': user_id})
@@ -139,7 +172,7 @@ def get_value_0(c, user_id):
"name": x[1], "name": x[1],
"user_code": x[4], "user_code": x[4],
"display_name": x[1], "display_name": x[1],
"ticket": 114514, "ticket": x[26],
"character": x[6], "character": x[6],
"is_locked_name_duplicate": False, "is_locked_name_duplicate": False,
"is_skill_sealed": int2b(x[7]), "is_skill_sealed": int2b(x[7]),
@@ -148,10 +181,11 @@ def get_value_0(c, user_id):
"next_fragstam_ts": -1, "next_fragstam_ts": -1,
"max_stamina_ts": 1586274871917, "max_stamina_ts": 1586274871917,
"stamina": 12, "stamina": 12,
"world_unlocks": [], "world_unlocks": ["scenery_chap1", "scenery_chap2", "scenery_chap3", "scenery_chap4", "scenery_chap5"],
"world_songs": ["babaroque", "shadesoflight", "kanagawa", "lucifer", "anokumene", "ignotus", "rabbitintheblackroom", "qualia", "redandblue", "bookmaker", "darakunosono", "espebranch", "blacklotus", "givemeanightmare", "vividtheory", "onefr", "gekka", "vexaria3", "infinityheaven3", "fairytale3", "goodtek3", "suomi", "rugie", "faintlight", "harutopia", "goodtek", "dreaminattraction", "syro", "diode", "freefall", "grimheart", "blaster", "cyberneciacatharsis", "monochromeprincess", "revixy", "vector", "supernova", "nhelv", "purgatorium3", "dement3", "crossover", "guardina", "axiumcrisis", "worldvanquisher", "sheriruth", "pragmatism", "gloryroad", "etherstrike", "corpssansorganes", "lostdesire", "blrink", "essenceoftwilight", "lapis"], "world_songs": ["babaroque", "shadesoflight", "kanagawa", "lucifer", "anokumene", "ignotus", "rabbitintheblackroom", "qualia", "redandblue", "bookmaker", "darakunosono", "espebranch", "blacklotus", "givemeanightmare", "vividtheory", "onefr", "gekka", "vexaria3", "infinityheaven3", "fairytale3", "goodtek3", "suomi", "rugie", "faintlight", "harutopia", "goodtek", "dreaminattraction", "syro", "diode", "freefall", "grimheart", "blaster", "cyberneciacatharsis", "monochromeprincess", "revixy", "vector", "supernova", "nhelv", "purgatorium3", "dement3", "crossover", "guardina", "axiumcrisis", "worldvanquisher", "sheriruth", "pragmatism", "gloryroad", "etherstrike", "corpssansorganes", "lostdesire", "blrink", "essenceoftwilight", "lapis", "solitarydream", "lumia3"],
"singles": ["dataerror", "yourvoiceso", "crosssoul", "impurebird", "auxesia", "modelista", "yozakurafubuki", "surrender", "metallicpunisher", "carminescythe", "bethere", "callmyname", "fallensquare", "dropdead", "alexandrite", "astraltale", "phantasia", "empireofwinter", "libertas", "dottodot", "dreadnought", "mirzam", "heavenlycaress", "filament", "avantraze", "battlenoone", "saikyostronger", "izana", "einherjar", "laqryma", "amygdata", "altale", "feelssoright", "scarletcage", "teriqma", "mahoroba", "badtek", "maliciousmischance", "buchigireberserker", "galaxyfriends", "buchigireberserker2", "xeraphinite"], "singles": get_user_singles(c, user_id), # ["dataerror", "yourvoiceso", "crosssoul", "impurebird", "auxesia", "modelista", "yozakurafubuki", "surrender", "metallicpunisher", "carminescythe", "bethere", "callmyname", "fallensquare", "dropdead", "alexandrite", "astraltale", "phantasia", "empireofwinter", "libertas", "dottodot", "dreadnought", "mirzam", "heavenlycaress", "filament", "avantraze", "battlenoone", "saikyostronger", "izana", "einherjar", "laqryma", "amygdata", "altale", "feelssoright", "scarletcage", "teriqma", "mahoroba", "badtek", "maliciousmischance", "buchigireberserker", "galaxyfriends", "xeraphinite", "xanatos"]
"packs": ["vs", "extend", "dynamix", "prelude", "core", "yugamu", "omatsuri", "zettai", "mirai", "shiawase", "chunithm", "nijuusei", "groovecoaster", "rei", "tonesphere", "lanota"], "packs": get_user_packs(c, user_id),
# ["vs", "extend", "dynamix", "prelude", "core", "yugamu", "omatsuri", "zettai", "mirai", "shiawase", "chunithm", "nijuusei", "groovecoaster", "rei", "tonesphere", "lanota"]
"characters": characters, "characters": characters,
"cores": [], "cores": [],
"recent_score": get_recent_score(c, user_id), "recent_score": get_recent_score(c, user_id),
@@ -180,7 +214,6 @@ def arc_aggregate_small(user_id):
def arc_aggregate_big(user_id): def arc_aggregate_big(user_id):
# 返回用户数据和地图歌曲信息 # 返回用户数据和地图歌曲信息
# 因为没有整理地图和曲包数据(不需要世界模式),所以直接复制了
conn = sqlite3.connect('./database/arcaea_database.db') conn = sqlite3.connect('./database/arcaea_database.db')
c = conn.cursor() c = conn.cursor()
@@ -190,185 +223,7 @@ def arc_aggregate_big(user_id):
"value": get_value_0(c, user_id) "value": get_value_0(c, user_id)
}, { }, {
"id": 1, "id": 1,
"value": [{ "value": server.arcpurchase.get_item(c, 'pack')
"name": "core",
"items": [{
"id": "core",
"type": "pack",
"is_available": True
}],
"price": 500,
"orig_price": 500,
"discount_from": 1583712000000,
"discount_to": 1584316799000
}, {
"name": "shiawase",
"items": [{
"id": "shiawase",
"type": "pack",
"is_available": True
}, {
"id": "kou",
"type": "character",
"is_available": True
}],
"price": 500,
"orig_price": 500,
"discount_from": 1552089600000,
"discount_to": 1552694399000
}, {
"name": "dynamix",
"items": [{
"id": "dynamix",
"type": "pack",
"is_available": True
}, {
"id": "sapphire",
"type": "character",
"is_available": True
}],
"price": 500,
"orig_price": 500,
"discount_from": 1583712000000,
"discount_to": 1584316799000
}, {
"name": "mirai",
"items": [{
"id": "mirai",
"type": "pack",
"is_available": True
}, {
"id": "lethe",
"type": "character",
"is_available": True
}],
"price": 500,
"orig_price": 500,
"discount_from": 1552089600000,
"discount_to": 1552694399000
}, {
"name": "yugamu",
"items": [{
"id": "yugamu",
"type": "pack",
"is_available": True
}],
"price": 500,
"orig_price": 500,
"discount_from": 1583712000000,
"discount_to": 1584316799000
}, {
"name": "lanota",
"items": [{
"id": "lanota",
"type": "pack",
"is_available": True
}],
"price": 500,
"orig_price": 500,
"discount_from": 1583712000000,
"discount_to": 1584316799000
}, {
"name": "nijuusei",
"items": [{
"id": "nijuusei",
"type": "pack",
"is_available": True
}],
"price": 500,
"orig_price": 500,
"discount_from": 1583712000000,
"discount_to": 1584316799000
}, {
"name": "rei",
"items": [{
"id": "rei",
"type": "pack",
"is_available": True
}],
"price": 500,
"orig_price": 500,
"discount_from": 1583712000000,
"discount_to": 1584316799000
}, {
"name": "tonesphere",
"items": [{
"id": "tonesphere",
"type": "pack",
"is_available": True
}],
"price": 500,
"orig_price": 500,
"discount_from": 1583712000000,
"discount_to": 1584316799000
}, {
"name": "groovecoaster",
"items": [{
"id": "groovecoaster",
"type": "pack",
"is_available": True
}],
"price": 500,
"orig_price": 500,
"discount_from": 1583712000000,
"discount_to": 1584316799000
}, {
"name": "zettai",
"items": [{
"id": "zettai",
"type": "pack",
"is_available": True
}],
"price": 500,
"orig_price": 500,
"discount_from": 1583712000000,
"discount_to": 1584316799000
}, {
"name": "chunithm",
"items": [{
"id": "chunithm",
"type": "pack",
"is_available": True
}],
"price": 300,
"orig_price": 300
}, {
"name": "prelude",
"items": [{
"id": "prelude",
"type": "pack",
"is_available": True
}],
"price": 400,
"orig_price": 400
}, {
"name": "omatsuri",
"items": [{
"id": "omatsuri",
"type": "pack",
"is_available": True
}],
"price": 500,
"orig_price": 500
}, {
"name": "vs",
"items": [{
"id": "vs",
"type": "pack",
"is_available": True
}],
"price": 500,
"orig_price": 500
}, {
"name": "extend",
"items": [{
"id": "extend",
"type": "pack",
"is_available": True
}],
"price": 700,
"orig_price": 700
}]
}, { }, {
"id": 2, "id": 2,
"value": {} "value": {}
@@ -378,7 +233,7 @@ def arc_aggregate_big(user_id):
"max_stamina": 12, "max_stamina": 12,
"stamina_recover_tick": 1800000, "stamina_recover_tick": 1800000,
"core_exp": 250, "core_exp": 250,
"curr_ts": 1599547606825, "curr_ts": int(time.time())*1000,
"level_steps": [{ "level_steps": [{
"level": 1, "level": 1,
"level_exp": 0 "level_exp": 0

View File

@@ -1,3 +1,7 @@
[CONFIG] [CONFIG]
HOST = 192.168.1.113 HOST = 192.168.1.113
PORT = 80 PORT = 80
[WEB]
USERNAME = admin
PASSWORD = admin

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -210,8 +210,28 @@ input[type=submit] {
font-weight: bold; font-weight: bold;
} }
.footer{ .footer {
font-size: 0.5em; font-size: 0.5em;
color: grey; color: grey;
text-align: center; text-align: center;
} }
.char-item {
margin-bottom: 10px;
clear: both;
}
.char-name {
font-size: 1.3em;
font-weight: bold;
display: inline-block;
}
.char-id {
font-size: 1em;
display: inline-block;
}
.char-num {
font-size: 1em;
}

View File

@@ -0,0 +1,59 @@
{% extends 'base.html' %}
{% block header %}
<h1>{% block title %}All characters{% endblock %}</h1>
{% endblock %}
{% block content %}
{% if posts %}<br />
{% for char in posts %}
<div class="char-item">
<span>Id: </span>
<span class="char-id">{{char['character_id']}}</span>
<br />
<span>Name: </span>
<span class="char-name">{{char['name']}}</span>
<br />
<span>Level: </span>
<span class="char-num">{{char['level']}}</span>
<br />
<span>Frag: </span>
<span class="char-num">{{char['frag']}}</span>
<br />
<span>Prog: </span>
<span class="char-num">{{char['prog']}}</span>
<br />
<span>Overdrive: </span>
<span class="char-num">{{char['overdrive']}}</span>
<br />
<span>Skill id: </span>
<span class="char-num">{{char['skill_id']}}</span>
<br />
<span>Uncapped skill id: </span>
<span class="char-num">{{char['skill_id_uncap']}}</span>
<br />
<span>Is uncapped: </span>
<span class="char-num">{{char['is_uncapped']}}</span>
<br />
</div>
{% if not loop.last %}
<br />
<hr />
<br />
{% endif %}
{% endfor %}
{% endif %}
{% endblock %}

View File

@@ -0,0 +1,51 @@
{% extends 'base.html' %}
{% block header %}
<h1>{% block title %}All items{% endblock %}</h1>
{% endblock %}
{% block content %}
{% if posts %}<br />
{% for item in posts %}
<div class="char-item">
<span>Id: </span>
<span class="name">{{item['item_id']}}</span>
<br />
<span>Type: </span>
<span class="char-num">{{item['type']}}</span>
<br />
<span>Is available: </span>
<span class="char-num">{{item['is_available']}}</span>
<br />
<span>Price: </span>
<span class="char-num">{{item['price']}}</span>
<br />
<span>Original price: </span>
<span class="char-num">{{item['orig_price']}}</span>
<br />
<span>Discount from: </span>
<span class="char-num">{{item['discount_from']}}</span>
<br />
<span>Discount to: </span>
<span class="char-num">{{item['discount_to']}}</span>
<br />
</div>
{% if not loop.last %}
<br />
<hr />
<br />
{% endif %}
{% endfor %}
{% endif %}
{% endblock %}

View File

@@ -13,7 +13,8 @@
<span class="rank">User code: {{user['user_code']}}</span> <span class="rank">User code: {{user['user_code']}}</span>
</div> </div>
<div class="join-date">注册于 Registered in: {{user['join_date']}}</div> <div class="join-date">注册于 Registered in: {{user['join_date']}}</div>
<div class="ptt">PTT: {{user['rating_ptt']//100 ~ '.' ~ user['rating_ptt']%100}}</div> <div class="ptt">PTT: {{'%0.2f'|format(user['rating_ptt']/100|float)}}</div>
<div class="ptt">Memories: {{user['ticket']}}</div>
<div> <div>
<div>Recent plays: </div> <div>Recent plays: </div>
<div> <div>

View File

@@ -0,0 +1,55 @@
{% extends 'base.html' %}
{% block header %}
<h1>{% block title %}Change the characters{% endblock %}</h1>
{% endblock %}
{% block content %}
<form action="/web/changesong/editchar" method="post">
<div class="title">Edit the character</div>
<label for="id">ID of the character</label>
<input name="id" id="sid" required>
<label for="level">Level</label>
<input name="level" id="level">
<label for="frag">Frag</label>
<input name="frag" id="frag">
<label for="prog">Prog</label>
<input name="prog" id="prog">
<label for="overdrive">Overdrive</label>
<input name="overdrive" id="overdrive">
<div>
<select name="skill_id">
<option value="">Skill id</option>
{% for skill_id in skill_ids %}
<option value={{ skill_id }}>{{ skill_id }}</option>
{% endfor %}
</select>
</div>
<div>
<select name="skill_id_uncap">
<option value="">Uncapped skill id</option>
{% for skill_id in skill_ids %}
<option value={{ skill_id }}>{{ skill_id }}</option>
{% endfor %}
</select>
</div>
<br />
<div class="content">如果某些不需要修改,留空即可</div>
<div class="content">If some things do not need to be modified, leave it blank.</div>
<div class="content">更新时角色数据表不会同步</div>
<div class="content">The character table will not be synchronized when updated.</div>
<input type="submit" value="Edit">
</form>
<br />
<hr />
<form action="/web/changesong/updatechar" method="post">
<div class="title">Update the character</div>
<div class="content">更新所有用户拥有的角色</div>
<div class="content">Update all users' characters.</div>
<input type="submit" value="Update">
</form>
{% endblock %}

View File

@@ -0,0 +1,41 @@
{% extends 'base.html' %}
{% block header %}
<h1>{% block title %}Change the items{% endblock %}</h1>
{% endblock %}
{% block content %}
<form action="/web/changeitem" method="post">
<div class="title">Change the item</div>
<label for="item_id">ID of the item</label>
<input name="item_id" id="item_id" required>
<label for="type">Type</label>
<div>
<select name="type">
<option value='single'>Single</option>
<option value='pack'>Pack</option>
</select>
</div>
<br />
<div class="content">可修改项 Modifiable items:</div>
<div><label for="is_available">是否可用 Is available:</label>
<br />
<br />
<label><input type="radio" name="is_available" value=1>True</label>
<br />
<label><input type="radio" name="is_available" value=0>False</label>
</div>
<label for="price">Price</label>
<input name="price" id="price">
<label for="orig_price">Original price</label>
<input name="orig_price" id="orig_price">
<label for="discount_from">Discount from</label>
<input type="datetime-local" name="discount_from" id="discount_from">
<label for="discount_to">Discount to</label>
<input type="datetime-local" name="discount_to" id="discount_to">
<div class="content">如果某些不需要修改,留空即可</div>
<div class="content">If some things do not need to be modified, leave it blank.</div>
<div class="content">时间填写是一个HTML5控件</div>
<div class="content">Time filling is an HTML5 control.</div>
<input type="submit" value="Edit">
</form>
{% endblock %}

View File

@@ -0,0 +1,37 @@
{% extends 'base.html' %}
{% block header %}
<h1>{% block title %}Change user information{% endblock %}</h1>
{% endblock %}
{% block content %}
<form action="/web/changeuser/edituser" method="post">
<div class="title">Edit the user</div>
<label for="name">Arcaea Username</label>
<input name="name" id="name">
or<br />
<label for="user_code">Arcaea User Code</label>
<input name="user_code" id="user_code">
<br />
<br />
<label for="ticket">Memories</label>
<input name="ticket" id="ticket">
<br />
<div class="content">如果某些不需要修改,留空即可</div>
<div class="content">If some things do not need to be modified, leave it blank.</div>
<div class="content">目前只支持修改记忆源点</div>
<div class="content">At present, only memories can be modified.</div>
<input type="submit" value="Edit">
</form>
<br />
<hr />
<form action="/web/changeuser/edituser" method="post">
<div class="title">Edit all the users</div>
<label for="ticket">Memories</label>
<input name="ticket" id="ticket">
<input type="submit" value="Edit all">
</form>
{% endblock %}

View File

@@ -0,0 +1,44 @@
{% extends 'base.html' %}
{% block header %}
<h1>{% block title %}Change user purchase information{% endblock %}</h1>
{% endblock %}
{% block content %}
<form action="/web/changeuserpurchase/edituser" method="post">
<div class="title">Edit the user</div>
<label for="name">Arcaea Username</label>
<input name="name" id="name">
or<br />
<label for="user_code">Arcaea User Code</label>
<input name="user_code" id="user_code">
<br />
<br />
<div>对所有单曲和曲包的操作 Operation to all singles and packs:
<br />
<br />
<label><input type="radio" name="method" value="0">解锁 Unlock</label>
<br />
<label><input type="radio" name="method" value="1">封绝 Lock</label>
</div>
<br />
<input type="submit" value="Execute">
</form>
<br />
<hr />
<form action="/web/changeuserpurchase/" method="post">
<div class="title">Edit all the users</div>
<br />
<div>对所有单曲和曲包的操作 Operation to all singles and packs:
<br />
<br />
<label><input type="radio" name="method" value="0">解锁 Unlock</label>
<br />
<label><input type="radio" name="method" value="1">封绝 Lock</label>
</div>
<br />
<input type="submit" value="Execute">
</form>
{% endblock %}

View File

@@ -14,11 +14,17 @@
<a href="{{ url_for('index.single_player_ptt') }}">单个玩家PTT详情查询 Single player ptt</a></br></br> <a href="{{ url_for('index.single_player_ptt') }}">单个玩家PTT详情查询 Single player ptt</a></br></br>
<a href="{{ url_for('index.all_player') }}">所有玩家信息查询 All players</a></br></br> <a href="{{ url_for('index.all_player') }}">所有玩家信息查询 All players</a></br></br>
<a href="{{ url_for('index.all_song') }}">铺面信息查询 All songs</a></br></br> <a href="{{ url_for('index.all_song') }}">铺面信息查询 All songs</a></br></br>
<a href="{{ url_for('index.all_character') }}">角色信息查询 All characters</a></br></br>
<a href="{{ url_for('index.all_item') }}">购买信息查询 All items</a></br></br>
<a href="{{ url_for('index.single_chart_top') }}">单个铺面排行榜查询 Single song chart tops</a> <a href="{{ url_for('index.single_chart_top') }}">单个铺面排行榜查询 Single song chart tops</a>
<hr> <hr>
<h1>系统方面 System</h1> <h1>系统方面 System</h1>
<a href="{{ url_for('index.update_database') }}">数据库更新 Update databases</a></br></br> <a href="{{ url_for('index.update_database') }}">数据库更新 Update databases</a></br></br>
<a href="{{ url_for('index.change_song') }}">歌曲修改 Change the songs</a> <a href="{{ url_for('index.change_song') }}">歌曲修改 Change the songs</a></br></br>
<a href="{{ url_for('index.change_character') }}">角色修改 Change the characters</a></br></br>
<a href="{{ url_for('index.change_item') }}">购买信息修改 Change the items</a></br></br>
<a href="{{ url_for('index.change_user') }}">用户信息修改 Change user information</a></br></br>
<a href="{{ url_for('index.change_user_purchase') }}">用户购买信息修改 Change user purchase information</a>
{% endblock %} {% endblock %}

View File

@@ -24,7 +24,8 @@
<span class="rank">User code: {{user['user_code']}}</span> <span class="rank">User code: {{user['user_code']}}</span>
</div> </div>
<div class="join-date">注册于 Registered in: {{user['join_date']}}</div> <div class="join-date">注册于 Registered in: {{user['join_date']}}</div>
<div class="ptt">PTT: {{user['rating_ptt']//100 ~ '.' ~ user['rating_ptt']%100}}</div> <div class="ptt">Memories: {{user['ticket']}}</div>
<div class="ptt">PTT: {{'%0.2f'|format(user['rating_ptt']/100|float)}}</div>
<div class="ptt">Best 30 PTT: {{bestptt}}</div> <div class="ptt">Best 30 PTT: {{bestptt}}</div>
<div class="ptt">Recent 10 PTT: {{recentptt}}</div> <div class="ptt">Recent 10 PTT: {{recentptt}}</div>
<div> <div>

View File

@@ -157,7 +157,8 @@ def all_player():
'miss_count': i[17], 'miss_count': i[17],
'time_played': time_played, 'time_played': time_played,
'clear_type': i[21], 'clear_type': i[21],
'rating': i[22] 'rating': i[22],
'ticket': i[26]
}) })
else: else:
error = '没有玩家数据 No player data.' error = '没有玩家数据 No player data.'
@@ -358,3 +359,411 @@ def delete_song():
flash(error) flash(error)
return redirect(url_for('index.change_song')) return redirect(url_for('index.change_song'))
@bp.route('/allchar', methods=['GET'])
@login_required
def all_character():
# 所有角色数据
conn = sqlite3.connect('./database/arcaea_database.db')
c = conn.cursor()
c.execute('''select * from character''')
x = c.fetchall()
error = None
if x:
posts = []
for i in x:
posts.append({'character_id': i[0],
'name': i[1],
'level': i[2],
'frag': i[5],
'prog': i[6],
'overdrive': i[7],
'skill_id': i[8],
'skill_id_uncap': i[11],
'char_type': i[12],
'is_uncapped': i[14] == 1
})
else:
error = '没有角色数据 No character data.'
conn.commit()
conn.close()
if error:
flash(error)
return render_template('web/allchar.html')
else:
return render_template('web/allchar.html', posts=posts)
@bp.route('/changechar', methods=['GET'])
@login_required
def change_character():
# 修改角色数据
skill_ids = ['No_skill', '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']
return render_template('web/changechar.html', skill_ids=skill_ids)
@bp.route('/changesong/editchar', methods=['POST'])
@login_required
def edit_char():
# 修改角色数据
error = None
try:
character_id = int(request.form['id'])
level = request.form['level']
frag = request.form['frag']
prog = request.form['prog']
overdrive = request.form['overdrive']
skill_id = request.form['skill_id']
skill_id_uncap = request.form['skill_id_uncap']
if level:
level = int(level)
else:
level = None
if frag:
frag = float(frag)
else:
frag = None
if prog:
prog = float(prog)
else:
prog = None
if overdrive:
overdrive = float(overdrive)
else:
overdrive = None
except:
error = '数据错误 Wrong data.'
flash(error)
return redirect(url_for('index.change_character'))
conn = sqlite3.connect('./database/arcaea_database.db')
c = conn.cursor()
c.execute(
'''select exists(select * from character where character_id=:a)''', {'a': character_id})
if c.fetchone() == (1,):
if level is None and frag is None and prog is None and overdrive is None and skill_id is None and skill_id_uncap is None:
error = '无修改 No change.'
else:
sql = '''update character set level_exp=25000'''
sql_dict = {'character_id': character_id}
if level is not None:
sql += ', level = :level'
sql_dict['level'] = level
if frag is not None:
sql += ', frag = :frag'
sql_dict['frag'] = frag
if prog is not None:
sql += ', prog = :prog'
sql_dict['prog'] = prog
if overdrive is not None:
sql += ', overdrive = :overdrive'
sql_dict['overdrive'] = overdrive
if skill_id is not None:
sql += ', skill_id = :skill_id'
if skill_id == 'No_skill':
sql_dict['skill_id'] = ''
else:
sql_dict['skill_id'] = skill_id
if skill_id_uncap is not None:
sql += ', skill_id_uncap = :skill_id_uncap'
if skill_id_uncap == 'No_skill':
sql_dict['skill_id_uncap'] = ''
else:
sql_dict['skill_id_uncap'] = skill_id_uncap
sql += ' where character_id = :character_id'
c.execute(sql, sql_dict)
flash('角色修改成功 Successfully edit the character.')
else:
error = '角色不存在 The character does not exist.'
conn.commit()
conn.close()
if error:
flash(error)
return redirect(url_for('index.change_character'))
@bp.route('/changesong/updatechar', methods=['POST'])
@login_required
def update_character():
# 更新角色数据
conn = sqlite3.connect('./database/arcaea_database.db')
c = conn.cursor()
web.system.update_user_char(c)
conn.commit()
conn.close()
flash('数据更新成功 Success update data.')
return redirect(url_for('index.change_character'))
@bp.route('/changeuser', methods=['GET'])
@login_required
def change_user():
# 修改用户信息
return render_template('web/changeuser.html')
@bp.route('/changeuser/edituser', methods=['POST'])
@login_required
def edit_user():
# 修改用户数据
error = None
flag = True
name = None
user_code = None
try:
ticket = request.form['ticket']
if ticket:
ticket = int(ticket)
else:
ticket = None
except:
error = '数据错误 Wrong data.'
flash(error)
return redirect(url_for('index.change_user'))
conn = sqlite3.connect('./database/arcaea_database.db')
c = conn.cursor()
# 全修改
if 'name' not in request.form and 'user_code' not in request.form:
flag = False
if not ticket:
error = '无修改 No change.'
else:
sql = '''update user set ticket = :ticket'''
sql_dict = {'ticket': ticket}
c.execute(sql, sql_dict)
flash("全部用户信息修改成功 Successfully edit all the users' information.")
else:
name = request.form['name']
user_code = request.form['user_code']
# 指定修改
if name or user_code:
if user_code:
c.execute('''select user_id from user where user_code=:a''', {
'a': user_code})
else:
c.execute(
'''select user_id from user where name=:a''', {'a': name})
user_id = c.fetchone()
posts = []
if user_id:
user_id = user_id[0]
if not ticket:
error = '无修改 No change.'
else:
sql = '''update user set ticket = :ticket where user_id = :user_id'''
sql_dict = {'ticket': ticket, 'user_id': user_id}
c.execute(sql, sql_dict)
flash('用户信息修改成功 Successfully edit the user information.')
else:
error = '玩家不存在 The player does not exist.'
else:
if flag:
error = '输入为空 Null Input.'
conn.commit()
conn.close()
if error:
flash(error)
return redirect(url_for('index.change_user'))
@bp.route('/changeuserpurchase', methods=['GET'])
@login_required
def change_user_purchase():
# 修改用户购买
return render_template('web/changeuserpurchase.html')
@bp.route('/changeuserpurchase/edituser', methods=['POST'])
@login_required
def edit_user_purchase():
# 修改用户购买
error = None
flag = True
name = None
user_code = None
try:
method = request.form['method']
except:
flash('输入为空 Null Input.')
return redirect(url_for('index.change_user_purchase'))
conn = sqlite3.connect('./database/arcaea_database.db')
c = conn.cursor()
# 全修改
if 'name' not in request.form and 'user_code' not in request.form:
flag = False
if method == '0':
web.system.unlock_all_user_item(c)
else:
c.execute('''delete from user_item''')
flash("全部用户购买信息修改成功 Successfully edit all the users' purchase information.")
else:
name = request.form['name']
user_code = request.form['user_code']
# 指定修改
if name or user_code:
if user_code:
c.execute('''select user_id from user where user_code=:a''', {
'a': user_code})
else:
c.execute(
'''select user_id from user where name=:a''', {'a': name})
user_id = c.fetchone()
posts = []
if user_id:
user_id = user_id[0]
if method == '0':
web.system.unlock_user_item(c, user_id)
else:
c.execute('''delete from user_item where user_id=:a''', {
'a': user_id})
flash('用户购买信息修改成功 Successfully edit the user purchase information.')
else:
error = '玩家不存在 The player does not exist.'
else:
if flag:
error = '输入为空 Null Input.'
conn.commit()
conn.close()
if error:
flash(error)
return redirect(url_for('index.change_user_purchase'))
@bp.route('/allitem', methods=['GET'])
@login_required
def all_item():
# 所有购买数据
error = None
posts = web.system.get_all_item()
if not posts:
error = '没有购买数据 No item data.'
if error:
flash(error)
return redirect(url_for('index.all_item'))
else:
return render_template('web/allitem.html', posts=posts)
@bp.route('/changeitem', methods=['GET', 'POST'])
@login_required
def change_item():
# 修改购买信息
error = None
if request.method == 'POST':
try:
item_id = request.form['item_id']
item_type = request.form['type']
price = request.form['price']
orig_price = request.form['orig_price']
discount_from = request.form['discount_from']
discount_to = request.form['discount_to']
try:
is_available = request.form['is_available']
if is_available:
is_available = int(is_available)
else:
is_available = None
except:
is_available = None
if price:
price = int(price)
else:
price = None
if orig_price:
orig_price = int(orig_price)
else:
orig_price = None
if discount_from:
discount_from = int(time.mktime(time.strptime(discount_from, "%Y-%m-%dT%H:%M"))) * 1000
else:
discount_from = None
if discount_to:
discount_to = int(time.mktime(time.strptime(discount_to, "%Y-%m-%dT%H:%M"))) * 1000
else:
discount_to = None
except:
error = '数据错误 Wrong data.'
flash(error)
return redirect(url_for('index.change_item'))
conn = sqlite3.connect('./database/arcaea_database.db')
c = conn.cursor()
c.execute(
'''select exists(select * from item where item_id=:a and type=:b)''', {'a': item_id, 'b': item_type})
if c.fetchone() == (1,):
if is_available is None and price is None and orig_price is None and not discount_from and not discount_to:
error = '无修改 No change.'
else:
sql = '''update item set type=:type'''
sql_dict = {'item_id': item_id, 'type': item_type}
if price is not None:
sql += ', price = :price'
sql_dict['price'] = price
if orig_price is not None:
sql += ', orig_price = :orig_price'
sql_dict['orig_price'] = orig_price
if discount_from is not None:
sql += ', discount_from = :discount_from'
sql_dict['discount_from'] = discount_from
if discount_to is not None:
sql += ', discount_to = :discount_to'
sql_dict['discount_to'] = discount_to
if is_available is not None:
sql += ', is_available = :is_available'
sql_dict['is_available'] = is_available
sql += ' where item_id = :item_id and type = :type'
c.execute(sql, sql_dict)
flash('购买项目修改成功 Successfully edit the item.')
else:
error = '购买项目不存在 The item does not exist.'
conn.commit()
conn.close()
if error:
flash(error)
return render_template('web/changeitem.html')

View File

@@ -2,6 +2,7 @@
from flask import (Blueprint, flash, g, redirect, from flask import (Blueprint, flash, g, redirect,
render_template, request, session, url_for) render_template, request, session, url_for)
import functools import functools
import configparser
bp = Blueprint('login', __name__, url_prefix='/web') bp = Blueprint('login', __name__, url_prefix='/web')
@@ -14,12 +15,18 @@ def login():
password = request.form['password'] password = request.form['password']
error = None error = None
if username != 'admin' and password != 'admin': config = configparser.ConfigParser()
path = r'setting.ini'
config.read(path, encoding="utf-8")
USERNAME = config.get('WEB', 'USERNAME')
PASSWORD = config.get('WEB', 'PASSWORD')
if username != USERNAME and password != PASSWORD:
error = '错误的用户名或密码 Incorrect username or password.' error = '错误的用户名或密码 Incorrect username or password.'
if error is None: if error is None:
session.clear() session.clear()
session['user_id'] = 'admin' session['user_id'] = USERNAME + PASSWORD
return redirect(url_for('index.index')) return redirect(url_for('index.index'))
flash(error) flash(error)
@@ -40,11 +47,17 @@ def login_required(view):
@functools.wraps(view) @functools.wraps(view)
def wrapped_view(**kwargs): def wrapped_view(**kwargs):
x = session.get('user_id') x = session.get('user_id')
# 少用户存在验证
if x is None: config = configparser.ConfigParser()
path = r'setting.ini'
config.read(path, encoding="utf-8")
USERNAME = config.get('WEB', 'USERNAME')
PASSWORD = config.get('WEB', 'PASSWORD')
if x != USERNAME + PASSWORD:
return redirect(url_for('login.login')) return redirect(url_for('login.login'))
g.user = {'user_id': x, 'username': 'admin'} g.user = {'user_id': x, 'username': USERNAME}
return view(**kwargs) return view(**kwargs)
return wrapped_view return wrapped_view

View File

@@ -1,5 +1,126 @@
import os import os
import sqlite3 import sqlite3
import time
def int2b(x):
# int与布尔值转换
if x is None or x == 0:
return False
else:
return True
def get_table_info(c, table_name):
# 得到表结构,返回主键列表和字段名列表
pk = []
name = []
c.execute('''pragma table_info ("'''+table_name+'''")''')
x = c.fetchall()
if x:
for i in x:
name.append(i[1])
if i[5] != 0:
pk.append(i[1])
return pk, name
def get_sql_select_table(table_name, get_field, where_field=[], where_value=[]):
# sql语句拼接select ... from ... where ...
sql = 'select '
sql_dict = {}
if len(get_field) >= 2:
sql += get_field[0]
for i in range(1, len(get_field)):
sql += ',' + get_field[i]
sql += ' from ' + table_name
elif len(get_field) == 1:
sql += get_field[0] + ' from ' + table_name
else:
sql += '* from ' + table_name
if where_field and where_value:
sql += ' where '
sql += where_field[0] + '=:' + where_field[0]
sql_dict[where_field[0]] = where_value[0]
if len(where_field) >= 1:
for i in range(1, len(where_field)):
sql_dict[where_field[i]] = where_value[i]
sql += ' and ' + where_field[i] + '=:' + where_field[i]
sql += ' order by rowid'
return sql, sql_dict
def get_sql_insert_table(table_name, field, value):
# sql语句拼接insert into ...(...) values(...)
sql = 'insert into ' + table_name + '('
sql_dict = {}
sql2 = ''
if len(field) >= 2:
sql += field[0]
sql2 += ':' + field[0]
sql_dict[field[0]] = value[0]
for i in range(1, len(field)):
sql += ',' + field[i]
sql2 += ', :' + field[i]
sql_dict[field[i]] = value[i]
sql += ') values('
elif len(field) == 1:
sql += field[0] + ') values('
sql2 += ':' + field[0]
sql_dict[field[0]] = value[0]
else:
return 'error', {}
sql += sql2 + ')'
return sql, sql_dict
def update_one_table(c1, c2, table_name):
# 从c1向c2更新数据表c2中存在的信息不变
c1.execute(
'''select * from sqlite_master where type = 'table' and name = :a''', {'a': table_name})
c2.execute(
'''select * from sqlite_master where type = 'table' and name = :a''', {'a': table_name})
if not c1.fetchone() or not c2.fetchone():
return 'error'
db1_pk, db1_name = get_table_info(c1, table_name)
db2_pk, db2_name = get_table_info(c2, table_name)
if db1_pk != db2_pk:
return 'error'
field = []
for i in db1_name:
if i in db2_name:
field.append(i)
sql, sql_dict = get_sql_select_table(table_name, db1_pk)
c1.execute(sql)
x = c1.fetchall()
sql, sql_dict = get_sql_select_table(table_name, field)
c1.execute(sql)
y = c1.fetchall()
if x:
for i in range(0, len(x)):
sql, sql_dict = get_sql_select_table(
table_name, [], db1_pk, list(x[i]))
sql = 'select exists(' + sql + ')'
c2.execute(sql, sql_dict)
if c2.fetchone() == (0,):
sql, sql_dict = get_sql_insert_table(
table_name, field, list(y[i]))
c2.execute(sql, sql_dict)
return None
def update_user_char(c): def update_user_char(c):
@@ -19,7 +140,7 @@ def update_user_char(c):
def update_database(): def update_database():
# 将old数据库不存在数据加入到新数据库上并删除old数据库 # 将old数据库不存在数据加入到新数据库上并删除old数据库
# 对于arcaea_datebase.db更新best_scorefriendrecent30useruser_world并用character数据更新user_char # 对于arcaea_datebase.db更新best_scorefriendrecent30useruser_world, user_item并用character数据更新user_char
# 对于arcsong.db更新songs # 对于arcsong.db更新songs
if os.path.isfile("database/old_arcaea_database.db") and os.path.isfile("database/arcaea_database.db"): if os.path.isfile("database/old_arcaea_database.db") and os.path.isfile("database/arcaea_database.db"):
conn1 = sqlite3.connect('./database/old_arcaea_database.db') conn1 = sqlite3.connect('./database/old_arcaea_database.db')
@@ -27,62 +148,15 @@ def update_database():
conn2 = sqlite3.connect('./database/arcaea_database.db') conn2 = sqlite3.connect('./database/arcaea_database.db')
c2 = conn2.cursor() c2 = conn2.cursor()
# user update_one_table(c1, c2, 'user')
c1.execute('''select * from user''') update_one_table(c1, c2, 'friend')
x = c1.fetchall() update_one_table(c1, c2, 'best_score')
if x: update_one_table(c1, c2, 'recent30')
for i in x: update_one_table(c1, c2, 'user_world')
c2.execute( update_one_table(c1, c2, 'user_item')
'''select exists(select * from user where user_id=:a)''', {'a': i[0]})
if c2.fetchone() == (0,):
c2.execute('''insert into user values(:a0,:a1,:a2,:a3,:a4,:a5,:a6,:a7,:a8,:a9,:a10,:a11,:a12,:a13,:a14,:a15,:a16,:a17,:a18,:a19,:a20,:a21,:a22,:a23,:a24,:a25)''', {
'a0': i[0], 'a1': i[1], 'a2': i[2], 'a3': i[3], 'a4': i[4], 'a5': i[5], 'a6': i[6], 'a7': i[7], 'a8': i[8], 'a9': i[9], 'a10': i[10], 'a11': i[11], 'a12': i[12], 'a13': i[13], 'a14': i[14], 'a15': i[15], 'a16': i[16], 'a17': i[17], 'a18': i[18], 'a19': i[19], 'a20': i[20], 'a21': i[21], 'a22': i[22], 'a23': i[23], 'a24': i[24], 'a25': i[25]})
# friend
c1.execute('''select * from friend''')
x = c1.fetchall()
if x:
for i in x:
c2.execute(
'''select exists(select * from friend where user_id_me=:a and user_id_other=:b)''', {'a': i[0], 'b': i[1]})
if c2.fetchone() == (0,):
c2.execute('''insert into friend values(:a,:b)''', {
'a': i[0], 'b': i[1]})
# best_score
c1.execute('''select * from best_score''')
x = c1.fetchall()
if x:
for i in x:
c2.execute('''select exists(select * from best_score where user_id=:a and song_id=:b and difficulty=:c)''', {
'a': i[0], 'b': i[1], 'c': i[2]})
if c2.fetchone() == (0,):
c2.execute('''insert into best_score values(:a0,:a1,:a2,:a3,:a4,:a5,:a6,:a7,:a8,:a9,:a10,:a11,:a12,:a13)''', {
'a0': i[0], 'a1': i[1], 'a2': i[2], 'a3': i[3], 'a4': i[4], 'a5': i[5], 'a6': i[6], 'a7': i[7], 'a8': i[8], 'a9': i[9], 'a10': i[10], 'a11': i[11], 'a12': i[12], 'a13': i[13]})
# recent30
c1.execute('''select * from recent30''')
x = c1.fetchall()
if x:
for i in x:
c2.execute(
'''select exists(select * from recent30 where user_id=:a)''', {'a': i[0]})
if c2.fetchone() == (0,):
c2.execute('''insert into recent30 values(:a0,:a1,:a2,:a3,:a4,:a5,:a6,:a7,:a8,:a9,:a10,:a11,:a12,:a13,:a14,:a15,:a16,:a17,:a18,:a19,:a20,:a21,:a22,:a23,:a24,:a25,:a26,:a27,:a28,:a29,:a30,:a31,:a32,:a33,:a34,:a35,:a36,:a37,:a38,:a39,:a40,:a41,:a42,:a43,:a44,:a45,:a46,:a47,:a48,:a49,:a50,:a51,:a52,:a53,:a54,:a55,:a56,:a57,:a58,:a59,:a60)''', {'a0': i[0], 'a1': i[1], 'a2': i[2], 'a3': i[3], 'a4': i[4], 'a5': i[5], 'a6': i[6], 'a7': i[7], 'a8': i[8], 'a9': i[9], 'a10': i[10], 'a11': i[11], 'a12': i[12], 'a13': i[13], 'a14': i[14], 'a15': i[15], 'a16': i[16], 'a17': i[17], 'a18': i[
18], 'a19': i[19], 'a20': i[20], 'a21': i[21], 'a22': i[22], 'a23': i[23], 'a24': i[24], 'a25': i[25], 'a26': i[26], 'a27': i[27], 'a28': i[28], 'a29': i[29], 'a30': i[30], 'a31': i[31], 'a32': i[32], 'a33': i[33], 'a34': i[34], 'a35': i[35], 'a36': i[36], 'a37': i[37], 'a38': i[38], 'a39': i[39], 'a40': i[40], 'a41': i[41], 'a42': i[42], 'a43': i[43], 'a44': i[44], 'a45': i[45], 'a46': i[46], 'a47': i[47], 'a48': i[48], 'a49': i[49], 'a50': i[50], 'a51': i[51], 'a52': i[52], 'a53': i[53], 'a54': i[54], 'a55': i[55], 'a56': i[56], 'a57': i[57], 'a58': i[58], 'a59': i[59], 'a60': i[60]})
# user_world
c1.execute('''select * from user_world''')
x = c1.fetchall()
if x:
for i in x:
c2.execute(
'''select exists(select * from user_world where user_id=:a and map_id=:b)''', {'a': i[0], 'b': i[1]})
if c2.fetchone() == (0,):
c2.execute('''insert into user_world values(:a0,:a1,:a2,:a3,:a4)''', {
'a0': i[0], 'a1': i[1], 'a2': i[2], 'a3': i[3], 'a4': i[4]})
update_user_char(c2) update_user_char(c2)
conn1.commit() conn1.commit()
conn1.close() conn1.close()
conn2.commit() conn2.commit()
@@ -96,18 +170,76 @@ def update_database():
conn2 = sqlite3.connect('./database/arcsong.db') conn2 = sqlite3.connect('./database/arcsong.db')
c2 = conn2.cursor() c2 = conn2.cursor()
c1.execute('''select * from songs''') update_one_table(c1, c2, 'songs')
x = c1.fetchall()
if x:
for i in x:
c2.execute(
'''select exists(select * from songs where sid=:a)''', {'a': i[0]})
if c2.fetchone() == (0,):
c2.execute('''insert into songs values(:a0,:a1,:a2,:a3,:a4,:a5,:a6,:a7,:a8,:a9,:a10,:a11,:a12,:a13,:a14,:a15,:a16,:a17,:a18,:a19,:a20,:a21,:a22,:a23,:a24,:a25,:a26,:a27)''', {'a0': i[0], 'a1': i[1], 'a2': i[2], 'a3': i[3], 'a4': i[4], 'a5': i[5], 'a6': i[6], 'a7': i[7], 'a8': i[
8], 'a9': i[9], 'a10': i[10], 'a11': i[11], 'a12': i[12], 'a13': i[13], 'a14': i[14], 'a15': i[15], 'a16': i[16], 'a17': i[17], 'a18': i[18], 'a19': i[19], 'a20': i[20], 'a21': i[21], 'a22': i[22], 'a23': i[23], 'a24': i[24], 'a25': i[25], 'a26': i[26], 'a27': i[27]})
conn1.commit() conn1.commit()
conn1.close() conn1.close()
conn2.commit() conn2.commit()
conn2.close() conn2.close()
os.remove('database/old_arcsong.db') os.remove('database/old_arcsong.db')
def unlock_all_user_item(c):
# 解锁所有用户购买
c.execute('''select user_id from user''')
x = c.fetchall()
c.execute('''select item_id, type from item''')
y = c.fetchall()
c.execute('''delete from user_item''')
if x and y:
for i in x:
for j in y:
c.execute('''insert into user_item values(:a,:b,:c)''', {
'a': i[0], 'b': j[0], 'c': j[1]})
return
def unlock_user_item(c, user_id):
# 解锁用户购买
c.execute('''select item_id, type from item''')
y = c.fetchall()
for j in y:
c.execute('''select exists(select * from user_item where user_id=:a and item_id=:b and type=:c)''', {
'a': user_id, 'b': j[0], 'c': j[1]})
if c.fetchone() == (0,):
c.execute('''insert into user_item values(:a,:b,:c)''', {
'a': user_id, 'b': j[0], 'c': j[1]})
return
def get_all_item():
# 所有购买数据查询
conn = sqlite3.connect('./database/arcaea_database.db')
c = conn.cursor()
c.execute('''select * from item''')
x = c.fetchall()
re = []
if x:
for i in x:
discount_from = None
discount_to = None
if i[5] and i[5] >= 0:
discount_from = time.strftime(
"%Y-%m-%d %H:%M:%S", time.localtime(int(i[5])/1000))
if i[6] and i[6] >= 0:
discount_to = time.strftime(
"%Y-%m-%d %H:%M:%S", time.localtime(int(i[6])//1000))
re.append({'item_id': i[0],
'type': i[1],
'is_available': int2b(i[2]),
'price': i[3],
'orig_price': i[4],
'discount_from': discount_from,
'discount_to': discount_to
})
conn.commit()
conn.close()
return re

View File

@@ -64,7 +64,8 @@ def get_user(c, user_id):
'miss_count': x[17], 'miss_count': x[17],
'time_played': time_played, 'time_played': time_played,
'clear_type': x[21], 'clear_type': x[21],
'rating': x[22] 'rating': x[22],
'ticket': x[26]
} }
return r return r