v2.3.0: Phase 1 + E1 — onboarding wizard, salary, inline edit, today card, health break, Discord webhook
Added (6 user-friendly features):
- Onboarding wizard (5 steps, forced on first launch, re-runnable from Help menu)
- Salary estimation (optional, hourly wage + overtime multiplier)
- Inline clock-in/out time editing (click label to edit)
- Today summary card (post-clockout, auto-hide on next clock-in)
- Health break reminder (continuous N-hour at desk warning)
- Discord webhook notifications (clock-in/out/health, mobile push, no server)
Database:
- break_records.break_type column ('break'/'lunch'/'dinner')
- notification_log table (dedupe + analytics)
- Auto-complete onboarding for existing users (work_records exists)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
14d88656fe
commit
e4011d9fc3
@@ -0,0 +1,108 @@
|
||||
"""
|
||||
Discord 웹훅 알림 (단방향 push).
|
||||
|
||||
URL 1개로 끝. 봇 등록·서버 운영 0. 실패 시 silent (앱 동작 안 막음).
|
||||
"""
|
||||
from __future__ import annotations
|
||||
import json
|
||||
import urllib.request
|
||||
import urllib.error
|
||||
from datetime import datetime
|
||||
from typing import Optional, List
|
||||
|
||||
# Discord embed 색상 (decimal)
|
||||
COLOR_GREEN = 0x57F287
|
||||
COLOR_BLUE = 0x5865F2
|
||||
COLOR_YELLOW = 0xFEE75C
|
||||
COLOR_PINK = 0xEB459E
|
||||
COLOR_ORANGE = 0xED4245
|
||||
|
||||
|
||||
def send(webhook_url: str, title: str, description: str,
|
||||
color: int = COLOR_BLUE, fields: Optional[List[dict]] = None,
|
||||
timeout: int = 5) -> bool:
|
||||
"""Discord 웹훅으로 embed 메시지 발송.
|
||||
|
||||
Args:
|
||||
webhook_url: Discord webhook URL (https://discord.com/api/webhooks/{ID}/{TOKEN})
|
||||
title, description: embed 본문
|
||||
color: embed 좌측 색상 바 (10진수, 0xRRGGBB)
|
||||
fields: [{"name": "필드명", "value": "값", "inline": True}, ...]
|
||||
timeout: 요청 타임아웃(초)
|
||||
|
||||
Returns:
|
||||
성공 시 True. URL 비었거나 네트워크/4xx/5xx 시 False.
|
||||
"""
|
||||
if not webhook_url or not webhook_url.startswith('https://'):
|
||||
return False
|
||||
|
||||
payload = {
|
||||
"embeds": [{
|
||||
"title": title,
|
||||
"description": description,
|
||||
"color": color,
|
||||
"fields": fields or [],
|
||||
"timestamp": datetime.utcnow().isoformat(),
|
||||
"footer": {"text": "Clock-out Time Calculator"},
|
||||
}]
|
||||
}
|
||||
data = json.dumps(payload, ensure_ascii=False).encode('utf-8')
|
||||
req = urllib.request.Request(
|
||||
webhook_url, data=data,
|
||||
headers={'Content-Type': 'application/json; charset=utf-8'},
|
||||
method='POST',
|
||||
)
|
||||
try:
|
||||
with urllib.request.urlopen(req, timeout=timeout) as resp:
|
||||
return 200 <= resp.status < 300
|
||||
except (urllib.error.URLError, urllib.error.HTTPError, OSError, TimeoutError):
|
||||
return False
|
||||
|
||||
|
||||
def send_test(webhook_url: str) -> bool:
|
||||
"""온보딩/설정에서 호출하는 테스트 메시지."""
|
||||
return send(
|
||||
webhook_url,
|
||||
title="🔔 테스트 알림",
|
||||
description="Clock-out Time Calculator의 Discord 연동이 정상 작동합니다.",
|
||||
color=COLOR_GREEN,
|
||||
)
|
||||
|
||||
|
||||
def send_clock_in(webhook_url: str, time_str: str) -> bool:
|
||||
return send(
|
||||
webhook_url,
|
||||
title="🟢 출근",
|
||||
description=f"오늘 {time_str}에 출근 기록되었습니다.",
|
||||
color=COLOR_GREEN,
|
||||
)
|
||||
|
||||
|
||||
def send_clock_out(webhook_url: str, time_str: str, total_hours: float,
|
||||
overtime_minutes: int, overtime_earned: int) -> bool:
|
||||
fields = [
|
||||
{"name": "총 근무시간", "value": f"{total_hours:.1f}시간", "inline": True},
|
||||
]
|
||||
if overtime_minutes > 0:
|
||||
fields.append({
|
||||
"name": "연장근무",
|
||||
"value": f"{overtime_minutes}분 → {overtime_earned}분 적립",
|
||||
"inline": True,
|
||||
})
|
||||
color = COLOR_YELLOW if overtime_minutes > 0 else COLOR_BLUE
|
||||
return send(
|
||||
webhook_url,
|
||||
title="✅ 퇴근",
|
||||
description=f"오늘 {time_str}에 퇴근 처리되었습니다.",
|
||||
color=color,
|
||||
fields=fields,
|
||||
)
|
||||
|
||||
|
||||
def send_health_warning(webhook_url: str, hours_continuous: float) -> bool:
|
||||
return send(
|
||||
webhook_url,
|
||||
title="🌿 휴식 권고",
|
||||
description=f"{hours_continuous:.1f}시간 연속 근무 중입니다.\n잠시 일어나서 스트레칭하세요.",
|
||||
color=COLOR_PINK,
|
||||
)
|
||||
Reference in New Issue
Block a user