如何基于 Python 快速搭建 QQ 开放平台 QQ 群官方机器人详细教程(更新中)

注册 QQ 开放平台账号

账号注册

QQ 机器人:一个机器人可以被添加到 群聊/频道 内对话,QQ 用户也可以直接跟机器人 单独对话。

开发者账号主体要求

单聊对话:【定向邀请】
群聊场景:仅支持企业主体【个人主体暂不支持】
频道场景:企业主体与个人主体均可申请

注册地址:QQ 开放平台(opens new window)

注册创建机器人:获得的开发机器人接入票据 AppID AppSecret Token

创建机器人:https://q.qq.com/#/app/create-bot

在这里插入图片描述

开发设置:https://q.qq.com/qqbot/#/developer/developer-setting

在这里插入图片描述

查询官方文档

新版机器人官方文档:https://bot.q.qq.com/wiki/develop/api-v2/

GitHub 地址

在这里插入图片描述

安装 qq-botpy 环境

更新包的话需要添加 --upgrade 兼容版本:python3.8+

pip install qq-botpy

使用

需要使用的地方import botpy

import botpy

兼容提示

原机器人的老版本qq-bot仍然可以使用,但新接口的支持上会逐渐暂停,此次升级不会影响线上使用的机器人。

基础案例演示

通过继承实现bot.Client, 实现自己的机器人Client

实现机器人相关事件的处理方法,如 on_at_message_create, 详细的事件监听列表,请参考 事件监听.md

定义机器人被@的后自动回复

import botpy
from botpy.message import Message

class MyClient(botpy.Client):
    async def on_at_message_create(self, message: Message):
        await message.reply(content=f"机器人{self.robot.name}收到你的@消息了: {message.content}")

注意:每个事件会下发具体的数据对象,如message相关事件是message.Message的对象 (部分事件透传了后台数据,暂未实现对象缓存)

设置机器人需要监听的事件通道并启动client

import botpy
from botpy.message import Message

class MyClient(botpy.Client):
    async def on_at_message_create(self, message: Message):
        await self.api.post_message(channel_id=message.channel_id, content="content")

intents = botpy.Intents(public_guild_messages=True) 
client = MyClient(intents=intents)
client.run(appid="12345", token="xxxx")

其他案例

examples 目录下存放示例机器人 具体使用可参考 Readme.md:https://github.com/tencent-connect/botpy/blob/master/examples/README.md

examples/
.
├── README.md
├── config.example.yaml          # 示例配置文件(需要修改为config.yaml)
├── demo_announce.py             # 机器人公告API使用示例
├── demo_api_permission.py       # 机器人授权查询API使用示例
├── demo_at_reply.py             # 机器人at被动回复async示例
├── demo_at_reply_ark.py         # 机器人at被动回复ark消息示例
├── demo_at_reply_embed.py       # 机器人at被动回复embed消息示例
├── demo_at_reply_command.py     # 机器人at被动使用Command指令装饰器回复消息示例
├── demo_at_reply_file_data.py   # 机器人at被动回复本地图片消息示例
├── demo_at_reply_keyboard.py    # 机器人at被动回复md带内嵌键盘的示例
├── demo_at_reply_markdown.py    # 机器人at被动回复md消息示例
├── demo_at_reply_reference.py   # 机器人at被动回复消息引用示例
├── demo_dms_reply.py            # 机器人私信被动回复示例
├── demo_get_reaction_users.py   # 机器人获取表情表态成员列表示例
├── demo_guild_member_event.py   # 机器人频道成员变化事件示例
├── demo_interaction.py          # 机器人互动事件示例(未启用)
├── demo_pins_message.py         # 机器人消息置顶示例
├── demo_recall.py               # 机器人消息撤回示例
├── demo_schedule.py             # 机器人日程相关示例

配置 IP 白名单

部分之前就注册的机器人可能不需要进行配置,但是新注册的机器人在进行腾讯开放平台的提审之前,需要进行 IP 白名单的配置。

注意:只有白名单内的IP可以在沙箱环境外成功调用openAPI接口,机器人上线提审前IP白名单不可为空。

在这里插入图片描述

配置 Sandbox QQ 群

在QQ群配置

如需开发在QQ群使用的机器人功能请完成此项配置

在这里插入图片描述
管理员QQ在沙箱群需为群主/管理员,且群成员数不大于20人。建议沙箱群包含「测试」相关字眼,且群头像与其他群区分开。配置完成后,群主可从沙箱群“设置-群机器人”打开机器人列表页添加测试机器人进行开发调试。

案例:OpenAi 问答

常用镜像源

清华:https://pypi.tuna.tsinghua.edu.cn/simple
阿里云:http://mirrors.aliyun.com/pypi/simple/
中国科技大学 https://pypi.mirrors.ustc.edu.cn/simple/
华中理工大学:http://pypi.hustunique.com/
山东理工大学:http://pypi.sdutlinux.org/
豆瓣:http://pypi.douban.com/simple/

配置镜像源

pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
Writing to C:\Users\Administrator\AppData\Roaming\pip\pip.ini

然后我们把这个 C:\Users\Administrator\AppData\Roaming\pip\pip.ini 路径添加到系统环境变量就好了(怎么添加环境变量?跟给 pip 添加环境变量一样操作,只是路径不一样)

安装 openai 库

pip install openai==1.33.0 -i 镜像源

项目结构

bot.py      					机器人主函数
plugins/chat_api.py 		    调用查询 OpenAi 接口插件
config.yaml						机器人基本信息
.env 					        OpenAi token 和 中转路径配置

配置 Token 和 中转地址

在项目同级目录创建 .env 文件

OPENAI_API_KEY=sk-······
OPENAI_BASE_URL=https://api.······

./plugins/chat_api.py 调用查询 OpenAi 接口

from openai import OpenAI
import dotenv
dotenv.load_dotenv(".env")

client = OpenAI()


def chat_answer(text):
    response = client.chat.completions.create(
        model="gpt-3.5-turbo",
        messages=[
            {"role": "system", "content": "You are a helpful assistant."},
            {"role": "user", "content": text}
        ]
    )
    return response.choices[0].message.content

查询 openai bot.py 源码

import os

import botpy
from botpy import logging
from botpy.ext.cog_yaml import read
from botpy.message import GroupMessage
from plugins import chat_api

test_config = read(os.path.join(os.path.dirname(__file__), "config.yaml"))
_log = logging.get_logger()


class MyClient(botpy.Client):
    async def on_ready(self):
        _log.info(f"robot 「{self.robot.name}」 on_ready!")

    async def on_group_at_message_create(self, message: GroupMessage):
        openid = message.author.member_openid
        _log.info(f"robot {openid} on_ready!")
        msg = message.content.strip()

        result = chat_api.chat_answer(text=msg)
        await message._api.post_group_message(
            group_openid=message.group_openid,
            msg_type=0,
            msg_id=message.id,
            content=f"{result}")
        return

if __name__ == "__main__":
    intents = botpy.Intents(public_messages=True)
    client = MyClient(intents=intents)
    client.run(appid=test_config["appid"], secret=test_config["secret"])

运行效果

在这里插入图片描述

案例:查询天气信息

项目结构

bot.py      					机器人主函数
plugins/weather_api.py 		    调用查询天气接口插件
config.yaml						机器人基本信息

./plugins/weather_api.py 调用查询天气接口

import requests


def get_weather(city_name):
    # 设置请求的URL和参数
    url = f'https://apis.juhe.cn/simpleWeather/query?key=50a3bd415158e186903d6e6994157589&city={city_name}'
    # 发送GET请求
    response = requests.get(url)
    # 检查请求是否成功
    if response.status_code == 200:
        # 解析返回的JSON数据
        data = response.json()

        # 检查是否查询成功
        if data['reason'] == '查询成功!':
            # 返回天气数据
            return data['result']
        else:
            return {"error": "查询失败: " + data['reason']}
    else:
        return {"error": "请求失败,状态码: " + str(response.status_code)}


# 调用函数并处理返回的天气数据
def format_weather(city_name):
    # 假设这里你已经有了城市的URL编码,这里用'%E9%87%8D%E5%BA%86'作为示例
    city_encoded = city_name  # 重庆的URL编码
    weather_data = get_weather(city_encoded)

    # 检查是否返回了错误
    if 'error' in weather_data:
        return weather_data['error']
    else:
        # 实时天气
        realtime_weather = weather_data['realtime']
        result = f"实时天气:" + "\n" +  f"{realtime_weather['info']}, 温度: {realtime_weather['temperature']}℃, 湿度: {realtime_weather['humidity']}%, 风向: {realtime_weather['direct']}, 风力: {realtime_weather['power']}级, AQI: {realtime_weather['aqi']}"
        # 未来几天的天气
        result = result + "\n" + "未来几天的天气:"
        for day in weather_data['future']:
            result = result + "\n" + f"日期: {day['date']}, 天气: {day['weather']}, 温度: {day['temperature']}, 风向: {day['direct']}"
        return result

查询天气信息 bot.py 源码

import os

import botpy
from botpy import logging
from botpy.ext.cog_yaml import read
from botpy.message import GroupMessage
from plugins import weather_api

config = read(os.path.join(os.path.dirname(__file__), "config.yaml"))
_log = logging.get_logger()


class MyClient(botpy.Client):
    async def on_ready(self):
        _log.info(f"robot 「{self.robot.name}」 on_ready!")

    async def on_group_at_message_create(self, message: GroupMessage):
        openid = message.author.member_openid
        _log.info(f"robot {openid} on_ready!")
        msg = message.content.strip()

        if msg.startswith("/天气"):
            city_name = msg.replace("/天气", "").strip()
            result = weather_api.format_weather(city_name)
            await message._api.post_group_message(
                group_openid=message.group_openid,
                msg_type=0,
                msg_id=message.id,
                content=f"{result}")


if __name__ == "__main__":
    intents = botpy.Intents(public_messages=True)
    client = MyClient(intents=intents)
    client.run(appid=config["appid"], secret=config["secret"])

运行结果预览

在这里插入图片描述

案例:配置关键词回复

项目结构

bot.py      					机器人主函数
plugins/match_question.py 		查询本地 excel 问答表插件
config.yaml						机器人基本信息
关键词.xlsx						关键词问答表

在这里插入图片描述

./plugins/match_question.py 查询本地 excel 问答表

from openpyxl import load_workbook

# 加载工作簿
wb = load_workbook('关键词.xlsx')
# 选择活动工作表,或者通过名称或索引选择其他工作表
sheet = wb.active  # 或者 wb['Sheet1'] 或 wb.worksheets[0]
question_list = []

# 遍历行和列(从第二行开始,假设第一行是标题)
for row in sheet.iter_rows(min_row=2, values_only=True):  # 添加 values_only=True 以只获取单元格的值
    question = {
        "question": row[0] if row[0] is not None else '',  # 确保如果单元格为空,则使用空字符串
        "answer": row[1] if row[1] is not None else '',
    }
    question_list.append(question)  # 添加创建的字典到列表中,而不是原始的 row


def match(word):
    """
    匹配关键词
    :param word:
    :return:
    """
    for question in question_list:
        if word in question['question']:
            return question['answer']

    for question in question_list:
        if word == question['question']:
            return question['answer']

    return ""

bot.py 源码

import os

import botpy
from botpy import logging
from botpy.ext.cog_yaml import read
from botpy.message import GroupMessage
from plugins import match_question

test_config = read(os.path.join(os.path.dirname(__file__), "config.yaml"))
_log = logging.get_logger()


class MyClient(botpy.Client):
    async def on_ready(self):
        _log.info(f"robot 「{self.robot.name}」 on_ready!")

    async def on_group_at_message_create(self, message: GroupMessage):
        openid = message.author.member_openid
        _log.info(f"robot {openid} on_ready!")
        msg = message.content.strip()

        if match_question.match(msg):
            result = match_question.match(msg)
            await message._api.post_group_message(
                group_openid=message.group_openid,
                msg_type=0,
                msg_id=message.id,
                content=f"{result}")
            return


if __name__ == "__main__":
    intents = botpy.Intents(public_messages=True)
    client = MyClient(intents=intents)
    client.run(appid=test_config["appid"], secret=test_config["secret"])

运行效果

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/706179.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于uni-app与图鸟UI打造的各领域移动端模板大赏

随着移动互联网的迅猛发展,各类移动端应用层出不穷,为了帮助企业快速搭建高效、美观的移动平台,我们基于强大的uni-app与图鸟UI,精心打造了不下于40套覆盖多个领域的移动端模板。今天,就让我们一起领略这些模板的风采吧…

淘宝镜像地址失效

1. 使用nvm安装node时候报错 报错内容 Get "https://npm.taobao.org/mirrors/node/latest/SHASUMS256.txt": tls: failed to verify certificate: x509: certificate has expired or is not yet valid:报错原因 淘宝镜像地址的证书过期了 解决 找到nvm安装的根目…

Java-集合类-Arrays.asList()使用需要注意的大坑

Arrays.asList使用需要注意的大坑 大坑1.不可修改列表大小&&原始数组与列表共享数据2.对于基本类型数组的使用限制 两个错误案例wrong1wrong2 ​ Arrays.asList() 是 Java 中一个常用的方法,它 用于将数组转换为列表(List)。这个方…

Bybatis动态SQL的绑定和公共sql语句片段

Mybatis除了大部分动态标签,最后还有三个标签,分别是bind,sql和include: ①bind:这个标签作用就是将OGNL标签里的值,进行二次加工,在绑定到另一个变量里,供其他标签使用 调用getUse…

Java—集合简述

集合类继承结构图 Collection|---------------------| | | Set List Queue| | | SortedSet ArrayList Deque| LinkedList | NavigableSet Vector ArrayDeque| Stack | TreeSet …

数据结构笔记-2、线性表

2.1、线性表的定义和基本操作 如有侵权请联系删除。 2.1.1、线性表的定义: ​ 线性表是具有相同数据类型的 n (n>0) 个数据元素的有限序列,其中 n 为表长,当 n 0 时线性表是一个空表。若用 L 命名线性表,则其一般表示为&am…

【软件工程导论】——期末复习(冲刺篇)

📖 前言:快考试了,做篇期末总结,都是重点与必考点。 题型:材料分析题、简答题、看图分析题 课本: 目录 🕒 1. 软件生存周期与软件过程🕘 1.1 软件生存周期🕘 1.2 传统…

Java老人护理上门服务类型系统小程序APP源码

🌸 老人上门护理服务系统:温暖与专业并存 🌸 一、🏠 走进老人上门护理服务系统 随着社会的快速发展,我们越来越关注老年人的生活质量。老人上门护理服务系统应运而生,它结合了现代科技与人性化服务&#…

webpack代码分割

webpack代码分割方式 entry配置:通过多个 entry 文件来实现动态加载(按需加载):通过主动使用import来动态加载抽取公共代码:使用splitChunks配置来抽取公共代码 基础概念 概念含义Entry入口,Webpack 执行构建的第一步将从 Entr…

Adobe Illustrator (AI)小技巧总结

AI2024(64bit) Adobe Illustrator 软件安装包下载地址: 百度网盘下载https://pan.baidu.com/s/1C10-2JVN1rxFF5VFRuV2Yw?pwdSIMS 1.效果-扭曲与变换-变换,两个图形组合(CtrlG)中心点在中间 例:角度7.5副本24半圆48格…

pdf格式转成jpg图片,pdf格式如何转jpg

pdf转图片的方法,对于许多人来说可能是一个稍显陌生的操作。然而,在日常生活和工作中,我们有时确实需要将pdf文件转换为图片格式,以便于在特定的场合或平台上进行分享、展示或编辑。以下,我们将详细介绍一个pdf转成图片…

网工内推 | 外企、上市公司运维工程师,有软考中高项证书优先

01 优尼派特(苏州)物流有限公司 🔷招聘岗位:软件运维测试工程师 🔷任职要求: 1、负责公司自主研发的软件售后服务工作, 包括软件的安装, 调试, 升级,培训, 参数配置, 需求与Bug的处理; 2、负责数据库升级及…

Joplin Typora 粘贴图片 | 当使用Typora作为Joplin编辑器时,如何粘贴图片并上传到Joplin服务器,替换链接

一、背景 当我们使用Joplin时,上传图片时会自动上传到Joplin服务器并替换链接 但是Joplin的编辑器不好用,我更习惯用Typora来编辑, 然而Typora中上传的图片只能在本地,无法上传到Joplin服务器,在其他客户端也看不到图片…

快速理解 Node.js 版本差异:3 分钟指南

Node.js 是一个广泛使用的 JavaScript 运行时环境,允许开发者在服务器端运行 JavaScript 代码。随着技术的发展,Node.js 不断推出新版本,引入新特性和改进。了解不同版本之间的差异对于开发者来说至关重要。以下是一个快速指南,帮…

什么是相对路径?什么是绝对路径?打包时路径怎么搞?

简单点说: 绝对路径:绝对路径是一个完整的路径,从根目录开始一直到目标文件或目录的路径。通常我们直接使用"/ "代表从根目录开始的目录路径。它提供了文件或目录在文件系统中的确切位置,与当前工作目录无关。绝对路径…

华媒舍:15种媒体推广普遍不正确及解决方法

做为新闻媒体推广的重要一环,主流媒体推广在这个时代仍然发挥着重要的作用。由于种种原因,大家在主流媒体推广中常常做出各种各样不正确。为了能帮助广大推广工作人员们更好地进行主流媒体推广,本文可能详细介绍15种常见的错误,并…

修改版的VectorDBBench更好用

原版本VectorDBBench的几个问题 在这里就不介绍VectorDBBench是干什么的了,上官网即可。 1.并发数设置的太少 2.测试时长30秒太长 3.连接milvus无用户和密码框,这个是最大的问题 4.修改了一下其它参数 由于很多网友发私信问一些milvus的相关技术问…

史上最全,呕心沥血总结oracle推进SCN方法(八)

作者介绍:老苏,10余年DBA工作运维经验,擅长Oracle、MySQL、PG数据库运维(如安装迁移,性能优化、故障应急处理等) 公众号:老苏畅谈运维 欢迎关注本人公众号,更多精彩与您分享。前面介…

查分易老师怎么用?

老师们想知道怎么样创建一个查分易成绩查询系统吗?" 这是许多老师在学期结束时关心的问题。别担心,查分易小程序让查询发布工作变得省事又高效。 首先成绩Excel表格格式要设置正确。第一行必须是表头,包含学生的"姓名"、"…

qt(使用c++建立图形化界面)

建立QQ页面 MainWindow::MainWindow(QWidget *parent): QMainWindow(parent) {//1:设置窗口标题this->setWindowTitle("QQ");//2:重新设计窗口大小this->resize(540,420);//3:设置窗口小图标 添加QIcon头文件 注意路径中替换/this->setWindowIcon(QIcon(&q…