forked from iam-Jam24/Python-CLI
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathalarm_interactive.py
More file actions
197 lines (162 loc) Β· 6.74 KB
/
Copy pathalarm_interactive.py
File metadata and controls
197 lines (162 loc) Β· 6.74 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
#!/usr/bin/env python3
"""
Interactive alarm reminder with natural language input.
Accepts simple commands like:
- "add alarm for 7 am"
- "remind me at 2:30 pm"
- "alarm at 09:15"
- "notify me in 30 seconds"
- "set alarm for 3 pm with message take medicine"
"""
import re
import datetime
import time
import sys
from scheduler.notifications import notify_alarm_ringing, show_popup_notification
def parse_natural_time(text: str) -> tuple[int, str]:
"""
Parse natural language time input and return (seconds_until, description).
Examples:
"7am" or "7 am" or "at 7am" -> (secs until 7 AM, "7:00 AM")
"2:30pm" or "2:30 pm" -> (secs until 2:30 PM, "2:30 PM")
"in 30 seconds" or "30 seconds" -> (30, "in 30 seconds")
"in 5 minutes" or "5 minutes" -> (300, "in 5 minutes")
"in 2 hours" or "2 hours" -> (7200, "in 2 hours")
"""
text = text.lower().strip()
# Remove leading words like "add alarm", "remind", "set alarm", "alarm"
text = re.sub(r'^(add\s+alarm|remind\s+me|set\s+alarm|alarm|for|at|in)\s+', '', text)
text = text.strip()
# Pattern 1: "X seconds/minutes/hours" (with or without "in")
match = re.search(r'(\d+)\s*(second|minute|hour)s?(?:\s+with)?', text)
if match:
value = int(match.group(1))
unit = match.group(2)
multiplier = {"second": 1, "minute": 60, "hour": 3600}[unit]
seconds = value * multiplier
unit_name = f"{unit}{'s' if value > 1 else ''}"
return seconds, f"in {value} {unit_name}"
# Pattern 2: "HH:MM am/pm" or "H am/pm" (with optional "at")
match = re.search(r'(\d{1,2}):?(\d{2})?\s*(am|pm)', text)
if match:
hour = int(match.group(1))
minute = int(match.group(2)) if match.group(2) else 0
ampm = match.group(3)
# Convert 12-hour to 24-hour format
if ampm == 'pm' and hour != 12:
hour += 12
elif ampm == 'am' and hour == 12:
hour = 0
# Validate hour/minute
if hour > 23 or minute > 59:
raise ValueError(f"Invalid time: {hour}:{minute:02d}")
# Calculate seconds until that time
now = datetime.datetime.now()
target = now.replace(hour=hour, minute=minute, second=0, microsecond=0)
# If time has passed, schedule for tomorrow
if target <= now:
target += datetime.timedelta(days=1)
seconds = int((target - now).total_seconds())
time_str = f"{hour % 12 or 12}:{minute:02d} {'AM' if hour < 12 else 'PM'}"
return seconds, time_str
raise ValueError(f"Could not parse time from: '{text}'")
def extract_message(text: str) -> str:
"""Extract optional message from input (after 'with message' or 'with')."""
# Look for content after "with" keyword
match = re.search(r'with\s+(.+?)$', text, re.IGNORECASE)
if match:
msg = match.group(1).strip()
if msg and len(msg) > 2:
return msg
return "Alarm"
def interactive_mode():
"""Run in interactive mode, accepting user input until 'quit' or 'exit'."""
print("\n" + "="*70)
print("π INTERACTIVE ALARM REMINDER")
print("="*70)
print("\nπ Simple commands to try:")
print(" 7am (set alarm for 7 AM today or tomorrow)")
print(" 2:30pm (set alarm for 2:30 PM)")
print(" 9:15am with meds (alarm at 9:15 AM with reminder text)")
print(" in 30 seconds (set alarm 30 seconds from now)")
print(" 5 minutes (set alarm in 5 minutes)")
print(" 2 hours (set alarm in 2 hours)")
print(" quit or exit (stop the program)")
print("-"*70 + "\n")
while True:
try:
user_input = input("π Enter alarm command: ").strip()
if not user_input:
continue
if user_input.lower() in ('quit', 'exit'):
print("π Goodbye!")
break
# Parse the input
try:
wait_seconds, time_desc = parse_natural_time(user_input)
message = extract_message(user_input)
alarm_name = f"Alarm at {time_desc}"
# Show scheduling message
print(f"\nβ
Scheduled: {alarm_name} | Message: {message}")
print(f" Waiting: {wait_seconds} seconds ({time_desc})")
# Show notification
show_popup_notification(
title="Alarm Scheduled",
message=f"{alarm_name}\n{message}",
timeout=3
)
# Run the alarm
print()
for i in range(wait_seconds, 0, -1):
sys.stdout.write(f"\rβ³ {i:3d}s remaining...")
sys.stdout.flush()
time.sleep(1)
print("\n\nπ¨ ALARM TIME REACHED!")
notify_alarm_ringing(alarm_name, duration=5)
show_popup_notification(
title=f"β° {alarm_name}",
message=message,
timeout=5
)
print(f"β Alarm completed: {alarm_name}\n")
except ValueError as e:
print(f"β Error: {e}")
print(" Try formats like: '7 am', '2:30 pm', 'in 30 seconds'\n")
except KeyboardInterrupt:
print("\n\nπ Alarm interrupted by user. Goodbye!")
break
except Exception as e:
print(f"β οΈ Unexpected error: {e}")
def batch_mode(args):
"""Run a single alarm from command-line arguments."""
command = " ".join(args)
try:
wait_seconds, time_desc = parse_natural_time(command)
message = extract_message(command)
alarm_name = f"Alarm at {time_desc}"
print(f"Scheduled: {alarm_name} | Message: {message}")
print(f"Waiting: {wait_seconds} seconds\n")
for i in range(wait_seconds, 0, -1):
sys.stdout.write(f"\rβ³ {i:3d}s remaining...")
sys.stdout.flush()
time.sleep(1)
print("\n\nπ¨ ALARM TIME REACHED!")
notify_alarm_ringing(alarm_name, duration=5)
show_popup_notification(
title=f"β° {alarm_name}",
message=message,
timeout=5
)
print(f"β Alarm completed\n")
except ValueError as e:
print(f"Error: {e}")
sys.exit(1)
def main():
if len(sys.argv) > 1:
# Batch mode: process command line arguments
batch_mode(sys.argv[1:])
else:
# Interactive mode
interactive_mode()
if __name__ == '__main__':
main()