Add basic implementation of PublicRooms method

This commit is contained in:
nxshock 2019-08-02 22:55:35 +05:00
parent 823ab7e100
commit 782c328e3d
9 changed files with 173 additions and 13 deletions

View File

@ -113,7 +113,7 @@ Implemented from [Client-Server API](https://matrix.org/docs/spec/client_server/
- [x] [10.5.1 GET /_matrix/client/r0/directory/list/room/{roomId}](https://matrix.org/docs/spec/client_server/latest#get-matrix-client-r0-directory-list-room-roomid)
- [x] [10.5.2 PUT /_matrix/client/r0/directory/list/room/{roomId}](https://matrix.org/docs/spec/client_server/latest#put-matrix-client-r0-directory-list-room-roomid)
- [ ] [10.5.3 GET /_matrix/client/r0/publicRooms](https://matrix.org/docs/spec/client_server/latest#get-matrix-client-r0-publicrooms)
- [x] [10.5.3 GET /_matrix/client/r0/publicRooms](https://matrix.org/docs/spec/client_server/latest#get-matrix-client-r0-publicrooms)
- [ ] [10.5.4 POST /_matrix/client/r0/publicRooms](https://matrix.org/docs/spec/client_server/latest#post-matrix-client-r0-publicrooms)
## [11 User Data](https://matrix.org/docs/spec/client_server/latest#user-data)

View File

@ -15,6 +15,7 @@ type Backend interface {
GetUserByName(userName string) User
GetRoomByID(id string) Room
Sync(token string, request sync.SyncRequest) (response *sync.SyncReply, err *models.ApiError)
PublicRooms() []Room
}
type Room interface {
@ -26,6 +27,9 @@ type Room interface {
Topic() string
Events() []rooms.Event
Visibility() createroom.VisibilityType
WorldReadable() bool
GuestCanJoin() bool
AvatarURL() string
State() createroom.Preset
}

View File

@ -1,8 +1,11 @@
package memory
import (
"sort"
"sync"
"github.com/nxshock/signaller/internal/models/createroom"
"github.com/nxshock/signaller/internal"
"github.com/nxshock/signaller/internal/models"
mSync "github.com/nxshock/signaller/internal/models/sync"
@ -111,3 +114,20 @@ func (backend *Backend) GetUserByName(userName string) internal.User {
return nil
}
func (backend *Backend) PublicRooms() []internal.Room {
backend.mutex.Lock()
defer backend.mutex.Unlock()
var rooms []internal.Room
for _, room := range backend.rooms {
if room.State() == createroom.PublicChat {
rooms = append(rooms, room)
}
}
sort.Sort(BySize(rooms))
return rooms
}

View File

@ -15,6 +15,9 @@ type Room struct {
name string
topic string
state createroom.Preset
worldReadable bool
guestCanJoin bool
avatarURL string
creator internal.User
joined []internal.User
@ -93,3 +96,24 @@ func (room *Room) State() createroom.Preset {
return room.state
}
func (room *Room) WorldReadable() bool {
room.mutex.RLock()
defer room.mutex.RUnlock()
return room.worldReadable
}
func (room *Room) GuestCanJoin() bool {
room.mutex.RLock()
defer room.mutex.RUnlock()
return room.guestCanJoin
}
func (room *Room) AvatarURL() string {
room.mutex.RLock()
defer room.mutex.RUnlock()
return room.avatarURL
}

View File

@ -0,0 +1,11 @@
package memory
import (
"github.com/nxshock/signaller/internal"
)
type BySize []internal.Room
func (a BySize) Len() int { return len(a) }
func (a BySize) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a BySize) Less(i, j int) bool { return len(a[i].Users()) > len(a[j].Users()) }

View File

@ -8,21 +8,19 @@ import (
"strconv"
"strings"
"github.com/nxshock/signaller/internal/models/registeravailable"
"github.com/nxshock/signaller/internal/models/devices"
"github.com/gorilla/mux"
"github.com/nxshock/signaller/internal/models/common"
"github.com/nxshock/signaller/internal/models"
"github.com/nxshock/signaller/internal/models/capabilities"
"github.com/nxshock/signaller/internal/models/common"
"github.com/nxshock/signaller/internal/models/devices"
"github.com/nxshock/signaller/internal/models/joinedrooms"
"github.com/nxshock/signaller/internal/models/listroom"
login "github.com/nxshock/signaller/internal/models/login"
"github.com/nxshock/signaller/internal/models/password"
"github.com/nxshock/signaller/internal/models/publicrooms"
register "github.com/nxshock/signaller/internal/models/register"
"github.com/nxshock/signaller/internal/models/registeravailable"
mSync "github.com/nxshock/signaller/internal/models/sync"
"github.com/nxshock/signaller/internal/models/versions"
"github.com/nxshock/signaller/internal/models/whoami"
@ -412,6 +410,58 @@ func listRoomHandler(w http.ResponseWriter, r *http.Request) {
}
}
// https://matrix.org/docs/spec/client_server/latest#id335
func publicRoomsHandler(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
var request publicrooms.Request
err := getRequest(r, &request)
if err != nil {
errorResponse(w, models.M_BAD_JSON, http.StatusBadRequest, err.Error())
return
}
if request.Limit <= 0 {
request.Limit = 50 // TODO: move to const
}
chunks := roomsToPublicRoomsChunks(currServer.Backend.PublicRooms())
var response publicrooms.Response
// TODO: test and rewrite this code
if request.Since == "" {
if request.Limit >= len(chunks) {
response.Chunk = chunks
// TODO: should fill response.TotalRoomCountEstimate?
} else {
response.Chunk = chunks[:request.Limit]
response.NextBatch = strconv.Itoa(request.Limit + 1)
response.TotalRoomCountEstimate = len(chunks)
}
} else {
response.PrevBatch = "0"
from, err := strconv.Atoi(request.Since)
if err != nil {
errorResponse(w, models.M_INVALID_PARAM, http.StatusBadRequest, "Wrong Since field specified") // TODO: check code
return
}
if len(chunks) >= from {
if request.Limit >= len(chunks) {
response.Chunk = chunks[from : from+request.Limit]
} else {
response.Chunk = chunks[from:]
}
}
}
sendJsonResponse(w, http.StatusOK, response)
case http.MethodPost: // TODO: implement
default:
errorResponse(w, models.M_UNKNOWN, http.StatusBadRequest, "wrong method: "+r.Method)
}
}
func sendJsonResponse(w http.ResponseWriter, httpStatus int, data interface{}) error {
b, err := json.Marshal(data)
if err != nil {

View File

@ -0,0 +1,26 @@
package publicrooms
type Request struct {
Limit int `json:"limit"` // Limit the number of results returned.
Since string `json:"since"` // A pagination token from a previous request, allowing clients to get the next (or previous) batch of rooms. The direction of pagination is specified solely by which token is supplied, rather than via an explicit flag.
Server string `json:"server"` // The server to fetch the public room lists from. Defaults to the local server.
}
type Response struct {
Chunk []PublicRoomsChunk `json:"chunk"` // A paginated chunk of public rooms.
NextBatch string `json:"next_batch,omitempty"` // A pagination token for the response. The absence of this token means there are no more results to fetch and the client should stop paginating.
PrevBatch string `json:"prev_batch,omitempty"` // A pagination token that allows fetching previous results. The absence of this token means there are no results before this batch, i.e. this is the first batch.
TotalRoomCountEstimate int `json:"total_room_count_estimate,omitempty"` // An estimate on the total number of public rooms, if the server has an estimate.
}
type PublicRoomsChunk struct {
Aliases []string `json:"aliases,omitempty"` // Aliases of the room. May be empty.
CanonicalAlias string `json:"canonical_alias,omitempty"` // The canonical alias of the room, if any.
Name string `json:"name,omitempty"` // The name of the room, if any.
NumJoinedMembers int `json:"num_joined_members"` // Required. The number of members joined to the room.
RoomID string `json:"room_id"` // Required. The ID of the room.
Topic string `json:"topic,omitempty"` // The topic of the room, if any.
WorldReadable bool `json:"world_readable"` // Required. Whether the room may be viewed by guest users without joining.
GuestCanJoin bool `json:"guest_can_join"` // Required. Whether guest users may join the room and participate in it. If they can, they will be subject to ordinary power level rules like any other user.
AvatarURL string `json:"avatar_url,omitempty"` // The URL for the room's avatar, if one is set.
}

View File

@ -39,6 +39,7 @@ func NewServer(port int) (*Server, error) {
router.HandleFunc("/_matrix/client/r0/directory/list/room/{roomID}", listRoomHandler)
router.HandleFunc("/_matrix/client/r0/rooms/{roomId}/leave", leaveRoomHandler)
router.HandleFunc("/_matrix/client/r0/register/available", registerAvailableHandler)
router.HandleFunc("/_matrix/client/r0/publicRooms", publicRoomsHandler)
router.HandleFunc("/", RootHandler)
if port <= 0 || port > 65535 {

24
internal/utils.go Normal file
View File

@ -0,0 +1,24 @@
package internal
import "github.com/nxshock/signaller/internal/models/publicrooms"
func roomsToPublicRoomsChunks(rooms []Room) []publicrooms.PublicRoomsChunk {
var chunks []publicrooms.PublicRoomsChunk
for _, room := range rooms {
chunk := publicrooms.PublicRoomsChunk{
// TODO: Aliases:
CanonicalAlias: room.AliasName(),
Name: room.Name(),
NumJoinedMembers: len(room.Users()),
RoomID: room.ID(),
Topic: room.Topic(),
WorldReadable: room.WorldReadable(),
GuestCanJoin: room.GuestCanJoin(),
AvatarURL: room.AvatarURL()}
chunks = append(chunks, chunk)
}
return chunks
}