Events rework

Now Event is an interface. Moved all events stuff to separate events module.
This commit is contained in:
nxshock 2019-08-10 12:48:55 +05:00
parent 18566511c9
commit 197b296a24
14 changed files with 123 additions and 151 deletions

View File

@ -5,7 +5,7 @@ import (
"github.com/signaller-matrix/signaller/internal/models/common" "github.com/signaller-matrix/signaller/internal/models/common"
"github.com/signaller-matrix/signaller/internal/models/createroom" "github.com/signaller-matrix/signaller/internal/models/createroom"
"github.com/signaller-matrix/signaller/internal/models/devices" "github.com/signaller-matrix/signaller/internal/models/devices"
"github.com/signaller-matrix/signaller/internal/models/rooms" "github.com/signaller-matrix/signaller/internal/models/events"
"github.com/signaller-matrix/signaller/internal/models/sync" "github.com/signaller-matrix/signaller/internal/models/sync"
) )
@ -18,10 +18,10 @@ type Backend interface {
Sync(token string, request sync.SyncRequest) (response *sync.SyncReply, err models.ApiError) Sync(token string, request sync.SyncRequest) (response *sync.SyncReply, err models.ApiError)
PublicRooms(filter string) []Room PublicRooms(filter string) []Room
ValidateUsernameFunc() func(string) error ValidateUsernameFunc() func(string) error
GetEventByID(id string) rooms.Event GetEventByID(id string) events.Event
PutEvent(rooms.Event) error PutEvent(events.Event) error
GetRoomByAlias(string) Room GetRoomByAlias(string) Room
GetEventsSince(user User, sinceToken string, limit int) []rooms.Event GetEventsSince(user User, sinceToken string, limit int) []events.Event
} }
type Room interface { type Room interface {

View File

@ -2,6 +2,7 @@ package memory
import ( import (
"fmt" "fmt"
"reflect"
"regexp" "regexp"
"sort" "sort"
"strings" "strings"
@ -12,7 +13,7 @@ import (
"github.com/signaller-matrix/signaller/internal/models" "github.com/signaller-matrix/signaller/internal/models"
"github.com/signaller-matrix/signaller/internal/models/common" "github.com/signaller-matrix/signaller/internal/models/common"
"github.com/signaller-matrix/signaller/internal/models/createroom" "github.com/signaller-matrix/signaller/internal/models/createroom"
"github.com/signaller-matrix/signaller/internal/models/rooms" "github.com/signaller-matrix/signaller/internal/models/events"
mSync "github.com/signaller-matrix/signaller/internal/models/sync" mSync "github.com/signaller-matrix/signaller/internal/models/sync"
"github.com/wangjia184/sortedset" "github.com/wangjia184/sortedset"
) )
@ -185,32 +186,32 @@ func defaultValidationUsernameFunc(userName string) error {
return nil return nil
} }
func (backend *Backend) GetEventByID(id string) rooms.Event { func (backend *Backend) GetEventByID(id string) events.Event {
backend.mutex.RLock() backend.mutex.RLock()
defer backend.mutex.RUnlock() defer backend.mutex.RUnlock()
return backend.events.GetByKey(id).Value.(rooms.Event) return backend.events.GetByKey(id).Value.(events.Event)
} }
func (backend *Backend) PutEvent(event rooms.Event) error { func (backend *Backend) PutEvent(event events.Event) error {
backend.mutex.Lock() backend.mutex.Lock()
defer backend.mutex.Unlock() defer backend.mutex.Unlock()
backend.events.AddOrUpdate(event.EventID, sortedset.SCORE(time.Now().Unix()), event) backend.events.AddOrUpdate(event.ID(), sortedset.SCORE(time.Now().Unix()), event)
return nil return nil
} }
func (backend *Backend) GetEventsSince(user internal.User, sinceToken string, limit int) []rooms.Event { func (backend *Backend) GetEventsSince(user internal.User, sinceToken string, limit int) []events.Event {
sinceEventNode := backend.events.GetByKey(sinceToken) sinceEventNode := backend.events.GetByKey(sinceToken)
sEvents := backend.events.GetByScoreRange(sinceEventNode.Score(), -1, &sortedset.GetByScoreRangeOptions{ sEvents := backend.events.GetByScoreRange(sinceEventNode.Score(), -1, &sortedset.GetByScoreRangeOptions{
Limit: limit, Limit: limit,
}) })
events := extractEventsFromNodes(sEvents) eventsSlice := extractEventsFromNodes(sEvents)
var returnEvents []rooms.Event var returnEvents []events.Event
for _, event := range events { for _, event := range eventsSlice {
if isEventRelatedToUser(event, user) { if isEventRelatedToUser(event, user) {
returnEvents = append(returnEvents, event) returnEvents = append(returnEvents, event)
} }
@ -219,17 +220,21 @@ func (backend *Backend) GetEventsSince(user internal.User, sinceToken string, li
return returnEvents return returnEvents
} }
func extractEventsFromNodes(nodes []*sortedset.SortedSetNode) []rooms.Event { func extractEventsFromNodes(nodes []*sortedset.SortedSetNode) []events.Event {
var events []rooms.Event var eventsSlice []events.Event
for _, e := range nodes { for _, e := range nodes {
events = append(events, e.Value.(rooms.Event)) eventsSlice = append(eventsSlice, e.Value.(events.Event))
} }
return events return eventsSlice
} }
func isEventRelatedToUser(event rooms.Event, user internal.User) bool { func isEventRelatedToUser(event events.Event, user internal.User) bool {
if internal.InArray(event.RoomID, extractRoomIDsFromModel(user.JoinedRooms())) { // get RoomID field from event interface
// TODO: what if there are no RoomID field?
roomID := reflect.ValueOf(event).Elem().FieldByName("RoomID").Addr().Interface().(string)
if internal.InArray(roomID, extractRoomIDsFromModel(user.JoinedRooms())) {
return true return true
} }
return false return false

View File

@ -1,31 +1,12 @@
package memory package memory
import ( /*
"encoding/json"
"time"
"github.com/signaller-matrix/signaller/internal"
"github.com/signaller-matrix/signaller/internal/models/common"
"github.com/signaller-matrix/signaller/internal/models/rooms"
)
type RoomEvent struct { type RoomEvent struct {
Content json.RawMessage Content json.RawMessage
Type common.EventType Type events.EventType
EventID string EventID string
Sender internal.User Sender internal.User
OriginServerTS time.Time OriginServerTS time.Time
Room internal.Room Room internal.Room
} }
*/
func (roomEvent *RoomEvent) ToEvent() rooms.Event {
event := rooms.Event{
Content: roomEvent.Content,
Type: roomEvent.Type,
EventID: roomEvent.EventID,
Sender: roomEvent.Sender.ID(),
OriginServerTS: roomEvent.OriginServerTS.Unix(),
RoomID: roomEvent.Room.ID()}
return event
}

View File

@ -10,6 +10,7 @@ import (
"github.com/signaller-matrix/signaller/internal/models/common" "github.com/signaller-matrix/signaller/internal/models/common"
"github.com/signaller-matrix/signaller/internal/models/createroom" "github.com/signaller-matrix/signaller/internal/models/createroom"
"github.com/signaller-matrix/signaller/internal/models/devices" "github.com/signaller-matrix/signaller/internal/models/devices"
"github.com/signaller-matrix/signaller/internal/models/events"
) )
type User struct { type User struct {
@ -42,46 +43,46 @@ func (user *User) CreateRoom(request createroom.Request) (internal.Room, models.
} }
} }
t := time.Now() currentUnixTime := time.Now().Unix()
events := make([]RoomEvent, 0) eventsSlice := make([]events.RoomEvent, 0)
// Create room event // Create room event
events = append(events, RoomEvent{ eventsSlice = append(eventsSlice, events.RoomEvent{
Content: nil, ContentData: nil,
Type: common.Create, EType: events.Create,
EventID: internal.RandomString(eventIDSize), EventID: internal.RandomString(eventIDSize),
Sender: user, Sender: user.ID(),
OriginServerTS: t}) OriginServerTs: currentUnixTime})
// TODO: Add join room event // TODO: Add join room event
// Set join rules event // Set join rules event
events = append(events, RoomEvent{ eventsSlice = append(eventsSlice, events.RoomEvent{
Content: []byte(request.Visibility), // TODO: check visibility vs join rules ContentData: []byte(request.Visibility), // TODO: check visibility vs join rules
Type: common.JoinRules, EType: events.JoinRules,
EventID: internal.RandomString(eventIDSize), EventID: internal.RandomString(eventIDSize),
Sender: user, Sender: user.ID(),
OriginServerTS: t}) OriginServerTs: currentUnixTime})
// Set room name event // Set room name event
if request.Name != "" { if request.Name != "" {
events = append(events, RoomEvent{ eventsSlice = append(eventsSlice, events.RoomEvent{
Content: nil, // TODO: add ContentData: nil, // TODO: add
Type: common.Name, EType: events.Name,
EventID: internal.RandomString(eventIDSize), EventID: internal.RandomString(eventIDSize),
Sender: user, Sender: user.ID(),
OriginServerTS: t}) OriginServerTs: currentUnixTime})
} }
// Set room alias event // Set room alias event
if request.RoomAliasName != "" { if request.RoomAliasName != "" {
events = append(events, RoomEvent{ eventsSlice = append(eventsSlice, events.RoomEvent{
Content: nil, // TODO: add ContentData: nil, // TODO: add
Type: common.CanonicalAlias, EType: events.CanonicalAlias,
EventID: internal.RandomString(eventIDSize), EventID: internal.RandomString(eventIDSize),
Sender: user, Sender: user.ID(),
OriginServerTS: t}) OriginServerTs: currentUnixTime})
} }
room := &Room{ room := &Room{
@ -95,13 +96,12 @@ func (user *User) CreateRoom(request createroom.Request) (internal.Room, models.
server: user.backend, server: user.backend,
state: request.Preset} state: request.Preset}
for i, _ := range events { for i, _ := range eventsSlice {
events[i].Room = room eventsSlice[i].RoomID = room.id
//v.Room = room
} }
for i, _ := range events { for i, _ := range eventsSlice {
user.backend.PutEvent(events[i].ToEvent()) user.backend.PutEvent(&eventsSlice[i])
} }
user.backend.rooms[room.ID()] = room user.backend.rooms[room.ID()] = room
@ -123,13 +123,13 @@ func (user *User) SetTopic(room internal.Room, topic string) models.ApiError {
memRoom.mutex.Unlock() memRoom.mutex.Unlock()
rEvent := &RoomEvent{ rEvent := &events.RoomEvent{
Type: common.Topic, EType: events.Topic,
Sender: user, Sender: user.ID(),
OriginServerTS: time.Now(), OriginServerTs: time.Now().Unix(),
Room: room} RoomID: memRoom.id}
user.backend.PutEvent(rEvent.ToEvent()) user.backend.PutEvent(rEvent)
return nil return nil
} }
@ -203,15 +203,15 @@ func (user *User) SendMessage(room internal.Room, text string) models.ApiError {
return models.NewError(models.M_FORBIDDEN, "") return models.NewError(models.M_FORBIDDEN, "")
} }
rEvent := &RoomEvent{ rEvent := &events.RoomEvent{
Content: nil, ContentData: nil,
Type: common.Message, EType: events.Message,
EventID: internal.RandomString(defaultTokenSize), EventID: internal.RandomString(defaultTokenSize),
Sender: user, Sender: user.ID(),
OriginServerTS: time.Now(), OriginServerTs: time.Now().Unix(),
Room: room} RoomID: memRoom.id}
user.backend.PutEvent(rEvent.ToEvent()) user.backend.PutEvent(rEvent)
return nil return nil
} }

View File

@ -1,5 +0,0 @@
package common
type Presence struct {
events []Event `json:"events"` // List of events.
}

View File

@ -1,6 +0,0 @@
package common
// TODO: проверить правильность выбора типа
type ToDevice struct {
events []Event `json:"events` // List of send-to-device messages
}

View File

@ -1,7 +0,0 @@
package common
type UnsignedData struct {
Age int `json:"age"` // The time in milliseconds that has elapsed since the event was sent. This field is generated by the local homeserver, and may be incorrect if the local time on at least one of the two servers is out of sync, which can cause the age to either be negative or greater than it actually is.
RedactedBecause Event `json:"redacted_because"` // Optional. The event that redacted this event, if any.
TransactionID string `json:"transaction_id"` // The client-supplied transaction ID, if the client being given the event is the same one which sent it.
}

View File

@ -1,8 +1,6 @@
package createroom package createroom
import ( import "github.com/signaller-matrix/signaller/internal/models/events"
common "github.com/signaller-matrix/signaller/internal/models/common"
)
// https://matrix.org/docs/spec/client_server/latest#post-matrix-client-r0-createroom // https://matrix.org/docs/spec/client_server/latest#post-matrix-client-r0-createroom
type VisibilityType string type VisibilityType string
@ -40,7 +38,7 @@ type Request struct {
RoomVersion string `json:"room_version,omitempty"` // The room version to set for the room. If not provided, the homeserver is to use its configured default. If provided, the homeserver will return a 400 error with the errcode M_UNSUPPORTED_ROOM_VERSION if it does not support the room version. RoomVersion string `json:"room_version,omitempty"` // The room version to set for the room. If not provided, the homeserver is to use its configured default. If provided, the homeserver will return a 400 error with the errcode M_UNSUPPORTED_ROOM_VERSION if it does not support the room version.
// TODO: проверить тип // TODO: проверить тип
// CreationContent CreationContentType `json:"creation_content,omitempty"` // CreationContent CreationContentType `json:"creation_content,omitempty"`
InitialState []common.StateEvent `json:"initial_state,omitempty"` // A list of state events to set in the new room. This allows the user to override the default state events set in the new room. The expected format of the state events are an object with type, state_key and content keys set. Takes precedence over events set by preset, but gets overriden by name and topic keys. InitialState []events.StateEvent `json:"initial_state,omitempty"` // A list of state events to set in the new room. This allows the user to override the default state events set in the new room. The expected format of the state events are an object with type, state_key and content keys set. Takes precedence over events set by preset, but gets overriden by name and topic keys.
Preset Preset `json:"preset,omitempty"` // Convenience parameter for setting various default state events based on a preset. If unspecified, the server should use the visibility to determine which preset to use. A visbility of public equates to a preset of public_chat and private visibility equates to a preset of private_chat. One of: ["private_chat", "public_chat", "trusted_private_chat"] Preset Preset `json:"preset,omitempty"` // Convenience parameter for setting various default state events based on a preset. If unspecified, the server should use the visibility to determine which preset to use. A visbility of public equates to a preset of public_chat and private visibility equates to a preset of private_chat. One of: ["private_chat", "public_chat", "trusted_private_chat"]
IsDirect bool `json:"is_direct,omitempty"` // This flag makes the server set the is_direct flag on the m.room.member events sent to the users in invite and invite_3pid. IsDirect bool `json:"is_direct,omitempty"` // This flag makes the server set the is_direct flag on the m.room.member events sent to the users in invite and invite_3pid.
// PowerLevelContentOverride `json:"power_level_content_override"` // PowerLevelContentOverride `json:"power_level_content_override"`

View File

@ -1,4 +1,4 @@
package common package events
type DeviceLists struct { type DeviceLists struct {
Changed []string `json:"changed"` // List of users who have updated their device identity keys, or who now share an encrypted room with the client since the previous sync response. Changed []string `json:"changed"` // List of users who have updated their device identity keys, or who now share an encrypted room with the client since the previous sync response.

View File

@ -1,4 +1,4 @@
package common package events
import "encoding/json" import "encoding/json"
@ -56,10 +56,10 @@ const (
PinnedEvents EventType = "m.room.pinned_events" PinnedEvents EventType = "m.room.pinned_events"
) )
type Event struct { type Event interface {
// TODO: object Content() json.RawMessage // Required. The fields in this object will vary depending on the type of event. When interacting with the REST API, this is the HTTP body.
Content json.RawMessage `json:"content"` // Required. The fields in this object will vary depending on the type of event. When interacting with the REST API, this is the HTTP body. Type() EventType // Required. The type of event. This SHOULD be namespaced similar to Java package naming conventions e.g. 'com.example.subdomain.event.type'
Type string `json:"type"` // Required. The type of event. This SHOULD be namespaced similar to Java package naming conventions e.g. 'com.example.subdomain.event.type' ID() string
} }
type EventContent struct { type EventContent struct {
@ -111,3 +111,17 @@ type StrippedState struct {
Type string `json:"type"` // Required. The type for the event. Type string `json:"type"` // Required. The type for the event.
Sender string `json:"sender"` // Required. The sender for the event. Sender string `json:"sender"` // Required. The sender for the event.
} }
type UnsignedData struct {
Age int `json:"age"` // The time in milliseconds that has elapsed since the event was sent. This field is generated by the local homeserver, and may be incorrect if the local time on at least one of the two servers is out of sync, which can cause the age to either be negative or greater than it actually is.
RedactedBecause Event `json:"redacted_because"` // Optional. The event that redacted this event, if any.
TransactionID string `json:"transaction_id"` // The client-supplied transaction ID, if the client being given the event is the same one which sent it.
}
type Presence struct {
Events []Event `json:"events"` // List of events.
}
type ToDevice struct {
Events []Event `json:"events` // List of send-to-device messages
}

View File

@ -1,4 +1,4 @@
package common package events
// https://matrix.org/docs/spec/client_server/r0.4.0.html#get-matrix-client-r0-rooms-roomid-members // https://matrix.org/docs/spec/client_server/r0.4.0.html#get-matrix-client-r0-rooms-roomid-members
type MemberEvent struct { type MemberEvent struct {

View File

@ -1,21 +1,30 @@
package common package events
import "encoding/json" import (
"encoding/json"
type Timeline struct { )
Events []RoomEvent `json:"events"` // List of events.
Limited bool `json:"limited"` // True if the number of events returned was limited by the limit on the filter.
PrevBatch string `json:"prev_batch"` // A token that can be supplied to the from parameter of the rooms/{roomId}/messages endpoint.
}
type RoomEvent struct { type RoomEvent struct {
// TODO: object // TODO: object
Content json.RawMessage `json:"content"` // Required. The fields in this object will vary depending on the type of event. When interacting with the REST API, this is the HTTP body. ContentData json.RawMessage `json:"content"` // Required. The fields in this object will vary depending on the type of event. When interacting with the REST API, this is the HTTP body.
Type string `json:"type"` // Required. The type of event. This SHOULD be namespaced similar to Java package naming conventions e.g. 'com.example.subdomain.event.type' EType EventType `json:"type"` // Required. The type of event. This SHOULD be namespaced similar to Java package naming conventions e.g. 'com.example.subdomain.event.type'
EventID string `json:"event_id"` // Required. The globally unique event identifier. EventID string `json:"event_id"` // Required. The globally unique event identifier.
Sender string `json:"sender"` // Required. Contains the fully-qualified ID of the user who sent this event. Sender string `json:"sender"` // Required. Contains the fully-qualified ID of the user who sent this event.
OriginServerTs int64 `json:"origin_server_ts"` // Required. Timestamp in milliseconds on originating homeserver when this event was sent. OriginServerTs int64 `json:"origin_server_ts"` // Required. Timestamp in milliseconds on originating homeserver when this event was sent.
Unsigned UnsignedData `json:"unsigned"` // Contains optional extra information about the event. Unsigned UnsignedData `json:"unsigned"` // Contains optional extra information about the event.
RoomID string `json:"room_id"` // Required. The ID of the room associated with this event. Will not be present on events that arrive through /sync, despite being required everywhere else.
}
func (roomEvent *RoomEvent) Content() json.RawMessage {
return roomEvent.ContentData
}
func (roomEvent *RoomEvent) ID() string {
return roomEvent.EventID
}
func (roomEvent *RoomEvent) Type() EventType {
return roomEvent.EType
} }
type JoinedRoom struct { type JoinedRoom struct {
@ -48,3 +57,9 @@ type InvitedRoom struct {
type InviteState struct { type InviteState struct {
Events []StrippedState `json:"events"` // The StrippedState events that form the invite state. Events []StrippedState `json:"events"` // The StrippedState events that form the invite state.
} }
type Timeline struct {
Events []RoomEvent `json:"events"` // List of events.
Limited bool `json:"limited"` // True if the number of events returned was limited by the limit on the filter.
PrevBatch string `json:"prev_batch"` // A token that can be supplied to the from parameter of the rooms/{roomId}/messages endpoint.
}

View File

@ -1,11 +1,5 @@
package rooms package rooms
import (
"encoding/json"
"github.com/signaller-matrix/signaller/internal/models/common"
)
type JoinRule string type JoinRule string
const ( const (
@ -14,20 +8,3 @@ const (
Invite = "invite" Invite = "invite"
Private = "private" Private = "private"
) )
// https://matrix.org/docs/spec/client_server/latest#room-event-fields
type Event struct {
Content json.RawMessage `json:"content"` // Required. The fields in this object will vary depending on the type of event. When interacting with the REST API, this is the HTTP body.
Type common.EventType `json:"type"` // Required. The type of event. This SHOULD be namespaced similar to Java package naming conventions e.g. 'com.example.subdomain.event.type'
EventID string `json:"event_id"` // Required. The globally unique event identifier.
Sender string `json:"sender"` // Required. Contains the fully-qualified ID of the user who sent this event.
OriginServerTS int64 `json:"origin_server_ts"` // Required. Timestamp in milliseconds on originating homeserver when this event was sent.
Unsigned UnsignedData `json:"unsigned,omitempty"` // Contains optional extra information about the event.
RoomID string `json:"room_id"` // Required. The ID of the room associated with this event. Will not be present on events that arrive through /sync, despite being required everywhere else.
}
type UnsignedData struct {
Age int `json:"age"` // The time in milliseconds that has elapsed since the event was sent. This field is generated by the local homeserver, and may be incorrect if the local time on at least one of the two servers is out of sync, which can cause the age to either be negative or greater than it actually is.
RedactedBecause common.Event `json:"redacted_because,omitempty"` // Optional. The event that redacted this event, if any.
TransactionID string `json:"transaction_id"` // The client-supplied transaction ID, if the client being given the event is the same one which sent it.
}

View File

@ -1,21 +1,21 @@
package sync package sync
import ( import (
common "github.com/signaller-matrix/signaller/internal/models/common" "github.com/signaller-matrix/signaller/internal/models/events"
) )
type SyncReply struct { type SyncReply struct {
NextBatch string `json:"next_batch"` // Required. The batch token to supply in the since param of the next /sync request. NextBatch string `json:"next_batch"` // Required. The batch token to supply in the since param of the next /sync request.
Rooms RoomsSyncReply `json:"rooms"` // Updates to rooms. Rooms RoomsSyncReply `json:"rooms"` // Updates to rooms.
Presence common.Presence `json:"presence"` // The updates to the presence status of other users. Presence events.Presence `json:"presence"` // The updates to the presence status of other users.
AccountData common.AccountData `json:"account_data"` // The global private data created by this user. AccountData events.AccountData `json:"account_data"` // The global private data created by this user.
ToDevice common.ToDevice `json:"to_device"` // Information on the send-to-device messages for the client device, as defined in Send-to-Device messaging. ToDevice events.ToDevice `json:"to_device"` // Information on the send-to-device messages for the client device, as defined in Send-to-Device messaging.
DeviceLists common.DeviceLists `json:"device_lists"` // Information on end-to-end device updates, as specified in End-to-end encryption. DeviceLists events.DeviceLists `json:"device_lists"` // Information on end-to-end device updates, as specified in End-to-end encryption.
DeviceOneTimeKeysCount map[string]int `json:"device_one_time_keys_count"` // Information on end-to-end encryption keys, as specified in End-to-end encryption. DeviceOneTimeKeysCount map[string]int `json:"device_one_time_keys_count"` // Information on end-to-end encryption keys, as specified in End-to-end encryption.
} }
type RoomsSyncReply struct { type RoomsSyncReply struct {
Join map[string]common.JoinedRoom `json:"join"` // The rooms that the user has joined. Join map[string]events.JoinedRoom `json:"join"` // The rooms that the user has joined.
Invite map[string]common.InvitedRoom `json:"invite"` // The rooms that the user has been invited to. Invite map[string]events.InvitedRoom `json:"invite"` // The rooms that the user has been invited to.
Leave map[string]common.LeftRoom `json:"leave"` // The rooms that the user has left or been banned from. Leave map[string]events.LeftRoom `json:"leave"` // The rooms that the user has left or been banned from.
} }