Introduce an xirc package
This commit is contained in:
parent
89412187d4
commit
b92afa7cca
@ -7,6 +7,8 @@ import (
|
||||
"strings"
|
||||
|
||||
"gopkg.in/irc.v3"
|
||||
|
||||
"git.sr.ht/~emersion/soju/xirc"
|
||||
)
|
||||
|
||||
func forwardChannel(ctx context.Context, dc *downstreamConn, ch *upstreamChannel) {
|
||||
@ -27,7 +29,7 @@ func forwardChannel(ctx context.Context, dc *downstreamConn, ch *upstreamChannel
|
||||
} else {
|
||||
timestampStr := "*"
|
||||
if r != nil {
|
||||
timestampStr = fmt.Sprintf("timestamp=%s", formatServerTime(r.Timestamp))
|
||||
timestampStr = fmt.Sprintf("timestamp=%s", xirc.FormatServerTime(r.Timestamp))
|
||||
}
|
||||
dc.SendMessage(&irc.Message{
|
||||
Prefix: dc.prefix(),
|
||||
|
@ -18,6 +18,7 @@ import (
|
||||
"gopkg.in/irc.v3"
|
||||
|
||||
"git.sr.ht/~emersion/soju/database"
|
||||
"git.sr.ht/~emersion/soju/xirc"
|
||||
)
|
||||
|
||||
type ircError struct {
|
||||
@ -2472,7 +2473,7 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
|
||||
dc.logger.Printf("broadcasting bouncer-wide %v: %v", msg.Command, text)
|
||||
|
||||
broadcastTags := tags.Copy()
|
||||
broadcastTags["time"] = irc.TagValue(formatServerTime(time.Now()))
|
||||
broadcastTags["time"] = irc.TagValue(xirc.FormatServerTime(time.Now()))
|
||||
broadcastMsg := &irc.Message{
|
||||
Tags: broadcastTags,
|
||||
Prefix: servicePrefix,
|
||||
@ -2498,7 +2499,7 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
|
||||
if casemapASCII(name) == serviceNickCM {
|
||||
if dc.caps.IsEnabled("echo-message") {
|
||||
echoTags := tags.Copy()
|
||||
echoTags["time"] = irc.TagValue(formatServerTime(time.Now()))
|
||||
echoTags["time"] = irc.TagValue(xirc.FormatServerTime(time.Now()))
|
||||
dc.SendMessage(&irc.Message{
|
||||
Tags: echoTags,
|
||||
Prefix: dc.prefix(),
|
||||
@ -2547,7 +2548,7 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
|
||||
}
|
||||
|
||||
echoTags := tags.Copy()
|
||||
echoTags["time"] = irc.TagValue(formatServerTime(time.Now()))
|
||||
echoTags["time"] = irc.TagValue(xirc.FormatServerTime(time.Now()))
|
||||
if uc.account != "" {
|
||||
echoTags["account"] = irc.TagValue(uc.account)
|
||||
}
|
||||
@ -2871,7 +2872,7 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
|
||||
Tags: irc.Tags{"batch": batchRef},
|
||||
Prefix: dc.srv.prefix(),
|
||||
Command: "CHATHISTORY",
|
||||
Params: []string{"TARGETS", target.Name, formatServerTime(target.LatestMessage)},
|
||||
Params: []string{"TARGETS", target.Name, xirc.FormatServerTime(target.LatestMessage)},
|
||||
})
|
||||
}
|
||||
})
|
||||
@ -2941,7 +2942,7 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
|
||||
}}
|
||||
}
|
||||
|
||||
timestamp, err := time.Parse(serverTimeLayout, criteriaParts[1])
|
||||
timestamp, err := time.Parse(xirc.ServerTimeLayout, criteriaParts[1])
|
||||
if err != nil {
|
||||
return ircError{&irc.Message{
|
||||
Command: "FAIL",
|
||||
@ -2967,7 +2968,7 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
|
||||
|
||||
timestampStr := "*"
|
||||
if !r.Timestamp.IsZero() {
|
||||
timestampStr = fmt.Sprintf("timestamp=%s", formatServerTime(r.Timestamp))
|
||||
timestampStr = fmt.Sprintf("timestamp=%s", xirc.FormatServerTime(r.Timestamp))
|
||||
}
|
||||
network.forEachDownstream(func(d *downstreamConn) {
|
||||
if broadcast || dc.id == d.id {
|
||||
@ -3001,7 +3002,7 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
|
||||
value := string(v)
|
||||
switch name {
|
||||
case "before", "after":
|
||||
timestamp, err := time.Parse(serverTimeLayout, value)
|
||||
timestamp, err := time.Parse(xirc.ServerTimeLayout, value)
|
||||
if err != nil {
|
||||
return ircError{&irc.Message{
|
||||
Command: "FAIL",
|
||||
|
34
irc.go
34
irc.go
@ -11,8 +11,11 @@ import (
|
||||
"gopkg.in/irc.v3"
|
||||
|
||||
"git.sr.ht/~emersion/soju/database"
|
||||
"git.sr.ht/~emersion/soju/xirc"
|
||||
)
|
||||
|
||||
// TODO: generalize and move helpers to the xirc package
|
||||
|
||||
const (
|
||||
rpl_statsping = "246"
|
||||
rpl_localusers = "265"
|
||||
@ -42,13 +45,6 @@ const (
|
||||
maxSASLLength = 400
|
||||
)
|
||||
|
||||
// The server-time layout, as defined in the IRCv3 spec.
|
||||
const serverTimeLayout = "2006-01-02T15:04:05.000Z"
|
||||
|
||||
func formatServerTime(t time.Time) string {
|
||||
return t.UTC().Format(serverTimeLayout)
|
||||
}
|
||||
|
||||
type userModes string
|
||||
|
||||
func (ms userModes) Has(c byte) bool {
|
||||
@ -479,28 +475,6 @@ func (js *joinSorter) Swap(i, j int) {
|
||||
js.keys[i], js.keys[j] = js.keys[j], js.keys[i]
|
||||
}
|
||||
|
||||
// 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 casemapping func(string) string
|
||||
|
||||
func casemapNone(name string) string {
|
||||
@ -728,7 +702,7 @@ func parseChatHistoryBound(param string) time.Time {
|
||||
}
|
||||
switch parts[0] {
|
||||
case "timestamp":
|
||||
timestamp, err := time.Parse(serverTimeLayout, parts[1])
|
||||
timestamp, err := time.Parse(xirc.ServerTimeLayout, parts[1])
|
||||
if err != nil {
|
||||
return time.Time{}
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ import (
|
||||
"gopkg.in/irc.v3"
|
||||
|
||||
"git.sr.ht/~emersion/soju/database"
|
||||
"git.sr.ht/~emersion/soju/xirc"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -135,7 +136,7 @@ func (ms *fsMessageStore) Append(network *database.Network, entity string, msg *
|
||||
var t time.Time
|
||||
if tag, ok := msg.Tags["time"]; ok {
|
||||
var err error
|
||||
t, err = time.Parse(serverTimeLayout, string(tag))
|
||||
t, err = time.Parse(xirc.ServerTimeLayout, string(tag))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to parse message time tag: %v", err)
|
||||
}
|
||||
@ -245,7 +246,7 @@ func formatMessage(msg *irc.Message) string {
|
||||
case "NOTICE":
|
||||
return fmt.Sprintf("-%s- %s", msg.Prefix.Name, msg.Params[1])
|
||||
case "PRIVMSG":
|
||||
if cmd, params, ok := parseCTCPMessage(msg); ok && cmd == "ACTION" {
|
||||
if cmd, params, ok := xirc.ParseCTCPMessage(msg); ok && cmd == "ACTION" {
|
||||
return fmt.Sprintf("* %s %s", msg.Prefix.Name, params)
|
||||
} else {
|
||||
return fmt.Sprintf("<%s> %s", msg.Prefix.Name, msg.Params[1])
|
||||
@ -392,7 +393,7 @@ func (ms *fsMessageStore) parseMessage(line string, network *database.Network, e
|
||||
|
||||
msg := &irc.Message{
|
||||
Tags: map[string]irc.TagValue{
|
||||
"time": irc.TagValue(formatServerTime(t)),
|
||||
"time": irc.TagValue(xirc.FormatServerTime(t)),
|
||||
},
|
||||
Prefix: prefix,
|
||||
Command: cmd,
|
||||
|
@ -19,6 +19,7 @@ import (
|
||||
"gopkg.in/irc.v3"
|
||||
|
||||
"git.sr.ht/~emersion/soju/database"
|
||||
"git.sr.ht/~emersion/soju/xirc"
|
||||
)
|
||||
|
||||
// permanentUpstreamCaps is the static list of upstream capabilities always
|
||||
@ -464,7 +465,7 @@ func (uc *upstreamConn) handleMessage(ctx context.Context, msg *irc.Message) err
|
||||
}
|
||||
|
||||
if _, ok := msg.Tags["time"]; !ok && !isNumeric(msg.Command) {
|
||||
msg.Tags["time"] = irc.TagValue(formatServerTime(time.Now()))
|
||||
msg.Tags["time"] = irc.TagValue(xirc.FormatServerTime(time.Now()))
|
||||
}
|
||||
|
||||
switch msg.Command {
|
||||
|
39
xirc/xirc.go
Normal file
39
xirc/xirc.go
Normal file
@ -0,0 +1,39 @@
|
||||
// Package xirc contains an extended IRC library.
|
||||
package xirc
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gopkg.in/irc.v3"
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
Loading…
Reference in New Issue
Block a user