Giter VIP home page Giter VIP logo

wx's Introduction

(minimalist) WeChat Middleware for Express.js

微信应用框架

wx是极简设计的微信(公共平台)应用参考级框架,而并非微信接口在node.js下的幂等映射。

安装

wx要求在线的**Redis**实例。默认情况下,wx尝试连接本地实例,您可以指定Redis连接。利用Rediswx具备:

  • 集群模式下自动管理微信令牌;
  • 浏览器端捕捉二维码扫描;
  • 等功能。

===

  1. npm install wx

  2. ☞ 登录**微信公众平台**
    ☞ 高级功能
    ☞ 开发模式,获取token

    *配置服务器时仅token必须。

  3. 服务器端

 app = express()    
 wx  = new require 'wx'
   token            : 'xxx-xx-xx'
   app_id           : 'xx-xxx'
   app_secret       : 'xxxxxxxxxxxx'
   encoding_aes_key : 'xxxxxx'
 app.use '/wx', wx
  1. 启动应用程序后,修改微信开发模式中服务器配置为:http://server.address/wx,且有与程序相一致的token

接受文本消息

通过wx.text注册文本消息处理流程:

探索几种根据用户发送文本,通过res.text被动回复的方式:

匹配正则表达式

wx.text /(.+)天气/, (req, res) ->
  res.text "#{req.params[1]},晴[太阳]"

接收文本常量

wx.text '你好', (req, res) ->
  res.text "#{req.user.nickname},么么哒"

接收任意文本

wx.text (req, res) ->
  res.text "#{req.content},喵呜~"

*wx按注册顺序匹配消息,最后注册接收任意文本句柄。

接受其他消息

图片

下面演示了通过wx.image接收用户发送图片,再以res.image被动回复相同图片的方式:

wx.img, (req, res) ->
  res.image req.media_id

可以通过wx.download下载图片,得到图片buffer:

wx.download req.media_id, (err, image) ->

*res.image亦接收图片文件路径,自动上传图片(为避免请求超时,建议预传图片,或先ok再发送客服图片消息)。

语音

下面演示了通过wx.voice接收用户发送图片,再以res.voice被动回复相同语音的方式:

wx.voice, (req, res) ->
  res.voice req.media_id

可以通过wx.download下载图片,得到语音buffer:

wx.download req.media_id, (err, voice) ->

*res.voice亦接收语音文件路径,自动上传语音(为避免请求超时,建议预传语音,或先ok再发送客服语音消息)。

视频

下面演示了通过wx.video接收用户发送图片,再以res.video被动回复视频文件的方式:

wx.video, (req, res) ->
  res.video
    title        : '视频标题'
    description  : '视频描述'
    video        : 'video.mp4'

地理位置

下面演示了通过wx.location接收用户发送的地理位置,通过req.labelreq.location_yreq.location_x获取地址、维度、经度的方式:

wx.location, (req, res) ->
  coordinate = req.location_x + ',' + req.location_y
  res.text "您在#{req.label or coordinate}"

链接

下面演示了通过wx.link接收用户发送链接,再通过req.titlereq.descriptionreq.url获取标题、描述与链接的方式:

wx.link, (req, res) ->
  res.text "《#{req.title}》一文发人深省…"

发送消息

文本

被动回复文本消息:

wx.text '文本', (req, res) ->
  res.text '北京,晴[太阳]'

5秒内无法应答时,先200,再通过req.user发送客服文本消息(避免微信服务器发起重试):

wx.text '文本', (req, res) ->
  res.ok()
    req.user.text '北京,晴[太阳]'

主动发送客服文本消息:

wx.user('xx_xxx').text('北京,晴[太阳]')

图片

被动回复图片消息:

使用res.image被动回复图片。既可指定本地文件路径,由wx自动上传图片;亦可直接传入有效的media_id,省略上传图片步骤。

wx.image, (req, res) ->
   res.image req.media_id

如需上传图片,建议先200,再通过req.user发送客服消息,以免因上传耗时超5秒导致重试。

wx.img, (req, res) ->
  res.ok()
    req.user.image 'image.jpg'

上传图片获取media_id方式为:

wx.upload 'image', 'image.jpg', (req, res) ->
  # res.media_id 是上传图片的 media_id

主动发送客服图片消息:

wx.user('xx_xxx').image('image.jpg')

语音

被动回复语音消息:

使用res.voice被动回复语音。既可指定本地文件路径,由wx自动上传语音;亦可直接传入有效的media_id,省略上传语音步骤。

wx.voice, (req, res) ->
   res.voice req.media_id

如需上传语音,建议先200,再通过req.user发送客服消息,以免因上传耗时超5秒导致重试。

wx.voice, (req, res) ->
  res.ok()
    req.user.image 'voice.amr'

上传图片获取media_id方式为:

wx.upload 'voice', 'voice.amr', (req, res) ->
  # res.media_id 是可以重用的语音 media_id

主动发送客服语音消息:

wx.user('xx_xxx').voice('image.jpg')

视频

被动发送视频消息:

使用res.video被动回复视频。既可指定本地文件路径,由wx自动上传语音;亦可直接传入有效的media_id,省略上传视频步骤。

wx.video, (req, res) ->
  res.video
    title        : '视频标题'
    description  : '视频描述'
    video        : 'video.mp4'

如需上传视频,建议先200,再通过req.user发送客服消息,以免因上传耗时超5秒导致重试。

wx.video, (req, res) ->
  res.ok()
    req.user.video ...

上传视频获取media_id方式为:

wx.upload 'video', 'video.mp4', (req, res) ->
  # res.media_id 是可以重用的视频 media_id

主动发送视频消息:

wx.user('xx_xxx').video ...

音乐

被动发送音乐消息:

需上传专辑图片时,先200,再通过req.user.music发送客服音乐消息(避免上传耗时超5秒,微信服务器发起重试):

wx.text '音乐', (req, res) ->
  music =
    title        : '音乐标题'
    description  : '音乐描述'
    music_url    : 'http://weixinjs.org/music.mp3'
    hq_music_url : 'http://weixinjs.org/music.mp3'
    thumb_media  : 'cover.jpg'
  res.music music

无客服消息权限,或已知专辑封面thumb_media_id时,可直接回复音乐:

wx.text '音乐', (req, res) ->
  res.music music

上传缩略图获取media_id方式为:

wx.upload 'thumb', 'cover.jpg', (req, res) ->
  # res.thumb_media_id 是可以重用的缩略图 thumb_media_id

主动发送视频消息: 请举一反三

图文

被动发送图文消息:

wx.text '图文', (req, res) ->
  news = [
    title       : '头条新闻标题'
    description : '头条新闻描述'
    pic_url     : ''
    url         : ''
  ,
    title       : '次条新闻标题'
    description : '次条新闻描述'
    pic_url     : ''
    url         : ''
  ]
  res.news news

亦可通过req.user发送客服新闻消息

wx.text '新闻', (req, res) ->
  res.ok()
  req.user.news news

主动发送图文消息: 举一反三

转客服

消息转多客服:

通过res.transfer方法,将消息转发到多客服:

wx.text /客服.*/, (req, res) ->
  res.transfer()

*公共平台开发者文档有更多关于多客服的开发者资料。在多客服网站下载客户端。

点击按钮

响应按钮点击,我们努力迫近最为自然的方式:
使用wx.click方法,指定被点击按钮名称,即可捕捉该按钮点击事件。

wx.click '点击按钮', (req, res) ->
   res.text "被#{req.user.nickname}点了一下[害羞]"

编辑按钮

编辑菜单是一种内容创意,应当由markdown来完成。使用wx,你可以立即预览菜单效果,无需关注更多。安装wx后,可以通过http://server.address/wx/admin地址进入管理界面.

通过以下Markdown格式的文件可以编辑并实时预览按钮。

+ 点击按钮 
+ [链接文档](http://mp.weixin.qq.com/wiki) 
+ 二级菜单
   - [链接跳转](http://github.com/baoshan/wx)
   - 拉取信息

扫描二维码登录

服务器端

通过wx.scan注册扫码处理流程*。响应句柄参数分别为:

  1. 浏览器请求,包含req.session会话、req.user微信用户、req.query二维码参数;
  2. 微信响应,可被动回复各种消息;
  3. 浏览器回调方法,可向浏览器发送任意数据。
wx.scan (req, res, desktop_callback) ->
  req.session.user = req.user
  res.text "#{req.user.nickname}从#{req.query.from}登录"
  desktop_callback req.user

标记语言*

    <script src="/wx/wx.js"></script>     
    <img id="登录二维码" src="/wx/qrcode?from=网页" />

浏览器端

$('#登录二维码').scan (user) -> 
  {headimgurl: src, nickname: title} = user
  $img = $ "<img src='#{src}' /><p>#{title}</p>"
  $(@).replaceWith($img)

*建议为所有二维码指定名称进行分类,见临时二维码与永久二维码部分。

临时二维码

指定名称,可为二维码分类。下面二维码名称为fruits

    <script src="/wx/wx.js"></script>     
    <img src="/wx/qrcode/fruits?name=cherry" />

*wx自动为被扫描的二维码增加scanned类名。

服务器端在wx.scan中指定二维码名称,注册该类二维码的处理流程。

wx.scan 'fruits', (req, res, desktop_callback) ->
  res.text "#{req.user.nickname}偷吃了#{req.query.name}"
  desktop_callback '吃一口'

永久二维码

*永久二维码图案稳定,永不过期,适于印刷品、广告等,实现来源统计用途。 名称在1100000间为永久二维码,[continuous]条码支持连续扫描:

服务器端

通过permanent名称注册永久二维码扫码处理流程,用req.params.scene_id获取编号。

wx.scan 'permanent', (req, res, callback) ->
  {nickname} = req.user
  {from} = req.query
  {scene_id} = req.params
  res.text "#{nickname}扫描#{from}编号#{scene_id}的永久二维码"
  callback req.user

浏览器端

通过scan方法,捕捉永久二维码扫描事件

$('#永久二维码').scan ({nickname: title, headimgurl: src}) ->
  $img = $("<img src='#{src}' title='#{title}' />")
  $('#div_headimgs').append($img)
  $img.load -> $(@).addClass('loaded')

关注与取消关注

关注

通过wx.subscribe注册关注事件。

wx.subscribe (req, res) ->
  nickname = req.user.nickname
  res.text "[礼物]欢迎#{nickname}关注微信应用框架!"

*当用户通过扫描二维码关注时,如已注册相应的二维码扫码处理流程,则交给该流程处理,不触发subscribe事件,仍可根据scan事件的req.eventsubscribe判断用户为通过扫码关注。

取消关注

通过wx.unsubscribe注册取消关注事件。

wx.unsubscribe (req, res) ->
  # 按具体需求进行处理。
  res.ok()

获取关注者列表

通过wx.subscribers方法,拉取关注者列表:

wx.subscribers (err, res) ->
  console.log res if res

获取到的关注者列表结构:

{
  "total" : 23000,
  "count" : 10000,
  "data"  : {"openid": ["OPENID1", ...]},
  "next_openid": "NEXT_OPENID"
}

亦可指定next_openid,拉取后续用户列表:

wx.subscribers next_openid, (err, res) ->

更多时候……

可以考虑用wx.populate_subscribers获取指定范围内关注者完整信息(分页显示关注用户、批量发送客服消息等)。

wx.populate_subscribers from, to, (err, res) ->
  {total, subscribers} = res if res

获取到的指定范围关注者完整信息结构:

{
  total       : 23000,
  subscribers : [{
    "subscribe"      : 1,
    "openid"         : "......",
    "nickname"       : "Band",
    "sex"            : 1,
    "language"       : "zh_CN",
    "city"           : "广州",
    "province"       : "广东",
    "country"        : "**",
    "headimgurl"     : "http://wx.qlogo.cn/...",
    "subscribe_time" : 1382694957
  }, ...]
}

有限状态机

定义用户所处状态与各状态间跃迁条件,微信应用框架帮助您分解复杂业务,且毫不妥协研发体验:

wx.click '申办', (req, res) ->
  res.text '回复“固话”申办固话,回复“宽带”申办宽带。'
  req.user.state '申办'
  
wx.state('申办').text '固话', (req, res) ->
  req.text '正在为您申办固话业务,请提供认证照片。'
  req.user.state '认证'
  
wx.state('认证').image (req, res) ->
  req.text '已收到您提交的认证图片,请发送装机地址。'
  req.user.state '装机'
  
wx.state('装机').location (req, res) ->
  res.text "工程师正前往#{req.label}为您安装固话。"
  req.user.state null

wx's People

Contributors

baoshan avatar faceair avatar kuaiyusheng23 avatar liuxiaodong avatar rankjie avatar yfwz100 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

wx's Issues

Expose ACCESS_TOKEN to `wx` object.

As wx will not take care of all the stuff provided by wechat server, we need to expose some internals to the public.

In many cases, we need share the ACCESS_TOKEN in the application to fetch resources that are not managed by wx module. Relying on redis's key is always not a good idea as the key itself is not part of the application and in case that one day the key will be changed because of naming conflict.

I propose the API as

wx.env (accessToken) ->
  # do something about access token.

Other internals should also be addressed here but what I know yet is just the ACCESS_TOKEN.

Issues on file upload.

I found that the code was using curl to upload multimedia file. It broke the application on Windows because of the absence of curl. Besides the issue on Windows, another problem is that it could leverage the native data pipeline on Node.js . It limits the data source to local files.

Add release tags in github?

The library is quite stable now. Adding tags has many benefits. The most common one is that it could make comparing changes between releases easier. :)

Unexpected behaviour in initialization of wx module.

I came across a confusing problem when I used this library. I have an existing express application and want to mount /wx as the path of weixin application. So I separate the code of weixin in a file named wx-route.js and the content is as following:

var WX= require('wx');

var wx = new WX({/* weixin secret param*/});
wx.text(function (req, res) {
  res.text('Hello'); 
});
module.exports = wx;

The main application just include it as a middleware:

var express = require('express');

var app = express();
app.use('/wx', require('./wx-route'));

When the server receives request from Weixin, it ended with the following output:

TypeError: Cannot read property 'length' of undefined
    at process_message (/var/www/dh-wx/node_modules/wx/lib/wx.js:431:44)
    at /var/www/dh-wx/node_modules/wx/lib/wx.js:520:24
    at Request._callback (/var/www/dh-wx/node_modules/wx/lib/wx.js:974:22)
    at Request.self.callback (/var/www/dh-wx/node_modules/request/request.js:123:22)
    at Request.emit (events.js:98:17)
    at Request.<anonymous> (/var/www/dh-wx/node_modules/request/request.js:1047:14)
    at Request.emit (events.js:117:20)
    at IncomingMessage.<anonymous> (/var/www/dh-wx/node_modules/request/request.js:998:12)
    at IncomingMessage.emit (events.js:117:20)
    at _stream_readable.js:938:16

However, if I changed the initialization as follows:

var WX = new require('wx');
var wx = WX({/* weixin secret param*/});

The server runs normally.

It's really confused that the user need to use new require('wx')({...}) to initialize the module. I think people always expecting the required object is a static object/function/class which could create other objects that depends on this library instead of new a required library as a class.

Or, at least, give a message to warn the user when they're not using the library correctly.

temp QR on browser

when i use temp QR on browser like (http://weixinjs.org/), i see the browser is always Polling .
when scan the qr ,i can get right res.text on my phone , but browser still cann't get the new url to response.

” wx.scan (req, res, desktop_callback) “
'desktop_callback' seems don't give the right answer.

when i debug on this ,it will get into this step :
"
else if (scan_handler = scan_handlers[name]) {
_(req.query).extend(query);
console.log(res);
return scan_handler(req, res, function() {});
}
"

密文模式实验版本异常

{ ToUserName: [ 'gh_f2ff6630a71f' ],
  Encrypt: [ 'lup7T9Ux0LWK8+hpAASO4xHMzhrDRaf9+0mhiv7JwoPkTZd+I4s4OfQlWHEMZ7vXuczLytcfxNeIYIVF99vDKz8XMnOpR8xYcZSj8BR7L55EnodaDT0t0aWaSlUJdHP+fp3Dp8ZqstMrvayISIc0BQFQGr//ri5OgYuTayCqvbsogMlVvesxARmOCwXn93Rre1v6DoYy5JzWlcVd95edjq9N67BSA0VkP7bkaf9fVGrACBFHTayeKQJHX1hQUW8g0En/FgoYbiWAfRdKK/ZHcNkRa8wmKFg5i7X6OlSGBDr/LRxh40d+POO0cyNX1qK+9J91Lk0UGMVALVzV3j5oQml/zdwAXAeTUUgmWWYov8KNncMccxbV3Q0moFu0FgzuNtzm9Ai7hGNkIP4ZXsl1V4IVkHUmUsI2kO2bM3M+YG4b7Kfd5YKwNFc9bZhpuZL15DzffB1fGdabUjz+7eiEUmhLTMKLqO4bllu4y+c54pk3U8sJLpSQB50iNcxFHCxqm9kJ/tWL5z9GxvYYof7XlaOp+bwpGpFFV0Gnjxjt2b2y6ZnsH2E0n9JOSTlBQ7gUZxqfyUP0+kQxMActKzWnfQ==' ] }

events.js:72
        throw er; // Unhandled 'error' event
              ^
TypeError: Cannot read property '0' of undefined
  at /root/wx/demo/node_modules/wx/lib/wx-msg-crypt.js:120:71

源代码:

signature = generated_signature(_this.token, xml.TimeStamp[0], xml.Nonce[0], xml.Encrypt[0]);

@yfwz100帮助。

Different name and key in menu generation?

I just notice that the item generated from wx has the same name and key. This may lead to a problem when user want to change the name of the menu item and leave the key unchanged. At present, changing the menu will break the application because the logic is according to the generated menu.

I think it's necessary to provide a way to customize the menu JSON directly.

errcode "undefined" issue

wx.coffee line 1008:
return callback err or res.body if err or res.body.errcode

res.body.errcode: Cannot read property 'errcode' of undefined

wx demo不能使用

使用此框架,配置文件wx_secret.coffee 中加入token:'xtoucher',配置对应的微信的设置,不能通过。还请帮助。

some questions about templateMessage:

模板消息应该是比较灵活的,可以主动发消息给任何openid的用户。
但是这里的template封装死了,必须要先得到res,这个不知道是不是一个小bug~~

Granted credential from webchat server produces 40001 error.

I came across a strange problem when using this library. The application will periodically throw an error of 40001. If it occurred, my temporally solution is to kill the redis server(data had to be erased) and restart the application manually. But it's really annoying.

Sadly, I didn't know how to reproduce this problem. Could you provide some clue for me to figure out the problem?

不支持redis.auth

在折腾BAE,使用redis是要授权的,所以现在的wx没办法使用。

要不就支持redis.auth,要不就支持接收redis实例,怎样比较好?

这是nodejs服务器版的sdk?

我想知道这个程序是跑的服务器环境还是浏览器环境?我们目前的做法是前后端分离,用nodejs搭建的vue项目,部署是build过后发布的静态文件,好像与这个项目不是同一个东西。是这么理解的吗?这个相当于代替了java的服务器?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.