diff --git a/internal/backend.go b/internal/backend.go index d646409..ac9f4f9 100644 --- a/internal/backend.go +++ b/internal/backend.go @@ -5,7 +5,7 @@ import ( "github.com/signaller-matrix/signaller/internal/models/common" "github.com/signaller-matrix/signaller/internal/models/createroom" "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" ) @@ -18,10 +18,10 @@ type Backend interface { Sync(token string, request sync.SyncRequest) (response *sync.SyncReply, err models.ApiError) PublicRooms(filter string) []Room ValidateUsernameFunc() func(string) error - GetEventByID(id string) rooms.Event - PutEvent(rooms.Event) error + GetEventByID(id string) events.Event + PutEvent(events.Event) error GetRoomByAlias(string) Room - GetEventsSince(user User, sinceToken string, limit int) []rooms.Event + GetEventsSince(user User, sinceToken string, limit int) []events.Event } type Room interface { diff --git a/internal/backends/memory/backend.go b/internal/backends/memory/backend.go index 04cf4e9..0990a56 100644 --- a/internal/backends/memory/backend.go +++ b/internal/backends/memory/backend.go @@ -2,6 +2,7 @@ package memory import ( "fmt" + "reflect" "regexp" "sort" "strings" @@ -12,7 +13,7 @@ import ( "github.com/signaller-matrix/signaller/internal/models" "github.com/signaller-matrix/signaller/internal/models/common" "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" "github.com/wangjia184/sortedset" ) @@ -185,32 +186,32 @@ func defaultValidationUsernameFunc(userName string) error { return nil } -func (backend *Backend) GetEventByID(id string) rooms.Event { +func (backend *Backend) GetEventByID(id string) events.Event { backend.mutex.RLock() 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() 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 } -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) sEvents := backend.events.GetByScoreRange(sinceEventNode.Score(), -1, &sortedset.GetByScoreRangeOptions{ Limit: limit, }) - events := extractEventsFromNodes(sEvents) + eventsSlice := extractEventsFromNodes(sEvents) - var returnEvents []rooms.Event - for _, event := range events { + var returnEvents []events.Event + for _, event := range eventsSlice { if isEventRelatedToUser(event, user) { returnEvents = append(returnEvents, event) } @@ -219,17 +220,21 @@ func (backend *Backend) GetEventsSince(user internal.User, sinceToken string, li return returnEvents } -func extractEventsFromNodes(nodes []*sortedset.SortedSetNode) []rooms.Event { - var events []rooms.Event +func extractEventsFromNodes(nodes []*sortedset.SortedSetNode) []events.Event { + var eventsSlice []events.Event 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 { - if internal.InArray(event.RoomID, extractRoomIDsFromModel(user.JoinedRooms())) { +func isEventRelatedToUser(event events.Event, user internal.User) bool { + // 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 false diff --git a/internal/backends/memory/roomevents.go b/internal/backends/memory/roomevents.go index f1469c3..ffadc20 100644 --- a/internal/backends/memory/roomevents.go +++ b/internal/backends/memory/roomevents.go @@ -1,31 +1,12 @@ 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 { Content json.RawMessage - Type common.EventType + Type events.EventType EventID string Sender internal.User OriginServerTS time.Time 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 -} +*/ diff --git a/internal/backends/memory/user.go b/internal/backends/memory/user.go index 9b72485..7c63660 100644 --- a/internal/backends/memory/user.go +++ b/internal/backends/memory/user.go @@ -10,6 +10,7 @@ import ( "github.com/signaller-matrix/signaller/internal/models/common" "github.com/signaller-matrix/signaller/internal/models/createroom" "github.com/signaller-matrix/signaller/internal/models/devices" + "github.com/signaller-matrix/signaller/internal/models/events" ) 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 - events = append(events, RoomEvent{ - Content: nil, - Type: common.Create, + eventsSlice = append(eventsSlice, events.RoomEvent{ + ContentData: nil, + EType: events.Create, EventID: internal.RandomString(eventIDSize), - Sender: user, - OriginServerTS: t}) + Sender: user.ID(), + OriginServerTs: currentUnixTime}) // TODO: Add join room event // Set join rules event - events = append(events, RoomEvent{ - Content: []byte(request.Visibility), // TODO: check visibility vs join rules - Type: common.JoinRules, + eventsSlice = append(eventsSlice, events.RoomEvent{ + ContentData: []byte(request.Visibility), // TODO: check visibility vs join rules + EType: events.JoinRules, EventID: internal.RandomString(eventIDSize), - Sender: user, - OriginServerTS: t}) + Sender: user.ID(), + OriginServerTs: currentUnixTime}) // Set room name event if request.Name != "" { - events = append(events, RoomEvent{ - Content: nil, // TODO: add - Type: common.Name, + eventsSlice = append(eventsSlice, events.RoomEvent{ + ContentData: nil, // TODO: add + EType: events.Name, EventID: internal.RandomString(eventIDSize), - Sender: user, - OriginServerTS: t}) + Sender: user.ID(), + OriginServerTs: currentUnixTime}) } // Set room alias event if request.RoomAliasName != "" { - events = append(events, RoomEvent{ - Content: nil, // TODO: add - Type: common.CanonicalAlias, + eventsSlice = append(eventsSlice, events.RoomEvent{ + ContentData: nil, // TODO: add + EType: events.CanonicalAlias, EventID: internal.RandomString(eventIDSize), - Sender: user, - OriginServerTS: t}) + Sender: user.ID(), + OriginServerTs: currentUnixTime}) } room := &Room{ @@ -95,13 +96,12 @@ func (user *User) CreateRoom(request createroom.Request) (internal.Room, models. server: user.backend, state: request.Preset} - for i, _ := range events { - events[i].Room = room - //v.Room = room + for i, _ := range eventsSlice { + eventsSlice[i].RoomID = room.id } - for i, _ := range events { - user.backend.PutEvent(events[i].ToEvent()) + for i, _ := range eventsSlice { + user.backend.PutEvent(&eventsSlice[i]) } user.backend.rooms[room.ID()] = room @@ -123,13 +123,13 @@ func (user *User) SetTopic(room internal.Room, topic string) models.ApiError { memRoom.mutex.Unlock() - rEvent := &RoomEvent{ - Type: common.Topic, - Sender: user, - OriginServerTS: time.Now(), - Room: room} + rEvent := &events.RoomEvent{ + EType: events.Topic, + Sender: user.ID(), + OriginServerTs: time.Now().Unix(), + RoomID: memRoom.id} - user.backend.PutEvent(rEvent.ToEvent()) + user.backend.PutEvent(rEvent) return nil } @@ -203,15 +203,15 @@ func (user *User) SendMessage(room internal.Room, text string) models.ApiError { return models.NewError(models.M_FORBIDDEN, "") } - rEvent := &RoomEvent{ - Content: nil, - Type: common.Message, + rEvent := &events.RoomEvent{ + ContentData: nil, + EType: events.Message, EventID: internal.RandomString(defaultTokenSize), - Sender: user, - OriginServerTS: time.Now(), - Room: room} + Sender: user.ID(), + OriginServerTs: time.Now().Unix(), + RoomID: memRoom.id} - user.backend.PutEvent(rEvent.ToEvent()) + user.backend.PutEvent(rEvent) return nil } diff --git a/internal/models/common/presence.go b/internal/models/common/presence.go deleted file mode 100644 index 0ec0e42..0000000 --- a/internal/models/common/presence.go +++ /dev/null @@ -1,5 +0,0 @@ -package common - -type Presence struct { - events []Event `json:"events"` // List of events. -} diff --git a/internal/models/common/to_device.go b/internal/models/common/to_device.go deleted file mode 100644 index ef288be..0000000 --- a/internal/models/common/to_device.go +++ /dev/null @@ -1,6 +0,0 @@ -package common - -// TODO: проверить правильность выбора типа -type ToDevice struct { - events []Event `json:"events` // List of send-to-device messages -} diff --git a/internal/models/common/unsigned_data.go b/internal/models/common/unsigned_data.go deleted file mode 100644 index 6234fe9..0000000 --- a/internal/models/common/unsigned_data.go +++ /dev/null @@ -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. -} diff --git a/internal/models/createroom/request.go b/internal/models/createroom/request.go index 1839de7..62e7410 100644 --- a/internal/models/createroom/request.go +++ b/internal/models/createroom/request.go @@ -1,8 +1,6 @@ package createroom -import ( - common "github.com/signaller-matrix/signaller/internal/models/common" -) +import "github.com/signaller-matrix/signaller/internal/models/events" // https://matrix.org/docs/spec/client_server/latest#post-matrix-client-r0-createroom 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. // TODO: проверить тип // 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"] 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"` diff --git a/internal/models/common/device_lists.go b/internal/models/events/device_lists.go similarity index 95% rename from internal/models/common/device_lists.go rename to internal/models/events/device_lists.go index a983d27..adc70c3 100644 --- a/internal/models/common/device_lists.go +++ b/internal/models/events/device_lists.go @@ -1,4 +1,4 @@ -package common +package events 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. diff --git a/internal/models/common/event.go b/internal/models/events/events.go similarity index 82% rename from internal/models/common/event.go rename to internal/models/events/events.go index 790a89d..2f09723 100644 --- a/internal/models/common/event.go +++ b/internal/models/events/events.go @@ -1,4 +1,4 @@ -package common +package events import "encoding/json" @@ -56,10 +56,10 @@ const ( PinnedEvents EventType = "m.room.pinned_events" ) -type Event struct { - // 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. - 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' +type Event interface { + 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. + Type() EventType // 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 { @@ -111,3 +111,17 @@ type StrippedState struct { Type string `json:"type"` // Required. The type 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 +} diff --git a/internal/models/common/member_event.go b/internal/models/events/member_event.go similarity index 98% rename from internal/models/common/member_event.go rename to internal/models/events/member_event.go index 08d8a27..283f838 100644 --- a/internal/models/common/member_event.go +++ b/internal/models/events/member_event.go @@ -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 type MemberEvent struct { diff --git a/internal/models/common/room_event.go b/internal/models/events/room_event.go similarity index 85% rename from internal/models/common/room_event.go rename to internal/models/events/room_event.go index a0c128a..4c01cc7 100644 --- a/internal/models/common/room_event.go +++ b/internal/models/events/room_event.go @@ -1,21 +1,30 @@ -package common +package events -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. -} +import ( + "encoding/json" +) type RoomEvent struct { // 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. - 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' + 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. + 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. 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"` // 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 { @@ -48,3 +57,9 @@ type InvitedRoom struct { type InviteState struct { 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. +} diff --git a/internal/models/rooms/rooms.go b/internal/models/rooms/rooms.go index 925bee2..90d6127 100644 --- a/internal/models/rooms/rooms.go +++ b/internal/models/rooms/rooms.go @@ -1,11 +1,5 @@ package rooms -import ( - "encoding/json" - - "github.com/signaller-matrix/signaller/internal/models/common" -) - type JoinRule string const ( @@ -14,20 +8,3 @@ const ( Invite = "invite" 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. -} diff --git a/internal/models/sync/reply.go b/internal/models/sync/reply.go index 6331271..e533871 100644 --- a/internal/models/sync/reply.go +++ b/internal/models/sync/reply.go @@ -1,21 +1,21 @@ package sync import ( - common "github.com/signaller-matrix/signaller/internal/models/common" + "github.com/signaller-matrix/signaller/internal/models/events" ) type SyncReply struct { 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. - Presence common.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. - ToDevice common.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. + Presence events.Presence `json:"presence"` // The updates to the presence status of other users. + AccountData events.AccountData `json:"account_data"` // The global private data created by this user. + 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 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. } type RoomsSyncReply struct { - Join map[string]common.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. - Leave map[string]common.LeftRoom `json:"leave"` // The rooms that the user has left or been banned from. + Join map[string]events.JoinedRoom `json:"join"` // The rooms that the user has joined. + Invite map[string]events.InvitedRoom `json:"invite"` // The rooms that the user has been invited to. + Leave map[string]events.LeftRoom `json:"leave"` // The rooms that the user has left or been banned from. }