Wx

FarBox的强悍之处在于其自定义API,除了与FarBox官方微信号的消息机制可以自定义外,你还可以将网站接入自己的“微信公众号”。

下文的两个模板文件源码,头部都省略了“send_message”与“send_posts”的定义,其源码附注于本文尾。

在自定义的过程中,你可能需要了解微信公众平台开发者文档;如果你刚开始接触FarBox的自定义API,你需要了解doc.farbox.com上的文档。

个人账户的自定义

这是你自己的微信帐号与FarBox官方帐号之间的数据交互,模板文件的所在位置为/template/service/weixin.jade

注意事项

  1. 按照微信的接口要求,页面最终返回的是一个XML结构的数据。
  2. 这是FarBox系统级页面,不需要校验请求的合法性;于此同时,变量request中一些字段是不可用的,比如request.url
  3. 如果页面错误,或者返回的数据格式有误,那么系统将会自动接管,以默认的规则回应微信。

/template/service/weixin.jade 源码

默认的源代码如下(Jade语法):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
msg_type = request.xml.MsgType
message = request.xml.Content or request.xml.Recognition or ''
if message == '最近'
    posts_to_push = get_data(type='post', limit=4, with_page=False)
    if posts_to_push
        +send_posts(posts_to_push)
    else
        +send_message('抱歉,找不到文章。')
elif message in ['help', '帮助']
    +send_message('帮助文档第一行\n\n帮助文档第二行。')
elif message == 'time'
    +set_var('record_time', True)
    +send_message('设置成功,后续内容将记录时间戳;输入notime可以取消这个状态。')
elif message in ['notime', 'no time']
    +set_var('record_time', False)
    +send_message('设置成功,后续内容不再记录时间戳。')
elif message == 'today'
    +set_var('post_path', '')
    post_filename = now.format('%Y-%m-%d.txt')
    +send_message('设置成功,内容将保存至 %s ' % post_filename)
elif message.startswith('new ')
    post_filename = message.replace('new ', '', 1) + '.txt'
    +set_var('post_path', post_filename)
    +send_message('设置成功,内容将保存至 %s ; 输入today可以重置。' % post_filename)
else
    if msg_type == 'image'
        filename = '/_image/weixin/%s/%s.jpg' % (now.format('%Y-%m-%d'), request.xml.CreateTime)
        +put(filename, from_url=request.xml.PicUrl)
        message = '![Image](%s)' % filename
    if message
        if get_var('record_time')
            message = '%s %s'%(now.format('%Y-%m-%d %H:%M:%S'), message)
        post_filename = get_var('post_path') or now.format('%Y-%m-%d.txt')
        +append(post_filename, message, lines=2, check=True)
        +send_message('已保存到 '+post_filename)
    else
        +send_message('暂不支持的类型。')

公众帐号的自定义

如果你有微信公众帐号,那么,可以直接把在FarBox上的网站(博客)接入其中。
默认模板文件的所在位置为/template/m/weixin.jade

如何Debug?

在模板文件中,增加下面两行代码。可以将微信API发送过来的请求以及数据记录下来,并保存到你的Dropbox中。

+put('/_system/weixin_request_url.txt', request.url)
+put('/_system/weixin_request_data.txt', request.data)

然后,(MacOS/Linux系统)进入到网站目录下的_system文件夹,使用命令行运行curl进行模拟,可以重现一次微信API的请求:

set url (cat weixin_request_url.txt); curl -d @weixin_request_data.txt -H "Content-Type: text/xml" -X POST "$url"

注意事项:

1, 非测试时,不要有这样的代码,因为会很快消耗掉掉put函数的运行配额
2, 要去除if timestamp_diff(rv.timestamp)>120; +raise_404()这两行代码,这是基于时间戳的校验,如不去掉,Debug发出的请求会被认为是非法的而被忽略。当然,测试完毕后,应该要加回去。
3,如果数据没那么及时地从Dropbox同步回本地,你需要重启Dropbox或者处于VPN的状态下。

/template/m/weixin.jade 源码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
+set_no_inject()

if request.method == 'GET' and request.args.set_token == 'true' and request.args.token
    +need_admin()
    +set_var('weixin_token', request.args.token)
    | ok, your weixin token was saved.

rv = request.args
if rv.nonce and rv.timestamp
    weixin_token = get_var('weixin_token') or site.configs.weixin_token or 'farbox'
    if rv.signature != get_hash(weixin_token, rv.nonce, rv.timestamp, hashlib='sha1')
        +raise_404()
    if timestamp_diff(rv.timestamp)>120
        +raise_404()

    if request.method == 'GET'
        | {{ request.args.echostr }}
    else
        msg_type = request.xml.MsgType
        message = request.xml.Content or request.xml.Recognition or ''
        if msg_type == 'event'
            if request.xml.Event == 'subscribe'
                +send_message('谢谢订阅《%s》,输入`最近`返回最近文章,其它关键字则自动进行搜索。'%site.title)
        else
            if message=='最近'
                posts_to_push = get_data(type='post', limit=4, with_page=False)
            elif message
                posts_to_push = get_data(type='post', keywords=message, with_page=False, limit=5)
            if posts_to_push
                +send_posts(posts_to_push)
            else
                +send_message('抱歉,暂未找到对应内容')

send_message & send_posts

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
mixin send_message(message)
    xml
        ToUserName <![CDATA[{{request.xml.FromUserName}}]]>
        FromUserName <![CDATA[{{request.xml.ToUserName}}]]>
        CreateTime= now.format('%s')
        MsgType text
        Content <![CDATA[{{message}}]]>
        FuncFlag 0

mixin send_posts(posts)
    posts = posts[:5]
    xml
        ToUserName <![CDATA[{{request.xml.FromUserName}}]]>
        FromUserName <![CDATA[{{request.xml.ToUserName}}]]>
        CreateTime= now.format('%s')
        MsgType news
        ArticleCount= posts.length
        Articles
            for post in posts
                item
                    Title <![CDATA[{{post.title}}]]>
                    Description <![CDATA[{{post.content.limit(80).no_pic_no_html}}]]>

                    pwd = get_outbound_link_password(days=7)
                    img_get_vars = 'width=360&height=200&outbound_link_password='+pwd
                    if post.cover
                        PicUrl <![CDATA[http://{{site._domain}}{{post.cover}}?{{img_get_vars}}]]>
                    elif loop.index==1
                        PicUrl <![CDATA[http://{{site._domain}}/farbox_free_image.jpg?{{img_get_vars}}]]>
                    Url <![CDATA[http://{{site._domain}}{{post.url}}#main]]>