基於python和酷Q的QQ機器人開發實踐(1)

基本框架 

(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機器人。後期的博客將會涉及接受更爲複雜的指令的只能機器人。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章