Archived
1
0
Fork 0

Merge branch 'Teahouse-Studios:master' into master

This commit is contained in:
多羅狼 2024-01-10 12:29:46 +08:00 committed by GitHub
commit eb3553f6a0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 228 additions and 9 deletions

View file

@ -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'),
)

View file

@ -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):

View 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

View 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

View file

@ -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"
}

View file

@ -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": "發生錯誤:無法隨機歌曲,請校對輸入。"
}

View file

@ -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)])

View file

@ -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)

View file

@ -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)

View file

@ -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": "未找到符合要求的谱面。",

View file

@ -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": "未找到符合要求的譜面。",

View file

@ -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'],