diff --git a/misc/wx/access.go b/misc/wx/access.go
new file mode 100644
index 00000000..d2c772ad
--- /dev/null
+++ b/misc/wx/access.go
@@ -0,0 +1,95 @@
+package wx
+
+import (
+ "crypto/sha1"
+ "encoding/hex"
+ "fmt"
+ "strings"
+ "time"
+
+ ice "github.com/shylinux/icebergs"
+ "github.com/shylinux/icebergs/base/mdb"
+ "github.com/shylinux/icebergs/base/web"
+ kit "github.com/shylinux/toolkits"
+)
+
+func _wx_sign(m *ice.Message, nonce, stamp string) string {
+ b := sha1.Sum([]byte(strings.Join(kit.Sort([]string{
+ fmt.Sprintf("jsapi_ticket=%s", m.Cmdx(ACCESS, TICKET)),
+ fmt.Sprintf("url=%s", m.Option(ice.MSG_USERWEB)),
+ fmt.Sprintf("timestamp=%s", stamp),
+ fmt.Sprintf("noncestr=%s", nonce),
+ }), "&")))
+ return hex.EncodeToString(b[:])
+}
+func _wx_config(m *ice.Message, nonce string) {
+ m.Option(APPID, m.Conf(LOGIN, kit.Keym(APPID)))
+ m.Option(SCRIPT, m.Conf(ACCESS, kit.Keym(SCRIPT)))
+ m.Option("signature", _wx_sign(m, m.Option("noncestr", nonce), m.Option("timestamp", kit.Format(time.Now().Unix()))))
+}
+
+const (
+ WEIXIN = "weixin"
+ EXPIRE = "expire"
+ TICKET = "ticket"
+ EXPIRES = "expires"
+ SCRIPT = "script"
+ CONFIG = "config"
+)
+const (
+ ERRCODE = "errcode"
+ ERRMSG = "errmsg"
+)
+const ACCESS = "access"
+
+func init() {
+ Index.Merge(&ice.Context{
+ Configs: map[string]*ice.Config{
+ ACCESS: {Name: ACCESS, Help: "认证", Value: kit.Data(
+ SCRIPT, "https://res.wx.qq.com/open/js/jweixin-1.6.0.js",
+ WEIXIN, "https://api.weixin.qq.com",
+ )},
+ },
+ Commands: map[string]*ice.Command{
+ ice.CTX_INIT: {Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
+ m.Cmd(web.SPIDE, mdb.CREATE, WEIXIN, m.Conf(ACCESS, kit.Keym(WEIXIN)))
+ }},
+ ACCESS: {Name: "access appid auto ticket token login", Help: "认证", Action: map[string]*ice.Action{
+ LOGIN: {Name: "login appid appmm token", Help: "登录", Hand: func(m *ice.Message, arg ...string) {
+ m.Conf(LOGIN, kit.Keym(APPID), m.Option(APPID))
+ m.Conf(LOGIN, kit.Keym(APPMM), m.Option(APPMM))
+ m.Conf(LOGIN, kit.Keym(TOKEN), m.Option(TOKEN))
+ }},
+ TOKEN: {Name: "token", Help: "令牌", Hand: func(m *ice.Message, arg ...string) {
+ if now := time.Now().Unix(); m.Conf(ACCESS, kit.Keym(TOKEN)) == "" || now > kit.Int64(m.Conf(ACCESS, kit.Keym(EXPIRE))) {
+ msg := m.Cmd(web.SPIDE, WEIXIN, web.SPIDE_GET, "/cgi-bin/token?grant_type=client_credential",
+ APPID, m.Conf(LOGIN, kit.Keym(APPID)), "secret", m.Conf(LOGIN, kit.Keym(APPMM)))
+ if m.Warn(msg.Append(ERRCODE) != "", msg.Append(ERRCODE), msg.Append(ERRMSG)) {
+ return
+ }
+
+ m.Conf(ACCESS, kit.Keym(EXPIRE), now+kit.Int64(msg.Append("expires_in")))
+ m.Conf(ACCESS, kit.Keym(TOKEN), msg.Append("access_token"))
+ }
+ m.Echo(m.Conf(ACCESS, kit.Keym(TOKEN)))
+ }},
+ TICKET: {Name: "ticket", Help: "票据", Hand: func(m *ice.Message, arg ...string) {
+ if now := time.Now().Unix(); m.Conf(ACCESS, kit.Keym(TICKET)) == "" || now > kit.Int64(m.Conf(ACCESS, kit.Keym(EXPIRES))) {
+ msg := m.Cmd(web.SPIDE, WEIXIN, web.SPIDE_GET, "/cgi-bin/ticket/getticket?type=jsapi", "access_token", m.Cmdx(ACCESS, TOKEN))
+ if m.Warn(msg.Append(ERRCODE) != "0", msg.Append(ERRCODE), msg.Append(ERRMSG)) {
+ return
+ }
+
+ m.Conf(ACCESS, kit.Keym(EXPIRES), now+kit.Int64(msg.Append("expires_in")))
+ m.Conf(ACCESS, kit.Keym(TICKET), msg.Append(TICKET))
+ }
+ m.Echo(m.Conf(ACCESS, kit.Keym(TICKET)))
+ }},
+ CONFIG: {Name: "config", Help: "配置", Hand: func(m *ice.Message, arg ...string) {
+ _wx_config(m, "some")
+ }},
+ }, Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
+ m.Echo(kit.Formats(m.Confv(ACCESS)))
+ }},
+ }})
+}
diff --git a/misc/wx/event.go b/misc/wx/event.go
new file mode 100644
index 00000000..f6e42e95
--- /dev/null
+++ b/misc/wx/event.go
@@ -0,0 +1,26 @@
+package wx
+
+import (
+ ice "github.com/shylinux/icebergs"
+ kit "github.com/shylinux/toolkits"
+)
+
+const EVENT = "event"
+
+func init() {
+ Index.Merge(&ice.Context{
+ Configs: map[string]*ice.Config{
+ EVENT: {Name: EVENT, Help: "事件", Value: kit.Data()},
+ },
+ Commands: map[string]*ice.Command{
+ EVENT: {Name: "event", Help: "事件", Action: map[string]*ice.Action{
+ "subscribe": {Name: "subscribe", Help: "订阅", Hand: func(m *ice.Message, arg ...string) {
+ _wx_action(m.Cmdy(MENU, "home"))
+ }},
+ "unsubscribe": {Name: "unsubscribe", Help: "取关", Hand: func(m *ice.Message, arg ...string) {
+ }},
+ }, Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
+ }},
+ }},
+ )
+}
diff --git a/misc/wx/favor.go b/misc/wx/favor.go
new file mode 100644
index 00000000..e9952d17
--- /dev/null
+++ b/misc/wx/favor.go
@@ -0,0 +1,38 @@
+package wx
+
+import (
+ ice "github.com/shylinux/icebergs"
+ "github.com/shylinux/icebergs/base/aaa"
+ "github.com/shylinux/icebergs/base/cli"
+ "github.com/shylinux/icebergs/base/mdb"
+ kit "github.com/shylinux/toolkits"
+)
+
+const FAVOR = "favor"
+
+func init() {
+ Index.Merge(&ice.Context{
+ Configs: map[string]*ice.Config{
+ FAVOR: {Name: "favor", Help: "收藏", Value: kit.Data(
+ kit.MDB_SHORT, kit.MDB_TEXT, kit.MDB_FIELD, "time,name,text",
+ )},
+ },
+ Commands: map[string]*ice.Command{
+ FAVOR: {Name: "favor text auto create", Help: "收藏", Action: map[string]*ice.Action{
+ mdb.CREATE: {Name: "create name text", Help: "添加", Hand: func(m *ice.Message, arg ...string) {
+ m.Cmdy(mdb.INSERT, m.Prefix(FAVOR), "", mdb.HASH, arg)
+ }},
+ mdb.REMOVE: {Name: "remove", Help: "删除", Hand: func(m *ice.Message, arg ...string) {
+ m.Cmdy(mdb.DELETE, m.Prefix(FAVOR), "", mdb.HASH, m.OptionSimple(kit.MDB_TEXT))
+ }},
+ }, Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
+ m.Fields(len(arg) == 0, m.Conf(FAVOR, kit.META_FIELD))
+ m.Cmdy(mdb.SELECT, m.Prefix(FAVOR), "", mdb.HASH, kit.MDB_TEXT, arg)
+ m.Table(func(index int, value map[string]string, head []string) {
+ m.PushImages(cli.QRCODE, kit.MergeURL("https://open.weixin.qq.com/qr/code", aaa.USERNAME, value[kit.MDB_TEXT]))
+ })
+ m.PushAction(mdb.REMOVE)
+ }},
+ },
+ })
+}
diff --git a/misc/wx/login.go b/misc/wx/login.go
new file mode 100644
index 00000000..d35edc7c
--- /dev/null
+++ b/misc/wx/login.go
@@ -0,0 +1,133 @@
+package wx
+
+import (
+ "crypto/sha1"
+ "encoding/hex"
+ "encoding/xml"
+ "strings"
+
+ ice "github.com/shylinux/icebergs"
+ "github.com/shylinux/icebergs/base/aaa"
+ "github.com/shylinux/icebergs/base/mdb"
+ "github.com/shylinux/icebergs/base/web"
+ "github.com/shylinux/icebergs/core/wiki"
+ kit "github.com/shylinux/toolkits"
+)
+
+func _wx_parse(m *ice.Message) {
+ data := struct {
+ FromUserName string
+ ToUserName string
+ CreateTime int64
+ MsgId int64
+ Event string
+ MsgType string
+ Content string
+ }{}
+ xml.NewDecoder(m.R.Body).Decode(&data)
+ m.Debug("data: %#v", data)
+
+ m.Option("FromUserName", data.FromUserName)
+ m.Option("ToUserName", data.ToUserName)
+ m.Option("CreateTime", data.CreateTime)
+ m.Option("MsgId", data.MsgId)
+
+ m.Option("Event", data.Event)
+ m.Option("MsgType", data.MsgType)
+ m.Option("Content", data.Content)
+}
+func _wx_reply(m *ice.Message, tmpl string) {
+ if res, err := kit.Render(m.Conf(LOGIN, kit.Keym(kit.MDB_TEMPLATE, tmpl)), m); err == nil {
+ m.Set(ice.MSG_RESULT).RenderResult(string(res))
+ }
+}
+func _wx_action(m *ice.Message) {
+ m.Option(ice.MSG_OUTPUT, ice.RENDER_RESULT)
+ m.Set(ice.MSG_RESULT)
+
+ m.Echo(`
+
+
+%s
+
+`, m.Option("ToUserName"), m.Option("FromUserName"), m.Option("CreateTime"), "news")
+
+ count := 0
+ m.Table(func(index int, value map[string]string, head []string) { count++ })
+ m.Echo(`%d`, count)
+
+ share := m.Cmdx(web.SHARE, mdb.CREATE, kit.MDB_TYPE, web.LOGIN,
+ aaa.USERNAME, m.Option(ice.MSG_USERNAME), aaa.USERROLE, m.Option(ice.MSG_USERROLE))
+
+ m.Echo(``)
+ m.Table(func(index int, value map[string]string, head []string) {
+ m.Echo(`-
+
+
+
+
+
+`, value[wiki.TITLE], value[wiki.SPARK], value[wiki.IMAGE],
+ kit.MergeURL(kit.Format(value[wiki.REFER]), web.SHARE, share))
+ })
+ m.Echo(``)
+ m.Echo(``)
+
+ m.Debug("echo: %v", m.Result())
+}
+
+const (
+ APPID = "appid"
+ APPMM = "appmm"
+ TOKEN = "token"
+)
+const LOGIN = "login"
+
+func init() {
+ Index.Merge(&ice.Context{
+ Configs: map[string]*ice.Config{
+ LOGIN: {Name: LOGIN, Help: "登录", Value: kit.Data(
+ APPID, "", APPMM, "", TOKEN, "",
+ kit.MDB_TEMPLATE, kit.Dict(TEXT, text),
+ )},
+ },
+ Commands: map[string]*ice.Command{
+ "/login/": {Name: "/login/", Help: "认证", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
+ check := kit.Sort([]string{m.Conf(LOGIN, kit.Keym(TOKEN)), m.Option("timestamp"), m.Option("nonce")})
+ if b := sha1.Sum([]byte(strings.Join(check, ""))); m.Warn(m.Option("signature") != hex.EncodeToString(b[:]), ice.ErrNotRight) {
+ return // 验证失败
+ }
+ if m.Option("echostr") != "" {
+ m.Render(ice.RENDER_RESULT, m.Option("echostr"))
+ return // 绑定验证
+ }
+
+ // 解析数据
+ _wx_parse(m)
+
+ // 用户登录
+ m.Option(ice.MSG_USERZONE, "wx")
+ aaa.UserLogin(m, m.Append("FromUserName"), "")
+
+ switch m.Option("MsgType") {
+ case EVENT: // 事件
+ m.Cmdy(EVENT, m.Option("Event"))
+
+ case TEXT: // 文本
+ cmds := kit.Split(m.Option("Content"))
+ if m.Warn(!m.Right(cmds), ice.ErrNotRight) {
+ cmds = []string{MENU, mdb.CREATE}
+ }
+ m.Cmdy(TEXT, cmds)
+ }
+ }},
+ }})
+}
+
+var text = `
+
+
+{{.Option "CreateTime"}}
+
+
+`
diff --git a/misc/wx/menu.go b/misc/wx/menu.go
new file mode 100644
index 00000000..856a7b73
--- /dev/null
+++ b/misc/wx/menu.go
@@ -0,0 +1,40 @@
+package wx
+
+import (
+ ice "github.com/shylinux/icebergs"
+ "github.com/shylinux/icebergs/base/mdb"
+ kit "github.com/shylinux/toolkits"
+)
+
+const MENU = "menu"
+
+func init() {
+ Index.Merge(&ice.Context{
+ Configs: map[string]*ice.Config{
+ MENU: {Name: MENU, Help: "菜单", Value: kit.Data(
+ kit.MDB_SHORT, kit.MDB_ZONE, kit.MDB_FIELD, "time,id,title,refer,image",
+ )},
+ },
+ Commands: map[string]*ice.Command{
+ MENU: {Name: "menu zone id auto create", Help: "菜单", Action: map[string]*ice.Action{
+ mdb.CREATE: {Name: "create zone", Help: "创建", Hand: func(m *ice.Message, arg ...string) {
+ m.Cmdy(mdb.INSERT, m.Prefix(MENU), "", mdb.HASH, arg)
+ }},
+ mdb.INSERT: {Name: "insert zone=home title=hi refer=hello image=", Help: "添加", Hand: func(m *ice.Message, arg ...string) {
+ m.Cmdy(mdb.INSERT, m.Prefix(MENU), "", mdb.HASH, m.OptionSimple(kit.MDB_ZONE))
+ m.Cmdy(mdb.INSERT, m.Prefix(MENU), kit.KeyHash(m.Option(kit.MDB_ZONE)), mdb.LIST, arg[2:])
+ }},
+ mdb.MODIFY: {Name: "modify", Help: "编辑", Hand: func(m *ice.Message, arg ...string) {
+ m.Cmdy(mdb.MODIFY, m.Prefix(MENU), "", mdb.ZONE, m.Option(kit.MDB_ZONE), m.Option(kit.MDB_ID), arg)
+ }},
+ mdb.REMOVE: {Name: "remove", Help: "删除", Hand: func(m *ice.Message, arg ...string) {
+ m.Cmdy(mdb.DELETE, m.Prefix(MENU), "", mdb.ZONE, m.OptionSimple(kit.MDB_ZONE))
+ }},
+ }, Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
+ m.Fields(len(arg) < 2, kit.Select(m.Conf(MENU, kit.META_FIELD), "time,zone,count", len(arg) == 0))
+ if m.Cmdy(mdb.SELECT, m.Prefix(MENU), "", mdb.ZONE, arg); len(arg) == 0 {
+ m.PushAction(mdb.REMOVE)
+ }
+ }},
+ }})
+}
diff --git a/misc/wx/text.go b/misc/wx/text.go
new file mode 100644
index 00000000..6022099f
--- /dev/null
+++ b/misc/wx/text.go
@@ -0,0 +1,34 @@
+package wx
+
+import (
+ ice "github.com/shylinux/icebergs"
+ "github.com/shylinux/icebergs/base/cli"
+ kit "github.com/shylinux/toolkits"
+)
+
+const TEXT = "text"
+
+func init() {
+ Index.Merge(&ice.Context{
+ Configs: map[string]*ice.Config{
+ TEXT: {Name: TEXT, Help: "文本", Value: kit.Data()},
+ },
+ Commands: map[string]*ice.Command{
+ TEXT: {Name: "text", Help: "文本", Action: map[string]*ice.Action{
+ "menu": {Name: "menu name", Help: "菜单", Hand: func(m *ice.Message, arg ...string) {
+ _wx_action(m.Cmdy(MENU, kit.Select("home", m.Option(kit.MDB_NAME))))
+ }},
+ }, Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
+ // 执行命令
+ if m.Cmdy(arg); len(m.Appendv(ice.MSG_APPEND)) == 0 && len(m.Resultv()) == 0 {
+ m.Cmdy(cli.SYSTEM, arg)
+ } else if len(m.Resultv()) == 0 {
+ m.Table()
+ }
+
+ // 返回结果
+ _wx_reply(m, TEXT)
+ }},
+ }},
+ )
+}