[QQAI机器人]-接入腾讯AI接口
程序员文章站
2024-02-13 23:09:28
...
简述
腾讯AI的接口有什么功能
腾讯Ai有一些特别有用的智能接口
详细的如下图
为什么使用腾讯AI
因为 免费 ! ! !
点击进入 腾讯AI官网
开发环境
环境配置
JCQ的环境配置这里就不说了,这里直说怎么去实现
[QQAI机器人]-使用Java开发环境搭建
配置好完成后,使用里面的 Maven Demo
在就这里需要用到jsoup,如果不会请参考
导包
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>
准备过程
- 首先,你需要注册腾讯AI
- 然后创建一个应用
- 记下自己的AppID和appkey
功能实现
本次代码的架构图
源码内部有大量注释,希望认真观看
接口调用
接口调用我们使用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时必须带上接口请求签名,其中签名信息由接口请求参数和应用**根据本文提供的签名算法生成。
计算步骤
- 用于计算签名的参数在不同接口之间会有差异,但算法过程固定如下4个步骤。
- 将<key, value>请求参数对按key进行字典升序排序,得到有序的参数对列表N
- 将列表N中的参数对按URL键值对的格式拼接成字符串,得到字符串T(如:key1=value1&key2=value2),URL键值拼接过程value部分需要URL编码,URL编码算法用大写字母,例如%E8,而不是小写%e8
- 将应用**以app_key为键名,组成URL键值拼接到字符串T末尾,得到字符串S(如:key1=value1&key2=value2&app_key=**)
- 对字符串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方法
}
之后运行程序
控制台输出内容代表已经完成
上传程序到QQ机器人这边就不说了
请借鉴我前面的文章