2020-07-18 14:08:25 +00:00
|
|
|
package nctalk
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2020-08-30 11:49:26 +00:00
|
|
|
"crypto/tls"
|
2020-07-18 14:08:25 +00:00
|
|
|
"strconv"
|
2020-08-30 23:49:43 +00:00
|
|
|
"strings"
|
2020-07-18 14:08:25 +00:00
|
|
|
|
|
|
|
"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")
|
2020-08-30 11:49:26 +00:00
|
|
|
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()
|
2020-07-18 14:08:25 +00:00
|
|
|
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 {
|
2020-10-19 21:16:34 +00:00
|
|
|
tr, err := room.NewTalkRoom(b.user, channel.Name)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-07-18 14:08:25 +00:00
|
|
|
newRoom := Broom{
|
2020-10-19 21:16:34 +00:00
|
|
|
room: tr,
|
2020-07-18 14:08:25 +00:00
|
|
|
}
|
|
|
|
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)
|
2020-10-01 20:59:35 +00:00
|
|
|
|
2020-07-18 14:08:25 +00:00
|
|
|
go func() {
|
|
|
|
for msg := range c {
|
2020-10-19 21:16:34 +00:00
|
|
|
msg := msg
|
2020-12-09 23:06:27 +00:00
|
|
|
|
|
|
|
if msg.Error != nil {
|
|
|
|
b.Log.Errorf("Fatal message poll error: %s\n", msg.Error)
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-06-01 21:17:07 +00:00
|
|
|
// Ignore messages that are from the bot user
|
2021-06-19 19:45:19 +00:00
|
|
|
if msg.ActorID == b.user.User || msg.ActorType == "bridged" {
|
2020-07-18 14:08:25 +00:00
|
|
|
continue
|
|
|
|
}
|
2020-10-19 21:16:34 +00:00
|
|
|
|
2021-06-01 21:17:07 +00:00
|
|
|
// Handle deleting messages
|
|
|
|
if msg.MessageType == ocs.MessageSystem && msg.Parent != nil && msg.Parent.MessageType == ocs.MessageDelete {
|
|
|
|
b.handleDeletingMessage(&msg, &newRoom)
|
|
|
|
continue
|
|
|
|
}
|
2020-10-19 21:16:34 +00:00
|
|
|
|
2021-06-01 21:17:07 +00:00
|
|
|
// Handle sending messages
|
|
|
|
if msg.MessageType == ocs.MessageComment {
|
|
|
|
b.handleSendingMessage(&msg, &newRoom)
|
2020-10-19 21:16:34 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2020-07-18 14:08:25 +00:00
|
|
|
}
|
|
|
|
}()
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2021-06-01 21:17:07 +00:00
|
|
|
// 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)
|
2021-05-27 19:44:54 +00:00
|
|
|
|
2021-06-01 21:17:07 +00:00
|
|
|
return "", nil
|
|
|
|
}
|
2021-05-27 19:44:54 +00:00
|
|
|
|
2021-06-19 19:45:19 +00:00
|
|
|
sentMessage, err := b.sendText(r, &msg, msg.Text)
|
2021-06-01 21:17:07 +00:00
|
|
|
if err != nil {
|
|
|
|
b.Log.Errorf("Could not send message to room %v from %v: %v", msg.Channel, msg.Username, err)
|
2021-05-27 19:44:54 +00:00
|
|
|
|
2021-06-01 21:17:07 +00:00
|
|
|
return "", nil
|
|
|
|
}
|
|
|
|
return strconv.Itoa(sentMessage.ID), nil
|
|
|
|
}
|
2021-05-27 19:44:54 +00:00
|
|
|
|
2021-06-01 21:17:07 +00:00
|
|
|
// 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
|
2020-07-18 14:08:25 +00:00
|
|
|
}
|
2021-06-01 21:17:07 +00:00
|
|
|
|
|
|
|
// Message is not a type that is currently supported
|
|
|
|
return "", nil
|
2020-07-18 14:08:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (b *Btalk) getRoom(token string) *Broom {
|
|
|
|
for _, r := range b.rooms {
|
|
|
|
if r.room.Token == token {
|
|
|
|
return &r
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2020-08-30 23:49:43 +00:00
|
|
|
|
2021-06-19 19:45:19 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2020-10-19 21:16:34 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2021-05-27 19:44:54 +00:00
|
|
|
func (b *Btalk) handleSendingFile(msg *config.Message, r *Broom) error {
|
|
|
|
for _, f := range msg.Extra["file"] {
|
|
|
|
fi := f.(config.FileInfo)
|
|
|
|
if fi.URL == "" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2021-06-19 19:45:19 +00:00
|
|
|
message := ""
|
2021-05-27 19:44:54 +00:00
|
|
|
if fi.Comment != "" {
|
|
|
|
message += fi.Comment + " "
|
|
|
|
}
|
|
|
|
message += fi.URL
|
2021-06-19 19:45:19 +00:00
|
|
|
_, err := b.sendText(r, msg, message)
|
2021-05-27 19:44:54 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-06-01 21:17:07 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2020-08-30 23:49:43 +00:00
|
|
|
// 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 != "" {
|
2020-10-19 21:16:34 +00:00
|
|
|
text = parameter.Name
|
2020-08-30 23:49:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
message = strings.ReplaceAll(message, "{"+id+"}", text)
|
|
|
|
}
|
|
|
|
|
|
|
|
return message
|
|
|
|
}
|
2020-10-01 20:59:35 +00:00
|
|
|
|
2021-06-01 21:17:07 +00:00
|
|
|
func DisplayName(msg *ocs.TalkRoomMessageData, suffix string) string {
|
2020-10-01 20:59:35 +00:00
|
|
|
if msg.ActorType == ocs.ActorGuest {
|
|
|
|
if msg.ActorDisplayName == "" {
|
|
|
|
return "Guest"
|
|
|
|
}
|
|
|
|
|
|
|
|
return msg.ActorDisplayName + suffix
|
|
|
|
}
|
|
|
|
|
|
|
|
return msg.ActorDisplayName
|
|
|
|
}
|