Skip to content

Reading input

Every handler receives a *goroku.Message. These helpers cover almost all argument parsing.

Args

Everything after the command word, trimmed:

go
// .say hello there
msg.Args() // "hello there"

// .say
msg.Args() // ""

Inner spacing is preserved — .say a b gives "a b" — so it is safe for text you intend to echo verbatim.

ArgsList and Arg

Split on whitespace:

go
// .ban 42 spam here
msg.ArgsList() // []string{"42", "spam", "here"}
msg.Arg(0)     // "42"
msg.Arg(1)     // "spam"
msg.Arg(9)     // "" — no panic

Arg is bounds-safe. You never need to check the length first:

go
func (m *Mod) BanCmd(msg *goroku.Message) error {
	id := msg.Arg(0)
	if id == "" {
		return msg.Answer(m.T("no_id", "❌ <b>Who?</b>"))
	}
	reason := msg.Arg(1) // "" when omitted, which is fine
	return m.ban(id, reason)
}

ArgsOrReply

The pattern most commands want: use the typed arguments, or fall back to the message the user replied to.

go
func (m *HTML) HTMLCmd(msg *goroku.Message) error {
	code := msg.ArgsOrReply()
	if code == "" {
		return msg.Answer(m.T("no_args", "❌ <b>Send text or reply</b>"))
	}
	return msg.Answer(code)
}

Both of these now work:

.html <b>hi</b>          → renders the typed markup
.html  (as a reply)      → renders the replied-to message

An empty result means there was neither — treat it as "nothing to do".

The replied-to message

For anything beyond its text, fetch it:

go
reply, err := msg.GetReplyMessage()
if err != nil || reply == nil {
	return msg.Answer(m.T("reply_needed", "❌ <b>Reply to a message</b>"))
}

if reply.Media != nil {
	// handle the attachment
}

To require a reply, prefer the declarative form — the dispatcher filters before your handler runs:

go
"mycmd": { OnlyReply: true },

Message fields

FieldTypeMeaning
IDint64Message ID
ChatIDint64Where it was sent
SenderIDint64Who sent it
TextstringText, formatting applied
RawTextstringText as typed
OutboolSent by you
IsPrivate, IsGroup, IsChannelboolChat type
Mediatg.MessageMediaClassAttachment, nil when absent
ReplyToMsgIDint640 when not a reply
IsForwardedboolForwarded from elsewhere

Cancellation

msg.Context() is cancelled when the bot shuts down. Pass it to anything slow:

go
req, err := http.NewRequestWithContext(msg.Context(), http.MethodGet, url, nil)
if err != nil {
	return err
}
resp, err := http.DefaultClient.Do(req)

Handlers that ignore it still work — shutdown just waits for them.

Released under the GNU AGPL v3 License.