从0到1将公众号接入聊天机器人(二)之程序实现

1.实现步骤

(1)概述

其实将公众号接入图灵机器人,就是相当于微信将用户发的信息传到自己实现的服务器,服务器再将信息传给图灵机器人,获取图灵返回结果响应给用户。


流程.PNG
(2)步骤

①解析接收的微信信息,提取信息
②调用图灵接口发送请求
③解析图灵响应,提取信息
④封装微信响应,回复用户

2.程序实现

项目使用了Springboot框架构建

(1)解析接收的微信信息

①查看微信公众平台api文档接收普通消息的格式
此处只处理文本消息,看到微信推送过来的xml格式的:

<xml>
  <ToUserName><![CDATA[toUser]]></ToUserName>
  <FromUserName><![CDATA[fromUser]]></FromUserName>
  <CreateTime>1348831860</CreateTime>
  <MsgType><![CDATA[text]]></MsgType>
  <Content><![CDATA[this is a test]]></Content>
  <MsgId>1234567890123456</MsgId>
</xml>

我们需要关注的是Content里面的内容
②读取请求输入流
微信服务器将POST消息的XML数据包到开发者填写的URL上,所以下面接收的是POST形式的请求

   public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       req.setCharacterEncoding("UTF-8");
       resp.setCharacterEncoding("UTF-8");
       resp.setContentType("text/html;charset=utf-8");
       //解析请求
       InputStream inputStream = req.getInputStream();
       BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
       String str;
       StringBuilder builder = new StringBuilder();
       while (null != (str = br.readLine())) {
           builder.append(str);
       }
       str = builder.toString();
}

③定义一个pojo类,和xml里的字段对应上,注意,此处为了反射注入方便,没遵循驼峰命名。

/**
 * weChat接收普通消息的实体类
 */
public class ReceiveEntity {
    private String ToUserName;//开发者微信号
    private String FromUserName;//发送方帐号
    private String CreateTime;//消息创建时间
    private String MsgType;//消息类型
    private String Content;//文本消息内容
    private String MsgId;//消息id
    
    //....getter、setter
}

④通过dom4j解析注入ReceiveEntity 实例

    public ReceiveEntity parseWeChatMsgAndReturnEntity(String content) {
        ReceiveEntity res = null;
        try {
            Document doc = DocumentHelper.parseText(content);
            Element root = doc.getRootElement();
            Iterator<Element> iter = root.elementIterator();
            //反射注入属性
            Class clazz = Class.forName("包路径.ReceiveEntity");
            res = (ReceiveEntity) clazz.newInstance();
            while (iter.hasNext()) {
                Element element = iter.next();
                Field field = clazz.getDeclaredField(element.getName());
                Method method = clazz.getDeclaredMethod("set" + element.getName(), field.getType());
                method.invoke(res, element.getText());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return res;
    }
(2)调用图灵接口发送请求

①查看图灵API V2.0接入文档
知道请求方式是HTTP POST,请求参数格式为json:

{
    "reqType":0,
    "perception": {
        "inputText": {
            "text": "附近的酒店"
        },
        "inputImage": {
            "url": "imageUrl"
        },
        "selfInfo": {
            "location": {
                "city": "北京",
                "province": "北京",
                "street": "信息路"
            }
        }
    },
    "userInfo": {
        "apiKey": "",
        "userId": ""
    }
}

②构建json格式的请求
还记得上面解析成ReceiveEntity 实例的xml信息吗,把content传进参数就可以了。
代码中userId是注册图灵账号的个人唯一id,apiKey创建机器人后可得到。下面注释的是非必须的参数。reqType也是非必须的。

    private String getRequestJsonParam(String text) { // 构造JSON请求参数
        JSONObject root = new JSONObject();
        root.put("reqType", 0);

        JSONObject perception = new JSONObject();
        JSONObject inputText = new JSONObject();
        inputText.put("text", text);
        perception.put("inputText", inputText);
        root.put("perception", perception);

        JSONObject userInfo = new JSONObject();
        userInfo.put("apiKey", apiKey);
        userInfo.put("userId", userId);
        root.put("userInfo", userInfo);

//        JSONObject selfInfo = new JSONObject();
//        JSONObject location = new JSONObject();
//        location.put("city", "xx");
//        location.put("province", "xx");
//        location.put("street", "xx");
//        selfInfo.put("location", location);
//        perception.put("selfInfo", selfInfo);
        return root.toString();
    }

③发送请求
借助HttpClient创建POST请求,url是文档给出的接口地址http://openapi.tuling123.com/openapi/api/v2
,param是步骤②封装好的json参数。

    public static String sendPost(String url, String param) {
        // 创建Post请求
        HttpPost httpPost = new HttpPost(url);
        httpPost.setHeader("Content-Type", "application/json;charset=utf8");
        String res = "";
        CloseableHttpResponse response = null;
        try {
            //装填参数
            httpPost.setEntity(new ByteArrayEntity(param.getBytes("UTF-8")));
            //执行post请求
             response = HttpClients.createDefault().execute(httpPost);
            //获取响应实体
            if (response != null && response.getStatusLine().getStatusCode() == 200) {
                res = EntityUtils.toString(response.getEntity());
            }
            return res;
        } catch (IOException e) {
            e.printStackTrace();
            return "";
        }
    }
(3)解析图灵响应

①图灵响应信息格式
查看文档,返回的格式如下:

  {
    "intent": {
        "code": 10005,
        "intentName": "",
        "actionName": "",
        "parameters": {
            "nearby_place": "酒店"
        }
    },
    "results": [
        {
            "groupType": 1,
            "resultType": "url",
            "values": {
                "url": "http://m.elong.com/hotel/0101/nlist/#indate=2016-12-10&outdate=2016-12-11&keywords=%E4%BF%A1%E6%81%AF%E8%B7%AF"
            }
        },
        {
            "groupType": 1,
            "resultType": "text",
            "values": {
                "text": "亲,已帮你找到相关酒店信息"
            }
        }
    ]
}

我们需要拿到的是results下面的text
②解析图灵响应的json
为了方便,我同样构建了个pojo存放results下面的信息。

public class ResultEntity {
    private int groupType;
    private String resultType;
    private Map<String,String> values;
    //getter、setter...
    private String parseResponse(String resp) {
        if (StringUtils.isEmpty(resp)) {
            return "";
        }
        JSONObject jsonObject = JSONObject.parseObject(resp);
        JSONArray array = jsonObject.getJSONArray("results");
        String res = "";
        for (int i = 0; i < array.size(); i++) {
            JSONObject obj = array.getJSONObject(i);
            ResultEntity entity = JSON.toJavaObject(obj, ResultEntity.class);
            if (entity.getResultType().equals("text")) {
                res = entity.getValues().get("text");
                break;
            }
        }
        return res;
    }

好啦,现在拿到了结果了,赶紧给用户发过去吧

(4)封装微信响应,回复用户

①被动回复用户消息格式
回复文本消息格式:

<xml>
  <ToUserName><![CDATA[toUser]]></ToUserName>
  <FromUserName><![CDATA[fromUser]]></FromUserName>
  <CreateTime>12345678</CreateTime>
  <MsgType><![CDATA[text]]></MsgType>
  <Content><![CDATA[你好]]></Content>
</xml>

②封装响应

    public String getWeChatXmlFormatResponse(ReceiveEntity receiveEntity, String tuLinResult) {
        String res = "<xml>\n"
                + "  <ToUserName><![CDATA[" + receiveEntity.getFromUserName() + "]]></ToUserName>\n"
                + "  <FromUserName><![CDATA[" + receiveEntity.getToUserName() + "]]></FromUserName>\n"
                + "  <CreateTime>" + new Date().toString() + "</CreateTime>\n"
                + "  <MsgType><![CDATA[text]]></MsgType>\n"
                + "  <Content><![CDATA[" + tuLinResult + "]]></Content>\n"
                + "</xml>";
        return res;
    }

③返回

        String res = weChatService.getWeChatXmlFormatResponse(receiveEntity, tuLinResult);
        OutputStream outputStream = resp.getOutputStream();
        outputStream.write(res.getBytes("UTF-8"));
        outputStream.flush();
        outputStream.close();

3.总结

现在已经可以在自己代码上测试了,下一节接入公众号和阿里云完成最后的步骤吧

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容