summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog23
-rw-r--r--lib/rbot/ircbot.rb194
-rw-r--r--lib/rbot/message.rb18
3 files changed, 158 insertions, 77 deletions
diff --git a/ChangeLog b/ChangeLog
index ababc59d..b6df965e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,26 @@
+2007-02-05 Giuseppe Bilotta <giuseppe.bilotta@gmail.com>
+
+ * sendmsg improvements: the bot sendmsg() method and all the methods
+ that rely on it (say(), notice(), and message methods such as reply())
+ now accept an option hash to customize its behaviour.
+ * :newlines, which can be set to either :split or :join,
+ depending on whether newlines in messages should be used as
+ split-points, or should be be replaced by some string (defined
+ in the :join_with option)
+ * :max_lines, which determines the maximum number of lines to be
+ sent by each messages. Last line is truncated (see next
+ options). Set to nil to have no limits.
+ * :overlong, which determines the behaviour when overlong lines
+ are to be sent; possible values are :split or :truncate.
+ * If :overlong is set to :split, :split_at determines the
+ string/regexp to split at; default is /\s+/, other usual
+ choice could be /\s+\|\s+/.
+ * If :overlong is set to :split, the :purge_split option
+ determines whether the :split_at expression should be removed
+ from the next lines (defaults to true).
+ * If :overlong is set to :truncate, the value of the option
+ :truncate_text is replaced at the end of the truncated line
+
2007-02-03 Giuseppe Bilotta <giuseppe.bilotta@gmail.com>
* Plugin message mapper: Enhancements to the :requirements option.
diff --git a/lib/rbot/ircbot.rb b/lib/rbot/ircbot.rb
index 5cb9c5d8..956415f9 100644
--- a/lib/rbot/ircbot.rb
+++ b/lib/rbot/ircbot.rb
@@ -555,6 +555,27 @@ class IrcBot
#debug "UNKNOWN: #{data[:serverstring]}"
irclog data[:serverstring], ".unknown"
}
+
+ set_default_send_options
+ end
+
+ def set_default_send_options
+ # Default send options for NOTICE and PRIVMSG
+ # TODO document, for plugin writers
+ # TODO some of these options, like :truncate_text and :max_lines,
+ # should be made into config variables that trigger this routine on change
+ @default_send_options = {
+ :queue_channel => nil, # use default queue channel
+ :queue_ring => nil, # use default queue ring
+ :newlines => :split, # or :join
+ :join_with => ' ', # by default, use a single space
+ :max_lines => nil, # maximum number of lines to send with a single command
+ :overlong => :split, # or :truncate
+ # TODO an array of splitpoints would be preferrable for this option:
+ :split_at => /\s+/, # by default, split overlong lines at whitespace
+ :purge_split => true, # should the split string be removed?
+ :truncate_text => "#{Reverse}...#{Reverse}" # text to be appened when truncating
+ }
end
# checks if we should be quiet on a channel
@@ -690,7 +711,43 @@ class IrcBot
# Type can be PRIVMSG, NOTICE, etc, but those you should really use the
# relevant say() or notice() methods. This one should be used for IRCd
# extensions you want to use in modules.
- def sendmsg(type, where, message, chan=nil, ring=0)
+ def sendmsg(type, where, original_message, options={})
+ opts = @default_send_options.merge(options)
+
+ # For starters, set up appropriate queue channels and rings
+ mchan = opts[:queue_channel]
+ mring = opts[:queue_ring]
+ if mchan
+ chan = mchan
+ else
+ chan = where
+ end
+ if mring
+ ring = mring
+ else
+ case where
+ when User
+ ring = 1
+ else
+ ring = 2
+ end
+ end
+
+ message = original_message.to_s.gsub(/[\r\n]+/, "\n")
+ case opts[:newlines]
+ when :join
+ lines = [message.gsub("\n", opts[:join_with])]
+ when :split
+ lines = Array.new
+ message.each_line { |line|
+ line.chomp!
+ next unless(line.length > 0)
+ lines << line
+ }
+ else
+ raise "Unknown :newlines option #{opts[:newlines]} while sending #{original_message.inspect}"
+ end
+
# The IRC protocol requires that each raw message must be not longer
# than 512 characters. From this length with have to subtract the EOL
# terminators (CR+LF) and the length of ":botnick!botuser@bothost "
@@ -713,21 +770,55 @@ class IrcBot
# And this is what's left
left = max_len - fixed.length
- begin
- if(left >= message.length)
- sendq "#{fixed}#{message}", chan, ring
- log_sent(type, where, message)
- return
- end
- line = message.slice!(0, left)
- lastspace = line.rindex(/\s+/)
- if(lastspace)
- message = line.slice!(lastspace, line.length) + message
- message.gsub!(/^\s+/, "")
- end
- sendq "#{fixed}#{line}", chan, ring
- log_sent(type, where, line)
- end while(message.length > 0)
+
+ case opts[:overlong]
+ when :split
+ truncate = false
+ split_at = opts[:split_at]
+ when :truncate
+ truncate = opts[:truncate_text]
+ truncate = @default_send_options[:truncate_text] if truncate.length > left
+ truncate = "" if truncate.length > left
+ else
+ raise "Unknown :overlong option #{opts[:overlong]} while sending #{original_message.inspect}"
+ end
+
+ # Counter to check the number of lines sent by this command
+ cmd_lines = 0
+ max_lines = opts[:max_lines]
+ line = String.new
+ lines.each { |msg|
+ begin
+ if(left >= msg.length)
+ sendq "#{fixed}#{msg}", chan, ring
+ log_sent(type, where, msg)
+ return
+ end
+ if opts[:max_lines] and cmd_lines == max_lines - 1
+ debug "Max lines count reached for message #{original_message.inspect} while sending #{msg.inspect}, truncating"
+ truncate = opts[:truncate_text]
+ truncate = @default_send_options[:truncate_text] if truncate.length > left
+ truncate = "" if truncate.length > left
+ end
+ if truncate
+ line.replace msg.slice(0, left-truncate.length)
+ line.sub!(/\s+\S*$/, truncate)
+ raise "PROGRAMMER ERROR! #{line.inspect} of length #{line.length} > #{left}" if line.length > left
+ sendq "#{fixed}#{line}", chan, ring
+ log_sent(type, where, line)
+ return
+ end
+ line.replace msg.slice!(0, left)
+ lastspace = line.rindex(opts[:split_at])
+ if(lastspace)
+ msg.replace line.slice!(lastspace, line.length) + msg
+ msg.gsub!(/^#{opts[:split_at]}/, "") if opts[:purge_split]
+ end
+ sendq "#{fixed}#{line}", chan, ring
+ log_sent(type, where, line)
+ cmd_lines += 1
+ end while(msg.length > 0)
+ }
end
# queue an arbitraty message for the server
@@ -737,72 +828,39 @@ class IrcBot
end
# send a notice message to channel/nick +where+
- def notice(where, message, mchan="", mring=-1)
- if mchan == ""
- chan = where
- else
- chan = mchan
- end
- if mring < 0
- case where
- when User
- ring = 1
- else
- ring = 2
- end
- else
- ring = mring
+ def notice(where, message, options={})
+ unless quiet_on?(where)
+ sendmsg "NOTICE", where, message, options
end
- message.each_line { |line|
- line.chomp!
- next unless(line.length > 0)
- sendmsg "NOTICE", where, line, chan, ring
- }
end
# say something (PRIVMSG) to channel/nick +where+
- def say(where, message, mchan="", mring=-1)
- if mchan == ""
- chan = where
- else
- chan = mchan
- end
- if mring < 0
- case where
- when User
- ring = 1
- else
- ring = 2
- end
- else
- ring = mring
+ def say(where, message, options={})
+ unless quiet_on?(where)
+ sendmsg "PRIVMSG", where, message, options
end
- message.to_s.gsub(/[\r\n]+/, "\n").each_line { |line|
- line.chomp!
- next unless(line.length > 0)
- unless quiet_on?(where)
- sendmsg "PRIVMSG", where, line, chan, ring
- end
- }
end
# perform a CTCP action with message +message+ to channel/nick +where+
- def action(where, message, mchan="", mring=-1)
- if mchan == ""
- chan = where
- else
+ def action(where, message, options={})
+ mchan = options.fetch(:queue_channel, nil)
+ mring = options.fetch(:queue_ring, nil)
+ if mchan
chan = mchan
+ else
+ chan = where
end
- if mring < 0
+ if mring
+ ring = mring
+ else
case where
- when Channel
- ring = 2
- else
+ when User
ring = 1
+ else
+ ring = 2
end
- else
- ring = mring
end
+ # FIXME doesn't check message length. Can we make this exploit sendmsg?
sendq "PRIVMSG #{where} :\001ACTION #{message}\001", chan, ring
case where
when Channel
diff --git a/lib/rbot/message.rb b/lib/rbot/message.rb
index df5ac8d3..62ba7469 100644
--- a/lib/rbot/message.rb
+++ b/lib/rbot/message.rb
@@ -214,27 +214,27 @@ module Irc
# <tt>@bot.say m.replyto, string</tt>
# So if the message is private, it will reply to the user. If it was
# in a channel, it will reply in the channel.
- def plainreply(string)
- @bot.say @replyto, string
+ def plainreply(string, options={})
+ @bot.say @replyto, string, options
@replied = true
end
# Same as reply, but when replying in public it adds the nick of the user
# the bot is replying to
- def nickreply(string)
+ def nickreply(string, options={})
extra = self.public? ? "#{@source}#{@bot.config['core.nick_postfix']} " : ""
- @bot.say @replyto, extra + string
+ @bot.say @replyto, extra + string, options
@replied = true
end
# the default reply style is to nickreply unless the reply already contains
# the nick or core.reply_with_nick is set to false
#
- def reply(string)
+ def reply(string, options={})
if @bot.config['core.reply_with_nick'] and not string =~ /\b#{@source}\b/
- return nickreply(string)
+ return nickreply(string, options)
end
- plainreply(string)
+ plainreply(string, options)
end
# convenience method to reply to a message with an action. It's the
@@ -242,8 +242,8 @@ module Irc
# <tt>@bot.action m.replyto, string</tt>
# So if the message is private, it will reply to the user. If it was
# in a channel, it will reply in the channel.
- def act(string)
- @bot.action @replyto, string
+ def act(string, options={})
+ @bot.action @replyto, string, options
@replied = true
end