DEV Community

Evan Lin for Google Developer Experts

Posted on • Originally published at evanlin.com on

[Python] LINE Bot: Contact Manager Evolution - Generate vCard QR Codes

image-20251114103158657

Background

In the previous LINE Bot Smart Business Card Manager project, we have already implemented the function of automatically recognizing business cards using the Gemini Pro Vision API. Users only need to take a photo of the business card and upload it, and the AI can automatically parse information such as name, title, company, phone number, and email, and store it in the Firebase Realtime Database.

But in actual use, I found a pain point:

📱 I already have digitized business card data, but I still have to manually enter each field to add it to my phone's address book...

Imagine these scenarios:

  • 📇 Attending a seminar: Collected 20 business cards, and still have to manually add them to the address book after recognition is complete
  • 💼 Business visit: Got a customer's business card and wanted to quickly add it to the phone contacts
  • 🤝 Social occasions: Met new friends and wanted to save their contact information immediately

So I thought: Since the data is already digitized, why can't I add it to the address book with one click?

The most ideal way is: Generate a vCard QR Code, and let users scan it to directly add it to their address book!

Project Code

https://github.com/kkdai/linebot-namecard-python

(Through this code, you can quickly deploy to GCP Cloud Run and enjoy the convenience of serverless)

📚 About vCard and QR Code

Introduction to vCard Format

vCard (Virtual Contact File) is a standard format for electronic business cards, with the file extension .vcf. Almost all smartphones and mail clients natively support vCard, including:

  • 📱 iPhone: Automatically identifies and prompts "Add Contact"
  • 🤖 Android: Import via the Contacts App
  • 💻 Computer: Outlook, Apple Mail, Gmail, etc. all support

vCard 3.0 Format Example

BEGIN:VCARD
VERSION:3.0
FN:Kevin Dai
N:Dai;Kevin;;;
ORG:LINE Taiwan
TITLE:Software Engineer
TEL;TYPE=WORK,VOICE:+886-123-456-789
EMAIL;TYPE=WORK:kevin@example.com
ADR;TYPE=WORK:;;Taipei, Taiwan;;;;
NOTE:Met at DevFest 2025
END:VCARD

Enter fullscreen mode Exit fullscreen mode

Advantages of QR Code + vCard

Encoding vCard into a QR Code has several advantages:

  1. Scan and Add: Automatically recognized after scanning with the camera App
  2. Cross-platform: Both iPhone/Android are supported
  3. No download required: No need to save files and then import
  4. Complete data: Contains all contact information and notes

✨ Project Feature Introduction

Core Function Flow

User uploads business card image
    ↓
Gemini Vision API recognition
    ↓
Stored in Firebase Realtime Database
    ↓
Displays business card Flex Message
    ↓
【New Feature】Click the "📥 Add to Address Book" button
    ↓
Generate vCard QR Code
    ↓
Upload to Firebase Storage
    ↓
Returns QR Code image to the user
    ↓
User scans → Add to address book ✅

Enter fullscreen mode Exit fullscreen mode

New Feature Highlights

  1. 📥 One-click generate QR Code
    • Click the "Add to Address Book" button on the business card
    • Automatically generate a vCard QR Code containing complete information
    • Includes name, title, company, phone number, email, address, and notes
  2. ☁️ Firebase Storage Integration
    • QR Code images are uploaded to Firebase Storage
    • Automatically set to public read
    • Sent to users via LINE ImageMessage
  3. 🤖 Gemini Vision Collaboration
    • Original business card recognition function (Gemini Vision API)
    • Recognition results → Firebase Database → QR Code
    • Complete integration of AI recognition + cloud storage + mobile applications
  4. 📱 User-friendly
    • Automatically generate usage instructions
    • Supports iPhone/Android
    • Scan to add to address book

💻 Core Function Implementation

1. vCard Format Generation

First, implement the generation of the vCard format string, which is the foundation of the entire function.

File location: app/qrcode_utils.py

def generate_vcard_string(namecard_data: Dict[str, str]) -> str:
    """
    Generate vCard 3.0 format string from namecard data.

    Args:
        namecard_data: Dictionary containing namecard fields

    Returns:
        vCard formatted string
    """
    name = namecard_data.get('name', '')
    title = namecard_data.get('title', '')
    company = namecard_data.get('company', '')
    phone = namecard_data.get('phone', '')
    email = namecard_data.get('email', '')
    address = namecard_data.get('address', '')
    memo = namecard_data.get('memo', '')

    # Build vCard 3.0 format
    vcard_lines = [
        'BEGIN:VCARD',
        'VERSION:3.0',
        f'FN:{name}',
        f'N:{name};;;', # Family Name; Given Name; Additional Names; Honorific Prefixes; Honorific Suffixes
    ]

    if company:
        vcard_lines.append(f'ORG:{company}')

    if title:
        vcard_lines.append(f'TITLE:{title}')

    if phone:
        # Clean phone number format for vCard
        clean_phone = phone.replace('-', '').replace(' ', '')
        vcard_lines.append(f'TEL;TYPE=WORK,VOICE:{clean_phone}')

    if email:
        vcard_lines.append(f'EMAIL;TYPE=WORK:{email}')

    if address:
        # vCard address format: PO Box;Extended Address;Street;City;Region;Postal Code;Country
        vcard_lines.append(f'ADR;TYPE=WORK:;;{address};;;;')

    if memo:
        # Escape special characters in memo
        escaped_memo = memo.replace('\n', '\\n').replace(',', '\\,').replace(';', '\\;')
        vcard_lines.append(f'NOTE:{escaped_memo}')

    vcard_lines.append('END:VCARD')

    return '\n'.join(vcard_lines)

Enter fullscreen mode Exit fullscreen mode

Design points:

  • ✅ Use vCard 3.0 format (best compatibility)
  • Handle empty fields: Only add corresponding fields when there is data
  • Phone number cleaning: Remove - and spaces to ensure the correct format
  • Special character escaping: Line breaks, commas, and semicolons in the notes need to be escaped
  • Complete information: Includes the note field, retaining additional information from AI recognition

2. QR Code Image Generation

Use the qrcode package to encode the vCard string into a QR Code image.

def generate_vcard_qrcode(namecard_data: Dict[str, str],
                          box_size: int = 10,
                          border: int = 2) -> BytesIO:
    """
    Generate QR Code image containing vCard data.

    Args:
        namecard_data: Dictionary containing namecard fields
        box_size: Size of each box in pixels (default: 10)
        border: Border size in boxes (default: 2)

    Returns:
        BytesIO object containing PNG image data
    """
    # Generate vCard string
    vcard_string = generate_vcard_string(namecard_data)

    # Create QR Code instance
    qr = qrcode.QRCode(
        version=None, # Auto-determine version based on data size
        error_correction=qrcode.constants.ERROR_CORRECT_L,
        box_size=box_size,
        border=border,
    )

    # Add vCard data
    qr.add_data(vcard_string)
    qr.make(fit=True)

    # Generate image
    img = qr.make_image(fill_color="black", back_color="white")

    # Save to BytesIO
    img_bytes = BytesIO()
    img.save(img_bytes, format='PNG')
    img_bytes.seek(0) # Reset pointer to beginning

    return img_bytes

Enter fullscreen mode Exit fullscreen mode

Key parameter descriptions:

Parameter Description Reason for selection
version=None Automatically determines the QR Code size Automatically adjusts based on data volume to ensure it can be scanned
error_correction=L Error correction level (Low) vCard data will not be frequently damaged, choose the minimum level to reduce the QR Code size
box_size=10 Size of each box in pixels (default: 10) Good scanability on the phone screen
border=2 Border size in boxes (default: 2) Meets the minimum border of the QR Code standard

Why use BytesIO?

  • ✅ No need to write to the physical file system
  • ✅ Directly process images in memory
  • ✅ Convenient for subsequent uploading to Firebase Storage
  • ✅ Reduce I/O operations and improve performance

3. Firebase Storage Integration

This is the core of the entire function: uploading the QR Code image to Firebase Storage and obtaining the public URL.

File location: app/firebase_utils.py

from firebase_admin import storage
from io import BytesIO

def upload_qrcode_to_storage(
        image_bytes: BytesIO, user_id: str, card_id: str) -> str:
    """
    Upload QR Code image to Firebase Storage and return the public URL

    Args:
        image_bytes: BytesIO object of the QR Code image
        user_id: User ID
        card_id: Business card ID

    Returns:
        Public URL of the image, returns None if it fails
    """
    try:
        bucket = storage.bucket()
        blob_name = f"qrcodes/{user_id}/{card_id}.png"
        blob = bucket.blob(blob_name)

        # Upload image
        image_bytes.seek(0) # Reset pointer to the beginning
        blob.upload_from_file(image_bytes, content_type='image/png')

        # Set to public read
        blob.make_public()

        # Return public URL
        return blob.public_url
    except Exception as e:
        print(f"Error uploading QR code to storage: {e}")
        return None

Enter fullscreen mode Exit fullscreen mode

Design considerations:

  1. File path structure: qrcodes/{user_id}/{card_id}.png
    • Categorized by user for easy management
    • Use card_id to ensure the file name is unique
    • Repeated generation of the same business card will overwrite the old file
  2. Public permissions: blob.make_public()
    • The QR Code needs to be accessed by the LINE Bot via URL
    • Firebase Storage Rules are set to allow read: if true
    • Write permissions are only given to the Admin SDK (Cloud Run)
  3. Content-Type setting: content_type='image/png'
    • Ensure the browser displays the image correctly
    • LINE ImageMessage requires the correct MIME type

4. Firebase Initialization Configuration

In app/main.py, correctly set up the Firebase Storage Bucket:

import firebase_admin
from firebase_admin import credentials

# Firebase initialization
firebase_config = {
    "databaseURL": config.FIREBASE_URL,
}
# If Storage Bucket is set, add the configuration
if config.FIREBASE_STORAGE_BUCKET:
    firebase_config["storageBucket"] = config.FIREBASE_STORAGE_BUCKET

try:
    cred = credentials.ApplicationDefault()
    firebase_admin.initialize_app(cred, firebase_config)
    print("Firebase Admin SDK initialized successfully.")
except Exception as e:
    # Parse JSON from environment variables
    gac_str = os.environ.get("GOOGLE_APPLICATION_CREDENTIALS_JSON")
    if gac_str:
        cred_json = json.loads(gac_str)
        cred = credentials.Certificate(cred_json)
        firebase_admin.initialize_app(cred, firebase_config)
        print("Firebase Admin SDK initialized successfully from ENV VAR.")

Enter fullscreen mode Exit fullscreen mode

Environment variable settings:

# Need to be set when deploying to Cloud Run
FIREBASE_STORAGE_BUCKET=your-project-id.firebasestorage.app

# Or the old format
FIREBASE_STORAGE_BUCKET=your-project-id.appspot.com

Enter fullscreen mode Exit fullscreen mode

Why do you need to explicitly set the Storage Bucket?

  • Firebase Admin SDK defaults to only initializing the Database
  • If storageBucket is not specified, calling storage.bucket() will fail
  • Explicit setting can avoid runtime errors

5. LINE Bot Postback Handling

When a user clicks the "Add to Address Book" button, handle the complete process.

File location: app/line_handlers.py

from linebot.models import ImageSendMessage, TextSendMessage

async def handle_download_contact(
        event: PostbackEvent, user_id: str, card_id: str, card_name: str):
    """Handle the request to download the contact QR Code"""
    try:
        # 1. Get the complete business card data from Firebase
        card_data = firebase_utils.get_card_by_id(user_id, card_id)
        if not card_data:
            await line_bot_api.reply_message(
                event.reply_token,
                TextSendMessage(text='Could not find the business card data.'))
            return

        # 2. Generate vCard QR Code
        qrcode_image = qrcode_utils.generate_vcard_qrcode(card_data)

        # 3. Upload to Firebase Storage and get the URL
        image_url = firebase_utils.upload_qrcode_to_storage(
            qrcode_image, user_id, card_id)

        if not image_url:
            await line_bot_api.reply_message(
                event.reply_token,
                TextSendMessage(text='An error occurred while generating the QR Code, please try again later.'))
            return

        # 4. Generate usage instructions
        instruction_text = qrcode_utils.get_qrcode_usage_instruction(card_name)

        # 5. Return the QR Code image and usage instructions
        image_message = ImageSendMessage(
            original_content_url=image_url,
            preview_image_url=image_url
        )
        text_message = TextSendMessage(text=instruction_text)

        await line_bot_api.reply_message(
            event.reply_token,
            [image_message, text_message])

    except Exception as e:
        print(f"Error in handle_download_contact: {e}")
        await line_bot_api.reply_message(
            event.reply_token,
            TextSendMessage(text='An error occurred while processing your request, please try again later.'))

Enter fullscreen mode Exit fullscreen mode

Flow design highlights:

  1. Complete error handling: Error checking for each step
  2. Friendly prompts: Clear error messages when it fails
  3. Return two messages at once: Image + instruction text
  4. Asynchronous processing: Use async/await to avoid blocking

6. Flex Message Button Configuration

Add an "Add to Address Book" button to the business card's Flex Message.

File location: app/flex_messages.py

"footer": {
    "type": "box",
    "layout": "vertical",
    "spacing": "sm",
    "contents": [
        {
            "type": "box",
            "layout": "horizontal",
            "spacing": "sm",
            "contents": [
                {
                    "type": "button",
                    "style": "link",
                    "height": "sm",
                    "action": {
                        "type": "postback",
                        "label": "Add/Modify Memo",
                        "data": f"action=add_memo&card_id={card_id}",
                        "displayText": f"I want to add a memo for {name}"
                    },
                    "flex": 1
                },
                {
                    "type": "button",
                    "style": "link",
                    "height": "sm",
                    "action": {
                        "type": "postback",
                        "label": "Edit Data",
                        "data": f"action=edit_card&card_id={card_id}",
                        "displayText": f"I want to edit {name}'s business card"
                    },
                    "flex": 1
                }
            ]
        },
        {
            "type": "button",
            "style": "primary",
            "height": "sm",
            "action": {
                "type": "postback",
                "label": "📥 Add to Address Book",
                "data": f"action=download_contact&card_id={card_id}",
                "displayText": f"Download {name}'s contact information"
            },
            "margin": "sm"
        }
    ]
}

Enter fullscreen mode Exit fullscreen mode

UI design considerations:

┌────────────────────────────────────┐
│ [Add/Modify Memo] [Edit Data] │ ← First row side by side (link style)
│ [📥 Add to Address Book] │ ← Second row independent (primary style)
└────────────────────────────────────┘

Enter fullscreen mode Exit fullscreen mode
  • First row side by side: Commonly used editing functions, use link style
  • Second row independent: Download function, use primary style to highlight
  • Emoji visualization: 📥 icon to let users visually identify the download function

7. Usage Instruction Generation

Provide clear usage instructions so that users know how to use the QR Code.

def get_qrcode_usage_instruction(name: str) -> str:
    """
    Get user instruction message for using the QR Code.

    Args:
        name: Name of the person on the namecard

    Returns:
        Instruction message string
    """
    return f"""Generated contact QR Code for "{name}"!

📱 How to use:
1. Scan the QR Code above with your phone's camera App
2. The system will automatically recognize contact information
3. Click "Add Contact" to import

✅ Supports all smartphones with iPhone and Android"""

Enter fullscreen mode Exit fullscreen mode

Design philosophy:

  • Personalized message: Includes the name of the business card owner
  • Clear steps: 1-2-3 simple and clear
  • Cross-platform instructions: Emphasizes that iPhone/Android are supported
  • Emoji visualization: 📱 and ✅ make the message more friendly

🤖 Gemini Vision API's Role in the Overall Architecture

Although this time the QR Code function itself does not use Gemini, the entire business card manager system is a complete application centered on the Gemini Vision API.

Gemini + Firebase Storage Collaboration Flow

┌─────────────────────────────────────────────┐
│ User uploads business card photo │
└──────────────┬──────────────────────────────┘
               ↓
┌─────────────────────────────────────────────┐
│ LINE Bot receives image │
│ (app/line_handlers.py) │
└──────────────┬──────────────────────────────┘
               ↓
┌─────────────────────────────────────────────┐
│ Gemini Pro Vision API recognition │
│ - Name, title, company │
│ - Phone, Email, Address │
│ (app/gemini_utils.py) │
└──────────────┬──────────────────────────────┘
               ↓
┌─────────────────────────────────────────────┐
│ Stored in Firebase Realtime Database │
│ /namecard/{user_id}/{card_id}/ │
└──────────────┬──────────────────────────────┘
               ↓
┌─────────────────────────────────────────────┐
│ User clicks "📥 Add to Address Book" │
└──────────────┬──────────────────────────────┘
               ↓
┌─────────────────────────────────────────────┐
│ Generate vCard QR Code │
│ (app/qrcode_utils.py) │
└──────────────┬──────────────────────────────┘
               ↓
┌─────────────────────────────────────────────┐
│ Upload to Firebase Storage │
│ qrcodes/{user_id}/{card_id}.png │
│ (app/firebase_utils.py) │
└──────────────┬──────────────────────────────┘
               ↓
┌─────────────────────────────────────────────┐
│ Returns QR Code to the user │
│ User scans → Add to address book ✅ │
└─────────────────────────────────────────────┘

Enter fullscreen mode Exit fullscreen mode

Key Role of Gemini Vision API

In app/gemini_utils.py, we use Gemini Pro Vision to parse business card images:

def generate_json_from_image(img: PIL.Image, prompt: str):
    """
    Use Gemini Pro Vision to extract structured data from image.
    """
    model = genai.GenerativeModel('gemini-1.5-pro')
    response = model.generate_content([prompt, img])
    return response

Enter fullscreen mode Exit fullscreen mode

Prompt Design (app/config.py):

IMGAGE_PROMPT = """
This is a business card, you are a business card secretary. Please organize the following information into json for me.
If you can't see it, please fill in N/A
Just json:
name, title, address, email, phone, company.
Where the content format of phone is #886-0123-456-789,1234. If there is no extension, ignore ,1234
"""

Enter fullscreen mode Exit fullscreen mode

Why choose Gemini Vision?

  1. Strong Chinese recognition ability: Taiwanese business cards often have Chinese, and Gemini handles the effect well
  2. Structured output: Directly generate JSON format, easy to parse
  3. Fault tolerance: Automatically fills in "N/A" when it cannot be recognized
  4. Flexible format: Supports various business card layouts

Firebase Dual Service Integration

This project uses two major services of Firebase at the same time:

Service Purpose Data type Access method
Realtime Database Store structured business card data JSON firebase_admin.db
Storage Store QR Code images Binary (PNG) firebase_admin.storage

Why do you need two services?

  • Database: Suitable for structured data, supports real-time querying and updating
  • Storage: Suitable for large binary files, provides CDN acceleration

Data flow:

Gemini Vision → Database (structured data)
                    ↓
                QR Code generation
                    ↓
                Storage (image files)
                    ↓
                LINE Bot (image URL)

Enter fullscreen mode Exit fullscreen mode

🔧 Challenges and Solutions Encountered

1. Firebase Storage Bucket Configuration Problem

Problem: When initializing the Firebase Admin SDK, there was an error because the Storage Bucket was not set.

Error message:

ValueError: Invalid None value for Firebase Storage bucket.

Enter fullscreen mode Exit fullscreen mode

Cause analysis:

  • Firebase Admin SDK defaults to only initializing Realtime Database
  • Must explicitly specify storageBucket when initialize_app()
  • Environment variables were not set correctly

Solution:

  1. Add configuration in config.py:
FIREBASE_STORAGE_BUCKET = os.environ.get("FIREBASE_STORAGE_BUCKET")

Enter fullscreen mode Exit fullscreen mode
  1. Add during initialization in main.py:
firebase_config = {
    "databaseURL": config.FIREBASE_URL,
}
if config.FIREBASE_STORAGE_BUCKET:
    firebase_config["storageBucket"] = config.FIREBASE_STORAGE_BUCKET

firebase_admin.initialize_app(cred, firebase_config)

Enter fullscreen mode Exit fullscreen mode
  1. Set environment variables during deployment:
--set-env-vars "...,FIREBASE_STORAGE_BUCKET=line-vertex.firebasestorage.app,..."

Enter fullscreen mode Exit fullscreen mode

Lessons learned:

  • Different services of Firebase require different configuration parameters
  • Environment variables must be fully checked to avoid runtime errors
  • Both the new format .firebasestorage.app and the old format .appspot.com are supported

2. Permission Settings for Storage Rules

Problem: How to set Firebase Storage Rules so that Cloud Run can write, but QR Code images can be publicly read?

Initial idea:

// ❌ This will allow anyone to write
allow read, write: if true;

Enter fullscreen mode Exit fullscreen mode

Correct solution:

Use the feature that Firebase Admin SDK will bypass Rules:

rules_version = '2';

service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read: if true; // Anyone can read
      allow write: if false; // Prevent client-side writing
    }
  }
}

Enter fullscreen mode Exit fullscreen mode

Why is this feasible?

  1. ✅ Cloud Run uses Admin SDK, has full permissions (bypasses Rules)
  2. allow read: if true allows the LINE Bot to access the image via URL
  3. allow write: if false prevents malicious clients from uploading files
  4. ✅ The public permissions set by blob.make_public() are still valid

Key learning:

  • The difference in permissions between Admin SDK vs client-side SDK
  • Storage Rules only affect client-side access
  • Using Admin SDK for cloud services is the best practice

3. QR Code Size and Scanability Optimization

Problem: The generated QR Code is too small or too large, making it difficult to scan.

Experiment process:

Parameter combination Result Problem
box_size=5, border=1 Image too small Difficult to scan with a phone
box_size=15, border=4 Image too large Distorted after LINE compression
box_size=10, border=2 ✅ Moderate Smooth scanning

Final solution:

qr = qrcode.QRCode(
    version=None, # Automatically adjust size
    error_correction=qrcode.constants.ERROR_CORRECT_L, # Low error correction
    box_size=10, # 10px per box
    border=2, # 2 box border
)

Enter fullscreen mode Exit fullscreen mode

Why choose ERROR_CORRECT_L (low error correction)?

  • vCard data is relatively stable and will not be damaged
  • Low error correction = QR Code is simpler = scan faster
  • If you use high error correction (H), the QR Code will become very complex

Test results:

  • ✅ iPhone camera: scan in seconds
  • ✅ Android camera: scan in seconds
  • ✅ LINE built-in scanner: normal

4. vCard Special Character Handling

Problem: If there are special characters such as line breaks, commas, and semicolons in the notes, it will cause the vCard format to be incorrect.

Error example:

NOTE:This person is very important, remember to contact; next meeting time: 2025/11/15

Enter fullscreen mode Exit fullscreen mode

The vCard parser will treat commas and semicolons as separators, leading to data confusion.

Solution:

if memo:
    # Escape special characters in memo
    escaped_memo = memo.replace('\n', '\\n').replace(',', '\\,').replace(';', '\\;')
    vcard_lines.append(f'NOTE:{escaped_memo}')

Enter fullscreen mode Exit fullscreen mode

vCard escape rules:

Character After escaping Description
Line break \n \\n Line break in text
Comma , \\, Avoid being used as a separator
Semicolon ; \\; Avoid being used as a separator

Lessons learned:

  • vCard has its own escape rules and cannot directly copy JSON
  • The notes entered by the user may contain any character
  • Fully test various special character situations

5. BytesIO Pointer Reset Problem

Problem: When uploading an image to Firebase Storage, sometimes an empty file is uploaded.

Error cause:

img_bytes = BytesIO()
img.save(img_bytes, format='PNG')
# ❌ At this time, the pointer is at the end of the file

blob.upload_from_file(img_bytes, content_type='image/png')
# ❌ Read from the end = read empty content

Enter fullscreen mode Exit fullscreen mode

Solution:

img_bytes = BytesIO()
img.save(img_bytes, format='PNG')
img_bytes.seek(0) # ✅ Reset pointer to the beginning

blob.upload_from_file(img_bytes, content_type='image/png')

Enter fullscreen mode Exit fullscreen mode

Why do you need seek(0)?

  1. img.save() will move the pointer to the end of the file
  2. upload_from_file() reads from the current position
  3. If you don't reset, it will read 0 bytes

Lessons learned:

  • Pay attention to the pointer position when using BytesIO
  • Remember to seek(0) after writing before reading
  • This is a common beginner's trap

6. URL Requirements for LINE ImageMessage

Problem: Sometimes the QR Code cannot be displayed in LINE.

Cause analysis:

LINE Bot's ImageSendMessage has strict requirements for URLs:

  1. ✅ Must be HTTPS
  2. ✅ The image must be JPEG or PNG
  3. ✅ The URL must be publicly accessible
  4. original_content_url and preview_image_url can be the same

Correct usage:

image_message = ImageSendMessage(
    original_content_url=image_url, # Firebase Storage's public URL
    preview_image_url=image_url # You can use the same URL
)

Enter fullscreen mode Exit fullscreen mode

Advantages of Firebase Storage:

  • ✅ Automatically provides HTTPS URL
  • blob.make_public() ensures public access
  • ✅ CDN acceleration, fast loading
  • blob.public_url directly obtains the complete URL

🎯 Summary and Future Improvements

Project Highlights

  1. 🤖 AI-driven business card recognition
    • Gemini Pro Vision API automatically parses business cards
    • Supports Chinese business cards, high recognition accuracy
    • Structured data storage, convenient for subsequent processing
  2. 📥 One-click add to address book
    • vCard QR Code standard format
    • iPhone/Android native support
    • Scan and add, no manual input required
  3. ☁️ Firebase dual service integration
    • Realtime Database stores structured data
    • Storage stores QR Code images
    • Admin SDK ensures security
  4. 🚀 Serverless architecture
    • Deployed to Google Cloud Run
    • Automatic scaling, pay-as-you-go
    • Cold start optimization, fast response
  5. 🎨 User experience optimization
    • LINE Flex Message beautiful interface
    • Postback button interaction is smooth
    • Clear usage instructions

Architecture Advantages

┌────────────────────────────────────────┐
│ Google Cloud Platform │
│ ┌──────────────────────────────────┐ │
│ │ Cloud Run (Serverless) │ │
│ │ - FastAPI │ │
│ │ - LINE Bot SDK │ │
│ │ - Firebase Admin SDK │ │
│ └──────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────┐ │
│ │ Gemini Pro Vision API │ │
│ │ - Business card image recognition │ │
│ │ - Structured data extraction │ │
│ └──────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────┐ │
│ │ Firebase Services │ │
│ │ - Realtime Database (Business card data) │ │
│ │ - Storage (QR Code images) │ │
│ └──────────────────────────────────┘ │
└────────────────────────────────────────┘

Enter fullscreen mode Exit fullscreen mode

Practical Experience Sharing

1. Selection of Firebase Services

When to use Realtime Database?

  • ✅ Structured data (JSON)
  • ✅ Need real-time querying and updating
  • ✅ Small data volume (business card information)
  • ✅ Need simple query logic

When to use Firebase Storage?

  • ✅ Binary files (images, videos, PDFs)
  • ✅ Need public access URL
  • ✅ Need CDN acceleration
  • ✅ Larger file size

The best combination for this project:

Business card text data → Realtime Database
QR Code images → Storage

Enter fullscreen mode Exit fullscreen mode

2. Practicality of vCard Standards

vCard is an underestimated standard:

  • Cross-platform: All devices are supported
  • No APP required: No need to install additional software
  • Standardized: A mature standard with 30 years of history
  • Extensible: Supports photos, social media, etc.

Usage scenarios far beyond business cards:

  • Email signature files
  • Website "Contact Us" page
  • Conference registration system
  • Social media profiles

3. Design Philosophy of QR Codes

Good QR Code design:

  • ✅ Moderate size (10-15 px per module)
  • ✅ Minimum border (2 modules)
  • ✅ Low error correction (if the content is stable)
  • ✅ High contrast (black and white is best)

Avoid over-design:

  • ❌ Add Logo (increases scanning difficulty)
  • ❌ Use color (easy to distort)
  • ❌ Over-artistic (reduces readability)

4. Firebase Admin SDK vs Client-Side SDK

Feature Admin SDK Client-side SDK
Execution environment Server-side Browser/Phone
Permissions Full permissions (bypasses Rules) Restricted by Rules
Authentication Service Account User authentication
Applicable scenarios Cloud Run, Cloud Functions Web App, Mobile App
Security High (does not expose credentials) Requires Rules protection

Choice for this project:

  • ✅ Use Admin SDK (Cloud Run environment)
  • ✅ Storage Rules set to write: false
  • ✅ Admin SDK can still write (bypasses Rules)

5. Best Practices for Gemini API

Prompt design skills:

# ✅ Good Prompt
"""
This is a business card, you are a business card secretary. Please organize the following information into json for me.
If you can't see it, please fill in N/A
Just json:
name, title, address, email, phone, company.
"""

# ❌ Bad Prompt
"Extract name, title, company from this business card"

Enter fullscreen mode Exit fullscreen mode

Why is the first one better?

  1. Role setting: "You are a business card secretary" lets AI understand the task
  2. Clear format: Requires JSON, no other instructions
  3. Fault tolerance: Fill in N/A when it cannot be recognized
  4. Chinese instructions: More accurate when processing Chinese business cards

Future Improvement Directions

1. Feature Expansion

Short term (1-2 weeks):

  • QR Code adds company Logo (improve brand recognition)
  • Support multiple QR Code style selections
  • QR Code download as a file (not just image links)
  • Batch generate QR Codes for multiple business cards

Mid-term (1-2 months):

  • Integrate NFC virtual business cards (iPhone Wallet)
  • <

Top comments (0)