基本框架
(1) 酷Q:目前最常用的QQ機器人軟件(尤其是在smartQQ失效以後)。
(2) 酷Q HTTP插件:通過HTTP對酷Q的事件進行上報以及接收HTTP請求來調用酷Q的DLL接口,從而可以使用其它語言編寫酷Q插件。
(3) nonebot:基於酷Q的Python異步QQ機器人框架。
準備工作
1.安裝酷Q
百度網盤https://pan.baidu.com/s/1W0sO610thTdQy48T4_qNKQ,提取碼:fqc3
酷Q官網最近好像不能註冊了,我在網上找了份資源,解壓後打開CQA.exe,輸入賬號密碼即可。注意:務必用小號作爲機器人賬號!
可以在應用-應用管理中查看並管理所有的機器人插件,在日誌中查看機器人是否有正確做出迴應。酷Q的這一部分是比較簡單的,大家基本看看就知道了。
2.安裝酷Q HTTP插件
百度網盤https://pan.baidu.com/s/1uSH1Ixsx1rBf00BjpgAzdw,提取碼:yyjg
這裏同樣用的是網盤資源,github上的下載確實有點慢(https://github.com/richardchien/coolq-http-api/releases),這裏可能不是最新版(最新版請訪問上述github鏈接),但是也無傷大雅。
你將會下載這樣的文件io.github.richardchien.coolqhttpapi.cpk,將這個cpk文件放到酷Q的插件目錄下,插件目錄一般爲“CQA.exe文件所在目錄/app”,如果你不確定,也可以打開酷Q軟件後右鍵懸浮窗:應用-應用目錄。
在酷Q中點擊重載應用,HTTP插件便會啓動,會彈出下圖這樣的窗口
接下來要完成初始化設置,首次啓用HTTP插件會生成一個默認配置文件,在“CQA.exe文件所在目/data/app/io.github.richardchien.coolqhttpapi/config”文件夾下會有這樣一個json文件,名字是你登陸的QQ號。我們要對這個配置文件進行修改,修改下圖中的三行,修改完成後重載應用。
"ws_reverse_api_url": "ws://127.0.0.1:8080/ws/api/",
"ws_reverse_event_url": "ws://127.0.0.1:8080/ws/event/",
"use_ws_reverse": true,
如果在這一塊遇到問題,可以訪問CQHTTP 插件的官方文檔:https://cqhttp.cc/docs/4.15/
3.安裝nonebot框架
這個是最好搞定的,你只需要在你的python環境中安裝nonebot模塊即可
pip install nonebot
nonebot官方文檔:https://nonebot.cqp.moe/guide/
建議大家在動手實踐之前一定要詳細看一遍!
正式開始!
1.“Hello World”
我們先來看一個最最基礎,也是官方文檔中給出的最小實例。
首先創建一個myRobot文件夾,建議放在酷Q的工作路徑下(其實放哪無所謂)。我們創建一個python文件‘bot.py’
#bot.py
import nonebot
if __name__ == '__main__':
nonebot.init()
nonebot.load_builtin_plugins()
nonebot.run(host='127.0.0.1', port=8080)
運行之後我們在控制檯可以看到這樣的日誌,表明已經設置成功
我們可以給這個機器人QQ賬號發送一個基本的內置指令“echo”,這也是基礎案例包含的唯一指令(其實還有say指令,但涉及到設置超級用戶,具體可以參見說明文檔),意思是讓機器人發送後面的文字,比如我們輸入“/echo hello world”。
我們可以在控制檯和酷Q的日誌看到如下log
那麼echo函數是怎樣定義的呢?我們來看一下源碼
@on_command('echo')
async def echo(session: CommandSession):
await session.send(session.state.get('message') or session.current_arg)
@on_command()是一個命令識別的裝飾器,其中的參數包括name(指令的名稱),aliases(指令的別名),permission(用戶權限設置),only_to_me(在羣聊中喚醒機器人是否需要@,默認爲True)等等,這裏只定義了指令的名稱,當然我們也可以定義如下,這樣我們可以使用“顯示”或者“打印”命令來喚醒機器人,並且在羣聊中不需要@機器人。
@on_command('echo', aliases=('顯示','打印',), only_to_me = False)
裝飾器下面是一個異步的echo函數(關於異步編程可以參見異步IO),這個函數調用session.send()函數,直接把參數中的消息內容原樣發送。
2.翻譯功能
在上面我們學習了只包含基礎echo指令的機器人,接下來我們給它添磚加瓦,添加一些新的功能。
首先我們在上述myRobot文件夾下創建配置文件‘config.py’,主要內容是SUPERUSERS和COMMAND_START,前者定義超級用戶的QQ號,擁有更多權限;後者是設置指令的開頭,這裏設置了五個開頭,拿之前的‘echo’指令爲例,現在用戶可以使用‘echo’,‘/echo’,‘!echo’,‘/echo’,‘!echo’來喚醒機器人(之前只能用‘/echo’)。
from nonebot.default_config import *
SUPERUSERS = {你想設置的超級用戶QQ號}#1
COMMAND_START = {'', '/', '!', '/', '!'}
#HOST = '0.0.0.0'
#PORT = 8080
接着我們在myRobot文件夾新建文件夾‘plugins’,用來放置新的功能python文件,接着在‘plugins文件夾下新建‘translate.py’。
from nonebot import on_command, CommandSession
import requests #爬蟲庫
import re #正則表達式處理
@on_command('translate', aliases=('翻譯','中譯英')) #定義翻譯指令及別名
async def translate(session: CommandSession):
message = session.get('message', prompt='您想翻譯成英文的語句是?') #獲取待翻譯的語句
translate_send = await get_translate(message) #將待翻譯的語句傳給翻譯函數
await session.send(translate_send) #發送翻譯後的語句
async def get_translate(message): #異步的翻譯函數
translate_sentence = get_content(message)
return translate_sentence
def get_content(message): #翻譯功能
url = 'http://fanyi.youdao.com/translate?&doctype=json&type=AUTO&i='+message #有道的免費api
res = requests.get(url) #爬取json格式的網頁
text = res.text
result = re.findall('"tgt":.*"',text)[0].split('"')[3] #用正則表達式提取翻譯後的語句
return result
@translate.args_parser #translate命令的參數解析器
async def _(session: CommandSession):
stripped_arg = session.current_arg_text.strip() #去除首尾空白符
if session.is_first_run: #如果是第一次運行
if stripped_arg: #如果用戶的待翻譯語句跟在指令後面,則直接傳參
session.state['message'] = stripped_arg
return
if not stripped_arg: #如果用戶的待翻譯語句不在指令後面,則讓他另行輸入
session.pause('要翻譯的不能爲空呢,請重新輸入')
#將參數放入會話狀態中
session.state[session.current_key] = stripped_arg
這個翻譯功能的實現明顯要複雜一些了,還會涉及爬蟲以及正則表達式知識,具體解釋見註釋部分。
本翻譯功能基於有道的免費api,所以翻譯效果不是很好(如果你是做着玩玩是無所謂,商業用途考慮購買專業的api,我不是在打廣告!)。大部分這樣的api功能都需要爬蟲和正則表達式知識/json格式處理知識(大致瞭解即可)。
接下來我們將重寫‘bot.py’,將新的翻譯功能添加進去。
import nonebot
import config
from os import path
if __name__ == '__main__':
#加載配置文件
nonebot.init(config)
#加載功能插件(放在plugins文件夾中的python文件)
nonebot.load_plugins(path.join(path.dirname(__file__), 'plugins'), 'plugins')
#nonebot.run()
nonebot.run(host='127.0.0.1', port=8080)
接着我們重新運行‘bot.py’,可以看到以下日誌,可以看到plugins.translate已經成功加載(help和weather是我另寫的其他插件)。
讓我們來做個嘗試吧!
3.天氣功能
我們在plugins文件夾下接着新建‘weather.py’
from nonebot import on_command, CommandSession
# on_command 裝飾器將函數聲明爲一個命令處理器
# 這裏 weather 爲命令的名字,同時允許使用別名「天氣」「天氣預報」「查天氣」
@on_command('weather', aliases=('天氣', '天氣預報', '查天氣'))
async def weather(session: CommandSession):
# 從會話狀態(session.state)中獲取城市名稱(city),如果當前不存在,則詢問用戶
city = session.get('city', prompt='你想查詢哪個城市的天氣呢?')
# 獲取城市的天氣預報
weather_report = await get_weather_of_city(city)
# 向用戶發送天氣預報
await session.send(weather_report)
# weather.args_parser 裝飾器將函數聲明爲 weather 命令的參數解析器
# 命令解析器用於將用戶輸入的參數解析成命令真正需要的數據
@weather.args_parser
async def _(session: CommandSession):
# 去掉消息首尾的空白符
stripped_arg = session.current_arg_text.strip()
if session.is_first_run:
# 該命令第一次運行(第一次進入命令會話)
if stripped_arg:
# 第一次運行參數不爲空,意味着用戶直接將城市名跟在命令名後面,作爲參數傳入
# 例如用戶可能發送了:天氣 南京
session.state['city'] = stripped_arg
return
if not stripped_arg:
# 用戶沒有發送有效的城市名稱(而是發送了空白字符),則提示重新輸入
# 這裏 session.pause() 將會發送消息並暫停當前會話(該行後面的代碼不會被運行)
session.pause('要查詢的城市名稱不能爲空呢,請重新輸入')
# 如果當前正在向用戶詢問更多信息(例如本例中的要查詢的城市),且用戶輸入有效,則放入會話狀態
session.state[session.current_key] = stripped_arg
async def get_weather_of_city(city: str) -> str:
return get_weather(city)
import requests
def get_weather(city):
#這裏可以自行申請免費的天氣api,將我的appid和密碼換成自己的即可
url = 'https://www.tianqiapi.com/free/day?appid=????&appsecret=????&city=' + city
res = requests.get(url)
if 'errcode' in res.json().keys():
return '該城市不存在!'
else:
result= '今日'+city+'的天氣是'+res.json()['wea']+',實時溫度'+res.json()['tem']+'℃,'+res.json()['win']+res.json()['win_speed']+',空氣指數:'+res.json()['air']
return result
這是官方文檔的一個示例,不過官方文檔中沒有補充天氣api部分,我申請了一個免費了api(https://www.tianqiapi.com/)
我們來看看效果
結語
這一階段的學習就到這裏啦,相信大家應該初步瞭解瞭如何製作簡易的QQ機器人。後期的博客將會涉及接受更爲複雜的指令的只能機器人。