99 lines
3.1 KiB
Python
99 lines
3.1 KiB
Python
#!/usr/bin/env python3
|
|
"""standup_bot.py — CLI Daily Standup Bot, Draft implementation.
|
|
|
|
Reads ./standup.md, formats a dated summary, writes to ./standup-output/,
|
|
and optionally emails via SMTP. Stdlib only.
|
|
"""
|
|
|
|
import argparse
|
|
import os
|
|
import smtplib
|
|
import sys
|
|
from datetime import date
|
|
from email.mime.text import MIMEText
|
|
from pathlib import Path
|
|
|
|
REQUIRED_SECTIONS = ("## Yesterday", "## Today", "## Blockers")
|
|
DEFAULT_INPUT = Path("./standup.md")
|
|
DEFAULT_OUTPUT_DIR = Path("./standup-output")
|
|
|
|
|
|
def parse_args(argv=None):
|
|
p = argparse.ArgumentParser(description="Daily standup formatter")
|
|
p.add_argument("--input", type=Path, default=DEFAULT_INPUT, help="standup markdown")
|
|
p.add_argument("--out-dir", type=Path, default=DEFAULT_OUTPUT_DIR, help="output directory")
|
|
p.add_argument("--to", default=None, help="recipient email (overrides STANDUP_TO)")
|
|
return p.parse_args(argv)
|
|
|
|
|
|
def read_standup(path: Path) -> str:
|
|
if not path.exists():
|
|
print(f"error: {path} not found", file=sys.stderr)
|
|
sys.exit(2)
|
|
text = path.read_text(encoding="utf-8")
|
|
missing = [s for s in REQUIRED_SECTIONS if s not in text]
|
|
if missing:
|
|
print(f"error: missing sections {missing!r} in {path}", file=sys.stderr)
|
|
sys.exit(2)
|
|
return text
|
|
|
|
|
|
def format_summary(text: str, source_name: str, recipient: str | None = None) -> str:
|
|
today = date.today().isoformat()
|
|
lines = [f"# Standup — {today}", "", f"_Source: {source_name}_", ""]
|
|
if recipient:
|
|
lines.append(f"_Recipient: {recipient}_")
|
|
lines.append("")
|
|
for section in REQUIRED_SECTIONS:
|
|
lines.append(section)
|
|
lines.append("")
|
|
capture = False
|
|
for line in text.splitlines():
|
|
if line.strip() == section:
|
|
capture = True
|
|
continue
|
|
if capture:
|
|
if line.startswith("## ") and line.strip() != section:
|
|
break
|
|
lines.append(line)
|
|
return "\n".join(lines) + "\n"
|
|
|
|
|
|
def write_output(summary: str, out_dir: Path) -> Path:
|
|
out_dir.mkdir(parents=True, exist_ok=True)
|
|
out_path = out_dir / f"{date.today().isoformat()}.md"
|
|
out_path.write_text(summary, encoding="utf-8")
|
|
return out_path
|
|
|
|
|
|
def maybe_email(summary: str, to_addr: str | None) -> None:
|
|
host = os.environ.get("SMTP_HOST")
|
|
user = os.environ.get("SMTP_USER")
|
|
pwd = os.environ.get("SMTP_PASS")
|
|
if not (host and user and pwd):
|
|
return
|
|
recipient = to_addr or os.environ.get("STANDUP_TO")
|
|
if not recipient:
|
|
return
|
|
msg = MIMEText(summary)
|
|
msg["Subject"] = f"Standup — {date.today().isoformat()}"
|
|
msg["From"] = user
|
|
msg["To"] = recipient
|
|
with smtplib.SMTP(host) as s:
|
|
s.login(user, pwd)
|
|
s.send_message(msg)
|
|
|
|
|
|
def main(argv=None) -> int:
|
|
args = parse_args(argv)
|
|
text = read_standup(args.input)
|
|
summary = format_summary(text, str(args.input), args.to)
|
|
print(summary, end="")
|
|
write_output(summary, args.out_dir)
|
|
maybe_email(summary, args.to)
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|