get delete messages to work

This commit is contained in:
Cubicroot 2021-06-06 21:13:07 +02:00
parent b392ea1b44
commit eebc7f7e31
7 changed files with 103 additions and 18 deletions

View file

@ -142,6 +142,18 @@ curl \
HTML-Content might not be fully rendered in your Matrix-Client - see the corresponding [Matrix specs](https://spec.matrix.org/unstable/client-server-api/#mroommessage-msgtypes). This also holds for Markdown, as it is transfered to the corresponding HTML-syntax. HTML-Content might not be fully rendered in your Matrix-Client - see the corresponding [Matrix specs](https://spec.matrix.org/unstable/client-server-api/#mroommessage-msgtypes). This also holds for Markdown, as it is transfered to the corresponding HTML-syntax.
### Deleting a Message
You can delete a message, this will send a notification in response to the original message informing you that the message is "deleted".
You need the message ID for deleting a message. As it might contain characters not valid in uris we provide an additional `id_url_encoded` field for messages, use that value for deleting a message.
```bash
curl \
--request DELETE \
"https://pushbits.example.com/message/${MESSAGE_ID}?token=$PB_TOKEN"
```
## 👮 Acknowledgments ## 👮 Acknowledgments
The idea for this software and most parts of the initial source are heavily inspired by [Gotify](https://gotify.net/). The idea for this software and most parts of the initial source are heavily inspired by [Gotify](https://gotify.net/).

5
internal/api/errors.go Normal file
View file

@ -0,0 +1,5 @@
package api
import "errors"
var ErrorMessageNotFound = errors.New("Message not found")

View file

@ -3,6 +3,7 @@ package api
import ( import (
"log" "log"
"net/http" "net/http"
"net/url"
"strings" "strings"
"time" "time"
@ -52,6 +53,7 @@ func (h *NotificationHandler) CreateNotification(ctx *gin.Context) {
} }
notification.ID = messageID notification.ID = messageID
notification.UrlEncodedID = url.QueryEscape(messageID)
ctx.JSON(http.StatusOK, &notification) ctx.JSON(http.StatusOK, &notification)
} }
@ -70,5 +72,9 @@ func (h *NotificationHandler) DeleteNotification(ctx *gin.Context) {
Date: time.Now(), Date: time.Now(),
} }
h.DP.DeleteNotification(application, &n) if success := successOrAbort(ctx, http.StatusInternalServerError, h.DP.DeleteNotification(application, &n)); !success {
return
}
ctx.Status(http.StatusOK)
} }

View file

@ -11,8 +11,14 @@ import (
func successOrAbort(ctx *gin.Context, code int, err error) bool { func successOrAbort(ctx *gin.Context, code int, err error) bool {
if err != nil { if err != nil {
// If we know the error force error code
switch err {
case ErrorMessageNotFound:
ctx.AbortWithError(http.StatusNotFound, err)
default:
ctx.AbortWithError(code, err) ctx.AbortWithError(code, err)
} }
}
return err == nil return err == nil
} }

View file

@ -1,19 +1,30 @@
package dispatcher package dispatcher
import ( import (
"errors"
"fmt" "fmt"
"html" "html"
"log" "log"
"strings" "strings"
"github.com/gomarkdown/markdown" "github.com/gomarkdown/markdown"
"github.com/matrix-org/gomatrix"
"github.com/pushbits/server/internal/api"
"github.com/pushbits/server/internal/model" "github.com/pushbits/server/internal/model"
) )
type MessageFormat string
const (
FormatHTML = MessageFormat("org.matrix.custom.html")
)
type ReplyEvent struct { type ReplyEvent struct {
Body string `json:"body"` Body string `json:"body"`
FormattedBody string `json:"formatted_body"`
Msgtype string `json:"msgtype"` Msgtype string `json:"msgtype"`
RelatesTo RelatesTo `json:"m.relates_to,omitempty"` RelatesTo RelatesTo `json:"m.relates_to,omitempty"`
Format MessageFormat `json:"format"`
} }
type RelatesTo struct { type RelatesTo struct {
@ -39,30 +50,56 @@ func (d *Dispatcher) SendNotification(a *model.Application, n *model.Notificatio
// DeleteNotification sends a notification to the specified user that another notificaion is deleted // DeleteNotification sends a notification to the specified user that another notificaion is deleted
func (d *Dispatcher) DeleteNotification(a *model.Application, n *model.DeleteNotification) error { func (d *Dispatcher) DeleteNotification(a *model.Application, n *model.DeleteNotification) error {
var deleteMessageFormattedBody string
var deleteMessageBody string
log.Printf("Sending delete notification to room %s", a.MatrixID) log.Printf("Sending delete notification to room %s", a.MatrixID)
deleteMessage, err := d.getMessage(a, n.ID)
if err != nil {
log.Println(err)
return api.ErrorMessageNotFound
}
if val, ok := deleteMessage.Content["body"]; ok {
body, ok := val.(string)
if ok {
deleteMessageBody = body
deleteMessageFormattedBody = body
}
} else {
log.Println("Message to delete has wrong format")
return api.ErrorMessageNotFound
}
if val, ok := deleteMessage.Content["formatted_body"]; ok {
body, ok := val.(string)
if ok {
deleteMessageFormattedBody = body
}
}
// formatting according to https://matrix.org/docs/spec/client_server/latest#fallbacks-and-event-representation
formattedBody := fmt.Sprintf("<mx-reply><blockquote><a href='https://matrix.to/#/%s/%s'>In reply to</a> <a href='https://matrix.to/#/%s'>%s</a><br />%s</blockquote>\n</mx-reply><i>This message got deleted.</i>", deleteMessage.RoomID, deleteMessage.ID, deleteMessage.Sender, deleteMessage.Sender, deleteMessageFormattedBody)
body := fmt.Sprintf("> <%s>%s\n\nThis message got deleted", deleteMessage.Sender, deleteMessageBody)
event := ReplyEvent{ event := ReplyEvent{
Body: "<i>This message got deleted.</i>", FormattedBody: formattedBody,
Body: body,
Msgtype: "m.text", Msgtype: "m.text",
Format: FormatHTML,
} }
irt := make(map[string]string) irt := make(map[string]string)
irt["event_id"] = n.ID
rt := RelatesTo{ rt := RelatesTo{
InReplyTo: irt, InReplyTo: irt,
} }
event.RelatesTo = rt event.RelatesTo = rt
irt["event_id"] = deleteMessage.ID
_, err := d.client.SendMessageEvent(a.MatrixID, "m.room.message", event) _, err = d.client.SendMessageEvent(a.MatrixID, "m.room.message", event)
return err return err
/*
messages, _ := d.client.Messages(a.MatrixID, "", "", 'b', 10)
js, _ := json.Marshal(messages)
log.Println(string(js))
*/
} }
// HTML-formats the title // HTML-formats the title
@ -130,3 +167,21 @@ func (d *Dispatcher) coloredText(color string, text string) string {
return "<font data-mx-color='" + color + "'>" + text + "</font>" return "<font data-mx-color='" + color + "'>" + text + "</font>"
} }
// TODO cubicroot: find a way to only request on specific event
func (d *Dispatcher) getMessage(a *model.Application, id string) (gomatrix.Event, error) {
start := ""
end := ""
maxPages := 10 // maximum pages to request (10 messages per page)
for i := 0; i < maxPages; i++ {
messages, _ := d.client.Messages(a.MatrixID, start, end, 'b', 10)
for _, event := range messages.Chunk {
if event.ID == id {
return event, nil
}
}
start = messages.End
}
return gomatrix.Event{}, errors.New("Message not found")
}

View file

@ -7,6 +7,7 @@ import (
// Notification holds information like the message, the title, and the priority of a notification. // Notification holds information like the message, the title, and the priority of a notification.
type Notification struct { type Notification struct {
ID string `json:"id"` ID string `json:"id"`
UrlEncodedID string `json:"id_url_encoded"`
ApplicationID uint `json:"appid"` ApplicationID uint `json:"appid"`
Message string `json:"message" form:"message" query:"message" binding:"required"` Message string `json:"message" form:"message" query:"message" binding:"required"`
Title string `json:"title" form:"title" query:"title"` Title string `json:"title" form:"title" query:"title"`

View file

@ -46,7 +46,7 @@ func Create(debug bool, cm *credentials.Manager, db *database.Database, dp *disp
r.GET("/health", healthHandler.Health) r.GET("/health", healthHandler.Health)
r.POST("/message", auth.RequireApplicationToken(), notificationHandler.CreateNotification) r.POST("/message", auth.RequireApplicationToken(), notificationHandler.CreateNotification)
r.DELETE("/message/:messageid", api.RequireIDInURI(), auth.RequireApplicationToken(), notificationHandler.DeleteNotification) r.DELETE("/message/:messageid", api.RequireMessageIDInURI(), auth.RequireApplicationToken(), notificationHandler.DeleteNotification)
r.GET("/test", auth.RequireApplicationToken(), notificationHandler.DeleteNotification) r.GET("/test", auth.RequireApplicationToken(), notificationHandler.DeleteNotification)