zircon 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +27 -0
- data/Rakefile +2 -0
- data/lib/zircon.rb +114 -0
- data/lib/zircon/callback.rb +37 -0
- data/lib/zircon/message.rb +48 -0
- data/lib/zircon/message/patterns.rb +47 -0
- data/lib/zircon/version.rb +3 -0
- data/spec/spec_helper.rb +2 -0
- data/spec/zircon_spec.rb +31 -0
- data/zircon.gemspec +19 -0
- metadata +72 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Ryo NAKAMURA
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# Zircon
|
2
|
+
IRC client library written in Pure Ruby.
|
3
|
+
|
4
|
+
## Installation
|
5
|
+
|
6
|
+
```
|
7
|
+
$ gem install zircon
|
8
|
+
```
|
9
|
+
|
10
|
+
## Example
|
11
|
+
|
12
|
+
```ruby
|
13
|
+
require "zircon"
|
14
|
+
|
15
|
+
client = Zircon.new(
|
16
|
+
:server => "chat.freenode.net",
|
17
|
+
:port => "6667",
|
18
|
+
:channel => "#chatroid",
|
19
|
+
:username => "zircon"
|
20
|
+
)
|
21
|
+
|
22
|
+
client.on_privmsg do |message|
|
23
|
+
client.privmsg "#chatroid", ":zircon!"
|
24
|
+
end
|
25
|
+
|
26
|
+
client.run!
|
27
|
+
```
|
data/Rakefile
ADDED
data/lib/zircon.rb
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
require "zircon/version"
|
2
|
+
require "zircon/callback"
|
3
|
+
require "zircon/message"
|
4
|
+
require "socket"
|
5
|
+
|
6
|
+
class Zircon
|
7
|
+
include Callback
|
8
|
+
|
9
|
+
attr_accessor(
|
10
|
+
:server,
|
11
|
+
:port,
|
12
|
+
:channel,
|
13
|
+
:username,
|
14
|
+
:nickname,
|
15
|
+
:realname
|
16
|
+
)
|
17
|
+
|
18
|
+
COMMAND_NAMES = %w[
|
19
|
+
ADMIN
|
20
|
+
AWAY
|
21
|
+
CREDITS
|
22
|
+
CYCLE
|
23
|
+
DALINFO
|
24
|
+
INVITE
|
25
|
+
ISON
|
26
|
+
JOIN
|
27
|
+
KICK
|
28
|
+
KNOCK
|
29
|
+
LICENSE
|
30
|
+
LINKS
|
31
|
+
LIST
|
32
|
+
LUSERS
|
33
|
+
MAP
|
34
|
+
MODE
|
35
|
+
MOTD
|
36
|
+
NAMES
|
37
|
+
NICK
|
38
|
+
NOTICE
|
39
|
+
PART
|
40
|
+
PASS
|
41
|
+
PING
|
42
|
+
PONG
|
43
|
+
PRIVMSG
|
44
|
+
QUIT
|
45
|
+
RULES
|
46
|
+
SETNAME
|
47
|
+
SILENCE
|
48
|
+
STATS
|
49
|
+
TIME
|
50
|
+
TOPIC
|
51
|
+
USER
|
52
|
+
USERHOST
|
53
|
+
VERSION
|
54
|
+
VHOST
|
55
|
+
WATCH
|
56
|
+
WHO
|
57
|
+
WHOIS
|
58
|
+
WHOWAS
|
59
|
+
].freeze
|
60
|
+
|
61
|
+
def initialize(args = {})
|
62
|
+
@server = args[:server]
|
63
|
+
@port = args[:port]
|
64
|
+
@channel = args[:channel]
|
65
|
+
@password = args[:password]
|
66
|
+
@username = args[:username]
|
67
|
+
@nickname = args[:nickname] || @username
|
68
|
+
@realname = args[:realname] || @username
|
69
|
+
on_ping { |message| pong(message.text) }
|
70
|
+
end
|
71
|
+
|
72
|
+
def run!
|
73
|
+
login
|
74
|
+
wait_message
|
75
|
+
end
|
76
|
+
|
77
|
+
COMMAND_NAMES.each do |name|
|
78
|
+
define_method(name.downcase) do |*params|
|
79
|
+
command(name, *params)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def login
|
86
|
+
pass @password if @password
|
87
|
+
nick @nickname
|
88
|
+
user @username, 0, "*", ":" + @realname
|
89
|
+
join @channel if @channel
|
90
|
+
end
|
91
|
+
|
92
|
+
def wait_message
|
93
|
+
loop do
|
94
|
+
message = Message.new(gets)
|
95
|
+
send("trigger_#{message.type}", message)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def command(*args)
|
100
|
+
write(args * " " + "\r\n")
|
101
|
+
end
|
102
|
+
|
103
|
+
def write(text)
|
104
|
+
socket << text
|
105
|
+
end
|
106
|
+
|
107
|
+
def gets
|
108
|
+
socket.gets
|
109
|
+
end
|
110
|
+
|
111
|
+
def socket
|
112
|
+
@socket ||= TCPSocket.new(@server, @port)
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
class Zircon
|
2
|
+
# You can call methods like this:
|
3
|
+
# * list_message : return Array of callbacks for "message"
|
4
|
+
# * on_message : store a given block as callback for "message"
|
5
|
+
# * trigger_message : trigger callbacks for "message" with given args
|
6
|
+
module Callback
|
7
|
+
private
|
8
|
+
|
9
|
+
def method_missing(method_name, *args, &block)
|
10
|
+
if method_name =~ /(list|on|trigger)_([a-z0-9]+)/
|
11
|
+
send($1, $2, *args, &block)
|
12
|
+
else
|
13
|
+
super
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def list(type)
|
18
|
+
callbacks[type]
|
19
|
+
end
|
20
|
+
|
21
|
+
def on(type, &block)
|
22
|
+
callbacks[type] << block
|
23
|
+
end
|
24
|
+
|
25
|
+
def trigger(type, *args)
|
26
|
+
callbacks[type].each do |callback|
|
27
|
+
callback.call(*args)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def callbacks
|
32
|
+
@callbacks ||= Hash.new do |hash, key|
|
33
|
+
hash[key] = []
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require "zircon/message/patterns"
|
2
|
+
|
3
|
+
class Zircon
|
4
|
+
class Message
|
5
|
+
attr_reader :prefix, :command, :text
|
6
|
+
|
7
|
+
def initialize(text)
|
8
|
+
if match = Patterns::MESSAGE_PATTERN.match(text)
|
9
|
+
@text = text
|
10
|
+
@prefix = match[1]
|
11
|
+
@command = match[2]
|
12
|
+
@rest = match[3..-1]
|
13
|
+
else
|
14
|
+
raise ArgumentError.new("Invalid message")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def type
|
19
|
+
@type ||= @command.to_s.downcase
|
20
|
+
end
|
21
|
+
|
22
|
+
def name
|
23
|
+
@name ||= begin
|
24
|
+
@prefix && @prefix.split("!").first
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def params
|
29
|
+
@params ||= begin
|
30
|
+
params = []
|
31
|
+
case
|
32
|
+
when !@rest[0].blank?
|
33
|
+
middle, trailer, = *@rest
|
34
|
+
params = middle.split(" ")
|
35
|
+
when !@rest[2].blank?
|
36
|
+
middle, trailer, = *@rest[2, 2]
|
37
|
+
params = middle.split(" ")
|
38
|
+
when @rest[1]
|
39
|
+
trailer = @rest[1]
|
40
|
+
when @rest[3]
|
41
|
+
trailer = @rest[3]
|
42
|
+
end
|
43
|
+
params << trailer if trailer
|
44
|
+
params
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# coding: ASCII-8BIT
|
2
|
+
|
3
|
+
class Zircon
|
4
|
+
class Message
|
5
|
+
module Patterns
|
6
|
+
# IRC Message Pseudo BNF code
|
7
|
+
# http://www.haun.org/kent/lib/rfc1459-irc-ja.html
|
8
|
+
# ------------------------------------------------
|
9
|
+
# <message> ::= [':' <prefix> <SPACE> ] <command> <params> <crlf>
|
10
|
+
# <prefix> ::= <servername> | <nick> [ '!' <user> ] [ '@' <host> ]
|
11
|
+
# <command> ::= <letter> { <letter> } | <number> <number> <number>
|
12
|
+
# <SPACE> ::= ' ' { ' ' }
|
13
|
+
# <params> ::= <SPACE> [ ':' <trailing> | <middle> <params> ]
|
14
|
+
# <middle> ::= <先頭が':'ではなく,SPACE,NUL,CR,CFを含まない、空でないオクテットの列>
|
15
|
+
# <trailing> ::= <SPACE,NUL,CR,CFを含まないオクテットの列(空のオクッテトの列も可)>
|
16
|
+
# <crlf> ::= CR LF
|
17
|
+
|
18
|
+
# unit
|
19
|
+
DIGIT = "0-9"
|
20
|
+
LETTER = "A-Za-z"
|
21
|
+
HEXDIGIT = "#{DIGIT}A-Fa-f"
|
22
|
+
CRLF = '\x0D\x0A'
|
23
|
+
NOSPCRLFCL = '\x01-\x09\x0B-\x0C\x0E-\x1F\x21-\x39\x3B-\xFF'
|
24
|
+
|
25
|
+
# units
|
26
|
+
COMMAND = "(?:[#{LETTER}]+|[#{DIGIT}]{3})"
|
27
|
+
SHORTNAME = "[#{LETTER}#{DIGIT}](?:[-#{LETTER}#{DIGIT}]*[#{LETTER}#{DIGIT}])?"
|
28
|
+
HOSTNAME = "#{SHORTNAME}(?:\\.#{SHORTNAME})*"
|
29
|
+
SERVERNAME = HOSTNAME
|
30
|
+
IP4ADDR = "\d{1,3}(?:\\.\d{1,3}){3}"
|
31
|
+
IP6ADDR = "(?:[#{HEXDIGIT}]+(?::[#{HEXDIGIT}]+){7}|0:0:0:0:0:(?:0|FFFF):#{IP4ADDR})"
|
32
|
+
HOSTADDR = "(?:#{IP4ADDR}|#{IP6ADDR})"
|
33
|
+
HOST = "(?:#{HOSTNAME}|#{HOSTADDR})"
|
34
|
+
USER = '[\x01-\x09\x0B-\x0C\x0E-\x1F\x21-\x3F\x41-\xFF]+'
|
35
|
+
NICKNAME = "\\S+"
|
36
|
+
MIDDLE = "[#{NOSPCRLFCL}][:#{NOSPCRLFCL}]*"
|
37
|
+
TRAILING = "[: #{NOSPCRLFCL}]*"
|
38
|
+
PARAMS = "(?:((?: #{MIDDLE}){0,14})(?: :(#{TRAILING}))?|((?: #{MIDDLE}){14}):?(#{TRAILING}))"
|
39
|
+
PREFIX = "(?:#{NICKNAME}(?:(?:!#{USER})?@#{HOST})?|#{SERVERNAME})"
|
40
|
+
MESSAGE = "(?::(#{PREFIX}) )?(#{COMMAND})#{PARAMS}\s*#{CRLF}"
|
41
|
+
|
42
|
+
# pattern
|
43
|
+
CLIENT_PATTERN = /\A#{NICKNAME}(?:(?:!#{USER})?@#{HOST})\z/on
|
44
|
+
MESSAGE_PATTERN = /\A#{MESSAGE}\z/on
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/spec/spec_helper.rb
ADDED
data/spec/zircon_spec.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Zircon do
|
4
|
+
describe "#run!" do
|
5
|
+
it "should try to login and wait message from server" do
|
6
|
+
Zircon.any_instance.should_receive(:login)
|
7
|
+
Zircon.any_instance.should_receive(:wait_message)
|
8
|
+
Zircon.new.run!
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "#wait_message" do
|
13
|
+
it "should trigger callback related to message type" do
|
14
|
+
message = mock
|
15
|
+
message.stub(:type).and_return("xxx")
|
16
|
+
Zircon::Message.stub(:new).and_return(message)
|
17
|
+
Zircon.any_instance.stub(:loop) { |&block| block.call }
|
18
|
+
Zircon.any_instance.stub(:login)
|
19
|
+
Zircon.any_instance.stub(:gets)
|
20
|
+
Zircon.any_instance.should_receive(:trigger_xxx).with(message)
|
21
|
+
Zircon.new.run!
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "IRC command method (#join, #kick, #notice, ...)" do
|
26
|
+
it "should write type and args in space-separated form to socket" do
|
27
|
+
Zircon.any_instance.should_receive(:write).with("PRIVMSG channel :message\r\n")
|
28
|
+
Zircon.new.privmsg("channel", ":message")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/zircon.gemspec
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/zircon/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Ryo NAKAMURA"]
|
6
|
+
gem.email = ["r7kamura@gmail.com"]
|
7
|
+
gem.description = "Zircon is a mineral belonging to the group of nesosilicates."
|
8
|
+
gem.summary = "IRC client library written in Pure Ruby"
|
9
|
+
gem.homepage = "https://github.com/r7kamura/zircon"
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "zircon"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = Zircon::VERSION
|
17
|
+
|
18
|
+
gem.add_development_dependency "rspec"
|
19
|
+
end
|
metadata
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: zircon
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Ryo NAKAMURA
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-09-22 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: &70144831922480 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70144831922480
|
25
|
+
description: Zircon is a mineral belonging to the group of nesosilicates.
|
26
|
+
email:
|
27
|
+
- r7kamura@gmail.com
|
28
|
+
executables: []
|
29
|
+
extensions: []
|
30
|
+
extra_rdoc_files: []
|
31
|
+
files:
|
32
|
+
- .gitignore
|
33
|
+
- Gemfile
|
34
|
+
- LICENSE
|
35
|
+
- README.md
|
36
|
+
- Rakefile
|
37
|
+
- lib/zircon.rb
|
38
|
+
- lib/zircon/callback.rb
|
39
|
+
- lib/zircon/message.rb
|
40
|
+
- lib/zircon/message/patterns.rb
|
41
|
+
- lib/zircon/version.rb
|
42
|
+
- spec/spec_helper.rb
|
43
|
+
- spec/zircon_spec.rb
|
44
|
+
- zircon.gemspec
|
45
|
+
homepage: https://github.com/r7kamura/zircon
|
46
|
+
licenses: []
|
47
|
+
post_install_message:
|
48
|
+
rdoc_options: []
|
49
|
+
require_paths:
|
50
|
+
- lib
|
51
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
52
|
+
none: false
|
53
|
+
requirements:
|
54
|
+
- - ! '>='
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: '0'
|
57
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - ! '>='
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
requirements: []
|
64
|
+
rubyforge_project:
|
65
|
+
rubygems_version: 1.8.15
|
66
|
+
signing_key:
|
67
|
+
specification_version: 3
|
68
|
+
summary: IRC client library written in Pure Ruby
|
69
|
+
test_files:
|
70
|
+
- spec/spec_helper.rb
|
71
|
+
- spec/zircon_spec.rb
|
72
|
+
has_rdoc:
|