cinch 0.3.5 → 1.0.0
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.
- data/LICENSE +20 -0
- data/README.md +192 -0
- data/Rakefile +53 -43
- data/examples/basic/autovoice.rb +32 -0
- data/examples/basic/google.rb +35 -0
- data/examples/basic/hello.rb +15 -0
- data/examples/basic/join_part.rb +38 -0
- data/examples/basic/memo.rb +39 -0
- data/examples/basic/msg.rb +16 -0
- data/examples/basic/seen.rb +36 -0
- data/examples/basic/urban_dict.rb +35 -0
- data/examples/basic/url_shorten.rb +35 -0
- data/examples/plugins/autovoice.rb +40 -0
- data/examples/plugins/custom_prefix.rb +23 -0
- data/examples/plugins/google.rb +37 -0
- data/examples/plugins/hello.rb +22 -0
- data/examples/plugins/join_part.rb +42 -0
- data/examples/plugins/memo.rb +50 -0
- data/examples/plugins/msg.rb +22 -0
- data/examples/plugins/multiple_matches.rb +41 -0
- data/examples/plugins/seen.rb +45 -0
- data/examples/plugins/urban_dict.rb +30 -0
- data/examples/plugins/url_shorten.rb +32 -0
- data/lib/cinch.rb +7 -20
- data/lib/cinch/ban.rb +41 -0
- data/lib/cinch/bot.rb +479 -0
- data/lib/cinch/callback.rb +11 -0
- data/lib/cinch/channel.rb +419 -0
- data/lib/cinch/constants.rb +369 -0
- data/lib/cinch/exceptions.rb +25 -0
- data/lib/cinch/helpers.rb +21 -0
- data/lib/cinch/irc.rb +344 -38
- data/lib/cinch/isupport.rb +96 -0
- data/lib/cinch/logger/formatted_logger.rb +80 -0
- data/lib/cinch/logger/logger.rb +44 -0
- data/lib/cinch/logger/null_logger.rb +18 -0
- data/lib/cinch/mask.rb +46 -0
- data/lib/cinch/message.rb +183 -0
- data/lib/cinch/message_queue.rb +62 -0
- data/lib/cinch/plugin.rb +205 -0
- data/lib/cinch/rubyext/infinity.rb +1 -0
- data/lib/cinch/rubyext/module.rb +18 -0
- data/lib/cinch/rubyext/queue.rb +19 -0
- data/lib/cinch/rubyext/string.rb +24 -0
- data/lib/cinch/syncable.rb +55 -0
- data/lib/cinch/user.rb +325 -0
- data/spec/bot_spec.rb +5 -0
- data/spec/channel_spec.rb +5 -0
- data/spec/cinch_spec.rb +5 -0
- data/spec/irc_spec.rb +5 -0
- data/spec/message_spec.rb +5 -0
- data/spec/plugin_spec.rb +5 -0
- data/spec/{helper.rb → spec_helper.rb} +0 -0
- data/spec/user_spec.rb +5 -0
- metadata +69 -51
- data/README.rdoc +0 -195
- data/examples/autovoice.rb +0 -32
- data/examples/custom_patterns.rb +0 -19
- data/examples/custom_prefix.rb +0 -25
- data/examples/google.rb +0 -31
- data/examples/hello.rb +0 -13
- data/examples/join_part.rb +0 -26
- data/examples/memo.rb +0 -40
- data/examples/msg.rb +0 -14
- data/examples/named-param-types.rb +0 -19
- data/examples/seen.rb +0 -41
- data/examples/urban_dict.rb +0 -31
- data/examples/url_shorten.rb +0 -34
- data/lib/cinch/base.rb +0 -368
- data/lib/cinch/irc/message.rb +0 -135
- data/lib/cinch/irc/parser.rb +0 -141
- data/lib/cinch/irc/socket.rb +0 -329
- data/lib/cinch/names.rb +0 -54
- data/lib/cinch/rules.rb +0 -171
- data/spec/base_spec.rb +0 -94
- data/spec/irc/helper.rb +0 -8
- data/spec/irc/message_spec.rb +0 -61
- data/spec/irc/parser_spec.rb +0 -103
- data/spec/irc/socket_spec.rb +0 -90
- data/spec/names_spec.rb +0 -393
- data/spec/options_spec.rb +0 -45
- data/spec/rules_spec.rb +0 -109
data/examples/custom_patterns.rb
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
require 'cinch'
|
2
|
-
|
3
|
-
bot = Cinch.setup do
|
4
|
-
server "irc.freenode.org"
|
5
|
-
channels %w{ #cinch }
|
6
|
-
end
|
7
|
-
|
8
|
-
bot.add_custom_pattern(:friends, /injekt|lee|john|bob/)
|
9
|
-
bot.add_custom_pattern(:hex, /[\dA-Fa-f]+/)
|
10
|
-
|
11
|
-
bot.plugin("I like :person-friends", :prefix => false) do |m|
|
12
|
-
m.reply "I like #{m.args[:person]} too!"
|
13
|
-
end
|
14
|
-
|
15
|
-
bot.plugin("checkhex :n-hex") do |m|
|
16
|
-
m.answer "Yes, #{m.args[:n]} is indeed hex."
|
17
|
-
end
|
18
|
-
|
19
|
-
bot.run
|
data/examples/custom_prefix.rb
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
require 'cinch'
|
2
|
-
|
3
|
-
bot = Cinch.setup do
|
4
|
-
server "irc.freenode.org"
|
5
|
-
channels %w( #cinch )
|
6
|
-
end
|
7
|
-
|
8
|
-
bot.plugin "default" do |m|
|
9
|
-
m.reply "default prefix"
|
10
|
-
end
|
11
|
-
|
12
|
-
bot.plugin "custom", :prefix => '@' do |m|
|
13
|
-
m.reply "custom prefix"
|
14
|
-
end
|
15
|
-
|
16
|
-
bot.plugin "botnick", :prefix => :botnick do |m|
|
17
|
-
m.reply "botnick prefix"
|
18
|
-
end
|
19
|
-
|
20
|
-
bot.plugin "botnick2", :prefix => bot.nick do |m|
|
21
|
-
m.reply "another botnick prefix"
|
22
|
-
end
|
23
|
-
|
24
|
-
bot.run
|
25
|
-
|
data/examples/google.rb
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
require 'cinch'
|
2
|
-
require 'open-uri'
|
3
|
-
require 'nokogiri'
|
4
|
-
require 'cgi'
|
5
|
-
|
6
|
-
bot = Cinch.setup do
|
7
|
-
server "irc.freenode.net"
|
8
|
-
nick "MrCinch"
|
9
|
-
channels %w/ #cinch /
|
10
|
-
end
|
11
|
-
|
12
|
-
# Extremely basic method, grabs the first result returned by Google
|
13
|
-
# or "No results found" otherwise
|
14
|
-
def google(query)
|
15
|
-
url = "http://www.google.com/search?q=#{CGI.escape(query)}"
|
16
|
-
res = Nokogiri::HTML(open(url)).at("h3.r")
|
17
|
-
|
18
|
-
title = res.text
|
19
|
-
link = res.at('a')[:href]
|
20
|
-
desc = res.at("./following::div").children.first.text
|
21
|
-
rescue
|
22
|
-
"No results found"
|
23
|
-
else
|
24
|
-
CGI.unescape_html "#{title} - #{desc} (#{link})"
|
25
|
-
end
|
26
|
-
|
27
|
-
bot.plugin("google :query") do |m|
|
28
|
-
m.reply google(m.args[:query])
|
29
|
-
end
|
30
|
-
|
31
|
-
bot.run
|
data/examples/hello.rb
DELETED
data/examples/join_part.rb
DELETED
@@ -1,26 +0,0 @@
|
|
1
|
-
require 'cinch'
|
2
|
-
|
3
|
-
bot = Cinch.setup do
|
4
|
-
server "irc.freenode.org"
|
5
|
-
nick "CinchBot"
|
6
|
-
channels %w( #cinch )
|
7
|
-
end
|
8
|
-
|
9
|
-
# Who should be able to access these plugins
|
10
|
-
admin = 'injekt'
|
11
|
-
|
12
|
-
bot.plugin "join :channel", :nick => admin do |m|
|
13
|
-
bot.join m.args[:channel]
|
14
|
-
end
|
15
|
-
|
16
|
-
bot.plugin "part :channel", :nick => admin do |m|
|
17
|
-
bot.part m.args[:channel]
|
18
|
-
end
|
19
|
-
|
20
|
-
# Part current channel if none is given
|
21
|
-
bot.plugin "part", :nick => admin do |m|
|
22
|
-
bot.part m.channel
|
23
|
-
end
|
24
|
-
|
25
|
-
bot.run
|
26
|
-
|
data/examples/memo.rb
DELETED
@@ -1,40 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require 'cinch'
|
4
|
-
|
5
|
-
bot = Cinch.setup do
|
6
|
-
server "irc.freenode.org"
|
7
|
-
channels %w( #cinch )
|
8
|
-
end
|
9
|
-
|
10
|
-
class Memo < Struct.new(:nick, :channel, :text, :time)
|
11
|
-
def to_s
|
12
|
-
"[#{time.asctime}] <#{channel}/#{nick}> #{text}"
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
@memos = {}
|
17
|
-
|
18
|
-
bot.on :privmsg do |m|
|
19
|
-
if @memos.has_key?(m.nick)
|
20
|
-
bot.privmsg m.nick, @memos[m.nick].to_s
|
21
|
-
@memos.delete(m.nick)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
bot.plugin("memo :nick-string :memo") do |m|
|
26
|
-
nick = m.args[:nick]
|
27
|
-
|
28
|
-
if @memos.key?(nick)
|
29
|
-
m.reply "There's already a memo #{nick}. You can only store one right now"
|
30
|
-
elsif nick == m.nick
|
31
|
-
m.reply "You can't leave memos for yourself.."
|
32
|
-
elsif nick == bot.options.nick
|
33
|
-
m.reply "You can't leave memos for me.."
|
34
|
-
else
|
35
|
-
@memos[nick] = Memo.new(m.nick, m.channel, m.args[:memo], Time.new)
|
36
|
-
m.reply "Added memo for #{nick}"
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
bot.run
|
data/examples/msg.rb
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
require 'cinch'
|
2
|
-
|
3
|
-
bot = Cinch.setup do
|
4
|
-
server "irc.freenode.org"
|
5
|
-
channels %w( #cinch )
|
6
|
-
end
|
7
|
-
|
8
|
-
bot.plugin("say :n-digit :text") do |m|
|
9
|
-
m.args[:n].to_i.times {
|
10
|
-
m.reply m.args[:text]
|
11
|
-
}
|
12
|
-
end
|
13
|
-
|
14
|
-
bot.plugin("say :text-word :rest") do |m|
|
15
|
-
stuff = [m.args[:text], m.args[:rest]].join(' ')
|
16
|
-
m.reply stuff
|
17
|
-
end
|
18
|
-
|
19
|
-
bot.run
|
data/examples/seen.rb
DELETED
@@ -1,41 +0,0 @@
|
|
1
|
-
require 'cinch'
|
2
|
-
|
3
|
-
users = {}
|
4
|
-
|
5
|
-
class Seen < Struct.new(:who, :where, :what, :time)
|
6
|
-
def to_s
|
7
|
-
"[#{time.asctime}] #{who} was seen in #{where} saying #{what}"
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
|
-
bot = Cinch.setup(
|
12
|
-
:server => 'irc.freenode.org',
|
13
|
-
:channels => ['#cinch'],
|
14
|
-
:prefix => '!',
|
15
|
-
:verbose => true,
|
16
|
-
)
|
17
|
-
|
18
|
-
# Only log a PRIVMSG
|
19
|
-
bot.on :privmsg do |m|
|
20
|
-
# Dont record a private message
|
21
|
-
unless m.private?
|
22
|
-
users[m.nick] = Seen.new(m.nick, m.channel, m.text, Time.new)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
bot.plugin("seen :nick") do |m|
|
27
|
-
nick = m.args[:nick]
|
28
|
-
|
29
|
-
if nick == bot.nick
|
30
|
-
m.reply "That's me!"
|
31
|
-
elsif nick == m.nick
|
32
|
-
m.reply "That's you!"
|
33
|
-
elsif users.key?(nick)
|
34
|
-
m.reply users[nick].to_s
|
35
|
-
else
|
36
|
-
m.reply "I haven't seen #{nick}"
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
bot.run
|
41
|
-
|
data/examples/urban_dict.rb
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
require 'cinch'
|
2
|
-
require 'open-uri'
|
3
|
-
require 'nokogiri'
|
4
|
-
require 'cgi'
|
5
|
-
|
6
|
-
# This bot connects to urban dictionary and returns the first result
|
7
|
-
# for a given query, replying with the result directly to the sender
|
8
|
-
|
9
|
-
bot = Cinch.setup do
|
10
|
-
server "irc.freenode.net"
|
11
|
-
nick "MrCinch"
|
12
|
-
channels %w/ #cinch /
|
13
|
-
end
|
14
|
-
|
15
|
-
# This method assumes everything will go ok, it's not the best method
|
16
|
-
# of doing this *by far* and is simply a helper method to show how it
|
17
|
-
# can be done.. it works!
|
18
|
-
def urban_dict(query)
|
19
|
-
url = "http://www.urbandictionary.com/define.php?term=#{CGI.escape(query)}"
|
20
|
-
CGI.unescape_html Nokogiri::HTML(open(url)).at("div.definition").text.gsub(/\s+/, ' ') rescue nil
|
21
|
-
end
|
22
|
-
|
23
|
-
bot.plugin("urban :query") do |m|
|
24
|
-
m.answer urban_dict(m.args[:query]) || "No results found"
|
25
|
-
end
|
26
|
-
|
27
|
-
bot.run
|
28
|
-
|
29
|
-
# injekt> !urban cinch
|
30
|
-
# MrCinch> injekt: describing an action that's extremely easy.
|
31
|
-
|
data/examples/url_shorten.rb
DELETED
@@ -1,34 +0,0 @@
|
|
1
|
-
require 'open-uri'
|
2
|
-
require 'cinch'
|
3
|
-
|
4
|
-
# Automatically shorten URL's found in messages
|
5
|
-
# Using the tinyURL API
|
6
|
-
|
7
|
-
bot = Cinch.configure do
|
8
|
-
server "irc.freenode.org"
|
9
|
-
verbose true
|
10
|
-
channels %w/#cinch/
|
11
|
-
end
|
12
|
-
|
13
|
-
def shorten(url)
|
14
|
-
url = open("http://tinyurl.com/api-create.php?url=#{URI.escape(url)}").read
|
15
|
-
url == "Error" ? nil : url
|
16
|
-
rescue OpenURI::HTTPError
|
17
|
-
nil
|
18
|
-
end
|
19
|
-
|
20
|
-
bot.on :privmsg do |m|
|
21
|
-
unless m.private?
|
22
|
-
urls = URI.extract(m.text, "http")
|
23
|
-
|
24
|
-
unless urls.empty?
|
25
|
-
short_urls = urls.map {|url| shorten(url) }.compact
|
26
|
-
|
27
|
-
unless short_urls.empty?
|
28
|
-
m.reply short_urls.join(", ")
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
bot.start
|
data/lib/cinch/base.rb
DELETED
@@ -1,368 +0,0 @@
|
|
1
|
-
module Cinch
|
2
|
-
|
3
|
-
# == Description
|
4
|
-
# The base for an IRC connection
|
5
|
-
# TODO: More documentation
|
6
|
-
#
|
7
|
-
# == Example
|
8
|
-
# bot = Cinch::Base.new :server => 'irc.freenode.org'
|
9
|
-
#
|
10
|
-
# bot.on :join do |m|
|
11
|
-
# m.reply "Welcome to #{m.channel}, #{m.nick}!" unless m.nick == bot.nick
|
12
|
-
# end
|
13
|
-
#
|
14
|
-
# bot.plugin "say :text" do |m|
|
15
|
-
# m.reply m.args[:text]
|
16
|
-
# end
|
17
|
-
#
|
18
|
-
# == Author
|
19
|
-
# * Lee Jarvis - ljjarvis@gmail.com
|
20
|
-
class Base
|
21
|
-
|
22
|
-
# A Hash holding rules and attributes
|
23
|
-
attr_reader :rules
|
24
|
-
|
25
|
-
# A Hash holding listeners and reply Procs
|
26
|
-
attr_reader :listeners
|
27
|
-
|
28
|
-
# An OpenStruct holding all configuration options
|
29
|
-
attr_reader :options
|
30
|
-
|
31
|
-
# Our IRC::Socket instance
|
32
|
-
attr_reader :irc
|
33
|
-
|
34
|
-
# Default options hash
|
35
|
-
DEFAULTS = {
|
36
|
-
:port => 6667,
|
37
|
-
:nick => "Cinch",
|
38
|
-
:nick_suffix => '_',
|
39
|
-
:username => 'cinch',
|
40
|
-
:realname => "Cinch IRC Bot Building Framework",
|
41
|
-
:prefix => '!',
|
42
|
-
:usermode => 0,
|
43
|
-
:password => nil,
|
44
|
-
:ssl => false,
|
45
|
-
}
|
46
|
-
|
47
|
-
# Options can be passed via a hash, a block, or on the instance
|
48
|
-
# independantly. Or of course via the command line
|
49
|
-
#
|
50
|
-
# == Example
|
51
|
-
# # With a Hash
|
52
|
-
# bot = Cinch::Base.new(:server => 'irc.freenode.org')
|
53
|
-
#
|
54
|
-
# # With a block
|
55
|
-
# bot = Cinch::Base.new do
|
56
|
-
# server "irc.freenode.org"
|
57
|
-
# end
|
58
|
-
#
|
59
|
-
# # After the instance is created
|
60
|
-
# bot = Cinch::Base.new
|
61
|
-
# bot.options.server = "irc.freenode.org"
|
62
|
-
#
|
63
|
-
# # Nothing, but invoked with "ruby foo.rb -s irc.freenode.org"
|
64
|
-
# bot = Cinch::Base.new
|
65
|
-
def initialize(ops={}, &blk)
|
66
|
-
options = DEFAULTS.merge(ops).merge(Options.new(&blk))
|
67
|
-
@options = OpenStruct.new(options.merge(cli_ops))
|
68
|
-
|
69
|
-
@rules = Rules.new
|
70
|
-
@listeners = {}
|
71
|
-
@listeners[:ctcp] = {}
|
72
|
-
|
73
|
-
@irc = IRC::Socket.new(options[:server], options[:port], options[:ssl])
|
74
|
-
@parser = IRC::Parser.new
|
75
|
-
|
76
|
-
# Default listeners
|
77
|
-
on(:ping) {|m| @irc.pong(m.text) }
|
78
|
-
|
79
|
-
on(:ctcp, :version) {|m| m.ctcp_reply "Cinch IRC Bot Building Framework v#{Cinch::VERSION}"}
|
80
|
-
end
|
81
|
-
|
82
|
-
# Add a new plugin
|
83
|
-
#
|
84
|
-
# == Example
|
85
|
-
# plugin('hello') do |m|
|
86
|
-
# m.reply "Hello, #{m.nick}!"
|
87
|
-
# end
|
88
|
-
def plugin(rule, options={}, &blk)
|
89
|
-
rule, keys = compile(rule)
|
90
|
-
|
91
|
-
if @rules.has_rule?(rule)
|
92
|
-
@rules.add_callback(rule, blk)
|
93
|
-
@rules.merge_options(rule, options)
|
94
|
-
else
|
95
|
-
@rules.add_rule(rule, keys, options, blk)
|
96
|
-
end
|
97
|
-
end
|
98
|
-
alias :rule :plugin
|
99
|
-
|
100
|
-
# Add new listeners
|
101
|
-
#
|
102
|
-
# == Example
|
103
|
-
# on(376) do |m|
|
104
|
-
# m.join "#mychan"
|
105
|
-
# end
|
106
|
-
#
|
107
|
-
# This method also provides an alternative way to define a rule.
|
108
|
-
# The following are equivalent
|
109
|
-
# plugin("foo bar") do |m|
|
110
|
-
# m.reply "baz!"
|
111
|
-
# end
|
112
|
-
#
|
113
|
-
# on(:message, "foo bar") do |m|
|
114
|
-
# m.reply "baz!"
|
115
|
-
# end
|
116
|
-
#
|
117
|
-
# This also gives us the opportunity to reply to CTCP messages
|
118
|
-
# on(:ctcp, :time) do |m|
|
119
|
-
# m.ctcp_reply Time.now.asctime
|
120
|
-
# end
|
121
|
-
#
|
122
|
-
# Note that when adding listeners for numberic IRC replies which
|
123
|
-
# begin with a 0 (digit), make sure you define the command as a
|
124
|
-
# String and not Integer. This is because 001.to_s == "1" so the
|
125
|
-
# command will not work as expected.
|
126
|
-
def on(*commands, &blk)
|
127
|
-
if commands.first == :message
|
128
|
-
rule, options = commands[1..2]
|
129
|
-
options = {} unless options.is_a?(Hash)
|
130
|
-
plugin(rule, options, &blk)
|
131
|
-
elsif commands.first == :ctcp
|
132
|
-
action = commands[1]
|
133
|
-
|
134
|
-
if @listeners[:ctcp].key?(action)
|
135
|
-
@listeners[:ctcp][action] << blk
|
136
|
-
else
|
137
|
-
@listeners[:ctcp][action] = [blk]
|
138
|
-
end
|
139
|
-
else
|
140
|
-
commands.map {|x| x.to_s.downcase.to_sym }.each do |cmd|
|
141
|
-
if @listeners.key?(cmd)
|
142
|
-
@listeners[cmd] << blk
|
143
|
-
else
|
144
|
-
@listeners[cmd] = [blk]
|
145
|
-
end
|
146
|
-
end
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
|
-
# This method builds a regular expression from your rule
|
151
|
-
# and defines all named parameters, as well as dealing with
|
152
|
-
# patterns.
|
153
|
-
#
|
154
|
-
# All patterns which exist in Cinch::IRC::Parser are supported
|
155
|
-
#
|
156
|
-
# == Examples
|
157
|
-
# === Capturing digits
|
158
|
-
# bot.plugin("say :text-digit")
|
159
|
-
# * Does match !say 3
|
160
|
-
# * Does not match !say 3 4
|
161
|
-
# * Does not match !say foo
|
162
|
-
#
|
163
|
-
# === Both digit and word
|
164
|
-
# bot.plugin("say :n-digit :text-word")
|
165
|
-
# * Does match !say 3 foo
|
166
|
-
# * Does not match !say 3 foo bar
|
167
|
-
#
|
168
|
-
# === Capturing until the end of the line
|
169
|
-
# bot.plugin("say :text")
|
170
|
-
# * Does match !say foo
|
171
|
-
# * Does match !say foo bar
|
172
|
-
#
|
173
|
-
# === Or mix them all
|
174
|
-
# bot.plugin("say :n-digit :who-word :text")
|
175
|
-
#
|
176
|
-
# Using "!say 3 injekt some text here" would provide
|
177
|
-
# the following attributes
|
178
|
-
# * m.args[:n] => 3
|
179
|
-
# * m.args[:who] => injekt
|
180
|
-
# * m.args[:text] => some text here
|
181
|
-
def compile(rule)
|
182
|
-
return [rule, []] if rule.is_a?(Regexp)
|
183
|
-
keys = []
|
184
|
-
special_chars = %w{. + ( )}
|
185
|
-
|
186
|
-
pattern = rule.to_s.gsub(/((:[\w\-]+)|[\*#{special_chars.join}])/) do |match|
|
187
|
-
case match
|
188
|
-
when *special_chars
|
189
|
-
Regexp.escape(match)
|
190
|
-
else
|
191
|
-
k = $2
|
192
|
-
if k =~ /\-\w+$/
|
193
|
-
key, type = k.split('-')
|
194
|
-
keys << key[1..-1]
|
195
|
-
|
196
|
-
if @parser.has_pattern?(type)
|
197
|
-
"(#{@parser.pattern(type)})"
|
198
|
-
else
|
199
|
-
"([^\x00\r\n]+?)"
|
200
|
-
end
|
201
|
-
else
|
202
|
-
keys << k[1..-1]
|
203
|
-
"([^\x00\r\n]+?)"
|
204
|
-
end
|
205
|
-
end
|
206
|
-
end
|
207
|
-
["^#{pattern}$", keys]
|
208
|
-
end
|
209
|
-
|
210
|
-
# Add a custom pattern for rule validation
|
211
|
-
#
|
212
|
-
# == Example
|
213
|
-
# bot = Cinch.setup do
|
214
|
-
# server 'irc.freenode.org'
|
215
|
-
# port 6667
|
216
|
-
# end
|
217
|
-
#
|
218
|
-
# bot.add_pattern(:number, /[0-9]/)
|
219
|
-
#
|
220
|
-
# bot.plugin("getnum :foo-number") do |m|
|
221
|
-
# m.reply "Your number was: #{m.args[:foo]}"
|
222
|
-
# end
|
223
|
-
def add_pattern(name, pattern)
|
224
|
-
@parser.add_pattern(name, pattern)
|
225
|
-
end
|
226
|
-
alias :add_custom_pattern :add_pattern
|
227
|
-
|
228
|
-
# Run run run
|
229
|
-
def run
|
230
|
-
# Configure some runtime listeners
|
231
|
-
on(433) do |m|
|
232
|
-
@options.nick += @options.nick_suffix
|
233
|
-
@irc.nick @options.nick
|
234
|
-
end
|
235
|
-
|
236
|
-
if @options.respond_to?(:channels)
|
237
|
-
on("004") { @options.channels.each {|c| @irc.join(c) } }
|
238
|
-
end
|
239
|
-
|
240
|
-
@irc.connect options.server, options.port
|
241
|
-
@irc.pass options.password if options.password
|
242
|
-
@irc.nick options.nick
|
243
|
-
@irc.user options.username, options.usermode, '*', options.realname
|
244
|
-
|
245
|
-
begin
|
246
|
-
process(@irc.read) while @irc.connected?
|
247
|
-
rescue Interrupt
|
248
|
-
@irc.quit("Interrupted")
|
249
|
-
puts "\nInterrupted. Shutting down .."
|
250
|
-
exit
|
251
|
-
end
|
252
|
-
end
|
253
|
-
alias :start :run
|
254
|
-
|
255
|
-
private
|
256
|
-
|
257
|
-
# Process the next line read from the server
|
258
|
-
def process(line)
|
259
|
-
return unless line
|
260
|
-
message = @parser.parse(line)
|
261
|
-
message.irc = @irc unless message.irc
|
262
|
-
puts message if options.verbose
|
263
|
-
|
264
|
-
# runs on any symbol
|
265
|
-
@listeners[:any].each { |l| l.call(message) } if @listeners.key?(:any)
|
266
|
-
|
267
|
-
if @listeners.key?(message.symbol)
|
268
|
-
if message.symbol == :ctcp
|
269
|
-
action = message.ctcp_action.downcase.to_sym
|
270
|
-
|
271
|
-
if @listeners[:ctcp].include?(action)
|
272
|
-
@listeners[:ctcp][action].each {|l| l.call(message) }
|
273
|
-
end
|
274
|
-
else
|
275
|
-
@listeners[message.symbol].each {|l| l.call(message) }
|
276
|
-
end
|
277
|
-
end
|
278
|
-
|
279
|
-
if [:privmsg].include?(message.symbol)
|
280
|
-
rules.each do |rule|
|
281
|
-
pattern = rule.to_s
|
282
|
-
rule.keys.map! {|key| key.to_sym }
|
283
|
-
prefix = nil
|
284
|
-
|
285
|
-
if options.prefix
|
286
|
-
if rule.options.key?(:prefix)
|
287
|
-
if [:bot, :botnick, options.nick].include? rule.options[:prefix]
|
288
|
-
prefix = options.nick + "[:,] "
|
289
|
-
else
|
290
|
-
prefix = rule.options[:prefix]
|
291
|
-
end
|
292
|
-
else
|
293
|
-
if [:bot, :botnick, options.nick].include? options.prefix
|
294
|
-
prefix = options.nick + "[:,] "
|
295
|
-
else
|
296
|
-
prefix = options.prefix
|
297
|
-
end
|
298
|
-
end
|
299
|
-
end
|
300
|
-
|
301
|
-
# insert prefix unless it already exists
|
302
|
-
pattern.insert(1, prefix) unless pattern[1..prefix.size] == prefix
|
303
|
-
|
304
|
-
if rule.options[:ignore_case]
|
305
|
-
regex = Regexp.new(pattern, Regexp::IGNORECASE)
|
306
|
-
else
|
307
|
-
regex = Regexp.new(pattern)
|
308
|
-
end
|
309
|
-
|
310
|
-
if message.text && mdata = message.text.rstrip.match(regex)
|
311
|
-
unless rule.keys.empty? || mdata.captures.empty?
|
312
|
-
args = Hash[rule.keys.zip(mdata.captures)]
|
313
|
-
message.args = args
|
314
|
-
end
|
315
|
-
# execute rule
|
316
|
-
rule.execute(message)
|
317
|
-
end
|
318
|
-
end
|
319
|
-
end
|
320
|
-
end
|
321
|
-
|
322
|
-
# Parse command line options
|
323
|
-
def cli_ops
|
324
|
-
options = {}
|
325
|
-
if ARGV.any?
|
326
|
-
begin
|
327
|
-
OptionParser.new do |op|
|
328
|
-
op.on("-s server") {|v| options[:server] = v }
|
329
|
-
op.on("-p port") {|v| options[:port] = v.to_i }
|
330
|
-
op.on("-n nick") {|v| options[:nick] = v }
|
331
|
-
op.on("-c command_prefix") {|v| options[:prefix] = v }
|
332
|
-
op.on("-v", "--verbose", "Enable verbose mode") {|v| options[:verbose] = true }
|
333
|
-
op.on("--ssl") {|v| options[:ssl] = true }
|
334
|
-
op.on("-C", "--channels x,y,z", Array, "Autojoin channels") {|v|
|
335
|
-
options[:channels] = v.map {|c| %w(# + &).include?(c[0].chr) ? c : c.insert(0, '#') }
|
336
|
-
}
|
337
|
-
end.parse(ARGV)
|
338
|
-
rescue OptionParser::MissingArgument => err
|
339
|
-
warn "Missing values for options: #{err.args.join(', ')}\nFalling back to default"
|
340
|
-
rescue OptionParser::InvalidOption => err
|
341
|
-
warn err.message
|
342
|
-
end
|
343
|
-
end
|
344
|
-
options
|
345
|
-
end
|
346
|
-
|
347
|
-
# Catch methods
|
348
|
-
def method_missing(meth, *args, &blk) # :nodoc:
|
349
|
-
if options.respond_to?(meth)
|
350
|
-
options.send(meth)
|
351
|
-
elsif @irc.respond_to?(meth)
|
352
|
-
@irc.send(meth, *args)
|
353
|
-
else
|
354
|
-
super
|
355
|
-
end
|
356
|
-
end
|
357
|
-
|
358
|
-
# Option management
|
359
|
-
class Options < Hash # :nodoc:
|
360
|
-
def initialize(&blk)
|
361
|
-
instance_eval(&blk) if block_given?
|
362
|
-
end
|
363
|
-
def method_missing(meth, *args, &blk)
|
364
|
-
self[meth] = args.first unless args.empty?
|
365
|
-
end
|
366
|
-
end
|
367
|
-
end
|
368
|
-
end
|