Files
hbot/commands.go
Shin'ya Minazuki 0af744d59e Add preliminary support for NickServ authentication
(remove everything else)
2026-02-06 09:51:31 -03:00

152 lines
4.0 KiB
Go

package hbot
import (
"bufio"
"fmt"
"strings"
"unicode/utf8"
)
// Action sends an action to 'who' (user or channel)
func (bot *Bot) Action(who, text string) {
msg := fmt.Sprintf("\u0001ACTION %s\u0001", text)
bot.Msg(who, msg)
}
// ChMode is used to change users modes in a channel
// operator = "+o" deop = "-o"
// ban = "+b"
func (bot *Bot) ChMode(user, channel, mode string) {
bot.Send("MODE " + channel + " " + mode + " " + user)
}
// Join a channel
func (bot *Bot) Join(ch string) {
bot.Send("JOIN " + ch)
}
// Msg sends a message to 'who' (user or channel)
func (bot *Bot) Msg(who, text string) {
const command = "PRIVMSG"
for _, line := range bot.splitText(text, command, who) {
bot.Send(command + " " + who + " :" + line)
}
}
// MsgMaxSize returns maximum number of bytes that fit into one message.
// Useful, for example, if you want to generate a wall of emojis that fit into one message,
// or you want to cap some output to one message
func (bot *Bot) MsgMaxSize(who string) int {
const command = "PRIVMSG"
maxSize := bot.maxMsgSize(command, who)
return maxSize
}
// Notice sends a NOTICE message to 'who' (user or channel)
func (bot *Bot) Notice(who, text string) {
const command = "NOTICE"
for _, line := range bot.splitText(text, command, who) {
bot.Send(command + " " + who + " :" + line)
}
}
// NoticeMaxSize returns maximum number of bytes that fit into one message.
// Useful, for example, if you want to generate a wall of emojis that fit into one message,
// or you want to cap some output to one message
func (bot *Bot) NoticeMaxSize(who string) int {
const command = "NOTICE"
maxSize := bot.maxMsgSize(command, who)
return maxSize
}
// Part a channel
func (bot *Bot) Part(ch, msg string) {
bot.Send("PART " + ch + " " + msg)
}
// Reply sends a message to where the message came from (user or channel)
func (bot *Bot) Reply(m *Message, text string) {
bot.Msg(replyTarget(m), text)
}
// ReplyMaxSize is just like MsgMaxSize
// but calculates message size for the reply target
func (bot *Bot) ReplyMaxSize(m *Message) int {
const command = "PRIVMSG"
maxSize := bot.maxMsgSize(command, replyTarget(m))
return maxSize
}
// Send any command to the server
func (bot *Bot) Send(command string) {
bot.outgoing <- command
}
// SetNick sets the bots nick on the irc server.
// This does not alter *Bot.Nick, so be vary of that
func (bot *Bot) SetNick(nick string) {
bot.Send(fmt.Sprintf("NICK %s", nick))
}
// Topic sets the channel 'c' topic (requires bot has proper permissions)
func (bot *Bot) Topic(c, topic string) {
str := fmt.Sprintf("TOPIC %s :%s", c, topic)
bot.Send(str)
}
func (bot *Bot) maxMsgSize(command, who string) int {
// Maximum message size that fits into 512 bytes.
// Carriage return and linefeed are not counted here as they
// are added by handleOutgoingMessages()
maxSize := 510 - len(fmt.Sprintf(":%s %s %s :", bot.Prefix(), command, who))
if _, present := bot.CapStatus(CapIdentifyMsg); present {
maxSize--
}
// https://ircv3.net/specs/extensions/multiline
if bot.config.MsgSafetyBuffer {
maxSize -= 10
}
return maxSize
}
func replyTarget(m *Message) string {
if to := m.Param(0); strings.Contains(to, "#") {
return to
}
return m.Prefix.Name
}
// Splits a given string into a string slice, in chunks ending
// either with \n, or with \r\n, or splitting text to maximally allowed size.
func (bot *Bot) splitText(text, command, who string) []string {
var ret []string
// Sanitize input
text = strings.ToValidUTF8(text, "")
maxSize := bot.maxMsgSize(command, who)
scanner := bufio.NewScanner(strings.NewReader(text))
for scanner.Scan() {
line := scanner.Text()
for len(line) > maxSize {
totalSize := 0
runeSize := 0
// utf-8 aware splitting
for _, v := range line {
runeSize = utf8.RuneLen(v)
if totalSize+runeSize > maxSize {
ret = append(ret, line[:totalSize])
line = line[totalSize:]
totalSize = runeSize
continue
}
totalSize += runeSize
}
}
ret = append(ret, line)
}
return ret
}