相信读者同学们都了解wechat-admin,甚至在本地运行过了。今天是wechat-admin项目系列文章的第一篇:项目设计。

在你技术学习的过程中或者已经具备了开发所需要的知识时,某一天灵光一闪决定去做一个(web)项目。那么通常前期分这么几步:

  1. 需求确认。切忌一上来就写代码,先得在心中能把一个项目能清晰的拆分成一条条的需求,另外也要不断地和需求方确认你的理解是不是正确。
  2. 技术确认。首先是确定你能不能hold得住,别摊子铺的太大最后搂不住,或者在预定时间内无法完成。
  3. 用较短时间对技术实现难点确认。熟悉的写出来只是时间问题,那些未知不可控的才是你需要首先确认的,如果发现一开始的技术选型有问题,那么就要尽早的停止改其他方案。

我希望大家在向下看之前,(闭上眼)思考一下:

假如现在让你来写这个项目,你用什么技术方案,你准备如何的实现?

好的,思考之后。来看看我是怎么做的,为什么这样做,以及这个过程中有什么调整和故事吧。

需求 & 选型

大家可以看到我在「特性」中的功能列表,其实一开始的需求只有:

  1. 微信扫码登录
  2. 拉取和存储联系人、群列表、群成员等信息
  3. 自动建群,加人
  4. Web管理页面展示这些信息
  5. Web页面上可设置一些需要的功能参数
  6. 消息提醒
  7. 自动回复机器人

那么接着拆分这些需求。由于我主要是在周末和晚上这种闲暇零散时间,所以我希望让这个拆分粒度尽量的小,控制在一个点1-2小时,最多半天这个标准上。有些需求可以同时做(防止长时间做一件事无聊),有些是其他的需求完成才可以开始的。所以,需求和选型是这样:

微信扫码登录

这个是一开始要做的,我对ItChat/wxpy并不熟悉,这个是整个项目唯一的感觉「不安全」的点了:微信登录和其他常规登录的体验是完全不一样的。别的都是账户/密码、手机号、第三方登录,案例很多,很好做。但是微信是要用手机扫码,所以我要解决2个问题:

  1. 加「钩子」,把登录微信的状态(等待扫码/扫码完成等待确认/确认完成)及时的通知我,让我在Web页面上进行对应的步骤
  2. 需要通过某种方式和服务端实现保持一个长的连接,收到这个通知可以推送数据到浏览器

进一步去确认需求啦。首先阅读ItChat/wxpy源码,发现它能拿到这种通知,但是设计的是在终端用log的方式打印出来,不过本身支持callback函数也能实现钩子函数。不过我还是fork了他俩。解释下原因:

  1. 我不喜欢callback的方式,这会提高项目的复杂程度,高聚合。所以我要使用信号解耦这部分内容。
  2. 给ItChat提交过代码,好几天没人理,索性自己维护fork版本,遇到不满足需要就可以快速改,提高效率

另外后来还踩了一个坑儿,就是页面动态改扫码图片的src会有缓存。所以在信号中把扫码图片变成Data URL协议的方式传递回来。

向浏览器推送数据更知名的方案是Websockets,它可以双向的数据推送,不过太重了用不到,我需要一个轻量级的方案。由于豆瓣有一些场景也是SSE(Server Sent Events),为了学习它,我也选择了SSE。

拉取和存储联系人、群列表、群成员等信息

wxpy提供了直接的API拉取这些信息,但是为什么要本地存呢?

  1. 最主要的,为了降低调用微信接口的次数,降低被封的风险。当我用正确的方式存下来,再加上我改良的wxpy的puid方案,同样可以实现对这些信息进程展示、筛选等查询内容
  2. 开源嘛,就是为了多给大家一个学习的源码项目的机会,想展示SQLAlchemy的更多用法

另外扫码登录之后,拉取和存储的过程可能会比较长,另外在Web页面上还有「强制刷新」的功能,这些如果让页面等待用户体验很差,都需要异步执行,所以用Celery异步的执行。

有个插曲,其实一开始觉得这种小项目用Celery小题大做了,选择了rq,但是随着我的需求变多变复杂(之后的文章会提到),rq的局限性就越来越凸显了。以前我对rq、huey这种框架都是「小项目不妨用用」的态度,不过现在我真的用过了之后才发现在Python圈 Celery永远是首选,请相信我。

自动建群,拉人

操作都是由wxpy封装的(wxpy封装了ItChat),它不支持通常说明Web微信没有这个功能。需要注意的是,建群时还需要至少2个人,要不然不能建群,建群接口当天也会被封。所以「自动建群」需要点技术选型,那就是在设置页面有一个项,包含默认拉的人都有谁,那么需要存起来,我在这个项目把这样的内容都存在了Redis里面。

由于公司基本设施的问题,我Redis用的很少,常见也很简单,一直没有用过Redis的ORM,这次项目其实是为了尝试一下。不过一开始使用的是我fork的redisco,我给它增加了Python 3的支持。不过后来干掉换成了现在用的Walrus,因为在使用过程发现太难用了。

这种早期的新技术选型失败是不可避免的,不过还是在我可控制的范围。

Web管理页面展示这些信息,并且可设置功能参数,消息提醒

这也是本文的重点之一了。Python Web开发者通常都不是单纯的后端开发,所以前端的知识、框架、工具也是要熟练使用。网上这种前后端结合的项目和经验比较少,本文也来谈一谈:

为什么选择了Vue?

如果你不是一个专职前端工程师,并且也没有打算未来转成前端开发者,但是由于工作或者兴趣需要写HTML、JS时,使用框架是一个正确的选择。过去纯手写JS或者用jQuery的方式写代码效率是很低下,尤其在页面交互复杂时,而现在有React、Vue这些框架,帮助你省太多的事情了。去年的网易云音乐精彩评论使用的是 React + Mobx + Fetch + Material-UI + ES6 + Webpack + Babel,对应的技术选型文章可以看commentbox用到了那些前端技术

最近工作中都在使用Vue,比如豆瓣电影「选影视」以及新的电影管理后台(Vue+Vue-Material),我在这里向各位Python开发者极力推荐它,理由如下:

  1. 最大程度的降低了初学者的学习曲线
  2. 数据双向绑定。实现一个相对复杂的页面需要的代码量很少。更多MVVM和前端发展历史可以看我之前写的 浅谈MVC、MTV和MVVM

  3. 单文件组件化和它的语法对于写模板的同学来说最易接受

  4. 文档和周边生态都相对完善
  5. Vue-cli这个命令行脚手架包含了丰富的模板,可以非常快的初始化出来一个项目,极为方便

为什么选择Element-UI

我尝试过Vue下的各种UI库,Element是其中功能最全,API和文档最完善和最易于使用的。举个例子,我用Vue-Material时经常需要翻源码了解用法,货比货得扔啊。但是它也是我的选型(也是为了配合厂内其它的Material-UI),含着泪也要用下去…

另外,如果你想开启一个使用Material Design的新项目,建议使用Muse-UI

如何实现前后端的交互?

不止一个同学问过我这个问题。三点来概括吧:

  1. 前端是一个单页面应用,路由由vue-router来实现,对于后端来说,只需要渲染一个index.html就好了。
  2. 后端提供API,对Flask进行一些定制,让它返回的内容mimetype就是application/json,并且统一封装了返回的格式
  3. 前端再打开页面或者在事件中通过Axios这个HTTP客户端库发出请求到后端,后端接口接收并返回对应的内容

自动回复机器人

在我念书的年代,也上过人人网,当时小黄鸡非常知名,觉得很神奇。当我工作,尤其是做了开发之后,发现其实对于API调用者来说是没有技术含量的。现在市面上有很多知名的机器人,使用它们的每日限额的免费接口就可以。另外我也用到了一些机器学习的ChatterBot。

我改了wxpy的源码,用插件的方式让这些额外的功能可插拔。

其他需求

剩下的那些功能,都是在现有的技术选型基础上去实现的。有些是过程中产生的灵感,有的是阅读源码的时候发现的。