diff options
Diffstat (limited to 'teleirc/matterbridge/bridge/nctalk/nctalk.go')
| -rw-r--r-- | teleirc/matterbridge/bridge/nctalk/nctalk.go | 295 |
1 files changed, 295 insertions, 0 deletions
diff --git a/teleirc/matterbridge/bridge/nctalk/nctalk.go b/teleirc/matterbridge/bridge/nctalk/nctalk.go new file mode 100644 index 0000000..82acba4 --- /dev/null +++ b/teleirc/matterbridge/bridge/nctalk/nctalk.go @@ -0,0 +1,295 @@ +package nctalk + +import ( + "context" + "crypto/tls" + "strconv" + "strings" + + "github.com/42wim/matterbridge/bridge" + "github.com/42wim/matterbridge/bridge/config" + + "gomod.garykim.dev/nc-talk/ocs" + "gomod.garykim.dev/nc-talk/room" + "gomod.garykim.dev/nc-talk/user" +) + +type Btalk struct { + user *user.TalkUser + rooms []Broom + *bridge.Config +} + +func New(cfg *bridge.Config) bridge.Bridger { + return &Btalk{Config: cfg} +} + +type Broom struct { + room *room.TalkRoom + ctx context.Context + ctxCancel context.CancelFunc +} + +func (b *Btalk) Connect() error { + b.Log.Info("Connecting") + tconfig := &user.TalkUserConfig{ + TLSConfig: &tls.Config{ + InsecureSkipVerify: b.GetBool("SkipTLSVerify"), //nolint:gosec + }, + } + var err error + b.user, err = user.NewUser(b.GetString("Server"), b.GetString("Login"), b.GetString("Password"), tconfig) + if err != nil { + b.Log.Error("Config could not be used") + return err + } + _, err = b.user.Capabilities() + if err != nil { + b.Log.Error("Cannot Connect") + return err + } + b.Log.Info("Connected") + return nil +} + +func (b *Btalk) Disconnect() error { + for _, r := range b.rooms { + r.ctxCancel() + } + return nil +} + +func (b *Btalk) JoinChannel(channel config.ChannelInfo) error { + tr, err := room.NewTalkRoom(b.user, channel.Name) + if err != nil { + return err + } + newRoom := Broom{ + room: tr, + } + newRoom.ctx, newRoom.ctxCancel = context.WithCancel(context.Background()) + c, err := newRoom.room.ReceiveMessages(newRoom.ctx) + if err != nil { + return err + } + b.rooms = append(b.rooms, newRoom) + + go func() { + for msg := range c { + msg := msg + + if msg.Error != nil { + b.Log.Errorf("Fatal message poll error: %s\n", msg.Error) + + return + } + + // Ignore messages that are from the bot user + if msg.ActorID == b.user.User || msg.ActorType == "bridged" { + continue + } + + // Handle deleting messages + if msg.MessageType == ocs.MessageSystem && msg.Parent != nil && msg.Parent.MessageType == ocs.MessageDelete { + b.handleDeletingMessage(&msg, &newRoom) + continue + } + + // Handle sending messages + if msg.MessageType == ocs.MessageComment { + b.handleSendingMessage(&msg, &newRoom) + continue + } + + } + }() + return nil +} + +func (b *Btalk) Send(msg config.Message) (string, error) { + r := b.getRoom(msg.Channel) + if r == nil { + b.Log.Errorf("Could not find room for %v", msg.Channel) + return "", nil + } + + // Standard Message Send + if msg.Event == "" { + // Handle sending files if they are included + err := b.handleSendingFile(&msg, r) + if err != nil { + b.Log.Errorf("Could not send files in message to room %v from %v: %v", msg.Channel, msg.Username, err) + + return "", nil + } + + sentMessage, err := b.sendText(r, &msg, msg.Text) + if err != nil { + b.Log.Errorf("Could not send message to room %v from %v: %v", msg.Channel, msg.Username, err) + + return "", nil + } + return strconv.Itoa(sentMessage.ID), nil + } + + // Message Deletion + if msg.Event == config.EventMsgDelete { + messageID, err := strconv.Atoi(msg.ID) + if err != nil { + return "", err + } + data, err := r.room.DeleteMessage(messageID) + if err != nil { + return "", err + } + return strconv.Itoa(data.ID), nil + } + + // Message is not a type that is currently supported + return "", nil +} + +func (b *Btalk) getRoom(token string) *Broom { + for _, r := range b.rooms { + if r.room.Token == token { + return &r + } + } + return nil +} + +func (b *Btalk) sendText(r *Broom, msg *config.Message, text string) (*ocs.TalkRoomMessageData, error) { + messageToSend := &room.Message{Message: msg.Username + text} + + if b.GetBool("SeparateDisplayName") { + messageToSend.Message = text + messageToSend.ActorDisplayName = msg.Username + } + + return r.room.SendComplexMessage(messageToSend) +} + +func (b *Btalk) handleFiles(mmsg *config.Message, message *ocs.TalkRoomMessageData) error { + for _, parameter := range message.MessageParameters { + if parameter.Type == ocs.ROSTypeFile { + // Get the file + file, err := b.user.DownloadFile(parameter.Path) + if err != nil { + return err + } + + if mmsg.Extra == nil { + mmsg.Extra = make(map[string][]interface{}) + } + + mmsg.Extra["file"] = append(mmsg.Extra["file"], config.FileInfo{ + Name: parameter.Name, + Data: file, + Size: int64(len(*file)), + Avatar: false, + }) + } + } + + return nil +} + +func (b *Btalk) handleSendingFile(msg *config.Message, r *Broom) error { + for _, f := range msg.Extra["file"] { + fi := f.(config.FileInfo) + if fi.URL == "" { + continue + } + + message := "" + if fi.Comment != "" { + message += fi.Comment + " " + } + message += fi.URL + _, err := b.sendText(r, msg, message) + if err != nil { + return err + } + } + + return nil +} + +func (b *Btalk) handleSendingMessage(msg *ocs.TalkRoomMessageData, r *Broom) { + remoteMessage := config.Message{ + Text: formatRichObjectString(msg.Message, msg.MessageParameters), + Channel: r.room.Token, + Username: DisplayName(msg, b.guestSuffix()), + UserID: msg.ActorID, + Account: b.Account, + } + // It is possible for the ID to not be set on older versions of Talk so we only set it if + // the ID is not blank + if msg.ID != 0 { + remoteMessage.ID = strconv.Itoa(msg.ID) + } + + // Handle Files + err := b.handleFiles(&remoteMessage, msg) + if err != nil { + b.Log.Errorf("Error handling file: %#v", msg) + + return + } + + b.Log.Debugf("<= Message is %#v", remoteMessage) + b.Remote <- remoteMessage +} + +func (b *Btalk) handleDeletingMessage(msg *ocs.TalkRoomMessageData, r *Broom) { + remoteMessage := config.Message{ + Event: config.EventMsgDelete, + Text: config.EventMsgDelete, + Channel: r.room.Token, + ID: strconv.Itoa(msg.Parent.ID), + Account: b.Account, + } + b.Log.Debugf("<= Message being deleted is %#v", remoteMessage) + b.Remote <- remoteMessage +} + +func (b *Btalk) guestSuffix() string { + guestSuffix := " (Guest)" + if b.IsKeySet("GuestSuffix") { + guestSuffix = b.GetString("GuestSuffix") + } + + return guestSuffix +} + +// Spec: https://github.com/nextcloud/server/issues/1706#issue-182308785 +func formatRichObjectString(message string, parameters map[string]ocs.RichObjectString) string { + for id, parameter := range parameters { + text := parameter.Name + + switch parameter.Type { + case ocs.ROSTypeUser, ocs.ROSTypeGroup: + text = "@" + text + case ocs.ROSTypeFile: + if parameter.Link != "" { + text = parameter.Name + } + } + + message = strings.ReplaceAll(message, "{"+id+"}", text) + } + + return message +} + +func DisplayName(msg *ocs.TalkRoomMessageData, suffix string) string { + if msg.ActorType == ocs.ActorGuest { + if msg.ActorDisplayName == "" { + return "Guest" + } + + return msg.ActorDisplayName + suffix + } + + return msg.ActorDisplayName +} |
