Skip to content

Hide Spam and unknown-sender chats via bridge.conf - #49

Merged
nixfred merged 2 commits into
nixfred:mainfrom
joshuaswarren:hide-filtered-chats
Sep 10, 2026
Merged

nixfred merged 2 commits into
nixfred:mainfrom
joshuaswarren:hide-filtered-chats

Conversation

@joshuaswarren

Copy link
Copy Markdown
Contributor

Why

iPhone Messages files Spam as chat.is_filtered = 2 and Filter Unknown Senders as 1. The phone badge ignores both. Blip read the same chat.db and counted them, so the bar could show 3 while the phone showed 0.

There is no folder setting today. The mute list is per sender/phrase, not the folder.

What

Two bridge.conf keys, default off (previous behaviour):

hide_spam=on
hide_unknown=on

The Linux shim injects --hide-spam / --hide-unknown on imsg only. The Mac query drops those chats from recent, chats, and search (no sidebar row, no unread, no toast). Explicit thread / from still load a chat if you have its id.

chat.is_filtered missing (older schema) → no extra predicate.

Needs

Re-run blip-setup (or copy bridge/mac/imsg to the Mac and the shim to ~/bin/imsg) so an older imsg is not handed unknown flags.

Test

python3 bridge/mac/test_filtered.py — 7 cases, no macOS, no real chat.db.

iPhone Messages files Spam as chat.is_filtered=2 and Filter Unknown
Senders as 1; the phone badge ignores both. Blip counted them, so the
bar disagreed with the phone. hide_spam=on / hide_unknown=on drop those
chats at the Mac query (default off). Mute list still covers inbox spam.
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 9, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-09T23:21:07.357994Z 7e5c7ea New commits
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

hide_spam drops those rows in SQL, so catch-up can never see the old
unreadOldest timestamp. The capped path then copied the prior ledger
back, pinning every poll at 8192 rows and keeping the bar badge.
Restore only chats still listed by imsg chats.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7e5c7eaa42

ℹ️ 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".

Comment thread collector.ts
Comment on lines +1493 to +1499
const listed = (deep || fetched.capped) ? dropMutedChats(fetchChats(), mute, muted) : null;
if (fetched.capped) {
// The window stopped short of the oldest outstanding unread: a chat with
// NO row in the window keeps its ledger entry instead of being reset to
// zero by a rebuild that never saw it (Astra B#3).
const inWindow = new Set(msgs.map(chatKey));
for (const [c, n] of Object.entries(state.unreadCounts)) {
if (n > 0 && !inWindow.has(c) && !(c in exactCounts)) {
exactCounts[c] = n;
if (state.unreadOldest[c]) exactOldest[c] = state.unreadOldest[c]!;
}
}
const kept = keepCappedUnread(
exactCounts, exactOldest,
state.unreadCounts, state.unreadOldest,
inWindow, visibleLedgerChats(msgs, listed),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve unread entries when the chat listing is incomplete

When catch-up reaches the 8,192-row cap, fetchChats() can return null after a transient second bridge failure or omit an older unread conversation because CHAT_LIST_LIMIT is only 300. In either case visibleLedgerChats treats the absent chat as hidden, so keepCappedUnread permanently removes its prior unread count; the next poll then advances the cutoff and cannot recover those older unread messages. Absence from this fallible, bounded listing cannot distinguish a filtered chat from an unlisted one, so preserve the ledger unless the bridge explicitly identifies the chat as hidden.

Useful? React with 👍 / 👎.

Comment thread README.md
Comment on lines +422 to +423
Needs a `blip-setup` re-run (or a copy of `bridge/mac/imsg` to the Mac)
so `--hide-spam` / `--hide-unknown` exist on the far side.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Require updating the Linux shim in the manual install path

The documented alternative does not enable this feature for an existing installation: copying only bridge/mac/imsg adds support for the flags, but the old Linux shim never reads hide_spam or hide_unknown and therefore never supplies those flags. Users following this parenthetical will see no filtering; instruct them to update both bridge/mac/imsg and bridge/linux/blip-shim, as the commit message already does.

Useful? React with 👍 / 👎.

nixfred added a commit that referenced this pull request Sep 10, 2026
…ad as a failure

PR #43 merged fine but its workflow run is recorded as failed, because the
approval expired before any job started. Zero jobs, no logs, and an annotation
that looks exactly like a test regression to whoever opened the PR.

Verified rather than assumed. The repo's policy is
`{"approval_policy":"first_time_contributors"}`, the cited run
(34064507217, event=pull_request, head duplicate-cards-and-instant-send) is
conclusion=failure, and this is not historical: PR #49 has two runs sitting at
conclusion=action_required right now, which is why it shows "no checks
reported" while #45 through #48 all show a passing test job.

CONTRIBUTING now tells contributors what an expired approval looks like and
that it is not their code, and tells maintainers to approve before merging so
a merged PR carries a real green tick.

Deliberately NOT done, both need the owner's decision:

- Changing the approval policy. Relaxing it means untrusted fork code runs in
  CI on push, which is a security trade-off, not a docs fix.
- Approving the two runs waiting on #49. Same reason: it executes that fork's
  workflow.

No runtime change, which matches the reporter's own framing.

LR-T: issue-44, ci, fork-pr-approval, github-actions
LR-D: blip, ci, docs
LR-K: an-expired-approval-is-not-a-test-failure

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MjLJVjERSup2CMwVehKja7
@nixfred
nixfred merged commit 7903e26 into nixfred:main Sep 10, 2026
DanielLemky pushed a commit to DanielLemky/blip that referenced this pull request Sep 16, 2026
…nst the wrong bytes

WHAT: Fred — "when someone sends me more than one picture in a single text,
the photos dont come thru," with a real example. Diagnosed on that message
(two HEICs from one send):

  chat.db says      5.07 MB   4.27 MB     (the HEIC source)
  sips --jpeg gives 7.80 MB   6.33 MB     (the converted transfer)
  auto-fetch cap              5.00 MB

Two independent rejections, one at each end. autoFetchImages() gated on the
SOURCE size, so the 5.07 MB photo was never even requested; fetch.ts applied
the same 5 MB to the CONVERTED bytes, so the 4.27 MB one was requested and
then refused. Neither ever appeared. Nothing to do with there being two of
them — a single full-resolution iPhone photo fails identically; batches just
make it obvious, because that is when people send full-size frames.
FIX: an auto-fetch is for a bubble that draws at ~800 px, so ask the Mac for a
display-sized frame instead of a full one. `imsg attachment --max-dim N`
resamples with sips (-Z, long edge, never upscales, quality 80). fetch.ts
requests it for EVERY image, not just HEIC — a 12 MP PNG shipped raw is just
as slow. The same two photos now arrive at 0.86 MB and 0.73 MB.
Previews get their own cache slot, `<id>-prev-<name>.jpg`: always JPEG, so
always named .jpg (xdg-open dispatches on extension — war room nixfred#49), and it
cannot collide with `<id>-orig-…`, so CLICKING a photo still fetches the
untouched original. The QML source gate becomes a sanity ceiling (32 MB)
rather than a transfer budget, since the Mac now bounds what is sent.
VERIFIED: both attachments of the real message fetch from scratch on the auto
path; demo fixtures now send two photos in one message and the harness
screenshot shows both rendered inline, which is also the new README image.
282 tests. Deployed to fnix and to the live plugin.

LR-T: blip, attachments, photos, heic, sips, cache, auto-fetch
LR-D: blip, imessage-bridge
LR-K: preview-max-dim, source-vs-converted-cap, prev-cache-slot
LR-L: law-10, law-11
LR-R: 9711f40

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K3V19BaTrQa2HCPFYbcfTy
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