Archived
1
0
Fork 0
This repository has been archived on 2024-04-26. You can view files and clone it, but cannot push or open issues or pull requests.
akari-bot/modules/cytoid/rating.py

316 lines
12 KiB
Python
Raw Normal View History

2022-06-20 12:14:32 +00:00
import asyncio
2021-02-12 16:44:53 +00:00
import os
import time
import traceback
import uuid
from datetime import datetime, timedelta
from os.path import abspath
import aiohttp
2021-09-10 18:05:27 +00:00
import ujson as json
2022-07-31 08:27:58 +00:00
from PIL import Image, ImageEnhance, ImageFont, ImageDraw, ImageOps
2022-06-19 12:42:32 +00:00
from aiofile import async_open
2021-02-12 16:44:53 +00:00
from gql import Client, gql
from gql.transport.aiohttp import AIOHTTPTransport
2022-07-31 08:27:58 +00:00
from config import Config
2022-06-20 13:35:56 +00:00
from core.logger import Logger
2021-02-12 16:44:53 +00:00
from core.utils import get_url
async def get_rating(uid, query_type):
try:
if query_type == 'b30':
query_type = 'bestRecords'
elif query_type == 'r30':
query_type = 'recentRecords'
Profile_url = 'http://services.cytoid.io/profile/' + uid
2022-08-01 15:09:59 +00:00
Profile_json = json.loads(await get_url(Profile_url, 200))
2021-02-12 16:44:53 +00:00
if 'statusCode' in Profile_json:
if Profile_json['statusCode'] == 404:
return {'status': False, 'text': '发生错误:此用户不存在。'}
2021-02-12 16:44:53 +00:00
ProfileId = Profile_json['user']['id']
ProfileRating = Profile_json['rating']
ProfileLevel = Profile_json['exp']['currentLevel']
ProfileUid = Profile_json['user']['uid']
nick = Profile_json['user']['name']
if nick is None:
nick = ProfileUid
if 'avatar' in Profile_json['user']:
Avatar_img = Profile_json['user']['avatar']['medium']
else:
Avatar_img = None
transport = AIOHTTPTransport(url='https://services.cytoid.io/graphql')
client = Client(transport=transport, fetch_schema_from_transport=True)
query = gql(
f"""
query StudioAnalytics($id: ID = "{ProfileId}") {{
profile(id: $id) {{
id
{query_type}(limit: 30) {{
...RecordFragment
}}
}}
}}
fragment RecordFragment on UserRecord {{
id
date
chart {{
id
difficulty
type
level {{
uid
title
}}
}}
2021-02-12 17:18:39 +00:00
score
2021-02-12 16:44:53 +00:00
accuracy
rating
2022-08-07 12:41:27 +00:00
details {{
perfect
great
good
bad
miss
}}
2021-02-12 16:44:53 +00:00
}}
""")
result = await client.execute_async(query)
2022-06-20 13:35:56 +00:00
workdir = os.path.abspath(Config("cache_path") + str(uuid.uuid4()))
2021-02-12 16:44:53 +00:00
os.mkdir(workdir)
bestRecords = result['profile'][query_type]
rank = 0
2022-06-20 13:35:56 +00:00
resources = []
2022-06-20 12:14:32 +00:00
songcards = []
2022-06-20 13:35:56 +00:00
async def mkresources(x, rank):
2021-02-12 16:44:53 +00:00
thumbpath = await download_cover_thumb(x['chart']['level']['uid'])
chart_type = x['chart']['type']
difficulty = x['chart']['difficulty']
chart_name = x['chart']['level']['title']
2021-02-12 17:18:39 +00:00
score = str(x['score'])
2021-02-12 16:44:53 +00:00
acc = x['accuracy']
rt = x['rating']
2022-08-07 12:41:27 +00:00
details = x['details']
2021-02-12 16:44:53 +00:00
_date = datetime.strptime(x['date'], "%Y-%m-%dT%H:%M:%S.%fZ")
local_time = _date + timedelta(hours=8)
playtime = local_time.timestamp()
nowtime = time.time()
playtime = playtime - nowtime
playtime = - playtime
t = playtime / 60 / 60 / 24
dw = 'd'
if t < 1:
t = playtime / 60 / 60
dw = 'h'
if t < 1:
t = playtime / 60
dw = 'm'
if t < 1:
t = playtime
dw = 's'
playtime = str(int(t)) + dw
if thumbpath:
havecover = True
else:
havecover = False
2022-07-31 08:27:58 +00:00
songcards.append(
2022-08-10 11:27:08 +00:00
make_songcard(thumbpath, chart_type, difficulty, chart_name, score, acc, rt, playtime, rank, details,
2022-07-31 08:27:58 +00:00
havecover))
2022-06-20 13:35:56 +00:00
for x in bestRecords:
rank += 1
resources.append(mkresources(x, rank))
await asyncio.gather(*resources)
2022-08-10 11:27:08 +00:00
cards_ = await asyncio.gather(*songcards)
cards_d = {}
for x in cards_:
for k in x:
cards_d[k] = x[k]
cards = [cards_d[x] for x in cards_d]
2022-06-20 13:35:56 +00:00
2021-02-12 16:44:53 +00:00
# b30card
2023-01-08 09:31:30 +00:00
b30img = Image.new("RGBA", (1955, 1600), '#1e2129')
2021-02-12 16:44:53 +00:00
avatar_path = await download_avatar_thumb(Avatar_img, ProfileId)
if avatar_path:
im = Image.open(avatar_path)
im = im.resize((110, 110))
2021-02-12 18:22:24 +00:00
try:
bigsize = (im.size[0] * 3, im.size[1] * 3)
mask = Image.new('L', bigsize, 0)
draw = ImageDraw.Draw(mask)
draw.ellipse((0, 0) + bigsize, fill=255)
mask = mask.resize(im.size, Image.ANTIALIAS)
im.putalpha(mask)
output = ImageOps.fit(im, mask.size, centering=(0.5, 0.5))
output.putalpha(mask)
output.convert('RGBA')
b30img.alpha_composite(output, (1825, 22))
2021-02-12 18:22:24 +00:00
except:
traceback.print_exc()
2021-02-12 16:44:53 +00:00
font4 = ImageFont.truetype(os.path.abspath('./assets/Nunito-Regular.ttf'), 35)
drawtext = ImageDraw.Draw(b30img)
get_name_width = font4.getsize(nick)[0]
get_img_width = b30img.width
2023-01-08 09:31:30 +00:00
drawtext.text((get_img_width - get_name_width - 150, 30), nick, '#ffffff', font=font4)
2021-02-12 16:44:53 +00:00
font5 = ImageFont.truetype(os.path.abspath('./assets/Noto Sans CJK DemiLight.otf'), 20)
level_text = f'等级 {ProfileLevel}'
level_text_width = font5.getsize(level_text)[0]
level_text_height = font5.getsize(level_text)[1]
img_level = Image.new("RGBA", (level_text_width + 20, 40), '#050a1a')
drawtext_level = ImageDraw.Draw(img_level)
drawtext_level.text(((img_level.width - level_text_width) / 2, (img_level.height - level_text_height) / 2),
level_text, '#ffffff', font=font5)
b30img.alpha_composite(img_level, (1825 - img_level.width - 20, 85))
font6 = ImageFont.truetype(os.path.abspath('./assets/Nunito-Light.ttf'), 20)
rating_text = f'Rating {str(round(float(ProfileRating), 2))}'
rating_text_width = font6.getsize(rating_text)[0]
rating_text_height = font6.getsize(rating_text)[1]
img_rating = Image.new("RGBA", (rating_text_width + 20, 40), '#050a1a')
drawtext_level = ImageDraw.Draw(img_rating)
drawtext_level.text(((img_rating.width - rating_text_width) / 2, (img_rating.height - rating_text_height) / 2),
rating_text, '#ffffff', font=font6)
b30img.alpha_composite(img_rating, (1825 - img_level.width - img_rating.width - 30, 85))
2022-01-20 06:16:49 +00:00
textdraw = ImageDraw.Draw(b30img)
textdraw.text((5, 5), f'Based on CytoidAPI | Generated by Teahouse Studios "Akaribot"',
'white', font=font6)
2021-02-12 16:44:53 +00:00
i = 0
fname = 1
t = 0
s = 0
2022-08-10 11:27:08 +00:00
for card in cards:
2021-02-12 16:44:53 +00:00
try:
w = 15 + 384 * i
h = 135
if s == 5:
s = 0
t += 1
h = h + 240 * t
w = w - 384 * 5 * t
i += 1
2022-08-10 11:27:08 +00:00
b30img.alpha_composite(card, (w, h))
2021-02-12 16:44:53 +00:00
fname += 1
s += 1
except Exception:
traceback.print_exc()
break
if __name__ == '__main__':
b30img.show()
else:
2022-06-20 13:35:56 +00:00
savefilename = os.path.abspath(f'{Config("cache_path")}{str(uuid.uuid4())}.jpg')
2021-02-12 16:44:53 +00:00
b30img.convert("RGB").save(savefilename)
2022-06-20 13:35:56 +00:00
# shutil.rmtree(workdir)
return {'status': True, 'path': savefilename}
2021-02-12 16:44:53 +00:00
except Exception as e:
traceback.print_exc()
return {'status': False, 'text': '发生错误:' + str(e)}
2021-02-12 16:44:53 +00:00
async def download_cover_thumb(uid):
try:
d = abspath('./assets/cytoid-cover/' + uid + '/')
if not os.path.exists(d):
os.makedirs(d)
path = d + '/thumbnail.png'
if not os.path.exists(d):
os.mkdir(d)
if not os.path.exists(path):
2021-02-12 20:02:26 +00:00
level_url = 'http://services.cytoid.io/levels/' + uid
get_level = json.loads(await get_url(level_url))
2022-06-20 13:35:56 +00:00
cover_thumbnail = get_level['cover']['original'] + "?h=240&w=384"
2021-02-12 16:44:53 +00:00
async with aiohttp.ClientSession() as session:
async with session.get(cover_thumbnail) as resp:
2022-06-19 12:42:32 +00:00
async with async_open(path, 'wb+') as jpg:
await jpg.write(await resp.read())
2021-02-12 16:44:53 +00:00
return path
else:
return path
except:
traceback.print_exc()
return False
async def download_avatar_thumb(link, id):
2022-08-04 07:52:42 +00:00
Logger.debug(f'Downloading avatar for {str(id)}')
2021-02-12 16:44:53 +00:00
try:
d = abspath('./assets/cytoid-avatar/')
if not os.path.exists(d):
os.makedirs(d)
path = d + f'/{id}.png'
if not os.path.exists(d):
os.mkdir(d)
2021-02-12 20:02:26 +00:00
if os.path.exists(path):
os.remove(path)
async with aiohttp.ClientSession() as session:
async with session.get(link, timeout=aiohttp.ClientTimeout(total=20)) as resp:
2022-06-19 12:42:32 +00:00
async with async_open(path, 'wb+') as jpg:
await jpg.write(await resp.read())
2021-02-12 20:02:26 +00:00
return path
2021-02-12 16:44:53 +00:00
except:
traceback.print_exc()
return False
2022-08-10 11:27:08 +00:00
async def make_songcard(coverpath, chart_type, difficulty, chart_name, score, acc, rt, playtime, rank, details,
2022-06-20 12:14:32 +00:00
havecover=True):
2021-02-12 16:44:53 +00:00
if havecover:
2021-11-06 12:01:37 +00:00
try:
img = Image.open(coverpath)
except:
os.remove(coverpath)
2021-11-06 12:01:37 +00:00
img = Image.new('RGBA', (384, 240), 'black')
2021-02-12 16:44:53 +00:00
else:
img = Image.new('RGBA', (384, 240), 'black')
img = img.convert('RGBA')
downlight = ImageEnhance.Brightness(img)
img_size = downlight.image.size
resize_multiplier = 384 / img_size[0]
img_h = int(img_size[1] * resize_multiplier)
if img_h < 240:
resize_multiplier = 240 / img_size[1]
resize_img_w = int(img_size[0] * resize_multiplier)
resize_img_h = int(img_size[1] * resize_multiplier)
crop_start_x = int((resize_img_w - 384) / 2)
crop_start_y = int((resize_img_h - 240) / 2)
img = downlight.enhance(0.5).resize((resize_img_w,
resize_img_h),
2023-01-08 09:21:22 +00:00
).crop((crop_start_x, crop_start_y,
384 + crop_start_x, 240 + crop_start_y))
elif img_h > 240:
crop_start_y = int((img_h - 240) / 2)
2023-01-08 09:21:22 +00:00
img = downlight.enhance(0.5).resize((384, img_h))\
.crop((0, crop_start_y, 384, 240 + crop_start_y))
else:
2023-01-08 09:21:22 +00:00
img = downlight.enhance(0.5).resize((384, img_h))
2021-02-12 16:44:53 +00:00
img_type = Image.open(f'./assets/cytoid/{chart_type}.png')
img_type = img_type.convert('RGBA')
img_type = img_type.resize((40, 40))
img.alpha_composite(img_type, (20, 20))
font_path = './assets/Noto Sans CJK DemiLight.otf'
font = ImageFont.truetype(os.path.abspath(font_path), 25)
font2 = ImageFont.truetype(os.path.abspath(font_path), 15)
font3 = ImageFont.truetype(os.path.abspath(font_path), 20)
drawtext = ImageDraw.Draw(img)
2021-02-12 17:18:39 +00:00
drawtext.text((20, 130), score, '#ffffff', font=font3)
2021-02-12 16:44:53 +00:00
drawtext.text((20, 155), chart_name, '#ffffff', font=font)
drawtext.text((20, 185),
f'Acc: {round(acc, 4)} Perfect: {details["perfect"]} Great: {details["great"]} Good: {details["good"]}'
f'\nRating: {round(rt, 4)} Bad: {details["bad"]} Miss: {details["miss"]}', font=font2)
2021-02-12 16:44:53 +00:00
playtime = f'{playtime} #{rank}'
playtime_width = font3.getsize(playtime)[0]
songimg_width = 384
drawtext.text((songimg_width - playtime_width - 15, 205), playtime, '#ffffff', font=font3)
type_ = str(difficulty)
type_text = Image.new('RGBA', (32, 32))
draw_typetext = ImageDraw.Draw(type_text)
draw_typetext.text(((32 - font3.getsize(type_)[0] - font.getoffset(type_)[0]) / 2, 0), type_, "#ffffff", font=font3)
img.alpha_composite(type_text, (23, 29))
2022-08-04 07:52:42 +00:00
Logger.debug('Image generated: ' + str(rank))
2022-08-10 11:27:08 +00:00
return {int(rank): img}