commit c761717

Hubert Hirtz  ·  2020-11-21 11:29:51 +0000 UTC
parent 8828195
Document the IRC library
2 files changed,  +86, -27
+51, -21
  1@@ -42,6 +42,7 @@ func (auth *SASLPlain) Respond(challenge string) (res string, err error) {
  2 	return
  3 }
  4 
  5+// SupportedCapabilities is the set of capabilities supported by this library.
  6 var SupportedCapabilities = map[string]struct{}{
  7 	"account-notify":    {},
  8 	"account-tag":       {},
  9@@ -61,6 +62,8 @@ var SupportedCapabilities = map[string]struct{}{
 10 	"userhost-in-names": {},
 11 }
 12 
 13+// Values taken by the "@+typing=" client tag.  TypingUnspec means the value or
 14+// tag is absent.
 15 const (
 16 	TypingUnspec = iota
 17 	TypingActive
 18@@ -68,6 +71,13 @@ const (
 19 	TypingDone
 20 )
 21 
 22+// action contains the arguments of a user action.
 23+//
 24+// To keep connection reads and writes in a single coroutine, the library
 25+// interface functions like Join("#channel") or PrivMsg("target", "message")
 26+// don't interact with the IRC session directly.  Instead, they push an action
 27+// in the action channel.  This action is then processed by the correct
 28+// coroutine.
 29 type action interface{}
 30 
 31 type (
 32@@ -105,22 +115,25 @@ type (
 33 	}
 34 )
 35 
 36+// User is a known IRC user (we share a channel with it).
 37 type User struct {
 38-	Name    *Prefix
 39-	AwayMsg string
 40+	Name    *Prefix // the nick, user and hostname of the user if known.
 41+	AwayMsg string  // the away message if the user is away, "" otherwise.
 42 }
 43 
 44+// Channel is a joined channel.
 45 type Channel struct {
 46-	Name      string
 47-	Members   map[*User]string
 48-	Topic     string
 49-	TopicWho  *Prefix
 50-	TopicTime time.Time
 51-	Secret    bool
 52-
 53-	complete bool
 54+	Name      string           // the name of the channel.
 55+	Members   map[*User]string // the set of members associated with their membership.
 56+	Topic     string           // the topic of the channel, or "" if absent.
 57+	TopicWho  *Prefix          // the name of the last user who set the topic.
 58+	TopicTime time.Time        // the last time the topic has been changed.
 59+	Secret    bool             // whether the channel is on the server channel list.
 60+
 61+	complete bool // whether this stucture is fully initialized.
 62 }
 63 
 64+// SessionParams defines how to connect to an IRC server.
 65 type SessionParams struct {
 66 	Nickname string
 67 	Username string
 68@@ -128,24 +141,25 @@ type SessionParams struct {
 69 
 70 	Auth SASLClient
 71 
 72-	Debug bool
 73+	Debug bool // whether the Session should report all messages it sends and receive.
 74 }
 75 
 76+// Session is an IRC session/connection/whatever.
 77 type Session struct {
 78 	conn io.ReadWriteCloser
 79-	msgs chan Message
 80-	acts chan action
 81-	evts chan Event
 82+	msgs chan Message // incoming messages.
 83+	acts chan action  // user actions.
 84+	evts chan Event   // events sent to the user.
 85 
 86 	debug bool
 87 
 88 	running      atomic.Value // bool
 89 	registered   bool
 90-	typings      *Typings
 91-	typingStamps map[string]time.Time
 92+	typings      *Typings             // incoming typing notifications.
 93+	typingStamps map[string]time.Time // user typing instants.
 94 
 95 	nick   string
 96-	nickCf string
 97+	nickCf string // casemapped nickname.
 98 	user   string
 99 	real   string
100 	acct   string
101@@ -154,13 +168,18 @@ type Session struct {
102 
103 	availableCaps map[string]string
104 	enabledCaps   map[string]struct{}
105-	features      map[string]string
106+	features      map[string]string // server ISUPPORT advertized features.
107 
108-	users     map[string]*User
109-	channels  map[string]Channel
110-	chBatches map[string]HistoryEvent
111+	users     map[string]*User        // known users.
112+	channels  map[string]Channel      // joined channels.
113+	chBatches map[string]HistoryEvent // channel history batches being processed.
114 }
115 
116+// NewSession starts an IRC session from the given connection and session
117+// parameters.
118+//
119+// It returns an error when the paramaters are invalid, or when it cannot write
120+// to the connection.
121 func NewSession(conn io.ReadWriteCloser, params SessionParams) (*Session, error) {
122 	s := &Session{
123 		conn:          conn,
124@@ -226,10 +245,12 @@ func NewSession(conn io.ReadWriteCloser, params SessionParams) (*Session, error)
125 	return s, nil
126 }
127 
128+// Running reports whether we are still connected to the server.
129 func (s *Session) Running() bool {
130 	return s.running.Load().(bool)
131 }
132 
133+// Stop stops the session and closes the connection.
134 func (s *Session) Stop() {
135 	if !s.Running() {
136 		return
137@@ -241,10 +262,13 @@ func (s *Session) Stop() {
138 	close(s.msgs)
139 }
140 
141+// Poll returns the event channel where incoming events are reported.
142 func (s *Session) Poll() (events <-chan Event) {
143 	return s.evts
144 }
145 
146+// HasCapability reports whether the given capability has been negociated
147+// successfully.
148 func (s *Session) HasCapability(capability string) bool {
149 	_, ok := s.enabledCaps[capability]
150 	return ok
151@@ -254,6 +278,7 @@ func (s *Session) Nick() string {
152 	return s.nick
153 }
154 
155+// NickCf is our casemapped nickname.
156 func (s *Session) NickCf() string {
157 	return s.nickCf
158 }
159@@ -274,6 +299,7 @@ func (s *Session) Casemap(name string) string {
160 	}
161 }
162 
163+// Users returns the list of all known nicknames.
164 func (s *Session) Users() []string {
165 	users := make([]string, 0, len(s.users))
166 	for _, u := range s.users {
167@@ -282,6 +308,8 @@ func (s *Session) Users() []string {
168 	return users
169 }
170 
171+// Names returns the list of users in the given channel, or nil if this channel
172+// is not known by the session.
173 func (s *Session) Names(channel string) []Member {
174 	var names []Member
175 	if c, ok := s.channels[s.Casemap(channel)]; ok {
176@@ -296,6 +324,7 @@ func (s *Session) Names(channel string) []Member {
177 	return names
178 }
179 
180+// Typings returns the list of nickname who are currently typing.
181 func (s *Session) Typings(target string) []string {
182 	targetCf := s.Casemap(target)
183 	var res []string
184@@ -333,6 +362,7 @@ func (s *Session) Topic(channel string) (topic string, who *Prefix, at time.Time
185 	return
186 }
187 
188+// SendRaw sends its given argument verbatim to the server.
189 func (s *Session) SendRaw(raw string) {
190 	s.acts <- actionSendRaw{raw}
191 }
+35, -6
  1@@ -8,6 +8,8 @@ import (
  2 	"time"
  3 )
  4 
  5+// CasemapASCII of name is the canonical representation of name according to the
  6+// ascii casemapping.
  7 func CasemapASCII(name string) string {
  8 	var sb strings.Builder
  9 	sb.Grow(len(name))
 10@@ -20,6 +22,8 @@ func CasemapASCII(name string) string {
 11 	return sb.String()
 12 }
 13 
 14+// CasemapASCII of name is the canonical representation of name according to the
 15+// rfc-1459 casemapping.
 16 func CasemapRFC1459(name string) string {
 17 	var sb strings.Builder
 18 	sb.Grow(len(name))
 19@@ -40,20 +44,21 @@ func CasemapRFC1459(name string) string {
 20 	return sb.String()
 21 }
 22 
 23-func word(s string) (w, rest string) {
 24+// word returns the first word of s and the rest of s.
 25+func word(s string) (word, rest string) {
 26 	split := strings.SplitN(s, " ", 2)
 27-
 28 	if len(split) < 2 {
 29-		w = split[0]
 30+		word = split[0]
 31 		rest = ""
 32 	} else {
 33-		w = split[0]
 34+		word = split[0]
 35 		rest = split[1]
 36 	}
 37-
 38 	return
 39 }
 40 
 41+// tagEscape returns the the value of '\c' given c according to the message-tags
 42+// specification.
 43 func tagEscape(c rune) (escape rune) {
 44 	switch c {
 45 	case ':':
 46@@ -67,10 +72,11 @@ func tagEscape(c rune) (escape rune) {
 47 	default:
 48 		escape = c
 49 	}
 50-
 51 	return
 52 }
 53 
 54+// unescapeTagValue removes escapes from the given string and replaces them with
 55+// their meaningful values.
 56 func unescapeTagValue(escaped string) string {
 57 	var builder strings.Builder
 58 	builder.Grow(len(escaped))
 59@@ -96,6 +102,7 @@ func unescapeTagValue(escaped string) string {
 60 	return builder.String()
 61 }
 62 
 63+// escapeTagValue does the inverse operation of unescapeTagValue.
 64 func escapeTagValue(unescaped string) string {
 65 	var sb strings.Builder
 66 	sb.Grow(len(unescaped) * 2)
 67@@ -163,6 +170,8 @@ type Prefix struct {
 68 	Host string
 69 }
 70 
 71+// ParsePrefix parses a "nick!user@host" combination (or a prefix) from the given
 72+// string.
 73 func ParsePrefix(s string) (p *Prefix) {
 74 	if s == "" {
 75 		return
 76@@ -185,6 +194,7 @@ func ParsePrefix(s string) (p *Prefix) {
 77 	return
 78 }
 79 
 80+// Copy makes a copy of the prefix, but doesn't copy the internal strings.
 81 func (p *Prefix) Copy() *Prefix {
 82 	if p == nil {
 83 		return nil
 84@@ -194,6 +204,7 @@ func (p *Prefix) Copy() *Prefix {
 85 	return res
 86 }
 87 
 88+// String returns the "nick!user@host" representation of the prefix.
 89 func (p *Prefix) String() string {
 90 	if p == nil {
 91 		return ""
 92@@ -210,6 +221,7 @@ func (p *Prefix) String() string {
 93 	}
 94 }
 95 
 96+// Message is the representation of an IRC message.
 97 type Message struct {
 98 	Tags    map[string]string
 99 	Prefix  *Prefix
100@@ -217,6 +229,8 @@ type Message struct {
101 	Params  []string
102 }
103 
104+// ParseMessage parses the message from the given string, which must be trimmed
105+// of "\r\n" beforehand.
106 func ParseMessage(line string) (msg Message, err error) {
107 	line = strings.TrimLeft(line, " ")
108 	if line == "" {
109@@ -268,6 +282,7 @@ func ParseMessage(line string) (msg Message, err error) {
110 	return
111 }
112 
113+// IsReply reports whether the message command is a server reply.
114 func (msg *Message) IsReply() bool {
115 	if len(msg.Command) != 3 {
116 		return false
117@@ -280,6 +295,8 @@ func (msg *Message) IsReply() bool {
118 	return true
119 }
120 
121+// String returns the protocol representation of the message, without an ending
122+// "\r\n".
123 func (msg *Message) String() string {
124 	var sb strings.Builder
125 
126@@ -317,6 +334,7 @@ func (msg *Message) String() string {
127 	return sb.String()
128 }
129 
130+// IsValid reports whether the message is correctly formed.
131 func (msg *Message) IsValid() bool {
132 	switch msg.Command {
133 	case "AUTHENTICATE", "PING", "PONG":
134@@ -377,6 +395,7 @@ func (msg *Message) IsValid() bool {
135 	}
136 }
137 
138+// Time returns the time when the message has been sent, if present.
139 func (msg *Message) Time() (t time.Time, ok bool) {
140 	var tag string
141 	var year, month, day, hour, minute, second, millis int
142@@ -398,6 +417,8 @@ func (msg *Message) Time() (t time.Time, ok bool) {
143 	return
144 }
145 
146+// TimeOrNow returns the time when the message has been sent, or time.Now() if
147+// absent.
148 func (msg *Message) TimeOrNow() time.Time {
149 	t, ok := msg.Time()
150 	if ok {
151@@ -406,6 +427,7 @@ func (msg *Message) TimeOrNow() time.Time {
152 	return time.Now().UTC()
153 }
154 
155+// Severity is the severity of a server reply.
156 type Severity int
157 
158 const (
159@@ -414,6 +436,7 @@ const (
160 	SeverityFail
161 )
162 
163+// ReplySeverity returns the severity of a server reply.
164 func ReplySeverity(reply string) Severity {
165 	switch reply[0] {
166 	case '4', '5':
167@@ -434,12 +457,15 @@ func ReplySeverity(reply string) Severity {
168 	}
169 }
170 
171+// Cap is a capability token in "CAP" server responses.
172 type Cap struct {
173 	Name   string
174 	Value  string
175 	Enable bool
176 }
177 
178+// ParseCaps parses the last argument (capability list) of "CAP LS/LIST/NEW/DEL"
179+// server responses.
180 func ParseCaps(caps string) (diff []Cap) {
181 	for _, c := range strings.Split(caps, " ") {
182 		if c == "" || c == "-" || c == "=" || c == "-=" {
183@@ -467,11 +493,14 @@ func ParseCaps(caps string) (diff []Cap) {
184 	return
185 }
186 
187+// Member is a token in RPL_NAMREPLY's last parameter.
188 type Member struct {
189 	PowerLevel string
190 	Name       *Prefix
191 }
192 
193+// ParseNameReply parses the last parameter of RPL_NAMREPLY, according to the
194+// membership prefixes of the server.
195 func ParseNameReply(trailing string, prefixes string) (names []Member) {
196 	for _, word := range strings.Split(trailing, " ") {
197 		if word == "" {