Repository navigation
fix(social): a held room line shows only in its own live room (#188) #189
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| A held Bot line now shows only in the face-to-face room it was held in; a stale card and waiting dot from an old room no longer appear in a new one. ([#188](https://github.com/ThinkFlowLab/FlyBot/issues/188)) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -81,9 +81,9 @@ | |
| const TEXTUAL = new Set(['text/plain', 'text/markdown', 'text/csv', 'text/tab-separated-values', 'application/json']) | ||
| const sizeLabel = n => (n < 1024 ? `${n} B` : n < 1048576 ? `${(n / 1024).toFixed(1)} KB` : `${(n / 1048576).toFixed(1)} MB`) | ||
| // Remote strings that reach model-visible text: one line, no brackets or markup, capped. | ||
| export const cleanText = (value, fallback = '') => String(value ?? '').replace(/[\u0000-\u001f\u007f-\u009f\u2028\u2029]/g, ' ').replace(/[[\]"<>`]/g, '').replace(/\s+/g, ' ').trim().slice(0, 60) || fallback | ||
| // Owner-only draft body: keep punctuation the friend will see; strip controls and cap. | ||
| export const draftText = (value) => String(value ?? '').replace(/[\u0000-\u001f\u007f-\u009f\u2028\u2029]/g, ' ').replace(/\s+/g, ' ').trim().slice(0, 800) | ||
| // Owner cards never show the FLYBOT-FACE-HUMAN wire envelope (#159). | ||
| export function approvalDraftText(value) { | ||
| const raw = draftText(value) | ||
|
|
@@ -136,7 +136,7 @@ | |
| } | ||
| const SEEN_KEPT = 1000 | ||
| const CREDENTIAL_NAME = /(^id_(rsa|dsa|ecdsa|ed25519)|\.(pem|key|p12|pfx|kdbx|keychain)$|credential|secret|token|password|passwd|^flybot-social.*\.json$|^\.?env$|^\.netrc$|^known_hosts$|^authorized_keys$)/i | ||
| const safeName = name => String(name ?? 'file').replace(/[\u0000-\u001f\u007f/\\:]/g, '_').replace(/^\.+/, '_').slice(0, 120) || 'file' | ||
| // The text Copy puts on the clipboard for a per-Bot invite: what to do with the link, for a | ||
| // person in FlyBot or for their Grok bot. | ||
| export function inviteText({ handle, name, home, url, expiresAt }) { | ||
|
|
@@ -575,7 +575,7 @@ | |
| if (result?.unavailable) return | ||
| } catch (error) { | ||
| if (/invalid_avatar|invalid_color|invalid_request/i.test(`${error?.code ?? ''} ${error?.message ?? ''}`)) { | ||
| settings.lookSynced = { ...(settings.lookSynced ?? {}), [bot.id]: fingerprint } | ||
| saveSettings() | ||
| return | ||
| } | ||
|
|
@@ -1722,6 +1722,22 @@ | |
| if (keys.length > 200) for (const key of keys.sort((a, b) => map[a] - map[b]).slice(0, keys.length - 200)) delete map[key] | ||
| saveSettings() | ||
| } | ||
| // The room a held room line belongs to (#188). Bot Social's approval card may not name it, so | ||
| // the send path records it; a hold with no known room is legacy and stays out of every room. | ||
| const holdRooms = () => (settings.holdRooms ??= {}) | ||
| function rememberHoldRoom(id, faceRoomId) { | ||
| if (!id || !faceRoomId) return | ||
| const map = holdRooms() | ||
| map[id] = { roomId: faceRoomId, at: now() } | ||
| const keys = Object.keys(map) | ||
| if (keys.length > 200) for (const key of keys.sort((a, b) => map[a].at - map[b].at).slice(0, keys.length - 200)) delete map[key] | ||
| saveSettings() | ||
| } | ||
| const faceHoldLive = (roomId) => { | ||
| const hosted = roomId ? flybot.rooms.ofFace?.(roomId) : undefined | ||
| if (hosted?.face?.status === 'left' || hosted?.face?.status === 'closed') return false | ||
| return !(settings.pastFaceRooms ?? []).some(item => item.faceRoomId === roomId) | ||
| } | ||
| const ownLineApproving = new Set() | ||
| async function approveOwnLine(item) { | ||
| if (!item?.id || ownLineApproving.has(item.id)) return | ||
|
|
@@ -1743,6 +1759,10 @@ | |
| const items = raw | ||
| .map(item => approvalView(item, { localName: localNameOf })) | ||
| .filter(item => item && (item.kind === 'outbound_message' || item.kind === 'face_room_message') && (item.status === 'pending' || item.status === 'pending_approval')) | ||
| // A held room line belongs to one live room only (#188): no known room (legacy) or a room | ||
| // that is left / closed here means no card and no waiting dot anywhere. | ||
| .map(item => (item.kind === 'face_room_message' && !item.roomId && holdRooms()[item.id]?.roomId ? { ...item, roomId: holdRooms()[item.id].roomId } : item)) | ||
| .filter(item => item.kind !== 'face_room_message' || (ownLineIds()[item.id] || (item.roomId && faceHoldLive(item.roomId)))) | ||
| const own = items.filter(item => ownLineIds()[item.id]) | ||
| const held = items.filter(item => !ownLineIds()[item.id]) | ||
| pendingCache = { at: now(), items: held } | ||
|
|
@@ -1839,7 +1859,7 @@ | |
| const faceId = room?.face?.roomId | ||
| return pendingCache.items.filter((item) => { | ||
| if (item.kind === 'face_room_message') { | ||
| return Boolean(faceId) && (!item.roomId || item.roomId === faceId || item.roomId === room.id) | ||
| return Boolean(item.roomId) && (item.roomId === faceId || item.roomId === room.id) | ||
| } | ||
| if (item.roomId) return item.roomId === room.id || (faceId && item.roomId === faceId) | ||
| return handles.has(norm(item.from)) || handles.has(norm(item.to)) | ||
|
|
@@ -2263,6 +2283,7 @@ | |
| try { | ||
| const sent = await faceApi.send(serverId, { text: outbound.text, botId: outbound.botId }) | ||
| if (sent?.status === 'pending_approval') { | ||
| rememberHoldRoom(sent.approvalId, serverId) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When a caller uses the still-registered Useful? React with 👍 / 👎. |
||
| // A person's own line is theirs: typing it is the approval, so it goes out at once | ||
| // with no card (#179). Only Bot-written room lines wait for Approve / Reject. | ||
| if (outbound.human && sent.approvalId) { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When
social/face-leaveencounters a transient error,leaveFaceRoomhas already added the room topastFaceRoomsat lines 2231-2232, but the non-alreadyLeftFacebranch rethrows without removing that provisional entry. This new predicate then filters every hold from the still-hosted open room on the next status poll, and a later settings write can persist the bad marker; delay or roll back the past-room update when the leave request fails.Useful? React with 👍 / 👎.