效果如下
一、前言
1.环境配置
jdk 11 + Python 3.7 (无需敲python代码,我也不太会,只需跑起项目即可)
2.关联工具
Vist 项目 :
链接:https://pan.baidu.com/s/1hHHyAbYCnsZ8teuKn92Wmg
提取码:1234
音频转换工具:
链接:https://pan.baidu.com/s/1-bwq7lSTJiYdYM9WHAkdKQ
提取码:1234
3.Vits项目启动
vits文件夹内,start.bat启动项目,端口8023,初次启动肯定会报错很正常(解决比较麻烦可以看下面,搭建问题有空我会描述更清晰点)
4.Vist 项目环境搭建问题
环境:python 3.7
vist目录执行 cd monotonic_align python setup.py build_ext
–inplace
错误解决麻烦请看 https://gitee.com/sumght/vits-yunzai-plugin
项目启动可能需要比较麻烦,看人
最主要的可能是torch无法找到对应的版本系统安装不了, 我也忘记当初怎么慢慢的调好了
启动碰到需要引用的包报错直接 python i 插件名
二、Java 项目结构
注意lib需要放那些东西
三、代码编写
1、maven配置
java-mirai-qrcode-0.1.jar 请看我的前篇文章,Java mirai 扫码登录**
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.bot</groupId>
<artifactId>bot</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>JavaBot</name>
<description>Bot Spring Boot</description>
<properties>
<java.version>11</java.version>
<simbot.version>2.3.4</simbot.version>
<kotlin.version>1.7.10</kotlin.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web-services</artifactId>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.11.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.11.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.11.0</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.6</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.4</version>
</dependency>
<dependency>
<groupId>net.mamoe</groupId>
<artifactId>mirai-core-jvm</artifactId>
<version>2.15.0-M1</version>
</dependency>
<dependency>
<groupId>java-mirai-qrcode</groupId>
<artifactId>lame</artifactId>
<version>0.1</version>
<scope>system</scope>
<systemPath>${project.basedir}/lib/java-mirai-qrcode-0.1.jar</systemPath>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>11</source>
<target>11</target>
</configuration>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.yml</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.yml</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>lib</directory>
<targetPath>/BOOT-INF/lib/</targetPath>
<includes>
<include>**/*.jar</include>
</includes>
</resource>
</resources>
</build>
</project>
2、启动类
注意启动类不是常规的SpringApplication.run(BotApplication.class);启动
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
@SpringBootApplication
public class BotApplication {
public static void main(String[] args) {
SpringApplicationBuilder builder = new SpringApplicationBuilder(BotApplication.class);
builder.headless(false).web(WebApplicationType.NONE).run(args);
}
}
3、Application.yml的配置
server:
port: 2101
bot:
# 你的qq号,建议用小号
account: 123456948978
vits:
# 生成语音文件地址
file: 'C:\file\audio'
# 生成角色语音本地API
api: 'http://127.0.0.1:8023/create'
# vits工程文件地址(未启动项目时备用)
path: 'D:\vits\run_new.py'
logging:
level:
learning: debug
file:
name: log/app-user.log
4、工具类
AudioUtils 工具类,处理音频文件转换
package com.bot.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* 音频格式转换
*
* @author sqd233
*/
@Component
public class AudioUtils {
static Logger logger = LoggerFactory.getLogger(AudioUtils.class);
/**
* 工具地址
**/
public static String path = "lib\\silk_converter\\";
/**
* MP3/WAV转SILk格式
* @param filePath 例:D:\\file\\audio.mp3
* @param isSource isSource 是否清空原文件
* @return
*/
public static String toSilk(String filePath, boolean isSource){
Integer index = filePath.lastIndexOf("\\") + 1;
return toSilk(filePath.substring(0, index), filePath.substring(index, filePath.length()), isSource);
}
/**
* MP3/WAV转SILk格式
*
* @param path 文件路径 例:D:\\file\\
* @param name 文件名称 例:audio.mp3/audio.wav
* @param isSource 是否清空原文件
* @return silk文件路径
* @throws Exception
*/
public static String toSilk(String path, String name, boolean isSource) {
try {
// 判断后缀格式
String suffix = name.split("\\.")[1];
if (!suffix.toLowerCase().equals("mp3") && !suffix.toLowerCase().equals("wav")) {
throw new Exception("文件格式必须是mp3/wav");
}
String filePath = path + name;
File file = new File(filePath);
if (!file.exists()) {
throw new Exception("文件不存在!");
}
// 文件名时拼接
SimpleDateFormat ttime = new SimpleDateFormat("yyyyMMddhhMMSS");
String time = ttime.format(new Date());
// 导出的pcm格式路径
String pcmPath = path + "PCM_" + time + ".pcm";
// 先将mp3/wav转换成pcm格式
toPcm(filePath, pcmPath);
// 导出的silk格式路径
String silkPath = path + "SILK_" + time + ".silk";
// 转换成silk格式
pcmToSilk(pcmPath, silkPath);
// 删除pcm文件
File pcmFile = new File(pcmPath);
if (pcmFile.exists()) {
pcmFile.delete();
}
if (isSource) {
File audioFile = new File(filePath);
if (audioFile.exists()) {
audioFile.delete();
}
}
return silkPath;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 调用ffmpeg,wav转 pcm
*
* @param wavPath wav文件地址
* @param target 转后文件地址
*/
public static void wavToPcm (String wavPath, String target) {
// ffmpeg -i input.wav -f s16le -ar 44100 -acodec pcm_s16le output.raw
toPcm(wavPath, target);
}
/**
* 调用ffmpeg,mp3转 pcm
*
* @param mp3Path mp3文件地址
* @param target 转后文件地址
*/
public static void mp3ToPcm(String mp3Path, String target) {
//ffmpeg -y -i 源文件 -f s16le -ar 24000 -ac 1 转换后文件位置
toPcm(mp3Path, target);
}
/**
* mp3/wav 通用
* @param fpath
* @param target
*/
private static void toPcm(String fpath, String target) {
List<String> commend = new ArrayList<String>();
commend.add(path + "ffmpeg.exe");
commend.add("-y");
commend.add("-i");
commend.add(fpath);
commend.add("-f");
commend.add("s16le");
commend.add("-ar");
commend.add("24000");
commend.add("-ac");
commend.add("-2");
commend.add(target);
Process p = null;
try {
ProcessBuilder builder = new ProcessBuilder();
builder.command(commend);
p = builder.start();
p.waitFor();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (p != null) {
p.destroy();
}
}
}
/**
* silk_v3_encoder.exe,转成Silk格式
* @param pcmPath pcm 文件地址
* @param target 转换后的silk地址
* silk_v3_encoder.exe 路径
* pcm文件地址
* silk输出地址
* -Fs_API <Hz> : API sampling rate in Hz, default: 24000
* -Fs_maxInternal <Hz> : Maximum internal sampling rate in Hz, default: 24000
* -packetlength <ms> : Packet interval in ms, default: 20
* -rate <bps> : Target bitrate; default: 25000
* -loss <perc> : Uplink loss estimate, in percent (0-100); default: 0
* -complexity <comp> : Set complexity, 0: low, 1: medium, 2: high; default: 2
* -DTX <flag> : Enable DTX (0/1); default: 0
* -quiet : Print only some basic values
* -tencent : Compatible with QQ/Wechat
*/
public static void pcmToSilk(String pcmPath, String target) {
Process process = null;
try {
process = Runtime.getRuntime().exec("cmd /c start " + path + "silk_v3_encoder.exe " + pcmPath + " " + target + " -tencent");
process.waitFor();
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (process != null) {
process.destroy();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* mp3转amr(低质量qq语音)
* @param mp3Path MP3文件地址
* @param target 转换后文件地址
* return
*/
public static void mp3ToAmr(String mp3Path, String target) {
// 被转换文件地址
File source = new File(path);
try {
if (!source.exists()) {
throw new Exception("文件不存在!");
}
List<String> commend = new ArrayList<String>();
commend.add(path + "ffmpeg.exe");
commend.add("-y");
commend.add("-i");
commend.add(mp3Path);
commend.add("-ac");
commend.add("1");
commend.add("-ar");
commend.add("8000");
commend.add(target);
try {
ProcessBuilder builder = new ProcessBuilder();
builder.command(commend);
Process p = builder.start();
p.waitFor();
} catch (Exception e) {
e.printStackTrace();
}
} catch (Exception e) {
logger.error("mp3转amr异常-{}", e);
}
}
/**
* 一个音频转byte类型的方法
* @param filePath
* @return
*/
public static byte[] byteAudio(String filePath) {
try {
InputStream inStream = new FileInputStream(filePath);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = inStream.read(buffer)) > 0) {
baos.write(buffer, 0, bytesRead);
}
inStream.close();
baos.close();
return baos.toByteArray();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
BotGlobalUtils 储存全局变量Bot
import net.mamoe.mirai.Bot;
public class BotGlobalUtils {
private static Bot bot;
public static void setBot(Bot bot) {
BotGlobalUtils.bot = bot;
}
public static Bot getBot() {
return bot;
}
}
MessageUtils 处理qq群信息发送,其他信息的处理我没有加,比如发送图片,@,或者各种发送的混合的方法如需要可以在评论下留言
package com.bot.util;
import net.mamoe.mirai.contact.Group;
import net.mamoe.mirai.message.data.Audio;
import net.mamoe.mirai.utils.ExternalResource;
public class MessageUtils {
/**
* 发送群语音
* @param code
* @param bytes
*/
public static void sendGroupAudio (Long code, byte [] bytes) {
Audio audio;
ExternalResource resource = ExternalResource.create(bytes);
Group group = BotGlobalUtils.getBot().getGroup(code);
try {
audio = group.uploadAudio(resource);
group.sendMessage(audio);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (resource != null) {
resource.close();
}
} catch (Exception c) {
c.printStackTrace();
}
}
}
public static void sendGroupMsg(Long groupCode, String msg) {
Group group = BotGlobalUtils.getBot().getGroup(groupCode);
if (group != null) {
group.sendMessage(msg);
}
}
}
5、Vist 原神音频生成处理及发送
先创建config包,在下面创建RestTemplateConfig类用于实例化RestTemplate,方便我们调用接口
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate;
@Configuration public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
创建vist包,把常量、配置类和pojo请求类先建好
VitsConstant 常量类
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class VitsConstant {
/** 角色音色 **/
public static final List<String> CHARACTER = Arrays.stream(new String[]{
"派蒙", "凯亚", "安柏", "丽莎", "琴", "香菱", "枫原万叶", "迪卢克", "温迪", "可莉", "早柚", "托马",
"芭芭拉", "优菈", "云堇", "钟离", "魈", "凝光", "雷电将军", "北斗", "甘雨", "七七", "刻晴", "神里绫华", "戴因斯雷布",
"雷泽", "神里绫人", "罗莎莉亚", "阿贝多", "八重神子", "宵宫", "荒泷一斗", "九条裟罗", "夜兰", "珊瑚宫心海", "五郎",
"散兵", "女士", "达达利亚", "莫娜", "班尼特", "申鹤", "行秋", "烟绯", "久岐忍", "辛焱", "砂糖",
"胡桃", "重云", "菲谢尔", "诺艾尔", "迪奥娜", "鹿野院平藏"}).collect(Collectors.toList());
/** 中文数字 **/
public static final List<String> CHINESE_NUM = Arrays.stream((new String[]{"零", "一", "二", "三", "四", "五", "六", "七", "八", "九"})).collect(Collectors.toList());
}
VitsPojo vist项目请求参数组装
package com.bot.vits.pojo;
public class VitsPojo {
/**
* 音色
**/
private Integer character;
/**
* 文件路径 例: C:/file
**/
private String path;
/**
* 文件名称 例:example.wav
**/
private String fileName;
/**
* 噪声规模 (感情变化度 <= 1) 不传默认0.667
**/
private Float noise_scale;
/**
* dp噪声规模 (音速发音长度) 不传默认0.8
**/
private Float noise_scale_w;
/**
* 长度规模 (说话速度) 不传默认1
**/
private Float length_scale;
/**
* 文本
**/
private String text;
public VitsPojo() {
}
public VitsPojo(VitsPojo pojo, String text) {
this.character = pojo.getCharacter();
this.path = pojo.getPath();
this.fileName = pojo.getFileName();
this.noise_scale = pojo.getNoise_scale();
this.noise_scale_w = pojo.getNoise_scale_w();
this.text = text;
}
public VitsPojo(Integer character, String text) {
this.character = character;
this.text = text;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public Integer getCharacter() {
return character;
}
public void setCharacter(Integer character) {
this.character = character;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public Float getNoise_scale() {
return noise_scale;
}
public void setNoise_scale(Float noise_scale) {
this.noise_scale = noise_scale;
}
public Float getNoise_scale_w() {
return noise_scale_w;
}
public void setNoise_scale_w(Float noise_scale_w) {
this.noise_scale_w = noise_scale_w;
}
public Float getLength_scale() {
return length_scale;
}
public void setLength_scale(Float length_scale) {
this.length_scale = length_scale;
}
}
VitsProperties 配置类
package com.bot.vits.properties;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "vits")
public class VitsProperties {
/** 音频文件路径 **/
private String file;
/** 生成角色语音本地API **/
private String api;
/** vits工程文件地址(未启动项目时备用) **/
private String path;
public String getFile() {
return file;
}
public void setFile(String file) {
this.file = file;
}
public String getApi() {
return api;
}
public void setApi(String api) {
this.api = api;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
}
新建service.VistService类,用于语音生成处理
package com.bot.vits.service;
import com.bot.enums.InsEnums;
import com.bot.param.GroupParam;
import com.bot.util.AudioUtils;
import com.bot.util.MessageUtils;
import com.bot.vits.constant.VitsConstant;
import com.bot.vits.pojo.VitsPojo;
import com.bot.vits.properties.VitsProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Service
public class VitsService {
SimpleDateFormat ttime = new SimpleDateFormat("yyyyMMddhhMMSS");
static Logger logger = LoggerFactory.getLogger(VitsService.class);
@Autowired
private VitsProperties vitsProperties;
@Autowired
private RestTemplate restTemplate;
/**
* 根据名称获取下标(vist项目音色是根据下表来的)
* @param name 角色名称
* @return
*/
public static Integer characterIndex (String name) {
for (int i = 0; i < VitsConstant.CHARACTER.size(); i++) {
if (VitsConstant.CHARACTER.get(i).equals(name)) {
return i;
}
}
return null;
}
/**
* 阿拉伯数字转换中文数字
* @param text
* @return
*/
public static String chineseNumber (String text) {
String speak = "";
for (int i = 0; i < text.length(); i++) {
String c = String.valueOf(text.charAt(i));
if (c.matches("\\d+")) {
c = VitsConstant.CHINESE_NUM.get(Integer.parseInt(c));
}
speak = speak + c;
}
return speak;
}
/**
* 模仿语音并发送
* @param groupParam
*/
public void imitate (GroupParam groupParam) {
// 正则获取模仿角色名称及需要说的话
Pattern pattern = Pattern.compile(InsEnums.IMITATE.getRegex());
Matcher matcher = pattern.matcher(groupParam.getSerializeMessage());
if (matcher.find()) {
// 音色
Integer character = characterIndex(matcher.group(1));
if (character != null) {
// 阿拉伯数据转义
String message = chineseNumber(matcher.group(2));
// 获取音频路径
String audioUrl = reqVits(new VitsPojo(character, message));
if (StringUtils.isEmpty(audioUrl)) {
MessageUtils.sendGroupMsg(groupParam.getGroupCode(), "生成语音异常");
return;
}
// 生成发送语音格式
String silkPath = AudioUtils.toSilk(audioUrl, true);
if (silkPath == null) {
MessageUtils.sendGroupMsg(groupParam.getGroupCode(), "生成语音异常");
return;
}
MessageUtils.sendGroupAudio(groupParam.getGroupCode(), AudioUtils.byteAudio(silkPath));
// 删除silk文件
File silkFile = new File(silkPath);
if (silkFile.exists()) {
silkFile.delete();
}
} else {
MessageUtils.sendGroupMsg(groupParam.getGroupCode(), "模仿角色不存在");
}
}
}
/**
* 创建生成语音文件
* @param vitsPojo
* @return 返回文件路径
*/
public String reqVits (VitsPojo vitsPojo) {
// 设置噪声规模
vitsPojo.setNoise_scale(0.667F);
// 设置音速发音长度
vitsPojo.setNoise_scale_w(0.8F);
// 设置说话速度
vitsPojo.setLength_scale(1F);
// 保存文件路径
vitsPojo.setFileName(ttime.format(new Date()) + ".wav");
vitsPojo.setPath(vitsProperties.getFile());
// 阿拉伯数字转中文数字
try {
// 创建文件夹
File file = new File(vitsProperties.getFile());
if (!file.exists()) {
file.createNewFile();
}
// 调用生成接口 (假如接口调不通会尝试用cmd来生成语音文件)
restTemplate.getForObject(vitsProperties.getApi() + buildParam(vitsPojo, true), String.class);
} catch (Exception e) {
logger.error("API生成原神角色语音异常", e);
// 接口调不通会尝试用cmd指令来生成语音文件
Process process = null;
try {
if (vitsProperties.getPath() == null) {
throw new Exception("没有配置vits工程文件地址-指令生成Ai语音失败");
}
String path = vitsProperties.getPath().substring(0, vitsProperties.getPath().lastIndexOf("\\") + 1);
String pyFile =vitsProperties.getPath().substring(vitsProperties.getPath().lastIndexOf("\\") + 1, vitsProperties.getPath().length());
process = Runtime.getRuntime().exec("python " + pyFile + buildParam(vitsPojo, false),null,
new File(path));
// 将结果循环打印输出
String cmd;
// 将命令的结果以流的方式读入
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
while ((cmd = bufferedReader.readLine()) != null) {
System.out.println(cmd);
}
process.waitFor(10, TimeUnit.SECONDS);
} catch (Exception i) {
logger.error("指令生成原神角色语音异常", e);
} finally {
if (process != null) {
process.destroy();
}
}
}
return vitsProperties.getFile() + "\\" + vitsPojo.getFileName();
}
/**
* 参数处理
* @param vitsPojo 对象
* @param isUrl 是否外部接口
* @return
*/
public static String buildParam (VitsPojo vitsPojo, boolean isUrl) throws IllegalAccessException {
String request = "";
for (Field field : vitsPojo.getClass().getDeclaredFields()) {
// 可读
field.setAccessible(true);
if (!StringUtils.isEmpty(field.get(vitsPojo))) {
if (isUrl) {
String param = field.getName() + "=" + field.get(vitsPojo);
request = request + ( ("".equals(request))? "?" + param : "&" + param);
} else {
request = request + " --" + field.getName() + "=" + field.get(vitsPojo);
}
}
}
System.out.println("原神语音生成参数打印:" + request);
return request;
}
}
6、QQ群指令校验和发送
创建指令处理的枚举类,enums.InsEnums
import java.util.regex.Pattern;
public enum InsEnums {
IMITATE(1, "^模仿(.*)说(.*)"),
ALL_ROLE(2, "#角色大全")
;
/** 指令Code **/
private Integer code;
/** 正则表达式 **/
private String regex;
/**
* 校验指令
* @param serializeMessage
* @return
*/
public boolean validate (String serializeMessage) {
Pattern pattern = Pattern.compile(regex);
if (pattern.matcher(serializeMessage).matches()) {
return true;
}
return false;
}
InsEnums (Integer code, String regex) {
this.code = code;
this.regex = regex;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getRegex() {
return regex;
}
public void setRegex(String regex) {
this.regex = regex;
}
}
我们再创建adapter,在下面创建群聊处理类GroupAdapter
import com.bot.enums.InsEnums;
import com.bot.param.GroupParam;
import com.bot.util.MessageUtils;
import com.bot.vits.constant.VitsConstant;
import com.bot.vits.service.VitsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class GroupAdapter {
@Autowired
private VitsService vitsService;
/**
* 处理群聊信息
* @param groupParam
*/
public void handle (GroupParam groupParam) {
if (InsEnums.IMITATE.validate(groupParam.getSerializeMessage())) {
// 这是发送例’模仿可莉说你好啊,旅行者‘时机器人的回复
vitsService.imitate(groupParam);
} else if (InsEnums.ALL_ROLE.validate(groupParam.getSerializeMessage())) {
// 这是发送’#角色大全‘时机器人的回复
MessageUtils.sendGroupMsg(groupParam.getGroupCode(), String.join(",", VitsConstant.CHARACTER));
}
}
}
7、QQ登录及自动回复信息(调用GroupAdapter 类)
创建properties包,在properties包下创建BotProperties
@ConfigurationProperties(prefix = “bot”) 的bot指的是application.yml下的bot
@ConfigurationProperties(prefix = "bot")
public class BotProperties {
/** qq号 **/
private Long account;
public Long getAccount() {
return account;
}
public void setAccount(Long account) {
this.account = account;
}
}
创建param包,在param包下创建GroupParam类,用于保存QQ群发送的信息或群信息
import net.mamoe.mirai.contact.Group; import net.mamoe.mirai.contact.Member; import net.mamoe.mirai.message.data.MessageChain;
/** * qq群信息参数 */
public class GroupParam {
/** QQ群号 **/
private Long groupCode;
/** QQ群称 **/
private String groupName;
/** 发送的消息 **/
private String serializeMessage;
/** 信息发送人QQ号 **/
private Long personCode;
/** 信息发送人QQ昵称 **/
private String personName;
public GroupParam () {}
public GroupParam (MessageChain message, Group group, Member sender) {
this.groupCode = group.getId();
this.groupName = group.getName();
this.serializeMessage = message.serializeToMiraiCode();
this.personCode = sender.getId();
this.personName = sender.getNick();
}
public Long getGroupCode() {
return groupCode;
}
public void setGroupCode(Long groupCode) {
this.groupCode = groupCode;
}
public String getGroupName() {
return groupName;
}
public void setGroupName(String groupName) {
this.groupName = groupName;
}
public String getSerializeMessage() {
return serializeMessage;
}
public void setSerializeMessage(String serializeMessage) {
this.serializeMessage = serializeMessage;
}
public Long getPersonCode() {
return personCode;
}
public void setPersonCode(Long personCode) {
this.personCode = personCode;
}
public String getPersonName() {
return personName;
}
public void setPersonName(String personName) {
this.personName = personName;
} }
创建config包,在config包下创建BotAutoLogin类
GroupAdapter 就是处理群消息的方法了
import com.bot.adapter.GroupAdapter;
import com.bot.param.GroupParam;
import com.bot.properties.BotProperties;
import com.bot.util.BotGlobalUtils;
import com.bot.vits.properties.VitsProperties;
import com.qrcode.QRCodeBot;
import kotlin.coroutines.EmptyCoroutineContext;
import net.mamoe.mirai.Bot;
import net.mamoe.mirai.event.ConcurrencyKind;
import net.mamoe.mirai.event.EventPriority;
import net.mamoe.mirai.event.events.GroupMessageEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;x
@Configuration
@EnableConfigurationProperties({BotProperties.class, VitsProperties.class})
public class BotAutoLogin {
@Autowired
private BotProperties botProperties;
@Autowired
private GroupAdapter groupAdapter;
@Bean
public void login () {
// 自动登录
Bot bot = QRCodeBot.getQRCodeBot(botProperties.getAccount());
bot.login();
// 群聊信息
bot.getEventChannel().subscribeAlways(GroupMessageEvent.class, EmptyCoroutineContext.INSTANCE, ConcurrencyKind.CONCURRENT, EventPriority.NORMAL, event -> {
// 组装群聊参数
GroupParam param = new GroupParam(event.getMessage(), event.getGroup(), event.getSender());
groupAdapter.handle(param);
});
BotGlobalUtils.setBot(bot);
}
}