Add customizable auto-detaching, auto-reattaching, relaying.
This uses the fields added previously to the Channel struct to implement the actual detaching/reattaching/relaying logic. The `FilterDefault` values of the messages filters are currently hardcoded. The values of the message filters are not currently user-settable. This introduces a new user event, eventChannelDetach, which stores an upstreamConn (which might become invalid at the time of processing), and a channel name, used for auto-detaching. Every time the channel detach timer is refreshed (by receveing a message, etc.), a new timer is created on the upstreamChannel, which will dispatch this event after the duration (and discards the previous timer, if any).
This commit is contained in:
parent
939c087754
commit
a76b22bf29
@ -1157,13 +1157,23 @@ func (dc *downstreamConn) handleMessageRegistered(msg *irc.Message) error {
|
|||||||
Params: params,
|
Params: params,
|
||||||
})
|
})
|
||||||
|
|
||||||
ch := &Channel{Name: upstreamName, Key: key, Detached: false}
|
var ch *Channel
|
||||||
if current, ok := uc.network.channels[ch.Name]; ok && key == "" {
|
var ok bool
|
||||||
|
if ch, ok = uc.network.channels[upstreamName]; ok {
|
||||||
// Don't clear the channel key if there's one set
|
// Don't clear the channel key if there's one set
|
||||||
// TODO: add a way to unset the channel key
|
// TODO: add a way to unset the channel key
|
||||||
ch.Key = current.Key
|
if key != "" {
|
||||||
|
ch.Key = key
|
||||||
|
}
|
||||||
|
uc.network.attach(ch)
|
||||||
|
} else {
|
||||||
|
ch = &Channel{
|
||||||
|
Name: upstreamName,
|
||||||
|
Key: key,
|
||||||
|
}
|
||||||
|
uc.network.channels[upstreamName] = ch
|
||||||
}
|
}
|
||||||
if err := uc.network.createUpdateChannel(ch); err != nil {
|
if err := dc.srv.db.StoreChannel(uc.network.ID, ch); err != nil {
|
||||||
dc.logger.Printf("failed to create or update channel %q: %v", upstreamName, err)
|
dc.logger.Printf("failed to create or update channel %q: %v", upstreamName, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1185,9 +1195,19 @@ func (dc *downstreamConn) handleMessageRegistered(msg *irc.Message) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if strings.EqualFold(reason, "detach") {
|
if strings.EqualFold(reason, "detach") {
|
||||||
ch := &Channel{Name: upstreamName, Detached: true}
|
var ch *Channel
|
||||||
if err := uc.network.createUpdateChannel(ch); err != nil {
|
var ok bool
|
||||||
dc.logger.Printf("failed to detach channel %q: %v", upstreamName, err)
|
if ch, ok = uc.network.channels[upstreamName]; ok {
|
||||||
|
uc.network.detach(ch)
|
||||||
|
} else {
|
||||||
|
ch = &Channel{
|
||||||
|
Name: name,
|
||||||
|
Detached: true,
|
||||||
|
}
|
||||||
|
uc.network.channels[upstreamName] = ch
|
||||||
|
}
|
||||||
|
if err := dc.srv.db.StoreChannel(uc.network.ID, ch); err != nil {
|
||||||
|
dc.logger.Printf("failed to create or update channel %q: %v", upstreamName, err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
params := []string{upstreamName}
|
params := []string{upstreamName}
|
||||||
@ -1613,6 +1633,8 @@ func (dc *downstreamConn) handleMessageRegistered(msg *irc.Message) error {
|
|||||||
Params: []string{upstreamName, text},
|
Params: []string{upstreamName, text},
|
||||||
}
|
}
|
||||||
uc.produce(upstreamName, echoMsg, dc)
|
uc.produce(upstreamName, echoMsg, dc)
|
||||||
|
|
||||||
|
uc.updateChannelAutoDetach(upstreamName)
|
||||||
}
|
}
|
||||||
case "NOTICE":
|
case "NOTICE":
|
||||||
var targetsStr, text string
|
var targetsStr, text string
|
||||||
@ -1636,6 +1658,8 @@ func (dc *downstreamConn) handleMessageRegistered(msg *irc.Message) error {
|
|||||||
Command: "NOTICE",
|
Command: "NOTICE",
|
||||||
Params: []string{upstreamName, unmarshaledText},
|
Params: []string{upstreamName, unmarshaledText},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
uc.updateChannelAutoDetach(upstreamName)
|
||||||
}
|
}
|
||||||
case "TAGMSG":
|
case "TAGMSG":
|
||||||
var targetsStr string
|
var targetsStr string
|
||||||
@ -1658,6 +1682,8 @@ func (dc *downstreamConn) handleMessageRegistered(msg *irc.Message) error {
|
|||||||
Command: "TAGMSG",
|
Command: "TAGMSG",
|
||||||
Params: []string{upstreamName},
|
Params: []string{upstreamName},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
uc.updateChannelAutoDetach(upstreamName)
|
||||||
}
|
}
|
||||||
case "INVITE":
|
case "INVITE":
|
||||||
var user, channel string
|
var user, channel string
|
||||||
|
69
upstream.go
69
upstream.go
@ -50,6 +50,25 @@ type upstreamChannel struct {
|
|||||||
creationTime string
|
creationTime string
|
||||||
Members map[string]*memberships
|
Members map[string]*memberships
|
||||||
complete bool
|
complete bool
|
||||||
|
detachTimer *time.Timer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uc *upstreamChannel) updateAutoDetach(dur time.Duration) {
|
||||||
|
if uc.detachTimer != nil {
|
||||||
|
uc.detachTimer.Stop()
|
||||||
|
uc.detachTimer = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if dur == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
uc.detachTimer = time.AfterFunc(dur, func() {
|
||||||
|
uc.conn.network.user.events <- eventChannelDetach{
|
||||||
|
uc: uc.conn,
|
||||||
|
name: uc.Name,
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
type upstreamConn struct {
|
type upstreamConn struct {
|
||||||
@ -403,14 +422,19 @@ func (uc *upstreamConn) handleMessage(msg *irc.Message) error {
|
|||||||
if target == uc.nick {
|
if target == uc.nick {
|
||||||
target = msg.Prefix.Name
|
target = msg.Prefix.Name
|
||||||
}
|
}
|
||||||
uc.produce(target, msg, nil)
|
|
||||||
|
|
||||||
highlight := msg.Prefix.Name != uc.nick && isHighlight(text, uc.nick)
|
if ch, ok := uc.network.channels[target]; ok {
|
||||||
if ch, ok := uc.network.channels[target]; ok && ch.Detached && highlight {
|
if ch.Detached {
|
||||||
uc.forEachDownstream(func(dc *downstreamConn) {
|
uc.handleDetachedMessage(msg.Prefix.Name, text, ch)
|
||||||
sendServiceNOTICE(dc, fmt.Sprintf("highlight in %v: <%v> %v", dc.marshalEntity(uc.network, ch.Name), msg.Prefix.Name, text))
|
}
|
||||||
})
|
|
||||||
|
highlight := msg.Prefix.Name != uc.nick && isHighlight(text, uc.nick)
|
||||||
|
if ch.DetachOn == FilterMessage || ch.DetachOn == FilterDefault || (ch.DetachOn == FilterHighlight && highlight) {
|
||||||
|
uc.updateChannelAutoDetach(target)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uc.produce(target, msg, nil)
|
||||||
}
|
}
|
||||||
case "CAP":
|
case "CAP":
|
||||||
var subCmd string
|
var subCmd string
|
||||||
@ -736,6 +760,7 @@ func (uc *upstreamConn) handleMessage(msg *irc.Message) error {
|
|||||||
conn: uc,
|
conn: uc,
|
||||||
Members: make(map[string]*memberships),
|
Members: make(map[string]*memberships),
|
||||||
}
|
}
|
||||||
|
uc.updateChannelAutoDetach(ch)
|
||||||
|
|
||||||
uc.SendMessage(&irc.Message{
|
uc.SendMessage(&irc.Message{
|
||||||
Command: "MODE",
|
Command: "MODE",
|
||||||
@ -766,7 +791,10 @@ func (uc *upstreamConn) handleMessage(msg *irc.Message) error {
|
|||||||
for _, ch := range strings.Split(channels, ",") {
|
for _, ch := range strings.Split(channels, ",") {
|
||||||
if msg.Prefix.Name == uc.nick {
|
if msg.Prefix.Name == uc.nick {
|
||||||
uc.logger.Printf("parted channel %q", ch)
|
uc.logger.Printf("parted channel %q", ch)
|
||||||
delete(uc.channels, ch)
|
if uch, ok := uc.channels[ch]; ok {
|
||||||
|
delete(uc.channels, ch)
|
||||||
|
uch.updateAutoDetach(0)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
ch, err := uc.getChannel(ch)
|
ch, err := uc.getChannel(ch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1408,6 +1436,25 @@ func (uc *upstreamConn) handleMessage(msg *irc.Message) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (uc *upstreamConn) handleDetachedMessage(sender string, text string, ch *Channel) {
|
||||||
|
highlight := sender != uc.nick && isHighlight(text, uc.nick)
|
||||||
|
if ch.RelayDetached == FilterMessage || ((ch.RelayDetached == FilterHighlight || ch.RelayDetached == FilterDefault) && highlight) {
|
||||||
|
uc.forEachDownstream(func(dc *downstreamConn) {
|
||||||
|
if highlight {
|
||||||
|
sendServiceNOTICE(dc, fmt.Sprintf("highlight in %v: <%v> %v", dc.marshalEntity(uc.network, ch.Name), sender, text))
|
||||||
|
} else {
|
||||||
|
sendServiceNOTICE(dc, fmt.Sprintf("message in %v: <%v> %v", dc.marshalEntity(uc.network, ch.Name), sender, text))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if ch.ReattachOn == FilterMessage || (ch.ReattachOn == FilterHighlight && highlight) {
|
||||||
|
uc.network.attach(ch)
|
||||||
|
if err := uc.srv.db.StoreChannel(uc.network.ID, ch); err != nil {
|
||||||
|
uc.logger.Printf("failed to update channel %q: %v", ch.Name, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (uc *upstreamConn) handleSupportedCaps(capsStr string) {
|
func (uc *upstreamConn) handleSupportedCaps(capsStr string) {
|
||||||
caps := strings.Fields(capsStr)
|
caps := strings.Fields(capsStr)
|
||||||
for _, s := range caps {
|
for _, s := range caps {
|
||||||
@ -1701,3 +1748,11 @@ func (uc *upstreamConn) updateAway() {
|
|||||||
}
|
}
|
||||||
uc.away = away
|
uc.away = away
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (uc *upstreamConn) updateChannelAutoDetach(name string) {
|
||||||
|
if uch, ok := uc.channels[name]; ok {
|
||||||
|
if ch, ok := uc.network.channels[name]; ok && !ch.Detached {
|
||||||
|
uch.updateAutoDetach(ch.DetachAfter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
118
user.go
118
user.go
@ -48,6 +48,11 @@ type eventDownstreamDisconnected struct {
|
|||||||
dc *downstreamConn
|
dc *downstreamConn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type eventChannelDetach struct {
|
||||||
|
uc *upstreamConn
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
|
||||||
type eventStop struct{}
|
type eventStop struct{}
|
||||||
|
|
||||||
type networkHistory struct {
|
type networkHistory struct {
|
||||||
@ -176,56 +181,59 @@ func (net *network) stop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (net *network) createUpdateChannel(ch *Channel) error {
|
func (net *network) detach(ch *Channel) {
|
||||||
if current, ok := net.channels[ch.Name]; ok {
|
if ch.Detached {
|
||||||
ch.ID = current.ID // update channel if it already exists
|
return
|
||||||
}
|
}
|
||||||
if err := net.user.srv.db.StoreChannel(net.ID, ch); err != nil {
|
ch.Detached = true
|
||||||
return err
|
net.user.srv.Logger.Printf("network %q: detaching channel %q", net.GetName(), ch.Name)
|
||||||
}
|
|
||||||
prev := net.channels[ch.Name]
|
|
||||||
net.channels[ch.Name] = ch
|
|
||||||
|
|
||||||
if prev != nil && prev.Detached != ch.Detached {
|
if net.conn != nil {
|
||||||
history := net.history[ch.Name]
|
if uch, ok := net.conn.channels[ch.Name]; ok {
|
||||||
if ch.Detached {
|
uch.updateAutoDetach(0)
|
||||||
net.user.srv.Logger.Printf("network %q: detaching channel %q", net.GetName(), ch.Name)
|
|
||||||
net.forEachDownstream(func(dc *downstreamConn) {
|
|
||||||
net.offlineClients[dc.clientName] = struct{}{}
|
|
||||||
|
|
||||||
dc.SendMessage(&irc.Message{
|
|
||||||
Prefix: dc.prefix(),
|
|
||||||
Command: "PART",
|
|
||||||
Params: []string{dc.marshalEntity(net, ch.Name), "Detach"},
|
|
||||||
})
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
net.user.srv.Logger.Printf("network %q: attaching channel %q", net.GetName(), ch.Name)
|
|
||||||
|
|
||||||
var uch *upstreamChannel
|
|
||||||
if net.conn != nil {
|
|
||||||
uch = net.conn.channels[ch.Name]
|
|
||||||
}
|
|
||||||
|
|
||||||
net.forEachDownstream(func(dc *downstreamConn) {
|
|
||||||
dc.SendMessage(&irc.Message{
|
|
||||||
Prefix: dc.prefix(),
|
|
||||||
Command: "JOIN",
|
|
||||||
Params: []string{dc.marshalEntity(net, ch.Name)},
|
|
||||||
})
|
|
||||||
|
|
||||||
if uch != nil {
|
|
||||||
forwardChannel(dc, uch)
|
|
||||||
}
|
|
||||||
|
|
||||||
if history != nil {
|
|
||||||
dc.sendNetworkHistory(net)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
net.forEachDownstream(func(dc *downstreamConn) {
|
||||||
|
net.offlineClients[dc.clientName] = struct{}{}
|
||||||
|
|
||||||
|
dc.SendMessage(&irc.Message{
|
||||||
|
Prefix: dc.prefix(),
|
||||||
|
Command: "PART",
|
||||||
|
Params: []string{dc.marshalEntity(net, ch.Name), "Detach"},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (net *network) attach(ch *Channel) {
|
||||||
|
if !ch.Detached {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ch.Detached = false
|
||||||
|
net.user.srv.Logger.Printf("network %q: attaching channel %q", net.GetName(), ch.Name)
|
||||||
|
|
||||||
|
var uch *upstreamChannel
|
||||||
|
if net.conn != nil {
|
||||||
|
uch = net.conn.channels[ch.Name]
|
||||||
|
|
||||||
|
net.conn.updateChannelAutoDetach(ch.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
net.forEachDownstream(func(dc *downstreamConn) {
|
||||||
|
dc.SendMessage(&irc.Message{
|
||||||
|
Prefix: dc.prefix(),
|
||||||
|
Command: "JOIN",
|
||||||
|
Params: []string{dc.marshalEntity(net, ch.Name)},
|
||||||
|
})
|
||||||
|
|
||||||
|
if uch != nil {
|
||||||
|
forwardChannel(dc, uch)
|
||||||
|
}
|
||||||
|
|
||||||
|
if net.history[ch.Name] != nil {
|
||||||
|
dc.sendNetworkHistory(net)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (net *network) deleteChannel(name string) error {
|
func (net *network) deleteChannel(name string) error {
|
||||||
@ -233,6 +241,12 @@ func (net *network) deleteChannel(name string) error {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("unknown channel %q", name)
|
return fmt.Errorf("unknown channel %q", name)
|
||||||
}
|
}
|
||||||
|
if net.conn != nil {
|
||||||
|
if uch, ok := net.conn.channels[ch.Name]; ok {
|
||||||
|
uch.updateAutoDetach(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if err := net.user.srv.db.DeleteChannel(ch.ID); err != nil {
|
if err := net.user.srv.db.DeleteChannel(ch.ID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -398,6 +412,16 @@ func (u *user) run() {
|
|||||||
if err := uc.handleMessage(msg); err != nil {
|
if err := uc.handleMessage(msg); err != nil {
|
||||||
uc.logger.Printf("failed to handle message %q: %v", msg, err)
|
uc.logger.Printf("failed to handle message %q: %v", msg, err)
|
||||||
}
|
}
|
||||||
|
case eventChannelDetach:
|
||||||
|
uc, name := e.uc, e.name
|
||||||
|
c, ok := uc.network.channels[name]
|
||||||
|
if !ok || c.Detached {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
uc.network.detach(c)
|
||||||
|
if err := uc.srv.db.StoreChannel(uc.network.ID, c); err != nil {
|
||||||
|
u.srv.Logger.Printf("failed to store updated detached channel %q: %v", c.Name, err)
|
||||||
|
}
|
||||||
case eventDownstreamConnected:
|
case eventDownstreamConnected:
|
||||||
dc := e.dc
|
dc := e.dc
|
||||||
|
|
||||||
@ -475,6 +499,10 @@ func (u *user) handleUpstreamDisconnected(uc *upstreamConn) {
|
|||||||
|
|
||||||
uc.endPendingLISTs(true)
|
uc.endPendingLISTs(true)
|
||||||
|
|
||||||
|
for _, uch := range uc.channels {
|
||||||
|
uch.updateAutoDetach(0)
|
||||||
|
}
|
||||||
|
|
||||||
uc.forEachDownstream(func(dc *downstreamConn) {
|
uc.forEachDownstream(func(dc *downstreamConn) {
|
||||||
dc.updateSupportedCaps()
|
dc.updateSupportedCaps()
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user