net-irc 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (8) hide show
  1. data/ChangeLog +0 -0
  2. data/README +92 -0
  3. data/Rakefile +131 -0
  4. data/examples/lig.rb +276 -0
  5. data/examples/tig.rb +446 -0
  6. data/examples/wig.rb +132 -0
  7. data/lib/net/irc.rb +844 -0
  8. metadata +72 -0
data/ChangeLog ADDED
File without changes
data/README ADDED
@@ -0,0 +1,92 @@
1
+
2
+ = net-irc
3
+
4
+
5
+ == Description
6
+
7
+ IRC library. This is mostly conform to RFC1459 but partly not for convenience.
8
+
9
+
10
+ == Installation
11
+
12
+ === Archive Installation
13
+
14
+ rake install
15
+
16
+ === Gem Installation
17
+
18
+ gem install net-irc
19
+
20
+
21
+ == Features/Problems
22
+
23
+ * IRC client (for bot)
24
+ * IRC server (for gateway to webservices)
25
+
26
+ == Synopsis
27
+
28
+ === Client
29
+
30
+ require "net/irc"
31
+
32
+ class SimpleClient < Net::IRC::Client
33
+ def on_privmsg(m)
34
+ super
35
+ channel, message = *m
36
+ if message =~ /Hello/
37
+ post NOTICE, channel, "Hello!"
38
+ end
39
+ end
40
+ end
41
+
42
+ Net::IRC::Client manages channel status and the information is set in @channels.
43
+ So, be careful to use @channels instance variable and call super surely.
44
+
45
+ === Server
46
+
47
+ see example/lig.rb
48
+
49
+
50
+ == IRC Gateways
51
+
52
+ There are some gateways connecting to webservices.
53
+
54
+ * Lingr
55
+ * Twitter
56
+ * Wassr
57
+
58
+ If you want to run it, type following:
59
+
60
+ $ cd `ruby -rubygems -e 'print Gem.searcher.find("net/irc").full_gem_path+"/examples"'`
61
+
62
+ Lingr:
63
+ $ sudo gem install pit # for configuration
64
+ $ ./lig.rb
65
+
66
+ Twitter:
67
+ $ ./tig.rb
68
+
69
+ Wassr:
70
+ $ ./wig.rb
71
+
72
+ Run as daemon in default. If you want to help:
73
+
74
+ $ ./lig.rb --help
75
+ Usage: ./lig.rb [opts]
76
+
77
+
78
+ Options:
79
+ -p, --port [PORT=16669] listen port number
80
+ -h, --host [HOST=localhost] listen host
81
+ -l, --log LOG log file
82
+ -a, --api_key API_KEY Your api key on Lingr
83
+ --debug Enable debug mode
84
+
85
+
86
+ == Copyright
87
+
88
+ This library is based on RICE ( http://arika.org/ruby/rice ) written by akira yamada.
89
+
90
+ Author:: cho45 <cho45@lowreal.net>
91
+ Copyright:: Copyright (c) 2008 cho45
92
+ License:: Ruby's
data/Rakefile ADDED
@@ -0,0 +1,131 @@
1
+ require 'rubygems'
2
+ require "shipit"
3
+ require 'rake'
4
+ require 'rake/clean'
5
+ require 'rake/packagetask'
6
+ require 'rake/gempackagetask'
7
+ require 'rake/rdoctask'
8
+ require 'rake/contrib/rubyforgepublisher'
9
+ require 'rake/contrib/sshpublisher'
10
+ require 'fileutils'
11
+ require 'spec/rake/spectask'
12
+
13
+ include FileUtils
14
+
15
+ $LOAD_PATH.unshift "lib"
16
+ require "net/irc"
17
+
18
+ NAME = "net-irc"
19
+ AUTHOR = "cho45"
20
+ EMAIL = "cho45@lowreal.net"
21
+ DESCRIPTION = ""
22
+ RUBYFORGE_PROJECT = "lowreal"
23
+ HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
24
+ BIN_FILES = %w( )
25
+ VERS = Net::IRC::VERSION
26
+
27
+ REV = File.read(".svn/entries")[/committed-rev="(d+)"/, 1] rescue nil
28
+ CLEAN.include ['**/.*.sw?', '*.gem', '.config']
29
+ RDOC_OPTS = [
30
+ '--title', "#{NAME} documentation",
31
+ "--charset", "utf-8",
32
+ "--opname", "index.html",
33
+ "--line-numbers",
34
+ "--main", "README",
35
+ "--inline-source",
36
+ ]
37
+
38
+ task :default => [:spec]
39
+ task :package => [:clean]
40
+
41
+ Spec::Rake::SpecTask.new do |t|
42
+ t.spec_opts = ['--options', "spec/spec.opts"]
43
+ t.spec_files = FileList['spec/*_spec.rb']
44
+ end
45
+
46
+ spec = Gem::Specification.new do |s|
47
+ s.name = NAME
48
+ s.version = VERS
49
+ s.platform = Gem::Platform::RUBY
50
+ s.has_rdoc = true
51
+ s.extra_rdoc_files = ["README", "ChangeLog"]
52
+ s.rdoc_options += RDOC_OPTS + ['--exclude', '^(examples|extras)/']
53
+ s.summary = DESCRIPTION
54
+ s.description = DESCRIPTION
55
+ s.author = AUTHOR
56
+ s.email = EMAIL
57
+ s.homepage = HOMEPATH
58
+ s.executables = BIN_FILES
59
+ s.rubyforge_project = RUBYFORGE_PROJECT
60
+ s.bindir = "bin"
61
+ s.require_path = "lib"
62
+ s.autorequire = ""
63
+
64
+ #s.add_dependency('activesupport', '>=1.3.1')
65
+ #s.required_ruby_version = '>= 1.8.2'
66
+
67
+ s.files = %w(README ChangeLog Rakefile) +
68
+ Dir.glob("{bin,doc,test,lib,templates,generator,extras,website,script}/**/*") +
69
+ Dir.glob("ext/**/*.{h,c,rb}") +
70
+ Dir.glob("examples/**/*.rb") +
71
+ Dir.glob("tools/*.rb")
72
+
73
+ s.extensions = FileList["ext/**/extconf.rb"].to_a
74
+ end
75
+
76
+ Rake::GemPackageTask.new(spec) do |p|
77
+ p.need_tar = true
78
+ p.gem_spec = spec
79
+ end
80
+
81
+ task :install do
82
+ name = "#{NAME}-#{VERS}.gem"
83
+ sh %{rake package}
84
+ sh %{sudo gem install pkg/#{name}}
85
+ end
86
+
87
+ task :uninstall => [:clean] do
88
+ sh %{sudo gem uninstall #{NAME}}
89
+ end
90
+
91
+
92
+ Rake::RDocTask.new do |rdoc|
93
+ rdoc.rdoc_dir = 'html'
94
+ rdoc.options += RDOC_OPTS
95
+ rdoc.template = "resh"
96
+ #rdoc.template = "#{ENV['template']}.rb" if ENV['template']
97
+ if ENV['DOC_FILES']
98
+ rdoc.rdoc_files.include(ENV['DOC_FILES'].split(/,\s*/))
99
+ else
100
+ rdoc.rdoc_files.include('README', 'ChangeLog')
101
+ rdoc.rdoc_files.include('lib/**/*.rb')
102
+ rdoc.rdoc_files.include('ext/**/*.c')
103
+ end
104
+ end
105
+
106
+ desc "Publish to RubyForge"
107
+ task :rubyforge => [:rdoc, :package] do
108
+ require 'rubyforge'
109
+ @local_dir = "html"
110
+ @host = "cho45@rubyforge.org"
111
+ @remote_dir = "/var/www/gforge-projects/#{RUBYFORGE_PROJECT}/#{NAME}"
112
+ sh %{rsync -r --delete --verbose #{@local_dir}/ #{@host}:#{@remote_dir}}
113
+ end
114
+
115
+ Rake::ShipitTask.new do |s|
116
+ s.Step.new {
117
+ system("svn", "up")
118
+ }.and {}
119
+ s.Step.new {
120
+ raise "changelog-with-hatenastar.rb is not found" unless system("which", "changelog-with-hatenastar.rb")
121
+ }.and {
122
+ system("changelog-with-hatenastar.rb > ChangeLog")
123
+ }
124
+ s.ChangeVersion "lib/net/irc.rb", "VERSION"
125
+ s.Commit
126
+ s.Task :clean, :package
127
+ s.RubyForge
128
+ s.Tag
129
+ s.Twitter
130
+ s.Task :rubyforge
131
+ end
data/examples/lig.rb ADDED
@@ -0,0 +1,276 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH << "lib"
4
+ $LOAD_PATH << "../lib"
5
+
6
+ require "rubygems"
7
+
8
+ # http://svn.lingr.com/api/toolkits/ruby/infoteria/api_client.rb
9
+ begin
10
+ require "api_client"
11
+ rescue LoadError
12
+ require "net/http"
13
+ require "uri"
14
+ File.open("api_client.rb", "w") do |f|
15
+ f.puts Net::HTTP.get(URI("http://svn.lingr.com/api/toolkits/ruby/infoteria/api_client.rb"))
16
+ end
17
+ require "api_client"
18
+ end
19
+
20
+ require "net/irc"
21
+ require "pit"
22
+
23
+
24
+ class LingrIrcGateway < Net::IRC::Server::Session
25
+ def server_name
26
+ "lingrgw"
27
+ end
28
+
29
+ def server_version
30
+ "0.0.0"
31
+ end
32
+
33
+ def initialize(*args)
34
+ super
35
+ @channels = {}
36
+ end
37
+
38
+ def on_user(m)
39
+ super
40
+ @real, @copts = @real.split(/\s/)
41
+ @copts ||= []
42
+
43
+ log "Hello #{@nick}, this is Lingr IRC Gateway."
44
+ log "Client Option: #{@copts.join(", ")}"
45
+ @log.info "Client Option: #{@copts.join(", ")}"
46
+ @log.info "Client initialization is completed."
47
+
48
+ @lingr = Lingr::ApiClient.new(@opts.api_key)
49
+ @lingr.create_session('human')
50
+ @lingr.login(@real, @pass)
51
+ @user_info = @lingr.get_user_info[:response]
52
+ end
53
+
54
+ def on_privmsg(m)
55
+ target, message = *m.params
56
+ @lingr.say(@channels[target.downcase][:ticket], message)
57
+ end
58
+
59
+ def on_whois(m)
60
+ nick = m.params[0]
61
+ # TODO
62
+ end
63
+
64
+ def on_who(m)
65
+ channel = m.params[0]
66
+ info = @channels[channel.downcase]
67
+ res = @lingr.get_room_info(info[:chan_id], nil, info[:password])
68
+ if res[:succeeded]
69
+ res = res[:response]
70
+ res["occupants"].each do |o|
71
+ u_id, o_id, nick = *make_ids(o)
72
+ post nil, RPL_WHOREPLY, channel, o_id, "lingr.com", "lingr.com", nick, "H", "0 #{o["description"].to_s.gsub(/\s+/, " ")}"
73
+ end
74
+ post nil, RPL_ENDOFWHO, channel
75
+ else
76
+ log "Maybe gateway don't know password for channel #{channel}. Please part and join."
77
+ end
78
+ end
79
+
80
+ def on_join(m)
81
+ channels = m.params[0].split(/\s*,\s*/)
82
+ password = m.params[1]
83
+ channels.each do |channel|
84
+ next if @channels.key? channel.downcase
85
+ @log.debug "Enter room -> #{channel}"
86
+ res = @lingr.enter_room(channel.sub(/^#/, ""), @nick, password)
87
+ if res[:succeeded]
88
+ res[:response]["password"] = password
89
+ o_id = res[:response]["occupant_id"]
90
+ post "#{@nick}!#{o_id}@lingr.com", JOIN, channel
91
+ create_observer(channel, res[:response])
92
+ else
93
+ log "Error: #{(res && rese['error']) ? res[:response]["error"]["message"] : "socket error"}"
94
+ end
95
+ end
96
+ end
97
+
98
+ def on_part(m)
99
+ channel = m.params[0]
100
+ info = @channels[channel].downcase
101
+
102
+ if info
103
+ info[:observer].kill
104
+ @lingr.exit_room(info[:ticket])
105
+ @channels.delete(channel.downcase)
106
+ post @nick, PART, channel, "Parted"
107
+ end
108
+ end
109
+
110
+ private
111
+
112
+ def create_observer(channel, response)
113
+ Thread.start(channel, response) do |chan, res|
114
+ begin
115
+ post server_name, TOPIC, chan, "#{res["room"]["url"]} #{res["room"]["description"]}"
116
+ @channels[chan.downcase] = {
117
+ :ticket => res["ticket"],
118
+ :counter => res["counter"],
119
+ :o_id => res["occupant_id"],
120
+ :chan_id => res["room"]["id"],
121
+ :password => res["password"],
122
+ :hcounter => 0,
123
+ :observer => Thread.current,
124
+ }
125
+ first = true
126
+ while true
127
+ info = @channels[chan.downcase]
128
+ res = @lingr.observe_room info[:ticket], info[:counter]
129
+ @log.debug "observe_room returned"
130
+ if res[:succeeded]
131
+ info[:counter] = res[:response]["counter"] if res[:response]["counter"]
132
+ (res[:response]["messages"] || []).each do |m|
133
+ next if m["id"].to_i <= info[:hcounter]
134
+
135
+ u_id, o_id, nick = *make_ids(m)
136
+
137
+ case m["type"]
138
+ when "user"
139
+ if first
140
+ post nick, NOTICE, chan, m["text"]
141
+ else
142
+ post nick, PRIVMSG, chan, m["text"] unless info[:o_id] == o_id
143
+ end
144
+ when "private"
145
+ # TODO
146
+ post nick, PRIVMSG, chan, "\x01ACTION Sent private: #{m["text"]}\x01" unless info[:o_id] == o_id
147
+ when "system:enter"
148
+ post "#{nick}!#{o_id}@lingr.com", JOIN, chan unless nick == @nick
149
+ when "system:leave"
150
+ #post "#{nick}!#{o_id}@lingr.com", PART, chan unless nick == @nick
151
+ when "system:nickname_change"
152
+ post nick, NOTICE, chan, m["text"]
153
+ when "system:broadcast"
154
+ post nil, NOTICE, chan, m["text"]
155
+ end
156
+
157
+ info[:hcounter] = m["id"].to_i if m["id"]
158
+ end
159
+
160
+ if res["occupants"]
161
+ res["occupants"].each do |o|
162
+ # new_roster[o["id"]] = o["nickname"]
163
+ if o["nickname"]
164
+ nick = o["nickname"]
165
+ o_id = m["occupant_id"]
166
+ post "#{nick}!#{o_id}@lingr.com", JOIN, chan
167
+ end
168
+ end
169
+ end
170
+ else
171
+ @log.debug "observe failed : #{res[:response].inspect}"
172
+ log "Error: #{(res && res['error']) ? res[:response]["error"]["message"] : "socket error"}"
173
+ end
174
+ first = false
175
+ end
176
+ rescue Exception => e
177
+ puts e
178
+ puts e.backtrace
179
+ end
180
+ end
181
+ end
182
+
183
+ def log(str)
184
+ str.gsub!(/\s/, " ")
185
+ post nil, NOTICE, @nick, str
186
+ end
187
+
188
+ def make_ids(o)
189
+ u_id = o["user_id"]
190
+ o_id = o["occupant_id"] || o["id"]
191
+ nick = o["nickname"].gsub(/\s+/, "") + "^#{u_id || "anon"}"
192
+ [u_id, o_id, nick]
193
+ end
194
+ end
195
+
196
+
197
+ if __FILE__ == $0
198
+ require "rubygems"
199
+ require "optparse"
200
+ require "pit"
201
+
202
+ opts = {
203
+ :port => 16669,
204
+ :host => "localhost",
205
+ :debug => false,
206
+ :log => nil,
207
+ :debug => false,
208
+ }
209
+
210
+ OptionParser.new do |parser|
211
+ parser.instance_eval do
212
+ self.banner = <<-EOB.gsub(/^\t+/, "")
213
+ Usage: #{$0} [opts]
214
+
215
+ EOB
216
+
217
+ separator ""
218
+
219
+ separator "Options:"
220
+ on("-p", "--port [PORT=#{opts[:port]}]", "listen port number") do |port|
221
+ opts[:port] = port
222
+ end
223
+
224
+ on("-h", "--host [HOST=#{opts[:host]}]", "listen host") do |host|
225
+ opts[:host] = host
226
+ end
227
+
228
+ on("-l", "--log LOG", "log file") do |log|
229
+ opts[:log] = log
230
+ end
231
+
232
+ on("-a", "--api_key API_KEY", "Your api key on Lingr") do |key|
233
+ opts[:api_key] = key
234
+ end
235
+
236
+ on("--debug", "Enable debug mode") do |debug|
237
+ opts[:log] = $stdout
238
+ opts[:debug] = true
239
+ end
240
+
241
+ parse!(ARGV)
242
+ end
243
+ end
244
+
245
+ opts[:logger] = Logger.new(opts[:log], "daily")
246
+ opts[:logger].level = opts[:debug] ? Logger::DEBUG : Logger::INFO
247
+
248
+ def daemonize(debug=false)
249
+ return yield if $DEBUG || debug
250
+ Process.fork do
251
+ Process.setsid
252
+ Dir.chdir "/"
253
+ trap("SIGINT") { exit! 0 }
254
+ trap("SIGTERM") { exit! 0 }
255
+ trap("SIGHUP") { exit! 0 }
256
+ File.open("/dev/null") {|f|
257
+ STDIN.reopen f
258
+ STDOUT.reopen f
259
+ STDERR.reopen f
260
+ }
261
+ yield
262
+ end
263
+ exit! 0
264
+ end
265
+
266
+ opts[:api_key] = Pit.get("lig.rb", :require => {
267
+ "api_key" => "API key of lingr"
268
+ })["api_key"] unless opts[:api_key]
269
+
270
+ daemonize(opts[:debug]) do
271
+ Net::IRC::Server.new(opts[:host], opts[:port], LingrIrcGateway, opts).start
272
+ end
273
+
274
+ end
275
+
276
+