feat(feishu): 添加飞书消息反应功能
- 在配置中新增 react_emoji 字段用于设置反应表情类型 - 实现 ReactToMessage 方法为入站消息添加表情反应 - 添加 addReaction 和 removeReaction 方法管理反应操作 - 提供撤消函数支持移除已添加的表情反应 - 默认使用 OK 表情作为反应类型
This commit is contained in:
parent
302cd05be4
commit
99a50eb3b2
2 changed files with 102 additions and 0 deletions
|
|
@ -93,6 +93,107 @@ func (c *FeishuChannel) Stop(ctx context.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReactToMessage implements channels.ReactionCapable.
|
||||||
|
// It adds an emoji reaction to the inbound message and returns an undo function.
|
||||||
|
// Common emoji types: THUMBSUP, OK, EYES, DONE, HEART, ROCKET
|
||||||
|
func (c *FeishuChannel) ReactToMessage(ctx context.Context, chatID, messageID string) (func(), error) {
|
||||||
|
if messageID == "" {
|
||||||
|
return func() {}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
emojiType := c.config.ReactEmoji
|
||||||
|
if emojiType == "" {
|
||||||
|
emojiType = "OK" // Default emoji
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add reaction and get reaction_id
|
||||||
|
reactionID, err := c.addReaction(ctx, messageID, emojiType)
|
||||||
|
if err != nil {
|
||||||
|
logger.DebugCF("feishu", "Failed to add reaction", map[string]any{
|
||||||
|
"message_id": messageID,
|
||||||
|
"emoji": emojiType,
|
||||||
|
"error": err.Error(),
|
||||||
|
})
|
||||||
|
// Return no-op on error
|
||||||
|
return func() {}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.DebugCF("feishu", "Added reaction to message", map[string]any{
|
||||||
|
"message_id": messageID,
|
||||||
|
"emoji": emojiType,
|
||||||
|
"reaction_id": reactionID,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Return undo function
|
||||||
|
return func() {
|
||||||
|
if reactionID != "" {
|
||||||
|
c.removeReaction(context.Background(), messageID, reactionID)
|
||||||
|
}
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// addReaction adds an emoji reaction to a message and returns the reaction_id.
|
||||||
|
func (c *FeishuChannel) addReaction(ctx context.Context, messageID, emojiType string) (string, error) {
|
||||||
|
req := larkim.NewCreateMessageReactionReqBuilder().
|
||||||
|
MessageId(messageID).
|
||||||
|
Body(larkim.NewCreateMessageReactionReqBodyBuilder().
|
||||||
|
ReactionType(larkim.NewEmojiBuilder().
|
||||||
|
EmojiType(emojiType).
|
||||||
|
Build()).
|
||||||
|
Build()).
|
||||||
|
Build()
|
||||||
|
|
||||||
|
resp, err := c.client.Im.V1.MessageReaction.Create(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("feishu add reaction: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !resp.Success() {
|
||||||
|
return "", fmt.Errorf("feishu add reaction failed: code=%d msg=%s", resp.Code, resp.Msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.Data != nil && resp.Data.ReactionId != nil {
|
||||||
|
return *resp.Data.ReactionId, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// removeReaction removes an emoji reaction from a message using reaction_id.
|
||||||
|
func (c *FeishuChannel) removeReaction(ctx context.Context, messageID, reactionID string) error {
|
||||||
|
req := larkim.NewDeleteMessageReactionReqBuilder().
|
||||||
|
MessageId(messageID).
|
||||||
|
ReactionId(reactionID).
|
||||||
|
Build()
|
||||||
|
|
||||||
|
resp, err := c.client.Im.V1.MessageReaction.Delete(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
logger.DebugCF("feishu", "Failed to remove reaction", map[string]any{
|
||||||
|
"message_id": messageID,
|
||||||
|
"reaction_id": reactionID,
|
||||||
|
"error": err.Error(),
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !resp.Success() {
|
||||||
|
logger.DebugCF("feishu", "Failed to remove reaction", map[string]any{
|
||||||
|
"message_id": messageID,
|
||||||
|
"reaction_id": reactionID,
|
||||||
|
"code": resp.Code,
|
||||||
|
"msg": resp.Msg,
|
||||||
|
})
|
||||||
|
return fmt.Errorf("feishu remove reaction failed: code=%d msg=%s", resp.Code, resp.Msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.DebugCF("feishu", "Removed reaction from message", map[string]any{
|
||||||
|
"message_id": messageID,
|
||||||
|
"reaction_id": reactionID,
|
||||||
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *FeishuChannel) Send(ctx context.Context, msg bus.OutboundMessage) error {
|
func (c *FeishuChannel) Send(ctx context.Context, msg bus.OutboundMessage) error {
|
||||||
if !c.IsRunning() {
|
if !c.IsRunning() {
|
||||||
return channels.ErrNotRunning
|
return channels.ErrNotRunning
|
||||||
|
|
|
||||||
|
|
@ -252,6 +252,7 @@ type FeishuConfig struct {
|
||||||
AllowFrom FlexibleStringSlice `json:"allow_from" env:"PICOCLAW_CHANNELS_FEISHU_ALLOW_FROM"`
|
AllowFrom FlexibleStringSlice `json:"allow_from" env:"PICOCLAW_CHANNELS_FEISHU_ALLOW_FROM"`
|
||||||
GroupTrigger GroupTriggerConfig `json:"group_trigger,omitempty"`
|
GroupTrigger GroupTriggerConfig `json:"group_trigger,omitempty"`
|
||||||
ReasoningChannelID string `json:"reasoning_channel_id" env:"PICOCLAW_CHANNELS_FEISHU_REASONING_CHANNEL_ID"`
|
ReasoningChannelID string `json:"reasoning_channel_id" env:"PICOCLAW_CHANNELS_FEISHU_REASONING_CHANNEL_ID"`
|
||||||
|
ReactEmoji string `json:"react_emoji" env:"PICOCLAW_CHANNELS_FEISHU_REACT_EMOJI"` // Emoji type for reaction, e.g. "THUMBSUP", "EYES", "OK"
|
||||||
}
|
}
|
||||||
|
|
||||||
type DiscordConfig struct {
|
type DiscordConfig struct {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue