forked from External/ergo
chanserv enhancements and miscellaneous fixes
* Fix #684 * Fix #683 * Add `CHANSERV CLEAR` * Allow mode changes from channel founders even when they aren't joined * Operators with the chanreg capability are exempt from max-channels-per-account * Small fixes and cleanup
This commit is contained in:
parent
62473468f0
commit
07865b8f63
11 changed files with 566 additions and 57 deletions
|
|
@ -32,6 +32,8 @@ const (
|
|||
keyChannelPassword = "channel.key %s"
|
||||
keyChannelModes = "channel.modes %s"
|
||||
keyChannelAccountToUMode = "channel.accounttoumode %s"
|
||||
|
||||
keyChannelPurged = "channel.purged %s"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -96,6 +98,12 @@ type RegisteredChannel struct {
|
|||
Invites map[string]MaskInfo
|
||||
}
|
||||
|
||||
type ChannelPurgeRecord struct {
|
||||
Oper string
|
||||
PurgedAt time.Time
|
||||
Reason string
|
||||
}
|
||||
|
||||
// ChannelRegistry manages registered channels.
|
||||
type ChannelRegistry struct {
|
||||
server *Server
|
||||
|
|
@ -124,6 +132,24 @@ func (reg *ChannelRegistry) AllChannels() (result map[string]bool) {
|
|||
return
|
||||
}
|
||||
|
||||
// PurgedChannels returns the set of all channel names that have been purged
|
||||
func (reg *ChannelRegistry) PurgedChannels() (result map[string]empty) {
|
||||
result = make(map[string]empty)
|
||||
|
||||
prefix := fmt.Sprintf(keyChannelPurged, "")
|
||||
reg.server.store.View(func(tx *buntdb.Tx) error {
|
||||
return tx.AscendGreaterOrEqual("", prefix, func(key, value string) bool {
|
||||
if !strings.HasPrefix(key, prefix) {
|
||||
return false
|
||||
}
|
||||
channel := strings.TrimPrefix(key, prefix)
|
||||
result[channel] = empty{}
|
||||
return true
|
||||
})
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// StoreChannel obtains a consistent view of a channel, then persists it to the store.
|
||||
func (reg *ChannelRegistry) StoreChannel(info RegisteredChannel, includeFlags uint) (err error) {
|
||||
if !reg.server.ChannelRegistrationEnabled() {
|
||||
|
|
@ -191,11 +217,11 @@ func (reg *ChannelRegistry) LoadChannel(nameCasefolded string) (info RegisteredC
|
|||
|
||||
info = RegisteredChannel{
|
||||
Name: name,
|
||||
RegisteredAt: time.Unix(regTimeInt, 0),
|
||||
RegisteredAt: time.Unix(regTimeInt, 0).UTC(),
|
||||
Founder: founder,
|
||||
Topic: topic,
|
||||
TopicSetBy: topicSetBy,
|
||||
TopicSetTime: time.Unix(topicSetTimeInt, 0),
|
||||
TopicSetTime: time.Unix(topicSetTimeInt, 0).UTC(),
|
||||
Key: password,
|
||||
Modes: modeSlice,
|
||||
Bans: banlist,
|
||||
|
|
@ -256,14 +282,12 @@ func (reg *ChannelRegistry) deleteChannel(tx *buntdb.Tx, key string, info Regist
|
|||
}
|
||||
}
|
||||
|
||||
// saveChannel saves a channel to the store.
|
||||
func (reg *ChannelRegistry) saveChannel(tx *buntdb.Tx, channelInfo RegisteredChannel, includeFlags uint) {
|
||||
func (reg *ChannelRegistry) updateAccountToChannelMapping(tx *buntdb.Tx, channelInfo RegisteredChannel) {
|
||||
channelKey := channelInfo.NameCasefolded
|
||||
// maintain the mapping of account -> registered channels
|
||||
chanExistsKey := fmt.Sprintf(keyChannelExists, channelKey)
|
||||
_, existsErr := tx.Get(chanExistsKey)
|
||||
if existsErr == buntdb.ErrNotFound {
|
||||
// this is a new registration, need to update account-to-channels
|
||||
chanFounderKey := fmt.Sprintf(keyChannelFounder, channelKey)
|
||||
founder, existsErr := tx.Get(chanFounderKey)
|
||||
if existsErr == buntdb.ErrNotFound || founder != channelInfo.Founder {
|
||||
// add to new founder's list
|
||||
accountChannelsKey := fmt.Sprintf(keyAccountChannels, channelInfo.Founder)
|
||||
alreadyChannels, _ := tx.Get(accountChannelsKey)
|
||||
newChannels := channelKey // this is the casefolded channel name
|
||||
|
|
@ -272,9 +296,30 @@ func (reg *ChannelRegistry) saveChannel(tx *buntdb.Tx, channelInfo RegisteredCha
|
|||
}
|
||||
tx.Set(accountChannelsKey, newChannels, nil)
|
||||
}
|
||||
if existsErr == nil && founder != channelInfo.Founder {
|
||||
// remove from old founder's list
|
||||
accountChannelsKey := fmt.Sprintf(keyAccountChannels, founder)
|
||||
alreadyChannelsRaw, _ := tx.Get(accountChannelsKey)
|
||||
var newChannels []string
|
||||
if alreadyChannelsRaw != "" {
|
||||
for _, chname := range strings.Split(alreadyChannelsRaw, ",") {
|
||||
if chname != channelInfo.NameCasefolded {
|
||||
newChannels = append(newChannels, chname)
|
||||
}
|
||||
}
|
||||
}
|
||||
tx.Set(accountChannelsKey, strings.Join(newChannels, ","), nil)
|
||||
}
|
||||
}
|
||||
|
||||
// saveChannel saves a channel to the store.
|
||||
func (reg *ChannelRegistry) saveChannel(tx *buntdb.Tx, channelInfo RegisteredChannel, includeFlags uint) {
|
||||
channelKey := channelInfo.NameCasefolded
|
||||
// maintain the mapping of account -> registered channels
|
||||
reg.updateAccountToChannelMapping(tx, channelInfo)
|
||||
|
||||
if includeFlags&IncludeInitial != 0 {
|
||||
tx.Set(chanExistsKey, "1", nil)
|
||||
tx.Set(fmt.Sprintf(keyChannelExists, channelKey), "1", nil)
|
||||
tx.Set(fmt.Sprintf(keyChannelName, channelKey), channelInfo.Name, nil)
|
||||
tx.Set(fmt.Sprintf(keyChannelRegTime, channelKey), strconv.FormatInt(channelInfo.RegisteredAt.Unix(), 10), nil)
|
||||
tx.Set(fmt.Sprintf(keyChannelFounder, channelKey), channelInfo.Founder, nil)
|
||||
|
|
@ -306,3 +351,48 @@ func (reg *ChannelRegistry) saveChannel(tx *buntdb.Tx, channelInfo RegisteredCha
|
|||
tx.Set(fmt.Sprintf(keyChannelAccountToUMode, channelKey), string(accountToUModeString), nil)
|
||||
}
|
||||
}
|
||||
|
||||
// PurgeChannel records a channel purge.
|
||||
func (reg *ChannelRegistry) PurgeChannel(chname string, record ChannelPurgeRecord) (err error) {
|
||||
serialized, err := json.Marshal(record)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
serializedStr := string(serialized)
|
||||
key := fmt.Sprintf(keyChannelPurged, chname)
|
||||
|
||||
return reg.server.store.Update(func(tx *buntdb.Tx) error {
|
||||
tx.Set(key, serializedStr, nil)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// LoadPurgeRecord retrieves information about whether and how a channel was purged.
|
||||
func (reg *ChannelRegistry) LoadPurgeRecord(chname string) (record ChannelPurgeRecord, err error) {
|
||||
var rawRecord string
|
||||
key := fmt.Sprintf(keyChannelPurged, chname)
|
||||
reg.server.store.View(func(tx *buntdb.Tx) error {
|
||||
rawRecord, _ = tx.Get(key)
|
||||
return nil
|
||||
})
|
||||
if rawRecord == "" {
|
||||
err = errNoSuchChannel
|
||||
return
|
||||
}
|
||||
err = json.Unmarshal([]byte(rawRecord), &record)
|
||||
if err != nil {
|
||||
reg.server.logger.Error("internal", "corrupt purge record", chname, err.Error())
|
||||
err = errNoSuchChannel
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// UnpurgeChannel deletes the record of a channel purge.
|
||||
func (reg *ChannelRegistry) UnpurgeChannel(chname string) (err error) {
|
||||
key := fmt.Sprintf(keyChannelPurged, chname)
|
||||
return reg.server.store.Update(func(tx *buntdb.Tx) error {
|
||||
tx.Delete(key)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue