Session 2026-09-15 code: imap_client (auth+metrics), handlers (TG date format), cron fix (venv python), openspec imap-realtime-sync proposal

This commit is contained in:
2026-09-15 17:48:05 +00:00
parent 65c167acc2
commit defcf53d75
10 changed files with 982 additions and 28 deletions
+36 -2
View File
@@ -108,6 +108,17 @@ TG_TIMEOUT = 20
# ---------------------------------------------------------------------------
# Frontmatter
# ---------------------------------------------------------------------------
def _unquote_yaml(v):
"""Снять обрамляющие двойные кавычки YAML-значения (артефакт простого парсера).
Если значение начинается/заканчивается на ", снять их и разэкранировать \\".
"""
v = v.strip()
if len(v) >= 2 and v[0] == '"' and v[-1] == '"':
v = v[1:-1].replace('\\"', '"').replace("\\\\", "\\")
return v
def parse_email_md(path):
"""Прочитать email.md, вернуть (headers, body, content, fm_end)."""
content = path.read_text(encoding="utf-8", errors="replace")
@@ -120,7 +131,7 @@ def parse_email_md(path):
for line in yaml_block.split("\n"):
m = re.match(r"^(\w[\w_-]*)\s*:\s*(.*)$", line)
if m:
headers[m.group(1)] = m.group(2).strip()
headers[m.group(1)] = _unquote_yaml(m.group(2))
else:
headers, body, fm_end = {}, content.strip(), None
return headers, body, content, fm_end
@@ -379,15 +390,38 @@ def tg_call(method, **params):
return data.get("result", {})
def format_date_for_tg(date_str):
"""'2026-09-02 10:49+03:00' → '02.09.2026 10:49' (ДД.ММ.ГГГГ ЧЧ:ММ).
Известные форматы frontmatter:
'2026-09-02 10:49+03:00' (ISO, может быть пробел/буква перед таймзоной)
'2026-09-02T10:49:03+03:00' (RFC3339)
Если распарсить не удалось — вернуть как есть (не ломать уведомление).
"""
s = (date_str or "").strip()
if not s:
return ""
t = s
# нормализуем: отрезаем таймзону (+03:00 / +0300 / Z)
m = re.match(r"^(\d{4})-(\d{2})-(\d{2})[T ](\d{2}):(\d{2})", s)
if m:
return f"{m.group(3)}.{m.group(2)}.{m.group(1)} {m.group(4)}:{m.group(5)}"
return s
def handle_urgent(path, headers, body):
"""Отправить уведомление в Telegram. Возвращает (ok, detail)."""
subject = (headers.get("subject") or "(без темы)").strip()
sender = (headers.get("from") or "?").strip()
# Дата/время получения — из frontmatter, формат ДД.ММ.ГГГГ ЧЧ:ММ
date = format_date_for_tg(headers.get("date") or headers.get("internal_date"))
date_line = f"📅 {date}\n" if date else ""
preview = body.strip()
if len(preview) > MAX_PREVIEW_CHARS:
preview = preview[:MAX_PREVIEW_CHARS].rstrip() + "…"
text = (
f"⚠️ СРОЧНОЕ письмо\n\n"
f"⚠️ СРОЧНОЕ письмо\n"
f"{date_line}"
f"От: {sender}\n"
f"Тема: {subject}\n\n"
f"{preview}\n\n"