A Python library for creating Asterisk Gateway Interface (AGI) applications.
The Asterisk Gateway Interface (AGI) is a powerful interface that allows external programs to control call flow in Asterisk PBX. AGI scripts can be written in any language and provide a way to add custom logic to your dialplan.
- Simple and intuitive API - Easy-to-use Python interface for AGI commands
- Comprehensive command support - Covers all major AGI commands and functions
- Robust variable handling - Access to all AGI environment variables
- Audio and media control - Stream files, play prompts, and control media
- Database operations - Read/write to Asterisk database
- Call control - Answer, hangup, transfer, and redirect calls
- TTS and speech - Text-to-speech and digit/number speaking functions
- Debug support - Built-in debugging capabilities
- Production ready - Used in real-world VoIP applications
Install BasicAGI using pip:
pip install basicagiHere's a simple AGI script that answers a call, plays a welcome message, and hangs up:
#!/usr/bin/env python3
from basicagi import BasicAGI
def main():
# Create AGI instance
agi = BasicAGI()
# Answer the call
agi.answer()
# Play welcome message
agi.stream_file("welcome")
# Say the caller ID
caller_id = agi.get_agi_var("callerid")
if caller_id:
agi.say_digits(caller_id)
# Hang up
agi.hangup()
if __name__ == "__main__":
main()from basicagi import BasicAGI
agi = BasicAGI()
# Answer the call
agi.answer()
# Get caller information
caller_id = agi.get_agi_var("callerid")
channel = agi.get_agi_var("channel")
# Set caller ID
agi.set_callerid("1234567890", "My Company")
# Play audio file with digit interruption
digit = agi.stream_file("menu", "123456789*0#")
# Process user input
if digit == "1":
agi.stream_file("option1")
elif digit == "2":
agi.stream_file("option2")
else:
agi.stream_file("invalid")
agi.hangup()from basicagi import BasicAGI
def handle_ivr():
agi = BasicAGI()
agi.answer()
while True:
# Play main menu
digit = agi.get_option("main-menu", "123456789*0#", 5000)
if digit == "1":
# Sales department
agi.stream_file("connecting-sales")
agi.exec("Dial", "PJSIP/sales@internal")
break
elif digit == "2":
# Support department
agi.stream_file("connecting-support")
agi.exec("Dial", "PJSIP/support@internal")
break
elif digit == "0":
# Operator
agi.stream_file("connecting-operator")
agi.exec("Dial", "PJSIP/operator@internal")
break
else:
# Invalid option
agi.stream_file("invalid-option")
continue
agi.hangup()
if __name__ == "__main__":
handle_ivr()from basicagi import BasicAGI
agi = BasicAGI()
agi.answer()
# Store caller information
caller_id = agi.get_agi_var("callerid")
agi.database_put("callers", caller_id, "last_call_time", str(time.time()))
# Retrieve stored data
last_call = agi.database_get("callers", f"{caller_id}/last_call_time")
if last_call:
agi.verbose(f"Caller {caller_id} last called at {last_call}")
# Set CDR variables
agi.set_cdr_variable("custom_field", "important_call")
agi.hangup()from basicagi import BasicAGI
import time
agi = BasicAGI()
agi.answer()
# Say current time
current_time = int(time.time())
agi.say_datetime(current_time, "", "ABdY 'digits/at' IMp")
# Say a number
agi.say_number(12345)
# Spell out text
agi.say_alpha("HELLO")
# Say individual digits
agi.say_digits("567890")
agi.hangup()Access Asterisk environment variables easily:
from basicagi import BasicAGI
agi = BasicAGI()
# Get specific variable
caller_id = agi.get_agi_var("callerid")
channel = agi.get_agi_var("channel")
extension = agi.get_agi_var("extension")
# Get all available variables
all_vars = agi.get_all_agi_vars()
for key, value in all_vars.items():
agi.verbose(f"{key}: {value}")
# Get list of all variable keys
var_keys = agi.get_agi_var_keys()Enable debugging by setting the AGI_DEBUG environment variable:
export AGI_DEBUG=true
python your_agi_script.pyOr in your Asterisk dialplan:
exten => 123,1,Set(AGI_DEBUG=true)
same => n,AGI(your_script.py)
Add your AGI script to your Asterisk dialplan:
[incoming]
exten => _X.,1,AGI(your_script.py)
same => n,Hangup()
Make sure your AGI script is executable:
chmod +x /var/lib/asterisk/agi-bin/your_script.pyPlace your scripts in the Asterisk AGI directory (typically /var/lib/asterisk/agi-bin/)
answer()- Answer the channelhangup(channel=None)- Hang up the channelexec(application, *args)- Execute an Asterisk application
stream_file(audio_file, escape_digits="", offset=None)- Stream an audio fileget_option(audio_file, escape_digits="", timeout=0)- Stream file and wait for digitcontrol_stream_file(...)- Stream file with control (FF/RW/pause)set_music(on_off, music_class="")- Enable/disable music on hold
say_alpha(string, escape_digits="")- Say character stringsay_digits(number, escape_digits="")- Say digits one by onesay_number(number, escape_digits="", gender="")- Say numbersay_date(date, escape_digits="")- Say datesay_time(time, escape_digits="")- Say timesay_datetime(time, escape_digits="", format="", timezone="")- Say date and time
get_variable(name)- Get channel variableset_variable(name, value="")- Set channel variableget_agi_var(keyname)- Get AGI environment variableget_all_agi_vars()- Get all AGI variablesset_cdr_variable(name, value="")- Set CDR variable
database_get(family, key)- Get value from Asterisk databasedatabase_put(family, key, value)- Put value into Asterisk databasedatabase_del(family, key)- Delete key from databasedatabase_deltree(family, key=None)- Delete database tree
set_callerid(number, name=None)- Set caller IDset_context(context)- Set context for continuationset_extension(extension)- Set extension for continuationset_priority(priority)- Set priority for continuationchannel_status(channel=None)- Get channel status
get_data(audio_file, timeout=None, max_digits=None)- Get data from userwait_for_digit(timeout=-1)- Wait for digit pressreceive_char(timeout=0)- Receive character from channelreceive_text(timeout=0)- Receive text from channel
# Create virtual environment
uv venv .venv
# Install package in development mode with dependencies
source .venv/bin/activate && uv pip install -e .
# Install test and development dependencies
source .venv/bin/activate && uv pip install pytest pytest-cov ruffBefore submitting any changes, ensure your code passes all quality checks:
# Sort imports with ruff
source .venv/bin/activate && ruff check --select I --fix basicagi tests examples
# Format code with ruff
source .venv/bin/activate && ruff format basicagi tests examples
# Run linter with ruff
source .venv/bin/activate && ruff check basicagi tests examples
# Fix linting issues automatically
source .venv/bin/activate && ruff check --fix basicagi tests examples
# Run tests
source .venv/bin/activate && python -m pytest tests/
# Run tests with coverage
source .venv/bin/activate && python -m pytest tests/ --cov=basicagi --cov-report=htmlAutomate code quality checks with a Git pre-commit hook:
# Install the Git pre-commit hook
./git-hooks-setup.shThe Git pre-commit hook will automatically run before each commit and will:
- Sort imports with ruff
- Format code with ruff
- Run linting with ruff
- Run tests if test files are modified
To bypass the hook for a specific commit: git commit --no-verify
If not using pre-commit hooks, run these commands before committing:
- Sort imports:
ruff check --select I --fix basicagi tests examples - Format code:
ruff format basicagi tests examples - Check linting:
ruff check basicagi tests examples - Run tests:
python -m pytest tests/
This ensures consistent code style and catches issues early.
Contributions are welcome! Please feel free to submit a Pull Request.
This project is licensed under the MIT License - see the LICENSE file for details.
For bug reports and feature requests, please use the GitHub Issues page.