Skip to main content

Python SDK for PocketPing - real-time customer chat with mobile notifications

Project description

PocketPing Python SDK

Python SDK for PocketPing - real-time customer chat with mobile notifications.

Installation

pip install pocketping

# With all optional dependencies
pip install pocketping[all]

# Or pick what you need
pip install pocketping[fastapi]      # FastAPI integration
pip install pocketping[telegram]     # Telegram bridge
pip install pocketping[discord]      # Discord bridge
pip install pocketping[slack]        # Slack bridge
pip install pocketping[ai]           # AI providers (OpenAI, Gemini, Claude)

Quick Start with FastAPI

from contextlib import asynccontextmanager
from fastapi import FastAPI
from pocketping import PocketPing
from pocketping.fastapi import create_router, lifespan_handler, add_cors_middleware
from pocketping.bridges.telegram import TelegramBridge
from pocketping.ai import OpenAIProvider
import os

# Initialize PocketPing
pp = PocketPing(
    welcome_message="Hi! 👋 How can we help you today?",
    ai_provider=OpenAIProvider(api_key=os.getenv("OPENAI_API_KEY")),
    ai_takeover_delay=300,  # 5 minutes before AI takes over
    bridges=[
        TelegramBridge(
            bot_token=os.getenv("TELEGRAM_BOT_TOKEN"),
            forum_chat_id=os.getenv("TELEGRAM_FORUM_CHAT_ID"),  # Supergroup with topics
        ),
    ],
)

@asynccontextmanager
async def lifespan(app: FastAPI):
    async with lifespan_handler(pp):
        yield

app = FastAPI(lifespan=lifespan)
add_cors_middleware(app)

# Mount PocketPing routes
app.include_router(create_router(pp), prefix="/pocketping")

@app.get("/")
def home():
    return {"message": "PocketPing is running!"}

Bridges

Telegram

Two modes available:

Forum Topics Mode (Recommended for Teams)

Each conversation gets its own topic - perfect for multiple operators:

from pocketping.bridges.telegram import TelegramBridge

bridge = TelegramBridge(
    bot_token="your_bot_token",
    forum_chat_id="-100123456789",  # Supergroup with topics enabled
    show_url=True,
    show_metadata=True,
)

Setup:

  1. Create a Telegram group
  2. Convert to Supergroup (Settings > Group Type)
  3. Enable Topics (Settings > Topics)
  4. Add your bot as admin with "Manage Topics" permission
  5. Get the chat_id (starts with -100)

Benefits:

  • Each visitor = separate topic (no message mixing)
  • Just type in the topic to reply (no swipe-reply needed)
  • All team members see all conversations
  • /close command to mark conversations as done

Legacy Mode (Single Operator)

All messages in one chat, reply-based:

bridge = TelegramBridge(
    bot_token="your_bot_token",
    chat_ids=["your_chat_id"],  # Can be string or list
    show_url=True,
)

Commands:

  • /online - Mark yourself as available
  • /offline - Mark yourself as away
  • /status - View status
  • /close - Close conversation (forum mode only)

Discord

Two modes available:

Thread Mode (Default, Recommended for Teams)

Each conversation gets its own thread:

from pocketping.bridges.discord import DiscordBridge

bridge = DiscordBridge(
    bot_token="your_bot_token",
    channel_id=123456789,  # Your channel ID (int)
    use_threads=True,  # Default
    show_url=True,
    show_metadata=True,
)

Setup:

  1. Create a Discord bot at https://discord.com/developers/applications
  2. Enable MESSAGE CONTENT INTENT in Bot settings
  3. Add permissions: Send Messages, Create Public Threads, Send Messages in Threads, Add Reactions
  4. Invite bot and get channel ID (Developer Mode > Right-click > Copy ID)

Benefits:

  • Each visitor = separate thread (no message mixing)
  • Just type in the thread to reply
  • All team members see all conversations
  • !close command to archive threads

Legacy Mode (Single Operator)

All messages in channel, reply-based:

bridge = DiscordBridge(
    bot_token="your_bot_token",
    channel_id=123456789,
    use_threads=False,
)

Commands:

  • !online - Mark yourself as available
  • !offline - Mark yourself as away
  • !status - View status
  • !close - Close conversation (thread mode only)

Slack

from pocketping.bridges.slack import SlackBridge

bridge = SlackBridge(
    bot_token="xoxb-your-bot-token",
    app_token="xapp-your-app-token",  # For Socket Mode
    channel_id="C0123456789",
    show_url=True,
)

Mention the bot with commands:

  • @PocketPing online - Mark yourself as available
  • @PocketPing offline - Mark yourself as away
  • @PocketPing status - View status

Reply in thread to respond to users.

AI Providers

OpenAI

from pocketping.ai import OpenAIProvider

ai = OpenAIProvider(
    api_key="sk-...",
    model="gpt-4o-mini",  # default
)

Google Gemini

from pocketping.ai import GeminiProvider

ai = GeminiProvider(
    api_key="your_api_key",
    model="gemini-1.5-flash",  # default
)

Anthropic Claude

from pocketping.ai import AnthropicProvider

ai = AnthropicProvider(
    api_key="sk-ant-...",
    model="claude-sonnet-4-20250514",  # default
)

Custom System Prompt

pp = PocketPing(
    ai_provider=OpenAIProvider(api_key="..."),
    ai_system_prompt="""
    You are a helpful support assistant for Acme Inc.
    Our products include: Widget Pro, Widget Basic, and Widget Enterprise.
    Be friendly and concise. If you don't know something, offer to connect them with a human.
    """,
    ai_takeover_delay=180,  # 3 minutes
)

IP Filtering

Block or allow specific IP addresses or CIDR ranges:

from pocketping import PocketPing, IpFilterConfig

pp = PocketPing(
    ip_filter=IpFilterConfig(
        enabled=True,
        mode='blocklist',  # 'allowlist' | 'blocklist' | 'both'
        blocklist=[
            '203.0.113.0/24',   # CIDR range
            '198.51.100.50',    # Single IP
        ],
        allowlist=[
            '10.0.0.0/8',       # Internal network
        ],
        log_blocked=True,      # Log blocked requests (default: True)
        blocked_status_code=403,
        blocked_message='Forbidden',
    ),
)

# Or with a custom filter function
def my_filter(ip: str, request) -> bool | None:
    # Return True to allow, False to block, None to defer to list-based filtering
    if ip.startswith('192.168.'):
        return True  # Always allow local
    return None  # Use blocklist/allowlist

pp = PocketPing(
    ip_filter=IpFilterConfig(
        enabled=True,
        mode='blocklist',
        custom_filter=my_filter,
    ),
)

Modes

Mode Behavior
blocklist Block IPs in blocklist, allow all others (default)
allowlist Only allow IPs in allowlist, block all others
both Allowlist takes precedence, then blocklist is applied

CIDR Support

The SDK supports CIDR notation for IP ranges:

  • Single IP: 192.168.1.1 (treated as /32)
  • Class C: 192.168.1.0/24 (256 addresses)
  • Class B: 172.16.0.0/16 (65,536 addresses)
  • Class A: 10.0.0.0/8 (16M addresses)

Manual IP Check

# Check IP manually
result = pp.check_ip_filter('192.168.1.50')
# result: IpFilterResult(allowed=bool, reason=str, matched_rule=str|None)

# Get client IP from request headers
client_ip = pp.get_client_ip(request.headers)
# Checks: CF-Connecting-IP, X-Real-IP, X-Forwarded-For

Presence Detection

The ai_takeover_delay setting controls how long to wait before AI takes over:

  1. Visitor sends a message
  2. Timer starts
  3. If operator responds → Timer resets, AI stays inactive
  4. If ai_takeover_delay seconds pass with no operator response → AI takes over
  5. If operator responds after AI takeover → AI becomes inactive again
pp = PocketPing(
    ai_provider=OpenAIProvider(api_key="..."),
    ai_takeover_delay=300,  # 5 minutes (default)
)

Custom Storage

Implement the Storage interface for persistence:

from pocketping import Storage, Session, Message

class PostgresStorage(Storage):
    async def create_session(self, session: Session) -> None:
        # Your implementation
        pass

    async def get_session(self, session_id: str) -> Session | None:
        # Your implementation
        pass

    # ... implement other methods

pp = PocketPing(storage=PostgresStorage())

Events / Callbacks

def on_new_session(session):
    print(f"New session: {session.id}")

def on_message(message, session):
    print(f"Message from {message.sender}: {message.content}")

pp = PocketPing(
    on_new_session=on_new_session,
    on_message=on_message,
)

Custom Events

PocketPing supports bidirectional custom events between your website and backend. This enables powerful interactions like triggering alerts, sending offers, or reacting to user behavior.

Listening for Events (Widget → Backend)

Subscribe to events triggered from the widget:

from pocketping import PocketPing, CustomEvent, Session

pp = PocketPing()

# Using callback in config
def handle_event(event: CustomEvent, session: Session):
    print(f"Event {event.name} from session {session.id}")
    print(f"Data: {event.data}")

pp = PocketPing(on_event=handle_event)

# Or using decorator-style subscription
@pp.on_event('clicked_pricing')
async def on_pricing_click(event: CustomEvent, session: Session):
    print(f"User interested in: {event.data.get('plan')}")
    # Notify sales team, log to analytics, etc.

# Subscribe to all events with wildcard
@pp.on_event('*')
async def log_all_events(event: CustomEvent, session: Session):
    print(f"Event: {event.name} | Data: {event.data}")

# Unsubscribe when needed
pp.off_event('clicked_pricing', on_pricing_click)

Sending Events (Backend → Widget)

Send events to specific sessions or broadcast to all:

# Send to a specific session
await pp.emit_event(
    session_id='session-123',
    event_name='show_offer',
    data={'discount': 20, 'code': 'SAVE20'}
)

# Broadcast to all connected sessions
await pp.broadcast_event(
    event_name='announcement',
    data={'message': 'New feature launched!'}
)

Event Structure

from pocketping import CustomEvent

event = CustomEvent(
    name='clicked_pricing',           # Event name
    data={'plan': 'pro', 'page': '/pricing'},  # Optional payload
    timestamp=datetime.utcnow(),      # Auto-set
    session_id='session-123',         # Set by SDK when from widget
)

Use Cases

Event Direction Use Case
clicked_pricing Widget → Backend Alert sales when visitor shows interest
error_occurred Widget → Backend Get notified of frontend errors
cart_abandoned Widget → Backend Trigger follow-up actions
show_offer Backend → Widget Display personalized discount
request_callback Backend → Widget Show callback scheduling modal
announcement Backend → Widget System-wide notification

Bridge Integration

Custom events are automatically forwarded to all configured bridges (Telegram, Discord, Slack). Events appear with full context:

⚡ Custom Event

📌 Event: clicked_pricing
{
  "plan": "pro",
  "source": "homepage"
}

Session: abc123...

Webhook Forwarding

Forward all events to your own webhook for integrations with Zapier, Make, n8n, or custom backends:

pp = PocketPing(
    # Forward events to your webhook
    webhook_url='https://your-server.com/pocketping-events',
    webhook_secret='your-hmac-secret',  # Optional: adds X-PocketPing-Signature header
    webhook_timeout=5.0,  # Timeout in seconds (default: 5.0)
)

Webhook payload:

{
  "event": {
    "name": "clicked_pricing",
    "data": { "plan": "pro" },
    "timestamp": "2026-01-21T00:00:00.000Z",
    "sessionId": "sess_abc123"
  },
  "session": {
    "id": "sess_abc123",
    "visitorId": "visitor_xyz",
    "metadata": { "url": "...", "country": "France" }
  },
  "sentAt": "2026-01-21T00:00:00.000Z"
}

Verifying signatures:

import hmac
import hashlib

def verify_signature(body: bytes, signature: str, secret: str) -> bool:
    expected = hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
    return signature == f"sha256={expected}"

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pocketping-1.2.0.tar.gz (43.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pocketping-1.2.0-py3-none-any.whl (43.8 kB view details)

Uploaded Python 3

File details

Details for the file pocketping-1.2.0.tar.gz.

File metadata

  • Download URL: pocketping-1.2.0.tar.gz
  • Upload date:
  • Size: 43.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for pocketping-1.2.0.tar.gz
Algorithm Hash digest
SHA256 dbeff690ea21d995247dde16aad433bf2cb8863e052c0874b1c4cf9b1fdde303
MD5 ed9d23bf6f2823edcfe9fe4b8f883cad
BLAKE2b-256 339b090b9b31a4000448c6c505131c475049c3974828cfc91915c1d2e8ca301e

See more details on using hashes here.

Provenance

The following attestation bundles were made for pocketping-1.2.0.tar.gz:

Publisher: release.yml on Ruwad-io/pocketping

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pocketping-1.2.0-py3-none-any.whl.

File metadata

  • Download URL: pocketping-1.2.0-py3-none-any.whl
  • Upload date:
  • Size: 43.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for pocketping-1.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 d971234b20f2740d0ddc4c079c0d693807f5688e52e413e06f853cd5b3132892
MD5 497a83d3135afcfa953f3f5b9f9ec7b8
BLAKE2b-256 1a436837fe1da87629a3f1300739a48fceb74d5deb243f9f81fa82511715b0e7

See more details on using hashes here.

Provenance

The following attestation bundles were made for pocketping-1.2.0-py3-none-any.whl:

Publisher: release.yml on Ruwad-io/pocketping

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page