mirror of
				https://github.com/nonebot/nonebot2.git
				synced 2025-10-31 06:56:39 +00:00 
			
		
		
		
	Add docs for scheduler
This commit is contained in:
		
							
								
								
									
										21
									
								
								docs/guide/code/awesome-bot-6/awesome/plugins/group_admin.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								docs/guide/code/awesome-bot-6/awesome/plugins/group_admin.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| from none import on_request, RequestSession | ||||
| from none import on_notice, NoticeSession | ||||
|  | ||||
|  | ||||
| # 将函数注册为群请求处理器 | ||||
| @on_request('group') | ||||
| async def _(session: RequestSession): | ||||
|     # 判断验证信息是否符合要求 | ||||
|     if session.ctx['comment'] == '暗号': | ||||
|         # 验证信息正确,同意入群 | ||||
|         await session.approve() | ||||
|         return | ||||
|     # 验证信息错误,拒绝入群 | ||||
|     await session.reject('请说暗号') | ||||
|  | ||||
|  | ||||
| # 将函数注册为群成员增加通知处理器 | ||||
| @on_notice('group_increase') | ||||
| async def _(session: NoticeSession): | ||||
|     # 发送欢迎消息 | ||||
|     await session.send('欢迎新朋友~') | ||||
							
								
								
									
										16
									
								
								docs/guide/code/awesome-bot-6/awesome/plugins/scheduler.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								docs/guide/code/awesome-bot-6/awesome/plugins/scheduler.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| from datetime import datetime | ||||
|  | ||||
| import none | ||||
| import pytz | ||||
| from aiocqhttp.exceptions import Error as CQHttpError | ||||
|  | ||||
|  | ||||
| @none.scheduler.scheduled_job('cron', hour='*') | ||||
| async def _(): | ||||
|     bot = none.get_bot() | ||||
|     now = datetime.now(pytz.timezone('Asia/Shanghai')) | ||||
|     try: | ||||
|         await bot.send_group_msg(group_id=672076603, | ||||
|                                  message=f'现在{now.hour}点整啦!') | ||||
|     except CQHttpError: | ||||
|         pass | ||||
							
								
								
									
										87
									
								
								docs/guide/code/awesome-bot-6/awesome/plugins/tuling.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								docs/guide/code/awesome-bot-6/awesome/plugins/tuling.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,87 @@ | ||||
| import json | ||||
| from typing import Optional | ||||
|  | ||||
| import aiohttp | ||||
| from aiocqhttp.message import escape | ||||
| from none import on_command, CommandSession | ||||
| from none import on_natural_language, NLPSession, NLPResult | ||||
| from none.helpers import context_id | ||||
|  | ||||
| # 定义无法获取图灵回复时的「表达(Expression)」 | ||||
| EXPR_DONT_UNDERSTAND = ( | ||||
|     '我现在还不太明白你在说什么呢,但没关系,以后的我会变得更强呢!', | ||||
|     '我有点看不懂你的意思呀,可以跟我聊些简单的话题嘛', | ||||
|     '其实我不太明白你的意思……', | ||||
|     '抱歉哦,我现在的能力还不能够明白你在说什么,但我会加油的~' | ||||
| ) | ||||
|  | ||||
|  | ||||
| # 注册一个仅内部使用的命令,不需要 aliases | ||||
| @on_command('tuling') | ||||
| async def tuling(session: CommandSession): | ||||
|     # 获取可选参数,这里如果没有 message 参数,命令不会被中断,message 变量会是 None | ||||
|     message = session.get_optional('message') | ||||
|  | ||||
|     # 通过封装的函数获取图灵机器人的回复 | ||||
|     reply = await call_tuling_api(session, message) | ||||
|     if reply: | ||||
|         # 如果调用图灵机器人成功,得到了回复,则转义之后发送给用户 | ||||
|         # 转义会把消息中的某些特殊字符做转换,以避免酷 Q 将它们理解为 CQ 码 | ||||
|         await session.send(escape(reply)) | ||||
|     else: | ||||
|         # 如果调用失败,或者它返回的内容我们目前处理不了,发送无法获取图灵回复时的「表达」 | ||||
|         # session.send_expr() 内部会调用 none.expression.render() | ||||
|         # 该函数会将一个「表达」渲染成一个字符串消息 | ||||
|         await session.send_expr(EXPR_DONT_UNDERSTAND) | ||||
|  | ||||
|  | ||||
| @on_natural_language | ||||
| async def _(session: NLPSession): | ||||
|     # 以置信度 60.0 返回 tuling 命令 | ||||
|     # 确保任何消息都在且仅在其它自然语言处理器无法理解的时候使用 tuling 命令 | ||||
|     return NLPResult(60.0, 'tuling', {'message': session.msg_text}) | ||||
|  | ||||
|  | ||||
| async def call_tuling_api(session: CommandSession, text: str) -> Optional[str]: | ||||
|     # 调用图灵机器人的 API 获取回复 | ||||
|  | ||||
|     if not text: | ||||
|         return None | ||||
|  | ||||
|     url = 'http://openapi.tuling123.com/openapi/api/v2' | ||||
|  | ||||
|     # 构造请求数据 | ||||
|     payload = { | ||||
|         'reqType': 0, | ||||
|         'perception': { | ||||
|             'inputText': { | ||||
|                 'text': text | ||||
|             } | ||||
|         }, | ||||
|         'userInfo': { | ||||
|             'apiKey': session.bot.config.TULING_API_KEY, | ||||
|             'userId': context_id(session.ctx, use_hash=True) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     group_unique_id = context_id(session.ctx, mode='group', use_hash=True) | ||||
|     if group_unique_id: | ||||
|         payload['userInfo']['groupId'] = group_unique_id | ||||
|  | ||||
|     try: | ||||
|         # 使用 aiohttp 库发送最终的请求 | ||||
|         async with aiohttp.ClientSession() as sess: | ||||
|             async with sess.post(url, json=payload) as response: | ||||
|                 if response.status != 200: | ||||
|                     # 如果 HTTP 响应状态码不是 200,说明调用失败 | ||||
|                     return None | ||||
|  | ||||
|                 resp_payload = json.loads(await response.text()) | ||||
|                 if resp_payload['results']: | ||||
|                     for result in resp_payload['results']: | ||||
|                         if result['resultType'] == 'text': | ||||
|                             # 返回文本类型的回复 | ||||
|                             return result['values']['text'] | ||||
|     except (aiohttp.ClientError, json.JSONDecodeError, KeyError): | ||||
|         # 抛出上面任何异常,说明调用失败 | ||||
|         return None | ||||
| @@ -0,0 +1,43 @@ | ||||
| from none import on_command, CommandSession | ||||
| from none import on_natural_language, NLPSession, NLPResult | ||||
| from jieba import posseg | ||||
|  | ||||
| from .data_source import get_weather_of_city | ||||
|  | ||||
|  | ||||
| @on_command('weather', aliases=('天气', '天气预报', '查天气')) | ||||
| async def weather(session: CommandSession): | ||||
|     city = session.get('city', prompt='你想查询哪个城市的天气呢?') | ||||
|     weather_report = await get_weather_of_city(city) | ||||
|     await session.send(weather_report) | ||||
|  | ||||
|  | ||||
| @weather.args_parser | ||||
| async def _(session: CommandSession): | ||||
|     stripped_arg = session.current_arg_text.strip() | ||||
|     if session.current_key: | ||||
|         session.args[session.current_key] = stripped_arg | ||||
|     elif stripped_arg: | ||||
|         session.args['city'] = stripped_arg | ||||
|  | ||||
|  | ||||
| # on_natural_language 装饰器将函数声明为一个自然语言处理器 | ||||
| # keywords 表示需要响应的关键词,类型为任意可迭代对象,元素类型为 str | ||||
| # 如果不传入 keywords,则响应所有没有被当作命令处理的消息 | ||||
| @on_natural_language(keywords=('天气',)) | ||||
| async def _(session: NLPSession): | ||||
|     # 去掉消息首尾的空白符 | ||||
|     stripped_msg_text = session.msg_text.strip() | ||||
|     # 对消息进行分词和词性标注 | ||||
|     words = posseg.lcut(stripped_msg_text) | ||||
|  | ||||
|     city = None | ||||
|     # 遍历 posseg.lcut 返回的列表 | ||||
|     for word in words: | ||||
|         # 每个元素是一个 pair 对象,包含 word 和 flag 两个属性,分别表示词和词性 | ||||
|         if word.flag == 'ns': | ||||
|             # ns 词性表示地名 | ||||
|             city = word.word | ||||
|  | ||||
|     # 返回处理结果,三个参数分别为置信度、命令名、命令会话的参数 | ||||
|     return NLPResult(90.0, 'weather', {'city': city}) | ||||
| @@ -0,0 +1,2 @@ | ||||
| async def get_weather_of_city(city: str) -> str: | ||||
|     return f'{city}的天气是……' | ||||
							
								
								
									
										11
									
								
								docs/guide/code/awesome-bot-6/bot.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								docs/guide/code/awesome-bot-6/bot.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| from os import path | ||||
|  | ||||
| import none | ||||
|  | ||||
| import config | ||||
|  | ||||
| if __name__ == '__main__': | ||||
|     none.init(config) | ||||
|     none.load_plugins(path.join(path.dirname(__file__), 'awesome', 'plugins'), | ||||
|                       'awesome.plugins') | ||||
|     none.run() | ||||
							
								
								
									
										10
									
								
								docs/guide/code/awesome-bot-6/config.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								docs/guide/code/awesome-bot-6/config.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| from none.default_config import * | ||||
|  | ||||
| HOST = '0.0.0.0' | ||||
| PORT = 8080 | ||||
|  | ||||
| SUPERUSERS = {12345678} | ||||
| COMMAND_START.add('') | ||||
| NICKNAME = {'小明', '明明'} | ||||
|  | ||||
| TULING_API_KEY = '' | ||||
							
								
								
									
										4
									
								
								docs/guide/code/awesome-bot-6/requirements.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								docs/guide/code/awesome-bot-6/requirements.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| none-bot>=0.2.1 | ||||
| jieba | ||||
| aiohttp | ||||
| pytz | ||||
							
								
								
									
										50
									
								
								docs/guide/scheduler.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								docs/guide/scheduler.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,50 @@ | ||||
| # 添加计划任务 <Badge text="0.3.0+" type="tip"/> | ||||
|  | ||||
| 实际应用中还经常会有定时执行任务的需求,为了方便这类需求的开发,NoneBot 可选地包含了计划任务功能。 | ||||
|  | ||||
| ::: tip 提示 | ||||
| 本章的完整代码可以在 [awesome-bot-6](https://github.com/richardchien/none-bot/tree/master/docs/guide/code/awesome-bot-6) 查看。 | ||||
| ::: | ||||
|  | ||||
| ## 安装 APScheduler | ||||
|  | ||||
| 计划任务功能在 NoneBot 中是可选功能,只有当同时安装了 [APScheduler](https://github.com/agronholm/apscheduler) 时,才会启用。 | ||||
|  | ||||
| 使用下面命令安装 APScheduler: | ||||
|  | ||||
| ```bash | ||||
| pip install apscheduler | ||||
| ``` | ||||
|  | ||||
| 安装成功之后就可以通过 `none.scheduler` 访问 [`AsyncIOScheduler`](https://apscheduler.readthedocs.io/en/latest/modules/schedulers/asyncio.html#apscheduler.schedulers.asyncio.AsyncIOScheduler) 对象。 | ||||
|  | ||||
| ## 定时发送消息 | ||||
|  | ||||
| 这里以一个整点报时的功能为例,来介绍定时任务的使用。 | ||||
|  | ||||
| 新建文件 `awesome/plugins/scheduler.py`,编写代码如下: | ||||
|  | ||||
| ```python {8} | ||||
| from datetime import datetime | ||||
|  | ||||
| import none | ||||
| import pytz | ||||
| from aiocqhttp.exceptions import Error as CQHttpError | ||||
|  | ||||
|  | ||||
| @none.scheduler.scheduled_job('cron', hour='*') | ||||
| async def _(): | ||||
|     bot = none.get_bot() | ||||
|     now = datetime.now(pytz.timezone('Asia/Shanghai')) | ||||
|     try: | ||||
|         await bot.send_group_msg(group_id=672076603, | ||||
|                                  message=f'现在{now.hour}点整啦!') | ||||
|     except CQHttpError: | ||||
|         pass | ||||
| ``` | ||||
|  | ||||
| 这里最主要的就是第 8 行,`none.scheduler.scheduled_job()` 是一个装饰器,第一个参数是触发器类型(这里是 `cron`,表示使用 [Cron](https://apscheduler.readthedocs.io/en/latest/modules/triggers/cron.html#module-apscheduler.triggers.cron)) 类型的触发参数)。这里 `hour='*'` 表示每小时都执行,`minute` 和 `second` 不填时默认为 `0`,也就是说装饰器所装饰的这个函数会在每小时的第一秒被执行。 | ||||
|  | ||||
| 除了 `cron`,还有两种触发器类型 `interval` 和 `date`。例如,你可以使用 `none.scheduler.scheduled_job('interval', minutes=10)` 来每十分钟执行一次任务。 | ||||
|  | ||||
| 限于篇幅,这里无法给出太详细的接口介绍,`none.scheduler` 是一个 APScheduler 的 `AsyncIOScheduler` 对象,因此关于它的更多使用方法,可以参考 [APScheduler 的官方文档](https://apscheduler.readthedocs.io/en/latest/userguide.html)。 | ||||
		Reference in New Issue
	
	Block a user