rflow-components-irc 0.0.1

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/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
data/Rakefile ADDED
@@ -0,0 +1,15 @@
1
+ require 'bundler'
2
+ require 'rspec/core/rake_task'
3
+ require 'rdoc/task'
4
+ Bundler::GemHelper.install_tasks
5
+
6
+ RSpec::Core::RakeTask.new(:spec) do |t|
7
+ t.verbose = true
8
+ t.rspec_opts = '--tty --color'
9
+ end
10
+
11
+ Rake::RDocTask.new do |rd|
12
+ rd.main = "README"
13
+ rd.rdoc_files.include("README", "lib/**/*.rb")
14
+ rd.rdoc_dir = File.join('doc', 'html')
15
+ end
@@ -0,0 +1,216 @@
1
+ require 'eventmachine'
2
+
3
+ require 'rflow'
4
+
5
+ class RFlow
6
+ module Components
7
+ module IRC
8
+
9
+ class Client < RFlow::Component
10
+ input_port :to_server
11
+ output_port :from_server
12
+
13
+ attr_accessor :config
14
+ attr_accessor :client_connection
15
+
16
+ attr_accessor :server, :port
17
+
18
+ DEFAULT_CONFIG = {
19
+ 'server' => '127.0.0.1',
20
+ 'port' => 6667,
21
+ 'server_password' => nil,
22
+ 'nickname' => 'rflow',
23
+ 'username' => 'rflow',
24
+ 'oper_user' => nil,
25
+ 'oper_password' => nil,
26
+ 'nickserv_password' => nil,
27
+ 'reconnect_interval' => 2,
28
+ }
29
+
30
+ def configure!(config)
31
+ @config = DEFAULT_CONFIG.merge config
32
+
33
+ @config['port'] = @config['port'].to_i
34
+ @config['reconnect_interval'] = @config['reconnect_interval'].to_i
35
+ # TODO: More config sanitization
36
+
37
+ @server = @config['server']
38
+ @port = @config['port'].to_i
39
+ end
40
+
41
+ def run!
42
+ @client_connection = EM.connect(@server, @port, Connection) do |conn|
43
+ conn.client = self
44
+ end
45
+ end
46
+
47
+ def process_message(input_port, input_port_key, connection, message)
48
+ RFlow.logger.debug "Received a message"
49
+ return unless message.data_type_name == 'RFlow::Message::Data::IRC::Message'
50
+
51
+ RFlow.logger.debug "Received an IRC::Message message, determining if its mine"
52
+ my_events = message.provenance.find_all {|processing_event| processing_event.component_instance_uuid == instance_uuid}
53
+ RFlow.logger.debug "Found #{my_events.size} processing events from me"
54
+ # Attempt to send the data to each context match
55
+ my_events.each do |processing_event|
56
+ RFlow.logger.debug "Inspecting context #{client_connection.signature.to_s} == #{processing_event.context.to_s}"
57
+ if client_connection.signature.to_s == processing_event.context.to_s
58
+ RFlow.logger.debug "Found connection for #{processing_event.context}"
59
+ client_connection.send_irc_message message
60
+ end
61
+ end
62
+ end
63
+
64
+ def shutdown!
65
+ end
66
+
67
+ def cleanup!
68
+ end
69
+
70
+ class Connection < EventMachine::Connection
71
+ include EventMachine::Protocols::LineText2
72
+
73
+ IRC_LINE_REGEX = /^(?::(\S+) )?(\S+)(?: (.+))?$/
74
+
75
+ attr_accessor :client
76
+ attr_reader :client_ip, :client_port, :server_ip, :server_port
77
+
78
+ def post_init
79
+ @client_port, @client_ip = Socket.unpack_sockaddr_in(get_peername) rescue ["?", "?.?.?.?"]
80
+ @server_port, @server_ip = Socket.unpack_sockaddr_in(get_sockname) rescue ["?", "?.?.?.?"]
81
+ RFlow.logger.debug "Connection from #{@client_ip}:#{@client_port} to #{@server_ip}:#{@server_port}"
82
+ super
83
+ end
84
+
85
+
86
+ def connection_completed
87
+ @reconnecting = false
88
+ @connected = true
89
+
90
+ RFlow.logger.info("Connected to IRC server #{client.config['server']}:#{client.config['port']}")
91
+
92
+ command "PASS", [client.config['server_password']] unless client.config['server_password'].nil?
93
+ command "NICK", [client.config['nickname']]
94
+ command "USER", [client.config['username'], client.config['username'], client.config['username'], client.config['username']]
95
+ command "NickServ IDENTIFY", [client.config['nickname'], client.config['nickserv_password']] unless client.config['nickserv_password'].nil?
96
+ command "OPER", [client.config['oper_user'] || client.config['nickname'], client.config['oper_password']] unless client.config['oper_password'].nil?
97
+ end
98
+
99
+
100
+ def receive_line(line)
101
+ RFlow.logger.debug("IRCClient#receive_line: #{line}")
102
+ processing_event = RFlow::Message::ProcessingEvent.new(client.instance_uuid, Time.now.utc)
103
+
104
+ prefix, cmd, params = parse_irc_line(line)
105
+
106
+ # Now have an optional prefix, required cmd, and optional
107
+ # param array
108
+
109
+ case cmd
110
+ when /PING/
111
+ command('PONG', params)
112
+ else
113
+ # create an IRC message here and send it along
114
+ RFlow.logger.debug("Sending IRC message '#{line}', signature '#{signature.class}:#{signature}', '#{signature.to_s.class}:#{signature.to_s}'")
115
+ irc_message = RFlow::Message.new('RFlow::Message::Data::IRC::Message')
116
+
117
+ irc_message.data.prefix = prefix
118
+ irc_message.data.command = cmd
119
+ irc_message.data.parameters = params
120
+
121
+ processing_event.context = signature.to_s
122
+ processing_event.completed_at = Time.now.utc
123
+ irc_message.provenance << processing_event
124
+
125
+ client.from_server.send_message irc_message
126
+ end
127
+ end
128
+
129
+
130
+ def unbind(reason=nil)
131
+ if @connected or @reconnecting
132
+ RFlow.logger.error("Disconnected from IRC server #{client.config['server']}:#{client.config['port']} due to '#{reason}', reconnecting ...")
133
+ EM.add_timer(client.config['reconnect_interval']) do
134
+ RFlow.logger.error "Attempting reconnect to IRC server #{client.config['server']}:#{client.config['port']}"
135
+ reconnect(client.config['server'], client.config['port'])
136
+ end
137
+ @connected = false
138
+ @reconnecting = true
139
+ else
140
+ error_message = "Unable to connect to IRC server #{client.config['server']}:#{client.config['port']} due to '#{reason}'"
141
+ RFlow.logger.error error_message
142
+ raise RuntimeError, error_message
143
+ end
144
+ end
145
+
146
+
147
+ def parse_irc_line(line)
148
+ match = IRC_LINE_REGEX.match(line)
149
+ unless match
150
+ RFlow.logger.error "Error parsing IRC line '#{line}'"
151
+ return
152
+ end
153
+
154
+ prefix, cmd, param_string = match[1..3]
155
+
156
+ param_string, trailing_param = param_string.split(' :', 1)
157
+ params = param_string.split(' ')
158
+ params << trailing_param
159
+
160
+ [prefix, cmd, params]
161
+ end
162
+
163
+
164
+ def send_irc_message(irc_message)
165
+ RFlow.logger.debug "Sending an IRC message to #{client_ip}:#{client_port}"
166
+
167
+ command irc_message.data.command, irc_message.data.parameters, irc_message.data.prefix
168
+ end
169
+
170
+
171
+ def send_irc_line(line)
172
+ RFlow.logger.debug "Sending line '#{line}'"
173
+ send_data "#{line}\r\n"
174
+ end
175
+
176
+
177
+ def privmsg(nick, msg)
178
+ msg.split("\n").each do |msg_line|
179
+ command("PRIVMSG #{nick} :#{msg_line}")
180
+ end
181
+ end
182
+
183
+
184
+ def command(cmd, args=[], prefix=nil)
185
+ RFlow.logger.debug("command: '#{cmd}' with args ['#{args.join("', '")}'] and prefix '#{prefix}'")
186
+ line = ''
187
+ if prefix
188
+ line << ":#{prefix} "
189
+ end
190
+
191
+
192
+ line << cmd.upcase
193
+
194
+ last_arg = args.pop
195
+ unless args.empty?
196
+ line << " #{args.join ' '}"
197
+ end
198
+
199
+ if last_arg =~ /\s/
200
+ line << ' :' << last_arg
201
+ else
202
+ line << ' ' << last_arg
203
+ end
204
+
205
+ send_irc_line line
206
+ end
207
+
208
+
209
+ end
210
+
211
+ end
212
+
213
+ end
214
+ end
215
+ end
216
+
@@ -0,0 +1,27 @@
1
+ class RFlow
2
+ module Components
3
+ module IRC
4
+
5
+ # The set of extensions to add capability to IRC data types
6
+ module Extensions
7
+
8
+ module IRCMessageExtension
9
+ def self.extended(base_data)
10
+ base_data.data_object ||= {'prefix' => nil, 'command' => 'PRIVMSG', 'parameters' => []}
11
+ end
12
+
13
+ # Default accessors
14
+ ['prefix', 'command', 'parameters'].each do |name|
15
+ define_method name do |*args|
16
+ data_object[name]
17
+ end
18
+ define_method :"#{name}=" do |*args|
19
+ data_object[name] = args.first
20
+ end
21
+ end
22
+ end
23
+
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,7 @@
1
+ class RFlow
2
+ module Components
3
+ module IRC
4
+ VERSION = "0.0.1"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,25 @@
1
+ require 'rflow/components/irc/extensions'
2
+ require 'rflow/components/irc/client'
3
+
4
+ class RFlow
5
+ module Components
6
+ module IRC
7
+ # Load the schemas
8
+ SCHEMA_DIRECTORY = ::File.expand_path(::File.join(::File.dirname(__FILE__), '..', '..', '..', 'schema'))
9
+
10
+ SCHEMA_FILES = {
11
+ 'irc_message.avsc' => 'RFlow::Message::Data::IRC::Message',
12
+ }
13
+
14
+ SCHEMA_FILES.each do |file_name, data_type_name|
15
+ schema_string = ::File.read(::File.join(SCHEMA_DIRECTORY, file_name))
16
+ RFlow::Configuration.add_available_data_type data_type_name, 'avro', schema_string
17
+ end
18
+
19
+ # Load the data extensions
20
+ RFlow::Configuration.add_available_data_extension('RFlow::Message::Data::IRC::Message',
21
+ RFlow::Components::IRC::Extensions::IRCMessageExtension)
22
+
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,3 @@
1
+ require 'rflow'
2
+ require 'rflow/components/irc'
3
+
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "rflow/components/irc/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "rflow-components-irc"
7
+ s.version = RFlow::Components::IRC::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.required_ruby_version = '>= 1.9'
10
+ s.authors = ["Michael L. Artz"]
11
+ s.email = ["michael.artz@redjack.com"]
12
+ s.homepage = "https://github.com/redjack/rflow-components-irc"
13
+ s.summary = %q{IRC client and server components for the RFlow FBP framework}
14
+ s.description = %q{IRC client and server components for the RFlow FBP framework. Also includes the necessary message types}
15
+
16
+ s.rubyforge_project = "rflow-components-irc"
17
+
18
+ s.files = `git ls-files`.split("\n")
19
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
20
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
21
+ s.require_paths = ["lib"]
22
+
23
+ s.add_dependency 'rflow', '~> 0.0.1'
24
+
25
+ s.add_development_dependency 'rspec', '~> 2.5.0'
26
+ s.add_development_dependency 'rake', '~> 0.8.7'
27
+ end
@@ -0,0 +1,16 @@
1
+ {
2
+ "type": "record",
3
+ "name": "Message",
4
+ "namespace": "org.rflow.message.data.irc",
5
+ "aliases": [],
6
+ "fields": [
7
+ {"name": "client_ip", "type": ["string", "null"]},
8
+ {"name": "client_port", "type": ["int", "null"]},
9
+ {"name": "server_ip", "type": ["string", "null"]},
10
+ {"name": "server_port", "type": ["int", "null"]},
11
+
12
+ {"name": "prefix", "type": ["string", "null"]},
13
+ {"name": "command", "type": "string"},
14
+ {"name": "parameters", "type": {"type": "array", "items": ["bytes", "null"]}}
15
+ ]
16
+ }
@@ -0,0 +1,47 @@
1
+ require 'spec_helper.rb'
2
+
3
+ # describe RFlow::Components::File::Extensions::FileExtension do
4
+ # before(:each) do
5
+ # @schema_string = RFlow::Configuration.available_data_types['RFlow::Message::Data::File']['avro']
6
+ # end
7
+ #
8
+ # it "should add the extension to RFlow::Configuration" do
9
+ # RFlow::Configuration.available_data_extensions['RFlow::Message::Data::File'].should include(described_class)
10
+ # end
11
+ #
12
+ # it "should set the defaults" do
13
+ # file = RFlow::Message.new('RFlow::Message::Data::File')
14
+ #
15
+ # file.data.path.should == '/'
16
+ # file.data.size.should == 0
17
+ # file.data.content.should == ''
18
+ # file.data.creation_timestamp.should == nil
19
+ # file.data.modification_timestamp.should == nil
20
+ # file.data.accessed_timestamp.should == nil
21
+ # end
22
+ #
23
+ # it "should correctly use integers or strings for size field" do
24
+ # file = RFlow::Message.new('RFlow::Message::Data::File')
25
+ #
26
+ # file.data.size.should == 0
27
+ # file.data.size = 10
28
+ # file.data.size.should == 10
29
+ # file.data.size = '20'
30
+ # file.data.size == 20
31
+ # end
32
+ #
33
+ # it "should correctly use Time or xmlschema strings for timestamp fields" do
34
+ # file = RFlow::Message.new('RFlow::Message::Data::File')
35
+ #
36
+ # file.data.creation_timestamp.should == nil
37
+ # now = Time.now
38
+ #
39
+ # file.data.creation_timestamp = now
40
+ # file.data.creation_timestamp.should == Time.xmlschema(now.xmlschema(9))
41
+ #
42
+ # file.data.creation_timestamp = now.xmlschema
43
+ # file.data.creation_timestamp.should == Time.xmlschema(now.xmlschema)
44
+ # end
45
+ #
46
+ #
47
+ # end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper.rb'
2
+
3
+ describe 'RFlow::Message::Data::IRC::Message Avro Schema' do
4
+ before(:each) do
5
+ @schema_string = RFlow::Configuration.available_data_types['RFlow::Message::Data::IRC::Message']['avro']
6
+ end
7
+
8
+ it "should encode and decode an object" do
9
+ irc_message = {
10
+ # 'prefix' => nil,
11
+ 'command' => 'PRIVMSG',
12
+ 'parameters' => ['yo yo yo'],
13
+ }
14
+
15
+ expect {encode_avro(@schema_string, irc_message)}.to_not raise_error
16
+ avro_encoded_irc_message = encode_avro(@schema_string, irc_message)
17
+
18
+ expect {decode_avro(@schema_string, avro_encoded_irc_message)}.to_not raise_error
19
+ decoded_irc_message = decode_avro(@schema_string, avro_encoded_irc_message)
20
+
21
+ decoded_irc_message['prefix'].should == irc_message['prefix']
22
+ decoded_irc_message['command'].should == irc_message['command']
23
+ decoded_irc_message['parameters'].should == irc_message['parameters']
24
+
25
+ end
26
+ end
27
+
@@ -0,0 +1,19 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'rflow-components-irc'))
2
+
3
+ require 'logger'
4
+
5
+ def decode_avro(schema_string, serialized_object)
6
+ schema = Avro::Schema.parse(schema_string)
7
+ serialized_object.force_encoding 'BINARY'
8
+ sio = StringIO.new(serialized_object)
9
+ Avro::IO::DatumReader.new(schema, schema).read Avro::IO::BinaryDecoder.new(sio)
10
+ end
11
+
12
+ def encode_avro(schema_string, object)
13
+ encoded_string = ''
14
+ encoded_string.force_encoding 'BINARY'
15
+ schema = Avro::Schema.parse(schema_string)
16
+ sio = StringIO.new(encoded_string)
17
+ Avro::IO::DatumWriter.new(schema).write object, Avro::IO::BinaryEncoder.new(sio)
18
+ encoded_string
19
+ end
metadata ADDED
@@ -0,0 +1,110 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rflow-components-irc
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Michael L. Artz
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-03-17 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rflow
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 0.0.1
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 0.0.1
30
+ - !ruby/object:Gem::Dependency
31
+ name: rspec
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 2.5.0
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 2.5.0
46
+ - !ruby/object:Gem::Dependency
47
+ name: rake
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: 0.8.7
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 0.8.7
62
+ description: IRC client and server components for the RFlow FBP framework. Also includes
63
+ the necessary message types
64
+ email:
65
+ - michael.artz@redjack.com
66
+ executables: []
67
+ extensions: []
68
+ extra_rdoc_files: []
69
+ files:
70
+ - .gitignore
71
+ - Gemfile
72
+ - Rakefile
73
+ - lib/rflow-components-irc.rb
74
+ - lib/rflow/components/irc.rb
75
+ - lib/rflow/components/irc/client.rb
76
+ - lib/rflow/components/irc/extensions.rb
77
+ - lib/rflow/components/irc/version.rb
78
+ - rflow-components-irc.gemspec
79
+ - schema/irc_message.avsc
80
+ - spec/extensions_spec.rb
81
+ - spec/schema_spec.rb
82
+ - spec/spec_helper.rb
83
+ homepage: https://github.com/redjack/rflow-components-irc
84
+ licenses: []
85
+ post_install_message:
86
+ rdoc_options: []
87
+ require_paths:
88
+ - lib
89
+ required_ruby_version: !ruby/object:Gem::Requirement
90
+ none: false
91
+ requirements:
92
+ - - ! '>='
93
+ - !ruby/object:Gem::Version
94
+ version: '1.9'
95
+ required_rubygems_version: !ruby/object:Gem::Requirement
96
+ none: false
97
+ requirements:
98
+ - - ! '>='
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ requirements: []
102
+ rubyforge_project: rflow-components-irc
103
+ rubygems_version: 1.8.24
104
+ signing_key:
105
+ specification_version: 3
106
+ summary: IRC client and server components for the RFlow FBP framework
107
+ test_files:
108
+ - spec/extensions_spec.rb
109
+ - spec/schema_spec.rb
110
+ - spec/spec_helper.rb