Skip to main content

BasicAGI

A Python library for creating Asterisk Gateway Interface (AGI) applications.

PyPI version Python versions License: MIT

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 channel
  • hangup(channel=None) - Hang up the channel
  • exec(application, *args) - Execute an Asterisk application

Audio & Media

  • stream_file(audio_file, escape_digits="", offset=None) - Stream an audio file
  • get_option(audio_file, escape_digits="", timeout=0) - Stream file and wait for digit
  • control_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 string
  • say_digits(number, escape_digits="") - Say digits one by one
  • say_number(number, escape_digits="", gender="") - Say number
  • say_date(date, escape_digits="") - Say date
  • say_time(time, escape_digits="") - Say time
  • say_datetime(time, escape_digits="", format="", timezone="") - Say date and time

Variables & Data

  • get_variable(name) - Get channel variable
  • set_variable(name, value="") - Set channel variable
  • get_agi_var(keyname) - Get AGI environment variable
  • get_all_agi_vars() - Get all AGI variables
  • set_cdr_variable(name, value="") - Set CDR variable

Database Operations

  • database_get(family, key) - Get value from Asterisk database
  • database_put(family, key, value) - Put value into Asterisk database
  • database_del(family, key) - Delete key from database
  • database_deltree(family, key=None) - Delete database tree

Call Control

  • set_callerid(number, name=None) - Set caller ID
  • set_context(context) - Set context for continuation
  • set_extension(extension) - Set extension for continuation
  • set_priority(priority) - Set priority for continuation
  • channel_status(channel=None) - Get channel status

User Input

  • get_data(audio_file, timeout=None, max_digits=None) - Get data from user
  • wait_for_digit(timeout=-1) - Wait for digit press
  • receive_char(timeout=0) - Receive character from channel
  • receive_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

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:

  1. Sort imports: ruff check --select I --fix basicagi tests examples
  2. Format code: ruff format basicagi tests examples
  3. Check linting: ruff check basicagi tests examples
  4. 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.

  • Asterisk - The open source PBX
  • pyst2 - Python library for Asterisk

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)

Source distribution for basicagi 1.0.1
File Size Uploaded Provenance
basicagi-1.0.1.tar.gz 15.2 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for basicagi 1.0.1
File Interpreter ABI Platform
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.

Release history Release notifications | RSS feed

This release

1.0.1 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page