mirror of
https://github.com/sipeed/picoclaw.git
synced 2026-06-12 18:08:54 +00:00
feat(channels): auto-orchestrate Placeholder/Typing/Reaction via capability interfaces
Define PlaceholderCapable, TypingCapable, and ReactionCapable interfaces and have BaseChannel.HandleMessage auto-detect and trigger all three as independent pipelines on inbound messages. This replaces the scattered manual orchestration code in each channel's handleMessage with a single unified dispatch in the framework layer. Changes: - Add PlaceholderCapable interface to interfaces.go - Add ReactionCapable + RecordReactionUndo to interfaces.go - BaseChannel.HandleMessage auto-triggers Typing → Reaction → Placeholder - Manager gains reactionUndos sync.Map with TTL janitor cleanup - Telegram: extract SendPlaceholder from manual code, add StartTyping - Discord: add SendPlaceholder + StartTyping - Pico: add SendPlaceholder (uses Pico Protocol message.create) - Slack: extract ReactToMessage from manual code - OneBot: extract ReactToMessage, remove leaked pendingEmojiMsg sync.Map - LINE: move group-chat guard into StartTyping, remove manual orchestration - Config: add Placeholder to PicoConfig; remove from Slack/LINE/OneBot (no MessageEditor, so placeholder config was dead code)
This commit is contained in:
@@ -23,21 +23,20 @@ import (
|
||||
|
||||
type OneBotChannel struct {
|
||||
*channels.BaseChannel
|
||||
config config.OneBotConfig
|
||||
conn *websocket.Conn
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
dedup map[string]struct{}
|
||||
dedupRing []string
|
||||
dedupIdx int
|
||||
mu sync.Mutex
|
||||
writeMu sync.Mutex
|
||||
echoCounter int64
|
||||
selfID int64
|
||||
pending map[string]chan json.RawMessage
|
||||
pendingMu sync.Mutex
|
||||
lastMessageID sync.Map
|
||||
pendingEmojiMsg sync.Map
|
||||
config config.OneBotConfig
|
||||
conn *websocket.Conn
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
dedup map[string]struct{}
|
||||
dedupRing []string
|
||||
dedupIdx int
|
||||
mu sync.Mutex
|
||||
writeMu sync.Mutex
|
||||
echoCounter int64
|
||||
selfID int64
|
||||
pending map[string]chan json.RawMessage
|
||||
pendingMu sync.Mutex
|
||||
lastMessageID sync.Map
|
||||
}
|
||||
|
||||
type oneBotRawEvent struct {
|
||||
@@ -129,6 +128,22 @@ func (c *OneBotChannel) setMsgEmojiLike(messageID string, emojiID int, set bool)
|
||||
}()
|
||||
}
|
||||
|
||||
// ReactToMessage implements channels.ReactionCapable.
|
||||
// It adds an emoji reaction (ID 289) to group messages and returns an undo function.
|
||||
// Private messages return a no-op since reactions are only meaningful in groups.
|
||||
func (c *OneBotChannel) ReactToMessage(ctx context.Context, chatID, messageID string) (func(), error) {
|
||||
// Only react in group chats
|
||||
if !strings.HasPrefix(chatID, "group:") {
|
||||
return func() {}, nil
|
||||
}
|
||||
|
||||
c.setMsgEmojiLike(messageID, 289, true)
|
||||
|
||||
return func() {
|
||||
c.setMsgEmojiLike(messageID, 289, false)
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *OneBotChannel) Start(ctx context.Context) error {
|
||||
if c.config.WSUrl == "" {
|
||||
return fmt.Errorf("OneBot ws_url not configured")
|
||||
@@ -1044,18 +1059,6 @@ func (c *OneBotChannel) handleMessage(raw *oneBotRawEvent) {
|
||||
|
||||
c.lastMessageID.Store(chatID, messageID)
|
||||
|
||||
if raw.MessageType == "group" && messageID != "" && messageID != "0" {
|
||||
c.setMsgEmojiLike(messageID, 289, true)
|
||||
c.pendingEmojiMsg.Store(chatID, messageID)
|
||||
// Register emoji stop with Manager for outbound orchestration
|
||||
if rec := c.GetPlaceholderRecorder(); rec != nil {
|
||||
capturedMsgID := messageID
|
||||
rec.RecordTypingStop("onebot", chatID, func() {
|
||||
c.setMsgEmojiLike(capturedMsgID, 289, false)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
senderInfo := bus.SenderInfo{
|
||||
Platform: "onebot",
|
||||
PlatformID: senderID,
|
||||
|
||||
Reference in New Issue
Block a user