diff --git a/base/web/render.go b/base/web/render.go index 7badd1db..841f8d8a 100644 --- a/base/web/render.go +++ b/base/web/render.go @@ -12,10 +12,9 @@ import ( ) const ( - REDIRECT = "redirect" - REFRESH = "refresh" - STATUS = "status" - COOKIE = "cookie" + REFRESH = "refresh" + STATUS = "status" + COOKIE = "cookie" ) func Render(msg *ice.Message, cmd string, args ...interface{}) { @@ -24,7 +23,7 @@ func Render(msg *ice.Message, cmd string, args ...interface{}) { } switch arg := kit.Simple(args...); cmd { - case REDIRECT: // url [arg...] + case ice.RENDER_REDIRECT: // url [arg...] http.Redirect(msg.W, msg.R, kit.MergeURL(arg[0], arg[1:]), 307) case REFRESH: // [delay [text]] diff --git a/base/web/share.go b/base/web/share.go index 0c902446..a1998cc2 100644 --- a/base/web/share.go +++ b/base/web/share.go @@ -181,10 +181,10 @@ func init() { switch msg.Append(kit.MDB_TYPE) { case LOGIN, RIVER: - m.Render(REDIRECT, "/", list) + m.RenderRedirect("/", list) case STORM: - m.Render(REDIRECT, "/page/share.html", SHARE, m.Option(SHARE)) + m.RenderRedirect("/page/share.html", SHARE, m.Option(SHARE)) } }}, diff --git a/conf.go b/conf.go index f04596e3..6f95c2c8 100644 --- a/conf.go +++ b/conf.go @@ -126,8 +126,9 @@ const ( // RENDER RENDER_VIDEOS = "_videos" RENDER_QRCODE = "_qrcode" RENDER_SCRIPT = "_script" - RENDER_DOWNLOAD = "_download" RENDER_TEMPLATE = "_template" + RENDER_DOWNLOAD = "_download" + RENDER_REDIRECT = "_redirect" ) const ( // PROCESS PROCESS_REFRESH = "_refresh" diff --git a/misc.go b/misc.go index a9f77ddf..23601525 100644 --- a/misc.go +++ b/misc.go @@ -257,9 +257,7 @@ func (m *Message) Render(cmd string, args ...interface{}) *Message { if len(args) == 1 { args = append(args, m) } - m.Debug("what %v", args) if res, err := kit.Render(args[0].(string), args[1]); m.Assert(err) { - m.Debug("what") m.Echo(string(res)) } } @@ -274,6 +272,12 @@ func (m *Message) RenderTemplate(args ...interface{}) *Message { func (m *Message) RenderDownload(args ...interface{}) *Message { return m.Render(RENDER_DOWNLOAD, args...) } +func (m *Message) RenderRedirect(args ...interface{}) *Message { + return m.Render(RENDER_REDIRECT, args...) +} +func (m *Message) RenderIndex(serve, repos string) *Message { + return m.RenderDownload(path.Join(m.Conf(serve, kit.Keym(repos, kit.SSH_PATH)), m.Conf(serve, kit.Keym(repos, kit.SSH_INDEX)))) +} type Sort struct { Fields string diff --git a/misc/lark/app.go b/misc/lark/app.go new file mode 100644 index 00000000..e6c456d0 --- /dev/null +++ b/misc/lark/app.go @@ -0,0 +1,55 @@ +package lark + +import ( + "time" + + ice "github.com/shylinux/icebergs" + "github.com/shylinux/icebergs/base/mdb" + "github.com/shylinux/icebergs/base/web" + kit "github.com/shylinux/toolkits" +) + +const ( + LOGIN = "login" + APPID = "appid" + APPMM = "appmm" + TOKEN = "token" + EXPIRE = "expire" +) +const APP = "app" + +func init() { + Index.Merge(&ice.Context{ + Configs: map[string]*ice.Config{ + APP: {Name: APP, Help: "服务配置", Value: kit.Data( + kit.MDB_SHORT, APPID, kit.MDB_FIELD, "time,appid,appmm,duty,token,expire", + LARK, "https://open.feishu.cn/", + )}, + }, + 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, LARK, m.Conf(APP, kit.Keym(LARK))) + }}, + APP: {Name: "app appid auto token login", Help: "应用", Action: map[string]*ice.Action{ + LOGIN: {Name: "login appid appmm duty", Help: "登录", Hand: func(m *ice.Message, arg ...string) { + m.Cmdy(mdb.INSERT, m.Prefix(APP), "", mdb.HASH, arg) + }}, + TOKEN: {Name: "token appid", Help: "令牌", Hand: func(m *ice.Message, arg ...string) { + msg := m.Cmd(APP, m.Option(APPID)) + if now := time.Now().Unix(); msg.Append(TOKEN) == "" || now > kit.Int64(msg.Append(EXPIRE)) { + sub := m.Cmd(web.SPIDE, LARK, web.SPIDE_POST, "/open-apis/auth/v3/tenant_access_token/internal/", + APP_ID, msg.Append(APPID), "app_secret", msg.Append(APPMM)) + + m.Cmd(mdb.MODIFY, m.Prefix(APP), "", mdb.HASH, m.OptionSimple(APPID), + TOKEN, sub.Append("tenant_access_token"), EXPIRE, now+kit.Int64(sub.Append(EXPIRE))) + msg.Append(TOKEN, sub.Append("tenant_access_token")) + } + m.Echo(msg.Append(TOKEN)) + }}, + }, Hand: func(m *ice.Message, c *ice.Context, key string, arg ...string) { + m.Fields(len(arg) == 0, m.Conf(APP, kit.META_FIELD)) + m.Cmdy(mdb.SELECT, m.Prefix(APP), "", mdb.HASH, APPID, arg) + }}, + }, + }) +} diff --git a/misc/lark/company.go b/misc/lark/company.go new file mode 100644 index 00000000..8b512ab5 --- /dev/null +++ b/misc/lark/company.go @@ -0,0 +1,61 @@ +package lark + +import ( + ice "github.com/shylinux/icebergs" + "github.com/shylinux/icebergs/base/aaa" + kit "github.com/shylinux/toolkits" +) + +func _company_list(m *ice.Message, appid string) { + _, data := _lark_get(m, appid, "/open-apis/contact/v1/scope/get/") + + kit.Fetch(kit.Value(data, "data.authed_departments"), func(index int, ship_id string) { + _, data := _lark_get(m, appid, "/open-apis/contact/v1/department/detail/batch_get", "department_ids", ship_id) + kit.Fetch(kit.Value(data, "data.department_infos"), func(index int, value map[string]interface{}) { + m.Push(SHIP_ID, ship_id) + m.Push(kit.MDB_NAME, value[kit.MDB_NAME]) + m.Push(kit.MDB_COUNT, value["member_count"]) + m.Push(CHAT_ID, value[CHAT_ID]) + }) + }) + m.Sort(kit.MDB_NAME) +} +func _company_members(m *ice.Message, appid string, ship_id string) { + _, data := _lark_get(m, appid, "/open-apis/contact/v1/department/user/list", + "department_id", ship_id, "page_size", "100", "fetch_child", "true") + + kit.Fetch(kit.Value(data, "data.user_list"), func(index int, value map[string]interface{}) { + msg := m.Cmd(EMPLOYEE, appid, value[OPEN_ID]) + m.PushImages(aaa.AVATAR, msg.Append("avatar_72")) + m.Push(aaa.GENDER, kit.Select("女", "男", msg.Append(aaa.GENDER) == "1")) + m.Push(kit.MDB_NAME, msg.Append(kit.MDB_NAME)) + m.Push(kit.MDB_TEXT, msg.Append("description")) + m.Push(OPEN_ID, msg.Append(OPEN_ID)) + }) + m.Sort(kit.MDB_NAME) +} + +const COMPANY = "company" + +func init() { + Index.Merge(&ice.Context{Commands: map[string]*ice.Command{ + COMPANY: {Name: "company appid ship_id open_id text auto", Help: "组织", Hand: func(m *ice.Message, c *ice.Context, key string, arg ...string) { + switch len(arg) { + case 0: // 应用列表 + m.Cmdy(APP) + + case 1: // 组织列表 + _company_list(m, arg[0]) + + case 2: // 员工列表 + _company_members(m, arg[0], arg[1]) + + case 3: // 员工详情 + m.Cmdy(EMPLOYEE, arg[0], arg[2]) + + default: // 员工通知 + m.Cmdy(SEND, arg[0], OPEN_ID, arg[2], arg[3:]) + } + }}, + }}) +} diff --git a/misc/lark/duty.go b/misc/lark/duty.go new file mode 100644 index 00000000..447a5bc1 --- /dev/null +++ b/misc/lark/duty.go @@ -0,0 +1,21 @@ +package lark + +import ( + ice "github.com/shylinux/icebergs" +) + +const DUTY = "duty" + +func init() { + Index.Merge(&ice.Context{Commands: map[string]*ice.Command{ + DUTY: {Name: "duty appid [title] text auto", Help: "通告", Hand: func(m *ice.Message, c *ice.Context, key string, arg ...string) { + if len(arg) < 2 { + m.Cmdy(APP) + return + } + + duty := m.Cmd(APP, arg[0]).Append(DUTY) + m.Cmdy(SEND, arg[0], duty, arg[1:]) + }}, + }}) +} diff --git a/misc/lark/employee.go b/misc/lark/employee.go new file mode 100644 index 00000000..e1b09a94 --- /dev/null +++ b/misc/lark/employee.go @@ -0,0 +1,46 @@ +package lark + +import ( + "strings" + + ice "github.com/shylinux/icebergs" + "github.com/shylinux/icebergs/base/mdb" + kit "github.com/shylinux/toolkits" +) + +func _employee_info(m *ice.Message, appid string, arg ...string) { + for _, id := range arg { + _, data := _lark_get(m, appid, "/open-apis/contact/v1/user/batch_get", "open_ids", id) + kit.Fetch(kit.Value(data, "data.user_infos"), func(index int, value map[string]interface{}) { + m.Push(mdb.DETAIL, value) + }) + } +} +func _employee_openid(m *ice.Message, appid string, arg ...string) { + us := []string{} + for i := 0; i < len(arg); i++ { + us = append(us, kit.Select("mobiles", "emails", strings.Contains(arg[i], "@")), arg[i]) + } + + _lark_get(m, appid, "/open-apis/user/v1/batch_get_id", us) + for i := 0; i < len(arg); i++ { + m.Echo(m.Append(kit.Keys("data.mobile_users", arg[i], "0.open_id"))) + } +} + +const EMPLOYEE = "employee" + +func init() { + Index.Merge(&ice.Context{Commands: map[string]*ice.Command{ + EMPLOYEE: {Name: "employee appid open_id|mobile|email auto", Help: "员工", Hand: func(m *ice.Message, c *ice.Context, key string, arg ...string) { + if len(arg) < 2 { + return + } + if strings.HasPrefix(arg[1], "ou_") { + _employee_info(m, arg[0], arg[1:]...) + } else { + _employee_openid(m, arg[0], arg[1:]...) + } + }}, + }}) +} diff --git a/misc/lark/event.go b/misc/lark/event.go new file mode 100644 index 00000000..4513daae --- /dev/null +++ b/misc/lark/event.go @@ -0,0 +1,38 @@ +package lark + +import ( + ice "github.com/shylinux/icebergs" + kit "github.com/shylinux/toolkits" +) + +const ( + P2P_CHAT_CREATE = "p2p_chat_create" + MESSAGE_READ = "message_read" + CHAT_DISBAND = "chat_disband" + ADD_BOT = "add_bot" + MSG_TYPE = "msg_type" +) +const EVENT = "event" + +func init() { + Index.Merge(&ice.Context{Commands: map[string]*ice.Command{ + EVENT: {Name: "event", Help: "事件", Action: map[string]*ice.Action{ + P2P_CHAT_CREATE: {Name: "", Help: "", Hand: func(m *ice.Message, arg ...string) { + if m.Options(OPEN_CHAT_ID) { + m.Cmdy(SEND, m.Option(APP_ID), m.Option(OPEN_CHAT_ID), m.Conf(APP, kit.Keym(kit.MDB_TEMPLATE, m.Option(kit.MDB_TYPE)))) + } + }}, + MESSAGE_READ: {Name: "", Help: "", Hand: func(m *ice.Message, arg ...string) { + }}, + CHAT_DISBAND: {Name: "", Help: "", Hand: func(m *ice.Message, arg ...string) { + }}, + ADD_BOT: {Name: "", Help: "", Hand: func(m *ice.Message, arg ...string) { + if m.Options(OPEN_CHAT_ID) { + m.Cmdy(SEND, m.Option(APP_ID), m.Option(OPEN_CHAT_ID), m.Conf(APP, kit.Keym(kit.MDB_TEMPLATE, m.Option(kit.MDB_TYPE)))) + } + }}, + }, Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + m.Cmdy(MSG, m.Option(MSG_TYPE)) + }}, + }}) +} diff --git a/misc/lark/form.go b/misc/lark/form.go new file mode 100644 index 00000000..43cb8cfb --- /dev/null +++ b/misc/lark/form.go @@ -0,0 +1,78 @@ +package lark + +import ( + ice "github.com/shylinux/icebergs" + "github.com/shylinux/icebergs/base/aaa" + "github.com/shylinux/icebergs/base/web" + kit "github.com/shylinux/toolkits" +) + +const FORM = "form" + +func init() { + Index.Merge(&ice.Context{Commands: map[string]*ice.Command{ + FORM: {Name: "form [chat_id|open_id|user_id|email] target title text [confirm|value|url arg...]...", Help: "消息", Hand: func(m *ice.Message, c *ice.Context, key string, arg ...string) { + var form = kit.Dict(CONTENT, kit.Dict()) + switch arg[0] { + case CHAT_ID, OPEN_ID, USER_ID, aaa.EMAIL: + form[arg[0]], arg = arg[1], arg[2:] + default: + form[CHAT_ID], arg = arg[0], arg[1:] + } + + elements := []interface{}{} + elements = append(elements, kit.Dict( + "tag", "div", "text", kit.Dict( + "tag", "plain_text", CONTENT, kit.Select(" ", arg[1]), + ), + )) + + actions := []interface{}{} + for i := 2; i < len(arg); i++ { + button := kit.Dict( + "tag", "button", "type", "default", "text", kit.Dict( + "tag", "plain_text", CONTENT, kit.Select(" ", arg[i]), + ), + ) + + content := arg[i] + switch arg[i+1] { + case "confirm": + button[arg[i+1]], i = kit.Dict( + "title", kit.Dict("tag", "lark_md", CONTENT, arg[i+2]), + "text", kit.Dict("tag", "lark_md", CONTENT, arg[i+3]), + ), i+3 + case "value": + button[arg[i+1]], i = kit.Dict(arg[i+2], arg[i+3]), i+3 + case "url": + button[arg[i+1]], i = arg[i+2], i+2 + default: + button["value"], i = kit.Dict( + ice.MSG_RIVER, m.Option(ice.MSG_RIVER), + ice.MSG_STORM, m.Option(ice.MSG_STORM), + arg[i+1], arg[i+2], + ), i+2 + } + kit.Value(button, "value.content", content) + kit.Value(button, "value.open_chat_id", m.Option(OPEN_CHAT_ID)) + kit.Value(button, "value.description", arg[1]) + kit.Value(button, "value.title", arg[0]) + + actions = append(actions, button) + } + elements = append(elements, kit.Dict("tag", "action", "actions", actions)) + + kit.Value(form, "msg_type", "interactive") + kit.Value(form, "card", kit.Dict( + "config", kit.Dict("wide_screen_mode", true), + "header", kit.Dict( + "title", kit.Dict("tag", "lark_md", CONTENT, arg[0]), + ), + "elements", elements, + )) + + msg := _lark_post(m, m.Option(APP_ID), "/open-apis/message/v4/send/", web.SPIDE_DATA, kit.Formats(form)) + m.Debug("%v", msg.Optionv(web.SPIDE_RES)) + }}, + }}) +} diff --git a/misc/lark/group.go b/misc/lark/group.go new file mode 100644 index 00000000..4321092c --- /dev/null +++ b/misc/lark/group.go @@ -0,0 +1,56 @@ +package lark + +import ( + ice "github.com/shylinux/icebergs" + "github.com/shylinux/icebergs/base/aaa" + kit "github.com/shylinux/toolkits" +) + +func _group_list(m *ice.Message, appid string) { + _, data := _lark_get(m, appid, "/open-apis/chat/v4/list") + kit.Fetch(kit.Value(data, "data.groups"), func(index int, value map[string]interface{}) { + m.Push(CHAT_ID, value[CHAT_ID]) + m.PushImages(aaa.AVATAR, kit.Format(value[aaa.AVATAR]), "72") + m.Push(kit.MDB_NAME, value[kit.MDB_NAME]) + m.Push(kit.MDB_TEXT, value["description"]) + m.Push(OPEN_ID, value["owner_open_id"]) + }) + m.Sort(kit.MDB_NAME) +} +func _group_members(m *ice.Message, appid string, chat_id string) { + _, data := _lark_get(m, appid, "/open-apis/chat/v4/info", "chat_id", chat_id) + kit.Fetch(kit.Value(data, "data.members"), func(index int, value map[string]interface{}) { + msg := m.Cmd(EMPLOYEE, appid, value[OPEN_ID]) + m.PushImages(aaa.AVATAR, msg.Append("avatar_72")) + m.Push(aaa.GENDER, kit.Select("女", "男", msg.Append(aaa.GENDER) == "1")) + m.Push(kit.MDB_NAME, msg.Append(kit.MDB_NAME)) + m.Push(kit.MDB_TEXT, msg.Append("description")) + m.Push(OPEN_ID, msg.Append(OPEN_ID)) + }) + m.Sort(kit.MDB_NAME) +} + +const GROUP = "group" + +func init() { + Index.Merge(&ice.Context{Commands: map[string]*ice.Command{ + GROUP: {Name: "group appid chat_id open_id text auto", Help: "群组", Hand: func(m *ice.Message, c *ice.Context, key string, arg ...string) { + switch len(arg) { + case 0: // 应用列表 + m.Cmdy(APP) + + case 1: // 群组列表 + _group_list(m, arg[0]) + + case 2: // 组员列表 + _group_members(m, arg[0], arg[1]) + + case 3: // 组员详情 + m.Cmdy(EMPLOYEE, arg[0], arg[2]) + + default: // 组员通知 + m.Cmdy(SEND, arg[0], OPEN_ID, arg[2], arg[3:]) + } + }}, + }}) +} diff --git a/misc/lark/home.go b/misc/lark/home.go new file mode 100644 index 00000000..60df83cd --- /dev/null +++ b/misc/lark/home.go @@ -0,0 +1,40 @@ +package lark + +import ( + ice "github.com/shylinux/icebergs" + "github.com/shylinux/icebergs/base/web" + "github.com/shylinux/icebergs/core/chat" + kit "github.com/shylinux/toolkits" +) + +const HOME = "home" + +func init() { + Index.Merge(&ice.Context{Commands: map[string]*ice.Command{ + HOME: {Name: "home river storm title content", Help: "首页", Hand: func(m *ice.Message, c *ice.Context, key string, arg ...string) { + name := kit.Select(m.Option(ice.MSG_USERNAME), m.Option(ice.MSG_USERNICK)) + if len(name) > 10 { + name = name[:10] + } + name += "的" + kit.Select("应用列表", arg, 2) + + text, link, list := kit.Select("", arg, 3), kit.MergeURL2(m.Conf(web.SHARE, kit.Keym(kit.MDB_DOMAIN)), "/chat/lark/sso"), []string{} + if len(arg) == 0 { + m.Cmd("web.chat./river").Table(func(index int, val map[string]string, head []string) { + m.Cmd("web.chat./river", val[kit.MDB_HASH], chat.TOOL).Table(func(index int, value map[string]string, head []string) { + list = append(list, kit.Keys(val[kit.MDB_NAME], value[kit.MDB_NAME]), + kit.SSH_CMD, kit.Format([]string{HOME, val[kit.MDB_HASH], value[kit.MDB_HASH], val[kit.MDB_NAME] + "." + value[kit.MDB_NAME]})) + }) + }) + } else { + m.Option(ice.MSG_RIVER, arg[0]) + m.Option(ice.MSG_STORM, arg[1]) + link = kit.MergeURL(link, chat.RIVER, arg[0], chat.STORM, arg[1]) + m.Cmd("web.chat./river", arg[0], chat.TOOL, arg[1]).Table(func(index int, value map[string]string, head []string) { + list = append(list, value[kit.SSH_CMD], kit.SSH_CMD, kit.Keys(value[kit.SSH_CTX], value[kit.SSH_CMD])) + }) + } + m.Cmd(FORM, CHAT_ID, m.Option(OPEN_CHAT_ID), name, text, "打开网页", "url", link, list) + }}, + }}) +} diff --git a/misc/lark/lark.go b/misc/lark/lark.go index 461b410e..85b69c22 100644 --- a/misc/lark/lark.go +++ b/misc/lark/lark.go @@ -2,468 +2,20 @@ package lark import ( ice "github.com/shylinux/icebergs" - "github.com/shylinux/icebergs/base/aaa" - "github.com/shylinux/icebergs/base/cli" - "github.com/shylinux/icebergs/base/mdb" "github.com/shylinux/icebergs/base/web" "github.com/shylinux/icebergs/core/chat" - "github.com/shylinux/icebergs/core/wiki" - kit "github.com/shylinux/toolkits" - - "encoding/json" - "math/rand" - "net/http" - "strings" - "time" -) - -func _lark_get(m *ice.Message, bot string, arg ...interface{}) (*ice.Message, interface{}) { - m.Option(web.SPIDE_HEADER, "Authorization", "Bearer "+m.Cmdx(APP, TOKEN, bot), web.ContentType, web.ContentJSON) - msg := m.Cmd(web.SPIDE, LARK, http.MethodGet, arg) - return msg, msg.Optionv(web.SPIDE_RES) -} -func _lark_post(m *ice.Message, bot string, arg ...interface{}) *ice.Message { - m.Option(web.SPIDE_HEADER, "Authorization", "Bearer "+m.Cmdx(APP, TOKEN, bot), web.ContentType, web.ContentJSON) - return m.Cmd(web.SPIDE, LARK, arg) -} -func _lark_parse(m *ice.Message) { - data := m.Optionv(ice.MSG_USERDATA) - if data == nil { - json.NewDecoder(m.R.Body).Decode(&data) - m.Optionv(ice.MSG_USERDATA, data) - - switch d := data.(type) { - case map[string]interface{}: - for k, v := range d { - switch d := v.(type) { - case map[string]interface{}: - for k, v := range d { - m.Add(ice.MSG_OPTION, k, kit.Format(v)) - } - default: - for _, v := range kit.Simple(v) { - m.Add(ice.MSG_OPTION, kit.Keys("msg", k), kit.Format(v)) - } - } - } - } - } - m.Debug("msg: %v", kit.Format(data)) -} - -const ( - P2P_CHAT_CREATE = "p2p_chat_create" - ADD_BOT = "add_bot" -) -const ( - SHIP_ID = "ship_id" - OPEN_ID = "open_id" - CHAT_ID = "chat_id" - OPEN_CHAT_ID = "open_chat_id" - USER_OPEN_ID = "user_open_id" -) -const ( - LOGIN = "login" - APPID = "appid" - APPMM = "appmm" - TOKEN = "token" - EXPIRE = "expire" -) -const ( - APP = "app" - COMPANY = "company" - EMPLOYEE = "employee" - GROUP = "group" - - SEND = "send" - DUTY = "duty" - HOME = "home" - FORM = "form" - TALK = "talk" - RAND = "rand" ) const LARK = "lark" var Index = &ice.Context{Name: LARK, Help: "机器人", - Configs: map[string]*ice.Config{ - APP: {Name: APP, Help: "服务配置", Value: kit.Data(kit.MDB_SHORT, kit.MDB_NAME, - LARK, "https://open.feishu.cn", DUTY, "", kit.MDB_TEMPLATE, kit.Dict( - ADD_BOT, "我来也~", P2P_CHAT_CREATE, "让我们做好朋友吧~", - ), - )}, - COMPANY: {Name: COMPANY, Help: "组织配置", Value: kit.Data(kit.MDB_SHORT, SHIP_ID)}, - EMPLOYEE: {Name: EMPLOYEE, Help: "员工配置", Value: kit.Data(kit.MDB_SHORT, OPEN_ID)}, - }, Commands: map[string]*ice.Command{ ice.CTX_INIT: {Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { m.Load() - m.Cmd(web.SPIDE, mdb.CREATE, LARK, m.Conf(APP, kit.Keym(LARK))) }}, ice.CTX_EXIT: {Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { m.Save() }}, - - APP: {Name: "app name auto token login", Help: "应用", Action: map[string]*ice.Action{ - LOGIN: {Name: "login name appid appmm", Help: "登录", Hand: func(m *ice.Message, arg ...string) { - m.Cmdy(mdb.INSERT, m.Prefix(APP), "", mdb.HASH, arg) - }}, - TOKEN: {Name: "token name", Help: "令牌", Hand: func(m *ice.Message, arg ...string) { - m.Option(mdb.FIELDS, "time,appid,appmm,token,expire") - msg := m.Cmd(mdb.SELECT, m.Prefix(APP), "", mdb.HASH, kit.MDB_NAME, m.Option(kit.MDB_NAME)) - if now := time.Now().Unix(); msg.Append(TOKEN) == "" || now > kit.Int64(msg.Append(EXPIRE)) { - sub := m.Cmd(web.SPIDE, LARK, "/open-apis/auth/v3/tenant_access_token/internal/", - "app_id", msg.Append(APPID), "app_secret", msg.Append(APPMM)) - - m.Cmd(mdb.MODIFY, m.Prefix(APP), "", mdb.HASH, kit.MDB_NAME, m.Option(kit.MDB_NAME), - TOKEN, msg.Append(TOKEN, sub.Append("tenant_access_token")), EXPIRE, now+kit.Int64(sub.Append(EXPIRE))) - } - m.Echo(msg.Append(TOKEN)) - }}, - }, Hand: func(m *ice.Message, c *ice.Context, key string, arg ...string) { - m.Option(mdb.FIELDS, kit.Select("time,name,appid,token,expire", mdb.DETAIL, len(arg) > 0)) - m.Cmdy(mdb.SELECT, m.Prefix(APP), "", mdb.HASH, kit.MDB_NAME, arg) - }}, - COMPANY: {Name: "company ship_id open_id text auto", Help: "组织", Action: map[string]*ice.Action{ - "info": {Name: "info ship_id", Hand: func(m *ice.Message, arg ...string) { - _, data := _lark_get(m, "bot", "/open-apis/contact/v1/department/detail/batch_get", "department_ids", m.Option(SHIP_ID)) - kit.Fetch(kit.Value(data, "data.department_infos"), func(index int, value map[string]interface{}) { - m.Push("", value) - }) - }}, - "list": {Name: "list ship_id", Hand: func(m *ice.Message, arg ...string) { - _, data := _lark_get(m, "bot", "/open-apis/contact/v1/department/user/list", - "department_id", m.Option(SHIP_ID), "page_size", "100", "fetch_child", "true") - - kit.Fetch(kit.Value(data, "data.user_list"), func(index int, value map[string]interface{}) { - msg := m.Cmd(EMPLOYEE, value[OPEN_ID]) - m.PushImages(aaa.AVATAR, msg.Append("avatar_72")) - m.Push(aaa.GENDER, kit.Select("女", "男", msg.Append(aaa.GENDER) == "1")) - m.Push(kit.MDB_NAME, msg.Append(kit.MDB_NAME)) - m.Push(kit.MDB_TEXT, msg.Append("description")) - m.Push(OPEN_ID, msg.Append(OPEN_ID)) - }) - }}, - }, Hand: func(m *ice.Message, c *ice.Context, key string, arg ...string) { - if len(arg) == 0 { // 组织列表 - _, data := _lark_get(m, "bot", "/open-apis/contact/v1/scope/get/") - kit.Fetch(kit.Value(data, "data.authed_departments"), func(index int, value string) { - m.Push(SHIP_ID, value) - msg := m.Cmd(COMPANY, "info", value) - m.Push(kit.MDB_NAME, msg.Append(kit.MDB_NAME)) - m.Push(kit.MDB_COUNT, msg.Append("member_count")) - m.Push(CHAT_ID, msg.Append(CHAT_ID)) - }) - m.Sort(kit.MDB_NAME) - - } else if len(arg) == 1 { // 员工列表 - m.Cmdy(COMPANY, "list", arg[0]) - - } else if len(arg) == 2 { // 员工详情 - m.Cmdy(EMPLOYEE, arg[1]) - - } else { // 员工通知 - m.Cmdy(SEND, OPEN_ID, arg[1], arg[2:]) - } - }}, - EMPLOYEE: {Name: "employee open_id|mobile|email auto", Help: "员工", Hand: func(m *ice.Message, c *ice.Context, key string, arg ...string) { - if len(arg) == 0 { - return - } - if strings.HasPrefix(arg[0], "ou_") { - _, data := _lark_get(m, "bot", "/open-apis/contact/v1/user/batch_get", "open_ids", arg[0]) - kit.Fetch(kit.Value(data, "data.user_infos"), func(index int, value map[string]interface{}) { - m.Push(mdb.DETAIL, value) - }) - return - } - - us := []string{} - for i := 0; i < len(arg); i++ { - us = append(us, kit.Select("mobiles", "emails", strings.Contains(arg[i], "@")), arg[i]) - } - - _lark_get(m, "bot", "/open-apis/user/v1/batch_get_id", us) - for i := 0; i < len(arg); i++ { - m.Echo(m.Append(kit.Keys("data.mobile_users", arg[i], "0.open_id"))) - } - }}, - GROUP: {Name: "group chat_id open_id text auto", Help: "群组", Action: map[string]*ice.Action{ - "list": {Name: "list chat_id", Hand: func(m *ice.Message, arg ...string) { - _, data := _lark_get(m, "bot", "/open-apis/chat/v4/info", "chat_id", m.Option(CHAT_ID)) - kit.Fetch(kit.Value(data, "data.members"), func(index int, value map[string]interface{}) { - msg := m.Cmd(EMPLOYEE, value[OPEN_ID]) - m.PushImages(aaa.AVATAR, msg.Append("avatar_72")) - m.Push(aaa.GENDER, kit.Select("女", "男", msg.Append(aaa.GENDER) == "1")) - m.Push(kit.MDB_NAME, msg.Append(kit.MDB_NAME)) - m.Push(kit.MDB_TEXT, msg.Append("description")) - m.Push(OPEN_ID, msg.Append(OPEN_ID)) - }) - }}, - }, Hand: func(m *ice.Message, c *ice.Context, key string, arg ...string) { - if len(arg) == 0 { // 群组列表 - _, data := _lark_get(m, "bot", "/open-apis/chat/v4/list") - kit.Fetch(kit.Value(data, "data.groups"), func(index int, value map[string]interface{}) { - m.Push(CHAT_ID, value[CHAT_ID]) - m.PushImages(aaa.AVATAR, kit.Format(value[aaa.AVATAR]), "72") - m.Push(kit.MDB_NAME, value[kit.MDB_NAME]) - m.Push(kit.MDB_TEXT, value["description"]) - m.Push(OPEN_ID, value["owner_open_id"]) - }) - m.Sort(kit.MDB_NAME) - - } else if len(arg) == 1 { // 组员列表 - m.Cmdy(GROUP, "list", arg[0]) - - } else if len(arg) == 2 { // 组员详情 - m.Cmdy(EMPLOYEE, arg[1]) - - } else { // 组员通知 - m.Cmdy(SEND, CHAT_ID, arg[0], arg[2:]) - } - }}, - - SEND: {Name: "send [chat_id|open_id|user_id|email] target [title] text", Help: "消息", Hand: func(m *ice.Message, c *ice.Context, key string, arg ...string) { - var form = kit.Dict("content", kit.Dict()) - switch arg[0] { - case CHAT_ID, OPEN_ID, "user_id", "email": - form[arg[0]], arg = arg[1], arg[2:] - default: - form[CHAT_ID], arg = arg[0], arg[1:] - } - - switch len(arg) { - case 0: - case 1: - kit.Value(form, "msg_type", "text") - kit.Value(form, "content.text", arg[0]) - if strings.TrimSpace(arg[0]) == "" { - return - } - default: - if len(arg) == 2 && strings.TrimSpace(arg[1]) == "" { - return - } - content := []interface{}{} - line := []interface{}{} - for _, v := range arg[1:] { - if v == "\n" { - content, line = append(content, line), []interface{}{} - continue - } - line = append(line, map[string]interface{}{"tag": "text", "text": v + " "}) - } - content = append(content, line) - - kit.Value(form, "msg_type", "post") - kit.Value(form, "content.post", map[string]interface{}{ - "zh_cn": map[string]interface{}{"title": arg[0], "content": content}, - }) - } - - m.Copy(_lark_post(m, "bot", "/open-apis/message/v4/send/", web.SPIDE_DATA, kit.Format(form))) - }}, - DUTY: {Name: "duty [title] text", Help: "通告", Hand: func(m *ice.Message, c *ice.Context, key string, arg ...string) { - m.Cmdy(SEND, m.Conf(APP, kit.Keym(DUTY)), arg) - }}, - HOME: {Name: "home river storm title", Help: "首页", Hand: func(m *ice.Message, c *ice.Context, key string, arg ...string) { - name := kit.Select(m.Option(ice.MSG_USERNAME), m.Option(ice.MSG_USERNICK)) - if len(name) > 10 { - name = name[:10] - } - name += "的" + kit.Select("应用列表", arg, 2) - text := "" - - link, list := m.Conf(web.SHARE, "meta.domain"), []string{} - if len(arg) == 0 { - m.Cmd("web.chat./river").Table(func(index int, val map[string]string, head []string) { - m.Cmd("web.chat./river", val[kit.MDB_HASH], chat.TOOL).Table(func(index int, value map[string]string, head []string) { - list = append(list, kit.Keys(val[kit.MDB_NAME], value[kit.MDB_NAME]), - kit.SSH_CMD, kit.Format([]string{"home", val[kit.MDB_HASH], value[kit.MDB_HASH], val[kit.MDB_NAME] + "." + value[kit.MDB_NAME]})) - }) - }) - } else { - m.Option(ice.MSG_RIVER, arg[0]) - m.Option(ice.MSG_STORM, arg[1]) - link = kit.MergeURL(link, chat.RIVER, arg[0], chat.STORM, arg[1]) - m.Cmd("web.chat./river", arg[0], chat.TOOL, arg[1]).Table(func(index int, value map[string]string, head []string) { - list = append(list, value[kit.SSH_CMD], kit.SSH_CMD, kit.Keys(value[kit.SSH_CTX], value[kit.SSH_CMD])) - }) - } - m.Cmd(FORM, CHAT_ID, m.Option(OPEN_CHAT_ID), name, text, "打开网页", "url", link, list) - }}, - FORM: {Name: "form chat_id|open_id|user_id|email target title text [confirm|value|url arg...]...", Help: "消息", Hand: func(m *ice.Message, c *ice.Context, key string, arg ...string) { - var form = map[string]interface{}{"content": map[string]interface{}{}} - switch arg[0] { - case CHAT_ID, OPEN_ID, "user_id", "email": - form[arg[0]], arg = arg[1], arg[2:] - default: - form[CHAT_ID], arg = arg[0], arg[1:] - } - - elements := []interface{}{} - elements = append(elements, map[string]interface{}{ - "tag": "div", "text": map[string]interface{}{ - "tag": "plain_text", "content": arg[1], - }, - }) - - actions := []interface{}{} - for i := 2; i < len(arg); i++ { - button := map[string]interface{}{ - "type": "default", "tag": "button", "text": map[string]interface{}{ - "tag": "plain_text", "content": arg[i], - }, - } - - content := arg[i] - switch arg[i+1] { - case "confirm": - button[arg[i+1]], i = map[string]interface{}{ - "title": map[string]interface{}{"tag": "lark_md", "content": arg[i+2]}, - "text": map[string]interface{}{"tag": "lark_md", "content": arg[i+3]}, - }, i+3 - case "value": - button[arg[i+1]], i = map[string]interface{}{arg[i+2]: arg[i+3]}, i+3 - case "url": - button[arg[i+1]], i = arg[i+2], i+2 - default: - button["value"], i = map[string]interface{}{ - arg[i+1]: arg[i+2], - ice.MSG_RIVER: m.Option(ice.MSG_RIVER), - ice.MSG_STORM: m.Option(ice.MSG_STORM), - }, i+2 - } - kit.Value(button, "value.content", content) - kit.Value(button, "value.open_chat_id", m.Option(OPEN_CHAT_ID)) - kit.Value(button, "value.description", arg[1]) - kit.Value(button, "value.title", arg[0]) - - actions = append(actions, button) - } - elements = append(elements, map[string]interface{}{"tag": "action", "actions": actions}) - - kit.Value(form, "msg_type", "interactive") - kit.Value(form, "card", map[string]interface{}{ - "config": map[string]interface{}{"wide_screen_mode": true}, - "header": map[string]interface{}{ - "title": map[string]interface{}{"tag": "lark_md", "content": arg[0]}, - }, - "elements": elements, - }) - - _lark_post(m, "bot", "/open-apis/message/v4/send/", web.SPIDE_DATA, kit.Formats(form)) - }}, - TALK: {Name: "talk text", Help: "聊天", Hand: func(m *ice.Message, c *ice.Context, key string, arg ...string) { - m.Option(ice.MSG_USERZONE, LARK) - - cmds := kit.Split(strings.Join(arg, " ")) - if aaa.UserLogin(m, m.Option(OPEN_ID), ""); !m.Right(cmds) { - if aaa.UserLogin(m, m.Option(OPEN_CHAT_ID), ""); !m.Right(cmds) { - m.Cmd(DUTY, m.Option(OPEN_CHAT_ID), m.Option("text_without_at_bot")) - m.Cmd(HOME) - return // 没有权限 - } - } - - if cmds[0] == HOME { - m.Cmd(HOME, cmds[1:]) - return // 没有命令 - } - - // 执行命令 - if msg := m.Cmd(cmds); len(msg.Appendv(ice.MSG_APPEND)) > 0 || len(msg.Resultv()) > 0 { - if m.Copy(msg); len(m.Resultv()) == 0 { - m.Table() - } - } else { - m.Cmdy(cli.SYSTEM, cmds) - } - }}, - RAND: {Name: "rand", Help: "随机", Hand: func(m *ice.Message, c *ice.Context, key string, arg ...string) { - msg := m.Cmd(GROUP, EMPLOYEE, m.Option(OPEN_CHAT_ID)) - list := msg.Appendv("name") - if strings.Contains(m.Option("content"), "誰") { - m.Echo(strings.Replace(m.Option("content"), "誰", list[rand.Intn(len(list))], 1)) - return - } - m.Echo(list[rand.Intn(len(list))]) - }}, - - web.WEB_LOGIN: {Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {}}, - "/msg": {Name: "/msg", Help: "聊天消息", Hand: func(m *ice.Message, c *ice.Context, key string, arg ...string) { - data := m.Optionv(ice.MSG_USERDATA) - if kit.Value(data, "action") != nil { - m.Option(ice.MSG_USERUA, "") - kit.Fetch(kit.Value(data, "action.value"), func(key string, value string) { m.Option(key, value) }) - - m.Cmdy(TALK, kit.Parse(nil, "", kit.Split(m.Option(kit.SSH_CMD))...)) - m.Cmd(SEND, CHAT_ID, m.Option(OPEN_CHAT_ID), m.Option(wiki.TITLE)+" "+m.Option(kit.SSH_CMD), m.Result()) - return - } - - switch _lark_parse(m); m.Option("msg.type") { - case "url_verification": // 绑定验证 - m.Render(ice.RENDER_RESULT, kit.Format(kit.Dict("challenge", m.Option("msg.challenge")))) - - case "event_callback": - switch m.Option("type") { - case "message_read": - case "chat_disband": - case P2P_CHAT_CREATE, ADD_BOT: - // 创建对话 - if m.Options(OPEN_CHAT_ID) { - m.Cmdy(SEND, m.Option(OPEN_CHAT_ID), m.Conf(APP, kit.Keym(kit.MDB_TEMPLATE, m.Option("type")))) - } - default: - switch m.Option("msg_type") { - case "location": - case "image": - // m.Rich(META, nil, kit.Dict( - // "url", m.Option("image_url"), - // "width", m.Option("image_width"), - // "height", m.Option("image_height"), - // )) - default: - if m.Options(OPEN_CHAT_ID) { - if m.Cmdy(TALK, strings.TrimSpace(m.Option("text_without_at_bot"))); len(m.Resultv()) > 0 { - m.Cmd(SEND, m.Option(OPEN_CHAT_ID), m.Result()) - } - } else { - m.Cmd(DUTY, m.Option("type"), kit.Formats(data)) - } - } - } - default: - m.Cmd(DUTY, m.Option("msg.type"), kit.Formats(data)) - } - }}, - "/sso": {Name: "/sso", Help: "网页", Hand: func(m *ice.Message, c *ice.Context, key string, arg ...string) { - if m.Options("code") { - msg := m.Cmd(web.SPIDE, LARK, "/open-apis/authen/v1/access_token", "grant_type", "authorization_code", - "code", m.Option("code"), "app_access_token", m.Cmdx(APP, "token", "bot")) - - m.Option(aaa.USERZONE, LARK) - user := msg.Append("data.open_id") - web.RenderCookie(m, aaa.SessCreate(m, user)) - m.Render("redirect", m.Conf(web.SHARE, "meta.domain")) - - msg = m.Cmd(EMPLOYEE, m.Option(aaa.USERNAME, user)) - m.Cmd(aaa.USER, mdb.MODIFY, aaa.USERZONE, LARK, aaa.USERNICK, msg.Append(kit.MDB_NAME), - aaa.AVATAR, msg.Append("avatar_url"), aaa.GENDER, kit.Select("女", "男", msg.Append(aaa.GENDER) == "1"), - aaa.COUNTRY, msg.Append(aaa.COUNTRY), aaa.CITY, msg.Append(aaa.CITY), - aaa.MOBILE, msg.Append(aaa.MOBILE), - ) - return - } - - m.Option(mdb.FIELDS, "time,appid,appmm,token,expire") - m.Cmd(mdb.SELECT, m.Prefix(APP), "", mdb.HASH, kit.MDB_NAME, "bot").Table(func(index int, value map[string]string, head []string) { - m.Render("redirect", kit.MergeURL2(m.Conf(APP, kit.Keym(LARK)), "/open-apis/authen/v1/index"), - "app_id", value[APPID], "redirect_uri", kit.MergeURL2(m.Conf(web.SHARE, kit.Keym(kit.MDB_DOMAIN)), "/chat/lark/sso"), - ) - }) - }}, }, } diff --git a/misc/lark/lark.shy b/misc/lark/lark.shy index 33862172..fb92d3a8 100644 --- a/misc/lark/lark.shy +++ b/misc/lark/lark.shy @@ -1,5 +1,6 @@ title "飞书机器人" refer "官网" ` +官网 https://shylinux.com/chat/lark/sso 官网 https://www.feishu.cn/ 文档 https://open.feishu.cn/document/uQjL04CN/ucDOz4yN4MjL3gzM 源码 https://github.com/shylinux/icebergs/blob/master/misc/lark/lark.go @@ -12,6 +13,7 @@ chapter "应用" field "app" web.chat.lark.app field "group" web.chat.lark.group field "company" web.chat.lark.company +field "duty" web.chat.lark.duty chapter "权限" field sess aaa.sess diff --git a/misc/lark/msg.go b/misc/lark/msg.go new file mode 100644 index 00000000..291b14d3 --- /dev/null +++ b/misc/lark/msg.go @@ -0,0 +1,106 @@ +package lark + +import ( + "encoding/json" + "net/http" + "strings" + + ice "github.com/shylinux/icebergs" + "github.com/shylinux/icebergs/base/web" + "github.com/shylinux/icebergs/core/wiki" + kit "github.com/shylinux/toolkits" +) + +func _lark_get(m *ice.Message, appid string, arg ...interface{}) (*ice.Message, interface{}) { + m.Option(web.SPIDE_HEADER, "Authorization", "Bearer "+m.Cmdx(APP, TOKEN, appid), web.ContentType, web.ContentJSON) + msg := m.Cmd(web.SPIDE, LARK, http.MethodGet, arg) + return msg, msg.Optionv(web.SPIDE_RES) +} +func _lark_post(m *ice.Message, appid string, arg ...interface{}) *ice.Message { + m.Option(web.SPIDE_HEADER, "Authorization", "Bearer "+m.Cmdx(APP, TOKEN, appid), web.ContentType, web.ContentJSON) + return m.Cmd(web.SPIDE, LARK, arg) +} +func _lark_parse(m *ice.Message) { + data := m.Optionv(ice.MSG_USERDATA) + if data == nil { + json.NewDecoder(m.R.Body).Decode(&data) + m.Optionv(ice.MSG_USERDATA, data) + + switch d := data.(type) { + case map[string]interface{}: + for k, v := range d { + switch d := v.(type) { + case map[string]interface{}: + for k, v := range d { + m.Add(ice.MSG_OPTION, k, kit.Format(v)) + } + default: + for _, v := range kit.Simple(v) { + m.Add(ice.MSG_OPTION, kit.Keys(MSG, k), kit.Format(v)) + } + } + } + } + } + m.Debug("msg: %v", kit.Format(data)) +} + +const ( + APP_ID = "app_id" + SHIP_ID = "ship_id" + OPEN_ID = "open_id" + CHAT_ID = "chat_id" + USER_ID = "user_id" + OPEN_CHAT_ID = "open_chat_id" + USER_OPEN_ID = "user_open_id" +) + +const MSG = "msg" + +func init() { + Index.Merge(&ice.Context{Commands: map[string]*ice.Command{ + web.WEB_LOGIN: {Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + m.Option(ice.MSG_USERZONE, LARK) + }}, + "/msg": {Name: "/msg", Help: "聊天消息", Hand: func(m *ice.Message, c *ice.Context, key string, arg ...string) { + data := m.Optionv(ice.MSG_USERDATA) + if kit.Value(data, "action") != nil { // 卡片回调 + m.Cmd(MSG, "card") + return + } + + switch _lark_parse(m); m.Option("msg.type") { + case "url_verification": // 绑定验证 + m.Render(ice.RENDER_RESULT, kit.Format(kit.Dict("challenge", m.Option("msg.challenge")))) + + case "event_callback": // 事件回调 + m.Cmd(EVENT, m.Option(kit.MDB_TYPE)) + + default: // 未知消息 + m.Cmd(DUTY, m.Option("msg.type"), kit.Formats(data)) + } + }}, + MSG: {Name: "msg", Help: "聊天消息", Action: map[string]*ice.Action{ + "location": {Name: "", Help: "", Hand: func(m *ice.Message, arg ...string) { + }}, + "image": {Name: "", Help: "", Hand: func(m *ice.Message, arg ...string) { + }}, + "card": {Name: "", Help: "", Hand: func(m *ice.Message, arg ...string) { + data := m.Optionv(ice.MSG_USERDATA) + kit.Fetch(kit.Value(data, "action.value"), func(key string, value string) { m.Option(key, value) }) + + m.Cmdy(TALK, kit.Parse(nil, "", kit.Split(m.Option(kit.SSH_CMD))...)) + m.Cmd(SEND, m.Option(APP_ID), CHAT_ID, m.Option(OPEN_CHAT_ID), + m.Option(wiki.TITLE)+" "+m.Option(kit.SSH_CMD), m.Result()) + }}, + }, Hand: func(m *ice.Message, c *ice.Context, key string, arg ...string) { + if m.Options(OPEN_CHAT_ID) { + if m.Cmdy(TALK, strings.TrimSpace(m.Option("text_without_at_bot"))); len(m.Resultv()) > 0 { + m.Cmd(SEND, m.Option(APP_ID), m.Option(OPEN_CHAT_ID), m.Result()) + } + } else { + m.Cmd(DUTY, m.Option(APP_ID), m.Option(kit.MDB_TYPE), kit.Formats(m.Optionv(ice.MSG_USERDATA))) + } + }}, + }}) +} diff --git a/misc/lark/rand.go b/misc/lark/rand.go new file mode 100644 index 00000000..57d75da0 --- /dev/null +++ b/misc/lark/rand.go @@ -0,0 +1,25 @@ +package lark + +import ( + "math/rand" + "strings" + + ice "github.com/shylinux/icebergs" + kit "github.com/shylinux/toolkits" +) + +const RAND = "rand" + +func init() { + Index.Merge(&ice.Context{Commands: map[string]*ice.Command{ + RAND: {Name: "rand", Help: "随机", Hand: func(m *ice.Message, c *ice.Context, key string, arg ...string) { + msg := m.Cmd(GROUP, m.Option(APP_ID), EMPLOYEE, m.Option(OPEN_CHAT_ID)) + list := msg.Appendv(kit.MDB_NAME) + if strings.Contains(m.Option(CONTENT), "誰") { + m.Echo(strings.Replace(m.Option(CONTENT), "誰", list[rand.Intn(len(list))], 1)) + return + } + m.Echo(list[rand.Intn(len(list))]) + }}, + }}) +} diff --git a/misc/lark/send.go b/misc/lark/send.go new file mode 100644 index 00000000..24eab717 --- /dev/null +++ b/misc/lark/send.go @@ -0,0 +1,68 @@ +package lark + +import ( + "strings" + + ice "github.com/shylinux/icebergs" + "github.com/shylinux/icebergs/base/aaa" + "github.com/shylinux/icebergs/base/web" + kit "github.com/shylinux/toolkits" +) + +func _send_text(m *ice.Message, form map[string]interface{}, arg ...string) bool { + switch len(arg) { + case 0: + case 1: + kit.Value(form, "msg_type", "text") + kit.Value(form, "content.text", arg[0]) + if strings.TrimSpace(arg[0]) == "" { + return false + } + default: + if len(arg) == 2 && strings.TrimSpace(arg[1]) == "" { + return false + } + content := []interface{}{} + line := []interface{}{} + for _, v := range arg[1:] { + if v == "\n" { + content, line = append(content, line), []interface{}{} + continue + } + line = append(line, map[string]interface{}{"tag": "text", "text": v + " "}) + } + content = append(content, line) + + kit.Value(form, "msg_type", "post") + kit.Value(form, "content.post", map[string]interface{}{ + "zh_cn": map[string]interface{}{"title": arg[0], CONTENT: content}, + }) + } + return true +} + +const ( + CONTENT = "content" +) +const SEND = "send" + +func init() { + Index.Merge(&ice.Context{Commands: map[string]*ice.Command{ + SEND: {Name: "send appid [chat_id|open_id|user_id|email] target [title] text", Help: "消息", Hand: func(m *ice.Message, c *ice.Context, key string, arg ...string) { + form := kit.Dict(CONTENT, kit.Dict()) + appid, arg := arg[0], arg[1:] + switch arg[0] { + case CHAT_ID, OPEN_ID, USER_ID, aaa.EMAIL: + form[arg[0]], arg = arg[1], arg[2:] + default: + form[CHAT_ID], arg = arg[0], arg[1:] + } + + if _send_text(m, form, arg...) { + msg := _lark_post(m, appid, "/open-apis/message/v4/send/", web.SPIDE_DATA, kit.Format(form)) + m.Push("time", m.Time()) + m.Push("message_id", msg.Append("data.message_id")) + } + }}, + }}) +} diff --git a/misc/lark/sso.go b/misc/lark/sso.go new file mode 100644 index 00000000..d6ea2b1a --- /dev/null +++ b/misc/lark/sso.go @@ -0,0 +1,49 @@ +package lark + +import ( + ice "github.com/shylinux/icebergs" + "github.com/shylinux/icebergs/base/aaa" + "github.com/shylinux/icebergs/base/mdb" + "github.com/shylinux/icebergs/base/web" + kit "github.com/shylinux/toolkits" +) + +const SSO = "sso" + +func init() { + Index.Merge(&ice.Context{Commands: map[string]*ice.Command{ + "/sso": {Name: "/sso", Help: "网页", Hand: func(m *ice.Message, c *ice.Context, key string, arg ...string) { + if m.Option(ice.MSG_USERNAME) != "" { // 默认主页 + m.RenderIndex(web.SERVE, ice.VOLCANOS) + return + } + + home := kit.MergeURL2(m.Option(ice.MSG_USERWEB), "/chat/lark/sso") + if m.Option(kit.MDB_CODE) != "" { // 登录成功 + msg := m.Cmd(web.SPIDE, LARK, "/open-apis/authen/v1/access_token", "grant_type", "authorization_code", + kit.MDB_CODE, m.Option(kit.MDB_CODE), "app_access_token", m.Cmdx(APP, TOKEN, m.Cmd(APP).Append(APPID))) + + // 创建会话 + m.Option(aaa.USERNAME, msg.Append("data.open_id")) + web.RenderCookie(m, aaa.SessCreate(m, m.Option(aaa.USERNAME))) + m.RenderRedirect(kit.Select(home, m.Option(kit.MDB_BACK))) + + // 更新用户 + msg = m.Cmd(EMPLOYEE, m.Option(aaa.USERNAME)) + m.Cmd(aaa.USER, mdb.MODIFY, aaa.USERZONE, LARK, aaa.USERNICK, msg.Append(kit.MDB_NAME), + aaa.AVATAR, msg.Append("avatar_url"), aaa.GENDER, kit.Select("女", "男", msg.Append(aaa.GENDER) == "1"), + aaa.COUNTRY, msg.Append(aaa.COUNTRY), aaa.CITY, msg.Append(aaa.CITY), + aaa.MOBILE, msg.Append(aaa.MOBILE), + ) + return + } + + if back := m.R.Header.Get("Referer"); back != "" { + home = kit.MergeURL(home, kit.MDB_BACK, back) + } + // 登录页面 + m.RenderRedirect(kit.MergeURL2(m.Conf(APP, kit.Keym(LARK)), "/open-apis/authen/v1/index"), + "redirect_uri", home, APP_ID, m.Cmd(APP).Append(APPID)) + }}, + }}) +} diff --git a/misc/lark/talk.go b/misc/lark/talk.go new file mode 100644 index 00000000..432913f0 --- /dev/null +++ b/misc/lark/talk.go @@ -0,0 +1,29 @@ +package lark + +import ( + "strings" + + ice "github.com/shylinux/icebergs" + "github.com/shylinux/icebergs/base/aaa" + kit "github.com/shylinux/toolkits" +) + +const TALK = "talk" + +func init() { + Index.Merge(&ice.Context{Commands: map[string]*ice.Command{ + TALK: {Name: "talk text", Help: "聊天", Hand: func(m *ice.Message, c *ice.Context, key string, arg ...string) { + cmds := kit.Split(strings.Join(arg, " ")) + if aaa.UserLogin(m, m.Option(OPEN_ID), ""); !m.Right(cmds) { + if aaa.UserLogin(m, m.Option(OPEN_CHAT_ID), ""); !m.Right(cmds) { + m.Cmd(DUTY, m.Option(APP_ID), m.Option(OPEN_CHAT_ID), m.Option("text_without_at_bot")) + m.Cmd(HOME) + return // 没有权限 + } + } + + // 执行命令 + m.Cmdy(cmds) + }}, + }}) +} diff --git a/type.go b/type.go index 640a12b1..fcf00107 100644 --- a/type.go +++ b/type.go @@ -718,8 +718,9 @@ func (m *Message) cmd(arg ...interface{}) *Message { return m } + ok := false run := func(msg *Message, ctx *Context, cmd *Command, key string, arg ...string) { - if cbs != nil { + if ok = true; cbs != nil { msg.Option(list[0]+".cb", cbs) } for k, v := range opts { @@ -744,8 +745,8 @@ func (m *Message) cmd(arg ...interface{}) *Message { } // 系统命令 - if m.Warn(!m.Hand, ErrNotFound, list) { - return m.Set(MSG_RESULT).Cmd("cli.system", list) + if m.Warn(!ok, ErrNotFound, list) { + return m.Set(MSG_RESULT).Cmdy("cli.system", list) } return m }