diff --git a/README.md b/README.md index a37533f..ece4729 100644 --- a/README.md +++ b/README.md @@ -18,23 +18,24 @@ This is a small local Arcaea server based on Python and Flask, which can simulat - 爬梯 Climbing steps - 自定义世界模式 Customizable World Mode - 自定义歌曲下载 Customizable songs download +- 单曲和曲包购买(没啥用) Single songs and song packs purchase(useless) - 全角色立绘 All character drawings - 角色技能 Character skills - 自定义角色属性 Customizable characters attributes - 全剧情解锁 Unlock all the stories -- 后台查分 Background search scores -- 后台自定义歌曲定数 Customize chart consts in the background +- 后台查询 Background search +- 后台自定义信息 Customize some things in the background - 成绩校验 Score check +- 下载校验 Download check 没有以下 We don't have: - 角色数值 Character characteristic value -- 购买 Purchase -- 歌曲解锁、曲包解锁 Songs unlocking and music packs unlocking - 数据同步的时间和设备记录 The record of time and device for data synchronization - 服务器安全性保证 Server security assurance 可能有问题 There may be problems: - Recent 30 +- 一些歌曲的解锁 Some Songs' unlocking ## 说明 Statement 只是很有趣,用处探索中。 @@ -53,13 +54,16 @@ It is just so interesting. What it can do is under exploration. > > Tips: When updating, please keep the original database in case of data loss. -### Version 1.6 -- 适用于Arcaea 3.2.3版本 For Arcaea 3.2.3 +### Version 1.7 +- 仍然适用于Arcaea 3.2.3版本 Still for Arcaea 3.2.3 - 更新了歌曲数据库 Update the song database. -- 新增了自定义角色属性 Add customizable characters attributes. -- 用户的记忆源点可以修改了,但仍然没有实际用途 The users' memories can be modified, but still is of no practical use. -- 网页端安全性增强,用户名及密码可修改 The security of webpage is enhanced, and the username and password can be modified now. -- 改进了数据库更新方式,可以一定程度上应对数据表结构修改 The method of database updating is improved, which can deal with the modification of data table structure to some extent. +- 新增了购买系统,包括单曲购买和曲包购买 Add purchase system, including single purchase and pack purchase. +- 后台查询与修改项目增多 Increase background query and modification items. +- 新增用户下载量限制和下载校验 Add user download limit and download verification. +- 新增了**梦**的语音 Add the voice of **Yume**. +- 新增了网站图标 Add a favicon. +- 尝试解锁了所有的场景 Try to unlock all the scenes. +- 修复了一些Bug Fix some bugs. ## 运行环境与依赖 Running environment and requirements - Windows操作系统 Windows operating system @@ -81,7 +85,9 @@ It is just so interesting. What it can do is under exploration. ## 鸣谢 Thanks 歌曲数据库来自 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 me if necessary diff --git a/latest version/database/arcaea_database.db b/latest version/database/arcaea_database.db index 3b927d3..147e9b6 100644 Binary files a/latest version/database/arcaea_database.db and b/latest version/database/arcaea_database.db differ diff --git a/latest version/database/arcsong.db b/latest version/database/arcsong.db index e79e77d..f7725eb 100644 Binary files a/latest version/database/arcsong.db and b/latest version/database/arcsong.db differ diff --git a/latest version/database/database_initialize.py b/latest version/database/database_initialize.py index fc13913..12d852f 100644 --- a/latest version/database/database_initialize.py +++ b/latest version/database/database_initialize.py @@ -1,6 +1,7 @@ import sqlite3 import hashlib import time +import json # 数据库初始化文件,删掉arcaea_database.db文件后运行即可,谨慎使用 @@ -171,59 +172,116 @@ prog_boost_multiply int, 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)' -,'Tairitsu(Grievous Lady)','Stella','Hikari & Fisica','Ilith','Eto','Luna' -,'Shirabe','Hikari(Zero)','Hikari(Fracture)','Hikari(Summer)','Tairitsu(Summer)' -,'Tairitsu&Trin','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'] +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', + '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'] -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','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',''] +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', + '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', ''] -skill_id_uncap = ['','','frags_kou','','visual_ink','','','','','','','','','shirabe_entry_fee','','','','','','','','frags_yume','','','','','','','','','','','','','','','','',''] +skill_id_uncap = ['', '', 'frags_kou', '', 'visual_ink', '', '', '', '', '', '', '', '', 'shirabe_entry_fee', + '', '', '', '', '', '', '', 'frags_yume', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', ''] for i in range(0, 39): 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,"'''+skill_id[i]+'''",0,0,"'''+skill_id_uncap[i]+'''",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) else: if i != 5: - 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)''' + 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) +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.close() - -def arc_register(name: str, password: str): +def arc_register(name: str, password: str): def build_user_code(c): return '123456789' def build_user_id(c): return 2000000 -## 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): +# 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): # 为用户添加所有可用角色 c.execute('''select * from character''') @@ -233,7 +291,6 @@ def insert_user_char(c, user_id): 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]}) - conn = sqlite3.connect('arcaea_database.db') c = conn.cursor() hash_pwd = hashlib.sha256(password.encode("utf8")).hexdigest() @@ -243,13 +300,14 @@ def insert_user_char(c, user_id): user_code = build_user_code(c) user_id = build_user_id(c) now = int(time.time() * 1000) - c.execute('''insert into user(user_id, name, password, join_date, user_code, rating_ptt, + c.execute('''insert into user(user_id, name, password, join_date, user_code, rating_ptt, character_id, is_skill_sealed, is_char_uncapped, is_char_uncapped_override, is_hide_rating, favorite_character, max_stamina_notification_enabled, current_map, ticket) 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}) c.execute('''insert into recent30(user_id) values(: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) conn.commit() conn.close() diff --git a/latest version/database/map/byd_lumia.json b/latest version/database/map/byd_lumia.json new file mode 100644 index 0000000..3512439 --- /dev/null +++ b/latest version/database/map/byd_lumia.json @@ -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" + }] + }] +} \ No newline at end of file diff --git a/latest version/database/packs.json b/latest version/database/packs.json new file mode 100644 index 0000000..a586926 --- /dev/null +++ b/latest version/database/packs.json @@ -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 +}] \ No newline at end of file diff --git a/latest version/database/singles.json b/latest version/database/singles.json new file mode 100644 index 0000000..5527600 --- /dev/null +++ b/latest version/database/singles.json @@ -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 +}] \ No newline at end of file diff --git a/latest version/main.py b/latest version/main.py index 1cff63c..e245acb 100644 --- a/latest version/main.py +++ b/latest version/main.py @@ -9,13 +9,19 @@ import web.index import server.arcworld import server.arcdownload +import server.arcpurchase import os + app = Flask(__name__) wsgi_app = app.wsgi_app def error_return(error_code): # 错误返回 + # -7 处理交易时发生了错误 + # -5 所有的曲目都已经下载完毕 + # -4 您的账号已在别处登录 + # -3 无法连接至服务器 # 2 Arcaea服务器正在维护 # 5 请更新Arcaea到最新版本 # 100 无法在此ip地址下登录游戏 @@ -32,19 +38,27 @@ def error_return(error_code): # 错误返回 # 121 账户冻结 # 122 账户暂时冻结 # 123 账户被限制 + # 124 你今天不能再使用这个IP地址创建新的账号 # 150 非常抱歉您已被限制使用此功能 # 151 目前无法使用此功能 # 401 用户不存在 # 403 无法连接至服务器 - # 501 502 此物品目前无法获取 + # 501 502 -6 此物品目前无法获取 # 504 无效的序列码 # 505 此序列码已被使用 # 506 你已拥有了此物品 # 601 好友列表已满 # 602 此用户已是好友 # 604 你不能加自己为好友 + # 903 下载量超过了限制,请24小时后重试 + # 905 请在再次使用此功能前等待24小时 # 1001 设备数量达到上限 - # 1002 该设备已使用过本功能 + # 1002 此设备已使用过此功能 + # 9801 下载歌曲时发生问题,请再试一次 + # 9802 保存歌曲时发生问题,请检查设备空间容量 + # 9905 没有在云端发现任何数据 + # 9907 更新数据时发生了问题 + # 9908 服务器只支持最新的版本,请更新Arcaea # 其它 发生未知错误 return jsonify({ "success": False, @@ -57,6 +71,15 @@ def hello(): 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']) # 登录接口 def login(): headers = request.headers @@ -92,7 +115,8 @@ def register(): return error_return(108) -@app.route('/coffee/12/compose/aggregate', methods=['GET']) # 用户信息获取 +# 集成式请求,没想到什么好办法处理,就先这样写着 +@app.route('/coffee/12/compose/aggregate', methods=['GET']) def aggregate(): calls = request.args.get('calls') headers = request.headers @@ -432,12 +456,20 @@ def cloud_post(): return error_return(108) -@app.route('/coffee/12/purchase/me/redeem', methods=['POST']) # 兑换码,自然没有用 +@app.route('/coffee/12/purchase/me/redeem', methods=['POST']) # 兑换码,依然没有用 def redeem(): return error_return(504) -# 购买,自然没有用,只是为了world模式boost一下 +# 礼物确认 +@app.route('/coffee/12/present/me/claim/', methods=['POST']) +def claim_present(present_id): + return jsonify({ + "success": True + }) + + +# 购买,为了world模式boost一下 @app.route('/coffee/12/purchase/me/item', methods=['POST']) def item(): return jsonify({ @@ -445,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(): + 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({ "success": True }) -@app.route('/coffee/12/purchase/bundle/single', methods=['GET']) # 单曲购买,自然没有用 +@app.route('/coffee/12/purchase/bundle/single', methods=['GET']) # 单曲购买信息获取 def single(): return jsonify({ "success": True, - "value": [{ - "name": "testsingle", - "items": [{ - "id": "testsingle", - "type": "single", - "is_available": False - }], - "price": 100, - "orig_price": 100 - }] + "value": server.arcpurchase.get_single_purchase() }) @@ -475,6 +512,7 @@ def world_all(): token = headers['Authorization'] token = token[7:] try: + user_id = server.auth.token_get_id(token) if user_id: return jsonify({ @@ -539,21 +577,22 @@ def download_song(): token = headers['Authorization'] token = token[7:] song_ids = request.args.getlist('sid') - try: user_id = server.auth.token_get_id(token) if user_id: - re = {} - if not song_ids: - re = server.arcdownload.get_all_songs() - else: - for song_id in song_ids: - re.update(server.arcdownload.get_one_song(song_id)) + if server.arcdownload.is_able_download(user_id): + re = {} + if not song_ids: + re = server.arcdownload.get_all_songs(user_id) + else: + re = server.arcdownload.get_some_songs(user_id, song_ids) - return jsonify({ - "success": True, - "value": re - }) + return jsonify({ + "success": True, + "value": re + }) + else: + return error_return(903) else: return error_return(108) except: @@ -562,12 +601,17 @@ def download_song(): @app.route('/download/', methods=['GET']) # 下载 def download(file_path): + t = request.args.get('t') try: - path = os.path.join('./database/songs', file_path) - if os.path.isfile(path) and not('../' in path or '..\\' in path): - return send_from_directory('./database/songs', file_path, as_attachment=True) + message = server.arcdownload.is_token_able_download(t) + if message == 0: + path = os.path.join('./database/songs', file_path) + if os.path.isfile(path) and not('../' in path or '..\\' in path): + return send_from_directory('./database/songs', file_path, as_attachment=True) + else: + return error_return(109) else: - return error_return(109) + return error_return(message) except: return error_return(108) diff --git a/latest version/server/arcdownload.py b/latest version/server/arcdownload.py index 4ed2da7..3c7127c 100644 --- a/latest version/server/arcdownload.py +++ b/latest version/server/arcdownload.py @@ -1,6 +1,11 @@ import os import hashlib from flask import url_for +import sqlite3 +import time + +time_limit = 3000 # 每个玩家24小时下载次数限制 +time_gap_limit = 1000 # 下载链接有效秒数 def get_file_md5(file_path): @@ -18,31 +23,105 @@ def get_file_md5(file_path): 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)) 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: 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': 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: if 'chart' not in re: re['chart'] = {} - 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} -def get_all_songs(file_dir='./database/songs'): +def get_all_songs(user_id, file_dir='./database/songs'): # 获取所有歌的下载链接,返回字典 dir_list = os.listdir(file_dir) re = {} + conn = sqlite3.connect('./database/arcaea_database.db') + c = conn.cursor() for i in dir_list: 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 + +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 diff --git a/latest version/server/arcpurchase.py b/latest version/server/arcpurchase.py new file mode 100644 index 0000000..6580a9d --- /dev/null +++ b/latest version/server/arcpurchase.py @@ -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 + } diff --git a/latest version/server/arcscore.py b/latest version/server/arcscore.py index 8cf87fa..43f5e39 100644 --- a/latest version/server/arcscore.py +++ b/latest version/server/arcscore.py @@ -1390,7 +1390,18 @@ def arc_all_get(user_id): }, { "complete": 1, "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": { "": song_1 }, diff --git a/latest version/server/info.py b/latest version/server/info.py index 4fd5074..93b2aec 100644 --- a/latest version/server/info.py +++ b/latest version/server/info.py @@ -1,5 +1,7 @@ import sqlite3 import server.arcworld +import server.arcpurchase +import time def int2b(x): @@ -56,7 +58,7 @@ def get_user_character(c, user_id): y = c.fetchone() if y is not None: char_name = y[0] - s.append({ + char = { "is_uncapped_override": int2b(i[14]), "is_uncapped": int2b(i[13]), "uncap_cores": [], @@ -73,7 +75,10 @@ def get_user_character(c, user_id): "level": i[2], "name": char_name, "character_id": i[1] - }) + } + if i[1] == 21: + char["voice"] = [0, 1, 2, 3, 100, 1000, 1001] + s.append(char) return s else: @@ -115,6 +120,34 @@ def get_user_friend(c, user_id): 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): # 构造value id=0的数据,返回字典 c.execute('''select * from user where user_id = :x''', {'x': user_id}) @@ -148,10 +181,11 @@ def get_value_0(c, user_id): "next_fragstam_ts": -1, "max_stamina_ts": 1586274871917, "stamina": 12, - "world_unlocks": [], - "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"], - "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", "xanatos"], - "packs": ["vs", "extend", "dynamix", "prelude", "core", "yugamu", "omatsuri", "zettai", "mirai", "shiawase", "chunithm", "nijuusei", "groovecoaster", "rei", "tonesphere", "lanota"], + "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", "solitarydream", "lumia3"], + "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": get_user_packs(c, user_id), + # ["vs", "extend", "dynamix", "prelude", "core", "yugamu", "omatsuri", "zettai", "mirai", "shiawase", "chunithm", "nijuusei", "groovecoaster", "rei", "tonesphere", "lanota"] "characters": characters, "cores": [], "recent_score": get_recent_score(c, user_id), @@ -189,185 +223,7 @@ def arc_aggregate_big(user_id): "value": get_value_0(c, user_id) }, { "id": 1, - "value": [{ - "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 - }] + "value": server.arcpurchase.get_item(c, 'pack') }, { "id": 2, "value": {} @@ -377,7 +233,7 @@ def arc_aggregate_big(user_id): "max_stamina": 12, "stamina_recover_tick": 1800000, "core_exp": 250, - "curr_ts": 1599547606825, + "curr_ts": int(time.time())*1000, "level_steps": [{ "level": 1, "level_exp": 0 diff --git a/latest version/static/favicon.ico b/latest version/static/favicon.ico new file mode 100644 index 0000000..0120be6 Binary files /dev/null and b/latest version/static/favicon.ico differ diff --git a/latest version/templates/web/allitem.html b/latest version/templates/web/allitem.html new file mode 100644 index 0000000..06d74cf --- /dev/null +++ b/latest version/templates/web/allitem.html @@ -0,0 +1,51 @@ +{% extends 'base.html' %} +{% block header %} +

{% block title %}All items{% endblock %}

+{% endblock %} + +{% block content %} +{% if posts %}
+{% for item in posts %} + +
+ Id: + {{item['item_id']}} +
+ + Type: + {{item['type']}} +
+ + Is available: + {{item['is_available']}} +
+ + Price: + {{item['price']}} +
+ + Original price: + {{item['orig_price']}} +
+ + Discount from: + {{item['discount_from']}} +
+ + Discount to: + {{item['discount_to']}} +
+ + + + +
+{% if not loop.last %} +
+
+
+{% endif %} +{% endfor %} + +{% endif %} +{% endblock %} \ No newline at end of file diff --git a/latest version/templates/web/allplayer.html b/latest version/templates/web/allplayer.html index 61e4157..3a9fd94 100644 --- a/latest version/templates/web/allplayer.html +++ b/latest version/templates/web/allplayer.html @@ -13,7 +13,7 @@

{% block title %}All players{% endblock %}

User code: {{user['user_code']}}
注册于 Registered in: {{user['join_date']}}
-
PTT: {{user['rating_ptt']//100 ~ '.' ~ user['rating_ptt']%100}}
+
PTT: {{'%0.2f'|format(user['rating_ptt']/100|float)}}
Memories: {{user['ticket']}}
Recent plays:
diff --git a/latest version/templates/web/changeitem.html b/latest version/templates/web/changeitem.html new file mode 100644 index 0000000..c91d57a --- /dev/null +++ b/latest version/templates/web/changeitem.html @@ -0,0 +1,41 @@ +{% extends 'base.html' %} +{% block header %} +

{% block title %}Change the items{% endblock %}

+{% endblock %} + +{% block content %} +
+
Change the item
+ + + +
+ +
+
+
可修改项 Modifiable items:
+
+
+
+ +
+ +
+ + + + + + + + +
如果某些不需要修改,留空即可
+
If some things do not need to be modified, leave it blank.
+
时间填写是一个HTML5控件
+
Time filling is an HTML5 control.
+ +
+{% endblock %} \ No newline at end of file diff --git a/latest version/templates/web/changeuser.html b/latest version/templates/web/changeuser.html index ba2d56e..1b731e2 100644 --- a/latest version/templates/web/changeuser.html +++ b/latest version/templates/web/changeuser.html @@ -25,4 +25,13 @@

{% block title %}Change user information{% endblock %}

At present, only memories can be modified.
+
+
+
+
Edit all the users
+ + + + +
{% endblock %} \ No newline at end of file diff --git a/latest version/templates/web/changeuserpurchase.html b/latest version/templates/web/changeuserpurchase.html new file mode 100644 index 0000000..95c06e4 --- /dev/null +++ b/latest version/templates/web/changeuserpurchase.html @@ -0,0 +1,44 @@ +{% extends 'base.html' %} +{% block header %} +

{% block title %}Change user purchase information{% endblock %}

+{% endblock %} + +{% block content %} +
+
Edit the user
+ + + or
+ + +
+
+ +
对所有单曲和曲包的操作 Operation to all singles and packs: +
+
+ +
+ +
+ + +
+ +
+
+
+
+
Edit all the users
+
+
对所有单曲和曲包的操作 Operation to all singles and packs: +
+
+ +
+ +
+
+ +
+{% endblock %} \ No newline at end of file diff --git a/latest version/templates/web/index.html b/latest version/templates/web/index.html index 2aa8848..1f37a2f 100644 --- a/latest version/templates/web/index.html +++ b/latest version/templates/web/index.html @@ -15,13 +15,16 @@

游戏方面 Game

所有玩家信息查询 All players

铺面信息查询 All songs

角色信息查询 All characters

+购买信息查询 All items

单个铺面排行榜查询 Single song chart tops

系统方面 System

数据库更新 Update databases

歌曲修改 Change the songs

角色修改 Change the characters

-用户信息修改 Change user information +购买信息修改 Change the items

+用户信息修改 Change user information

+用户购买信息修改 Change user purchase information {% endblock %} \ No newline at end of file diff --git a/latest version/templates/web/singleplayerptt.html b/latest version/templates/web/singleplayerptt.html index 2b6d471..ddf9045 100644 --- a/latest version/templates/web/singleplayerptt.html +++ b/latest version/templates/web/singleplayerptt.html @@ -25,7 +25,7 @@

{% block title %}Single player ptt{% endblock %}

注册于 Registered in: {{user['join_date']}}
Memories: {{user['ticket']}}
-
PTT: {{user['rating_ptt']//100 ~ '.' ~ user['rating_ptt']%100}}
+
PTT: {{'%0.2f'|format(user['rating_ptt']/100|float)}}
Best 30 PTT: {{bestptt}}
Recent 10 PTT: {{recentptt}}
diff --git a/latest version/web/index.py b/latest version/web/index.py index 8cc2740..101c0a2 100644 --- a/latest version/web/index.py +++ b/latest version/web/index.py @@ -447,31 +447,31 @@ def edit_char(): c.execute( '''select exists(select * from character where character_id=:a)''', {'a': character_id}) if c.fetchone() == (1,): - if not level and not frag and not prog and not overdrive and not skill_id and not skill_id_uncap: + 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: + if level is not None: sql += ', level = :level' sql_dict['level'] = level - if frag: + if frag is not None: sql += ', frag = :frag' sql_dict['frag'] = frag - if prog: + if prog is not None: sql += ', prog = :prog' sql_dict['prog'] = prog - if overdrive: + if overdrive is not None: sql += ', overdrive = :overdrive' sql_dict['overdrive'] = overdrive - if skill_id: + 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: + 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'] = '' @@ -520,12 +520,42 @@ def edit_user(): # 修改用户数据 error = None - name = request.form['name'] - user_code = request.form['user_code'] + 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: - conn = sqlite3.connect('./database/arcaea_database.db') - c = conn.cursor() + if user_code: c.execute('''select user_id from user where user_code=:a''', { 'a': user_code}) @@ -537,16 +567,6 @@ def edit_user(): posts = [] if user_id: user_id = user_id[0] - 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')) if not ticket: error = '无修改 No change.' @@ -560,12 +580,190 @@ def edit_user(): error = '玩家不存在 The player does not exist.' else: - error = '输入为空 Null Input.' + 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') diff --git a/latest version/web/system.py b/latest version/web/system.py index 1b1e34c..d0b2355 100644 --- a/latest version/web/system.py +++ b/latest version/web/system.py @@ -1,5 +1,14 @@ import os 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): @@ -76,6 +85,13 @@ def get_sql_insert_table(table_name, field, value): 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: @@ -124,7 +140,7 @@ def update_user_char(c): def update_database(): # 将old数据库不存在数据加入到新数据库上,并删除old数据库 - # 对于arcaea_datebase.db,更新best_score,friend,recent30,user,user_world并用character数据更新user_char + # 对于arcaea_datebase.db,更新best_score,friend,recent30,user,user_world, user_item并用character数据更新user_char # 对于arcsong.db,更新songs 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') @@ -137,6 +153,7 @@ def update_database(): update_one_table(c1, c2, 'best_score') update_one_table(c1, c2, 'recent30') update_one_table(c1, c2, 'user_world') + update_one_table(c1, c2, 'user_item') update_user_char(c2) @@ -160,3 +177,69 @@ def update_database(): conn2.commit() conn2.close() 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