Skip to main content

Python SDK

Integrate PocketPing into your Python backend.

Installation​

pip install pocketping

Quick Start​

FastAPI​

from fastapi import FastAPI
from pocketping import PocketPing
from pocketping.bridges import TelegramBridge

app = FastAPI()

pp = PocketPing(
bridges=[
TelegramBridge(
bot_token="YOUR_BOT_TOKEN",
chat_id="YOUR_CHAT_ID"
)
]
)

# Mount routes at /pocketping
pp.mount_fastapi(app, prefix="/pocketping")

# Run: uvicorn main:app --host 0.0.0.0 --port 8000

Flask​

from flask import Flask
from pocketping import PocketPing
from pocketping.bridges import TelegramBridge

app = Flask(__name__)

pp = PocketPing(
bridges=[
TelegramBridge(
bot_token="YOUR_BOT_TOKEN",
chat_id="YOUR_CHAT_ID"
)
]
)

# Register blueprint
app.register_blueprint(pp.flask_blueprint, url_prefix="/pocketping")

if __name__ == "__main__":
app.run(port=8000)

Django​

# settings.py
POCKETPING_CONFIG = {
"welcome_message": "Hi! How can we help?",
}

# urls.py
from pocketping.django import pocketping_urls

urlpatterns = [
path("pocketping/", include(pocketping_urls)),
]

Built-in Bridges​

The SDK includes built-in bridges for Telegram, Discord, and Slack with automatic validation and helpful setup guides.

import os
from pocketping import PocketPing
from pocketping.bridges import TelegramBridge, DiscordBridge, SlackBridge
from pocketping.exceptions import SetupError

pp = PocketPing()

# Add Telegram bridge
try:
pp.add_bridge(TelegramBridge(
token=os.environ["TELEGRAM_BOT_TOKEN"],
chat_id=os.environ["TELEGRAM_CHAT_ID"]
))
except SetupError as e:
# Helpful error with setup guide
print(e.formatted_guide())
exit(1)

# Add Discord bridge (bot mode)
try:
pp.add_bridge(DiscordBridge(
bot_token=os.environ["DISCORD_BOT_TOKEN"],
channel_id=os.environ["DISCORD_CHANNEL_ID"]
))
except SetupError as e:
print(e.formatted_guide())
exit(1)

# Add Slack bridge (bot mode)
try:
pp.add_bridge(SlackBridge(
bot_token=os.environ["SLACK_BOT_TOKEN"],
channel_id=os.environ["SLACK_CHANNEL_ID"]
))
except SetupError as e:
print(e.formatted_guide())
exit(1)

Validation Errors​

If configuration is missing or invalid, you'll see a helpful setup guide:

┌─────────────────────────────────────────────────────────────┐
│ ⚠️ Telegram Setup Required
├─────────────────────────────────────────────────────────────┤
│
│ Missing: bot_token
│
│ To create a Telegram Bot:
│
│ 1. Open @BotFather in Telegram
│ 2. Send /newbot
│ 3. Choose a name and username
│ 4. Copy the Bot Token you receive
│
│ 📖 Full guide: https://pocketping.io/docs/telegram
│
│ 💡 Quick fix: npx @pocketping/cli init telegram
│
└─────────────────────────────────────────────────────────────┘

Bridge Modes​

BridgeModeConstructorFeatures
TelegramBotTelegramBridge(bot_token=..., chat_id=...)Send, edit, delete
DiscordWebhookDiscordBridge(webhook_url=...)Send only
DiscordBotDiscordBridge(bot_token=..., channel_id=...)Send, edit, delete
SlackWebhookSlackBridge(webhook_url=...)Send only
SlackBotSlackBridge(bot_token=..., channel_id=...)Send, edit, delete
Bot vs Webhook

Use Bot mode for full bidirectional communication. Webhooks are simpler but only support sending messages.

Discord Bot requires long-running server

Discord bot mode uses the Discord Gateway (WebSocket) to receive operator replies. This only works on long-running servers (FastAPI with uvicorn, Flask, Django with gunicorn, etc.).

Does NOT work with:

  • AWS Lambda
  • Google Cloud Functions
  • Azure Functions
  • Any serverless environment

For serverless + Discord bidirectional: Use the Bridge Server instead, or use DiscordBridge(webhook_url=...) (send-only).

Telegram & Slack work with serverless

Telegram and Slack use HTTP webhooks (not WebSocket), so they work fully with serverless environments like Lambda, Cloud Functions, etc.


Configuration​

from pocketping import PocketPing
from pocketping.storage import PostgresStorage
from pocketping.bridges import TelegramBridge, DiscordBridge

pp = PocketPing(
# Welcome message for new visitors
welcome_message="Hi! How can we help?",

# Built-in bridges (or add later with pp.add_bridge(...))
bridges=[
TelegramBridge(bot_token="...", chat_id="..."),
DiscordBridge.bot(token="...", channel_id="..."),
],

# Optional: Custom storage
storage=PostgresStorage("postgresql://..."),

# Optional: Event handlers
on_session_start=lambda s: print(f"New session: {s.id}"),
on_message=lambda s, m: print(f"Message: {m.content}"),
)

API​

Sessions​

# Get a specific session
session = await pp.get_session("sess_xxx")

# Get session messages
messages = await pp.get_messages("sess_xxx")
Sessions live in your storage

The SDK does not keep an in-memory list of "all sessions". To enumerate conversations, query your Storage implementation directly, or track sessions in the on_session_start handler.

Messages​

# Send an operator reply to a session
await pp.send_operator_message("sess_xxx", "Hello from the server!")

Visitor Identification​

Enrich sessions with user data so operators can see who they're talking to:

from pocketping.models import IdentifyRequest, UserIdentity

# Identify a visitor
await pp.handle_identify(IdentifyRequest(
session_id="sess_xxx",
identity=UserIdentity(
id="user_123", # Required
email="[email protected]",
name="John Doe",
plan="pro", # Custom fields supported
company="Acme Inc",
),
))

Required field: identity.id must be a non-empty string.

Custom Events​

Handle events from the widget and send events back.

Receiving Events from Widget​

Use the on_event callback to handle events sent by PocketPing.trigger() in the widget:

from pocketping import PocketPing

def handle_event(event, session):
print(f"Event: {event.name}", event.data)

# Track in analytics
analytics.track(event.name, {
**event.data,
"session_id": session.id,
"visitor_id": session.visitor_id,
})

# Trigger automation
if event.name == "clicked_pricing":
pp.send_operator_message(
session.id,
"I see you're checking our pricing! Need help?",
)

pp = PocketPing(
on_event=handle_event,
)

Sending Events to Widget​

Use emit_event() to send events that the widget can listen to:

# Send a promotional offer to a specific session
pp.emit_event("sess_xxx", "show_offer", {
"discount": 20,
"code": "SAVE20",
"message": "Special offer just for you!"
})

# Open the chat widget remotely
pp.emit_event("sess_xxx", "open_chat")

Custom Storage​

Implement the Storage class for persistence:

from pocketping.storage import Storage
from typing import Optional, List
import asyncpg

class PostgresStorage(Storage):
def __init__(self, dsn: str):
self.dsn = dsn

async def create_session(self, session: dict) -> None:
conn = await asyncpg.connect(self.dsn)
await conn.execute('''
INSERT INTO sessions (id, visitor_id, created_at)
VALUES ($1, $2, $3)
''', session["id"], session["visitor_id"], session["created_at"])
await conn.close()

async def get_session(self, session_id: str) -> Optional[dict]:
conn = await asyncpg.connect(self.dsn)
row = await conn.fetchrow(
"SELECT * FROM sessions WHERE id = $1",
session_id
)
await conn.close()
return dict(row) if row else None

async def save_message(self, session_id: str, message: dict) -> None:
conn = await asyncpg.connect(self.dsn)
await conn.execute('''
INSERT INTO messages (id, session_id, content, type, created_at)
VALUES ($1, $2, $3, $4, $5)
''', message["id"], session_id, message["content"],
message["type"], message["created_at"])
await conn.close()

async def get_messages(self, session_id: str) -> List[dict]:
conn = await asyncpg.connect(self.dsn)
rows = await conn.fetch(
"SELECT * FROM messages WHERE session_id = $1 ORDER BY created_at",
session_id
)
await conn.close()
return [dict(row) for row in rows]

# Usage
pp = PocketPing(
storage=PostgresStorage("postgresql://user:pass@localhost/db"),
bridge=TelegramBridge(...)
)

Type Hints​

The SDK includes type hints for better IDE support:

from pocketping import PocketPing, Session, Message
from pocketping.storage import Storage
from typing import Callable

def on_new_session(session: Session) -> None:
print(f"New session: {session.id}")

def on_new_message(session: Session, message: Message) -> None:
print(f"Message in {session.id}: {message.content}")

pp = PocketPing(
welcome_message="Hi! How can we help?",
on_session_start=on_new_session,
on_message=on_new_message,
)

User-Agent Filtering​

Block bots and automated requests from creating chat sessions.

Quick Setup​

from pocketping import PocketPing
from pocketping.utils.user_agent_filter import UaFilterConfig, UaFilterMode

pp = PocketPing(
ua_filter=UaFilterConfig(
enabled=True,
use_default_bots=True, # Block ~50 known bot patterns
)
)

Configuration Options​

pp = PocketPing(
ua_filter=UaFilterConfig(
enabled=True,
mode=UaFilterMode.BLOCKLIST, # BLOCKLIST | ALLOWLIST | BOTH
use_default_bots=True,
blocklist=[
"my-custom-scraper",
"bad-bot",
r"/spam-\d+/", # Regex pattern
],
allowlist=[
"my-monitoring-bot",
r"/internal-.*/", # Regex: allow internal tools
],
log_blocked=True,
blocked_status_code=403,
blocked_message="Forbidden",
)
)

Manual Filtering​

from pocketping.utils.user_agent_filter import (
check_ua_filter, is_bot, DEFAULT_BOT_PATTERNS, UaFilterConfig
)

# Quick bot check
if is_bot(request.headers.get("User-Agent", "")):
return {"error": "Bots not allowed"}, 403

# Full filter check
result = check_ua_filter(
user_agent=request.headers.get("User-Agent"),
config=UaFilterConfig(enabled=True, use_default_bots=True),
request_info={"path": request.path}
)

if not result.allowed:
print(f"Blocked: {result.reason}, pattern: {result.matched_pattern}")

Next Steps​