欢迎来到 安卓源码空间!
安卓源码空间

         Java springbot项目qq机器人AI生成原神角色语音发送到QQ群



效果如下


在这里插入图片描述


一、前言


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();  
 // 群聊信息 b 
 ot.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); } }  
 

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


copyright@ 2020-2028  安卓源码空间网版权所有   

备案号:豫ICP备2023034476号-1号