1+ #!/usr/bin/env python3
2+ """
3+ Phone Number Manager with GUI
4+ Handles phone number collection, message input, and macOS integration
5+ """
6+
7+ import tkinter as tk
8+ from tkinter import ttk , messagebox , simpledialog
9+ import random
10+ import subprocess
11+ import sys
12+ import os
13+
14+
15+ class PhoneNumberManager :
16+ def __init__ (self ):
17+ self .phone_numbers = []
18+ self .message_text = ""
19+ self .root = tk .Tk ()
20+ self .setup_main_window ()
21+
22+ def setup_main_window (self ):
23+ """Setup the main phone number input window"""
24+ self .root .title ("Phone Number Manager" )
25+ self .root .geometry ("450x400" )
26+ self .root .configure (bg = '#f0f0f0' )
27+
28+ # Center the window
29+ self .center_window (450 , 400 )
30+
31+ # Title label
32+ title_label = tk .Label (
33+ self .root ,
34+ text = "Phone Number Manager" ,
35+ font = ('Arial' , 16 , 'bold' ),
36+ bg = '#f0f0f0'
37+ )
38+ title_label .pack (pady = 10 )
39+
40+ # Phone number input frame
41+ input_frame = tk .Frame (self .root , bg = '#f0f0f0' )
42+ input_frame .pack (pady = 10 )
43+
44+ tk .Label (input_frame , text = "Phone Number:" , bg = '#f0f0f0' ).pack (anchor = 'w' )
45+ self .phone_entry = tk .Entry (input_frame , width = 30 , font = ('Arial' , 12 ))
46+ self .phone_entry .pack (pady = 5 )
47+ self .phone_entry .focus ()
48+
49+ # Phone numbers list
50+ list_frame = tk .Frame (self .root , bg = '#f0f0f0' )
51+ list_frame .pack (pady = 10 , fill = 'both' , expand = True )
52+
53+ tk .Label (list_frame , text = "Added Phone Numbers:" , bg = '#f0f0f0' ).pack (anchor = 'w' )
54+
55+ # Listbox with scrollbar
56+ listbox_frame = tk .Frame (list_frame )
57+ listbox_frame .pack (fill = 'both' , expand = True , padx = 10 )
58+
59+ scrollbar = tk .Scrollbar (listbox_frame )
60+ scrollbar .pack (side = 'right' , fill = 'y' )
61+
62+ self .phone_listbox = tk .Listbox (
63+ listbox_frame ,
64+ yscrollcommand = scrollbar .set ,
65+ font = ('Arial' , 10 )
66+ )
67+ self .phone_listbox .pack (side = 'left' , fill = 'both' , expand = True )
68+ scrollbar .config (command = self .phone_listbox .yview )
69+
70+ # Buttons frame
71+ button_frame = tk .Frame (self .root , bg = '#f0f0f0' )
72+ button_frame .pack (pady = 20 )
73+
74+ # Buttons
75+ add_btn = tk .Button (
76+ button_frame ,
77+ text = "Add Another Phone Number" ,
78+ command = self .add_phone_number ,
79+ bg = '#4CAF50' ,
80+ fg = 'black' ,
81+ font = ('Arial' , 10 , 'bold' ),
82+ padx = 10 ,
83+ relief = 'raised' ,
84+ bd = 2
85+ )
86+ add_btn .pack (side = 'left' , padx = 5 )
87+
88+ ok_btn = tk .Button (
89+ button_frame ,
90+ text = "OK" ,
91+ command = self .proceed_to_message ,
92+ bg = '#4CAF50' ,
93+ fg = 'black' ,
94+ font = ('Arial' , 10 , 'bold' ),
95+ padx = 20 ,
96+ relief = 'raised' ,
97+ bd = 2
98+ )
99+ ok_btn .pack (side = 'left' , padx = 5 )
100+
101+ cancel_btn = tk .Button (
102+ button_frame ,
103+ text = "Cancel" ,
104+ command = self .cancel_operation ,
105+ bg = '#4CAF50' ,
106+ fg = 'black' ,
107+ font = ('Arial' , 10 , 'bold' ),
108+ padx = 15 ,
109+ relief = 'raised' ,
110+ bd = 2
111+ )
112+ cancel_btn .pack (side = 'left' , padx = 5 )
113+
114+ # Bind Enter key to add phone number
115+ self .phone_entry .bind ('<Return>' , lambda e : self .add_phone_number ())
116+
117+ def center_window (self , width , height ):
118+ """Center window on screen"""
119+ screen_width = self .root .winfo_screenwidth ()
120+ screen_height = self .root .winfo_screenheight ()
121+ x = (screen_width - width ) // 2
122+ y = (screen_height - height ) // 2
123+ self .root .geometry (f"{ width } x{ height } +{ x } +{ y } " )
124+
125+ def add_phone_number (self ):
126+ """Add phone number to the list"""
127+ phone = self .phone_entry .get ().strip ()
128+ if phone :
129+ if phone not in self .phone_numbers :
130+ self .phone_numbers .append (phone )
131+ self .phone_listbox .insert (tk .END , phone )
132+ self .phone_entry .delete (0 , tk .END )
133+ print (f"Added phone number: { phone } " )
134+ else :
135+ messagebox .showwarning ("Duplicate" , "This phone number is already in the list!" )
136+ else :
137+ messagebox .showwarning ("Empty Field" , "Please enter a phone number!" )
138+
139+ def proceed_to_message (self ):
140+ """Proceed to message input if we have phone numbers"""
141+ if not self .phone_numbers :
142+ messagebox .showwarning ("No Phone Numbers" , "Please add at least one phone number!" )
143+ return
144+
145+ # Add current entry if it exists
146+ current_phone = self .phone_entry .get ().strip ()
147+ if current_phone and current_phone not in self .phone_numbers :
148+ self .phone_numbers .append (current_phone )
149+
150+ print (f"Proceeding with { len (self .phone_numbers )} phone numbers: { self .phone_numbers } " )
151+ self .show_message_window ()
152+
153+ def cancel_operation (self ):
154+ """Cancel and exit"""
155+ self .root .destroy ()
156+
157+ def show_message_window (self ):
158+ """Show the message input window"""
159+ self .root .withdraw () # Hide main window
160+
161+ # Create message window
162+ self .message_window = tk .Toplevel ()
163+ self .message_window .title ("Enter Message" )
164+ self .message_window .geometry ("450x300" )
165+ self .message_window .configure (bg = '#f0f0f0' )
166+ self .center_message_window (450 , 300 )
167+
168+ # Title
169+ title_label = tk .Label (
170+ self .message_window ,
171+ text = "Enter Your Message" ,
172+ font = ('Arial' , 16 , 'bold' ),
173+ bg = '#f0f0f0'
174+ )
175+ title_label .pack (pady = 10 )
176+
177+ # Message input
178+ tk .Label (self .message_window , text = "Message:" , bg = '#f0f0f0' ).pack (anchor = 'w' , padx = 20 )
179+
180+ # Text area with scrollbar
181+ text_frame = tk .Frame (self .message_window )
182+ text_frame .pack (pady = 10 , padx = 20 , fill = 'both' , expand = True )
183+
184+ text_scrollbar = tk .Scrollbar (text_frame )
185+ text_scrollbar .pack (side = 'right' , fill = 'y' )
186+
187+ self .message_text_widget = tk .Text (
188+ text_frame ,
189+ height = 8 ,
190+ font = ('Arial' , 12 ),
191+ yscrollcommand = text_scrollbar .set ,
192+ wrap = 'word'
193+ )
194+ self .message_text_widget .pack (side = 'left' , fill = 'both' , expand = True )
195+ text_scrollbar .config (command = self .message_text_widget .yview )
196+
197+ self .message_text_widget .focus ()
198+
199+ # Buttons
200+ button_frame = tk .Frame (self .message_window , bg = '#f0f0f0' )
201+ button_frame .pack (pady = 20 )
202+
203+ ok_btn = tk .Button (
204+ button_frame ,
205+ text = "OK" ,
206+ command = self .send_message ,
207+ bg = '#4CAF50' ,
208+ fg = 'black' ,
209+ font = ('Arial' , 10 , 'bold' ),
210+ padx = 30 ,
211+ relief = 'raised' ,
212+ bd = 2
213+ )
214+ ok_btn .pack (side = 'left' , padx = 10 )
215+
216+ cancel_btn = tk .Button (
217+ button_frame ,
218+ text = "Cancel" ,
219+ command = self .cancel_message ,
220+ bg = '#4CAF50' ,
221+ fg = 'black' ,
222+ font = ('Arial' , 10 , 'bold' ),
223+ padx = 20 ,
224+ relief = 'raised' ,
225+ bd = 2
226+ )
227+ cancel_btn .pack (side = 'left' , padx = 10 )
228+
229+ # Handle window close
230+ self .message_window .protocol ("WM_DELETE_WINDOW" , self .cancel_message )
231+
232+ def center_message_window (self , width , height ):
233+ """Center message window on screen"""
234+ screen_width = self .message_window .winfo_screenwidth ()
235+ screen_height = self .message_window .winfo_screenheight ()
236+ x = (screen_width - width ) // 2
237+ y = (screen_height - height ) // 2
238+ self .message_window .geometry (f"{ width } x{ height } +{ x } +{ y } " )
239+
240+ def send_message (self ):
241+ """Process the message and integrate with macOS"""
242+ self .message_text = self .message_text_widget .get ("1.0" , tk .END ).strip ()
243+
244+ if not self .message_text :
245+ messagebox .showwarning ("Empty Message" , "Please enter a message!" )
246+ return
247+
248+ # Select random phone number (but don't show it to user)
249+ selected_phone = random .choice (self .phone_numbers )
250+ print (f"Selected phone number: { selected_phone } " )
251+ print (f"Message: { self .message_text } " )
252+
253+ # Show confirmation without revealing the phone number
254+ result = messagebox .askyesno (
255+ "Send Message" ,
256+ f"Ready to send your message!\n \n "
257+ f"This will add a new contact and send your message.\n \n "
258+ f"Continue?"
259+ )
260+
261+ if result :
262+ try :
263+ self .add_contact_to_system (selected_phone )
264+ self .send_imessage (selected_phone , self .message_text )
265+ messagebox .showinfo ("Success" , "Contact added and SMS sent!" )
266+ except Exception as e :
267+ messagebox .showerror ("Error" , f"An error occurred: { str (e )} " )
268+
269+ self .cleanup ()
270+
271+ def add_contact_to_system (self , phone_number ):
272+ """Add contact to macOS Contacts app using AppleScript"""
273+ applescript = f'''
274+ tell application "Contacts"
275+ set newContact to make new person
276+ set first name of newContact to "Test"
277+ make new phone at end of phones of newContact with properties {{label:"mobile", value:"{ phone_number } "}}
278+ save
279+ end tell
280+ '''
281+
282+ try :
283+ subprocess .run (['osascript' , '-e' , applescript ], check = True )
284+ print (f"Added contact 'Test' with phone number: { phone_number } " )
285+ except subprocess .CalledProcessError as e :
286+ print (f"Error adding contact: { e } " )
287+ raise Exception ("Failed to add contact. Make sure Contacts app is accessible." )
288+
289+ def send_imessage (self , phone_number , message ):
290+ """Send SMS text message using AppleScript"""
291+ # Clean the phone number and message for AppleScript
292+ clean_phone = phone_number .replace ('"' , '\\ "' )
293+ clean_message = message .replace ('"' , '\\ "' ).replace ('\n ' , '\\ n' )
294+
295+ applescript = f'''
296+ tell application "Messages"
297+ set targetService to 1st account whose service type = SMS
298+ set targetBuddy to participant "{ clean_phone } " of targetService
299+ send "{ clean_message } " to targetBuddy
300+ end tell
301+ '''
302+
303+ try :
304+ subprocess .run (['osascript' , '-e' , applescript ], check = True )
305+ print (f"Sent SMS to { phone_number } : { message } " )
306+ except subprocess .CalledProcessError as e :
307+ print (f"Error sending SMS: { e } " )
308+ raise Exception ("Failed to send SMS. Make sure Messages app is accessible and SMS is set up." )
309+
310+ def cancel_message (self ):
311+ """Cancel message input and return to main window"""
312+ self .message_window .destroy ()
313+ self .root .deiconify () # Show main window again
314+
315+ def cleanup (self ):
316+ """Clean up and exit"""
317+ if hasattr (self , 'message_window' ):
318+ self .message_window .destroy ()
319+ self .root .destroy ()
320+
321+ def run (self ):
322+ """Start the application"""
323+ self .root .mainloop ()
324+
325+
326+ def main ():
327+ """Main function to run the application"""
328+ print ("Starting Phone Number Manager..." )
329+ print ("Note: This app requires macOS and proper permissions for Contacts and Messages." )
330+
331+ app = PhoneNumberManager ()
332+ app .run ()
333+
334+
335+ if __name__ == "__main__" :
336+ main ()
0 commit comments