def modify_existing_contact(old_contact): # create temp file and open it with the specified text editor tf = tempfile.NamedTemporaryFile(mode='w+t', delete=False) temp_file_name = tf.name tf.write("# Edit contact: %s\n%s" \ % (old_contact.get_full_name(), helpers.get_existing_contact_template(old_contact))) tf.close() # start editor to edit contact template child = subprocess.Popen([Config().get_editor(), temp_file_name]) streamdata = child.communicate()[0] # read temp file contents after editing tf = open(temp_file_name, "r") new_contact = CarddavObject.from_existing_contact_with_new_user_input( old_contact, tf.read()) tf.close() os.remove(temp_file_name) # check if the user changed anything if old_contact == new_contact: print("Nothing changed.") else: new_contact.write_to_file(overwrite=True) print("Modification successful\n\n%s" % new_contact.print_vcard())
def merge_existing_contacts(source_contact, target_contact, delete_source_contact): # create temp files for each vcard # source vcard source_tf = tempfile.NamedTemporaryFile(mode='w+t', delete=False) source_temp_file_name = source_tf.name source_tf.write("# merge from %s\n%s" \ % (source_contact.get_full_name(), helpers.get_existing_contact_template(source_contact))) source_tf.close() # target vcard target_tf = tempfile.NamedTemporaryFile(mode='w+t', delete=False) target_temp_file_name = target_tf.name target_tf.write("# merge into %s\n%s" \ % (target_contact.get_full_name(), helpers.get_existing_contact_template(target_contact))) target_tf.close() # start editor to edit contact template child = subprocess.Popen([Config().get_merge_editor(), source_temp_file_name, target_temp_file_name]) streamdata = child.communicate()[0] # template of source vcard is not required anymore os.remove(source_temp_file_name) # instead we are interested in the target template contents target_tf = open(target_temp_file_name, "r") merged_contact = CarddavObject.from_existing_contact_with_new_user_input(target_contact, target_tf.read()) target_tf.close() os.remove(target_temp_file_name) # compare them if target_contact == merged_contact: print("Merge unsuccessfull: Target contact was not modified") return while True: if delete_source_contact: input_string = raw_input( "Merge contact %s from address book %s into contact %s from address book %s\n\n" \ "To be removed\n\n%s\n\nMerged\n\n%s\n\nAre you sure? (y/n): " \ % (source_contact.get_full_name(), source_contact.get_address_book().get_name(), merged_contact.get_full_name(), merged_contact.get_address_book().get_name(), source_contact.print_vcard(), merged_contact.print_vcard())) else: input_string = raw_input( "Merge contact %s from address book %s into contact %s from address book %s\n\n" \ "Keep unchanged\n\n%s\n\nMerged:\n\n%s\n\nAre you sure? (y/n): " \ % (source_contact.get_full_name(), source_contact.get_address_book().get_name(), merged_contact.get_full_name(), merged_contact.get_address_book().get_name(), source_contact.print_vcard(), merged_contact.print_vcard())) if input_string.lower() in ["", "n", "q"]: print("Canceled") return if input_string.lower() == "y": break # save merged_contact to disk and delete source contact merged_contact.write_to_file(overwrite=True) if delete_source_contact: source_contact.delete_vcard_file() print("Merge successful\n\n%s" % merged_contact.print_vcard())
def modify_existing_contact(old_contact): # create temp file and open it with the specified text editor tf = tempfile.NamedTemporaryFile(mode='w+t', delete=False) temp_file_name = tf.name tf.write("# Edit contact: %s\n%s" \ % (old_contact.get_full_name(), old_contact.get_template())) tf.close() temp_file_creation = helpers.file_modification_date(temp_file_name) while True: # start editor to edit contact template child = subprocess.Popen([Config().get_editor(), temp_file_name]) streamdata = child.communicate()[0] if temp_file_creation == helpers.file_modification_date(temp_file_name): print "not modified" new_contact = None os.remove(temp_file_name) break # read temp file contents after editing tf = open(temp_file_name, "r") new_contact_template = tf.read() tf.close() # try to create contact from user input try: new_contact = CarddavObject.from_existing_contact_with_new_user_input( old_contact, new_contact_template) except ValueError as e: print("\n%s\n" % e) while True: input_string = raw_input("Do you want to open the editor again (y/n)? ") if input_string.lower() in ["", "n", "q"]: print("Canceled") os.remove(temp_file_name) sys.exit(0) if input_string.lower() == "y": break else: os.remove(temp_file_name) break # check if the user changed anything if new_contact is None \ or old_contact == new_contact: print("Nothing changed\n\n%s" % old_contact.print_vcard()) else: new_contact.write_to_file(overwrite=True) print("Modification successful\n\n%s" % new_contact.print_vcard())
def modify_existing_contact(old_contact): # create temp file and open it with the specified text editor tf = tempfile.NamedTemporaryFile(mode='w+t', delete=False) temp_file_name = tf.name tf.write("# Edit contact: %s\n%s" \ % (old_contact.get_full_name(), helpers.get_existing_contact_template(old_contact))) tf.close() # start editor to edit contact template child = subprocess.Popen([Config().get_editor(), temp_file_name]) streamdata = child.communicate()[0] # read temp file contents after editing tf = open(temp_file_name, "r") new_contact = CarddavObject.from_existing_contact_with_new_user_input(old_contact, tf.read()) tf.close() os.remove(temp_file_name) # check if the user changed anything if old_contact == new_contact: print("Nothing changed.") else: new_contact.write_to_file(overwrite=True) print("Modification successful\n\n%s" % new_contact.print_vcard())
def merge_existing_contacts(source_contact, target_contact, delete_source_contact): # create temp files for each vcard # source vcard source_tf = tempfile.NamedTemporaryFile(mode='w+t', delete=False) source_temp_file_name = source_tf.name source_tf.write("# merge from %s\n%s" \ % (source_contact.get_full_name(), helpers.get_existing_contact_template(source_contact))) source_tf.close() # target vcard target_tf = tempfile.NamedTemporaryFile(mode='w+t', delete=False) target_temp_file_name = target_tf.name target_tf.write("# merge into %s\n%s" \ % (target_contact.get_full_name(), helpers.get_existing_contact_template(target_contact))) target_tf.close() # start editor to edit contact template child = subprocess.Popen([ Config().get_merge_editor(), source_temp_file_name, target_temp_file_name ]) streamdata = child.communicate()[0] # template of source vcard is not required anymore os.remove(source_temp_file_name) # instead we are interested in the target template contents target_tf = open(target_temp_file_name, "r") merged_contact = CarddavObject.from_existing_contact_with_new_user_input( target_contact, target_tf.read()) target_tf.close() os.remove(target_temp_file_name) # compare them if target_contact == merged_contact: print("Merge unsuccessfull: Target contact was not modified") return while True: if delete_source_contact: input_string = raw_input( "Merge contact %s from address book %s into contact %s from address book %s\n\n" \ "To be removed\n\n%s\n\nMerged\n\n%s\n\nAre you sure? (y/n): " \ % (source_contact.get_full_name(), source_contact.get_address_book().get_name(), merged_contact.get_full_name(), merged_contact.get_address_book().get_name(), source_contact.print_vcard(), merged_contact.print_vcard())) else: input_string = raw_input( "Merge contact %s from address book %s into contact %s from address book %s\n\n" \ "Keep unchanged\n\n%s\n\nMerged:\n\n%s\n\nAre you sure? (y/n): " \ % (source_contact.get_full_name(), source_contact.get_address_book().get_name(), merged_contact.get_full_name(), merged_contact.get_address_book().get_name(), source_contact.print_vcard(), merged_contact.print_vcard())) if input_string.lower() in ["", "n", "q"]: print("Canceled") return if input_string.lower() == "y": break # save merged_contact to disk and delete source contact merged_contact.write_to_file(overwrite=True) if delete_source_contact: source_contact.delete_vcard_file() print("Merge successful\n\n%s" % merged_contact.print_vcard())
def main(): # create the args parser parser = argparse.ArgumentParser( description = "Khard is a carddav address book for the console", formatter_class = argparse.RawTextHelpFormatter) parser.add_argument("-a", "--addressbook", default="", help="Specify address book names as comma separated list") parser.add_argument("--open-editor", action="store_true", help="Open the default text editor after successful creation of new contact from stdin or template file") parser.add_argument("-r", "--reverse", action="store_true", help="Sort contacts in reverse order") parser.add_argument("-s", "--search", default="", help="Search in all contact data fields\n" \ " default: -s \"contact\"\n" \ " merge: -s \"source contact,target contact\"\n" \ " copy/move: -s \"source contact,target address book\"") parser.add_argument("--sort", default="alphabetical", help="Sort contacts list. Possible values: alphabetical, addressbook") parser.add_argument("-t", "--template-file", default="", help="Specify template file name\n" \ " new: khard -a addr_name -t input.yaml\n" \ " modify: khard -s anything -t input.yaml\n" \ " export: khard -s anything -t export.yaml") parser.add_argument("-u", "--uid", default="", help="Select contact by uid") parser.add_argument("-v", "--version", action="store_true", help="Get current program version") parser.add_argument("action", nargs="?", default="", help="Possible actions:\n" \ " list, details, export, email, phone, source,\n" \ " new, add-email, merge, modify, copy, move and remove") args = parser.parse_args() # version if args.version == True: print("Khard version %s" % khard_version) sys.exit(0) # validate value for action if args.action == "": args.action = Config().get_default_action() if args.action not in Config().get_list_of_actions(): print("Unsupported action. Possible values are: %s" % ', '.join(Config().get_list_of_actions())) sys.exit(1) # load address books which are defined in the configuration file selected_address_books = [] if args.addressbook == "": selected_address_books = Config().get_all_address_books() else: for name in args.addressbook.split(","): if Config().get_address_book(name) is None: print("Error: The entered address book \"%s\" does not exist.\nPossible values are: %s" \ % (name, ', '.join([ str(book) for book in Config().get_all_address_books() ]))) sys.exit(1) else: selected_address_books.append(Config().get_address_book(name)) # search parameter # may either contain one search term for a standard search or two terms, devided by a "," to # search for two contacts to merge them search_terms = args.search.split(",") if len(search_terms) == 1: search_terms.append("") # sort criteria if args.sort not in ["alphabetical", "addressbook"]: print("Unsupported sort criteria. Possible values: alphabetical, addressbook") sys.exit(1) # create a list of all found vcard objects if args.uid: contact = Config().get_contact_by_uid(args.uid) if contact: vcard_list = [contact] else: print("Found no contact for uid %s" % args.uid) sys.exit(1) else: vcard_list = get_contact_list_by_user_selection( selected_address_books, args.sort, args.reverse, search_terms[0], False) # read from template file or stdin if available input_from_stdin_or_file = "" if args.action in ["add-email", "new", "modify"]: if args.template_file: with open(args.template_file, "r") as f: input_from_stdin_or_file = f.read() elif not sys.stdin.isatty(): input_from_stdin_or_file = sys.stdin.read() sys.stdin = open('/dev/tty') # create new contact if args.action == "new": if len(selected_address_books) == 1: selected_address_book = selected_address_books[0] else: if args.addressbook == "": # ask for address book print("Create new contact\nEnter address book name") for book in Config().get_all_address_books(): print(" %s" % book.get_name()) while True: input_string = raw_input("Address book: ") if input_string == "": print("Canceled") sys.exit(0) if Config().get_address_book(input_string) in Config().get_all_address_books(): selected_address_book = Config().get_address_book(input_string) print("") break else: print("Please enter only a single address book name") sys.exit(1) # if there is some data in stdin if input_from_stdin_or_file: # create new contact from stdin try: new_contact = CarddavObject.from_user_input( selected_address_book, input_from_stdin_or_file) except ValueError as e: print(e) sys.exit(1) else: new_contact.write_to_file() if args.open_editor: modify_existing_contact(new_contact) else: print("Creation successful\n\n%s" % new_contact.print_vcard()) else: create_new_contact(selected_address_book) # add email address to contact or create a new one if necessary if args.action == "add-email": # get name and email address email_address = "" name = "" for line in input_from_stdin_or_file.splitlines(): if line.startswith("From:"): try: name = line[6:line.index("<")-1] email_address = line[line.index("<")+1:line.index(">")] except ValueError as e: email_address = line[6:].strip() break print("Khard: Add email address to contact") if not email_address: print("Found no email address") sys.exit(1) print("Email address: %s" % email_address) if not name: name = raw_input("Contact's name: ") else: # remove quotes from name string, otherwise decoding fails name = name.replace("\"", "") # fix encoding of senders name name, encoding = decode_header(name)[0] if encoding: name = name.decode(encoding).encode("utf-8").replace("\"", "") # query user input. user_input = raw_input("Contact's name [%s]: " % name) # if empty, use the extracted name from above name = user_input or name # search for an existing contact selected_vcard = choose_vcard_from_list( get_contact_list_by_user_selection( selected_address_books, args.sort, args.reverse, name, True)) if selected_vcard is None: # create new contact while True: input_string = raw_input("Contact %s does not exist. Do you want to create it (y/n)? " % name) if input_string.lower() in ["", "n", "q"]: print("Canceled") sys.exit(0) if input_string.lower() == "y": break # ask for address book, in which to create the new contact print("Available address books: %s" \ % ', '.join([ str(book) for book in Config().get_all_address_books() ])) while True: book_name = raw_input("Address book [%s]: " % selected_address_books[0].get_name()) \ or selected_address_books[0].get_name() if Config().get_address_book(book_name) is not None: break # ask for name and organisation of new contact while True: first_name = raw_input("First name: ") last_name = raw_input("Last name: ") organisation = raw_input("Organisation: ") if not first_name and not last_name and not organisation: print("Error: All fields are empty.") else: break selected_vcard = CarddavObject.from_user_input( Config().get_address_book(book_name), "First name : %s\nLast name : %s\nOrganisation : %s" % (first_name, last_name, organisation)) # check if the contact already contains the email address for type, email_list in sorted(selected_vcard.get_email_addresses().items(), key=lambda k: k[0].lower()): for email in email_list: if email == email_address: print("The contact %s already contains the email address %s" % (selected_vcard, email_address)) sys.exit(0) # ask for confirmation again while True: input_string = raw_input("Do you want to add the email address %s to the contact %s (y/n)? " \ % (email_address, selected_vcard.get_full_name())) if input_string.lower() in ["", "n", "q"]: print("Canceled") sys.exit(0) if input_string.lower() == "y": break # ask for the email label print("\nAdding email address %s to contact %s\n" \ "Enter email label\n" \ " At least one of: home, internet, pref, uri, work, x400\n" \ " Or a custom label (only letters" % (email_address, selected_vcard)) while True: label = raw_input("email label [internet]: ") or "internet" try: selected_vcard.add_email_address(label, email_address) except ValueError as e: print(e) else: break # save to disk selected_vcard.write_to_file(overwrite=True) print("Done.\n\n%s" % selected_vcard.print_vcard()) # print phone application friendly contacts table if args.action == "phone": all_phone_numbers_list = [] matching_phone_number_list = [] regexp = re.compile(search_terms[0].replace("*", ".*").replace(" ", ".*"), re.IGNORECASE) for vcard in vcard_list: for type, number_list in sorted(vcard.get_phone_numbers().items(), key=lambda k: k[0].lower()): for number in sorted(number_list): phone_number_line = "%s\t%s\t%s" % (number, vcard.get_full_name(), type) if len(re.sub("\D", "", search_terms[0])) >= 3: # the user likely searches for a phone number cause the search string contains # at least three digits # so we remove all non-digit chars from the phone number field and match against that if regexp.search(re.sub("\D", "", number)) != None: matching_phone_number_list.append(phone_number_line) else: # the user doesn't search for a phone number so we can perform a standard search # without removing all non-digit chars from the phone number string if regexp.search(phone_number_line) != None: matching_phone_number_list.append(phone_number_line) # collect all phone numbers in a different list as fallback all_phone_numbers_list.append(phone_number_line) if len(matching_phone_number_list) > 0: print('\n'.join(matching_phone_number_list)) elif len(all_phone_numbers_list) > 0: print('\n'.join(all_phone_numbers_list)) else: sys.exit(1) # print mail client friendly contacts table # compatible to mutt and alot # output format: # single line of text # email_address\tname\ttype # email_address\tname\ttype # [...] if args.action == "email": matching_email_address_list = [] all_email_address_list = [] regexp = re.compile(search_terms[0].replace("*", ".*").replace(" ", ".*"), re.IGNORECASE) for vcard in vcard_list: for type, email_list in sorted(vcard.get_email_addresses().items(), key=lambda k: k[0].lower()): for email in sorted(email_list): email_address_line = "%s\t%s\t%s" \ % (email, vcard.get_full_name(), type) if regexp.search(email_address_line) != None: matching_email_address_list.append(email_address_line) # collect all email addresses in a different list as fallback all_email_address_list.append(email_address_line) print("searching for '%s' ..." % search_terms[0]) if len(matching_email_address_list) > 0: print('\n'.join(matching_email_address_list)) elif len(all_email_address_list) > 0: print('\n'.join(all_email_address_list)) else: sys.exit(1) # print user friendly contacts table if args.action == "list": if len(vcard_list) == 0: print("Found no contacts") sys.exit(1) list_contacts(vcard_list) # show source or details, modify or remove contact if args.action in ["details", "modify", "remove", "source", "export"]: selected_vcard = choose_vcard_from_list(vcard_list) if selected_vcard is None: print("Found no contact") sys.exit(1) if args.action == "details": print selected_vcard.print_vcard() elif args.action == "export": if args.template_file: with open(args.template_file, "w") as f: f.write(selected_vcard.get_template()) else: print selected_vcard.get_template() elif args.action == "modify": # if there is some data in stdin if input_from_stdin_or_file: # create new contact from stdin try: new_contact = CarddavObject.from_existing_contact_with_new_user_input( selected_vcard, input_from_stdin_or_file) except ValueError as e: print(e) sys.exit(1) if selected_vcard == new_contact: print("Nothing changed\n\n%s" % new_contact.print_vcard()) else: print("Modification\n\n%s\n" % new_contact.print_vcard()) while True: input_string = raw_input("Do you want to proceed (y/n)? ") if input_string.lower() in ["", "n", "q"]: print("Canceled") break if input_string.lower() == "y": new_contact.write_to_file(overwrite=True) print("Done") break else: modify_existing_contact(selected_vcard) elif args.action == "remove": while True: input_string = raw_input("Deleting contact %s from address book %s. Are you sure? (y/n): " \ % (selected_vcard.get_full_name(), selected_vcard.get_address_book().get_name())) if input_string.lower() in ["", "n", "q"]: print("Canceled") sys.exit(0) if input_string.lower() == "y": break selected_vcard.delete_vcard_file() print("Contact deleted successfully") elif args.action == "source": child = subprocess.Popen([Config().get_editor(), selected_vcard.get_filename()]) streamdata = child.communicate()[0] # merge contacts if args.action == "merge": # get the source vcard, from which to merge source_vcard = choose_vcard_from_list(vcard_list) if source_vcard is None: print("Found no source contact for merging") sys.exit(1) # get the target vcard, into which to merge print("Merge from %s from address book %s\n\nNow choose the contact into which to merge:" \ % (source_vcard.get_full_name(), source_vcard.get_address_book().get_name())) target_vcard = choose_vcard_from_list( get_contact_list_by_user_selection( selected_address_books, args.sort, args.reverse, search_terms[1], False)) if target_vcard is None: print("Found no target contact for merging") sys.exit(1) # merging if source_vcard == target_vcard: print("The selected contacts are already identical") else: merge_existing_contacts(source_vcard, target_vcard, True) # copy or move contact if args.action in ["copy", "move"]: # get the source vcard, which to copy or move source_vcard = choose_vcard_from_list(vcard_list) if source_vcard is None: print("Found no contact") sys.exit(1) # get target address book from search query if provided available_address_books = [ book for book in Config().get_all_address_books() if book != source_vcard.get_address_book() ] target_address_book = None if search_terms[1] != "": target_address_book = Config().get_address_book(search_terms[1]) if target_address_book == None: print("The given target address book %s does not exist\n" % search_terms[1]) elif target_address_book not in available_address_books: print("The contact %s is already in the address book %s" \ % (source_vcard.get_full_name(), target_address_book.get_name())) sys.exit(1) # otherwise query the target address book name from user if target_address_book == None: print("%s contact %s from address book %s\n\nAvailable address books:\n %s" % (args.action.title(), source_vcard.get_full_name(), source_vcard.get_address_book().get_name(), '\n '.join([ str(book) for book in available_address_books ]))) while target_address_book is None: input_string = raw_input("Into address book: ") if input_string == "": print("Canceled") sys.exit(0) if Config().get_address_book(input_string) in available_address_books: print("") target_address_book = Config().get_address_book(input_string) # check if a contact already exists in the target address book target_vcard = choose_vcard_from_list( get_contact_list_by_user_selection( [target_address_book], args.sort, args.reverse, source_vcard.get_full_name(), True)) # if the target contact doesn't exist, move or copy the source contact into the target # address book without further questions print target_address_book print target_vcard if target_vcard is None: copy_contact(source_vcard, target_address_book, args.action == "move") else: if source_vcard == target_vcard: # source and target contact are identical if args.action == "move": copy_contact(source_vcard, target_address_book, True) else: print("The selected contacts are already identical") else: # source and target contacts are different # either overwrite the target one or merge into target contact print("The address book %s already contains the contact %s\n\n" \ "Source\n\n%s\n\nTarget\n\n%s\n\n" \ "Possible actions:\n" \ " a: %s anyway\n m: Merge from source into target contact\n o: Overwrite target contact\n q: Quit" \ % (target_vcard.get_address_book().get_name(), source_vcard.get_full_name(), source_vcard.print_vcard(), target_vcard.print_vcard(), "Move" if args.action == "move" else "Copy")) while True: input_string = raw_input("Your choice: ") if input_string.lower() == "a": copy_contact(source_vcard, target_address_book, args.action == "move") break if input_string.lower() == "o": target_vcard.delete_vcard_file() copy_contact(source_vcard, target_address_book, args.action == "move") break if input_string.lower() == "m": merge_existing_contacts(source_vcard, target_vcard, args.action == "move") break if input_string.lower() in ["", "q"]: print("Canceled") break
def merge_existing_contacts(source_contact, target_contact, delete_source_contact): # create temp files for each vcard # source vcard source_tf = tempfile.NamedTemporaryFile(mode='w+t', delete=False) source_temp_file_name = source_tf.name source_tf.write("# merge from %s\n%s" \ % (source_contact.get_full_name(), source_contact.get_template())) source_tf.close() # target vcard target_tf = tempfile.NamedTemporaryFile(mode='w+t', delete=False) target_temp_file_name = target_tf.name target_tf.write("# merge into %s\n%s" \ % (target_contact.get_full_name(), target_contact.get_template())) target_tf.close() target_temp_file_creation = helpers.file_modification_date(target_temp_file_name) while True: # start editor to edit contact template child = subprocess.Popen([Config().get_merge_editor(), source_temp_file_name, target_temp_file_name]) streamdata = child.communicate()[0] if target_temp_file_creation == helpers.file_modification_date(target_temp_file_name): merged_contact = None os.remove(source_temp_file_name) os.remove(target_temp_file_name) break # load target template contents target_tf = open(target_temp_file_name, "r") merged_contact_template = target_tf.read() target_tf.close() # try to create contact from user input try: merged_contact = CarddavObject.from_existing_contact_with_new_user_input( target_contact, merged_contact_template) except ValueError as e: print("\n%s\n" % e) while True: input_string = raw_input("Do you want to open the editor again (y/n)? ") if input_string.lower() in ["", "n", "q"]: print("Canceled") os.remove(source_temp_file_name) os.remove(target_temp_file_name) sys.exit(0) if input_string.lower() == "y": break else: os.remove(source_temp_file_name) os.remove(target_temp_file_name) break # compare them if merged_contact is None \ or target_contact == merged_contact: print("Target contact unmodified\n\n%s" % target_contact.print_vcard()) sys.exit(0) while True: if delete_source_contact: input_string = raw_input( "Merge contact %s from address book %s into contact %s from address book %s\n\n" \ "To be removed\n\n%s\n\nMerged\n\n%s\n\nAre you sure? (y/n): " \ % (source_contact.get_full_name(), source_contact.get_address_book().get_name(), merged_contact.get_full_name(), merged_contact.get_address_book().get_name(), source_contact.print_vcard(), merged_contact.print_vcard())) else: input_string = raw_input( "Merge contact %s from address book %s into contact %s from address book %s\n\n" \ "Keep unchanged\n\n%s\n\nMerged:\n\n%s\n\nAre you sure? (y/n): " \ % (source_contact.get_full_name(), source_contact.get_address_book().get_name(), merged_contact.get_full_name(), merged_contact.get_address_book().get_name(), source_contact.print_vcard(), merged_contact.print_vcard())) if input_string.lower() in ["", "n", "q"]: print("Canceled") return if input_string.lower() == "y": break # save merged_contact to disk and delete source contact merged_contact.write_to_file(overwrite=True) if delete_source_contact: source_contact.delete_vcard_file() print("Merge successful\n\n%s" % merged_contact.print_vcard())