0Pricing
AI Agents · Pelajaran

Membaca dan Mengirim Email Secara Terprogram

Mencantumkan pesan, mengambil isi pesan, dan mengirim email MIME.

Membaca dan Mengirim Email Secara Terprogram adalah pelajaran AI Agents gratis di CoddyKit. Ini adalah pelajaran 2 dari 4. Kamu bisa membaca pelajaran lengkapnya di bawah secara gratis — lalu praktikkan langsung di browser dengan editor kode bawaan dan tutor AI 24/7. Ini adalah bagian dari jalur belajar AI Agents, dan progresmu tersinkronisasi di web dan aplikasi CoddyKit. Kursus AI Agents mencakup 4 pelajaran total.

Membuat Daftar Pesan dengan Gmail API

Langkah pertama untuk membaca email adalah membuat daftar pesan yang sesuai dengan kriteria Anda. service.users().messages().list() mengembalikan ID pesan dan ID rangkaian percakapan — bukan konten lengkapnya. Selanjutnya, ambil setiap pesan secara terpisah. Pola dua langkah ini membuat panggilan pembuatan daftar tetap cepat.

def list_messages(service, user_id='me', query='', max_results=10):
    results = service.users().messages().list(
        userId=user_id,
        q=query,          # Gmail search query
        maxResults=max_results
    ).execute()

    messages = results.get('messages', [])
    print(f'Found {len(messages)} messages')
    return messages

# Examples of Gmail search queries:
# 'is:unread' — unread messages
# 'from:boss@company.com is:unread' — unread from boss
# 'subject:invoice label:inbox' — invoices in inbox
# 'after:2026/05/01 has:attachment' — recent with attachments
messages = list_messages(gmail_service, query='is:unread label:inbox')

Mengambil Pesan Lengkap

Gunakan service.users().messages().get() untuk mengambil konten pesan lengkap. Parameter format mengatur banyaknya data yang dikembalikan: 'full' menyertakan header dan isi, 'metadata' hanya mengembalikan header, sedangkan 'minimal' hanya mengembalikan ID dan label.

def get_message(service, message_id, user_id='me'):
    message = service.users().messages().get(
        userId=user_id,
        id=message_id,
        format='full'  # 'full', 'metadata', or 'minimal'
    ).execute()
    return message

# Fetch the first unread message
messages = list_messages(gmail_service, query='is:unread', max_results=1)
if messages:
    msg = get_message(gmail_service, messages[0]['id'])
    print('Thread ID:', msg['threadId'])
    print('Labels:', msg['labelIds'])
    print('Snippet:', msg['snippet'][:100])

Mengekstrak Header Email

Header (From, To, Subject, Date) disimpan dalam message['payload']['headers'] sebagai daftar dict {'name': ..., 'value': ...}. Tulis fungsi pembantu untuk mengekstrak header berdasarkan namanya — Anda akan sangat sering menggunakannya.

def get_header(message, name):
    headers = message.get('payload', {}).get('headers', [])
    for h in headers:
        if h['name'].lower() == name.lower():
            return h['value']
    return ''

def extract_email_meta(message):
    return {
        'id': message['id'],
        'from': get_header(message, 'From'),
        'to': get_header(message, 'To'),
        'subject': get_header(message, 'Subject'),
        'date': get_header(message, 'Date'),
        'snippet': message.get('snippet', '')
    }

meta = extract_email_meta(msg)
print(f'From: {meta["from"]}')
print(f'Subject: {meta["subject"]}')
print(f'Date: {meta["date"]}')

Mendekode Isi Email (base64)

Isi email dalam Gmail API dikodekan dengan base64url — varian base64 yang aman untuk URL, dengan + menjadi - dan / menjadi _. Gunakan base64.urlsafe_b64decode() untuk mendekode. Tangani email sederhana (satu bagian) maupun email multipart.

import base64

def decode_body(data):
    if not data:
        return ''
    decoded_bytes = base64.urlsafe_b64decode(data + '==')
    return decoded_bytes.decode('utf-8', errors='replace')

def get_email_body(message):
    payload = message.get('payload', {})
    mime_type = payload.get('mimeType', '')

    # Simple (non-multipart) email
    if 'body' in payload and payload['body'].get('data'):
        return decode_body(payload['body']['data'])

    # Multipart email: find the text/plain or text/html part
    parts = payload.get('parts', [])
    for part in parts:
        if part.get('mimeType') == 'text/plain':
            return decode_body(part['body'].get('data', ''))

    # Fallback: try text/html
    for part in parts:
        if part.get('mimeType') == 'text/html':
            return decode_body(part['body'].get('data', ''))

    return message.get('snippet', '')

# --- demo ---
encoded = base64.urlsafe_b64encode(b'Hello from the agent!').decode().rstrip('=')
print('Decoded body:', decode_body(encoded))

message = {
    'payload': {
        'mimeType': 'multipart/alternative',
        'parts': [
            {'mimeType': 'text/plain', 'body': {'data': encoded}}
        ]
    }
}
print('Email body:', get_email_body(message))

Menangani Email Multipart Secara Rekursif

Email kompleks (dengan lampiran, gambar sebaris, atau konten campuran) merupakan struktur multipart bertingkat. Bagian isi dapat bertingkat hingga kedalaman berapa pun. Fungsi rekursif yang menelusuri pohon bagian dapat menangani semua kasus.

import base64

def extract_parts(payload, target_mime='text/plain'):
    parts_text = []
    mime_type = payload.get('mimeType', '')

    if mime_type == target_mime:
        data = payload.get('body', {}).get('data', '')
        if data:
            decoded = base64.urlsafe_b64decode(data + '==').decode('utf-8', errors='replace')
            parts_text.append(decoded)

    # Recurse into sub-parts
    for part in payload.get('parts', []):
        parts_text.extend(extract_parts(part, target_mime))

    return parts_text

def get_plain_text(message):
    payload = message.get('payload', {})
    texts = extract_parts(payload, 'text/plain')
    return '\n\n'.join(texts) if texts else message.get('snippet', '')

body_text = get_plain_text(msg)
print(f'Body ({len(body_text)} chars):', body_text[:200])

Menandai Pesan sebagai Telah Dibaca

Setelah memproses email, agen Anda sebaiknya menandainya sebagai telah dibaca dengan menghapus label UNREAD. Gunakan service.users().messages().modify() dengan removeLabelIds=['UNREAD']. Anda juga dapat menambahkan label seperti PROCESSED untuk melacak email yang telah ditangani agen.

def mark_as_read(service, message_id, user_id='me'):
    service.users().messages().modify(
        userId=user_id,
        id=message_id,
        body={'removeLabelIds': ['UNREAD']}
    ).execute()
    print(f'Marked {message_id} as read')

def add_label(service, message_id, label_id, user_id='me'):
    service.users().messages().modify(
        userId=user_id,
        id=message_id,
        body={'addLabelIds': [label_id]}
    ).execute()

# Get label ID by name
def get_label_id(service, label_name, user_id='me'):
    labels = service.users().labels().list(userId=user_id).execute()
    for label in labels.get('labels', []):
        if label['name'].lower() == label_name.lower():
            return label['id']
    return None

# --- demo: minimal stand-in for the Gmail API's service object ---
class _Exec:
    def __init__(self, result):
        self._result = result
    def execute(self):
        return self._result

class _FakeUsers:
    def messages(self):
        return self
    def modify(self, **kwargs):
        print(f'[gmail api] messages.modify({kwargs})')
        return _Exec({'id': kwargs.get('id')})
    def labels(self):
        return self
    def list(self, **kwargs):
        return _Exec({'labels': [{'id': 'Label_1', 'name': 'Processed'}]})

class _FakeService:
    def users(self):
        return _FakeUsers()

service = _FakeService()
mark_as_read(service, 'msg_42')
add_label(service, 'msg_42', 'Label_1')
print('Label id for "Processed":', get_label_id(service, 'Processed'))

Menyusun Email dengan MIMEText

Untuk mengirim email, susun terlebih dahulu sebagai pesan MIME menggunakan pustaka standar Python email. Kemudian, lakukan pengodean base64url pada byte mentahnya dan POST ke Gmail API. MIMEText menangani pengodean isi pesan dengan benar.

import base64
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart

def create_message(sender, to, subject, body_text, body_html=None):
    if body_html:
        msg = MIMEMultipart('alternative')
        msg.attach(MIMEText(body_text, 'plain', 'utf-8'))
        msg.attach(MIMEText(body_html, 'html', 'utf-8'))
    else:
        msg = MIMEText(body_text, 'plain', 'utf-8')

    msg['From'] = sender
    msg['To'] = to
    msg['Subject'] = subject

    # Encode as base64url
    raw = base64.urlsafe_b64encode(msg.as_bytes()).decode('utf-8')
    return {'raw': raw}

message = create_message(
    sender='agent@yourcompany.com',
    to='recipient@example.com',
    subject='Weekly Summary',
    body_text='Hello,\n\nHere is your summary.\n\nBest,\nAgent'
)

# --- demo ---
print('Message keys:', list(message.keys()))
print('Base64 length:', len(message['raw']))

Mengirim Email dengan Gmail API

Kirim pesan yang telah disusun menggunakan service.users().messages().send(). Parameter userId='me' merujuk pada pengguna yang telah diautentikasi. API mengembalikan pesan terkirim beserta ID dan ID rangkaian percakapannya.

from googleapiclient.errors import HttpError

def send_message(service, message, user_id='me'):
    try:
        sent = service.users().messages().send(
            userId=user_id,
            body=message
        ).execute()
        print(f'Message sent! ID: {sent["id"]}')
        return sent
    except HttpError as e:
        import json
        body = json.loads(e.content.decode())
        print(f'Send failed ({e.resp.status}): {body.get("error", {}).get("message")}')
        return None

# Send the message
message = create_message(
    sender='me',
    to='team@company.com',
    subject='Agent Report',
    body_text='Processing complete. 42 tasks handled.'
)
send_message(gmail_service, message)

Membuat dan Mengirim Draf Email

Alih-alih langsung mengirim, agen dapat membuat draf untuk ditinjau manusia. Gunakan service.users().drafts().create(). Selanjutnya, manusia dapat meninjau dan mengirim draf tersebut dari antarmuka Gmail. Ini adalah pola yang direkomendasikan untuk email apa pun yang memerlukan persetujuan manusia.

def create_draft(service, message, user_id='me'):
    draft = service.users().drafts().create(
        userId=user_id,
        body={'message': message}
    ).execute()
    print(f'Draft created: {draft["id"]}')
    return draft

def send_draft(service, draft_id, user_id='me'):
    sent = service.users().drafts().send(
        userId=user_id,
        body={'id': draft_id}
    ).execute()
    print(f'Draft sent as message: {sent["id"]}')
    return sent

# Create a draft for review
message = create_message(
    sender='me',
    to='client@example.com',
    subject='Proposal Follow-up',
    body_text='Dear Client,\n\nFollowing up on our proposal...'
)
draft = create_draft(gmail_service, message)
# Human reviews in Gmail, then agent sends:
# send_draft(gmail_service, draft['id'])

Memproses Banyak Email Secara Batch

Saat memproses banyak email, jangan mengambilnya satu per satu dalam perulangan yang rapat — Anda akan mencapai batas kuota. Gunakan perulangan terkendali dengan jeda singkat, atau fitur permintaan batch Gmail API untuk menggabungkan beberapa operasi dalam satu panggilan HTTP.

import time

def process_unread_emails(service, max_emails=20):
    messages = list_messages(
        service,
        query='is:unread label:inbox',
        max_results=max_emails
    )

    processed = []
    for i, msg_ref in enumerate(messages):
        # Rate-limit: process max 5 per second
        if i > 0 and i % 5 == 0:
            time.sleep(1)

        msg = get_message(service, msg_ref['id'])
        meta = extract_email_meta(msg)
        body = get_plain_text(msg)

        result = {
            'id': msg['id'],
            'from': meta['from'],
            'subject': meta['subject'],
            'body_preview': body[:200]
        }
        processed.append(result)
        mark_as_read(service, msg['id'])

    return processed

Membalas Email (Dalam Rangkaian Percakapan)

Untuk mengirim balasan dalam rangkaian percakapan, tetapkan header In-Reply-To dan References ke header Message-ID pesan asli, lalu teruskan threadId ke panggilan pengiriman. Dengan begitu, balasan tetap berada dalam rangkaian percakapan Gmail yang sama.

import base64
from email.mime.text import MIMEText

def create_reply(original_message, reply_text, sender='me'):
    original_msg_id = get_header(original_message, 'Message-ID')
    to = get_header(original_message, 'From')
    subject = get_header(original_message, 'Subject')
    if not subject.startswith('Re:'):
        subject = 'Re: ' + subject

    msg = MIMEText(reply_text, 'plain', 'utf-8')
    msg['From'] = sender
    msg['To'] = to
    msg['Subject'] = subject
    msg['In-Reply-To'] = original_msg_id
    msg['References'] = original_msg_id

    raw = base64.urlsafe_b64encode(msg.as_bytes()).decode('utf-8')
    return {
        'raw': raw,
        'threadId': original_message['threadId']  # keeps it in thread
    }

Pemeriksaan Singkat: Isi Email base64

Uji pemahaman Anda tentang penanganan pesan Gmail API.

Ringkasan Membaca dan Mengirim Email

Sekarang agen Anda dapat membaca dan mengirim email secara terprogram:

  • Membuat daftar: messages().list(q='is:unread') mengembalikan ID; gunakan sintaks pencarian Gmail
  • Mengambil: messages().get(id=..., format='full') mengembalikan pesan lengkap
  • Mengurai header: ekstrak From/Subject/Date dari payload.headers
  • Mendekode isi: base64.urlsafe_b64decode(data) untuk teks; telusuri bagian multipart secara rekursif
  • Mengirim: susun dengan MIMEText, lakukan pengodean base64url, lalu POST melalui messages().send()
  • Membalas dalam rangkaian percakapan: tetapkan header In-Reply-To + threadId dalam isi pengiriman

Pertanyaan yang Sering Diajukan

Apakah pelajaran “Membaca dan Mengirim Email Secara Terprogram” gratis?

Ya — teks lengkap “Membaca dan Mengirim Email Secara Terprogram” gratis dibaca di sini di web. Untuk praktiknya secara interaktif (editor kode bawaan dan tutor AI 24/7) dan buka sisa kursus AI Agents, upgrade ke CoddyKit PRO. Kursus AI Agents mencakup 4 pelajaran total.

Apa yang akan aku pelajari di “Membaca dan Mengirim Email Secara Terprogram”?

Mencantumkan pesan, mengambil isi pesan, dan mengirim email MIME. Kamu berlatih AI Agents dengan kode praktik yang langsung kamu jalankan di browser, dan tutor AI 24/7 menjawab pertanyaanmu saat kamu mengerjakan pelajaran ini.

Apakah aku perlu pengalaman untuk memulai AI Agents?

Tidak diperlukan pengalaman sebelumnya. AI Agents di CoddyKit dirancang untuk pemula hingga pelajar tingkat lanjut, jadi kamu bisa memulai di sini atau dari awal dan belajar sesuai kecepatan kamu sendiri. Ini adalah pelajaran 2 dari 4.

Berapa lama pelajaran “Membaca dan Mengirim Email Secara Terprogram” memakan waktu?

Sebagian besar pelajaran CoddyKit memakan waktu sekitar 5–10 menit. Setiap pelajaran ringkas dan interaktif, jadi kamu membuat kemajuan stabil dan melanjutkan dari tempat kamu tinggalkan di web dan aplikasi.

Bisakah aku menulis dan menjalankan kode dalam pelajaran AI Agents ini?

Ya. Setiap pelajaran AI Agents menyertakan editor kode bawaan, jadi kamu menulis dan menjalankan kode nyata langsung di browser dan mendapatkan umpan balik AI instan — tidak diperlukan penyiapan lokal.

Semua pelajaran dalam kursus ini

  1. Menghubungkan ke Gmail melalui API
  2. Membaca dan Mengirim Email Secara Terprogram
  3. Membuat dan Menanyakan Acara Kalender
  4. Membangun Agen Asisten Email Sederhana
← Kembali ke AI Agents