Implement the new bot mode spec:
https://github.com/ircv3/ircv3-specifications/pull/439
This commit is contained in:
Shivaram Lingamneni 2021-03-17 14:36:52 -04:00
parent 507d53c507
commit 1efde964e1
10 changed files with 130 additions and 96 deletions

View File

@ -60,6 +60,8 @@ const (
MultilineConcatTag = "draft/multiline-concat" MultilineConcatTag = "draft/multiline-concat"
// draft/relaymsg: // draft/relaymsg:
RelaymsgTagName = "draft/relaymsg" RelaymsgTagName = "draft/relaymsg"
// BOT mode: https://github.com/ircv3/ircv3-specifications/pull/439
BotTagName = "draft/bot"
) )
func init() { func init() {

View File

@ -729,6 +729,7 @@ func (channel *Channel) AddHistoryItem(item history.Item, account string) (err e
// Join joins the given client to this channel (if they can be joined). // Join joins the given client to this channel (if they can be joined).
func (channel *Channel) Join(client *Client, key string, isSajoin bool, rb *ResponseBuffer) (joinErr error, forward string) { func (channel *Channel) Join(client *Client, key string, isSajoin bool, rb *ResponseBuffer) (joinErr error, forward string) {
details := client.Details() details := client.Details()
isBot := client.HasMode(modes.Bot)
channel.stateMutex.RLock() channel.stateMutex.RLock()
chname := channel.name chname := channel.name
@ -824,6 +825,7 @@ func (channel *Channel) Join(client *Client, key string, isSajoin bool, rb *Resp
Nick: details.nickMask, Nick: details.nickMask,
AccountName: details.accountName, AccountName: details.accountName,
Message: message, Message: message,
IsBot: isBot,
} }
histItem.Params[0] = details.realname histItem.Params[0] = details.realname
channel.AddHistoryItem(histItem, details.account) channel.AddHistoryItem(histItem, details.account)
@ -840,7 +842,7 @@ func (channel *Channel) Join(client *Client, key string, isSajoin bool, rb *Resp
// cache the most common case (JOIN without extended-join) // cache the most common case (JOIN without extended-join)
var cache MessageCache var cache MessageCache
cache.Initialize(channel.server, message.Time, message.Msgid, details.nickMask, details.accountName, nil, "JOIN", chname) cache.Initialize(channel.server, message.Time, message.Msgid, details.nickMask, details.accountName, isBot, nil, "JOIN", chname)
isAway, awayMessage := client.Away() isAway, awayMessage := client.Away()
for _, member := range channel.Members() { for _, member := range channel.Members() {
if respectAuditorium { if respectAuditorium {
@ -859,7 +861,7 @@ func (channel *Channel) Join(client *Client, key string, isSajoin bool, rb *Resp
continue continue
} }
if session.capabilities.Has(caps.ExtendedJoin) { if session.capabilities.Has(caps.ExtendedJoin) {
session.sendFromClientInternal(false, message.Time, message.Msgid, details.nickMask, details.accountName, nil, "JOIN", chname, details.accountName, details.realname) session.sendFromClientInternal(false, message.Time, message.Msgid, details.nickMask, details.accountName, isBot, nil, "JOIN", chname, details.accountName, details.realname)
} else { } else {
cache.Send(session) cache.Send(session)
} }
@ -867,15 +869,15 @@ func (channel *Channel) Join(client *Client, key string, isSajoin bool, rb *Resp
session.Send(nil, client.server.name, "MODE", chname, modestr, details.nick) session.Send(nil, client.server.name, "MODE", chname, modestr, details.nick)
} }
if isAway && session.capabilities.Has(caps.AwayNotify) { if isAway && session.capabilities.Has(caps.AwayNotify) {
session.sendFromClientInternal(false, time.Time{}, "", details.nickMask, details.accountName, nil, "AWAY", awayMessage) session.sendFromClientInternal(false, time.Time{}, "", details.nickMask, details.accountName, isBot, nil, "AWAY", awayMessage)
} }
} }
} }
if rb.session.capabilities.Has(caps.ExtendedJoin) { if rb.session.capabilities.Has(caps.ExtendedJoin) {
rb.AddFromClient(message.Time, message.Msgid, details.nickMask, details.accountName, nil, "JOIN", chname, details.accountName, details.realname) rb.AddFromClient(message.Time, message.Msgid, details.nickMask, details.accountName, isBot, nil, "JOIN", chname, details.accountName, details.realname)
} else { } else {
rb.AddFromClient(message.Time, message.Msgid, details.nickMask, details.accountName, nil, "JOIN", chname) rb.AddFromClient(message.Time, message.Msgid, details.nickMask, details.accountName, isBot, nil, "JOIN", chname)
} }
if rb.session.client == client { if rb.session.client == client {
@ -988,6 +990,7 @@ func (channel *Channel) Part(client *Client, message string, rb *ResponseBuffer)
splitMessage := utils.MakeMessage(message) splitMessage := utils.MakeMessage(message)
details := client.Details() details := client.Details()
isBot := client.HasMode(modes.Bot)
params := make([]string, 1, 2) params := make([]string, 1, 2)
params[0] = chname params[0] = chname
if message != "" { if message != "" {
@ -996,7 +999,7 @@ func (channel *Channel) Part(client *Client, message string, rb *ResponseBuffer)
respectAuditorium := channel.flags.HasMode(modes.Auditorium) && respectAuditorium := channel.flags.HasMode(modes.Auditorium) &&
clientData.modes.HighestChannelUserMode() == modes.Mode(0) clientData.modes.HighestChannelUserMode() == modes.Mode(0)
var cache MessageCache var cache MessageCache
cache.Initialize(channel.server, splitMessage.Time, splitMessage.Msgid, details.nickMask, details.accountName, nil, "PART", params...) cache.Initialize(channel.server, splitMessage.Time, splitMessage.Msgid, details.nickMask, details.accountName, isBot, nil, "PART", params...)
for _, member := range channel.Members() { for _, member := range channel.Members() {
if respectAuditorium { if respectAuditorium {
channel.stateMutex.RLock() channel.stateMutex.RLock()
@ -1010,10 +1013,10 @@ func (channel *Channel) Part(client *Client, message string, rb *ResponseBuffer)
cache.Send(session) cache.Send(session)
} }
} }
rb.AddFromClient(splitMessage.Time, splitMessage.Msgid, details.nickMask, details.accountName, nil, "PART", params...) rb.AddFromClient(splitMessage.Time, splitMessage.Msgid, details.nickMask, details.accountName, isBot, nil, "PART", params...)
for _, session := range client.Sessions() { for _, session := range client.Sessions() {
if session != rb.session { if session != rb.session {
session.sendFromClientInternal(false, splitMessage.Time, splitMessage.Msgid, details.nickMask, details.accountName, nil, "PART", params...) session.sendFromClientInternal(false, splitMessage.Time, splitMessage.Msgid, details.nickMask, details.accountName, isBot, nil, "PART", params...)
} }
} }
@ -1023,6 +1026,7 @@ func (channel *Channel) Part(client *Client, message string, rb *ResponseBuffer)
Nick: details.nickMask, Nick: details.nickMask,
AccountName: details.accountName, AccountName: details.accountName,
Message: splitMessage, Message: splitMessage,
IsBot: isBot,
}, details.account) }, details.account)
} }
@ -1133,19 +1137,19 @@ func (channel *Channel) replayHistoryItems(rb *ResponseBuffer, items []history.I
nick := NUHToNick(item.Nick) nick := NUHToNick(item.Nick)
switch item.Type { switch item.Type {
case history.Privmsg: case history.Privmsg:
rb.AddSplitMessageFromClient(item.Nick, item.AccountName, item.Tags, "PRIVMSG", chname, item.Message) rb.AddSplitMessageFromClient(item.Nick, item.AccountName, item.IsBot, item.Tags, "PRIVMSG", chname, item.Message)
case history.Notice: case history.Notice:
rb.AddSplitMessageFromClient(item.Nick, item.AccountName, item.Tags, "NOTICE", chname, item.Message) rb.AddSplitMessageFromClient(item.Nick, item.AccountName, item.IsBot, item.Tags, "NOTICE", chname, item.Message)
case history.Tagmsg: case history.Tagmsg:
if eventPlayback { if eventPlayback {
rb.AddSplitMessageFromClient(item.Nick, item.AccountName, item.Tags, "TAGMSG", chname, item.Message) rb.AddSplitMessageFromClient(item.Nick, item.AccountName, item.IsBot, item.Tags, "TAGMSG", chname, item.Message)
} }
case history.Join: case history.Join:
if eventPlayback { if eventPlayback {
if extendedJoin { if extendedJoin {
rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, nil, "JOIN", chname, item.AccountName, item.Params[0]) rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, item.IsBot, nil, "JOIN", chname, item.AccountName, item.Params[0])
} else { } else {
rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, nil, "JOIN", chname) rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, item.IsBot, nil, "JOIN", chname)
} }
} else { } else {
if !playJoinsAsPrivmsg { if !playJoinsAsPrivmsg {
@ -1157,48 +1161,48 @@ func (channel *Channel) replayHistoryItems(rb *ResponseBuffer, items []history.I
} else { } else {
message = fmt.Sprintf(client.t("%[1]s [account: %[2]s] joined the channel"), nick, item.AccountName) message = fmt.Sprintf(client.t("%[1]s [account: %[2]s] joined the channel"), nick, item.AccountName)
} }
rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histservService.prefix, "*", nil, "PRIVMSG", chname, message) rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histservService.prefix, "*", false, nil, "PRIVMSG", chname, message)
} }
case history.Part: case history.Part:
if eventPlayback { if eventPlayback {
rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, nil, "PART", chname, item.Message.Message) rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, item.IsBot, nil, "PART", chname, item.Message.Message)
} else { } else {
if !playJoinsAsPrivmsg { if !playJoinsAsPrivmsg {
continue // #474 continue // #474
} }
message := fmt.Sprintf(client.t("%[1]s left the channel (%[2]s)"), nick, item.Message.Message) message := fmt.Sprintf(client.t("%[1]s left the channel (%[2]s)"), nick, item.Message.Message)
rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histservService.prefix, "*", nil, "PRIVMSG", chname, message) rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histservService.prefix, "*", false, nil, "PRIVMSG", chname, message)
} }
case history.Kick: case history.Kick:
if eventPlayback { if eventPlayback {
rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, nil, "KICK", chname, item.Params[0], item.Message.Message) rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, item.IsBot, nil, "KICK", chname, item.Params[0], item.Message.Message)
} else { } else {
message := fmt.Sprintf(client.t("%[1]s kicked %[2]s (%[3]s)"), nick, item.Params[0], item.Message.Message) message := fmt.Sprintf(client.t("%[1]s kicked %[2]s (%[3]s)"), nick, item.Params[0], item.Message.Message)
rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histservService.prefix, "*", nil, "PRIVMSG", chname, message) rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histservService.prefix, "*", false, nil, "PRIVMSG", chname, message)
} }
case history.Quit: case history.Quit:
if eventPlayback { if eventPlayback {
rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, nil, "QUIT", item.Message.Message) rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, item.IsBot, nil, "QUIT", item.Message.Message)
} else { } else {
if !playJoinsAsPrivmsg { if !playJoinsAsPrivmsg {
continue // #474 continue // #474
} }
message := fmt.Sprintf(client.t("%[1]s quit (%[2]s)"), nick, item.Message.Message) message := fmt.Sprintf(client.t("%[1]s quit (%[2]s)"), nick, item.Message.Message)
rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histservService.prefix, "*", nil, "PRIVMSG", chname, message) rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histservService.prefix, "*", false, nil, "PRIVMSG", chname, message)
} }
case history.Nick: case history.Nick:
if eventPlayback { if eventPlayback {
rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, nil, "NICK", item.Params[0]) rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, item.IsBot, nil, "NICK", item.Params[0])
} else { } else {
message := fmt.Sprintf(client.t("%[1]s changed nick to %[2]s"), nick, item.Params[0]) message := fmt.Sprintf(client.t("%[1]s changed nick to %[2]s"), nick, item.Params[0])
rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histservService.prefix, "*", nil, "PRIVMSG", chname, message) rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histservService.prefix, "*", false, nil, "PRIVMSG", chname, message)
} }
case history.Topic: case history.Topic:
if eventPlayback { if eventPlayback {
rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, nil, "TOPIC", chname, item.Message.Message) rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, item.IsBot, nil, "TOPIC", chname, item.Message.Message)
} else { } else {
message := fmt.Sprintf(client.t("%[1]s set the channel topic to: %[2]s"), nick, item.Message.Message) message := fmt.Sprintf(client.t("%[1]s set the channel topic to: %[2]s"), nick, item.Message.Message)
rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histservService.prefix, "*", nil, "PRIVMSG", chname, message) rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histservService.prefix, "*", false, nil, "PRIVMSG", chname, message)
} }
case history.Mode: case history.Mode:
params := make([]string, len(item.Message.Split)+1) params := make([]string, len(item.Message.Split)+1)
@ -1207,10 +1211,10 @@ func (channel *Channel) replayHistoryItems(rb *ResponseBuffer, items []history.I
params[i+1] = pair.Message params[i+1] = pair.Message
} }
if eventPlayback { if eventPlayback {
rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, nil, "MODE", params...) rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, item.IsBot, nil, "MODE", params...)
} else { } else {
message := fmt.Sprintf(client.t("%[1]s set channel modes: %[2]s"), nick, strings.Join(params[1:], " ")) message := fmt.Sprintf(client.t("%[1]s set channel modes: %[2]s"), nick, strings.Join(params[1:], " "))
rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histservService.prefix, "*", nil, "PRIVMSG", chname, message) rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histservService.prefix, "*", false, nil, "PRIVMSG", chname, message)
} }
} }
} }
@ -1268,12 +1272,13 @@ func (channel *Channel) SetTopic(client *Client, topic string, rb *ResponseBuffe
channel.stateMutex.Unlock() channel.stateMutex.Unlock()
details := client.Details() details := client.Details()
isBot := client.HasMode(modes.Bot)
message := utils.MakeMessage(topic) message := utils.MakeMessage(topic)
rb.AddFromClient(message.Time, message.Msgid, details.nickMask, details.accountName, nil, "TOPIC", chname, topic) rb.AddFromClient(message.Time, message.Msgid, details.nickMask, details.accountName, isBot, nil, "TOPIC", chname, topic)
for _, member := range channel.Members() { for _, member := range channel.Members() {
for _, session := range member.Sessions() { for _, session := range member.Sessions() {
if session != rb.session { if session != rb.session {
session.sendFromClientInternal(false, message.Time, message.Msgid, details.nickMask, details.accountName, nil, "TOPIC", chname, topic) session.sendFromClientInternal(false, message.Time, message.Msgid, details.nickMask, details.accountName, isBot, nil, "TOPIC", chname, topic)
} }
} }
} }
@ -1397,7 +1402,7 @@ func (channel *Channel) SendSplitMessage(command string, minPrefixMode modes.Mod
rb.addEchoMessage(clientOnlyTags, details.nickMask, details.accountName, command, chname, message) rb.addEchoMessage(clientOnlyTags, details.nickMask, details.accountName, command, chname, message)
var cache MessageCache var cache MessageCache
cache.InitializeSplitMessage(channel.server, details.nickMask, details.accountName, clientOnlyTags, command, chname, message) cache.InitializeSplitMessage(channel.server, details.nickMask, details.accountName, client.HasMode(modes.Bot), clientOnlyTags, command, chname, message)
for _, member := range channel.Members() { for _, member := range channel.Members() {
if minPrefixMode != modes.Mode(0) && !channel.ClientIsAtLeast(member, minPrefixMode) { if minPrefixMode != modes.Mode(0) && !channel.ClientIsAtLeast(member, minPrefixMode) {
// STATUSMSG or OpModerated // STATUSMSG or OpModerated
@ -1519,23 +1524,25 @@ func (channel *Channel) Kick(client *Client, target *Client, comment string, rb
message := utils.MakeMessage(comment) message := utils.MakeMessage(comment)
details := client.Details() details := client.Details()
isBot := client.HasMode(modes.Bot)
targetNick := target.Nick() targetNick := target.Nick()
chname := channel.Name() chname := channel.Name()
for _, member := range channel.Members() { for _, member := range channel.Members() {
for _, session := range member.Sessions() { for _, session := range member.Sessions() {
if session != rb.session { if session != rb.session {
session.sendFromClientInternal(false, message.Time, message.Msgid, details.nickMask, details.accountName, nil, "KICK", chname, targetNick, comment) session.sendFromClientInternal(false, message.Time, message.Msgid, details.nickMask, details.accountName, isBot, nil, "KICK", chname, targetNick, comment)
} }
} }
} }
rb.AddFromClient(message.Time, message.Msgid, details.nickMask, details.accountName, nil, "KICK", chname, targetNick, comment) rb.AddFromClient(message.Time, message.Msgid, details.nickMask, details.accountName, isBot, nil, "KICK", chname, targetNick, comment)
histItem := history.Item{ histItem := history.Item{
Type: history.Kick, Type: history.Kick,
Nick: details.nickMask, Nick: details.nickMask,
AccountName: details.accountName, AccountName: details.accountName,
Message: message, Message: message,
IsBot: isBot,
} }
histItem.Params[0] = targetNick histItem.Params[0] = targetNick
channel.AddHistoryItem(histItem, details.account) channel.AddHistoryItem(histItem, details.account)
@ -1563,7 +1570,7 @@ func (channel *Channel) Purge(source string) {
tnick := member.Nick() tnick := member.Nick()
msgid := utils.GenerateSecretToken() msgid := utils.GenerateSecretToken()
for _, session := range member.Sessions() { for _, session := range member.Sessions() {
session.sendFromClientInternal(false, now, msgid, source, "*", nil, "KICK", chname, tnick, member.t("This channel has been purged by the server administrators and cannot be used")) session.sendFromClientInternal(false, now, msgid, source, "*", false, nil, "KICK", chname, tnick, member.t("This channel has been purged by the server administrators and cannot be used"))
} }
member.removeChannel(channel) member.removeChannel(channel)
} }
@ -1600,6 +1607,7 @@ func (channel *Channel) Invite(invitee *Client, inviter *Client, rb *ResponseBuf
} }
details := inviter.Details() details := inviter.Details()
isBot := inviter.HasMode(modes.Bot)
tDetails := invitee.Details() tDetails := invitee.Details()
tnick := invitee.Nick() tnick := invitee.Nick()
message := utils.MakeMessage(chname) message := utils.MakeMessage(chname)
@ -1614,13 +1622,15 @@ func (channel *Channel) Invite(invitee *Client, inviter *Client, rb *ResponseBuf
} }
for _, session := range member.Sessions() { for _, session := range member.Sessions() {
if session.capabilities.Has(caps.InviteNotify) { if session.capabilities.Has(caps.InviteNotify) {
session.sendFromClientInternal(false, message.Time, message.Msgid, details.nickMask, details.accountName, nil, "INVITE", tnick, chname) session.sendFromClientInternal(false, message.Time, message.Msgid, details.nickMask, details.accountName, isBot, nil, "INVITE", tnick, chname)
} }
} }
} }
rb.Add(nil, inviter.server.name, RPL_INVITING, details.nick, tnick, chname) rb.Add(nil, inviter.server.name, RPL_INVITING, details.nick, tnick, chname)
invitee.sendFromClientInternal(false, message.Time, message.Msgid, details.nickMask, details.accountName, nil, "INVITE", tnick, chname) for _, iSession := range invitee.Sessions() {
iSession.sendFromClientInternal(false, message.Time, message.Msgid, details.nickMask, details.accountName, isBot, nil, "INVITE", tnick, chname)
}
if away, awayMessage := invitee.Away(); away { if away, awayMessage := invitee.Away(); away {
rb.Add(nil, inviter.server.name, RPL_AWAY, details.nick, tnick, awayMessage) rb.Add(nil, inviter.server.name, RPL_AWAY, details.nick, tnick, awayMessage)
} }

View File

@ -271,7 +271,7 @@ func csAmodeHandler(service *ircService, server *Server, client *Client, command
if member.Account() == change.Arg { if member.Account() == change.Arg {
applied, change := channel.applyModeToMember(client, change, rb) applied, change := channel.applyModeToMember(client, change, rb)
if applied { if applied {
announceCmodeChanges(channel, modes.ModeChanges{change}, server.name, "*", "", rb) announceCmodeChanges(channel, modes.ModeChanges{change}, server.name, "*", "", false, rb)
} }
} }
} }
@ -334,7 +334,7 @@ func csOpHandler(service *ircService, server *Server, client *Client, command st
}, },
rb) rb)
if applied { if applied {
announceCmodeChanges(channelInfo, modes.ModeChanges{change}, server.name, "*", "", rb) announceCmodeChanges(channelInfo, modes.ModeChanges{change}, server.name, "*", "", false, rb)
} }
service.Notice(rb, client.t("Successfully granted operator privileges")) service.Notice(rb, client.t("Successfully granted operator privileges"))
@ -386,7 +386,8 @@ func csDeopHandler(service *ircService, server *Server, client *Client, command
// the changes as coming from chanserv // the changes as coming from chanserv
applied := channel.ApplyChannelModeChanges(client, false, modeChanges, rb) applied := channel.ApplyChannelModeChanges(client, false, modeChanges, rb)
details := client.Details() details := client.Details()
announceCmodeChanges(channel, applied, details.nickMask, details.accountName, details.account, rb) isBot := client.HasMode(modes.Bot)
announceCmodeChanges(channel, applied, details.nickMask, details.accountName, details.account, isBot, rb)
if len(applied) == 0 { if len(applied) == 0 {
return return
@ -437,7 +438,7 @@ func csRegisterHandler(service *ircService, server *Server, client *Client, comm
}, },
rb) rb)
if applied { if applied {
announceCmodeChanges(channelInfo, modes.ModeChanges{change}, service.prefix, "*", "", rb) announceCmodeChanges(channelInfo, modes.ModeChanges{change}, service.prefix, "*", "", false, rb)
} }
} }

View File

@ -1095,9 +1095,9 @@ func (client *Client) replayPrivmsgHistory(rb *ResponseBuffer, items []history.I
continue continue
} }
if hasEventPlayback { if hasEventPlayback {
rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, nil, "INVITE", nick, item.Message.Message) rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, item.IsBot, nil, "INVITE", nick, item.Message.Message)
} else { } else {
rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histservService.prefix, "*", nil, "PRIVMSG", fmt.Sprintf(client.t("%[1]s invited you to channel %[2]s"), NUHToNick(item.Nick), item.Message.Message)) rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histservService.prefix, "*", false, nil, "PRIVMSG", fmt.Sprintf(client.t("%[1]s invited you to channel %[2]s"), NUHToNick(item.Nick), item.Message.Message))
} }
continue continue
case history.Privmsg: case history.Privmsg:
@ -1118,11 +1118,11 @@ func (client *Client) replayPrivmsgHistory(rb *ResponseBuffer, items []history.I
tags = item.Tags tags = item.Tags
} }
if !isSelfMessage(&item) { if !isSelfMessage(&item) {
rb.AddSplitMessageFromClient(item.Nick, item.AccountName, tags, command, nick, item.Message) rb.AddSplitMessageFromClient(item.Nick, item.AccountName, item.IsBot, tags, command, nick, item.Message)
} else { } else {
// this message was sent *from* the client to another nick; the target is item.Params[0] // this message was sent *from* the client to another nick; the target is item.Params[0]
// substitute client's current nickmask in case client changed nick // substitute client's current nickmask in case client changed nick
rb.AddSplitMessageFromClient(details.nickMask, item.AccountName, tags, command, item.Params[0], item.Message) rb.AddSplitMessageFromClient(details.nickMask, item.AccountName, item.IsBot, tags, command, item.Params[0], item.Message)
} }
} }
@ -1244,8 +1244,9 @@ func (client *Client) SetOper(oper *Oper) {
// this is annoying to do correctly // this is annoying to do correctly
func (client *Client) sendChghost(oldNickMask string, vhost string) { func (client *Client) sendChghost(oldNickMask string, vhost string) {
details := client.Details() details := client.Details()
isBot := client.HasMode(modes.Bot)
for fClient := range client.Friends(caps.ChgHost) { for fClient := range client.Friends(caps.ChgHost) {
fClient.sendFromClientInternal(false, time.Time{}, "", oldNickMask, details.accountName, nil, "CHGHOST", details.username, vhost) fClient.sendFromClientInternal(false, time.Time{}, "", oldNickMask, details.accountName, isBot, nil, "CHGHOST", details.username, vhost)
} }
} }
@ -1594,14 +1595,16 @@ func (client *Client) destroy(session *Session) {
quitMessage = "Exited" quitMessage = "Exited"
} }
splitQuitMessage := utils.MakeMessage(quitMessage) splitQuitMessage := utils.MakeMessage(quitMessage)
isBot := client.HasMode(modes.Bot)
quitItem = history.Item{ quitItem = history.Item{
Type: history.Quit, Type: history.Quit,
Nick: details.nickMask, Nick: details.nickMask,
AccountName: details.accountName, AccountName: details.accountName,
Message: splitQuitMessage, Message: splitQuitMessage,
IsBot: isBot,
} }
var cache MessageCache var cache MessageCache
cache.Initialize(client.server, splitQuitMessage.Time, splitQuitMessage.Msgid, details.nickMask, details.accountName, nil, "QUIT", quitMessage) cache.Initialize(client.server, splitQuitMessage.Time, splitQuitMessage.Msgid, details.nickMask, details.accountName, isBot, nil, "QUIT", quitMessage)
for friend := range friends { for friend := range friends {
for _, session := range friend.Sessions() { for _, session := range friend.Sessions() {
cache.Send(session) cache.Send(session)
@ -1615,12 +1618,12 @@ func (client *Client) destroy(session *Session) {
// SendSplitMsgFromClient sends an IRC PRIVMSG/NOTICE coming from a specific client. // SendSplitMsgFromClient sends an IRC PRIVMSG/NOTICE coming from a specific client.
// Adds account-tag to the line as well. // Adds account-tag to the line as well.
func (session *Session) sendSplitMsgFromClientInternal(blocking bool, nickmask, accountName string, tags map[string]string, command, target string, message utils.SplitMessage) { func (session *Session) sendSplitMsgFromClientInternal(blocking bool, nickmask, accountName string, isBot bool, tags map[string]string, command, target string, message utils.SplitMessage) {
if message.Is512() { if message.Is512() {
session.sendFromClientInternal(blocking, message.Time, message.Msgid, nickmask, accountName, tags, command, target, message.Message) session.sendFromClientInternal(blocking, message.Time, message.Msgid, nickmask, accountName, isBot, tags, command, target, message.Message)
} else { } else {
if session.capabilities.Has(caps.Multiline) { if session.capabilities.Has(caps.Multiline) {
for _, msg := range composeMultilineBatch(session.generateBatchID(), nickmask, accountName, tags, command, target, message) { for _, msg := range composeMultilineBatch(session.generateBatchID(), nickmask, accountName, isBot, tags, command, target, message) {
session.SendRawMessage(msg, blocking) session.SendRawMessage(msg, blocking)
} }
} else { } else {
@ -1634,24 +1637,13 @@ func (session *Session) sendSplitMsgFromClientInternal(blocking bool, nickmask,
msgidSent = true msgidSent = true
msgid = message.Msgid msgid = message.Msgid
} }
session.sendFromClientInternal(blocking, message.Time, msgid, nickmask, accountName, tags, command, target, messagePair.Message) session.sendFromClientInternal(blocking, message.Time, msgid, nickmask, accountName, isBot, tags, command, target, messagePair.Message)
} }
} }
} }
} }
// Sends a line with `nickmask` as the prefix, adding `time` and `account` tags if supported func (session *Session) sendFromClientInternal(blocking bool, serverTime time.Time, msgid string, nickmask, accountName string, isBot bool, tags map[string]string, command string, params ...string) (err error) {
func (client *Client) sendFromClientInternal(blocking bool, serverTime time.Time, msgid string, nickmask, accountName string, tags map[string]string, command string, params ...string) (err error) {
for _, session := range client.Sessions() {
err_ := session.sendFromClientInternal(blocking, serverTime, msgid, nickmask, accountName, tags, command, params...)
if err_ != nil {
err = err_
}
}
return
}
func (session *Session) sendFromClientInternal(blocking bool, serverTime time.Time, msgid string, nickmask, accountName string, tags map[string]string, command string, params ...string) (err error) {
msg := ircmsg.MakeMessage(tags, nickmask, command, params...) msg := ircmsg.MakeMessage(tags, nickmask, command, params...)
// attach account-tag // attach account-tag
if session.capabilities.Has(caps.AccountTag) && accountName != "*" { if session.capabilities.Has(caps.AccountTag) && accountName != "*" {
@ -1663,17 +1655,24 @@ func (session *Session) sendFromClientInternal(blocking bool, serverTime time.Ti
} }
// attach server-time // attach server-time
session.setTimeTag(&msg, serverTime) session.setTimeTag(&msg, serverTime)
// attach bot tag
if isBot && session.capabilities.Has(caps.MessageTags) {
msg.SetTag(caps.BotTagName, "")
}
return session.SendRawMessage(msg, blocking) return session.SendRawMessage(msg, blocking)
} }
func composeMultilineBatch(batchID, fromNickMask, fromAccount string, tags map[string]string, command, target string, message utils.SplitMessage) (result []ircmsg.Message) { func composeMultilineBatch(batchID, fromNickMask, fromAccount string, isBot bool, tags map[string]string, command, target string, message utils.SplitMessage) (result []ircmsg.Message) {
batchStart := ircmsg.MakeMessage(tags, fromNickMask, "BATCH", "+"+batchID, caps.MultilineBatchType, target) batchStart := ircmsg.MakeMessage(tags, fromNickMask, "BATCH", "+"+batchID, caps.MultilineBatchType, target)
batchStart.SetTag("time", message.Time.Format(IRCv3TimestampFormat)) batchStart.SetTag("time", message.Time.Format(IRCv3TimestampFormat))
batchStart.SetTag("msgid", message.Msgid) batchStart.SetTag("msgid", message.Msgid)
if fromAccount != "*" { if fromAccount != "*" {
batchStart.SetTag("account", fromAccount) batchStart.SetTag("account", fromAccount)
} }
if isBot {
batchStart.SetTag(caps.BotTagName, "")
}
result = append(result, batchStart) result = append(result, batchStart)
for _, msg := range message.Split { for _, msg := range message.Split {

View File

@ -367,11 +367,12 @@ func awayHandler(server *Server, client *Client, msg ircmsg.Message, rb *Respons
func dispatchAwayNotify(client *Client, isAway bool, awayMessage string) { func dispatchAwayNotify(client *Client, isAway bool, awayMessage string) {
// dispatch away-notify // dispatch away-notify
details := client.Details() details := client.Details()
isBot := client.HasMode(modes.Bot)
for session := range client.Friends(caps.AwayNotify) { for session := range client.Friends(caps.AwayNotify) {
if isAway { if isAway {
session.sendFromClientInternal(false, time.Time{}, "", details.nickMask, details.accountName, nil, "AWAY", awayMessage) session.sendFromClientInternal(false, time.Time{}, "", details.nickMask, details.accountName, isBot, nil, "AWAY", awayMessage)
} else { } else {
session.sendFromClientInternal(false, time.Time{}, "", details.nickMask, details.accountName, nil, "AWAY") session.sendFromClientInternal(false, time.Time{}, "", details.nickMask, details.accountName, isBot, nil, "AWAY")
} }
} }
} }
@ -1689,12 +1690,13 @@ func cmodeHandler(server *Server, client *Client, msg ircmsg.Message, rb *Respon
// process mode changes, include list operations (an empty set of changes does a list) // process mode changes, include list operations (an empty set of changes does a list)
applied := channel.ApplyChannelModeChanges(client, msg.Command == "SAMODE", changes, rb) applied := channel.ApplyChannelModeChanges(client, msg.Command == "SAMODE", changes, rb)
details := client.Details() details := client.Details()
announceCmodeChanges(channel, applied, details.nickMask, details.accountName, details.account, rb) isBot := client.HasMode(modes.Bot)
announceCmodeChanges(channel, applied, details.nickMask, details.accountName, details.account, isBot, rb)
return false return false
} }
func announceCmodeChanges(channel *Channel, applied modes.ModeChanges, source, accountName, account string, rb *ResponseBuffer) { func announceCmodeChanges(channel *Channel, applied modes.ModeChanges, source, accountName, account string, isBot bool, rb *ResponseBuffer) {
// send out changes // send out changes
if len(applied) > 0 { if len(applied) > 0 {
message := utils.MakeMessage("") message := utils.MakeMessage("")
@ -1703,11 +1705,11 @@ func announceCmodeChanges(channel *Channel, applied modes.ModeChanges, source, a
message.Split = append(message.Split, utils.MessagePair{Message: changeString}) message.Split = append(message.Split, utils.MessagePair{Message: changeString})
} }
args := append([]string{channel.name}, changeStrings...) args := append([]string{channel.name}, changeStrings...)
rb.AddFromClient(message.Time, message.Msgid, source, accountName, nil, "MODE", args...) rb.AddFromClient(message.Time, message.Msgid, source, accountName, isBot, nil, "MODE", args...)
for _, member := range channel.Members() { for _, member := range channel.Members() {
for _, session := range member.Sessions() { for _, session := range member.Sessions() {
if session != rb.session { if session != rb.session {
session.sendFromClientInternal(false, message.Time, message.Msgid, source, accountName, nil, "MODE", args...) session.sendFromClientInternal(false, message.Time, message.Msgid, source, accountName, isBot, nil, "MODE", args...)
} }
} }
} }
@ -1716,6 +1718,7 @@ func announceCmodeChanges(channel *Channel, applied modes.ModeChanges, source, a
Nick: source, Nick: source,
AccountName: accountName, AccountName: accountName,
Message: message, Message: message,
IsBot: isBot,
}, account) }, account)
} }
} }
@ -2204,17 +2207,18 @@ func dispatchMessageToTarget(client *Client, tags map[string]string, histType hi
} }
} }
isBot := client.HasMode(modes.Bot)
for _, session := range deliverySessions { for _, session := range deliverySessions {
hasTagsCap := session.capabilities.Has(caps.MessageTags) hasTagsCap := session.capabilities.Has(caps.MessageTags)
// don't send TAGMSG at all if they don't have the tags cap // don't send TAGMSG at all if they don't have the tags cap
if histType == history.Tagmsg && hasTagsCap { if histType == history.Tagmsg && hasTagsCap {
session.sendFromClientInternal(false, message.Time, message.Msgid, nickMaskString, accountName, tags, command, tnick) session.sendFromClientInternal(false, message.Time, message.Msgid, nickMaskString, accountName, isBot, tags, command, tnick)
} else if histType != history.Tagmsg && !(session.isTor && message.IsRestrictedCTCPMessage()) { } else if histType != history.Tagmsg && !(session.isTor && message.IsRestrictedCTCPMessage()) {
tagsToSend := tags tagsToSend := tags
if !hasTagsCap { if !hasTagsCap {
tagsToSend = nil tagsToSend = nil
} }
session.sendSplitMsgFromClientInternal(false, nickMaskString, accountName, tagsToSend, command, tnick, message) session.sendSplitMsgFromClientInternal(false, nickMaskString, accountName, isBot, tagsToSend, command, tnick, message)
} }
} }
@ -2674,9 +2678,9 @@ func relaymsgHandler(server *Server, client *Client, msg ircmsg.Message, rb *Res
} }
if session == rb.session { if session == rb.session {
rb.AddSplitMessageFromClient(nick, "*", tagsToUse, "PRIVMSG", channelName, message) rb.AddSplitMessageFromClient(nick, "*", false, tagsToUse, "PRIVMSG", channelName, message)
} else { } else {
session.sendSplitMsgFromClientInternal(false, nick, "*", tagsToUse, "PRIVMSG", channelName, message) session.sendSplitMsgFromClientInternal(false, nick, "*", false, tagsToUse, "PRIVMSG", channelName, message)
} }
} }
} }
@ -2835,11 +2839,12 @@ func setnameHandler(server *Server, client *Client, msg ircmsg.Message, rb *Resp
now := time.Now().UTC() now := time.Now().UTC()
friends := client.Friends(caps.SetName) friends := client.Friends(caps.SetName)
delete(friends, rb.session) delete(friends, rb.session)
isBot := client.HasMode(modes.Bot)
for session := range friends { for session := range friends {
session.sendFromClientInternal(false, now, "", details.nickMask, details.accountName, nil, "SETNAME", details.realname) session.sendFromClientInternal(false, now, "", details.nickMask, details.accountName, isBot, nil, "SETNAME", details.realname)
} }
// respond to the user unconditionally, even if they don't have the cap // respond to the user unconditionally, even if they don't have the cap
rb.AddFromClient(now, "", details.nickMask, details.accountName, nil, "SETNAME", details.realname) rb.AddFromClient(now, "", details.nickMask, details.accountName, isBot, nil, "SETNAME", details.realname)
return false return false
} }

View File

@ -45,6 +45,7 @@ type Item struct {
// an incoming or outgoing message). this lets us emulate the "query buffer" functionality // an incoming or outgoing message). this lets us emulate the "query buffer" functionality
// required by CHATHISTORY: // required by CHATHISTORY:
CfCorrespondent string CfCorrespondent string
IsBot bool `json:"IsBot,omitempty"`
} }
// HasMsgid tests whether a message has the message id `msgid`. // HasMsgid tests whether a message has the message id `msgid`.

View File

@ -35,6 +35,7 @@ type MessageCache struct {
tags map[string]string tags map[string]string
source string source string
command string command string
isBot bool
params []string params []string
@ -42,7 +43,7 @@ type MessageCache struct {
splitMessage utils.SplitMessage splitMessage utils.SplitMessage
} }
func addAllTags(msg *ircmsg.Message, tags map[string]string, serverTime time.Time, msgid, accountName string) { func addAllTags(msg *ircmsg.Message, tags map[string]string, serverTime time.Time, msgid, accountName string, isBot bool) {
msg.UpdateTags(tags) msg.UpdateTags(tags)
msg.SetTag("time", serverTime.Format(IRCv3TimestampFormat)) msg.SetTag("time", serverTime.Format(IRCv3TimestampFormat))
if accountName != "*" { if accountName != "*" {
@ -51,6 +52,9 @@ func addAllTags(msg *ircmsg.Message, tags map[string]string, serverTime time.Tim
if msgid != "" { if msgid != "" {
msg.SetTag("msgid", msgid) msg.SetTag("msgid", msgid)
} }
if isBot {
msg.SetTag(caps.BotTagName, "")
}
} }
func (m *MessageCache) handleErr(server *Server, err error) bool { func (m *MessageCache) handleErr(server *Server, err error) bool {
@ -64,11 +68,12 @@ func (m *MessageCache) handleErr(server *Server, err error) bool {
return false return false
} }
func (m *MessageCache) Initialize(server *Server, serverTime time.Time, msgid string, nickmask, accountName string, tags map[string]string, command string, params ...string) (err error) { func (m *MessageCache) Initialize(server *Server, serverTime time.Time, msgid string, nickmask, accountName string, isBot bool, tags map[string]string, command string, params ...string) (err error) {
m.time = serverTime m.time = serverTime
m.msgid = msgid m.msgid = msgid
m.source = nickmask m.source = nickmask
m.accountName = accountName m.accountName = accountName
m.isBot = isBot
m.tags = tags m.tags = tags
m.command = command m.command = command
m.params = params m.params = params
@ -87,7 +92,7 @@ func (m *MessageCache) Initialize(server *Server, serverTime time.Time, msgid st
return return
} }
addAllTags(&msg, tags, serverTime, msgid, accountName) addAllTags(&msg, tags, serverTime, msgid, accountName, isBot)
m.fullTags, err = msg.LineBytesStrict(false, MaxLineLen) m.fullTags, err = msg.LineBytesStrict(false, MaxLineLen)
if m.handleErr(server, err) { if m.handleErr(server, err) {
return return
@ -95,11 +100,12 @@ func (m *MessageCache) Initialize(server *Server, serverTime time.Time, msgid st
return return
} }
func (m *MessageCache) InitializeSplitMessage(server *Server, nickmask, accountName string, tags map[string]string, command, target string, message utils.SplitMessage) (err error) { func (m *MessageCache) InitializeSplitMessage(server *Server, nickmask, accountName string, isBot bool, tags map[string]string, command, target string, message utils.SplitMessage) (err error) {
m.time = message.Time m.time = message.Time
m.msgid = message.Msgid m.msgid = message.Msgid
m.source = nickmask m.source = nickmask
m.accountName = accountName m.accountName = accountName
m.isBot = isBot
m.tags = tags m.tags = tags
m.command = command m.command = command
m.target = target m.target = target
@ -130,7 +136,7 @@ func (m *MessageCache) InitializeSplitMessage(server *Server, nickmask, accountN
} }
} }
addAllTags(&msg, tags, message.Time, message.Msgid, accountName) addAllTags(&msg, tags, message.Time, message.Msgid, accountName, isBot)
m.fullTags, err = msg.LineBytesStrict(false, MaxLineLen) m.fullTags, err = msg.LineBytesStrict(false, MaxLineLen)
if m.handleErr(server, err) { if m.handleErr(server, err) {
return return
@ -158,7 +164,7 @@ func (m *MessageCache) InitializeSplitMessage(server *Server, nickmask, accountN
// so a collision isn't expected until there are on the order of 2**32 // so a collision isn't expected until there are on the order of 2**32
// concurrent batches being relayed: // concurrent batches being relayed:
batchID := utils.GenerateSecretToken()[:utils.SecretTokenLength/2] batchID := utils.GenerateSecretToken()[:utils.SecretTokenLength/2]
batch := composeMultilineBatch(batchID, nickmask, accountName, tags, command, target, message) batch := composeMultilineBatch(batchID, nickmask, accountName, isBot, tags, command, target, message)
m.fullTagsMultiline = make([][]byte, len(batch)) m.fullTagsMultiline = make([][]byte, len(batch))
for i, msg := range batch { for i, msg := range batch {
if forceTrailing { if forceTrailing {
@ -184,7 +190,7 @@ func (m *MessageCache) Send(session *Session) {
session.sendBytes(m.plain, false) session.sendBytes(m.plain, false)
} else { } else {
// slowpath // slowpath
session.sendFromClientInternal(false, m.time, m.msgid, m.source, m.accountName, nil, m.command, m.params...) session.sendFromClientInternal(false, m.time, m.msgid, m.source, m.accountName, m.isBot, nil, m.command, m.params...)
} }
} }
} else if m.fullTagsMultiline != nil { } else if m.fullTagsMultiline != nil {
@ -199,7 +205,7 @@ func (m *MessageCache) Send(session *Session) {
} }
} else { } else {
// slowpath // slowpath
session.sendSplitMsgFromClientInternal(false, m.source, m.accountName, m.tags, m.command, m.target, m.splitMessage) session.sendSplitMsgFromClientInternal(false, m.source, m.accountName, m.isBot, m.tags, m.command, m.target, m.splitMessage)
} }
} }
} }

View File

@ -11,6 +11,7 @@ import (
"github.com/goshuirc/irc-go/ircfmt" "github.com/goshuirc/irc-go/ircfmt"
"github.com/oragono/oragono/irc/history" "github.com/oragono/oragono/irc/history"
"github.com/oragono/oragono/irc/modes"
"github.com/oragono/oragono/irc/sno" "github.com/oragono/oragono/irc/sno"
"github.com/oragono/oragono/irc/utils" "github.com/oragono/oragono/irc/utils"
) )
@ -101,10 +102,11 @@ func performNickChange(server *Server, client *Client, target *Client, session *
target.server.snomasks.Send(sno.LocalNicks, fmt.Sprintf(ircfmt.Unescape("Operator %s changed nickname of $%s$r to %s"), client.Nick(), details.nick, assignedNickname)) target.server.snomasks.Send(sno.LocalNicks, fmt.Sprintf(ircfmt.Unescape("Operator %s changed nickname of $%s$r to %s"), client.Nick(), details.nick, assignedNickname))
} }
target.server.whoWas.Append(details.WhoWas) target.server.whoWas.Append(details.WhoWas)
rb.AddFromClient(message.Time, message.Msgid, origNickMask, details.accountName, nil, "NICK", assignedNickname) isBot := !isSanick && client.HasMode(modes.Bot)
rb.AddFromClient(message.Time, message.Msgid, origNickMask, details.accountName, isBot, nil, "NICK", assignedNickname)
for session := range target.Friends() { for session := range target.Friends() {
if session != rb.session { if session != rb.session {
session.sendFromClientInternal(false, message.Time, message.Msgid, origNickMask, details.accountName, nil, "NICK", assignedNickname) session.sendFromClientInternal(false, message.Time, message.Msgid, origNickMask, details.accountName, isBot, nil, "NICK", assignedNickname)
} }
} }
} }

View File

@ -96,7 +96,7 @@ func (rb *ResponseBuffer) Broadcast(tags map[string]string, prefix string, comma
} }
// AddFromClient adds a new message from a specific client to our queue. // AddFromClient adds a new message from a specific client to our queue.
func (rb *ResponseBuffer) AddFromClient(time time.Time, msgid string, fromNickMask string, fromAccount string, tags map[string]string, command string, params ...string) { func (rb *ResponseBuffer) AddFromClient(time time.Time, msgid string, fromNickMask string, fromAccount string, isBot bool, tags map[string]string, command string, params ...string) {
msg := ircmsg.MakeMessage(nil, fromNickMask, command, params...) msg := ircmsg.MakeMessage(nil, fromNickMask, command, params...)
if rb.session.capabilities.Has(caps.MessageTags) { if rb.session.capabilities.Has(caps.MessageTags) {
msg.UpdateTags(tags) msg.UpdateTags(tags)
@ -107,9 +107,14 @@ func (rb *ResponseBuffer) AddFromClient(time time.Time, msgid string, fromNickMa
msg.SetTag("account", fromAccount) msg.SetTag("account", fromAccount)
} }
// attach message-id // attach message-id
if len(msgid) > 0 && rb.session.capabilities.Has(caps.MessageTags) { if rb.session.capabilities.Has(caps.MessageTags) {
if len(msgid) != 0 {
msg.SetTag("msgid", msgid) msg.SetTag("msgid", msgid)
} }
if isBot {
msg.SetTag(caps.BotTagName, "")
}
}
// attach server-time // attach server-time
rb.session.setTimeTag(&msg, time) rb.session.setTimeTag(&msg, time)
@ -117,17 +122,17 @@ func (rb *ResponseBuffer) AddFromClient(time time.Time, msgid string, fromNickMa
} }
// AddSplitMessageFromClient adds a new split message from a specific client to our queue. // AddSplitMessageFromClient adds a new split message from a specific client to our queue.
func (rb *ResponseBuffer) AddSplitMessageFromClient(fromNickMask string, fromAccount string, tags map[string]string, command string, target string, message utils.SplitMessage) { func (rb *ResponseBuffer) AddSplitMessageFromClient(fromNickMask string, fromAccount string, isBot bool, tags map[string]string, command string, target string, message utils.SplitMessage) {
if message.Is512() { if message.Is512() {
if message.Message == "" { if message.Message == "" {
// XXX this is a TAGMSG // XXX this is a TAGMSG
rb.AddFromClient(message.Time, message.Msgid, fromNickMask, fromAccount, tags, command, target) rb.AddFromClient(message.Time, message.Msgid, fromNickMask, fromAccount, isBot, tags, command, target)
} else { } else {
rb.AddFromClient(message.Time, message.Msgid, fromNickMask, fromAccount, tags, command, target, message.Message) rb.AddFromClient(message.Time, message.Msgid, fromNickMask, fromAccount, isBot, tags, command, target, message.Message)
} }
} else { } else {
if rb.session.capabilities.Has(caps.Multiline) { if rb.session.capabilities.Has(caps.Multiline) {
batch := composeMultilineBatch(rb.session.generateBatchID(), fromNickMask, fromAccount, tags, command, target, message) batch := composeMultilineBatch(rb.session.generateBatchID(), fromNickMask, fromAccount, isBot, tags, command, target, message)
rb.setNestedBatchTag(&batch[0]) rb.setNestedBatchTag(&batch[0])
rb.setNestedBatchTag(&batch[len(batch)-1]) rb.setNestedBatchTag(&batch[len(batch)-1])
rb.messages = append(rb.messages, batch...) rb.messages = append(rb.messages, batch...)
@ -137,25 +142,26 @@ func (rb *ResponseBuffer) AddSplitMessageFromClient(fromNickMask string, fromAcc
if i == 0 { if i == 0 {
msgid = message.Msgid msgid = message.Msgid
} }
rb.AddFromClient(message.Time, msgid, fromNickMask, fromAccount, tags, command, target, messagePair.Message) rb.AddFromClient(message.Time, msgid, fromNickMask, fromAccount, isBot, tags, command, target, messagePair.Message)
} }
} }
} }
} }
func (rb *ResponseBuffer) addEchoMessage(tags map[string]string, nickMask, accountName, command, target string, message utils.SplitMessage) { func (rb *ResponseBuffer) addEchoMessage(tags map[string]string, nickMask, accountName, command, target string, message utils.SplitMessage) {
// TODO fix isBot here
if rb.session.capabilities.Has(caps.EchoMessage) { if rb.session.capabilities.Has(caps.EchoMessage) {
hasTagsCap := rb.session.capabilities.Has(caps.MessageTags) hasTagsCap := rb.session.capabilities.Has(caps.MessageTags)
if command == "TAGMSG" { if command == "TAGMSG" {
if hasTagsCap { if hasTagsCap {
rb.AddFromClient(message.Time, message.Msgid, nickMask, accountName, tags, command, target) rb.AddFromClient(message.Time, message.Msgid, nickMask, accountName, false, tags, command, target)
} }
} else { } else {
tagsToSend := tags tagsToSend := tags
if !hasTagsCap { if !hasTagsCap {
tagsToSend = nil tagsToSend = nil
} }
rb.AddSplitMessageFromClient(nickMask, accountName, tagsToSend, command, target, message) rb.AddSplitMessageFromClient(nickMask, accountName, false, tagsToSend, command, target, message)
} }
} }
} }

View File

@ -92,15 +92,16 @@ func sendRoleplayMessage(server *Server, client *Client, source string, targetSt
return return
} }
isBot := client.HasMode(modes.Bot)
for _, member := range channel.Members() { for _, member := range channel.Members() {
for _, session := range member.Sessions() { for _, session := range member.Sessions() {
// see discussion on #865: clients do not understand how to do local echo // see discussion on #865: clients do not understand how to do local echo
// of roleplay commands, so send them a copy whether they have echo-message // of roleplay commands, so send them a copy whether they have echo-message
// or not // or not
if rb.session == session { if rb.session == session {
rb.AddSplitMessageFromClient(sourceMask, "", nil, "PRIVMSG", targetString, splitMessage) rb.AddSplitMessageFromClient(sourceMask, "*", isBot, nil, "PRIVMSG", targetString, splitMessage)
} else { } else {
session.sendSplitMsgFromClientInternal(false, sourceMask, "*", nil, "PRIVMSG", targetString, splitMessage) session.sendSplitMsgFromClientInternal(false, sourceMask, "*", isBot, nil, "PRIVMSG", targetString, splitMessage)
} }
} }
} }
@ -125,8 +126,9 @@ func sendRoleplayMessage(server *Server, client *Client, source string, targetSt
cnick := client.Nick() cnick := client.Nick()
tnick := user.Nick() tnick := user.Nick()
isBot := client.HasMode(modes.Bot)
for _, session := range user.Sessions() { for _, session := range user.Sessions() {
session.sendSplitMsgFromClientInternal(false, sourceMask, "*", nil, "PRIVMSG", tnick, splitMessage) session.sendSplitMsgFromClientInternal(false, sourceMask, "*", isBot, nil, "PRIVMSG", tnick, splitMessage)
} }
if away, awayMessage := user.Away(); away { if away, awayMessage := user.Away(); away {
//TODO(dan): possibly implement cooldown of away notifications to users //TODO(dan): possibly implement cooldown of away notifications to users