forked from ugjka/kittybot
-
Notifications
You must be signed in to change notification settings - Fork 0
/
commands.go
154 lines (133 loc) · 3.99 KB
/
commands.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package kitty
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.MsgSafetyBuffer {
maxSize -= 10
}
return maxSize
}
func replyTarget(m *Message) string {
var target string
if strings.Contains(m.To, "#") {
target = m.To
} else {
target = m.From
}
return target
}
// 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
}