Python AGI script

Hi all, I am trying to use (for the first time) external script (in pyhton) and i would like to ask you some help on it. I don’t really know if is a python issue or something else, anyway someone that has experience can help.
So far i can send call to AGI Script and from debug i see that script stop just before

data = parse_csv(csv_file)

Can you help me understand why?

Here my extension:

[advanced_routing]
exten => start,1,NoOp(*** ADVANCED ROUTING STARTED ***)
same => n,AGI(parse_csv.py,numeri.csv)
same => n,NoOp(Il nuovo DISPLAY NAME: ${NEW_CALLERIDNAME})
same => n,NoOp(Il nuovo numero da chiamare: ${NEW_CALLEDNUM})
same => n,Hangup()

Here script:

import sys
import csv
from asterisk.agi import *

def parse_csv(csv_file):
data = {}
with open(csv_file, ‘r’) as file:
reader = csv.DictReader(file, delimiter=‘;’)
for row in reader:
data[row[‘Nummer’]] = {‘Name’: row[‘Name’], ‘InternalNummer’: row[‘InternalNummer’]}
return data

def update_caller_info(calling_number, data):
if calling_number in data:
name = data[calling_number][‘Name’]
internal_number = data[calling_number][‘InternalNummer’]
return name, internal_number
else:
return None, None

def main():

if len(sys.argv) != 2:
    print("Usage: ./agi_script.py <csv_file>")
    sys.exit(1)

agi = AGI()
agi.verbose(" python agi started ")

calling_number = agi.env["agi_callerid"]
csv_file = sys.argv[1]

data = parse_csv(csv_file)
name, internal_number = update_caller_info(calling_number, data)
agi.verbose('Questo è il nuovo nome: ', name)
agi.verbose('Questo è il nuovo numero da chiamare: ', internal_number)

if name and internal_number:
    agi.set_variable('NEW_CALLERIDNAME', name)
    agi.set_variable('NEW_CALLEDNUM', internal_number)
else:
    agi.verbose("CALLERID not updated: Caller number not found in the CSV file.")

if name == “main”:
main()

and finally debug:

== Using SIP RTP CoS mark 5
> 0x7f26140305f0 – Strict RTP learning after remote address set to: 192.168.25.20:7224
– Executing [+390471547200@from-trunk-sip-carrier:1] NoOp(“SIP/carrier-0000000d”, "Chiamata entrante dal TRUNK SIP: *** carrier *** ") in new stack
– Executing [+390471547200@from-trunk-sip-carrier:2] Goto(“SIP/carrier-0000000d”, “from-trunk,+390471547200,1”) in new stack
– Goto (from-trunk,+390471547200,1)
– Executing [+390471547200@from-trunk:1] Set(“SIP/carrier-0000000d”, “CHANNEL(tonezone)=it”) in new stack
– Executing [+390471547200@from-trunk:2] Set(“SIP/carrier-0000000d”, “CHANNEL(musicclass)=none”) in new stack
– Executing [+390471547200@from-trunk:3] Goto(“SIP/carrier-0000000d”, “advanced_routing,start,1”) in new stack
– Goto (advanced_routing,start,1)
– Executing [start@advanced_routing:1] NoOp(“SIP/carrier-0000000d”, “*** ADVANCED ROUTING STARTED ***”) in new stack
– Executing [start@advanced_routing:2] AGI(“SIP/carrier-0000000d”, “parse_csv.py,numeri.csv”) in new stack
– Launched AGI Script /var/lib/asterisk/agi-bin/parse_csv.py
<SIP/carrier-0000000d>AGI Tx >> agi_request: parse_csv.py
<SIP/carrier-0000000d>AGI Tx >> agi_channel: SIP/carrier-0000000d
<SIP/carrier-0000000d>AGI Tx >> agi_language: en
<SIP/carrier-0000000d>AGI Tx >> agi_type: SIP
<SIP/carrier-0000000d>AGI Tx >> agi_uniqueid: 1713279265.13
<SIP/carrier-0000000d>AGI Tx >> agi_version: 18.22.0
<SIP/carrier-0000000d>AGI Tx >> agi_callerid: +390522375511
<SIP/carrier-0000000d>AGI Tx >> agi_calleridname: CARRIER
<SIP/carrier-0000000d>AGI Tx >> agi_callingpres: 0
<SIP/carrier-0000000d>AGI Tx >> agi_callingani2: 0
<SIP/carrier-0000000d>AGI Tx >> agi_callington: 0
<SIP/carrier-0000000d>AGI Tx >> agi_callingtns: 0
<SIP/carrier-0000000d>AGI Tx >> agi_dnid: +390471547200
<SIP/carrier-0000000d>AGI Tx >> agi_rdnis: unknown
<SIP/carrier-0000000d>AGI Tx >> agi_context: advanced_routing
<SIP/carrier-0000000d>AGI Tx >> agi_extension: start
<SIP/carrier-0000000d>AGI Tx >> agi_priority: 2
<SIP/carrier-0000000d>AGI Tx >> agi_enhanced: 0.0
<SIP/carrier-0000000d>AGI Tx >> agi_accountcode:
<SIP/carrier-0000000d>AGI Tx >> agi_threadid: 139801326724864
<SIP/carrier-0000000d>AGI Tx >> agi_arg_1: numeri.csv
<SIP/carrier-0000000d>AGI Tx >>
<SIP/carrier-0000000d>AGI Rx << VERBOSE “#### python agi started ####” 1
parse_csv.py,numeri.csv: #### python agi started ####
<SIP/carrier-0000000d>AGI Tx >> 200 result=1
– <SIP/carrier-0000000d>AGI Script parse_csv.py completed, returning 0
– Executing [start@advanced_routing:3] NoOp(“SIP/carrier-0000000d”, "Il nuovo DISPLAY NAME: ") in new stack
– Executing [start@advanced_routing:4] NoOp(“SIP/carrier-0000000d”, "Il nuovo numero da chiamare: ") in new stack
– Executing [start@advanced_routing:5] Hangup(“SIP/carrier-0000000d”, “”) in new stack
== Spawn extension (advanced_routing, start, 5) exited non-zero on ‘SIP/carrier-0000000d’

regards

One of the first things you need to do when using AGI is to ensure your script works locally outside of Asterisk. Additionally, instead of using relative paths inside the script, make sure you use the full path so that any external files referenced in the script can be found when running the AGI. Also, ensure that your permissions are correctly set, which I believe they are in your case

Regarding testing your script outside of Asterisk, you can’t directly test AGI applications outside the Asterisk environment. However, you can simulate their behavior by replacing them with verbose messages. This allows you to ensure that the script runs correctly. Once it passes all these local tests, you can then integrate your AGI script into Asterisk.

It’s hopeless trying to debug Python code without being able to see exception tracebacks. These messages go to stderr. Unfortunately, when Asterisk spawns an AGI process, it redirects stderr to /dev/null. You can redirect it back to somewhere sensible by putting something like this at the top of your code (choose any suitable filename/directory):

log = open("/tmp/agi_try.log", "w")
os.dup2(log.fileno(), 2)
log.close()
del log

Now you have a file which should hopefully contain some clues after your program dies.

Another option is to use FastAGI. That way you run your own process, and you get to control where all its I/O goes.

Thank you guys, @ldo and @ambiorixg12 you helped me a lot. Adding the log part to the script I saw that the problem was precisely the path to the csv file.
The strange thing is that if run the script from CLI it worked correctly. Running from asterisk gave the problem.
Thank you.

It would be good if there were a way to configure Asterisk to redirect stderr for the AGI processes it spawns to some logfile or pipe or something. It could display the messages on its console.

1 Like