# retoor <retoor@molodetz.nl>
|
|
|
|
from .._shared import endpoint, field
|
|
|
|
GROUP = {
|
|
"slug": "profiles",
|
|
"title": "Profiles & Social Graph",
|
|
"intro": """
|
|
# Profiles & Social Graph
|
|
|
|
Profile data, the follow graph, the leaderboard, and avatar generation. Find users with
|
|
[Search & Lookups](/docs/lookups.html); follows generate entries in [Notifications](/docs/notifications.html).
|
|
|
|
Every endpoint follows the shared [Conventions & Errors](/docs/conventions.html) (auth, content
|
|
negotiation, pagination, status codes); see [Authentication](/docs/authentication.html) for the
|
|
four ways to sign requests.
|
|
""",
|
|
"endpoints": [
|
|
endpoint(
|
|
id="my-profile",
|
|
method="GET",
|
|
path="/profile",
|
|
title="View your own profile",
|
|
summary="Render the signed-in user's own profile, in the exact format of GET /profile/{username}. Requires authentication.",
|
|
auth="user",
|
|
interactive=True,
|
|
params=[
|
|
field(
|
|
"tab",
|
|
"query",
|
|
"enum",
|
|
False,
|
|
"posts",
|
|
"Profile tab.",
|
|
["posts", "activity", "followers", "following", "media", "awards"],
|
|
),
|
|
],
|
|
),
|
|
endpoint(
|
|
id="profile-detail",
|
|
method="GET",
|
|
path="/profile/{username}",
|
|
title="View a profile",
|
|
summary="Render a user profile, including an online-presence indicator (JSON exposes profile_online and profile_user.last_seen). Returns an HTML page.",
|
|
auth="public",
|
|
interactive=True,
|
|
params=[
|
|
field(
|
|
"username",
|
|
"path",
|
|
"string",
|
|
True,
|
|
"{{ username }}",
|
|
"Target username.",
|
|
),
|
|
field(
|
|
"tab",
|
|
"query",
|
|
"enum",
|
|
False,
|
|
"posts",
|
|
"Profile tab.",
|
|
["posts", "activity", "followers", "following", "media", "awards"],
|
|
),
|
|
],
|
|
),
|
|
endpoint(
|
|
id="profile-update",
|
|
method="POST",
|
|
path="/profile/update",
|
|
title="Update your profile",
|
|
summary="Update your own bio and links.",
|
|
auth="user",
|
|
encoding="form",
|
|
destructive=True,
|
|
params=[
|
|
field(
|
|
"bio",
|
|
"form",
|
|
"textarea",
|
|
False,
|
|
"Building things.",
|
|
"Bio, up to 500 characters.",
|
|
),
|
|
field(
|
|
"location",
|
|
"form",
|
|
"string",
|
|
False,
|
|
"Earth",
|
|
"Location, up to 200 characters.",
|
|
),
|
|
field("git_link", "form", "string", False, "", "Git profile URL."),
|
|
field(
|
|
"website", "form", "string", False, "", "Personal website URL."
|
|
),
|
|
],
|
|
),
|
|
endpoint(
|
|
id="profile-ai-correction",
|
|
method="POST",
|
|
path="/profile/{username}/ai-correction",
|
|
title="Configure AI content correction",
|
|
summary="Enable or disable automatic AI rewriting of your prose and set the correction instruction. Opt-in, default off. Admins may target any user.",
|
|
auth="user",
|
|
encoding="form",
|
|
destructive=True,
|
|
params=[
|
|
field(
|
|
"username",
|
|
"path",
|
|
"string",
|
|
True,
|
|
"bob_test",
|
|
"Profile owner. Must be yourself unless you are an admin.",
|
|
),
|
|
field(
|
|
"enabled",
|
|
"form",
|
|
"boolean",
|
|
False,
|
|
"true",
|
|
"true to enable background AI correction, false to disable it.",
|
|
),
|
|
field(
|
|
"sync",
|
|
"form",
|
|
"boolean",
|
|
False,
|
|
"false",
|
|
"true to apply the correction synchronously (the save waits), false for background.",
|
|
),
|
|
field(
|
|
"prompt",
|
|
"form",
|
|
"textarea",
|
|
False,
|
|
"Leave literary as is, only do punctuation and casing",
|
|
"Correction instruction, up to 20000 characters.",
|
|
),
|
|
],
|
|
sample_response={
|
|
"ok": True,
|
|
"redirect": "/profile/bob_test",
|
|
"data": {
|
|
"url": "/profile/bob_test",
|
|
"enabled": True,
|
|
"sync": False,
|
|
"prompt": "Leave literary as is, only do punctuation and casing",
|
|
},
|
|
},
|
|
),
|
|
endpoint(
|
|
id="profile-interactions",
|
|
method="POST",
|
|
path="/profile/{username}/interactions",
|
|
title="Configure Devii interactive widgets",
|
|
summary="Enable or disable CA-IWP interactive prompts (ui_prompt) for this account, or reset to the administrator default. Guests always use the site default. Admins may target any user.",
|
|
auth="user",
|
|
encoding="form",
|
|
destructive=True,
|
|
params=[
|
|
field(
|
|
"username",
|
|
"path",
|
|
"string",
|
|
True,
|
|
"bob_test",
|
|
"Profile owner. Must be yourself unless you are an admin.",
|
|
),
|
|
field(
|
|
"enabled",
|
|
"form",
|
|
"boolean",
|
|
False,
|
|
"true",
|
|
"true to enable interactive widgets, false to disable. Ignored when reset is true.",
|
|
),
|
|
field(
|
|
"reset",
|
|
"form",
|
|
"boolean",
|
|
False,
|
|
"false",
|
|
"true to clear the user override and inherit the administrator default.",
|
|
),
|
|
],
|
|
sample_response={
|
|
"ok": True,
|
|
"redirect": "/profile/bob_test",
|
|
"data": {
|
|
"url": "/profile/bob_test",
|
|
"enabled": True,
|
|
"source": "user",
|
|
"default": True,
|
|
"override": True,
|
|
},
|
|
},
|
|
),
|
|
endpoint(
|
|
id="profile-ai-modifier",
|
|
method="POST",
|
|
path="/profile/{username}/ai-modifier",
|
|
title="Configure the AI modifier",
|
|
summary="Enable or disable the inline '@ai <instruction>' modifier on your prose and set its prompt. Enabled by default, applied synchronously by default. Admins may target any user.",
|
|
auth="user",
|
|
encoding="form",
|
|
destructive=True,
|
|
params=[
|
|
field(
|
|
"username",
|
|
"path",
|
|
"string",
|
|
True,
|
|
"bob_test",
|
|
"Profile owner. Must be yourself unless you are an admin.",
|
|
),
|
|
field(
|
|
"enabled",
|
|
"form",
|
|
"boolean",
|
|
False,
|
|
"true",
|
|
"true to enable the AI modifier, false to disable it.",
|
|
),
|
|
field(
|
|
"sync",
|
|
"form",
|
|
"boolean",
|
|
False,
|
|
"true",
|
|
"true to apply the modification synchronously (the save waits), false for background.",
|
|
),
|
|
field(
|
|
"prompt",
|
|
"form",
|
|
"textarea",
|
|
False,
|
|
"Execute what is behind `@ai` (the prompt) and replace that part including `@ai`",
|
|
"Modifier instruction, up to 20000 characters.",
|
|
),
|
|
],
|
|
sample_response={
|
|
"ok": True,
|
|
"redirect": "/profile/bob_test",
|
|
"data": {
|
|
"url": "/profile/bob_test",
|
|
"enabled": True,
|
|
"sync": True,
|
|
"prompt": "Execute what is behind `@ai` (the prompt) and replace that part including `@ai`",
|
|
},
|
|
},
|
|
),
|
|
endpoint(
|
|
id="profile-telegram",
|
|
method="POST",
|
|
path="/profile/{username}/telegram",
|
|
title="Pair or unpair Telegram",
|
|
summary="Request a single-use Telegram pairing code, or unpair the connected account. Send action=request (default) to receive a code valid for a few minutes, or action=unpair to disconnect. Admins may target any user.",
|
|
auth="user",
|
|
encoding="form",
|
|
destructive=True,
|
|
params=[
|
|
field(
|
|
"username",
|
|
"path",
|
|
"string",
|
|
True,
|
|
"bob_test",
|
|
"Profile owner. Must be yourself unless you are an admin.",
|
|
),
|
|
field(
|
|
"action",
|
|
"form",
|
|
"string",
|
|
False,
|
|
"request",
|
|
"Either request to issue a pairing code, or unpair to disconnect Telegram.",
|
|
),
|
|
],
|
|
sample_response={
|
|
"ok": True,
|
|
"paired": False,
|
|
"code": "1234",
|
|
"expires_at": "2026-06-18T12:05:00+00:00",
|
|
"ttl_minutes": 5,
|
|
},
|
|
),
|
|
endpoint(
|
|
id="profile-regenerate-key",
|
|
method="POST",
|
|
path="/profile/regenerate-api-key",
|
|
title="Regenerate your API key",
|
|
summary="Issue a new API key and invalidate the current one.",
|
|
auth="user",
|
|
interactive=False,
|
|
destructive=True,
|
|
notes=[
|
|
"> Running this invalidates the key these documentation panels use. Do it from your "
|
|
"[profile page](/profile/{{ username }}) instead, then reload these docs.",
|
|
],
|
|
sample_response={"api_key": "NEW_UUID"},
|
|
),
|
|
endpoint(
|
|
id="profile-give-award",
|
|
method="POST",
|
|
path="/profile/{username}/award",
|
|
title="Give a member an award",
|
|
summary="Create a pending award on another member's profile and enqueue image generation.",
|
|
auth="user",
|
|
encoding="json",
|
|
params=[
|
|
field(
|
|
"username",
|
|
"path",
|
|
"string",
|
|
True,
|
|
"{{ username }}",
|
|
"Receiver username.",
|
|
),
|
|
field(
|
|
"description",
|
|
"body",
|
|
"string",
|
|
True,
|
|
"Great work on the release!",
|
|
"Award message (1-125 characters).",
|
|
),
|
|
],
|
|
sample_response={
|
|
"ok": True,
|
|
"data": {
|
|
"award_uid": "AWARD_UID",
|
|
"award_slug": "abc123-great-work",
|
|
},
|
|
},
|
|
),
|
|
endpoint(
|
|
id="profile-regenerate-avatar",
|
|
method="POST",
|
|
path="/profile/{username}/regenerate-avatar",
|
|
title="Regenerate a user avatar",
|
|
summary="Replace the user's avatar with a freshly generated random one.",
|
|
auth="user",
|
|
interactive=False,
|
|
destructive=True,
|
|
params=[
|
|
field(
|
|
"username",
|
|
"path",
|
|
required=True,
|
|
description="Profile owner. Allowed for the owner or any admin.",
|
|
),
|
|
],
|
|
notes=[
|
|
"> Irreversible: the previous avatar is gone for good and cannot be brought back.",
|
|
],
|
|
sample_response={
|
|
"ok": True,
|
|
"data": {
|
|
"url": "/profile/{{ username }}",
|
|
"avatar_seed": "NEW_UUID",
|
|
"avatar_url": "/avatar/multiavatar/NEW_UUID?size=80",
|
|
},
|
|
},
|
|
),
|
|
endpoint(
|
|
id="profile-customization-global",
|
|
method="POST",
|
|
path="/profile/{username}/customization/global",
|
|
title="Toggle site-wide customizations",
|
|
summary="Show or suppress your site-wide custom CSS and JS without deleting it. Admins may target any user.",
|
|
auth="user",
|
|
encoding="form",
|
|
destructive=True,
|
|
params=[
|
|
field(
|
|
"username",
|
|
"path",
|
|
"string",
|
|
True,
|
|
"bob_test",
|
|
"Profile owner. Must be yourself unless you are an admin.",
|
|
),
|
|
field(
|
|
"value",
|
|
"form",
|
|
"boolean",
|
|
False,
|
|
"0",
|
|
"1 to show your site-wide customizations, 0 to suppress them.",
|
|
),
|
|
],
|
|
),
|
|
endpoint(
|
|
id="profile-customization-pagetype",
|
|
method="POST",
|
|
path="/profile/{username}/customization/pagetype",
|
|
title="Toggle per-page customizations",
|
|
summary="Show or suppress your per-page custom CSS and JS without deleting it. Admins may target any user.",
|
|
auth="user",
|
|
encoding="form",
|
|
destructive=True,
|
|
params=[
|
|
field(
|
|
"username",
|
|
"path",
|
|
"string",
|
|
True,
|
|
"bob_test",
|
|
"Profile owner. Must be yourself unless you are an admin.",
|
|
),
|
|
field(
|
|
"value",
|
|
"form",
|
|
"boolean",
|
|
False,
|
|
"0",
|
|
"1 to show your per-page customizations, 0 to suppress them.",
|
|
),
|
|
],
|
|
),
|
|
endpoint(
|
|
id="profile-notification-toggle",
|
|
method="POST",
|
|
path="/profile/{username}/notifications",
|
|
title="Toggle a notification preference",
|
|
summary="Enable or disable one notification type on one channel (in-app or push). Admins may target any user. Types: comment, reply, mention, vote, follow, message, badge, level, issue, reminder, harvest_stolen.",
|
|
auth="user",
|
|
encoding="form",
|
|
destructive=True,
|
|
params=[
|
|
field(
|
|
"username",
|
|
"path",
|
|
"string",
|
|
True,
|
|
"bob_test",
|
|
"Profile owner. Must be yourself unless you are an admin.",
|
|
),
|
|
field(
|
|
"notification_type",
|
|
"form",
|
|
"string",
|
|
True,
|
|
"vote",
|
|
"One of: comment, reply, mention, vote, follow, message, badge, level, issue, reminder, harvest_stolen.",
|
|
),
|
|
field(
|
|
"channel",
|
|
"form",
|
|
"string",
|
|
True,
|
|
"push",
|
|
"One of in_app, push or telegram (telegram is off by default and requires a paired Telegram account).",
|
|
),
|
|
field(
|
|
"value",
|
|
"form",
|
|
"boolean",
|
|
False,
|
|
"1",
|
|
"1 to deliver this notification on this channel, 0 to suppress it.",
|
|
),
|
|
],
|
|
sample_response={
|
|
"ok": True,
|
|
"redirect": "/profile/{{ username }}?tab=notifications",
|
|
"data": {
|
|
"notification_type": "vote",
|
|
"channel": "push",
|
|
"value": False,
|
|
},
|
|
},
|
|
),
|
|
endpoint(
|
|
id="profile-notification-reset",
|
|
method="POST",
|
|
path="/profile/{username}/notifications/reset",
|
|
title="Reset notification preferences",
|
|
summary="Clear all of a user's notification overrides so every type falls back to the platform default. Admins may target any user.",
|
|
auth="user",
|
|
encoding="form",
|
|
destructive=True,
|
|
params=[
|
|
field(
|
|
"username",
|
|
"path",
|
|
"string",
|
|
True,
|
|
"bob_test",
|
|
"Profile owner. Must be yourself unless you are an admin.",
|
|
),
|
|
],
|
|
sample_response={
|
|
"ok": True,
|
|
"redirect": "/profile/{{ username }}?tab=notifications",
|
|
},
|
|
),
|
|
endpoint(
|
|
id="media-delete",
|
|
method="POST",
|
|
path="/media/{uid}/delete",
|
|
title="Delete media",
|
|
summary="Remove one of your uploaded media attachments. It disappears from your profile Media tab and from any post, project, gist, or other place it was attached. You can delete media you uploaded; administrators may remove any user's media.",
|
|
auth="user",
|
|
destructive=True,
|
|
params=[
|
|
field(
|
|
"uid",
|
|
"path",
|
|
"string",
|
|
True,
|
|
"",
|
|
"Attachment uid, taken from the Media tab response.",
|
|
),
|
|
],
|
|
sample_response={
|
|
"ok": True,
|
|
"redirect": "/profile/{{ username }}?tab=media",
|
|
},
|
|
),
|
|
endpoint(
|
|
id="follow-user",
|
|
method="POST",
|
|
path="/follow/{username}",
|
|
title="Follow a user",
|
|
summary="Follow another user. Idempotent.",
|
|
auth="user",
|
|
destructive=True,
|
|
params=[
|
|
field(
|
|
"username",
|
|
"path",
|
|
"string",
|
|
True,
|
|
"bob_test",
|
|
"Username to follow.",
|
|
)
|
|
],
|
|
),
|
|
endpoint(
|
|
id="unfollow-user",
|
|
method="POST",
|
|
path="/follow/unfollow/{username}",
|
|
title="Unfollow a user",
|
|
summary="Stop following a user.",
|
|
auth="user",
|
|
destructive=True,
|
|
params=[
|
|
field(
|
|
"username",
|
|
"path",
|
|
"string",
|
|
True,
|
|
"bob_test",
|
|
"Username to unfollow.",
|
|
)
|
|
],
|
|
),
|
|
endpoint(
|
|
id="block-user",
|
|
method="POST",
|
|
path="/block/{username}",
|
|
title="Block a user",
|
|
summary="Block a user. Their posts, comments and messages are hidden from you everywhere except their own profile, and they can no longer create notifications for you. Idempotent.",
|
|
auth="user",
|
|
destructive=True,
|
|
params=[
|
|
field(
|
|
"username",
|
|
"path",
|
|
"string",
|
|
True,
|
|
"bob_test",
|
|
"Username to block.",
|
|
)
|
|
],
|
|
sample_response={"ok": True, "redirect": "/profile/bob_test"},
|
|
),
|
|
endpoint(
|
|
id="unblock-user",
|
|
method="POST",
|
|
path="/block/unblock/{username}",
|
|
title="Unblock a user",
|
|
summary="Reverse a block. Their content becomes visible again.",
|
|
auth="user",
|
|
destructive=True,
|
|
params=[
|
|
field(
|
|
"username",
|
|
"path",
|
|
"string",
|
|
True,
|
|
"bob_test",
|
|
"Username to unblock.",
|
|
)
|
|
],
|
|
sample_response={"ok": True, "redirect": "/profile/bob_test"},
|
|
),
|
|
endpoint(
|
|
id="mute-user",
|
|
method="POST",
|
|
path="/mute/{username}",
|
|
title="Mute a user",
|
|
summary="Mute a user so they can no longer create notifications for you. Their content stays visible. Idempotent.",
|
|
auth="user",
|
|
destructive=True,
|
|
params=[
|
|
field(
|
|
"username",
|
|
"path",
|
|
"string",
|
|
True,
|
|
"bob_test",
|
|
"Username to mute.",
|
|
)
|
|
],
|
|
sample_response={"ok": True, "redirect": "/profile/bob_test"},
|
|
),
|
|
endpoint(
|
|
id="unmute-user",
|
|
method="POST",
|
|
path="/mute/unmute/{username}",
|
|
title="Unmute a user",
|
|
summary="Reverse a mute. They can create notifications for you again.",
|
|
auth="user",
|
|
destructive=True,
|
|
params=[
|
|
field(
|
|
"username",
|
|
"path",
|
|
"string",
|
|
True,
|
|
"bob_test",
|
|
"Username to unmute.",
|
|
)
|
|
],
|
|
sample_response={"ok": True, "redirect": "/profile/bob_test"},
|
|
),
|
|
endpoint(
|
|
id="list-followers",
|
|
method="GET",
|
|
path="/profile/{username}/followers",
|
|
title="List followers",
|
|
summary="List the users who follow a profile, 25 per page. Returns JSON.",
|
|
auth="public",
|
|
interactive=True,
|
|
params=[
|
|
field(
|
|
"username",
|
|
"path",
|
|
"string",
|
|
True,
|
|
"{{ username }}",
|
|
"Target username.",
|
|
),
|
|
field(
|
|
"page",
|
|
"query",
|
|
"integer",
|
|
False,
|
|
"1",
|
|
"Page number, 25 per page.",
|
|
),
|
|
],
|
|
sample_response={
|
|
"username": "{{ username }}",
|
|
"mode": "followers",
|
|
"count": 2,
|
|
"page": 1,
|
|
"total_pages": 1,
|
|
"followers": [
|
|
{
|
|
"uid": "UUID",
|
|
"username": "bob_test",
|
|
"bio": "Building things.",
|
|
"is_following": False,
|
|
},
|
|
],
|
|
},
|
|
),
|
|
endpoint(
|
|
id="list-following",
|
|
method="GET",
|
|
path="/profile/{username}/following",
|
|
title="List following",
|
|
summary="List the users a profile follows, 25 per page. Returns JSON.",
|
|
auth="public",
|
|
interactive=True,
|
|
params=[
|
|
field(
|
|
"username",
|
|
"path",
|
|
"string",
|
|
True,
|
|
"{{ username }}",
|
|
"Target username.",
|
|
),
|
|
field(
|
|
"page",
|
|
"query",
|
|
"integer",
|
|
False,
|
|
"1",
|
|
"Page number, 25 per page.",
|
|
),
|
|
],
|
|
sample_response={
|
|
"username": "{{ username }}",
|
|
"mode": "following",
|
|
"count": 1,
|
|
"page": 1,
|
|
"total_pages": 1,
|
|
"following": [
|
|
{
|
|
"uid": "UUID",
|
|
"username": "alice_test",
|
|
"bio": "",
|
|
"is_following": True,
|
|
},
|
|
],
|
|
},
|
|
),
|
|
endpoint(
|
|
id="leaderboard",
|
|
method="GET",
|
|
path="/leaderboard",
|
|
title="View the leaderboard",
|
|
summary="Top contributors by stars. Returns an HTML page.",
|
|
auth="public",
|
|
interactive=True,
|
|
),
|
|
endpoint(
|
|
id="award-image",
|
|
method="GET",
|
|
path="/awards/{slug_or_uid}/{size}",
|
|
title="Award image redirect",
|
|
summary="Redirect to the stored PNG attachment for a published award.",
|
|
auth="public",
|
|
params=[
|
|
field(
|
|
"slug_or_uid",
|
|
"path",
|
|
"string",
|
|
True,
|
|
"abc123-great-work",
|
|
"Award slug or bare uid.",
|
|
),
|
|
field(
|
|
"size",
|
|
"path",
|
|
"enum",
|
|
True,
|
|
"256",
|
|
"Image size.",
|
|
["512", "256", "64"],
|
|
),
|
|
],
|
|
notes=[
|
|
"> Pending or revoked awards return 404.",
|
|
"> Response includes long-lived cache headers.",
|
|
],
|
|
),
|
|
endpoint(
|
|
id="avatar",
|
|
method="GET",
|
|
path="/avatar/{style}/{seed}",
|
|
title="Generate an avatar",
|
|
summary="Deterministic SVG avatar for a seed. Returns an image.",
|
|
auth="public",
|
|
interactive=False,
|
|
params=[
|
|
field(
|
|
"style",
|
|
"path",
|
|
"enum",
|
|
True,
|
|
"multiavatar",
|
|
"Avatar style.",
|
|
["multiavatar"],
|
|
),
|
|
field(
|
|
"seed",
|
|
"path",
|
|
"string",
|
|
True,
|
|
"{{ username }}",
|
|
"Seed string, usually a username.",
|
|
),
|
|
field("size", "query", "int", False, "128", "Pixel size."),
|
|
],
|
|
),
|
|
],
|
|
}
|