iirc 0.5.1 → 0.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +24 -0
  3. data/README.md +7 -7
  4. data/examples/facts.rb +2 -2
  5. data/examples/greeter.rb +5 -5
  6. data/examples/sed.rb +83 -0
  7. data/examples/wolfram.rb +2 -2
  8. data/lib/iirc/bot.rb +5 -1
  9. data/lib/iirc/event.rb +34 -0
  10. data/lib/iirc/{bot → modules}/accept_invites.rb +6 -3
  11. data/lib/iirc/{bot → modules}/ambient.rb +22 -1
  12. data/lib/iirc/modules/autojoin.rb +25 -0
  13. data/lib/iirc/{bot → modules}/channels.rb +7 -3
  14. data/lib/iirc/{bot → modules}/configure.rb +1 -1
  15. data/lib/iirc/{bot → modules}/formatting.rb +1 -1
  16. data/lib/iirc/{bot → modules}/hooks.rb +7 -3
  17. data/lib/iirc/{bot → modules}/ircv3/batches.rb +1 -1
  18. data/lib/iirc/{bot → modules}/ircv3/caps.rb +1 -1
  19. data/lib/iirc/{bot → modules}/ircv3/labeled_requests.rb +1 -1
  20. data/lib/iirc/{bot → modules}/ircv3/parsing.rb +1 -1
  21. data/lib/iirc/{bot → modules}/isupport.rb +22 -2
  22. data/lib/iirc/modules/members.rb +39 -0
  23. data/lib/iirc/{bot → modules}/oper_up.rb +1 -1
  24. data/lib/iirc/{bot → modules}/parsing.rb +1 -1
  25. data/lib/iirc/{bot → modules}/pong.rb +1 -1
  26. data/lib/iirc/modules/print_io.rb +17 -0
  27. data/lib/iirc/modules/reading.rb +12 -0
  28. data/lib/iirc/{bot → modules}/redis.rb +1 -1
  29. data/lib/iirc/{bot → modules}/regex_hooks.rb +3 -3
  30. data/lib/iirc/{bot → modules}/reply_target.rb +1 -1
  31. data/lib/iirc/{bot → modules}/track_own_nick.rb +1 -1
  32. data/lib/iirc/{bot → modules}/verbs.rb +1 -1
  33. data/lib/iirc/sender.rb +6 -5
  34. data/lib/iirc/version.rb +1 -1
  35. data/lib/iirc.rb +34 -36
  36. metadata +26 -25
  37. data/lib/iirc/bot/autojoin.rb +0 -18
  38. data/lib/iirc/bot/members.rb +0 -28
  39. data/lib/iirc/bot/print_io.rb +0 -12
  40. data/lib/iirc/bot/reading.rb +0 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 23bf3a78d1201f86c54b9d6409bb98d24b1dbeeb38bfa0d9173db4945ffab46b
4
- data.tar.gz: b18ad6cde53fc59365e1dfc935d2c1d16e6e962d3168b5b7a64feb5ca30a786b
3
+ metadata.gz: 49f724afe62aa2f76c2d5effda29ce98c9f9b5ae8d83f175ac0de51385e9c431
4
+ data.tar.gz: e61a5053975303f7e45dc4be44f3cebddc63a3171a984c1785fcae666be27d07
5
5
  SHA512:
6
- metadata.gz: 9be4d722d3d609865e8a829197ea20a80ad14789e3809e746025414e61cc94df29b301969b6d1814a9fb4c63947243a65989d723fca2ef44c343df1f48e87574
7
- data.tar.gz: 4767a7575a0ff58c2df94872322d8cc1440a2d18ff03ef1fc33b9350b1754246d9484b1ec3f7a125659f45effd76ae814056fb75b98b1d286038258763e8a47e
6
+ metadata.gz: fc76b8e4d7455058a20dff37d6d29cb0082d69b65681a0e36b7eeccb7abdb25cf58b52bbb23bb652c34747756e0352e2969bb99a669349cada61041cfcdd0dec
7
+ data.tar.gz: 3c4a85314c46eb87752f1a85e2235d1744a0e2f3b9a024af7c5bc5e8b7a604d505edf9905022393bacb973b7bfd7e94da815321cba5d651cea21776d1dc07486
data/CHANGELOG.md CHANGED
@@ -1,3 +1,27 @@
1
+ ## [0.6.2] - 2022-04-28
2
+
3
+ - [Event] Add CTCP parsing methods
4
+ - [Sender] Nick is the server name when sender is a server
5
+
6
+ ## [0.6.1] - 2022-04-01
7
+
8
+ - Add SedBot example
9
+ - [RegexHooks] Passes matches as args
10
+ - e.g. (evt, match_one, match_two, ..)
11
+ - [RegexHooks] Add some tests
12
+
13
+ ## [0.6.0] - 2022-03-30
14
+
15
+ - Move modules from IIRC::Bot to IIRC namespace
16
+
17
+ - Document AcceptInvites
18
+ - Document Ambient
19
+ - Document AutoJoin
20
+ - Document Channels
21
+ - Document Members
22
+ - Document PrintIO
23
+ - Document Reading
24
+
1
25
  ## [0.5.1] - 2022-03-29
2
26
 
3
27
  - [Batteries] Include ISupport
data/README.md CHANGED
@@ -10,10 +10,10 @@ It's based on composition, with code reload, extensibility and predictability in
10
10
  require 'iirc'
11
11
 
12
12
  class CoolBot < IIRC::IRCv3Bot
13
- include Verbs
14
- include AutoJoin
15
- include RegexHooks
16
- include PrintIO
13
+ include IIRC::Verbs
14
+ include IIRC::AutoJoin
15
+ include IIRC::RegexHooks
16
+ include IIRC::PrintIO
17
17
 
18
18
  def configure_coolness
19
19
  on /^!poke/, :poke_back
@@ -35,8 +35,8 @@ CoolBot.run 'irc.libera.chat' if __FILE__ == $0
35
35
  require 'iirc'
36
36
 
37
37
  class SillyBot < IIRC::IRCv3Bot
38
- include AcceptInvites
39
- include Batteries # Verbs, Ambient, RegexHooks used here
38
+ include IIRC::AcceptInvites
39
+ include IIRC::Batteries # Verbs, Ambient, RegexHooks used here
40
40
 
41
41
  def configure_silliness
42
42
  on /^!uptime/, :say_uptime
@@ -110,7 +110,7 @@ For example:
110
110
 
111
111
  ```ruby
112
112
  class CoolBot < IIRC::IRCv3Bot
113
- include RegexHooks
113
+ include IIRC::RegexHooks
114
114
 
115
115
  def configure_reload
116
116
  on /^=reload/, :reload!
data/examples/facts.rb CHANGED
@@ -1,8 +1,8 @@
1
1
  require "iirc"
2
2
 
3
3
  IIRC {
4
- include IIRC::Bot::Batteries
5
- include IIRC::Bot::PrintIO
4
+ include IIRC::Batteries
5
+ include IIRC::PrintIO
6
6
 
7
7
  def facts
8
8
  @facts ||= {}
data/examples/greeter.rb CHANGED
@@ -1,12 +1,12 @@
1
1
  require "iirc"
2
2
 
3
3
  class Greeter < IIRC::Bot
4
- include AutoJoin
5
- include Verbs
6
- include PrintIO
4
+ include IIRC::AutoJoin
5
+ include IIRC::Verbs
6
+ include IIRC::PrintIO
7
7
 
8
8
  def on_join(evt)
9
- say evt.target, "Hello #{evt.sender.nick}!" unless me === evt.sender
9
+ say evt.target, "Hello #{evt.nick}!" unless me === evt.sender
10
10
  end
11
11
 
12
12
  def autojoin_channels
@@ -14,4 +14,4 @@ class Greeter < IIRC::Bot
14
14
  end
15
15
  end
16
16
 
17
- Greeter.run 'irc.libera.chat', nick: 'GreeterBot' if __FILE__ == $0
17
+ Greeter.run 'irc.libera.chat', nick: 'GreeterBot' if __FILE__ == $0
data/examples/sed.rb ADDED
@@ -0,0 +1,83 @@
1
+ require "iirc"
2
+ require "timeout"
3
+
4
+ SED_REGEX ||= Regexp.new(DATA.read, Regexp::EXTENDED)
5
+
6
+ class SedBot < IIRC::IRCv3Bot
7
+ include IIRC::Batteries
8
+ include IIRC::AcceptInvites
9
+ include IIRC::PrintIO
10
+
11
+ def autojoin_channels
12
+ ['##iirc']
13
+ end
14
+
15
+ def history
16
+ @history ||= Hash.new { |h,k| h[k] = [] }
17
+ end
18
+
19
+ def on_privmsg evt
20
+ case evt.message
21
+ when SED_REGEX
22
+ needle, sub, flags = Regexp.new($1, $3.include?('i')), $2, $3
23
+
24
+ say Timeout.timeout(5) {
25
+ history[reply_target]
26
+ .find { |msg| msg =~ needle }
27
+ .then { |msg|
28
+ if msg and flags[/g/]
29
+ italic msg.gsub(needle, sub)
30
+ elsif msg
31
+ italic msg.sub(needle, sub)
32
+ else
33
+ 'Sorry, no match found'
34
+ end
35
+ }
36
+ }
37
+ else
38
+ history[reply_target].unshift evt.message
39
+ end
40
+ end
41
+ end
42
+
43
+ if __FILE__ == $0
44
+ SedBot.run 'irc.libera.chat', nick: 'Sed[%x]'%rand(256)
45
+ end
46
+
47
+ __END__
48
+
49
+ # Copied from https://www.azabani.com/2014/02/08/writing-irc-sedbot.html
50
+ # This matches the s/(needle)/(replacement)/(flags) parts of a sed pattern
51
+
52
+ ^ # start of the message
53
+ (?: # BEGIN first sed expression
54
+ s/ # sed replacement expression delimiter
55
+ ( # BEGIN needle component
56
+ (?: # BEGIN single needle character
57
+ [^\\/] # anything that isn't a slash or backslash...
58
+ |\\. # ...or any backslash escape
59
+ )* # END single needle character, zero or more
60
+ ) # END needle component
61
+ / # slash between needle and replacement
62
+ ( # BEGIN replacement component
63
+ (?: # BEGIN single replacement character
64
+ [^\\/]|\\. # escape or non-slash-backslash, as above
65
+ )* # END single replacement character, zero or more
66
+ ) # END replacement component
67
+ / # slash between replacement and flags
68
+ ( # BEGIN flags component
69
+ (?: # BEGIN single flag
70
+ g|i|\d+ # "g", "i" or a sequence of digits
71
+ )* # END single flag, zero or more
72
+ ) # END flags component
73
+ ) # END first sed expression
74
+ (?: # BEGIN optional subsequent sed expressions
75
+ ; # semicolon between sed expressions
76
+ s/ # sed replacement expression delimiter, as above
77
+ ((?:[^\\/]|\\.)*) # needle component, as above
78
+ / # slash between needle and replacement, as above
79
+ ((?:[^\\/]|\\.)*) # replacement component, as above
80
+ / # slash between replacement and flags, as above
81
+ ((?:g|i|\d+)*) # flags component, as above
82
+ )* # END optional subsequent sed expressions, zero or more
83
+ $ # end of the message
data/examples/wolfram.rb CHANGED
@@ -2,8 +2,8 @@ require "net/https"
2
2
  require "iirc"
3
3
 
4
4
  class Wolfram < IIRC::IRCv3Bot
5
- include Batteries
6
- include PrintIO
5
+ include IIRC::Batteries
6
+ include IIRC::PrintIO
7
7
 
8
8
  def on_privmsg(evt)
9
9
  case evt.message
data/lib/iirc/bot.rb CHANGED
@@ -21,6 +21,10 @@ module IIRC
21
21
  super() if defined? super
22
22
  self.socket = socket
23
23
  self.user = user.is_a?(User) ? user : User.new(**user)
24
+
25
+ if !user.key?(:nick)
26
+ self.user.nick ||= self.class.name&.split('::')&.last
27
+ end
24
28
  end
25
29
 
26
30
  def register!
@@ -35,4 +39,4 @@ module IIRC
35
39
  raise ArgumentError.new('no nick given') unless me.nick && !me.nick.empty?
36
40
  end
37
41
  end
38
- end
42
+ end
data/lib/iirc/event.rb CHANGED
@@ -28,5 +28,39 @@ module IIRC
28
28
  def message
29
29
  args.last
30
30
  end
31
+
32
+ CTCP_REGEX = /^\x01(\w+)(?: ([^\x01]+))?\x01?$/
33
+
34
+ def ctcp?
35
+ message&.match?(CTCP_REGEX)
36
+ end
37
+
38
+ def ctcp_verb
39
+ $1.downcase.to_sym if message =~ CTCP_REGEX
40
+ end
41
+
42
+ def ctcp_message
43
+ $2 if message =~ CTCP_REGEX
44
+ end
45
+
46
+ # Parses CTCP data in message, returning nil or an Array like [:action, "does the Macarena"], [:version], [:version, "SomeClient v0.1"]
47
+ # @example Pattern matching (case)
48
+ # case evt.ctcp
49
+ # in [:action, msg]
50
+ # say "#{evt.nick} did an action: #{italic msg}"
51
+ # in [:version]
52
+ # say "\x01VERSION Ruby IIRC (v#{IIRC::VERSION})\x01"
53
+ # in [:dcc, type, *]
54
+ # say "You want to start a DCC #{type} with me? Are you crazy!?"
55
+ # else
56
+ # end
57
+ # @example Pattern matching (if)
58
+ # on(:privmsg, :be_annoying) { |evt|
59
+ # act "#{thing} also" if [:action, thing] in evt.ctcp
60
+ # }
61
+ # @return [Array<Symbol, String?>, nil] CTCP verb and text content, if any (e.g. [:action, "says hello"], [:clientinfo], [:version, "SomeClient v0.1"])
62
+ def ctcp
63
+ [ctcp_verb, ctcp_message].compact if ctcp?
64
+ end
31
65
  end
32
66
  end
@@ -1,8 +1,11 @@
1
1
  module IIRC
2
- module Bot::AcceptInvites
3
- # Override this to decide whether we should accept a given invite.
2
+ # Accept INVITEs to join a channel.
3
+ # By default, all invites are accepted.
4
+ # To choose which ones to accept, define {#accept_invite?} on your object / class.
5
+ module AcceptInvites
6
+ # Decides whether to accept a given INVITE.
4
7
  # By default, all invites are accepted.
5
- # @param [Event]
8
+ # @param evt [Event] the invite
6
9
  # @return [true] if we should join the channel
7
10
  def accept_invite?(evt)
8
11
  true
@@ -1,7 +1,28 @@
1
1
  require_relative "reply_target"
2
2
 
3
3
  module IIRC
4
- module Bot::Ambient
4
+ # Ambient lets you access the current event without having to pass it around.
5
+ #
6
+ # A thread-local variable is set by {Events} when an event comes in.
7
+ #
8
+ # Overloads of {#say}, {#act}, {#mode}, {#join} etc. make their
9
+ # first argument optional.
10
+ #
11
+ # This module pulls in both {Events} and {Verbs}.
12
+ #
13
+ # @example say (with Ambient)
14
+ # def on_privmsg
15
+ # say "Hello!"
16
+ # end
17
+ # @example say (without)
18
+ # def on_privmsg(evt)
19
+ # say reply_target(evt), "Hello!"
20
+ # end
21
+ # @example mode (with Ambient)
22
+ # def on_join(evt)
23
+ # mode "+v #{evt.nick}"
24
+ # end
25
+ module Ambient
5
26
  module Events
6
27
  private
7
28
  def configure_ambient_events
@@ -0,0 +1,25 @@
1
+ module IIRC
2
+ # Automatically joins channels on RPL_WELCOME (001).
3
+ #
4
+ # Override {#autojoin_channels} to choose which channels to join.
5
+ # {#autojoin!} may be called at any time to resend.
6
+ module AutoJoin
7
+ private def configure_autojoin
8
+ on :'001', :autojoin!
9
+ end
10
+
11
+ # Send JOIN for each in {#autojoin_channels}
12
+ # @return [Array<String>] channels joined
13
+ def autojoin!
14
+ for channel in autojoin_channels
15
+ self << "JOIN #{channel}"
16
+ end
17
+ end
18
+
19
+ # The channels to join. By default, returns an empty array.
20
+ # @return [Array<String>] channel names
21
+ def autojoin_channels
22
+ []
23
+ end
24
+ end
25
+ end
@@ -1,15 +1,19 @@
1
1
  module IIRC
2
- module Bot::Channels
2
+ # Keeps track of the channels we are currently in.
3
+ # The list can be retrieved with {#channels}.
4
+ # Based on JOIN, PART and KICK messages received for our nick.
5
+ module Channels
6
+ # @return [Set<String>]
3
7
  def channels
4
8
  @channels ||= Set.new
5
9
  end
6
10
 
7
11
  private
8
12
  def configure_channel_tracking
9
- hook :track_self_channels
13
+ hook :track_own_channels
10
14
  end
11
15
 
12
- def track_self_channels(evt)
16
+ def track_own_channels(evt)
13
17
  case evt.verb
14
18
  when :'001'
15
19
  channels.clear
@@ -1,5 +1,5 @@
1
1
  module IIRC
2
- module Bot::Configure
2
+ module Configure
3
3
  def self.included(m)
4
4
  require 'set'
5
5
  m.extend ClassMethods
@@ -1,5 +1,5 @@
1
1
  module IIRC
2
- module Bot::Formatting
2
+ module Formatting
3
3
  module_function
4
4
  def bold(s) "\x02#{s}\x02" end
5
5
  def italic(s) "\x1d#{s}\x1d" end
@@ -1,5 +1,5 @@
1
1
  module IIRC
2
- module Bot::Hooks
2
+ module Hooks
3
3
  def hooks
4
4
  @hooks ||= Hash.new { |h,v| h[v] = Set.new }
5
5
  end
@@ -7,8 +7,7 @@ module IIRC
7
7
  def run
8
8
  lines { |line|
9
9
  begin
10
- evt = parse(line)
11
- fire! evt.verb, evt
10
+ self >> line
12
11
  rescue Exception => ex
13
12
  puts ex.message
14
13
  puts ex.backtrace
@@ -16,6 +15,11 @@ module IIRC
16
15
  }
17
16
  end
18
17
 
18
+ def >>(line)
19
+ evt = parse(line)
20
+ fire! evt.verb, evt
21
+ end
22
+
19
23
  def on verb=nil, action=nil, &blk
20
24
  if action and blk
21
25
  define_singleton_method(action, blk)
@@ -1,5 +1,5 @@
1
1
  module IIRC
2
- module Bot::IRCv3
2
+ module IRCv3
3
3
  class Batch < Event
4
4
  def events
5
5
  @events ||= []
@@ -1,5 +1,5 @@
1
1
  module IIRC
2
- module Bot::IRCv3
2
+ module IRCv3
3
3
  module Caps
4
4
  def caps
5
5
  raise NotImplementedError.new
@@ -1,5 +1,5 @@
1
1
  module IIRC
2
- module Bot::IRCv3
2
+ module IRCv3
3
3
  module LabeledRequests
4
4
  def labeled_request(line)
5
5
  SecureRandom.alphanumeric(8).tap { |id|
@@ -1,5 +1,5 @@
1
1
  module IIRC
2
- module Bot::IRCv3
2
+ module IRCv3
3
3
  module Parsing
4
4
  autoload :IRCParser, 'ircparser'
5
5
 
@@ -11,7 +11,7 @@ module IIRC
11
11
  # @see https://defs.ircdocs.horse/defs/isupport.html List of tokens and their values (defs.ircdocs.horse)
12
12
  # @see https://datatracker.ietf.org/doc/html/draft-hardy-irc-isupport-00
13
13
  # @see https://datatracker.ietf.org/doc/html/draft-brocklesby-irc-isupport-00
14
- module Bot::ISupport
14
+ module ISupport
15
15
  # Hash of tokens sent by the server after registration indicating feature support and limits.
16
16
  # @return [Hash#extend(Inquiry)] the raw isupport key=>value pairs. keys with no value are assigned +true+.
17
17
  def isupport
@@ -20,9 +20,14 @@ module IIRC
20
20
 
21
21
  private
22
22
  def configure_isupport
23
+ on :'001', :clear_isupport
23
24
  on :'005', :store_isupport
24
25
  end
25
26
 
27
+ def clear_isupport
28
+ @isupport = nil
29
+ end
30
+
26
31
  def store_isupport(evt)
27
32
  tokens = evt.args[1..-2]
28
33
 
@@ -38,7 +43,7 @@ module IIRC
38
43
  #
39
44
  # The key=>value format returned by #isupport won't change.
40
45
  # Rather, methods which process the values can be added here.
41
- module Bot::ISupport::Inquiry
46
+ module ISupport::Inquiry
42
47
  # Whether or not the server supports a user mode which lets clients mark themselves as bots.
43
48
  def bot_mode?
44
49
  !!bot_mode
@@ -66,5 +71,20 @@ module IIRC
66
71
  def channel_prefixes
67
72
  (@channel_prefixes ||= self['CHANTYPES']&.chars.freeze) || ['#'].freeze
68
73
  end
74
+
75
+ # Modes which grant privileges to a user in a channel and their respective
76
+ # prefix characters seen in NAMES, WHO and WHOIS replies.
77
+ # @see https://modern.ircdocs.horse/#channel-membership-prefixes
78
+ # @example qaohv
79
+ # isupport.prefix_modes # => {'q'=>'~', 'a'=>'&', 'o'=>'@', 'h'=>'%', 'v'=>'+'}
80
+ # @return [Hash<String,String>] channel mode => prefix character
81
+ def prefix_modes
82
+ if self['PREFIX'].is_a? String
83
+ modes, symbols = self['PREFIX'][1..].split(')').map!(&:chars)
84
+ Hash[modes.zip(symbols)].freeze
85
+ else
86
+ {}.freeze
87
+ end
88
+ end
69
89
  end
70
90
  end
@@ -0,0 +1,39 @@
1
+ module IIRC
2
+ # Processes lists of channel participants sent by the server into {#members}.
3
+ # Namely, it processes NAMES replies, such as the ones sent automatically when we
4
+ # join a channel.
5
+ # @note IIRC doesn't send NAMES by itself, but will process any replies received, such
6
+ # as to JOIN, or any requests you have made yourself.
7
+ # @see IIRC::Verbs#names send a NAMES request
8
+ module Members
9
+ # Nicknames present in channels, by channel.
10
+ # Updated from NAMES replies.
11
+ # @return [Hash<String,[String]>]
12
+ def members
13
+ @members ||= {}
14
+ end
15
+
16
+ private
17
+ # @note In a future release, this may also track using JOIN/KICK/PART, etc.
18
+ def configure_members_tracking
19
+ on :'353', :receive_names
20
+ on :'366', :receive_names_end
21
+ end
22
+
23
+ def receive_names evt
24
+ names = evt.args[3]
25
+ channel = evt.args[2]
26
+ members_receiving[channel].concat names.tr('&+@%~', '').split(' ')
27
+ end
28
+
29
+ def receive_names_end evt
30
+ channel = evt.args[1]
31
+ members[channel] = members_receiving.delete(channel)
32
+ end
33
+
34
+ # @return [Hash]
35
+ def members_receiving
36
+ @members_receiving ||= Hash.new { |h,k| h[k] = [] }
37
+ end
38
+ end
39
+ end
@@ -1,5 +1,5 @@
1
1
  module IIRC
2
- module Bot::OperUp
2
+ module OperUp
3
3
  def oper_up!
4
4
  self << "OPER #{ENV['IRC_OPER']}" if ENV['IRC_OPER']
5
5
  end
@@ -1,5 +1,5 @@
1
1
  module IIRC
2
- module Bot::Parsing
2
+ module Parsing
3
3
  def parse(line)
4
4
  Event.new.tap { |evt|
5
5
  words = line.chomp.split " "
@@ -1,5 +1,5 @@
1
1
  module IIRC
2
- module Bot::Pong
2
+ module Pong
3
3
  def on_ping evt
4
4
  self << "PONG :#{evt.args[0]}"
5
5
  end
@@ -0,0 +1,17 @@
1
+ module IIRC
2
+ # Wraps {Reading#lines} and {Bot#<<} to print lines received and sent to stdout.
3
+ # Useful for debugging.
4
+ module PrintIO
5
+ # Prints `>> #{line}` to stdout, then yields, for each line from super.
6
+ def lines
7
+ super { |line| puts ">> #{line}"; yield line }
8
+ end
9
+
10
+ # Prints `<< #{line}` to stdout, then calls super
11
+ # @return [self]
12
+ def <<(line)
13
+ puts "<< #{line}"
14
+ super
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,12 @@
1
+ module IIRC
2
+ module Reading
3
+ # Read lines from {#socket} in a blocking loop until EOF or an error is raised.
4
+ # @return nil
5
+ # @yield [String] line encoded as UTF-8.
6
+ def lines
7
+ loop {
8
+ yield socket.readline.force_encoding('utf-8').encode
9
+ }
10
+ end
11
+ end
12
+ end
@@ -1,5 +1,5 @@
1
1
  module IIRC
2
- module Bot::Redis
2
+ module Redis
3
3
  def self.included(*)
4
4
  require 'redis'
5
5
  end
@@ -1,5 +1,5 @@
1
1
  module IIRC
2
- module Bot::RegexHooks
2
+ module RegexHooks
3
3
  def regex_hooks
4
4
  hooks.filter { |k,v| Regexp === k }.freeze
5
5
  end
@@ -13,8 +13,8 @@ module IIRC
13
13
  regex_hooks
14
14
  .filter { |k,v| k === evt.message }
15
15
  .each_value { |actions|
16
- actions.each { |action| call action, evt }
16
+ actions.each { |action| call action, evt, *Regexp.last_match&.values_at(1..) }
17
17
  }
18
18
  end
19
19
  end
20
- end
20
+ end
@@ -1,5 +1,5 @@
1
1
  module IIRC
2
- module Bot::ReplyTarget
2
+ module ReplyTarget
3
3
  # The +reply_target+ of an event is the sender's nickname when _we_ are the
4
4
  # +target+. Otherwise, it returns the regular +target+.
5
5
  #
@@ -1,5 +1,5 @@
1
1
  module IIRC
2
- module Bot::TrackOwnNick
2
+ module TrackOwnNick
3
3
  private
4
4
 
5
5
  def configure_own_nick_tracking
@@ -1,5 +1,5 @@
1
1
  module IIRC
2
- module Bot::Verbs
2
+ module Verbs
3
3
  def join(channel) self << "JOIN #{channel}" end
4
4
  def part(channel, reason=nil) self << "PART #{channel}#{" :#{reason}" if reason}" end
5
5
  def names(channel) self << "NAMES #{channel}" end
data/lib/iirc/sender.rb CHANGED
@@ -31,9 +31,9 @@ module IIRC
31
31
  not user.nil?
32
32
  end
33
33
 
34
- def nick; user&.nick end
34
+ def nick; user ? user.nick : without_leading_colon end
35
35
  def username; user&.username end
36
- def host; server? ? without_leading_colon : user.host end
36
+ def host; user ? user.host : without_leading_colon end
37
37
 
38
38
  def to_prefix
39
39
  ":#{self}"
@@ -48,8 +48,9 @@ module IIRC
48
48
  end
49
49
 
50
50
  module_function
51
- # Extends and returns a source string with the IIRC::Sender mixin.
52
- # Duplicates first if the string is frozen. Returns nil if source is nil.
51
+ # Adds the IIRC::Sender mixin to a string.
52
+ # If the string is frozen, a copy is returned.
53
+ # If the source is nil, nil is returned.
53
54
  #
54
55
  # @example :nick!user@host
55
56
  # v = IIRC.Sender(":JimmyWales!jim@wikipedia.org") # => "JimmyWales!jim@wikipedia.org"
@@ -66,4 +67,4 @@ module IIRC
66
67
  (source.frozen? ? source.dup : source).extend(Sender)
67
68
  end
68
69
  end
69
- end
70
+ end
data/lib/iirc/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module IIRC
4
- VERSION = "0.5.1"
4
+ VERSION = "0.6.2"
5
5
  end
data/lib/iirc.rb CHANGED
@@ -4,33 +4,33 @@ require_relative "iirc/event"
4
4
  require_relative "iirc/sender"
5
5
  require_relative "iirc/user"
6
6
 
7
+ require_relative "iirc/bot"
7
8
  require_relative "iirc/numerics"
8
9
 
9
- require_relative "iirc/bot"
10
- require_relative "iirc/bot/accept_invites"
11
- require_relative "iirc/bot/ambient"
12
- require_relative "iirc/bot/autojoin"
13
- require_relative "iirc/bot/channels"
14
- require_relative "iirc/bot/configure"
15
- require_relative "iirc/bot/formatting"
16
- require_relative "iirc/bot/hooks"
17
- require_relative "iirc/bot/isupport"
18
- require_relative "iirc/bot/members"
19
- require_relative "iirc/bot/oper_up"
20
- require_relative "iirc/bot/parsing"
21
- require_relative "iirc/bot/pong"
22
- require_relative "iirc/bot/print_io"
23
- require_relative "iirc/bot/reading"
24
- require_relative "iirc/bot/redis"
25
- require_relative "iirc/bot/regex_hooks"
26
- require_relative "iirc/bot/reply_target"
27
- require_relative "iirc/bot/track_own_nick"
28
- require_relative "iirc/bot/verbs"
29
-
30
- require_relative "iirc/bot/ircv3/caps"
31
- require_relative "iirc/bot/ircv3/parsing"
32
- require_relative "iirc/bot/ircv3/batches"
33
- require_relative "iirc/bot/ircv3/labeled_requests"
10
+ require_relative "iirc/modules/accept_invites"
11
+ require_relative "iirc/modules/ambient"
12
+ require_relative "iirc/modules/autojoin"
13
+ require_relative "iirc/modules/channels"
14
+ require_relative "iirc/modules/configure"
15
+ require_relative "iirc/modules/formatting"
16
+ require_relative "iirc/modules/hooks"
17
+ require_relative "iirc/modules/isupport"
18
+ require_relative "iirc/modules/members"
19
+ require_relative "iirc/modules/oper_up"
20
+ require_relative "iirc/modules/parsing"
21
+ require_relative "iirc/modules/pong"
22
+ require_relative "iirc/modules/print_io"
23
+ require_relative "iirc/modules/reading"
24
+ require_relative "iirc/modules/redis"
25
+ require_relative "iirc/modules/regex_hooks"
26
+ require_relative "iirc/modules/reply_target"
27
+ require_relative "iirc/modules/track_own_nick"
28
+ require_relative "iirc/modules/verbs"
29
+
30
+ require_relative "iirc/modules/ircv3/caps"
31
+ require_relative "iirc/modules/ircv3/parsing"
32
+ require_relative "iirc/modules/ircv3/batches"
33
+ require_relative "iirc/modules/ircv3/labeled_requests"
34
34
 
35
35
  module IIRC
36
36
  class Error < StandardError; end
@@ -49,8 +49,6 @@ module IIRC
49
49
  local_host: local_host, local_port: local_port,
50
50
  ssl_context: ssl_context)
51
51
 
52
- user_params[:nick] = self.name&.split('::').last if !user_params.key?(:nick)
53
-
54
52
  new(socket, **user_params).tap { |bot|
55
53
  bot.register!
56
54
  bot.tap(&blk) if blk
@@ -67,15 +65,15 @@ module IIRC
67
65
  end
68
66
 
69
67
  # Batteries is a recommended set of modules for writing interactive bots.
70
- module Bot::Batteries
71
- include Bot::Channels
72
- include Bot::Members
73
- include Bot::Formatting
74
- include Bot::AutoJoin
75
- include Bot::Verbs
76
- include Bot::Ambient
77
- include Bot::RegexHooks
78
- include Bot::ISupport
68
+ module Batteries
69
+ include Channels
70
+ include Members
71
+ include Formatting
72
+ include AutoJoin
73
+ include Verbs
74
+ include Ambient
75
+ include RegexHooks
76
+ include ISupport
79
77
  end
80
78
 
81
79
  module SSL
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: iirc
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 0.6.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - mooff
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-03-29 00:00:00.000000000 Z
11
+ date: 2022-04-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ircparser
@@ -43,34 +43,35 @@ files:
43
43
  - bin/setup
44
44
  - examples/facts.rb
45
45
  - examples/greeter.rb
46
+ - examples/sed.rb
46
47
  - examples/wolfram.rb
47
48
  - iirc.gemspec
48
49
  - lib/iirc.rb
49
50
  - lib/iirc/bot.rb
50
- - lib/iirc/bot/accept_invites.rb
51
- - lib/iirc/bot/ambient.rb
52
- - lib/iirc/bot/autojoin.rb
53
- - lib/iirc/bot/channels.rb
54
- - lib/iirc/bot/configure.rb
55
- - lib/iirc/bot/formatting.rb
56
- - lib/iirc/bot/hooks.rb
57
- - lib/iirc/bot/ircv3/batches.rb
58
- - lib/iirc/bot/ircv3/caps.rb
59
- - lib/iirc/bot/ircv3/labeled_requests.rb
60
- - lib/iirc/bot/ircv3/parsing.rb
61
- - lib/iirc/bot/isupport.rb
62
- - lib/iirc/bot/members.rb
63
- - lib/iirc/bot/oper_up.rb
64
- - lib/iirc/bot/parsing.rb
65
- - lib/iirc/bot/pong.rb
66
- - lib/iirc/bot/print_io.rb
67
- - lib/iirc/bot/reading.rb
68
- - lib/iirc/bot/redis.rb
69
- - lib/iirc/bot/regex_hooks.rb
70
- - lib/iirc/bot/reply_target.rb
71
- - lib/iirc/bot/track_own_nick.rb
72
- - lib/iirc/bot/verbs.rb
73
51
  - lib/iirc/event.rb
52
+ - lib/iirc/modules/accept_invites.rb
53
+ - lib/iirc/modules/ambient.rb
54
+ - lib/iirc/modules/autojoin.rb
55
+ - lib/iirc/modules/channels.rb
56
+ - lib/iirc/modules/configure.rb
57
+ - lib/iirc/modules/formatting.rb
58
+ - lib/iirc/modules/hooks.rb
59
+ - lib/iirc/modules/ircv3/batches.rb
60
+ - lib/iirc/modules/ircv3/caps.rb
61
+ - lib/iirc/modules/ircv3/labeled_requests.rb
62
+ - lib/iirc/modules/ircv3/parsing.rb
63
+ - lib/iirc/modules/isupport.rb
64
+ - lib/iirc/modules/members.rb
65
+ - lib/iirc/modules/oper_up.rb
66
+ - lib/iirc/modules/parsing.rb
67
+ - lib/iirc/modules/pong.rb
68
+ - lib/iirc/modules/print_io.rb
69
+ - lib/iirc/modules/reading.rb
70
+ - lib/iirc/modules/redis.rb
71
+ - lib/iirc/modules/regex_hooks.rb
72
+ - lib/iirc/modules/reply_target.rb
73
+ - lib/iirc/modules/track_own_nick.rb
74
+ - lib/iirc/modules/verbs.rb
74
75
  - lib/iirc/numerics.rb
75
76
  - lib/iirc/sender.rb
76
77
  - lib/iirc/user.rb
@@ -1,18 +0,0 @@
1
- module IIRC
2
- module Bot::AutoJoin
3
- private def configure_autojoin_hook
4
- on :'001', :autojoin!
5
- end
6
-
7
- def autojoin!
8
- for channel in autojoin_channels
9
- self << "JOIN #{channel}"
10
- end
11
- end
12
-
13
- # override this to read from e.g. redis, config
14
- def autojoin_channels
15
- []
16
- end
17
- end
18
- end
@@ -1,28 +0,0 @@
1
- module IIRC
2
- module Bot::Members
3
- def members
4
- @members ||= {}
5
- end
6
-
7
- private
8
- def configure_members_tracking
9
- on :'353', :receive_names
10
- on :'366', :receive_names_end
11
- end
12
-
13
- def receive_names evt
14
- names = evt.args[3]
15
- channel = evt.args[2]
16
- members_receiving[channel].concat names.tr('&+@%~', '').split(' ')
17
- end
18
-
19
- def receive_names_end evt
20
- channel = evt.args[1]
21
- members[channel] = members_receiving.delete(channel)
22
- end
23
-
24
- def members_receiving
25
- @members_receiving ||= Hash.new { |h,k| h[k] = [] }
26
- end
27
- end
28
- end
@@ -1,12 +0,0 @@
1
- module IIRC
2
- module Bot::PrintIO
3
- def lines
4
- super { |line| puts ">> #{line}"; yield line }
5
- end
6
-
7
- def <<(line)
8
- puts "<< #{line}"
9
- super
10
- end
11
- end
12
- end
@@ -1,9 +0,0 @@
1
- module IIRC
2
- module Bot::Reading
3
- def lines
4
- loop {
5
- yield socket.readline.force_encoding('utf-8').encode
6
- }
7
- end
8
- end
9
- end