diff --git a/doc/soju.1.scd b/doc/soju.1.scd index 133ff66..70b78b4 100644 --- a/doc/soju.1.scd +++ b/doc/soju.1.scd @@ -170,6 +170,70 @@ abbreviated form, for instance *network* can be abbreviated as *net* or just *network status* Show a list of saved networks and their current status. +*channel update* [options...] + Update the options of an existing channel. + + Options are: + + *-relay-detached* + Set when to relay messages from detached channels to the user with a BouncerServ NOTICE. + + Modes are: + + *message* + Relay any message from this channel when detached. + + *highlight* + Relay only messages mentioning you when detached. + + *none* + Don't relay any messages from this channel when detached. + + *default* + Currently same as *highlight*. This is the default behaviour. + + *-reattach-on* + Set when to automatically reattach to detached channels. + + Modes are: + + *message* + Reattach to this channel when any message is received. + + *highlight* + Reattach to this channel when any message mentioning you is received. + + *none* + Never automatically reattach to this channel. + + *default* + Currently same as *none*. This is the default behaviour. + + *-detach-after* + Automatically detach this channel after the specified duration has elapsed without receving any message corresponding to *-detach-on*. + + Example duration values: *1h30m*, *30s*, *2.5h*. + + Setting this value to 0 will disable this behaviour, i.e. this channel will never be automatically detached. This is the default behaviour. + + *-detach-on* + Set when to reset the auto-detach timer used by *-detach-after*, causing it to wait again for the auto-detach duration timer before detaching. + Joining, reattaching, sending a message, or changing any channel option will reset the timer, in addition to the messages specified by the mode. + + Modes are: + + *message* + Receiving any message from this channel will reset the auto-detach timer. + + *highlight* + Receiving any message mentioning you from this channel will reset the auto-detach timer. + + *none* + Receiving messages from this channel will not reset the auto-detach timer. Sending messages or joining the channel will still reset the timer. + + *default* + Currently same as *message*. This is the default behaviour. + *certfp generate* [options...] Generate self-signed certificate and use it for authentication (via SASL EXTERNAL). diff --git a/service.go b/service.go index 8d73386..1c239e1 100644 --- a/service.go +++ b/service.go @@ -218,6 +218,15 @@ func init() { desc: "change your password", handle: handlePasswordChange, }, + "channel": { + children: serviceCommandSet{ + "update": { + usage: " [-relay-detached ] [-reattach-on ] [-detach-after ] [-detach-on ]", + desc: "update a channel", + handle: handleServiceChannelUpdate, + }, + }, + }, } } @@ -427,7 +436,7 @@ func handleServiceNetworkStatus(dc *downstreamConn, params []string) error { func handleServiceNetworkUpdate(dc *downstreamConn, params []string) error { if len(params) < 1 { - return fmt.Errorf("expected exactly one argument") + return fmt.Errorf("expected at least one argument") } fs := newNetworkFlagSet() @@ -696,3 +705,84 @@ func handleUserDelete(dc *downstreamConn, params []string) error { sendServicePRIVMSG(dc, fmt.Sprintf("deleted user %q", username)) return nil } + +type channelFlagSet struct { + *flag.FlagSet + RelayDetached, ReattachOn, DetachAfter, DetachOn *string +} + +func newChannelFlagSet() *channelFlagSet { + fs := &channelFlagSet{FlagSet: newFlagSet()} + fs.Var(stringPtrFlag{&fs.RelayDetached}, "relay-detached", "") + fs.Var(stringPtrFlag{&fs.ReattachOn}, "reattach-on", "") + fs.Var(stringPtrFlag{&fs.DetachAfter}, "detach-after", "") + fs.Var(stringPtrFlag{&fs.DetachOn}, "detach-on", "") + return fs +} + +func (fs *channelFlagSet) update(channel *Channel) error { + if fs.RelayDetached != nil { + filter, err := parseFilter(*fs.RelayDetached) + if err != nil { + return err + } + channel.RelayDetached = filter + } + if fs.ReattachOn != nil { + filter, err := parseFilter(*fs.ReattachOn) + if err != nil { + return err + } + channel.ReattachOn = filter + } + if fs.DetachAfter != nil { + dur, err := time.ParseDuration(*fs.DetachAfter) + if err != nil || dur < 0 { + return fmt.Errorf("unknown duration for -detach-after %q (duration format: 0, 300s, 22h30m, ...)", *fs.DetachAfter) + } + channel.DetachAfter = dur + } + if fs.DetachOn != nil { + filter, err := parseFilter(*fs.DetachOn) + if err != nil { + return err + } + channel.DetachOn = filter + } + return nil +} + +func handleServiceChannelUpdate(dc *downstreamConn, params []string) error { + if len(params) < 1 { + return fmt.Errorf("expected at least one argument") + } + name := params[0] + + fs := newChannelFlagSet() + if err := fs.Parse(params[1:]); err != nil { + return err + } + + uc, upstreamName, err := dc.unmarshalEntity(name) + if err != nil { + return fmt.Errorf("unknown channel %q", name) + } + + ch, ok := uc.network.channels[upstreamName] + if !ok { + return fmt.Errorf("unknown channel %q", name) + } + + if err := fs.update(ch); err != nil { + return err + } + + uc.updateChannelAutoDetach(upstreamName) + + if err := dc.srv.db.StoreChannel(uc.network.ID, ch); err != nil { + return fmt.Errorf("failed to update channel: %v", err) + } + + sendServicePRIVMSG(dc, fmt.Sprintf("updated channel %q", name)) + return nil +}