对柳峰博主的微信公众号后台示例的部分重构

柳峰博主的专栏(http://blog.csdn.net/column/details/wechatmp.html)和王信平博主的专栏(http://www.cnblogs.com/wangshuo1/)对微信公众号开发已经做了比较详尽的阐述,基本上照搬,就可以做出第一个微信公众号后台应用。但是在『照搬』的过程中,发现有些地方总是觉得别扭,由着完美主义者的性格使然,对以下这几个地方做个小优化吧。

一、区别消息和响应

原来的消息类(用户发给后台)和响应类(后台回给用户)有这些:

我改成了这样的:

我把『消息Message』和『响应Response』在命名上分开了。这样在编程时不会总是想着要去区别,这个『Message』究竟是用户发给后台的『消息』呢,还是后台回给用户的『响应』。

二、给消息和响应添加构造函数

原来消息和响应都只是使用缺省构造函数(详见http://blog.csdn.net/lyq8479/article/details/8949088),实际上是把这两个类当做了『结构体』来看待。基于面向对象的思想,还是觉得需要有构造函数。

2.1 BaseMessage类的构造函数

 1    public BaseMessage(String toUserName, String fromUserName, String createTime, String msgType, String msgId){
 2         this.ToUserName=toUserName;
 3         this.FromUserName=fromUserName;
 4         this.CreateTime=Long.parseLong(createTime);
 5         this.MsgType=msgType;
 6         this.MsgId=Long.parseLong(msgId);
 7     }
 8
 9     public BaseMessage(Map<String, String> mapMessage){
10         try {
11             // 开发者微信号
12             String toUserName = mapMessage.get("ToUserName");
13             // 发送方帐号(open_id)
14             String fromUserName = mapMessage.get("FromUserName");
15             // 消息类型
16             String msgType = mapMessage.get("MsgType");
17             // 建立时间
18             String createTime = mapMessage.get("CreateTime");
19             // 消息ID
20             String msgId = mapMessage.get("MsgId");
21
22             this.ToUserName = toUserName;
23             this.FromUserName = fromUserName;
24             this.CreateTime = Long.parseLong(createTime);
25             this.MsgType = msgType;
26             this.MsgId = Long.parseLong(msgId);
27         }
28         catch (Exception e) {
29             e.printStackTrace();
30         }
31     }

2.2 ImageMessage类的构造函数

 1     public ImageMessage(String toUserName,
 2                         String fromUserName,
 3                         String createTime,
 4                         String msgType,
 5                         String msgId,
 6                         String picUrl) {
 7         super(toUserName,fromUserName,createTime,msgType,msgId);
 8         assert (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_IMAGE));
 9         this.PicUrl=picUrl;
10     }
11
12     public ImageMessage(Map<String, String> mapMessage){
13         super(mapMessage);
14         assert (this.getMsgType().equals(MessageUtil.REQ_MESSAGE_TYPE_IMAGE));
15         this.PicUrl=mapMessage.get("PicUrl");
16     }

2.3 TextMessage类的构造函数

 1     public TextMessage(String toUserName,
 2                        String fromUserName,
 3                        String createTime,
 4                        String msgType,
 5                        String msgId,
 6                        String content) {
 7         super(toUserName,fromUserName,createTime,msgType,msgId);
 8         assert (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_TEXT));
 9         this.Content=content;
10
11     }
12
13     public TextMessage(Map<String, String> mapMessage){
14         super(mapMessage);
15         assert (this.getMsgType().equals(MessageUtil.REQ_MESSAGE_TYPE_TEXT));
16         this.Content=mapMessage.get("Content");
17     }

这里只列举了消息类的基类、图片消息、文本消息三个构造函数,其余的位置消息、声音消息、短视频、视频、链接消息的构造函数类似。

2.4 BaseResponse类的构造函数(这个有点意思)

 1     public BaseResponse(BaseMessage baseMessage){
 2         this.ToUserName=baseMessage.getFromUserName(); //返回的目标是当初的发送源
 3         this.FromUserName=baseMessage.getToUserName(); //发送的来源是当初的发送目标
 4         this.MsgType= MessageUtil.RESP_MESSAGE_TYPE_TEXT; // 默认返回消息为文本
 5         this.CreateTime=new Date().getTime(); //默认为当前时间
 6         this.FuncFlag=0; //默认为非星标
 7     }
 8
 9     public BaseResponse(String toUserName,String fromUserName, String msgType){
10         this.ToUserName=toUserName;
11         this.FromUserName=fromUserName;
12         this.MsgType=msgType;
13         this.CreateTime=new Date().getTime(); //默认为当前时间
14         this.FuncFlag=0; //默认为非星标
15     }

响应类的基类的构造函数,我使用了消息类的基类作为参数,是因为响应消息的返回目标,就是当初的发送源(用户),响应消息的发送源,其实就是当初的发送目标(微信后台程序)。

2.5 TextResponse类的构造函数

 1     public TextResponse(String toUserName,String fromUserName, String msgType,String content){
 2         super(toUserName,fromUserName,msgType);
 3         this.Content=content;
 4     }
 5     public TextResponse(BaseMessage baseMessage,
 6                          String content){
 7         super(baseMessage);
 8         this.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_TEXT);
 9         this.Content=content;
10     }

2.6 Article类的构造函数

1     public Article(String title, String description, String picUrl, String url) {
2         this.Title=title;
3         this.Description=description;
4         this.PicUrl=picUrl;
5         this.Url=url;
6     }

2.7 NewsResponse类的构造函数

 1 public class NewsResponse extends BaseResponse {
 2     // 图文消息个数,限制为10条以内
 3     private int ArticleCount;
 4     // 多条图文消息信息,默认第一个item为大图
 5     private List<Article> Articles;
 6
 7     public NewsResponse(String toUserName,String fromUserName, String msgType){
 8         super(toUserName,fromUserName,msgType);
 9         ArticleCount=0;
10         this.Articles = new ArrayList<Article>();
11     }
12     public NewsResponse(BaseMessage baseMessage){
13         super(baseMessage);
14         this.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_NEWS);
15         ArticleCount=0;
16         this.Articles = new ArrayList<Article>();
17     }
18
19     //如果已经达到10篇文章,图文消息不可以再增加新的文章。返回FALSE;
20     public boolean addArticle(Article article) {
21         if (ArticleCount>=10){
22             return false;
23         }
24         Articles.add(article);
25         ArticleCount++;
26         return true;
27     }
28     public Article getArticle(int articleIndex){
29         return Articles.get(articleIndex);
30     }
31
32     public int getArticleCount() {
33         return ArticleCount;
34     }
35
36 //    public void setArticleCount(int articleCount) {
37 //        ArticleCount = articleCount;
38 //    }
39
40     public List<Article> getArticles() {
41         return Articles;
42     }
43
44 //    public void setArticles(List<Article> articles) {
45 //        Articles = articles;
46 //    }
47 }

这个NewsResponse类也要说一下,原来的设计也够奇葩的,居然直接把List结构暴露出来对其读写,List里的Article的数量,可以单独赋值,呵呵。于是果断注释掉计数变量的读写方法,新增了addArticle和getArticle这两个方法,用以从List中添加和获取Article,计算变量,应该自动累加,不应该对其手工赋值。

MusicResponse类的改造类似,不细说了。

三、归一化命名

原来的响应消息转成XML有三个函数,分别是textMessageToXml、musicMessageToXml、newsMessageToXml(详见http://blog.csdn.net/lyq8479/article/details/8949088),我把它们的命名归一为responseToXml,调用的时候,也不用区别传进去的是啥响应消息来选择不同名字的函数,省心。

 1     /**
 2      * 文本消息对象转换成xml
 3      *
 4      * @param textMessage 文本消息对象
 5      * @return xml
 6      */
 7     public static String responseToXml(TextResponse textMessage) {
 8         xstream.alias("xml", textMessage.getClass());
 9         return xstream.toXML(textMessage);
10     }
11
12     /**
13      * 音乐消息对象转换成xml
14      *
15      * @param musicMessage 音乐消息对象
16      * @return xml
17      */
18     public static String responseToXml(MusicResponse musicMessage) {
19         xstream.alias("xml", musicMessage.getClass());
20         return xstream.toXML(musicMessage);
21     }
22
23     /**
24      * 图文消息对象转换成xml
25      *
26      * @param newsMessage 图文消息对象
27      * @return xml
28      */
29     public static String responseToXml(NewsResponse newsMessage) {
30         xstream.alias("xml", newsMessage.getClass());
31         xstream.alias("item", newsMessage.getArticle(0).getClass());
32         return xstream.toXML(newsMessage);
33     }

四、构造消息路由

原来的消息处理的主函数(详见http://blog.csdn.net/lyq8479/article/details/9393195),在一个超级巨大的processRequest函数中对消息进行处理,一堆if else if 下来,眼都花了,所以我简化了processRequest函数,只在这个函数里面构造消息路由,针对不同类型的消息,转去不同的处理逻辑中。

 1    //定义一个logger,用于发送日志,可以在console或者服务器上看到。存储目录在log4j.properties中定义
 2     private static final Logger logger = Logger.getLogger(CoreService.class);
 3
 4     //处理请求
 5     public static String processRequest(HttpServletRequest request){
 6         String returnMsg=null;
 7         try{
 8             Map<String, String> requestMap = MessageUtil.parseXml(request); //解析XML
 9             String msgType = requestMap.get("MsgType"); //消息类型
10             logger.info("***step1 msgType=" + msgType);
11             switch (msgType){
12                 case MessageUtil.REQ_MESSAGE_TYPE_TEXT:
13                     returnMsg=HandleTextMsg(new TextMessage(requestMap));
14                     break;
15                 case MessageUtil.REQ_MESSAGE_TYPE_IMAGE:
16                     returnMsg=HandleImageMsg(new ImageMessage(requestMap));
17                     break;
18                 case MessageUtil.REQ_MESSAGE_TYPE_LINK:
19                     returnMsg=HandleLinkMsg(new LinkMessage(requestMap));
20                     break;
21                 case MessageUtil.REQ_MESSAGE_TYPE_LOCATION:
22                     returnMsg=HandleLocationMsg(new LocationMessage(requestMap));
23                     break;
24                 case MessageUtil.REQ_MESSAGE_TYPE_SHORT_VIDEO:
25                     returnMsg=HandleShortVideoMsg(new ShortVideoMessage(requestMap));
26                     break;
27                 case MessageUtil.REQ_MESSAGE_TYPE_VIDEO:
28                     returnMsg=HandleVideoMsg(new VideoMessage(requestMap));
29                     break;
30                 case MessageUtil.REQ_MESSAGE_TYPE_VOICE:
31                     returnMsg=HandleVoiceMsg(new VoiceMessage(requestMap));
32                     break;
33                 case MessageUtil.REQ_MESSAGE_TYPE_EVENT:
34                     // 事件类型
35                     String eventType = requestMap.get("Event");
36                     switch (eventType){
37                         case MessageUtil.EVENT_TYPE_SUBSCRIBE:
38                             returnMsg=HandleSubscribeEvent(new BaseMessage(requestMap));
39                             break;
40                         case MessageUtil.EVENT_TYPE_UNSUBSCRIBE:
41                             returnMsg=HandleUnsubscribeEvent(new BaseMessage(requestMap));
42                             break;
43                         case MessageUtil.EVENT_TYPE_CLICK:
44                             returnMsg=HandleClickEvent(new BaseMessage(requestMap));
45                             break;
46                         default:
47                             returnMsg=HandleDefaultMsgOrEvent(new BaseMessage(requestMap));
48                     }
49                     break;
50                 default:
51                     returnMsg=HandleDefaultMsgOrEvent(new BaseMessage(requestMap));
52             }
53         }catch (Exception e) {
54             e.printStackTrace();
55         }
56         return returnMsg;
57     }

下面是对各种消息的handle函数

  1     public static String HandleTextMsg(TextMessage textMessage)
  2     {
  3         String respContent = "您发送的是文本消息!";
  4         if (textMessage.getContent().indexOf("/:") > -1)
  5             respContent="你"+textMessage.getContent()+"什么?";
  6         TextResponse textResponse=new TextResponse(textMessage, respContent);
  7         String respMsg=MessageUtil.responseToXml(textResponse);
  8         logger.info("***step2 Response=" + respMsg);
  9         return respMsg;
 10     }
 11     public static String HandleImageMsg(ImageMessage imageMessage)
 12     {
 13         String title="鉴定结果";
 14         String picUrl=imageMessage.getPicUrl();
 15         String forwardUrl= HttpTools.imagePlusPlusPath+picUrl;
 16         logger.info("***step2 image++ Url=" + forwardUrl);
 17
 18         //从image++网站识别上传的图片的拍摄地点、拍摄物体。
 19         String imagePlusPlusReturn=getHttpResponse(forwardUrl);
 20         logger.info("***step3 image++ Response=" + imagePlusPlusReturn);
 21         String description = ReadJSONString(imagePlusPlusReturn);
 22         logger.info("***step4 image++ Response=" + description);
 23
 24         Article article=new Article(title,description,picUrl,forwardUrl);
 25         NewsResponse newsResponse=new NewsResponse(imageMessage);
 26         newsResponse.addArticle(article);
 27         String respMsg=MessageUtil.responseToXml(newsResponse);
 28         logger.info("***step5  Weixin Response=" + respMsg);
 29         return respMsg;
 30     }
 31     public static String HandleLinkMsg(LinkMessage linkMessage)
 32     {
 33         String respContent = "您发送的是链接!";
 34         TextResponse textResponse=new TextResponse(linkMessage, respContent);
 35         String respMsg=MessageUtil.responseToXml(textResponse);
 36         logger.info("***step2 Response=" + respMsg);
 37         return respMsg;
 38     }
 39     public static String HandleLocationMsg(LocationMessage locationMessage)
 40     {
 41         String respContent = "您发送的是地理位置!";
 42         TextResponse textResponse=new TextResponse(locationMessage, respContent);
 43         String respMsg=MessageUtil.responseToXml(textResponse);
 44         logger.info("***step2 Response=" + respMsg);
 45         return respMsg;
 46     }
 47     public static String HandleShortVideoMsg(ShortVideoMessage shortVideoMessage)
 48     {
 49         String respContent = "您发送的是小视频!";
 50         TextResponse textResponse=new TextResponse(shortVideoMessage, respContent);
 51         String respMsg=MessageUtil.responseToXml(textResponse);
 52         logger.info("***step2 Response=" + respMsg);
 53         return respMsg;
 54     }
 55     public static String HandleVideoMsg(VideoMessage videoMessage)
 56     {
 57         String respContent = "您发送的是视频!";
 58         TextResponse textResponse=new TextResponse(videoMessage, respContent);
 59         String respMsg=MessageUtil.responseToXml(textResponse);
 60         logger.info("***step2 Response=" + respMsg);
 61         return respMsg;
 62     }
 63     public static String HandleVoiceMsg(VoiceMessage voiceMessage)
 64     {
 65         String respContent = "您发送的是语音!";
 66         TextResponse textResponse=new TextResponse(voiceMessage, respContent);
 67         String respMsg=MessageUtil.responseToXml(textResponse);
 68         logger.info("***step2 Response=" + respMsg);
 69         return respMsg;
 70     }
 71     public static String HandleSubscribeEvent(BaseMessage baseMessage)
 72     {
 73         String respContent = "欢迎订阅!";
 74         TextResponse textResponse=new TextResponse(baseMessage, respContent);
 75         String respMsg=MessageUtil.responseToXml(textResponse);
 76         logger.info("***step2 Response=" + respMsg);
 77         return respMsg;
 78     }
 79     public static String HandleUnsubscribeEvent(BaseMessage baseMessage)
 80     {
 81         String respContent = "再见,希望您再来!";
 82         TextResponse textResponse=new TextResponse(baseMessage, respContent);
 83         String respMsg=MessageUtil.responseToXml(textResponse);
 84         logger.info("***step2 Response=" + respMsg);
 85         return respMsg;
 86     }
 87     public static String HandleClickEvent(BaseMessage baseMessage)
 88     {
 89         String respContent = "您点击了自定义菜单!";
 90         TextResponse textResponse=new TextResponse(baseMessage, respContent);
 91         String respMsg=MessageUtil.responseToXml(textResponse);
 92         logger.info("***step2 Response=" + respMsg);
 93         return respMsg;
 94     }
 95     public static String HandleDefaultMsgOrEvent(BaseMessage baseMessage)
 96     {
 97         String respContent = "请求不可识别,请稍候尝试!";
 98         TextResponse textResponse=new TextResponse(baseMessage, respContent);
 99         String respMsg=MessageUtil.responseToXml(textResponse);
100         logger.info("***step2 Response=" + respMsg);
101         return respMsg;
102     }

五、应用:试试图像识别

我的第一个微信公众号后台应用,是识别用户上传的图片,是什么东西,在哪里拍摄的。调用的是image++网站的api,核心代码如下:

 1 /*
 2 读取一个网址的所有返回内容
 3  */
 4     public static String getHttpResponse(String connectUrl) {
 5         BufferedReader in = null;
 6         StringBuffer result = null;
 7         try {
 8             URI uri = new URI(connectUrl);
 9             URL url = uri.toURL();
10             URLConnection connection = url.openConnection();
11             connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
12             connection.setRequestProperty("Charset", "utf-8");
13             connection.connect();
14
15             result = new StringBuffer();
16             //读取URL的响应
17             in = new BufferedReader(new InputStreamReader(
18                     connection.getInputStream()));
19             String line;
20             while ((line = in.readLine()) != null) {
21                 result.append(line);
22             }
23
24             return result.toString();
25
26         } catch (Exception e) {
27             e.printStackTrace();
28         }finally {
29             try {
30                 if (in != null) {
31                     in.close();
32                 }
33             } catch (Exception e2) {
34                 e2.printStackTrace();
35             }
36         }
37         return null;
38     }
39
40     /*
41     从JSON字符串中读取某些关键信息
42     JSON文件来自:http://www.imageplusplus.com/doc
43      */
44     public static String ReadJSONString(String jSonString){
45         try {
46             int imageConfidenceIndex=jSonString.indexOf("confidence",jSonString.indexOf("image"));
47             int imageConfidenceIndexEnd=jSonString.indexOf(",",imageConfidenceIndex);
48             String imageConfidence=jSonString.substring(imageConfidenceIndex+12,imageConfidenceIndexEnd);
49             int imageConfidencePercent=(int)(Float.parseFloat(imageConfidence)*100);
50             int imageValueIndex=jSonString.indexOf("value",jSonString.indexOf("image"));
51             int imageValueIndexEnd=jSonString.indexOf("}",imageValueIndex)-1;
52             String imageValue=jSonString.substring(imageValueIndex+8,imageValueIndexEnd).trim();
53
54             int sceneConfidenceIndex=jSonString.indexOf("confidence",jSonString.indexOf("scene"));
55             int sceneConfidenceIndexEnd=jSonString.indexOf(",",sceneConfidenceIndex);
56             String sceneConfidence=jSonString.substring(sceneConfidenceIndex+12,sceneConfidenceIndexEnd);
57             int sceneConfidencePercent=(int)(Float.parseFloat(sceneConfidence)*100);
58             int sceneValueIndex=jSonString.indexOf("value",jSonString.indexOf("scene"));
59             int sceneValueIndexEnd=jSonString.indexOf("}",sceneValueIndex)-1;
60             String sceneValue=jSonString.substring(sceneValueIndex+8,sceneValueIndexEnd).trim();
61
62             String result="这张照片有【"+imageConfidencePercent+"%】的可能是【"+imageValue+"】," +
63                     "有【"+sceneConfidencePercent+"%】的可能是在【"+sceneValue+"】拍摄的。";
64             return result;
65         } catch (Exception e) {
66             e.printStackTrace();
67         }
68         return "服务器秀逗了,请重试!";
69     }
70 }

全部的源代码,我放在了这里:20161013微信公众号后台应用示例src.zip

示例源码中用到几个外部jar lib,去官网下载,然后扔进项目里面就好。

时间: 10-11

对柳峰博主的微信公众号后台示例的部分重构的相关文章

郝萌主的微信公众号上线了

更多游戏源码,请点击我 如果文章对您有所帮助,欢迎关注郝萌主的微信公众号,支持郝萌主的独立游戏工作,更多信息就扫扫我吧^_^ + -------------------------------------------------------- End -------------------------------------------------------- 如果文章对您有所帮助,欢迎关注郝萌主的微信公众号,支持郝萌主的独立游戏工作,更多信息就扫扫我吧^_^ + --------------

谈近期我对博客及微信公众号的态度

2017年的元旦,我开了一个微信公众号"对刘谈心",主要就是随笔的形式谈谈自己生活和工作中的一些感悟与体会,希望与我认识的不认识的朋友进行一些交流,希望我写的东西偶尔能引起你的一点共鸣或些微给你点帮助. 三三两两,断断续续,至今我已经写了超过20篇原创的随笔放到了我的微信公众号上面,不过我没有太多的精力去宣传,也没有那么想过让自己成为"IP 网红". 每一篇文章我只在我的朋友圈里晒一次,偶尔去一两个我的微信群里宣传一下. 于是半年下来,我的关注数是非常地"

PHP微信公众号后台开发(Yii2实现)

本文内容较多,包括微信接入.获取微信用户信息.微信支付.JSSDK配置参数获取等部分.如果读者对微信开发没有一个主观上的认识,那么建议读者先研读微信公众平台开发者文档,然后再阅读本文,效果更佳! 微信开发的完整例子已经整理在Github,欢迎查看: yii2-wechat-demo.[八宝粥的博客] 接入微信 Yii2后台配置 1.在app/config/params.php中配置token参数 return [ //微信接入 'wechat' =>[ 'token' => 'your tok

使用coding.net搭建自己的微信公众号后台服务

首先你得要注册成为Coding.net的用户. 打开之后映入眼帘的就是这个萌萌的图标啦!PS:本来今天早上想继续写来这,结果...Coding.net挂了,希望可怜的攻城师们能早点恢复吧.>(7月23日早6点)---好吧,我把操作步骤放到了项目说明里面.直接去项目说明看吧附上项目地址:https://coding.net/u/yaoml/p/weixinByFlask/git 使用coding.net搭建自己的微信公众号后台服务

Spring Boot 开发微信公众号后台

Hello 各位小伙伴,松哥今天要和大家聊一个有意思的话题,就是使用 Spring Boot 开发微信公众号后台. 很多小伙伴可能注意到松哥的个人网站(http://www.javaboy.org)前一阵子上线了一个公众号内回复口令解锁网站文章的功能,还有之前就有的公众号内回复口令获取超 2TB 免费视频教程的功能(免费视频教程),这两个都是松哥基于 Spring Boot 来做的,最近松哥打算通过一个系列的文章,来向小伙伴们介绍下如何通过 Spring Boot 来开发公众号后台. 1. 缘起

Python微信公众号后台开发&lt;003&gt;:自定义菜单

有同学问道微信公众号后台开发的自定义菜单怎么实现? 这个问题本来想放到后面的,因为的确对公众号的影响挺明显的, 因为开启后台服务,公众号的自定义菜单就不见了,很影响使用, 也有同学问这个问题,就提前了,后面如果有进展会进行更新. 开发文档: https://developers.weixin.qq.com/doc/offiaccount/Getting_Started/Explanation_of_interface_privileges.html 订阅号账号分类及权限 订阅号分为个人号和企业订

django搭建微信公众号后台——验证(7.19)

php研究了好几个框架,感觉还是django强大,用起来也顺手,虽然django在微信后台这方面用得没有那么普及,但还是打算试一试,拿我自己的公众号试手. 今天主要是创建项目然后验证通过,代码还是放在pythonanywhere上. 具体怎么创建就不写了,之前有过一篇文章,这里开发用了星星的wechat-python-sdk. 在微信后台填了网址之后,在urls.py设置路由 url(r'^$', index, name='index'), 在views.py里进行验证: #-*- coding

开发微信公众号后台,原来这么简单?[微信公众号开发]

可能你不知道,有个框架叫wx-tools(小心翼翼地说) Wx-tools是基于微信公众平台API的轻量级框架. 基于Wx-tools你可以开速开发一个订阅号/服务号的web应用后台. 博主最近终于有空!!已经更新到2.0.0啦!! GitHub仓库 下载wx-tools-2.0.0.jar wx-tools开发文档及demo 据说看到文档最后的都是真爱粉! 不多说!讲讲使用wx-tools开发轻量级公众号到底有多简单? 简单到哭 就三步! 1. 创建web工程,添加jar包及依赖 2. 配置w

Python微信公众号后台开发&lt;005&gt;:集成智能聊天机器人?

?给公众号集成一个智能聊天机器人 一.前述 ChatterBot是一个基于机器学习的聊天机器人引擎,构建在python上,主要特点是可以自可以从已有的对话中进行学(jiyi)习(pipei). 二.具体 1.安装 是的,安装超级简单,用pip就可以啦 pip install chatterbot 2.流程 大家已经知道chatterbot的聊天逻辑和输入输出以及存储,是由各种adapter来限定的,我们先看看流程图,一会再一起看点例子,看看怎么用. 3.每个部分都设计了不同的“适配器”(Adap