456 lines
16 KiB
Python
456 lines
16 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Generatore di QR Code Universale
|
|
Supporta WiFi, URL, testo, email, SMS, contatti vCard, eventi calendario e altro
|
|
"""
|
|
|
|
import qrcode
|
|
from PIL import Image
|
|
import argparse
|
|
import sys
|
|
import os
|
|
import re
|
|
from datetime import datetime
|
|
from urllib.parse import quote
|
|
|
|
class UniversalQRGenerator:
|
|
def __init__(self):
|
|
self.qr_types = {
|
|
'wifi': 'Connessione WiFi',
|
|
'url': 'URL/Link web',
|
|
'text': 'Testo semplice',
|
|
'email': 'Email',
|
|
'sms': 'SMS',
|
|
'phone': 'Numero telefono',
|
|
'vcard': 'Biglietto da visita (vCard)',
|
|
'event': 'Evento calendario',
|
|
'geo': 'Posizione geografica',
|
|
'whatsapp': 'Messaggio WhatsApp',
|
|
'raw': 'Contenuto personalizzato'
|
|
}
|
|
|
|
# === GENERATORI DI CONTENUTO ===
|
|
|
|
def generate_wifi_qr(self, ssid, password=None, security='WPA', hidden=False):
|
|
"""Genera QR code per WiFi"""
|
|
if security not in ['WPA', 'WEP', 'nopass']:
|
|
raise ValueError("Sicurezza deve essere: WPA, WEP o nopass")
|
|
|
|
ssid_escaped = self._escape_wifi_chars(ssid)
|
|
password_escaped = self._escape_wifi_chars(password) if password else ""
|
|
|
|
wifi_string = f"WIFI:T:{security};"
|
|
wifi_string += f"S:{ssid_escaped};"
|
|
if password and security != 'nopass':
|
|
wifi_string += f"P:{password_escaped};"
|
|
if hidden:
|
|
wifi_string += "H:true;"
|
|
wifi_string += ";"
|
|
|
|
return wifi_string
|
|
|
|
def generate_url_qr(self, url):
|
|
"""Genera QR code per URL"""
|
|
if not url.startswith(('http://', 'https://')):
|
|
url = 'https://' + url
|
|
return url
|
|
|
|
def generate_email_qr(self, email, subject=None, body=None):
|
|
"""Genera QR code per email"""
|
|
if not self._is_valid_email(email):
|
|
raise ValueError("Email non valida")
|
|
|
|
mailto_string = f"mailto:{email}"
|
|
params = []
|
|
if subject:
|
|
params.append(f"subject={quote(subject)}")
|
|
if body:
|
|
params.append(f"body={quote(body)}")
|
|
|
|
if params:
|
|
mailto_string += "?" + "&".join(params)
|
|
|
|
return mailto_string
|
|
|
|
def generate_sms_qr(self, number, message=None):
|
|
"""Genera QR code per SMS"""
|
|
sms_string = f"sms:{number}"
|
|
if message:
|
|
sms_string += f"?body={quote(message)}"
|
|
return sms_string
|
|
|
|
def generate_phone_qr(self, number):
|
|
"""Genera QR code per chiamata telefonica"""
|
|
return f"tel:{number}"
|
|
|
|
def generate_vcard_qr(self, first_name, last_name, phone=None, email=None,
|
|
organization=None, url=None):
|
|
"""Genera QR code per biglietto da visita (vCard)"""
|
|
vcard = "BEGIN:VCARD\nVERSION:3.0\n"
|
|
vcard += f"FN:{first_name} {last_name}\n"
|
|
vcard += f"N:{last_name};{first_name};;;\n"
|
|
|
|
if phone:
|
|
vcard += f"TEL:{phone}\n"
|
|
if email:
|
|
vcard += f"EMAIL:{email}\n"
|
|
if organization:
|
|
vcard += f"ORG:{organization}\n"
|
|
if url:
|
|
vcard += f"URL:{url}\n"
|
|
|
|
vcard += "END:VCARD"
|
|
return vcard
|
|
|
|
def generate_event_qr(self, title, start_date, end_date=None, location=None, description=None):
|
|
"""Genera QR code per evento calendario"""
|
|
# Formato: YYYYMMDDTHHMMSS
|
|
start_formatted = start_date.strftime("%Y%m%dT%H%M%S")
|
|
end_formatted = end_date.strftime("%Y%m%dT%H%M%S") if end_date else start_formatted
|
|
|
|
vevent = "BEGIN:VEVENT\n"
|
|
vevent += f"SUMMARY:{title}\n"
|
|
vevent += f"DTSTART:{start_formatted}\n"
|
|
vevent += f"DTEND:{end_formatted}\n"
|
|
|
|
if location:
|
|
vevent += f"LOCATION:{location}\n"
|
|
if description:
|
|
vevent += f"DESCRIPTION:{description}\n"
|
|
|
|
vevent += "END:VEVENT"
|
|
return vevent
|
|
|
|
def generate_geo_qr(self, latitude, longitude, altitude=None):
|
|
"""Genera QR code per posizione geografica"""
|
|
if altitude:
|
|
return f"geo:{latitude},{longitude},{altitude}"
|
|
else:
|
|
return f"geo:{latitude},{longitude}"
|
|
|
|
def generate_whatsapp_qr(self, number, message=None):
|
|
"""Genera QR code per messaggio WhatsApp"""
|
|
# Rimuovi caratteri non numerici dal numero
|
|
clean_number = re.sub(r'[^\d+]', '', number)
|
|
|
|
wa_string = f"https://wa.me/{clean_number}"
|
|
if message:
|
|
wa_string += f"?text={quote(message)}"
|
|
return wa_string
|
|
|
|
def generate_text_qr(self, text):
|
|
"""Genera QR code per testo semplice"""
|
|
return text
|
|
|
|
def generate_raw_qr(self, content):
|
|
"""Genera QR code per contenuto personalizzato"""
|
|
return content
|
|
|
|
# === UTILITÀ ===
|
|
|
|
def _escape_wifi_chars(self, text):
|
|
"""Escape caratteri speciali per WiFi"""
|
|
if not text:
|
|
return ""
|
|
special_chars = ['\\', ';', ',', '"', ':']
|
|
escaped_text = text
|
|
for char in special_chars:
|
|
escaped_text = escaped_text.replace(char, f'\\{char}')
|
|
return escaped_text
|
|
|
|
def _is_valid_email(self, email):
|
|
"""Validazione email semplice"""
|
|
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
|
|
return re.match(pattern, email) is not None
|
|
|
|
# === GENERATORE QR CODE ===
|
|
|
|
def create_qr_code(self, content, filename='qrcode.png',
|
|
error_correction=qrcode.constants.ERROR_CORRECT_M,
|
|
border=4, box_size=10, fill_color="black", back_color="white"):
|
|
"""Genera il QR code dal contenuto"""
|
|
try:
|
|
qr = qrcode.QRCode(
|
|
version=1,
|
|
error_correction=error_correction,
|
|
box_size=box_size,
|
|
border=border,
|
|
)
|
|
|
|
qr.add_data(content)
|
|
qr.make(fit=True)
|
|
|
|
img = qr.make_image(fill_color=fill_color, back_color=back_color)
|
|
img.save(filename)
|
|
|
|
return img, content
|
|
|
|
except Exception as e:
|
|
print(f"❌ Errore nella generazione del QR code: {e}")
|
|
return None, None
|
|
|
|
# === INTERFACCIA PRINCIPALE ===
|
|
|
|
def generate_qr(self, qr_type, filename='qrcode.png', show_details=True, **kwargs):
|
|
"""Metodo principale per generare QR code di qualsiasi tipo"""
|
|
try:
|
|
# Genera il contenuto in base al tipo
|
|
if qr_type == 'wifi':
|
|
content = self.generate_wifi_qr(**kwargs)
|
|
elif qr_type == 'url':
|
|
content = self.generate_url_qr(**kwargs)
|
|
elif qr_type == 'email':
|
|
content = self.generate_email_qr(**kwargs)
|
|
elif qr_type == 'sms':
|
|
content = self.generate_sms_qr(**kwargs)
|
|
elif qr_type == 'phone':
|
|
content = self.generate_phone_qr(**kwargs)
|
|
elif qr_type == 'vcard':
|
|
content = self.generate_vcard_qr(**kwargs)
|
|
elif qr_type == 'event':
|
|
content = self.generate_event_qr(**kwargs)
|
|
elif qr_type == 'geo':
|
|
content = self.generate_geo_qr(**kwargs)
|
|
elif qr_type == 'whatsapp':
|
|
content = self.generate_whatsapp_qr(**kwargs)
|
|
elif qr_type == 'text':
|
|
content = self.generate_text_qr(**kwargs)
|
|
elif qr_type == 'raw':
|
|
content = self.generate_raw_qr(**kwargs)
|
|
else:
|
|
raise ValueError(f"Tipo QR non supportato: {qr_type}")
|
|
|
|
# Genera il QR code
|
|
img, final_content = self.create_qr_code(content, filename)
|
|
|
|
if img and show_details:
|
|
print("✅ QR Code generato con successo!")
|
|
print(f"📁 File salvato: {filename}")
|
|
print(f"🏷️ Tipo: {self.qr_types.get(qr_type, qr_type)}")
|
|
print(f"📄 Contenuto: {final_content[:100]}{'...' if len(final_content) > 100 else ''}")
|
|
|
|
return img
|
|
|
|
except Exception as e:
|
|
print(f"❌ Errore: {e}")
|
|
return None
|
|
|
|
def main():
|
|
"""Interfaccia da riga di comando"""
|
|
parser = argparse.ArgumentParser(
|
|
description='Generatore QR Code Universale',
|
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
epilog="""
|
|
Tipi di QR Code supportati:
|
|
wifi - Connessione WiFi
|
|
url - URL/Link web
|
|
text - Testo semplice
|
|
email - Email
|
|
sms - SMS
|
|
phone - Numero telefono
|
|
vcard - Biglietto da visita
|
|
event - Evento calendario
|
|
geo - Posizione geografica
|
|
whatsapp - Messaggio WhatsApp
|
|
raw - Contenuto personalizzato
|
|
|
|
Esempi di utilizzo:
|
|
python qr_generator.py wifi -s "MiaRete" -p "password123"
|
|
python qr_generator.py url --url "https://google.com"
|
|
python qr_generator.py email --email "test@example.com" --subject "Ciao"
|
|
python qr_generator.py sms --number "+39123456789" --message "Ciao!"
|
|
python qr_generator.py vcard --first-name "Mario" --last-name "Rossi" --phone "+39123456789"
|
|
python qr_generator.py geo --latitude 45.4642 --longitude 9.1900
|
|
"""
|
|
)
|
|
|
|
parser.add_argument('type', choices=list(UniversalQRGenerator().qr_types.keys()),
|
|
help='Tipo di QR code da generare')
|
|
parser.add_argument('-o', '--output', default='qrcode.png',
|
|
help='Nome del file di output')
|
|
parser.add_argument('-q', '--quiet', action='store_true',
|
|
help='Non mostrare i dettagli')
|
|
|
|
# Argomenti per WiFi
|
|
wifi_group = parser.add_argument_group('WiFi')
|
|
wifi_group.add_argument('-s', '--ssid', help='Nome rete WiFi')
|
|
wifi_group.add_argument('-p', '--password', help='Password WiFi')
|
|
wifi_group.add_argument('--security', choices=['WPA', 'WEP', 'nopass'],
|
|
default='WPA', help='Tipo sicurezza WiFi')
|
|
wifi_group.add_argument('--hidden', action='store_true', help='Rete nascosta')
|
|
|
|
# Argomenti per URL
|
|
url_group = parser.add_argument_group('URL')
|
|
url_group.add_argument('--url', help='URL del sito web')
|
|
|
|
# Argomenti per email
|
|
email_group = parser.add_argument_group('Email')
|
|
email_group.add_argument('--email', help='Indirizzo email')
|
|
email_group.add_argument('--subject', help='Oggetto email')
|
|
email_group.add_argument('--body', help='Corpo email')
|
|
|
|
# Argomenti per SMS/Phone
|
|
comm_group = parser.add_argument_group('SMS/Telefono')
|
|
comm_group.add_argument('--number', help='Numero di telefono')
|
|
comm_group.add_argument('--message', help='Messaggio SMS/WhatsApp')
|
|
|
|
# Argomenti per vCard
|
|
vcard_group = parser.add_argument_group('vCard')
|
|
vcard_group.add_argument('--first-name', help='Nome')
|
|
vcard_group.add_argument('--last-name', help='Cognome')
|
|
vcard_group.add_argument('--organization', help='Organizzazione')
|
|
|
|
# Argomenti per geolocalizzazione
|
|
geo_group = parser.add_argument_group('Geolocalizzazione')
|
|
geo_group.add_argument('--latitude', type=float, help='Latitudine')
|
|
geo_group.add_argument('--longitude', type=float, help='Longitudine')
|
|
geo_group.add_argument('--altitude', type=float, help='Altitudine')
|
|
|
|
# Argomenti per testo/raw
|
|
text_group = parser.add_argument_group('Testo/Raw')
|
|
text_group.add_argument('--text', help='Testo da codificare')
|
|
text_group.add_argument('--content', help='Contenuto raw personalizzato')
|
|
|
|
args = parser.parse_args()
|
|
|
|
# Preparazione parametri per il generatore
|
|
generator = UniversalQRGenerator()
|
|
kwargs = {}
|
|
|
|
if args.type == 'wifi':
|
|
if not args.ssid:
|
|
print("❌ SSID obbligatorio per WiFi")
|
|
sys.exit(1)
|
|
kwargs = {
|
|
'ssid': args.ssid,
|
|
'password': args.password,
|
|
'security': args.security,
|
|
'hidden': args.hidden
|
|
}
|
|
elif args.type == 'url':
|
|
if not args.url:
|
|
print("❌ URL obbligatorio")
|
|
sys.exit(1)
|
|
kwargs = {'url': args.url}
|
|
elif args.type == 'email':
|
|
if not args.email:
|
|
print("❌ Email obbligatoria")
|
|
sys.exit(1)
|
|
kwargs = {
|
|
'email': args.email,
|
|
'subject': args.subject,
|
|
'body': args.body
|
|
}
|
|
elif args.type in ['sms', 'phone']:
|
|
if not args.number:
|
|
print("❌ Numero obbligatorio")
|
|
sys.exit(1)
|
|
kwargs = {'number': args.number}
|
|
if args.type == 'sms' and args.message:
|
|
kwargs['message'] = args.message
|
|
elif args.type == 'vcard':
|
|
if not (args.first_name and args.last_name):
|
|
print("❌ Nome e cognome obbligatori per vCard")
|
|
sys.exit(1)
|
|
kwargs = {
|
|
'first_name': args.first_name,
|
|
'last_name': args.last_name,
|
|
'phone': args.number,
|
|
'email': args.email,
|
|
'organization': args.organization
|
|
}
|
|
elif args.type == 'geo':
|
|
if args.latitude is None or args.longitude is None:
|
|
print("❌ Latitudine e longitudine obbligatorie")
|
|
sys.exit(1)
|
|
kwargs = {
|
|
'latitude': args.latitude,
|
|
'longitude': args.longitude,
|
|
'altitude': args.altitude
|
|
}
|
|
elif args.type == 'whatsapp':
|
|
if not args.number:
|
|
print("❌ Numero obbligatorio per WhatsApp")
|
|
sys.exit(1)
|
|
kwargs = {
|
|
'number': args.number,
|
|
'message': args.message
|
|
}
|
|
elif args.type == 'text':
|
|
if not args.text:
|
|
print("❌ Testo obbligatorio")
|
|
sys.exit(1)
|
|
kwargs = {'text': args.text}
|
|
elif args.type == 'raw':
|
|
if not args.content:
|
|
print("❌ Contenuto obbligatorio")
|
|
sys.exit(1)
|
|
kwargs = {'content': args.content}
|
|
|
|
# Genera il QR code
|
|
result = generator.generate_qr(
|
|
args.type,
|
|
filename=args.output,
|
|
show_details=not args.quiet,
|
|
**kwargs
|
|
)
|
|
|
|
sys.exit(0 if result else 1)
|
|
|
|
def interactive_mode():
|
|
"""Modalità interattiva"""
|
|
generator = UniversalQRGenerator()
|
|
|
|
print("🔧 Generatore QR Code Universale - Modalità Interattiva")
|
|
print("=" * 60)
|
|
|
|
# Selezione tipo
|
|
print("\n📋 Tipi di QR Code disponibili:")
|
|
types_list = list(generator.qr_types.items())
|
|
for i, (key, desc) in enumerate(types_list, 1):
|
|
print(f" {i:2d}. {desc}")
|
|
|
|
try:
|
|
choice = int(input(f"\nScegli (1-{len(types_list)}): "))
|
|
qr_type = types_list[choice - 1][0]
|
|
except (ValueError, IndexError):
|
|
print("❌ Scelta non valida")
|
|
return
|
|
|
|
print(f"\n🎯 Tipo selezionato: {generator.qr_types[qr_type]}")
|
|
|
|
# Input specifici per tipo
|
|
kwargs = {}
|
|
|
|
if qr_type == 'wifi':
|
|
kwargs['ssid'] = input("📶 SSID (nome rete): ")
|
|
kwargs['security'] = input("🔒 Sicurezza (WPA/WEP/nopass) [WPA]: ") or 'WPA'
|
|
if kwargs['security'] != 'nopass':
|
|
kwargs['password'] = input("🔑 Password: ")
|
|
kwargs['hidden'] = input("👁️ Rete nascosta? (s/N): ").lower().startswith('s')
|
|
|
|
elif qr_type == 'url':
|
|
kwargs['url'] = input("🌐 URL: ")
|
|
|
|
elif qr_type == 'email':
|
|
kwargs['email'] = input("📧 Email: ")
|
|
kwargs['subject'] = input("📝 Oggetto (opzionale): ") or None
|
|
kwargs['body'] = input("💬 Messaggio (opzionale): ") or None
|
|
|
|
elif qr_type == 'text':
|
|
kwargs['text'] = input("📄 Testo: ")
|
|
|
|
# Altri tipi...
|
|
|
|
# Nome file
|
|
filename = input("📁 Nome file [qrcode.png]: ") or "qrcode.png"
|
|
|
|
# Genera
|
|
print("\n⏳ Generazione in corso...")
|
|
generator.generate_qr(qr_type, filename=filename, **kwargs)
|
|
|
|
if __name__ == "__main__":
|
|
if len(sys.argv) == 1:
|
|
interactive_mode()
|
|
else:
|
|
main() |