最近对人工智能感兴趣,查询之后发现了图灵,开放的机器人接口,自然要实现一把了。
当然了本人页面功底较差,(ps:毕竟主要java吗。)对话框,基本上样式抠的图灵的示例窗口,自己实现各种js的效果。
有什么不完善的地方,大家见谅啊。
一、aichart.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>aichart</title>
<script type="text/javascript" src="/mblog/assets/js/jquery.min.js"></script>
<script type="text/javascript" src="/mblog/assets/tl/tl.js"></script>
<style type="text/css">
.dialog{
width: 150px;
height: 250px;
position:fixed;
right:0;
top:10%
}
.dialog img{
display: block;
max-width: 100%;
height: auto;
width: auto\9;
}
.dialog a:-webkit-any-link {
color: -webkit-link;
text-decoration: underline;
cursor: auto;
}
.dialog .dialog-right{
width:150px;
height: 250px;
border: 2px solid #C6DAF5;
z-index:999;
position: fixed;
right: 0;
}
.dialog .dialog-left{
width:430px;
height: 250px;
position: fixed;
border: 2px solid #C6DAF5;
z-index:998;
float: right;
right: -300px;
}
.dialog .dialog-right .right-title{
width:100%;
height: 10%;
font-size:12pt;
font-weight:bold;
text-align: center;
border: 2px solid #C6DAF5;
background-color: #C6DAF5;
}
.dialog .dialog-right .right-pic{
width:100%;
height: 90%;
background:url(assets/images/ai/timg.png) center;
}
.dialog-left .chart-area{
width: 440px;
height: 250px;
border: #dbdada solid 1px;
-ms-box-shadow: 0 0 5px 0 #dbdada;
-moz-box-shadow: 0 0 5px 0 #dbdada;
-webkit-box-shadow: 0 0 5px 0 #dbdada;
-o-box-shadow: 0 0 5px 0 #dbdada;
box-shadow: 0 0 5px 0 #dbdada;
background: #fff;
overflow: hidden;
position: relative;
z-index:998;
}
.dialog-left .chart-area .roll{
width: 2px;
height: 200px;
position: absolute;
right: 12px;
top: 20px;
background: #ededed;
z-index: 5;
display: none;
}
.chart-area .roll span {
width: 6px;
height: 26px;
background: #ededed;
position: absolute;
left: -2px;
top: 0px;
-ms-border-radius: 3px;
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
-o-border-radius: 3px;
border-radius: 3px;
cursor: pointer;
}
.displayArea {
margin-top: 3px;
width: 430px;
height: 200px;
overflow-y: scroll;
border-bottom: #f0f0f0 solid 1px;
position: relative;
overflow:auto;
}
.rotWord {
width:100%;
overflow:hidden;
margin-bottom: 8px;
}
.rotWord1 {
width:100%;
overflow:hidden;
margin-bottom: 8px;
}
.rotWord span {
background: url(assets/images/ai/timg.png) no-repeat;
height: 30px;
width: 30px;
float: left;
margin-left: 5px;
}
.rotWord1 span {
background: url(assets/images/ai/timg.png) no-repeat;
height: 30px;
width: 30px;
float: left;
margin-left: 5px;
}
.rotWord1 .findMsg{
float: left;
margin-left: 14px;
width: 210px;
padding-bottom: 10px;
background: #f1f1f1;
-ms-border-radius: 8px;
-moz-border-radius: 8px;
-webkit-border-radius: 8px;
-o-border-radius: 8px;
border-radius: 8px;
position: relative;
}
.rotWord1 .findMsg i{
position: absolute;
width: 0;
height: 0;
border-top: 6px solid transparent;
border-bottom: 6px solid transparent;
border-right: 6px solid #f1f1f1;
left: -6px;
top: 14px;
}
.rotWord1 .findMsg .main{
width: 188px;
border: #dcdcdc solid 1px;
margin: 10px auto 0;
-ms-border-radius: 4px;
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
-o-border-radius: 4px;
border-radius: 4px;
}
.rotWord1 .findMsg .pic{
position: relative;
}
.rotWord1 .findMsg .pic img{
display: block;
width: 188px;
height: 74px;
}
.rotWord1 .findMsg p{
font-size: 12px;
height: 16px;
line-height: 16px;
text-align: center;
position: absolute;
left: 0;
bottom: 0;
width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
background: rgba(0,0,0,0.5);
color: #fff;
}
.rotWord1 .findMsg a{
text-decoration: none;
}
.rotWord1 .findMsg .info{
height: 36px;
padding: 2px 6px;
background: #fff;
}
.rotWord1 .findMsg .info img{
float: right;
width: 25px;
height: 25px;
margin: 6px 7px 0 0;
}
.rotWord1 .findMsg .info .make{
float: left;
width: 140px;
line-height: 38px;
height: 38px;
font-size: 12px;
color: #8e8c8c;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.rotWord1 .findMsg .line{
height: 1px;
background: #dcdcdc;
}
.rotWord p {
word-break: break-all;
float: left;
color: #333;
font-size: 14px;
padding: 3px 15px;
line-height: 24px;
background: #f1f1f1;
-ms-border-radius: 6px;
-moz-border-radius: 6px;
-webkit-border-radius: 6px;
-o-border-radius: 6px;
border-radius: 6px;
position: relative;
max-width: 220px;
margin: 0px 0px 0px 14px;
}
.rotWord p a{
color: #2373be;
text-decoration: none;
}
.rotWord p i{
position: absolute;
width: 0;
height: 0;
border-top: 6px solid transparent;
border-bottom: 6px solid transparent;
border-right: 6px solid #f1f1f1;
left: -6px;
top: 10px;
}
.mWord {
width:100%;
overflow:hidden;
margin-bottom: 5px;
}
.mWord span {
background: url(assets/images/ai/timg.png) no-repeat;
height: 30px;
width: 30px;
float: right;
margin-right: 5px;
}
.mWord p {
word-break: break-all;
float: right;
color: #fff;
font-size: 14px;
padding: 3px 15px;
line-height: 24px;
background: #19b955;
-ms-border-radius: 6px;
-moz-border-radius: 6px;
-webkit-border-radius: 6px;
-o-border-radius: 6px;
border-radius: 6px;
position: relative;
max-width: 210px;
margin: 0px 14px 0px 0px;
}
.mWord p i{
position: absolute;
width: 0;
height: 0;
border-top: 6px solid transparent;
border-bottom: 6px solid transparent;
border-left: 6px solid #19b955;
right: -6px;
top: 10px;
}
.writeArea {
height: 30px;
margin-top: 10px;
}
.writeArea input {
width: 330px;
height: 25px;
line-height: 25px;
float: left;
margin-left: 12px;
border: #d0cfcf solid 1px;
-ms-border-radius: 4px;
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
-o-border-radius: 4px;
border-radius: 4px;
text-indent: 14px;
-ms-box-shadow: 1px 1px 2px 0 #d0cfcf;
-moz-box-shadow: 1px 1px 2px 0 #d0cfcf;
-webkit-box-shadow: 1px 1px 2px 0 #d0cfcf;
-o-box-shadow: 1px 1px 2px 0 #d0cfcf;
box-shadow: 1px 1px 2px 0 #d0cfcf;
font-size: 14px;
}
.writeArea span {
cursor: pointer;
float: left;
margin-left: 12px;
color: #fff;
font-size: 14px;
width: 40px;
height: 25px;
text-align: center;
line-height: 25px;
-ms-border-radius: 4px;
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
-o-border-radius: 4px;
border-radius: 4px;
background: #00a3f0;
}
</style>
</head>
<body style="height:1500px">
<div id="ai_chart_dialog" class="dialog">
<div class="dialog-right">
<div class="right-title">小智</div>
<div class="right-pic"></div>
</div>
<div class="dialog-left">
<div class="chart-area">
<div class="roll" style="display: none;"><span class="point" style="top: 0px;"></span></div>
<div class="displayArea" >
<div class="diswap">
<div class="rotWord"> <span></span> <p id="member">小娜在此<i></i></p></div>
</div>
</div>
<div class="writeArea">
<input type="text" maxlength="200" placeholder="聊点什么吧">
<span >发 送</span>
</div>
</div>
</div>
</div>
<script type="text/javascript">
var tuling_button = $('#ai_chart_dialog .dialog-left .writeArea span');//document.getElementById("tuling_button");
function getLen(val){
var len = 0;
for (var i=0; i<val.length; i++) {
if (val.charCodeAt(i)>127 || val.charCodeAt(i)==94) {
len += 2;
} else {
len ++;
}
}
return len;
}
function talk(){
//获取输入值
var input = $('#ai_chart_dialog .dialog-left .writeArea input');//document.getElementById("tuling_input");
var val = input.val();
var tuling_dialog = $('#ai_chart_dialog .dialog-left .displayArea .diswap');//document.getElementById("tuling_dialog");
var len = getLen(val);
if(len <=0){
//添加机器人回话
tuling_dialog.append("<div class='rotWord' ><span></span><p>还是说点什么吧?<i></i></p></div>");
//滚动条自动到底
}else if(len >32){
//添加机器人回话
tuling_dialog.append("<div class='rotWord' ><span></span><p>好长的句子啊,理解不了了:(<i></i></p></div>");
}else{
//添加自己的问题
tuling_dialog.append("<div class='mWord'><span></span><p>"+val+"<i></i></p></div>");
//发送请求
$.post("AI/tl",{info:val},function(result){
var obj = eval('(' + result + ')');
if(obj.code == 0){
tuling_dialog.append(AITL.parseResult(AITL.toJson(obj.data)));
$('#ai_chart_dialog .dialog-left .displayArea').scrollTop($('#ai_chart_dialog .dialog-left .displayArea')[0].scrollHeight);
}else{
alert("error");
$('#ai_chart_dialog .dialog-left .displayArea').scrollTop($('#ai_chart_dialog .dialog-left .displayArea')[0].scrollHeight);
}
});
}
//滚动条自动到底
$('#ai_chart_dialog .dialog-left .displayArea').scrollTop($('#ai_chart_dialog .dialog-left .displayArea')[0].scrollHeight);// = $('#ai_chart_dialog .dialog-left .displayArea').height();
//清空输入值
input.val("");
}
//绑定回车输入
$('#ai_chart_dialog .dialog-left .writeArea input').bind('keyup', function(event) {
if (event.keyCode == "13") {
//回车执行查询
talk();
}
});
//绑定点击发送时间
$('#ai_chart_dialog .dialog-left .writeArea span').click(function(){
talk();
});
$(function(){
var expanded = true;
$('#ai_chart_dialog .dialog-right').click(function(){
if(expanded){
$('#ai_chart_dialog').width(600);
$('#ai_chart_dialog .dialog-left').animate({right:'150px'},100);
}else{
$('#ai_chart_dialog').width(150);
$('#ai_chart_dialog .dialog-left').animate({right:'-300px'},100);
}
expanded = !expanded;
});
});
</script>
</body>
</html>
二、tl.js
function AITL() { function error(){ return "<div class='rotWord' ><span></span><p>小娜好像生病了,麻烦找找站长医生给看看吧:(<i></i></p></div>"; } function isJson(resjson){ var isjson = typeof(obj) == "object" && Object.prototype.toString.call(obj).toLowerCase() == "[object object]" && !obj.length; return isjson; }; this.parseText = function(resjson){ var res=""; if(isJson(resjson)){ res = error(); }else{ res = "<div class='rotWord' ><span></span><p>"+resjson.text+"<i></i></p></div>"; } return res; }; this.parseLink = function(resjson){ var res=""; if(isJson(resjson)){ res = error(); }else{ res = "<div class='rotWord' ><span></span><p>"+resjson.text+"<a href='"+resjson.url+"' target=\"_blank\">打开页面</a><i></i></p></div>"; } return res; }; this.parseNews = function(resjson){ var res=""; if(isJson(resjson)){ res = error(); }else{ res = "<div class=\"rotWord1 \">"+ "<span></span> " + "<div class=\"findMsg\"> " + "<i></i> " + "<div class=\"main\"> " ; var newslist = resjson.list; for(var j=0;j<newslist.length;j++){ var news = newslist[j]; if(j==0){ res = res+ "<div class=\"pic\">" + "<p>"+news.article+"</p> " + "<a href=\"" + news.detailurl + "\" target=\"_blank\">" + "<img src=\""+news.icon+"\" alt=\"\">" + "</a> " + "</div> "; }else if(j==newslist.length-1){ res = res+ "<div class=\"info\"> " + "<a href=\""+news.detailurl+"\" target=\"_blank\"> " + "<div class=\"make\">"+news.article+"</div> " + "</a>" + "<a href=\""+news.detailurl+"\" target=\"_blank\">" + "<img src=\""+news.icon+"\" alt=\"\">" + "</a> " + "</div> " + "</div> " + "</div> " + "</div>"; }else{ res = res+ "<div class=\"info\"> " + "<a href=\""+news.detailurl+"\" target=\"_blank\"> " + "<div class=\"make\">"+news.article+"</div> " + "</a>" + "<a href=\""+news.detailurl+"\" target=\"_blank\">" + "<img src=\""+news.icon+"\" alt=\"\">" + "</a> " + "</div> " + "<div class=\"line\"></div> "; } } } return res; }; this.parseFood = function(resjson){ var res=""; if(isJson(resjson)){ res = error(); }else{ res = "<div class=\"rotWord1 clearfix\"> " + "<span></span> " + "<div class=\"findMsg\"> " + "<i></i> " + "<div class=\"main\">"; var foodlist = resjson.list; for(var j=0;j<foodlist.length;j++){ var food = foodlist[j]; if(j==foodlist.length-1){ res = res + "<div class=\"info\"> " + "<a href=\""+food.detailurl+"\" target=\"_blank\"> " + "<div class=\"make\">"+food.info+"</div> " + "</a>" + "</div>" + "</div> " + "</div> " + "</div>"; }else{ res = res + "<div class=\"info\"> " + "<a href=\""+food.detailurl+"\" target=\"_blank\"> " + "<div class=\"make\">"+food.info+"</div> " + "</a>" + "</div> " + "<div class=\"line\"></div>"; } } } return res; }; this.parseErGe = function(resjson){ var res = ""; return res; }; this.parseShiCi = function(resjson){ var res = ""; return res; }; this.parseError = function(resjson){ var res = ""; if(isJson(resjson)){ res = error(); }else{ if(resjson.code == 40002){ res = "<div class='rotWord' ><span></span><p>还是说点什么吧。<i></i></p></div>"; }else if(resjson.code == 40004){ res = "<div class='rotWord' ><span></span><p>她下班了,明天再来玩吧。<i></i></p></div>"; }else{ res = error(); } } return res; }; this.parseNOCODE = function(resjson){ var res = ""; if(isJson(resjson)){ res = error(); }else{ res = error(); } return res; }; }; AITL.toJson = function(str) { return JSON.parse(str); }; AITL.parseResult = function(resjson) { var a = new AITL(); var code = resjson.code; if (code == 100000) { return a.parseText(resjson); } else if (code == 200000) { return a.parseLink(resjson); } else if (code == 302000) { return a.parseNews(resjson); } else if (code == 308000) { return a.parseFood(resjson); } else if (code == 313000) { return a.parseErGe(resjson); } else if (code == 314000) { return a.parseShiCi(resjson); } else if (code == 40001 || code ==40002 || code == 40004 || code ==4007) { return a.parseError(resjson); } else{ return a.parseNOCODE(resjson); } }
三、后端java
controller 这个只给有用的部分吧,毕竟用什么框架的都有自己弄个就好了
String userid = request.getSession().getId();
//图灵网站上的secret
//待加密的json数据
String data = "{\"key\":\""+apiKey+"\",\"info\":\""+info+"\",\"userid\":\""+userid+"\"}";
//获取时间戳
String timestamp = String.valueOf(System.currentTimeMillis());
//生成密钥
String keyParam = secret+timestamp+apiKey;
String key = Md5.MD5(keyParam);
//加密
Aes mc = new Aes(key);
data = mc.encrypt(data);
//封装请求参数
JSONObject json = new JSONObject();
json.put("key", apiKey);
json.put("timestamp", timestamp);
json.put("data", data);
//请求图灵api
String result = PostServer.SendPost(json.toString(), url);
使用到的其他的类,是图灵给的demo里面的东西。也贴出来吧。
Aes.java
package org.mblog.tlai.util;
import java.security.Key;
import java.security.MessageDigest;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
/**
* aes加密算法
* @author 图灵机器人
*
*/
public class Aes {
private Key key;
/**
* AES CBC模式使用的Initialization Vector
*/
private IvParameterSpec iv;
/**
* Cipher 物件
*/
private Cipher cipher;
/**
* 构造方法
* @param strKet
* 密钥
*/
public Aes(String strKey) {
try {
this.key = new SecretKeySpec(getHash("MD5", strKey), "AES");
this.iv = new IvParameterSpec(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0 });
this.cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
} catch (final Exception ex) {
throw new RuntimeException(ex.getMessage());
}
}
/**
* 加密方法
*
* 说明:采用128位
*
* @return 加密结果
*/
public String encrypt(String strContent) {
try {
byte[] data = strContent.getBytes("UTF-8");
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
byte[] encryptData = cipher.doFinal(data);
String encryptResult = new String(Base64.encodeBase64(
encryptData), "UTF-8");
return encryptResult;
} catch (Exception ex) {
throw new RuntimeException(ex.getMessage());
}
}
/**
*
* @param algorithm
* @param text
* @return
*/
private static byte[] getHash(String algorithm, String text) {
try {
byte[] bytes = text.getBytes("UTF-8");
final MessageDigest digest = MessageDigest.getInstance(algorithm);
digest.update(bytes);
return digest.digest();
} catch (final Exception ex) {
throw new RuntimeException(ex.getMessage());
}
}
}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Md5.java
package org.mblog.tlai.util;
import java.security.MessageDigest;
/**
* md5加密
* @author 图灵机器人
*
*/
public class Md5 {
/**
* MD5加密算法
*
* 说明:32位加密算法
*
* @param 待加密的数据
* @return 加密结果,全小写的字符串
*/
public static String MD5(String s) {
char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f' };
try {
byte[] btInput = s.getBytes("utf-8");
// 获得MD5摘要算法的 MessageDigest 对象
MessageDigest mdInst = MessageDigest.getInstance("MD5");
// 使用指定的字节更新摘要
mdInst.update(btInput);
// 获得密文
byte[] md = mdInst.digest();
// 把密文转换成十六进制的字符串形式
int j = md.length;
char str[] = new char[j * 2];
int k = 0;
for (int i = 0; i < j; i++) {
byte byte0 = md[i];
str[k++] = hexDigits[byte0 >>> 4 & 0xf];
str[k++] = hexDigits[byte0 & 0xf];
}
return new String(str);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
++++++++++++++++++++++++++++++++++++++++++
PostServer.java
package org.mblog.tlai.util;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
/**
* HTTP工具类
* @author 图灵机器人
*
*/
public class PostServer {
/**
* 向后台发送post请求
* @param param
* @param url
* @return
*/
public static String SendPost(String param, String url) {
OutputStreamWriter out = null;
BufferedReader in = null;
String result = "";
try {
URL realUrl = new URL(url);
HttpURLConnection conn = (HttpURLConnection) realUrl
.openConnection();
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setUseCaches(false);
conn.setRequestMethod("POST");
conn.setConnectTimeout(50000);
conn.setReadTimeout(50000);
conn.setRequestProperty("Content-Type", "application/json");
conn.setRequestProperty("Accept", "application/json");
conn.setRequestProperty("Authorization", "token");
conn.setRequestProperty("tag", "htc_new");
conn.connect();
out = new OutputStreamWriter(conn.getOutputStream(), "UTF-8");
out.write(param);
out.flush();
out.close();
//
in = new BufferedReader(new InputStreamReader(
conn.getInputStream(), "UTF-8"));
String line = "";
while ((line = in.readLine()) != null) {
result += line;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (out != null) {
out.close();
}
if (in != null) {
in.close();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
return result;
}
}