Files
gerbeur/api/services/ws-service.ts/index.html
2026-11-08 21:22:53 +00:00

519 lines
57 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<title>api&#x2F;services&#x2F;ws-service.ts - gerbeur documentation</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="doc-current-file" content="">
<link rel="stylesheet" href="..&#x2F;..&#x2F;..&#x2F;styles.css">
<link rel="stylesheet" href="..&#x2F;..&#x2F;..&#x2F;page.css">
<link id="ddocResetStylesheet" rel="stylesheet" href="..&#x2F;..&#x2F;..&#x2F;reset.css"><link href="../../../comrak.css" rel="stylesheet" /><link href="../../../prism.css" rel="stylesheet" /><script src="../../../prism.js"></script><script src="..&#x2F;..&#x2F;..&#x2F;script.js" defer></script>
<script src="..&#x2F;..&#x2F;..&#x2F;darkmode_toggle.js"></script><script src="..&#x2F;..&#x2F;..&#x2F;search_index.js" defer></script>
<script src="..&#x2F;..&#x2F;..&#x2F;fuse.js" defer></script>
<script src="..&#x2F;..&#x2F;..&#x2F;search.js" defer></script></head>
<body>
<div class="ddoc">
<div><nav id="topnav">
<div class="h-full">
<div class="flex items-center"><ul class="breadcrumbs">
<li>
<a href="..&#x2F;..&#x2F;..&#x2F;" class="contextLink">gerbeur</a>
</li><span class="text-black dark:text-white"><svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M5.76748 11.8159C5.5378 11.577 5.54525 11.1972 5.78411 10.9675L8.93431 8L5.78411 5.0325C5.54525 4.80282 5.5378 4.423 5.76748 4.18413C5.99715 3.94527 6.37698 3.93782 6.61584 4.1675L10.2158 7.5675C10.3335 7.68062 10.4 7.83679 10.4 8C10.4 8.16321 10.3335 8.31938 10.2158 8.4325L6.61584 11.8325C6.37698 12.0622 5.99715 12.0547 5.76748 11.8159Z"
fill="currentColor"
/>
</svg>
</span>api&#x2F;services&#x2F;ws-service.ts</ul>
</div>
<div class="flex items-center gap-2">
<button id="theme-toggle" type="button" aria-label="Toggle dark mode" style="display: none;"><svg
xmlns="http://www.w3.org/2000/svg"
width="14"
height="14"
viewBox="0 0 24 24"
fill="currentColor"
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path
d="M12 1.992a10 10 0 1 0 9.236 13.838c.341 -.82 -.476 -1.644 -1.298 -1.31a6.5 6.5 0 0 1 -6.864 -10.787l.077 -.08c.551 -.63 .113 -1.653 -.758 -1.653h-.266l-.068 -.006l-.06 -.002z"
/>
</svg>
<svg
xmlns="http://www.w3.org/2000/svg"
width="14"
height="14"
viewBox="0 0 24 24"
fill="currentColor"
class="hidden"
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path
d="M12 19a1 1 0 0 1 .993 .883l.007 .117v1a1 1 0 0 1 -1.993 .117l-.007 -.117v-1a1 1 0 0 1 1 -1z"
/>
<path
d="M18.313 16.91l.094 .083l.7 .7a1 1 0 0 1 -1.32 1.497l-.094 -.083l-.7 -.7a1 1 0 0 1 1.218 -1.567l.102 .07z"
/>
<path
d="M7.007 16.993a1 1 0 0 1 .083 1.32l-.083 .094l-.7 .7a1 1 0 0 1 -1.497 -1.32l.083 -.094l.7 -.7a1 1 0 0 1 1.414 0z"
/>
<path
d="M4 11a1 1 0 0 1 .117 1.993l-.117 .007h-1a1 1 0 0 1 -.117 -1.993l.117 -.007h1z"
/>
<path
d="M21 11a1 1 0 0 1 .117 1.993l-.117 .007h-1a1 1 0 0 1 -.117 -1.993l.117 -.007h1z"
/>
<path
d="M6.213 4.81l.094 .083l.7 .7a1 1 0 0 1 -1.32 1.497l-.094 -.083l-.7 -.7a1 1 0 0 1 1.217 -1.567l.102 .07z"
/>
<path
d="M19.107 4.893a1 1 0 0 1 .083 1.32l-.083 .094l-.7 .7a1 1 0 0 1 -1.497 -1.32l.083 -.094l.7 -.7a1 1 0 0 1 1.414 0z"
/>
<path
d="M12 2a1 1 0 0 1 .993 .883l.007 .117v1a1 1 0 0 1 -1.993 .117l-.007 -.117v-1a1 1 0 0 1 1 -1z"
/>
<path
d="M12 7a5 5 0 1 1 -4.995 5.217l-.005 -.217l.005 -.217a5 5 0 0 1 4.995 -4.783z"
/>
</svg>
</button>
<input
type="text"
id="searchbar"
style="display: none;"
class="py-2 px-2.5 mx-1 rounded text-sm border border-gray-300 bg-transparent dark:bg-gray-800 dark:border-gray-700"
/>
</div>
</div>
</nav>
<div id="searchResults"></div><div id="content">
<main><section>
<div class="space-y-2 flex-1"><div class="space-y-7" id="module_doc"><div class="markdown"><p>Keeps the set of open sockets and sends them the server's messages.</p>
<p>The WebSocket route (<code>routes/ws.ts</code>) registers each socket and handles what
it sends. The services and routes that change dumps, comments, playlists and
users call the <code>broadcast*</code> functions here after the change is stored. A
<code>broadcast*</code> function sends to everyone unless its doc names an audience, so
its caller must not pass it anything private. Its jobs ping every socket,
closing the ones that stop answering, and expire the typing indicators.</p>
</div><section class="section" id="api&#x2F;services&#x2F;ws-service_ts_functions"><div>
<h2 class="anchorable mb-1"><a href="#api&#x2F;services&#x2F;ws-service_ts_functions" class="anchor" aria-label="Anchor" tabIndex="-1"><svg
width="16"
height="16"
viewBox="0 0 14 14"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g clip-path="url(#clip0_1081_1237)">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M6.80328 2.8656C6.68736 2.99 6.62425 3.15454 6.62725 3.32456C6.63025 3.49457 6.69913 3.65678 6.81936 3.77702C6.9396 3.89725 7.10181 3.96613 7.27182 3.96913C7.44184 3.97213 7.60638 3.90902 7.73078 3.7931L8.82453 2.69935C8.98712 2.53676 9.18015 2.40778 9.39259 2.31978C9.60503 2.23179 9.83272 2.1865 10.0627 2.1865C10.2926 2.1865 10.5203 2.23179 10.7327 2.31978C10.9452 2.40778 11.1382 2.53676 11.3008 2.69935C11.4634 2.86194 11.5923 3.05497 11.6803 3.2674C11.7683 3.47984 11.8136 3.70753 11.8136 3.93747C11.8136 4.16741 11.7683 4.3951 11.6803 4.60754C11.5923 4.81998 11.4634 5.013 11.3008 5.1756L9.11328 7.3631C8.95075 7.52581 8.75775 7.65488 8.5453 7.74295C8.33285 7.83102 8.10513 7.87635 7.87516 7.87635C7.64518 7.87635 7.41746 7.83102 7.20501 7.74295C6.99256 7.65488 6.79956 7.52581 6.63703 7.3631C6.51263 7.24718 6.34809 7.18407 6.17807 7.18707C6.00806 7.19007 5.84585 7.25894 5.72561 7.37918C5.60538 7.49942 5.5365 7.66163 5.5335 7.83164C5.5305 8.00165 5.59361 8.1662 5.70953 8.2906C5.99391 8.57501 6.33154 8.80062 6.70312 8.95455C7.0747 9.10847 7.47296 9.18769 7.87516 9.18769C8.27736 9.18769 8.67562 9.10847 9.0472 8.95455C9.41878 8.80062 9.7564 8.57501 10.0408 8.2906L12.2283 6.1031C12.8026 5.52874 13.1253 4.74974 13.1253 3.93747C13.1253 3.12521 12.8026 2.34621 12.2283 1.77185C11.6539 1.19749 10.8749 0.874817 10.0627 0.874817C9.25039 0.874817 8.47139 1.19749 7.89703 1.77185L6.80328 2.8656ZM2.69953 11.3006C2.53682 11.1381 2.40774 10.9451 2.31968 10.7326C2.23161 10.5202 2.18628 10.2924 2.18628 10.0625C2.18628 9.8325 2.23161 9.60477 2.31968 9.39233C2.40774 9.17988 2.53682 8.98688 2.69953 8.82435L4.88703 6.63685C5.04956 6.47414 5.24256 6.34506 5.45501 6.25699C5.66746 6.16893 5.89518 6.1236 6.12516 6.1236C6.35513 6.1236 6.58285 6.16893 6.7953 6.25699C7.00775 6.34506 7.20075 6.47414 7.36328 6.63685C7.48768 6.75277 7.65223 6.81588 7.82224 6.81288C7.99225 6.80988 8.15446 6.741 8.2747 6.62077C8.39494 6.50053 8.46381 6.33832 8.46681 6.1683C8.46981 5.99829 8.4067 5.83375 8.29078 5.70935C8.0064 5.42494 7.66878 5.19933 7.2972 5.0454C6.92562 4.89148 6.52736 4.81225 6.12516 4.81225C5.72296 4.81225 5.3247 4.89148 4.95312 5.0454C4.58154 5.19933 4.24391 5.42494 3.95953 5.70935L1.77203 7.89685C1.19767 8.47121 0.875 9.25021 0.875 10.0625C0.875 10.8747 1.19767 11.6537 1.77203 12.2281C2.34639 12.8025 3.12539 13.1251 3.93766 13.1251C4.74992 13.1251 5.52892 12.8025 6.10328 12.2281L7.19703 11.1343C7.31295 11.0099 7.37606 10.8454 7.37306 10.6754C7.37006 10.5054 7.30119 10.3432 7.18095 10.2229C7.06071 10.1027 6.8985 10.0338 6.72849 10.0308C6.55848 10.0278 6.39393 10.0909 6.26953 10.2068L5.17578 11.3006C5.01325 11.4633 4.82025 11.5924 4.6078 11.6805C4.39535 11.7685 4.16763 11.8138 3.93766 11.8138C3.70768 11.8138 3.47996 11.7685 3.26751 11.6805C3.05506 11.5924 2.86206 11.4633 2.69953 11.3006Z"
fill="currentColor"
/>
</g>
<defs>
<clipPath id="clip0_1081_1237">
<rect width="14" height="14" fill="white" />
</clipPath>
</defs>
</svg>
</a>
Functions</h2></div><div class="namespaceSection"><div id="namespace_broadcastcategorieschanged" class="namespaceItem" ><div class="docNodeKindIcon"><div class="text-Function bg-Function/15 dark:text-FunctionDark dark:bg-FunctionDark/15" title="Function">f</div></div>
<div class="namespaceItemContent">
<a href="..&#x2F;..&#x2F;..&#x2F;.&#x2F;api&#x2F;services&#x2F;ws-service.ts&#x2F;~&#x2F;broadcastCategoriesChanged.html" title="broadcastCategoriesChanged">
<span>broadcastCategoriesChanged</span><span>()<span>: <span class="td-kw">void</span></span></span></a><div class="namespaceItemContentDoc"><div class="markdown_summary"><p>Sends everyone <code>categories_changed</code>, after the category manager created,
edited or deleted one, so that every client fetches them again.</p>
</div></div></div>
</div><div id="namespace_broadcastchatmessage" class="namespaceItem" ><div class="docNodeKindIcon"><div class="text-Function bg-Function/15 dark:text-FunctionDark dark:bg-FunctionDark/15" title="Function">f</div></div>
<div class="namespaceItemContent">
<a href="..&#x2F;..&#x2F;..&#x2F;.&#x2F;api&#x2F;services&#x2F;ws-service.ts&#x2F;~&#x2F;broadcastChatMessage.html" title="broadcastChatMessage">
<span>broadcastChatMessage</span><span>(<div class="ml-4"><div><span>message<span>: <span class="td-ref">ChatMessage</span></span></span>,</div><div><span>nonce<span class="td-op">?</span><span>: <span class="td-kw">string</span></span></span></div></div>)<span>: <span class="td-kw">void</span></span></span></a><div class="namespaceItemContentDoc"><div class="markdown_summary"><p>Sends <code>chat_message</code> to every signed-in socket, the sender's included.
<code>nonce</code> comes from the sender's <code>chat_send</code>, and lets it swap its pending
copy for the message.</p>
</div></div></div>
</div><div id="namespace_broadcastchatmessagedeleted" class="namespaceItem" ><div class="docNodeKindIcon"><div class="text-Function bg-Function/15 dark:text-FunctionDark dark:bg-FunctionDark/15" title="Function">f</div></div>
<div class="namespaceItemContent">
<a href="..&#x2F;..&#x2F;..&#x2F;.&#x2F;api&#x2F;services&#x2F;ws-service.ts&#x2F;~&#x2F;broadcastChatMessageDeleted.html" title="broadcastChatMessageDeleted">
<span>broadcastChatMessageDeleted</span><span>(<span><span>id<span>: <span class="td-kw">string</span></span></span></span>)<span>: <span class="td-kw">void</span></span></span></a><div class="namespaceItemContentDoc"><div class="markdown_summary"><p>Sends <code>chat_message_deleted</code> to every signed-in socket.</p>
</div></div></div>
</div><div id="namespace_broadcastchatmessageupdated" class="namespaceItem" ><div class="docNodeKindIcon"><div class="text-Function bg-Function/15 dark:text-FunctionDark dark:bg-FunctionDark/15" title="Function">f</div></div>
<div class="namespaceItemContent">
<a href="..&#x2F;..&#x2F;..&#x2F;.&#x2F;api&#x2F;services&#x2F;ws-service.ts&#x2F;~&#x2F;broadcastChatMessageUpdated.html" title="broadcastChatMessageUpdated">
<span>broadcastChatMessageUpdated</span><span>(<span><span>message<span>: <span class="td-ref">ChatMessage</span></span></span></span>)<span>: <span class="td-kw">void</span></span></span></a><div class="namespaceItemContentDoc"><div class="markdown_summary"><p>Sends <code>chat_message_updated</code> to every signed-in socket.</p>
</div></div></div>
</div><div id="namespace_broadcastcommentcreated" class="namespaceItem" ><div class="docNodeKindIcon"><div class="text-Function bg-Function/15 dark:text-FunctionDark dark:bg-FunctionDark/15" title="Function">f</div></div>
<div class="namespaceItemContent">
<a href="..&#x2F;..&#x2F;..&#x2F;.&#x2F;api&#x2F;services&#x2F;ws-service.ts&#x2F;~&#x2F;broadcastCommentCreated.html" title="broadcastCommentCreated">
<span>broadcastCommentCreated</span><span>(<div class="ml-4"><div><span>comment<span>: <span class="td-ref">Comment</span></span></span>,</div><div><span>dump<span>: { isPrivate<span>: <span class="td-kw">boolean</span></span>; userId<span>: <span class="td-kw">string</span></span>; }</span></span></div></div>)<span>: <span class="td-kw">void</span></span></span></a><div class="namespaceItemContentDoc"><div class="markdown_summary"><p>Sends <code>comment_created</code> to those who may see the comment's dump: everyone,
or its owner alone when it is private. The comments route calls it, as it
does the two below.</p>
</div></div></div>
</div><div id="namespace_broadcastcommentdeleted" class="namespaceItem" ><div class="docNodeKindIcon"><div class="text-Function bg-Function/15 dark:text-FunctionDark dark:bg-FunctionDark/15" title="Function">f</div></div>
<div class="namespaceItemContent">
<a href="..&#x2F;..&#x2F;..&#x2F;.&#x2F;api&#x2F;services&#x2F;ws-service.ts&#x2F;~&#x2F;broadcastCommentDeleted.html" title="broadcastCommentDeleted">
<span>broadcastCommentDeleted</span><span>(<div class="ml-4"><div><span>commentId<span>: <span class="td-kw">string</span></span></span>,</div><div><span>dump<span>: { id<span>: <span class="td-kw">string</span></span>; isPrivate<span>: <span class="td-kw">boolean</span></span>; userId<span>: <span class="td-kw">string</span></span>; }</span></span></div></div>)<span>: <span class="td-kw">void</span></span></span></a><div class="namespaceItemContentDoc"><div class="markdown_summary"><p>Sends <code>comment_deleted</code>, as <a href="../../.././api/services/ws-service.ts/~/broadcastCommentCreated.html">broadcastCommentCreated</a> does.</p>
</div></div></div>
</div><div id="namespace_broadcastcommentlikeupdate" class="namespaceItem" ><div class="docNodeKindIcon"><div class="text-Function bg-Function/15 dark:text-FunctionDark dark:bg-FunctionDark/15" title="Function">f</div></div>
<div class="namespaceItemContent">
<a href="..&#x2F;..&#x2F;..&#x2F;.&#x2F;api&#x2F;services&#x2F;ws-service.ts&#x2F;~&#x2F;broadcastCommentLikeUpdate.html" title="broadcastCommentLikeUpdate">
<span>broadcastCommentLikeUpdate</span><span>(<div class="ml-4"><div><span>commentId<span>: <span class="td-kw">string</span></span></span>,</div><div><span>likeCount<span>: <span class="td-kw">number</span></span></span>,</div><div><span>likerId<span>: <span class="td-kw">string</span></span></span>,</div><div><span>action<span>: <span><span class="td-str">"cast"</span><span class="td-op"> | </span><span class="td-str">"remove"</span></span></span></span>,</div><div><span>onlyUserId<span class="td-op">?</span><span>: <span class="td-kw">string</span></span></span></div></div>)<span>: <span class="td-kw">void</span></span></span></a><div class="namespaceItemContentDoc"><div class="markdown_summary"><p>Sends <code>comment_likes_update</code> after the route stored a like, to everyone, or
with <code>onlyUserId</code> to that user's sockets alone (for a comment on a private
dump).</p>
</div></div></div>
</div><div id="namespace_broadcastcommentupdated" class="namespaceItem" ><div class="docNodeKindIcon"><div class="text-Function bg-Function/15 dark:text-FunctionDark dark:bg-FunctionDark/15" title="Function">f</div></div>
<div class="namespaceItemContent">
<a href="..&#x2F;..&#x2F;..&#x2F;.&#x2F;api&#x2F;services&#x2F;ws-service.ts&#x2F;~&#x2F;broadcastCommentUpdated.html" title="broadcastCommentUpdated">
<span>broadcastCommentUpdated</span><span>(<div class="ml-4"><div><span>comment<span>: <span class="td-ref">Comment</span></span></span>,</div><div><span>dump<span>: { isPrivate<span>: <span class="td-kw">boolean</span></span>; userId<span>: <span class="td-kw">string</span></span>; }</span></span></div></div>)<span>: <span class="td-kw">void</span></span></span></a><div class="namespaceItemContentDoc"><div class="markdown_summary"><p>Sends <code>comment_updated</code>, as <a href="../../.././api/services/ws-service.ts/~/broadcastCommentCreated.html">broadcastCommentCreated</a> does.</p>
</div></div></div>
</div><div id="namespace_broadcastdumpchange" class="namespaceItem" ><div class="docNodeKindIcon"><div class="text-Function bg-Function/15 dark:text-FunctionDark dark:bg-FunctionDark/15" title="Function">f</div></div>
<div class="namespaceItemContent">
<a href="..&#x2F;..&#x2F;..&#x2F;.&#x2F;api&#x2F;services&#x2F;ws-service.ts&#x2F;~&#x2F;broadcastDumpChange.html" title="broadcastDumpChange">
<span>broadcastDumpChange</span><span>(<div class="ml-4"><div><span>before<span>: <span><span class="td-ref">Dump</span><span class="td-op"> | </span><span class="td-kw">null</span></span></span></span>,</div><div><span>after<span>: <span><span class="td-ref">Dump</span><span class="td-op"> | </span><span class="td-kw">null</span></span></span></span></div></div>)<span>: <span class="td-kw">void</span></span></span></a><div class="namespaceItemContentDoc"><div class="markdown_summary"><p>Tells the clients that may see a dump how it changed, as
broadcastChange says, for the dump service after every write.</p>
</div></div></div>
</div><div id="namespace_broadcastdumprelatedchanged" class="namespaceItem" ><div class="docNodeKindIcon"><div class="text-Function bg-Function/15 dark:text-FunctionDark dark:bg-FunctionDark/15" title="Function">f</div></div>
<div class="namespaceItemContent">
<a href="..&#x2F;..&#x2F;..&#x2F;.&#x2F;api&#x2F;services&#x2F;ws-service.ts&#x2F;~&#x2F;broadcastDumpRelatedChanged.html" title="broadcastDumpRelatedChanged">
<span>broadcastDumpRelatedChanged</span><span>(<div class="ml-4"><div><span>dump<span>: <span class="td-ref">Pick</span>&lt;<span class="td-ref">Dump</span><span>, </span><span><span class="td-str">"id"</span><span class="td-op"> | </span><span class="td-str">"isPrivate"</span><span class="td-op"> | </span><span class="td-str">"userId"</span></span>&gt;</span></span>,</div><div><span>others<span>: <span class="td-ref">Pick</span>&lt;<span class="td-ref">Dump</span><span>, </span><span><span class="td-str">"id"</span><span class="td-op"> | </span><span class="td-str">"isPrivate"</span><span class="td-op"> | </span><span class="td-str">"userId"</span></span>&gt;[]</span></span></div></div>)<span>: <span class="td-kw">void</span></span></span></a><div class="namespaceItemContentDoc"><div class="markdown_summary"><p>Sends <code>dump_related_changed</code> after the links between <code>dump</code> and each of
<code>others</code> changed, for the backlink service. A link shows only to those who
may see both its ends, so a socket is told only when its user may see
<code>dump</code> and one of <code>others</code>, and of those alone.</p>
</div></div></div>
</div><div id="namespace_broadcastplaylistchange" class="namespaceItem" ><div class="docNodeKindIcon"><div class="text-Function bg-Function/15 dark:text-FunctionDark dark:bg-FunctionDark/15" title="Function">f</div></div>
<div class="namespaceItemContent">
<a href="..&#x2F;..&#x2F;..&#x2F;.&#x2F;api&#x2F;services&#x2F;ws-service.ts&#x2F;~&#x2F;broadcastPlaylistChange.html" title="broadcastPlaylistChange">
<span>broadcastPlaylistChange</span><span>(<div class="ml-4"><div><span>before<span>: <span><span class="td-ref">Playlist</span><span class="td-op"> | </span><span class="td-kw">null</span></span></span></span>,</div><div><span>after<span>: <span><span class="td-ref">Playlist</span><span class="td-op"> | </span><span class="td-kw">null</span></span></span></span>,</div><div><span>members<span>: <span class="td-ref">DatedPlaylistMember</span>[]</span></span></div></div>)<span>: <span class="td-kw">void</span></span></span></a><div class="namespaceItemContentDoc"><div class="markdown_summary"><p>Tells the clients that may see a playlist how it changed, as
broadcastChange says, for the playlist service after every write to
its details. A playlist turned private thus leaves the others' views without
their learning what it became. <code>members</code> are the dumps of <code>after</code>: each
socket gets the playlist with the count of those its user may see and the
date of the latest, the owner's private dumps being theirs alone to count.</p>
</div></div></div>
</div><div id="namespace_broadcastplaylistdumpsupdated" class="namespaceItem" ><div class="docNodeKindIcon"><div class="text-Function bg-Function/15 dark:text-FunctionDark dark:bg-FunctionDark/15" title="Function">f</div></div>
<div class="namespaceItemContent">
<a href="..&#x2F;..&#x2F;..&#x2F;.&#x2F;api&#x2F;services&#x2F;ws-service.ts&#x2F;~&#x2F;broadcastPlaylistDumpsUpdated.html" title="broadcastPlaylistDumpsUpdated">
<span>broadcastPlaylistDumpsUpdated</span><span>(<div class="ml-4"><div><span>playlist<span>: <span class="td-ref">Pick</span>&lt;<span class="td-ref">Playlist</span><span>, </span><span><span class="td-str">"id"</span><span class="td-op"> | </span><span class="td-str">"isPublic"</span><span class="td-op"> | </span><span class="td-str">"userId"</span></span>&gt;</span></span>,</div><div><span>members<span>: <span class="td-ref">DatedPlaylistMember</span>[]</span></span></div></div>)<span>: <span class="td-kw">void</span></span></span></a><div class="namespaceItemContentDoc"><div class="markdown_summary"><p>Sends <code>playlist_dumps_updated</code> to the playlist's audience, each socket with
only the dumps its user may see, as <code>GET</code> would list them. The private dumps
in a public playlist stay hidden from everyone but their posters, and so
does their number: a client counts the dumps it is sent.</p>
</div></div></div>
</div><div id="namespace_broadcastpresence" class="namespaceItem" ><div class="docNodeKindIcon"><div class="text-Function bg-Function/15 dark:text-FunctionDark dark:bg-FunctionDark/15" title="Function">f</div></div>
<div class="namespaceItemContent">
<a href="..&#x2F;..&#x2F;..&#x2F;.&#x2F;api&#x2F;services&#x2F;ws-service.ts&#x2F;~&#x2F;broadcastPresence.html" title="broadcastPresence">
<span>broadcastPresence</span><span>()<span>: <span class="td-kw">void</span></span></span></a><div class="namespaceItemContentDoc"><div class="markdown_summary"><p>Sends everyone <code>presence_update</code>. The route calls it whenever a socket opens
or closes.</p>
</div></div></div>
</div><div id="namespace_broadcasttyping" class="namespaceItem" ><div class="docNodeKindIcon"><div class="text-Function bg-Function/15 dark:text-FunctionDark dark:bg-FunctionDark/15" title="Function">f</div></div>
<div class="namespaceItemContent">
<a href="..&#x2F;..&#x2F;..&#x2F;.&#x2F;api&#x2F;services&#x2F;ws-service.ts&#x2F;~&#x2F;broadcastTyping.html" title="broadcastTyping">
<span>broadcastTyping</span><span>()<span>: <span class="td-kw">void</span></span></span></a><div class="namespaceItemContentDoc"><div class="markdown_summary"><p>Sends <code>chat_typing_update</code> to every signed-in socket. The route also calls
it when a typing user's socket closes.</p>
</div></div></div>
</div><div id="namespace_broadcastuserupdated" class="namespaceItem" ><div class="docNodeKindIcon"><div class="text-Function bg-Function/15 dark:text-FunctionDark dark:bg-FunctionDark/15" title="Function">f</div></div>
<div class="namespaceItemContent">
<a href="..&#x2F;..&#x2F;..&#x2F;.&#x2F;api&#x2F;services&#x2F;ws-service.ts&#x2F;~&#x2F;broadcastUserUpdated.html" title="broadcastUserUpdated">
<span>broadcastUserUpdated</span><span>(<span><span>user<span>: <span class="td-ref">PublicUser</span></span></span></span>)<span>: <span class="td-kw">void</span></span></span></a><div class="namespaceItemContentDoc"><div class="markdown_summary"><p>Sends everyone <code>user_updated</code>, after a profile edit, a role change or an
avatar upload.</p>
</div></div></div>
</div><div id="namespace_broadcastvoteupdate" class="namespaceItem" ><div class="docNodeKindIcon"><div class="text-Function bg-Function/15 dark:text-FunctionDark dark:bg-FunctionDark/15" title="Function">f</div></div>
<div class="namespaceItemContent">
<a href="..&#x2F;..&#x2F;..&#x2F;.&#x2F;api&#x2F;services&#x2F;ws-service.ts&#x2F;~&#x2F;broadcastVoteUpdate.html" title="broadcastVoteUpdate">
<span>broadcastVoteUpdate</span><span>(<div class="ml-4"><div><span>dumpId<span>: <span class="td-kw">string</span></span></span>,</div><div><span>voteCount<span>: <span class="td-kw">number</span></span></span>,</div><div><span>voterId<span>: <span class="td-kw">string</span></span></span>,</div><div><span>action<span>: <span><span class="td-str">"cast"</span><span class="td-op"> | </span><span class="td-str">"remove"</span></span></span></span>,</div><div><span>onlyUserId<span class="td-op">?</span><span>: <span class="td-kw">string</span></span></span></div></div>)<span>: <span class="td-kw">void</span></span></span></a><div class="namespaceItemContentDoc"><div class="markdown_summary"><p>Sends <code>votes_update</code> after the route stored a vote, to everyone, or with
<code>onlyUserId</code> to that user's sockets alone (for a private dump).</p>
</div></div></div>
</div><div id="namespace_closeusersockets" class="namespaceItem" ><div class="docNodeKindIcon"><div class="text-Function bg-Function/15 dark:text-FunctionDark dark:bg-FunctionDark/15" title="Function">f</div></div>
<div class="namespaceItemContent">
<a href="..&#x2F;..&#x2F;..&#x2F;.&#x2F;api&#x2F;services&#x2F;ws-service.ts&#x2F;~&#x2F;closeUserSockets.html" title="closeUserSockets">
<span>closeUserSockets</span><span>(<div class="ml-4"><div><span>userId<span>: <span class="td-kw">string</span></span></span>,</div><div><span>reason<span>: <span class="td-kw">string</span></span></span></div></div>)<span>: <span class="td-kw">void</span></span></span></a><div class="namespaceItemContentDoc"><div class="markdown_summary"><p>Closes every socket of <code>userId</code> without signing anyone out. The user service
calls it on a password change. Each tab reconnects with the token it holds:
the tab that made the change has a fresh one and stays signed in, while the
others connect anonymously and sign out on their next 401.</p>
</div></div></div>
</div><div id="namespace_getonlineusers" class="namespaceItem" ><div class="docNodeKindIcon"><div class="text-Function bg-Function/15 dark:text-FunctionDark dark:bg-FunctionDark/15" title="Function">f</div></div>
<div class="namespaceItemContent">
<a href="..&#x2F;..&#x2F;..&#x2F;.&#x2F;api&#x2F;services&#x2F;ws-service.ts&#x2F;~&#x2F;getOnlineUsers.html" title="getOnlineUsers">
<span>getOnlineUsers</span><span>()<span>: <span class="td-ref">OnlineUser</span>[]</span></span></a><div class="namespaceItemContentDoc"><div class="markdown_summary"><p>Returns each signed-in user with a socket open, once. The route sends it in
<code>welcome</code>, and <a href="../../.././api/services/ws-service.ts/~/broadcastPresence.html">broadcastPresence</a> in <code>presence_update</code>.</p>
</div></div></div>
</div><div id="namespace_gettypingusers" class="namespaceItem" ><div class="docNodeKindIcon"><div class="text-Function bg-Function/15 dark:text-FunctionDark dark:bg-FunctionDark/15" title="Function">f</div></div>
<div class="namespaceItemContent">
<a href="..&#x2F;..&#x2F;..&#x2F;.&#x2F;api&#x2F;services&#x2F;ws-service.ts&#x2F;~&#x2F;getTypingUsers.html" title="getTypingUsers">
<span>getTypingUsers</span><span>()<span>: <span class="td-ref">OnlineUser</span>[]</span></span></a><div class="namespaceItemContentDoc"><div class="markdown_summary"><p>Returns each user who sent <code>chat_typing</code> true within the last
TYPING_TTL, once, for <a href="../../.././api/services/ws-service.ts/~/broadcastTyping.html">broadcastTyping</a>.</p>
</div></div></div>
</div><div id="namespace_handleclientpong" class="namespaceItem" ><div class="docNodeKindIcon"><div class="text-Function bg-Function/15 dark:text-FunctionDark dark:bg-FunctionDark/15" title="Function">f</div></div>
<div class="namespaceItemContent">
<a href="..&#x2F;..&#x2F;..&#x2F;.&#x2F;api&#x2F;services&#x2F;ws-service.ts&#x2F;~&#x2F;handleClientPong.html" title="handleClientPong">
<span>handleClientPong</span><span>(<span><span>client<span>: <span class="td-ref">WsClient</span></span></span></span>)<span>: <span class="td-kw">void</span></span></span></a><div class="namespaceItemContentDoc"><div class="markdown_summary"><p>Records a client's <code>pong</code>, which keeps its socket open.</p>
</div></div></div>
</div><div id="namespace_isuserchatopen" class="namespaceItem" ><div class="docNodeKindIcon"><div class="text-Function bg-Function/15 dark:text-FunctionDark dark:bg-FunctionDark/15" title="Function">f</div></div>
<div class="namespaceItemContent">
<a href="..&#x2F;..&#x2F;..&#x2F;.&#x2F;api&#x2F;services&#x2F;ws-service.ts&#x2F;~&#x2F;isUserChatOpen.html" title="isUserChatOpen">
<span>isUserChatOpen</span><span>(<span><span>userId<span>: <span class="td-kw">string</span></span></span></span>)<span>: <span class="td-kw">boolean</span></span></span></a><div class="namespaceItemContentDoc"><div class="markdown_summary"><p>Checks whether <code>userId</code> is reading the chat on any of their sockets. The
route passes it to <code>notifyMentions</code>, which skips those users: they saw the
mention live, and their client chimes for it.</p>
</div></div></div>
</div><div id="namespace_register" class="namespaceItem" ><div class="docNodeKindIcon"><div class="text-Function bg-Function/15 dark:text-FunctionDark dark:bg-FunctionDark/15" title="Function">f</div></div>
<div class="namespaceItemContent">
<a href="..&#x2F;..&#x2F;..&#x2F;.&#x2F;api&#x2F;services&#x2F;ws-service.ts&#x2F;~&#x2F;register.html" title="register">
<span>register</span><span>(<span><span>client<span>: <span class="td-ref">WsClient</span></span></span></span>)<span>: <span class="td-kw">void</span></span></span></a><div class="namespaceItemContentDoc"><div class="markdown_summary"><p>Adds a client once its socket opens. From then on, it receives the
broadcasts, and is shown online if it is signed in.</p>
</div></div></div>
</div><div id="namespace_send" class="namespaceItem" ><div class="docNodeKindIcon"><div class="text-Function bg-Function/15 dark:text-FunctionDark dark:bg-FunctionDark/15" title="Function">f</div></div>
<div class="namespaceItemContent">
<a href="..&#x2F;..&#x2F;..&#x2F;.&#x2F;api&#x2F;services&#x2F;ws-service.ts&#x2F;~&#x2F;send.html" title="send">
<span>send</span><span>(<div class="ml-4"><div><span>socket<span>: <span class="td-ref">WebSocket</span></span></span>,</div><div><span>data<span>: <span class="td-ref">ServerToClientMessage</span></span></span></div></div>)<span>: <span class="td-kw">void</span></span></span></a><div class="namespaceItemContentDoc"><div class="markdown_summary"><p>Sends <code>data</code> to one socket unless it is closing. The WebSocket route answers
a client's own message with it: a pong, a vote's ack, an error.</p>
</div></div></div>
</div><div id="namespace_sendtouser" class="namespaceItem" ><div class="docNodeKindIcon"><div class="text-Function bg-Function/15 dark:text-FunctionDark dark:bg-FunctionDark/15" title="Function">f</div></div>
<div class="namespaceItemContent">
<a href="..&#x2F;..&#x2F;..&#x2F;.&#x2F;api&#x2F;services&#x2F;ws-service.ts&#x2F;~&#x2F;sendToUser.html" title="sendToUser">
<span>sendToUser</span><span>(<div class="ml-4"><div><span>userId<span>: <span class="td-kw">string</span></span></span>,</div><div><span>data<span>: <span class="td-ref">ServerToClientMessage</span></span></span></div></div>)<span>: <span class="td-kw">void</span></span></span></a><div class="namespaceItemContentDoc"><div class="markdown_summary"><p>Sends <code>data</code> to every socket of <code>userId</code>. The notification service sends
<code>notification_created</code> and <code>notifications_read</code> with it, the follow service
<code>follows_changed</code>, and the route <code>chat_read_update</code>.</p>
</div></div></div>
</div><div id="namespace_setclienttyping" class="namespaceItem" ><div class="docNodeKindIcon"><div class="text-Function bg-Function/15 dark:text-FunctionDark dark:bg-FunctionDark/15" title="Function">f</div></div>
<div class="namespaceItemContent">
<a href="..&#x2F;..&#x2F;..&#x2F;.&#x2F;api&#x2F;services&#x2F;ws-service.ts&#x2F;~&#x2F;setClientTyping.html" title="setClientTyping">
<span>setClientTyping</span><span>(<div class="ml-4"><div><span>client<span>: <span class="td-ref">WsClient</span></span></span>,</div><div><span>typing<span>: <span class="td-kw">boolean</span></span></span></div></div>)<span>: <span class="td-kw">void</span></span></span></a><div class="namespaceItemContentDoc"><div class="markdown_summary"><p>Records a client's <code>chat_typing</code>, and broadcasts the typing list when the
client starts or stops. The route also clears it when the client sends a
message. A client that stops without saying so is expired after
TYPING_TTL.</p>
</div></div></div>
</div><div id="namespace_taketoken" class="namespaceItem" ><div class="docNodeKindIcon"><div class="text-Function bg-Function/15 dark:text-FunctionDark dark:bg-FunctionDark/15" title="Function">f</div></div>
<div class="namespaceItemContent">
<a href="..&#x2F;..&#x2F;..&#x2F;.&#x2F;api&#x2F;lib&#x2F;rate-limit.ts&#x2F;~&#x2F;takeToken.html" title="takeToken">
<span>takeToken</span><span>(<div class="ml-4"><div><span>key<span>: <span class="td-kw">string</span></span></span>,</div><div><span>limit<span>: <span class="td-ref">RateLimit</span></span></span></div></div>)<span>: <span class="td-kw">boolean</span></span></span></a><div class="namespaceItemContentDoc"><div class="markdown_summary"><p>Takes one token from <code>key</code>'s bucket, or returns false when it is empty. The
caller picks the key, such as <code>typing:&lt;userId&gt;</code>, and keys by user or account
so that more tabs or parallel requests don't buy more tokens.</p>
</div></div></div>
</div><div id="namespace_unregister" class="namespaceItem" ><div class="docNodeKindIcon"><div class="text-Function bg-Function/15 dark:text-FunctionDark dark:bg-FunctionDark/15" title="Function">f</div></div>
<div class="namespaceItemContent">
<a href="..&#x2F;..&#x2F;..&#x2F;.&#x2F;api&#x2F;services&#x2F;ws-service.ts&#x2F;~&#x2F;unregister.html" title="unregister">
<span>unregister</span><span>(<span><span>client<span>: <span class="td-ref">WsClient</span></span></span></span>)<span>: <span class="td-kw">void</span></span></span></a><div class="namespaceItemContentDoc"><div class="markdown_summary"><p>Removes a client when its socket closes. The caller rebroadcasts presence.</p>
</div></div></div>
</div><div id="namespace_updateclientavatar" class="namespaceItem" ><div class="docNodeKindIcon"><div class="text-Function bg-Function/15 dark:text-FunctionDark dark:bg-FunctionDark/15" title="Function">f</div></div>
<div class="namespaceItemContent">
<a href="..&#x2F;..&#x2F;..&#x2F;.&#x2F;api&#x2F;services&#x2F;ws-service.ts&#x2F;~&#x2F;updateClientAvatar.html" title="updateClientAvatar">
<span>updateClientAvatar</span><span>(<div class="ml-4"><div><span>userId<span>: <span class="td-kw">string</span></span></span>,</div><div><span>avatarMime<span>: <span class="td-kw">string</span></span></span></div></div>)<span>: <span class="td-kw">void</span></span></span></a><div class="namespaceItemContentDoc"><div class="markdown_summary"><p>Records <code>userId</code>'s new avatar on their clients, so presence and the typing
list show it, and rebroadcasts presence. <code>routes/avatars.ts</code> calls it on an
upload.</p>
</div></div></div>
</div><div id="namespace_updateclientusername" class="namespaceItem" ><div class="docNodeKindIcon"><div class="text-Function bg-Function/15 dark:text-FunctionDark dark:bg-FunctionDark/15" title="Function">f</div></div>
<div class="namespaceItemContent">
<a href="..&#x2F;..&#x2F;..&#x2F;.&#x2F;api&#x2F;services&#x2F;ws-service.ts&#x2F;~&#x2F;updateClientUsername.html" title="updateClientUsername">
<span>updateClientUsername</span><span>(<div class="ml-4"><div><span>userId<span>: <span class="td-kw">string</span></span></span>,</div><div><span>username<span>: <span class="td-kw">string</span></span></span></div></div>)<span>: <span class="td-kw">void</span></span></span></a><div class="namespaceItemContentDoc"><div class="markdown_summary"><p>Records <code>userId</code>'s new name on their clients, so presence and the typing
list show it, and rebroadcasts presence. The routes that rename a user call
it, along with <code>broadcastUserUpdated</code>.</p>
</div></div></div>
</div></div>
</section>
<section class="section" id="api&#x2F;services&#x2F;ws-service_ts_interfaces"><div>
<h2 class="anchorable mb-1"><a href="#api&#x2F;services&#x2F;ws-service_ts_interfaces" class="anchor" aria-label="Anchor" tabIndex="-1"><svg
width="16"
height="16"
viewBox="0 0 14 14"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g clip-path="url(#clip0_1081_1237)">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M6.80328 2.8656C6.68736 2.99 6.62425 3.15454 6.62725 3.32456C6.63025 3.49457 6.69913 3.65678 6.81936 3.77702C6.9396 3.89725 7.10181 3.96613 7.27182 3.96913C7.44184 3.97213 7.60638 3.90902 7.73078 3.7931L8.82453 2.69935C8.98712 2.53676 9.18015 2.40778 9.39259 2.31978C9.60503 2.23179 9.83272 2.1865 10.0627 2.1865C10.2926 2.1865 10.5203 2.23179 10.7327 2.31978C10.9452 2.40778 11.1382 2.53676 11.3008 2.69935C11.4634 2.86194 11.5923 3.05497 11.6803 3.2674C11.7683 3.47984 11.8136 3.70753 11.8136 3.93747C11.8136 4.16741 11.7683 4.3951 11.6803 4.60754C11.5923 4.81998 11.4634 5.013 11.3008 5.1756L9.11328 7.3631C8.95075 7.52581 8.75775 7.65488 8.5453 7.74295C8.33285 7.83102 8.10513 7.87635 7.87516 7.87635C7.64518 7.87635 7.41746 7.83102 7.20501 7.74295C6.99256 7.65488 6.79956 7.52581 6.63703 7.3631C6.51263 7.24718 6.34809 7.18407 6.17807 7.18707C6.00806 7.19007 5.84585 7.25894 5.72561 7.37918C5.60538 7.49942 5.5365 7.66163 5.5335 7.83164C5.5305 8.00165 5.59361 8.1662 5.70953 8.2906C5.99391 8.57501 6.33154 8.80062 6.70312 8.95455C7.0747 9.10847 7.47296 9.18769 7.87516 9.18769C8.27736 9.18769 8.67562 9.10847 9.0472 8.95455C9.41878 8.80062 9.7564 8.57501 10.0408 8.2906L12.2283 6.1031C12.8026 5.52874 13.1253 4.74974 13.1253 3.93747C13.1253 3.12521 12.8026 2.34621 12.2283 1.77185C11.6539 1.19749 10.8749 0.874817 10.0627 0.874817C9.25039 0.874817 8.47139 1.19749 7.89703 1.77185L6.80328 2.8656ZM2.69953 11.3006C2.53682 11.1381 2.40774 10.9451 2.31968 10.7326C2.23161 10.5202 2.18628 10.2924 2.18628 10.0625C2.18628 9.8325 2.23161 9.60477 2.31968 9.39233C2.40774 9.17988 2.53682 8.98688 2.69953 8.82435L4.88703 6.63685C5.04956 6.47414 5.24256 6.34506 5.45501 6.25699C5.66746 6.16893 5.89518 6.1236 6.12516 6.1236C6.35513 6.1236 6.58285 6.16893 6.7953 6.25699C7.00775 6.34506 7.20075 6.47414 7.36328 6.63685C7.48768 6.75277 7.65223 6.81588 7.82224 6.81288C7.99225 6.80988 8.15446 6.741 8.2747 6.62077C8.39494 6.50053 8.46381 6.33832 8.46681 6.1683C8.46981 5.99829 8.4067 5.83375 8.29078 5.70935C8.0064 5.42494 7.66878 5.19933 7.2972 5.0454C6.92562 4.89148 6.52736 4.81225 6.12516 4.81225C5.72296 4.81225 5.3247 4.89148 4.95312 5.0454C4.58154 5.19933 4.24391 5.42494 3.95953 5.70935L1.77203 7.89685C1.19767 8.47121 0.875 9.25021 0.875 10.0625C0.875 10.8747 1.19767 11.6537 1.77203 12.2281C2.34639 12.8025 3.12539 13.1251 3.93766 13.1251C4.74992 13.1251 5.52892 12.8025 6.10328 12.2281L7.19703 11.1343C7.31295 11.0099 7.37606 10.8454 7.37306 10.6754C7.37006 10.5054 7.30119 10.3432 7.18095 10.2229C7.06071 10.1027 6.8985 10.0338 6.72849 10.0308C6.55848 10.0278 6.39393 10.0909 6.26953 10.2068L5.17578 11.3006C5.01325 11.4633 4.82025 11.5924 4.6078 11.6805C4.39535 11.7685 4.16763 11.8138 3.93766 11.8138C3.70768 11.8138 3.47996 11.7685 3.26751 11.6805C3.05506 11.5924 2.86206 11.4633 2.69953 11.3006Z"
fill="currentColor"
/>
</g>
<defs>
<clipPath id="clip0_1081_1237">
<rect width="14" height="14" fill="white" />
</clipPath>
</defs>
</svg>
</a>
Interfaces</h2></div><div class="namespaceSection"><div id="namespace_ratelimit" class="namespaceItem" ><div class="docNodeKindIcon"><div class="text-Interface bg-Interface/15 dark:text-InterfaceDark dark:bg-InterfaceDark/15" title="Interface">I</div></div>
<div class="namespaceItemContent">
<a href="..&#x2F;..&#x2F;..&#x2F;.&#x2F;api&#x2F;lib&#x2F;rate-limit.ts&#x2F;~&#x2F;RateLimit.html" title="RateLimit">
<span>RateLimit</span></a><div class="namespaceItemContentDoc"><div class="markdown_summary"><p>Passed to <a href="../../.././api/services/ws-service.ts/~/takeToken.html">takeToken</a> by each limited action, which declares its own
as a constant.</p>
</div></div><ul class="namespaceItemContentSubItems"><li>
<a href="..&#x2F;..&#x2F;..&#x2F;.&#x2F;api&#x2F;lib&#x2F;rate-limit.ts&#x2F;~&#x2F;RateLimit.html#property_capacity">
<span>capacity</span><span>: <span class="td-kw">number</span></span></a><div class="namespaceItemContentDoc"><div class="markdown_summary"><p>How many tokens a full bucket holds, and so the largest burst.</p>
</div></div></li><li>
<a href="..&#x2F;..&#x2F;..&#x2F;.&#x2F;api&#x2F;lib&#x2F;rate-limit.ts&#x2F;~&#x2F;RateLimit.html#property_refill">
<span>refill</span><span>: <span class="td-kw">number</span></span></a><div class="namespaceItemContentDoc"><div class="markdown_summary"><p>Tokens regained per second.</p>
</div></div></li></ul></div>
</div><div id="namespace_wsclient" class="namespaceItem" ><div class="docNodeKindIcon"><div class="text-Interface bg-Interface/15 dark:text-InterfaceDark dark:bg-InterfaceDark/15" title="Interface">I</div></div>
<div class="namespaceItemContent">
<a href="..&#x2F;..&#x2F;..&#x2F;.&#x2F;api&#x2F;services&#x2F;ws-service.ts&#x2F;~&#x2F;WsClient.html" title="WsClient">
<span>WsClient</span></a><div class="namespaceItemContentDoc"><div class="markdown_summary"><p>Created by the WebSocket route for each socket it opens, and kept here until
the socket closes. The user fields are read at connect and left undefined for
an anonymous socket.</p>
</div></div><ul class="namespaceItemContentSubItems"><li>
<a href="..&#x2F;..&#x2F;..&#x2F;.&#x2F;api&#x2F;services&#x2F;ws-service.ts&#x2F;~&#x2F;WsClient.html#property_avatarmime">
<span>avatarMime</span><span>: <span class="td-kw">string</span></span></a></li><li>
<a href="..&#x2F;..&#x2F;..&#x2F;.&#x2F;api&#x2F;services&#x2F;ws-service.ts&#x2F;~&#x2F;WsClient.html#property_avatarversion">
<span>avatarVersion</span><span>: <span class="td-kw">number</span></span></a><div class="namespaceItemContentDoc"><div class="markdown_summary"><p>Set by <a href="../../.././api/services/ws-service.ts/~/updateClientAvatar.html">updateClientAvatar</a>, for the client's avatar URLs.</p>
</div></div></li><li>
<a href="..&#x2F;..&#x2F;..&#x2F;.&#x2F;api&#x2F;services&#x2F;ws-service.ts&#x2F;~&#x2F;WsClient.html#property_chatopen">
<span>chatOpen</span><span>: <span class="td-kw">boolean</span></span></a><div class="namespaceItemContentDoc"><div class="markdown_summary"><p>Set by <code>chat_focus</code>: the chatbox is open and the tab visible, so the user
is reading the chat and gets no notification for a mention in it.</p>
</div></div></li><li>
<a href="..&#x2F;..&#x2F;..&#x2F;.&#x2F;api&#x2F;services&#x2F;ws-service.ts&#x2F;~&#x2F;WsClient.html#property_pongreceived">
<span>pongReceived</span><span>: <span class="td-kw">boolean</span></span></a><div class="namespaceItemContentDoc"><div class="markdown_summary"><p>Undefined until the first ping, so a new socket isn't closed at once.</p>
</div></div></li><li>
<a href="..&#x2F;..&#x2F;..&#x2F;.&#x2F;api&#x2F;services&#x2F;ws-service.ts&#x2F;~&#x2F;WsClient.html#property_role">
<span>role</span><span>: <span class="td-ref">Role</span></span></a><div class="namespaceItemContentDoc"><div class="markdown_summary"><p>Read again from the database on each chat edit or delete, so a demoted
moderator loses the right at once.</p>
</div></div></li><li>
<a href="..&#x2F;..&#x2F;..&#x2F;.&#x2F;api&#x2F;services&#x2F;ws-service.ts&#x2F;~&#x2F;WsClient.html#property_socket">
<span>socket</span><span>: <span class="td-ref">WebSocket</span></span></a></li><li>
<a href="..&#x2F;..&#x2F;..&#x2F;.&#x2F;api&#x2F;services&#x2F;ws-service.ts&#x2F;~&#x2F;WsClient.html#property_typingat">
<span>typingAt</span><span>: <span class="td-kw">number</span></span></a><div class="namespaceItemContentDoc"><div class="markdown_summary"><p>When the last <code>chat_typing</code> true arrived, in ms.</p>
</div></div></li><li>
<a href="..&#x2F;..&#x2F;..&#x2F;.&#x2F;api&#x2F;services&#x2F;ws-service.ts&#x2F;~&#x2F;WsClient.html#property_userid">
<span>userId</span><span>: <span class="td-kw">string</span></span></a></li><li>
<a href="..&#x2F;..&#x2F;..&#x2F;.&#x2F;api&#x2F;services&#x2F;ws-service.ts&#x2F;~&#x2F;WsClient.html#property_username">
<span>username</span><span>: <span class="td-kw">string</span></span></a></li></ul></div>
</div></div>
</section>
<section class="section" id="api&#x2F;services&#x2F;ws-service_ts_variables"><div>
<h2 class="anchorable mb-1"><a href="#api&#x2F;services&#x2F;ws-service_ts_variables" class="anchor" aria-label="Anchor" tabIndex="-1"><svg
width="16"
height="16"
viewBox="0 0 14 14"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g clip-path="url(#clip0_1081_1237)">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M6.80328 2.8656C6.68736 2.99 6.62425 3.15454 6.62725 3.32456C6.63025 3.49457 6.69913 3.65678 6.81936 3.77702C6.9396 3.89725 7.10181 3.96613 7.27182 3.96913C7.44184 3.97213 7.60638 3.90902 7.73078 3.7931L8.82453 2.69935C8.98712 2.53676 9.18015 2.40778 9.39259 2.31978C9.60503 2.23179 9.83272 2.1865 10.0627 2.1865C10.2926 2.1865 10.5203 2.23179 10.7327 2.31978C10.9452 2.40778 11.1382 2.53676 11.3008 2.69935C11.4634 2.86194 11.5923 3.05497 11.6803 3.2674C11.7683 3.47984 11.8136 3.70753 11.8136 3.93747C11.8136 4.16741 11.7683 4.3951 11.6803 4.60754C11.5923 4.81998 11.4634 5.013 11.3008 5.1756L9.11328 7.3631C8.95075 7.52581 8.75775 7.65488 8.5453 7.74295C8.33285 7.83102 8.10513 7.87635 7.87516 7.87635C7.64518 7.87635 7.41746 7.83102 7.20501 7.74295C6.99256 7.65488 6.79956 7.52581 6.63703 7.3631C6.51263 7.24718 6.34809 7.18407 6.17807 7.18707C6.00806 7.19007 5.84585 7.25894 5.72561 7.37918C5.60538 7.49942 5.5365 7.66163 5.5335 7.83164C5.5305 8.00165 5.59361 8.1662 5.70953 8.2906C5.99391 8.57501 6.33154 8.80062 6.70312 8.95455C7.0747 9.10847 7.47296 9.18769 7.87516 9.18769C8.27736 9.18769 8.67562 9.10847 9.0472 8.95455C9.41878 8.80062 9.7564 8.57501 10.0408 8.2906L12.2283 6.1031C12.8026 5.52874 13.1253 4.74974 13.1253 3.93747C13.1253 3.12521 12.8026 2.34621 12.2283 1.77185C11.6539 1.19749 10.8749 0.874817 10.0627 0.874817C9.25039 0.874817 8.47139 1.19749 7.89703 1.77185L6.80328 2.8656ZM2.69953 11.3006C2.53682 11.1381 2.40774 10.9451 2.31968 10.7326C2.23161 10.5202 2.18628 10.2924 2.18628 10.0625C2.18628 9.8325 2.23161 9.60477 2.31968 9.39233C2.40774 9.17988 2.53682 8.98688 2.69953 8.82435L4.88703 6.63685C5.04956 6.47414 5.24256 6.34506 5.45501 6.25699C5.66746 6.16893 5.89518 6.1236 6.12516 6.1236C6.35513 6.1236 6.58285 6.16893 6.7953 6.25699C7.00775 6.34506 7.20075 6.47414 7.36328 6.63685C7.48768 6.75277 7.65223 6.81588 7.82224 6.81288C7.99225 6.80988 8.15446 6.741 8.2747 6.62077C8.39494 6.50053 8.46381 6.33832 8.46681 6.1683C8.46981 5.99829 8.4067 5.83375 8.29078 5.70935C8.0064 5.42494 7.66878 5.19933 7.2972 5.0454C6.92562 4.89148 6.52736 4.81225 6.12516 4.81225C5.72296 4.81225 5.3247 4.89148 4.95312 5.0454C4.58154 5.19933 4.24391 5.42494 3.95953 5.70935L1.77203 7.89685C1.19767 8.47121 0.875 9.25021 0.875 10.0625C0.875 10.8747 1.19767 11.6537 1.77203 12.2281C2.34639 12.8025 3.12539 13.1251 3.93766 13.1251C4.74992 13.1251 5.52892 12.8025 6.10328 12.2281L7.19703 11.1343C7.31295 11.0099 7.37606 10.8454 7.37306 10.6754C7.37006 10.5054 7.30119 10.3432 7.18095 10.2229C7.06071 10.1027 6.8985 10.0338 6.72849 10.0308C6.55848 10.0278 6.39393 10.0909 6.26953 10.2068L5.17578 11.3006C5.01325 11.4633 4.82025 11.5924 4.6078 11.6805C4.39535 11.7685 4.16763 11.8138 3.93766 11.8138C3.70768 11.8138 3.47996 11.7685 3.26751 11.6805C3.05506 11.5924 2.86206 11.4633 2.69953 11.3006Z"
fill="currentColor"
/>
</g>
<defs>
<clipPath id="clip0_1081_1237">
<rect width="14" height="14" fill="white" />
</clipPath>
</defs>
</svg>
</a>
Variables</h2></div><div class="namespaceSection"><div id="namespace_pingjob" class="namespaceItem" ><div class="docNodeKindIcon"><div class="text-Variable bg-Variable/15 dark:text-VariableDark dark:bg-VariableDark/15" title="Variable">v</div></div>
<div class="namespaceItemContent">
<a href="..&#x2F;..&#x2F;..&#x2F;.&#x2F;api&#x2F;services&#x2F;ws-service.ts&#x2F;~&#x2F;pingJob.html" title="pingJob">
<span>pingJob</span><span>: <span class="td-ref">Job</span></span></a><div class="namespaceItemContentDoc"><div class="markdown_summary"><p>Pings every client each PING_INTERVAL, closing the ones that didn't
answer the previous ping. <code>main.ts</code> starts it.</p>
</div></div></div>
</div><div id="namespace_typingexpiryjob" class="namespaceItem" ><div class="docNodeKindIcon"><div class="text-Variable bg-Variable/15 dark:text-VariableDark dark:bg-VariableDark/15" title="Variable">v</div></div>
<div class="namespaceItemContent">
<a href="..&#x2F;..&#x2F;..&#x2F;.&#x2F;api&#x2F;services&#x2F;ws-service.ts&#x2F;~&#x2F;typingExpiryJob.html" title="typingExpiryJob">
<span>typingExpiryJob</span><span>: <span class="td-ref">Job</span></span></a><div class="namespaceItemContentDoc"><div class="markdown_summary"><p>Expires the typing users who went quiet without a false, such as by closing
the tab, every 2 seconds. <code>main.ts</code> starts it.</p>
</div></div></div>
</div></div>
</section>
</div>
</div>
</section>
</main><div class="toc">
<div><div class="usageContent">
<h3>Usage</h3><div class="markdown"><pre class="highlight"><code class="language-typescript">import * as mod from &quot;api/services/ws-service.ts&quot;;
</code><button class="copyButton" data-copy="import * as mod from &quot;api/services/ws-service.ts&quot;;
"><svg
class="copy"
width="15"
height="15"
viewBox="0 0 15 15"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<rect x="2" y="2" width="7" height="7" fill="none" />
<rect x="6" y="6" width="7" height="7" fill="none" />
<path
d="M1.55566 2.7C1.55566 2.03726 2.09292 1.5 2.75566 1.5H8.75566C9.41841 1.5 9.95566 2.03726 9.95566 2.7V5.1H12.3557C13.0184 5.1 13.5557 5.63726 13.5557 6.3V12.3C13.5557 12.9627 13.0184 13.5 12.3557 13.5H6.35566C5.69292 13.5 5.15566 12.9627 5.15566 12.3V9.9H2.75566C2.09292 9.9 1.55566 9.36274 1.55566 8.7V2.7ZM6.35566 9.9V12.3H12.3557V6.3H9.95566V8.7C9.95566 9.36274 9.41841 9.9 8.75566 9.9H6.35566ZM8.75566 8.7V2.7H2.75566V8.7H8.75566Z"
fill="currentColor"
/>
</svg>
<svg
class="check"
width="15"
height="15"
viewBox="0 0 24 24"
stroke-width="2"
stroke="currentColor"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
xmlns="http://www.w3.org/2000/svg"
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M5 12l5 5l10 -10" />
</svg>
</button><code></code></pre>
</div></div><nav class="topSymbols">
<h3>Symbols</h3>
<ul><li>
<a href="..&#x2F;..&#x2F;..&#x2F;.&#x2F;api&#x2F;middleware&#x2F;auth.ts&#x2F;~&#x2F;AuthState.html" title="AuthState"><div class="docNodeKindIcon"><div class="text-Interface bg-Interface/15 dark:text-InterfaceDark dark:bg-InterfaceDark/15" title="Interface">I</div></div>
<span class="hover:bg-Interface/15 hover:bg-InterfaceDark/15">
AuthState
</span>
</a>
</li><li>
<a href="..&#x2F;..&#x2F;..&#x2F;.&#x2F;api&#x2F;middleware&#x2F;auth.ts&#x2F;~&#x2F;AuthContext.html" title="AuthContext"><div class="docNodeKindIcon"><div class="text-TypeAlias bg-TypeAlias/15 dark:text-TypeAliasDark dark:bg-TypeAliasDark/15" title="Type Alias">T</div></div>
<span class="hover:bg-TypeAlias/15 hover:bg-TypeAliasDark/15">
AuthContext
</span>
</a>
</li><li>
<a href="..&#x2F;..&#x2F;..&#x2F;.&#x2F;api&#x2F;middleware&#x2F;auth.ts&#x2F;~&#x2F;authMiddleware.html" title="authMiddleware"><div class="docNodeKindIcon"><div class="text-Function bg-Function/15 dark:text-FunctionDark dark:bg-FunctionDark/15" title="Function">f</div></div>
<span class="hover:bg-Function/15 hover:bg-FunctionDark/15">
authMiddleware
</span>
</a>
</li><li>
<a href="..&#x2F;..&#x2F;..&#x2F;.&#x2F;api&#x2F;middleware&#x2F;auth.ts&#x2F;~&#x2F;requirePermission.html" title="requirePermission"><div class="docNodeKindIcon"><div class="text-Function bg-Function/15 dark:text-FunctionDark dark:bg-FunctionDark/15" title="Function">f</div></div>
<span class="hover:bg-Function/15 hover:bg-FunctionDark/15">
requirePermission
</span>
</a>
</li><li>
<a href="..&#x2F;..&#x2F;..&#x2F;.&#x2F;api&#x2F;middleware&#x2F;error.ts&#x2F;~&#x2F;errorMiddleware.html" title="errorMiddleware"><div class="docNodeKindIcon"><div class="text-Function bg-Function/15 dark:text-FunctionDark dark:bg-FunctionDark/15" title="Function">f</div></div>
<span class="hover:bg-Function/15 hover:bg-FunctionDark/15">
errorMiddleware
</span>
</a>
</li></ul><a class="flex items-center gap-0.5" href="..&#x2F;..&#x2F;..&#x2F;.&#x2F;all_symbols.html">
<span class="leading-none">view all 1307 symbols</span><svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M5.76748 11.8159C5.5378 11.577 5.54525 11.1972 5.78411 10.9675L8.93431 8L5.78411 5.0325C5.54525 4.80282 5.5378 4.423 5.76748 4.18413C5.99715 3.94527 6.37698 3.93782 6.61584 4.1675L10.2158 7.5675C10.3335 7.68062 10.4 7.83679 10.4 8C10.4 8.16321 10.3335 8.31938 10.2158 8.4325L6.61584 11.8325C6.37698 12.0622 5.99715 12.0547 5.76748 11.8159Z"
fill="currentColor"
/>
</svg>
</a></nav><nav class="documentNavigation">
<h3>Document Navigation</h3><ul><li><a href="#api/services/ws-service_ts_functions" title="Functions">Functions</a></li><li><ul><li><a href="#namespace_broadcastcategorieschanged" title="broadcastCategoriesChanged">broadcastCategoriesChanged</a></li><li><a href="#namespace_broadcastchatmessage" title="broadcastChatMessage">broadcastChatMessage</a></li><li><a href="#namespace_broadcastchatmessagedeleted" title="broadcastChatMessageDeleted">broadcastChatMessageDeleted</a></li><li><a href="#namespace_broadcastchatmessageupdated" title="broadcastChatMessageUpdated">broadcastChatMessageUpdated</a></li><li><a href="#namespace_broadcastcommentcreated" title="broadcastCommentCreated">broadcastCommentCreated</a></li><li><a href="#namespace_broadcastcommentdeleted" title="broadcastCommentDeleted">broadcastCommentDeleted</a></li><li><a href="#namespace_broadcastcommentlikeupdate" title="broadcastCommentLikeUpdate">broadcastCommentLikeUpdate</a></li><li><a href="#namespace_broadcastcommentupdated" title="broadcastCommentUpdated">broadcastCommentUpdated</a></li><li><a href="#namespace_broadcastdumpchange" title="broadcastDumpChange">broadcastDumpChange</a></li><li><a href="#namespace_broadcastdumprelatedchanged" title="broadcastDumpRelatedChanged">broadcastDumpRelatedChanged</a></li><li><a href="#namespace_broadcastplaylistchange" title="broadcastPlaylistChange">broadcastPlaylistChange</a></li><li><a href="#namespace_broadcastplaylistdumpsupdated" title="broadcastPlaylistDumpsUpdated">broadcastPlaylistDumpsUpdated</a></li><li><a href="#namespace_broadcastpresence" title="broadcastPresence">broadcastPresence</a></li><li><a href="#namespace_broadcasttyping" title="broadcastTyping">broadcastTyping</a></li><li><a href="#namespace_broadcastuserupdated" title="broadcastUserUpdated">broadcastUserUpdated</a></li><li><a href="#namespace_broadcastvoteupdate" title="broadcastVoteUpdate">broadcastVoteUpdate</a></li><li><a href="#namespace_closeusersockets" title="closeUserSockets">closeUserSockets</a></li><li><a href="#namespace_getonlineusers" title="getOnlineUsers">getOnlineUsers</a></li><li><a href="#namespace_gettypingusers" title="getTypingUsers">getTypingUsers</a></li><li><a href="#namespace_handleclientpong" title="handleClientPong">handleClientPong</a></li><li><a href="#namespace_isuserchatopen" title="isUserChatOpen">isUserChatOpen</a></li><li><a href="#namespace_register" title="register">register</a></li><li><a href="#namespace_send" title="send">send</a></li><li><a href="#namespace_sendtouser" title="sendToUser">sendToUser</a></li><li><a href="#namespace_setclienttyping" title="setClientTyping">setClientTyping</a></li><li><a href="#namespace_taketoken" title="takeToken">takeToken</a></li><li><a href="#namespace_unregister" title="unregister">unregister</a></li><li><a href="#namespace_updateclientavatar" title="updateClientAvatar">updateClientAvatar</a></li><li><a href="#namespace_updateclientusername" title="updateClientUsername">updateClientUsername</a></li></ul></li><li><a href="#api/services/ws-service_ts_interfaces" title="Interfaces">Interfaces</a></li><li><ul><li><a href="#namespace_ratelimit" title="RateLimit">RateLimit</a></li><li><a href="#namespace_wsclient" title="WsClient">WsClient</a></li></ul></li><li><a href="#api/services/ws-service_ts_variables" title="Variables">Variables</a></li><li><ul><li><a href="#namespace_pingjob" title="pingJob">pingJob</a></li><li><a href="#namespace_typingexpiryjob" title="typingExpiryJob">typingExpiryJob</a></li></ul></li></ul></nav></div>
</div></div>
</div>
</div>
</body>
</html>