✨查电费基础功能完成,支持简单推送
This commit is contained in:
parent
c07df37bb7
commit
db5123f259
|
@ -0,0 +1,201 @@
|
||||||
|
from nonebot_plugin_apscheduler import scheduler
|
||||||
|
import nonebot
|
||||||
|
from nonebot import on_command
|
||||||
|
from nonebot.rule import Rule
|
||||||
|
from nonebot.adapters.onebot.v11.adapter import Message, MessageSegment
|
||||||
|
from nonebot.adapters.onebot.v11 import Message, MessageSegment, Bot, MessageEvent
|
||||||
|
from nonebot.matcher import Matcher
|
||||||
|
from nonebot.params import CommandArg
|
||||||
|
import aiohttp
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
import asyncio
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from .config import Config
|
||||||
|
from .data import load_binding_data, BUILDING_TAB, HELP, get_bid_by_bname
|
||||||
|
|
||||||
|
|
||||||
|
electricity_check = on_command(
|
||||||
|
'eleccheck', rule=Rule(), aliases={'查电费'}, priority=5)
|
||||||
|
|
||||||
|
elec_check_config = Config.parse_obj(nonebot.get_driver().config.dict())
|
||||||
|
|
||||||
|
data_path = Path().absolute() / "data" / "eleccheck"
|
||||||
|
|
||||||
|
REQ_DATA = {"query_elec_roominfo": {"aid": "0030000000002501", "account": "251277", "room": {"roomid": "", "room": ""}, "floor": {
|
||||||
|
"floorid": "", "floor": ""}, "area": {"area": "云塘校区", "areaname": "云塘校区"}, "building": {"buildingid": "451", "building": "17栋"}}}
|
||||||
|
API = 'http://yktwd.csust.edu.cn:8988/web/Common/Tsm.html'
|
||||||
|
HEADERS = {
|
||||||
|
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36 Edg/110.0.1587.50',
|
||||||
|
'Referer': 'http://yktwd.csust.edu.cn:8988/web/common/checkEle.html?ticket=2E94FD6F19FB40558D53CC55AE4421E4&from=ehall&cometype=',
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded'
|
||||||
|
}
|
||||||
|
RESULT_PATTERN = r'房间剩余电量([\d\.]+)'
|
||||||
|
re_result = re.compile(RESULT_PATTERN)
|
||||||
|
|
||||||
|
bindings = load_binding_data(str(data_path / 'bindings.json'))
|
||||||
|
|
||||||
|
|
||||||
|
async def reload_bindings():
|
||||||
|
global bindings
|
||||||
|
bindings = load_binding_data(str(data_path / 'bindings.json'))
|
||||||
|
|
||||||
|
|
||||||
|
async def batch_check():
|
||||||
|
"""批量检测低电量房间并推送到群聊
|
||||||
|
"""
|
||||||
|
bot = nonebot.get_bot()
|
||||||
|
if not bindings:
|
||||||
|
return
|
||||||
|
|
||||||
|
for binding_info in bindings:
|
||||||
|
group = binding_info['group']
|
||||||
|
rooms = binding_info['rooms']
|
||||||
|
default_building = binding_info['building']
|
||||||
|
result = []
|
||||||
|
for r in rooms:
|
||||||
|
rid = r['roomid']
|
||||||
|
bname = r.get('building', default_building)
|
||||||
|
bid = get_bid_by_bname(bname)
|
||||||
|
try:
|
||||||
|
remain = await get_electricity(building_id=bid, building_name=bname, roomid=rid)
|
||||||
|
except Exception as e:
|
||||||
|
print('获取信息出错:' + str(e))
|
||||||
|
result.append({'roomid': rid, 'remain': remain, 'building': bname})
|
||||||
|
await asyncio.sleep(0.5)
|
||||||
|
|
||||||
|
msg = ['电量不足预警:\n']
|
||||||
|
for res in result:
|
||||||
|
elec = float(res['remain'])
|
||||||
|
if elec <= 50:
|
||||||
|
msg.append('\n{}{}剩余电量:{}'.format(
|
||||||
|
res['building'], res['roomid'], res['remain']))
|
||||||
|
if len(msg) > 1:
|
||||||
|
await bot.send_group_msg(group_id=group, message=Message(msg))
|
||||||
|
|
||||||
|
if bindings:
|
||||||
|
scheduler.add_job(
|
||||||
|
batch_check,
|
||||||
|
'cron',
|
||||||
|
hour=15,
|
||||||
|
minute=0,
|
||||||
|
second=0,
|
||||||
|
id='batch_elec_check_and_push'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_electricity(building_id: str, building_name: str, roomid: str) -> str:
|
||||||
|
"""通过API获取电量信息
|
||||||
|
|
||||||
|
Args:
|
||||||
|
building_id (str):
|
||||||
|
building_name (str):
|
||||||
|
roomid (str):
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
IOError:
|
||||||
|
RuntimeError:
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str:
|
||||||
|
"""
|
||||||
|
# 组织参数
|
||||||
|
jsondata = REQ_DATA.copy()
|
||||||
|
jsondata['query_elec_roominfo']['room']['roomid'] = roomid
|
||||||
|
jsondata['query_elec_roominfo']['room']['room'] = roomid
|
||||||
|
jsondata['query_elec_roominfo']['building']['buildingid'] = building_id
|
||||||
|
jsondata['query_elec_roominfo']['building']['building'] = building_name
|
||||||
|
payload = {
|
||||||
|
"jsondata": json.dumps(jsondata),
|
||||||
|
"funname": "synjones.onecard.query.elec.roominfo",
|
||||||
|
"json": True
|
||||||
|
}
|
||||||
|
|
||||||
|
# 查询请求
|
||||||
|
async with aiohttp.ClientSession() as session:
|
||||||
|
async with session.post(url=API, data=payload, headers=HEADERS) as res:
|
||||||
|
if res.status != 200:
|
||||||
|
raise IOError('接口调用失败: Code ' + res.status)
|
||||||
|
res_raw = await res.text()
|
||||||
|
|
||||||
|
try:
|
||||||
|
res_json = json.loads(res_raw)
|
||||||
|
except json.decoder.JSONDecodeError:
|
||||||
|
raise IOError('接口调用失败: ' + res_raw)
|
||||||
|
|
||||||
|
if res_json['query_elec_roominfo']['retcode'] != '0':
|
||||||
|
raise RuntimeError(
|
||||||
|
'获取失败: ' + res_json['query_elec_roominfo']['errmsg'])
|
||||||
|
|
||||||
|
msg = res_json['query_elec_roominfo']['errmsg'].strip()
|
||||||
|
# 检查返回结果是否合法,并抽取电量数值
|
||||||
|
match = re_result.match(msg)
|
||||||
|
if not match:
|
||||||
|
raise RuntimeError(
|
||||||
|
'获取失败: ' + res_json['query_elec_roominfo']['errmsg'])
|
||||||
|
|
||||||
|
elec_quantity = re_result.findall(msg)[0]
|
||||||
|
return elec_quantity
|
||||||
|
|
||||||
|
|
||||||
|
async def building_list() -> list[str]:
|
||||||
|
"""获取所有楼栋名列表
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list[str]: 楼栋名列表
|
||||||
|
"""
|
||||||
|
buildings = []
|
||||||
|
for b in BUILDING_TAB:
|
||||||
|
buildings.append(b['building'])
|
||||||
|
return buildings
|
||||||
|
|
||||||
|
|
||||||
|
@electricity_check.handle()
|
||||||
|
async def elec_check_handler(matcher: Matcher, event: MessageEvent, arg: Message = CommandArg()):
|
||||||
|
if event.message_type != 'group':
|
||||||
|
await matcher.finish()
|
||||||
|
|
||||||
|
group = event.group_id
|
||||||
|
# 白名单检查
|
||||||
|
if str(group) not in elec_check_config.elec_check_enable:
|
||||||
|
await matcher.finish()
|
||||||
|
|
||||||
|
# 参数检查
|
||||||
|
args = arg.extract_plain_text()
|
||||||
|
if len(args) == 0:
|
||||||
|
await matcher.finish(HELP)
|
||||||
|
args = args.rsplit()
|
||||||
|
|
||||||
|
if args[0] == '楼栋列表':
|
||||||
|
buildings = await building_list()
|
||||||
|
msg = '支持的所有楼栋名:\n' + '\n'.join(buildings)
|
||||||
|
await matcher.finish(msg)
|
||||||
|
if args[0] == '自动预警配置查询':
|
||||||
|
await matcher.finish('该功能尚未开放')
|
||||||
|
if args[0] == '重载':
|
||||||
|
# TODO: 管理员权限检测
|
||||||
|
await reload_bindings()
|
||||||
|
await matcher.finish('插件配置已重载')
|
||||||
|
if args[0] == '推送':
|
||||||
|
# TODO: 管理员权限检测
|
||||||
|
await batch_check()
|
||||||
|
await matcher.finish('已推送所有预警')
|
||||||
|
|
||||||
|
if len(args) < 2:
|
||||||
|
await matcher.finish(HELP)
|
||||||
|
|
||||||
|
building_name = args[0]
|
||||||
|
roomid = args[1]
|
||||||
|
building_id = get_bid_by_bname(building_name)
|
||||||
|
if not building_id:
|
||||||
|
await matcher.finish('参数错误:楼栋名不存在')
|
||||||
|
|
||||||
|
try:
|
||||||
|
electricity = await get_electricity(building_id=building_id,
|
||||||
|
building_name=building_name,
|
||||||
|
roomid=roomid)
|
||||||
|
except Exception as e:
|
||||||
|
await matcher.finish(str(e))
|
||||||
|
|
||||||
|
await matcher.finish('{}{}剩余电量:{}'.format(building_name, roomid, electricity))
|
|
@ -0,0 +1,6 @@
|
||||||
|
from typing import List
|
||||||
|
from pydantic import BaseModel, Extra
|
||||||
|
|
||||||
|
|
||||||
|
class Config(BaseModel, extra=Extra.ignore):
|
||||||
|
elec_check_enable: List = []
|
|
@ -0,0 +1,185 @@
|
||||||
|
import json
|
||||||
|
|
||||||
|
BUILDING_TAB = [{
|
||||||
|
"buildingid": "471",
|
||||||
|
"building": "16栋A区"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buildingid": "472",
|
||||||
|
"building": "16栋B区"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buildingid": "451",
|
||||||
|
"building": "17栋"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buildingid": "141",
|
||||||
|
"building": "弘毅轩1栋A区"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buildingid": "148",
|
||||||
|
"building": "弘毅轩1栋B区"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buildingid": "197",
|
||||||
|
"building": "弘毅轩2栋A区1-6楼"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buildingid": "201",
|
||||||
|
"building": "弘毅轩2栋B区"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buildingid": "205",
|
||||||
|
"building": "弘毅轩2栋C区"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buildingid": "206",
|
||||||
|
"building": "弘毅轩2栋D区"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buildingid": "155",
|
||||||
|
"building": "弘毅轩3栋A区"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buildingid": "183",
|
||||||
|
"building": "弘毅轩3栋B区"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buildingid": "162",
|
||||||
|
"building": "弘毅轩4栋A区"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buildingid": "169",
|
||||||
|
"building": "弘毅轩4栋B区"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buildingid": "450",
|
||||||
|
"building": "留学生公寓"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buildingid": "176",
|
||||||
|
"building": "敏行轩1栋A区"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buildingid": "184",
|
||||||
|
"building": "敏行轩1栋B区"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buildingid": "513",
|
||||||
|
"building": "敏行轩2栋A区"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buildingid": "520",
|
||||||
|
"building": "敏行轩2栋B区"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buildingid": "85",
|
||||||
|
"building": "行健轩1栋A区"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buildingid": "92",
|
||||||
|
"building": "行健轩1栋B区"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buildingid": "99",
|
||||||
|
"building": "行健轩2栋A区"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buildingid": "106",
|
||||||
|
"building": "行健轩2栋B区"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buildingid": "113",
|
||||||
|
"building": "行健轩3栋A区"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buildingid": "120",
|
||||||
|
"building": "行健轩3栋B区"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buildingid": "127",
|
||||||
|
"building": "行健轩4栋A区"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buildingid": "134",
|
||||||
|
"building": "行健轩4栋B区"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buildingid": "57",
|
||||||
|
"building": "行健轩5栋A区"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buildingid": "64",
|
||||||
|
"building": "行健轩5栋B区"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buildingid": "71",
|
||||||
|
"building": "行健轩6栋A区"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buildingid": "78",
|
||||||
|
"building": "行健轩6栋B区"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buildingid": "1",
|
||||||
|
"building": "至诚轩1栋A区"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buildingid": "8",
|
||||||
|
"building": "至诚轩1栋B区"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buildingid": "15",
|
||||||
|
"building": "至诚轩2栋A区"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buildingid": "22",
|
||||||
|
"building": "至诚轩2栋B区"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buildingid": "29",
|
||||||
|
"building": "至诚轩3栋A区"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buildingid": "36",
|
||||||
|
"building": "至诚轩3栋B区"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buildingid": "43",
|
||||||
|
"building": "至诚轩4栋A区"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buildingid": "50",
|
||||||
|
"building": "至诚轩4栋B区"
|
||||||
|
}]
|
||||||
|
|
||||||
|
building_names = dict()
|
||||||
|
|
||||||
|
for b in BUILDING_TAB:
|
||||||
|
building_names[b['building']] = b['buildingid']
|
||||||
|
|
||||||
|
HELP = """电费查询&预警插件
|
||||||
|
可以手动查询电费,低电量余额时自动提醒(需要管理员设置)
|
||||||
|
|
||||||
|
用法:
|
||||||
|
/查电费 <命令/楼栋名> [宿舍号]
|
||||||
|
|
||||||
|
例:
|
||||||
|
/查电费 17栋 406
|
||||||
|
/查电费 16栋A区 A201
|
||||||
|
|
||||||
|
可用命令:
|
||||||
|
楼栋列表 - 列出所有可使用的楼栋名
|
||||||
|
自动预警配置查询 - N/A
|
||||||
|
重载 - 重载插件配置文件(管理员)
|
||||||
|
推送 - 手动推送所有预警信息(管理员)"""
|
||||||
|
|
||||||
|
def load_binding_data(data_path: str) -> list[dict[str, str]]:
|
||||||
|
try:
|
||||||
|
with open(data_path, 'r', encoding='utf-8') as fp:
|
||||||
|
return json.load(fp)
|
||||||
|
except Exception:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_bid_by_bname(name: str) -> str:
|
||||||
|
return building_names.get(name, '')
|
Loading…
Reference in New Issue