From 1e8fb5e9c051e9024702a022c764daf67bb29f6a Mon Sep 17 00:00:00 2001 From: Jerry Date: Fri, 19 Nov 2021 15:52:42 +0800 Subject: [PATCH] first commit --- codecs.conf | 233 +++++++++++++++++++++++++++++++++++++++++++++++ confbridge.conf | 47 ++++++++++ extensions.conf | 127 ++++++++++++++++++++++++++ modules.conf | 83 +++++++++++++++++ musiconhold.conf | 7 ++ pjsip.conf | 144 +++++++++++++++++++++++++++++ whois.py | 137 ++++++++++++++++++++++++++++ 7 files changed, 778 insertions(+) create mode 100644 codecs.conf create mode 100644 confbridge.conf create mode 100644 extensions.conf create mode 100644 modules.conf create mode 100644 musiconhold.conf create mode 100644 pjsip.conf create mode 100644 whois.py diff --git a/codecs.conf b/codecs.conf new file mode 100644 index 0000000..475594a --- /dev/null +++ b/codecs.conf @@ -0,0 +1,233 @@ +[speex] +; CBR encoding quality [0..10] +; used only when vbr = false +quality => 3 + +; codec complexity [0..10] +; tradeoff between cpu/quality +complexity => 2 + +; perceptual enhancement [true / false] +; improves clarity of decoded speech +enhancement => true + +; voice activity detection [true / false] +; reduces bitrate when no voice detected, used only for CBR +; (implicit in VBR/ABR) +vad => true + +; variable bit rate [true / false] +; uses bit rate proportionate to voice complexity +vbr => true + +; available bit rate [bps, 0 = off] +; encoding quality modulated to match this target bit rate +; not recommended with dtx or pp_vad - may cause bandwidth spikes +abr => 0 + +; VBR encoding quality [0-10] +; floating-point values allowed +vbr_quality => 4 + +; discontinuous transmission [true / false] +; stops transmitting completely when silence is detected +; pp_vad is far more effective but more CPU intensive +dtx => false + +; preprocessor configuration +; these options only affect Speex v1.1.8 or newer + +; enable preprocessor [true / false] +; allows dsp functionality below but incurs CPU overhead +preprocess => false + +; preproc voice activity detection [true / false] +; more advanced equivalent of DTX, based on voice frequencies +pp_vad => false + +; preproc automatic gain control [true / false] +pp_agc => false +pp_agc_level => 8000 + +; preproc denoiser [true / false] +pp_denoise => false + +; preproc dereverb [true / false] +pp_dereverb => false +pp_dereverb_decay => 0.4 +pp_dereverb_level => 0.3 + +; experimental bitrate changes depending on RTCP feedback [true / false] +experimental_rtcp_feedback => false + + +[plc] +; for all codecs which do not support native PLC +; this determines whether to perform generic PLC +; there is a minor performance penalty for this. +; By default plc is applied only when the 2 codecs +; in a channel are different. +genericplc => true +; Apply generic plc to channels even if the 2 codecs +; are the same. This forces transcoding via slin so +; the performance impact should be considered. +; Ignored if genericplc is not also enabled. +genericplc_on_equal_codecs => false + +; Generate custom formats for formats requiring attributes. +; After defining the custom format, the name used in defining +; the format can be used throughout Asterisk in the format 'allow' +; and 'disallow' options. +; +; Example: silk8 is a predefined custom format in this config file. +; Once this config file is loaded, silk8 can be used anywhere a +; peer's codec capabilities are defined. +; +; In sip.conf 'silk8' can be defined as a capability for a peer. +; [peer1] +; type=peer +; host=dynamic +; disallow=all +; allow=silk8 ;custom codec defined in codecs.conf +; +; LIMITATIONS +; Custom formats can only be defined at startup. Any changes to this +; file made after startup will not take into effect until after Asterisk +; is restarted. +; + +; Default Custom SILK format definitions, only one custom SILK format per +; sample rate is allowed. +[silk8] +type=silk +samprate=8000 +fec=true ; turn on or off encoding with forward error correction. + ; On recommended, off by default. +packetloss_percentage=10 ; Estimated packet loss percentage in uplink direction. This + ; affects how much redundancy is built in when using fec. + ; The higher the percentage, the larger amount of bandwidth is + ; used. Default is 0%, 10% is recommended when fec is in use. + +maxbitrate=10000 ; Use the table below to make sure a useful bitrate is choosen + ; for maxbitrate. If not set or value is not within the bounds + ; of the encoder, a default value is chosen. + ; + ; sample rate | bitrate range + ; 8khz | 5000 - 20000 bps + ; 12khz | 7000 - 25000 bps + ; 16khz | 8000 - 30000 bps + ; 24khz | 20000- 40000 bps + ; +;dtx=true ; Encode using discontinuous transmission mode or not. Turning this + ; on will save bandwidth during periods of silence at the cost of + ; increased computational complexity. Off by default. + +[silk12] +type=silk +samprate=12000 +maxbitrate=12000 +fec=true +packetloss_percentage=10; + +[silk16] +type=silk +samprate=16000 +maxbitrate=20000 +fec=true +packetloss_percentage=10; + +[silk24] +type=silk +samprate=24000 +maxbitrate=30000 +fec=true +packetloss_percentage=10; + + +; Default custom CELT codec definitions. Only one custom CELT definition is allowed +; per a sample rate. +;[celt44] +;type=celt +;samprate=44100 ; The samplerate in hz. This option is required. +;framesize=480 ; The framesize option represents the duration of each frame in samples. + ; This must be a factor of 2. This option is only advertised in an SDP + ; when it is set. Otherwise a default of framesize of 480 is assumed + ; internally + +;[celt48] +;type=celt +;samprate=48000 + +;[celt32] +;type=celt +;samprate=32000 + +;============================ OPUS Section Options ============================ +; +; NOTE: Accurate documentation corresponding to your downloaded version of +; codec_opus is available from Asterisk's CLI: +; +; *CLI> config show help codec_opus opus +; +;[opus] +;type= ; Must be of type "opus" (default: "") +;packet_loss= ; Encoder's packet loss percentage. Can be any number between 0 + ; and 100, inclusive. A higher value results in more loss + ; resistance. (default: 0) +;complexity= ; Encoder's computational complexity. Can be any number between 0 + ; and 10, inclusive. Note, 10 equals the highest complexity. + ; (default: 10) +;max_bandwidth= ; Encoder's maximum bandwidth allowed. Sets an upper bandwidth + ; bound on the encoder. Can be any of the following: narrow, + ; medium, wide, super_wide, full. (default: full) +;signal= ; Encoder's signal type. Aids in mode selection on the encoder: Can + ; be any of the following: auto, voice, music. (default: auto) +;application= ; Encoder's application type. Can be any of the following: voip, + ; audio, low_delay. (default: voip) +;max_playback_rate= ; Override the maximum playback rate in the offer's SDP. + ; Any value between 8000 and 48000 (inclusive) is valid, + ; however typically it should match one of the usual opus + ; bandwidths. (default: 48000) +;bitrate= ; Override the maximum average bitrate in the offer's SDP. Any value + ; between 500 and 512000 is valid. The following values are also + ; allowed: auto, max. (default: auto) +;cbr= ; Override the constant bit rate parameter in the offer's SDP. A value of + ; 0/false/no represents a variable bit rate whereas 1/true/yes represents + ; a constant bit rate. (default: no) +;fec= ; Override the use inband fec parameter in the offer's SDP. A value of + ; 0/false/no represents disabled whereas 1/true/yes represents enabled. + ; (default: yes) +;dtx= ; Override the use dtx parameter in the offer's SDP. A value of 0/false/no + ; represents disabled whereas 1/true/yes represents enabled. (default: no) + +;=============================== OPUS Examples ================================ +; +;[opus] +;type=opus +;max_playback_rate=8000 ; Limit the maximum playback rate on the encoder +;fec=no ; No inband fec + +;[myopus] +;type=opus +;max_bandwidth=wide ; Maximum encoded bandwidth set to wide band (0-8000 Hz +; ; audio bandwidth at 16Khz sample rate) +;cbr=yes ; Negotiate a constant bit rate + +[opus48] +type=opus +max_playback_rate=48000 +max_bandwidth=wide +bitrate=48000 +packet_loss=10 + +[opus64] +type=opus +max_playback_rate=48000 +max_bandwidth=full +bitrate=64000 + +[opus96] +type=opus +max_playback_rate=48000 +max_bandwidth=full +bitrate=96000 diff --git a/confbridge.conf b/confbridge.conf new file mode 100644 index 0000000..b13a57d --- /dev/null +++ b/confbridge.conf @@ -0,0 +1,47 @@ +[sample_user_menu] +type=menu +*=playback_and_continue(conf-usermenu) +*1=toggle_mute +1=toggle_mute +*4=decrease_listening_volume +4=decrease_listening_volume +*6=increase_listening_volume +6=increase_listening_volume +*7=decrease_talking_volume +7=decrease_talking_volume +*8=leave_conference +8=leave_conference +*9=increase_talking_volume +9=increase_talking_volume + +[sample_admin_menu] +type=menu +*=playback_and_continue(conf-adminmenu) +*1=toggle_mute +1=toggle_mute +*2=admin_toggle_conference_lock ; only applied to admin users +2=admin_toggle_conference_lock ; only applied to admin users +*3=admin_kick_last ; only applied to admin users +3=admin_kick_last ; only applied to admin users +*4=decrease_listening_volume +4=decrease_listening_volume +*6=increase_listening_volume +6=increase_listening_volume +*7=decrease_talking_volume +7=decrease_talking_volume +*8=no_op +8=no_op +*9=increase_talking_volume +9=increase_talking_volume + +[user1] +type=user +;pin=1234 +marked=yes +admin=no +music_on_hold_when_empty=yes +dsp_drop_silence=yes + +[bridge1] +type=bridge +max_members=10 diff --git a/extensions.conf b/extensions.conf new file mode 100644 index 0000000..cfd621e --- /dev/null +++ b/extensions.conf @@ -0,0 +1,127 @@ +[general] +static=yes +writeprotect=no +clearglobalvars=no + +[extdn42whois] +;exten => i,1,NoOp() +;exten => t,1,Goto(s,1) + +exten => _X.,5,Set(CALLNUM=${EXTEN}) + same => n,Goto(s,1) + +exten => s,1,Playback(silence/1) + same => n,Set(USERINPUT=) + same => n,Read(USERINPUT,jerry-whois,10,,1,6) + same => n,GotoIf($["${READSTATUS}" = "TIMEOUT"]?s,1:) + same => n,GotoIf($["${USERINPUT}" = ""]?extmymenu,${CALLNUM},5:whois,1) + +exten => whois,1,Set(SESSIONUNID=${RAND(0,100000)}) + same => n,TrySystem(/var/lib/asterisk/scripts/jerry/whois.py ${USERINPUT} ${SESSIONUNID}) + same => n,Playback(/var/tmp/ast-dynamic/${SESSIONUNID}) + same => n,GotoIf($["${PLAYBACKSTATUS}" = "SUCCESS"]?whois,whoisend:) + same => n,Playback(im-sorry&something-terribly-wrong) + same => n(whoisend),Goto(s,1) + +[extmymenu] +exten => i,1,Playback(silence/1&goodbye) + same => n,Hangup() + +exten => _X.,5,Set(CALLNUM=${EXTEN}) + same => n,Goto(s,1) + +exten => s,1,Wait(1) +;same => n(loop),Background(vm-press&letters/a&number) + same => n(loop),Background(jerry-intro) + same => n,WaitExten(15) + +exten => t,1,Goto(s,loop) + +exten => _X,1,NoOp() +;same => n,Playback(silence/1&you-entered) +;same => n,SayNumber(${EXTEN}) + same => n,Wait(1) + same => n,Goto(${EXTEN},100) + +exten => 1,100,NoOp() + same => n,Goto(extdn42whois,${CALLNUM},5) + same => n,Goto(menuend,1) + +exten => 2,100,NoOp() + same => n,ConfBridge(1,bridge1,user1,sample_user_menu) + same => n,Goto(menuend,1) + +exten => 3,100,NoOp() + same => n,Playback(your&number&is) + same => n,SayAlpha(${CALLERID(num)}) + same => n,Playback(silence/1&calling) + same => n,SayAlpha(${CALLNUM}) + same => n,Goto(menuend,1) + +exten => 4,100,NoOp() + same => n,Read(TMPNOM,z-external,1,,1,0.1) +;same => n,Playback(z-external) + same => n,Goto(menuend,1) + +exten => 5,100,NoOp() + same => n,Read(TMPNOM,z-macroform-cold_day,1,,1,0.1) +;same => n,Playback(z-macroform-cold_day) + same => n,Goto(menuend,1) + +exten => _X,100,NoOp() + same => n,Goto(menuend,1) + +exten => menuend,1,NoOp() + same => n,Wait(1) + same => n,Goto(s,loop) + +[extmyself] +exten => i,1,NoOp() + +exten => chanunavail,1,Playback(im-sorry&number-not-answering&please-try-call-later) +exten => chanunavail,2,Hangup() + +exten => 424036180001,5,Dial(PJSIP/REDACTED,300,m) +exten => 424036180002,5,Dial(PJSIP/REDACTED,300,m) +exten => 424036180003,5,Dial(PJSIP/REDACTED,300,m) +exten => 424036180004,5,Dial(PJSIP/REDACTED,300,m) +exten => 424036180005,5,Dial(PJSIP/REDACTED,300,m) +exten => _X.,6,GotoIf($["${DIALSTATUS}" = "CHANUNAVAIL"]?chanunavail,1:) +exten => 424036180000,5,Goto(extmymenu,${EXTEN},5) +exten => 424036183618,5,Playback(silence/1&your&number&is) + same => 6,SayAlpha(${CALLERID(num)}) + same => 7,Playback(silence/1) +;same => 8,SayAlpha(${CALLERID(name)}) +;same => 9,Playback(silence/1) +exten => 424036184242,5,Goto(extdn42whois,${EXTEN},5) +exten => _42403618XXXX,5,Playback(im-sorry&check-number-dial-again) + +[extpeers] +exten => _42403618XXXX,5,Answer() + same => n,Goto(extmyself,${EXTEN},5) + +exten => _42401332XXXX,5,NoOp() + same => n,Dial(PJSIP/${EXTEN}@nia) +;same => n,Dial(PJSIP/${EXTEN:-4}@nia) + +exten => _42403315XXXX,5,NoOp() + same => n,Dial(PJSIP/${EXTEN}@zane) + +exten => _42403088XXXX,5,NoOp() + same => n,Dial(PJSIP/${EXTEN}@sunnet) + +exten => _42401353XXXX,5,NoOp() + same => n,Dial(PJSIP/${EXTEN}@hertz) + +exten => _42400119XXXX,5,NoOp() + same => n,Dial(PJSIP/${EXTEN}@jrb0001) + +[jerry] +;exten => _X.,1,Set(CHANNEL(musicclass)=custom) +exten => _X.,1,NoOp() + +exten => _XXXX,2,Goto(42403618${EXTEN},1) +exten => _XXXXXXXX,2,Goto(4240${EXTEN},1) +exten => _X.,2,NoOp() + +exten => _X.,3,Goto(extpeers,${EXTEN},5) diff --git a/modules.conf b/modules.conf new file mode 100644 index 0000000..65d2ed3 --- /dev/null +++ b/modules.conf @@ -0,0 +1,83 @@ +; +; Asterisk configuration file +; +; Module Loader configuration file +; + +[modules] +autoload=yes +; +; Any modules that need to be loaded before the Asterisk core has been +; initialized (just after the logger has been initialized) can be loaded +; using 'preload'. This will frequently be needed if you wish to map all +; module configuration files into Realtime storage, since the Realtime +; driver will need to be loaded before the modules using those configuration +; files are initialized. +; +; An example of loading ODBC support would be: +;preload => res_odbc.so +;preload => res_config_odbc.so +; +; If you want, load the GTK console right away. +; Don't load the KDE console since +; it's not as sophisticated right now. +; +noload => pbx_gtkconsole.so +;load => pbx_gtkconsole.so +noload => pbx_kdeconsole.so +; +; Intercom application is obsoleted by +; chan_oss. Don't load it. +; +noload => app_intercom.so +; +; The 'modem' channel driver and its subdrivers are +; obsolete, don't load them. +; +noload => chan_modem.so +noload => chan_modem_aopen.so +noload => chan_modem_bestdata.so +noload => chan_modem_i4l.so +; +; Comment this out (after installing CAPI middleware and hardware +; drivers) if you have CAPI-able hardware and wish to use it in +; Asterisk. +; +noload => chan_capi.so +; +load => res_musiconhold.so +; +; Do not load load local channel drivers (using the system speaker) by default, +; they are not used in most installations and might block the sound hardware +; +noload => chan_alsa.so +noload => chan_console.so +noload => chan_oss.so +; +; Disable CDR logging to SQLite by default since it writes unconditionally to +; cdr.db without a way to rotate it. +; +noload => cdr_sqlite.so +; +; These conflict with app_directory.so and each other. +noload => app_directory_odbc.so +; +; Enable these if you want to configure Asterisk in a database +; +noload => res_config_odbc.so +noload => res_config_pgsql.so + +noload => chan_sip.so +;preload => codec_opus.so +;preload => format_ogg_opus.so +;preload => res_sorcery_config.so +;preload => res_format_attr_opus.so +;preload => codec_gsm.so +;preload => format_gsm.so +;preload => format_wav_gsm.so +;preload => codec_resample.so +; +; Module names listed in "global" section will have symbols globally +; exported to modules loaded after them. +; +[global] diff --git a/musiconhold.conf b/musiconhold.conf new file mode 100644 index 0000000..3baa2fc --- /dev/null +++ b/musiconhold.conf @@ -0,0 +1,7 @@ +[default] +mode=files +directory=moh + +;[custom] +;mode=files +;directory=/var/lib/asterisk/moh diff --git a/pjsip.conf b/pjsip.conf new file mode 100644 index 0000000..520502d --- /dev/null +++ b/pjsip.conf @@ -0,0 +1,144 @@ +[global] +type=global +user_agent=PBX + +[v4trans] +type=transport +protocol=udp +bind=0.0.0.0:5060 + +[v6trans] +type=transport +protocol=udp +bind=[::]:5060 + +;=============TEMPLATE============== + +[endpoint-template](!) +type=endpoint +context=jerry +;message_context=jerry-msg +allow=!all,ulaw,opus48,opus,speex,alaw +;direct_media=no +rtp_symmetric=no +force_rport=yes +rewrite_contact=yes + +[auth-template](!) +type=auth + +[aor-template](!) +type=aor +max_contacts=1 +remove_existing=yes + +;=============PHONES============== + +[REDACTED](endpoint-template) +auth = REDACTED +aors = REDACTED +allow=!all,opus48,opus,ulaw,speex,alaw +callerid=Jerry <424036180001> +[REDACTED](auth-template) +auth_type=userpass +username=REDACTED +password=REDACTED +[REDACTED](aor-template) + +[REDACTED](endpoint-template) +auth = REDACTED +aors = REDACTED +allow=!all,opus48,opus,ulaw,speex,alaw +callerid=Jerry <424036180002> +[REDACTED](auth-template) +auth_type=userpass +username=REDACTED +password=REDACTED +[REDACTED](aor-template) + +[REDACTED](endpoint-template) +auth = REDACTED +aors = REDACTED +allow=!all,opus48,opus,ulaw,speex,alaw +callerid=Jerry <424036180003> +[REDACTED](auth-template) +auth_type=userpass +username=REDACTED +password=REDACTED +[REDACTED](aor-template) + +[REDACTED](endpoint-template) +auth = REDACTED +aors = REDACTED +allow=!all,opus48,opus,ulaw,speex,alaw +callerid=Jerry <424036180004> +[REDACTED](auth-template) +auth_type=userpass +username=REDACTED +password=REDACTED +[REDACTED](aor-template) + +[REDACTED](endpoint-template) +auth = REDACTED +aors = REDACTED +allow=!all,opus48,opus,ulaw,speex,alaw +callerid=Jerry <424036180005> +[REDACTED](auth-template) +auth_type=userpass +username=REDACTED +password=REDACTED +[REDACTED](aor-template) + +;=============EXTERN============== + +;=============PEERS============== + +[nia](endpoint-template) +aors = nia +identify_by=ip +[nia](aor-template) +contact=sip:172.20.168.194:5160 +[nia] +type=identify +endpoint=nia +match=172.20.168.194 + +[zane](endpoint-template) +aors = zane +identify_by=ip +[zane](aor-template) +contact=sip:172.22.167.72:5060 +[zane] +type=identify +endpoint=zane +match=172.22.167.72 + +[sunnet](endpoint-template) +aors = sunnet +identify_by=ip +[sunnet](aor-template) +contact=sip:10.127.11.130:5060 +[sunnet] +type=identify +endpoint=sunnet +match=10.127.11.130 + +[hertz](endpoint-template) +aors = hertz +identify_by=ip +[hertz](aor-template) +contact=sip:172.20.29.73:5060 +[hertz] +type=identify +endpoint=hertz +match=172.20.29.73 + +[jrb0001](endpoint-template) +aors = jrb0001 +identify_by=ip +[jrb0001](aor-template) +contact=sip:[fd42:5d71:219:1003:216:3eff:fe9d:882f]:5060 +[jrb0001] +type=identify +endpoint=jrb0001 +match=fd42:5d71:219:1003:216:3eff:fe9d:882f diff --git a/whois.py b/whois.py new file mode 100644 index 0000000..336125a --- /dev/null +++ b/whois.py @@ -0,0 +1,137 @@ +#!/usr/bin/python + +import socket +import subprocess +from pathlib import Path +from sys import argv +import re +import shutil +from time import time + +WHOIS_SERVER = ('172.20.129.8', 43) +ASTROOT = Path("/var/lib/asterisk/sounds") +ERRSOUND = ASTROOT / "en" / "something-terribly-wrong.gsm" +ROOTDIR = Path("/var/tmp") +OUTDIR = ROOTDIR / "ast-dynamic" +OUTDIR.mkdir(exist_ok=True) + +def cleanup(): + try: + now = time() + for f in OUTDIR.iterdir(): + if f.is_file() and now - f.stat().st_mtime > 60.0: + f.unlink() + except Exception: + raise + +def espeak(to_speak, unid): + p1 = subprocess.run( + ["espeak-ng", "-v", "en-us", "--stdin", "-p", "80", "--stdout"], + input=str(to_speak).encode("utf-8"), + capture_output=True, + check=True, + timeout=10 + ) + unfile = f"{unid}.wav" + p2 = subprocess.run(['ffmpeg', '-hide_banner', '-i', "-", + '-vn', "-ac", "1", "-ar", "8000", "-y", + f"{OUTDIR / unfile}"], + input=p1.stdout, + capture_output=True, + check=True, + timeout=10 + ) + +def whois(arg): + try: + i = int(arg) + except ValueError: + raise ValueError(f"invalid autonomous system number {','.join(list(arg))}.") + else: + if 0 <= i <= 9999: + arg = f"AS424242{i:04d}" + elif 20000 <= i <= 29999: + arg = f"AS42424{i:05d}" + elif 70000 <= i <= 79999: + arg = f"AS42012{i:05d}" + else: + arg = f"AS{i}" + with socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM) as s: + s.settimeout(5) + s.connect(WHOIS_SERVER) + s.send((f"{arg}\r\n").encode("utf-8")) + r = bytes() + while len(_r := s.recv(1024)) != 0: + r += _r + r = r.decode('utf-8') + lines = r.split('\n') + target = None + for (i, line) in enumerate(lines[::-1]): + if line: + target = False + elif not line and target is False: + target = -i + break + lines = lines[target:] + def multiline_process(): + attr = "" + for i, line in enumerate(lines): + if not line: + continue + if line.startswith('%'): + continue + if m := re.match(r'^([\S]+):.*$', line): + attr = m.groups()[0] + assert attr + else: + assert attr + lines[i] = f"{attr}:{lines[i]}" + try: + multiline_process() + except Exception as e: + raise + whois_filter = lambda x: any([x.startswith(field) for field in {'remarks', 'nserver', 'descr', 'ds-rdata', 'mp-import', 'mp-export'}]) + lines = [l for l in lines if l and not whois_filter(l)] + lines_new = list() + repl = { + "aut-num": "Autonomous System Number", + "as-name": "Autonomous System Name", + "admin-c": "Administrative Contact", + "tech-c": "Technical Contact", + "mnt-by": "Maintained by", + } + for l in lines: + if not l.strip(): + continue + elif l.startswith('%'): + continue + elif l.startswith("aut-num:"): + _p, _s = l.split() + _s = ",".join(list(_s)).replace("A,S,", "") + l = f"{_p} {_s}" + for fr, to in repl.items(): + if l.startswith(f"{fr}:"): + l = l.replace(f"{fr}:", f"{to}:", 1) + lines_new.append(l) + + if not lines_new: + lines_new.append(f"There is no such Autonomous System {','.join(list(arg)).replace('A,S,', '')}") + else: + lines_new.insert(0, "Found the following who is record from the dn42 registry.") + return ".\n".join(lines_new)+"." + +def main(): + # argv[1]: as number + # argv[2]: unique identifier + cleanup() + try: + ret = whois(argv[1]) + except Exception as e: + ret = f"error, {e}" + try: + espeak(ret, argv[2]) + except Exception: + outpath = OUTDIR / f"{argv[2]}{ERRSOUND.suffix}" + shutil.copy(ERRSOUND, outpath) +if __name__ == "__main__": + main()