BasicAGI
A Python library for creating Asterisk Gateway Interface (AGI) applications.
What is AGI?
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.
Features
- 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
Installation
Install BasicAGI using pip:
pip install basicagi
Quick Start
Here'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()
Usage Examples
Basic Call Handling
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()
Interactive Voice Response (IVR)
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()
Database Operations
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()
Text-to-Speech and Number Handling
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()
AGI Environment Variables
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()
Debugging
Enable debugging by setting the AGI_DEBUG environment variable:
export AGI_DEBUG=true
python your_agi_script.py
Or in your Asterisk dialplan:
exten => 123,1,Set(AGI_DEBUG=true)
same => n,AGI(your_script.py)
Asterisk Integration
Using in Dialplan
Add your AGI script to your Asterisk dialplan:
[incoming]
exten => _X.,1,AGI(your_script.py)
same => n,Hangup()
File Permissions
Make sure your AGI script is executable:
chmod +x /var/lib/asterisk/agi-bin/your_script.py
AGI Directory
Place your scripts in the Asterisk AGI directory (typically /var/lib/asterisk/agi-bin/)
API Reference
Core Methods
answer()- Answer the channelhangup(channel=None)- Hang up the channelexec(application, *args)- Execute an Asterisk application
Audio & Media
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
Speech & TTS
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
Variables & Data
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 Operations
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
Call Control
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
User Input
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
Development
Setup Development Environment
# 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 ruff
Code Quality and Pre-commit Checks
Before 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=html
Git Pre-commit Hook (Recommended)
Automate code quality checks with a Git pre-commit hook:
# Install the Git pre-commit hook
./git-hooks-setup.sh
The 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
Manual Pre-commit Workflow
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.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
This project is licensed under the MIT License - see the LICENSE file for details.
Related Projects
Support
For bug reports and feature requests, please use the GitHub Issues page.
Metadata
Release files for basicagi 1.0.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| basicagi-1.0.1.tar.gz | 15.2 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| basicagi-1.0.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 24.9 kB
Release files / basicagi-1.0.1.tar.gz
| Download URL | basicagi-1.0.1.tar.gz |
|---|---|
| Size | 15.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a9cdedf9a5e40c9b6371b83bb09109976855f92841e0f16ac60246755751b9cd
|
|
BLAKE2b-256 checksum How to use checksums |
ab6318fa708f93c453a846d4163e199e06e5e22aa3ff7a4e160fb79a015cdafb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Release files / basicagi-1.0.1-py3-none-any.whl
| Download URL | basicagi-1.0.1-py3-none-any.whl |
|---|---|
| Size | 9.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
5326cd25a665f8bdfbb2ca82d2c1a83d4772495bc20e752d063469b834dbc0d5
|
|
BLAKE2b-256 checksum How to use checksums |
1037c1d813499e066d30eafb2802c8ba6a6018cc16e559226e4f7080b793da0a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Security for basicagi 1.0.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.