summary refs log tree commit diff
path: root/vendor/maunium.net/go/mautrix/event/ephemeral.go
blob: f447404bdb4c67fbea8b05b43ba745b1dbd2ab79 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
// 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"
	"time"

	"maunium.net/go/mautrix/id"
)

// TypingEventContent represents the content of a m.typing ephemeral event.
// https://spec.matrix.org/v1.2/client-server-api/#mtyping
type TypingEventContent struct {
	UserIDs []id.UserID `json:"user_ids"`
}

// ReceiptEventContent represents the content of a m.receipt ephemeral event.
// https://spec.matrix.org/v1.2/client-server-api/#mreceipt
type ReceiptEventContent map[id.EventID]Receipts

func (rec ReceiptEventContent) Set(evtID id.EventID, receiptType ReceiptType, userID id.UserID, receipt ReadReceipt) {
	rec.GetOrCreate(evtID).GetOrCreate(receiptType).Set(userID, receipt)
}

func (rec ReceiptEventContent) GetOrCreate(evt id.EventID) Receipts {
	receipts, ok := rec[evt]
	if !ok {
		receipts = make(Receipts)
		rec[evt] = receipts
	}
	return receipts
}

type ReceiptType string

const (
	ReceiptTypeRead        ReceiptType = "m.read"
	ReceiptTypeReadPrivate ReceiptType = "m.read.private"
)

type Receipts map[ReceiptType]UserReceipts

func (rps Receipts) GetOrCreate(receiptType ReceiptType) UserReceipts {
	read, ok := rps[receiptType]
	if !ok {
		read = make(UserReceipts)
		rps[receiptType] = read
	}
	return read
}

type UserReceipts map[id.UserID]ReadReceipt

func (ur UserReceipts) Set(userID id.UserID, receipt ReadReceipt) {
	ur[userID] = receipt
}

type ThreadID = id.EventID

const ReadReceiptThreadMain ThreadID = "main"

type ReadReceipt struct {
	Timestamp time.Time

	// Thread ID for thread-specific read receipts from MSC3771
	ThreadID ThreadID

	// Extra contains any unknown fields in the read receipt event.
	// Most servers don't allow clients to set them, so this will be empty in most cases.
	Extra map[string]interface{}
}

func (rr *ReadReceipt) UnmarshalJSON(data []byte) error {
	// Hacky compatibility hack against crappy clients that send double-encoded read receipts.
	// TODO is this actually needed? clients can't currently set custom content in receipts 🤔
	if data[0] == '"' && data[len(data)-1] == '"' {
		var strData string
		err := json.Unmarshal(data, &strData)
		if err != nil {
			return err
		}
		data = []byte(strData)
	}

	var parsed map[string]interface{}
	err := json.Unmarshal(data, &parsed)
	if err != nil {
		return err
	}
	threadID, _ := parsed["thread_id"].(string)
	ts, tsOK := parsed["ts"].(float64)
	delete(parsed, "thread_id")
	delete(parsed, "ts")
	*rr = ReadReceipt{
		ThreadID: ThreadID(threadID),
		Extra:    parsed,
	}
	if tsOK {
		rr.Timestamp = time.UnixMilli(int64(ts))
	}
	return nil
}

func (rr ReadReceipt) MarshalJSON() ([]byte, error) {
	data := rr.Extra
	if data == nil {
		data = make(map[string]interface{})
	}
	if rr.ThreadID != "" {
		data["thread_id"] = rr.ThreadID
	}
	if !rr.Timestamp.IsZero() {
		data["ts"] = rr.Timestamp.UnixMilli()
	}
	return json.Marshal(data)
}

type Presence string

const (
	PresenceOnline      Presence = "online"
	PresenceOffline     Presence = "offline"
	PresenceUnavailable Presence = "unavailable"
)

// PresenceEventContent represents the content of a m.presence ephemeral event.
// https://spec.matrix.org/v1.2/client-server-api/#mpresence
type PresenceEventContent struct {
	Presence        Presence            `json:"presence"`
	Displayname     string              `json:"displayname,omitempty"`
	AvatarURL       id.ContentURIString `json:"avatar_url,omitempty"`
	LastActiveAgo   int64               `json:"last_active_ago,omitempty"`
	CurrentlyActive bool                `json:"currently_active,omitempty"`
	StatusMessage   string              `json:"status_msg,omitempty"`
}