adhearsion-asterisk 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. data/.gitignore +10 -0
  2. data/.rspec +3 -0
  3. data/CHANGELOG.md +14 -0
  4. data/Gemfile +6 -0
  5. data/Guardfile +5 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +143 -0
  8. data/Rakefile +23 -0
  9. data/adhearsion-asterisk.gemspec +35 -0
  10. data/lib/adhearsion-asterisk.rb +1 -0
  11. data/lib/adhearsion/asterisk.rb +12 -0
  12. data/lib/adhearsion/asterisk/config_generator.rb +103 -0
  13. data/lib/adhearsion/asterisk/config_generator/agents.rb +138 -0
  14. data/lib/adhearsion/asterisk/config_generator/queues.rb +247 -0
  15. data/lib/adhearsion/asterisk/config_generator/voicemail.rb +238 -0
  16. data/lib/adhearsion/asterisk/config_manager.rb +60 -0
  17. data/lib/adhearsion/asterisk/plugin.rb +464 -0
  18. data/lib/adhearsion/asterisk/queue_proxy.rb +177 -0
  19. data/lib/adhearsion/asterisk/queue_proxy/agent_proxy.rb +81 -0
  20. data/lib/adhearsion/asterisk/queue_proxy/queue_agents_list_proxy.rb +132 -0
  21. data/lib/adhearsion/asterisk/version.rb +5 -0
  22. data/spec/adhearsion/asterisk/config_generators/agents_spec.rb +258 -0
  23. data/spec/adhearsion/asterisk/config_generators/queues_spec.rb +322 -0
  24. data/spec/adhearsion/asterisk/config_generators/voicemail_spec.rb +306 -0
  25. data/spec/adhearsion/asterisk/config_manager_spec.rb +125 -0
  26. data/spec/adhearsion/asterisk/plugin_spec.rb +618 -0
  27. data/spec/adhearsion/asterisk/queue_proxy/agent_proxy_spec.rb +90 -0
  28. data/spec/adhearsion/asterisk/queue_proxy/queue_agents_list_proxy_spec.rb +145 -0
  29. data/spec/adhearsion/asterisk/queue_proxy_spec.rb +156 -0
  30. data/spec/adhearsion/asterisk_spec.rb +9 -0
  31. data/spec/spec_helper.rb +23 -0
  32. data/spec/support/the_following_code.rb +3 -0
  33. metadata +229 -0
@@ -0,0 +1,322 @@
1
+ require 'spec_helper'
2
+ require 'adhearsion/asterisk/config_generator/queues'
3
+
4
+ module QueuesConfigGeneratorTestHelper
5
+
6
+ def reset_queues!
7
+ @queues = Adhearsion::Asterisk::ConfigGenerator::Queues.new
8
+ end
9
+
10
+ def generated_config_should_have_pair(pair)
11
+ generated_config_has_pair(pair).should be true
12
+ end
13
+
14
+ def generated_config_has_pair(pair)
15
+ queues.to_s.split("\n").grep(/=[^>]/).each do |line|
16
+ key, value = line.strip.split('=')
17
+ return true if pair == {key.to_sym => value}
18
+ end
19
+ false
20
+ end
21
+ end
22
+
23
+ describe "The queues.conf config file generator" do
24
+
25
+ include QueuesConfigGeneratorTestHelper
26
+
27
+ attr_reader :queues
28
+ before(:each) do
29
+ reset_queues!
30
+ end
31
+
32
+ it 'should set autofill=yes by default' do
33
+ generated_config_should_have_pair :autofill => 'yes'
34
+ end
35
+
36
+ it 'should have a [general] section' do
37
+ queues.conf.should include "[general]\n"
38
+ end
39
+
40
+ it 'should yield a Queues object in its constructor' do
41
+ Adhearsion::Asterisk::ConfigGenerator::Queues.new do |config|
42
+ config.should be_a_kind_of Adhearsion::Asterisk::ConfigGenerator::Queues
43
+ end
44
+ end
45
+
46
+ it 'should add the warning message to the to_s output' do
47
+ queues.conf.should =~ /^\s*;.{10}/
48
+ end
49
+
50
+ end
51
+
52
+ describe "The queues.conf config file queues's QueueDefinition" do
53
+
54
+ include QueuesConfigGeneratorTestHelper
55
+
56
+ attr_reader :queues
57
+ before(:each) do
58
+ reset_queues!
59
+ end
60
+
61
+ it 'should include [queue_name]' do
62
+ name_of_queue = "leet_hax0rz"
63
+ queues.queue(name_of_queue).to_s.should include "[#{name_of_queue}]"
64
+ end
65
+
66
+ it '#member should create a valid Agent "channel driver" to the member definition list' do
67
+ sample_queue = queues.queue "sales" do |sales|
68
+ sales.member 123
69
+ sales.member "Jay"
70
+ sales.member 'SIP/jay-desk-650'
71
+ sales.member 'IAX2/12345@voipms/15554443333'
72
+ end
73
+ sample_queue.members.should == %w[Agent/123 Agent/Jay SIP/jay-desk-650 IAX2/12345@voipms/15554443333]
74
+ end
75
+
76
+ it 'should automatically enable the two AMI-related events' do
77
+ @queues = queues.queue 'name'
78
+ generated_config_should_have_pair :eventwhencalled => 'vars'
79
+ generated_config_should_have_pair :eventmemberstatus => 'yes'
80
+ end
81
+
82
+ it '#strategy should only allow the pre-defined settings' do
83
+ [:ringall, :roundrobin, :leastrecent, :fewestcalls, :random, :rrmemory].each do |strategy|
84
+ the_following_code {
85
+ q = queues.queue 'foobar'
86
+ q.strategy strategy
87
+ }.should_not raise_error
88
+ end
89
+
90
+ the_following_code {
91
+ queues.queue('qwerty').strategy :this_is_not_a_valid_strategy
92
+ }.should raise_error ArgumentError
93
+
94
+ end
95
+
96
+ it '#sound_files raises an argument error when it sees an unrecognized key' do
97
+ the_following_code {
98
+ queues.queue 'foobar' do |foobar|
99
+ foobar.sound_files \
100
+ :you_are_next => rand.to_s,
101
+ :there_are => rand.to_s,
102
+ :calls_waiting => rand.to_s,
103
+ :hold_time => rand.to_s,
104
+ :minutes => rand.to_s,
105
+ :seconds => rand.to_s,
106
+ :thank_you => rand.to_s,
107
+ :less_than => rand.to_s,
108
+ :report_hold => rand.to_s,
109
+ :periodic_announcement => rand.to_s
110
+ end
111
+ }.should_not raise_error
112
+
113
+ [:x_you_are_next, :x_there_are, :x_calls_waiting, :x_hold_time, :x_minutes,
114
+ :x_seconds, :x_thank_you, :x_less_than, :x_report_hold, :x_periodic_announcement].each do |bad_key|
115
+ the_following_code {
116
+ queues.queue("foobar") do |foobar|
117
+ foobar.sound_files bad_key => rand.to_s
118
+ end
119
+ }.should raise_error ArgumentError
120
+ end
121
+
122
+ end
123
+
124
+ end
125
+
126
+ describe "The private, helper methods in QueueDefinition" do
127
+
128
+ include QueuesConfigGeneratorTestHelper
129
+
130
+ attr_reader :queue
131
+ before(:each) do
132
+ reset_queues!
133
+ @queue = @queues.queue "doesn't matter"
134
+ end
135
+
136
+ it '#boolean should convert a boolean into "yes" or "no"' do
137
+ mock_of_properties = mock "mock of the properties instance variable of a QueueDefinition"
138
+ mock_of_properties.expects(:[]=).once.with("icanhascheezburger", "yes")
139
+ queue.expects(:properties).returns mock_of_properties
140
+ queue.send(:boolean, "icanhascheezburger" => true)
141
+ end
142
+
143
+ it '#int should raise an argument error when its argument is not Numeric' do
144
+ the_following_code {
145
+ queue.send(:int, "eisley" => :i_break_things!)
146
+ }.should raise_error ArgumentError
147
+ end
148
+
149
+ it '#int should coerce a String into a Numeric if possible' do
150
+ mock_of_properties = mock "mock of the properties instance variable of a QueueDefinition"
151
+ mock_of_properties.expects(:[]=).once.with("chimpanzee", 1337)
152
+ queue.expects(:properties).returns mock_of_properties
153
+ queue.send(:int, "chimpanzee" => "1337")
154
+ end
155
+
156
+ it '#string should add the argument directly to the properties' do
157
+ mock_of_properties = mock "mock of the properties instance variable of a QueueDefinition"
158
+ mock_of_properties.expects(:[]=).once.with("eins", "zwei")
159
+ queue.expects(:properties).returns mock_of_properties
160
+ queue.send(:string, "eins" => "zwei")
161
+ end
162
+
163
+ it '#one_of() should add its successful match to the properties attribute' do
164
+ mock_properties = mock "mock of the properties instance variable of a QueueDefinition"
165
+ mock_properties.expects(:[]=).once.with(:doesnt_matter, 5)
166
+ queue.expects(:properties).once.returns mock_properties
167
+ queue.send(:one_of, 1..100, :doesnt_matter => 5)
168
+ end
169
+
170
+ it "one_of() should convert booleans to yes/no" do
171
+ mock_properties = mock "mock of the properties instance variable of a QueueDefinition"
172
+ mock_properties.expects(:[]=).once.with(:doesnt_matter, 'yes')
173
+ queue.expects(:properties).once.returns mock_properties
174
+ queue.send(:one_of, [true, false, :strict], :doesnt_matter => true)
175
+
176
+ mock_properties = mock "mock of the properties instance variable of a QueueDefinition"
177
+ mock_properties.expects(:[]=).once.with(:doesnt_matter, :strict)
178
+ queue.expects(:properties).once.returns mock_properties
179
+ queue.send(:one_of, [true, false, :strict], :doesnt_matter => :strict)
180
+
181
+ mock_properties = mock "mock of the properties instance variable of a QueueDefinition"
182
+ mock_properties.expects(:[]=).once.with(:doesnt_matter, 'no')
183
+ queue.expects(:properties).once.returns mock_properties
184
+ queue.send(:one_of, [true, false, :strict], :doesnt_matter => false)
185
+ end
186
+
187
+ it '#one_of() should raise an ArgumentError if a value is not in the criteria' do
188
+ the_following_code {
189
+ queue.send(:one_of, [:jay, :thomas, :phillips], :sister => :jill)
190
+ }.should raise_error ArgumentError
191
+ end
192
+ end
193
+
194
+ describe 'The queues.conf config file generator when ran against a really big example' do
195
+
196
+ include QueuesConfigGeneratorTestHelper
197
+
198
+ attr_reader :queues, :default_config
199
+ before(:each) do
200
+ reset_queues!
201
+ @default_config = default_config = <<-CONFIG
202
+ [general]
203
+ persistentmembers=yes
204
+ autofill=yes
205
+ monitor-type=MixMonitor
206
+
207
+ [markq]
208
+ musicclass=default
209
+ announce=queue-markq
210
+ strategy=ringall
211
+ servicelevel=60
212
+ context=qoutcon
213
+ timeout=15
214
+ retry=5
215
+ weight=0
216
+ wrapuptime=15
217
+ autofill=yes
218
+ autopause=yes
219
+ maxlen=0
220
+ setinterfacevar=yes
221
+ announce-frequency=90
222
+ periodic-announce-frequency=60
223
+ announce-holdtime=once
224
+ announce-round-seconds=10
225
+ queue-youarenext=queue-youarenext
226
+ queue-thereare=queue-thereare
227
+ queue-callswaiting=queue-callswaiting
228
+ queue-holdtime=queue-holdtime
229
+ queue-minutes=queue-minutes
230
+ queue-seconds=queue-seconds
231
+ queue-thankyou=queue-thankyou
232
+ queue-lessthan=queue-less-than
233
+ queue-reporthold=queue-reporthold
234
+ periodic-announce=queue-periodic-announce
235
+ monitor-format=gsm
236
+ monitor-type=MixMonitor
237
+ joinempty=yes
238
+ leavewhenempty=yes
239
+ eventwhencalled=vars
240
+ eventmemberstatus=yes
241
+ reportholdtime=no
242
+ ringinuse=no
243
+ memberdelay=0
244
+ timeoutrestart=no
245
+
246
+ member => Zap/1
247
+ member => Agent/007
248
+ CONFIG
249
+ end
250
+
251
+ it "a sample config with multiple queues" do
252
+
253
+ generated = Adhearsion::Asterisk::ConfigGenerator::Queues.new do |config|
254
+ config.persistent_members true
255
+ config.monitor_type :mix_monitor
256
+
257
+ config.queue 'markq' do |markq|
258
+ markq.music_class :default
259
+ markq.play_on_connect 'queue-markq'
260
+ markq.strategy :ringall
261
+ markq.service_level 60
262
+ markq.exit_to_context_on_digit_press 'qoutcon'
263
+ markq.ring_timeout 15
264
+ markq.retry_after_waiting 5
265
+ markq.weight 0
266
+ markq.wrapup_time 15
267
+ markq.autopause true
268
+ markq.maximum_length 0
269
+ markq.queue_status_announce_frequency 90
270
+ markq.announce_hold_time :once
271
+ markq.announce_round_seconds 10
272
+ markq.sound_files \
273
+ :you_are_next => "queue-youarenext",
274
+ :there_are => "queue-thereare",
275
+ :calls_waiting => "queue-callswaiting",
276
+ :hold_time => "queue-holdtime",
277
+ :minutes => "queue-minutes",
278
+ :seconds => "queue-seconds",
279
+ :thank_you => "queue-thankyou",
280
+ :less_than => "queue-less-than",
281
+ :report_hold => "queue-reporthold"
282
+ markq.periodically_announce "queue-periodic-announce"
283
+ markq.monitor_format :gsm
284
+ markq.monitor_type :mix_monitor
285
+ markq.join_empty true
286
+ markq.leave_when_empty true
287
+ markq.report_hold_time false
288
+ markq.ring_in_use false
289
+ markq.delay_connection_by 0
290
+ markq.timeout_restart false
291
+
292
+ markq.member 'Zap/1'
293
+ markq.member '007'
294
+ end
295
+ end
296
+
297
+ cleaned_up_default_config =
298
+ Adhearsion::Asterisk::ConfigGenerator.create_sanitary_hash_from(default_config)
299
+
300
+ cleaned_up_generated_config = generated.to_sanitary_hash
301
+
302
+ cleaned_up_generated_config.should == cleaned_up_default_config
303
+ end
304
+
305
+ end
306
+
307
+ describe "ConfigGeneratorTestHelper" do
308
+
309
+ include QueuesConfigGeneratorTestHelper
310
+
311
+ attr_reader :queues
312
+
313
+ it "generated_config_has_pair() works properly" do
314
+ @queues = mock "A fake queues with just one pair", :to_s => "foo=bar"
315
+ generated_config_has_pair(:foo => "bar").should be true
316
+
317
+ @queues = mock "A fake queues with just one pair"
318
+ @queues.expects(:to_s).twice.returns("[general]\n\nqaz=qwerty\nagent => 1,2,3")
319
+ generated_config_has_pair(:qaz => "qwerty").should be true
320
+ generated_config_has_pair(:foo => "bar").should be false
321
+ end
322
+ end
@@ -0,0 +1,306 @@
1
+ require 'spec_helper'
2
+ require 'adhearsion/asterisk/config_generator/voicemail'
3
+
4
+ describe 'Basic requirements of the Voicemail config generator' do
5
+ attr_reader :config
6
+ before :each do
7
+ @config = Adhearsion::Asterisk::ConfigGenerator::Voicemail.new
8
+ end
9
+
10
+ it 'should have a [general] context' do
11
+ config.to_sanitary_hash.has_key?('[general]').should be true
12
+ end
13
+
14
+ it 'should set the format to "wav" by default in the general section' do
15
+ config.to_sanitary_hash['[general]'].should include 'format=wav'
16
+ end
17
+
18
+ it 'an exception should be raised if the context name is "general"' do
19
+ the_following_code {
20
+ config.context(:general) {|_|}
21
+ }.should raise_error ArgumentError
22
+ end
23
+
24
+ end
25
+
26
+ describe 'Defining recording-related settings of the Voicemail config file' do
27
+
28
+ attr_reader :recordings
29
+ before :each do
30
+ @recordings = Adhearsion::Asterisk::ConfigGenerator::Voicemail::RecordingDefinition.new
31
+ end
32
+
33
+ it 'the recordings setting setter' do
34
+ Adhearsion::Asterisk::ConfigGenerator::Voicemail.new.recordings.should be_a_kind_of recordings.class
35
+ end
36
+
37
+ it 'recordings format should only allow a few options' do
38
+ the_following_code {
39
+ recordings.format :wav
40
+ recordings.format :wav49
41
+ recordings.format :gsm
42
+ }.should_not raise_error
43
+
44
+ the_following_code {
45
+ recordings.format :lolcats
46
+ }.should raise_error ArgumentError
47
+ end
48
+
49
+ end
50
+
51
+ describe 'Defining email-related Voicemail settings' do
52
+
53
+ attr_reader :email
54
+ before :each do
55
+ @email = Adhearsion::Asterisk::ConfigGenerator::Voicemail::EmailDefinition.new
56
+ end
57
+
58
+ it 'the [] operator is overloaded to return conveniences for the body() and subject() methods' do
59
+ variables = %{#{email[:name]} #{email[:mailbox]} #{email[:date]} #{email[:duration]} } +
60
+ %{#{email[:message_number]} #{email[:caller_id]} #{email[:caller_id_number]} } +
61
+ %{#{email[:caller_id_name]}}
62
+ formatted = %{${VM_NAME} ${VM_MAILBOX} ${VM_DATE} ${VM_DUR} ${VM_MSGNUM} ${VM_CALLERID} ${VM_CIDNUM} ${VM_CIDNAME}}
63
+ email.body variables
64
+ email.subject variables
65
+ email.properties[:emailbody].should == formatted
66
+ email.properties[:emailsubject].should == formatted
67
+ end
68
+
69
+ it 'when defining a body, newlines should be escaped and carriage returns removed' do
70
+ unescaped, escaped = "one\ntwo\n\r\r\nthree\n\n\n", 'one\ntwo\n\nthree\n\n\n'
71
+ email.body unescaped
72
+ email.properties[:emailbody].should == escaped
73
+ end
74
+
75
+ it 'the body must not be allowed to exceed 512 characters' do
76
+ the_following_code {
77
+ email.body "X" * 512
78
+ }.should_not raise_error ArgumentError
79
+
80
+ the_following_code {
81
+ email.body "X" * 513
82
+ }.should raise_error ArgumentError
83
+
84
+ the_following_code {
85
+ email.body "X" * 1000
86
+ }.should raise_error ArgumentError
87
+ end
88
+
89
+ it 'should store away the email command properly' do
90
+ mail_command = "/usr/sbin/sendmail -f alice@wonderland.com -t"
91
+ email.command mail_command
92
+ email.properties[:mailcmd].should == mail_command
93
+ end
94
+
95
+ end
96
+
97
+ describe 'A mailbox definition' do
98
+ attr_reader :mailbox
99
+ before :each do
100
+ @mailbox = Adhearsion::Asterisk::ConfigGenerator::Voicemail::ContextDefinition::MailboxDefinition.new("123")
101
+ end
102
+
103
+ it 'setting the name should be reflected in the to_hash form of the definition' do
104
+ mailbox.name "Foobar"
105
+ mailbox.to_hash[:name].should == "Foobar"
106
+ end
107
+
108
+ it 'setting the pin_number should be reflected in the to_hash form of the definition' do
109
+ mailbox.pin_number 555
110
+ mailbox.to_hash[:pin_number].should == 555
111
+ end
112
+
113
+ it 'the mailbox number should be available in the mailbox_number getter' do
114
+ Adhearsion::Asterisk::ConfigGenerator::Voicemail::ContextDefinition::MailboxDefinition.new '123'
115
+ mailbox.mailbox_number.should == '123'
116
+ end
117
+
118
+ it 'an ArgumentError should be raised if the mailbox_number is not numeric' do
119
+ the_following_code {
120
+ Adhearsion::Asterisk::ConfigGenerator::Voicemail::ContextDefinition::MailboxDefinition.new("this is not numeric")
121
+ }.should raise_error ArgumentError
122
+ end
123
+
124
+ it 'an ArgumentError should be raised if the pin_number is not numeric' do
125
+ the_following_code {
126
+ mailbox.pin_number "this is not numeric"
127
+ }.should raise_error ArgumentError
128
+ end
129
+
130
+ it "the string representation should be valid" do
131
+ expected = "123 => 1337,Jay Phillips,ahn@adhearsion.com"
132
+ mailbox.pin_number 1337
133
+ mailbox.name "Jay Phillips"
134
+ mailbox.email "ahn@adhearsion.com"
135
+ mailbox.to_s.should == expected
136
+ end
137
+
138
+ it 'should not add a trailing comma when the email is left out' do
139
+ mailbox.pin_number 1337
140
+ mailbox.name "Jay Phillips"
141
+ mailbox.to_s.ends_with?(',').should be false
142
+ end
143
+
144
+ it 'should not add a trailing comma when the email and name is left out' do
145
+ mailbox.pin_number 1337
146
+ mailbox.to_s.ends_with?(',').should be false
147
+ end
148
+
149
+ end
150
+
151
+ describe "A Voicemail context definition" do
152
+
153
+ it "should ultimately add a [] context definition to the string output" do
154
+ voicemail = Adhearsion::Asterisk::ConfigGenerator::Voicemail.new
155
+ voicemail.context "monkeys" do |config|
156
+ config.should be_a_kind_of voicemail.class::ContextDefinition
157
+ config.mailbox 1234 do |mailbox|
158
+ mailbox.pin_number 3333
159
+ mailbox.email "alice@wonderland.com"
160
+ mailbox.name "Alice Little"
161
+ end
162
+ end
163
+ voicemail.to_sanitary_hash.has_key?('[monkeys]').should be true
164
+ end
165
+
166
+ it 'should raise a LocalJumpError if no block is given' do
167
+ the_following_code {
168
+ Adhearsion::Asterisk::ConfigGenerator::Voicemail.new.context('lols')
169
+ }.should raise_error LocalJumpError
170
+ end
171
+
172
+ it 'its string representation should begin with a context declaration' do
173
+ vm = Adhearsion::Asterisk::ConfigGenerator::Voicemail.new
174
+ vm.context("jay") {|_|}.to_s.starts_with?("[jay]").should be true
175
+ end
176
+
177
+ end
178
+
179
+ describe 'Defining Voicemail contexts with mailbox definitions' do
180
+
181
+ end
182
+
183
+ describe 'An expansive example of the Voicemail config generator' do
184
+
185
+ before :each do
186
+ @employees = [
187
+ {:name => "Tango", :pin_number => 7777, :mailbox_number => 10},
188
+ {:name => "Echo", :pin_number => 7777, :mailbox_number => 20},
189
+ {:name => "Sierra", :pin_number => 7777, :mailbox_number => 30},
190
+ {:name => "Tango2", :pin_number => 7777, :mailbox_number => 40}
191
+ ].map { |hash| OpenStruct.new(hash) }
192
+
193
+ @groups = [
194
+ {:name => "Brand New Cadillac", :pin_number => 1111, :mailbox_number => 1},
195
+ {:name => "Jimmy Jazz", :pin_number => 2222, :mailbox_number => 2},
196
+ {:name => "Death or Glory", :pin_number => 3333, :mailbox_number => 3},
197
+ {:name => "Rudie Can't Fail", :pin_number => 4444, :mailbox_number => 4},
198
+ {:name => "Spanish Bombs", :pin_number => 5555, :mailbox_number => 5}
199
+ ].map { |hash| OpenStruct.new(hash) }
200
+ end
201
+
202
+ it 'a huge, brittle integration test' do
203
+ vm = Adhearsion::Asterisk::ConfigGenerator::Voicemail.new do |voicemail|
204
+ voicemail.context :default do |context|
205
+ context.mailbox 123 do |mailbox|
206
+ mailbox.name "Administrator"
207
+ mailbox.email "jabberwocky@wonderland.com"
208
+ mailbox.pin_number 9876
209
+ end
210
+ end
211
+
212
+ voicemail.context :employees do |context|
213
+ @employees.each do |employee|
214
+ context.mailbox employee.mailbox_number do |mailbox|
215
+ mailbox.pin_number 1337
216
+ mailbox.name employee.name
217
+ end
218
+ end
219
+ end
220
+
221
+ voicemail.context :groups do |context|
222
+ @groups.each do |group|
223
+ context.mailbox group.mailbox_number do |mailbox|
224
+ mailbox.pin_number 1337
225
+ mailbox.name group.name
226
+ mailbox.email "foo@qaz.org"
227
+ end
228
+ end
229
+ end
230
+
231
+ voicemail.execute_on_pin_change "/path/to/my/changer_script.rb"
232
+ ############################################################################
233
+ ############################################################################
234
+
235
+ signature = "Your Friendly Phone System"
236
+
237
+ # execute_after_email "netcat 192.168.1.2 12345"
238
+ # greeting_maximum 1.minute
239
+ # time_jumped_with_skip_key 3.seconds # milliseconds!
240
+ # logging_in do |config|
241
+ # config.maximum_attempts 3
242
+ # end
243
+
244
+ voicemail.recordings do |config|
245
+ config.format :wav # ONCE YOU PICK A FORMAT, NEVER CHANGE IT UNLESS YOU KNOW THE CONSEQUENCES!
246
+ config.allowed_length 3.seconds..5.minutes
247
+ config.maximum_silence 10.seconds
248
+ # config.silence_threshold 128 # wtf?
249
+ end
250
+
251
+ voicemail.emails do |config|
252
+ config.from :name => signature, :email => "noreply@adhearsion.com"
253
+ config.attach_recordings true
254
+ config.command '/usr/sbin/sendmail -f alice@wonderland.com -t'
255
+ config.subject "New voicemail for #{config[:name]}"
256
+ config.body <<-BODY.strip_heredoc
257
+ Dear #{config[:name]}:
258
+
259
+ The caller #{config[:caller_id]} left you a #{config[:duration]} long voicemail
260
+ (number #{config[:message_number]}) on #{config[:date]} in mailbox #{config[:mailbox]}.
261
+
262
+ #{ "The recording is attached to this email.\n" if config.attach_recordings? }
263
+ - #{signature}
264
+ BODY
265
+ end
266
+ end
267
+ internalized = vm.to_sanitary_hash
268
+ internalized.size.should == 5 # general, zonemessages, default, employees, groups
269
+
270
+ target_config = <<-CONFIG
271
+ [general]
272
+ attach=yes
273
+ emailbody=Dear ${VM_NAME}:\\n\\nThe caller ${VM_CALLERID} left you a ${VM_DUR} long voicemail\\n(number ${VM_MSGNUM}) on ${VM_DATE} in mailbox ${VM_MAILBOX}.\\n\\nThe recording is attached to this email.\\n\\n- Your Friendly Phone System\\n
274
+ emailsubject=New voicemail for ${VM_NAME}
275
+ externpass=/path/to/my/changer_script.rb
276
+ format=wav
277
+ fromstring=Your Friendly Phone System
278
+ mailcmd=/usr/sbin/sendmail -f alice@wonderland.com -t
279
+ serveremail=noreply@adhearsion.com
280
+
281
+ [zonemessages]
282
+ eastern=America/New_York|'vm-received' Q 'digits/at' IMp
283
+ central=America/Chicago|'vm-received' Q 'digits/at' IMp
284
+ central24=America/Chicago|'vm-received' q 'digits/at' H N 'hours'
285
+ military=Zulu|'vm-received' q 'digits/at' H N 'hours' 'phonetic/z_p'
286
+ european=Europe/Copenhagen|'vm-received' a d b 'digits/at' HM
287
+ [default]
288
+ 123 => 9876,Administrator,jabberwocky@wonderland.com
289
+
290
+ [employees]
291
+ 10 => 1337,Tango
292
+ 20 => 1337,Echo
293
+ 30 => 1337,Sierra
294
+ 40 => 1337,Tango2
295
+
296
+ [groups]
297
+ 1 => 1337,Brand New Cadillac,foo@qaz.org
298
+ 2 => 1337,Jimmy Jazz,foo@qaz.org
299
+ 3 => 1337,Death or Glory,foo@qaz.org
300
+ 4 => 1337,Rudie Can't Fail,foo@qaz.org
301
+ 5 => 1337,Spanish Bombs,foo@qaz.org
302
+ CONFIG
303
+ vm.to_s.split("\n").grep(/^$|^[^;]/).join("\n").strip.should == target_config.strip
304
+
305
+ end
306
+ end