Merge branch 'Teahouse-Studios:master' into master
This commit is contained in:
commit
eb3553f6a0
12 changed files with 228 additions and 9 deletions
|
@ -17,7 +17,7 @@ from core.utils.cooldown import CoolDown
|
|||
|
||||
from .formatting import generate_latex, generate_code_snippet # noqa: E402
|
||||
|
||||
if not Config('openai_api_key'):
|
||||
if Config('openai_api_key'):
|
||||
client = AsyncOpenAI(
|
||||
api_key=Config('openai_api_key'),
|
||||
)
|
||||
|
|
|
@ -2,6 +2,7 @@ from core.builtins import Bot, Plain, Image as BImage
|
|||
from core.component import module
|
||||
from core.utils.image import msgchain2image
|
||||
from modules.chunithm.libraries.music import Music, TotalList
|
||||
from modules.chunithm.libraries.utils import generate_best30_text, SONGS_PER_PAGE
|
||||
|
||||
total_list = TotalList()
|
||||
|
||||
|
@ -142,6 +143,19 @@ async def _(msg: Bot.MessageSession, keyword: str):
|
|||
await msg.finish([BImage(img)])
|
||||
|
||||
|
||||
@chu.command('b30 [<username>] {{chunithm.help.b30}}')
|
||||
async def _(msg: Bot.MessageSession, username: str = None):
|
||||
if not username and msg.target.sender_from == "QQ":
|
||||
payload = {'qq': msg.session.sender}
|
||||
else:
|
||||
if not username:
|
||||
await msg.finish(msg.locale.t("chunithm.message.no_username"))
|
||||
payload = {'username': username}
|
||||
|
||||
img = await generate_best30_text(msg, payload)
|
||||
await msg.finish([BImage(img)])
|
||||
|
||||
|
||||
@chu.command('id <id> [<diff>] {{chunithm.help.id}}')
|
||||
@chu.command('song <song> [<diff>] {{chunithm.help.song}}')
|
||||
async def _(msg: Bot.MessageSession, song: str, diff: str = None):
|
||||
|
|
28
modules/chunithm/libraries/apidata.py
Normal file
28
modules/chunithm/libraries/apidata.py
Normal file
|
@ -0,0 +1,28 @@
|
|||
import traceback
|
||||
import ujson as json
|
||||
|
||||
from core.utils.http import post_url
|
||||
|
||||
|
||||
async def get_record(msg, payload):
|
||||
url = f"https://www.diving-fish.com/api/chunithmprober/query/player"
|
||||
try:
|
||||
data = await post_url(url,
|
||||
data=json.dumps(payload),
|
||||
status_code=200,
|
||||
headers={'Content-Type': 'application/json', 'accept': '*/*'}, fmt='json')
|
||||
except ValueError as e:
|
||||
if str(e).startswith('400'):
|
||||
if "qq" in payload:
|
||||
await msg.finish(msg.locale.t("chunithm.message.user_unbound"))
|
||||
else:
|
||||
await msg.finish(msg.locale.t("chunithm.message.user_not_found"))
|
||||
elif str(e).startswith('403'):
|
||||
if "qq" in payload:
|
||||
await msg.finish(msg.locale.t("chunithm.message.forbidden.eula"))
|
||||
else:
|
||||
await msg.finish(msg.locale.t("chunithm.message.forbidden"))
|
||||
else:
|
||||
traceback.print_exc()
|
||||
|
||||
return data
|
87
modules/chunithm/libraries/utils.py
Normal file
87
modules/chunithm/libraries/utils.py
Normal file
|
@ -0,0 +1,87 @@
|
|||
import ujson as json
|
||||
from datetime import datetime
|
||||
|
||||
from core.builtins import Plain
|
||||
from core.utils.http import get_url
|
||||
from core.utils.image import msgchain2image
|
||||
from .apidata import get_record
|
||||
|
||||
SONGS_PER_PAGE = 20
|
||||
|
||||
score_to_rank = {
|
||||
(0, 499999): "D",
|
||||
(500000, 599999): "C",
|
||||
(600000, 699999): "B",
|
||||
(700000, 799999): "BB",
|
||||
(800000, 899999): "BBB",
|
||||
(900000, 924999): "A",
|
||||
(925000, 949999): "AA",
|
||||
(950000, 974999): "AAA",
|
||||
(975000, 989999): "S",
|
||||
(990000, 999999): "S+",
|
||||
(1000000, 1004999): "SS",
|
||||
(1005000, 1007499): "SS+",
|
||||
(1007500, 1008999): "SSS",
|
||||
(1009000, 1010000): "SSS+",
|
||||
}
|
||||
|
||||
combo_conversion = {
|
||||
"fullcombo": "FC",
|
||||
"alljustice": "AJ"
|
||||
}
|
||||
|
||||
async def generate_best30_text(msg, payload):
|
||||
data = await get_record(msg, payload)
|
||||
b30_records = data["records"]["b30"]
|
||||
r10_records = data["records"]["r10"]
|
||||
|
||||
html = "<style>pre { font-size: 15px; }</style><div style='margin-left: 30px; margin-right: 20px;'>\n"
|
||||
html += f"{msg.locale.t('chunithm.message.b30.text_prompt', user=data['username'], rating=round(data['rating'], 2))}\n<pre>"
|
||||
html += "Best30\n"
|
||||
for idx, chart in enumerate(b30_records, start=1):
|
||||
level = ''.join(filter(str.isalpha, chart["level_label"]))[:3].upper()
|
||||
rank = next(
|
||||
rank for interval, rank in score_to_rank.items() if interval[0] <= chart["score"] < interval[1] # 根据成绩获得等级
|
||||
)
|
||||
title = chart["title"]
|
||||
title = title[:17] + '...' if len(title) > 20 else title
|
||||
line = "#{:<2} {:>4} {:<3} {:>7} {:<4} {:<2} {:>4}->{:<5.2f} {:<20}\n".format(
|
||||
idx,
|
||||
chart["mid"],
|
||||
level,
|
||||
chart["score"],
|
||||
rank,
|
||||
combo_conversion.get(chart["fc"], ""),
|
||||
chart["ds"],
|
||||
chart["ra"],
|
||||
title
|
||||
)
|
||||
html += line
|
||||
html += "Recent10\n"
|
||||
for idx, chart in enumerate(r10_records, start=1):
|
||||
level = ''.join(filter(str.isalpha, chart["level_label"]))[:3].upper()
|
||||
rank = next(
|
||||
rank for interval, rank in score_to_rank.items() if interval[0] <= chart["score"] < interval[1] # 根据成绩获得等级
|
||||
)
|
||||
title = chart["title"]
|
||||
title = title[:17] + '...' if len(title) > 20 else title
|
||||
line = "#{:<2} {:>4} {:<3} {:>7} {:<4} {:<2} {:>4}->{:<5.2f} {:<20}\n".format(
|
||||
idx,
|
||||
chart["mid"],
|
||||
level,
|
||||
chart["score"],
|
||||
rank,
|
||||
combo_conversion.get(chart["fc"], ""),
|
||||
chart["ds"],
|
||||
chart["ra"],
|
||||
title
|
||||
)
|
||||
html += line
|
||||
html += "</pre>"
|
||||
time = msg.ts2strftime(datetime.now().timestamp(), iso=True, timezone=False)
|
||||
html += f"<p style='font-size: 10px; text-align: right;'>CHUNITHM Best30 Generator Beta\n{time}·Generated by Teahouse Studios \"Akaribot\"</p>"
|
||||
html += "</div>"
|
||||
|
||||
img = await msgchain2image([Plain(html)])
|
||||
return img
|
||||
|
|
@ -1,22 +1,29 @@
|
|||
{
|
||||
"chunithm.help.b50": "查询 CHUNITHM 用户的 B30 列表。(Beta 版)",
|
||||
"chunithm.help.base": "根据定数或定数范围搜索歌曲。",
|
||||
"chunithm.help.desc": "查询 CHUNITHM 相关内容。(Beta 版)",
|
||||
"chunithm.help.desc": "查询 CHUNITHM 相关内容。",
|
||||
"chunithm.help.id": "根据 ID 查询歌曲或谱面信息。",
|
||||
"chunithm.help.level": "根据等级搜索歌曲。",
|
||||
"chunithm.help.random": "随机一首指定条件的歌曲,留空则完全随机。",
|
||||
"chunithm.help.search": "根据歌名(或一部分)搜索歌曲。",
|
||||
"chunithm.help.song": "查询 CHUNITHM 歌曲或谱面信息,ID 需要加入前缀“id”,空格用“_”替代。",
|
||||
"chunithm.message.b30.text_prompt": "以下为 ${user}(Ra ${rating})的 Best30 列表:",
|
||||
"chunithm.message.base": "以下为定数 ${constant} 的曲目列表:",
|
||||
"chunithm.message.base.range": "以下为定数 ${constant}-${constant_max} 的曲目列表:",
|
||||
"chunithm.message.chart_not_found": "未找到符合要求的谱面。",
|
||||
"chunithm.message.error.non_digital": "发生错误:歌曲 ID 必须为数字。",
|
||||
"chunithm.message.forbidden": "此用户禁止了其他人获取数据。",
|
||||
"chunithm.message.forbidden.eula": "无法获取数据,请前往查分器并同意用户协议。\n查分器地址:https://www.diving-fish.com/maimaidx/prober",
|
||||
"chunithm.message.level": "以下为 ${level} 级的曲目列表:",
|
||||
"chunithm.message.level_invalid": "无效的等级,请检查输入。",
|
||||
"chunithm.message.music_not_found": "未找到符合要求的歌曲。",
|
||||
"chunithm.message.no_username": "请提供用户名!",
|
||||
"chunithm.message.pages": "(第 ${page} 页,共 ${total_pages} 页)",
|
||||
"chunithm.message.random.error": "发生错误:随机歌曲失败,请检查输入。",
|
||||
"chunithm.message.search": "“${keyword}”的搜索结果:",
|
||||
"chunithm.message.song": "艺术家:${artist}\n分类:${genre}\nBPM:${bpm}\n版本:${version}\n难度:${level}",
|
||||
"chunithm.message.song.diff": "${diff} ${level} (${ds})\n连击:${combo}\n谱师:${charter}",
|
||||
"chunithm.message.too_much": "结果过多(${length} 条),请缩小搜索范围。"
|
||||
"chunithm.message.too_much": "结果过多(${length} 条),请缩小搜索范围。",
|
||||
"chunithm.message.user_not_found": "未找到此玩家,请确保此玩家的用户名和查分器中的用户名相同。",
|
||||
"chunithm.message.user_unbound": "未绑定用户,请前往查分器并绑定你的 QQ 号。\n查分器地址:https://www.diving-fish.com/maimaidx/prober"
|
||||
}
|
|
@ -1,22 +1,29 @@
|
|||
{
|
||||
"chunithm.help.b30": "查詢 CHUNITHM 使用者的 B30 列表。(Beta 版)",
|
||||
"chunithm.help.base": "依據定數或定數範圍內搜尋歌曲。",
|
||||
"chunithm.help.desc": "查詢 CHUNITHM 相關的內容。(Beta 版)",
|
||||
"chunithm.help.desc": "查詢 CHUNITHM 相關的內容。",
|
||||
"chunithm.help.id": "依據 ID 查詢歌曲或譜面資訊。",
|
||||
"chunithm.help.level": "依據等級搜尋歌曲。",
|
||||
"chunithm.help.random": "隨機一首指定條件的歌曲,空白則完全隨機。",
|
||||
"chunithm.help.search": "依據歌名(或一部分)搜尋歌曲。",
|
||||
"chunithm.help.song": "查詢 CHUNITHM 歌曲或譜面資訊,ID 需要加入前綴「id」,空白用「_」取代。",
|
||||
"chunithm.message.b30.text_prompt": "以下為 ${user}(Ra ${rating})的 Best30 列表:",
|
||||
"chunithm.message.base": "以下為定數 ${constant} 的曲目列表:",
|
||||
"chunithm.message.base.range": "以下為定數 ${constant}-${constant_max} 的曲目列表:",
|
||||
"chunithm.message.chart_not_found": "未找到符合要求的譜面。",
|
||||
"chunithm.message.error.non_digital": "發生錯誤:歌曲 ID 必須為數字。",
|
||||
"chunithm.message.forbidden": "此使用者禁止了其他人取得資訊。",
|
||||
"chunithm.message.forbidden.eula": "無法取得資訊,請前往查分器並同意使用者協議。\n查分器網址:https://www.diving-fish.com/maimaidx/prober",
|
||||
"chunithm.message.level": "以下為 ${level} 級的曲目列表:",
|
||||
"chunithm.message.level_invalid": "無效的等級,請校對輸入。",
|
||||
"chunithm.message.music_not_found": "未找到符合要求的歌曲。",
|
||||
"chunithm.message.no_username": "請提供使用者名稱!",
|
||||
"chunithm.message.pages": "(第 ${page} 頁,共 ${total_pages} 頁)",
|
||||
"chunithm.message.search": "「${keyword}」的搜尋結果:",
|
||||
"chunithm.message.song": "藝術家:${artist}\n分類:${genre}\nBPM:${bpm}\n版本:${version}\n難度:${level}",
|
||||
"chunithm.message.song.diff": "${diff} ${level} (${ds})\n連擊:${combo}\n譜師:${charter}",
|
||||
"chunithm.message.too_much": "結果過多(${length} 條),請縮小搜尋範圍。",
|
||||
"chunithm.message.user_not_found": "未找到此使用者,請確認此使用者的名稱和查分器中的名稱相同。",
|
||||
"chunithm.message.user_unbound": "未綁定使用者,請前往查分器並綁定你的 QQ 號。\n查分器網址:https://www.diving-fish.com/maimaidx/prober",
|
||||
"chunitm.message.random.error": "發生錯誤:無法隨機歌曲,請校對輸入。"
|
||||
}
|
|
@ -8,7 +8,7 @@ from core.utils.image import msgchain2image
|
|||
from modules.maimai.libraries.best50 import computeRa, generate
|
||||
from modules.maimai.libraries.apidata import get_alias, get_info, search_by_alias, update_alias, update_covers
|
||||
from modules.maimai.libraries.music import get_cover_len5_id, TotalList
|
||||
from modules.maimai.libraries.utils import get_grade_info, get_level_process, \
|
||||
from modules.maimai.libraries.utils import generate_best50_text, get_grade_info, get_level_process, \
|
||||
get_plate_process, get_player_score, get_rank, get_score_list, SONGS_PER_PAGE
|
||||
from .regex import *
|
||||
|
||||
|
@ -175,15 +175,25 @@ async def _(msg: Bot.MessageSession, grade: str):
|
|||
await get_grade_info(msg, grade)
|
||||
|
||||
|
||||
@mai.command('b50 [<username>] {{maimai.help.b50}}')
|
||||
@mai.command(['b50 [<username>] {{maimai.help.b50}}',
|
||||
'b50 beta [<username>] {{maimai.help.b50.beta}}'])
|
||||
async def _(msg: Bot.MessageSession, username: str = None):
|
||||
beta = True
|
||||
if not username and msg.target.sender_from == "QQ":
|
||||
payload = {'qq': msg.session.sender, 'b50': True}
|
||||
else:
|
||||
if not username:
|
||||
await msg.finish(msg.locale.t("maimai.message.no_username"))
|
||||
payload = {'username': username, 'b50': True}
|
||||
img = await generate(msg, payload)
|
||||
|
||||
if not msg.parsed_msg.get('beta', False):
|
||||
try:
|
||||
img = await generate(msg, payload)
|
||||
beta = False
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
if beta:
|
||||
img = await generate_best50_text(msg, payload)
|
||||
await msg.finish([BImage(img)])
|
||||
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import math
|
||||
import os
|
||||
import traceback
|
||||
from typing import Optional, Dict, List, Tuple
|
||||
import ujson as json
|
||||
from PIL import Image, ImageDraw, ImageFont, ImageFilter
|
||||
|
@ -432,7 +433,7 @@ async def generate(msg, payload) -> Tuple[Optional[Image.Image], bool]:
|
|||
else:
|
||||
await msg.finish(msg.locale.t("maimai.message.forbidden"))
|
||||
else:
|
||||
raise
|
||||
traceback.print_exc()
|
||||
|
||||
sd_best = BestList(35)
|
||||
dx_best = BestList(15)
|
||||
|
|
|
@ -2,7 +2,9 @@ import os
|
|||
import ujson as json
|
||||
from datetime import datetime
|
||||
|
||||
from core.builtins import Plain
|
||||
from core.utils.http import get_url
|
||||
from core.utils.image import msgchain2image
|
||||
from core.utils.cache import random_cache_path
|
||||
from .apidata import get_record, get_plate
|
||||
from .music import TotalList
|
||||
|
@ -142,6 +144,65 @@ def key_process(input_key, conv_dict):
|
|||
else:
|
||||
return None, input_key
|
||||
|
||||
|
||||
async def generate_best50_text(msg, payload):
|
||||
data = await get_record(msg, payload)
|
||||
dx_charts = data["charts"]["dx"]
|
||||
sd_charts = data["charts"]["sd"]
|
||||
|
||||
html = "<style>pre { font-size: 15px; }</style><div style='margin-left: 30px; margin-right: 20px;'>\n"
|
||||
html += f"{msg.locale.t('maimai.message.b50.text_prompt', user=data['username'], rating=data['rating'])}\n<pre>"
|
||||
html += f"Standard ({sum(chart['ra'] for chart in sd_charts)})\n"
|
||||
for idx, chart in enumerate(sd_charts, start=1):
|
||||
level = ''.join(filter(str.isalpha, chart["level_label"]))[:3].upper()
|
||||
rank = next(
|
||||
rank for interval, rank in score_to_rank.items() if interval[0] <= chart["achievements"] < interval[1] # 根据成绩获得等级
|
||||
)
|
||||
title = chart["title"]
|
||||
title = title[:17] + '...' if len(title) > 20 else title
|
||||
line = "#{:<2} {:>5} {:<3} {:>8.4f}% {:<4} {:<3} {:<4} {:>4}->{:<3} {:<20}\n".format(
|
||||
idx,
|
||||
chart["song_id"],
|
||||
level,
|
||||
chart["achievements"],
|
||||
rank,
|
||||
combo_conversion.get(chart["fc"], ""),
|
||||
sync_conversion.get(chart["fs"], ""),
|
||||
chart["ds"],
|
||||
chart["ra"],
|
||||
title
|
||||
)
|
||||
html += line
|
||||
html += f"New ({sum(chart['ra'] for chart in dx_charts)})\n"
|
||||
for idx, chart in enumerate(dx_charts, start=1):
|
||||
level = ''.join(filter(str.isalpha, chart["level_label"]))[:3].upper()
|
||||
rank = next(
|
||||
rank for interval, rank in score_to_rank.items() if interval[0] <= chart["achievements"] < interval[1] # 根据成绩获得等级
|
||||
)
|
||||
title = chart["title"]
|
||||
title = title[:17] + '...' if len(title) > 20 else title
|
||||
line = "#{:<2} {:>5} {:<3} {:>8.4f}% {:<4} {:<3} {:<4} {:>4}->{:<3} {:<20}\n".format(
|
||||
idx,
|
||||
chart["song_id"],
|
||||
level,
|
||||
chart["achievements"],
|
||||
rank,
|
||||
combo_conversion.get(chart["fc"], ""),
|
||||
sync_conversion.get(chart["fs"], ""),
|
||||
chart["ds"],
|
||||
chart["ra"],
|
||||
title
|
||||
)
|
||||
html += line
|
||||
html += "</pre>"
|
||||
time = msg.ts2strftime(datetime.now().timestamp(), iso=True, timezone=False)
|
||||
html += f"<p style='font-size: 10px; text-align: right;'>Maimai Best50 Generator Beta\n{time}·Generated by Teahouse Studios \"Akaribot\"</p>"
|
||||
html += "</div>"
|
||||
|
||||
img = await msgchain2image([Plain(html)])
|
||||
return img
|
||||
|
||||
|
||||
async def get_rank(msg, payload):
|
||||
time = msg.ts2strftime(datetime.now().timestamp(), timezone=False)
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
{
|
||||
"maimai.help.alias": "查询歌曲的别名。",
|
||||
"maimai.help.b50": "查询 Maimai 用户的 B50 列表。",
|
||||
"maimai.help.b50.beta": "查询 Maimai 用户的 B50 列表。(Beta 版) ",
|
||||
"maimai.help.base": "根据定数或定数范围搜索歌曲。",
|
||||
"maimai.help.desc": "查询 Maimai 相关内容。",
|
||||
"maimai.help.grade": "查询段位认定列表。",
|
||||
|
@ -28,6 +29,7 @@
|
|||
"maimai.message.alias": "${title} 的别名:",
|
||||
"maimai.message.alias.alias_not_found": "未找到符合要求的别名。",
|
||||
"maimai.message.alias.file_not_found": "未找到别名文件,请使用“${prefix}maimai update”初始化文件。",
|
||||
"maimai.message.b50.text_prompt": "以下为 ${user}(Ra ${rating})的 Best50 列表:",
|
||||
"maimai.message.base": "以下为定数 ${constant} 的曲目列表:",
|
||||
"maimai.message.base.range": "以下为定数 ${constant}-${constant_max} 的曲目列表:",
|
||||
"maimai.message.chart_not_found": "未找到符合要求的谱面。",
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
{
|
||||
"maimai.help.alias": "查詢歌曲的別名。",
|
||||
"maimai.help.b50": "查詢 Maimai 使用者的 B50 列表。",
|
||||
"maimai.help.b50.beta": "查詢 Maimai 使用者的 B50 列表。(Beta 版)",
|
||||
"maimai.help.base": "依據定數或定數範圍內搜尋歌曲。",
|
||||
"maimai.help.desc": "查詢 Maimai 相關的內容。",
|
||||
"maimai.help.grade": "查詢段位認定列表。",
|
||||
|
@ -28,6 +29,7 @@
|
|||
"maimai.message.alias": "${title} 的別名:",
|
||||
"maimai.message.alias.alias_not_found": "未找到符合要求的別名。",
|
||||
"maimai.message.alias.file_not_found": "未找到別名檔案,請使用「${prefix}maimai update」初始化檔案。",
|
||||
"maimai.message.b50.text_prompt": "以下為 ${user}(Ra ${rating})的 Best50 列表:",
|
||||
"maimai.message.base": "以下為定數 ${constant} 的曲目列表:",
|
||||
"maimai.message.base.range": "以下為定數 ${constant}-${constant_max} 的曲目列表:",
|
||||
"maimai.message.chart_not_found": "未找到符合要求的譜面。",
|
||||
|
|
|
@ -12,7 +12,7 @@ from core.utils.cooldown import CoolDown
|
|||
|
||||
client = AsyncOpenAI(
|
||||
api_key=Config('openai_api_key'),
|
||||
)
|
||||
) if Config('openai_api_key') else None
|
||||
|
||||
s = module('summary',
|
||||
developers=['Dianliang233', 'OasisAkari'],
|
||||
|
|
Reference in a new issue