summer 1.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) 2009 Ryan Bigg
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.
@@ -0,0 +1,96 @@
1
+ # Summer
2
+
3
+ Summer is an IRC Bot "framework" "inspired" by [http://github.com/RISCFuture/autumn](Autumn). Its goal is to be tiny.
4
+
5
+ ## Installation
6
+
7
+ The project is currently in "preview" state 'cause that's all the rage nowadays. No there's no invites, BUT everybody gets access to it.
8
+ Sorry to inform you that you'll have to clone it and then run `rake install` if you want to use it.
9
+
10
+ ## Usage
11
+
12
+ To use summer, create a file like this:
13
+
14
+ require 'rubygems'
15
+ require 'summer'
16
+
17
+ class Bot < Summer::Connection
18
+
19
+ end
20
+
21
+ Bot.new("localhost")
22
+
23
+ Running it will make your bot attempt to connect to the server on localhost. For those of you who do not have an IRC server running locally, I would suggest trying irc.freenode.net instead.
24
+
25
+ ## Configuration
26
+
27
+ In the same directory create a directory called _config_ and in that put _summer.yml_ which can have the following keys:
28
+
29
+ * nick: The nickname of the bot.
30
+ * channel: A channel to join on startup.
31
+ * channels: Channels to join on startup.
32
+ * auto_rejoin: Set this to true if you want the bot to re-join any channel it's kicked from.
33
+
34
+ ## `did_start_up`
35
+
36
+ Called when the bot has received the final MOTD line (376 or 422) and has finished joining all the channels.
37
+
38
+ ## `channel_message(sender, channel, message)`
39
+
40
+ Called when the bot receives a channel message.
41
+
42
+ sender (`Hash`): Contains `nick` and `hostname`
43
+ channel (`String`): The channel name: e.g. "#logga"
44
+ message (`String`): The message that was received
45
+
46
+ ## `private_message(sender, bot, message)`
47
+
48
+ Called when the bot receives a private message.
49
+
50
+ sender (`Hash`): Contains `nick` and `hostname`
51
+ bot (`String`): The bot's name.
52
+ message (`String`): The message that was received
53
+
54
+ ## `join(sender, channel)`
55
+
56
+ Called when the bot sees someone join a channel.
57
+
58
+ sender (`Hash`): Contains `nick` and `hostname`
59
+ channel (`String`): The channel name: e.g. "#logga"
60
+
61
+ ## `part(sender, channel, message)`
62
+
63
+ Called when someone parts a channel:
64
+
65
+ sender (`Hash`): Contains `nick` and `hostname`
66
+ channel (`String`): The channel name: e.g. "#logga"
67
+ message (`String`): The message that was received
68
+
69
+ ## `quit(sender, message)`
70
+
71
+ Called when someone quits the server:
72
+
73
+ sender (`Hash`): Contains `nick` and `hostname`
74
+ message (`String`): The message that was received.
75
+
76
+
77
+ ## `kick(kicker, channel, victim, message)`
78
+
79
+ Called when someone quits the server:
80
+
81
+ kicker (`Hash`): Contains `nick` and `hostname`
82
+ channel (`String`): The channel name: e.g. "#logga"
83
+ victim (`String`): Just the nick of whoever was kicked.
84
+ message (`String`): The message that was received.
85
+
86
+
87
+ ## Handling raw messages
88
+
89
+ If you wish to handle raw messages that come into your bot you can define a `handle_xxx` method for that where `xxx` is the three-digit representation of the raw you wish to handle.
90
+
91
+
92
+ ### Upcoming
93
+
94
+ `mode` method to detect mode changes
95
+ Auto-rejoin after kick (configuration options)
96
+
@@ -0,0 +1,5 @@
1
+ class Array
2
+ def clean
3
+ join(" ").clean
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class Object
2
+ def really_try(method, *args)
3
+ try(method, *args) if respond_to?(method)
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class String
2
+ def clean
3
+ gsub(/^:/, '')
4
+ end
5
+ end
@@ -0,0 +1,149 @@
1
+ require 'socket'
2
+ require 'yaml'
3
+ require 'active_support/hash_with_indifferent_access'
4
+ require 'active_support/core_ext/object/try'
5
+
6
+ Dir[File.dirname(__FILE__) + '/ext/*.rb'].each { |f| require f }
7
+
8
+ require File.dirname(__FILE__) + "/summer/handlers"
9
+
10
+ module Summer
11
+ class Connection
12
+ include Handlers
13
+ attr_accessor :connection, :ready, :started, :config, :server, :port
14
+ def initialize(server, port=6667, dry=false)
15
+ @ready = false
16
+ @started = false
17
+
18
+ @server = server
19
+ @port = port
20
+
21
+ load_config
22
+ connect!
23
+
24
+ unless dry
25
+ loop do
26
+ startup! if @ready && !@started
27
+ message = @connection.gets
28
+ if message
29
+ parse(message)
30
+ else
31
+ break
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ def load_config
40
+ @config = HashWithIndifferentAccess.new(YAML::load_file(File.dirname($0) + "/config/summer.yml"))
41
+ @config[:channels] ||= []
42
+ @config[:channels] << @config.delete(:channel) if @config[:channel]
43
+ end
44
+
45
+ def connect!
46
+ @connection = TCPSocket.open(server, port)
47
+ response("USER #{config[:nick]} #{config[:nick]} #{config[:nick]} #{config[:nick]}")
48
+ response("NICK #{config[:nick]}")
49
+ end
50
+
51
+
52
+ # Will join channels specified in configuration.
53
+ def startup!
54
+ nickserv_identify if @config[:nickserv_password]
55
+ config[:channels].each do |channel|
56
+ join(channel)
57
+ end
58
+ @started = true
59
+ really_try(:did_start_up) if respond_to?(:did_start_up)
60
+ end
61
+
62
+ def nickserv_identify
63
+ privmsg("nickserv", "register #{@config[:nickserv_password]} #{@config[:nickserv_email]}")
64
+ privmsg("nickserv", "identify #{@config[:nickserv_password]}")
65
+ end
66
+ # Go somewhere.
67
+ def join(channel)
68
+ response("JOIN #{channel}")
69
+ end
70
+
71
+ # Leave somewhere
72
+ def part(channel)
73
+ response("PART #{channel}")
74
+ end
75
+
76
+ # What did they say?
77
+ def parse(message)
78
+ puts "<< #{message.to_s.strip}"
79
+ words = message.split(" ")
80
+ sender = words[0]
81
+ raw = words[1]
82
+ channel = words[2]
83
+ # Handling pings
84
+ if /^PING (.*?)\s$/.match(message)
85
+ response("PONG #{$1}")
86
+ # Handling raws
87
+ elsif /\d+/.match(raw)
88
+ send("handle_#{raw}", message) if raws_to_handle.include?(raw)
89
+ # Privmsgs
90
+ elsif raw == "PRIVMSG"
91
+ message = words[3..-1].clean
92
+ # Parse commands
93
+ if /^!(\w+)\s*(.*)/.match(message) && respond_to?("#{$1}_command")
94
+ really_try("#{$1}_command", parse_sender(sender), channel, $2)
95
+ # Plain and boring message
96
+ else
97
+ sender = parse_sender(sender)
98
+ method, channel = channel == me ? [:private_message, sender[:nick]] : [:channel_message, channel]
99
+ really_try(method, sender, channel, message)
100
+ end
101
+ # Joins
102
+ elsif raw == "JOIN"
103
+ really_try(:join, parse_sender(sender), channel)
104
+ elsif raw == "PART"
105
+ really_try(:part, parse_sender(sender), channel, words[3..-1].clean)
106
+ elsif raw == "QUIT"
107
+ really_try(:quit, parse_sender(sender), words[2..-1].clean)
108
+ elsif raw == "KICK"
109
+ really_try(:kick, parse_sender(sender), channel, words[3], words[4..-1].clean)
110
+ join(channel) if words[3] == me && config[:auto_rejoin]
111
+ elsif raw == "MODE"
112
+ really_try(:mode, parse_sender(sender), channel, words[3], words[4..-1].clean)
113
+ elsif raw == "TOPIC"
114
+ really_try(:topic, parse_sender(sender), channel, words[3..-1].clean)
115
+ end
116
+
117
+ end
118
+
119
+ def parse_sender(sender)
120
+ nick, hostname = sender.split("!")
121
+ { :nick => nick.clean, :hostname => hostname }
122
+ end
123
+
124
+ # These are the raws we care about.
125
+ def raws_to_handle
126
+ ["422", "376"]
127
+ end
128
+
129
+ def privmsg(message, to)
130
+ response("PRIVMSG #{to} :#{message}")
131
+ end
132
+
133
+ # Output something to the console and to the socket.
134
+ def response(message)
135
+ puts ">> #{message.strip}"
136
+ @connection.puts(message)
137
+ end
138
+
139
+ def me
140
+ config[:nick]
141
+ end
142
+
143
+ def log(message)
144
+ File.open(config[:log_file]) { |file| file.write(message) } if config[:log_file]
145
+ end
146
+
147
+ end
148
+
149
+ end
@@ -0,0 +1,10 @@
1
+ module Summer
2
+ module Handlers
3
+ def handle_422(message)
4
+ self.ready = true
5
+ end
6
+
7
+ alias_method :handle_376, :handle_422
8
+
9
+ end
10
+ end
metadata ADDED
@@ -0,0 +1,101 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: summer
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Ryan Bigg
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-01-30 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: shoulda
16
+ requirement: &2153293060 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *2153293060
25
+ - !ruby/object:Gem::Dependency
26
+ name: rdoc
27
+ requirement: &2153292580 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: '3.12'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *2153292580
36
+ - !ruby/object:Gem::Dependency
37
+ name: bundler
38
+ requirement: &2153292100 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ version: 1.0.0
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *2153292100
47
+ - !ruby/object:Gem::Dependency
48
+ name: jeweler
49
+ requirement: &2153291620 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 1.8.3
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *2153291620
58
+ description: Tiny IRC Bot Frameowkr
59
+ email: radarlistener@gmail.com
60
+ executables: []
61
+ extensions: []
62
+ extra_rdoc_files:
63
+ - LICENSE
64
+ - README.markdown
65
+ files:
66
+ - lib/ext/array.rb
67
+ - lib/ext/object.rb
68
+ - lib/ext/string.rb
69
+ - lib/summer.rb
70
+ - lib/summer/handlers.rb
71
+ - LICENSE
72
+ - README.markdown
73
+ homepage: http://github.com/radar/summer
74
+ licenses:
75
+ - MIT
76
+ post_install_message:
77
+ rdoc_options: []
78
+ require_paths:
79
+ - lib
80
+ required_ruby_version: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ segments:
87
+ - 0
88
+ hash: -3637726515248804584
89
+ required_rubygems_version: !ruby/object:Gem::Requirement
90
+ none: false
91
+ requirements:
92
+ - - ! '>='
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ requirements: []
96
+ rubyforge_project:
97
+ rubygems_version: 1.8.11
98
+ signing_key:
99
+ specification_version: 3
100
+ summary: Tiny IRC Bot Framework
101
+ test_files: []