sclemmer-robut 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +7 -0
  3. data/.travis.yml +11 -0
  4. data/Gemfile +21 -0
  5. data/Gemfile.lock +65 -0
  6. data/README.rdoc +199 -0
  7. data/Rakefile +27 -0
  8. data/bin/robut +10 -0
  9. data/examples/Chatfile +31 -0
  10. data/examples/config.ru +13 -0
  11. data/lib/rexml_patches.rb +26 -0
  12. data/lib/robut/connection.rb +124 -0
  13. data/lib/robut/plugin/alias.rb +109 -0
  14. data/lib/robut/plugin/calc.rb +26 -0
  15. data/lib/robut/plugin/echo.rb +9 -0
  16. data/lib/robut/plugin/google_images.rb +17 -0
  17. data/lib/robut/plugin/help.rb +16 -0
  18. data/lib/robut/plugin/later.rb +81 -0
  19. data/lib/robut/plugin/lunch.rb +76 -0
  20. data/lib/robut/plugin/meme.rb +32 -0
  21. data/lib/robut/plugin/pick.rb +18 -0
  22. data/lib/robut/plugin/ping.rb +9 -0
  23. data/lib/robut/plugin/quips.rb +60 -0
  24. data/lib/robut/plugin/say.rb +23 -0
  25. data/lib/robut/plugin/sayings.rb +37 -0
  26. data/lib/robut/plugin/stock.rb +45 -0
  27. data/lib/robut/plugin/twss.rb +19 -0
  28. data/lib/robut/plugin/weather.rb +126 -0
  29. data/lib/robut/plugin.rb +201 -0
  30. data/lib/robut/pm.rb +40 -0
  31. data/lib/robut/presence.rb +39 -0
  32. data/lib/robut/room.rb +30 -0
  33. data/lib/robut/storage/base.rb +21 -0
  34. data/lib/robut/storage/hash_store.rb +26 -0
  35. data/lib/robut/storage/yaml_store.rb +57 -0
  36. data/lib/robut/storage.rb +3 -0
  37. data/lib/robut/version.rb +4 -0
  38. data/lib/robut/web.rb +26 -0
  39. data/lib/robut.rb +9 -0
  40. data/sclemmer-robut.gemspec +24 -0
  41. data/test/fixtures/bad_location.xml +1 -0
  42. data/test/fixtures/las_vegas.xml +1 -0
  43. data/test/fixtures/seattle.xml +1 -0
  44. data/test/fixtures/tacoma.xml +1 -0
  45. data/test/mocks/connection_mock.rb +22 -0
  46. data/test/mocks/presence_mock.rb +25 -0
  47. data/test/simplecov_helper.rb +2 -0
  48. data/test/test_helper.rb +9 -0
  49. data/test/unit/connection_test.rb +161 -0
  50. data/test/unit/plugin/alias_test.rb +76 -0
  51. data/test/unit/plugin/echo_test.rb +27 -0
  52. data/test/unit/plugin/help_test.rb +46 -0
  53. data/test/unit/plugin/later_test.rb +40 -0
  54. data/test/unit/plugin/lunch_test.rb +36 -0
  55. data/test/unit/plugin/pick_test.rb +35 -0
  56. data/test/unit/plugin/ping_test.rb +22 -0
  57. data/test/unit/plugin/quips_test.rb +58 -0
  58. data/test/unit/plugin/say_test.rb +34 -0
  59. data/test/unit/plugin/weather_test.rb +101 -0
  60. data/test/unit/plugin_test.rb +91 -0
  61. data/test/unit/room_test.rb +51 -0
  62. data/test/unit/storage/hash_store_test.rb +15 -0
  63. data/test/unit/storage/yaml_store_test.rb +47 -0
  64. data/test/unit/storage/yaml_test.yml +1 -0
  65. data/test/unit/web_test.rb +46 -0
  66. metadata +162 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 76b46a0e0add81cb61e96c4325b483eb30b9aa26
4
+ data.tar.gz: c1e8ab8df7ed607a16ecbd71492fbd621b5e9d51
5
+ SHA512:
6
+ metadata.gz: 0dec9cb846b2a9c9d52a559012de494719fa403dcbfa3aca3e0519d229e099325b3698f9ae4b39665b2b8391bf1e6a4e060c5e04db0bc60f585ac0e5026c4764
7
+ data.tar.gz: 071bf604bc1b899acbc8c9b2c25a6e63525cb6fd47526da1681a2b8b0fe68a392089515ff85df31eb729299fc981ede1a7361a2e0ad84e9db34a02c8e37bea8c
data/.gitignore ADDED
@@ -0,0 +1,7 @@
1
+ *.gem
2
+ .bundle
3
+ pkg/*
4
+ doc/*
5
+ coverage/*
6
+ *.swp
7
+ .DS_Store
data/.travis.yml ADDED
@@ -0,0 +1,11 @@
1
+ language: ruby
2
+ rvm:
3
+ # - 1.8.7
4
+ - 1.9.3
5
+ - 2.0
6
+ # - jruby-18mode # JRuby in 1.8 mode
7
+ # - jruby-19mode # JRuby in 1.9 mode
8
+ # - rbx-18mode
9
+ # - rbx-19mode # currently in active development, may or may not work for your project
10
+ # uncomment this line if your project needs to run something other than `rake`:
11
+ # script: bundle exec rspec spec
data/Gemfile ADDED
@@ -0,0 +1,21 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ gem 'rake'
6
+
7
+ group :test do
8
+ gem 'simplecov'
9
+ gem 'webmock'
10
+ gem 'time-warp'
11
+ gem 'mocha'
12
+ gem 'rack-test'
13
+ gem 'minitest'
14
+ end
15
+
16
+ group :plugin do
17
+ gem 'calc'
18
+ gem 'twss-classifier'
19
+ gem 'meme_generator'
20
+ gem 'nokogiri'
21
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,65 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ sclemmer-robut (0.5.2)
5
+ sinatra (~> 1.3)
6
+ xmpp4r (~> 0.5.0)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ addressable (2.3.5)
12
+ calc (1.0.0)
13
+ crack (0.4.1)
14
+ safe_yaml (~> 0.9.0)
15
+ docile (1.1.1)
16
+ meme_generator (1.9)
17
+ nokogiri (~> 1.4)
18
+ metaclass (0.0.1)
19
+ mini_portile (0.5.2)
20
+ minitest (5.5.0)
21
+ mocha (0.14.0)
22
+ metaclass (~> 0.0.1)
23
+ multi_json (1.8.2)
24
+ nokogiri (1.6.1)
25
+ mini_portile (~> 0.5.0)
26
+ rack (1.5.2)
27
+ rack-protection (1.5.3)
28
+ rack
29
+ rack-test (0.6.2)
30
+ rack (>= 1.0)
31
+ rake (10.1.1)
32
+ safe_yaml (0.9.7)
33
+ simplecov (0.8.2)
34
+ docile (~> 1.1.0)
35
+ multi_json
36
+ simplecov-html (~> 0.8.0)
37
+ simplecov-html (0.8.0)
38
+ sinatra (1.4.5)
39
+ rack (~> 1.4)
40
+ rack-protection (~> 1.4)
41
+ tilt (~> 1.3, >= 1.3.4)
42
+ tilt (1.4.1)
43
+ time-warp (1.0.13)
44
+ twss-classifier (0.0.1)
45
+ webmock (1.16.1)
46
+ addressable (>= 2.2.7)
47
+ crack (>= 0.3.2)
48
+ xmpp4r (0.5.6)
49
+
50
+ PLATFORMS
51
+ ruby
52
+
53
+ DEPENDENCIES
54
+ calc
55
+ meme_generator
56
+ minitest
57
+ mocha
58
+ nokogiri
59
+ rack-test
60
+ rake
61
+ sclemmer-robut!
62
+ simplecov
63
+ time-warp
64
+ twss-classifier
65
+ webmock
data/README.rdoc ADDED
@@ -0,0 +1,199 @@
1
+ = Robut
2
+
3
+ The friendly plugin-enabled HipChat bot.
4
+
5
+ == Installation and usage
6
+
7
+ Robut can be installed by running <tt>gem install robut</tt>. This
8
+ installs the +robut+ binary. When run, +robut+ reads a Chatfile,
9
+ connects to the specified HipChat server and chatroom, and feeds every
10
+ line said in the chatroom through the plugins configured by the
11
+ Chatfile.
12
+
13
+ Once robut is running, the plugins listen to what's being said in the
14
+ chatroom. Most plugins listen for @replies to robut:
15
+
16
+ @robut lunch? # => "Banh Mi!"
17
+ @robut calc 1 + 1 # => 2
18
+
19
+ Others listen to everything, and don't require an @reply.
20
+
21
+ Some of the included plugins require extra gems to be installed:
22
+
23
+ [Robut::Plugin::TWSS] requires the <tt>twss</tt> gem.
24
+ [Robut::Plugin::Calc] requires the <tt>calc</tt> gem.
25
+ [Robut::Plugin::Weather] requires the <tt>nokogiri</tt> gem.
26
+ [Robut::Plugin::GoogleImages] requires the <tt>google-search</tt> gem.
27
+
28
+ A list of known 3rd-party plugins is available on the wiki: https://github.com/justinweiss/robut/wiki/Robut-Plugins
29
+ Feel free to add your own creations!
30
+
31
+ == The Chatfile
32
+
33
+ When the +robut+ command runs, it looks for and evals ruby code in a
34
+ file called +Chatfile+ in the current directory. You can override the
35
+ configuration file by passing +robut+ a path to a Chatfile as the
36
+ first parameter:
37
+
38
+ robut /path/to/Chatfile
39
+
40
+ The Chatfile is just ruby code. A simple example can be found here: Chatfile[https://github.com/justinweiss/robut/blob/master/examples/Chatfile]
41
+
42
+ == Robut::Web
43
+
44
+ You can run Robut as a rack application, which allows it to accept
45
+ incoming post requests (e.g. Heroku deploy hooks, GitHub post-recieve hooks, etc.)
46
+ that can send messages to all connected rooms. Have a look at
47
+ config.ru[https://github.com/justinweiss/robut/blob/master/examples/config.ru] for an example.
48
+
49
+ === Adding and configuring plugins
50
+
51
+ Plugins are ruby classes, so enabling a plugin just requires requiring
52
+ the plugin file, optionally configuring the plugin class, and adding
53
+ the class to the global plugin list:
54
+
55
+ require 'robut/plugin/lunch'
56
+ Robut::Plugin::Lunch.places = ["Banh Mi", "Mad Oven", "Mod Pizza", "Taphouse"]
57
+ Robut::Plugin.plugins << Robut::Plugin::Lunch
58
+
59
+ Each plugin can be configured differently, or not at all. It's best to
60
+ look at the docs for the plugins you want to use to figure out what
61
+ kind of configuration they support.
62
+
63
+ Some plugins might require storage (like the `lunch` plugin). You can
64
+ configure the type of storage you want to use based on the need for
65
+ persistence. The default is the HashStore which is in-memory only. Below
66
+ is an example of using the YamlStore.
67
+
68
+ Robut::Connection.configure do |config|
69
+ # ...
70
+ Robut::Storage::YamlStore.file = "~/.robut_store"
71
+ config.store = Robut::Storage::YamlStore
72
+ end
73
+
74
+
75
+ === Configuring the HipChat connection
76
+
77
+ The Chatfile also configures the HipChat connection. This is done in a
78
+ Robut::Connection.configure block:
79
+
80
+ # Configure the robut jabber connection and you're good to go!
81
+ Robut::Connection.configure do |config|
82
+ config.jid = '...@chat.hipchat.com/bot'
83
+ config.password = 'password'
84
+ config.nick = 'Bot Nick'
85
+ config.rooms = ['1234_room_1@conf.hipchat.com', '1234_room_2@conf.hipchat.com']
86
+
87
+ # Custom @mention name
88
+ config.mention_name = 'Bot'
89
+
90
+ # Example of the YamlStore which uses a yaml file for persistence
91
+ Robut::Storage::YamlStore.file = "~/.robut_store"
92
+ config.store = Robut::Storage::YamlStore
93
+
94
+ # Add a logger if you want to debug the connection
95
+ # config.logger = Logger.new(STDOUT)
96
+ end
97
+
98
+ This block usually goes at the end of the Chatfile.
99
+
100
+ == Built-in plugins
101
+
102
+ Robut includes a few plugins that we've found useful:
103
+
104
+ [Robut::Plugin::Calc] a simple calculator.
105
+
106
+ @robut calc 1 + 1 # => 2
107
+
108
+
109
+ [Robut::Plugin::Lunch] a random decider for lunch locations.
110
+
111
+ @robut lunch? # => "Banh Mi!"
112
+
113
+
114
+ [Robut::Plugin::Meme] generates meme images using memecaptain.
115
+
116
+ @robut meme all_the_things drink; all the beer
117
+
118
+ [Robut::Plugin::GoogleImages] does a google image search for a query and returns the first result.
119
+
120
+ @robut image ship it
121
+
122
+ [Robut::Plugin::Sayings] a simple regex listener and responder.
123
+
124
+ You're the worst robot ever, @robut. # => I know.
125
+
126
+ [Robut::Plugin::TWSS] an interface to the TWSS gem. Listens to anything said in the chat room and responds "That's what she said!" where appropriate.
127
+
128
+ well hurry up, you're not going fast enough # => "That's what she said!"
129
+
130
+
131
+ [Robut::Plugin::Echo] echo back whatever it gets.
132
+
133
+ @robut echo hello world # => "hello world"
134
+
135
+
136
+ [Robut::Plugin::Say] invokes the "say" command (text-to-speech).
137
+
138
+ @robut say this rocks # (make sure robut is running on a machine with speakers :)
139
+
140
+
141
+ [Robut::Plugin::Ping] responds with "pong".
142
+
143
+ @robut ping # => "pong"
144
+
145
+
146
+ [Robut::Plugin::Later] performs the given command after waiting an arbitrary amount of time.
147
+
148
+ @robut in 5 minutes echo @justin wake up! # => (5 minutes later) "@justin wake up!"
149
+
150
+
151
+ [Robut::Plugin::Weather] uses Google Weather to fetch for the weather for a given location and day.
152
+
153
+ @robut seattle weather saturday? # => "Forecast for Seattle, WA on Sat: Sunny, High: 77F, Low: 55F"
154
+
155
+ [Robut::Plugin::Alias] creates aliases to other robut commands.
156
+
157
+ @robut alias "cowboy" "@robut play bon jovi wanted dead or alive" # Cuz somtimes you need it.
158
+ @robut alias w weather? # less typing for common stuff
159
+
160
+ [Robut::Plugin::Quips] stores and posts Bugzilla-style quips.
161
+
162
+ @robut add quip It was great when I wrote it!
163
+ @robut quip # => It was great when I wrote it!
164
+ @robut remove quip It was great when I wrote it!
165
+
166
+ [Robut::Plugin::Help] lists usage for all the plugins loaded into robut
167
+
168
+ @robut help # => command usage
169
+
170
+ == Writing custom plugins
171
+
172
+ You can supply your own plugins to Robut. To create a plugin, include
173
+ the Robut::Plugin module and implement the <tt>handle(time,
174
+ sender_nick, message)</tt> to perform any plugin-specific logic.
175
+
176
+ Robut::Plugin provides a few helper methods that are documented
177
+ in its class definition.
178
+
179
+ == Contributing
180
+
181
+ To test your changes:
182
+
183
+ 1. Install Bundler[http://gembundler.com]
184
+ 2. Run `bundle install`
185
+ 3. Make your changes and run `bundle exec ruby -Ilib bin/robut ~/MyTestingChatfile`
186
+ 4. Add tests and verify by running `bundle exec rake test`
187
+
188
+ Once your changes are ready:
189
+
190
+ 1. Fork robut
191
+ 2. Create a topic branch: `git checkout -b my_branch`
192
+ 3. Commit your changes
193
+ 4. Push to your branch: `git push origin my_branch`
194
+ 5. Send me a pull request
195
+ 6. That's it!
196
+
197
+ == Todo
198
+
199
+ * More plugins!
data/Rakefile ADDED
@@ -0,0 +1,27 @@
1
+ require 'rake/testtask'
2
+ require 'rdoc/task'
3
+ require 'bundler'
4
+ Bundler::GemHelper.install_tasks
5
+
6
+ task :default => :test
7
+ task :build => :test
8
+
9
+ Rake::TestTask.new do |t|
10
+ t.libs << "test"
11
+ t.test_files = FileList['test/**/*_test.rb']
12
+ t.verbose = true
13
+ end
14
+
15
+ Rake::TestTask.new(:coverage) do |t|
16
+ t.libs << "test"
17
+ t.ruby_opts = ["-rsimplecov_helper"]
18
+ t.test_files = FileList['test/**/*_test.rb']
19
+ t.verbose = true
20
+ end
21
+
22
+ Rake::RDocTask.new do |rd|
23
+ rd.main = "README.rdoc"
24
+ rd.rdoc_files.include("README.rdoc", "lib/**/*.rb")
25
+ rd.rdoc_dir = 'doc'
26
+ end
27
+
data/bin/robut ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'robut'
4
+ require 'ostruct'
5
+ require 'logger'
6
+
7
+ load ARGV[0] || './Chatfile'
8
+
9
+ Robut::Connection.new.connect
10
+ loop { sleep 1 }
data/examples/Chatfile ADDED
@@ -0,0 +1,31 @@
1
+ # Require your plugins here
2
+ require 'robut/plugin/twss'
3
+ require 'robut/storage/yaml_store'
4
+
5
+ # Add the plugin classes to the Robut plugin list.
6
+ # Plugins are handled in the order that they appear in this array.
7
+ Robut::Plugin.plugins << Robut::Plugin::TWSS
8
+
9
+ # Configure the robut jabber connection and you're good to go!
10
+ Robut::Connection.configure do |config|
11
+ # Note that the jid must end with /bot if you don't want robut to
12
+ # spam the channel, as described by the last bullet point on this
13
+ # page: https://www.hipchat.com/help/category/xmpp
14
+ config.jid = '...@chat.hipchat.com/bot'
15
+ config.password = 'password'
16
+ config.nick = 'Bot Nick'
17
+ config.room = '...@conf.hipchat.com'
18
+
19
+ # Custom @mention name
20
+ # config.mention_name = 'Bot'
21
+
22
+ # Ignore personal messages
23
+ # config.enable_private_messaging = false
24
+
25
+ # Some plugins require storage
26
+ Robut::Storage::YamlStore.file = ".robut"
27
+ config.store = Robut::Storage::YamlStore
28
+
29
+ # Add a logger if you want to debug the connection
30
+ # config.logger = Logger.new(STDOUT)
31
+ end
@@ -0,0 +1,13 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ Bundler.require :default
4
+
5
+ require 'robut'
6
+ require 'ostruct'
7
+ require 'logger'
8
+
9
+ load './Chatfile'
10
+
11
+ Robut::Web.set :connection, Robut::Connection.new.connect
12
+
13
+ run Robut::Web
@@ -0,0 +1,26 @@
1
+ # Encoding patch, stolen from https://github.com/ln/xmpp4r/issues/3#issuecomment-1739952
2
+ require 'socket'
3
+ class TCPSocket
4
+ def external_encoding
5
+ Encoding::BINARY
6
+ end
7
+ end
8
+
9
+ require 'rexml/source'
10
+ class REXML::IOSource
11
+ alias_method :encoding_assign, :encoding=
12
+ def encoding=(value)
13
+ encoding_assign(value) if value
14
+ end
15
+ end
16
+
17
+ begin
18
+ # OpenSSL is optional and can be missing
19
+ require 'openssl'
20
+ class OpenSSL::SSL::SSLSocket
21
+ def external_encoding
22
+ Encoding::BINARY
23
+ end
24
+ end
25
+ rescue
26
+ end
@@ -0,0 +1,124 @@
1
+ require 'xmpp4r'
2
+ require 'xmpp4r/muc/helper/simplemucclient'
3
+ require 'xmpp4r/roster/helper/roster'
4
+ require 'ostruct'
5
+
6
+ if defined?(Encoding)
7
+ # Monkey-patch an incompatibility between ejabberd and rexml
8
+ require 'rexml_patches'
9
+ end
10
+
11
+ # Handles opening a connection to the HipChat server, and feeds all
12
+ # messages through our Robut::Plugin list.
13
+ class Robut::Connection
14
+
15
+ # The configuration used by the Robut connection.
16
+ #
17
+ # Parameters:
18
+ #
19
+ # [+jid+, +password+, +nick+] The HipChat credentials given on
20
+ # https://www.hipchat.com/account/xmpp
21
+ #
22
+ # [+rooms+] The chat room(s) to join, with each in the format <tt>jabber_name</tt>@<tt>conference_server</tt>
23
+ #
24
+ # [+logger+] a logger instance to use for debug output.
25
+ attr_accessor :config
26
+
27
+ # The Jabber::Client that's connected to the HipChat server.
28
+ attr_accessor :client
29
+
30
+ # The storage instance that's available to plugins
31
+ attr_accessor :store
32
+
33
+ # The roster of currently available people
34
+ attr_accessor :roster
35
+
36
+ # The rooms that robut is connected to.
37
+ attr_accessor :rooms
38
+
39
+ class << self
40
+ # Class-level config. This is set by the +configure+ class method,
41
+ # and is used if no configuration is passed to the +initialize+
42
+ # method.
43
+ attr_accessor :config
44
+ end
45
+
46
+ # Configures the connection at the class level. When the +robut+ bin
47
+ # file is loaded, it evals the file referenced by the first
48
+ # command-line parameter. This file can configure the connection
49
+ # instance later created by +robut+ by setting parameters in the
50
+ # Robut::Connection.configure block.
51
+ def self.configure
52
+ self.config = OpenStruct.new
53
+ yield config
54
+ end
55
+
56
+ # Sets the instance config to +config+, converting it into an
57
+ # OpenStruct if necessary.
58
+ def config=(config)
59
+ @config = config.kind_of?(Hash) ? OpenStruct.new(config) : config
60
+ end
61
+
62
+ # Initializes the connection. If no +config+ is passed, it defaults
63
+ # to the class_level +config+ instance variable.
64
+ def initialize(_config = nil)
65
+ self.config = _config || self.class.config
66
+
67
+ self.client = Jabber::Client.new(self.config.jid)
68
+ self.store = self.config.store || Robut::Storage::HashStore # default to in-memory store only
69
+ self.config.rooms ||= Array(self.config.room) # legacy support?
70
+ self.config.enable_private_messaging = true if self.config.enable_private_messaging.nil?
71
+
72
+ if self.config.logger
73
+ Jabber.logger = self.config.logger
74
+ Jabber.debug = true
75
+ end
76
+ end
77
+
78
+ # Connects to the specified room with the given credentials, and
79
+ # enters an infinite loop. Any messages sent to the room will pass
80
+ # through all the included plugins.
81
+ def connect
82
+ client.connect
83
+ client.auth(config.password)
84
+ client.send(Jabber::Presence.new.set_type(:available))
85
+
86
+ self.roster = Jabber::Roster::Helper.new(client)
87
+ roster.wait_for_roster
88
+
89
+ self.rooms = self.config.rooms.collect do |room_name|
90
+ Robut::Room.new(self, room_name).tap {|r| r.join }
91
+ end
92
+
93
+ if self.config.enable_private_messaging
94
+ Robut::PM.new(self, rooms)
95
+ end
96
+
97
+ trap_signals
98
+ self
99
+ end
100
+
101
+ # Send a message to all rooms.
102
+ def reply(*args, &block)
103
+ self.rooms.each do |room|
104
+ room.reply(*args, &block)
105
+ end
106
+ end
107
+
108
+ private
109
+ # Since we're entering an infinite loop, we have to trap TERM and
110
+ # INT. If something like the Rdio plugin has started a server that
111
+ # has already trapped those signals, we want to run those signal
112
+ # handlers first.
113
+ def trap_signals
114
+ old_signal_callbacks = {}
115
+ signal_callback = Proc.new do |signal|
116
+ old_signal_callbacks[signal].call if old_signal_callbacks[signal]
117
+ exit
118
+ end
119
+
120
+ [:INT, :TERM].each do |sig|
121
+ old_signal_callbacks[sig] = trap(sig) { signal_callback.call(sig) }
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,109 @@
1
+ require 'shellwords'
2
+
3
+ # Alias robut commands:
4
+ #
5
+ # @robut alias "something" "some long message"
6
+ #
7
+ # Later if @robut receives the message "@robut something" he will
8
+ # repond as if he received "@robut some long message"
9
+ #
10
+ #
11
+ # Valid use:
12
+ #
13
+ # @robut alias this "something long"
14
+ # @robut alias "this thing" "something long"
15
+ # @robut alias this something_long
16
+ #
17
+ # Listing all aliases
18
+ #
19
+ # @robut aliases
20
+ #
21
+ # Removing aliases
22
+ #
23
+ # @robut remove alias "this alias"
24
+ # @robut remove alias this
25
+ # @robut remove clear aliases # removes everything
26
+ #
27
+ # Note: you probably want the Alias plugin as one of the first things
28
+ # in the plugin array (since plugins are executed in order).
29
+ class Robut::Plugin::Alias
30
+ include Robut::Plugin
31
+
32
+ # Returns a description of how to use this plugin
33
+ def usage
34
+ [
35
+ "#{at_nick} alias <words> <expansion> - when #{nick} sees <words>, pretend it saw <expansion> instead",
36
+ "#{at_nick} aliases - show all aliases",
37
+ "#{at_nick} remove alias <words> - remove <words> as an alias",
38
+ "#{at_nick} clear aliases - remove all aliases"
39
+ ]
40
+ end
41
+
42
+ # Perform the calculation specified in +message+, and send the
43
+ # result back.
44
+ def handle(time, sender_nick, message)
45
+ if new_message = get_alias(message)
46
+ # Apply the alias
47
+ fake_message Time.now, sender_nick, new_message
48
+ elsif sent_to_me?(message)
49
+ message = without_nick message
50
+ if message =~ /^remove alias (.*)/
51
+ # Remove the alias
52
+ key = parse_alias_key($1)
53
+ remove_alias key
54
+ return true
55
+ elsif message =~ /^clear aliases$/
56
+ self.aliases = {}
57
+ return true
58
+ elsif message =~ /^alias (.*)/
59
+ # Create a new alias
60
+ message = $1
61
+ key, value = parse_alias message
62
+ store_alias key, value
63
+ return true # hault plugin execution chain
64
+ elsif words(message).first == 'aliases'
65
+ # List all aliases
66
+ m, a = [], aliases # create reference to avoid going to the store every time
67
+ a.keys.sort.each { |key| m << "#{key} => #{a[key]}" }
68
+ reply m.join("\n")
69
+ return true
70
+ end
71
+ end
72
+ end
73
+
74
+ # Given a message, returns what it is aliased to (or nil)
75
+ def get_alias(msg)
76
+ (store['aliases'] || {})[msg]
77
+ end
78
+
79
+ def store_alias(key, value)
80
+ aliases[key] = value
81
+ store['aliases'] = aliases
82
+ end
83
+
84
+ def remove_alias(key)
85
+ new_aliases = aliases
86
+ new_aliases.delete(key)
87
+ store['aliases'] = new_aliases
88
+ end
89
+
90
+ def aliases
91
+ store['aliases'] ||= {}
92
+ end
93
+
94
+ def aliases=(v)
95
+ store['aliases'] = v
96
+ end
97
+
98
+ # Returns alias and command
99
+ def parse_alias(str)
100
+ r = Shellwords.shellwords str
101
+ return r[0], r[1] if r.length == 2
102
+ return r[0], r[1..-1].join(' ')
103
+ end
104
+
105
+ def parse_alias_key(str)
106
+ Shellwords.shellwords(str).join(' ')
107
+ end
108
+
109
+ end
@@ -0,0 +1,26 @@
1
+ require 'calc'
2
+
3
+ # A simple calculator. This delegates all calculations to the 'calc'
4
+ # gem.
5
+ class Robut::Plugin::Calc
6
+ include Robut::Plugin
7
+
8
+ # Returns a description of how to use this plugin
9
+ def usage
10
+ "#{at_nick} calc <calculation> - replies with the result of <calculation>"
11
+ end
12
+
13
+ # Perform the calculation specified in +message+, and send the
14
+ # result back.
15
+ def handle(time, sender_nick, message)
16
+ if sent_to_me?(message) && words(message).first == 'calc'
17
+ calculation = words(message, 'calc').join(' ')
18
+ begin
19
+ reply("#{calculation} = #{::Calc.evaluate(calculation)}")
20
+ rescue
21
+ reply("Can't calculate that.")
22
+ end
23
+ end
24
+ end
25
+
26
+ end
@@ -0,0 +1,9 @@
1
+ # A plugin that tells robut to repeat whatever he's told.
2
+ class Robut::Plugin::Echo
3
+ include Robut::Plugin
4
+
5
+ desc "echo <message> - replies to the channel with <message>"
6
+ match /^echo (.*)/, :sent_to_me => true do |phrase|
7
+ reply(phrase) unless phrase.empty?
8
+ end
9
+ end