100 Commits
Author SHA1 Message Date
earthjasonlin c28825ec87 fix: use find_user_by_chat_id for expand_promo_detail AI config 2026-07-23 11:28:28 +08:00
earthjasonlin a517768083 feat: AI hint supports multi-turn conversation with context - hint_history list stored in conversation, accumulates across rounds - Each new hint appended, full history passed to AI as combined prompt - Format: 'hint1 → hint2 → hint3' so AI understands the conversation flow 2026-07-22 13:32:09 +08:00
earthjasonlin e0c2110c09 fix: use original email subject for reply, not AI-extracted title Reply emails should use the original subject from IMAP header (e.g. 'Re: [original]'), not the AI-generated Chinese summary title. 2026-07-22 13:28:36 +08:00
earthjasonlin b18dcb0527 fix: wrap original text in code block to avoid MarkdownV2 parse errors Email bodies contain *, _, etc. that break MarkdownV2 entity parsing. Wrapping in backtick code block prevents Telegram from parsing internals. 2026-07-19 05:54:20 +08:00
earthjasonlin 9adb32cb0c feat: cleanup reply flow messages + AI comparison explanation - Track extra_msg_ids in conversation for code block messages - _cleanup_conv_messages: delete all intermediate messages on cancel/send - Cancel/send/edit all clean up extra messages before restoring summary view - AI prompts now return 'comparison' field with Chinese explanation of differences - _show_ai_suggestions displays comparison text above button list - generate_more_replies returns {suggestions, comparison} 2026-07-19 01:28:05 +08:00
earthjasonlin beaa3f146e feat: show full recipient and CC in email summary - Email class: add cc field - fetch_unseen_emails: parse Cc header - _format_normal: display full recipient, add CC line if present - send_summary/ai_process: pass cc through to email_context - save_email_context: store cc for callback use 2026-07-18 20:07:21 +08:00
earthjasonlin 7a01b29655 fix: update email_client and smtp_client for new config structure - email_client: use account.imap.server/port/ssl/starttls instead of flat fields - smtp_client: use s.ssl/starttls instead of s.use_ssl/use_starttls - tg_bot: fix _do_send_reply to use find_user_by_chat_id for email_accounts 2026-07-17 22:19:20 +08:00
earthjasonlin c6180834bc chore: remove old YAML config files, keep config.example.json 2026-07-17 22:08:14 +08:00
earthjasonlin f2b3793d4c feat: multi-user support - Config format: JSON with bot_token (global), users array - Each user has chat_id, AI config, email_accounts - Each email account has separate IMAP/SMTP with ssl/starttls - Config module: new dataclasses (UserConfig, ImapConfig, SmtpConfig) - find_user_by_chat_id() for callback dispatch - Main: email poller iterates all users, queues carry user object - Summarizer: accepts UserConfig, uses user-specific AI and chat_id - TG bot: all functions use bot_token string instead of TelegramConfig - Callback/message handlers look up user by chat_id - AI regen/hint threads use find_user_by_chat_id for AI config - Config example: config.example.json - Tests: 3/3 passing 2026-07-17 22:05:59 +08:00
earthjasonlin 9906f1675f chore: remove reply_suggestions from email summarization prompt AI reply suggestions are now generated on-demand when user clicks full/short reply buttons, using style-specific prompts. 2026-07-17 21:02:50 +08:00
earthjasonlin 8386ab3c89 fix: always regenerate AI replies when selecting style, ignore summary cache The cached reply_suggestions from summarization use a generic prompt, not matching full/short style. Now always triggers fresh generation. 2026-07-17 21:01:03 +08:00
earthjasonlin 1d70fe2491 fix: pass links/is_promotion to _summary_keyboard in cancel and send_reply handlers 2026-07-17 20:56:59 +08:00
earthjasonlin bedf5b887c feat: 2-col reply buttons, direct send, language-matched prompts - Buttons: [回复N 直接发送] [回复N 编辑] in 2-column layout - CALLBACK_SEND_REPLY: send immediately without editing - Prompts enforce matching email language (no Chinese forced on English) - Full mode: greeting + comma only, no [Your Name] placeholder - Tests: 5/5 passing 2026-07-17 20:49:11 +08:00
earthjasonlin 6ce9a806ea fix: clear reply suggestions cache on cancel, regenerate when switching styles - Cancel now clears reply_suggestions from email_context - Selecting style with empty cache triggers fresh AI generation - Selecting style with cache uses cached suggestions - Tests: 3/3 passing 2026-07-17 20:33:09 +08:00
earthjasonlin 84f2428aeb fix: send AI suggestion as MarkdownV2 code block for copy-on-tap 2026-07-17 20:28:46 +08:00
earthjasonlin 51064e48aa feat: AI reply suggestions show as copyable code block, user edits before sending - Click suggestion -> show text in backtick code block (tap to copy) - Enter awaiting_reply state so user can edit/send like normal reply - No direct email sending on selection anymore - Tests: 3/3 passing 2026-07-17 20:23:59 +08:00
earthjasonlin ab2bf7facb feat: AI reply modes - full (formal email) and short (concise) - Split 'AI回复' button into 'AI完整回复' and 'AI简洁回复' - Full mode: complete email format with greeting/signature, 300 char limit - Short mode: brief text message style, 50 char limit - Style persists in conversation state for regen/hint consistency - Tests: 6/6 passing 2026-07-17 20:14:48 +08:00
earthjasonlin 2a9be9896f fix: restore links when returning to summary view _summary_keyboard was called without links param in VIEW_SUMM handler, causing extracted links to disappear after viewing original text. 2026-07-17 20:10:18 +08:00
earthjasonlin 2f29e03cec fix: read Telegram caption field for media messages Telegram sends text in 'caption' field (not 'text') when message has media. Handle both cases: text or caption, with text taking priority. 2026-07-17 20:04:27 +08:00
earthjasonlin b2e219ece5 fix: send reply immediately on media-only message, no accumulation - Remove accumulation logic: media-only now sends as reply directly - All 3 cases (text-only, media-only, text+media) send immediately - Tests: 4/4 passing 2026-07-17 19:53:38 +08:00
earthjasonlin 131e440d6b style: simplify media accumulation - silent accumulate, immediate send on text 2026-07-17 19:52:08 +08:00
earthjasonlin 4002dec33c fix: add missing account_idx to save_email_context in tg_send_and_mark 2026-07-17 19:36:50 +08:00
earthjasonlin d76a119190 feat: support attachments in manual reply (photo/video/document/voice) - Add _extract_media: extract media info from Telegram messages - Add _download_tg_file: download files from Telegram API - Modify _handle_message: accumulate attachments in awaiting_reply, send with text - Modify _handle_update: process messages without text (media-only) - Modify smtp_client.send_reply: support MIME multipart with attachments - Add comprehensive tests (11 tests, all passing) 2026-07-17 19:04:27 +08:00
earthjasonlin d05277581c fix: save_conversation in CALLBACK_HINT, move AI hint to thread pool 2026-07-17 18:45:40 +08:00
earthjasonlin fe24a07d15 fix: parse_mode always MarkdownV2, add MEMORY.md with project rules 2026-07-17 18:36:53 +08:00
earthjasonlin 0abb7de5d3 fix: move regenerate to AI thread pool, fix parse_mode None error 2026-07-17 18:30:57 +08:00
earthjasonlin 5ca739456d perf: reduce poll timeout to 1s and wait to 50ms for faster button response 2026-07-15 19:20:52 +08:00
earthjasonlin c26d8aa294 fix: reduce long-poll timeout to 2s, make mark_as_seen async to unblock TG sender 2026-07-15 19:15:27 +08:00
earthjasonlin c940a456bc fix: clear all database tables on startup 2026-07-15 19:09:41 +08:00
earthjasonlin b226f0b597 fix: clear processed_emails on startup to avoid stale state 2026-07-15 19:09:02 +08:00
earthjasonlin 687e60ede4 fix: add error logging for send_thinking failures in email poller 2026-07-15 19:02:59 +08:00
earthjasonlin da1b266350 fix: increase Telegram long-poll timeout to 5s and HTTP timeout to 15s 2026-07-15 18:51:04 +08:00
earthjasonlin dcdfba11fa feat: show email subject and sender in all status messages 2026-07-15 18:39:58 +08:00
earthjasonlin 2822075f2f refactor: split TG worker into poller (callbacks) and sender (email summaries) threads 2026-07-15 18:35:01 +08:00
earthjasonlin 2744d9dd77 fix: process one queued email per poll cycle to keep callbacks responsive 2026-07-15 18:32:42 +08:00
earthjasonlin 17514302f9 fix: import is_uid_processed instead of is_uids_processed 2026-07-15 18:25:57 +08:00
earthjasonlin d42022cc2e fix: ensure data directory exists before DB connection in all threads 2026-07-15 18:18:24 +08:00
earthjasonlin 54c4bf00fb refactor: replace all in-memory state with SQLite database 2026-07-14 14:49:23 +08:00
earthjasonlin c3132f746a feat: AI generates plain_text field, view full text shows readable version instead of HTML 2026-07-14 14:37:12 +08:00
earthjasonlin dd7fc61fd4 fix: remove all body truncation, show full email text everywhere 2026-07-14 14:34:17 +08:00
earthjasonlin 5113d78662 prompt: detailed instructions for extracting unsubscribe links from promotional emails 2026-07-13 08:38:05 +08:00
earthjasonlin 8b885f1c4c fix: use threading.Event for immediate shutdown instead of sleep loops 2026-07-13 08:34:02 +08:00
earthjasonlin 8b577b4c12 prompt: emphasize extracting unsubscribe links from promotional emails 2026-07-13 08:30:44 +08:00
earthjasonlin 26468aa43d fix: use ellipsis character instead of dots in status messages for MarkdownV2 compatibility 2026-07-13 08:28:23 +08:00
earthjasonlin 98147e6a6c fix: omit parse_mode from payload when None instead of sending null 2026-07-13 08:27:16 +08:00
earthjasonlin bd5b824a6a fix: add parse_mode param to edit_message, status messages use plain text 2026-07-13 08:23:42 +08:00
earthjasonlin 01504e6295 feat: three-stage status: 获取邮件中 -> 等待处理 -> AI思考中 2026-07-13 08:19:36 +08:00
earthjasonlin 02b0941ed0 feat: two-stage status messages: 获取邮件中 -> AI思考中 2026-07-13 08:15:25 +08:00
earthjasonlin 85b02651b9 revert: use debug log for unselectable folders instead of warning 2026-07-13 08:11:30 +08:00
earthjasonlin e74ae8be88 fix: quote folder names in IMAP SELECT for folders with spaces 2026-07-13 08:10:39 +08:00
earthjasonlin f38259e4bb fix: mark_as_seen uses readonly=False to actually set Seen flag 2026-07-13 08:05:40 +08:00
earthjasonlin 5368812e45 fix: skip NoSelect folders, clean up debug logging 2026-07-13 08:04:26 +08:00
earthjasonlin 8c088ddff5 debug: add skip_uids logging to diagnose re-fetch issue 2026-07-13 07:54:13 +08:00
earthjasonlin 0ec2048bc5 fix: never discard from _processing_uids to prevent re-fetching 2026-07-13 07:49:09 +08:00
earthjasonlin 155a6dfc53 fix: skip already-processing UIDs at FETCH level to avoid downloading email content 2026-07-13 07:35:28 +08:00
earthjasonlin d31ec18ea3 fix: restore -FLAGS Seen after FETCH, protected by _processing_uids 2026-07-13 07:13:08 +08:00
earthjasonlin 02cf4b88b6 fix: add _processing_uids to prevent re-fetching emails being processed 2026-07-13 07:12:43 +08:00
earthjasonlin ae140df3fb chore: remove accidental file 2026-07-13 06:57:28 +08:00
earthjasonlin e9398526e6 fix: login without selecting INBOX first so conn.list() returns all folders 2026-07-13 06:57:06 +08:00
earthjasonlin 479b855390 feat: scan all IMAP folders for unread emails, not just INBOX 2026-07-13 00:16:28 +08:00
earthjasonlin 46bc7c7057 prompt: force simplified Chinese for all AI outputs except reply suggestions 2026-07-13 00:12:50 +08:00
earthjasonlin bbc43f8972 fix: keep pending_uids until TG send completes to prevent duplicate processing 2026-07-12 23:51:45 +08:00
earthjasonlin 87e4b0b01e prompt: refine link extraction to exclude promotional links 2026-07-12 10:15:25 +08:00
earthjasonlin fc4c5a175c feat: show thinking indicator during AI processing, accept non-numeric verification codes 2026-07-11 22:54:06 +08:00
earthjasonlin 1c7d0c1b00 fix: mark emails back as UNSEEN after FETCH to counter server auto-read 2026-07-11 22:45:31 +08:00
earthjasonlin 5320b10317 fix: per-email error handling with connection rebuild, remove @retry on fetch 2026-07-11 22:43:39 +08:00
earthjasonlin d8807ffbb5 fix: remove body truncation for large emails 2026-07-11 22:38:40 +08:00
earthjasonlin 8d40ed8b19 fix: use BODY.PEEK for large emails to prevent server auto-marking as read on timeout 2026-07-11 22:38:07 +08:00
earthjasonlin 57191ab00e feat: remove body truncation for all AI requests, increase API timeout to 120s 2026-07-11 22:32:47 +08:00
earthjasonlin 13b76c00ab feat: add tell me more button for promotion emails with AI-generated details 2026-07-11 22:17:10 +08:00
earthjasonlin cf0f7d3956 style: restructure email formats with 收件账户 and app_name for verification 2026-07-11 22:11:15 +08:00
earthjasonlin e56bdef0f7 fix: use single backtick for tap-to-copy, remove copy_text button 2026-07-11 22:05:02 +08:00
earthjasonlin 6d1fe29df4 fix: copy_text field must be an object with text key 2026-07-11 21:57:19 +08:00
earthjasonlin 05b15d9ee3 fix: use copy_text field instead of duplicate text key 2026-07-11 21:52:07 +08:00
earthjasonlin 03c18cdb76 fix: copy button shows only the code number 2026-07-11 21:49:54 +08:00
earthjasonlin b835812f38 feat: add copy_text button for verification codes 2026-07-11 21:47:54 +08:00
earthjasonlin 070c32071a style: beautify email formats with type labels and consistent layout 2026-07-11 21:42:35 +08:00
earthjasonlin 5a21aeee14 chore: remove test scripts 2026-07-11 21:35:58 +08:00
earthjasonlin 8007fa1cc4 fix: always use MarkdownV2 for all message types 2026-07-11 21:35:22 +08:00
earthjasonlin 43994d2b0d fix: remove null parse_mode from tg payload, add error logging 2026-07-11 21:22:20 +08:00
earthjasonlin a992af0e02 refactor: remove action_required/action_items/key_points, keep summary concise 2026-07-11 19:46:18 +08:00
earthjasonlin 1957eb2a73 feat: extract clickable links from email and show as URL buttons 2026-07-11 19:38:33 +08:00
earthjasonlin be4e957af9 feat: verification emails displayed as platform + code 2026-07-08 16:15:51 +08:00
earthjasonlin 9991e4ef17 feat: promotional emails summarized as who promotes what 2026-07-08 16:13:54 +08:00
earthjasonlin ea6358019d chore: AI并行workers 5→10 2026-07-06 20:13:15 +08:00
earthjasonlin d70b817fd4 feat: AI并行处理 + 修复邮件丢失
- _ai_processor 改用 ThreadPoolExecutor(5 workers) 并行调用AI
- _tg_worker 每轮排空所有待发TG消息(原来每轮只取1条)
- fetch_unseen_emails 单封FETCH失败不再丢弃整个批次,跳过继续
2026-07-06 20:12:24 +08:00
earthjasonlin 0c1ccdfc79 fix: 修复 _email_poller 线程静默死亡问题
- IMAP 连接加 30s socket timeout,防止 _send_id_command 等操作永久阻塞
- _email_poller 加顶层 try/except,异常后 sleep 5s 继续循环
- @retry() 加指数退避延迟(1s,2s,4s...),避免重试风暴
2026-07-06 19:54:23 +08:00
earthjasonlin c684090b9c feat: handle Reply-To header, add account_email field, decode address headers 2026-07-02 22:37:15 +08:00
earthjasonlin d7daddf491 feat: IMAP 支持 SSL/STARTTLS 配置 2026-07-02 22:18:51 +08:00
earthjasonlin 05135ee0cc chore: 添加 requirements.txt 2026-07-02 22:05:07 +08:00
earthjasonlin ad950ff367 feat: 收件人/发件人改用邮件原始头数据
- sender 从 decode 改为使用原始 From 头
- 新增 recipient 字段,使用原始 To 头
- 移除 ai_process 中对 acct.username 的依赖
2026-07-02 22:04:27 +08:00
earthjasonlin 2ebcad0a70 feat: include reply content in send confirmation message 2026-07-02 21:01:05 +08:00
earthjasonlin d334b6f3eb feat: enhance logging detail across all modules
- email_client: log IMAP connection, login, UNSEEN count, each email
  details (subject/sender/body size), mark-as-seen progress
- ai_client: log AI request params, timing, token usage, response size
- smtp_client: log SMTP connect, login, send details
- tg_bot: log all callback actions with subject context, message states
- main: periodic queue depth report (email_queue/tg_queue/pending_uids)
2026-07-02 21:00:40 +08:00
earthjasonlin 934d6a7545 fix: thread-safe logging with QueueHandler + QueueListener
- Replace logging.basicConfig with QueueHandler/QueueListener
  so all log output writes from a single thread
- Add %(threadName)s to format for thread identification
2026-07-02 20:57:33 +08:00
earthjasonlin be412168bb fix: also delete user's message when cleaning up reply/hint flow 2026-07-02 20:56:30 +08:00
earthjasonlin 08a1a32367 fix: use original email From header for reply address instead of AI output
- send_summary now accepts original_sender (raw From header)
- Context stores original_sender separately from AI-generated sender
- _do_send_reply tries original_sender first, then AI sender fallback
2026-07-02 20:55:40 +08:00
earthjasonlin ded61e25c1 refactor: split AI processor and TG worker into separate threads
- Three independent threads: email poller, AI processor, TG worker
- Two queues: _email_queue (raw emails→AI), _tg_queue (AI results→TG)
- summarizer.py split: ai_process() (AI only) + tg_send_and_mark() (TG only)
- AI retries no longer affect TG polling or email polling
2026-07-02 20:52:34 +08:00
earthjasonlin 1be3aadb08 refactor: separate email poll, email process, TG poll into threads
- main.py spawns 3 daemon threads: email poller, email processor, TG poller
- Each thread runs independently so retries don't block other operations
- _pending_uids set + lock prevents duplicate queueing across poll cycles
- summarizer.py split into poll_accounts() (generator) and process_email()
- Graceful shutdown via shared _running flag
2026-07-02 20:48:05 +08:00
earthjasonlin 09d11a6c03 feat: cancel button, regenerate AI replies, custom reply hints
- Reply prompt now has an inline '取消回复' button instead of typing text
- AI reply selection adds '换一批' button: passes all previous suggestions
  to AI to generate different replies
- '我想说:' button: lets user type a hint/tone instruction, AI tailors
  suggestions accordingly
- Cancel button also works on prompt/hint messages (deletes them)
- Extract _show_ai_suggestions helper for reuse
2026-07-02 20:45:40 +08:00
earthjasonlin 18db9caa8b feat: cancel reply flow with cleanup & remove AI reply confirmation
- send_text now returns message_id for tracking
- Reply prompt includes cancel hint; typing '取消'/'/cancel' clears it
- Prompt message auto-deleted after reply sent or cancelled
- AI reply suggestions always send immediately on tap (no confirm step)
- Removed _confirm_keyboard, CALLBACK_CONFIRM_REPLY handler
2026-07-02 20:41:56 +08:00