Skip to main content

django-word-battle

A reusable Django REST Framework + Channels backend for competitive multiplayer word-change games. Players take turns changing letters (FALL → CALL → TALL), and a player loses when they have no legal move, run out of time or resign.

The product name is not hard-coded. Set WORD_BATTLE["GAME_NAME"] or the WORD_BATTLE_GAME_NAME environment variable and the API, admin, emails and API docs use it. The Python package keeps the neutral import name word_battle.

pip install "django-word-battle[all]"

What's inside

Area What you get
Rules engine Pure-Python authoritative engine: 1..k letter changes, scaling by word length, add/remove letters, lock-last-letter, power moves, repeat policy, move cap. Fast legal-move discovery through a word graph. Start-word rating that avoids dud games.
Modes Classic (untimed/async), Blitz, Ranked, Long Words, Shape-shifter, Power Blitz, Arena (3–8 players), Daily Challenge and Practice. All are configured in the admin, not in code.
Matches Private challenges with invite codes, rematches, bots (always labelled), spectators with privacy controls, resign/leave, server-side clocks with latency grace, timeouts, eliminations and placements.
Real time WebSockets per match and per user, with JWT auth. Gapless, persisted event log with sequence numbers for resync. Idempotent moves (client_move_id), presence, reactions, per-socket rate limits.
Matchmaking PostgreSQL queue using SKIP LOCKED, a rating window that widens with wait time, region hints, avoidance of recent opponents and blocked players, arena filling, and bot fallback.
Competition Glicko-2 ratings per pool, both all-time and per season. Leaderboards, daily challenge leaderboard and streaks, achievements, XP and levels.
Economy Idempotent reward ledger with a daily coin cap. Wallet separates earned and purchased coins and never allows cash-out. Cosmetic store, inventory and equipping.
Monetization Google Play verification (Developer API, RTDN via Pub/Sub with OIDC check), Apple StoreKit 2 JWS verification (x5c chain up to Apple Root G3, Apple OIDs), App Store Server Notifications V2, refunds and revocations, subscriptions and entitlements. AdMob rewarded-ad SSV with ECDSA verification and replay protection.
Safety Friends, blocks, reports, moderation actions with appeals, word reports, profanity filtering, fast-move and win-trading signals for human review. No automatic bans.
Ops Feature flags with rollout percentages, audit log, admin with read-only match outcomes and audited wallet adjustments, metrics endpoint (DAU/MAU, retention, verified revenue), health checks, system checks, Celery tasks or a no-Celery worker loop.
Privacy Personal data export and account deletion; match and accounting history are anonymised, not destroyed.

Real-money wagering, deposits, cash-out and paid-entry cash prizes are not implemented. Sponsored real-world prizes are blocked by a database constraint until legal approval, an app-store policy review and official rules are recorded. They also need the real_world_prizes flag, which can only be turned on in the admin.

Quick start (existing Django project)

# settings.py
INSTALLED_APPS += ["rest_framework", "channels", "word_battle"]
ASGI_APPLICATION = "config.asgi.application"
CHANNEL_LAYERS = {"default": {"BACKEND": "channels_redis.core.RedisChannelLayer", "CONFIG": {
    "hosts": [{"address": "redis://localhost:6379/0", "socket_timeout": 30}]}}}
REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": ["rest_framework_simplejwt.authentication.JWTAuthentication"],
    "DEFAULT_PERMISSION_CLASSES": ["rest_framework.permissions.IsAuthenticated"],
    "EXCEPTION_HANDLER": "word_battle.api.exceptions.exception_handler",
}
WORD_BATTLE = {"GAME_NAME": "Word Clash"}

# urls.py
path("api/v1/", include("word_battle.api.urls")),

# asgi.py
from word_battle.realtime.middleware import OriginOrNativeValidator, WordBattleAuthMiddlewareStack
from word_battle.realtime.routing import websocket_urlpatterns
application = ProtocolTypeRouter({
    "http": get_asgi_application(),
    "websocket": OriginOrNativeValidator(WordBattleAuthMiddlewareStack(URLRouter(websocket_urlpatterns))),
})
python manage.py migrate
python manage.py wb_setup_defaults      # dictionary (ENABLE), modes, rewards, achievements, cosmetics, flags, start words
celery -A config worker & celery -A config beat   # or: python manage.py wb_worker

Set app.conf.beat_schedule = word_battle.tasks.BEAT_SCHEDULE in your Celery app. The schedule covers turn timeouts every 1 s, matchmaking every 2 s, and the daily challenge, housekeeping and anti-cheat scans.

To run the complete example stack with Docker:

cp .env.example .env
docker compose up --build        # API on :8000, docs at /api/docs/, admin at /admin/

Configuration

All keys are optional. Nested dicts are merged with the defaults; see src/word_battle/conf.py for every key.

Key Default Purpose
GAME_NAME / GAME_SLUG Word Battle / word-battle Branding, shown via /meta/, admin and notifications
ASYNC_TASKS False Push and other background work through Celery
BOT_EXECUTION inline inline (in-request) or celery (with a human-like delay)
LATENCY_GRACE_SECONDS 1.5 Extra time after a deadline before a timeout is applied
MATCHMAKING window 100 → 800, bots after 30 s Rating window growth, bot fallback, arena fill wait
RATING Glicko-2 1500/350/0.06, τ 0.5 Initial rating values; minimum games for leaderboards
REWARDS daily coin cap 500 Reward codes used automatically and the XP curve
ADS 10 rewarded ads per day AdMob key URL, reward code, daily cap
GOOGLE_PLAY, APPLE — Store verification credentials (see docs/monetization.md)
NOTIFICATION_BACKEND logging word_battle.services.notifications.FCMBackend for push
THROTTLE_RATES moves 120/min … Per-scope rate limits
FEATURE_FLAGS — Defaults; rows in the FeatureFlag table override them
HOOKS — Dotted paths called on match completion, verified purchases and reports

The package also sends Django signals: match_completed, move_accepted, purchase_verified, reward_granted and achievement_unlocked, all from word_battle.signals.

Management commands

Command Purpose
wb_setup_defaults Load the dictionary and default content (safe to re-run)
wb_import_dictionary --file words.txt [--common common.txt] Import a new dictionary version
wb_rate_start_words [mode ...] Pre-rate playable start words (rejects starts with an instant trap)
wb_worker Scheduler loop when you don't run Celery beat

Documentation

Development

pip install -e ".[dev]"
createdb word_battle    # tests need PostgreSQL (row locks, SKIP LOCKED); set DATABASE_URL if different
pytest                  # 109 tests: engine, services, API, WebSockets, real concurrency
ruff check src tests example_project
python scripts/smoke_test.py --base http://localhost:8000   # end-to-end against a running stack

Licence

MIT. The bundled ENABLE word list is in the public domain. A "common words" list is not bundled, because the popular frequency lists are not licensed for commercial use. Supply your own with wb_import_dictionary --common.

Metadata

Release files for django-word-battle 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for django-word-battle 0.1.0
File Size Uploaded
django_word_battle-0.1.0.tar.gz 559.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for django-word-battle 0.1.0
File Interpreter ABI Platform
django_word_battle-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 1.1 MB

Release files / django_word_battle-0.1.0.tar.gz

Download URL django_word_battle-0.1.0.tar.gz
Size 559.6 kB
Tags Source
SHA-256 checksum
How to use checksums
6fc9dffdf945d2c6d1b2f2ce46b0d319fbf7988a1d58bd262e3cea4f212f1714
BLAKE2b-256 checksum
How to use checksums
03d21d3ae2ff3dcd9f4d9f66078f5089bba2e9174e715e52c9a9609dfda2e4e1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 14, 2026.

Transparency log

Release files / django_word_battle-0.1.0-py3-none-any.whl

Download URL django_word_battle-0.1.0-py3-none-any.whl
Size 582.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
32bd7fed6bbe8efb1e28dd86da5341985f216dce520dd7cccef0ac0982bbf113
BLAKE2b-256 checksum
How to use checksums
e1d8f0648834fd5e22912c535de69045187a4936e3588f298660e3bc814492ad
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 14, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

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