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(` +<![CDATA[%s]]> + + + + +`, 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) + }}, + }}, + ) +}