~emersion/soju

0ddc0de7e55070bcdf9c29fb88330dbe1444f776 — delthas 9 months ago a76b22b
service: Introduce `channel update`

This adds the `channel update` service command, which is used to set the
auto-detach, auto-reattach, and message relaying settings of a channel.

Of note is that currently the parser parses `#` as a comment, which
means any `channel update #foo ...` will actually need to be escaped to
`channel update "#foo" ...`
2 files changed, 155 insertions(+), 1 deletions(-)

M doc/soju.1.scd
M service.go
M doc/soju.1.scd => doc/soju.1.scd +64 -0
@@ 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* <name> [options...]
	Update the options of an existing channel.

	Options are:

	*-relay-detached* <mode>
		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* <mode>
		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* <duration>
		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* <mode>
		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...] <network name>
	Generate self-signed certificate and use it for authentication (via SASL
	EXTERNAL).

M service.go => service.go +91 -1
@@ 218,6 218,15 @@ func init() {
			desc:   "change your password",
			handle: handlePasswordChange,
		},
		"channel": {
			children: serviceCommandSet{
				"update": {
					usage:  "<name> [-relay-detached <default|none|highlight|message>] [-reattach-on <default|none|highlight|message>] [-detach-after <duration>] [-detach-on <default|none|highlight|message>]",
					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
}