Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: DingTalk Adapter #367

Merged
merged 2 commits into from
Oct 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions api/api_bind.go
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,7 @@ func Bind(e *echo.Echo, _myDice *dice.DiceManager) {
e.POST(prefix+"/im_connections/addTelegram", ImConnectionsAddTelegram)
e.POST(prefix+"/im_connections/addMinecraft", ImConnectionsAddMinecraft)
e.POST(prefix+"/im_connections/addDodo", ImConnectionsAddDodo)
e.POST(prefix+"/im_connections/addDingtalk", ImConnectionsAddDingTalk)
e.POST(prefix+"/im_connections/addWalleQ", ImConnectionsAddWalleQ)
e.POST(prefix+"/im_connections/addGocqSeparate", ImConnectionsAddGocqSeparate)
e.POST(prefix+"/im_connections/addRed", ImConnectionsAddRed)
Expand Down
30 changes: 30 additions & 0 deletions api/conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,10 @@ func ImConnectionsDel(c echo.Context) error {
i.Adapter.SetEnable(false)
myDice.ImSession.EndPoints = append(myDice.ImSession.EndPoints[:index], myDice.ImSession.EndPoints[index+1:]...)
return c.JSON(http.StatusOK, i)
case "DINGTALK":
i.Adapter.SetEnable(false)
myDice.ImSession.EndPoints = append(myDice.ImSession.EndPoints[:index], myDice.ImSession.EndPoints[index+1:]...)
return c.JSON(http.StatusOK, i)
}
}
}
Expand Down Expand Up @@ -518,6 +522,32 @@ func ImConnectionsAddDodo(c echo.Context) error {
return c.String(430, "")
}

func ImConnectionsAddDingTalk(c echo.Context) error {
if !doAuth(c) {
return c.JSON(http.StatusForbidden, nil)
}

v := struct {
ClientID string `yaml:"clientID" json:"clientID"`
Token string `yaml:"token" json:"token"`
Nickname string `yaml:"nickname" json:"nickname"`
RobotCode string `yaml:"robotCode" json:"robotCode"`
}{}
err := c.Bind(&v)
if err == nil {
conn := dice.NewDingTalkConnItem(v.ClientID, v.Token, v.Nickname, v.RobotCode)
conn.Session = myDice.ImSession
pa := conn.Adapter.(*dice.PlatformAdapterDingTalk)
pa.Session = myDice.ImSession
myDice.ImSession.EndPoints = append(myDice.ImSession.EndPoints, conn)
myDice.LastUpdatedTime = time.Now().Unix()
myDice.Save(false)
go dice.ServeDingTalk(myDice, conn)
return c.JSON(200, conn)
}
return c.String(430, "")
}

func ImConnectionsAdd(c echo.Context) error {
if !doAuth(c) {
return c.JSON(http.StatusForbidden, nil)
Expand Down
13 changes: 13 additions & 0 deletions dice/im_session.go
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,15 @@ func (ep *EndPointInfo) UnmarshalYAML(value *yaml.Node) error {
return err
}
ep.Adapter = val.Adapter
case "DINGTALK":
var val struct {
Adapter *PlatformAdapterDingTalk `yaml:"adapter"`
}
err = value.Decode(&val)
if err != nil {
return err
}
ep.Adapter = val.Adapter
}
return err
}
Expand Down Expand Up @@ -1233,6 +1242,10 @@ func (ep *EndPointInfo) AdapterSetup() {
pa := ep.Adapter.(*PlatformAdapterDodo)
pa.Session = ep.Session
pa.EndPoint = ep
case "DINGTALK":
pa := ep.Adapter.(*PlatformAdapterDingTalk)
pa.Session = ep.Session
pa.EndPoint = ep
}
}

Expand Down
254 changes: 254 additions & 0 deletions dice/platform_adapter_dingtalk.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
package dice

import (
"fmt"
"strings"
"time"

dingtalk "github.com/Szzrain/DingTalk-go"
"github.com/open-dingtalk/dingtalk-stream-sdk-go/chatbot"
)

type PlatformAdapterDingTalk struct {
Session *IMSession `yaml:"-" json:"-"`
ClientID string `yaml:"clientID" json:"clientID"`
Token string `yaml:"token" json:"token"`
RobotCode string `yaml:"robotCode" json:"robotCode"`
CoolAppCode string `yaml:"coolAppCode" json:"coolAppCode"`
EndPoint *EndPointInfo `yaml:"-" json:"-"`
IntentSession *dingtalk.Session `yaml:"-" json:"-"`
}

func (pa *PlatformAdapterDingTalk) DoRelogin() bool {
err := pa.IntentSession.Close()
if err != nil {
pa.Session.Parent.Logger.Error("Dingtalk 断开连接失败: ", err)
return false
}
pa.Session.Parent.Logger.Infof("正在启用DingTalk服务……")
if pa.IntentSession == nil {
pa.Serve()
return false
}
err = pa.IntentSession.Open()
if err != nil {
pa.Session.Parent.Logger.Errorf("与DingTalk服务进行连接时出错:%s", err.Error())
pa.EndPoint.State = 3
pa.EndPoint.Enable = false
return false
}
return true
}

func (pa *PlatformAdapterDingTalk) SetEnable(enable bool) {
if enable {
pa.Session.Parent.Logger.Infof("正在启用DingTalk服务……")
if pa.IntentSession == nil {
pa.Serve()
return
}
err := pa.IntentSession.Open()
if err != nil {
pa.Session.Parent.Logger.Errorf("与DingTalk服务进行连接时出错:%s", err.Error())
pa.EndPoint.State = 3
pa.EndPoint.Enable = false
return
}
} else {
err := pa.IntentSession.Close()
if err != nil {
pa.Session.Parent.Logger.Error("Dingtalk 断开连接失败: ", err)
return
}
pa.EndPoint.State = 0
pa.EndPoint.Enable = false
}
}

func (pa *PlatformAdapterDingTalk) QuitGroup(ctx *MsgContext, id string) {
pa.SendToGroup(ctx, id, "不支持此功能, 请手动移除机器人", "")
}

func (pa *PlatformAdapterDingTalk) SendToPerson(ctx *MsgContext, uid string, text string, flag string) {
msg := dingtalk.MessageSampleText{Content: text}
rawUserID := ExtractDingTalkUserID(uid)
_, err := pa.IntentSession.MessagePrivateSend(rawUserID, pa.RobotCode, &msg)
if err != nil {
pa.Session.Parent.Logger.Error("Dingtalk SendToPerson Error: ", err)
return
}
pa.Session.OnMessageSend(ctx, &Message{
Platform: "DINGTALK",
MessageType: "private",
Message: text,
Sender: SenderBase{
UserID: pa.EndPoint.UserID,
Nickname: pa.EndPoint.Nickname,
},
}, flag)
}

func (pa *PlatformAdapterDingTalk) SendToGroup(ctx *MsgContext, uid string, text string, flag string) {
msg := dingtalk.MessageSampleText{Content: text}
rawGroupID := ExtractDingTalkGroupID(uid)
_, err := pa.IntentSession.MessageGroupSend(rawGroupID, pa.RobotCode, pa.CoolAppCode, &msg)
if err != nil {
pa.Session.Parent.Logger.Error("Dingtalk SendToGroup Error: ", err)
return
}
pa.Session.OnMessageSend(ctx, &Message{
Platform: "DINGTALK",
MessageType: "group",
Message: text,
GroupID: uid,
Sender: SenderBase{
UserID: pa.EndPoint.UserID,
Nickname: pa.EndPoint.Nickname,
},
}, flag)
}

func (pa *PlatformAdapterDingTalk) SetGroupCardName(groupID string, userID string, name string) {
}

func (pa *PlatformAdapterDingTalk) SendFileToPerson(ctx *MsgContext, uid string, path string, flag string) {
dice := pa.Session.Parent
fileElement, err := dice.FilepathToFileElement(path)
if err == nil {
pa.SendToPerson(ctx, uid, fmt.Sprintf("[尝试发送文件: %s,但不支持]", fileElement.File), flag)
} else {
pa.SendToPerson(ctx, uid, fmt.Sprintf("[尝试发送文件出错: %s]", err.Error()), flag)
}
}

func (pa *PlatformAdapterDingTalk) SendFileToGroup(ctx *MsgContext, uid string, path string, flag string) {
dice := pa.Session.Parent
fileElement, err := dice.FilepathToFileElement(path)
if err == nil {
pa.SendToGroup(ctx, uid, fmt.Sprintf("[尝试发送文件: %s,但不支持]", fileElement.File), flag)
} else {
pa.SendToGroup(ctx, uid, fmt.Sprintf("[尝试发送文件出错: %s]", err.Error()), flag)
}
}

func (pa *PlatformAdapterDingTalk) MemberBan(groupID string, userID string, duration int64) {
}

func (pa *PlatformAdapterDingTalk) MemberKick(groupID string, userID string) {
}

func (pa *PlatformAdapterDingTalk) GetGroupInfoAsync(groupID string) {

}

func (pa *PlatformAdapterDingTalk) OnChatReceive(_ *dingtalk.Session, data *chatbot.BotCallbackDataModel) {
pa.EndPoint.UserID = FormatDiceIDDingTalk(data.ChatbotUserId)
if pa.Session.ServiceAtNew[FormatDiceIDDingTalkGroup(data.ConversationId)] != nil {
pa.Session.ServiceAtNew[FormatDiceIDDingTalkGroup(data.ConversationId)].GroupName = data.ConversationTitle
}
msg := pa.toStdMessage(data)
pa.Session.Execute(pa.EndPoint, msg, false)
}

func (pa *PlatformAdapterDingTalk) OnGroupJoined(_ *dingtalk.Session, data *dingtalk.GroupJoinedEvent) {
palogger := pa.Session.Parent.Logger
msg := &Message{
Platform: "DINGTALK",
RawID: data.EventId,
GroupID: FormatDiceIDDingTalkGroup(data.OpenConversationId),
Sender: SenderBase{
UserID: FormatDiceIDDingTalk(data.Operator),
},
}
palogger.Info("Dingtalk OnGroupJoined: ", data)
pa.CoolAppCode = data.CoolAppCode
pa.RobotCode = data.RobotCode
d := pa.Session.Parent
d.LastUpdatedTime = time.Now().Unix()
d.Save(false)
logger := pa.Session.Parent.Logger
ep := pa.EndPoint
ctx := &MsgContext{MessageType: "group", EndPoint: ep, Session: pa.Session, Dice: pa.Session.Parent}
gi := SetBotOnAtGroup(ctx, msg.GroupID)
gi.InviteUserID = msg.Sender.UserID
gi.EnteredTime = time.Now().Unix()
ctx.Player = &GroupPlayerInfo{}
logger.Infof("发送入群致辞,群: <%s>(%d)", "%未知群名%", data.OpenConversationId)
text := DiceFormatTmpl(ctx, "核心:骰子进群")
for _, i := range strings.Split(text, "###SPLIT###") {
pa.SendToGroup(ctx, msg.GroupID, strings.TrimSpace(i), "")
}
if ctx.Session.ServiceAtNew[msg.GroupID] != nil {
for _, i := range ctx.Session.ServiceAtNew[msg.GroupID].ActivatedExtList {
if i.OnGroupJoined != nil {
i.callWithJsCheck(ctx.Dice, func() {
i.OnGroupJoined(ctx, msg)
})
}
}
}
}

func (pa *PlatformAdapterDingTalk) toStdMessage(data *chatbot.BotCallbackDataModel) *Message {
msg := &Message{
Platform: "DINGTALK",
RawID: data.MsgId,
Message: data.Text.Content,
Sender: SenderBase{
Nickname: data.SenderNick,
UserID: FormatDiceIDDingTalk(data.SenderStaffId),
},
Time: time.Now().Unix(),
}
if data.IsAdmin {
msg.Sender.GroupRole = "admin"
}
if data.ConversationType == "2" {
msg.GroupID = FormatDiceIDDingTalkGroup(data.ConversationId)
msg.MessageType = "group"
} else {
msg.MessageType = "private"
}
return msg
}

func (pa *PlatformAdapterDingTalk) Serve() int {
logger := pa.Session.Parent.Logger
logger.Info("Dingtalk Serve")
pa.IntentSession = dingtalk.New(pa.ClientID, pa.Token)
pa.IntentSession.AddEventHandler(pa.OnChatReceive)
pa.IntentSession.AddEventHandler(pa.OnGroupJoined)
err := pa.IntentSession.Open()
if err != nil {
return 1
}
logger.Info("Dingtalk 连接成功")
pa.EndPoint.State = 1
pa.EndPoint.Enable = true
d := pa.Session.Parent
d.LastUpdatedTime = time.Now().Unix()
d.Save(false)
return 0
}

func FormatDiceIDDingTalk(diceDingTalk string) string {
return fmt.Sprintf("DINGTALK:%s", diceDingTalk)
}

func FormatDiceIDDingTalkGroup(diceDingTalk string) string {
return fmt.Sprintf("DINGTALK-Group:%s", diceDingTalk)
}

func ExtractDingTalkUserID(id string) string {
if strings.HasPrefix(id, "DINGTALK:") {
return id[len("DINGTALK:"):]
}
return id
}

func ExtractDingTalkGroupID(id string) string {
if strings.HasPrefix(id, "DINGTALK-Group:") {
return id[len("DINGTALK-Group:"):]
}
return id
}
39 changes: 39 additions & 0 deletions dice/platform_adapter_dingtalk_helper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package dice

import (
"time"

"github.com/google/uuid"
)

func NewDingTalkConnItem(clientID string, token string, nickname string, robotCode string) *EndPointInfo {
conn := new(EndPointInfo)
conn.ID = uuid.New().String()
conn.Platform = "DINGTALK"
conn.Nickname = nickname
conn.ProtocolType = ""
conn.Enable = false
conn.RelWorkDir = "extra/dingtalk-" + conn.ID
conn.Adapter = &PlatformAdapterDingTalk{
EndPoint: conn,
ClientID: clientID,
Token: token,
RobotCode: robotCode,
}
return conn
}

func ServeDingTalk(d *Dice, ep *EndPointInfo) {
defer CrashLog()
if ep.Platform == "DINGTALK" {
conn := ep.Adapter.(*PlatformAdapterDingTalk)
d.Logger.Infof("Dingtalk 尝试连接")
if conn.Serve() != 0 {
d.Logger.Errorf("连接Dingtalk 失败")
ep.State = 3
ep.Enable = false
d.LastUpdatedTime = time.Now().Unix()
d.Save(false)
}
}
}
Loading