Repository navigation
docs: Starlight documentation site at ttlj.docs.isaackogan.com - #162
Open
isaackogan wants to merge 31 commits into
Open
isaackogan wants to merge 31 commits into
isaackogan wants to merge 31 commits into
Conversation
…sign and plan Claude-Session: https://claude.ai/code/session_014bbgdemLuSLpxB3kaK7zfJ
…ilder requests Claude-Session: https://claude.ai/code/session_014bbgdemLuSLpxB3kaK7zfJ
Social scrapers need a raster og:image; the raw GitHub SVG the config pointed at is not one. Render the project logo to a 1200x630 PNG with a white background (logo contained and centred) under docs/public and point og:image at it on the site's own origin. Claude-Session: https://claude.ai/code/session_014bbgdemLuSLpxB3kaK7zfJ
Port the Python sibling's post-build checks to Node (ESM, node: built-ins only): homepage title, per-page canonical equal to the page's own URL, Open Graph and meta description, sitemap index and pages, robots.txt, dofollow Euler Stream links on every page with varied homepage anchor text, and the sidebar navigation marker. The CLI prints one PASS/FAIL line per check and exits 1 on any failure; CI runs it as a separate step after the build via `pnpm --dir docs run seo`. The test script uses Node's own glob (node --test "scripts/**/*.test.mjs") because node --test treats a bare directory argument as a file to run. Claude-Session: https://claude.ai/code/session_014bbgdemLuSLpxB3kaK7zfJ
Add a documentation pointer after the introduction and a Documentation entry at the top of the events list, both to https://ttlj.docs.isaackogan.com/. Each events-list item keeps its in-page anchor and gains a docs link to the event's section on the hosted event pages, and each event section carries a Documentation line with the same link. Each section heading's source link gains the api/ path segment the files moved under, so the links resolve again. The two links to the Node original now point at zerodytrash/TikTok-Live-Connector, where that repository lives. Claude-Session: https://claude.ai/code/session_014bbgdemLuSLpxB3kaK7zfJ
…d mapping behaviour Keep the connection, WebSocket, and troubleshooting pages consistent on the daemon flag the socket's read thread inherits; say "every exception the library defines"; tag the collaboration fence and list the dev prerequisites; and apply the deferred wording minors (room-info defaults and update paths, Euler roomInfo fields, offline + Euler path, Euler text frames, connectAsync(Consumer) failures, unhandled-exception order, autoDiscard split, SOCKS propagation and read timeout, catch-all dispatch order, forAnyMessage hook order, mapping-exception causes, mapping fragment imports, user attributes, Gradle url assignment, ffmpeg path, README blank line, .gitignore separator). Claude-Session: https://claude.ai/code/session_014bbgdemLuSLpxB3kaK7zfJ
…, queue master deploys The upstream job now sets has_docs=false with a notice when a scheduled run has no Cloudflare secrets, treats only a 404 from the GitHub API as "no docs", and fails the job on any other status or a failed request. Pull-request runs still cancel each other; master pushes and scheduled runs queue in the shared group instead of cancelling a deploy. The hosting page, docs README, and contributing page describe the new step, both notices, the failure mode, and that a fork-only change is overwritten by the 06:17 UTC upstream deploy until merged upstream. Claude-Session: https://claude.ai/code/session_014bbgdemLuSLpxB3kaK7zfJ
…ks raw
summary() no longer un-escapes "\<"; the type page's description
frontmatter, which is JSON-quoted, gets the plain form through
Markdown.plain(). <pre> blocks are cut out of the description before
the HTML conversion, escaping and tidying, and emitted as a fenced block
with entities decoded, {@code} content verbatim (JavaParser's cut at the
first brace is repaired) and common indentation removed; single-line
contexts (field bullets, enum values, @param/@return) render them as a
code span instead of a broken fence. MarkdownTest pins the listing
line, the description frontmatter, the fenced block, the bullets with
pipes, the link and the {@link} on a synthetic compilation unit.
Claude-Session: https://claude.ai/code/session_014bbgdemLuSLpxB3kaK7zfJ
…ted links in the check The events section now also requires a "## <EventClassName>" heading on a grouped page under events/ other than the catalog, so the row alone no longer passes; the readme section resolves every hosted link in the README to an authored page and, with a fragment, to a heading whose slug equals it. Fence languages are compared case-insensitively. Tests cover a passing and a failing case for each rule and pin the real README against the real pages. Claude-Session: https://claude.ai/code/session_014bbgdemLuSLpxB3kaK7zfJ
Schema pages take their directory from the .proto file's name, threaded through ProtoFile, with the package-derived stem as the fallback. The content check scans README handler items only within the README's events list, reports duplicated catalog rows, and fails the examples section on an unclosed fence instead of dropping it. The example compiler maps javac errors on hoisted imports back to the line they were written on, batches full units by every type they declare so same-named helper classes do not collide, and lifts javac's error cap. The SEO gate's tests assert the exact check count, drop two unused exports and fix the run comment; the Euler host regex escapes every dot. Claude-Session: https://claude.ai/code/session_014bbgdemLuSLpxB3kaK7zfJ
Contributor
Author
|
LOL. Claude, my god. What an insane PR. Just merge it, it's only docs, lmfao, then DM me on discord i need to give you GitHub actions secrets |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This adds a documentation site for TikTokLiveJava under
docs/, live at https://ttlj.docs.isaackogan.com/. It is built with Astro and Starlight, generated from the library sources on every build, and checked so that it cannot drift from the code.What is in it
API,Client,extension-recorder, andextension-collector(JavaParser), and schema pages for every message and enum in the three.protofiles. Generated pages are never committed;pnpm --dir docs buildregenerates them.docs/tools/): a small Maven tool that compiles every Java example on the pages against the built library and verifies that every event class, everyLiveClientSettingsfield, everyLiveClientmethod andEventsBuilderhandler, every exception class, and both extensions are documented. The build fails if any of that drifts.docs/wrangler.jsonc(Cloudflare Workers static assets) and.github/workflows/docs.yml.How the workflow behaves here
On this repository the workflow validates every pull request and push that touches the library or the docs (library build, tool tests, content check, site build, SEO gate) and uploads the built site as an artifact. It deploys only when the repository has the
CLOUDFLARE_API_TOKENandCLOUDFLARE_ACCOUNT_IDsecrets, and the daily scheduled run skips itself while they are absent, so nothing changes for you unless you add them. Until then the site is deployed from my fork, which rebuilds daily from this repository'smasteronce the docs are merged, so the hosted pages track your releases without further pushes.Local development
npx -y pnpm@10.34.2 --dir docs install npx -y pnpm@10.34.2 --dir docs build # library, tool, check, generate, site npx -y pnpm@10.34.2 --dir docs devRequires JDK 17 or newer, Maven, and Node 24.
docs/src/content/docs/contributing.mdhas the authoring rules.Changes outside
docs/README.md: a pointer to the hosted documentation, a· docslink on each event in the list, aDocumentation:line under each event section, and the event section source links corrected to theapi/path that exists (they returned 404). Two links to the Node project now point atzerodytrash/TikTok-Live-Connector.collaboration.md: a short Documentation section..github/workflows/docs.yml: new.docs/tools/src/**/*.java: carry the repository's license header exactly as the rootlicense-maven-pluginapplies it on every build (so the build leaves the tree clean); the header text and attribution are the project's own.docs/superpowers/: the design document and implementation plan the site was built from, kept for the record.Library findings
Verified against
1.13.1-Releasewhile writing the documentation; the site documents the current behaviour, so each of these is a candidate fix rather than a blocker for this PR. Paths are relative to the repository root;client/…stands forClient/src/main/java/io/github/jwdeveloper/tiktok/client/…andapi/…forAPI/src/main/java/io/github/jwdeveloper/tiktok/api/….Connection and sockets
connect()on a connected client flips state but keeps the socket —client/TikTokLiveClient.java:119-121(and:108-110on the Euler path) throwsTikTokLiveException("Already connected"), which:87-90handles like a connection failure: the state becomesDISCONNECTEDandTikTokErrorEventplusTikTokDisconnectedEventare published, whilewebSocketClient.stop()is never called; a user sees a live client announce a disconnect although messages keep arriving. Fix: perform the state check before thetry, outside the catch.offline = truenever connects —client/TikTokLiveHttpOfflineClient.java:42-44returns aLiveUserData.Responsewhose room info isnull;TikTokRoomInfo.copy()skips the null (client/TikTokRoomInfo.java:61), soclient/TikTokLiveClient.java:136dereferences it (userData.getRoomInfo().getRoomId()) and throwsNullPointerException, which:100-104logs as "Unhandled exception report this bug to github" and swallows; a user sees that message and a stack trace, andconnect()returns without connecting or throwing. Fix: have the offline client return aTikTokRoomInfowith a placeholder id, or read the id fromroomInfoaftercopy().connectAsync(Consumer)drops failures —client/TikTokLiveClient.java:209-211chainsthenAccept(onConnection)and discards the future; whenconnect()throws, the consumer never runs and no code observes the exception, so a user gets neither a callback nor a handle to learn the outcome (only theTikTokErrorEventthatconnect()publishes on its own). Fix: return theCompletableFuturefrom this overload, or attach.exceptionally(...).toMillisPart()—client/websocket/TikTokWebSocketClient.java:69,client/websocket/euler/TikTokWebSocketEulerClient.java:65andclient/http/HttpProxyClient.java:112-113passgetTimeout().toMillisPart(), the sub-second remainder of theDuration(0 forDuration.ofSeconds(n)), as the connect (and SOCKS read) timeout in milliseconds; Java-WebSocket andHttpsURLConnectionboth treat 0 as "no timeout", so a user who sets a whole-second timeout gets none. Fix:(int) getTimeout().toMillis().stop()leaves a handshaking socket running —client/websocket/euler/TikTokWebSocketEulerClient.java:81-96closes only whenisConnected()(:98-100, which requiresisOpen()) and then nulls the reference;connect()(:75) is non-blocking, so adisconnect()issued before the handshake completes neither closes the socket nor keeps a way to close it later, and a user ends up with a background connection they cannot stop. Fix: close any socket that is not yetisClosed(), asclient/websocket/TikTokWebSocketClient.java:147-152does.client/websocket/euler/TikTokWebSocketEulerListener.java:53-63ignores a JSON frame without amessagesarray and any message whosetypeis neitherworkerInfonorroomInfo(theswitchhas nodefault); a user sees nothing, not evenTikTokWebsocketUnhandledMessageEvent, when the server sends an error object or a new frame type. Fix: log or publish an unhandled event from adefaultbranch and for frames withoutmessages.client/websocket/TikTokWebSocketClient.java:115-118wrapsSSLContext.getInstance("SSL")/init()incatch (Exception e)and throwsTikTokProxyRequestException("Unable to set Socks proxy SSL instance")withoute; if it ever fires, a user sees the message with no underlying stack trace. Fix: catchNoSuchAlgorithmException | KeyManagementExceptionand passeas the cause.HTTP, proxies and mapping
client/http/HttpProxyClient.java:64-70loopswhile (proxySettings.hasNext())andcontinues on any status other than 200, butapi/data/settings/ProxyClientSettings.java:80-82defineshasNext()as!proxyList.isEmpty()andnext()wraps around (:94-95); when every proxy connects but TikTok answers 403/429, a user seesconnect()hang. Fix: bound the loop to one pass over the list (getProxyCount()attempts), or map non-200 to a failedActionResultasclient/http/HttpClient.java:55-69does.autoDiscardapplies different rules per proxy type — the HTTP branch (client/http/HttpProxyClient.java:71-74) removes the endpoint only onHttpConnectTimeoutException/ConnectException, the SOCKS branch (:139-144) on anyIOException; a user with the same list sees different proxies survive depending ontype. Fix: apply one rule in both branches.TikTokProxyRequestExceptionnever reaches the caller, and exhaustion falls back to a direct request —client/TikTokLiveHttpClient.java:98-109(same shape at:73-83,:128-139,:185-196,:212-222) retrieswhile (proxyClientSettings.hasNext())and swallows the exception; withautoDiscardthe list empties and the trailing call outside the loop is built as a plainHttpClient(client/http/HttpClientBuilder.java:99-101), so the request goes out unproxied from the user's own address; withoutautoDiscarda persistently failing proxy loops forever. A user can never catchTikTokProxyRequestExceptionfromconnect(),fetch*orsendChat. Fix: count attempts, rethrow the lastTikTokProxyRequestException, and do not fall through to a direct request while proxies are enabled.roomIdsetting is never read —api/data/settings/LiveClientSettings.java:116-118says the id "can be set manually", but nothing reads it:client/TikTokLiveClient.java:125-136always resolves the room from the host name. A user who sets it sees no effect. Fix: honour it intryConnect()or remove the field.totalViewersintoviewersCountand never setsageRestricted—client/http/mappers/LiveUserDataMapper.java:116-117callssetViewersCountfortotalViewers, overwriting thecurrentViewersvalue from:114-115, andmapEulerstream(:100-144) has nosetAgeRestricted; on the Euler WebSocket path a user seesgetViewersCount()equal to the total,getTotalViewersCount()0 andisAgeRestricted()always false. Fix:setTotalViewersCountat:117, and setageRestrictedfrom the payload where available.client/http/mappers/LiveUserDataMapper.java:94-97(and:140-143) convertsJsonSyntaxException,IllegalStateExceptionandNullPointerExceptionintoUserStatus.NotFound, soclient/TikTokLiveClient.java:131-132throwsTikTokLiveUnknownHostException("User not found: …"); a user whose request was blocked, rate-limited or served an HTML page is told the host does not exist. Fix: throwTikTokLiveRequestExceptionfrom the catch instead.client/http/HttpClient.java:60-66is the response mapper for every request, so a 429 or 5xx fromwww.tiktok.com/api-live/user/roomorwebcast.tiktok.com/webcast/room/inforeads "Sign server rate limit reached" / "Sign server Error"; a user debugging a TikTok block is pointed at the wrong service. Fix: drop "Sign server" from the generic messages or include the request host.Events and extensions
client/mappers/TikTokGenericEventMapper.java:62(mapToEvent) has no caller (client/mappers/TikTokLiveMapperHelper.java:46uses onlygetParsingMethod), and the registrations that would feed it are commented out atclient/mappers/MessagesMapperFactory.java:132-149;TikTokChestEvent,TikTokDetectEvent,TikTokEnvelopeEvent,TikTokGoalUpdateEvent,TikTokIMDeleteEvent,TikTokInRoomBannerEvent,TikTokLinkApplyEvent,TikTokLinkLayerEvent,TikTokLinkMicFanTicketEvent,TikTokLinkMicMethodEvent,TikTokLinkReplyEvent,TikTokRankTextEvent,TikTokRankUpdateEvent,TikTokShopEvent,TikTokSubNotifyEventandTikTokUnauthorizedMemberEventare therefore never published by the default client, and a user subscribed to them receives the message only asTikTokWebsocketUnhandledMessageEvent. Fix: register each withforMessage(the shape atMessagesMapperFactory.java:143-146), or deprecate the classes.client/TikTokLiveEventHandler.java:39-42looks up handlers forTikTokEvent.classandevent.getClass()only, and:45keeps them in aHashSet; a user registered forTikTokGiftEventnever seesTikTokGiftComboEvent(a subclass), likewiseTikTokLinkEventfor its subclasses, and several handlers for one event run in no defined order. Fix: walkgetSuperclass()up toTikTokEventwhen publishing, and use aLinkedHashSet(orList) to keep registration order.TikTokHttpResponseEventhas no publisher —api/live/builder/EventsBuilder.java:95-96andapi/listener/TikTokEventListenerBase.java:119exposeonHttpResponse, but nothing constructs the event, so a user's handler never runs. Fix: publish it fromclient/http/HttpClient.toHttpResponse()or remove the hook.@EventMeta—api/data/events/common/TikTokEvent.java,TikTokLiveClientEvent.java,TikTokEmptyEvent.java,TikTokHeaderEvent.javaandTikTokUnhandledEvent.javacarry no@EventMetawhile the other 74 event classes do; a user (or tool) that reads the annotation to categorise events gets noEventTypefor them. Fix: add@EventMeta(eventType = …)or state that base classes are exempt.Finishedis conditional and a late finishing frame still double-counts after fix(gift): stop streak gifts being dropped and double counted #160 —client/mappers/handlers/TikTokGiftEventHandler.java:121-124publishes theFinishedcombo event only when anActive/Beginframe for the same key was seen, and once a streak is flushed by the 5-minute timeout (:143-149,:51) its key is remembered for only 60 s (FINALIZED_RETENTION_MS,:57, checked at:117-119), so a finishing frame arriving later reaches:125withprevious == nulland raises a secondTikTokGiftEvent; a user counting gifts sees a streak that began before they connected end withoutFinished(theTikTokGiftEventstill fires) and a slow streak counted twice. Fix: publishFinishedfor every finishing frame, and keep the finalized key at least as long as the combo timeout.extension-recorder/src/main/java/io/github/jwdeveloper/tiktok/extension/recorder/impl/RecorderListener.java:98callsfile.getParentFile().mkdirs();getParentFile()is null for a path with no directory (e.g.setOutputFile(new File("stream.flv"))), theNullPointerExceptionis caught at:117-119and only printed, and because it precedes the innertry/finally(:101-116) noTikTokLiveRecorderEndedEventis published; a user seesTikTokLiveRecorderStartedEvent, a stack trace, and no file. Fix:var parent = file.getAbsoluteFile().getParentFile(); if (parent != null) parent.mkdirs();.README
README.md:169and:173are both## Events,:188-189listonEventtwice, and:297and:318are two## onEventsections; the intro at:30links#member,#gift,#subscribe,#roomuser,#social,#questionnew,#likeand#linkmicbattle, none of which matches a heading (GitHub generates e.g.#ongift-tiktokgiftevent), so a user clicking them goes nowhere. On1.13.1-Releasethe same items are at:167/:171,:184-185,:285/:304and:30. Fix: remove the duplicates and point the intro at the generated anchors.https://claude.ai/code/session_014bbgdemLuSLpxB3kaK7zfJ