2018-04-10 18:31:28 +02:00
// gomuks - A terminal Matrix client written in Go.
2020-04-19 17:10:14 +02:00
// Copyright (C) 2020 Tulir Asokan
2018-04-10 18:31:28 +02:00
//
// This program is free software: you can redistribute it and/or modify
2019-01-17 13:13:25 +01:00
// it under the terms of the GNU Affero General Public License as published by
2018-04-10 18:31:28 +02:00
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2019-01-17 13:13:25 +01:00
// GNU Affero General Public License for more details.
2018-04-10 18:31:28 +02:00
//
2019-01-17 13:13:25 +01:00
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
2018-04-10 18:31:28 +02:00
2019-04-09 17:42:49 +02:00
package messages
2018-04-10 18:31:28 +02:00
import (
"fmt"
2018-04-24 21:08:57 +02:00
"strings"
2018-04-10 18:31:28 +02:00
2020-04-16 18:27:35 +02:00
"maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/id"
2019-04-09 17:42:49 +02:00
"maunium.net/go/tcell"
2020-04-29 01:45:54 +02:00
"maunium.net/go/gomuks/debug"
2018-04-10 18:31:28 +02:00
"maunium.net/go/gomuks/interface"
2020-04-29 01:45:54 +02:00
"maunium.net/go/gomuks/matrix/muksevt"
2018-04-10 18:31:28 +02:00
"maunium.net/go/gomuks/matrix/rooms"
2019-04-09 17:45:41 +02:00
"maunium.net/go/gomuks/ui/messages/html"
2018-04-11 16:57:15 +02:00
"maunium.net/go/gomuks/ui/messages/tstring"
2018-04-10 18:31:28 +02:00
"maunium.net/go/gomuks/ui/widget"
)
2020-04-16 18:27:35 +02:00
func getCachedEvent ( mainView ifc . MainView , roomID id . RoomID , eventID id . EventID ) * UIMessage {
2019-04-10 20:06:19 +02:00
if roomView := mainView . GetRoom ( roomID ) ; roomView != nil {
if replyToIfcMsg := roomView . GetEvent ( eventID ) ; replyToIfcMsg != nil {
2019-06-15 00:11:51 +02:00
if replyToMsg , ok := replyToIfcMsg . ( * UIMessage ) ; ok && replyToMsg != nil {
2019-04-10 20:06:19 +02:00
return replyToMsg
}
}
}
return nil
}
2020-04-16 18:27:35 +02:00
func ParseEvent ( matrix ifc . MatrixContainer , mainView ifc . MainView , room * rooms . Room , evt * muksevt . Event ) * UIMessage {
2019-04-09 17:45:41 +02:00
msg := directParseEvent ( matrix , room , evt )
if msg == nil {
return nil
}
2020-04-19 14:00:49 +02:00
if content , ok := evt . Content . Parsed . ( * event . MessageEventContent ) ; ok && len ( content . GetReplyTo ( ) ) > 0 {
if replyToMsg := getCachedEvent ( mainView , room . ID , content . GetReplyTo ( ) ) ; replyToMsg != nil {
2019-06-16 13:29:03 +02:00
msg . ReplyTo = replyToMsg . Clone ( )
2020-04-19 14:00:49 +02:00
} else if replyToEvt , _ := matrix . GetEvent ( room , content . GetReplyTo ( ) ) ; replyToEvt != nil {
2019-06-15 00:11:51 +02:00
if replyToMsg := directParseEvent ( matrix , room , replyToEvt ) ; replyToMsg != nil {
msg . ReplyTo = replyToMsg
2020-02-20 20:56:03 +01:00
msg . ReplyTo . Reactions = nil
2019-04-10 01:19:38 +02:00
} else {
// TODO add unrenderable reply header
}
2019-04-09 17:45:41 +02:00
} else {
// TODO add unknown reply header
}
}
return msg
}
2020-04-16 18:27:35 +02:00
func directParseEvent ( matrix ifc . MatrixContainer , room * rooms . Room , evt * muksevt . Event ) * UIMessage {
displayname := string ( evt . Sender )
2019-06-15 00:11:51 +02:00
member := room . GetMember ( evt . Sender )
if member != nil {
displayname = member . Displayname
}
2020-04-16 18:27:35 +02:00
if evt . Unsigned . RedactedBecause != nil || evt . Type == event . EventRedaction {
2019-06-16 19:42:13 +02:00
return NewRedactedMessage ( evt , displayname )
}
2020-04-19 14:00:49 +02:00
switch content := evt . Content . Parsed . ( type ) {
case * event . MessageEventContent :
if evt . Type == event . EventSticker {
content . MsgType = event . MsgImage
}
2019-06-15 00:11:51 +02:00
return ParseMessage ( matrix , room , evt , displayname )
2020-04-19 14:00:49 +02:00
case * event . EncryptedEventContent :
2020-05-05 19:38:58 +02:00
return NewExpandedTextMessage ( evt , displayname , tstring . NewStyleTString ( "Decryption failed or gomuks not built with encryption support" , tcell . StyleDefault . Italic ( true ) ) )
2020-04-19 14:00:49 +02:00
case * event . TopicEventContent , * event . RoomNameEventContent , * event . CanonicalAliasEventContent :
2019-06-15 00:11:51 +02:00
return ParseStateEvent ( evt , displayname )
2020-04-19 14:00:49 +02:00
case * event . MemberEventContent :
2018-04-18 12:38:33 +02:00
return ParseMembershipEvent ( room , evt )
2020-04-19 14:00:49 +02:00
default :
debug . Printf ( "Unknown event content type %T in directParseEvent" , content )
return nil
2018-04-10 18:31:28 +02:00
}
}
2020-04-16 18:27:35 +02:00
func ParseStateEvent ( evt * muksevt . Event , displayname string ) * UIMessage {
2019-01-11 22:28:47 +01:00
text := tstring . NewColorTString ( displayname , widget . GetHashColor ( evt . Sender ) )
2020-04-19 14:00:49 +02:00
switch content := evt . Content . Parsed . ( type ) {
case * event . TopicEventContent :
if len ( content . Topic ) == 0 {
2019-01-11 22:28:47 +01:00
text = text . AppendColor ( " removed the topic." , tcell . ColorGreen )
} else {
text = text . AppendColor ( " changed the topic to " , tcell . ColorGreen ) .
2020-04-19 14:00:49 +02:00
AppendStyle ( content . Topic , tcell . StyleDefault . Underline ( true ) ) .
2019-01-11 22:28:47 +01:00
AppendColor ( "." , tcell . ColorGreen )
}
2020-04-19 14:00:49 +02:00
case * event . RoomNameEventContent :
if len ( content . Name ) == 0 {
2019-01-11 22:28:47 +01:00
text = text . AppendColor ( " removed the room name." , tcell . ColorGreen )
} else {
text = text . AppendColor ( " changed the room name to " , tcell . ColorGreen ) .
2020-04-19 14:00:49 +02:00
AppendStyle ( content . Name , tcell . StyleDefault . Underline ( true ) ) .
2019-01-11 22:28:47 +01:00
AppendColor ( "." , tcell . ColorGreen )
}
2020-04-19 14:00:49 +02:00
case * event . CanonicalAliasEventContent :
if len ( content . Alias ) == 0 {
2019-01-11 22:28:47 +01:00
text = text . AppendColor ( " removed the main address of the room." , tcell . ColorGreen )
} else {
text = text . AppendColor ( " changed the main address of the room to " , tcell . ColorGreen ) .
2020-04-19 14:00:49 +02:00
AppendStyle ( string ( content . Alias ) , tcell . StyleDefault . Underline ( true ) ) .
2019-01-11 22:28:47 +01:00
AppendColor ( "." , tcell . ColorGreen )
}
2020-04-16 18:27:35 +02:00
//case event.StateAliases:
// text = ParseAliasEvent(evt, displayname)
2019-01-11 22:28:47 +01:00
}
2019-04-09 17:45:41 +02:00
return NewExpandedTextMessage ( evt , displayname , text )
2019-01-11 22:28:47 +01:00
}
2020-04-16 18:27:35 +02:00
func ParseMessage ( matrix ifc . MatrixContainer , room * rooms . Room , evt * muksevt . Event , displayname string ) * UIMessage {
2020-04-19 14:00:49 +02:00
content := evt . Content . AsMessage ( )
if len ( content . GetReplyTo ( ) ) > 0 {
content . RemoveReplyFallback ( )
2018-11-13 23:00:35 +01:00
}
2019-06-17 12:46:02 +02:00
if len ( evt . Gomuks . Edits ) > 0 {
2020-04-19 14:00:49 +02:00
content = evt . Gomuks . Edits [ len ( evt . Gomuks . Edits ) - 1 ] . Content . AsMessage ( ) . NewContent
2019-06-15 00:11:51 +02:00
}
2020-04-19 14:00:49 +02:00
switch content . MsgType {
2020-04-16 18:27:35 +02:00
case event . MsgText , event . MsgNotice , event . MsgEmote :
2020-04-19 14:00:49 +02:00
if content . Format == event . FormatHTML {
return NewHTMLMessage ( evt , displayname , html . Parse ( room , content , evt . Sender , displayname ) )
2018-04-13 23:34:25 +02:00
}
2020-04-19 14:00:49 +02:00
content . Body = strings . Replace ( content . Body , "\t" , " " , - 1 )
return NewTextMessage ( evt , displayname , content . Body )
2020-04-16 18:27:35 +02:00
case event . MsgImage , event . MsgVideo , event . MsgAudio , event . MsgFile :
2020-04-08 14:30:29 +02:00
msg := NewFileMessage ( matrix , evt , displayname )
if ! matrix . Preferences ( ) . DisableDownloads {
renderer := msg . Renderer . ( * FileMessage )
renderer . DownloadPreview ( )
2018-04-10 18:31:28 +02:00
}
2020-04-08 14:30:29 +02:00
return msg
2018-04-10 18:31:28 +02:00
}
return nil
}
2020-04-19 14:00:49 +02:00
func getMembershipChangeMessage ( evt * muksevt . Event , content * event . MemberEventContent , prevMembership event . Membership , senderDisplayname , displayname , prevDisplayname string ) ( sender string , text tstring . TString ) {
switch content . Membership {
2018-05-22 16:36:41 +02:00
case "invite" :
sender = "---"
text = tstring . NewColorTString ( fmt . Sprintf ( "%s invited %s." , senderDisplayname , displayname ) , tcell . ColorGreen )
text . Colorize ( 0 , len ( senderDisplayname ) , widget . GetHashColor ( evt . Sender ) )
2020-04-16 18:27:35 +02:00
text . Colorize ( len ( senderDisplayname ) + len ( " invited " ) , len ( displayname ) , widget . GetHashColor ( evt . StateKey ) )
2018-05-22 16:36:41 +02:00
case "join" :
sender = "-->"
2020-04-16 18:27:35 +02:00
if prevMembership == event . MembershipInvite {
2020-02-22 01:56:10 +01:00
text = tstring . NewColorTString ( fmt . Sprintf ( "%s accepted the invite." , displayname ) , tcell . ColorGreen )
} else {
text = tstring . NewColorTString ( fmt . Sprintf ( "%s joined the room." , displayname ) , tcell . ColorGreen )
}
2020-04-16 18:27:35 +02:00
text . Colorize ( 0 , len ( displayname ) , widget . GetHashColor ( evt . StateKey ) )
2018-05-22 16:36:41 +02:00
case "leave" :
sender = "<--"
2020-04-16 18:27:35 +02:00
if evt . Sender != id . UserID ( * evt . StateKey ) {
if prevMembership == event . MembershipBan {
2018-05-22 16:36:41 +02:00
text = tstring . NewColorTString ( fmt . Sprintf ( "%s unbanned %s" , senderDisplayname , displayname ) , tcell . ColorGreen )
2020-04-16 18:27:35 +02:00
text . Colorize ( len ( senderDisplayname ) + len ( " unbanned " ) , len ( displayname ) , widget . GetHashColor ( evt . StateKey ) )
2018-05-22 16:36:41 +02:00
} else {
2020-04-19 14:00:49 +02:00
text = tstring . NewColorTString ( fmt . Sprintf ( "%s kicked %s: %s" , senderDisplayname , displayname , content . Reason ) , tcell . ColorRed )
2020-04-16 18:27:35 +02:00
text . Colorize ( len ( senderDisplayname ) + len ( " kicked " ) , len ( displayname ) , widget . GetHashColor ( evt . StateKey ) )
2018-05-22 16:36:41 +02:00
}
text . Colorize ( 0 , len ( senderDisplayname ) , widget . GetHashColor ( evt . Sender ) )
} else {
if displayname == * evt . StateKey {
displayname = prevDisplayname
}
2020-04-16 18:27:35 +02:00
if prevMembership == event . MembershipInvite {
2020-02-22 01:56:10 +01:00
text = tstring . NewColorTString ( fmt . Sprintf ( "%s rejected the invite." , displayname ) , tcell . ColorRed )
} else {
text = tstring . NewColorTString ( fmt . Sprintf ( "%s left the room." , displayname ) , tcell . ColorRed )
}
2020-04-16 18:27:35 +02:00
text . Colorize ( 0 , len ( displayname ) , widget . GetHashColor ( evt . StateKey ) )
2018-05-22 16:36:41 +02:00
}
case "ban" :
2020-04-19 14:00:49 +02:00
text = tstring . NewColorTString ( fmt . Sprintf ( "%s banned %s: %s" , senderDisplayname , displayname , content . Reason ) , tcell . ColorRed )
2020-04-16 18:27:35 +02:00
text . Colorize ( len ( senderDisplayname ) + len ( " banned " ) , len ( displayname ) , widget . GetHashColor ( evt . StateKey ) )
2018-05-22 16:36:41 +02:00
text . Colorize ( 0 , len ( senderDisplayname ) , widget . GetHashColor ( evt . Sender ) )
}
return
}
2020-04-16 18:27:35 +02:00
func getMembershipEventContent ( room * rooms . Room , evt * muksevt . Event ) ( sender string , text tstring . TString ) {
2018-04-18 12:38:33 +02:00
member := room . GetMember ( evt . Sender )
2020-04-16 18:27:35 +02:00
senderDisplayname := string ( evt . Sender )
2018-04-18 12:38:33 +02:00
if member != nil {
2018-09-05 09:55:48 +02:00
senderDisplayname = member . Displayname
2018-04-18 12:38:33 +02:00
}
2018-04-18 16:33:59 +02:00
2020-04-19 14:00:49 +02:00
content := evt . Content . AsMember ( )
displayname := content . Displayname
2018-04-10 18:31:28 +02:00
if len ( displayname ) == 0 {
displayname = * evt . StateKey
}
2018-04-18 16:33:59 +02:00
2020-04-16 18:27:35 +02:00
prevMembership := event . MembershipLeave
2018-04-18 16:33:59 +02:00
prevDisplayname := * evt . StateKey
2018-04-10 18:31:28 +02:00
if evt . Unsigned . PrevContent != nil {
2020-04-19 14:00:49 +02:00
prevContent := evt . Unsigned . PrevContent . AsMember ( )
prevMembership = prevContent . Membership
prevDisplayname = prevContent . Displayname
2018-05-15 15:09:56 +02:00
if len ( prevDisplayname ) == 0 {
prevDisplayname = * evt . StateKey
}
2018-04-10 18:31:28 +02:00
}
2020-04-19 14:00:49 +02:00
if content . Membership != prevMembership {
sender , text = getMembershipChangeMessage ( evt , content , prevMembership , senderDisplayname , displayname , prevDisplayname )
2018-04-10 18:31:28 +02:00
} else if displayname != prevDisplayname {
sender = "---"
2020-04-16 18:27:35 +02:00
color := widget . GetHashColor ( evt . StateKey )
2019-01-11 22:28:47 +01:00
text = tstring . NewBlankTString ( ) .
AppendColor ( prevDisplayname , color ) .
AppendColor ( " changed their display name to " , tcell . ColorGreen ) .
AppendColor ( displayname , color ) .
AppendColor ( "." , tcell . ColorGreen )
2018-04-10 18:31:28 +02:00
}
return
}
2020-04-16 18:27:35 +02:00
func ParseMembershipEvent ( room * rooms . Room , evt * muksevt . Event ) * UIMessage {
2018-04-18 12:38:33 +02:00
displayname , text := getMembershipEventContent ( room , evt )
2018-04-18 16:33:59 +02:00
if len ( text ) == 0 {
return nil
}
2019-04-09 17:45:41 +02:00
return NewExpandedTextMessage ( evt , displayname , text )
2018-04-10 18:31:28 +02:00
}
2019-01-11 22:28:47 +01:00
2020-04-16 18:27:35 +02:00
//func ParseAliasEvent(evt *muksevt.Event, displayname string) tstring.TString {
// var prevAliases []string
// if evt.Unsigned.PrevContent != nil {
// prevAliases = evt.Unsigned.PrevContent.Aliases
// }
// aliases := evt.Content.Aliases
// var added, removed []tstring.TString
//Outer1:
// for _, oldAlias := range prevAliases {
// for _, newAlias := range aliases {
// if oldAlias == newAlias {
// continue Outer1
// }
// }
// removed = append(removed, tstring.NewStyleTString(oldAlias, tcell.StyleDefault.Foreground(widget.GetHashColor(oldAlias)).Underline(true)))
// }
//Outer2:
// for _, newAlias := range aliases {
// for _, oldAlias := range prevAliases {
// if oldAlias == newAlias {
// continue Outer2
// }
// }
// added = append(added, tstring.NewStyleTString(newAlias, tcell.StyleDefault.Foreground(widget.GetHashColor(newAlias)).Underline(true)))
// }
// var addedStr, removedStr tstring.TString
// if len(added) == 1 {
// addedStr = added[0]
// } else if len(added) > 1 {
// addedStr = tstring.
// Join(added[:len(added)-1], ", ").
// Append(" and ").
// AppendTString(added[len(added)-1])
// }
// if len(removed) == 1 {
// removedStr = removed[0]
// } else if len(removed) > 1 {
// removedStr = tstring.
// Join(removed[:len(removed)-1], ", ").
// Append(" and ").
// AppendTString(removed[len(removed)-1])
// }
// text := tstring.NewBlankTString()
// if len(addedStr) > 0 && len(removedStr) > 0 {
// text = text.AppendColor(fmt.Sprintf("%s added ", displayname), tcell.ColorGreen).
// AppendTString(addedStr).
// AppendColor(" and removed ", tcell.ColorGreen).
// AppendTString(removedStr).
// AppendColor(" as addresses for this room.", tcell.ColorGreen)
// } else if len(addedStr) > 0 {
// text = text.AppendColor(fmt.Sprintf("%s added ", displayname), tcell.ColorGreen).
// AppendTString(addedStr).
// AppendColor(" as addresses for this room.", tcell.ColorGreen)
// } else if len(removedStr) > 0 {
// text = text.AppendColor(fmt.Sprintf("%s removed ", displayname), tcell.ColorGreen).
// AppendTString(removedStr).
// AppendColor(" as addresses for this room.", tcell.ColorGreen)
// } else {
// return nil
// }
// return text
//}