mirror of
https://github.com/ergochat/ergo.git
synced 2025-12-20 02:00:11 -08:00
tidy up a bit
This commit is contained in:
parent
db4b23bb48
commit
6d94aa1591
10 changed files with 112 additions and 67 deletions
|
|
@ -238,7 +238,7 @@ CAPDEFS = [
|
|||
standard="Soju/Goguma vendor",
|
||||
),
|
||||
CapDef(
|
||||
identifier="MetadataTwoJudgementDay",
|
||||
identifier="Metadata",
|
||||
name="draft/metadata-2",
|
||||
url="https://ircv3.net/specs/extensions/metadata",
|
||||
standard="draft IRCv3",
|
||||
|
|
|
|||
|
|
@ -65,9 +65,9 @@ const (
|
|||
// https://github.com/progval/ircv3-specifications/blob/redaction/extensions/message-redaction.md
|
||||
MessageRedaction Capability = iota
|
||||
|
||||
// MetadataTwoJudgementDay is the draft IRCv3 capability named "draft/metadata-2":
|
||||
// Metadata is the draft IRCv3 capability named "draft/metadata-2":
|
||||
// https://ircv3.net/specs/extensions/metadata
|
||||
MetadataTwoJudgementDay Capability = iota
|
||||
Metadata Capability = iota
|
||||
|
||||
// Multiline is the proposed IRCv3 capability named "draft/multiline":
|
||||
// https://github.com/ircv3/ircv3-specifications/pull/398
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ type Channel struct {
|
|||
dirtyBits uint
|
||||
settings ChannelSettings
|
||||
uuid utils.UUID
|
||||
metadata MetadataStore
|
||||
metadata map[string]string
|
||||
// these caches are paired to allow iteration over channel members without holding the lock
|
||||
membersCache []*Client
|
||||
memberDataCache []*memberData
|
||||
|
|
@ -895,6 +895,10 @@ func (channel *Channel) Join(client *Client, key string, isSajoin bool, rb *Resp
|
|||
rb.Add(nil, client.server.name, "MARKREAD", chname, client.GetReadMarker(chcfname))
|
||||
}
|
||||
|
||||
if rb.session.capabilities.Has(caps.Metadata) {
|
||||
syncChannelMetadata(client.server, rb, channel)
|
||||
}
|
||||
|
||||
if rb.session.client == client {
|
||||
// don't send topic and names for a SAJOIN of a different client
|
||||
channel.SendTopic(client, rb, false)
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ type RegisteredChannel struct {
|
|||
// Settings are the chanserv-modifiable settings
|
||||
Settings ChannelSettings
|
||||
// Metadata set using the METADATA command
|
||||
Metadata MetadataStore
|
||||
Metadata map[string]string
|
||||
}
|
||||
|
||||
func (r *RegisteredChannel) Serialize() ([]byte, error) {
|
||||
|
|
|
|||
|
|
@ -131,7 +131,7 @@ type Client struct {
|
|||
clearablePushMessages map[string]time.Time
|
||||
pushSubscriptionsExist atomic.Uint32 // this is a cache on len(pushSubscriptions) != 0
|
||||
pushQueue pushQueue
|
||||
metadata MetadataStore
|
||||
metadata map[string]string
|
||||
}
|
||||
|
||||
type saslStatus struct {
|
||||
|
|
@ -216,7 +216,7 @@ type Session struct {
|
|||
|
||||
webPushEndpoint string // goroutine-local: web push endpoint registered by the current session
|
||||
|
||||
metadataSubscriptions []string
|
||||
metadataSubscriptions utils.HashSet[string]
|
||||
}
|
||||
|
||||
// MultilineBatch tracks the state of a client-to-server multiline batch.
|
||||
|
|
|
|||
|
|
@ -1646,7 +1646,7 @@ func LoadConfig(filename string) (config *Config, err error) {
|
|||
}
|
||||
|
||||
if !config.Metadata.Enabled {
|
||||
config.Server.supportedCaps.Disable(caps.MetadataTwoJudgementDay)
|
||||
config.Server.supportedCaps.Disable(caps.Metadata)
|
||||
} else {
|
||||
var metadataValues []string
|
||||
if config.Metadata.MaxSubs >= 0 {
|
||||
|
|
@ -1659,7 +1659,7 @@ func LoadConfig(filename string) (config *Config, err error) {
|
|||
metadataValues = append(metadataValues, fmt.Sprintf("max-value-bytes=%d", config.Metadata.MaxValueBytes))
|
||||
}
|
||||
if len(metadataValues) != 0 {
|
||||
config.Server.capValues[caps.MetadataTwoJudgementDay] = strings.Join(metadataValues, ",")
|
||||
config.Server.capValues[caps.Metadata] = strings.Join(metadataValues, ",")
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -833,24 +833,32 @@ func (session *Session) isSubscribedTo(key string) bool {
|
|||
session.client.stateMutex.RLock()
|
||||
defer session.client.stateMutex.RUnlock()
|
||||
|
||||
return slices.Contains(session.metadataSubscriptions, key)
|
||||
if session.metadataSubscriptions == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return session.metadataSubscriptions.Has(key)
|
||||
}
|
||||
|
||||
func (session *Session) SubscribeTo(keys ...string) ([]string, error) {
|
||||
session.client.stateMutex.Lock()
|
||||
defer session.client.stateMutex.Unlock()
|
||||
|
||||
if session.metadataSubscriptions == nil {
|
||||
session.metadataSubscriptions = make(utils.HashSet[string])
|
||||
}
|
||||
|
||||
var added []string
|
||||
|
||||
maxSubs := session.client.server.Config().Metadata.MaxSubs
|
||||
|
||||
for _, k := range keys {
|
||||
if !slices.Contains(session.metadataSubscriptions, k) {
|
||||
if !session.metadataSubscriptions.Has(k) {
|
||||
if len(session.metadataSubscriptions) > maxSubs {
|
||||
return added, errMetadataTooManySubs
|
||||
}
|
||||
added = append(added, k)
|
||||
session.metadataSubscriptions = append(session.metadataSubscriptions, k)
|
||||
session.metadataSubscriptions.Add(k)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -863,27 +871,25 @@ func (session *Session) UnsubscribeFrom(keys ...string) []string {
|
|||
|
||||
var removed []string
|
||||
|
||||
new := slices.DeleteFunc(session.metadataSubscriptions,
|
||||
func(keyName string) bool {
|
||||
if slices.Contains(keys, keyName) {
|
||||
removed = append(removed, keyName)
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
if session.metadataSubscriptions == nil {
|
||||
return []string{}
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
session.metadataSubscriptions = new
|
||||
for k := range session.metadataSubscriptions {
|
||||
if slices.Contains(keys, k) {
|
||||
removed = append(removed, k)
|
||||
session.metadataSubscriptions.Remove(k)
|
||||
}
|
||||
}
|
||||
|
||||
return removed
|
||||
}
|
||||
|
||||
func (session *Session) MetadataSubscriptions() []string {
|
||||
func (session *Session) MetadataSubscriptions() utils.HashSet[string] {
|
||||
session.client.stateMutex.Lock()
|
||||
defer session.client.stateMutex.Unlock()
|
||||
|
||||
return slices.Clone(session.metadataSubscriptions)
|
||||
return maps.Clone(session.metadataSubscriptions)
|
||||
}
|
||||
|
||||
func (channel *Channel) GetMetadata(key string) (string, error) {
|
||||
|
|
@ -901,7 +907,7 @@ func (channel *Channel) SetMetadata(key string, value string) {
|
|||
channel.stateMutex.Lock()
|
||||
|
||||
if channel.metadata == nil {
|
||||
channel.metadata = make(MetadataStore)
|
||||
channel.metadata = make(map[string]string)
|
||||
}
|
||||
|
||||
channel.metadata[key] = value
|
||||
|
|
@ -909,7 +915,7 @@ func (channel *Channel) SetMetadata(key string, value string) {
|
|||
channel.MarkDirty(IncludeAllAttrs)
|
||||
}
|
||||
|
||||
func (channel *Channel) ListMetadata() MetadataStore {
|
||||
func (channel *Channel) ListMetadata() map[string]string {
|
||||
channel.stateMutex.RLock()
|
||||
defer channel.stateMutex.RUnlock()
|
||||
|
||||
|
|
@ -924,11 +930,11 @@ func (channel *Channel) DeleteMetadata(key string) {
|
|||
channel.MarkDirty(IncludeAllAttrs)
|
||||
}
|
||||
|
||||
func (channel *Channel) ClearMetadata() MetadataStore {
|
||||
func (channel *Channel) ClearMetadata() map[string]string {
|
||||
channel.stateMutex.Lock()
|
||||
|
||||
oldMap := channel.metadata
|
||||
channel.metadata = make(MetadataStore)
|
||||
channel.metadata = make(map[string]string)
|
||||
|
||||
channel.stateMutex.Unlock()
|
||||
channel.MarkDirty(IncludeAllAttrs)
|
||||
|
|
@ -962,15 +968,13 @@ func (client *Client) SetMetadata(key string, value string) {
|
|||
defer client.stateMutex.Unlock()
|
||||
|
||||
if client.metadata == nil {
|
||||
client.metadata = make(MetadataStore)
|
||||
client.metadata = make(map[string]string)
|
||||
}
|
||||
|
||||
client.metadata[key] = value
|
||||
|
||||
// coming soon: https://www.youtube.com/watch?v=K14JkFfWUzc
|
||||
}
|
||||
|
||||
func (client *Client) ListMetadata() MetadataStore {
|
||||
func (client *Client) ListMetadata() map[string]string {
|
||||
client.stateMutex.RLock()
|
||||
defer client.stateMutex.RUnlock()
|
||||
|
||||
|
|
@ -984,12 +988,12 @@ func (client *Client) DeleteMetadata(key string) {
|
|||
delete(client.metadata, key)
|
||||
}
|
||||
|
||||
func (client *Client) ClearMetadata() MetadataStore {
|
||||
func (client *Client) ClearMetadata() map[string]string {
|
||||
client.stateMutex.Lock()
|
||||
defer client.stateMutex.Unlock()
|
||||
|
||||
oldMap := client.metadata
|
||||
client.metadata = make(MetadataStore)
|
||||
client.metadata = make(map[string]string)
|
||||
|
||||
return oldMap
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,11 +9,13 @@ package irc
|
|||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"maps"
|
||||
"net"
|
||||
"os"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
"runtime/pprof"
|
||||
"slices"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
|
@ -3248,10 +3250,10 @@ func metadataHandler(server *Server, client *Client, msg ircmsg.Message, rb *Res
|
|||
|
||||
lineLength := MaxLineLen - len(server.name) - len(RPL_METADATASUBOK) - len(client.Nick()) - 10
|
||||
|
||||
chunked := utils.ChunkifyParams(added, lineLength)
|
||||
chunked := utils.ChunkifyParams(slices.Values(added), lineLength)
|
||||
for _, line := range chunked {
|
||||
params := append([]string{client.Nick()}, line...)
|
||||
rb.Add(nil, server.name, RPL_METADATASUBS, params...)
|
||||
rb.Add(nil, server.name, RPL_METADATASUBOK, params...)
|
||||
}
|
||||
|
||||
case "unsub":
|
||||
|
|
@ -3260,42 +3262,29 @@ func metadataHandler(server *Server, client *Client, msg ircmsg.Message, rb *Res
|
|||
removed := rb.session.UnsubscribeFrom(keys...)
|
||||
|
||||
lineLength := MaxLineLen - len(server.name) - len(RPL_METADATAUNSUBOK) - len(client.Nick()) - 10
|
||||
chunked := utils.ChunkifyParams(removed, lineLength)
|
||||
chunked := utils.ChunkifyParams(slices.Values(removed), lineLength)
|
||||
for _, line := range chunked {
|
||||
params := append([]string{client.Nick()}, line...)
|
||||
rb.Add(nil, server.name, RPL_METADATASUBS, params...)
|
||||
rb.Add(nil, server.name, RPL_METADATAUNSUBOK, params...)
|
||||
}
|
||||
|
||||
case "subs":
|
||||
lineLength := MaxLineLen - len(server.name) - len(RPL_METADATASUBS) - len(client.Nick()) - 10 // for safety
|
||||
|
||||
chunked := utils.ChunkifyParams(rb.session.MetadataSubscriptions(), lineLength)
|
||||
subs := rb.session.MetadataSubscriptions()
|
||||
|
||||
chunked := utils.ChunkifyParams(maps.Keys(subs), lineLength)
|
||||
for _, line := range chunked {
|
||||
params := append([]string{client.Nick()}, line...)
|
||||
rb.Add(nil, server.name, RPL_METADATASUBS, params...)
|
||||
}
|
||||
|
||||
case "sync":
|
||||
batchId := rb.StartNestedBatch("metadata")
|
||||
defer rb.EndNestedBatch(batchId)
|
||||
|
||||
values := t.ListMetadata()
|
||||
for k, v := range values {
|
||||
if rb.session.isSubscribedTo(k) {
|
||||
visibility := "*"
|
||||
rb.Add(nil, server.name, "METADATA", target, k, visibility, v)
|
||||
}
|
||||
}
|
||||
if targetChannel != nil {
|
||||
for _, client := range targetChannel.Members() {
|
||||
values := client.ListMetadata()
|
||||
for k, v := range values {
|
||||
if rb.session.isSubscribedTo(k) {
|
||||
visibility := "*"
|
||||
rb.Add(nil, server.name, "METADATA", client.Nick(), k, visibility, v)
|
||||
}
|
||||
}
|
||||
syncChannelMetadata(server, rb, targetChannel)
|
||||
}
|
||||
if targetClient != nil {
|
||||
syncClientMetadata(server, rb, targetClient)
|
||||
}
|
||||
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -15,14 +15,12 @@ var (
|
|||
errMetadataNotFound = errors.New("key not found")
|
||||
)
|
||||
|
||||
type MetadataStore = map[string]string
|
||||
|
||||
type MetadataHaver = interface {
|
||||
SetMetadata(key string, value string)
|
||||
GetMetadata(key string) (string, error)
|
||||
DeleteMetadata(key string)
|
||||
ListMetadata() MetadataStore
|
||||
ClearMetadata() MetadataStore
|
||||
ListMetadata() map[string]string
|
||||
ClearMetadata() map[string]string
|
||||
CountMetadata() int
|
||||
}
|
||||
|
||||
|
|
@ -32,7 +30,7 @@ func notifySubscribers(server *Server, session *Session, target string, key stri
|
|||
targetClient := server.clients.Get(target)
|
||||
|
||||
if targetClient != nil {
|
||||
notify = targetClient.FriendsMonitors(caps.MetadataTwoJudgementDay)
|
||||
notify = targetClient.FriendsMonitors(caps.Metadata)
|
||||
// notify clients about changes regarding themselves
|
||||
for _, s := range targetClient.Sessions() {
|
||||
notify.Add(s)
|
||||
|
|
@ -42,7 +40,7 @@ func notifySubscribers(server *Server, session *Session, target string, key stri
|
|||
members := targetChannel.Members()
|
||||
for _, m := range members {
|
||||
for _, s := range m.Sessions() {
|
||||
if s.capabilities.Has(caps.MetadataTwoJudgementDay) {
|
||||
if s.capabilities.Has(caps.Metadata) {
|
||||
notify.Add(s)
|
||||
}
|
||||
}
|
||||
|
|
@ -65,6 +63,50 @@ func notifySubscribers(server *Server, session *Session, target string, key stri
|
|||
}
|
||||
}
|
||||
|
||||
func syncClientMetadata(server *Server, rb *ResponseBuffer, target *Client) {
|
||||
if len(rb.session.MetadataSubscriptions()) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
batchId := rb.StartNestedBatch("metadata")
|
||||
defer rb.EndNestedBatch(batchId)
|
||||
|
||||
values := target.ListMetadata()
|
||||
for k, v := range values {
|
||||
if rb.session.isSubscribedTo(k) {
|
||||
visibility := "*"
|
||||
rb.Add(nil, server.name, "METADATA", target.Nick(), k, visibility, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func syncChannelMetadata(server *Server, rb *ResponseBuffer, target *Channel) {
|
||||
if len(rb.session.MetadataSubscriptions()) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
batchId := rb.StartNestedBatch("metadata")
|
||||
defer rb.EndNestedBatch(batchId)
|
||||
|
||||
values := target.ListMetadata()
|
||||
for k, v := range values {
|
||||
if rb.session.isSubscribedTo(k) {
|
||||
visibility := "*"
|
||||
rb.Add(nil, server.name, "METADATA", target.Name(), k, visibility, v)
|
||||
}
|
||||
}
|
||||
|
||||
for _, client := range target.Members() {
|
||||
values := client.ListMetadata()
|
||||
for k, v := range values {
|
||||
if rb.session.isSubscribedTo(k) {
|
||||
visibility := "*"
|
||||
rb.Add(nil, server.name, "METADATA", client.Nick(), k, visibility, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var metadataEvilCharsRegexp = regexp.MustCompile("[^A-Za-z0-9_./:-]+")
|
||||
|
||||
func metadataKeyIsEvil(key string) bool {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
package utils
|
||||
|
||||
func ChunkifyParams(params []string, maxChars int) [][]string {
|
||||
import "iter"
|
||||
|
||||
func ChunkifyParams(params iter.Seq[string], maxChars int) [][]string {
|
||||
var chunked [][]string
|
||||
|
||||
var acc []string
|
||||
var length = 0
|
||||
|
||||
for _, p := range params {
|
||||
for p := range params {
|
||||
length = length + len(p) + 1 // (accounting for the space)
|
||||
|
||||
if length > maxChars {
|
||||
|
|
@ -18,5 +20,9 @@ func ChunkifyParams(params []string, maxChars int) [][]string {
|
|||
acc = append(acc, p)
|
||||
}
|
||||
|
||||
if len(acc) != 0 {
|
||||
chunked = append(chunked, acc)
|
||||
}
|
||||
|
||||
return chunked
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue