sclemmer-robut 0.5.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +7 -0
- data/.travis.yml +11 -0
- data/Gemfile +21 -0
- data/Gemfile.lock +65 -0
- data/README.rdoc +199 -0
- data/Rakefile +27 -0
- data/bin/robut +10 -0
- data/examples/Chatfile +31 -0
- data/examples/config.ru +13 -0
- data/lib/rexml_patches.rb +26 -0
- data/lib/robut/connection.rb +124 -0
- data/lib/robut/plugin/alias.rb +109 -0
- data/lib/robut/plugin/calc.rb +26 -0
- data/lib/robut/plugin/echo.rb +9 -0
- data/lib/robut/plugin/google_images.rb +17 -0
- data/lib/robut/plugin/help.rb +16 -0
- data/lib/robut/plugin/later.rb +81 -0
- data/lib/robut/plugin/lunch.rb +76 -0
- data/lib/robut/plugin/meme.rb +32 -0
- data/lib/robut/plugin/pick.rb +18 -0
- data/lib/robut/plugin/ping.rb +9 -0
- data/lib/robut/plugin/quips.rb +60 -0
- data/lib/robut/plugin/say.rb +23 -0
- data/lib/robut/plugin/sayings.rb +37 -0
- data/lib/robut/plugin/stock.rb +45 -0
- data/lib/robut/plugin/twss.rb +19 -0
- data/lib/robut/plugin/weather.rb +126 -0
- data/lib/robut/plugin.rb +201 -0
- data/lib/robut/pm.rb +40 -0
- data/lib/robut/presence.rb +39 -0
- data/lib/robut/room.rb +30 -0
- data/lib/robut/storage/base.rb +21 -0
- data/lib/robut/storage/hash_store.rb +26 -0
- data/lib/robut/storage/yaml_store.rb +57 -0
- data/lib/robut/storage.rb +3 -0
- data/lib/robut/version.rb +4 -0
- data/lib/robut/web.rb +26 -0
- data/lib/robut.rb +9 -0
- data/sclemmer-robut.gemspec +24 -0
- data/test/fixtures/bad_location.xml +1 -0
- data/test/fixtures/las_vegas.xml +1 -0
- data/test/fixtures/seattle.xml +1 -0
- data/test/fixtures/tacoma.xml +1 -0
- data/test/mocks/connection_mock.rb +22 -0
- data/test/mocks/presence_mock.rb +25 -0
- data/test/simplecov_helper.rb +2 -0
- data/test/test_helper.rb +9 -0
- data/test/unit/connection_test.rb +161 -0
- data/test/unit/plugin/alias_test.rb +76 -0
- data/test/unit/plugin/echo_test.rb +27 -0
- data/test/unit/plugin/help_test.rb +46 -0
- data/test/unit/plugin/later_test.rb +40 -0
- data/test/unit/plugin/lunch_test.rb +36 -0
- data/test/unit/plugin/pick_test.rb +35 -0
- data/test/unit/plugin/ping_test.rb +22 -0
- data/test/unit/plugin/quips_test.rb +58 -0
- data/test/unit/plugin/say_test.rb +34 -0
- data/test/unit/plugin/weather_test.rb +101 -0
- data/test/unit/plugin_test.rb +91 -0
- data/test/unit/room_test.rb +51 -0
- data/test/unit/storage/hash_store_test.rb +15 -0
- data/test/unit/storage/yaml_store_test.rb +47 -0
- data/test/unit/storage/yaml_test.yml +1 -0
- data/test/unit/web_test.rb +46 -0
- 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
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
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
|
data/examples/config.ru
ADDED
@@ -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
|