mirror of
https://github.com/ClovertaTheTrilobita/SanYeCao-Nonebot.git
synced 2026-04-01 22:04:51 +00:00
feat(cf): 新增查询CodeForces Rating功能,快来实践你的队友吧👁👁
This commit is contained in:
parent
722be73e14
commit
f30312fd6b
6 changed files with 140 additions and 6 deletions
|
|
@ -40,4 +40,8 @@ openai
|
||||||
typing_extensions
|
typing_extensions
|
||||||
psutil
|
psutil
|
||||||
Beautifulsoup4
|
Beautifulsoup4
|
||||||
aiohttp
|
aiohttp
|
||||||
|
pydantic
|
||||||
|
matplotlib
|
||||||
|
httpx
|
||||||
|
crypto
|
||||||
34
src/clover_sqlite/models/codeforces.py
Normal file
34
src/clover_sqlite/models/codeforces.py
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
from tortoise import fields
|
||||||
|
from src.clover_sqlite.data_init.db_connect import Model
|
||||||
|
|
||||||
|
class CodeForces(Model):
|
||||||
|
cf_id = fields.CharField(max_length=128, null=True, discription="用户codeforces账号")
|
||||||
|
user_id = fields.CharField(max_length=64, discription="用户openid")
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
table = "codeforces"
|
||||||
|
table_description = "cf表"
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def get_cf_id(cls, user_id: str | None):
|
||||||
|
cf_table = await CodeForces.filter(user_id=user_id).first()
|
||||||
|
if cf_table:
|
||||||
|
return cf_table.cf_id
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def insert_cf_id(cls, cf_id: str | None, user_id: str | None):
|
||||||
|
if not cf_id:
|
||||||
|
return -1
|
||||||
|
|
||||||
|
cf_table = await CodeForces.filter(user_id=user_id).first()
|
||||||
|
if cf_table:
|
||||||
|
cf_table.cf_id = cf_id
|
||||||
|
await cf_table.save()
|
||||||
|
return 1
|
||||||
|
else:
|
||||||
|
await cls.create(cf_id=cf_id, user_id=user_id)
|
||||||
|
return 0
|
||||||
|
|
@ -52,4 +52,10 @@ wenku8_password = "<passwd>"
|
||||||
"""
|
"""
|
||||||
多米HTTP代理api
|
多米HTTP代理api
|
||||||
"""
|
"""
|
||||||
proxy_api = "<KEY>"
|
proxy_api = "<KEY>"
|
||||||
|
|
||||||
|
"""
|
||||||
|
CodeForces API
|
||||||
|
"""
|
||||||
|
codeforces_key = "<KEY>"
|
||||||
|
codeforces_secret = "<KEY>"
|
||||||
|
|
@ -10,6 +10,9 @@ os.makedirs(image_local_qq_image_path, exist_ok=True)
|
||||||
# 个人图片路径
|
# 个人图片路径
|
||||||
image_local_path= path+"/image/MaoYuNa"
|
image_local_path= path+"/image/MaoYuNa"
|
||||||
os.makedirs(image_local_path, exist_ok=True)
|
os.makedirs(image_local_path, exist_ok=True)
|
||||||
|
# cf ratings路径
|
||||||
|
rating_path = path+'/image/cf_ratings'
|
||||||
|
os.makedirs(rating_path, exist_ok=True)
|
||||||
# 塔罗牌图片路径
|
# 塔罗牌图片路径
|
||||||
tarots_img_path = path+'/image/tarot/TarotImages/'
|
tarots_img_path = path+'/image/tarot/TarotImages/'
|
||||||
os.makedirs(tarots_img_path, exist_ok=True)
|
os.makedirs(tarots_img_path, exist_ok=True)
|
||||||
|
|
|
||||||
|
|
@ -10,11 +10,11 @@ from src.clover_sqlite.models.user import UserList
|
||||||
|
|
||||||
menu = ["/重启","/今日运势","/今日塔罗","/图","/随机图","/搜番","/日报","/点歌","/摸摸头","/群老婆","/今日老婆", "/开启ai","/关闭ai","/角色列表","/添加人设", "/更新人设", "/删除人设", "/切换人设", "/管理员注册",
|
menu = ["/重启","/今日运势","/今日塔罗","/图","/随机图","/搜番","/日报","/点歌","/摸摸头","/群老婆","/今日老婆", "/开启ai","/关闭ai","/角色列表","/添加人设", "/更新人设", "/删除人设", "/切换人设", "/管理员注册",
|
||||||
"/待办", "/test","/天气","我喜欢你", "❤", "/待办查询", "/新建待办", "/删除待办" ,"/cf","/B站搜索", "/BV搜索", "/喜报", "/悲报", "/luxun","/鲁迅说",
|
"/待办", "/test","/天气","我喜欢你", "❤", "/待办查询", "/新建待办", "/删除待办" ,"/cf","/B站搜索", "/BV搜索", "/喜报", "/悲报", "/luxun","/鲁迅说",
|
||||||
"/奶龙", "/repo", "/info", "/menu", "/轻小说","/本季新番","/下季新番","/新番观察","/绝对色感" ,"/jm"]
|
"/奶龙", "/repo", "/info", "/menu", "/轻小说","/本季新番","/下季新番","/新番观察","/绝对色感" ,"/jm", "/cfrt"]
|
||||||
|
|
||||||
send_menu = ["/menu","/今日运势","/今日塔罗","/图","/随机图","搜番","/日报","/点歌","/摸摸头","/群老婆","/待办","/天气",
|
send_menu = ["/menu","/今日运势","/今日塔罗","/图","/随机图","搜番","/日报","/点歌","/摸摸头","/群老婆","/待办","/天气",
|
||||||
"/待办查询", "/新建待办", "/删除待办" ,"/cf","/B站搜索", "/BV搜索", "/喜报", "/悲报","/鲁迅说",
|
"/待办查询", "/新建待办", "/删除待办" ,"/cf","/B站搜索", "/BV搜索", "/喜报", "/悲报","/鲁迅说",
|
||||||
"/轻小说","/本季新番","/新番观察","/下季新番","/绝对色感","/jm"]
|
"/轻小说","/本季新番","/新番观察","/下季新番","/绝对色感","https://codeforces.com/api/user.rating?handle=/jm"]
|
||||||
|
|
||||||
async def check_value_in_menu(message: MessageEvent) -> bool:
|
async def check_value_in_menu(message: MessageEvent) -> bool:
|
||||||
value = message.get_plaintext().strip().split(" ")
|
value = message.get_plaintext().strip().split(" ")
|
||||||
|
|
@ -30,7 +30,7 @@ async def check_value_in_menu(message: MessageEvent) -> bool:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
check = on_message(rule=to_me() & Rule(check_value_in_menu), priority=10)
|
check = on_message(rule=to_me() & Rule(check_value_in_menu), priority=1)
|
||||||
@check.handle()
|
@check.handle()
|
||||||
async def handle_function(message: MessageEvent):
|
async def handle_function(message: MessageEvent):
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,12 @@ from nonebot.plugin import on_command
|
||||||
from nonebot.rule import to_me
|
from nonebot.rule import to_me
|
||||||
from nonebot.adapters.qq import Message, MessageEvent, MessageSegment
|
from nonebot.adapters.qq import Message, MessageEvent, MessageSegment
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from src.configs.path_config import path
|
|
||||||
|
from src.clover_sqlite.models.codeforces import CodeForces
|
||||||
|
from src.configs.path_config import path, rating_path
|
||||||
|
from src.clover_sqlite.models.user import UserList
|
||||||
|
from datetime import datetime
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
|
||||||
cf_query = on_command("cf", rule=to_me(), priority=10, block=True)
|
cf_query = on_command("cf", rule=to_me(), priority=10, block=True)
|
||||||
@cf_query.handle()
|
@cf_query.handle()
|
||||||
|
|
@ -34,6 +39,88 @@ async def get_cf_rounds():
|
||||||
await cf_query.finish(msg)
|
await cf_query.finish(msg)
|
||||||
|
|
||||||
|
|
||||||
|
cf_ratings = on_command("cfrt", rule=to_me(), priority=10, block=True)
|
||||||
|
@cf_ratings.handle()
|
||||||
|
async def get_cf_ratings(message: MessageEvent):
|
||||||
|
content = message.get_plaintext().strip().split(" ")
|
||||||
|
if len(content) > 1:
|
||||||
|
status = await CodeForces.insert_cf_id(content[1], message.get_user_id())
|
||||||
|
if status == 1:
|
||||||
|
await cf_ratings.send(f"成功将cf账号换绑为{content[1]}🥳")
|
||||||
|
elif status == 0:
|
||||||
|
await cf_ratings.send(f"成功绑定cf账号{content[1]}🎉")
|
||||||
|
else:
|
||||||
|
await cf_ratings.finish(f"出现未知错误,我也不知道啥原因,但是能触发这个没什么用的异常也挺厉害的。")
|
||||||
|
|
||||||
|
cf_id = await CodeForces.get_cf_id(message.author.id)
|
||||||
|
if cf_id:
|
||||||
|
await cf_ratings.send("正在查询CodeForces Rating,请稍后👀")
|
||||||
|
try:
|
||||||
|
results = requests.get("https://codeforces.com/api/user.rating?handle={}".format(cf_id)).json()
|
||||||
|
except BaseException:
|
||||||
|
await cf_ratings.finish("API请求失败,这绝对不是咱的错,绝对不是!")
|
||||||
|
|
||||||
|
if results["status"] != "OK":
|
||||||
|
await cf_ratings.finish(f"未查询到该用户的信息哦,请检查用户名{cf_id}是否正确💦\n可使用 /cfrt+用户名 换绑")
|
||||||
|
|
||||||
|
# 准备绘图数据
|
||||||
|
timestamps = []
|
||||||
|
ratings = []
|
||||||
|
# 遍历所有比赛记录
|
||||||
|
for result in results["result"]:
|
||||||
|
# 提取时间戳和rating数据
|
||||||
|
timestamps.append(result["ratingUpdateTimeSeconds"])
|
||||||
|
ratings.append(result["newRating"])
|
||||||
|
|
||||||
|
if not timestamps:
|
||||||
|
await cf_ratings.finish("未找到Rating变化记录哦~")
|
||||||
|
|
||||||
|
# 转换时间戳为日期格式
|
||||||
|
dates = [datetime.fromtimestamp(ts) for ts in timestamps]
|
||||||
|
|
||||||
|
# 创建图表
|
||||||
|
plt.figure(figsize=(12, 6))
|
||||||
|
plt.plot(
|
||||||
|
dates,
|
||||||
|
ratings,
|
||||||
|
marker='o',
|
||||||
|
linestyle='-',
|
||||||
|
color='#2196F3',
|
||||||
|
linewidth=2,
|
||||||
|
markersize=6,
|
||||||
|
label='CF Rating'
|
||||||
|
)
|
||||||
|
|
||||||
|
# 美化图表
|
||||||
|
plt.title(f'Codeforces Rating Trend ({cf_id})', fontsize=14)
|
||||||
|
plt.xlabel('Contest Time', fontsize=12)
|
||||||
|
plt.ylabel('Rating', fontsize=12)
|
||||||
|
plt.grid(True, linestyle='--', alpha=0.7)
|
||||||
|
|
||||||
|
# 自动旋转日期标签
|
||||||
|
plt.gcf().autofmt_xdate()
|
||||||
|
|
||||||
|
# 添加最新分数标注
|
||||||
|
last_rating = ratings[-1]
|
||||||
|
plt.annotate(f'{last_rating}',
|
||||||
|
xy=(dates[-1], last_rating),
|
||||||
|
xytext=(10, -20),
|
||||||
|
textcoords='offset points',
|
||||||
|
arrowprops=dict(arrowstyle="->"))
|
||||||
|
|
||||||
|
# 保存图片
|
||||||
|
save_path = rating_path + f'cf_rating_{cf_id}.png'
|
||||||
|
plt.savefig(save_path, bbox_inches='tight', dpi=300)
|
||||||
|
plt.close() # 释放内存
|
||||||
|
|
||||||
|
# 发送图片(根据你的机器人框架调整发送逻辑)
|
||||||
|
await cf_ratings.finish(MessageSegment.file_image(Path(save_path)))
|
||||||
|
|
||||||
|
else:
|
||||||
|
await cf_ratings.send("您还未绑定CodeForces账户\n请输入 /cfrt+账户名 来绑定吧。")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_match_phase(phase):
|
def get_match_phase(phase):
|
||||||
phase_map = {
|
phase_map = {
|
||||||
"BEFORE": "未开始",
|
"BEFORE": "未开始",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue