Share your Python scripts here
Format:
1. What does it do?
2. Format the script like the image below:
Just made this. Very useful for dumbphones etc that can’t install fancy auth apps. Also males it easy to use on different types of devices.
Here’s a simple python script for 2fa. I made it for github but it works for probably most services. (research before.) your devices time must be set correctly for this to work but the device can be fully offline.
basically you just edit at the top of the file to enter your codes, save it and run python filename key (like github google etc. Defaults to github.)
for github you just click can’t scan and it’ll show characters. Other services i don’t know.
feel free to enhance and publish
obviously a good idea to backup this file as it contains the code used for 2fa needed to login to your accounts.
#!/data/data/com.termux/files/usr/bin/python
"""
PUT ALL YOUR SECRET CODES HERE.
replace the placeholders with the code.
you can add more accounts as needed just keep the same syntax.
"""
codes = {
"github": "enter yours here",
"google": "enter here",
}
import hmac, base64, struct, hashlib, time, sys, os
def generate_totp(secret):
try:
# Remove spaces
clean_secret = secret.replace(' ', '').upper()
# Add padding if missing
padding = '=' * (-len(clean_secret) % 8)
key = base64.b32decode(clean_secret + padding)
# Calculate standard TOTP
counter = struct.pack(">Q", int(time.time() / 30))
mac = hmac.new(key, counter, hashlib.sha1).digest()
offset = mac[-1] & 0x0F
binary = struct.unpack(">L", mac[offset:offset+4])[0] & 0x7FFFFFFF
code = str(binary % 1000000).zfill(6)
remaining = 30 - (int(time.time()) % 30)
return code, remaining
except Exception as e:
return None, f"Error: {e}"
if __name__ == "__main__":
account = sys.argv[1].lower() if len(sys.argv) > 1 else "github"
if account not in codes:
print(f"{account} isn't a saved key.\nYour keys are {list(codes.keys())}.\nadd more by editting this script.")
sys.exit(1)
secret = codes[account]
print(f"Generating TOTP for: {account}")
last_code = None
try:
while True:
otp, remaining = generate_totp(secret)
if not otp:
print(remaining) # Print error
break
sys.stdout.write(f"\rCode: \033[1;32m{otp}\033[0m | Expires in: {remaining:02d}s ")
sys.stdout.flush()
last_code = otp
time.sleep(0.5)
except KeyboardInterrupt:
print("\nExited.")
Not mine, found on reddit. Very useful.
QR code generation for text/url (python)
import qrcode
data = input("Enter text or URL: ")
qr = qrcode.QRCode(
version=1,
error_correction=qrcode.constants.ERROR_CORRECT_Q,
box_size=10,
border=4,
)
qr.add_data(data)
qr.make(fit=True)
img = qr.make_image(fill_color="black", back_color="white")
img.save("qrcode.png")
print("Saved as qrcode.png")
Or, with qrcode.js - https://qr.techloq.vip
this script can take 1 or multiple vcf files and remove duplicates. Also works on db files.
Features:
example usage:
python contacts.py -vc -o output.vcf in1.vcf in2.vcf contacts2.db (happens to be the exact use case I used it for
)
that’s basically using all options to show full capability.
-v is for verbose which will display all the duplicates it found.
-c is for adding +1 in the output vcf
-o is self explanatory for specifying an output file.
To clarify the .db is if your phone can’t export vcf files (some have limits -wink TCL-) but you’re rooted, the actual contacts are usually stored in Android at /data/data/com.android.providers.contacts/databases/contacts2.db just copy that file and the script will do the rest.
Code:
#!/usr/bin/env python3
import sqlite3
import os
import sys
import argparse
import re
import quopri
import base64
import binascii
# --- 1. The Schema (Internal Representation) ---
class StandardContact:
def __init__(self):
# Name components
self.name = {
'family': '', 'given': '', 'middle': '', 'prefix': '', 'suffix': '', 'fn': ''
}
# Data fields (Using sets to auto-deduplicate within a single contact)
self.phones = set() # (number, type)
self.emails = set() # (email, type)
self.addresses = set() # (street, city, region, zip, country, type)
self.urls = set()
self.org = {'company': '', 'title': ''}
self.note = ""
self.photo = None # {'data': bytes, 'type': str}
def add_phone(self, number, type_label='VOICE'):
# Normalize number for storage: remove surrounding whitespace
clean_num = number.strip()
if clean_num:
self.phones.add((clean_num, type_label.upper()))
def fingerprint(self):
"""
Generates a strict deduplication signature.
Two contacts are duplicates ONLY if all fields match EXACTLY.
"""
sig = []
# 1. Name (Normalized)
n_parts = [self.name[k].strip() for k in sorted(self.name.keys()) if k != 'fn']
sig.append(f"N:{'|'.join(n_parts)}")
# 2. Numbers
# We strip non-digits for the comparison signature to catch (555) vs 555
p_sigs = []
for num, type_ in self.phones:
digits = re.sub(r'\D', '', num)
# US Rule: If 11 digits and starts with 1, strip it for comparison
if len(digits) == 11 and digits.startswith('1'):
digits = digits[1:]
p_sigs.append(f"{digits}")
sig.append(f"TEL:{','.join(sorted(p_sigs))}")
# 3. Emails
e_sigs = [e[0].strip().lower() for e in self.emails]
sig.append(f"EMAIL:{','.join(sorted(e_sigs))}")
# 4. Org
sig.append(f"ORG:{self.name.get('company','').strip()}|{self.name.get('title','').strip()}")
return "||".join(sig)
def has_data(self):
# Check if contact is not empty
return any(self.name.values()) or self.phones or self.emails or self.note or self.addresses
# --- 2. The Parsers (Extract & Transform) ---
class ContactParser:
def parse_db(self, db_path):
print(f"[*] Parsing DB: {db_path}")
contacts = {} # Map ID -> StandardContact
try:
conn = sqlite3.connect(db_path)
cur = conn.cursor()
# Select relevant columns.
# Note: The meaning of data1-data15 depends on mimetype!
q = """
SELECT data.raw_contact_id, mimetypes.mimetype,
data.data1, data.data2, data.data3, data.data4, data.data5,
data.data6, data.data7, data.data8, data.data9, data.data10, data.data15
FROM data
JOIN mimetypes ON data.mimetype_id = mimetypes._id
JOIN raw_contacts ON data.raw_contact_id = raw_contacts._id
WHERE data.raw_contact_id IS NOT NULL
ORDER BY data.raw_contact_id
"""
cur.execute(q)
for row in cur.fetchall():
cid, mime, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d15 = row
if cid not in contacts:
contacts[cid] = StandardContact()
c = contacts[cid]
if mime == 'vnd.android.cursor.item/name':
# d2=Given, d3=Family, d4=Prefix, d5=Middle, d6=Suffix
c.name['given'] = d2 or ""
c.name['family'] = d3 or ""
c.name['prefix'] = d4 or ""
c.name['middle'] = d5 or ""
c.name['suffix'] = d6 or ""
c.name['fn'] = d1 or "" # data1 is usually formatted name
elif mime == 'vnd.android.cursor.item/phone_v2':
# d1=Number, d2=Type (int)
if d1:
# Map Android type int to string
t_map = {1:'HOME', 2:'CELL', 3:'WORK', 12:'MAIN'}
t_str = t_map.get(d2, 'VOICE')
c.add_phone(d1, t_str)
elif mime == 'vnd.android.cursor.item/email_v2':
if d1: c.emails.add((d1, 'INTERNET'))
elif mime == 'vnd.android.cursor.item/postal-address_v2':
# d4=Street, d7=City, d8=Region, d9=Postcode, d10=Country
if d1: c.addresses.add((d1, 'HOME')) # Simplified address
elif mime == 'vnd.android.cursor.item/postal-address_v2':
# data1=Formatted, data4=Street, data7=City, data8=Region, data9=Postcode, data10=Country
street = d4 or ""
city = d7 or ""
region = d8 or ""
postcode = d9 or ""
country = d10 or ""
# Fallback: If structured fields are empty but data1 exists, put data1 in street
if not (street or city or region or postcode or country) and d1:
street = d1
# Type mapping (1=Home, 2=Work, 3=Other)
addr_type = {1:'HOME', 2:'WORK', 3:'OTHER'}.get(d2, 'HOME')
# Store as tuple: (street, city, region, postcode, country, type)
c.addresses.add((street, city, region, postcode, country, addr_type))
elif mime == 'vnd.android.cursor.item/organization':
c.org['company'] = d1 or ""
c.org['title'] = d4 or ""
elif mime == 'vnd.android.cursor.item/note':
if d1: c.note = d1
elif mime == 'vnd.android.cursor.item/website':
if d1: c.urls.add(d1)
elif mime == 'vnd.android.cursor.item/photo':
# d15 is the BLOB
if d15:
c.photo = {'data': d15, 'type': 'JPEG'}
conn.close()
return list(contacts.values())
except Exception as e:
print(f"Error reading DB: {e}")
return []
def parse_vcf(self, vcf_path):
print(f"[*] Parsing VCF: {vcf_path}")
contacts = []
try:
# Handle encoding
try:
with open(vcf_path, 'r', encoding='utf-8') as f: lines = f.readlines()
except:
with open(vcf_path, 'r', encoding='latin-1') as f: lines = f.readlines()
# Unfold lines
unfolded = []
for line in lines:
if line.startswith(' '):
if unfolded: unfolded[-1] = unfolded[-1].strip() + line[1:]
else:
unfolded.append(line.strip())
current = None
for line in unfolded:
if line.startswith("BEGIN:VCARD"):
current = StandardContact()
elif line.startswith("END:VCARD"):
if current and current.has_data():
contacts.append(current)
current = None
elif current:
# Parse Line: KEY;PARAM=VAL:VALUE
if ':' not in line: continue
key_part, value = line.split(':', 1)
# Handle Quoted-Printable
if "ENCODING=QUOTED-PRINTABLE" in key_part.upper():
try:
value = quopri.decodestring(value).decode('utf-8', errors='replace')
except: pass
# Split Key and Params
key_split = key_part.split(';')
tag = key_split[0].upper()
params = key_split[1:]
if tag == 'N':
# Family;Given;Middle;Prefix;Suffix
parts = value.split(';')
if len(parts) >= 1: current.name['family'] = parts[0]
if len(parts) >= 2: current.name['given'] = parts[1]
if len(parts) >= 3: current.name['middle'] = parts[2]
if len(parts) >= 4: current.name['prefix'] = parts[3]
if len(parts) >= 5: current.name['suffix'] = parts[4]
elif tag == 'FN':
current.name['fn'] = value
elif tag == 'TEL':
# Try to find TYPE
t_type = 'VOICE'
for p in params:
if p.startswith('TYPE='): t_type = p.split('=')[1]
elif p in ['CELL', 'HOME', 'WORK']: t_type = p
current.add_phone(value, t_type)
elif tag == 'EMAIL':
current.emails.add((value, 'INTERNET'))
elif tag == 'ORG':
parts = value.split(';')
current.org['company'] = parts[0]
if len(parts) > 1: current.org['title'] = parts[1]
elif tag == 'NOTE':
current.note = value
elif tag == 'URL':
current.urls.add(value)
elif tag == 'ADR':
# ADR Format: ;;Street;City;Region;Zip;Country
parts = value.split(';')
# Pad with empty strings to avoid index errors
parts += [''] * (7 - len(parts))
street = parts[2].strip()
city = parts[3].strip()
region = parts[4].strip()
zip_code = parts[5].strip()
country = parts[6].strip()
# Find TYPE
a_type = 'HOME'
for p in params:
if p.startswith('TYPE='): a_type = p.split('=')[1].upper()
elif p in ['WORK', 'HOME', 'DOM', 'INTL', 'POSTAL', 'PARCEL']: a_type = p
current.addresses.add((street, city, region, zip_code, country, a_type))
elif tag == 'PHOTO':
# Value is base64 string
try:
# Strip whitespace
b64 = "".join(value.split())
raw = base64.b64decode(b64)
current.photo = {'data': raw, 'type': 'JPEG'}
except: pass
return contacts
except Exception as e:
print(f"Error reading VCF: {e}")
return []
# --- 3. The Writer (Load) ---
class VcfWriter:
def write(self, contacts, out_path, add_us_code=False):
print(f"[*] Writing {len(contacts)} contacts to {out_path}")
try:
with open(out_path, 'w', encoding='utf-8') as f:
for c in contacts:
f.write("BEGIN:VCARD\n")
f.write("VERSION:3.0\n")
# Name (Reconstruct N and FN if missing)
n_str = f"{c.name['family']};{c.name['given']};{c.name['middle']};{c.name['prefix']};{c.name['suffix']}"
f.write(f"N:{n_str}\n")
fn = c.name['fn']
if not fn:
# Build FN from parts
parts = [c.name['prefix'], c.name['given'], c.name['middle'], c.name['family'], c.name['suffix']]
fn = " ".join([p for p in parts if p]).strip()
f.write(f"FN:{fn}\n")
# Phones (Apply Country Code Logic Here)
for num, type_ in c.phones:
final_num = num
if add_us_code:
digits = re.sub(r'\D', '', num)
# 10 digits, no leading 0, original didn't start with +
if len(digits) == 10 and not digits.startswith('0') and not num.strip().startswith('+'):
final_num = f"+1{digits}"
f.write(f"TEL;TYPE={type_}:{final_num}\n")
# Emails
for em, type_ in c.emails:
f.write(f"EMAIL;TYPE={type_}:{em}\n")
# Addresses
for street, city, region, zip_code, country, a_type in c.addresses:
# We escape semicolons in the values just in case
def esc(s): return s.replace(';', '\\;')
adr_val = f";;{esc(street)};{esc(city)};{esc(region)};{esc(zip_code)};{esc(country)}"
f.write(f"ADR;TYPE={a_type}:{adr_val}\n")
# Org
if c.org['company'] or c.org['title']:
f.write(f"ORG:{c.org['company']};{c.org['title']}\n")
# Note
if c.note:
# Escape newlines for VCF
clean_note = c.note.replace('\n', '\\n')
f.write(f"NOTE:{clean_note}\n")
# Urls
for u in c.urls:
f.write(f"URL:{u}\n")
# Photo
if c.photo:
b64 = base64.b64encode(c.photo['data']).decode('utf-8')
f.write(f"PHOTO;ENCODING=b;TYPE={c.photo['type']}:{b64}\n")
f.write("END:VCARD\n")
return True
except Exception as e:
print(f"Error writing: {e}")
return False
# --- 4. Main Controller ---
def main():
parser = argparse.ArgumentParser(description="Contact Tool")
parser.add_argument('inputs', nargs='+', help="Input files (.vcf or .db)")
parser.add_argument('-o', '--output', help="Custom output filename")
parser.add_argument('-c', '--add-us-code', action='store_true', help="Add +1 to 10-digit US numbers")
parser.add_argument('-v', '--verbose', action='store_true', help="Print list of removed duplicates")
args = parser.parse_args()
# Verify inputs first
for f in args.inputs:
if not os.path.exists(f):
print(f"Error: File not found: {f}")
sys.exit(1)
all_contacts = []
contact_parser = ContactParser()
# Extract
for f in args.inputs:
if f.lower().endswith('.db'):
all_contacts.extend(contact_parser.parse_db(f))
else:
all_contacts.extend(contact_parser.parse_vcf(f))
# Deduplicate
unique_contacts = []
seen_hashes = set()
duplicates_count = 0
removed_list = []
print(f"[*] Processing {len(all_contacts)} raw contacts...")
for c in all_contacts:
h = c.fingerprint()
if h in seen_hashes:
duplicates_count += 1
# Try to construct a readable name for the log
parts = [c.name['prefix'], c.name['given'], c.name['middle'], c.name['family'], c.name['suffix']]
full_name = " ".join([p for p in parts if p]).strip() or c.name['fn'] or "Unnamed"
removed_list.append(full_name)
else:
seen_hashes.add(h)
unique_contacts.append(c)
print(f"[-] Removed {duplicates_count} exact duplicates.")
# Load (Write)
if args.output:
out_name = args.output
else:
base, _ = os.path.splitext(args.inputs[0])
out_name = f"{base}_clean.vcf"
writer = VcfWriter()
if writer.write(unique_contacts, out_name, args.add_us_code):
print(f"[+] Successfully saved {len(unique_contacts)} contacts to '{out_name}'")
# Verbose Logic: Only print if -v is passed AND there are duplicates
if args.verbose and removed_list:
print("\n--- Removed Duplicates ---")
for name in removed_list:
print(f" x {name}")
else:
print("[!] Write failed.")
if __name__ == "__main__":
main()
took me longer then i thought. (a nice few hours.) hope someone enjoys it!
(any questions just ask)
was bored so put it on github too.
https://github.com/flipphoneguy/contacts
Once we are on the topic…earlier this week i was trying to transfer more than 100 contacts from my TCL (no root). So in case it helps anyone here’s what i did: adb shell content query --uri content://com.android.contacts/data --projection display_name:data1 > all_contacts.txt and then run this script in the same folder as the txt file to make it into a vcf file.
(made with gemini)
import re
from collections import defaultdict
def create_vcf():
# Use a dictionary of lists to group multiple phone numbers/emails under one name
contacts = defaultdict(list)
# regex matches: display_name=NAME, data1=VALUE
# It now ignores the leading "Row: X" or "" parts
pattern = re.compile(r"display_name=(.*?), data1=(.*)")
try:
with open('all_contacts.txt', 'r', encoding='utf-8') as f:
for line in f:
# Remove extra whitespace and ignore empty lines
line = line.strip()
if not line:
continue
match = pattern.search(line)
if match:
name = match.group(1).strip()
value = match.group(2).strip()
# LOGIC: Only save the value if it's NOT just repeating the name
# Also ignore "NULL" or empty values
if value and value != name and value.lower() != "null":
contacts[name].append(value)
except FileNotFoundError:
print("Error: all_contacts.txt not found. Make sure it's in this folder!")
return
# Write to a single VCF file
with open('my_contacts.vcf', 'w', encoding='utf-8') as vcf:
count = 0
for name, details in contacts.items():
# If a contact exists but has no valid phone/email, we still create the card
vcf.write("BEGIN:VCARD\n")
vcf.write("VERSION:3.0\n")
vcf.write(f"FN:{name}\n")
for item in details:
if "@" in item:
vcf.write(f"EMAIL;TYPE=INTERNET:{item}\n")
else:
# Keep formatting (dashes/pluses) so it stays readable
vcf.write(f"TEL;TYPE=CELL:{item}\n")
vcf.write("END:VCARD\n")
count += 1
print(f"Success! Created 'my_contacts.vcf' with {count} unique contacts.")
if __name__ == "__main__":
create_vcf()
Alternatively you can upload the text file to chatgpt or copilot and ask it to make it a .vcf file
Should be ```python so it knows to syntax highlight it for python.