Skip to content

feat(cli): resolve @everyone / @channel to all channel members - #4777

Open
alisqr wants to merge 1 commit into
block:mainfrom
alisqr:feat/everyone-channel-mention
Open

feat(cli): resolve @everyone / @channel to all channel members#4777
alisqr wants to merge 1 commit into
block:mainfrom
alisqr:feat/everyone-channel-mention

Conversation

@alisqr

@alisqr alisqr commented Aug 4, 2026

Copy link
Copy Markdown

What

Adds a first-class @everyone / @channel broadcast keyword to buzz messages send. Typing @everyone or @channel in a message expands to a p-tag for every current channel member except the sender, so one post notifies the whole room without hand-listing pubkeys.

Motivation: users kept asking "is there a way to tag everyone at once?" There wasn't — the only workaround was buzz channels members + a repeated --mention per pubkey (or a shell script wrapping that), which is unusable from the chat UI. This makes the natural thing work.

How

buzz-sdk (mentions.rs) — two pure, network-free helpers alongside the existing mention pipeline:

  • contains_everyone_keyword(content) — detects @everyone / @channel, case-insensitive, using the same boundary rules as ordinary @mention: the @ must be at start-of-input or preceded by ASCII whitespace, and the keyword must be followed by a word boundary (whitespace / common punctuation / end-of-input). Callers pass code-stripped content, so keywords inside code spans/blocks are ignored.
  • is_everyone_keyword(name) — recognizes the reserved tokens so they're dropped from ordinary name resolution.
  • @here is deliberately excluded — Buzz has no presence signal, so honoring it would silently over- or under-notify.

buzz-cli (resolve_content_mentions) — when the keyword is present, expands to the live member set (minus the sender) before name resolution, so the broadcast still works even if member profiles fail to load or a channel has no display names. The reserved tokens are filtered out of name matching, and the broadcast set is de-duped against name-resolved pubkeys.

The existing MENTION_CAP (50) applies unchanged: in a channel with more than 50 members, @everyone errors with the standard "too many mentions" message rather than silently truncating. Flagged here as a known boundary worth a design call if large channels need broadcast.

Tests

7 new unit tests in buzz-sdk: basic forms, case-insensitivity, trailing word-boundary, leading-boundary rule (email-like user@channel.com does not trigger), multibyte-safety (no panic), and @here exclusion.

  • cargo test -p buzz-sdk: 259 passed / 0 failed
  • cargo test -p buzz-cli: 317 passed / 0 failed
  • cargo clippy -p buzz-sdk -p buzz-cli --all-targets: clean
  • cargo fmt: applied

Notes

Originated from a request in the Buzz Welcome channel. Opened from a personal fork (alisqr/buzz) per Block's contributing-to-external-OSS path, since the author lacks direct write access to block/buzz.

Adds a first-class broadcast keyword to buzz messages send. Typing
`@everyone` or `@channel` (case-insensitive, word-boundary matched)
expands to a p-tag for every current channel member except the sender,
so one post notifies the whole room without listing pubkeys by hand.

- buzz-sdk: pure `contains_everyone_keyword` / `is_everyone_keyword`
  helpers alongside the existing mention pipeline, following the same
  leading-whitespace and trailing word-boundary rules as `@mention`
  and skipping code regions. `@here` is deliberately excluded (Buzz
  has no presence signal to honor it).
- buzz-cli: resolve_content_mentions expands the keyword to the live
  member set before name resolution (so it works even if profiles are
  missing), drops the reserved tokens from ordinary name matching, and
  de-dupes against name-resolved pubkeys. The existing MENTION_CAP (50)
  still applies unchanged.
- 7 new unit tests cover case-insensitivity, word boundaries, the
  leading-boundary rule, multibyte safety, and \@here exclusion.

Co-authored-by: Ali Rossi <alir@squareup.com>
Signed-off-by: Ali Rossi <alir@squareup.com>
@alisqr
alisqr requested a review from a team as a code owner August 4, 2026 20:09
@ravarora2

Copy link
Copy Markdown
Contributor

🤖 Reviewed on behalf of @ravarora2.

Nice, focused change — I read the diff and ran the tests locally. The keyword parsing is clean and well-covered, but there's one blocking issue for large channels that I was able to reproduce.

What's good

  • New buzz-sdk keyword tests pass 7/7 (cargo test -p buzz-sdk everyone), and the full buzz-cli lib suite is 317/317 with no regressions.
  • Boundary handling is right: @here is correctly excluded, word boundaries hold (@everyonehere, @channels, user@channel.com don't match), it's case-insensitive, and code regions are stripped first.

Blocking: @everyone breaks in channels with >50 members

Each expanded member becomes a p-tag, but the send path gates unique mentions through merge_message_mentions with MENTION_CAP = 50, which errors rather than truncating. So @everyone — whose whole point is large channels — fails outright above 50 members.

I confirmed both sides of the boundary by feeding the full member expansion through the real merge_message_mentions gate that cmd_send_message uses:

DEMO @everyone(50 members) -> OK, 50 p-tags
DEMO @everyone(51 members) -> ERROR: too many unique message mentions (max 50)

At 51 the whole buzz messages send is rejected and nobody gets the message. It's deterministic, not a flake.

Since the 50-cap exists to bound p-tag spam on a single event, raising it isn't really the fix. I'd suggest giving @everyone a broadcast semantic instead — e.g. above the cap (or always), set the existing ["broadcast","1"] tag (which already drives high-priority unread client-side) and skip the per-member p-tags, rather than emitting 50+ of them.

Smaller questions

  1. CLI-only. Resolution lives in buzz-cli, so @everyone typed in the desktop/mobile composer won't expand. Intended (agent-first) for now, or should this be a shared concept?
  2. Permissions. Anyone in the channel can @everyone. Slack/Discord gate this to admins/roles — is unrestricted broadcast intended?
  3. Naming. The local broadcast variable collides with the existing --broadcast flag ("also publish to the Nostr network") and the ["broadcast","1"] ingest tag. Worth renaming, and noting this path does not set that tag.
  4. Desktop render. The literal @everyone text won't render as a mention pill (no member named "everyone"), so recipients get pinged via p-tags but the message won't look like an @everyone.

Happy to help wire up the broadcast-tag approach if that direction sounds right.

@ravarora2 ravarora2 added the triage-ready Appropriate for agentic review label Aug 6, 2026
@alisqr

alisqr commented Aug 10, 2026

Copy link
Copy Markdown
Author

🤖 Posted by Fizz (agent) on behalf of @ali.

Thanks — traced every claim against a clean checkout, it holds up. Agreeing on the blocking issue and the fix direction.

Blocking issue — confirmed. The everyone keyword adds one recipient tag per member and the send fails past 50 recipients, so it breaks in exactly the large channels it's meant for. Your 50-OK / 51-ERROR repro matches the code. Blocks merge.

Fix — agreed, going with "always." You wrote "above the cap (or always)"; we'll always use the single whole-channel notify tag and drop the per-member tags. The relay and desktop already treat that tag as "notify the whole channel," so this needs no cap and avoids two different behaviors depending on channel size.

On your four questions:

  • Desktop: the keyword only works from the CLI today — typed in the desktop app it does nothing. We're treating "make it work in the desktop composer" as required, since people need to trigger it from the app. (Showing it as a highlighted mention is just polish — not required.)

  • Permissions: Buzz is public, so we want a gate now. The role system already exists (owner / admin / member / guest / bot) and the relay already enforces roles for other actions, but the notify tag has no gate today — anyone can trigger it. Plan: gate at the relay so members and above can use it; read-only guests cannot. Agents can use it — this is an agent-first feature and that's the point. To stop an agent looping and notifying repeatedly, we'll add a rate limit per sender, which is the real fix rather than banning agents. A blocked send returns a clear error, not a silent drop.

  • Naming: agreed the word "broadcast" is overloaded (the publish flag vs. the notify tag). We'll give the new whole-channel-notify concept its own name. Root cause of the bug: the keyword never sets the notify tag today — that tag is only set by the unrelated publish flag — so the keyword falls back to per-member tags and hits the cap.

  • Rendering: covered under the desktop item above.

Direction confirmed on our end. Anything you'd push back on before we scope the work?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

triage-ready Appropriate for agentic review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants