mirror of https://github.com/mautrix/go.git
219 lines
7.7 KiB
Go
219 lines
7.7 KiB
Go
// Copyright (c) 2022 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/base32"
|
|
"encoding/binary"
|
|
"fmt"
|
|
|
|
"maunium.net/go/mautrix/id"
|
|
)
|
|
|
|
type MessageStatusReason string
|
|
|
|
const (
|
|
MessageStatusGenericError MessageStatusReason = "m.event_not_handled"
|
|
MessageStatusUnsupported MessageStatusReason = "com.beeper.unsupported_event"
|
|
MessageStatusUndecryptable MessageStatusReason = "com.beeper.undecryptable_event"
|
|
MessageStatusTooOld MessageStatusReason = "m.event_too_old"
|
|
MessageStatusNetworkError MessageStatusReason = "m.foreign_network_error"
|
|
MessageStatusNoPermission MessageStatusReason = "m.no_permission"
|
|
MessageStatusBridgeUnavailable MessageStatusReason = "m.bridge_unavailable"
|
|
)
|
|
|
|
type MessageStatus string
|
|
|
|
const (
|
|
MessageStatusSuccess MessageStatus = "SUCCESS"
|
|
MessageStatusPending MessageStatus = "PENDING"
|
|
MessageStatusRetriable MessageStatus = "FAIL_RETRIABLE"
|
|
MessageStatusFail MessageStatus = "FAIL_PERMANENT"
|
|
)
|
|
|
|
type BeeperMessageStatusEventContent struct {
|
|
Network string `json:"network,omitempty"`
|
|
RelatesTo RelatesTo `json:"m.relates_to"`
|
|
Status MessageStatus `json:"status"`
|
|
Reason MessageStatusReason `json:"reason,omitempty"`
|
|
// Deprecated: clients were showing this to users even though they aren't supposed to.
|
|
// Use InternalError for error messages that should be included in bug reports, but not shown in the UI.
|
|
Error string `json:"error,omitempty"`
|
|
InternalError string `json:"internal_error,omitempty"`
|
|
Message string `json:"message,omitempty"`
|
|
|
|
LastRetry id.EventID `json:"last_retry,omitempty"`
|
|
|
|
MutateEventKey string `json:"mutate_event_key,omitempty"`
|
|
|
|
// Indicates the set of users to whom the event was delivered. If nil, then
|
|
// the client should not expect delivered status at any later point. If not
|
|
// nil (even if empty), this field indicates which users the event was
|
|
// delivered to.
|
|
DeliveredToUsers *[]id.UserID `json:"delivered_to_users,omitempty"`
|
|
}
|
|
|
|
type BeeperRetryMetadata struct {
|
|
OriginalEventID id.EventID `json:"original_event_id"`
|
|
RetryCount int `json:"retry_count"`
|
|
// last_retry is also present, but not used by bridges
|
|
}
|
|
|
|
type BeeperRoomKeyAckEventContent struct {
|
|
RoomID id.RoomID `json:"room_id"`
|
|
SessionID id.SessionID `json:"session_id"`
|
|
FirstMessageIndex int `json:"first_message_index"`
|
|
}
|
|
|
|
type LinkPreview struct {
|
|
CanonicalURL string `json:"og:url,omitempty"`
|
|
Title string `json:"og:title,omitempty"`
|
|
Type string `json:"og:type,omitempty"`
|
|
Description string `json:"og:description,omitempty"`
|
|
SiteName string `json:"og:site_name,omitempty"`
|
|
|
|
ImageURL id.ContentURIString `json:"og:image,omitempty"`
|
|
|
|
ImageSize int `json:"matrix:image:size,omitempty"`
|
|
ImageWidth int `json:"og:image:width,omitempty"`
|
|
ImageHeight int `json:"og:image:height,omitempty"`
|
|
ImageType string `json:"og:image:type,omitempty"`
|
|
}
|
|
|
|
// BeeperLinkPreview contains the data for a bundled URL preview as specified in MSC4095
|
|
//
|
|
// https://github.com/matrix-org/matrix-spec-proposals/pull/4095
|
|
type BeeperLinkPreview struct {
|
|
LinkPreview
|
|
|
|
MatchedURL string `json:"matched_url,omitempty"`
|
|
ImageEncryption *EncryptedFileInfo `json:"beeper:image:encryption,omitempty"`
|
|
}
|
|
|
|
type BeeperProfileExtra struct {
|
|
RemoteID string `json:"com.beeper.bridge.remote_id,omitempty"`
|
|
Identifiers []string `json:"com.beeper.bridge.identifiers,omitempty"`
|
|
Service string `json:"com.beeper.bridge.service,omitempty"`
|
|
Network string `json:"com.beeper.bridge.network,omitempty"`
|
|
IsBridgeBot bool `json:"com.beeper.bridge.is_bridge_bot,omitempty"`
|
|
IsNetworkBot bool `json:"com.beeper.bridge.is_network_bot,omitempty"`
|
|
}
|
|
|
|
type BeeperPerMessageProfile struct {
|
|
ID string `json:"id"`
|
|
Displayname string `json:"displayname,omitempty"`
|
|
AvatarURL *id.ContentURIString `json:"avatar_url,omitempty"`
|
|
AvatarFile *EncryptedFileInfo `json:"avatar_file,omitempty"`
|
|
}
|
|
|
|
type BeeperEncodedOrder struct {
|
|
order int64
|
|
suborder int16
|
|
}
|
|
|
|
func NewBeeperEncodedOrder(order int64, suborder int16) *BeeperEncodedOrder {
|
|
return &BeeperEncodedOrder{order: order, suborder: suborder}
|
|
}
|
|
|
|
func BeeperEncodedOrderFromString(str string) (*BeeperEncodedOrder, error) {
|
|
order, suborder, err := decodeIntPair(str)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &BeeperEncodedOrder{order: order, suborder: suborder}, nil
|
|
}
|
|
|
|
func (b *BeeperEncodedOrder) String() string {
|
|
if b == nil {
|
|
return ""
|
|
}
|
|
return encodeIntPair(b.order, b.suborder)
|
|
}
|
|
|
|
func (b *BeeperEncodedOrder) OrderPair() (int64, int16) {
|
|
if b == nil {
|
|
return 0, 0
|
|
}
|
|
return b.order, b.suborder
|
|
}
|
|
|
|
func (b *BeeperEncodedOrder) IsZero() bool {
|
|
return b == nil || (b.order == 0 && b.suborder == 0)
|
|
}
|
|
|
|
func (b *BeeperEncodedOrder) MarshalJSON() ([]byte, error) {
|
|
return []byte(`"` + b.String() + `"`), nil
|
|
}
|
|
|
|
func (b *BeeperEncodedOrder) UnmarshalJSON(data []byte) error {
|
|
if b == nil {
|
|
return fmt.Errorf("BeeperEncodedOrder: receiver is nil")
|
|
}
|
|
str := string(data)
|
|
if len(str) < 2 {
|
|
return fmt.Errorf("invalid encoded order string: %s", str)
|
|
}
|
|
decoded, err := BeeperEncodedOrderFromString(str[1 : len(str)-1])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
b.order, b.suborder = decoded.order, decoded.suborder
|
|
return nil
|
|
}
|
|
|
|
// encodeIntPair encodes an int64 and an int16 into a lexicographically sortable string
|
|
func encodeIntPair(a int64, b int16) string {
|
|
// Create a buffer to hold the binary representation of the integers.
|
|
// Will need 8 bytes for the int64 and 2 bytes for the int16.
|
|
var buf [10]byte
|
|
|
|
// Flip the sign bit of each integer to map the entire int range to uint
|
|
// in a way that preserves the order of the original integers.
|
|
//
|
|
// Explanation:
|
|
// - By XORing with (1 << 63), we flip the most significant bit (sign bit) of the int64 value.
|
|
// - Negative numbers (which have a sign bit of 1) become smaller uint64 values.
|
|
// - Non-negative numbers (with a sign bit of 0) become larger uint64 values.
|
|
// - This mapping preserves the original ordering when the uint64 values are compared.
|
|
binary.BigEndian.PutUint64(buf[0:8], uint64(a)^(1<<63))
|
|
binary.BigEndian.PutUint16(buf[8:10], uint16(b)^(1<<15))
|
|
|
|
// Encode the buffer into a Base32 string without padding using the Hex encoding.
|
|
//
|
|
// Explanation:
|
|
// - Base32 encoding converts binary data into a text representation using 32 ASCII characters.
|
|
// - Using Base32HexEncoding ensures that the characters are in lexicographical order.
|
|
// - Disabling padding results in a consistent string length, which is important for sorting.
|
|
encoded := base32.HexEncoding.WithPadding(base32.NoPadding).EncodeToString(buf[:])
|
|
|
|
return encoded
|
|
}
|
|
|
|
// decodeIntPair decodes a string produced by encodeIntPair back into the original int64 and int16 values
|
|
func decodeIntPair(encoded string) (int64, int16, error) {
|
|
// Decode the Base32 string back into the original byte buffer.
|
|
buf, err := base32.HexEncoding.WithPadding(base32.NoPadding).DecodeString(encoded)
|
|
if err != nil {
|
|
return 0, 0, fmt.Errorf("failed to decode string: %w", err)
|
|
}
|
|
|
|
// Check that the decoded buffer has the expected length.
|
|
if len(buf) != 10 {
|
|
return 0, 0, fmt.Errorf("invalid encoded string length: expected 10 bytes, got %d", len(buf))
|
|
}
|
|
|
|
// Read the uint values from the buffer using big-endian byte order.
|
|
aPos := binary.BigEndian.Uint64(buf[0:8])
|
|
bPos := binary.BigEndian.Uint16(buf[8:10])
|
|
|
|
// Reverse the sign bit flip to retrieve the original values.
|
|
a := int64(aPos ^ (1 << 63))
|
|
b := int16(bPos ^ (1 << 15))
|
|
|
|
return a, b, nil
|
|
}
|