139 lines
5.1 KiB
Go
139 lines
5.1 KiB
Go
|
// Copyright (c) 2020 Tulir Asokan
|
||
|
//
|
||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||
|
|
||
|
package event
|
||
|
|
||
|
import (
|
||
|
"encoding/json"
|
||
|
|
||
|
"maunium.net/go/mautrix/id"
|
||
|
)
|
||
|
|
||
|
// Event represents a single Matrix event.
|
||
|
type Event struct {
|
||
|
StateKey *string `json:"state_key,omitempty"` // The state key for the event. Only present on State Events.
|
||
|
Sender id.UserID `json:"sender,omitempty"` // The user ID of the sender of the event
|
||
|
Type Type `json:"type"` // The event type
|
||
|
Timestamp int64 `json:"origin_server_ts,omitempty"` // The unix timestamp when this message was sent by the origin server
|
||
|
ID id.EventID `json:"event_id,omitempty"` // The unique ID of this event
|
||
|
RoomID id.RoomID `json:"room_id,omitempty"` // The room the event was sent to. May be nil (e.g. for presence)
|
||
|
Content Content `json:"content"` // The JSON content of the event.
|
||
|
Redacts id.EventID `json:"redacts,omitempty"` // The event ID that was redacted if a m.room.redaction event
|
||
|
Unsigned Unsigned `json:"unsigned,omitempty"` // Unsigned content set by own homeserver.
|
||
|
|
||
|
Mautrix MautrixInfo `json:"-"`
|
||
|
|
||
|
ToUserID id.UserID `json:"to_user_id,omitempty"` // The user ID that the to-device event was sent to. Only present in MSC2409 appservice transactions.
|
||
|
ToDeviceID id.DeviceID `json:"to_device_id,omitempty"` // The device ID that the to-device event was sent to. Only present in MSC2409 appservice transactions.
|
||
|
}
|
||
|
|
||
|
type eventForMarshaling struct {
|
||
|
StateKey *string `json:"state_key,omitempty"`
|
||
|
Sender id.UserID `json:"sender,omitempty"`
|
||
|
Type Type `json:"type"`
|
||
|
Timestamp int64 `json:"origin_server_ts,omitempty"`
|
||
|
ID id.EventID `json:"event_id,omitempty"`
|
||
|
RoomID id.RoomID `json:"room_id,omitempty"`
|
||
|
Content Content `json:"content"`
|
||
|
Redacts id.EventID `json:"redacts,omitempty"`
|
||
|
Unsigned *Unsigned `json:"unsigned,omitempty"`
|
||
|
|
||
|
PrevContent *Content `json:"prev_content,omitempty"`
|
||
|
ReplacesState *id.EventID `json:"replaces_state,omitempty"`
|
||
|
|
||
|
ToUserID id.UserID `json:"to_user_id,omitempty"`
|
||
|
ToDeviceID id.DeviceID `json:"to_device_id,omitempty"`
|
||
|
}
|
||
|
|
||
|
// UnmarshalJSON unmarshals the event, including moving prev_content from the top level to inside unsigned.
|
||
|
func (evt *Event) UnmarshalJSON(data []byte) error {
|
||
|
var efm eventForMarshaling
|
||
|
err := json.Unmarshal(data, &efm)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
evt.StateKey = efm.StateKey
|
||
|
evt.Sender = efm.Sender
|
||
|
evt.Type = efm.Type
|
||
|
evt.Timestamp = efm.Timestamp
|
||
|
evt.ID = efm.ID
|
||
|
evt.RoomID = efm.RoomID
|
||
|
evt.Content = efm.Content
|
||
|
evt.Redacts = efm.Redacts
|
||
|
if efm.Unsigned != nil {
|
||
|
evt.Unsigned = *efm.Unsigned
|
||
|
}
|
||
|
if efm.PrevContent != nil && evt.Unsigned.PrevContent == nil {
|
||
|
evt.Unsigned.PrevContent = efm.PrevContent
|
||
|
}
|
||
|
if efm.ReplacesState != nil && *efm.ReplacesState != "" && evt.Unsigned.ReplacesState == "" {
|
||
|
evt.Unsigned.ReplacesState = *efm.ReplacesState
|
||
|
}
|
||
|
evt.ToUserID = efm.ToUserID
|
||
|
evt.ToDeviceID = efm.ToDeviceID
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// MarshalJSON marshals the event, including omitting the unsigned field if it's empty.
|
||
|
//
|
||
|
// This is necessary because Unsigned is not a pointer (for convenience reasons),
|
||
|
// and encoding/json doesn't know how to check if a non-pointer struct is empty.
|
||
|
//
|
||
|
// TODO(tulir): maybe it makes more sense to make Unsigned a pointer and make an easy and safe way to access it?
|
||
|
func (evt *Event) MarshalJSON() ([]byte, error) {
|
||
|
unsigned := &evt.Unsigned
|
||
|
if unsigned.IsEmpty() {
|
||
|
unsigned = nil
|
||
|
}
|
||
|
return json.Marshal(&eventForMarshaling{
|
||
|
StateKey: evt.StateKey,
|
||
|
Sender: evt.Sender,
|
||
|
Type: evt.Type,
|
||
|
Timestamp: evt.Timestamp,
|
||
|
ID: evt.ID,
|
||
|
RoomID: evt.RoomID,
|
||
|
Content: evt.Content,
|
||
|
Redacts: evt.Redacts,
|
||
|
Unsigned: unsigned,
|
||
|
ToUserID: evt.ToUserID,
|
||
|
ToDeviceID: evt.ToDeviceID,
|
||
|
})
|
||
|
}
|
||
|
|
||
|
type MautrixInfo struct {
|
||
|
Verified bool
|
||
|
}
|
||
|
|
||
|
func (evt *Event) GetStateKey() string {
|
||
|
if evt.StateKey != nil {
|
||
|
return *evt.StateKey
|
||
|
}
|
||
|
return ""
|
||
|
}
|
||
|
|
||
|
type StrippedState struct {
|
||
|
Content Content `json:"content"`
|
||
|
Type Type `json:"type"`
|
||
|
StateKey string `json:"state_key"`
|
||
|
}
|
||
|
|
||
|
type Unsigned struct {
|
||
|
PrevContent *Content `json:"prev_content,omitempty"`
|
||
|
PrevSender id.UserID `json:"prev_sender,omitempty"`
|
||
|
ReplacesState id.EventID `json:"replaces_state,omitempty"`
|
||
|
Age int64 `json:"age,omitempty"`
|
||
|
TransactionID string `json:"transaction_id,omitempty"`
|
||
|
Relations Relations `json:"m.relations,omitempty"`
|
||
|
RedactedBecause *Event `json:"redacted_because,omitempty"`
|
||
|
InviteRoomState []StrippedState `json:"invite_room_state,omitempty"`
|
||
|
}
|
||
|
|
||
|
func (us *Unsigned) IsEmpty() bool {
|
||
|
return us.PrevContent == nil && us.PrevSender == "" && us.ReplacesState == "" && us.Age == 0 &&
|
||
|
us.TransactionID == "" && us.RedactedBecause == nil && us.InviteRoomState == nil && us.Relations.Raw == nil &&
|
||
|
us.Relations.Annotations.Map == nil && us.Relations.References.List == nil && us.Relations.Replaces.List == nil
|
||
|
}
|