查电费基础功能完成,支持简单推送

This commit is contained in:
Eigeen 2023-03-07 22:35:25 +08:00
parent c07df37bb7
commit db5123f259
3 changed files with 392 additions and 0 deletions

201
__init__.py Normal file
View File

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

6
config.py Normal file
View File

@ -0,0 +1,6 @@
from typing import List
from pydantic import BaseModel, Extra
class Config(BaseModel, extra=Extra.ignore):
elec_check_enable: List = []

185
data.py Normal file
View File

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