Java springbot项目qq机器人AI生成原神角色语音发送到QQ群,简单易懂,无需写python代码

效果如下
在这里插入图片描述

一、前言

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);
    }

}

到此结束,启动项目扫码登录,把机器人拉到群里就可以开始操作了~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值