Skip to content

Commit ed831bf

Browse files
committed
feat(chatops-lark): support bot mention in group chat commands
- Add support for parsing commands in group chats when the bot is mentioned - Introduce new message type detection for group and private messages - Update README to include bot name configuration - Implement bot mention detection using regex and mentions parsing - Add new command parsing logic for group chat scenarios
1 parent bff20ce commit ed831bf

File tree

2 files changed

+101
-5
lines changed

2 files changed

+101
-5
lines changed

chatops-lark/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ You can run it by following steps:
88
```yaml
99
cherry-pick-invite.audit_webhook: <your_audit_lark_webhook>
1010
cherry-pick-invite.github_token: <your_github_token>
11+
bot_name: <your_bot_name> # for @bot mention in group chat
1112
```
1213
2. Run the lark bot app:
1314
```bash

chatops-lark/pkg/events/handler/root.go

Lines changed: 100 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"encoding/json"
66
"fmt"
7+
"regexp"
78
"slices"
89
"strings"
910
"time"
@@ -20,6 +21,10 @@ import (
2021
const (
2122
ctxKeyGithubToken = "github_token"
2223
ctxKeyLarkSenderEmail = "lark.sender.email"
24+
25+
// Message types
26+
msgTypePrivate = "private"
27+
msgTypeGroup = "group"
2328
)
2429

2530
var (
@@ -59,9 +64,11 @@ var (
5964
)
6065

6166
type Command struct {
62-
Name string
63-
Args []string
64-
Sender *CommandSender
67+
Name string
68+
Args []string
69+
Sender *CommandSender
70+
MsgType string // Message type: private or group
71+
ChatID string // Group ID, only valid when MsgType is group
6572
}
6673

6774
type CommandSender struct {
@@ -124,7 +131,14 @@ func (r *rootHandler) Handle(ctx context.Context, event *larkim.P2MessageReceive
124131
}
125132

126133
hl := log.With().Str("eventId", eventID).Logger()
127-
command := shouldHandle(event)
134+
135+
// Get bot name from config with type assertion
136+
botName := ""
137+
if name, ok := r.Config["bot_name"].(string); ok {
138+
botName = name
139+
}
140+
141+
command := shouldHandle(event, botName)
128142
if command == nil {
129143
hl.Debug().Msg("none command received")
130144
return nil
@@ -271,7 +285,76 @@ func (r *rootHandler) deleteReaction(reqMsgID, reactionID string) error {
271285
return nil
272286
}
273287

274-
func shouldHandle(event *larkim.P2MessageReceiveV1) *Command {
288+
// determineMessageType determines the message type and checks if the bot was mentioned
289+
func determineMessageType(event *larkim.P2MessageReceiveV1, botName string) (msgType string, chatID string, isMentionBot bool) {
290+
// Check if the message is from a group chat
291+
if event.Event.Message.ChatId != nil && event.Event.Message.ChatType != nil && *event.Event.Message.ChatType == "group" {
292+
msgType = msgTypeGroup
293+
chatID = *event.Event.Message.ChatId
294+
isMentionBot = checkIfBotMentioned(event, botName)
295+
} else {
296+
msgType = msgTypePrivate
297+
}
298+
299+
return msgType, chatID, isMentionBot
300+
}
301+
302+
// checkIfBotMentioned checks if the bot was mentioned in the message
303+
func checkIfBotMentioned(event *larkim.P2MessageReceiveV1, botName string) bool {
304+
if event.Event.Message.Mentions == nil || len(event.Event.Message.Mentions) == 0 {
305+
return false
306+
}
307+
308+
for _, mention := range event.Event.Message.Mentions {
309+
if mention == nil {
310+
continue
311+
}
312+
if mention.Name != nil {
313+
// check if the bot was mentioned
314+
if *mention.Name == botName {
315+
return true
316+
}
317+
}
318+
}
319+
return false
320+
}
321+
322+
// parseGroupCommand parses commands from group messages
323+
// Supported format: @bot /command arg1 arg2...
324+
func parseGroupCommand(event *larkim.P2MessageReceiveV1) *Command {
325+
messageContent := strings.TrimSpace(*event.Event.Message.Content)
326+
327+
var content commandLarkMsgContent
328+
if err := json.Unmarshal([]byte(messageContent), &content); err != nil {
329+
return nil
330+
}
331+
332+
// In Lark messages, mentions are converted to @_user_X format
333+
// Use regex to match commands after @_user_X: /command arg1 arg2...
334+
re := regexp.MustCompile(`@_user_\d+\s+(/\S+)(.*)`)
335+
matches := re.FindStringSubmatch(content.Text)
336+
337+
if len(matches) < 3 {
338+
return nil
339+
}
340+
341+
commandName := matches[1]
342+
argsStr := strings.TrimSpace(matches[2])
343+
var args []string
344+
if argsStr != "" {
345+
args = strings.Fields(argsStr)
346+
}
347+
348+
// Check if it's a privileged command
349+
if slices.Contains(privilegedCommandList, commandName) {
350+
return nil
351+
}
352+
353+
return &Command{Name: commandName, Args: args}
354+
}
355+
356+
// parsePrivateCommand parses commands from private messages
357+
func parsePrivateCommand(event *larkim.P2MessageReceiveV1) *Command {
275358
messageContent := strings.TrimSpace(*event.Event.Message.Content)
276359

277360
var content commandLarkMsgContent
@@ -286,3 +369,15 @@ func shouldHandle(event *larkim.P2MessageReceiveV1) *Command {
286369

287370
return &Command{Name: messageParts[0], Args: messageParts[1:]}
288371
}
372+
373+
func shouldHandle(event *larkim.P2MessageReceiveV1, botName string) *Command {
374+
// Determine message type and whether the bot was mentioned
375+
msgType, _, isMentionBot := determineMessageType(event, botName)
376+
377+
if msgType == msgTypeGroup && isMentionBot {
378+
return parseGroupCommand(event)
379+
} else if msgType == msgTypePrivate {
380+
return parsePrivateCommand(event)
381+
}
382+
return nil
383+
}

0 commit comments

Comments
 (0)