💬 django-supchat
A Lightweight, Real-Time Support Chat for Django
Zero dependencies. No WebSockets. No Redis. No React.
Just Django, vanilla JS, and Server-Sent Events.
✨ Overview
django-supchat is a production-ready, drop-in support chat widget for Django projects. It provides a beautiful, real-time chat experience between website visitors (guests or authenticated users) and your support team — all powered by Django's ORM and Server-Sent Events (SSE).
🎯 Philosophy: Ship a powerful chat system without the complexity of WebSockets, Redis, or a frontend build step. Just
pip installand go.
🚀 Key Features
📦 Quick Start
Get up and running in under 2 minutes.
1. Install
pip install django-supchat
2. Configure Django
# settings.py
INSTALLED_APPS = [
# ...
"supchat",
]
# Optional: Customize the widget
SUPCHAT = {
"TITLE": "Support",
"POSITION": "right", # "right" or "left"
"THEME": "auto", # "auto", "light", or "dark"
"ALLOW_GUEST": True,
"MAX_MESSAGE_LENGTH": 2000,
}
3. Add URLs
# urls.py
from django.urls import include, path
urlpatterns = [
# ...
path("supchat/", include("supchat.urls")),
]
4. Run Migrations
python manage.py migrate
5. Render the Widget
{% raw %}
{% load supchat %}
<!DOCTYPE html>
<html lang="en">
<head>
<title>My Site</title>
</head>
<body>
<h1>Welcome to my site!</h1>
{% supchat %}
</body>
</html>
{% endraw %}
That's it! 🎉 Your chat widget is live.
📚 Documentation
Configuration Reference
Widget Settings
| Setting | Type | Default | Description |
|---|---|---|---|
TITLE |
str |
"Support" |
Widget title displayed in header |
POSITION |
str |
"right" |
Widget position: "right" or "left" |
THEME |
str |
"auto" |
Theme: "auto", "light", or "dark" |
ALLOW_GUEST |
bool |
True |
Allow anonymous visitors to chat |
MAX_MESSAGE_LENGTH |
int |
2000 |
Maximum characters per message |
Advanced Settings
| Setting | Type | Default | Description |
|---|---|---|---|
HEARTBEAT_INTERVAL |
int |
25 |
SSE heartbeat interval (seconds) |
SSE_RETRY_MS |
int |
5000 |
SSE reconnection delay (ms) |
GUEST_COOKIE_NAME |
str |
"supchat_guest" |
Name of the guest identity cookie |
GUEST_COOKIE_AGE |
int |
31536000 |
Cookie lifetime (1 year in seconds) |
RATE_LIMIT |
dict |
{"MESSAGES": 30, "WINDOW": 60} |
Default rate limit config |
RATE_LIMIT_CALLBACK |
callable |
None |
Custom rate limit function |
Per-Render Overrides
You can override settings per template render:
{% raw %}
{% supchat title="Help Desk" position="left" theme="dark" %}
{% endraw %}
🔐 Security Model
django-supchat takes security seriously:
- ✅ CSRF Protection — All POST endpoints require valid CSRF tokens
- ✅ Signed Guest Cookies — Guest identity stored in signed, HTTP-only cookies
- ✅ Conversation Access Control — Every endpoint (including SSE) verifies access
- ✅ Input Sanitization — HTML stripped, control chars removed, length capped
- ✅ XSS Prevention — Frontend uses
textContent, neverinnerHTML - ✅ Rate Limiting — Cache-backed default + pluggable hook for custom logic
Custom Rate Limiting
# settings.py
def my_rate_limiter(request, action):
# Return True to allow, False to deny
if request.user.is_staff:
return True
return False # Custom logic
SUPCHAT = {
"RATE_LIMIT_CALLBACK": my_rate_limiter,
}
👥 Operator Management
Creating Operators
Create Operator records in Django admin for your support staff:
from django.contrib.auth import get_user_model
from supchat.models import Operator
User = get_user_model()
user = User.objects.get(username="support_agent")
Operator.objects.create(user=user, is_online=True)
Assigning Operators Programmatically
from supchat.services import assign_operator
# Auto-assign to current user (if they're an operator)
assign_operator(request, conversation)
# Assign to a specific operator
assign_operator(request, conversation, operator=some_operator)
Operator Dashboard
Access the operator panel at /supchat/operator/ (requires staff permissions).
Features:
- 💬 Real-time conversation list with unread badges
- 📊 Analytics dashboard with 7-day & 30-day charts
- 🎯 Conversation assignment & status management
- 🌓 Dark/Light theme toggle
- 🌐 Bilingual interface (EN/FA)
🛠️ Public API
The service layer is the stable integration point. Use these functions in your views, signals, or management commands:
from supchat.services import (
get_or_create_conversation,
send_message,
close_conversation,
assign_operator,
mark_messages_read,
)
get_or_create_conversation(request) → (Conversation, guest_id | None)
Returns an open conversation for the user/guest. Creates one if none exists.
conversation, guest_id = get_or_create_conversation(request)
send_message(request, conversation, text) → Message
Sends a message and broadcasts it via SSE.
message = send_message(request, conversation, "Hello!")
close_conversation(request, conversation) → Conversation
Closes a conversation (operators only).
close_conversation(request, conversation)
assign_operator(request, conversation, operator=None) → Conversation
Assigns an operator to a conversation.
assign_operator(request, conversation, operator=my_operator)
mark_messages_read(request, conversation, message_ids=None) → int
Marks messages as read. Returns count of updated messages.
count = mark_messages_read(request, conversation)
🎨 Customization
Override any of these in your project:
your_project/
├── templates/
│ └── supchat/
│ └── widget.html # Override the widget template
└── static/
└── supchat/
├── supchat.css # Override styles
└── supchat.js # Override behavior
CSS Variables
Customize colors by overriding CSS variables:
.supchat-widget {
--sc-primary: #6366f1;
--sc-primary-hover: #4f46e5;
--sc-bubble-user: linear-gradient(135deg, #6366f1, #8b5cf6);
--sc-bubble-agent: #ffffff;
--sc-bg: rgba(255, 255, 255, 0.95);
--sc-fg: #0f172a;
--sc-border: rgba(226, 232, 240, 0.8);
}
JavaScript Events
Listen to chat events:
document.addEventListener('supchat:open', () => {
console.log('Chat opened');
});
document.addEventListener('supchat:close', () => {
console.log('Chat closed');
});
document.addEventListener('supchat:message', (e) => {
console.log('New message:', e.detail);
});
🚢 Deployment
SSE Requirements
The built-in SSE manager is process-local and works great for:
- Development
- Single-process deployments
- Small to medium traffic
For multi-worker deployments (Gunicorn, uWSGI with multiple workers), clients connected to different workers won't receive broadcasts. Solutions:
Option 1: Single Worker
gunicorn myproject.wsgi:application --workers 1 --threads 8
Option 2: Cross-Worker Fanout
Implement your own broadcast mechanism using Redis Pub/Sub, a message queue, or your cache backend:
# myproject/sse_bridge.py
from django.core.cache import cache
from supchat.sse import sse_manager
def broadcast_to_all_workers(conversation_id, event, data):
# Publish to Redis/cache
cache.set(f"supchat:broadcast:{conversation_id}", {
"event": event,
"data": data,
}, timeout=5)
# Each worker subscribes and calls:
# sse_manager.broadcast(conversation_id, event, data)
Proxy Configuration
Nginx:
location /supchat/ {
proxy_pass http://localhost:8000;
proxy_buffering off; # Critical for SSE
proxy_cache off;
proxy_read_timeout 86400s; # Long timeout for SSE
proxy_send_timeout 86400s;
}
Apache:
<Location /supchat/>
ProxyPass http://localhost:8000/supchat/
ProxyPassReverse http://localhost:8000/supchat/
SetEnv proxy-nokeepalive 1
SetEnv proxy-sendchunked 1
</Location>
🌐 Browser Support
| Browser | Version |
|---|---|
| Chrome | 90+ |
| Firefox | 88+ |
| Safari | 14+ |
| Edge | 90+ |
| iOS Safari | 14+ |
| Android Chrome | 90+ |
🗺️ Roadmap
- File/image attachments
- Message reactions & emoji
- Conversation tags & categories
- Canned responses / quick replies
- Chat transcripts via email
- Webhook integrations (Slack, Discord)
- Bot/AI auto-responses
- Multi-language support (beyond EN/FA)
- Mobile SDK (iOS/Android)
- WebSocket adapter (optional)
🤝 Contributing
Contributions are welcome! Here's how:
- Fork the repo
- Create a feature branch:
git checkout -b feature/amazing-feature - Commit your changes:
git commit -m 'Add amazing feature' - Push to the branch:
git push origin feature/amazing-feature - Open a Pull Request
Development Setup
git clone https://github.com/xo-aria/django-supchat.git
cd django-supchat
pip install -e '.[test]'
pytest
📄 License & Trademark
Code License
django-supchat is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0).
This means:
- ✅ You can use, modify, and distribute this software
- ✅ You can use it commercially
- ⚠️ You must disclose source code (even for SaaS/network services)
- ⚠️ You must include the same license in distributions
- ⚠️ You must state changes you made
📖 Read the full AGPL-3.0 license
Trademark Policy
The name "SupChat" is a trademark of xo-aria. See the Trademark Policy for details on:
- ✅ How to use the SupChat name
- ✅ Attribution requirements
- ❌ What you cannot do
- 🎨 Logo usage guidelines
Quick Summary:
- You must keep "SupChat" in the name when distributing
- You must include attribution:
Based on SupChat by xo-aria - https://github.com/xo-aria/django-supchat - You cannot rebrand without mentioning SupChat
- You cannot use SupChat logos without permission
Why AGPL-3.0?
We chose AGPL-3.0 to:
- Protect user freedom - Ensure the software stays free
- Close the SaaS loophole - Even network services must share source code
- Encourage contributions - Improvements benefit everyone
- Maintain quality - Prevent closed-source forks from fragmenting the project
Commercial Use
Yes, you can use SupChat commercially! Just:
- ✅ Comply with AGPL-3.0 (disclose source code)
- ✅ Follow the trademark policy (attribution)
- ✅ Document your changes
Need a commercial license without AGPL restrictions? Contact us.
🙏 Attribution
If you use SupChat in your project, we'd appreciate:
Required (by license):
Based on SupChat by xo-aria
https://github.com/xo-aria/django-supchat
Licensed under AGPL-3.0
Appreciated (but optional):
- ⭐ Star the repository on GitHub
- 📝 Mention "Powered by SupChat" in your docs/about page
- 🔗 Link back to https://github.com/xo-aria/django-supchat
- 🐦 Tweet about it (we'd love to see your projects!)
📞 Support & Contact
Bug Reports & Feature Requests:
Questions & Discussions:
Commercial Licensing:
- 📧 Email: [your-email@example.com]
- 💼 Custom licensing available for closed-source projects
Security Issues:
- 🔒 Please email security issues directly to [your-email@example.com]
- ⚠️ Do not open public issues for security vulnerabilities
If django-supchat helps your project, please consider:
⭐ Starring on GitHub • 🍴 Forking • 💬 Joining Discussions
Made with ❤️ by xo-aria
🙏 Acknowledgments
- Built with ❤️ for the Django community
- Inspired by Intercom, Crisp, and Tawk.to
- Icons from Lucide
- Charts powered by Chart.js
📞 Support
- 🐛 Bug Reports: GitHub Issues
- 💡 Feature Requests: GitHub Discussions
- 📧 Email: your-email@example.com
If django-supchat helps your project, consider giving it a ⭐ on GitHub!
Metadata
Release files for django-supchat 1.8.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| django_supchat-1.8.0.tar.gz | 56.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| django_supchat-1.8.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 115.2 kB
Release files / django_supchat-1.8.0.tar.gz
| Download URL | django_supchat-1.8.0.tar.gz |
|---|---|
| Size | 56.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b4aaae22179e801bcd94982f323ac7792b8273a1acbab11138b0cecd5b54c186
|
|
BLAKE2b-256 checksum How to use checksums |
55b325c57c0492e105e4d81a8ec09adeebb96c5c119654b1a01448d39558363b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Release files / django_supchat-1.8.0-py3-none-any.whl
| Download URL | django_supchat-1.8.0-py3-none-any.whl |
|---|---|
| Size | 59.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
25ac299e1d7f3df3887fafd9e74fac4c2bda26ce54835324139183fbc25b7b9e
|
|
BLAKE2b-256 checksum How to use checksums |
d0f0ebeedef14dd7fb0740e3452a32b4060eb2f8f218a5cc7b0d5e6e5d817567
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|