"""FWDOutRobot automates registration of fwdOUT routes. To use it, set the parameters to suit your case and run. Variables that require setting: * username * password * countrycode * areacodes * prefixes * designate the Asterisk channel to dial out for these prefixes. Optional are tweaks to: * default_calls_per_day * default_calls_per_hour * Further customization of the Dial() parameters or other Asterisk application to be associated with the prefixes. The string of prefixes will be parsed by sorting, stripping to three digits (the current version is targeted to US style dialplans), then in addition, any groups of ten prefixes e.g. 520-529 will be removed and replaced with a more general prefix of simply 52. The resulting list of prefixes will be concatenated onto the country & area code and registered by way of the fwdOUT website. The corresponding Asterisk dial patterns are printed to stdout for manual copy paste into your extensions.conf context.""" # # # Registration Robot of fwdOUT dialing prefixes # # Copyright (c) 2005, Andrew C. Brown # # This script is redistributable under the GNU General Public License # http://www.gnu.org/licenses/gpl.txt # # May 3, 2005 # andy_lists@bananabread.net # # Dependencies: Requires module ClientCookie. http://wwwsearch.sourceforge.net/ClientCookie/ # and Python 2.0-2.4. If you are using 2.4, you have a version of # the Cookielib already installed. You may need to change the import statement # below to correspond. See the ClientCookie site for details. import ClientCookie import urllib import urllib2 import string # import webbrowser # Only needed for auto viewing of results page when debug=1 ########################################################################### # !! MANDATORY SETTINGS !! # Change the strings to suit your case # If you don't understand Asterisk dial plans, you shouldn't be using this # script. ########################################################################### username = 'your fwdOUT username goes here!' password = 'our fwdOUT password goes here!' countrycode = '1' areacodes = 'your area codes - space separated' asterisk_dial = ',1,Dial(Zap/24/${EXTEN},60,T,L(1800000,1790000))' # Fill in all your desired prefixes here by area code. # For SBC, you can lookup your local prefixes at http://localcalling.sbc.com/LCA/lca_input.jsp prefixes = {} prefixes['650'] = '206 209 210' prefixes['408'] = '200 207 212' prefixes['801'] = '785 796 296' ################################################### # OPTIONAL: Change these to your preference ################################################### debug = 0 # enables visible results of the actual submission action live = 1 # 1 = Enable actual route registration, 2 = print prefixes to stdout default_calls_per_hour = 5 default_calls_per_day = 20 login_url = 'http://www.fwdout.net/bell-cgi/login2.cgi' submission_url = 'http://www.fwdout.net/bell-cgi/fwdOUT.cgi?mode=My_Routes' tmp_html = "C:\\tmp\\fwdouttest.html" webpage = '' def killdupes(thelist): """Return a list with any duplicate in the list removed.""" listcopy = [] while thelist: # If it's a dup, drop it and go to next iteration targetitem = thelist.pop() if targetitem not in listcopy: listcopy.append(targetitem) return listcopy def clip(thelist): """ Clips all items to a maximum of 3 digits. This is designed for use on USA prefix format where AreaCode + 3 Digit Prefix completely determines toll status. So normally one is not concerned with anything past the third digit. If for some reason you want to specify routes more narrowly (using more digits), you will need to remove use of this function. You will also need to rewrite the ten grouping feature which is not currently sophisticated enough to deal with longer digit patterns.""" return [item[:3] for item in thelist] def GeneratePatterns(prefixes): """Given list of 3 digit prefixes, returns unique sorted prefixes, truncated to 3 digits. If groups exist of all ten possible values of the third digit, those ten prefixes are omitted from the list and instead generalized by a TWO digit prefix. e.g. 360-369 would be replaced with simply 36.""" prefix_list_alpha = clip(prefixes.split()) prefix_list = killdupes(prefix_list_alpha) prefix_tried = [] while (len(prefix_list) > 0): nextprefix = prefix_list.pop() if len(prefix_tried)==0 or (nextprefix[:2] not in [pfix[:2] for pfix in prefix_tried]): # a list of all prefixes with the same first two digits fwdout_grouping = [pfix for pfix in prefix_list if (pfix[:2] == nextprefix[:2])] # If the prefix_list covers all ten possibilities, then # just add a shorter prefix to generalize. So in the 200 example, # '20' will be appended, representing 200 ... 209. if len(fwdout_grouping) == 9: # Would be ten if current prefix hadn't # already been popped. prefix_tried.append(nextprefix[:2]) # .. and remove the individuals e.g. 200...209 for match in fwdout_grouping: prefix_list.remove(match) else: prefix_tried.append(nextprefix) else: prefix_tried.append(nextprefix) prefix_tried.sort() return prefix_tried def X_Padding(text, length): """Pads the end of text string to {length} characters using X's. X's are Asterisk's wildcard symbol. This is to compensate for when prefixes are shortened for greater generality - it extends the Asterisk dial strings to the same total length.""" zpadding = length - len(text) padding = ''.zfill(zpadding) # I was just looking for a way to create a # string of arbitrary length. padding = padding.replace('0','X') return text + padding def AsteriskPattern(numbers, countrycode, areacode, length): """Concatenates country, area and prefixes together and returns an asterisk dial pattern string.""" results = [] for item in numbers: fullstring = X_Padding(countrycode + areacode + item, length) results.append('exten => _%s%s' % (fullstring, asterisk_dial)) return results if not live: # In offline mode, the prefixes are just printed to stdout and not submitted. def register_a_prefix(newprefix): print newprefix else: def register_a_prefix(newprefix): """Given a complete prefix, login to fwdOUT and register that prefix.""" # Login to fwdOUT user account login = {'username': username,'password': password} login_header =urllib.urlencode(login) req = urllib2.Request(login_url, login_header) f = ClientCookie.urlopen(req) # fwdOUT site requires cookies for # authentication f.close() # Now prepare submission and send newroute = {'newpatNew':newprefix, 'newcommentNew': '', 'newcallsperhourNew': default_calls_per_hour, 'newcallslefttodayNew': default_calls_per_day, 'itemNew':'', 'item1':'new', 'mode':'My_Routes', 'actionNew':'Create...'} regheader = urllib.urlencode(newroute) req2 = urllib2.Request(submission_url, regheader) f = ClientCookie.urlopen(req2) webpage = f.read() # Read back the results page. f.close() def main(): for areacode in areacodes.split(): patterns = GeneratePatterns(prefixes[areacode]) for item in patterns: register_a_prefix('1'+areacode+item) A_patterns = AsteriskPattern(patterns, countrycode, areacode, 11) for item in A_patterns: # Print out all the Asterisk dial strings to stdout print item # DEBUG output if live and webpage and debug: # Viewing the results webpage is optional and normally only needed for debugging. # This page will be a local file, so the links aren't usable anyway. # It's better to just open the real page. fout = file(tmp_html,"w") fout.write(webpage) fout.close() webbrowser.open(tmp_html) if __name__ == "__main__": main()