欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

[QQAI机器人]-接入腾讯AI接口

程序员文章站 2024-02-13 23:09:28
...

简述

腾讯AI的接口有什么功能

腾讯Ai有一些特别有用的智能接口
详细的如下图
[QQAI机器人]-接入腾讯AI接口

为什么使用腾讯AI

因为 免费 ! ! !

点击进入 腾讯AI官网

开发环境

环境配置

JCQ的环境配置这里就不说了,这里直说怎么去实现

[QQAI机器人]-使用Java开发环境搭建
配置好完成后,使用里面的 Maven Demo

在就这里需要用到jsoup,如果不会请参考

[QQAI机器人]-爬虫机器人

导包

jcq-coolq:JCQ开发工具包
jsoup:爬虫包
jackson : json解析

<dependency>
    <groupId>com.sobte.cqp</groupId>
    <artifactId>jcq-coolq</artifactId>
    <version>1.2.7</version>
</dependency>

<dependency>
    <groupId>org.jsoup</groupId>
    <artifactId>jsoup</artifactId>
    <version>1.11.3</version>
</dependency>

<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
   	<artifactId>jackson-core</artifactId>
   	<version>2.9.8</version>
</dependency>
<dependency>
   	<groupId>com.fasterxml.jackson.core</groupId>
  	<artifactId>jackson-databind</artifactId>
  	<version>2.9.8</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-annotations</artifactId>
    <version>2.9.8</version>
</dependency>

准备过程

  1. 首先,你需要注册腾讯AI
  2. 然后创建一个应用
  3. 记下自己的AppID和appkey
    [QQAI机器人]-接入腾讯AI接口

功能实现

本次代码的架构图
[QQAI机器人]-接入腾讯AI接口
源码内部有大量注释,希望认真观看


接口调用

接口调用我们使用post或者get都行,这里就拿post了

这里是一个流程类:
从收到好友的消息 到 给好友回复的一个流程

TencentImpl.java
package top.xsinfo;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.jsoup.Connection;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import top.xsinfo.entity.RobotRespond;

import java.io.IOException;

public class TencentImpl {
    //解析json用到的工具类
    private static ObjectMapper mapper = new ObjectMapper();


    /**
     * 获取连接
     * 设置消息头啥的,也是公共部分,所以这里单独弄了个方法
     * @param url 要访问的网址
     * @return 返回连接对象
     */
    private synchronized static Connection getConnect(String url){
        Connection connect = Jsoup.connect(url);

        connect.ignoreContentType(true)
                .userAgent("Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.2.15)")
                .timeout(8000);

        return connect;
    }

    /**
     * 闲聊程序
     * @param fromQQ 消息来源(对方QQ号)
     * @param msg 消息内容
     */
    public synchronized static void talk(long fromQQ,String msg){
        Document doc ;
        try {
            //这里使用的聊天接口,详细的访问地址请看官方文档
            Connection connect = getConnect("https://api.ai.qq.com/fcgi-bin/nlp/nlp_textchat");

            //这里是添加post提交数据(等下接口鉴权部分详细介绍)
            connect.data(TencentAPI.xian_liao(fromQQ,msg));

            //提交数据 并且得到一个json的返回结果
            doc = connect.post();
            String data = doc.body().html();

            //将返回的json通过解析解析成一个对象
            RobotRespond robotRespond = mapper.readValue(data, RobotRespond.class);

            //取出响应对象的属性
            String  ans = robotRespond.getData("answer");

            //酷Q接口函数,发送QQ消息
            AppDemo.CQ.sendPrivateMsg(fromQQ, ans);

        } catch (IOException e) {
            AppDemo.CQ.logError("TencentImpl_talk",e.getMessage());
        }

    }

    /**
     * 翻译功能
     * @param fromQQ 要发送给谁
     * @param msg 要翻译的内容
     * @param source 被翻译的字符串是什么语言
     * @param target 翻译成什么语言
     */
    public synchronized static void fanyi(long fromQQ,String msg,String source,String target){
        Document doc;

        try {
            //使用翻译接口
            Connection connect = getConnect("https://api.ai.qq.com/fcgi-bin/nlp/nlp_texttranslate");
            
            //获取要提交的数据
            connect.data(TencentAPI.fan_yi(msg,source,target));
            
            //使用post提交,并且捕获json字符串
            doc = connect.post();
            String data = doc.body().html();
            
            //将json字符串转换成一个对象
            RobotRespond robotRespond = mapper.readValue(data, RobotRespond.class);

			//获取答案
            String  ans = robotRespond.getData("target_text");
			
			//发送私聊消息
            AppDemo.CQ.sendPrivateMsg(fromQQ, ans);

        } catch (IOException e) {
            AppDemo.CQ.logError("TencentImpl_fanyi",e.getMessage());
            e.printStackTrace();
        }

    }
}


获取结果

json 转换成 对象 使用的jackson进行解析

实际封装的类如下

RobotRespond.java
package top.xsinfo.entity;


import java.io.IOException;
import java.util.Map;


public class RobotRespond {
//    使用post提交后 返回一个json,主要有以下3个部分
//
//    参数名称	是否必选	 数据类型	    描述
//---------------------------------------------------------
//    ret	      是	  int	    返回码; 0表示成功,非0表示出错
//    msg	      是	  string	返回信息;ret非0时表示出错时错误原因
//    data	      是	  object(Map)	返回数据;ret为0时有意义(详细的内容请看具体功能的说明文档)

    public int ret;
    public String msg;
    public Map<String,String> data;

	//获取数据
    public String getData(String key)throws IOException{
    	//如果状态是成功状态 则返回结果
        if (ret==0) return data.get(key);
        //否则抛出异常
        System.out.println(this);
        throw new IOException(ret+":"+msg);
    }


    @Override
    public String toString() {
        return "RobotRespond{" +
                "ret=" + ret +
                ", msg='" + msg + '\'' +
                ", data=" + data +
                '}';
    }
}


接口鉴权

腾讯AI开放平台HTTP API使用签名机制对每个接口请求进行权限校验,对于校验不通过的请求,API将拒绝处理,并返回鉴权失败错误。

接口调用者在调用API时必须带上接口请求签名,其中签名信息由接口请求参数和应用**根据本文提供的签名算法生成。

计算步骤

  1. 用于计算签名的参数在不同接口之间会有差异,但算法过程固定如下4个步骤。
  2. 将<key, value>请求参数对按key进行字典升序排序,得到有序的参数对列表N
  3. 将列表N中的参数对按URL键值对的格式拼接成字符串,得到字符串T(如:key1=value1&key2=value2),URL键值拼接过程value部分需要URL编码,URL编码算法用大写字母,例如%E8,而不是小写%e8
  4. 将应用**以app_key为键名,组成URL键值拼接到字符串T末尾,得到字符串S(如:key1=value1&key2=value2&app_key=**)
  5. 对字符串S进行MD5运算,将得到的MD5值所有字符转换成大写,得到接口请求签名

注意事项

  • 不同接口要求的参数对不一样,计算签名使用的参数对也不一样
  • 参数名区分大小写,参数值为空不参与签名
  • URL键值拼接过程value部分需要URL编码
  • 签名有效期5分钟,需要请求接口时刻实时计算签名信息
  • 更多注意事项,请查看常见问题

点击进入官方的说明文档

功能实现

备注十分详细,最好还是仔细阅读

最好还是自己在练习一下,写一写这个算法

大致的流程如下:
获取公共部分的map->添加自己特有数据->计算接口鉴权字符串->返回提交数据

TencentAPI.java
package top.xsinfo;

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;

public class TencentAPI {

    private final static String app_key = "你的app_key";
    private final static String app_id = "你的app_id";

    /**
     * 这里分开写是因为提交数据都有公共的部分
     * 例如 appid 时间戳 还有随机字符串
     * 不管使用什么api,但这些数据都会有
     * 所以封装成一个方法
     * @return 返回一个提交数据雏形
     */
    private synchronized static Map<String,String> getMap(){

        Map<String,String> map = new HashMap<String, String>();
        map.put("app_id",app_id);
        String s = String.valueOf(System.currentTimeMillis());
        s = s.substring(0, s.length() - 3);
        map.put("time_stamp",s);
        map.put("nonce_str",TencentAPI.getRandomString(10));

        return map;
    }


    /**
     * 封装的闲聊提交数据
     * 这些计算后的数据用于post提交
     * 详细数据请看api接口文档
     *
     * app_id	    是	int	    应用标识(AppId)
     * time_stamp	是	int	    请求时间戳(秒级)
     * nonce_str	是	string	随机字符串
     * sign	        是	string	签名信息,详见接口鉴权
     * session	    是	string	会话标识(应用内唯一)
     * question	    是	string	用户输入的聊天内容
     *
     * @param fromQQ 要给谁聊天
     * @param msg 回话内容
     * @return 返回提交数据
     */
    public static Map<String,String> xian_liao(long fromQQ,String msg){

        //获取一个公共部分的map对象
        Map<String,String> map = getMap();

        //配置本api的一些特有的属性
        map.put("session",String.valueOf(fromQQ));
        map.put("question",msg);

        //进行接口鉴权计算后的字符串
        String sign = sign(map);

        //将sign也添加到提交数据
        map.put("sign",sign);
        return map;
    }

    /**
     * 封装的翻译数据
     * 这些计算后的数据用于post提交
     * 详细数据请看api接口文档
     * 
     * app_id	    是	int	    应用标识(AppId)
     * time_stamp	是	int	    请求时间戳(秒级)
     * nonce_str	是	string	随机字符串
     * sign	        是	string	签名信息,详见接口鉴权
     * text	        是	string	待翻译文本
     * source	    是	string	两位小写字母	zh	源语言缩写,详细见接口文档
     * target	    是	string	两位小写字母	en	目标语言缩写,详细见接口文档
     *
     * @param msg 翻译后的消息
     * @return 返回提交数据
     */
    public static Map<String,String> fan_yi(String msg,String source,String target){
        //获取公共部分的提交数据map对象
        Map<String,String> map = getMap();

        //对提交数据添加自己特有的参数
        map.put("text",msg);
        map.put("source",source);
        map.put("target",target);

        //计算接口鉴权的字符串
        String sign = sign(map);

        //将接口鉴权的字符串也添加进来
        map.put("sign",sign);
        return map;
    }


    /**
     * 接口授权验证
     * @param map 要被计算的提交数据
     * @return 返回计算后的字符串
     */
    public synchronized static String sign(Map<String,String> map){
        //字符串频繁进行修改,所以用StringBuilder
        StringBuilder sb = new StringBuilder();

        //第一个提交参数是没有"&"字符的,用于判断是不是第一个参数
        boolean first = true;

        //将map的key导出为一个set集合,然后在转变成一个list集合进行排序
        List<String> strings = new ArrayList<String>(map.keySet());
        Collections.sort(strings);

        //这样就达到了字典升序的目的
        //然后我们在进行遍历,组合
        for (String string : strings) {
            //第一个参数不加"&"符号
            if (!first)sb.append("&");
            first=false;

            //一点点转变成一个类似于url的数据
            sb.append(string);
            sb.append("=");
            sb.append(TencentAPI.getURLEncoderString(map.get(string)));//这里使用到了url编码
        }

        //最后添加自己的appkey
        sb.append("&app_key=").append(app_key);

        //然后计算md5,计算完之后全部大写 返回
        return TencentAPI.MD5(sb.toString()).toUpperCase();
    }


    /**
     * URL编码
     * @param str 要被编码的字符串
     * @return 返回编码后的字符串
     */
    public static String getURLEncoderString(String str) {
        String result = "";
        if (null == str) {
            return "";
        }
        try {
            result = java.net.URLEncoder.encode(str, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return result;
    }



    /**
     * length用户要求产生字符串的长度
     * @param length 字符串的长度
     * @return 一个随机的字符串
     */
    public static String getRandomString(int length){
        String str="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        Random random=new Random();
        StringBuffer sb=new StringBuffer();
        for(int i=0;i<length;i++){
            int number=random.nextInt(62);
            sb.append(str.charAt(number));
        }
        return sb.toString();
    }


    /**
     * 计算字符串的MD5
     * @param input 要被计算的字符串
     * @return 返回计算后的MD5
     */
    public static String MD5(String input) {
        if(input == null || input.length() == 0) {
            return null;
        }
        try {
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            md5.update(input.getBytes());
            byte[] byteArray = md5.digest();

            StringBuilder sb = new StringBuilder();
            for (byte b : byteArray) {
                // 一个byte格式化成两位的16进制,不足两位高位补零
                sb.append(String.format("%02x", b));
            }
            return sb.toString();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return null;
    }

}



消息处理

写在主类的privateMsg()方法里
很简单一句话,将聊天内容和用户QQ传入到这个函数就行了

TencentImpl.talk(fromQQ,msg);

测试调用

好了 ,数据提交和计算接口鉴权,获取结果上面都已经说了

接下来就测试一下调用吧

我们就拿接口调用部分的 闲聊功能进行测试

main函数编写

public static void main(String[] args) {
    // CQ此变量为特殊变量,在JCQ启动时实例化赋值给每个插件,而在测试中可以用CQDebug类来代替他
    CQ = new CQDebug();//new CQDebug("应用目录","应用名称") 可以用此构造器初始化应用的目录
    CQ.logInfo("[JCQ] TEST Demo", "测试启动");// 现在就可以用CQ变量来执行任何想要的操作了
    // 要测试主类就先实例化一个主类对象
    AppDemo demo = new AppDemo();
    // 下面对主类进行各方法测试,按照JCQ运行过程,模拟实际情况
    demo.startup();// 程序运行开始 调用应用初始化方法
    demo.enable();// 程序初始化完成后,启用应用,让应用正常工作
    // 开始模拟发送消息
    
    // 模拟私聊消息
    // 开始模拟QQ用户发送消息,以下QQ全部编造,请勿添加
    demo.privateMsg(0, 10001, 2234567819L, "你好", 0);
    demo.privateMsg(0, 10002, 2222222224L, "喵呜喵呜喵呜", 0);

    // 以下是收尾触发函数
    // demo.disable();// 实际过程中程序结束不会触发disable,只有用户关闭了此插件才会触发
    demo.exit();// 最后程序运行结束,调用exit方法
}

之后运行程序
控制台输出内容代表已经完成
[QQAI机器人]-接入腾讯AI接口
上传程序到QQ机器人这边就不说了
请借鉴我前面的文章

[QQAI机器人]-使用Java开发环境搭建
[QQAI机器人]-爬虫机器人