clone

a self-hosted multitenant nostr relay with sites, file storage, git hosting, and inbox and outbox delivery

feat/tinyagent
tinyrelay / internal / records / rooms.go
rooms.go 3.2K
1package records23import (4 "context"5 "database/sql"6 "errors"7 "fmt"89 "github.com/FelineStateMachine/tinyrelay/internal/event"10)1112// PublishRoom emits the relay-signed metadata (39000), admins (39001) and13// members (39002) records for one room. A room that no longer exists has its14// records retired instead. The slug room is covered by PublishMembership.15func (s *Service) PublishRoom(ctx context.Context, roomID string, now int64) ([]event.Event, error) {16 var name, about, picture, access string17 var deleted int6418 err := s.store.DB().QueryRowContext(ctx, `SELECT name,about,picture,access,deleted_at FROM rooms WHERE id=?`, roomID).Scan(&name, &about, &picture, &access, &deleted)19 if errors.Is(err, sql.ErrNoRows) || (err == nil && deleted > 0) {20 return nil, s.RetireRoom(ctx, roomID)21 }22 if err != nil {23 return nil, fmt.Errorf("room record: %w", err)24 }25 rows, err := s.store.DB().QueryContext(ctx, `SELECT pubkey,role FROM room_members WHERE room_id=? ORDER BY CASE role WHEN 'owner' THEN 0 WHEN 'admin' THEN 1 ELSE 2 END,pubkey`, roomID)26 if err != nil {27 return nil, fmt.Errorf("room members: %w", err)28 }29 defer rows.Close()30 var admins, members [][]string31 for rows.Next() {32 var pk, role string33 if err := rows.Scan(&pk, &role); err != nil {34 return nil, err35 }36 members = append(members, []string{"p", pk})37 if role == "owner" || role == "admin" {38 admins = append(admins, []string{"p", pk, role})39 }40 }41 if err := rows.Err(); err != nil {42 return nil, err43 }44 p := s.policy()45 if access == "open" && !p.DirectoryPublic {46 members = nil47 }48 meta := [][]string{{"-"}, {"d", roomID}, {"name", name}, {"about", about}}49 if picture != "" {50 meta = append(meta, []string{"picture", picture})51 }52 if access == "members" {53 meta = append(meta, []string{"closed"})54 } else {55 meta = append(meta, []string{"open"})56 }57 if p.Reads == "members" {58 meta = append(meta, []string{"private"})59 } else {60 meta = append(meta, []string{"public"})61 }62 vals := []struct {63 kind int64 tags [][]string65 }{66 {event.KIND_GROUP_METADATA, meta},67 {event.KIND_GROUP_ADMINS, append([][]string{{"-"}, {"d", roomID}}, admins...)},68 {event.KIND_GROUP_MEMBERS, append([][]string{{"-"}, {"d", roomID}}, members...)},69 }70 out := make([]event.Event, 0, len(vals))71 for _, v := range vals {72 e, err := s.signed(ctx, v.kind, v.tags, "", now)73 if err != nil {74 return nil, err75 }76 out = append(out, e)77 }78 return out, nil79}8081// RoomNotice emits the relay-signed member notice a Buzz-compatible client82// shows when someone is added to (44100) or removed from (44101) a room.83func (s *Service) RoomNotice(ctx context.Context, roomID, pubkey string, added bool, now int64) (event.Event, error) {84 kind := event.KIND_ROOM_MEMBER_REMOVED85 if added {86 kind = event.KIND_ROOM_MEMBER_ADDED87 }88 return s.signed(ctx, kind, [][]string{{"h", roomID}, {"p", pubkey}}, "", now)89}9091// RetireRoom removes the relay's own records for a room that was deleted.92func (s *Service) RetireRoom(ctx context.Context, roomID string) error {93 _, err := s.store.DB().ExecContext(ctx, `DELETE FROM events WHERE pubkey=? AND kind IN (?,?,?) AND d=?`, s.PublicKey(), event.KIND_GROUP_METADATA, event.KIND_GROUP_ADMINS, event.KIND_GROUP_MEMBERS, roomID)94 return err95}