Repository navigation
feat(social): face-to-face group: join a room by entering the same 6-digit number (#83) - #88
Conversation
Friends' Bots gets Join with a number: type six digits, pick which Bot to bring (default first), and join with no preview. The matching room is hosted as a normal native room (origin face-to-face, reusing ThinkFlowLab#82 participants, owner badges, and the membership window). Leave is one click; Start chat voids the number. Member events and peer messages poll Bot Social with the event cursor; peer text is fenced. Bot replies POST /v1/face-rooms/{id}/messages and pending_approval uses the existing Approve / Reject queue. FlyBot members join unpublished (ISSUE ThinkFlowLab#51 hook). Closes ThinkFlowLab#83 Co-authored-by: Danyang Chen <birfychen@gmail.com>
The room header and pending-approval queue get Auto-send my Bot's
room messages, mapping to PATCH /v1/me { auto_approve_face_rooms }.
If Bot Social answers confirmation_required, the same click retries
with confirm_code so the UI never asks for a 6-character code.
Co-authored-by: Danyang Chen <birfychen@gmail.com>
…wLab#51) Bot settings (next to Invite to this Bot) gets a Published to friends checkbox that calls set-published { id, published }. Face-to-face join shows whether the brought Bot is published, using flybot.bot.isPublished and BotView.published from the merged ThinkFlowLab#51 core. Co-authored-by: Danyang Chen <birfychen@gmail.com>
The auto-send controls in the group overview use t(), so they need DICTIONARY.zh entries or the client i18n check fails. Co-authored-by: Danyang Chen <birfychen@gmail.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: cd383ab33f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| } else { | ||
| const local = locals.some(bot => bot.handle && bot.handle === line.authorId) | ||
| if (local) continue | ||
| void flybot.rooms.postRemote?.(room.id, { handle: line.authorId, name: line.who, text: line.text }) |
There was a problem hiding this comment.
Send the fenced peer body into face rooms
When a polled room.message comes from another owner, this posts line.text even though renderFaceEvent put the neutralized form in line.fenced (a repo-wide use search shows that field is otherwise only asserted in the test). postRemote embeds the raw value in the DSH group user message and transcript, so peer-controlled instructions or forged headers reach local Bots outside the promised <untrusted-message> boundary; pass the fenced value through the host path.
AGENTS.md reference: AGENTS.md:L18-L18
Useful? React with 👍 / 👎.
| const record = local | ||
| ? faceLocalRecord(local, seq, at, member) | ||
| : faceRemoteRecord(member, seq, at) | ||
| room.participants.push(record) |
There was a problem hiding this comment.
Allow signed-in owners to speak in face rooms
In a member-auth deployment, face-room snapshots create only local- and remote-bot participant records. The normal composer passes the signed-in owner as a human actor, and postRoomMessage then requires participantOf(room, 'human', actor.memberId), so the visible owner gets You are not a member of that room and cannot kick off or respond in the newly opened group. Treat the owner of the joined local Bot as a permitted face-room speaker (or materialize an appropriate local human participant).
Useful? React with 👍 / 👎.
| if (!room?.id) return undefined | ||
| settings.faceRooms[snapshot.roomId] = { | ||
| localRoomId: room.id, | ||
| cursor: Math.max(Number(settings.faceRooms[snapshot.roomId]?.cursor) || 0, Number(snapshot.eventCursor) || 0), |
There was a problem hiding this comment.
Acknowledge face events only after room delivery
When postRemote/postNotice rejects (for example, resolveAgent or session flush fails) or the host stops before that async work finishes, the snapshot cursor is already advanced, the event is put in seen, and both posting promises are discarded with void. The next GET uses this newer after cursor, so the remote message or notice is silently skipped forever; await delivery and commit the cursor/seen state only after it succeeds.
Useful? React with 👍 / 👎.
| room.origin === 'face-to-face' || members.length + remotes.length <= 2 | ||
| ? null | ||
| : h('button', { type: 'button', className: 'bt-x', 'aria-label': t('Remove {name} from the group', { name: remoteBotTitle(person) }), title: t('Remove from group'), onClick: () => update({ removeParticipants: [{ kind: 'bot', id: person.handle || person.id, remote: true }] }) }, '×'))), | ||
| room.origin === 'face-to-face' ? null : adding |
There was a problem hiding this comment.
Route face-room deletion through the leave endpoint
With the default face polling enabled, this face-specific settings branch hides member editing but leaves the generic Manage → Delete group action active. That action deletes only the core room; it never calls social/face-leave or removes the persisted face-room/server membership, so the next list poll sees the still-joined room and upsertFace recreates it as a new Session. Face-room deletion should invoke the leave flow or the generic delete control should be suppressed.
Useful? React with 👍 / 👎.
| const live = room.presentedParticipants ?? room.participants ?? [] | ||
| const humans = live.filter(p => p.kind === 'human' && p.leftSeq === undefined) | ||
| const remotes = live.filter(p => p.remote && p.leftSeq === undefined) | ||
| const members = room.members.map(id => roster.byId[id]).filter(Boolean) |
There was a problem hiding this comment.
Show the presented owner label for local face Bots
For every native face-room overview, presentRoom supplies owner-qualified labels for local participants too, but this consumes live only for humans/remotes and rebuilds local members from the bare roster. Consequently the owner's Bot is shown as just Writer rather than Writer · Alice's Bot (or Alice's Chief), contradicting the full member-list contract; use the matching presented participant when rendering local members.
Useful? React with 👍 / 👎.
Closes #83. Server side: Birfy/bot-social#2 (docs/FACE_ROOMS.md is the API contract this client is built against).
What it does
Checks (head cd383ab)
Run: plugins/social 58, core 675, build-client --check, lint, plugins/tree 54, ds-bot-patches check, lint-plugins.
Not run: smoke bench, live DSH/Desktop, real Bot Social, browser pass.
Known gap
An unpublished Bot that never had a Bot Social identity cannot join (social only registers published Bots, #87), contradicting the issue's 'unpublished Bots may join this one room'. Needs a follow-up decision.