Add support for sending Markdown messages
This commit is contained in:
parent
3750d5007f
commit
bb36996194
@ -29,6 +29,7 @@ type MatrixContainer interface {
|
|||||||
Start()
|
Start()
|
||||||
Stop()
|
Stop()
|
||||||
SendMessage(roomID, msgtype, message string) (string, error)
|
SendMessage(roomID, msgtype, message string) (string, error)
|
||||||
|
SendMarkdownMessage(roomID, msgtype, message string) (string, error)
|
||||||
SendTyping(roomID string, typing bool)
|
SendTyping(roomID string, typing bool)
|
||||||
JoinRoom(roomID string) error
|
JoinRoom(roomID string) error
|
||||||
LeaveRoom(roomID string) error
|
LeaveRoom(roomID string) error
|
||||||
|
@ -89,7 +89,6 @@ type MessageMeta interface {
|
|||||||
Timestamp() time.Time
|
Timestamp() time.Time
|
||||||
FormatTime() string
|
FormatTime() string
|
||||||
FormatDate() string
|
FormatDate() string
|
||||||
CopyFrom(from MessageMeta)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MessageState is an enum to specify if a Message is being sent, failed to send or was successfully sent.
|
// MessageState is an enum to specify if a Message is being sent, failed to send or was successfully sent.
|
||||||
|
@ -31,6 +31,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"gopkg.in/russross/blackfriday.v2"
|
||||||
"maunium.net/go/gomatrix"
|
"maunium.net/go/gomatrix"
|
||||||
"maunium.net/go/gomuks/config"
|
"maunium.net/go/gomuks/config"
|
||||||
"maunium.net/go/gomuks/debug"
|
"maunium.net/go/gomuks/debug"
|
||||||
@ -326,6 +327,30 @@ func (c *Container) SendMessage(roomID, msgtype, text string) (string, error) {
|
|||||||
return resp.EventID, nil
|
return resp.EventID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Container) SendMarkdownMessage(roomID, msgtype, text string) (string, error) {
|
||||||
|
defer c.gmx.Recover()
|
||||||
|
|
||||||
|
html := string(blackfriday.Run([]byte(text)))
|
||||||
|
if html == text {
|
||||||
|
return c.SendMessage(roomID, msgtype, text)
|
||||||
|
}
|
||||||
|
debug.Print(html)
|
||||||
|
debug.Print(text)
|
||||||
|
|
||||||
|
c.SendTyping(roomID, false)
|
||||||
|
resp, err := c.client.SendMessageEvent(roomID, "m.room.message",
|
||||||
|
map[string]interface{}{
|
||||||
|
"msgtype": msgtype,
|
||||||
|
"body": text,
|
||||||
|
"format": "org.matrix.custom.html",
|
||||||
|
"formatted_body": html,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return resp.EventID, nil
|
||||||
|
}
|
||||||
|
|
||||||
// SendTyping sets whether or not the user is typing in the given room.
|
// SendTyping sets whether or not the user is typing in the given room.
|
||||||
func (c *Container) SendTyping(roomID string, typing bool) {
|
func (c *Container) SendTyping(roomID string, typing bool) {
|
||||||
defer c.gmx.Recover()
|
defer c.gmx.Recover()
|
||||||
|
@ -162,8 +162,7 @@ func (view *MessageView) AddMessage(ifcMessage ifc.Message, direction ifc.Messag
|
|||||||
|
|
||||||
oldMsg, messageExists := view.messageIDs[message.ID()]
|
oldMsg, messageExists := view.messageIDs[message.ID()]
|
||||||
if messageExists {
|
if messageExists {
|
||||||
oldMsg.CopyFrom(message)
|
view.replaceMessage(oldMsg, message)
|
||||||
message = oldMsg
|
|
||||||
direction = ifc.IgnoreMessage
|
direction = ifc.IgnoreMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,8 +180,10 @@ func (view *MessageView) AddMessage(ifcMessage ifc.Message, direction ifc.Messag
|
|||||||
view.appendBuffer(message)
|
view.appendBuffer(message)
|
||||||
} else if direction == ifc.PrependMessage {
|
} else if direction == ifc.PrependMessage {
|
||||||
view.messages = append([]messages.UIMessage{message}, view.messages...)
|
view.messages = append([]messages.UIMessage{message}, view.messages...)
|
||||||
|
} else if oldMsg != nil {
|
||||||
|
view.replaceBuffer(oldMsg, message)
|
||||||
} else {
|
} else {
|
||||||
view.replaceBuffer(message)
|
view.replaceBuffer(message, message)
|
||||||
}
|
}
|
||||||
|
|
||||||
view.messageIDs[message.ID()] = message
|
view.messageIDs[message.ID()] = message
|
||||||
@ -207,11 +208,20 @@ func (view *MessageView) appendBuffer(message messages.UIMessage) {
|
|||||||
view.prevMsgCount++
|
view.prevMsgCount++
|
||||||
}
|
}
|
||||||
|
|
||||||
func (view *MessageView) replaceBuffer(message messages.UIMessage) {
|
func (view *MessageView) replaceMessage(original messages.UIMessage, new messages.UIMessage) {
|
||||||
|
view.messageIDs[new.ID()] = new
|
||||||
|
for index, msg := range view.messages {
|
||||||
|
if msg == original {
|
||||||
|
view.messages[index] = new
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (view *MessageView) replaceBuffer(original messages.UIMessage, new messages.UIMessage) {
|
||||||
start := -1
|
start := -1
|
||||||
end := -1
|
end := -1
|
||||||
for index, meta := range view.metaBuffer {
|
for index, meta := range view.metaBuffer {
|
||||||
if meta == message {
|
if meta == original {
|
||||||
if start == -1 {
|
if start == -1 {
|
||||||
start = index
|
start = index
|
||||||
}
|
}
|
||||||
@ -225,13 +235,17 @@ func (view *MessageView) replaceBuffer(message messages.UIMessage) {
|
|||||||
end++
|
end++
|
||||||
}
|
}
|
||||||
|
|
||||||
view.textBuffer = append(append(view.textBuffer[0:start], message.Buffer()...), view.textBuffer[end:]...)
|
view.textBuffer = append(append(view.textBuffer[0:start], new.Buffer()...), view.textBuffer[end:]...)
|
||||||
if len(message.Buffer()) != end-start+1 {
|
if len(new.Buffer()) != end-start+1 {
|
||||||
metaBuffer := view.metaBuffer[0:start]
|
metaBuffer := view.metaBuffer[0:start]
|
||||||
for range message.Buffer() {
|
for range new.Buffer() {
|
||||||
metaBuffer = append(metaBuffer, message)
|
metaBuffer = append(metaBuffer, new)
|
||||||
}
|
}
|
||||||
view.metaBuffer = append(metaBuffer, view.metaBuffer[end:]...)
|
view.metaBuffer = append(metaBuffer, view.metaBuffer[end:]...)
|
||||||
|
} else {
|
||||||
|
for i := start; i < end; i++ {
|
||||||
|
view.metaBuffer[i] = new
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,25 +61,6 @@ func newBaseMessage(id, sender, displayname, msgtype string, timestamp time.Time
|
|||||||
|
|
||||||
func (msg *BaseMessage) RegisterGomuks(gmx ifc.Gomuks) {}
|
func (msg *BaseMessage) RegisterGomuks(gmx ifc.Gomuks) {}
|
||||||
|
|
||||||
// CopyFrom replaces the content of this message object with the content of the given object.
|
|
||||||
func (msg *BaseMessage) CopyFrom(from ifc.MessageMeta) {
|
|
||||||
msg.MsgSender = from.Sender()
|
|
||||||
msg.MsgSenderColor = from.SenderColor()
|
|
||||||
|
|
||||||
fromMsg, ok := from.(UIMessage)
|
|
||||||
if ok {
|
|
||||||
msg.MsgSenderID = fromMsg.SenderID()
|
|
||||||
msg.MsgSender = fromMsg.RealSender()
|
|
||||||
msg.MsgID = fromMsg.ID()
|
|
||||||
msg.MsgType = fromMsg.Type()
|
|
||||||
msg.MsgTimestamp = fromMsg.Timestamp()
|
|
||||||
msg.MsgState = fromMsg.State()
|
|
||||||
msg.MsgIsService = fromMsg.IsService()
|
|
||||||
msg.MsgIsHighlight = fromMsg.IsHighlight()
|
|
||||||
msg.buffer = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sender gets the string that should be displayed as the sender of this message.
|
// Sender gets the string that should be displayed as the sender of this message.
|
||||||
//
|
//
|
||||||
// If the message is being sent, the sender is "Sending...".
|
// If the message is being sent, the sender is "Sending...".
|
||||||
|
@ -20,7 +20,6 @@ import (
|
|||||||
"encoding/gob"
|
"encoding/gob"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"maunium.net/go/gomuks/interface"
|
|
||||||
"maunium.net/go/gomuks/ui/messages/tstring"
|
"maunium.net/go/gomuks/ui/messages/tstring"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -37,7 +36,7 @@ type ExpandedTextMessage struct {
|
|||||||
func NewExpandedTextMessage(id, sender, displayname, msgtype string, text tstring.TString, timestamp time.Time) UIMessage {
|
func NewExpandedTextMessage(id, sender, displayname, msgtype string, text tstring.TString, timestamp time.Time) UIMessage {
|
||||||
return &ExpandedTextMessage{
|
return &ExpandedTextMessage{
|
||||||
BaseTextMessage: newBaseTextMessage(id, sender, displayname, msgtype, timestamp),
|
BaseTextMessage: newBaseTextMessage(id, sender, displayname, msgtype, timestamp),
|
||||||
MsgText: text,
|
MsgText: text,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,18 +44,6 @@ func (msg *ExpandedTextMessage) GenerateText() tstring.TString {
|
|||||||
return msg.MsgText
|
return msg.MsgText
|
||||||
}
|
}
|
||||||
|
|
||||||
// CopyFrom replaces the content of this message object with the content of the given object.
|
|
||||||
func (msg *ExpandedTextMessage) CopyFrom(from ifc.MessageMeta) {
|
|
||||||
msg.BaseTextMessage.CopyFrom(from)
|
|
||||||
|
|
||||||
fromExpandedMsg, ok := from.(*ExpandedTextMessage)
|
|
||||||
if ok {
|
|
||||||
msg.MsgText = fromExpandedMsg.MsgText
|
|
||||||
}
|
|
||||||
|
|
||||||
msg.RecalculateBuffer()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (msg *ExpandedTextMessage) NotificationContent() string {
|
func (msg *ExpandedTextMessage) NotificationContent() string {
|
||||||
return msg.MsgText.String()
|
return msg.MsgText.String()
|
||||||
}
|
}
|
||||||
|
@ -85,18 +85,6 @@ func (msg *ImageMessage) Path() string {
|
|||||||
return msg.gmx.Matrix().GetCachePath(msg.Homeserver, msg.FileID)
|
return msg.gmx.Matrix().GetCachePath(msg.Homeserver, msg.FileID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CopyFrom replaces the content of this message object with the content of the given object.
|
|
||||||
func (msg *ImageMessage) CopyFrom(from ifc.MessageMeta) {
|
|
||||||
msg.BaseMessage.CopyFrom(from)
|
|
||||||
|
|
||||||
fromImgMsg, ok := from.(*ImageMessage)
|
|
||||||
if ok {
|
|
||||||
msg.data = fromImgMsg.data
|
|
||||||
}
|
|
||||||
|
|
||||||
msg.RecalculateBuffer()
|
|
||||||
}
|
|
||||||
|
|
||||||
// CalculateBuffer generates the internal buffer for this message that consists
|
// CalculateBuffer generates the internal buffer for this message that consists
|
||||||
// of the text of this message split into lines at most as wide as the width
|
// of the text of this message split into lines at most as wide as the width
|
||||||
// parameter.
|
// parameter.
|
||||||
|
@ -20,13 +20,12 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"maunium.net/go/tcell"
|
"maunium.net/go/tcell"
|
||||||
"maunium.net/go/gomuks/interface"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// BasicMeta is a simple variable store implementation of MessageMeta.
|
// BasicMeta is a simple variable store implementation of MessageMeta.
|
||||||
type BasicMeta struct {
|
type BasicMeta struct {
|
||||||
BSender string
|
BSender string
|
||||||
BTimestamp time.Time
|
BTimestamp time.Time
|
||||||
BSenderColor, BTextColor, BTimestampColor tcell.Color
|
BSenderColor, BTextColor, BTimestampColor tcell.Color
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,12 +65,3 @@ func (meta *BasicMeta) TextColor() tcell.Color {
|
|||||||
func (meta *BasicMeta) TimestampColor() tcell.Color {
|
func (meta *BasicMeta) TimestampColor() tcell.Color {
|
||||||
return meta.BTimestampColor
|
return meta.BTimestampColor
|
||||||
}
|
}
|
||||||
|
|
||||||
// CopyFrom replaces the content of this meta object with the content of the given object.
|
|
||||||
func (meta *BasicMeta) CopyFrom(from ifc.MessageMeta) {
|
|
||||||
meta.BSender = from.Sender()
|
|
||||||
meta.BTimestamp = from.Timestamp()
|
|
||||||
meta.BSenderColor = from.SenderColor()
|
|
||||||
meta.BTextColor = from.TextColor()
|
|
||||||
meta.BTimestampColor = from.TimestampColor()
|
|
||||||
}
|
|
||||||
|
@ -37,6 +37,9 @@ var matrixToURL = regexp.MustCompile("^(?:https?://)?(?:www\\.)?matrix\\.to/#/([
|
|||||||
type MatrixHTMLProcessor struct {
|
type MatrixHTMLProcessor struct {
|
||||||
text tstring.TString
|
text tstring.TString
|
||||||
|
|
||||||
|
sender string
|
||||||
|
msgtype string
|
||||||
|
|
||||||
indent string
|
indent string
|
||||||
listType string
|
listType string
|
||||||
lineIsNew bool
|
lineIsNew bool
|
||||||
@ -52,7 +55,11 @@ func (parser *MatrixHTMLProcessor) newline() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (parser *MatrixHTMLProcessor) Preprocess() {}
|
func (parser *MatrixHTMLProcessor) Preprocess() {
|
||||||
|
if parser.msgtype == "m.emote" {
|
||||||
|
parser.text = tstring.NewColorTString(fmt.Sprintf("* %s ", parser.sender), widget.GetHashColor(parser.sender))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (parser *MatrixHTMLProcessor) HandleText(text string) {
|
func (parser *MatrixHTMLProcessor) HandleText(text string) {
|
||||||
style := tcell.StyleDefault
|
style := tcell.StyleDefault
|
||||||
@ -170,12 +177,15 @@ func (parser *MatrixHTMLProcessor) Postprocess() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ParseHTMLMessage parses a HTML-formatted Matrix event into a UIMessage.
|
// ParseHTMLMessage parses a HTML-formatted Matrix event into a UIMessage.
|
||||||
func ParseHTMLMessage(room *rooms.Room, evt *gomatrix.Event) tstring.TString {
|
func ParseHTMLMessage(room *rooms.Room, evt *gomatrix.Event, senderDisplayname string) tstring.TString {
|
||||||
htmlData, _ := evt.Content["formatted_body"].(string)
|
htmlData, _ := evt.Content["formatted_body"].(string)
|
||||||
|
msgtype, _ := evt.Content["msgtype"].(string)
|
||||||
|
|
||||||
processor := &MatrixHTMLProcessor{
|
processor := &MatrixHTMLProcessor{
|
||||||
room: room,
|
room: room,
|
||||||
text: tstring.NewBlankTString(),
|
text: tstring.NewBlankTString(),
|
||||||
|
msgtype: msgtype,
|
||||||
|
sender: senderDisplayname,
|
||||||
indent: "",
|
indent: "",
|
||||||
listType: "",
|
listType: "",
|
||||||
lineIsNew: true,
|
lineIsNew: true,
|
||||||
|
@ -60,7 +60,7 @@ func ParseMessage(gmx ifc.Gomuks, room *rooms.Room, evt *gomatrix.Event) message
|
|||||||
case "m.text", "m.notice", "m.emote":
|
case "m.text", "m.notice", "m.emote":
|
||||||
format, hasFormat := evt.Content["format"].(string)
|
format, hasFormat := evt.Content["format"].(string)
|
||||||
if hasFormat && format == "org.matrix.custom.html" {
|
if hasFormat && format == "org.matrix.custom.html" {
|
||||||
text := ParseHTMLMessage(room, evt)
|
text := ParseHTMLMessage(room, evt, displayname)
|
||||||
return messages.NewExpandedTextMessage(evt.ID, evt.Sender, displayname, msgtype, text, ts)
|
return messages.NewExpandedTextMessage(evt.ID, evt.Sender, displayname, msgtype, text, ts)
|
||||||
} else {
|
} else {
|
||||||
text, _ := evt.Content["body"].(string)
|
text, _ := evt.Content["body"].(string)
|
||||||
|
@ -56,18 +56,6 @@ func (msg *TextMessage) getCache() tstring.TString {
|
|||||||
return msg.cache
|
return msg.cache
|
||||||
}
|
}
|
||||||
|
|
||||||
// CopyFrom replaces the content of this message object with the content of the given object.
|
|
||||||
func (msg *TextMessage) CopyFrom(from ifc.MessageMeta) {
|
|
||||||
msg.BaseTextMessage.CopyFrom(from)
|
|
||||||
|
|
||||||
fromTextMsg, ok := from.(*TextMessage)
|
|
||||||
if ok {
|
|
||||||
msg.MsgText = fromTextMsg.MsgText
|
|
||||||
}
|
|
||||||
|
|
||||||
msg.cache = nil
|
|
||||||
msg.RecalculateBuffer()
|
|
||||||
}
|
|
||||||
func (msg *TextMessage) SetType(msgtype string) {
|
func (msg *TextMessage) SetType(msgtype string) {
|
||||||
msg.BaseTextMessage.SetType(msgtype)
|
msg.BaseTextMessage.SetType(msgtype)
|
||||||
msg.cache = nil
|
msg.cache = nil
|
||||||
|
@ -144,7 +144,7 @@ func (view *MainView) SendMessage(roomView *RoomView, text string) {
|
|||||||
|
|
||||||
func (view *MainView) sendTempMessage(roomView *RoomView, tempMessage ifc.Message, text string) {
|
func (view *MainView) sendTempMessage(roomView *RoomView, tempMessage ifc.Message, text string) {
|
||||||
defer view.gmx.Recover()
|
defer view.gmx.Recover()
|
||||||
eventID, err := view.matrix.SendMessage(roomView.Room.ID, tempMessage.Type(), text)
|
eventID, err := view.matrix.SendMarkdownMessage(roomView.Room.ID, tempMessage.Type(), text)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tempMessage.SetState(ifc.MessageStateFailed)
|
tempMessage.SetState(ifc.MessageStateFailed)
|
||||||
roomView.SetStatus(fmt.Sprintf("Failed to send message: %s", err))
|
roomView.SetStatus(fmt.Sprintf("Failed to send message: %s", err))
|
||||||
|
Loading…
Reference in New Issue
Block a user