blur 1.7.3 → 1.8.5
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 +4 -4
- data/.yardopts +5 -0
- data/LICENSE +13 -0
- data/README.md +35 -0
- data/library/blur.rb +6 -5
- data/library/blur/client.rb +18 -8
- data/library/blur/enhancements.rb +3 -1
- data/library/blur/evaluable.rb +13 -0
- data/library/blur/extension.rb +42 -0
- data/library/blur/handling.rb +42 -27
- data/library/blur/network.rb +46 -8
- data/library/blur/network/connection.rb +83 -2
- data/library/blur/network/isupport.rb +133 -0
- data/library/blur/script.rb +57 -23
- data/library/blur/script/cache.rb +1 -1
- data/library/blur/script/commands.rb +77 -0
- data/library/blur/script/dsl.rb +52 -72
- data/library/blur/version.rb +13 -0
- metadata +22 -17
- data/library/blur/script/fantasy.rb +0 -72
- data/library/blur/script/messageparsing.rb +0 -61
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0fd33b08ea566e3cf592a64b5ecaa68e28bf3715
|
4
|
+
data.tar.gz: 0780c217f54c21a06ba911d178e0323f822ef310
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a7a072a647cf7e8804991dde2b0c82173cfc0931188a46298c5e8b4441ebad34da61cdd160d987123c921da3aaf5c62790cdbac8dd464e9c93a86bc1e5055945
|
7
|
+
data.tar.gz: 741af149e93c26ade44f8b177d1466c5cc6960187a2c22d971343d7d69d7dc922e013e4a050d66a71cac5745b5b385e9664089af5f9bd63f7ea1dce20fa6fe96
|
data/.yardopts
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
Copyright (c) 2014, Mikkel Kroman
|
2
|
+
|
3
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
4
|
+
purpose with or without fee is hereby granted, provided that the above
|
5
|
+
copyright notice and this permission notice appear in all copies.
|
6
|
+
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
8
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
9
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
10
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
11
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
12
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
13
|
+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
Blur
|
2
|
+
====
|
3
|
+
Blur is an event-driven IRC-framework written in and for Ruby.
|
4
|
+
|
5
|
+
There are a bunch of other well-written, well-running IRC libraries made for
|
6
|
+
Ruby, but for me, they don't quite cut it as **the** library I wanted to use for
|
7
|
+
my IRC services. That's how Blur came to be.
|
8
|
+
|
9
|
+
Blur scales. A lot.
|
10
|
+
|
11
|
+
When I stresstested the library on my network, I ended up throttling my VDSL
|
12
|
+
connection before Blur even broke a sweat - albeit I only have 20/2.
|
13
|
+
|
14
|
+
I managed to connect with 5000 clones before it couldn't resolve the hostname
|
15
|
+
anymore, while this is an excellent feature, I would not suggest doing it.
|
16
|
+
|
17
|
+
[](https://bitdeli.com/free "Bitdeli Badge")
|
18
|
+
[](https://travis-ci.org/mkroman/blur)
|
19
|
+
[](https://gemnasium.com/mkroman/blur)
|
20
|
+
|
21
|
+
Features
|
22
|
+
--------
|
23
|
+
* SSL/TLS encryption
|
24
|
+
* Connect to multiple networks
|
25
|
+
* FiSH (channel-wide) encryptions
|
26
|
+
* Non-blocking connections (no threading)
|
27
|
+
* Extensible with scripts, (re)loadable during runtime
|
28
|
+
* Modular, makes it a piece of cake to extend its IRC-capability
|
29
|
+
|
30
|
+
Future Plans
|
31
|
+
------------
|
32
|
+
* DCC File-transfers
|
33
|
+
* DH1080 Key-Exchange
|
34
|
+
* ISupport implementation
|
35
|
+
* Better event-handling in scripts
|
data/library/blur.rb
CHANGED
@@ -9,27 +9,28 @@ require 'openssl'
|
|
9
9
|
require 'eventmachine'
|
10
10
|
|
11
11
|
# Require all library files.
|
12
|
+
require 'blur/version'
|
12
13
|
require 'blur/client'
|
14
|
+
require 'blur/evaluable'
|
15
|
+
require 'blur/script/dsl'
|
16
|
+
require 'blur/extension'
|
13
17
|
require 'blur/script'
|
14
18
|
require 'blur/network'
|
15
19
|
require 'blur/encryption'
|
16
20
|
require 'blur/enhancements'
|
17
21
|
require 'blur/script/cache'
|
18
22
|
require 'blur/network/user'
|
19
|
-
require 'blur/script/fantasy'
|
20
23
|
require 'blur/network/channel'
|
21
24
|
require 'blur/network/command'
|
25
|
+
require 'blur/network/isupport'
|
22
26
|
require 'blur/network/connection'
|
23
|
-
require 'blur/script/
|
27
|
+
require 'blur/script/commands'
|
24
28
|
|
25
29
|
# Blur is a very modular IRC-framework for ruby.
|
26
30
|
#
|
27
31
|
# It allows the developer to extend it in multiple ways.
|
28
32
|
# It can be by handlers, scripts, communications, and what have you.
|
29
33
|
module Blur
|
30
|
-
# The major and minor version-values of Blur.
|
31
|
-
Version = "1.7.3"
|
32
|
-
|
33
34
|
# Instantiates a client with given options and then makes the client instance
|
34
35
|
# evaluate the given block to form a DSL.
|
35
36
|
#
|
data/library/blur/client.rb
CHANGED
@@ -33,8 +33,6 @@ module Blur
|
|
33
33
|
|
34
34
|
load_scripts
|
35
35
|
trap 2, &method(:quit)
|
36
|
-
|
37
|
-
EventMachine.threadpool_size = 1
|
38
36
|
end
|
39
37
|
|
40
38
|
# Connect to each network available that is not already connected, then
|
@@ -43,12 +41,12 @@ module Blur
|
|
43
41
|
networks = @networks.select {|network| not network.connected? }
|
44
42
|
|
45
43
|
EventMachine.run do
|
44
|
+
EventMachine.error_handler{|e| p e }
|
45
|
+
|
46
46
|
networks.each do |network|
|
47
47
|
network.delegate = self
|
48
48
|
network.connect
|
49
49
|
end
|
50
|
-
|
51
|
-
EventMachine.error_handler{|e| p e }
|
52
50
|
end
|
53
51
|
end
|
54
52
|
|
@@ -69,6 +67,10 @@ module Blur
|
|
69
67
|
|
70
68
|
# Searches for scripts in working_directory/scripts and then loads them.
|
71
69
|
def load_scripts
|
70
|
+
# Load script extensions.
|
71
|
+
Script.load_extensions!
|
72
|
+
|
73
|
+
# Load the scripts.
|
72
74
|
script_path = File.dirname $0
|
73
75
|
|
74
76
|
Dir.glob("#{script_path}/scripts/*.rb").each do |path|
|
@@ -83,6 +85,9 @@ module Blur
|
|
83
85
|
#
|
84
86
|
# @see Script#unload!
|
85
87
|
def unload_scripts
|
88
|
+
# Unload script extensions.
|
89
|
+
Script.unload_extensions!
|
90
|
+
|
86
91
|
@scripts.each do |script|
|
87
92
|
script.unload!
|
88
93
|
end.clear
|
@@ -98,13 +103,13 @@ module Blur
|
|
98
103
|
#
|
99
104
|
# @param [optional, Symbol] signal The signal received by the system, if any.
|
100
105
|
def quit signal = :SIGINT
|
106
|
+
unload_scripts
|
107
|
+
|
101
108
|
@networks.each do |network|
|
102
109
|
network.transmit :QUIT, "Got SIGINT?"
|
103
110
|
network.disconnect
|
104
111
|
end
|
105
112
|
|
106
|
-
unload_scripts
|
107
|
-
|
108
113
|
EventMachine.stop
|
109
114
|
end
|
110
115
|
|
@@ -119,16 +124,21 @@ module Blur
|
|
119
124
|
def emit name, *args
|
120
125
|
EM.defer do
|
121
126
|
@callbacks[name].each do |callback|
|
122
|
-
|
127
|
+
begin
|
128
|
+
callback.call *args
|
129
|
+
rescue Exception => e
|
130
|
+
log.error "Callback `#{name}' threw an exception - #{exception.message ^ :bold} on line #{exception.line.to_s ^ :bold}"
|
131
|
+
puts exception.backtrace.join "\n"
|
132
|
+
end
|
123
133
|
end if @callbacks[name]
|
124
134
|
|
125
135
|
scripts = @scripts.select{|script| script.__emissions.include? name }
|
126
|
-
|
127
136
|
scripts.each do |script|
|
128
137
|
begin
|
129
138
|
script.__send__ name, *args
|
130
139
|
rescue Exception => exception
|
131
140
|
log.error "#{File.basename(script.__path) << " - " << exception.message ^ :bold} on line #{exception.line.to_s ^ :bold}"
|
141
|
+
puts exception.backtrace.join "\n"
|
132
142
|
end
|
133
143
|
end
|
134
144
|
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Blur
|
4
|
+
module Evaluable
|
5
|
+
# Evaluate the contents of the input file in the context of +self+.
|
6
|
+
def evaluate_source_file path
|
7
|
+
instance_eval File.read(path), File.basename(path), 0
|
8
|
+
@__evaluated = true
|
9
|
+
rescue Exception => exception
|
10
|
+
puts "#{exception.message ^ :bold} on line #{exception.line.to_s ^ :bold}"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Blur
|
4
|
+
# +Extension+ provides a sort of modules for scripts, for common functionality.
|
5
|
+
#
|
6
|
+
# Think of it as a kind of scripts for scripts.
|
7
|
+
class Extension
|
8
|
+
include Evaluable
|
9
|
+
include Script::DSL
|
10
|
+
include Logging
|
11
|
+
|
12
|
+
# @return the path in which the script remains.
|
13
|
+
attr_accessor :__path
|
14
|
+
# Can be used inside the script to act with the client itself.
|
15
|
+
# @return [Network::Client] the client delegate.
|
16
|
+
attr_accessor :__client
|
17
|
+
|
18
|
+
# Instantiates a new extension context and evaluates the +path+ extension
|
19
|
+
# file.
|
20
|
+
def initialize path
|
21
|
+
@__path = path
|
22
|
+
|
23
|
+
if evaluate_source_file path
|
24
|
+
log.info "Loaded extension #{@__path}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Purely for DSL purposes.
|
29
|
+
#
|
30
|
+
# @example
|
31
|
+
# Extension :http do
|
32
|
+
# …
|
33
|
+
# end
|
34
|
+
def Extension name, &block
|
35
|
+
@__name = name
|
36
|
+
|
37
|
+
instance_eval &block
|
38
|
+
|
39
|
+
true
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/library/blur/handling.rb
CHANGED
@@ -47,24 +47,24 @@ module Blur
|
|
47
47
|
# Called when the namelist of a channel was received.
|
48
48
|
def got_name_reply network, command
|
49
49
|
name = command[2]
|
50
|
-
users = command[3].split.map
|
50
|
+
users = command[3].split.map do |nick|
|
51
|
+
# Slice the nick if the first character is a user mode prefix.
|
52
|
+
if network.user_prefixes.include? nick.chr
|
53
|
+
nick.slice! 0
|
54
|
+
end
|
55
|
+
|
56
|
+
Network::User.new nick
|
57
|
+
end
|
51
58
|
|
52
|
-
if channel =
|
59
|
+
if channel = find_or_create_channel(name, network)
|
53
60
|
users.each do |user|
|
54
61
|
user.channel = channel
|
55
62
|
user.network = network
|
56
63
|
|
57
64
|
channel.users << user
|
58
65
|
end
|
59
|
-
else
|
60
|
-
channel = Network::Channel.new name, network, users
|
61
66
|
|
62
|
-
|
63
|
-
keyphrase = network.options[:fish][name]
|
64
|
-
channel.encryption = Encryption::FiSH.new keyphrase
|
65
|
-
end
|
66
|
-
|
67
|
-
network.channels << channel
|
67
|
+
emit :channel_who_reply, channel
|
68
68
|
end
|
69
69
|
end
|
70
70
|
|
@@ -75,21 +75,10 @@ module Blur
|
|
75
75
|
def got_channel_topic network, command
|
76
76
|
me, name, topic = command.params
|
77
77
|
|
78
|
-
if channel =
|
78
|
+
if channel = find_or_create_channel(name, network)
|
79
79
|
emit :topic_change, channel, topic
|
80
|
-
channel.topic = topic
|
81
|
-
else
|
82
|
-
channel = Network::Channel.new name, network, []
|
83
80
|
|
84
|
-
if network.fish? and network.options[:fish].key? name
|
85
|
-
keyphrase = network.options[:fish][name]
|
86
|
-
channel.encryption = Encryption::FiSH.new keyphrase
|
87
|
-
end
|
88
|
-
|
89
|
-
emit :topic_change, channel, topic
|
90
81
|
channel.topic = topic
|
91
|
-
|
92
|
-
network.channels << channel
|
93
82
|
end
|
94
83
|
end
|
95
84
|
|
@@ -101,15 +90,14 @@ module Blur
|
|
101
90
|
# Called when a user changed nickname.
|
102
91
|
#
|
103
92
|
# == Callbacks:
|
104
|
-
# Emits :user_rename with the parameters +channel+, +user+ and +
|
105
|
-
# where +nickname+ is the new name.
|
93
|
+
# Emits :user_rename with the parameters +channel+, +user+, +old_nick and +new_nick+
|
106
94
|
def got_nick network, command
|
107
95
|
nick = command.sender.nickname
|
108
96
|
|
109
97
|
if channels = network.channels_with_user(nick)
|
110
98
|
channels.each do |channel|
|
111
99
|
if user = channel.user_by_nick(nick)
|
112
|
-
emit :user_rename, channel, user, command[0]
|
100
|
+
emit :user_rename, channel, user, user.nick, command[0]
|
113
101
|
user.nick = command[0]
|
114
102
|
end
|
115
103
|
end
|
@@ -268,20 +256,47 @@ module Blur
|
|
268
256
|
end
|
269
257
|
|
270
258
|
if user = channel.user_by_nick(nick)
|
271
|
-
emit :user_mode, user, modes
|
272
259
|
user.merge_modes modes
|
260
|
+
emit :user_mode, user, modes
|
273
261
|
end
|
274
262
|
else
|
275
|
-
emit :channel_mode, channel, modes
|
276
263
|
channel.merge_modes modes
|
264
|
+
emit :channel_mode, channel, modes
|
277
265
|
end
|
278
266
|
end
|
279
267
|
end
|
280
268
|
|
269
|
+
# Called when the network announces its ISUPPORT parameters.
|
270
|
+
def got_005 network, command
|
271
|
+
params = command.params[1..-2]
|
272
|
+
|
273
|
+
network.isupport.parse *params
|
274
|
+
end
|
275
|
+
|
281
276
|
alias_method :got_353, :got_name_reply
|
282
277
|
alias_method :got_422, :got_end_of_motd
|
283
278
|
alias_method :got_376, :got_end_of_motd
|
284
279
|
alias_method :got_332, :got_channel_topic
|
280
|
+
|
281
|
+
private
|
282
|
+
|
283
|
+
def find_or_create_channel name, network, users = []
|
284
|
+
channel = network.channel_by_name name
|
285
|
+
|
286
|
+
if channel.nil?
|
287
|
+
channel = Network::Channel.new name, network, users
|
288
|
+
network.channels << channel
|
289
|
+
|
290
|
+
if network.fish? and network.options[:fish].key? name
|
291
|
+
keyphrase = network.options[:fish][name]
|
292
|
+
channel.encryption = Encryption::FiSH.new keyphrase
|
293
|
+
end
|
294
|
+
|
295
|
+
emit :channel_created, channel
|
296
|
+
end
|
297
|
+
|
298
|
+
channel
|
299
|
+
end
|
285
300
|
end
|
286
301
|
end
|
287
302
|
end
|
data/library/blur/network.rb
CHANGED
@@ -21,6 +21,8 @@ module Blur
|
|
21
21
|
attr_accessor :delegate
|
22
22
|
# @return [Network::Connection] the connection instance.
|
23
23
|
attr_accessor :connection
|
24
|
+
# @return [Network::ISupport] the network isupport specs.
|
25
|
+
attr_accessor :isupport
|
24
26
|
|
25
27
|
# Check whether or not connection is established.
|
26
28
|
def connected?; @connection and @connection.established? end
|
@@ -45,17 +47,31 @@ module Blur
|
|
45
47
|
|
46
48
|
# Instantiates the network.
|
47
49
|
#
|
48
|
-
# @param [Hash] options
|
49
|
-
# @option options [String] :hostname
|
50
|
-
#
|
51
|
-
# @option options [
|
52
|
-
# @option options [optional, String] :
|
53
|
-
#
|
54
|
-
# @option options [optional,
|
55
|
-
#
|
50
|
+
# @param [Hash] options The network options.
|
51
|
+
# @option options [String] :hostname The hostname or IP-address we want to
|
52
|
+
# connect to.
|
53
|
+
# @option options [String] :nickname The nickname to use.
|
54
|
+
# @option options [optional, String] :username (Copies :nickname)
|
55
|
+
# The username to use. This is also known as the ident.
|
56
|
+
# @option options [optional, String] :realname (Copies :username)
|
57
|
+
# The “real name” that we want to use. This is usually what shows up
|
58
|
+
# as "Name" when you whois a user.
|
59
|
+
# @option options [optional, String] :password The password for the network.
|
60
|
+
# This is sometimes needed for private networks.
|
61
|
+
# @option options [optional, Fixnum] :port (6697 if ssl, otherwise 6667)
|
62
|
+
# The remote port we want to connect to.
|
63
|
+
# @option options [optional, Boolean] :secure Set whether this is a secure
|
64
|
+
# (SSL-encrypted) connection.
|
65
|
+
# @option options [optional, String] :ssl_cert_file Local path of a
|
66
|
+
# readable file that contains a X509 CA certificate to validate against.
|
67
|
+
# @option options [optional, String] :ssl_fingerprint Validate that the
|
68
|
+
# remote certificate matches the specified fingerprint.
|
69
|
+
# @option options [optional, Boolean] :ssl_no_verify Disable verification
|
70
|
+
# alltogether.
|
56
71
|
def initialize options
|
57
72
|
@options = options
|
58
73
|
@channels = []
|
74
|
+
@isupport = ISupport.new self
|
59
75
|
|
60
76
|
unless options[:nickname]
|
61
77
|
raise ArgumentError, "nickname is missing from the networks option block"
|
@@ -100,6 +116,27 @@ module Blur
|
|
100
116
|
@channels.select { |channel| channel.user_by_nick nick }
|
101
117
|
end
|
102
118
|
|
119
|
+
# Returns a list of user prefixes that a nick might contain.
|
120
|
+
#
|
121
|
+
# @return [Array<String>] a list of user prefixes.
|
122
|
+
def user_prefixes
|
123
|
+
isupport["PREFIX"].values
|
124
|
+
end
|
125
|
+
|
126
|
+
# Returns a list of user modes that also gives a users nick a prefix.
|
127
|
+
#
|
128
|
+
# @return [Array<String>] a list of user modes.
|
129
|
+
def user_prefix_modes
|
130
|
+
isupport["PREFIX"].keys
|
131
|
+
end
|
132
|
+
|
133
|
+
# Returns a list of channel flags (channel mode D).
|
134
|
+
#
|
135
|
+
# @return [Array<String>] a list of channel flags.
|
136
|
+
def channel_flags
|
137
|
+
isupport["CHANMODES"]["D"]
|
138
|
+
end
|
139
|
+
|
103
140
|
# Attempt to establish a connection and send initial data.
|
104
141
|
#
|
105
142
|
# @see Connection
|
@@ -137,6 +174,7 @@ module Blur
|
|
137
174
|
|
138
175
|
@connection.send_data "#{command}\r\n"
|
139
176
|
end
|
177
|
+
|
140
178
|
|
141
179
|
# Convert it to a debug-friendly format.
|
142
180
|
def to_s
|
@@ -11,6 +11,8 @@ module Blur
|
|
11
11
|
# @see EventMachine::Protocols::LineAndTextProtocol
|
12
12
|
# @see EventMachine::Connection
|
13
13
|
class Connection < EM::Protocols::LineAndTextProtocol
|
14
|
+
SSLValidationError = Class.new StandardError
|
15
|
+
|
14
16
|
# Check whether or not connection is established.
|
15
17
|
def established?; @connected == true end
|
16
18
|
|
@@ -26,13 +28,18 @@ module Blur
|
|
26
28
|
# Called when a new connection is being set up, all we're going to use
|
27
29
|
# it for is to enable SSL/TLS on our connection.
|
28
30
|
def post_init
|
29
|
-
|
31
|
+
if @network.secure?
|
32
|
+
verify_peer = (@network.options[:ssl_no_verify] ? false : true)
|
33
|
+
|
34
|
+
start_tls verify_peer: verify_peer
|
35
|
+
end
|
30
36
|
end
|
31
37
|
|
32
38
|
# Called when a line was received, the connection sends it to the network
|
33
39
|
# delegate which then sends it to the client.
|
34
40
|
def receive_line line
|
35
41
|
command = Command.parse line
|
42
|
+
|
36
43
|
@network.got_command command
|
37
44
|
end
|
38
45
|
|
@@ -44,9 +51,56 @@ module Blur
|
|
44
51
|
connected!
|
45
52
|
end
|
46
53
|
|
54
|
+
# Validates that the peer certificate has the correct fingerprint as
|
55
|
+
# specified in the :fingerprint :ssl option.
|
56
|
+
#
|
57
|
+
# @note This doesn't support intermediate certificate authorities!
|
58
|
+
# @raise [SSLValidationError] Raised if the specified fingerprint doesn't
|
59
|
+
# match the certificates.
|
60
|
+
def ssl_verify_peer peer_cert
|
61
|
+
ssl_cert_file = @network.options[:ssl_cert_file]
|
62
|
+
peer_certificate = OpenSSL::X509::Certificate.new peer_cert
|
63
|
+
|
64
|
+
if ssl_cert_file
|
65
|
+
unless File.readable? ssl_cert_file
|
66
|
+
raise SSLValidationError, "Could not read the CA certificate file."
|
67
|
+
|
68
|
+
return false
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
if fingerprint_verification?
|
73
|
+
fingerprint = @network.options[:ssl_fingerprint].to_s
|
74
|
+
peer_fingerprint = cert_sha1_fingerprint peer_certificate
|
75
|
+
|
76
|
+
if fingerprint != peer_fingerprint
|
77
|
+
raise SSLValidationError,
|
78
|
+
"Expected fingerprint '#{fingerprint}', but got '#{peer_fingerprint}'"
|
79
|
+
|
80
|
+
return false
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
if certificate_verification?
|
85
|
+
ca_certificate = OpenSSL::X509::Certificate.new File.read ssl_cert_file
|
86
|
+
valid_signature = peer_certificate.verify ca_certificate.public_key
|
87
|
+
|
88
|
+
if not valid_signature
|
89
|
+
raise SSLValidationError, "Certificate verify failed"
|
90
|
+
|
91
|
+
return false
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
true
|
96
|
+
end
|
97
|
+
|
47
98
|
# Called once the connection is finally established.
|
48
99
|
def connection_completed
|
49
|
-
|
100
|
+
# We aren't completely connected yet if the connection is encrypted.
|
101
|
+
unless @network.secure?
|
102
|
+
connected!
|
103
|
+
end
|
50
104
|
end
|
51
105
|
|
52
106
|
# Called just as the connection is being terminated, either by remote or
|
@@ -65,6 +119,33 @@ module Blur
|
|
65
119
|
|
66
120
|
@network.connected!
|
67
121
|
end
|
122
|
+
|
123
|
+
# Returns true if we're expected to verify the certificate fingerprint.
|
124
|
+
def fingerprint_verification?
|
125
|
+
not @network.options[:ssl_fingerprint].nil?
|
126
|
+
end
|
127
|
+
|
128
|
+
# Returns true if we should verify the peer certificate.
|
129
|
+
def certificate_verification?
|
130
|
+
not @network.options[:ssl_cert_file].nil?
|
131
|
+
end
|
132
|
+
|
133
|
+
# Get the hexadecimal representation of the certificates public key.
|
134
|
+
def cert_sha1_fingerprint certificate
|
135
|
+
fingerprint = OpenSSL::Digest::SHA1.hexdigest certificate.to_der
|
136
|
+
|
137
|
+
# Format it the same way OpenSSL does.
|
138
|
+
fingerprint = fingerprint.chars.each_slice(2).map(&:join).join ':'
|
139
|
+
fingerprint.upcase
|
140
|
+
end
|
141
|
+
|
142
|
+
def ssl_fingerprint_error! peer_fingerprint
|
143
|
+
fingerprint = @network.options[:ssl_fingerprint]
|
144
|
+
|
145
|
+
raise SSLValidationError,
|
146
|
+
"Expected fingerprint '#{fingerprint}' but got '#{peer_fingerprint}'"
|
147
|
+
end
|
148
|
+
|
68
149
|
end
|
69
150
|
end
|
70
151
|
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Blur
|
4
|
+
class Network
|
5
|
+
# ISupport class that enables servers to announce what they support.
|
6
|
+
#
|
7
|
+
# @see https://tools.ietf.org/html/draft-brocklesby-irc-isupport-03
|
8
|
+
class ISupport < Hash
|
9
|
+
# Return the network reference.
|
10
|
+
attr_accessor :network
|
11
|
+
|
12
|
+
# ISUPPORT parameters which should always be casted to numeric values.
|
13
|
+
NumericParams = %w[CHANNELLEN MODES NICKLEN KICKLEN TOPICLEN AWAYLEN
|
14
|
+
MAXCHANNELS MAXBANS MAXPARA MAXTARGETS].freeze
|
15
|
+
|
16
|
+
# Our parsers for parameters that require special treatment.
|
17
|
+
Parsers = {
|
18
|
+
# CHANLIMIT=pfx:num[,pfx:num,...]
|
19
|
+
#
|
20
|
+
# This parameter specifies the maximum number of channels that a client
|
21
|
+
# may join. The value is a series of "pfx:num" pairs, where 'pfx'
|
22
|
+
# refers to one or more channel prefix characters (as specified in
|
23
|
+
# CHANTYPES), and 'num' indicates how many of these types of channel
|
24
|
+
# the client may join in total. If there is no limit to the number of
|
25
|
+
# certain channel type(s) a client may join, the limit should be
|
26
|
+
# specified as the empty string, for example "#:".
|
27
|
+
%w[CHANLIMIT] => -> (value) do
|
28
|
+
Hash.new.tap do |result|
|
29
|
+
params = value.split ?,
|
30
|
+
mappings = params.map{|param| param.split ?: }
|
31
|
+
|
32
|
+
mappings.each do |prefixes, limit|
|
33
|
+
prefixes.each_char do |prefix|
|
34
|
+
result[prefix] = limit ? limit.to_i : Float::INFINITY
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end,
|
39
|
+
|
40
|
+
# PREFIX=[(modes)prefixes]
|
41
|
+
#
|
42
|
+
#
|
43
|
+
# The PREFIX parameter specifies a list of channel status flags (the
|
44
|
+
# "modes" section) that clients may have on channels, followed by a
|
45
|
+
# mapping to the equivalent channel status flags ("prefixes"), which
|
46
|
+
# are used in NAMES and WHO replies. There is a one to one mapping
|
47
|
+
# between each mode and prefix.
|
48
|
+
#
|
49
|
+
# The order of the modes is from that which gives most privileges on
|
50
|
+
# the channel, to that which gives the least.
|
51
|
+
%w[PREFIX] => -> (value) do
|
52
|
+
Hash.new.tap do |result|
|
53
|
+
if value =~ /^\((.+)\)(.*)/
|
54
|
+
modes, prefix = $~[1..2]
|
55
|
+
|
56
|
+
modes.chars.each_with_index do |char, index|
|
57
|
+
result[char] = prefix[index]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end,
|
62
|
+
|
63
|
+
# CHANMODES=A,B,C,D
|
64
|
+
#
|
65
|
+
# The CHANMODES token specifies the modes that may be set on a channel.
|
66
|
+
# These modes are split into four categories, as follows:
|
67
|
+
#
|
68
|
+
# o Type A: Modes that add or remove an address to or from a list.
|
69
|
+
# These modes always take a parameter when sent by the server to a
|
70
|
+
# client; when sent by a client, they may be specified without a
|
71
|
+
# parameter, which requests the server to display the current
|
72
|
+
# contents of the corresponding list on the channel to the client.
|
73
|
+
# o Type B: Modes that change a setting on the channel. These modes
|
74
|
+
# always take a parameter.
|
75
|
+
# o Type C: Modes that change a setting on the channel. These modes
|
76
|
+
# take a parameter only when set; the parameter is absent when the
|
77
|
+
# mode is removed both in the client's and server's MODE command.
|
78
|
+
# o Type D: Modes that change a setting on the channel. These modes
|
79
|
+
# never take a parameter.
|
80
|
+
%w[CHANMODES] => -> (value) do
|
81
|
+
Hash.new.tap do |r|
|
82
|
+
r["A"], r["B"], r["C"], r["D"] = value.split(?,).map &:chars
|
83
|
+
end
|
84
|
+
end,
|
85
|
+
|
86
|
+
# Cast known params that are numeric, to a numeric value.
|
87
|
+
NumericParams => -> (value) do
|
88
|
+
value.to_i
|
89
|
+
end
|
90
|
+
}
|
91
|
+
|
92
|
+
# Initialize a new ISupport with a network reference.
|
93
|
+
#
|
94
|
+
# @param network [Network] The parent network.
|
95
|
+
def initialize network
|
96
|
+
@network = network
|
97
|
+
|
98
|
+
# Set default ISUPPORT values.
|
99
|
+
#
|
100
|
+
# @see
|
101
|
+
# https://tools.ietf.org/html/draft-brocklesby-irc-isupport-03#appendix-A
|
102
|
+
self["MODES"] = 3
|
103
|
+
self["PREFIX"] = { "o" => "@", "v" => "+" }
|
104
|
+
self["KICKLEN"] = 200
|
105
|
+
self["NICKLEN"] = 9
|
106
|
+
self["MAXLIST"] = { "#" => Float::INFINITY, "&" => Float::INFINITY }
|
107
|
+
self['TOPICLEN'] = 200
|
108
|
+
self["CHANMODES"] = {}
|
109
|
+
self["CHANTYPES"] = %w{# &}
|
110
|
+
self["CHANLIMIT"] = { "#" => Float::INFINITY, "&" => Float::INFINITY }
|
111
|
+
self["CHANNELLEN"] = 200
|
112
|
+
self["CASEMAPPING"] = "rfc1459"
|
113
|
+
end
|
114
|
+
|
115
|
+
# Parse a list of parameters to see what the server supports.
|
116
|
+
#
|
117
|
+
# @param parameters [Array] The list of parameters.
|
118
|
+
def parse *params
|
119
|
+
params.each do |parameter|
|
120
|
+
name, value = parameter.split ?=
|
121
|
+
|
122
|
+
if value
|
123
|
+
_, parser = Parsers.find{|key, value| key.include? name }
|
124
|
+
|
125
|
+
self[name] = parser.nil? ? value : parser.(value)
|
126
|
+
else
|
127
|
+
self[name] = true
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
data/library/blur/script.rb
CHANGED
@@ -8,19 +8,17 @@ module Blur
|
|
8
8
|
#
|
9
9
|
# @todo add examples in the documentation
|
10
10
|
# @see Script#Script
|
11
|
-
class Script
|
11
|
+
class Script
|
12
12
|
include Logging
|
13
|
+
include Evaluable
|
14
|
+
include DSL
|
13
15
|
|
16
|
+
ExtensionNotFoundError = Class.new StandardError
|
14
17
|
Emissions = [:connection_ready, :topic_change, :user_rename, :message,
|
15
18
|
:private_message, :user_entered, :user_left, :user_quit,
|
16
|
-
:user_kicked, :topic, :user_mode, :channel_mode
|
19
|
+
:user_kicked, :topic, :user_mode, :channel_mode,
|
20
|
+
:channel_created, :channel_who_reply]
|
17
21
|
|
18
|
-
# @return the name of the script.
|
19
|
-
attr_accessor :__name
|
20
|
-
# @return the author of the script.
|
21
|
-
attr_accessor :__author
|
22
|
-
# @return the version of the script.
|
23
|
-
attr_accessor :__version
|
24
22
|
# @return the path in which the script remains.
|
25
23
|
attr_accessor :__path
|
26
24
|
# Can be used inside the script to act with the client itself.
|
@@ -28,6 +26,27 @@ module Blur
|
|
28
26
|
attr_accessor :__client
|
29
27
|
# @return [Array] a list of handled emissions.
|
30
28
|
attr_accessor :__emissions
|
29
|
+
|
30
|
+
# A list of extensions.
|
31
|
+
@@__extensions = []
|
32
|
+
|
33
|
+
# Find and evaluate script extensions.
|
34
|
+
def self.load_extensions!
|
35
|
+
root_path = File.dirname $0
|
36
|
+
|
37
|
+
Dir.glob("#{root_path}/extensions/*.rb").each do |path|
|
38
|
+
extension = Extension.new path
|
39
|
+
extension.__client = self
|
40
|
+
extension.extension_loaded if extension.respond_to? :extension_loaded
|
41
|
+
|
42
|
+
@@__extensions << extension
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# "Unload" all script extensions.
|
47
|
+
def self.unload_extensions!
|
48
|
+
@@__extensions.clear
|
49
|
+
end
|
31
50
|
|
32
51
|
# Check to see if the script has been evaluated.
|
33
52
|
def evaluated?; @__evaluated end
|
@@ -38,7 +57,7 @@ module Blur
|
|
38
57
|
@__evaluated = false
|
39
58
|
@__emissions = []
|
40
59
|
|
41
|
-
if
|
60
|
+
if evaluate_source_file path
|
42
61
|
cache.load if Cache.exists? @__name
|
43
62
|
|
44
63
|
Emissions.each do |emission|
|
@@ -58,15 +77,40 @@ module Blur
|
|
58
77
|
# # …
|
59
78
|
# end
|
60
79
|
# end
|
61
|
-
def Script name,
|
62
|
-
@__name
|
63
|
-
|
64
|
-
|
80
|
+
def Script name, options = {}, &block
|
81
|
+
@__name = name
|
82
|
+
|
83
|
+
extensions = options[:using] || options[:uses]
|
84
|
+
|
85
|
+
# Automatically used extensions.
|
86
|
+
if extensions
|
87
|
+
extensions.each {|extension_name| using extension_name }
|
88
|
+
end
|
89
|
+
|
90
|
+
# Automatically included modules.
|
91
|
+
if options[:includes]
|
92
|
+
options[:includes].each{|module_name| self.extend module_name }
|
93
|
+
end
|
65
94
|
|
66
95
|
instance_eval &block
|
67
96
|
|
68
97
|
true
|
69
98
|
end
|
99
|
+
|
100
|
+
# Add script extension and define a method with the same name as
|
101
|
+
# the extension.
|
102
|
+
def using *extension_names
|
103
|
+
extension_names.each do |extension_name|
|
104
|
+
if extension = @@__extensions.find{|ext| ext.__name.to_s == extension_name.to_s }
|
105
|
+
extension.extension_used self if extension.respond_to? :extension_used
|
106
|
+
self.metaclass.send :define_method, :"#{extension_name}" do
|
107
|
+
return extension
|
108
|
+
end
|
109
|
+
else
|
110
|
+
raise ExtensionNotFoundError, "Extension not found: #{extension_name}"
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
70
114
|
|
71
115
|
# Unload the script and save the cache, if present.
|
72
116
|
def unload!
|
@@ -92,15 +136,5 @@ module Blur
|
|
92
136
|
def inspect
|
93
137
|
File.basename @__path
|
94
138
|
end
|
95
|
-
|
96
|
-
private
|
97
|
-
|
98
|
-
# Attempt to evaluate the contents of the script.
|
99
|
-
def evaluate
|
100
|
-
instance_eval File.read(@__path), File.basename(@__path), 0
|
101
|
-
@__evaluated = true
|
102
|
-
rescue Exception => exception
|
103
|
-
log.error "#{exception.message ^ :bold} on line #{exception.line.to_s ^ :bold}"
|
104
|
-
end
|
105
139
|
end
|
106
140
|
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Blur
|
4
|
+
class Script
|
5
|
+
# The +Commands+ module is a module that gives the ability to turn a
|
6
|
+
# script into a DSL-like framework.
|
7
|
+
module Commands
|
8
|
+
class Command
|
9
|
+
DefaultOptions = { prefix: '.', hostmask: nil }
|
10
|
+
|
11
|
+
def initialize triggers, options = {}, &block
|
12
|
+
@triggers = Array === triggers ? triggers : [triggers]
|
13
|
+
@options = DefaultOptions.merge options
|
14
|
+
@block = block
|
15
|
+
end
|
16
|
+
|
17
|
+
# Called by the Commands module.
|
18
|
+
#
|
19
|
+
# Calls the command block if the trigger matches the criteria.
|
20
|
+
def received_message user, channel, message
|
21
|
+
prefix = @options[:prefix]
|
22
|
+
|
23
|
+
# Return if the prefix don't match.
|
24
|
+
return unless message.start_with? prefix
|
25
|
+
|
26
|
+
# Return if the hostmask don't match.
|
27
|
+
# FIXME: Maybe use globbing instead of regular expressions?
|
28
|
+
unless @options[:hostmask].nil?
|
29
|
+
hostmask = "#{user.nick}!#{user.name}@#{user.host}"
|
30
|
+
|
31
|
+
return unless hostmask =~ @options[:hostmask]
|
32
|
+
end
|
33
|
+
|
34
|
+
command, args = split_message message
|
35
|
+
|
36
|
+
# Strip the prefix and compare the trigger name.
|
37
|
+
if self.matches_trigger? command
|
38
|
+
@block.call user, channel, args
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
protected
|
43
|
+
|
44
|
+
def split_message message
|
45
|
+
prefix = @options[:prefix]
|
46
|
+
command, args = message[prefix.length..-1].split $;, 2
|
47
|
+
|
48
|
+
return command, args
|
49
|
+
end
|
50
|
+
|
51
|
+
def matches_trigger? command
|
52
|
+
return @triggers.find{|trigger| trigger.to_s == command }
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Extend +base+ with self.
|
57
|
+
def self.extended base
|
58
|
+
base.instance_variable_set :@__commands, []
|
59
|
+
end
|
60
|
+
|
61
|
+
# Add a new command handler and trigger.
|
62
|
+
def command name, *args, &block
|
63
|
+
@__commands << Command.new(name, *args, &block)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Handle all calls to the scripts +message+ method, check to see if
|
67
|
+
# the message containts a valid command, serialize it and pass it to
|
68
|
+
# the script as command_name with the parameters +user+, +channel+
|
69
|
+
# and +message+.
|
70
|
+
def message user, channel, line
|
71
|
+
@__commands.each do |command|
|
72
|
+
command.received_message user, channel, line
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
data/library/blur/script/dsl.rb
CHANGED
@@ -1,72 +1,52 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
module Blur
|
4
|
-
class Script
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
#
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
# the
|
41
|
-
def
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
end
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
def message user, channel, line
|
54
|
-
return unless line.start_with? MessageTrigger
|
55
|
-
|
56
|
-
command, args = line.split $;, 2
|
57
|
-
trigger = strip command
|
58
|
-
|
59
|
-
if handler = @__dsl_triggers[trigger]
|
60
|
-
handler.(user, channel, args)
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
protected
|
65
|
-
|
66
|
-
# Strip all non-word characters from the input command.
|
67
|
-
def strip name
|
68
|
-
name.gsub /\W/, '' if name
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Blur
|
4
|
+
class Script
|
5
|
+
module DSL
|
6
|
+
def self.included base
|
7
|
+
base.send :attr_accessor, :__name
|
8
|
+
base.send :attr_accessor, :__author
|
9
|
+
base.send :attr_accessor, :__version
|
10
|
+
base.send :attr_accessor, :__description
|
11
|
+
end
|
12
|
+
|
13
|
+
# Set the author.
|
14
|
+
#
|
15
|
+
# @example
|
16
|
+
# Author "John Doe <john.doe@gmail.com>"
|
17
|
+
def Author *authors
|
18
|
+
@__author = authors.join ', '
|
19
|
+
end
|
20
|
+
|
21
|
+
# Set the description.
|
22
|
+
#
|
23
|
+
# @example
|
24
|
+
# Description "This is an example script."
|
25
|
+
def Description description
|
26
|
+
@__description = description
|
27
|
+
end
|
28
|
+
|
29
|
+
# Set the version.
|
30
|
+
#
|
31
|
+
# @example
|
32
|
+
# Version "1.0"
|
33
|
+
def Version version
|
34
|
+
@__version = version
|
35
|
+
end
|
36
|
+
|
37
|
+
# @return the name of the script.
|
38
|
+
def name; @__name end
|
39
|
+
|
40
|
+
# @return the name of the author(s).
|
41
|
+
def author; @__author end
|
42
|
+
|
43
|
+
# @return the script version.
|
44
|
+
def version; @__version end
|
45
|
+
|
46
|
+
# @return the description.
|
47
|
+
def description; @__description end
|
48
|
+
|
49
|
+
alias_method :Authors, :Author
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
metadata
CHANGED
@@ -1,41 +1,41 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: blur
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.8.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mikkel Kroman
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-01-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: majic
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '0.2'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0.2'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: eventmachine
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- -
|
31
|
+
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '0.12'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- -
|
38
|
+
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0.12'
|
41
41
|
description:
|
@@ -44,26 +44,32 @@ executables: []
|
|
44
44
|
extensions: []
|
45
45
|
extra_rdoc_files: []
|
46
46
|
files:
|
47
|
+
- ".yardopts"
|
48
|
+
- LICENSE
|
49
|
+
- README.md
|
50
|
+
- library/blur.rb
|
47
51
|
- library/blur/client.rb
|
52
|
+
- library/blur/encryption.rb
|
48
53
|
- library/blur/encryption/base64.rb
|
49
54
|
- library/blur/encryption/fish.rb
|
50
|
-
- library/blur/encryption.rb
|
51
55
|
- library/blur/enhancements.rb
|
56
|
+
- library/blur/evaluable.rb
|
57
|
+
- library/blur/extension.rb
|
52
58
|
- library/blur/handling.rb
|
59
|
+
- library/blur/network.rb
|
53
60
|
- library/blur/network/channel.rb
|
54
61
|
- library/blur/network/command.rb
|
55
62
|
- library/blur/network/connection.rb
|
63
|
+
- library/blur/network/isupport.rb
|
56
64
|
- library/blur/network/user.rb
|
57
|
-
- library/blur/
|
65
|
+
- library/blur/script.rb
|
58
66
|
- library/blur/script/cache.rb
|
67
|
+
- library/blur/script/commands.rb
|
59
68
|
- library/blur/script/dsl.rb
|
60
|
-
- library/blur/
|
61
|
-
- library/blur/script/messageparsing.rb
|
62
|
-
- library/blur/script.rb
|
63
|
-
- library/blur.rb
|
69
|
+
- library/blur/version.rb
|
64
70
|
homepage: https://github.com/mkroman/blur
|
65
71
|
licenses:
|
66
|
-
-
|
72
|
+
- MIT
|
67
73
|
metadata: {}
|
68
74
|
post_install_message:
|
69
75
|
rdoc_options: []
|
@@ -71,19 +77,18 @@ require_paths:
|
|
71
77
|
- library
|
72
78
|
required_ruby_version: !ruby/object:Gem::Requirement
|
73
79
|
requirements:
|
74
|
-
- -
|
80
|
+
- - ">="
|
75
81
|
- !ruby/object:Gem::Version
|
76
82
|
version: 1.9.1
|
77
83
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
78
84
|
requirements:
|
79
|
-
- -
|
85
|
+
- - ">="
|
80
86
|
- !ruby/object:Gem::Version
|
81
87
|
version: '0'
|
82
88
|
requirements: []
|
83
89
|
rubyforge_project:
|
84
|
-
rubygems_version: 2.
|
90
|
+
rubygems_version: 2.2.0
|
85
91
|
signing_key:
|
86
92
|
specification_version: 4
|
87
93
|
summary: An event-driven IRC-framework for Ruby.
|
88
94
|
test_files: []
|
89
|
-
has_rdoc:
|
@@ -1,72 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
module Blur
|
4
|
-
class Script < Module
|
5
|
-
# The +Fantasy+ module is a module that gives the ability to turn a
|
6
|
-
# script into a DSL-like framework.
|
7
|
-
#
|
8
|
-
# What it does is automatically test to see if a message starts with a
|
9
|
-
# trigger, and then, if so, it sends the command-part of the message to
|
10
|
-
# the script object itself.
|
11
|
-
#
|
12
|
-
# This way, the plugin-writer doesn't need to have repetetive code like
|
13
|
-
# that in every script.
|
14
|
-
#
|
15
|
-
# @example
|
16
|
-
# Script :example do
|
17
|
-
# include Fantasy
|
18
|
-
#
|
19
|
-
# def command_test user, channel, message
|
20
|
-
# channel.say "I hear you."
|
21
|
-
# end
|
22
|
-
# end
|
23
|
-
#
|
24
|
-
# # And if a user were to send the message ".test my method", it would
|
25
|
-
# # trigger the #command_test method with the following arguments
|
26
|
-
# #
|
27
|
-
# # user => #<Blur::Network::User … >
|
28
|
-
# # channel => #<Blur::Network::Channel … >
|
29
|
-
# # message => ".test my method"
|
30
|
-
module Fantasy
|
31
|
-
# The prefix that turns it into a possible command.
|
32
|
-
MessageTrigger = "."
|
33
|
-
|
34
|
-
# Extend +klass+ with self.
|
35
|
-
def self.included klass
|
36
|
-
klass.extend self
|
37
|
-
end
|
38
|
-
|
39
|
-
# Called when a script has been loaded, for use in modules extending
|
40
|
-
# the script.
|
41
|
-
def module_init
|
42
|
-
@__fantasy_triggers = []
|
43
|
-
|
44
|
-
public_methods.grep(/^command_/).each do |trigger|
|
45
|
-
@__fantasy_triggers << trigger.to_s[8..-1]
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
# Handle all calls to the scripts +message+ method, check to see if
|
50
|
-
# the message containts a valid command, serialize it and pass it to
|
51
|
-
# the script as command_name with the parameters +user+, +channel+
|
52
|
-
# and +message+.
|
53
|
-
def message user, channel, line
|
54
|
-
return unless line.start_with? MessageTrigger
|
55
|
-
|
56
|
-
command, args = line.split $;, 2
|
57
|
-
trigger = serialize command
|
58
|
-
|
59
|
-
if @__fantasy_triggers.include? trigger
|
60
|
-
__send__ :"command_#{trigger}", user, channel, args
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
protected
|
65
|
-
|
66
|
-
# Strip all non-word characters from the input command.
|
67
|
-
def serialize name
|
68
|
-
name.gsub /\W/, '' if name
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
@@ -1,61 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
module Blur
|
4
|
-
class Script < Module
|
5
|
-
# The +MessageParsing+ module is a module that gives the ability to turn a
|
6
|
-
# script into a DSL-like framework.
|
7
|
-
#
|
8
|
-
# What it does is automatically test to see if a message starts with a
|
9
|
-
# trigger, and then, if so, it sends the command-part of the message to
|
10
|
-
# the script object itself.
|
11
|
-
#
|
12
|
-
# This way, the plugin-writer doesn't need to have repetetive code like
|
13
|
-
# that in every script.
|
14
|
-
#
|
15
|
-
# @example
|
16
|
-
# Script :example do
|
17
|
-
# extend MessageParsing
|
18
|
-
#
|
19
|
-
# def command_test user, channel, message
|
20
|
-
# channel.say "I hear you."
|
21
|
-
# end
|
22
|
-
# end
|
23
|
-
#
|
24
|
-
# # And if a user were to send the message ".test my method", it would
|
25
|
-
# # trigger the #command_test method with the following arguments
|
26
|
-
# #
|
27
|
-
# # user => #<Blur::Network::User … >
|
28
|
-
# # channel => #<Blur::Network::Channel … >
|
29
|
-
# # message => ".test my method"
|
30
|
-
module MessageParsing
|
31
|
-
# The prefix that turns it into a possible command.
|
32
|
-
MessageTrigger = "."
|
33
|
-
|
34
|
-
def self.extended klass
|
35
|
-
warn "#{File.realpath __FILE__}: MessageParsing will be deprecated in the future, use Fantasy."
|
36
|
-
end
|
37
|
-
|
38
|
-
# Handle all calls to the scripts +message+ method, check to see if
|
39
|
-
# the message containts a valid command, serialize it and pass it to
|
40
|
-
# the script as command_name with the parameters +user+, +channel+
|
41
|
-
# and +message+.
|
42
|
-
def message user, channel, line
|
43
|
-
return unless line.start_with? MessageTrigger
|
44
|
-
|
45
|
-
command, args = line.split $;, 2
|
46
|
-
name = :"command_#{serialize command}"
|
47
|
-
|
48
|
-
if respond_to? name
|
49
|
-
__send__ name, user, channel, args
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
protected
|
54
|
-
|
55
|
-
# Strip all non-word characters from the input command.
|
56
|
-
def serialize name
|
57
|
-
name.gsub /\W/, '' if name
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|