package harmony import ( "fmt" "log" "strconv" "strings" "time" "github.com/42wim/matterbridge/bridge" "github.com/42wim/matterbridge/bridge/config" "github.com/harmony-development/shibshib" chatv1 "github.com/harmony-development/shibshib/gen/chat/v1" typesv1 "github.com/harmony-development/shibshib/gen/harmonytypes/v1" profilev1 "github.com/harmony-development/shibshib/gen/profile/v1" ) type cachedProfile struct { data *profilev1.GetProfileResponse lastUpdated time.Time } type Bharmony struct { *bridge.Config c *shibshib.Client profileCache map[uint64]cachedProfile } func uToStr(in uint64) string { return strconv.FormatUint(in, 10) } func strToU(in string) (uint64, error) { return strconv.ParseUint(in, 10, 64) } func New(cfg *bridge.Config) bridge.Bridger { b := &Bharmony{ Config: cfg, profileCache: map[uint64]cachedProfile{}, } return b } func (b *Bharmony) getProfile(u uint64) (*profilev1.GetProfileResponse, error) { if v, ok := b.profileCache[u]; ok && time.Since(v.lastUpdated) < time.Minute*10 { return v.data, nil } resp, err := b.c.ProfileKit.GetProfile(&profilev1.GetProfileRequest{ UserId: u, }) if err != nil { if v, ok := b.profileCache[u]; ok { return v.data, nil } return nil, err } b.profileCache[u] = cachedProfile{ data: resp, lastUpdated: time.Now(), } return resp, nil } func (b *Bharmony) avatarFor(m *chatv1.Message) string { if m.Overrides != nil { return m.Overrides.GetAvatar() } profi, err := b.getProfile(m.AuthorId) if err != nil { return "" } return b.c.TransformHMCURL(profi.Profile.GetUserAvatar()) } func (b *Bharmony) usernameFor(m *chatv1.Message) string { if m.Overrides != nil { return m.Overrides.GetUsername() } profi, err := b.getProfile(m.AuthorId) if err != nil { return "" } return profi.Profile.UserName } func (b *Bharmony) toMessage(msg *shibshib.LocatedMessage) config.Message { message := config.Message{} message.Account = b.Account message.UserID = uToStr(msg.Message.AuthorId) message.Avatar = b.avatarFor(msg.Message) message.Username = b.usernameFor(msg.Message) message.Channel = uToStr(msg.ChannelID) message.ID = uToStr(msg.MessageId) switch content := msg.Message.Content.Content.(type) { case *chatv1.Content_EmbedMessage: message.Text = "Embed" case *chatv1.Content_AttachmentMessage: var s strings.Builder for idx, attach := range content.AttachmentMessage.Files { s.WriteString(b.c.TransformHMCURL(attach.Id)) if idx < len(content.AttachmentMessage.Files)-1 { s.WriteString(", ") } } message.Text = s.String() case *chatv1.Content_PhotoMessage: var s strings.Builder for idx, attach := range content.PhotoMessage.GetPhotos() { s.WriteString(attach.GetCaption().GetText()) s.WriteString("\n") s.WriteString(b.c.TransformHMCURL(attach.GetHmc())) if idx < len(content.PhotoMessage.GetPhotos())-1 { s.WriteString("\n\n") } } message.Text = s.String() case *chatv1.Content_TextMessage: message.Text = content.TextMessage.Content.Text } return message } func (b *Bharmony) outputMessages() { for { msg := <-b.c.EventsStream() if msg.Message.AuthorId == b.c.UserID { continue } b.Remote <- b.toMessage(msg) } } func (b *Bharmony) GetUint64(conf string) uint64 { num, err := strToU(b.GetString(conf)) if err != nil { log.Fatal(err) } return num } func (b *Bharmony) Connect() (err error) { b.c, err = shibshib.NewClient(b.GetString("Homeserver"), b.GetString("Token"), b.GetUint64("UserID")) if err != nil { return } b.c.SubscribeToGuild(b.GetUint64("Community")) go b.outputMessages() return nil } func (b *Bharmony) send(msg config.Message) (id string, err error) { msgChan, err := strToU(msg.Channel) if err != nil { return } retID, err := b.c.ChatKit.SendMessage(&chatv1.SendMessageRequest{ GuildId: b.GetUint64("Community"), ChannelId: msgChan, Content: &chatv1.Content{ Content: &chatv1.Content_TextMessage{ TextMessage: &chatv1.Content_TextContent{ Content: &chatv1.FormattedText{ Text: msg.Text, }, }, }, }, Overrides: &chatv1.Overrides{ Username: &msg.Username, Avatar: &msg.Avatar, Reason: &chatv1.Overrides_Bridge{Bridge: &typesv1.Empty{}}, }, InReplyTo: nil, EchoId: nil, Metadata: nil, }) if err != nil { err = fmt.Errorf("send: error sending message: %w", err) log.Println(err.Error()) } return uToStr(retID.MessageId), err } func (b *Bharmony) delete(msg config.Message) (id string, err error) { msgChan, err := strToU(msg.Channel) if err != nil { return "", err } msgID, err := strToU(msg.ID) if err != nil { return "", err } _, err = b.c.ChatKit.DeleteMessage(&chatv1.DeleteMessageRequest{ GuildId: b.GetUint64("Community"), ChannelId: msgChan, MessageId: msgID, }) return "", err } func (b *Bharmony) typing(msg config.Message) (id string, err error) { msgChan, err := strToU(msg.Channel) if err != nil { return "", err } _, err = b.c.ChatKit.Typing(&chatv1.TypingRequest{ GuildId: b.GetUint64("Community"), ChannelId: msgChan, }) return "", err } func (b *Bharmony) Send(msg config.Message) (id string, err error) { switch msg.Event { case "": return b.send(msg) case config.EventMsgDelete: return b.delete(msg) case config.EventUserTyping: return b.typing(msg) default: return "", nil } } func (b *Bharmony) JoinChannel(channel config.ChannelInfo) error { return nil } func (b *Bharmony) Disconnect() error { return nil }