feat: 优化钉钉机器人的消息发送方式,兼容outgoing模式 (#128)

Co-authored-by: eryajf <eryajf@users.noreply.github.com>
This commit is contained in:
二丫讲梵
2023-03-25 16:27:34 +08:00
committed by GitHub
parent bc55f74c78
commit b7afcfd952
5 changed files with 46 additions and 21 deletions

View File

@@ -24,6 +24,8 @@
- [使用前提](#%E4%BD%BF%E7%94%A8%E5%89%8D%E6%8F%90) - [使用前提](#%E4%BD%BF%E7%94%A8%E5%89%8D%E6%8F%90)
- [使用教程](#%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B) - [使用教程](#%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B)
- [第一步,创建机器人](#%E7%AC%AC%E4%B8%80%E6%AD%A5%E5%88%9B%E5%BB%BA%E6%9C%BA%E5%99%A8%E4%BA%BA) - [第一步,创建机器人](#%E7%AC%AC%E4%B8%80%E6%AD%A5%E5%88%9B%E5%BB%BA%E6%9C%BA%E5%99%A8%E4%BA%BA)
- [方案一outgoing类型机器人](#%E6%96%B9%E6%A1%88%E4%B8%80outgoing%E7%B1%BB%E5%9E%8B%E6%9C%BA%E5%99%A8%E4%BA%BA)
- [方案二:企业内部应用](#%E6%96%B9%E6%A1%88%E4%BA%8C%E4%BC%81%E4%B8%9A%E5%86%85%E9%83%A8%E5%BA%94%E7%94%A8)
- [第二步,部署应用](#%E7%AC%AC%E4%BA%8C%E6%AD%A5%E9%83%A8%E7%BD%B2%E5%BA%94%E7%94%A8) - [第二步,部署应用](#%E7%AC%AC%E4%BA%8C%E6%AD%A5%E9%83%A8%E7%BD%B2%E5%BA%94%E7%94%A8)
- [docker部署](#docker%E9%83%A8%E7%BD%B2) - [docker部署](#docker%E9%83%A8%E7%BD%B2)
- [二进制部署](#%E4%BA%8C%E8%BF%9B%E5%88%B6%E9%83%A8%E7%BD%B2) - [二进制部署](#%E4%BA%8C%E8%BF%9B%E5%88%B6%E9%83%A8%E7%BD%B2)
@@ -85,6 +87,24 @@
### 第一步,创建机器人 ### 第一步,创建机器人
#### 方案一outgoing类型机器人
钉钉群内的机器人有一个outgoing模式当你创建机器人的时候可以选择启用这个模式然后直接配置回调地址免去在管理后台创建应用的步骤就可以直接投入使用。
官方文档:[自定义机器人接入](https://open.dingtalk.com/document/orgapp/custom-robot-access)
但是这个模式貌似是部分开放的(目前来看貌似是部分人有创建这个类型的白名单),所以如果你在钉钉群聊中添加`自定义机器人`的时候,看到和我一样的信息,则说明无法使用这种方式:
![image_20230325_162017](https://cdn.staticaly.com/gh/eryajf/tu/main/img/image_20230325_162017.jpg)
`📢 注意`
- 如果你的和我一样,那么就只能放弃这种方案,往下看第二种对接方案。
- 如果使用这种方案,那么就不能与机器人私聊对话,只能局限在群聊当中艾特机器人聊天。
- 如果使用这种方案,则在群聊当中并不能达到真正的艾特发消息人的效果,因为这种机器人回调过来的关键信息为空。
#### 方案二:企业内部应用
创建步骤参考文档:[企业内部开发机器人](https://open.dingtalk.com/document/robots/enterprise-created-chatbot),或者根据如下步骤进行配置。 创建步骤参考文档:[企业内部开发机器人](https://open.dingtalk.com/document/robots/enterprise-created-chatbot),或者根据如下步骤进行配置。
1. 创建机器人。 1. 创建机器人。

View File

@@ -44,12 +44,13 @@ func Start() {
logger.Warning("从钉钉回调过来的内容为空,根据过往的经验,或许重新创建一下机器人,能解决这个问题") logger.Warning("从钉钉回调过来的内容为空,根据过往的经验,或许重新创建一下机器人,能解决这个问题")
return ship.ErrBadRequest.New(fmt.Errorf("从钉钉回调过来的内容为空,根据过往的经验,或许重新创建一下机器人,能解决这个问题")) return ship.ErrBadRequest.New(fmt.Errorf("从钉钉回调过来的内容为空,根据过往的经验,或许重新创建一下机器人,能解决这个问题"))
} }
// 打印钉钉回调过来的请求明细 // 打印钉钉回调过来的请求明细
logger.Info(fmt.Sprintf("dingtalk callback parameters: %#v", msgObj)) logger.Info(fmt.Sprintf("dingtalk callback parameters: %#v", msgObj))
// TODO: 校验请求 // TODO: 校验请求
if len(msgObj.Text.Content) == 1 || strings.TrimSpace(msgObj.Text.Content) == "帮助" { if len(msgObj.Text.Content) == 1 || strings.TrimSpace(msgObj.Text.Content) == "帮助" {
// 欢迎信息 // 欢迎信息
_, err := msgObj.ReplyToDingtalk(string(public.TEXT), Welcome, msgObj.SenderStaffId) _, err := msgObj.ReplyToDingtalk(string(public.MARKDOWN), Welcome)
if err != nil { if err != nil {
logger.Warning(fmt.Errorf("send message error: %v", err)) logger.Warning(fmt.Errorf("send message error: %v", err))
return ship.ErrBadRequest.New(fmt.Errorf("send message error: %v", err)) return ship.ErrBadRequest.New(fmt.Errorf("send message error: %v", err))

View File

@@ -17,20 +17,20 @@ func ProcessRequest(rmsg *public.ReceiveMsg) error {
switch content { switch content {
case "单聊": case "单聊":
public.UserService.SetUserMode(rmsg.SenderStaffId, content) public.UserService.SetUserMode(rmsg.SenderStaffId, content)
_, err := rmsg.ReplyToDingtalk(string(public.TEXT), fmt.Sprintf("=====现在进入与👉%s👈单聊的模式 =====", rmsg.SenderNick), rmsg.SenderStaffId) _, err := rmsg.ReplyToDingtalk(string(public.TEXT), fmt.Sprintf("=====现在进入与👉%s👈单聊的模式 =====", rmsg.SenderNick))
if err != nil { if err != nil {
logger.Warning(fmt.Errorf("send message error: %v", err)) logger.Warning(fmt.Errorf("send message error: %v", err))
} }
case "串聊": case "串聊":
public.UserService.SetUserMode(rmsg.SenderStaffId, content) public.UserService.SetUserMode(rmsg.SenderStaffId, content)
_, err := rmsg.ReplyToDingtalk(string(public.TEXT), fmt.Sprintf("=====现在进入与👉%s👈串聊的模式 =====", rmsg.SenderNick), rmsg.SenderStaffId) _, err := rmsg.ReplyToDingtalk(string(public.TEXT), fmt.Sprintf("=====现在进入与👉%s👈串聊的模式 =====", rmsg.SenderNick))
if err != nil { if err != nil {
logger.Warning(fmt.Errorf("send message error: %v", err)) logger.Warning(fmt.Errorf("send message error: %v", err))
} }
case "重置": case "重置":
public.UserService.ClearUserMode(rmsg.SenderStaffId) public.UserService.ClearUserMode(rmsg.SenderStaffId)
public.UserService.ClearUserSessionContext(rmsg.SenderStaffId) public.UserService.ClearUserSessionContext(rmsg.SenderStaffId)
_, err := rmsg.ReplyToDingtalk(string(public.TEXT), fmt.Sprintf("=====已重置与👉%s👈的对话模式可以开始新的对话=====", rmsg.SenderNick), rmsg.SenderStaffId) _, err := rmsg.ReplyToDingtalk(string(public.TEXT), fmt.Sprintf("=====已重置与👉%s👈的对话模式可以开始新的对话=====", rmsg.SenderNick))
if err != nil { if err != nil {
logger.Warning(fmt.Errorf("send message error: %v", err)) logger.Warning(fmt.Errorf("send message error: %v", err))
} }
@@ -39,12 +39,12 @@ func ProcessRequest(rmsg *public.ReceiveMsg) error {
for _, v := range *public.Prompt { for _, v := range *public.Prompt {
title = title + v.Title + " | " title = title + v.Title + " | "
} }
_, err := rmsg.ReplyToDingtalk(string(public.TEXT), fmt.Sprintf("%s 您好当前程序内置集成了这些prompt\n====================================\n| %s \n====================================\n你可以选择某个prompt开头然后进行对话。\n以周报为例可发送 #周报 我本周用Go写了一个钉钉集成ChatGPT的聊天应用", rmsg.SenderNick, title), rmsg.SenderStaffId) _, err := rmsg.ReplyToDingtalk(string(public.TEXT), fmt.Sprintf("%s 您好当前程序内置集成了这些prompt\n====================================\n| %s \n====================================\n你可以选择某个prompt开头然后进行对话。\n以周报为例可发送 #周报 我本周用Go写了一个钉钉集成ChatGPT的聊天应用", rmsg.SenderNick, title))
if err != nil { if err != nil {
logger.Warning(fmt.Errorf("send message error: %v", err)) logger.Warning(fmt.Errorf("send message error: %v", err))
} }
case "图片": case "图片":
_, err := rmsg.ReplyToDingtalk(string(public.MARKDOWN), "发送以 **#图片** 开头的内容,将会触发绘画能力,图片生成之后,将会保存在程序根目录下的 **images目录** \n 如果你绘图没有思路,可以在这两个网站寻找灵感。\n - [https://lexica.art/](https://lexica.art/)\n- [https://www.clickprompt.org/zh-CN/](https://www.clickprompt.org/zh-CN/)", rmsg.SenderStaffId) _, err := rmsg.ReplyToDingtalk(string(public.MARKDOWN), "发送以 **#图片** 开头的内容,将会触发绘画能力,图片生成之后,将会保存在程序根目录下的 **images目录** \n 如果你绘图没有思路,可以在这两个网站寻找灵感。\n - [https://lexica.art/](https://lexica.art/)\n- [https://www.clickprompt.org/zh-CN/](https://www.clickprompt.org/zh-CN/)")
if err != nil { if err != nil {
logger.Warning(fmt.Errorf("send message error: %v", err)) logger.Warning(fmt.Errorf("send message error: %v", err))
} }
@@ -61,7 +61,7 @@ func ProcessRequest(rmsg *public.ReceiveMsg) error {
cacheMsg = fmt.Sprintf("💵 已用: 💲%v\n💵 剩余: 💲%v\n⏳ 有效时间: 从 %v 到 %v\n", fmt.Sprintf("%.2f", rst.TotalUsed), fmt.Sprintf("%.2f", rst.TotalAvailable), t1.Format("2006-01-02 15:04:05"), t2.Format("2006-01-02 15:04:05")) cacheMsg = fmt.Sprintf("💵 已用: 💲%v\n💵 剩余: 💲%v\n⏳ 有效时间: 从 %v 到 %v\n", fmt.Sprintf("%.2f", rst.TotalUsed), fmt.Sprintf("%.2f", rst.TotalAvailable), t1.Format("2006-01-02 15:04:05"), t2.Format("2006-01-02 15:04:05"))
} }
_, err := rmsg.ReplyToDingtalk(string(public.TEXT), cacheMsg, rmsg.SenderStaffId) _, err := rmsg.ReplyToDingtalk(string(public.TEXT), cacheMsg)
if err != nil { if err != nil {
logger.Warning(fmt.Errorf("send message error: %v", err)) logger.Warning(fmt.Errorf("send message error: %v", err))
} }
@@ -87,13 +87,13 @@ func Do(mode string, rmsg *public.ReceiveMsg) error {
logger.Info(fmt.Errorf("gpt request error: %v", err)) logger.Info(fmt.Errorf("gpt request error: %v", err))
if strings.Contains(fmt.Sprintf("%v", err), "maximum text length exceeded") { if strings.Contains(fmt.Sprintf("%v", err), "maximum text length exceeded") {
public.UserService.ClearUserSessionContext(rmsg.SenderStaffId) public.UserService.ClearUserSessionContext(rmsg.SenderStaffId)
_, err = rmsg.ReplyToDingtalk(string(public.TEXT), fmt.Sprintf("请求openai失败了错误信息%v看起来是超过最大对话限制了已自动重置您的对话", err), rmsg.SenderStaffId) _, err = rmsg.ReplyToDingtalk(string(public.TEXT), fmt.Sprintf("请求openai失败了错误信息%v看起来是超过最大对话限制了已自动重置您的对话", err))
if err != nil { if err != nil {
logger.Warning(fmt.Errorf("send message error: %v", err)) logger.Warning(fmt.Errorf("send message error: %v", err))
return err return err
} }
} else { } else {
_, err = rmsg.ReplyToDingtalk(string(public.TEXT), fmt.Sprintf("请求openai失败了错误信息%v", err), rmsg.SenderStaffId) _, err = rmsg.ReplyToDingtalk(string(public.TEXT), fmt.Sprintf("请求openai失败了错误信息%v", err))
if err != nil { if err != nil {
logger.Warning(fmt.Errorf("send message error: %v", err)) logger.Warning(fmt.Errorf("send message error: %v", err))
return err return err
@@ -107,8 +107,7 @@ func Do(mode string, rmsg *public.ReceiveMsg) error {
reply = strings.TrimSpace(reply) reply = strings.TrimSpace(reply)
reply = strings.Trim(reply, "\n") reply = strings.Trim(reply, "\n")
// 回复@我的用户 // 回复@我的用户
// fmt.Println("单聊结果是:", reply) _, err = rmsg.ReplyToDingtalk(string(public.TEXT), reply)
_, err = rmsg.ReplyToDingtalk(string(public.TEXT), reply, rmsg.SenderStaffId)
if err != nil { if err != nil {
logger.Warning(fmt.Errorf("send message error: %v", err)) logger.Warning(fmt.Errorf("send message error: %v", err))
return err return err
@@ -120,13 +119,13 @@ func Do(mode string, rmsg *public.ReceiveMsg) error {
logger.Info(fmt.Sprintf("gpt request error: %v", err)) logger.Info(fmt.Sprintf("gpt request error: %v", err))
if strings.Contains(fmt.Sprintf("%v", err), "maximum text length exceeded") { if strings.Contains(fmt.Sprintf("%v", err), "maximum text length exceeded") {
public.UserService.ClearUserSessionContext(rmsg.SenderStaffId) public.UserService.ClearUserSessionContext(rmsg.SenderStaffId)
_, err = rmsg.ReplyToDingtalk(string(public.TEXT), fmt.Sprintf("请求openai失败了错误信息%v看起来是超过最大对话限制了已自动重置您的对话", err), rmsg.SenderStaffId) _, err = rmsg.ReplyToDingtalk(string(public.TEXT), fmt.Sprintf("请求openai失败了错误信息%v看起来是超过最大对话限制了已自动重置您的对话", err))
if err != nil { if err != nil {
logger.Warning(fmt.Errorf("send message error: %v", err)) logger.Warning(fmt.Errorf("send message error: %v", err))
return err return err
} }
} else { } else {
_, err = rmsg.ReplyToDingtalk(string(public.TEXT), fmt.Sprintf("请求openai失败了错误信息%v", err), rmsg.SenderStaffId) _, err = rmsg.ReplyToDingtalk(string(public.TEXT), fmt.Sprintf("请求openai失败了错误信息%v", err))
if err != nil { if err != nil {
logger.Warning(fmt.Errorf("send message error: %v", err)) logger.Warning(fmt.Errorf("send message error: %v", err))
return err return err
@@ -140,7 +139,7 @@ func Do(mode string, rmsg *public.ReceiveMsg) error {
reply = strings.TrimSpace(reply) reply = strings.TrimSpace(reply)
reply = strings.Trim(reply, "\n") reply = strings.Trim(reply, "\n")
// 回复@我的用户 // 回复@我的用户
_, err = rmsg.ReplyToDingtalk(string(public.TEXT), reply, rmsg.SenderStaffId) _, err = rmsg.ReplyToDingtalk(string(public.TEXT), reply)
if err != nil { if err != nil {
logger.Warning(fmt.Errorf("send message error: %v", err)) logger.Warning(fmt.Errorf("send message error: %v", err))
return err return err
@@ -157,7 +156,7 @@ func ImageGenerate(rmsg *public.ReceiveMsg) error {
reply, err := chatgpt.ImageQa(rmsg.Text.Content, rmsg.SenderStaffId) reply, err := chatgpt.ImageQa(rmsg.Text.Content, rmsg.SenderStaffId)
if err != nil { if err != nil {
logger.Info(fmt.Errorf("gpt request error: %v", err)) logger.Info(fmt.Errorf("gpt request error: %v", err))
_, err = rmsg.ReplyToDingtalk(string(public.TEXT), fmt.Sprintf("请求openai失败了错误信息%v", err), rmsg.SenderStaffId) _, err = rmsg.ReplyToDingtalk(string(public.TEXT), fmt.Sprintf("请求openai失败了错误信息%v", err))
if err != nil { if err != nil {
logger.Warning(fmt.Errorf("send message error: %v", err)) logger.Warning(fmt.Errorf("send message error: %v", err))
return err return err
@@ -170,7 +169,7 @@ func ImageGenerate(rmsg *public.ReceiveMsg) error {
reply = strings.TrimSpace(reply) reply = strings.TrimSpace(reply)
reply = strings.Trim(reply, "\n") reply = strings.Trim(reply, "\n")
// 回复@我的用户 // 回复@我的用户
_, err = rmsg.ReplyToDingtalk(string(public.MARKDOWN), fmt.Sprintf(">点击图片可旋转或放大。\n![](%s)", reply), rmsg.SenderStaffId) _, err = rmsg.ReplyToDingtalk(string(public.MARKDOWN), fmt.Sprintf(">点击图片可旋转或放大。\n![](%s)", reply))
if err != nil { if err != nil {
logger.Warning(fmt.Errorf("send message error: %v", err)) logger.Warning(fmt.Errorf("send message error: %v", err))
return err return err

View File

@@ -3,6 +3,7 @@ package public
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt"
"net/http" "net/http"
) )
@@ -68,15 +69,19 @@ type At struct {
} }
// 发消息给钉钉 // 发消息给钉钉
func (r ReceiveMsg) ReplyToDingtalk(msgType, msg, staffId string) (statuscode int, err error) { func (r ReceiveMsg) ReplyToDingtalk(msgType, msg string) (statuscode int, err error) {
atUser := r.SenderStaffId
if atUser == "" {
msg = fmt.Sprintf("%s\n\n@%s", msg, r.SenderNick)
}
var msgtmp interface{} var msgtmp interface{}
switch msgType { switch msgType {
case string(TEXT): case string(TEXT):
msgtmp = &TextMessage{Text: &Text{Content: msg}, MsgType: TEXT, At: &At{AtUserIds: []string{staffId}}} msgtmp = &TextMessage{Text: &Text{Content: msg}, MsgType: TEXT, At: &At{AtUserIds: []string{atUser}}}
case string(MARKDOWN): case string(MARKDOWN):
msgtmp = &MarkDownMessage{MsgType: MARKDOWN, At: &At{AtUserIds: []string{staffId}}, MarkDown: &MarkDown{Title: "根据您提供的信息,为您生成图片如下", Text: msg}} msgtmp = &MarkDownMessage{MsgType: MARKDOWN, At: &At{AtUserIds: []string{atUser}}, MarkDown: &MarkDown{Title: "Markdown Type", Text: msg}}
default: default:
msgtmp = &TextMessage{Text: &Text{Content: msg}, MsgType: TEXT, At: &At{AtUserIds: []string{staffId}}} msgtmp = &TextMessage{Text: &Text{Content: msg}, MsgType: TEXT, At: &At{AtUserIds: []string{atUser}}}
} }
data, err := json.Marshal(msgtmp) data, err := json.Marshal(msgtmp)

View File

@@ -45,7 +45,7 @@ func CheckRequest(rmsg *ReceiveMsg) bool {
// 判断访问次数是否超过限制 // 判断访问次数是否超过限制
if count >= Config.MaxRequest { if count >= Config.MaxRequest {
logger.Info(fmt.Sprintf("亲爱的: %s您今日请求次数已达上限请明天再来交互发问资源有限请务必斟酌您的问题给您带来不便敬请谅解!", rmsg.SenderNick)) logger.Info(fmt.Sprintf("亲爱的: %s您今日请求次数已达上限请明天再来交互发问资源有限请务必斟酌您的问题给您带来不便敬请谅解!", rmsg.SenderNick))
_, err := rmsg.ReplyToDingtalk(string(TEXT), fmt.Sprintf("一个好的问题,胜过十个好的答案!\n亲爱的: %s您今日请求次数已达上限请明天再来交互发问资源有限请务必斟酌您的问题给您带来不便敬请谅解!", rmsg.SenderNick), rmsg.SenderStaffId) _, err := rmsg.ReplyToDingtalk(string(TEXT), fmt.Sprintf("一个好的问题,胜过十个好的答案!\n亲爱的: %s您今日请求次数已达上限请明天再来交互发问资源有限请务必斟酌您的问题给您带来不便敬请谅解!", rmsg.SenderNick))
if err != nil { if err != nil {
logger.Warning(fmt.Errorf("send message error: %v", err)) logger.Warning(fmt.Errorf("send message error: %v", err))
} }