sclemmer-robut 0.5.2

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.
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