Skip to content
This repository has been archived by the owner on Mar 16, 2023. It is now read-only.

Commit

Permalink
feat: 使用botpy重构
Browse files Browse the repository at this point in the history
  • Loading branch information
SaucePlum committed Jun 18, 2022
1 parent 55bc766 commit effebfa
Show file tree
Hide file tree
Showing 6 changed files with 179 additions and 214 deletions.
36 changes: 14 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
# QQ频道机器人-疫情助手(已上官方推荐)
<div align="center">

该代码库是基于QQ机器人框架进行开发的机器人,用于服务查询国内疫情最新动态、疫情资讯、风险地区、出行政策、疫情科普、防疫热线等服务
![botpy](https://socialify.git.ci/ReadSmall/COVID-Bot/image?description=1&font=Source%20Code%20Pro&forks=1&issues=1&language=1&logo=https%3A%2F%2Fgithub.com%2Ftencent-connect%2Fbot-docs%2Fblob%2Fmain%2Fdocs%2F.vuepress%2Fpublic%2Ffavicon-64px.png%3Fraw%3Dtrue&owner=1&pattern=Circuit%20Board&pulls=1&stargazers=1&theme=Light)

赞助开发者:[爱发电](https://afdian.net/@nian-bot)
[![Language](https://img.shields.io/badge/language-python-green.svg?style=plastic)](https://www.python.org/)
[![License](https://img.shields.io/badge/license-MIT-orange.svg?style=plastic)](https://github.com/ReadSmall/COVID-Bot/blob/master/LICENSE)
![Python](https://img.shields.io/badge/python-3.8+-blue)
![PyPI](https://img.shields.io/pypi/v/qq-botpy)
[![BK Pipelines Status](https://api.bkdevops.qq.com/process/api/external/pipelines/projects/qq-guild-open/p-713959939bdc4adca0eea2d4420eef4b/badge?X-DEVOPS-PROJECT-ID=qq-guild-open)](https://devops.woa.com/process/api-html/user/builds/projects/qq-guild-open/pipelines/p-713959939bdc4adca0eea2d4420eef4b/latestFinished?X-DEVOPS-PROJECT-ID=qq-guild-open)

开发者频道:[点击加入开发者QQ频道](https://qun.qq.com/qqweb/qunpro/share?_wv=3&_wwv=128&appChannel=share&inviteCode=1MVLD4&appChannel=share&businessType=9&from=246610&biz=ka)
_✨ QQ频道机器人-疫情助手 ✨_

[爱发电](https://afdian.net/@nian-bot)
·
[开发者频道](https://qun.qq.com/qqweb/qunpro/share?_wv=3&_wwv=128&appChannel=share&inviteCode=1MVLD4&appChannel=share&businessType=9&from=246610&biz=ka)

</div>

## 机器人指令

Expand Down Expand Up @@ -37,22 +47,6 @@

py包的依赖配置,通过`pip install -r requirements.txt` 可以安装所有的依赖包

### 环境配置

拷贝根目录的 `config.example.yml``config.yml`

```shell
cp config.example.yml config.yml
```

修改 `config.yml` ,填入自己的 BotAppID 和 Bot token 以及其他相关参数,参数介绍如下

```shell
token:
appid: "123" # 机器人appid
token: "xxx" # 机器人token
```

### 运行机器人

在代码库根目录执行下面命令
Expand All @@ -67,8 +61,6 @@ python3 bot.py
├── LICENSE
├── README.md
├── .gitignore
├── config.example.yml # 配置文件模版
├── config.yml # 实际的读取配置文件(需要自己从demo复制一份修改参数)
├── util.py # 字符相关的处理
├── command_register.py # 指令的装饰器处理
├── requirements.txt # py包的依赖配置,通过`pip install -r requirements.txt` 可以安装所有的依赖包
Expand Down
246 changes: 85 additions & 161 deletions bot.py
Original file line number Diff line number Diff line change
@@ -1,202 +1,126 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
bot.py.py: 疫情助手主程序
Author: NianGui
Time : 2022/4/23 0:29
"""
import os.path
import random

import qqbot
from qqbot.core.util.yaml_util import YamlUtil

from command_register import command
from util import get_menu, get_covid_data, get_grade_data, get_news_data, get_policy, get_policys, get_covid_phone
import botpy
from botpy import BotAPI
from botpy.message import Message

config = YamlUtil.read(os.path.join(os.path.dirname(__file__), "config.yml"))
T_TOKEN = qqbot.Token(config["bot"]["appid"], config["bot"]["token"])
from command_register import Commands
from util import get_menu, get_covid_data, get_grade_data, get_news_data, get_policy, get_policys, get_covid_phone, \
science, science_images


async def covid(event: str, message: qqbot.Message):
await _send_message("请在指令后带上城市名称\n\n例如:@疫情助手 /疫情 深圳", event, message)
async def covid(message: Message):
await message.reply(
content="请在指令后带上城市名称\n\n例如:@疫情助手 /疫情 深圳"
)
return True

async def grade(event: str, message: qqbot.Message):
await _send_message("请在指令后带上城市名称\n\n例如:@疫情助手 /风险地区 深圳", event, message)
async def grade(message: Message):
await message.reply(
content="请在指令后带上城市名称\n\n例如:@疫情助手 /风险地区 深圳"
)
return True

async def covid_phone(event: str, message: qqbot.Message):
await _send_message("请在指令后带上城市名称\n\n例如:@疫情助手 /防疫热线 深圳", event, message)
async def covid_phone(message: Message):
await message.reply(
content="请在指令后带上城市名称\n\n例如:@疫情助手 /防疫热线 深圳"
)
return True

async def policy(event: str, message: qqbot.Message):
await _send_message(
"请在指令后带上城市名称\n\n"
"当地政策:@疫情助手 /出行政策 深圳\n"
"两地政策:@疫情助手 /出行政策 深圳-广州",
event,
message
async def policy(message: Message):
await message.reply(
content="请在指令后带上城市名称\n\n当地政策:@疫情助手 /出行政策 深圳\n两地政策:@疫情助手 /出行政策 深圳-广州"
)
return True

async def _send_message(content: str, event: str, message: qqbot.Message, image: str = None):
"""
机器人发送消息
"""
msg_api = qqbot.AsyncMessageAPI(T_TOKEN, False)
dms_api = qqbot.AsyncDmsAPI(T_TOKEN, False)

send = qqbot.MessageSendRequest(content=content, msg_id=message.id, image=image)
if event == "DIRECT_MESSAGE_CREATE":
await dms_api.post_direct_message(message.guild_id, send)
else:
await msg_api.post_message(message.channel_id, send)

@command("/菜单")
async def ask_menu(city_name: str, event: str, message: qqbot.Message):
await _send_message(get_menu(), event, message)
@Commands("/菜单")
async def ask_menu(api: BotAPI, message: Message, params=None):
await message.reply(content=get_menu())
return True

@command("/疫情资讯")
async def ask_news(city_name: str, event: str, message: qqbot.Message):
@Commands("/疫情资讯")
async def ask_news(api: BotAPI, message: Message, params=None):
ret = await get_news_data()
await _send_message(ret, event, message)
await message.reply(content=ret)
return True

@command("/出行政策", check_param=True, invalid_func=policy)
async def ask_policy(city_name: str, event: str, message: qqbot.Message):
if '-' in city_name:
from_city = city_name.split('-')[0]
to_city = city_name.split('-')[1]
@Commands("/出行政策")
async def ask_policy(api: BotAPI, message: Message, params=None):
if params:
await policy(message)
return True
if '-' in params:
from_city = params.split('-')[0]
to_city = params.split('-')[1]
ret = await get_policys(from_city, to_city)
else:
ret = await get_policy(city_name)
ret = await get_policy(params)
if ret == '':
await _send_message(get_menu(), event, message)
await message.reply(content=get_menu())
return True
await _send_message(ret, event, message)
await message.reply(content=ret)
return True

@command("/疫情科普")
async def ask_science(city_name: str, event: str, message: qqbot.Message):
science = [
'包里备好消毒湿巾、酒精喷雾、酒精棉片、免洗洗手液、一次性手套。别嫌东西多包重,相信我,你大概率会用得上。',
'出门前戴好口罩,进公司后也别脱,它将是你一天的保护色。最好能包里准备了2—3个备用(如果你买得到的话),到公司后更换口罩,尽量建议到空旷无人地区。',
'饮食专家建议:适量饮水,每天不少于1500ml。',
'在平静状态下测得体温为37.3度即可判断为发热。',
'感染新冠肺炎的患者,大约会在1周后出现呼吸困难、严重者会出现凝血功能障碍、急性呼吸窘迫综合征等严重症状。',
'通常来讲,1岁以下的孩子不适合戴口罩。',
'新冠病毒从发病到入院的中位时间是11天。',
'新型冠状病毒肺炎确诊需要:流行病学史+临床表现+新型冠状病毒核酸检测。',
'新型冠状病毒肺炎感染的报告时间为2小时。',
'冠状病毒感染后免疫系统对肺细胞的攻击引发肺炎,此时可以通过适度使用免疫抑制剂对病人进行治疗,避免肺部严重受损。',
'家庭置备体温计、口罩、家用消毒用品等物品。未接触过疑似或确诊患者且外观完好、无异味或脏污的口罩,回家后可放置于居室通风干燥处,以备下次使用。需要丢弃的口罩,按照生活垃圾分类的要求处理。',
'随时保持手卫生,从公共场所返回、咳嗽手捂之后、饭前便后,用洗手液或香皂流水洗手,或者使用免洗洗手液。不确定手是否清洁时,避免用手接触口鼻眼。打喷嚏或咳嗽时,用手肘衣服遮住口鼻。',
'保持良好的生活习惯。居室整洁,勤开窗,经常通风,定时消毒。平衡膳食,均衡营养,适度运动,充分休息,不随地吐痰,口鼻分泌物用纸巾包好,弃置于有盖的垃圾箱内。',
'尽量减少外出活动。减少走亲访友和聚餐,尽量在家休息。减少到人员密集的公共场所活动,尤其是相对封闭、空气流动差的场所,例如公共浴池、温泉、影院、网吧、KTV、商场、车站、机场、码头和展览馆等。',
'根据具体条件适当参加户外锻炼,提高免疫力与生活质量。在户外活动过程中应避免与同伴以外的人近距离接触,注意个人卫生,不随地吐痰,打喷嚏或咳嗽时用肘部或纸巾遮住。',
'前往公众场所时应遵守相关部门规定,使用健康码等手段确认行动轨迹、健康状况等信息,获得许可后方可进入。',
'若出现发热、咳嗽、咽痛、胸闷、呼吸困难、乏力、恶心呕吐、腹泻、结膜炎、肌肉酸痛等可疑症状,应根据病情,及时到医疗机构就诊。',
'回家之后,请把外套挂在在通风处,不推荐喷酒精消毒,一是因为容易破坏衣物;二是冬天起静电火花遇上酒精很危险。',
'摘口罩时,记住4个“不要”:\n不要触碰口罩的外表面\n不要触碰口罩的内表面\n不要触碰别人使用过的口罩,避免交叉感染\n不要直接放在包里、兜里等处,容易造成持续感染风险,可以由内向外反向折叠后,用自封袋包装\n摘口罩的时候,尽量避免污染区(外面),主要摘下耳挂。',
'触摸被污染的物体表面,然后用脏手触碰嘴巴、鼻子或眼睛,这些均为新型冠状病毒可能的传播途径。',
'通过咳嗽或打喷嚏在空气传播,飞沫随着空气在飘荡,如果没有防护,非常容易中招。',
'什么是密切接触者?\n\n病例的密切接触者,即与病例发病后有如下接触情形之一,但未采取有效防护者:\n·与病例共同居住、学习、工作,或其他有密切接触的人员,如与病例近距离工作或共用同一教室或与病例在同一所房屋中生活;\n·诊疗、护理、探视病例的医护人员、家属或其他与病例有类似近距离接触的人员,如直接治疗及护理病例、到病例所在的密闭环境中探视病人或停留,病例同病室的其他患者及其陪护人员;\n·与病例乘坐同一交通工具并有近距离接触人员,包括在交通工具上照料护理过病人的人员;该病人的同行人员(家人、同事、朋友等);经调查评估后发现有可能近距离接触病人的其他乘客和乘务人员;',
'密切接触者应该怎么做?\n\n密切接触者应进行隔离医学观察。\n居家或集中隔离医学观察,观察期限为自最后一次与病例发生无有效防护的接触或可疑暴露后14天。\n居家医学观察时应独立居住,尽可能减少与其他人员的接触。尽量不要外出。如果必须外出,需经医学观察管理人员批准,并要佩戴一次性外科口罩,避免去人群密集场所。\n·医学观察期间,应配合指定的管理人员每天早、晚各进行一次体温测量,并如实告知健康状况。\n医学观察期间出现发热、咳嗽、气促等急性呼吸道感染症状者,应立即到定点医疗机构诊治。\n医学观察期满时,如未出现上述症状,则解除医学观察。',
'为什么要对密切接触者医学观察14天?\n\n目前对密切接触者采取较为严格的医学观察等预防性公共卫生措施十分必要,这是一种对公众健康安全负责任的态度,也是国际社会通行的做法。参考其他冠状病毒所致疾病潜伏期、此次新型冠状病毒病例相关信息及当前防控实际,将密切接触者医学观察期定为14天,并对密切接触者进行医学观察。',
'如果接到疾控部门通知,你是-个密切接触者,该怎么办?\n\n不用恐慌,按照要求进行居家或集中隔离医学观察。如果是在家中进行医学观察,请不要上班,不要随便外出,做好自我身体状况观察,定期接受社区医生随访,如果出现发热、咳嗽等异常临床表现,及时向当地疾病预防控制机构报告,在其指导下到指定医疗机构进行排查、诊治等。',
]
science_images = [
'http://images.china.cn/site1000/2020-02/05/43fb2c2d-07f3-4ed9-a1ab-69e8ad9aa5e3.jpg',
'http://images.china.cn/site1000/2020-02/05/bda956f4-0e18-4b19-9299-2b9ffb7927c5.jpg',
'http://images.china.cn/site1000/2020-02/05/11167389-3724-4aa4-ae49-ff97380c00ed.jpg',
'http://images.china.cn/site1000/2020-02/05/11167389-3724-4aa4-ae49-ff97380c00ed.jpg',
'http://images.china.cn/site1000/2020-02/05/7d058780-c2f1-476a-bc80-f5bae96bea1f.jpg',
'http://images.china.cn/site1000/2020-02/05/0a4ba046-ebe1-4c54-b377-b0e27281db50.png',
'http://images.china.cn/site1000/2020-02/05/35cbab10-524e-4f52-b7b5-4ebcd9c3180b.png',
'http://images.china.cn/site1000/2020-02/05/69ec7ce4-8875-4215-8545-3b83b6cf0e10.png',
'http://images.china.cn/site1000/2020-02/05/0b5fe2e7-03fd-4201-b1a7-deb2a794e589.jpg',
'http://images.china.cn/site1000/2020-02/05/0f08ee36-ff04-4c2d-8c0c-fa5da494595d.jpg',
'http://images.china.cn/site1000/2020-02/05/4d643e39-ac5d-4c46-bbe1-1ed43fa5464a.jpg',
'http://images.china.cn/site1000/2020-02/05/4d643e39-ac5d-4c46-bbe1-1ed43fa5464a.jpg',
'http://images.china.cn/site1000/2020-02/05/eb69ed80-9f28-4998-a755-a8d65e0473d0.png',
'http://images.china.cn/site1000/2020-02/05/27f0ebc5-2452-4374-ad62-3829ead85a68.png',
'http://images.china.cn/site1000/2020-02/05/34e837f9-4707-4caf-8d22-708554204b65.png',
'http://images.china.cn/site1000/2020-02/05/ef001555-38f6-4ef3-ae6a-2421587f903b.png',
'http://images.china.cn/site1000/2020-02/05/25ca3965-c907-4822-9835-c2b639877cf9.jpg',
'http://images.china.cn/site1000/2020-02/05/1724708c-8fa8-4494-82eb-2ee090954e83.jpg',
'http://images.china.cn/site1000/2020-02/05/9e03d23d-0f7a-4f3b-872d-d8bf816af70f.jpg',
'http://images.china.cn/site1000/2020-02/05/bc807de8-9a61-4baf-a0b1-1ea5915d7d2f.jpg'
]
await _send_message(random.choice(science), event, message, random.choice(science_images))

@Commands("/疫情科普")
async def ask_science(api: BotAPI, message: Message, params=None):
await api.post_message(
channel_id=message.id,
content=random.choice(science),
image=random.choice(science_images),
msg_id=message.id,
)
return True

@command("/疫情", check_param=True, invalid_func=covid)
async def ask_covid(city_name: str, event: str, message: qqbot.Message):
city_name = "中国" if city_name is None else city_name
ret = await get_covid_data(city_name)
@Commands("/疫情")
async def ask_covid(api: BotAPI, message: Message, params=None):
params = "中国" if params is None else params
ret = await get_covid_data(params)
if ret == '':
await _send_message(get_menu(), event, message)
await message.reply(content=get_menu())
return True
await _send_message(ret, event, message)
await message.reply(content=ret)
return True

@command("/防疫热线", check_param=True, invalid_func=covid_phone)
async def ask_covid_phone(city_name: str, event: str, message: qqbot.Message):
ret = await get_covid_phone(city_name)
@Commands("/防疫热线")
async def ask_covid_phone(api: BotAPI, message: Message, params=None):
if params:
await covid_phone(message)
return True
ret = await get_covid_phone(params)
if ret == '':
await _send_message('未找到该地防疫热线电话', event, message)
await message.reply(content="未找到该地防疫热线电话")
return True
await _send_message(ret, event, message)
await message.reply(content=ret)
return True

@command("/风险地区", check_param=True, invalid_func=grade)
async def ask_grade(city_name: str, event: str, message: qqbot.Message):
ret = await get_grade_data(city_name)
await _send_message(ret, event, message)
@Commands("/风险地区")
async def ask_grade(api: BotAPI, message: Message, params=None):
if params:
await grade(message)
return True
ret = await get_grade_data(params)
await message.reply(content=ret)
return True


async def _message_handler(event: str, message: qqbot.Message):
"""
定义事件回调的处理
:param event: 事件类型
:param message: 事件对象(如监听消息是Message对象)
"""
qqbot.logger.info("收到消息: %s" % message.content)

tasks = [
ask_menu, # /菜单
ask_science, # /疫情科普
ask_news, # /疫情资讯
ask_policy, # /出行政策
ask_covid, # /疫情
ask_covid_phone, # /防疫热线
ask_grade, # /风险地区
]
for task in tasks:
if await task("", event, message):
return
await _send_message("抱歉,没明白你的意思呢。\n" + get_menu(), event, message)


def run():
"""
启动机器人
"""
# @机器人后推送被动消息
qqbot_handler = qqbot.Handler(
qqbot.HandlerType.AT_MESSAGE_EVENT_HANDLER, _message_handler
)
# 私信消息
qqbot_direct_handler = qqbot.Handler(
qqbot.HandlerType.DIRECT_MESSAGE_EVENT_HANDLER, _message_handler
)
qqbot.async_listen_events(T_TOKEN, False, qqbot_handler, qqbot_direct_handler)

class MyClient(botpy.Client):
async def on_at_message_create(self, message: Message):
# 注册指令handler
tasks = [
ask_menu, # /菜单
ask_science, # /疫情科普
ask_news, # /疫情资讯
ask_policy, # /出行政策
ask_covid, # /疫情
ask_covid_phone, # /防疫热线
ask_grade, # /风险地区
]
for handler in tasks:
if await handler(api=self.api, message=message):
return

if __name__ == "__main__":
run()
intents = botpy.Intents(public_guild_messages=True)
client = MyClient(intents=intents)
client.run(appid="appid", token="token")
Loading

0 comments on commit effebfa

Please sign in to comment.