add groups.0xchat.com

This commit is contained in:
water783 2024-07-11 14:28:27 +08:00
parent 0c6725cbea
commit 49c2dc52a7
4 changed files with 146 additions and 3 deletions

View File

@ -193,6 +193,22 @@ func (s *State) applyModerationAction(ctx context.Context, event *nostr.Event) {
group.ToMembersEvent,
},
}[event.Kind] {
switch event.Kind {
case nostr.KindSimpleGroupCreateGroup:
group.LastMetadataUpdate = nostr.Now()
group.LastAdminsUpdate = nostr.Now()
group.LastMembersUpdate = nostr.Now()
case nostr.KindSimpleGroupEditMetadata, nostr.KindSimpleGroupEditGroupStatus:
group.LastMetadataUpdate = nostr.Now()
case nostr.KindSimpleGroupAddPermission:
group.LastMembersUpdate = nostr.Now()
group.LastAdminsUpdate = nostr.Now()
case nostr.KindSimpleGroupRemovePermission:
group.LastAdminsUpdate = nostr.Now()
case nostr.KindSimpleGroupAddUser, nostr.KindSimpleGroupRemoveUser:
group.LastMembersUpdate = nostr.Now()
}
evt := toBroadcast()
evt.Sign(s.privateKey)
s.Relay.BroadcastEvent(evt)

View File

@ -0,0 +1,40 @@
package main
import (
"context"
"time"
"github.com/fiatjaf/relay29"
"github.com/nbd-wtf/go-nostr"
"github.com/puzpuzpuz/xsync/v3"
"golang.org/x/time/rate"
)
var internalCallContextKey = struct{}{}
func blockDeletesOfOldMessages(ctx context.Context, target, deletion *nostr.Event) (acceptDeletion bool, msg string) {
if target.CreatedAt < nostr.Now()-60*60*2 /* 2 hours */ {
return false, "can't delete old event, contact relay admin"
}
return true, ""
}
// very strict rate limits
var rateLimitBuckets = xsync.NewMapOf[*relay29.Group, *rate.Limiter]()
func rateLimit(ctx context.Context, event *nostr.Event) (reject bool, msg string) {
group := state.GetGroupFromEvent(event)
bucket, _ := rateLimitBuckets.LoadOrCompute(group, func() *rate.Limiter {
return rate.NewLimiter(rate.Every(time.Minute*2), 30)
})
if rsv := bucket.Reserve(); rsv.Delay() != 0 {
rsv.Cancel()
return true, "rate-limited"
} else {
rsv.OK()
return
}
}

86
groups.0xchat.com/main.go Normal file
View File

@ -0,0 +1,86 @@
package main
import (
"net/http"
"os"
"slices"
"github.com/fiatjaf/eventstore/bolt"
"github.com/fiatjaf/khatru/policies"
"github.com/fiatjaf/relay29"
"github.com/kelseyhightower/envconfig"
"github.com/nbd-wtf/go-nostr"
"github.com/rs/zerolog"
)
type Settings struct {
Port string `envconfig:"PORT" default:"5577"`
Domain string `envconfig:"DOMAIN" required:"true"`
RelayName string `envconfig:"RELAY_NAME" required:"true"`
RelayPrivkey string `envconfig:"RELAY_PRIVKEY" required:"true"`
RelayDescription string `envconfig:"RELAY_DESCRIPTION"`
RelayContact string `envconfig:"RELAY_CONTACT"`
RelayIcon string `envconfig:"RELAY_ICON"`
DatabasePath string `envconfig:"DATABASE_PATH" default:"./db"`
RelayPubkey string `envconfig:"-"`
}
var (
s Settings
db = &bolt.BoltBackend{}
log = zerolog.New(os.Stderr).Output(zerolog.ConsoleWriter{Out: os.Stdout}).With().Timestamp().Logger()
state *relay29.State
)
func main() {
err := envconfig.Process("", &s)
if err != nil {
log.Fatal().Err(err).Msg("couldn't process envconfig")
return
}
s.RelayPubkey, _ = nostr.GetPublicKey(s.RelayPrivkey)
// load db
db.Path = s.DatabasePath
if err := db.Init(); err != nil {
log.Fatal().Err(err).Msg("failed to initialize database")
return
}
log.Debug().Str("path", db.Path).Msg("initialized database")
// init relay29 stuff
state = relay29.Init(relay29.Options{
Domain: s.Domain,
DB: db,
SecretKey: s.RelayPrivkey,
})
// init relay
state.Relay.Info.Name = s.RelayName
state.Relay.Info.Description = s.RelayDescription
state.Relay.Info.Contact = s.RelayContact
state.Relay.Info.Icon = s.RelayIcon
state.Relay.OverwriteDeletionOutcome = append(state.Relay.OverwriteDeletionOutcome,
blockDeletesOfOldMessages,
)
state.Relay.RejectEvent = slices.Insert(state.Relay.RejectEvent, 0,
policies.PreventLargeTags(64),
policies.PreventTooManyIndexableTags(6, []int{9005}, nil),
policies.RestrictToSpecifiedKinds(
7, 9, 10, 11, 12,
30023, 31922, 31923, 9802,
9000, 9001, 9002, 9003, 9004, 9005, 9006, 9007,
9021, 9735,
),
policies.PreventTimestampsInThePast(60),
policies.PreventTimestampsInTheFuture(30),
rateLimit,
)
log.Info().Str("relay-pubkey", s.RelayPubkey).Msg("running on http://0.0.0.0:" + s.Port)
if err := http.ListenAndServe(":"+s.Port, state.Relay); err != nil {
log.Fatal().Err(err).Msg("failed to serve")
}
}

View File

@ -96,7 +96,7 @@ func (s *State) NewGroup(id string) *Group {
Group: nip29.Group{
Address: nip29.GroupAddress{
ID: id,
Relay: "wss://" + s.Domain,
Relay: "ws://" + s.Domain,
},
Members: map[string]*nip29.Role{},
},
@ -112,11 +112,12 @@ func (s *State) loadGroups(ctx context.Context) {
group := s.NewGroup(id)
f := nostr.Filter{
Limit: 5000, Kinds: nip29.ModerationEventKinds, Tags: nostr.TagMap{"h": []string{id}},
Kinds: nip29.ModerationEventKinds,
Tags: nostr.TagMap{"h": []string{id}},
}
ch, _ := s.DB.QueryEvents(ctx, f)
events := make([]*nostr.Event, 0, 5000)
events := make([]*nostr.Event, 0)
for event := range ch {
events = append(events, event)
}