42wim/matterbridge

View on GitHub
bridge/harmony/harmony.go

Summary

Maintainability
A
0 mins
Test Coverage
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
}