136 lines
2.9 KiB
Go
136 lines
2.9 KiB
Go
// Package xirc contains an extended IRC library.
|
|
package xirc
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"gopkg.in/irc.v3"
|
|
)
|
|
|
|
const (
|
|
maxMessageLength = 512
|
|
maxMessageParams = 15
|
|
)
|
|
|
|
const MaxSASLLength = 400
|
|
|
|
const (
|
|
RPL_STATSPING = "246"
|
|
RPL_LOCALUSERS = "265"
|
|
RPL_GLOBALUSERS = "266"
|
|
RPL_WHOISCERTFP = "276"
|
|
RPL_WHOISREGNICK = "307"
|
|
RPL_WHOISSPECIAL = "320"
|
|
RPL_CREATIONTIME = "329"
|
|
RPL_WHOISACCOUNT = "330"
|
|
RPL_TOPICWHOTIME = "333"
|
|
RPL_WHOISACTUALLY = "338"
|
|
RPL_WHOSPCRPL = "354"
|
|
RPL_WHOISHOST = "378"
|
|
RPL_WHOISMODES = "379"
|
|
RPL_VISIBLEHOST = "396"
|
|
ERR_UNKNOWNERROR = "400"
|
|
ERR_INVALIDCAPCMD = "410"
|
|
RPL_WHOISSECURE = "671"
|
|
|
|
// https://ircv3.net/specs/extensions/bot-mode
|
|
RPL_WHOISBOT = "335"
|
|
)
|
|
|
|
// The server-time layout, as defined in the IRCv3 spec.
|
|
const ServerTimeLayout = "2006-01-02T15:04:05.000Z"
|
|
|
|
// FormatServerTime formats a time with the server-time layout.
|
|
func FormatServerTime(t time.Time) string {
|
|
return t.UTC().Format(ServerTimeLayout)
|
|
}
|
|
|
|
// ParseCTCPMessage parses a CTCP message. CTCP is defined in
|
|
// https://tools.ietf.org/html/draft-oakley-irc-ctcp-02
|
|
func ParseCTCPMessage(msg *irc.Message) (cmd string, params string, ok bool) {
|
|
if (msg.Command != "PRIVMSG" && msg.Command != "NOTICE") || len(msg.Params) < 2 {
|
|
return "", "", false
|
|
}
|
|
text := msg.Params[1]
|
|
|
|
if !strings.HasPrefix(text, "\x01") {
|
|
return "", "", false
|
|
}
|
|
text = strings.Trim(text, "\x01")
|
|
|
|
words := strings.SplitN(text, " ", 2)
|
|
cmd = strings.ToUpper(words[0])
|
|
if len(words) > 1 {
|
|
params = words[1]
|
|
}
|
|
|
|
return cmd, params, true
|
|
}
|
|
|
|
type ChannelStatus byte
|
|
|
|
const (
|
|
ChannelPublic ChannelStatus = '='
|
|
ChannelSecret ChannelStatus = '@'
|
|
ChannelPrivate ChannelStatus = '*'
|
|
)
|
|
|
|
func ParseChannelStatus(s string) (ChannelStatus, error) {
|
|
if len(s) > 1 {
|
|
return 0, fmt.Errorf("invalid channel status %q: more than one character", s)
|
|
}
|
|
switch cs := ChannelStatus(s[0]); cs {
|
|
case ChannelPublic, ChannelSecret, ChannelPrivate:
|
|
return cs, nil
|
|
default:
|
|
return 0, fmt.Errorf("invalid channel status %q: unknown status", s)
|
|
}
|
|
}
|
|
|
|
// Membership is a channel member rank.
|
|
type Membership struct {
|
|
Mode byte
|
|
Prefix byte
|
|
}
|
|
|
|
// MembershipSet is a set of memberships sorted by descending rank.
|
|
type MembershipSet []Membership
|
|
|
|
func (ms *MembershipSet) Add(availableMemberships []Membership, newMembership Membership) {
|
|
l := *ms
|
|
i := 0
|
|
for _, availableMembership := range availableMemberships {
|
|
if i >= len(l) {
|
|
break
|
|
}
|
|
if l[i] == availableMembership {
|
|
if availableMembership == newMembership {
|
|
// we already have this membership
|
|
return
|
|
}
|
|
i++
|
|
continue
|
|
}
|
|
if availableMembership == newMembership {
|
|
break
|
|
}
|
|
}
|
|
// insert newMembership at i
|
|
l = append(l, Membership{})
|
|
copy(l[i+1:], l[i:])
|
|
l[i] = newMembership
|
|
*ms = l
|
|
}
|
|
|
|
func (ms *MembershipSet) Remove(membership Membership) {
|
|
l := *ms
|
|
for i, m := range l {
|
|
if m == membership {
|
|
*ms = append(l[:i], l[i+1:]...)
|
|
return
|
|
}
|
|
}
|
|
}
|