Add mark_as_read config option to preserve email read status

Emails forwarded to Poke are no longer marked as read by default,
so users can continue using read/unread status to organize their
inbox. Set mark_as_read: true (global or per-account) to restore
the previous behavior.

Closes #5
This commit is contained in:
Kacper Kwapisz
2026-03-25 14:47:42 +01:00
parent 7d38b7ed81
commit 5a6ae3e94f
2 changed files with 12 additions and 2 deletions
+5
View File
@@ -11,6 +11,10 @@ poke_api_key: "your-api-key-here"
# Agents can still use create_draft. Override per account with allow_send. # Agents can still use create_draft. Override per account with allow_send.
allow_send: true allow_send: true
# Mark forwarded emails as read — set to true to mark emails as read after
# forwarding to Poke. Defaults to false. Override per account with mark_as_read.
mark_as_read: false
accounts: accounts:
# iCloud Mail — login is @icloud.com, send as your custom domain # iCloud Mail — login is @icloud.com, send as your custom domain
# Generate an App-Specific Password: https://support.apple.com/en-us/102654 # Generate an App-Specific Password: https://support.apple.com/en-us/102654
@@ -24,6 +28,7 @@ accounts:
from_address: "you@yourdomain.com" # Override From: header (login is @icloud.com but send as custom domain) from_address: "you@yourdomain.com" # Override From: header (login is @icloud.com but send as custom domain)
watch_folders: watch_folders:
- INBOX - INBOX
# mark_as_read: false # Keep emails unread after forwarding
# Custom SMTP server — SMTP falls back to IMAP credentials if not specified # Custom SMTP server — SMTP falls back to IMAP credentials if not specified
# - id: work # - id: work
+7 -2
View File
@@ -194,10 +194,12 @@ def parse_accounts(config: dict) -> list[dict]:
os.environ.get("SMTP_USERNAME", os.environ["IMAP_USERNAME"]), os.environ.get("SMTP_USERNAME", os.environ["IMAP_USERNAME"]),
), ),
"watch_folders": ["INBOX"], "watch_folders": ["INBOX"],
"mark_as_read": os.environ.get("MARK_AS_READ", "false").lower() == "true",
} }
] ]
global_allow_send = config.get("allow_send", True) global_allow_send = config.get("allow_send", True)
global_mark_as_read = config.get("mark_as_read", False)
required = ("imap_host", "imap_username", "imap_password") required = ("imap_host", "imap_username", "imap_password")
for i, acc in enumerate(accounts): for i, acc in enumerate(accounts):
acc.setdefault("id", f"account-{i}") acc.setdefault("id", f"account-{i}")
@@ -210,6 +212,7 @@ def parse_accounts(config: dict) -> list[dict]:
acc.setdefault("smtp_password", acc.get("imap_password")) acc.setdefault("smtp_password", acc.get("imap_password"))
acc.setdefault("from_address", acc.get("smtp_username")) acc.setdefault("from_address", acc.get("smtp_username"))
acc.setdefault("allow_send", global_allow_send) acc.setdefault("allow_send", global_allow_send)
acc.setdefault("mark_as_read", global_mark_as_read)
for field in required: for field in required:
if field not in acc: if field not in acc:
raise RuntimeError( raise RuntimeError(
@@ -491,7 +494,8 @@ async def watch_folder(
email_data = parse_email_message(raw) email_data = parse_email_message(raw)
await forward_to_poke(email_data, account, webhook_url, api_key) await forward_to_poke(email_data, account, webhook_url, api_key)
await asyncio.to_thread(client.set_flags, new_uids, [b"\\Seen"]) if account.get("mark_as_read", False):
await asyncio.to_thread(client.set_flags, new_uids, [b"\\Seen"])
existing_unseen.update(new_uids) existing_unseen.update(new_uids)
except asyncio.CancelledError: except asyncio.CancelledError:
@@ -535,7 +539,8 @@ async def _poll_folder(
continue continue
email_data = parse_email_message(raw) email_data = parse_email_message(raw)
await forward_to_poke(email_data, account, webhook_url, api_key) await forward_to_poke(email_data, account, webhook_url, api_key)
await asyncio.to_thread(client.set_flags, uids, [b"\\Seen"]) if account.get("mark_as_read", False):
await asyncio.to_thread(client.set_flags, uids, [b"\\Seen"])
except Exception as e: except Exception as e:
logger.warning("[%s/%s] Poll error: %s", account["id"], folder, e) logger.warning("[%s/%s] Poll error: %s", account["id"], folder, e)
raise # reconnect via outer loop raise # reconnect via outer loop