wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。
Q; T) b4 v7 E+ R借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。- 9 l; v! E0 l2 j6 j
- <p> </p><pre style="color: rgb(0, 0, 0); text-transform: none; line-height: normal; text-indent: 0px; letter-spacing: normal; font-style: normal; font-variant: normal; font-weight: normal; word-spacing: 0px; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px;">;; 定义新的游戏地图
+ S0 r% e2 \+ V8 Q* z! { - (def-map "/game/5x5.el" ; 对外开放的URL2 n6 N0 q; y9 K0 u+ Z. i O1 t
- 'tutorial-room-0) ; 默认的入口) E$ P S9 \& G& Q* h7 v
; K4 H3 { l3 @. g1 [0 X0 [- ;;; 游戏大厅( e/ ]5 L% e) b' R; A; {' e k
- (def-room living-room$ X8 U' ~( o5 z- P' W6 }; Z
- ;; 进入该房间后的提示语3 Z# B. W L/ P( T4 R- ^
- "1. 教程
7 z n) P- M$ B% _9 ?0 A* r - 2. 入门(3x3)6 H6 ?+ C% Y4 q
- 3. 初级(5x5)
8 y5 K8 \& g7 j' N) D: L) n - 4. 中级(7x7)" t- ?7 D P7 v. H& g) z
- 5. 高级(9x9)( }9 R' N/ H3 M/ R: h
- 0. 关于作者5 o% e: P! E; l
- 请选择1-5开始新游戏:"
. T0 R3 p) \1 ` - ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名
, y* c3 e1 x# l - ("1" tutorial-room-0)3 h: m; |2 \* u, n7 R
- ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配7 G5 R9 g/ t" [( \( r; Q6 S
- ; 相应的返回也可以为函数,动态返回房间名
# G: j! Q9 g! ?+ S: B* e! t - (t living-room)) ; 如果条件为t,为永真
6 E# V0 U ^3 ^
7 Y$ ?( c1 [7 H8 I, i# h, ^- ;;; 作者信息
/ l" w8 B8 U# Q - (def-room about-room6 O5 J" Y3 k; B6 _0 R1 O
- "作者:redraiment& u/ o! @& d! H- w: v
- 微博:http://weibo.com/redraiment
1 n* V1 ~. `1 B - 有任何建议,欢迎在微博或微信上联系redraiment。7 l: g4 c |: Z) v7 Q7 ]
- 请输入任意数字返回游戏大厅。"
" Q- W2 g- S( H- r - (t living-room))+ e- S2 H; h( E( c+ L6 T% Q* P
: w' N6 W2 q9 M! \$ o1 ?- ;;; 教程
: y3 `* o6 }. i9 R4 c - (defvar *wechat-5x5-tutorial-rooms* 0" a# _. r$ O2 Z8 Z! d) [: H
- "The number of tutorial rooms")7 r) T7 ~/ P N0 G5 y* c& }. o' F
X2 ]3 B& T n, H- ;;; 简化教程的定义( i% O9 `# k1 y9 |# b- |& [% a
- (defun string-last-line (content)3 y3 Y+ x6 @1 W+ x* b
- "多行内容组成的字符串中的最后一行"
. y+ [' o9 B; e7 |. W: B1 A - (with-temp-buffer
7 e+ n3 `$ w8 k" |& g) O - (insert content)
4 m. @6 ^6 F. Q7 K - (buffer-substring (line-beginning-position)
0 U( {6 K/ b# \: j- S - (point-max))))/ o, l E: t1 |
- ! J% n2 D0 B, y$ r0 _
- (defun def-tutorial-room (prompt)
4 r9 J" M! I/ L$ m - "根据提示语自动生成教程房间。
: g! g$ b. \- p* g; M* V - 3 u' _+ q ]: F* ^6 z% `% p
- 1. 提取最后一行作为问题;! c6 ~, O5 B5 L
- 2. 分析问题,获取期望用户输入的内容;5 m o$ a! o$ ]4 F4 J/ m
- 3. 定义教程房间和重复提问房间。"0 F9 A) ?* e! q9 w6 O
- (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*))), Y P0 T d' {: B& C2 m0 X3 ?, E4 V
- (repeat-room (concat room-name "-repeat"))$ K% ?! F4 D( d
- (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
- I4 i& f' R- E9 p( ?( C' L - (question (string-last-line prompt))( l6 O+ p3 t( S7 g) k( j, A
- (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
- r$ L8 v( c: u$ `' Y( H0 B - (match-string 1 question)))
`6 f7 {+ A T# s - (doors (if except# b* n: H9 g) [" t
- `((,except ,(intern next-room))
& M7 g+ z6 O# [" H; x - ("q" living-room)6 L3 o+ y. [( ^& g9 o6 a }3 A4 }
- ("Q" living-room)7 D( D; h7 X2 A1 P( W, [1 ~! J( r
- (t ,(intern repeat-room)))7 Q) G+ c5 v- _6 t* M
- '((t living-room)))))
1 _* d9 J5 \! O - (def-room-raw (intern room-name) prompt doors)1 r0 x, H$ o& w4 H4 p
- (def-room-raw (intern repeat-room) question doors))); N3 ]3 N( I! [) l7 k# F6 w
- $ x1 R# a6 l5 c6 V& J- D- y
- (defun def-tutorial (&rest prompts)
! g, h$ d% S1 q - "批量生成教程房间。"; x# c* t; Y5 X9 y# o0 `
- (dolist (prompt prompts)
8 B/ p" `$ v# T( Y# L$ h+ F) V - (def-tutorial-room prompt)))
# k2 U/ ]$ T% k- y% z - 5 A6 k, w; I- x. {8 g1 ^
- (def-tutorial! f9 |3 a& T5 s5 z9 ~. T" L2 K9 r
- "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
' I3 w1 O+ D' | y0 t9 l/ e6 i" [ - 1. 教程8 Z! j: G, z; {1 J
- 2. 入门(3x3)
n* D. D4 F$ I1 B/ `/ P& W - 3. 初级(5x5)
4 p! x( ~% j' T6 {' x' {" T - 4. 中级(7x7)- b: U6 `+ [2 N# R9 ?- A
- 5. 高级(9x9)! h, i& d/ |8 f
- 0. 关于作者
4 X. |) [* V, O3 ? - 请选择1-5开始新游戏:, w6 f( d5 X7 p( j& k m
- 您现在正在游戏大厅里。/ e/ d3 g* H- r( M
- 请输入“2”进入入门级房间": S4 _" @ h* }! k+ n6 T" \1 t
- " ①②③4 A! z1 |0 ?7 `; W) }
- 1 ■
- F" _$ ]9 {, V$ H6 Q7 f% B0 \ - 2■■■
# q5 _. Y0 l% k# r7 d. |/ f - 3 ■ ' ?* l8 n# G" Z- f5 L9 }
- 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
) Y! m4 _/ ~( o- D3 u0 T% @ - 请输入“22”来关闭第2行第2列的窗户。"
5 K6 L/ h0 }9 I3 X O( v - " ①②③
' n* e' O* G; _& E8 ^: U - 1
6 l/ B! Y! ]5 b- g - 2 " I& ]0 c; V# B, Z
- 3 / J* S* I- U* o% k: n, C
- 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。7 h, ]' W; _( ~% i
- 请输入“11”,试着开启左上角的窗户。"
+ P2 k' B( x: V x9 ]0 ^ - " ①②③# I& D( S5 j1 S' I$ B1 U3 ] @
- 1■■
* k+ o, g* `& n- H1 a& a$ Y - 2■
: Y% a, p( g/ j" s U - 3 7 \; {; ]8 g; r, J) @# M# ?' @
- 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
- n- t( y# ^7 l7 f* Y - 请输入“13”开启右上角的窗户。"
9 l9 t4 h) o* ]$ q j - " ①②③
d* g5 [6 \" q9 k& X9 R - 1■ ■
, ]$ A0 v: h, R. X; O - 2■ ■
5 w, T; Y# L. J$ j - 3 0 A' O7 } g& j( r3 {! _9 I4 V
- 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。, Q$ ?7 ^. }) ?2 A
- 请输入“31”开启左下角的窗户。"
' J; s" w9 m4 z0 i9 n - " ①②③5 j4 ` o4 C) x
- 1■ ■
% C' s4 E1 u: r- Q+ C" t - 2 ■
6 o3 \8 |% M: J* u6 Q# | - 3■■
; ]3 V' Q; b' p: S6 e - 此时,总共有5扇窗户被开启了。) y0 f6 n* T: v% T. o3 E' O% q7 j
- 请输入“33”开启右下角的窗户。"
* `' K, I& D: t - " ①②③$ u! x/ t% q7 t8 L
- 1■ ■; ?: }, Y b m
- 2
8 q& I2 l( D! W/ Q1 T - 3■ ■
e; Q; _! M+ b: I- [/ Q- D - 现在,只有四个角落的窗户被打开。( I1 i) q: t2 F8 x
- 请输入“22”完成最后一击!"7 [4 R* D5 T# M# t
- " ①②③
5 }/ `! A! f% V5 K% v3 {' \1 c& D - 1■■■
# S8 ^" K. C. l. F. ]" ]3 i* ]) U - 2■■■) O0 R8 B6 P7 {; u/ M- `
- 3■■■# X" d; G! e6 ~) H; v
- 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
s% w1 K1 M7 Z E9 h( ^) [
! E! O/ m; p; M. h& f/ t8 n- ;;; 棋盘4 |6 }7 B% L' W
- (defconst *wechat-5x5-white-chess* 122883 X6 C# T3 ?) ?. w9 ^
- " ")4 L+ ]( R4 m1 O! P' J2 B
- 9 \) f! M; {: K6 ^" q
- (defconst *wechat-5x5-black-chess* 9632/ z: \+ ]7 ~) W, M* f, I$ U
- "■")
: f0 u3 P' b% |' {, T# N ?6 o - 8 p% x1 b9 b8 W2 d0 G: |
- (defmacro with-board (&rest body)8 d6 ]9 W F. t* Z; Z
- `(with-temp-buffer. ]. t; E3 B6 W& q
- (unwind-protect
3 x2 ?7 k8 L' V. i# ` - (progn/ Z n& n5 S3 Y8 I: |3 x8 m) }/ y
- (if (session "board")
8 Q% N) l( U3 ?% k - (insert (session "board")))
* J+ g* e( ^4 `' U* s) f - ,@body)+ G* o1 ~1 V9 b% W
- (session "board" (buffer-string)))))
/ C( K) t' q0 Z9 ]. m9 @
# x0 a2 e4 j! o3 Z' y# F- (defun board-init (size) S! r2 q8 q8 N" _' p
- (session "size" size)
1 K' {4 j6 v' T7 h6 E( s - (session "step" 0)
7 v# _" u e. M/ t& O - (erase-buffer): H3 ]9 t8 s, r7 w& c/ i
- (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
# a, ?4 Q' ]9 X4 v" E( T, F - (dotimes (row size)
( H0 Z3 [8 \) R - (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
5 M& k z$ T6 g, w! F$ v! X! ]1 l - 8 a- v i& t7 t& k
- (defun board-contains-p (y x) \- ?/ }+ P/ I. U) E! j! `2 ?. J
- (let ((size (session "size")))$ R5 Z! ] P/ L- u
- (and (<= 1 y) (<= y size)1 }5 e4 b' l% G) c: b; D
- (<= 1 x) (<= x size))))
! f, v- G& U# T v- s - 1 D! n) |" Z8 }) [$ U) |! I6 M; f; i
- (defun board-toggle (y x)
2 [* l7 \2 h `) w - (when (board-contains-p y x)
Q, ?- j% S$ T. F$ | - (goto-line (1+ y))
) i1 w3 y- z& I+ Y) e' s) s& ~& v - (beginning-of-line)
. z! [. s) s$ k& o; D& a) U' o - (forward-char x)
' }& K8 V$ c+ d& v5 r& J - (insert (if (= *wechat-5x5-white-chess* (following-char))- K; x# b/ j" v9 d5 T$ i: z
- *wechat-5x5-black-chess*, y2 l( V& w1 h" F: W) {
- *wechat-5x5-white-chess*))& P( T, o& W6 ]* u+ {8 i) }
- (delete-char 1)))0 S4 \1 W" ?- [6 V; I, Y
& X9 D' [9 r1 z% p+ k- (defun board-put (y x)+ F, T3 T1 R$ f: F
- (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
) |$ z0 p3 F# c# ?8 N- k f' Y - (board-toggle (+ y (first dir))* t" Y3 ^6 [, _. Y# h9 S
- (+ x (second dir)))))
9 i9 y# W. s0 r) `" |: D ^
" k+ k0 h7 u/ {0 a* M; k; o3 w- (defun game-over-p ()
9 m2 r9 n4 `0 u% i% I7 u, V* W - (beginning-of-buffer)
$ V8 M% \+ q( ]6 I; R - (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
* y/ k! t+ F& ^! Y# l/ J+ b9 s
. B v J: R8 |! q4 U9 ?# P1 P- (defun board-show ()
/ X0 x v. Y/ F - (with-board, J6 A5 ]5 S6 r4 H6 Z5 j+ q
- (concat (buffer-string). H! t8 g% J' U6 u
- (if (game-over-p)9 h) N. ~* `. a% L2 a* V
- (format "共%d步,输入任意内容返回大厅" (session "step"))
6 y5 ]- f+ Y o+ j8 I$ r: }/ v8 s/ C9 W - (format "第%d步" (1+ (session "step")))))))4 G6 K' b1 A" F, t3 i# ?' m9 v# U- Z
- 5 x( i+ o/ w- T1 M& X- D
- (defun board-position-parse (cmd)
8 B8 r4 @$ U i - (if (= (length cmd) 2)4 |) F# a2 ~1 w! x' {
- (list (string-to-int (substring cmd 0 1))
; K, M0 \2 W# H; W' I! w, L - (string-to-int (substring cmd 1 2)))) m) e( ^' Y2 t/ D
- '(0 0)))7 |! t4 c% K( R3 V, F- u
5 X0 R. K; `" {; b' P. W1 f% c, [- ;;; 游戏房间
2 l; o8 V H9 f( l1 Z# e - (defun game-room-init (cmd)' I$ R) e1 Y0 ]4 d$ K
- (let* ((middle (string-to-int cmd))# `9 @# t7 e" Q, g/ g
- (size (1- (* 2 middle))))
5 i1 \( ?3 o" l% ~' Z9 n - (with-board
, i. \+ K: K3 @5 O L2 S - (board-init size)
4 R. C, j. r7 U# ]3 c' o% _, O) s - (board-put middle middle)))' H& Z1 C- f& Y* [5 b0 N& B
- 'game-room)! r: c8 Z, I. @) l( [0 p6 Q* L
6 O* K/ m5 ^9 @) J; I- (def-room game-room
% |" v. w8 Z: I$ R/ ~# s5 G - #'board-show
9 ] O/ p {8 K6 E- {# w9 @ - (t (lambda (cmd)
2 W2 \" J9 B/ d% V) S5 [ - (with-board
$ T& u( R E* O1 f/ } - (if (game-over-p)5 r+ \3 D* j6 r5 m% B8 G
- 'living-room" X' ^' c( v# M' U# A
- (destructuring-bind (y x) (board-position-parse cmd)( K2 E! U, k' n p8 N
- (when (board-contains-p y x)
) [3 `' }: c, X - (board-toggle y x)
* _7 s- ]) B2 F - (session "step" (1+ (session "step"))))
1 U/ h/ I" a% W7 i2 m - 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|