mirror of
https://github.com/memohai/Memoh.git
synced 2026-04-25 07:00:48 +09:00
191 lines
5.1 KiB
Go
191 lines
5.1 KiB
Go
package pipeline
|
|
|
|
import (
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/memohai/memoh/internal/channel"
|
|
)
|
|
|
|
// AdaptInbound converts a channel.InboundMessage into a pipeline CanonicalEvent.
|
|
// The event type is determined by the "event_type" metadata key set by channel
|
|
// adapters: "edit" → EditEvent, "service" → ServiceEvent. All other messages
|
|
// (including the default) produce a MessageEvent.
|
|
func AdaptInbound(msg channel.InboundMessage, sessionID, channelIdentityID, displayName string) CanonicalEvent {
|
|
eventType, _ := msg.Metadata["event_type"].(string)
|
|
switch eventType {
|
|
case "edit":
|
|
return adaptEdit(msg, sessionID, channelIdentityID, displayName)
|
|
case "service":
|
|
return adaptService(msg, sessionID)
|
|
default:
|
|
return adaptMessage(msg, sessionID, channelIdentityID, displayName)
|
|
}
|
|
}
|
|
|
|
func adaptMessage(msg channel.InboundMessage, sessionID, channelIdentityID, displayName string) MessageEvent {
|
|
now := msg.ReceivedAt
|
|
if now.IsZero() {
|
|
now = time.Now()
|
|
}
|
|
|
|
var sender *CanonicalUser
|
|
if channelIdentityID != "" || displayName != "" {
|
|
sender = &CanonicalUser{
|
|
ID: channelIdentityID,
|
|
DisplayName: displayName,
|
|
Username: strings.TrimSpace(msg.Sender.Attribute("username")),
|
|
IsBot: metadataBool(msg.Metadata, "is_bot"),
|
|
}
|
|
}
|
|
|
|
content := adaptContent(msg.Message.Text)
|
|
attachments := adaptAttachments(msg.Message.Attachments)
|
|
|
|
var replyToMessageID, replyToSender, replyToPreview string
|
|
if msg.Message.Reply != nil {
|
|
replyToMessageID = strings.TrimSpace(msg.Message.Reply.MessageID)
|
|
replyToSender = strings.TrimSpace(msg.Message.Reply.Sender)
|
|
replyToPreview = strings.TrimSpace(msg.Message.Reply.Preview)
|
|
}
|
|
|
|
_, offset := now.Zone()
|
|
utcOffsetMin := offset / 60
|
|
|
|
convType := channel.NormalizeConversationType(msg.Conversation.Type)
|
|
|
|
return MessageEvent{
|
|
SessionID: sessionID,
|
|
MessageID: strings.TrimSpace(msg.Message.ID),
|
|
Sender: sender,
|
|
ReceivedAtMs: now.UnixMilli(),
|
|
TimestampSec: now.Unix(),
|
|
UTCOffsetMin: utcOffsetMin,
|
|
Content: content,
|
|
ReplyToMessageID: replyToMessageID,
|
|
ReplyToSender: replyToSender,
|
|
ReplyToPreview: replyToPreview,
|
|
Attachments: attachments,
|
|
Conversation: ConversationMeta{
|
|
Channel: msg.Channel.String(),
|
|
ConversationName: strings.TrimSpace(msg.Conversation.Name),
|
|
ConversationType: convType,
|
|
Target: strings.TrimSpace(msg.ReplyTarget),
|
|
},
|
|
}
|
|
}
|
|
|
|
func adaptContent(text string) []ContentNode {
|
|
text = strings.TrimSpace(text)
|
|
if text == "" {
|
|
return nil
|
|
}
|
|
return []ContentNode{{Type: "text", Text: text}}
|
|
}
|
|
|
|
func adaptAttachments(atts []channel.Attachment) []Attachment {
|
|
if len(atts) == 0 {
|
|
return nil
|
|
}
|
|
result := make([]Attachment, 0, len(atts))
|
|
for _, a := range atts {
|
|
att := Attachment{
|
|
Type: string(a.Type),
|
|
MimeType: strings.TrimSpace(a.Mime),
|
|
FileName: strings.TrimSpace(a.Name),
|
|
ContentHash: strings.TrimSpace(a.ContentHash),
|
|
Width: a.Width,
|
|
Height: a.Height,
|
|
}
|
|
if a.DurationMs > 0 {
|
|
att.Duration = int(a.DurationMs / 1000)
|
|
}
|
|
if ref := a.Reference(); ref != "" {
|
|
att.FilePath = ref
|
|
}
|
|
result = append(result, att)
|
|
}
|
|
return result
|
|
}
|
|
|
|
func adaptEdit(msg channel.InboundMessage, sessionID, channelIdentityID, displayName string) EditEvent {
|
|
now := msg.ReceivedAt
|
|
if now.IsZero() {
|
|
now = time.Now()
|
|
}
|
|
|
|
var sender *CanonicalUser
|
|
if channelIdentityID != "" || displayName != "" {
|
|
sender = &CanonicalUser{
|
|
ID: channelIdentityID,
|
|
DisplayName: displayName,
|
|
Username: strings.TrimSpace(msg.Sender.Attribute("username")),
|
|
IsBot: metadataBool(msg.Metadata, "is_bot"),
|
|
}
|
|
}
|
|
|
|
_, offset := now.Zone()
|
|
return EditEvent{
|
|
SessionID: sessionID,
|
|
MessageID: strings.TrimSpace(msg.Message.ID),
|
|
Sender: sender,
|
|
ReceivedAtMs: now.UnixMilli(),
|
|
TimestampSec: now.Unix(),
|
|
UTCOffsetMin: offset / 60,
|
|
Content: adaptContent(msg.Message.Text),
|
|
Attachments: adaptAttachments(msg.Message.Attachments),
|
|
}
|
|
}
|
|
|
|
func adaptService(msg channel.InboundMessage, sessionID string) ServiceEvent {
|
|
now := msg.ReceivedAt
|
|
if now.IsZero() {
|
|
now = time.Now()
|
|
}
|
|
|
|
action, _ := msg.Metadata["service_action"].(string)
|
|
var actor *CanonicalUser
|
|
if msg.Sender.SubjectID != "" || msg.Sender.DisplayName != "" {
|
|
actor = &CanonicalUser{
|
|
ID: strings.TrimSpace(msg.Sender.SubjectID),
|
|
DisplayName: strings.TrimSpace(msg.Sender.DisplayName),
|
|
Username: strings.TrimSpace(msg.Sender.Attribute("username")),
|
|
}
|
|
}
|
|
|
|
_, offset := now.Zone()
|
|
event := ServiceEvent{
|
|
SessionID: sessionID,
|
|
Action: ServiceAction(action),
|
|
Actor: actor,
|
|
ReceivedAtMs: now.UnixMilli(),
|
|
TimestampSec: now.Unix(),
|
|
UTCOffsetMin: offset / 60,
|
|
}
|
|
if title, ok := msg.Metadata["new_title"].(string); ok {
|
|
event.NewTitle = title
|
|
}
|
|
if title, ok := msg.Metadata["old_title"].(string); ok {
|
|
event.OldTitle = title
|
|
}
|
|
return event
|
|
}
|
|
|
|
func metadataBool(meta map[string]any, key string) bool {
|
|
if meta == nil {
|
|
return false
|
|
}
|
|
v, ok := meta[key]
|
|
if !ok {
|
|
return false
|
|
}
|
|
switch val := v.(type) {
|
|
case bool:
|
|
return val
|
|
case string:
|
|
return strings.EqualFold(val, "true") || val == "1"
|
|
default:
|
|
return false
|
|
}
|
|
}
|