summaryrefslogtreecommitdiffstats
path: root/bridge/harmony/harmony.go
diff options
context:
space:
mode:
authorJanet Blackquill <uhhadd@gmail.com>2021-12-18 16:43:29 -0500
committerGitHub <noreply@github.com>2021-12-18 22:43:29 +0100
commitdbedc994216fa2e932f1aefd3ea27832419b85ef (patch)
tree2a577dda9e296c9f854ea0677394128d17ca6d17 /bridge/harmony/harmony.go
parent6cb359cb808e2353db2675b58da95ac0349689fe (diff)
downloadmatterbridge-msglm-dbedc994216fa2e932f1aefd3ea27832419b85ef.tar.gz
matterbridge-msglm-dbedc994216fa2e932f1aefd3ea27832419b85ef.tar.bz2
matterbridge-msglm-dbedc994216fa2e932f1aefd3ea27832419b85ef.zip
Add support for Harmony (#1656)
Harmony is a relatively new (1,5yo) chat protocol with a small community. This introduces support for Harmony into Matterbridge, using the functionality specifically designed for bridge bots. The implementation is a modest 200 lines of code.
Diffstat (limited to 'bridge/harmony/harmony.go')
-rw-r--r--bridge/harmony/harmony.go252
1 files changed, 252 insertions, 0 deletions
diff --git a/bridge/harmony/harmony.go b/bridge/harmony/harmony.go
new file mode 100644
index 00000000..14174c31
--- /dev/null
+++ b/bridge/harmony/harmony.go
@@ -0,0 +1,252 @@
+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
+}