Add 'business' profile type and category

Business accounts are cli profiles with businessAddress=True, so each connecting
customer gets their own group chat (handled via the existing chat UI) with an
optional welcome auto-reply. New BUSINESS_TYPES, a /business page + sidebar entry,
and a business variant of the create form. profile/chat pages route via a
_category helper. Adds business_test.py (customer connects -> lands in a business
group, not a direct contact) — passes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Jon
2026-06-05 17:09:00 +01:00
parent 6232c1589d
commit 609e91c6de
5 changed files with 160 additions and 11 deletions

View File

@@ -70,6 +70,15 @@ def _enrich(profiles: list[dict]) -> list[dict]:
return profiles
def _category(bot_type: str) -> str:
"""Which sidebar category a profile belongs to: users / business / bots."""
if bot_type in pm.BUSINESS_TYPES:
return "business"
if bot_type in pm.USER_TYPES:
return "users"
return "bots"
# ── Auth ──────────────────────────────────────────────────────────────────────
@app.get("/login", response_class=HTMLResponse)
@@ -111,6 +120,17 @@ async def users_page(request: Request):
})
@app.get("/business", response_class=HTMLResponse)
async def business_page(request: Request):
if redir := _redirect_if_unauth(request):
return redir
items = _enrich([p for p in db.list_profiles() if p["bot_type"] in pm.BUSINESS_TYPES])
return TEMPLATES.TemplateResponse(request, "list.html", {
"tab": "business", "items": items, "create_types": pm.BUSINESS_TYPES,
"nav_active": "business",
})
@app.get("/bots", response_class=HTMLResponse)
async def bots_page(request: Request):
if redir := _redirect_if_unauth(request):
@@ -189,7 +209,7 @@ async def profile_page(request: Request, profile_id: int):
contacts = bot.contacts if bot else []
groups = bot.groups if bot else []
log_lines = bot.log_lines[-50:] if bot else []
is_user = profile["bot_type"] in pm.USER_TYPES
cat = _category(profile["bot_type"])
# Split groups by their link role: channels (observer) vs regular groups (member).
# The is_channel flag is set during the bot's group refresh (see _classify_group).
channels = [g for g in groups if g.get("is_channel")]
@@ -200,8 +220,8 @@ async def profile_page(request: Request, profile_id: int):
"groups": plain_groups,
"channels": channels,
"log_lines": log_lines,
"back": "/users" if is_user else "/bots",
"nav_active": "users" if is_user else "bots",
"back": "/" + cat,
"nav_active": cat,
})
@@ -238,7 +258,6 @@ async def chat_room(request: Request, profile_id: int, chat_type: str, chat_id:
if pm.group_id(g) == chat_id:
is_channel = bool(g.get("is_channel"))
break
is_user = profile["bot_type"] in pm.USER_TYPES
return TEMPLATES.TemplateResponse(request, "chat.html", {
"profile": profile,
"running": pm.is_running(profile_id),
@@ -246,7 +265,7 @@ async def chat_room(request: Request, profile_id: int, chat_type: str, chat_id:
"chat_id": chat_id,
"chat_name": name,
"is_channel": is_channel,
"nav_active": "users" if is_user else "bots",
"nav_active": _category(profile["bot_type"]),
})