dendrite/cmd/dendrite-demo-libp2p/publicrooms.go

154 lines
4.3 KiB
Go

// Copyright 2020 The Matrix.org Foundation C.I.C.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"context"
"encoding/json"
"fmt"
"sync"
"sync/atomic"
"time"
roomserverAPI "github.com/matrix-org/dendrite/roomserver/api"
pubsub "github.com/libp2p/go-libp2p-pubsub"
"github.com/matrix-org/gomatrixserverlib"
"github.com/matrix-org/util"
)
const MaintenanceInterval = time.Second * 10
type discoveredRoom struct {
time time.Time
room gomatrixserverlib.PublicRoom
}
type publicRoomsProvider struct {
pubsub *pubsub.PubSub
topic *pubsub.Topic
subscription *pubsub.Subscription
foundRooms map[string]discoveredRoom // additional rooms we have learned about from the DHT
foundRoomsMutex sync.RWMutex // protects foundRooms
maintenanceTimer *time.Timer //
roomsAdvertised atomic.Value // stores int
rsAPI roomserverAPI.RoomserverInternalAPI
}
func newPublicRoomsProvider(ps *pubsub.PubSub, rsAPI roomserverAPI.RoomserverInternalAPI) *publicRoomsProvider {
return &publicRoomsProvider{
foundRooms: make(map[string]discoveredRoom),
pubsub: ps,
rsAPI: rsAPI,
}
}
func (p *publicRoomsProvider) Start() error {
if topic, err := p.pubsub.Join("/matrix/publicRooms"); err != nil {
return err
} else if sub, err := topic.Subscribe(); err == nil {
p.topic = topic
p.subscription = sub
go p.MaintenanceTimer()
go p.FindRooms()
p.roomsAdvertised.Store(0)
} else {
return err
}
return nil
}
func (p *publicRoomsProvider) MaintenanceTimer() {
if p.maintenanceTimer != nil && !p.maintenanceTimer.Stop() {
<-p.maintenanceTimer.C
}
p.Interval()
}
func (p *publicRoomsProvider) Interval() {
p.foundRoomsMutex.Lock()
for k, v := range p.foundRooms {
if time.Since(v.time) > time.Minute {
delete(p.foundRooms, k)
}
}
p.foundRoomsMutex.Unlock()
if err := p.AdvertiseRooms(); err != nil {
fmt.Println("Failed to advertise room in DHT:", err)
}
p.foundRoomsMutex.RLock()
defer p.foundRoomsMutex.RUnlock()
fmt.Println("Found", len(p.foundRooms), "room(s), advertised", p.roomsAdvertised.Load(), "room(s)")
p.maintenanceTimer = time.AfterFunc(MaintenanceInterval, p.Interval)
}
func (p *publicRoomsProvider) AdvertiseRooms() error {
ctx := context.Background()
var queryRes roomserverAPI.QueryPublishedRoomsResponse
// Query published rooms on our server. This will not invoke clientapi.ExtraPublicRoomsProvider
err := p.rsAPI.QueryPublishedRooms(ctx, &roomserverAPI.QueryPublishedRoomsRequest{}, &queryRes)
if err != nil {
util.GetLogger(ctx).WithError(err).Error("QueryPublishedRooms failed")
return err
}
ourRooms, err := roomserverAPI.PopulatePublicRooms(ctx, queryRes.RoomIDs, p.rsAPI)
if err != nil {
util.GetLogger(ctx).WithError(err).Error("PopulatePublicRooms failed")
return err
}
advertised := 0
for _, room := range ourRooms {
if j, err := json.Marshal(room); err == nil {
if err := p.topic.Publish(context.TODO(), j); err != nil {
fmt.Println("Failed to publish public room:", err)
} else {
advertised++
}
}
}
p.roomsAdvertised.Store(advertised)
return nil
}
func (p *publicRoomsProvider) FindRooms() {
for {
msg, err := p.subscription.Next(context.Background())
if err != nil {
continue
}
received := discoveredRoom{
time: time.Now(),
}
if err := json.Unmarshal(msg.Data, &received.room); err != nil {
fmt.Println("Unmarshal error:", err)
continue
}
fmt.Printf("received %+v \n", received)
p.foundRoomsMutex.Lock()
p.foundRooms[received.room.RoomID] = received
p.foundRoomsMutex.Unlock()
}
}
func (p *publicRoomsProvider) Rooms() (rooms []gomatrixserverlib.PublicRoom) {
p.foundRoomsMutex.RLock()
defer p.foundRoomsMutex.RUnlock()
for _, dr := range p.foundRooms {
rooms = append(rooms, dr.room)
}
return
}