blur 1.7.3 → 1.8.5
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/mkroman/blur/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
|
18
|
+
[![Build Status](https://travis-ci.org/mkroman/blur.svg?branch=isupport)](https://travis-ci.org/mkroman/blur)
|
19
|
+
[![Dependency Status](https://gemnasium.com/mkroman/blur.svg)](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
|