david-minibot 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2007 David Leal
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,4 @@
1
+ templates
2
+ =================
3
+
4
+ A plugin for the Merb framework that provides ....
data/Rakefile ADDED
@@ -0,0 +1,27 @@
1
+ require 'rubygems'
2
+ require 'rake/gempackagetask'
3
+ require 'spec/rake/spectask'
4
+
5
+ load 'minibot.gemspec'
6
+
7
+ Rake::GemPackageTask.new(SPEC) do |pkg|
8
+ pkg.gem_spec = SPEC
9
+ end
10
+
11
+ task :install => [:package] do
12
+ sh %{sudo gem install pkg/#{GEM}-#{VERSION}}
13
+ end
14
+
15
+ desc "Run all specs"
16
+ Spec::Rake::SpecTask.new('spec') do |t|
17
+ t.spec_files = FileList['spec/**/*.rb']
18
+ t.spec_opts = %w{--color}
19
+ end
20
+
21
+ desc "Run all specs with rcov"
22
+ Spec::Rake::SpecTask.new('spec:rcov') do |t|
23
+ t.spec_files = FileList['spec/**/*.rb']
24
+ t.rcov = true
25
+ t.rcov_opts = %w{--exclude spec --text-summary}
26
+ end
27
+
data/TODO ADDED
@@ -0,0 +1,14 @@
1
+ TODO:
2
+
3
+ * Add commands to:
4
+ * Reply to a private message
5
+ * Reply to a channel message
6
+ * Implement relevant parts of CTCP
7
+ * Discard events generated by the bot (parts, joins, others) (?)
8
+ * Handle PINGs
9
+ * Detect when using a registered nick.
10
+ * Add ability to reply both to public (by prefixing the username) and private messages
11
+ * Bot should reconnect upon disconnection
12
+ * Logging.
13
+ * Error handling.
14
+ * Signal trapping for graceful exiting.
@@ -0,0 +1,30 @@
1
+ require 'ostruct'
2
+
3
+ module MiniBot
4
+ module Commands
5
+ include Events::Constants
6
+
7
+ def join(*channels)
8
+ channels.each { |channel| server.write "JOIN #{channel}" }
9
+ end
10
+
11
+ def topic(channel)
12
+ topic = author = timestamp = nil
13
+ server.write "TOPIC #{channel}", *EXPECTED_REPLIES_TOPIC do |code, reply|
14
+ if code == RPL_TOPIC
15
+ topic = reply
16
+ elsif code == RPL_TOPIC_META
17
+ author, timestamp = *reply.split
18
+ end
19
+ end
20
+
21
+ [ topic, author, timestamp && Time.at(timestamp.to_i) ]
22
+ end
23
+
24
+ private
25
+
26
+ def pong
27
+ write "PONG #{@host_name}"
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,13 @@
1
+ module MiniBot
2
+ module Events
3
+ module Constants
4
+ RPL_WELCOME = "001"
5
+ RPL_NOTOPIC = "331"
6
+ RPL_TOPIC = "332"
7
+ RPL_TOPIC_META = "333"
8
+ ERR_NICKNAMEINUSE = "433"
9
+
10
+ EXPECTED_REPLIES_TOPIC = [ RPL_NOTOPIC, [ RPL_TOPIC, RPL_TOPIC_META ], RPL_TOPIC ]
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,55 @@
1
+ require 'socket'
2
+
3
+ module MiniBot
4
+ class Daemon
5
+ include Events
6
+ include Commands
7
+
8
+ attr_reader :config, :server
9
+
10
+ DEFAULTS = {
11
+ :port => 6667,
12
+ :channels => []
13
+ }
14
+
15
+ def run
16
+ begin
17
+ connect @config[:server], @config[:port]
18
+ authenticate @config[:nick], @config[:username], @config[:realname]
19
+ join *@config[:channels]
20
+ main_loop
21
+ ensure
22
+ disconnect
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def connect(server, port)
29
+ @server = Server.connect server, port
30
+ end
31
+
32
+ def disconnect
33
+ @server.disconnect
34
+ end
35
+
36
+ def initialize(config)
37
+ @config = DEFAULTS.merge config
38
+
39
+ @config[:username] ||= @config[:nick]
40
+ @config[:realname] ||= @config[:nick]
41
+ end
42
+
43
+ def main_loop
44
+ while msg = @server.read
45
+ dispatch msg
46
+ end
47
+ end
48
+
49
+ def authenticate(nick, username, realname)
50
+ @server.write "USER #{username} 0 xxx :#{realname}"
51
+ @server.write "NICK #{nick}"
52
+ end
53
+ end
54
+ end
55
+
@@ -0,0 +1,91 @@
1
+ module MiniBot
2
+ module Events
3
+ include Constants
4
+
5
+ def message(channel, sender, message)
6
+ end
7
+
8
+ def private_message(sender, message)
9
+ end
10
+
11
+ def user_joined(channel, nick)
12
+ end
13
+
14
+ def user_parted(channel, nick)
15
+ end
16
+
17
+ def user_action(channel, nick, message)
18
+ end
19
+
20
+ def invited(channel, nick)
21
+ end
22
+
23
+ def default(message_str)
24
+ end
25
+
26
+ def pinged
27
+ end
28
+
29
+ def topic_changed(channel, nick, topic)
30
+ end
31
+
32
+ def kicked(channel, nick, message)
33
+ end
34
+
35
+ def user_kicked(channel, kicker, kicked, message)
36
+ end
37
+
38
+ def ready
39
+ end
40
+
41
+ def error(num, message)
42
+ end
43
+
44
+ private
45
+
46
+ def dispatch(srv_msg)
47
+ if match = /^:(\w+)!.+ PRIVMSG (\S+) :([^\001].+)/.match(srv_msg)
48
+ target, origin, message = *match.values_at(2, 1, 3)
49
+ unless target == @nick
50
+ message target, origin, message
51
+ else
52
+ private_message origin, message
53
+ end
54
+ elsif match = /^:(\w+)!.+ JOIN :(\S+)/.match(srv_msg)
55
+ user_joined *match.values_at(2, 1)
56
+ elsif match = /^:(\w+)!.+ PART (\S+)/.match(srv_msg)
57
+ user_parted *match.values_at(2, 1)
58
+ elsif match = /^:(\w+)!.+ PRIVMSG (\S+) :\001ACTION (.+)\001/.match(srv_msg)
59
+ user_action *match.values_at(2, 1, 3)
60
+ elsif match = /^:(\w+)!.+ PRIVMSG #{@nick} :(.+)/.match(srv_msg)
61
+ private_message *match.values_at(1, 2)
62
+ elsif match = /^:(\w+)!.+ INVITE \w+ :(\S+)/.match(srv_msg)
63
+ invited *match.values_at(2, 1)
64
+ elsif match = /^PING/.match(srv_msg)
65
+ pinged
66
+ elsif match = /^:(\w+)!.+ TOPIC (\S+) :(.+)/.match(srv_msg)
67
+ topic_changed *match.values_at(2, 1, 3)
68
+ elsif match = /^:(\w+)!.+ KICK (\S+) #{@nick} :(.+)/.match(srv_msg)
69
+ kicked *match.values_at(2, 1, 3)
70
+ elsif match = /^:(\w+)!.+ KICK (\S+) (\w+) :(.+)/.match(srv_msg)
71
+ user_kicked *match.values_at(2, 1, 3, 4)
72
+ elsif match = /^:\S+ #{RPL_WELCOME} .*?(:.*)?$/.match(srv_msg)
73
+ ready
74
+ elsif match = /^:\S+ (\d{3}) \S+ (:.*)?$/.match(srv_msg)
75
+ code, msg = *match.values_at(1, 2)
76
+
77
+ if error?(code)
78
+ error code, msg.sub(/:/, '')
79
+ else
80
+ default(srv_msg)
81
+ end
82
+ else
83
+ default(srv_msg)
84
+ end
85
+ end
86
+
87
+ def error?(num)
88
+ return ("400" .. "599").include? num
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,121 @@
1
+ module MiniBot
2
+ class Server
3
+ REPLY_RE = /:\S+ (\d{3}) \S+ :?(.+)/
4
+
5
+ def self.connect(server, port)
6
+ s = new(server, port)
7
+ s.connect
8
+ s
9
+ end
10
+
11
+ def connect
12
+ @socket = TCPSocket.new(@server, @port)
13
+ end
14
+
15
+ def read
16
+ if @messages.first && message_complete?(@messages.first)
17
+ to_message(@messages.shift)
18
+ else
19
+ read_messages
20
+ read
21
+ end
22
+ end
23
+
24
+ def match_code(code, message)
25
+ if (match = reply?(message)) && match[1] == code
26
+ match[1, 2]
27
+ else
28
+ nil
29
+ end
30
+ end
31
+
32
+ def write(msg, *replies)
33
+ @socket.print "#{msg}\r\n"
34
+
35
+ unless replies.empty?
36
+ index = 0
37
+ matches, messages = catch :halted do
38
+ loop do
39
+ index, message = next_message(index)
40
+ replies.each do |r|
41
+ case r
42
+ when String
43
+ if match = /:\S+ (#{r}) \S+ :?(.+)/.match(message)
44
+ throw :halted, [[ match ], [ message ]]
45
+ end
46
+ when Array
47
+ matched = []
48
+ messages = []
49
+ r.each do |ri|
50
+ if match = /:\S+ (#{ri}) \S+ :?(.+)/.match(message)
51
+ matched << match
52
+ messages << message
53
+ index, message = next_message(index)
54
+ elsif !matched.empty?
55
+ index -= matched.length
56
+ message = matched.first
57
+ break
58
+ end
59
+ end
60
+
61
+ throw :halted, [matched, messages] unless matched.empty?
62
+ else
63
+ raise ArgumentError, "Unknown reply argument type: #{r.inspect}", caller
64
+ end
65
+ end
66
+ end
67
+ end
68
+
69
+ messages.each { |m| delete_message(m) }
70
+ matches.each { |m| yield m[1, 2] }
71
+ end
72
+ end
73
+
74
+ def extract(regexp)
75
+
76
+ end
77
+
78
+ def disconnect
79
+ @socket.close if @socket
80
+ end
81
+
82
+ private
83
+
84
+ def delete_message(m)
85
+ @messages.delete("#{m}\r")
86
+ end
87
+
88
+ def message_complete?(message)
89
+ message[-1] == ?\r
90
+ end
91
+
92
+ def to_message(raw)
93
+ raw.chomp
94
+ end
95
+
96
+ def next_message(index)
97
+ read_messages if !@messages[index] || !message_complete?(@messages[index])
98
+
99
+ [index + 1, to_message(@messages[index])]
100
+ end
101
+
102
+ def reply?(msg)
103
+ REPLY_RE.match msg
104
+ end
105
+
106
+ def initialize(server, port)
107
+ @server = server
108
+ @port = port
109
+ @messages = []
110
+ end
111
+
112
+ def read_messages
113
+ buffer = @socket.recvfrom(512).first
114
+ messages = buffer.split /\n/
115
+
116
+ @messages.last << messages.shift if @messages.last && @messages.last[-1] != ?\r
117
+
118
+ @messages += messages
119
+ end
120
+ end
121
+ end
data/lib/minibot.rb ADDED
@@ -0,0 +1,5 @@
1
+ require File.join(File.dirname(__FILE__), 'minibot', 'server')
2
+ require File.join(File.dirname(__FILE__), 'minibot', 'constants')
3
+ require File.join(File.dirname(__FILE__), 'minibot', 'events')
4
+ require File.join(File.dirname(__FILE__), 'minibot', 'commands')
5
+ require File.join(File.dirname(__FILE__), 'minibot', 'daemon')
metadata ADDED
@@ -0,0 +1,65 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: david-minibot
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - David Leal
8
+ autorequire: minibot
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-06-13 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: A mini Ruby IRC bot framework
17
+ email: dgleal@gmail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README
24
+ - LICENSE
25
+ - TODO
26
+ files:
27
+ - LICENSE
28
+ - README
29
+ - TODO
30
+ - Rakefile
31
+ - lib/minibot
32
+ - lib/minibot/commands.rb
33
+ - lib/minibot/daemon.rb
34
+ - lib/minibot/server.rb
35
+ - lib/minibot/events.rb
36
+ - lib/minibot/constants.rb
37
+ - lib/minibot.rb
38
+ has_rdoc: true
39
+ homepage: http://davidleal.com
40
+ post_install_message:
41
+ rdoc_options: []
42
+
43
+ require_paths:
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: "0"
50
+ version:
51
+ required_rubygems_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: "0"
56
+ version:
57
+ requirements: []
58
+
59
+ rubyforge_project:
60
+ rubygems_version: 1.0.1
61
+ signing_key:
62
+ specification_version: 2
63
+ summary: A mini Ruby IRC bot framework
64
+ test_files: []
65
+