A Python library for Asterisk Gateway Interface (AGI) applications
Project description
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.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file basicagi-1.0.1.tar.gz.
File metadata
- Download URL: basicagi-1.0.1.tar.gz
- Upload date:
- Size: 15.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a9cdedf9a5e40c9b6371b83bb09109976855f92841e0f16ac60246755751b9cd
|
|
| MD5 |
b2e836e6ebdd9bf8f919698b12c490c5
|
|
| BLAKE2b-256 |
ab6318fa708f93c453a846d4163e199e06e5e22aa3ff7a4e160fb79a015cdafb
|
File details
Details for the file basicagi-1.0.1-py3-none-any.whl.
File metadata
- Download URL: basicagi-1.0.1-py3-none-any.whl
- Upload date:
- Size: 9.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5326cd25a665f8bdfbb2ca82d2c1a83d4772495bc20e752d063469b834dbc0d5
|
|
| MD5 |
03a845edb42c5f0a87d9fb780e703a2d
|
|
| BLAKE2b-256 |
1037c1d813499e066d30eafb2802c8ba6a6018cc16e559226e4f7080b793da0a
|