active_mailbox 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Joshua Priddle
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,215 @@
1
+ # ActiveMailbox
2
+
3
+ ActiveMailbox provides a simple API and command line utility to work with
4
+ voicemail boxes and voicemails generated by Asterisk.
5
+
6
+
7
+ ## Command line usage via active_mailbox
8
+
9
+ ActiveMailbox includes a command line tool which administrators can use to
10
+ perform maintenance on a user's mailbox.
11
+
12
+ The command takes **three** arguments: the action (OPTION), the context
13
+ defined in voicemail.conf (CONTEXT), and the user's phone number (MAILBOX)
14
+
15
+ Usage: active_mailbox [OPTION] MAILBOX
16
+
17
+ Mailbox Options:
18
+ --context Look for [MAILBOX] in [CONTEXT]
19
+ --delete Delete [MAILBOX] and all messages
20
+ --sort Sort messages in [MAILBOX] (recursive)
21
+
22
+ Cleanup Options:
23
+ --clean-ghosts Cleanup 'ghost' messages
24
+ --clean-stale Cleanup messages older than 30 days
25
+ --clean-mp3s Cleanup [MAILBOX]/.mp3 directory
26
+ --purge Remove all messages, but leave [MAILBOX] folders intact
27
+
28
+ Greeting Options:
29
+ --delete-temp Delete [MAILBOX]/temp.wav
30
+ --delete-unavail Delete [MAILBOX]/unavail.wav
31
+ --delete-busy Delete [MAILBOX]/busy.wav
32
+
33
+ General Options:
34
+ -h, --help Show this message
35
+ -v, --version Show version
36
+
37
+
38
+ ### Example Command line usage
39
+
40
+ #### Delete unavailable greeting
41
+
42
+ active_mailbox --delete-unavail 15183332220
43
+
44
+
45
+ #### Delete temp greeting
46
+
47
+ active_mailbox --delete-temp 15183332220
48
+
49
+
50
+ #### Purge mailbox (deletes all messages, leaves folders intact)
51
+
52
+ active_mailbox --purge 15183332220
53
+
54
+
55
+ #### Delete messages older than 30 days
56
+
57
+ active_mailbox --clean-stale 15183332220
58
+
59
+
60
+ #### Delete mailbox
61
+
62
+ active_mailbox --delete 15183332222
63
+
64
+
65
+ #### Delete 'ghost' messages
66
+
67
+ Ghost voicemails happen if a message's txt file exists, but the
68
+ corresponding wav file does not. Example:
69
+
70
+ tree 518/15183332225/INBOX
71
+ 518/15183332225/INBOX
72
+ |-- msg0000.txt
73
+ |-- msg0000.wav
74
+ `-- msg0002.txt
75
+
76
+ Run `active_mailbox --clean-ghosts` to clear the offending txt files.
77
+
78
+ active_mailbox --clean-ghosts 518 15183332220
79
+
80
+ Observe the results:
81
+
82
+ tree 518/15183332225/INBOX
83
+ 518/15183332225/INBOX
84
+ |-- msg0000.txt
85
+ `-- msg0000.wav
86
+
87
+ #### Sorting
88
+
89
+ If voicemails are manually deleted, the INBOX order can be out of sync, as
90
+ depicted below:
91
+
92
+ tree 518/15183332225/INBOX
93
+ 518/15183332225/INBOX
94
+ |-- msg0000.txt
95
+ |-- msg0000.wav
96
+ |-- msg0002.txt
97
+ |-- msg0002.wav
98
+ |-- msg0006.txt
99
+ |-- msg0006.wav
100
+ |-- msg0009.txt
101
+ `-- msg0009.wav
102
+
103
+ Sort the messages so they play correctly in Asterisk using
104
+ `active_mailbox --sort`:
105
+
106
+ active_mailbox --sort 15183332225
107
+
108
+ Messages are renamed in INBOX after sorting
109
+
110
+ tree 518/15183332225/INBOX
111
+ 518/15183332225/INBOX
112
+ |-- msg0000.txt
113
+ |-- msg0000.wav
114
+ |-- msg0001.txt
115
+ |-- msg0001.wav
116
+ |-- msg0002.txt
117
+ |-- msg0002.wav
118
+ |-- msg0003.txt
119
+ `-- msg0003.wav
120
+
121
+
122
+ ## Library Usage
123
+
124
+ ActiveMailbox can also be used in ruby scripts to work with mailboxes
125
+ and voicemail messages.
126
+
127
+
128
+ ### Working with mailboxes
129
+
130
+ # Create a mailbox object
131
+ mailbox = ActiveMailbox::Mailbox.find('office_a', '1234')
132
+
133
+ # Destroy mailbox and all messages/greetings
134
+ mailbox.destroy!
135
+
136
+ # Determine the greeting Asterisk will playback
137
+ mailbox.current_greeting
138
+
139
+ # Destroy ``unavail.wav``
140
+ mailbox.destroy_greeting!
141
+
142
+ # Destroy ``temp.wav``
143
+ mailbox.destroy_temp!
144
+
145
+ # A Hash of the mailbox's messages
146
+ #
147
+ # Keys are the subdirectory, lowercased, as
148
+ # a symbol. Eg: INBOX = :inbox, Old = :old
149
+ #
150
+ # Values are arrays of Message objects
151
+ mailbox.folders
152
+
153
+ # Fetch array of messages in :inbox
154
+ inbox = mailbox.folders[:inbox]
155
+
156
+ # Or some sugar
157
+ inbox = mailbox.inbox
158
+
159
+ # Or :old
160
+ mailbox.folders[:old]
161
+ mailbox.old
162
+
163
+
164
+ ### Working with messages
165
+
166
+ # Set mailbox
167
+ mailbox = ActiveMailbox::Mailbox.find('office_a', '1234')
168
+
169
+ # Get first message in INBOX
170
+ vm = mailbox.inbox.first
171
+
172
+ # Path to Asterisk generated msg####.txt
173
+ vm.txt
174
+
175
+ # Path to Asterisk generated msg####.wav
176
+ vm.wav
177
+
178
+ # Caller ID name
179
+ vm.callerid_name
180
+
181
+ # Caller ID number
182
+ vm.callerid_number
183
+
184
+ # Message duration (in seconds)
185
+ vm.duration
186
+
187
+ # Delete this message
188
+ # Deletes wav, txt, mp3, xml files
189
+ vm.destroy!
190
+
191
+
192
+ ## Notes
193
+
194
+ If Asterisk does **not** keep voicemail in
195
+ `/var/spool/asterisk/voicemail` on this server, add the following
196
+ to `~/.bashrc` (or the appropriate shell config file):
197
+
198
+ export ASTERISK_VOICEMAIL_ROOT='/my/alternate/voicemail/path'
199
+
200
+ Note that you can specify this as an ENV variable when running the
201
+ `active_mailbox` executable.
202
+
203
+ ASTERISK_VOICEMAIL_ROOT='/root' active_mailbox [OPTION] MAILBOX
204
+
205
+
206
+ ## Development
207
+
208
+ ActiveMailbox is still in development, as such, don't use it yet unless you're
209
+ brave. Then again, if you're using Asterisk, you're already a cowboy so have
210
+ fun!
211
+
212
+
213
+ ## License
214
+
215
+ MIT License, see LICENSE.
@@ -0,0 +1,33 @@
1
+ require 'rake/testtask'
2
+
3
+ $:.unshift 'lib'
4
+
5
+ desc "Start an IRB session preloaded with this library"
6
+ task :console do
7
+ sh "ASTERISK_VOICEMAIL_ROOT='./test/fixtures/' irb -rlib/active_mailbox.rb -I./lib"
8
+ end
9
+
10
+ require 'sdoc_helpers'
11
+ desc "Push a new version to Gemcutter"
12
+ task :publish do
13
+ require 'active_mailbox/version'
14
+
15
+ ver = ActiveMailbox::Version
16
+
17
+ sh "gem build active_mailbox.gemspec"
18
+ sh "gem push active_mailbox-#{ver}.gem"
19
+ sh "git tag -a -m 'ActiveMailbox v#{ver}' v#{ver}"
20
+ sh "git push origin v#{ver}"
21
+ sh "git push origin master"
22
+ sh "git clean -fd"
23
+ sh "rake pages"
24
+ end
25
+
26
+ task :default => :test
27
+
28
+ require 'rake/testtask'
29
+ Rake::TestTask.new(:test) do |test|
30
+ test.libs << 'lib' << 'test' << '.'
31
+ test.pattern = 'test/**/*_test.rb'
32
+ test.verbose = true
33
+ end
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'active_mailbox'
4
+ require 'active_mailbox/cli'
5
+
6
+ ActiveMailbox::CLI.run!(ARGV)
@@ -0,0 +1,16 @@
1
+ require 'active_mailbox/version'
2
+ require 'active_mailbox/errors'
3
+ require 'active_mailbox/mailbox'
4
+ require 'active_mailbox/folder'
5
+ require 'active_mailbox/message'
6
+
7
+ module ActiveMailbox
8
+ # The directory Asterisk records voicemail in
9
+ #
10
+ # By default, this is `/var/spool/asterisk/voicemail`. If it isn't,
11
+ # you did something fancy when building Asterisk. Set
12
+ # ENV['ASTERISK_VOICEMAIL_ROOT'] to that directory in your library
13
+ # or add 'export ASTERISK_VOICEMAIL_ROOT="/my/voicemail"' to your
14
+ # ~/.bashrc
15
+ VOICEMAIL_ROOT = ENV['ASTERISK_VOICEMAIL_ROOT'] || "/var/spool/asterisk/voicemail"
16
+ end
@@ -0,0 +1,112 @@
1
+ require 'optparse'
2
+
3
+ module ActiveMailbox
4
+ #
5
+ # This class facilites ActiveMailbox's command line functions
6
+ #
7
+ class CLI
8
+
9
+ #
10
+ # Invoke the CLI
11
+ #
12
+ def self.run!(argv)
13
+ options = ActiveMailbox::CLI.parse(argv)
14
+
15
+ args = [options[:mailbox]]
16
+ args.unshift(options[:context]) if options[:context]
17
+
18
+ mailbox = ActiveMailbox::Mailbox.find(*args)
19
+
20
+ if options[:arg]
21
+ mailbox.send(options[:command], options[:arg])
22
+ else
23
+ mailbox.send(options[:command])
24
+ end
25
+ end
26
+
27
+ #
28
+ # Parse CLI arguments (ARGV)
29
+ #
30
+ def self.parse(argv)
31
+ options = {}
32
+
33
+ ::OptionParser.new do |opts|
34
+ opts.banner = "Usage: active_mailbox [OPTION] MAILBOX\n"
35
+
36
+ opts.separator ""
37
+ opts.separator "Mailbox Options:"
38
+
39
+ opts.on('--context', 'Look for [MAILBOX] in [CONTEXT]') do |context|
40
+ options[:context] = conext
41
+ end
42
+
43
+ opts.on('--delete', 'Delete [MAILBOX] and all messages') do
44
+ options[:command] = :delete!
45
+ end
46
+
47
+ opts.on('--sort', 'Sort messages in [MAILBOX] (recursive)') do
48
+ options[:command] = :sort!
49
+ end
50
+
51
+ opts.separator ""
52
+ opts.separator "Cleanup Options:"
53
+
54
+ opts.on('--clean-ghosts', "Cleanup 'ghost' messages") do
55
+ options[:command] = :clean_ghosts!
56
+ end
57
+
58
+ opts.on('--clean-stale', 'Cleanup messages older than 30 days') do
59
+ options[:command] = :clean_stale!
60
+ end
61
+
62
+ opts.on('--clean-mp3s', 'Cleanup [MAILBOX]/.mp3 directory') do
63
+ options[:command] = :clean_mp3s!
64
+ end
65
+
66
+ opts.on('--purge', 'Remove all messages, but leave [MAILBOX] folders intact') do
67
+ options[:command] = :purge!
68
+ end
69
+
70
+ opts.separator ""
71
+ opts.separator "Greeting Options:"
72
+
73
+ opts.on('--delete-temp', 'Delete [MAILBOX]/temp.wav') do
74
+ options[:command] = :delete_temp_greeting!
75
+ end
76
+
77
+ opts.on('--delete-unavail', 'Delete [MAILBOX]/unavail.wav') do
78
+ options[:command] = :delete_unavail_greeting!
79
+ end
80
+
81
+ opts.on('--delete-busy', 'Delete [MAILBOX]/busy.wav') do
82
+ options[:command] = :delete_busy_greeting!
83
+ end
84
+
85
+ opts.separator ""
86
+ opts.separator "General Options:"
87
+
88
+ opts.on('-h', '--help', 'Show this message') do
89
+ options[:command] = :help
90
+ puts opts
91
+ exit
92
+ end
93
+
94
+ opts.on('-v', '--version', 'Show version') do
95
+ options[:command] = :version
96
+ puts ActiveMailbox::Version
97
+ exit
98
+ end
99
+
100
+ begin
101
+ argv = ['-h'] if argv.empty?
102
+ opts.parse!(argv)
103
+ options[:mailbox] = argv.shift
104
+ options
105
+ rescue ::OptionParser::ParseError => err
106
+ STDERR.puts err.message, "\n", opts
107
+ exit(-1)
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,8 @@
1
+ module ActiveMailbox
2
+ module Errors #:nodoc: all
3
+ class MailboxNotFound < StandardError; end
4
+ class MessageNotFound < StandardError; end
5
+ class FolderNotFound < StandardError; end
6
+ class GreetingNotFound < StandardError; end
7
+ end
8
+ end
@@ -0,0 +1,166 @@
1
+ module ActiveMailbox
2
+ #
3
+ # ActiveMailbox::Folder represents an Asterisk mailbox Folder, such as
4
+ # INBOX, or Old. They contain the actuall messages for a mailbox.
5
+ #
6
+ class Folder
7
+
8
+ include Comparable
9
+
10
+ attr_reader :path, :mailbox
11
+
12
+ attr_accessor :reload_messages
13
+
14
+ #
15
+ # Create a new Folder object
16
+ #
17
+ def initialize(path, mailbox)
18
+ unless File.exists?(path)
19
+ raise ActiveMailbox::Errors::FolderNotFound, "#{path} does not exist"
20
+ end
21
+ @mailbox = mailbox
22
+ @path = path
23
+ end
24
+
25
+ #
26
+ # Compare base on path
27
+ #
28
+ def <=>(other)
29
+ path <=> other.path
30
+ end
31
+
32
+ #
33
+ # An array of Message objects in this folder
34
+ #
35
+ def messages(reload = false)
36
+ if reload or @reload_messages or ! defined?(@messages)
37
+ @messages = []
38
+ Dir.chdir(@path) do
39
+ Dir["*.txt"].each do |txt|
40
+ @messages << Message.new("#{Dir.pwd}/#{txt}", self)
41
+ end
42
+ end
43
+ end
44
+ @messages
45
+ ensure
46
+ @reload_messages = false
47
+ end
48
+
49
+ #
50
+ # The name of this folder
51
+ #
52
+ def name
53
+ @name ||= path.split('/').last
54
+ end
55
+
56
+ #
57
+ # Returns number of messages in this folder
58
+ #
59
+ def count
60
+ messages.count
61
+ end
62
+ alias :size :count
63
+
64
+ #
65
+ # Sort messages in this folder
66
+ #
67
+ # Eg:
68
+ # Before Sort: msg0002, msg0005, msg0010, msg0020
69
+ # After Sort: msg0000, msg0001, msg0002, msg0003
70
+ #
71
+ def sort!
72
+ unless messages.size == messages.last.number + 1
73
+
74
+ renamer = lambda do |old_file, new_file|
75
+ %w[wav txt].each do |ext|
76
+ File.rename("#{old_file}.#{ext}", "#{new_file}.#{ext}")
77
+ end
78
+ end
79
+
80
+ messages.each_with_index.map do |message, index|
81
+ old_file = message.path
82
+ tmp_file = old_file.sub(/msg[0-9]{4}/, 'temp_msg%04d' % index)
83
+ renamer.call(old_file, tmp_file)
84
+ [old_file, tmp_file]
85
+ end.each do |old_file, tmp_file|
86
+ new_file = tmp_file.sub('temp_msg', 'msg')
87
+ renamer.call(tmp_file, new_file)
88
+ end
89
+ end
90
+ ensure
91
+ @reload_messages = true
92
+ end
93
+
94
+ #
95
+ # Destroy all messages/info files in this folder
96
+ #
97
+ def purge!
98
+ Dir.chdir(@path) do
99
+ Dir["*"].each do |file|
100
+ File.unlink(file)
101
+ end
102
+ end
103
+ ensure
104
+ @reload_messages = true
105
+ end
106
+
107
+ #
108
+ # Clean all 'ghost' Messages in this Folder
109
+ #
110
+ # A 'ghost' voicemail occurs when the .wav file does not exist.
111
+ # Asterisk sees the info (txt) file and thinks a voicemail exists,
112
+ # then plays nothing as the wav is not found.
113
+ #
114
+ # At the same time, you might run into issues if a wav exists, but
115
+ # not the txt file.
116
+ #
117
+ def clean_ghosts!(autosort = true)
118
+ Dir.chdir(@path) do
119
+ message_list.each do |file|
120
+ txt = "#{Dir.pwd}/#{file}.txt"
121
+ wav = "#{Dir.pwd}/#{file}.wav"
122
+
123
+ txt_exists = File.exists?(txt)
124
+ wav_exists = File.exists?(wav)
125
+
126
+ unless txt_exists && wav_exists
127
+ File.unlink(txt) if txt_exists
128
+ File.unlink(wav) if wav_exists
129
+ end
130
+ end
131
+ end
132
+ sort! if autosort
133
+ end
134
+
135
+ #
136
+ # Destroy Messages in this Folder that are older than 30 days
137
+ #
138
+ def clean_stale!(autosort = true)
139
+ messages.each do |message|
140
+ message.destroy! if message.stale?
141
+ end
142
+ sort! if autosort
143
+ end
144
+
145
+ #
146
+ # Destroy this Folder
147
+ #
148
+ def destroy!
149
+ FileUtils.rm_rf(@path)
150
+ end
151
+
152
+ private
153
+ #
154
+ # Grabs a list of valid message files in this folder
155
+ #
156
+ def message_list(strip_extension = true)
157
+ Dir['msg[0-9][0-9][0-9][0-9]*'].map do |file|
158
+ if strip_extension
159
+ file.split('.').first
160
+ else
161
+ file
162
+ end
163
+ end.uniq
164
+ end
165
+ end
166
+ end