iirc 0.2.0

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 0fe307700ed349facc2bfced87e9f2081534b3aa8f5807e104e923c29970d63a
4
+ data.tar.gz: 53f7ed40fc8fc50f0f10cfb05baed481b0e9cea39158f28ccf4bc3d2f672f4b9
5
+ SHA512:
6
+ metadata.gz: b7505abc4e38ecf5b78c3f3159841b06250399457012e8d7c94415e56ef0a3f439a3e6e3523fceed31c5dcb9454288807d30a52f7377c3126e20a1417b7960b2
7
+ data.tar.gz: d1a53a1d19cd9da9db10eada940432597638dab3c5b07a61aa34fe04f3d02a90ac813e700d5cacdeb9497b6d57914364412e2fa6853f8209da02456be06055cc
@@ -0,0 +1,16 @@
1
+ name: Ruby
2
+
3
+ on: [push,pull_request]
4
+
5
+ jobs:
6
+ build:
7
+ runs-on: ubuntu-latest
8
+ steps:
9
+ - uses: actions/checkout@v2
10
+ - name: Set up Ruby
11
+ uses: ruby/setup-ruby@v1
12
+ with:
13
+ ruby-version: 2.7.4
14
+ bundler-cache: true
15
+ - name: Run the default task
16
+ run: bundle exec rake
data/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
data/CHANGELOG.md ADDED
@@ -0,0 +1,11 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.2.0] - 2022-03-12
4
+
5
+ - Extracted Bot::AmbientEvents
6
+ - Extracted IRCv3::LabeledRequests
7
+
8
+ ## [0.1.0] - 2022-03-12
9
+
10
+ - Library extracted from bot (developed since Dec 2021)
11
+ - Yay first release \o/
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in iirc.gemspec
6
+ gemspec
7
+
8
+ gem "rake", "~> 13.0"
9
+
10
+ gem "minitest", "~> 5.0"
data/README.md ADDED
@@ -0,0 +1,35 @@
1
+ # Lean, mean IRC processing machine
2
+
3
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/iirc`. To experiment with that code, run `bin/console` for an interactive prompt.
4
+
5
+ TODO: Delete this and the text above, and describe your gem
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'iirc'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle install
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install iirc
22
+
23
+ ## Usage
24
+
25
+ TODO: Write usage instructions here
26
+
27
+ ## Development
28
+
29
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
30
+
31
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
32
+
33
+ ## Contributing
34
+
35
+ Bug reports and pull requests are welcome on GitHub at https://github.com/awfulcooking/iirc.
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rake/testtask"
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << "test"
8
+ t.libs << "lib"
9
+ t.test_files = FileList["test/**/*_test.rb"]
10
+ end
11
+
12
+ task default: :test
data/bin/console ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "iirc"
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require "irb"
15
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/iirc.gemspec ADDED
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/iirc/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "iirc"
7
+ spec.version = IIRC::VERSION
8
+ spec.authors = ["mooff"]
9
+ spec.email = ["mooff@awful.cooking"]
10
+
11
+ spec.summary = "Lean, mean IRC processing machine"
12
+ spec.description = "A composable toolkit for IRC bots"
13
+ spec.homepage = "https://github.com/awfulcooking/iirc"
14
+ spec.required_ruby_version = ">= 2.6.0"
15
+
16
+ spec.metadata["homepage_uri"] = spec.homepage
17
+ spec.metadata["source_code_uri"] = "https://github.com/awfulcooking/iirc"
18
+ spec.metadata["changelog_uri"] = "https://github.com/awfulcooking/iirc/blob/main/CHANGELOG.md"
19
+
20
+ # Specify which files should be added to the gem when it is released.
21
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
22
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
23
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
24
+ end
25
+ spec.bindir = "exe"
26
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
27
+ spec.require_paths = ["lib"]
28
+
29
+ # Uncomment to register a new dependency of your gem
30
+ # spec.add_dependency "example-gem", "~> 1.0"
31
+
32
+ # For more information and examples about making a new gem, checkout our
33
+ # guide at: https://bundler.io/guides/creating_gem.html
34
+ end
@@ -0,0 +1,50 @@
1
+ module IIRC
2
+ module Bot::AmbientEvents
3
+ private
4
+ def configure_ambient_events
5
+ hook :set_ambient_event
6
+ end
7
+
8
+ def ambient_event
9
+ Thread.current[:ambient_event]
10
+ end
11
+
12
+ def ambient_target; ambient_event&.target; end
13
+ def ambient_sender; ambient_event&.sender; end
14
+
15
+ def set_ambient_event(evt)
16
+ Thread.current[:ambient_event] = evt
17
+ end
18
+
19
+ def with_ambient_event(evt)
20
+ initial_event = ambient_event
21
+ set_ambient_event(evt)
22
+ begin
23
+ yield evt
24
+ ensure
25
+ set_ambient_event(initial_event)
26
+ end
27
+ end
28
+ end
29
+
30
+ module Bot::AmbientVerbs
31
+ include Bot::AmbientEvents
32
+
33
+ def join(channel=ambient_target) super end
34
+ def part(channel=ambient_target, reason='') super end
35
+ def msg(channel=ambient_target, msg) super end
36
+ def act(channel=ambient_target, msg) super end
37
+ def cycle(channel=ambient_target, reason='') super end
38
+ def mode(channel=ambient_target, mode) super end
39
+ alias :say :msg
40
+ end
41
+
42
+ module Bot::AmbientEvents::LabeledRequests
43
+ def labeled_request(*)
44
+ initial_evt = ambient_event
45
+ super do |reply|
46
+ with_ambient_event(initial_evt) { yield reply }
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,18 @@
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
@@ -0,0 +1,35 @@
1
+ module IIRC
2
+ module Bot::Channels
3
+ def channels
4
+ @channels ||= Set.new
5
+ end
6
+
7
+ private
8
+ def configure_channel_tracking
9
+ hook :track_self_channels
10
+ end
11
+
12
+ def track_self_channels(evt)
13
+ case evt.verb
14
+ when :'001'
15
+ channels.clear
16
+ when :join
17
+ if me === evt.sender
18
+ for c in evt.target.split(',')
19
+ channels << c
20
+ end
21
+ end
22
+ when :part
23
+ if me === evt.sender
24
+ for c in evt.target.split(',')
25
+ channels.delete(c)
26
+ end
27
+ end
28
+ when :kick
29
+ if me === evt.args[1]
30
+ channels.delete(evt.target)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,43 @@
1
+ module IIRC
2
+ module Bot::Configure
3
+ def self.included(m)
4
+ require 'set'
5
+ m.extend ClassMethods
6
+ end
7
+
8
+ module ClassMethods
9
+ def configure_actions
10
+ @configures ||= Set.new
11
+ end
12
+
13
+ def configure action=nil, &blk
14
+ configure_actions << (action || blk)
15
+ end
16
+ end
17
+
18
+ def initialize(*)
19
+ super if defined? super
20
+ configure!
21
+ end
22
+
23
+ def configure!
24
+ for action in configure_actions
25
+ if action.is_a? Symbol
26
+ send(action)
27
+ else
28
+ instance_exec(&action)
29
+ end
30
+ end
31
+ end
32
+
33
+ private def configure_actions
34
+ singleton_class.ancestors
35
+ .filter { |m| m.respond_to?(:configure_actions) }
36
+ .flat_map { |m| m.configure_actions.to_a }
37
+ .concat((methods + private_methods).grep(/^configure_(?!actions)/))
38
+ .compact
39
+ .uniq
40
+ end
41
+ end
42
+
43
+ end
@@ -0,0 +1,11 @@
1
+ module IIRC
2
+ module Bot::Formatting
3
+ module_function
4
+ def bold s; "#{s}"; end
5
+ def italic s; "#{s}"; end
6
+ def strike s; "#{s}"; end
7
+ def underline s; "#{s}"; end
8
+ def strip s; s.gsub /\x03(?:\d{1,2}(?:,\d{1,2})?)?/, ''; end
9
+ alias :strip_formatting :strip
10
+ end
11
+ end
@@ -0,0 +1,66 @@
1
+ module IIRC
2
+ module Bot::Hooks
3
+ def hooks
4
+ @hooks ||= Hash.new { |h,v| h[v] = Set.new }
5
+ end
6
+
7
+ def on verb=nil, action=nil, &blk
8
+ if action and blk
9
+ define_singleton_method(action, blk)
10
+ singleton_class.send :private, action
11
+ end
12
+
13
+ if action or blk
14
+ hooks[verb] << (action || blk)
15
+ else
16
+ hooks[verb]
17
+ end
18
+ end
19
+
20
+ def hook action=nil, &blk
21
+ on nil, action, &blk
22
+ end
23
+
24
+ def off verb, action=nil, &blk
25
+ action ||= blk
26
+ if action
27
+ hooks[verb].delete action
28
+ else
29
+ hooks[verb].clear
30
+ end
31
+ end
32
+
33
+ def fire! verb, *args, **kwargs
34
+ if respond_to? :"on_#{verb}"
35
+ send(:"on_#{verb}", *args, **kwargs)
36
+ end
37
+ for action in [*hooks[nil], *hooks[verb]]
38
+ call action, *args, **kwargs
39
+ end
40
+ rescue StopIteration
41
+ StopIteration
42
+ end
43
+
44
+ def call action, *args, **kwargs
45
+ case action
46
+ when Symbol
47
+ args = method(action).arity.then { |n| n.negative? ? args : args[...n] }
48
+ send(action, *args, **kwargs)
49
+ else
50
+ action.call(*args, **kwargs)
51
+ end
52
+ end
53
+
54
+ def run
55
+ lines { |line|
56
+ begin
57
+ evt = parse(line)
58
+ fire! evt.verb, evt
59
+ rescue Exception => ex
60
+ puts ex.message
61
+ puts ex.backtrace
62
+ end
63
+ }
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,45 @@
1
+ module IIRC
2
+ module Bot::IRCv3
3
+ class Batch < Event
4
+ attr_accessor :events
5
+
6
+ def events
7
+ @events ||= []
8
+ end
9
+ end
10
+
11
+ module Batches
12
+ def configure_batches
13
+ hook :receive_batches
14
+ end
15
+
16
+ def batches_in_flight
17
+ @batches_in_flight ||= {}
18
+ end
19
+
20
+ def receive_batches evt
21
+ return if evt.is_a? Batch # fully formed synthetic batch
22
+
23
+ if evt.verb == :batch
24
+ ± = evt.target[0]
25
+ id = evt.target[1..]
26
+
27
+ if ± == '+'
28
+ batches_in_flight[id] = Batch.new(
29
+ sender: evt.sender,
30
+ verb: evt.verb,
31
+ args: evt.args.dup.tap { |args| args[0] = id },
32
+ tags: evt.tags,
33
+ )
34
+ else
35
+ fire! :batch, batches_in_flight.delete(id)
36
+ end
37
+ raise StopIteration
38
+ elsif evt.tags['batch']
39
+ id = evt.tags['batch']
40
+ batches_in_flight[id].events << evt
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,17 @@
1
+ module IIRC
2
+ module Bot::IRCv3
3
+ module Caps
4
+ def caps
5
+ raise NotImplementedError.new
6
+ end
7
+
8
+ def cap? name
9
+ caps.include? name
10
+ end
11
+
12
+ def cap! name
13
+ self << "CAP REQ :#{name}"
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,27 @@
1
+ module IIRC
2
+ module Bot::IRCv3
3
+ module LabeledRequests
4
+ def labeled_request(line)
5
+ SecureRandom.alphanumeric(8).tap { |id|
6
+ labeled_requests[id] = -> evt { yield evt }
7
+ self << "@label=#{id} #{line}"
8
+ }
9
+ end
10
+
11
+ def labeled_requests
12
+ @requests ||= {}
13
+ end
14
+
15
+ private
16
+ def configure_labeled_requests
17
+ hook :dispatch_replies
18
+ end
19
+
20
+ def dispatch_replies(evt)
21
+ if evt.tags['label']
22
+ labeled_requests.delete(evt.tags['label'])&.call(evt)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,18 @@
1
+ module IIRC
2
+ module Bot::IRCv3
3
+ module Parsing
4
+ autoload :IRCParser, 'ircparser'
5
+
6
+ def parse(line)
7
+ msg = IRCParser::Message.parse(line.chomp)
8
+
9
+ Bot::Event.new.tap { |evt|
10
+ evt.sender = IRCParser::RFCWireFormat.__stringify_prefix(msg.prefix) if msg.prefix
11
+ evt.tags = msg.tags
12
+ evt.verb = msg.command.downcase.to_sym
13
+ evt.args = msg.parameters
14
+ }
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,32 @@
1
+ module IIRC
2
+ module Bot::Members
3
+ def self.configure_actions
4
+ [:listen_for_names]
5
+ end
6
+
7
+ def members
8
+ @members ||= {}
9
+ end
10
+
11
+ private
12
+ def listen_for_names
13
+ on :'353', :receive_names
14
+ on :'366', :receive_names_end
15
+ end
16
+
17
+ def receive_names evt
18
+ names = evt.args[3]
19
+ channel = evt.args[2]
20
+ members_receiving[channel].concat names.tr('&+@%~', '').split(' ')
21
+ end
22
+
23
+ def receive_names_end evt
24
+ channel = evt.args[1]
25
+ members[channel] = members_receiving.delete(channel)
26
+ end
27
+
28
+ def members_receiving
29
+ @members_receiving ||= Hash.new { |h,k| h[k] = [] }
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,15 @@
1
+ module IIRC
2
+ module Bot::OperUp
3
+ def self.configure_actions
4
+ [:add_oper_up_hook]
5
+ end
6
+
7
+ def add_oper_up_hook
8
+ on :'001', :oper_up!
9
+ end
10
+
11
+ def oper_up!
12
+ self << "OPER #{ENV['IRC_OPER']}" if ENV['IRC_OPER']
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,24 @@
1
+ module IIRC
2
+ module Bot::Parsing
3
+ def parse(line)
4
+ Bot::Event.new.tap { |evt|
5
+ words = line.chomp.split " "
6
+
7
+ if words[0] and words[0].start_with? ":"
8
+ evt.sender = words.shift[1..]
9
+ end
10
+
11
+ evt.verb = words.shift.downcase.to_sym
12
+
13
+ while word = words.shift
14
+ if word.start_with? ':'
15
+ evt.args << [word[1..], *words].join(' ')
16
+ break
17
+ else
18
+ evt.args << word
19
+ end
20
+ end
21
+ }
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,7 @@
1
+ module IIRC
2
+ module Bot::Pong
3
+ def on_ping evt
4
+ self << "PONG :#{evt.args[0]}"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,9 @@
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
@@ -0,0 +1,11 @@
1
+ module IIRC
2
+ module Bot::Redis
3
+ def self.included(*)
4
+ require 'redis'
5
+ end
6
+
7
+ def redis
8
+ @redis ||= ::Redis.new
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,20 @@
1
+ module IIRC
2
+ module Bot::RegexHooks
3
+ def regex_hooks
4
+ hooks.filter { |k,v| Regexp === k }.freeze
5
+ end
6
+
7
+ private
8
+ def configure_regex_hooks
9
+ on :privmsg, :do_regex_matches
10
+ end
11
+
12
+ def do_regex_matches(evt)
13
+ regex_hooks
14
+ .filter { |k,v| k === evt.message }
15
+ .each_value { |actions|
16
+ actions.each { |action| call action, evt }
17
+ }
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,9 @@
1
+ module IIRC
2
+ module Bot::TrackOwnNick
3
+ def on_nick evt
4
+ if me === evt.sender
5
+ self.user.nick = evt.args[0]
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,25 @@
1
+ module IIRC
2
+ module Bot::Verbs
3
+ def join(channel) self << "JOIN #{channel}" end
4
+ def part(channel, reason=nil) self << "PART #{channel}#{" :#{reason}" if reason}" end
5
+ def names(channel) self << "NAMES #{channel}" end
6
+ def who(target) self << "WHO #{target}" end
7
+ def msg(target, msg) for line in msg.to_s.lines; self << "PRIVMSG #{target} :#{line.chomp}"; end; self end
8
+ def act(target, msg) for line in msg.to_s.lines; self << "PRIVMSG #{target} :\001ACTION #{line.chomp}\001"; end; self end
9
+ def notice(target,msg) for line in msg.to_s.lines; self << "NOTICE #{target} :#{line.chomp}"; end; self end
10
+ def quit(msg='Quitting') self << "QUIT :#{msg}" end
11
+ def invite(target, channel) self << "INVITE #{target} #{channel}" end
12
+ def cycle(target, reason='') part(target, reason); join(target) end
13
+ def nick(new) self << "NICK #{new}" end
14
+ def ping(token=nil) self << (token ? "PING :#{token}" : "PING") end
15
+ def pong(token=nil) self << (token ? "PONG :#{token}" : "PONG") end
16
+ def mode(target, mode) self << "MODE #{target} #{mode}" end
17
+ def wallops(msg) for line in msg.to_s.lines; self << "WALLOPS :#{line.chomp}"; end; self end
18
+ def kill(target, msg='.') self << "KILL #{target} :#{msg}" end
19
+ def kick(channel, target, msg='') self << "KICK #{channel} #{target} :#{msg}" end
20
+ def topic(channel, new_topic='') self << (new_topic ? "TOPIC #{channel} :#{new_topic.lines.first.chomp}" : "TOPIC #{channel}") end
21
+
22
+ alias :privmsg :msg
23
+ alias :say :msg
24
+ end
25
+ end
data/lib/iirc/bot.rb ADDED
@@ -0,0 +1,38 @@
1
+ module IIRC
2
+ class Bot
3
+ attr_accessor :socket
4
+ attr_accessor :user
5
+ alias :me :user
6
+
7
+ def <<(text)
8
+ socket << text + "\r\n"
9
+ self
10
+ end
11
+
12
+ def to_s
13
+ "[#{self.class.name}:#{user.nick}]"
14
+ end
15
+
16
+ def inspect
17
+ "<#{me}>"
18
+ end
19
+
20
+ def initialize(socket=nil, user={})
21
+ super() if defined? super
22
+ self.socket = socket
23
+ self.user = user.is_a?(User) ? user : User.new(**user)
24
+ end
25
+
26
+ def register!
27
+ ensure_registration_info!
28
+ self << "USER #{me.username} 0 0 :#{me.realname}"
29
+ self << "NICK #{me.nick}"
30
+ end
31
+
32
+ private def ensure_registration_info!
33
+ me.username ||= me.nick.downcase
34
+ me.realname ||= me.nick
35
+ raise ArgumentError.new('no nick given') unless me.nick && !me.nick.empty?
36
+ end
37
+ end
38
+ end
data/lib/iirc/event.rb ADDED
@@ -0,0 +1,29 @@
1
+ module IIRC
2
+ class Event
3
+ attr_accessor :sender, :verb, :args, :tags
4
+
5
+ def initialize(sender: nil, verb: nil, args: nil, tags: nil)
6
+ self.sender = sender
7
+ self.verb = verb
8
+ self.args = args
9
+ self.tags = tags
10
+ end
11
+
12
+ def sender=(v)
13
+ v.extend Sender if v
14
+ @sender = v
15
+ end
16
+
17
+ def args=(v)
18
+ @args = v || []
19
+ end
20
+
21
+ def target
22
+ args.first
23
+ end
24
+
25
+ def message
26
+ args.last
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,29 @@
1
+ module IIRC
2
+ module Sender
3
+ def user
4
+ @user ||= User.from_sender(self)
5
+ end
6
+
7
+ def server?
8
+ user.nil?
9
+ end
10
+
11
+ def user?
12
+ not user.nil?
13
+ end
14
+
15
+ def nick; user&.nick end
16
+ def username; user&.username end
17
+ def host; user&.host end
18
+
19
+ def to_prefix
20
+ ":#{self}"
21
+ end
22
+
23
+ def to_s
24
+ delete_prefix(':')
25
+ end
26
+
27
+ alias :inspect :to_prefix
28
+ end
29
+ end
data/lib/iirc/user.rb ADDED
@@ -0,0 +1,57 @@
1
+ module IIRC
2
+ class User
3
+ attr_accessor :nick, :username, :realname, :host
4
+ alias :name :realname
5
+ alias :gecos :realname
6
+
7
+ class << self
8
+ def from_sender(str)
9
+ str = str[1..] if str.start_with? ':'
10
+
11
+ username, _, host = str.partition("@")
12
+ nick, _, username = username.partition("!")
13
+
14
+ return nil if nick.include? '.' # sender is a server, not a user
15
+
16
+ nick = nil if nick.empty?
17
+ username = nil if username.empty?
18
+ host = nil if host.empty?
19
+
20
+ new(nick: nick, username: username, host: host)
21
+ end
22
+ end
23
+
24
+ def initialize(nick:nil,username:nil,realname:nil,host:nil)
25
+ self.nick = nick
26
+ self.username = username
27
+ self.realname = realname
28
+ self.host = host
29
+ end
30
+
31
+ def to_s
32
+ "#{nick}".tap do |s|
33
+ if username and host
34
+ s << "!#{username}@#{host}"
35
+ end
36
+ end
37
+ end
38
+
39
+ def to_prefix
40
+ ":#{self}"
41
+ end
42
+
43
+ def ===(other)
44
+ if other.respond_to? :nick
45
+ nick and nick.downcase == other.nick.downcase
46
+ elsif other.is_a? String
47
+ if other.include? ' ' or other.include? '.'
48
+ false
49
+ elsif nick
50
+ nick and nick.downcase == User.from_source(other)&.nick&.downcase
51
+ end
52
+ else
53
+ false
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IIRC
4
+ VERSION = "0.2.0"
5
+ end
data/lib/iirc.rb ADDED
@@ -0,0 +1,55 @@
1
+ require_relative "iirc/version"
2
+
3
+ require_relative "iirc/event"
4
+ require_relative "iirc/sender"
5
+ require_relative "iirc/user"
6
+
7
+ require_relative "iirc/bot"
8
+ require_relative "iirc/bot/ambient"
9
+ require_relative "iirc/bot/autojoin"
10
+ require_relative "iirc/bot/channels"
11
+ require_relative "iirc/bot/configure"
12
+ require_relative "iirc/bot/formatting"
13
+ require_relative "iirc/bot/hooks"
14
+ require_relative "iirc/bot/members"
15
+ require_relative "iirc/bot/oper_up"
16
+ require_relative "iirc/bot/parsing"
17
+ require_relative "iirc/bot/pong"
18
+ require_relative "iirc/bot/reading"
19
+ require_relative "iirc/bot/redis"
20
+ require_relative "iirc/bot/regex_hooks"
21
+ require_relative "iirc/bot/track_own_nick"
22
+ require_relative "iirc/bot/verbs"
23
+
24
+ require_relative "iirc/bot/ircv3/caps"
25
+ require_relative "iirc/bot/ircv3/parsing"
26
+ require_relative "iirc/bot/ircv3/batches"
27
+ require_relative "iirc/bot/ircv3/labeled_requests"
28
+
29
+ module IIRC
30
+ class Error < StandardError; end
31
+
32
+ class Bot
33
+ include Reading
34
+ include Parsing
35
+ include Hooks
36
+ include Configure
37
+ include Pong
38
+ include TrackOwnNick
39
+ end
40
+
41
+ class IRCv3Bot < Bot
42
+ include IRCv3::Parsing
43
+ include IRCv3::Caps
44
+ include IRCv3::Batches
45
+ include IRCv3::LabeledRequests
46
+ end
47
+
48
+ class Batteries < IRCv3Bot
49
+ include Channels
50
+ include Members
51
+ include Formatting
52
+ include AutoJoin
53
+ include Verbs
54
+ end
55
+ end
metadata ADDED
@@ -0,0 +1,79 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: iirc
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - mooff
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2022-03-12 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: A composable toolkit for IRC bots
14
+ email:
15
+ - mooff@awful.cooking
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - ".github/workflows/main.yml"
21
+ - ".gitignore"
22
+ - CHANGELOG.md
23
+ - Gemfile
24
+ - README.md
25
+ - Rakefile
26
+ - bin/console
27
+ - bin/setup
28
+ - iirc.gemspec
29
+ - lib/iirc.rb
30
+ - lib/iirc/bot.rb
31
+ - lib/iirc/bot/ambient.rb
32
+ - lib/iirc/bot/autojoin.rb
33
+ - lib/iirc/bot/channels.rb
34
+ - lib/iirc/bot/configure.rb
35
+ - lib/iirc/bot/formatting.rb
36
+ - lib/iirc/bot/hooks.rb
37
+ - lib/iirc/bot/ircv3/batches.rb
38
+ - lib/iirc/bot/ircv3/caps.rb
39
+ - lib/iirc/bot/ircv3/labeled_requests.rb
40
+ - lib/iirc/bot/ircv3/parsing.rb
41
+ - lib/iirc/bot/members.rb
42
+ - lib/iirc/bot/oper_up.rb
43
+ - lib/iirc/bot/parsing.rb
44
+ - lib/iirc/bot/pong.rb
45
+ - lib/iirc/bot/reading.rb
46
+ - lib/iirc/bot/redis.rb
47
+ - lib/iirc/bot/regex_hooks.rb
48
+ - lib/iirc/bot/track_own_nick.rb
49
+ - lib/iirc/bot/verbs.rb
50
+ - lib/iirc/event.rb
51
+ - lib/iirc/sender.rb
52
+ - lib/iirc/user.rb
53
+ - lib/iirc/version.rb
54
+ homepage: https://github.com/awfulcooking/iirc
55
+ licenses: []
56
+ metadata:
57
+ homepage_uri: https://github.com/awfulcooking/iirc
58
+ source_code_uri: https://github.com/awfulcooking/iirc
59
+ changelog_uri: https://github.com/awfulcooking/iirc/blob/main/CHANGELOG.md
60
+ post_install_message:
61
+ rdoc_options: []
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: 2.6.0
69
+ required_rubygems_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ requirements: []
75
+ rubygems_version: 3.2.5
76
+ signing_key:
77
+ specification_version: 4
78
+ summary: Lean, mean IRC processing machine
79
+ test_files: []