database 接入

This commit is contained in:
2025-11-18 19:36:05 +08:00
parent a8a7b62f76
commit f21da657db
23 changed files with 376 additions and 82 deletions

View File

@ -7,16 +7,19 @@ from nonebot.adapters.discord.event import MessageEvent as DiscordMessageEvent
from nonebot_plugin_alconna import Alconna, AlconnaMatcher, Args, UniMessage, on_alconna
from PIL import Image
import numpy as np
from konabot.common.database import DatabaseManager
from konabot.common.longtask import DepLongTaskTarget
from konabot.common.path import ASSETS_PATH
from konabot.common.web_render import WebRenderer
from konabot.plugins.air_conditioner.ac import AirConditioner, CrashType, generate_ac_image, wiggle_transform
from pathlib import Path
import random
import math
ROOT_PATH = Path(__file__).resolve().parent
def get_ac(id: str) -> AirConditioner:
ac = AirConditioner.air_conditioners.get(id)
ac = AirConditioner.get_ac(id)
if ac is None:
ac = AirConditioner(id)
return ac
@ -61,7 +64,7 @@ evt = on_alconna(Alconna(
async def _(event: BaseEvent, target: DepLongTaskTarget):
id = target.channel_id
ac = get_ac(id)
ac.on = True
ac.update_ac(state=True)
await send_ac_image(evt, ac)
evt = on_alconna(Alconna(
@ -72,7 +75,7 @@ evt = on_alconna(Alconna(
async def _(event: BaseEvent, target: DepLongTaskTarget):
id = target.channel_id
ac = get_ac(id)
ac.on = False
ac.update_ac(state=False)
await send_ac_image(evt, ac)
evt = on_alconna(Alconna(
@ -82,6 +85,8 @@ evt = on_alconna(Alconna(
@evt.handle()
async def _(event: BaseEvent, target: DepLongTaskTarget, temp: Optional[Union[int, float]] = 1):
if temp is None:
temp = 1
if temp <= 0:
return
id = target.channel_id
@ -89,7 +94,7 @@ async def _(event: BaseEvent, target: DepLongTaskTarget, temp: Optional[Union[in
if not ac.on or ac.burnt == True or ac.frozen == True:
await send_ac_image(evt, ac)
return
ac.temperature += temp
ac.update_ac(temperature_delta=temp)
if ac.temperature > 40:
# 根据温度随机出是否爆炸40度开始呈指数增长
possibility = -math.e ** ((40-ac.temperature) / 50) + 1
@ -115,6 +120,8 @@ evt = on_alconna(Alconna(
@evt.handle()
async def _(event: BaseEvent, target: DepLongTaskTarget, temp: Optional[Union[int, float]] = 1):
if temp is None:
temp = 1
if temp <= 0:
return
id = target.channel_id
@ -122,7 +129,7 @@ async def _(event: BaseEvent, target: DepLongTaskTarget, temp: Optional[Union[in
if not ac.on or ac.burnt == True or ac.frozen == True:
await send_ac_image(evt, ac)
return
ac.temperature -= temp
ac.update_ac(temperature_delta=-temp)
if ac.temperature < 0:
# 根据温度随机出是否冻结0度开始呈指数增长
possibility = -math.e ** (ac.temperature / 50) + 1
@ -141,6 +148,16 @@ async def _(event: BaseEvent, target: DepLongTaskTarget):
ac.change_ac()
await send_ac_image(evt, ac)
def query_number_ranking(id: str) -> tuple[int, int]:
result = DatabaseManager.query_by_sql_file(
ROOT_PATH / "sql" / "query_crash_and_rank.sql",
(id,id)
)
if len(result) == 0:
return 0, 0
else:
return result[0].values()
evt = on_alconna(Alconna(
"空调炸炸排行榜",
), use_cmd_start=True, use_cmd_sep=False, skip_for_unmatch=True)
@ -148,8 +165,9 @@ evt = on_alconna(Alconna(
@evt.handle()
async def _(event: BaseEvent, target: DepLongTaskTarget):
id = target.channel_id
ac = get_ac(id)
number, ranking = ac.get_crashes_and_ranking()
# ac = get_ac(id)
# number, ranking = ac.get_crashes_and_ranking()
number, ranking = query_number_ranking(id)
params = {
"number": number,
"ranking": ranking

View File

@ -0,0 +1,9 @@
# 预初始化,只要是导入本插件包就会执行这里的代码
from pathlib import Path
from konabot.common.database import DatabaseManager
# 初始化数据库表
DatabaseManager.execute_by_sql_file(
Path(__file__).resolve().parent / "sql" / "create_table.sql"
)

View File

@ -1,20 +1,71 @@
from enum import Enum
from io import BytesIO
from pathlib import Path
import cv2
import numpy as np
from PIL import Image, ImageDraw, ImageFont
from konabot.common.database import DatabaseManager
from konabot.common.path import ASSETS_PATH, FONTS_PATH
from konabot.common.path import DATA_PATH
import json
ROOT_PATH = Path(__file__).resolve().parent
class CrashType(Enum):
BURNT = 0
FROZEN = 1
class AirConditioner:
air_conditioners: dict[str, "AirConditioner"] = {}
@classmethod
def get_ac(cls, id: str) -> 'AirConditioner':
result = DatabaseManager.query_by_sql_file(ROOT_PATH / "sql" / "query_ac.sql", (id,))
if len(result) == 0:
ac = cls.create_ac(id)
return ac
ac_data = result[0]
ac = AirConditioner(id)
ac.on = bool(ac_data["on"])
ac.temperature = float(ac_data["temperature"])
ac.burnt = bool(ac_data["burnt"])
ac.frozen = bool(ac_data["frozen"])
return ac
@classmethod
def create_ac(cls, id: str) -> 'AirConditioner':
ac = AirConditioner(id)
DatabaseManager.execute_by_sql_file(
ROOT_PATH / "sql" / "insert_ac.sql",
(id, ac.on, ac.temperature, ac.burnt, ac.frozen)
)
return ac
def update_ac(self, state: bool = None, temperature_delta: float = None, burnt: bool = None, frozen: bool = None) -> 'AirConditioner':
if state is not None:
self.on = state
if temperature_delta is not None:
self.temperature += temperature_delta
if burnt is not None:
self.burnt = burnt
if frozen is not None:
self.frozen = frozen
DatabaseManager.execute_by_sql_file(
ROOT_PATH / "sql" / "update_ac.sql",
(self.on, self.temperature, self.burnt, self.frozen, self.id)
)
return self
def change_ac(self) -> 'AirConditioner':
self.on = False
self.temperature = 24
self.burnt = False
self.frozen = False
DatabaseManager.execute_by_sql_file(
ROOT_PATH / "sql" / "update_ac.sql",
(self.on, self.temperature, self.burnt, self.frozen, self.id)
)
return self
def __init__(self, id: str) -> None:
self.id = id
@ -22,45 +73,40 @@ class AirConditioner:
self.temperature = 24 # 默认温度
self.burnt = False
self.frozen = False
AirConditioner.air_conditioners[id] = self
def change_ac(self):
self.burnt = False
self.frozen = False
self.on = False
self.temperature = 24 # 重置为默认温度
def broke_ac(self, crash_type: CrashType):
'''
让空调坏掉,并保存数据
让空调坏掉
:param crash_type: CrashType 枚举,表示空调坏掉的类型
'''
match crash_type:
case CrashType.BURNT:
self.burnt = True
self.update_ac(burnt=True)
case CrashType.FROZEN:
self.frozen = True
self.save_crash_data(crash_type)
self.update_ac(frozen=True)
DatabaseManager.execute_by_sql_file(
ROOT_PATH / "sql" / "insert_crash.sql",
(self.id, crash_type.value)
)
def save_crash_data(self, crash_type: CrashType):
'''
如果空调爆炸了,就往本地的 ac_crash_data.json 里该 id 的记录加一
'''
data_file = DATA_PATH / "ac_crash_data.json"
crash_data = {}
if data_file.exists():
with open(data_file, "r", encoding="utf-8") as f:
crash_data = json.load(f)
if self.id not in crash_data:
crash_data[self.id] = {"burnt": 0, "frozen": 0}
match crash_type:
case CrashType.BURNT:
crash_data[self.id]["burnt"] += 1
case CrashType.FROZEN:
crash_data[self.id]["frozen"] += 1
with open(data_file, "w", encoding="utf-8") as f:
json.dump(crash_data, f, ensure_ascii=False, indent=4)
# def save_crash_data(self, crash_type: CrashType):
# '''
# 如果空调爆炸了,就往本地的 ac_crash_data.json 里该 id 的记录加一
# '''
# data_file = DATA_PATH / "ac_crash_data.json"
# crash_data = {}
# if data_file.exists():
# with open(data_file, "r", encoding="utf-8") as f:
# crash_data = json.load(f)
# if self.id not in crash_data:
# crash_data[self.id] = {"burnt": 0, "frozen": 0}
# match crash_type:
# case CrashType.BURNT:
# crash_data[self.id]["burnt"] += 1
# case CrashType.FROZEN:
# crash_data[self.id]["frozen"] += 1
# with open(data_file, "w", encoding="utf-8") as f:
# json.dump(crash_data, f, ensure_ascii=False, indent=4)
def get_crashes_and_ranking(self) -> tuple[int, int]:
'''

View File

@ -0,0 +1,15 @@
-- 创建所有表
CREATE TABLE IF NOT EXISTS air_conditioner (
id VARCHAR(128) PRIMARY KEY,
'on' BOOLEAN NOT NULL,
temperature REAL NOT NULL,
burnt BOOLEAN NOT NULL,
frozen BOOLEAN NOT NULL
);
CREATE TABLE IF NOT EXISTS air_conditioner_crash_log (
id VARCHAR(128) NOT NULL,
crash_type INT NOT NULL,
timestamp DATETIME NOT NULL,
FOREIGN KEY (id) REFERENCES air_conditioner(id)
);

View File

@ -0,0 +1,3 @@
-- 插入一台新空调
INSERT INTO air_conditioner (id, 'on', temperature, burnt, frozen)
VALUES (?, ?, ?, ?, ?);

View File

@ -0,0 +1,3 @@
-- 插入一条空调爆炸记录
INSERT INTO air_conditioner_crash_log (id, crash_type, timestamp)
VALUES (?, ?, CURRENT_TIMESTAMP);

View File

@ -0,0 +1,4 @@
-- 查询空调状态,如果没有就插入一条新的记录
SELECT *
FROM air_conditioner
WHERE id = ?;

View File

@ -0,0 +1,23 @@
-- 从 air_conditioner_crash_log 表中获取指定 id 损坏的次数以及损坏次数的排名
SELECT crash_count, crash_rank
FROM (
SELECT id,
COUNT(*) AS crash_count,
RANK() OVER (ORDER BY COUNT(*) DESC) AS crash_rank
FROM air_conditioner_crash_log
GROUP BY id
) AS ranked_data
WHERE id = ?
-- 如果该 id 没有损坏记录,则返回 0 次损坏和对应的最后一名
UNION
SELECT 0 AS crash_count,
(SELECT COUNT(DISTINCT id) + 1 FROM air_conditioner_crash_log) AS crash_rank
FROM (
SELECT DISTINCT id
FROM air_conditioner_crash_log
) AS ranked_data
WHERE NOT EXISTS (
SELECT 1
FROM air_conditioner_crash_log
WHERE id = ?
);

View File

@ -0,0 +1,4 @@
-- 更新空调状态
UPDATE air_conditioner
SET 'on' = ?, temperature = ?, burnt = ?, frozen = ?
WHERE id = ?;