Skip to content
This repository has been archived by the owner on Apr 13, 2019. It is now read-only.

Commit

Permalink
Handle STATUSMSG correctly
Browse files Browse the repository at this point in the history
Some IRC servers support STATUSMSG. STATUSMSG allows sending messages to
a subset of a channel's users, based on their mode. By sending a message
to +#channel, only people who have voice or higher will receive the
message.

Previously, Cinch didn't take this into account at all. Message#channel
would be nil and Message#reply only worked by chance (Cinch thought that
+#channel was a user and replied to it).

The new support explicitly supports STATUSMSG. For a message sent to
+#channel, Message#channel will return #channel and Message#reply will
reply to +#channel. Additionally, an attribute called
Message#statusmsg_mode was added, which would contain the STATUSMSG mode
that was addressed, using mode characters, not sigils. For +#channel,
statusmsg_mode would be "v".

There is currently no direct support for sending arbitrary STATUSMSG
messages. That is, Channel has no APIs for it. Users who really need it
have to construct their own Target instance. New APIs might be added in
the future.
  • Loading branch information
dominikh committed Oct 25, 2015
1 parent 70e9a88 commit 9dc50ab
Showing 1 changed file with 52 additions and 10 deletions.
62 changes: 52 additions & 10 deletions lib/cinch/message.rb
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,28 @@ class Message
# @return [Target]
attr_reader :target

# The STATUSMSG mode a channel message was sent to.
#
# Some IRC servers allow sending messages limited to people in a
# channel who have a certain mode. For example, by sending a
# message to `+#channel`, only people who are voiced, or have a
# higher mode (op) will receive the message.
#
# This attribute contains the mode character the message was sent
# to, or nil if it was a normal message. For the previous example,
# this attribute would be set to `"v"`, for voiced.
#
# @return [String, nil]
# @since 2.3.0
attr_reader :statusmsg_mode

def initialize(msg, bot)
@raw = msg
@bot = bot
@matches = {:ctcp => {}, :action => {}, :other => {}}
@events = []
@time = Time.now
@statusmsg_mode = nil
parse if msg
end

Expand All @@ -90,7 +106,7 @@ def parse
@params = parse_params(raw_params)

@user = parse_user
@channel = parse_channel
@channel, @statusmsg_mode = parse_channel
@target = @channel || @user
@server = parse_server
@error = parse_error
Expand Down Expand Up @@ -160,6 +176,11 @@ def match(regexp, type, strip_colors)
# Replies to a message, automatically determining if it was a
# channel or a private message.
#
# If the message is a STATUSMSG, i.e. it was send to `+#channel`
# or `@#channel` instead of `#channel`, the reply will be sent as
# the same kind of STATUSMSG. See {#statusmsg_mode} for more
# information on STATUSMSG.
#
# @param [String] text the message
# @param [Boolean] prefix if prefix is true and the message was in
# a channel, the reply will be prefixed by the nickname of whoever
Expand All @@ -171,7 +192,7 @@ def reply(text, prefix = false)
text = text.split("\n").map {|l| "#{user.nick}: #{l}"}.join("\n")
end

@target.send(text)
reply_target.send(text)
end

# Like #reply, but using {Target#safe_send} instead
Expand All @@ -183,16 +204,18 @@ def safe_reply(text, prefix = false)
if channel && prefix
text = "#{@user.nick}: #{text}"
end
@target.safe_send(text)
reply_target.safe_send(text)
end

# Reply to a message with an action.
#
# For its behaviour with regard to STATUSMSG, see {#reply}.
#
# @param [String] text the action message
# @return [void]
def action_reply(text)
text = text.to_s
@target.action(text)
reply_target.action(text)
end

# Like #action_reply, but using {Target#safe_action} instead
Expand All @@ -201,7 +224,7 @@ def action_reply(text)
# @return (see #action_reply)
def safe_action_reply(text)
text = text.to_s
@target.safe_action(text)
reply_target.safe_action(text)
end

# Reply to a CTCP message
Expand All @@ -221,6 +244,13 @@ def to_s
end

private
def reply_target
if @channel.nil? || @statusmsg_mode.nil?
return @target
end
prefix = @bot.irc.isupport["PREFIX"][@statusmsg_mode]
return Target.new(prefix + @channel.name, @bot)
end
def regular_command?
!numeric_reply? # a command can only be numeric or "regular"…
end
Expand Down Expand Up @@ -262,15 +292,27 @@ def parse_channel
# `QUIT :#sometext` will be interpreted as a channel. The
# alternative to the currently used heuristic would be to
# hardcode a list of commands that provide a channel argument.
chantypes = @bot.irc.isupport["CHANTYPES"]
if chantypes.include?(@params.first[0])
@bot.channel_list.find_ensured(@params.first)
elsif numeric_reply? and @params.size > 1 and chantypes.include?(@params[1][0])
@bot.channel_list.find_ensured(@params[1])
ch, status = privmsg_channel_name(@params.first)
if ch.nil? && numeric_reply? && @params.size > 1
ch, status = privmsg_channel_name(@params[1])
end
if ch
return @bot.channel_list.find_ensured(ch), status
end
end
end

def privmsg_channel_name(s)
chantypes = @bot.irc.isupport["CHANTYPES"]
statusmsg = @bot.irc.isupport["STATUSMSG"]
if statusmsg.include?(s[0]) && chantypes.include?(s[1])
status = @bot.irc.isupport["PREFIX"].invert[s[0]]
return s[1..-1], status
elsif chantypes.include?(s[0])
return s, nil
end
end

def parse_server
return unless @prefix
return if @prefix.match(/[@!]/)
Expand Down

0 comments on commit 9dc50ab

Please sign in to comment.