blur 1.4
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.
- data/library/blur.rb +20 -0
- data/library/blur/client.rb +89 -0
- data/library/blur/enhancements.rb +9 -0
- data/library/blur/handling.rb +114 -0
- data/library/blur/network.rb +71 -0
- data/library/blur/network/channel.rb +33 -0
- data/library/blur/network/command.rb +50 -0
- data/library/blur/network/connection.rb +50 -0
- data/library/blur/network/user.rb +25 -0
- metadata +73 -0
data/library/blur.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'pp'
|
4
|
+
require 'yaml'
|
5
|
+
require 'socket'
|
6
|
+
require 'ostruct'
|
7
|
+
|
8
|
+
Dir.glob("#{File.dirname __FILE__}/blur/**/*.rb").each &method(:require)
|
9
|
+
|
10
|
+
module Blur
|
11
|
+
class << Version = [1,4]
|
12
|
+
def to_s; join '.' end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.connect options, &block
|
16
|
+
Client.new(options).tap do |client|
|
17
|
+
client.instance_eval &block
|
18
|
+
end.connect
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'blur/handling'
|
4
|
+
|
5
|
+
module Blur
|
6
|
+
class Client
|
7
|
+
include Handling
|
8
|
+
|
9
|
+
attr_accessor :options, :networks
|
10
|
+
|
11
|
+
def initialize options
|
12
|
+
@options = options
|
13
|
+
@networks = []
|
14
|
+
@callbacks = {}
|
15
|
+
@connected = true
|
16
|
+
|
17
|
+
@options[:networks].each do |options|
|
18
|
+
@networks.<< Network.new options
|
19
|
+
end
|
20
|
+
|
21
|
+
trap 2, &method(:quit)
|
22
|
+
end
|
23
|
+
|
24
|
+
def connect
|
25
|
+
networks = @networks.select { |network| not network.connected? }
|
26
|
+
|
27
|
+
networks.each do |network|
|
28
|
+
network.delegate = self
|
29
|
+
network.connect
|
30
|
+
end
|
31
|
+
|
32
|
+
run_loop
|
33
|
+
end
|
34
|
+
|
35
|
+
def got_command network, command
|
36
|
+
puts "<- \e[1m#{network}\e[0m | #{command}"
|
37
|
+
name = :"got_#{command.name.downcase}"
|
38
|
+
|
39
|
+
if respond_to? name
|
40
|
+
__send__ name, network, command
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def quit signal
|
45
|
+
@networks.each do |network|
|
46
|
+
network.transmit :QUIT, "Got SIGINT?"
|
47
|
+
network.transcieve
|
48
|
+
network.disconnect
|
49
|
+
end
|
50
|
+
|
51
|
+
@connected = false
|
52
|
+
|
53
|
+
exit 0
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def run_loop
|
59
|
+
puts "Starting run loop ..."
|
60
|
+
|
61
|
+
while @connected
|
62
|
+
@networks.each do |network|
|
63
|
+
begin
|
64
|
+
network.transcieve
|
65
|
+
sleep 0.05
|
66
|
+
rescue StandardError => exception
|
67
|
+
puts "#{network} threw an exception: #{exception.class.name} #{exception.message} #{exception.backtrace.to_s}"
|
68
|
+
|
69
|
+
network.disconnect if network.connected?
|
70
|
+
@networks.delete network
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
puts "Ended run loop ..."
|
76
|
+
end
|
77
|
+
|
78
|
+
def emit name, *args
|
79
|
+
@callbacks[name].each do |callback|
|
80
|
+
callback.call *args
|
81
|
+
end if @callbacks[name]
|
82
|
+
end
|
83
|
+
|
84
|
+
def catch name, &block
|
85
|
+
(@callbacks[name] ||= []) << block
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Blur
|
4
|
+
class Client
|
5
|
+
module Handling
|
6
|
+
|
7
|
+
protected
|
8
|
+
|
9
|
+
# End of MOTD
|
10
|
+
def got_end_of_motd network, command
|
11
|
+
emit :connection_ready, network
|
12
|
+
|
13
|
+
network.options[:channels].each do |channel|
|
14
|
+
network.transmit :JOIN, channel
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# The /NAMES list
|
19
|
+
def got_353 network, command
|
20
|
+
name = command[2]
|
21
|
+
users = command[3].split.map &Network::User.method(:new)
|
22
|
+
|
23
|
+
if channel = network.channel_by_name(name)
|
24
|
+
users.each do |user|
|
25
|
+
user.channel = channel
|
26
|
+
channel.users << user
|
27
|
+
end
|
28
|
+
else
|
29
|
+
network.channels.<< Network::Channel.new name, network, users
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Are we still breathing?
|
34
|
+
def got_ping network, command
|
35
|
+
network.transmit :PONG, command[0]
|
36
|
+
end
|
37
|
+
|
38
|
+
# Someone changed their nickname
|
39
|
+
def got_nick network, command
|
40
|
+
nick = command.sender.nickname
|
41
|
+
|
42
|
+
if channels = network.channels_with_user(nick)
|
43
|
+
channels.each do |channel|
|
44
|
+
if user = channel.user_by_nick(nick)
|
45
|
+
emit :user_rename, channel, user, command[0]
|
46
|
+
user.nick = command[0]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Someone send a message
|
53
|
+
def got_privmsg network, command
|
54
|
+
return if command.sender.is_a? String # Ignore all server privmsgs
|
55
|
+
|
56
|
+
name, message = command.params
|
57
|
+
|
58
|
+
if channel = network.channel_by_name(name)
|
59
|
+
if user = channel.user_by_nick(command.sender.nickname)
|
60
|
+
emit :message, user, channel, message
|
61
|
+
else
|
62
|
+
# Odd… this shouldn't happen
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Someone joined a channel
|
68
|
+
def got_join network, command
|
69
|
+
name = command[0]
|
70
|
+
user = Network::User.new command.sender.nickname
|
71
|
+
|
72
|
+
if channel = network.channel_by_name(name)
|
73
|
+
user.name = command.sender.username
|
74
|
+
user.host = command.sender.hostname
|
75
|
+
|
76
|
+
channel.users << user
|
77
|
+
|
78
|
+
emit :user_entered, channel, user
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Someone left a channel
|
83
|
+
def got_part network, command
|
84
|
+
name = command[0]
|
85
|
+
|
86
|
+
if channel = network.channel_by_name(name)
|
87
|
+
if user = channel.user_by_nick(command.sender.nickname)
|
88
|
+
channel.users.delete user
|
89
|
+
|
90
|
+
emit :user_left, channel, user
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# Someone quit irc
|
96
|
+
def got_quit network, command
|
97
|
+
nick = command.sender.nickname
|
98
|
+
|
99
|
+
if channels = network.channels_with_user(nick)
|
100
|
+
channels.each do |channel|
|
101
|
+
if user = channel.user_by_nick(nick)
|
102
|
+
channel.users.delete user
|
103
|
+
|
104
|
+
emit :user_quit, channel, user
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
alias_method :got_422, :got_end_of_motd
|
111
|
+
alias_method :got_376, :got_end_of_motd
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Blur
|
4
|
+
class Network
|
5
|
+
class ConnectionError < StandardError; end
|
6
|
+
attr_accessor :options, :channels, :delegate, :connection
|
7
|
+
|
8
|
+
def connected?; @connection.established? end
|
9
|
+
|
10
|
+
def host; @options[:hostname] end
|
11
|
+
def port; @options[:port] ||= (@options[:secure] == true) ? 6697 : 6667 end
|
12
|
+
|
13
|
+
def initialize options = {}
|
14
|
+
@options = options
|
15
|
+
|
16
|
+
unless options[:nickname]
|
17
|
+
raise ArgumentError, "nickname is missing from the network's option block"
|
18
|
+
end
|
19
|
+
|
20
|
+
@options[:username] ||= @options[:nickname]
|
21
|
+
@options[:realname] ||= @options[:username]
|
22
|
+
@options[:channels] ||= []
|
23
|
+
|
24
|
+
@channels = []
|
25
|
+
@connection = Connection.new self, host, port
|
26
|
+
end
|
27
|
+
|
28
|
+
def say recipient, message
|
29
|
+
transmit :PRIVMSG, recipient, message
|
30
|
+
end
|
31
|
+
|
32
|
+
def got_command command
|
33
|
+
@delegate.got_command self, command
|
34
|
+
end
|
35
|
+
|
36
|
+
def channel_by_name name
|
37
|
+
@channels.find { |channel| channel.name == name }
|
38
|
+
end
|
39
|
+
|
40
|
+
def channels_with_user nick
|
41
|
+
@channels.select { |channel| channel.user_by_nick nick }
|
42
|
+
end
|
43
|
+
|
44
|
+
def connect
|
45
|
+
@connection.establish
|
46
|
+
|
47
|
+
transmit :PASS, @options[:password] if @options[:password]
|
48
|
+
transmit :NICK, @options[:nickname]
|
49
|
+
transmit :USER, @options[:username], :void, :void, @options[:realname]
|
50
|
+
end
|
51
|
+
|
52
|
+
def disconnect
|
53
|
+
@connection.terminate
|
54
|
+
end
|
55
|
+
|
56
|
+
def transmit name, *arguments
|
57
|
+
command = Command.new name, arguments
|
58
|
+
puts "-> \e[1m#{self}\e[0m | #{command}"
|
59
|
+
|
60
|
+
@connection.transmit command
|
61
|
+
end
|
62
|
+
|
63
|
+
def transcieve
|
64
|
+
@connection.transcieve
|
65
|
+
end
|
66
|
+
|
67
|
+
def to_s
|
68
|
+
%{#<#{self.class.name} "#{host}:#{port}">}
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Blur
|
4
|
+
class Network
|
5
|
+
class Channel
|
6
|
+
attr_accessor :name, :users
|
7
|
+
|
8
|
+
def initialize name, network = nil, users = []
|
9
|
+
@name = name
|
10
|
+
@users = users
|
11
|
+
@network = network
|
12
|
+
|
13
|
+
users.each { |user| user.channel = self }
|
14
|
+
end
|
15
|
+
|
16
|
+
def say message
|
17
|
+
@network.say self, message
|
18
|
+
end
|
19
|
+
|
20
|
+
def user_by_nick nick
|
21
|
+
@users.find { |user| user.nick == nick }
|
22
|
+
end
|
23
|
+
|
24
|
+
def inspect
|
25
|
+
%{#<#{self.class.name} @name=#{@name.inspect} @users=#{@users.inspect}}
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_s
|
29
|
+
@name
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Blur
|
4
|
+
class Network
|
5
|
+
class Command
|
6
|
+
attr_accessor :name, :params, :prefix
|
7
|
+
|
8
|
+
Pattern = /^(?:[:@]([^\s]+) )?([^\s]+)(?: ((?:[^:\s][^\s]* ?)*))?(?: ?:(.*))?$/
|
9
|
+
|
10
|
+
def self.parse data
|
11
|
+
match = data.strip.match Pattern
|
12
|
+
prefix, name, args, extra = match.captures
|
13
|
+
params = extra ? args.split << extra : args.split
|
14
|
+
|
15
|
+
new(name, params).tap do |this|
|
16
|
+
this.prefix = prefix
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def [] index; @params[index] end
|
21
|
+
|
22
|
+
def initialize name, params = []
|
23
|
+
@name, @params = name, params
|
24
|
+
end
|
25
|
+
|
26
|
+
def sender
|
27
|
+
return @sender if @sender
|
28
|
+
|
29
|
+
if prefix =~ /^(\S+)!(\S+)@(\S+)$/
|
30
|
+
@sender = OpenStruct.new nickname: $1, username: $2, hostname: $3
|
31
|
+
else
|
32
|
+
@sender = prefix
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def to_s
|
37
|
+
String.new.tap do |line|
|
38
|
+
line << "#{prefix} " if prefix
|
39
|
+
line << name.to_s
|
40
|
+
|
41
|
+
params.each_with_index do |param, index|
|
42
|
+
line << ' '
|
43
|
+
line << ?: if index == params.length - 1 and param =~ /[ :]/
|
44
|
+
line << param.to_s
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Blur
|
4
|
+
class Network
|
5
|
+
class Connection
|
6
|
+
def established?; @socket and not (@socket.closed? or @socket.eof?) end
|
7
|
+
|
8
|
+
def initialize delegate, host = nil, port = nil
|
9
|
+
@host = host
|
10
|
+
@port = port
|
11
|
+
@queue = []
|
12
|
+
@buffer = ""
|
13
|
+
@socket = nil
|
14
|
+
@delegate = delegate
|
15
|
+
end
|
16
|
+
|
17
|
+
def establish
|
18
|
+
@socket = TCPSocket.new @host, @port
|
19
|
+
end
|
20
|
+
|
21
|
+
def terminate
|
22
|
+
@socket.close
|
23
|
+
@socket = nil
|
24
|
+
end
|
25
|
+
|
26
|
+
def transmit command
|
27
|
+
@queue.push command
|
28
|
+
end
|
29
|
+
|
30
|
+
def transcieve
|
31
|
+
readable, writable = IO.select [@socket], [@socket]
|
32
|
+
|
33
|
+
# If the socket is ready to recieve, do so.
|
34
|
+
if socket = readable.first
|
35
|
+
@buffer.<< socket.read_nonblock 512
|
36
|
+
|
37
|
+
while index = @buffer.index(?\n)
|
38
|
+
command = Command.parse @buffer.slice! 0..index
|
39
|
+
@delegate.got_command command
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# If it's ready to write, do that too until the outoing queue is empty.
|
44
|
+
if socket = writable.first
|
45
|
+
socket.write_nonblock "#{command}\n" while command = @queue.shift
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Blur
|
4
|
+
class Network
|
5
|
+
class User
|
6
|
+
attr_accessor :nick, :name, :host, :channel
|
7
|
+
|
8
|
+
def initialize nick
|
9
|
+
@nick = nick.sub /^[@|~|\+|%|&]/, ''
|
10
|
+
end
|
11
|
+
|
12
|
+
def synchronize sender
|
13
|
+
@nick, @name, @host = sender.nickname, sender.username, sender.hostname
|
14
|
+
end
|
15
|
+
|
16
|
+
def inspect
|
17
|
+
%{#<#{self.class.name} @nick=#{@nick.inspect} @channel=#{@channel.name.inspect}>}
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_s
|
21
|
+
@nick
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: blur
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 1
|
7
|
+
- 4
|
8
|
+
version: "1.4"
|
9
|
+
platform: ruby
|
10
|
+
authors:
|
11
|
+
- Mikkel Kroman
|
12
|
+
autorequire:
|
13
|
+
bindir: executables
|
14
|
+
cert_chain: []
|
15
|
+
|
16
|
+
date: 2011-01-17 00:00:00 +01:00
|
17
|
+
default_executable:
|
18
|
+
dependencies: []
|
19
|
+
|
20
|
+
description:
|
21
|
+
email: mk@maero.dk
|
22
|
+
executables: []
|
23
|
+
|
24
|
+
extensions: []
|
25
|
+
|
26
|
+
extra_rdoc_files: []
|
27
|
+
|
28
|
+
files:
|
29
|
+
- library/blur/client.rb
|
30
|
+
- library/blur/enhancements.rb
|
31
|
+
- library/blur/handling.rb
|
32
|
+
- library/blur/network/channel.rb
|
33
|
+
- library/blur/network/command.rb
|
34
|
+
- library/blur/network/connection.rb
|
35
|
+
- library/blur/network/user.rb
|
36
|
+
- library/blur/network.rb
|
37
|
+
- library/blur.rb
|
38
|
+
has_rdoc: true
|
39
|
+
homepage:
|
40
|
+
licenses: []
|
41
|
+
|
42
|
+
post_install_message:
|
43
|
+
rdoc_options: []
|
44
|
+
|
45
|
+
require_paths:
|
46
|
+
- library
|
47
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
48
|
+
none: false
|
49
|
+
requirements:
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
segments:
|
53
|
+
- 1
|
54
|
+
- 9
|
55
|
+
- 1
|
56
|
+
version: 1.9.1
|
57
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
segments:
|
63
|
+
- 0
|
64
|
+
version: "0"
|
65
|
+
requirements: []
|
66
|
+
|
67
|
+
rubyforge_project:
|
68
|
+
rubygems_version: 1.3.7
|
69
|
+
signing_key:
|
70
|
+
specification_version: 3
|
71
|
+
summary: Extensible IRC library
|
72
|
+
test_files: []
|
73
|
+
|