irc_socket 1.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.
- checksums.yaml +7 -0
- data/LICENSE +20 -0
- data/README.rdoc +35 -0
- data/lib/irc_socket.rb +300 -0
- data/spec/irc_socket_spec.rb +92 -0
- metadata +61 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: e4c693a95c5ee11f00d4801bbb01747635a2fb134858dd9312a96224e1e31180
|
4
|
+
data.tar.gz: f512fa98cb41980cdb6006f243f701f961c6c3f025addb4f50014b65b3987cd1
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 49476fa472445c39e051f50528e211af8103669a6545bfb5f4500d96c347c5a5b066801bf89fc158459816f2dcb08de249e92bb5df542d221492bcefe5251370
|
7
|
+
data.tar.gz: 8c7cfca56dbb7b0570f893307fae06c7f200f64d4e0a318fe1ba36fd3165aa144fba9620b82dbf61a51398ce245bf561d8ae2ff26253fa5f9889933d1b5dfa01
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 Lee Jarvis
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
== Description
|
2
|
+
|
3
|
+
IRCSocket is an IRC wrapper around a TCPSocket. It implements all of the major
|
4
|
+
commands laid out in {RFC 2812}[http://irchelp.org/irchelp/rfc/rfc2812.txt].
|
5
|
+
All these commands are available as instance methods of an IRCSocket Object.
|
6
|
+
|
7
|
+
API documentation is available {here}[http://rdoc.injekt.net/irc-socket]
|
8
|
+
|
9
|
+
== Example
|
10
|
+
irc = IRCSocket.new('irc.freenode.org')
|
11
|
+
irc.connect
|
12
|
+
|
13
|
+
if irc.connected?
|
14
|
+
irc.nick "HulkHogan"
|
15
|
+
irc.user "Hulk", 0, "*", "I am Hulk Hogan"
|
16
|
+
|
17
|
+
while line = irc.read
|
18
|
+
|
19
|
+
# Join a channel after MOTD
|
20
|
+
if line.split[1] == '376'
|
21
|
+
irc.join "#mychannel"
|
22
|
+
end
|
23
|
+
|
24
|
+
puts "Received: #{line}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
== Installation
|
29
|
+
gem install irc-socket
|
30
|
+
|
31
|
+
Or alternatively you can clone the Github repository
|
32
|
+
git clone https://github.com/injekt/irc-socket
|
33
|
+
|
34
|
+
== Notes
|
35
|
+
I may have missed something in the RFC. Patches welcome.
|
data/lib/irc_socket.rb
ADDED
@@ -0,0 +1,300 @@
|
|
1
|
+
require 'socket'
|
2
|
+
|
3
|
+
# == Author
|
4
|
+
# * Lee Jarvis - ljjarvis@gmail.com
|
5
|
+
#
|
6
|
+
# == Description
|
7
|
+
# IRCSocket is an IRC wrapper around a TCPSocket. It implements all of the major
|
8
|
+
# commands laid out in {RFC 2812}[http://irchelp.org/irchelp/rfc/rfc2812.txt].
|
9
|
+
# All these commands are available as instance methods of an IRCSocket Object.
|
10
|
+
#
|
11
|
+
# == Example
|
12
|
+
# irc = IRCSocket.new('irc.freenode.org')
|
13
|
+
# irc.connect
|
14
|
+
#
|
15
|
+
# if irc.connected?
|
16
|
+
# irc.nick "HulkHogan"
|
17
|
+
# irc.user "Hulk", 0, "*", "I am Hulk Hogan"
|
18
|
+
#
|
19
|
+
# while line = irc.read
|
20
|
+
#
|
21
|
+
# # Join a channel after MOTD
|
22
|
+
# if line.split[1] == '376'
|
23
|
+
# irc.join "#mychannel"
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# puts "Received: #{line}"
|
27
|
+
# end
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# === Block Form
|
31
|
+
# IRCSocket.new('irc.freenode.org') do |irc|
|
32
|
+
# irc.nick "SpongeBob"
|
33
|
+
# irc.user "Spongey", 0, "*", "Square Pants"
|
34
|
+
#
|
35
|
+
# puts irc.read
|
36
|
+
# end
|
37
|
+
class IRCSocket
|
38
|
+
|
39
|
+
# The server our socket is connected to
|
40
|
+
attr_reader :server
|
41
|
+
|
42
|
+
# The port our socket is connected on
|
43
|
+
attr_reader :port
|
44
|
+
|
45
|
+
# The TCPSocket instance
|
46
|
+
attr_reader :socket
|
47
|
+
|
48
|
+
# Creates a new IRCSocket and automatically connects
|
49
|
+
#
|
50
|
+
# === Example
|
51
|
+
# irc = IRCSocket.open('irc.freenode.org')
|
52
|
+
#
|
53
|
+
# while data = irc.read
|
54
|
+
# puts data
|
55
|
+
# end
|
56
|
+
def self.open(server, port=6667, ssl=false)
|
57
|
+
irc = new(server, port, ssl)
|
58
|
+
irc.connect
|
59
|
+
irc
|
60
|
+
end
|
61
|
+
|
62
|
+
# Create a new IRCSocket to connect to +server+ on +port+. Defaults to port 6667.
|
63
|
+
# If an optional code block is given, it will be passed an instance of the IRCSocket.
|
64
|
+
#
|
65
|
+
# NOTE: Using the block form does not mean the socket will send the applicable QUIT
|
66
|
+
# command to leave the IRC server. You must send this yourself.
|
67
|
+
def initialize(server, port=6667, ssl=false)
|
68
|
+
@server = server
|
69
|
+
@port = port
|
70
|
+
@ssl = ssl
|
71
|
+
|
72
|
+
@socket = nil
|
73
|
+
@connected = false
|
74
|
+
|
75
|
+
if block_given?
|
76
|
+
connect
|
77
|
+
yield self
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# Check if our socket is alive and connected to an IRC server
|
82
|
+
def connected?
|
83
|
+
@connected
|
84
|
+
end
|
85
|
+
alias connected connected?
|
86
|
+
|
87
|
+
# Connect to an IRC server, returns true on a successful connection, or
|
88
|
+
# raises otherwise
|
89
|
+
def connect
|
90
|
+
socket = TCPSocket.new(server, port)
|
91
|
+
|
92
|
+
if @ssl
|
93
|
+
require 'openssl'
|
94
|
+
|
95
|
+
ssl = OpenSSL::SSL::SSLContext.new
|
96
|
+
ssl.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
97
|
+
@socket = OpenSSL::SSL::SSLSocket.new(socket, ssl)
|
98
|
+
@socket.sync = true
|
99
|
+
@socket.connect
|
100
|
+
else
|
101
|
+
@socket = socket
|
102
|
+
end
|
103
|
+
rescue Interrupt
|
104
|
+
raise
|
105
|
+
rescue Exception
|
106
|
+
raise
|
107
|
+
else
|
108
|
+
@connected = true
|
109
|
+
end
|
110
|
+
|
111
|
+
# Read the next line in from the server. If no arguments are passed
|
112
|
+
# the line will have the CRLF chomp'ed. Returns nil if no data could be read
|
113
|
+
def read(chompstr="\r\n")
|
114
|
+
if data = @socket.gets("\r\n")
|
115
|
+
data.chomp!(chompstr) if chompstr
|
116
|
+
data
|
117
|
+
else
|
118
|
+
nil
|
119
|
+
end
|
120
|
+
rescue IOError
|
121
|
+
nil
|
122
|
+
end
|
123
|
+
|
124
|
+
# Write to our Socket and append CRLF
|
125
|
+
def write(data)
|
126
|
+
@socket.write(data + "\r\n")
|
127
|
+
rescue IOError
|
128
|
+
raise
|
129
|
+
end
|
130
|
+
|
131
|
+
# Sugar for #write
|
132
|
+
def raw(*args) # :nodoc:
|
133
|
+
args.last.insert(0, ':') unless args.last.nil?
|
134
|
+
write args.join(' ').strip
|
135
|
+
end
|
136
|
+
|
137
|
+
# More sugar
|
138
|
+
def write_optional(command, *optional) # :nodoc:
|
139
|
+
command = "#{command} #{optional.join(' ')}" if optional
|
140
|
+
write(command.strip)
|
141
|
+
end
|
142
|
+
private :raw, :write_optional
|
143
|
+
|
144
|
+
# Send PASS command
|
145
|
+
def pass(password)
|
146
|
+
write("PASS #{password}")
|
147
|
+
end
|
148
|
+
|
149
|
+
# Send NICK command
|
150
|
+
def nick(nickname)
|
151
|
+
write("NICK #{nickname}")
|
152
|
+
end
|
153
|
+
|
154
|
+
# Send USER command
|
155
|
+
def user(user, mode, unused, realname)
|
156
|
+
write("USER #{user} #{mode} #{unused} :#{realname}")
|
157
|
+
end
|
158
|
+
|
159
|
+
# Send OPER command
|
160
|
+
def oper(name, password)
|
161
|
+
write("OPER #{name} #{password}")
|
162
|
+
end
|
163
|
+
|
164
|
+
# Send the MODE command.
|
165
|
+
# Should probably implement a better way of doing this
|
166
|
+
def mode(channel, *modes)
|
167
|
+
write("MODE #{channel} #{modes.join(' ')}")
|
168
|
+
end
|
169
|
+
|
170
|
+
# Send QUIT command
|
171
|
+
def quit(message=nil)
|
172
|
+
raw("QUIT", message)
|
173
|
+
end
|
174
|
+
|
175
|
+
# Send JOIN command - Join a channel with given password
|
176
|
+
def join(channel, password=nil)
|
177
|
+
write_optional("JOIN #{channel}", password)
|
178
|
+
end
|
179
|
+
|
180
|
+
# Send PART command
|
181
|
+
def part(channel, message=nil)
|
182
|
+
raw("PART", channel, message)
|
183
|
+
end
|
184
|
+
|
185
|
+
# Send TOPIC command
|
186
|
+
def topic(channel, topic=nil)
|
187
|
+
raw("TOPIC", channel, topic)
|
188
|
+
end
|
189
|
+
|
190
|
+
# Send NAMES command
|
191
|
+
def names(*channels)
|
192
|
+
write("NAMES #{channels.join(',') unless channels.empty?}")
|
193
|
+
end
|
194
|
+
|
195
|
+
# Send LIST command
|
196
|
+
def list(*channels)
|
197
|
+
write("LIST #{channels.join(',') unless channels.empty?}")
|
198
|
+
end
|
199
|
+
|
200
|
+
# Send INVITE command
|
201
|
+
def invite(nickname, channel)
|
202
|
+
write("INVITE #{nickname} #{channel}")
|
203
|
+
end
|
204
|
+
|
205
|
+
# Send KICK command
|
206
|
+
def kick(channel, user, comment=nil)
|
207
|
+
raw("KICK", channel, user, comment)
|
208
|
+
end
|
209
|
+
|
210
|
+
# Send PRIVMSG command
|
211
|
+
def privmsg(target, message)
|
212
|
+
write("PRIVMSG #{target} :#{message}")
|
213
|
+
end
|
214
|
+
|
215
|
+
# Send NOTICE command
|
216
|
+
def notice(target, message)
|
217
|
+
write("NOTICE #{target} :#{message}")
|
218
|
+
end
|
219
|
+
|
220
|
+
# Send MOTD command
|
221
|
+
def motd(target=nil)
|
222
|
+
write_optional("MOTD", target)
|
223
|
+
end
|
224
|
+
|
225
|
+
# Send VERSION command
|
226
|
+
def version(target=nil)
|
227
|
+
write_optional("VERSION", target)
|
228
|
+
end
|
229
|
+
|
230
|
+
# Send STATS command
|
231
|
+
def stats(*params)
|
232
|
+
write_optional("STATS", params)
|
233
|
+
end
|
234
|
+
|
235
|
+
# Send TIME command
|
236
|
+
def time(target=nil)
|
237
|
+
write_optional("TIME", target)
|
238
|
+
end
|
239
|
+
|
240
|
+
# Send INFO command
|
241
|
+
def info(target=nil)
|
242
|
+
write_optional("INFO", target)
|
243
|
+
end
|
244
|
+
|
245
|
+
# Send SQUERY command
|
246
|
+
def squery(target, message)
|
247
|
+
write("SQUERY #{target} :#{message}")
|
248
|
+
end
|
249
|
+
|
250
|
+
# Send WHO command
|
251
|
+
def who(*params)
|
252
|
+
write_optional("WHO", params)
|
253
|
+
end
|
254
|
+
|
255
|
+
# Send WHOIS command
|
256
|
+
def whois(*params)
|
257
|
+
write_optional("WHOIS", params)
|
258
|
+
end
|
259
|
+
|
260
|
+
# Send WHOWAS command
|
261
|
+
def whowas(*params)
|
262
|
+
write_optional("WHOWAS", params)
|
263
|
+
end
|
264
|
+
|
265
|
+
# Send KILL command
|
266
|
+
def kill(user, message)
|
267
|
+
write("KILL #{user} :#{message}")
|
268
|
+
end
|
269
|
+
|
270
|
+
# Send PING command
|
271
|
+
def ping(server)
|
272
|
+
write("PING #{server}")
|
273
|
+
end
|
274
|
+
|
275
|
+
# Send PONG command
|
276
|
+
def pong(server)
|
277
|
+
write("PONG #{server}")
|
278
|
+
end
|
279
|
+
|
280
|
+
# Send AWAY command
|
281
|
+
def away(message=nil)
|
282
|
+
raw("AWAY", message)
|
283
|
+
end
|
284
|
+
|
285
|
+
# Send USERS command
|
286
|
+
def users(target=nil)
|
287
|
+
write_optional("USERS", target)
|
288
|
+
end
|
289
|
+
|
290
|
+
# Send USERHOST command
|
291
|
+
def userhost(*users)
|
292
|
+
write("USERHOST #{users.join(' ')}")
|
293
|
+
end
|
294
|
+
|
295
|
+
# Close our socket instance
|
296
|
+
def close
|
297
|
+
@socket.close if connected?
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require File.expand_path('../../lib/irc-socket', __FILE__)
|
2
|
+
|
3
|
+
class IRCSocket
|
4
|
+
def write(data)
|
5
|
+
return data
|
6
|
+
end
|
7
|
+
|
8
|
+
def read(chompstr="\r\n")
|
9
|
+
str = "foo bar baz\r\n"
|
10
|
+
str.chomp!(chompstr) if chompstr
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
commands = [
|
15
|
+
[:pass, %w(foobar), "PASS foobar"],
|
16
|
+
[:nick, %(ipsum), "NICK ipsum"],
|
17
|
+
[:user, ["guest", 0, '*', "real name"], "USER guest 0 * :real name"],
|
18
|
+
[:oper, %w(foo bar), "OPER foo bar"],
|
19
|
+
[:mode, %w(#foo +v bar), "MODE #foo +v bar"],
|
20
|
+
[:quit, %w(goodbye), "QUIT :goodbye"],
|
21
|
+
[:join, %w(#mychan), "JOIN #mychan"],
|
22
|
+
[:part, %w(#mychan), "PART #mychan"],
|
23
|
+
[:part, %w(#mychan cya!), "PART #mychan :cya!", "with part message"],
|
24
|
+
[:topic, %w(#mychan newtopic), "TOPIC #mychan :newtopic"],
|
25
|
+
[:names, %w(#foo #bar), "NAMES #foo,#bar"],
|
26
|
+
[:list, %w(#foo #bar), "LIST #foo,#bar"],
|
27
|
+
[:invite, %w(foo #mychan), "INVITE foo #mychan"],
|
28
|
+
[:kick, %w(#chan villian), "KICK #chan villian"],
|
29
|
+
[:kick, %w(#chan villian gtfo!), "KICK #chan villian :gtfo!", "with kick reason"],
|
30
|
+
[:privmsg, ['#chan', 'foo bar baz'], "PRIVMSG #chan :foo bar baz"],
|
31
|
+
[:notice, ['#chan', 'foo bar baz'], "NOTICE #chan :foo bar baz"],
|
32
|
+
[:motd, %w(someserver), "MOTD someserver"],
|
33
|
+
[:version, %w(anotherserver), "VERSION anotherserver"],
|
34
|
+
[:stats, %w(m server), "STATS m server"],
|
35
|
+
[:time, %w(irc.someserver.net), "TIME irc.someserver.net"],
|
36
|
+
[:info, %w(foobar), "INFO foobar"],
|
37
|
+
[:squery, %w(irchelp HELPME), "SQUERY irchelp :HELPME"],
|
38
|
+
[:who, %w(*.com o), "WHO *.com o"],
|
39
|
+
[:whois, %w(foo.org user), "WHOIS foo.org user"],
|
40
|
+
[:whowas, %w(foo.org user), "WHOWAS foo.org user"],
|
41
|
+
[:kill, ['badperson', 'get out!'], "KILL badperson :get out!"],
|
42
|
+
[:ping, %w(010123444), "PING 010123444"],
|
43
|
+
[:pong, %w(irc.foobar.org), "PONG irc.foobar.org"],
|
44
|
+
[:away, [], "AWAY"],
|
45
|
+
[:away, ['gone for lunch'], "AWAY :gone for lunch"],
|
46
|
+
[:users, %w(irc.foobar.org), "USERS irc.foobar.org"],
|
47
|
+
[:userhost, %w(foo bar baz), "USERHOST foo bar baz"],
|
48
|
+
]
|
49
|
+
|
50
|
+
describe "IRCSocket" do
|
51
|
+
before do
|
52
|
+
@irc = IRCSocket.new('irc.myserver.net')
|
53
|
+
end
|
54
|
+
|
55
|
+
describe "::new" do
|
56
|
+
it "should return an IRCSocket" do
|
57
|
+
@irc.class.should == IRCSocket
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should default to port 6667" do
|
61
|
+
@irc.port.should == 6667
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should not automatically connect" do
|
65
|
+
@irc.connected.should == false
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should set socket instance as nil" do
|
69
|
+
@irc.socket.should == nil
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe "#read" do
|
74
|
+
it "should chomp CRLF by default" do
|
75
|
+
@irc.read.should == "foo bar baz"
|
76
|
+
@irc.read.should_not == "foo bar baz\r\n"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe "IRC commands as per rfc2812" do
|
81
|
+
commands.each do |requirements|
|
82
|
+
meth, params, pass, extra = *requirements
|
83
|
+
describe "##{meth}" do
|
84
|
+
it "should send a #{meth.to_s.upcase} command #{extra if extra}" do
|
85
|
+
@irc.send(meth, *params).should == pass
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
metadata
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: irc_socket
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Lee Jarvis
|
8
|
+
- Ken Spencer
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2018-11-05 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - '='
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: 2.1.0
|
21
|
+
type: :development
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - '='
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: 2.1.0
|
28
|
+
description: An IRC wrapper around TCPSocket
|
29
|
+
email: me+irc_socket@iotaspencer.me
|
30
|
+
executables: []
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- LICENSE
|
35
|
+
- README.rdoc
|
36
|
+
- lib/irc_socket.rb
|
37
|
+
- spec/irc_socket_spec.rb
|
38
|
+
homepage: http://rubydoc.info/github/IotaSpencer/irc_socket
|
39
|
+
licenses: []
|
40
|
+
metadata: {}
|
41
|
+
post_install_message:
|
42
|
+
rdoc_options: []
|
43
|
+
require_paths:
|
44
|
+
- lib
|
45
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - ">="
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: 1.8.6
|
50
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
requirements: []
|
56
|
+
rubyforge_project:
|
57
|
+
rubygems_version: 2.7.6
|
58
|
+
signing_key:
|
59
|
+
specification_version: 4
|
60
|
+
summary: An IRC wrapper around TCPSocket
|
61
|
+
test_files: []
|