文章目录
关于本篇教程
我再去年的时候写过一个simbot教程 不过我对那个教程不是很满意
于是就有了本篇教程
本篇教程使用的是simbot3.0版本
相比于上一篇教程新增了一些功能
如果有问题请留言
使用技术
- springboot
- simbot
- mysql
- redis(可选)
- mybaitsplus
- Jsoup(写爬虫功能)
关于simbot
simbot是一款极易上手的qq机器人框架
关于这款框架在我的上一篇文章中有所介绍
框架的官网
链接: simbot
建议去看作者写的教程
所需依赖
里面我是用的有redis 不需要的可以去掉
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>QQbot</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<java.version>1.8</java.version>
<simbot.version>3.0.0.preview.8.1</simbot.version>
<simbot-component-tcg.version>3.0.0.0.preview.3.1</simbot-component-tcg.version>
<simbot-component-mirai.version>3.0.0.0.preview.2.2</simbot-component-mirai.version>
</properties>
<dependencies>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.15</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.8.0</version>
</dependency>
<dependency>
<groupId>net.sf.json-lib</groupId>
<artifactId>json-lib</artifactId>
<version>2.1</version>
<classifier>jdk13</classifier>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- simbot-spring-boot-starter 库 -->
<dependency>
<groupId>love.forte.simbot.boot</groupId>
<artifactId>simboot-core-spring-boot-starter</artifactId>
<version>${simbot.version}</version>
</dependency>
<!-- 腾讯频道组件 -->
<dependency>
<groupId>love.forte.simbot.component</groupId>
<artifactId>simbot-component-tencent-guild-boot</artifactId>
<version>${simbot-component-tcg.version}</version>
</dependency>
<!-- mirai组件 -->
<dependency>
<groupId>love.forte.simbot.component</groupId>
<artifactId>simbot-component-mirai-boot</artifactId>
<version>${simbot-component-mirai.version}</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.2</version>
</dependency>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.15.2</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.2.0</version>
</dependency>
<!-- spring2.X集成redis所需common-pool2-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--mybatis-plus启动器-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
<!--lombok用于简化实体类开发-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.1</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.31</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<!--使用mirai组件-springboot-starter -->
<dependency>
<groupId>love.forte.simple-robot</groupId>
<artifactId>component-mirai-spring-boot-starter</artifactId>
<version>2.3.4</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
我的项目源码
源码地址
如果有看不明白的建议直接去我的代码里面看
项目结构
listener里面存放的是一些示范代码 如下所示
controller里面基本上都是群聊的一些功能
目前已实现的功能有
可爱的猫猫码
猫猫码是simbot中使用的发送特殊信息的一种格式
关于猫猫码的详细信息可以去文章开头的作者官方文档里面查看
我简单的对一些常用功能进行了封装(主要是我没找到怎么快捷使用猫猫码的方法)
package com.mybatisplus.utils;
public class MakeNeko {
//发送图片
public static String MakePicture(String url){
return "[CAT:image,file=" +url + "]";
}
//发送at
public static String MakeAt(String qq){
return "[CAT:at,code="+qq+"]";
}
//Voice 语音消息
//[CAT:voice,id=af24eb245,file=/img/b.mp4,url=http://voice/b.mp4]
public static String MakeVoice(String url){
return "[CAT:voice,url="+url+"]";
}
//File 文件消息
//[CAT:file,id=af24eb245,file=/img/b.mp4,url=http://voice/b.mp4]
public static String MakeFile (String url){
return "[CAT:file,url="+url+"]";
}
//Share 分享
//[CAT:share,title=标题,content=简述,url=http://forte.love,image=http://img/a.jpg]
public static String MakeShare (String title,String content, String url){
return "[CAT:share,title="+title+",content="+content+",url="+url+"]";
}
// nudge 戳一戳
public static String Makepoke (String qq){
return "[CAT:nudge,code="+qq+"]";
}
}
我们发送的猫猫码会被框架给解析成所有需要发送的图片或者其他东西
群监听及发送信息
示例:
关于groupmsg
@OnGroup
public void onGroupMsg(GroupMsg groupMsg) {
// 打印此次消息中的 纯文本消息内容。
// 纯文本消息中,不会包含任何特殊消息(例如图片、表情等)。
System.out.println(groupMsg.getText());
// 打印此次消息中的 消息内容。
// 消息内容会包含所有的消息内容,也包括特殊消息。特殊消息使用CAT码进行表示。
// 需要注意的是,绝大多数情况下,getMsg() 的效率低于甚至远低于 getText()
System.out.println(groupMsg.getMsg());
// 获取此次消息中的 消息主体。
// messageContent代表消息主体,其中通过可以获得 msg, 以及特殊消息列表。
// 特殊消息列表为 List<Neko>, 其中,Neko是CAT码的封装类型。
MessageContent msgContent = groupMsg.getMsgContent();
// 打印消息主体
System.out.println(msgContent);
// 打印消息主体中的所有图片的链接(如果有的话)
List<Neko> imageCats = msgContent.getCats("image");
System.out.println("img counts: " + imageCats.size());
for (Neko image : imageCats) {
System.out.println("Img url: " + image.get("url"));
}
// 获取发消息的人。
GroupAccountInfo accountInfo = groupMsg.getAccountInfo();
// 打印发消息者的账号与昵称。
System.out.println(accountInfo.getAccountCode());
System.out.println(accountInfo.getAccountNickname());
// 获取群信息
GroupInfo groupInfo = groupMsg.getGroupInfo();
// 打印群号与名称
System.out.println(groupInfo.getGroupCode());
System.out.println(groupInfo.getGroupName());
}
发送图片,at,转发等特殊信息请使用可爱的猫猫码
示例:
只需要对url进行一下拼装就可以发送图片了
at之类的就不在演示了
发送 聊天记录
有的时候要发送的信息比较多
如果一次性发送就会导致刷屏所以需要以转发聊天记录的方式发送
如
@Autowired
private MessageContentBuilderFactory factory;
@OnGroup
@Filter(value="nana模块管理",trim=true,matchType = MatchType.CONTAINS)
public void helpmk(GroupMsg groupMsg, Sender sender) {
if (!(factory instanceof MiraiMessageContentBuilderFactory)) {
throw new RuntimeException("不支持mirai组件");
}
MiraiMessageContentBuilder builder = ((MiraiMessageContentBuilderFactory) factory).getMessageContentBuilder();
builder.forwardMessage(forwardBuilder -> {
forwardBuilder.add(groupMsg.getBotInfo(),"nana启动学习模块\n" +
"nana关闭学习模块\n " +
"nana关闭回复模块\n " +
"nana启动回复模块\n" +
"nana关闭定时模块\n" +
"nana启动定时模块\n" +
"nana关闭天气模块\n" +
"nana启动天气模块 (超级管理员使用)\n"
+"nana添加群定时\n"
+"nana取消群定时\n"
+"nana添加群回复\n"
+"nana取消群回复\n"
+"nana开启聊天模块 \n"
+"nana关闭聊天模块\n"
+"nana添加群今天吃什么\n"
+"nana取消群今天吃什么"
);
});
final MiraiMessageContent messageContent = builder.build();
// 发送消息
sender.sendGroupMsg(groupMsg, messageContent);
}
当然一次也可以发送多条信息例如
持续会话
示例:
关于对某个人的持续会话建议去看框架作者所写的教程
https://simbot.forte.love/docs/basic/continuous-session
建议直接去看我的代码
private static final String AREA1_GROUP = "Area1";
private static final String AREA2_GROUP = "Area2";
private static final String AREA3_GROUP = "Area3";
private static final String AREA4_GROUP = "Area4";
@OnGroup
@Filter(value = "nana删除", matchType = MatchType.STARTS_WITH)
@ListenBreak
public void testConversationDomain(GroupMsg msg, ListenerContext context, Sender sender) {
if( Delete_flag ) {
ContinuousSessionScopeContext sessionContext = (ContinuousSessionScopeContext) context.getContext(ListenerContext.Scope.CONTINUOUS_SESSION);
assert sessionContext != null;
String accountCode = msg.getAccountInfo().getAccountCode();
String groupCode = msg.getGroupInfo().getGroupCode();
// 通过账号拼接一个此人在此群中的唯一key
String key = accountCode + "-" + groupCode;
hashMap.put(key,new Message());
// 发送提示信息
sender.sendGroupMsg(msg, "请问您要删除的关键词是什么?");
SessionCallback<String> callback = SessionCallback.<String>builder().onResume(text -> {
sender.sendGroupMsg(msg, text);
}).onError(e -> {
// 这里是第一个会话,此处通过 onError 来处理出现了异常的情况,例如超时
if (e instanceof TimeoutException) {
// 超时事件是 waiting的第三个参数,可以省略,默认下,超时时间为 1分钟
// 这个默认的超时时间可以通过配置 simbot.core.continuousSession.defaultTimeout 进行指定。如果小于等于0,则没有超时,但是不推荐不设置超时。
System.out.println("onError: 超时啦: " + e);
sender.sendGroupMsg(msg, "超时啦");
} else {
System.out.println("onError: 出错啦: " + e);
sender.sendGroupMsg(msg, "出错啦");
}
}).onCancel(e -> {
// 这里是第一个会话,此处通过 onCancel 来处理会话被手动关闭、超时关闭的情况的处理,有些时候会与 orError 同时被触发(例如超时的时候)
System.out.println("onCancel 关闭啦: " + e);
}).build(); // build 构建
// 这里开始等待第一个会话。
sessionContext.waiting(AREA1_GROUP, key, callback);
}else{
sender.sendGroupMsg(msg, "删除模块没有开启");
}
}
/**
* 针对上述第一个会话的监听。
* 通过 @OnlySession来限制,当且仅当由 AREA1_GROUP 这个组的会话的时候,此监听函数才会生效。
*/
@OnGroup
@OnlySession(group = AREA1_GROUP)
public void listenArea1(GroupMsg msg, ListenerContext context){
// 得到session上下文。
ContinuousSessionScopeContext session = (ContinuousSessionScopeContext) context.getContext(ListenerContext.Scope.CONTINUOUS_SESSION);
assert session != null;
String accountCode = msg.getAccountInfo().getAccountCode();
String groupCode = msg.getGroupInfo().getGroupCode();
// 通过账号拼接一个此人在此群中的唯一key
String key = accountCode + "-" + groupCode;
String text = msg.getMsg();
Message message1 = hashMap.get(key);
if(message1!=null){
// message1 = new Message();
message1.setKeymessage(text);
List<String> strings = service.Get_QQ_by_key(text); //根据要删除的内容返回这个key所有的qq号
int size = strings.size();
if(size!=0) {
for (String string : strings) {
//如果这个qq号和触发这个函数的QQ号相同并且权限满足
// if (string.contains(accountCode) || Integer.parseInt(adminService.get_Admin_permission(accountCode).getPermission()) < 2) {
if(true){
int i = 0;
int b = 0;
i = service.DeleteMessage(text);
// b = service.DeleteMessage_By_QQ(text, accountCode);
if (b+i == 0) {
session.push(AREA1_GROUP, key, "删除失败");
} else {
session.push(AREA1_GROUP, key, "删除成功");
}
break;
} else {
session.push(AREA1_GROUP, key, "您的权限不足 只能管理员或者本人才能删除");
}
}
hashMap.remove(key);
}else{
session.push(AREA1_GROUP, key, "在数据库中未检索到删除的key");
hashMap.remove(key);
}
}
}
/**
* 针对上述第二个会话的监听。
* 通过 @OnlySession来限制,当且仅当由 AREA2_GROUP 这个组的会话的时候,此监听函数才会生效。
*/
@OnGroup
@OnlySession(group = AREA2_GROUP)
public void listenArea2(GroupMsg msg, ListenerContext context){
// 得到session上下文。
ContinuousSessionScopeContext session = (ContinuousSessionScopeContext) context.getContext(ListenerContext.Scope.CONTINUOUS_SESSION);
assert session != null;
String accountCode = msg.getAccountInfo().getAccountCode();
String groupCode = msg.getGroupInfo().getGroupCode();
// 通过账号拼接一个此人在此群中的唯一key
String key = accountCode + "-" + groupCode;
String text = msg.getText();
MessageContent msgContent = msg.getMsgContent();
List<Neko> image = msgContent.getCats("image");
String url="";
if(image.size()!=0){
Neko neko = image.get(0);
url = neko.get("url");
message.setUrl(url);
}
if(url.equals("")){
try{
String value = text;
message.setValuemessage(value);
}catch(ArrayIndexOutOfBoundsException e){
if(message.getValuemessage().equals("")&&message.getUrl().equals("")){
session.push(AREA2_GROUP, key, "0");
message=null;
}
}
}
message.setValuemessage(text);
message.setQq(accountCode);
int study = service.InsertMessage(message);
session.push(AREA2_GROUP, key, study);
message=null;
}
关于机器人启动的问题请参照我的上一篇机器人教程
https://blog.csdn.net/qq_47431361/article/details/123312806