Skip to content

feat: implement draft/whoami capability#178

Merged
ValwareIRC merged 2 commits into
mainfrom
feat/whoami
May 11, 2026
Merged

feat: implement draft/whoami capability#178
ValwareIRC merged 2 commits into
mainfrom
feat/whoami

Conversation

@ValwareIRC
Copy link
Copy Markdown
Contributor

@ValwareIRC ValwareIRC commented Apr 24, 2026

Summary

Implements ircv3/ircv3-specifications#603

What changed

  • draft/whoami added to ourCaps alongside the required chghost
  • New utils getUserFromNuh() / getHostFromNuh() in src/lib/irc/utils.ts
  • IRCClient: myIdents/myHosts Maps per server; getMyIdent()/getMyHost() accessors; cleanup in removeServer(); SETNAME event extended with optional ident?/host?
  • IRCClientContext: exposes the new Maps to protocol handlers
  • handleChghost: detects self-CHGHOST and updates ctx.myIdents/ctx.myHosts — works with plain chghost even without draft/whoami
  • handleSetname: parses ident+host from the source NUH and includes them in the event, so the store can capture self-prefix from the draft/whoami registration-burst SETNAME
  • Server type: new optional myIdent? / myHost? fields
  • Store CHGHOST handler: writes server.myIdent/server.myHost on self-CHGHOST
  • Store SETNAME handler: initialises server.myIdent/server.myHost from the registration-burst SETNAME when ident+host are present; self-SETNAME realname update now also works correctly when currentUser exists
  • 12 new tests in tests/lib/whoami.test.ts

Behaviour with chghost-but-no-draft/whoami

Self-CHGHOST tracking (myIdents/myHosts) is active whenever chghost is negotiated, regardless of whether the server also advertises draft/whoami. The registration-burst SETNAME with ident/host is only sent by servers that support draft/whoami, so on those servers the self-prefix is known immediately after connection registration; on others it becomes known on the first CHGHOST.

Summary by CodeRabbit

  • New Features
    • Per-server tracking and retrieval of your own ident, host, and updated real name so client state reflects identity/hostname changes from the server.
  • Tests
    • New test suite for NUH parsing and user-protocol message handling.

Implements the draft/whoami IRCv3 extension, which gives clients

- Add draft/whoami to ourCaps (alongside the required chghost)
- Add getUserFromNuh() and getHostFromNuh() to irc/utils.ts
- Add myIdents/myHosts Maps to IRCClient + IRCClientContext; expose
  getMyIdent() / getMyHost() accessors; clean up on removeServer()
- Extend SETNAME EventMap entry with optional ident?/host? fields
- handleChghost: detect self-CHGHOST and update myIdents/myHosts
  (works with plain chghost too, not only when draft/whoami is active)
- handleSetname: parse ident+host from source NUH and include in
  event so the store can initialise self-prefix from the
  draft/whoami registration-burst SETNAME
- Add myIdent?/myHost? to the Server type
- Store CHGHOST handler: set server.myIdent/myHost on self-CHGHOST
- Store SETNAME handler: set server.myIdent/myHost from the
  registration-burst SETNAME when ident+host are present
- 12 new tests covering utils, handleChghost, and handleSetname
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 24, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 899f5279-7ec4-4ce1-aae4-1770f21afa19

📥 Commits

Reviewing files that changed from the base of the PR and between 530289b and e3ece7d.

📒 Files selected for processing (3)
  • src/lib/irc/IRCClient.ts
  • src/store/handlers/users.ts
  • src/types/index.ts

📝 Walkthrough

Walkthrough

Adds per-server tracking of the client's ident and host learned from NUH-bearing messages. Introduces NUH parsing helpers, requests the "draft/whoami" capability, records ident/host from CHGHOST and SETNAME, exposes accessors, and persists values on the server type and store handlers.

Changes

Per-server ident/host flow

Layer / File(s) Summary
Types / Context
src/types/index.ts, src/lib/irc/IRCClientContext.ts
Server gains optional myIdent?: string and myHost?: string. IRCClientContext adds myIdents: Map<string,string> and myHosts: Map<string,string> to hold per-server values.
NUH parsing utilities
src/lib/irc/utils.ts
Added getUserFromNuh(nuh) and getHostFromNuh(nuh) to extract ident and host segments from NUH strings (handle leading : and missing parts).
IRC client state & capability
src/lib/irc/IRCClient.ts
Added myIdents/myHosts maps, requested "draft/whoami" capability, cleared these maps on server removal, expanded EventMap.SETNAME to include optional ident/host, and added getMyIdent(serverId) / getMyHost(serverId) accessors.
Protocol handlers (lib)
src/lib/irc/handlers/users.ts
handleChghost records ident/host into ctx.myIdents/ctx.myHosts when the change affects the client's own nick. handleSetname extracts ident/host from the source NUH and emits SETNAME including optional ident/host.
Store handlers (state persistence)
src/store/handlers/users.ts
SETNAME handler recognizes self updates and updates currentUser.realname; when provided, persists myIdent/myHost on the matching Server. CHGHOST handler computes selfChanged and updates server myIdent/myHost alongside channel/user hostname updates.
Tests
tests/lib/whoami.test.ts
New Vitest file covering getUserFromNuh, getHostFromNuh, handleChghost and handleSetname behaviors (including self-detection and map updates) with a mocked IRCClientContext.
sequenceDiagram
    participant Client as IRCClient
    participant Server as IRC Server
    participant Store as App Store
    participant Handlers as Lib Handlers

    Client->>Server: Request capabilities (include "draft/whoami")
    Server-->>Client: Capabilities ok
    Server-->>Client: CHGHOST / SETNAME (source NUH or prefix)
    Client->>Handlers: deliver raw message
    Handlers->>Client: parse NUH (getUserFromNuh/getHostFromNuh)
    Handlers->>Client: emit SETNAME/CHGHOST events (include ident/host if present)
    Client->>Client: update myIdents/myHosts maps
    Client->>Store: dispatch handler -> persist Server.myIdent/myHost
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

Suggested reviewers

  • matheusfillipe

Poem

🐰 I parsed the NUH under moonlight,

ident tucked, host snug and bright.
CHGHOST hummed, SETNAME sang,
maps updated—hop! I sprang.
A little rabbit tracks the IRC night.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: implement draft/whoami capability' accurately summarizes the main change—adding support for the draft/whoami IRC capability to track the client's own ident and host information across servers.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/whoami

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown

Pages Preview
Preview URL: https://feat-whoami.obsidianirc.pages.dev

Automated deployment preview for the PR in the Cloudflare Pages.

@ValwareIRC ValwareIRC marked this pull request as draft April 24, 2026 00:30
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
tests/lib/whoami.test.ts (1)

144-151: Minor: :r realname value doesn't reflect real production parv.

In handleMessage at src/lib/irc/IRCClient.ts the trailing-parameter colon is stripped before the handler receives parv (see lines 1416-1423), so in production a SETNAME :r message would yield parv = ["r"], not [":r"]. The test still validates the join behavior, but consider passing ["r"] (and asserting realname === "r") for realism.

♻️ Proposed tweak
   test("emits SETNAME event with user and realname", () => {
     const ctx = makeCtx("alice");
-    handleSetname(ctx, "s1", ":alice!~u@bery6muzcsynw.irc", [":r"], undefined);
+    handleSetname(ctx, "s1", ":alice!~u@bery6muzcsynw.irc", ["r"], undefined);
     const data = ctx.events[0].data as { user: string; realname: string };
     expect(ctx.events[0].event).toBe("SETNAME");
     expect(data.user).toBe("alice");
-    expect(data.realname).toBe(":r");
+    expect(data.realname).toBe("r");
   });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/lib/whoami.test.ts` around lines 144 - 151, The test in
tests/lib/whoami.test.ts passes a trailing-parameter with the colon (parv =
[":r"]) which production strips before handlers; update the test that calls
handleSetname(ctx, "s1", ":alice!~u@bery6muzcsynw.irc", [":r"], undefined) to
pass ["r"] instead and change the assertion to expect(data.realname).toBe("r")
so the test reflects real-world behavior of the stripping in
handleMessage/IRCClient.ts; keep the same test name and event checks.
src/lib/irc/utils.ts (1)

6-15: LGTM — NUH parsing helpers handle the expected IRC prefix format correctly.

Edge cases are well-covered: missing ! or @ delimiters return "", and the optional leading colon is handled defensively. Tests at tests/lib/whoami.test.ts:8-36 provide good coverage.

One minor note: getHostFromNuh uses split("@")[1] which returns only the segment between the first and second @. Standard IRC NUHs never contain multiple @ so this is fine in practice, but if you want to be defensive against malformed input, withoutColon.slice(withoutColon.indexOf("@") + 1) would preserve any trailing @ characters.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/irc/utils.ts` around lines 6 - 15, getHostFromNuh currently uses
withoutColon.split("@")[1] which can drop trailing '@' characters in malformed
input; update getHostFromNuh to locate the first '@' via
withoutColon.indexOf("@") and return withoutColon.slice(index + 1) (handling -1
to return "") so the host preserves any trailing '@' characters and remains
defensive against malformed NUHs.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/store/handlers/users.ts`:
- Around line 965-982: The isSelf branch currently returns after updating
currentUser and myIdent/myHost, leaving any User records for yourself inside
channel.users stale; modify the isSelf return to also update channel user
entries on the same server: when computing updatedServers (the mapping over
state.servers for s.id === serverId), in addition to setting myIdent/myHost, map
s.channels (or s.channels list) and for each channel map channel.users replacing
any user object whose nick equals ircClient.getNick(serverId) (the local `user`
variable) with a copy that has realname set to the new realname; then return
currentUser and this updatedServers so both currentUser.realname and
channel.users[*].realname for self are updated.

---

Nitpick comments:
In `@src/lib/irc/utils.ts`:
- Around line 6-15: getHostFromNuh currently uses withoutColon.split("@")[1]
which can drop trailing '@' characters in malformed input; update getHostFromNuh
to locate the first '@' via withoutColon.indexOf("@") and return
withoutColon.slice(index + 1) (handling -1 to return "") so the host preserves
any trailing '@' characters and remains defensive against malformed NUHs.

In `@tests/lib/whoami.test.ts`:
- Around line 144-151: The test in tests/lib/whoami.test.ts passes a
trailing-parameter with the colon (parv = [":r"]) which production strips before
handlers; update the test that calls handleSetname(ctx, "s1",
":alice!~u@bery6muzcsynw.irc", [":r"], undefined) to pass ["r"] instead and
change the assertion to expect(data.realname).toBe("r") so the test reflects
real-world behavior of the stripping in handleMessage/IRCClient.ts; keep the
same test name and event checks.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: fac8915f-a68d-40a9-b578-5ebf308a0a38

📥 Commits

Reviewing files that changed from the base of the PR and between e7d7899 and 530289b.

📒 Files selected for processing (7)
  • src/lib/irc/IRCClient.ts
  • src/lib/irc/IRCClientContext.ts
  • src/lib/irc/handlers/users.ts
  • src/lib/irc/utils.ts
  • src/store/handlers/users.ts
  • src/types/index.ts
  • tests/lib/whoami.test.ts

Comment on lines +965 to 982
const isSelf = user === ircClient.getNick(serverId);

if (isSelf) {
// Self-SETNAME: update currentUser realname and, if we have prefix info from
// the draft/whoami registration burst, record myIdent/myHost on the server.
const updatedServers =
ident && host
? state.servers.map((s) =>
s.id === serverId ? { ...s, myIdent: ident, myHost: host } : s,
)
: state.servers;
return {
currentUser: {
...state.currentUser,
realname: realname,
},
currentUser: state.currentUser
? { ...state.currentUser, realname }
: state.currentUser,
servers: updatedServers,
};
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Self-SETNAME branch skips updating channel.users[*].realname for self.

When isSelf is true, the handler returns early after updating only currentUser.realname and the optional server myIdent/myHost. It never falls through to the channel-user mapping at lines 984-995, so the User entry representing yourself inside each channel's users array retains the stale realname. Other users see your updated realname (via their own SETNAME path), but your own UI views that source realname from channel.users (not currentUser) will not refresh.

🔧 Proposed fix to also update self in channel user lists
       if (isSelf) {
         // Self-SETNAME: update currentUser realname and, if we have prefix info from
         // the draft/whoami registration burst, record myIdent/myHost on the server.
-        const updatedServers =
-          ident && host
-            ? state.servers.map((s) =>
-                s.id === serverId ? { ...s, myIdent: ident, myHost: host } : s,
-              )
-            : state.servers;
+        const updatedServers = state.servers.map((s) => {
+          if (s.id !== serverId) return s;
+          const updatedChannels = s.channels.map((c) => ({
+            ...c,
+            users: c.users.map((u) =>
+              u.username === user ? { ...u, realname } : u,
+            ),
+          }));
+          return {
+            ...s,
+            channels: updatedChannels,
+            ...(ident && host ? { myIdent: ident, myHost: host } : {}),
+          };
+        });
         return {
           currentUser: state.currentUser
             ? { ...state.currentUser, realname }
             : state.currentUser,
           servers: updatedServers,
         };
       }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const isSelf = user === ircClient.getNick(serverId);
if (isSelf) {
// Self-SETNAME: update currentUser realname and, if we have prefix info from
// the draft/whoami registration burst, record myIdent/myHost on the server.
const updatedServers =
ident && host
? state.servers.map((s) =>
s.id === serverId ? { ...s, myIdent: ident, myHost: host } : s,
)
: state.servers;
return {
currentUser: {
...state.currentUser,
realname: realname,
},
currentUser: state.currentUser
? { ...state.currentUser, realname }
: state.currentUser,
servers: updatedServers,
};
}
const isSelf = user === ircClient.getNick(serverId);
if (isSelf) {
// Self-SETNAME: update currentUser realname and, if we have prefix info from
// the draft/whoami registration burst, record myIdent/myHost on the server.
const updatedServers = state.servers.map((s) => {
if (s.id !== serverId) return s;
const updatedChannels = s.channels.map((c) => ({
...c,
users: c.users.map((u) =>
u.username === user ? { ...u, realname } : u,
),
}));
return {
...s,
channels: updatedChannels,
...(ident && host ? { myIdent: ident, myHost: host } : {}),
};
});
return {
currentUser: state.currentUser
? { ...state.currentUser, realname }
: state.currentUser,
servers: updatedServers,
};
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/store/handlers/users.ts` around lines 965 - 982, The isSelf branch
currently returns after updating currentUser and myIdent/myHost, leaving any
User records for yourself inside channel.users stale; modify the isSelf return
to also update channel user entries on the same server: when computing
updatedServers (the mapping over state.servers for s.id === serverId), in
addition to setting myIdent/myHost, map s.channels (or s.channels list) and for
each channel map channel.users replacing any user object whose nick equals
ircClient.getNick(serverId) (the local `user` variable) with a copy that has
realname set to the new realname; then return currentUser and this
updatedServers so both currentUser.realname and channel.users[*].realname for
self are updated.

matheusfillipe
matheusfillipe previously approved these changes May 4, 2026
@ValwareIRC ValwareIRC marked this pull request as ready for review May 11, 2026 20:04
@ValwareIRC ValwareIRC merged commit 0372f3f into main May 11, 2026
5 checks passed
@ValwareIRC ValwareIRC deleted the feat/whoami branch May 11, 2026 20:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants