Kod gerçekte nerede
Bir doğrulama e-postasının, kodun yaşayabileceği en fazla üç yeri vardır ve hangisini okumanız gerektiği sonrasındaki her şeyi belirler. API'den gelen mesaj JSON'u üçünü birden verir: subject, text (düz metin parçası, ya da null) ve html (HTML parçası, ya da null).
| Nerede | Neye benzer | Nasıl okunur |
|---|---|---|
Düz metin parçası (text) | Your code is 481920. It expires in 10 minutes. | Var olduğunda önce bunu ayrıştırın. Biçimlendirme yoktur, çözülecek bir şey yoktur ve ifade sabittir. |
HTML parçası (html) | Aynı cümle bir tablonun içinde, genellikle rakamlar hücre başına bir tane olacak şekilde biçimlendirilmiş, ve her & bir varlık (entity) olarak yazılmış. | Etiketleri boşluklara indirgeyin, varlıkları çözün, boşlukları katlayın, sonra deseni uygulayın. Ham HTML üzerinde asla regex çalıştırmayın. |
| Konu satırı | 481920 is your verification code | Gönderen bunu yaptığında bir armağandır: ayrıştıracak hiçbir gövde yoktur. Konu üzerinde eşleştirin ve geri dönüş olarak gövdeyi kullanın. |
| Bir görsel | Tam olarak bu tür bir betiği alt etmek için resim olarak çizilmiş kod. | Nadirdir ve gönderenin otomasyon istemediğinin bir işaretidir. Gönderenin şablonu sizinse değiştirin; sizin değilse dürüst bir çözüm yolu yoktur. |
Tercih edilmesi gereken, düz metin parçasıdır ve çoğu şablonlama sisteminin HTML'den otomatik olarak ürettiği de odur — bu yüzden genellikle oradadır. null olduğunda, HTML parçası tek gövdedir ve sonraki iki bölüm onu güvenli şekilde okumakla ilgilidir.
Deseni kendi ifadenize sabitleyin
İçgüdüsel olan \d{6}'dır. Kodla eşleşir, ama aynı zamanda alt bilgideki yılla, adres bloğundaki posta koduyla, bir telefon numarasının son altı rakamıyla ve kodun iki satır üstünde görünen sipariş numarasıyla da eşleşir. Hangisi önce gelirse o kazanır ve test onu tam bir güvenle forma yazar.
| Desen | Ayrıca eşleştiği | Sonuç |
|---|---|---|
\d{6} | Yıllar, posta kodları, ayraçsız fiyatlar, sipariş numaraları, telefon numaraları, kargo takip numaraları. | Asla. Bu bir desen değil, yazı tura atmaktır. |
\b\d{6}\b | Yukarıdakilerin, her iki yanında da boşluk olan tam olarak altı rakam olma tesadüfü olan her biri — yine de çoğu. | Zar zor daha iyi. Kelime sınırları, bir kodun ne olduğunu bilmez. |
code is\D{0,12}(\d{6}) | Yalnızca kendi şablonunuzun koddan önce koyduğu kelimeleri izleyen altı rakam; aralarında bir iki nokta üst üste, bir boşluk ya da bir etiketten arta kalan boşluk kadar yer bırakarak. | Evet. Yalnızca kodla eşleşir, başka hiçbir şeyle değil, ve biri e-postayı yeniden ifade ettiği gün başarısız olur — ki bu, haberini almak isteyeceğiniz bir başarısızlıktır. |
\D{0,12}, pratik ayrıntıdır: etiketler boşluklarla değiştirildikten sonra, kelimeler ve rakamlar bir iki nokta üst üste, bir dizi boşluk ya da aralarında bulunan bir <strong>'ten kalan döküntüyle ayrılmış olabilir. En fazla bir düzine rakam-olmayan karakter, desenin başka bir sayıya atlamasına izin vermeden bunların hepsini kapsar.
Rakamları bölen şablonlar
Yaygın bir tasarım, kodun her rakamını kendi kutusuna koyar, böylece telefonda iyi okunur. HTML'de bu, altı tablo hücresi ya da altı <span>'dir ve sayı, kaynağın hiçbir yerinde art arda altı karakter olarak görünmez:
<p>Your code is</p>
<table><tr>
<td class="digit">4</td><td class="digit">8</td><td class="digit">1</td>
<td class="digit">9</td><td class="digit">2</td><td class="digit">0</td>
</tr></table>Ham HTML üzerindeki bir regex hiçbir şey bulamaz. Çözüm daha akıllı bir regex değildir; önce HTML'i sabit bir sırayla metne dönüştürmektir:
- Her etiketi bir boşlukla değiştirin. Hiçbir şeyle değil, bir boşlukla —
<td>4</td><td>8</td>, ardından gelene yapışık48değil,4 8haline gelmelidir. - Varlıkları çözün.
&, ,'. İki rakam arasındaki bir bölünemez boşluk, çözülene kadar bir regex için boşluk değildir. - Boşlukları katlayın, ardından rakamların aralarında boşluk olmasına izin vererek eşleştirin. Kutulu tasarım için
code is\D{0,12}(\d)\s*(\d)\s*(\d)\s*(\d)\s*(\d)\s*(\d)kullanıp grupları birleştirin; normal bir şablon için önceki bölümdeki yalın desen yeterlidir.
Aşağıdaki yardımcılar, birinci ve ikinci adımları sizin için yapar ve her iki parçayı aynı anda arar, böylece bir testin şablonun bu ay hangi tasarımı kullandığını bilmesi gerekmez.
En yeni mesaj her zaman doğru olan değildir
Buradaki her posta kutusu listelemesi en yeniden başlayarak döner ve çoğu ilk taslak messages[0]'ı okur. Üç durum bunu yanlış mesaj haline getirir:
- Tek bir eylemden iki mesaj
- Kayıt, göndereninin kuyruğunun boşaldığı sırayla bir hoş geldin postası ve bir kod postası gönderir. Yarı yarıya, hoş geldin mesajı en yenisidir. Herhangi bir şey almadan önce konu ya da gönderen üzerinde filtreleyin.
- Bir yeniden gönderim
- Test kodu iki kez istedi — biri yanlışlıkla, biri bilerek — ve sunucu yalnızca en sonuncuyu kabul ediyor. Eski mesaj hâlâ posta kutusundadır, hâlâ desenle eşleşir ve hâlâ artık geçersiz olan altı rakama ayrıştırılır.
- Önceki bir test çalıştırması
- Yalnızca adres yeniden kullanıldıysa, ki bu asla olmamalıdır. Çalıştırma başına yeni bir adres bu durumu imkansız kılar; birine sahip olamıyorsanız, aşağıdaki anlık görüntü geri dönüş çözümüdür.
Sağlam yaklaşım her test çalıştırıcısında aynıdır: postayı tetiklemeden önce posta kutusunda ne olduğuna bakın, ardından yalnızca henüz orada olmayan ve beklediğiniz konuyla eşleşen bir mesajı kabul edin.
// Remember what is already there, THEN trigger the resend, THEN wait for something new.
const before = new Set((await listMailbox(address)).messages.map(m => m.id));
await page.getByRole('button', { name: 'Resend code' }).click();
const fresh = await waitFor(address, m => !before.has(m.id) && /code/i.test(m.subject));Çalıştırma sırasında süresi dolan kodlar
Çoğu tek seferlik kod, beş ila on beş dakika geçerlidir. Bu, bir test paketi yirmi spec'i sıraya alana kadar cömert gelir; her biri kodunu başta ister ve sonda yazar. Üç kural kodu canlı tutar:
- Kodu olabildiğince geç isteyin. Gönderimi, diğer testler sırada beklerken çalışan bir kurulum adımında değil, beklemeden hemen önce tetikleyin.
- Bekleme son tarihini kodun ömrünün oldukça altında tutun. On dakikalık bir kod için altmış saniyelik bir son tarih, onu yazmak için dokuz dakika bırakır. On dakikalık bir son tarih ise hiçbir şey bırakmaz.
- Bir kodu asla başka bir test için saklamayın. Kodlar hem kısa ömürlüdür hem de tek kullanımlıktır; birini dağıtan paylaşılan bir fixture, tek bir sayı için iki test arasında bir yarıştır.
Doğrudan kullanılabilir extractor'lar
Aynı dört satırın üç sürümü: her iki parça birleştirilir, etiketler boşluğa çevrilir, varlıklar çözülür, boşluklar katlanır, ardından sabitlenmiş desen uygulanır. Deseni şablonunuzun ifadesiyle eşleşecek şekilde değiştirin, başka hiçbir şeye dokunmanız gerekmez.
Bir kabuktan, jq ile
curl -sG "https://grabmail.io/api/v1/message/$ID" --data-urlencode "mailbox=$ADDR" \
| jq -r '[.text, .html] | map(select(. != null)) | join(" ") | gsub("<[^>]*>"; " ")' \
| grep -oiE 'code is[^0-9]{0,12}[0-9]{6}' | grep -oE '[0-9]{6}' | head -1Python
import html
import re
TAGS = re.compile(r"<[^>]+>")
CODE = re.compile(r"code is\D{0,12}(\d{6})", re.I) # anchored on YOUR template's wording
def text_of(message: dict) -> str:
"""Both parts as plain text: tags out, entities decoded, whitespace folded."""
raw = f"{message.get('text') or ''}\n{message.get('html') or ''}"
return re.sub(r"\s+", " ", html.unescape(TAGS.sub(" ", raw)))
def code_from(message: dict, pattern: re.Pattern = CODE) -> str:
hit = pattern.search(text_of(message))
if not hit:
raise AssertionError(f"no code in message {message['id']!r} ({message['subject']!r})")
return hit.group(1)TypeScript
export type Message = { id: string; subject: string; text: string | null; html: string | null };
const TAGS = /<[^>]+>/g;
const ENTITIES: Record<string, string> = { '&': '&', '<': '<', '>': '>', '"': '"', ''': "'", ' ': ' ' };
/** Both parts as plain text: tags out, the common entities decoded, whitespace folded. */
export const textOf = (m: Message): string =>
`${m.text ?? ''}\n${m.html ?? ''}`
.replace(TAGS, ' ')
.replace(/&(amp|lt|gt|quot|#39|nbsp);/g, e => ENTITIES[e])
.replace(/\s+/g, ' ');
/** Anchored on your own wording. A reworded template fails loudly. */
export function codeFrom(m: Message, pattern = /code is\D{0,12}(\d{6})/i): string {
const hit = textOf(m).match(pattern);
if (!hit) throw new Error(`no code in message ${m.id} ("${m.subject}")`);
return hit[1];
}Bunların okuduğu mesaj JSON'u, API referansında belgelenen GET /api/v1/message/{id} ile alınır; kimliği baştan elde etmenizi sağlayan bekleme uçtan uca test rehberinde anlatılıyor, ayrıca Playwright, Cypress, Python ve Node.js için hazır yardımcılar olarak da mevcut.
Bitti demeden önce
- Şablonunuzun ifadesine sabitlenmiş ve şablonun yanında tutulan desen.
- Metin olarak aranan her iki parça: etiketler boşluğa çevrilir, varlıklar çözülür, boşluklar katlanır.
- Konu ya da gönderen üzerinde bir filtre, böylece bir hoş geldin postası bir kod postasına asla üstün gelmez.
- Herhangi bir yeniden gönderimden önce bir anlık görüntü ve ondan sonra yalnızca yeni mesajların kabul edilmesi.
- Kodun ömrünün oldukça altında bir bekleme son tarihi ve beklemeden hemen önce tetiklenen gönderim.
- Baktığı mesaj kimliğini ve konuyu belirten bir başarısızlık mesajı.
Bu, altı haneli bir extractor'ın yanlış sayı üzerinden geçtiği görülmüş her yolu kapsar. Aynı postayı okuyan bir ajan, aynı sorunlara sahiptir ve bunlar için bir araç eksiktir; MCP sunucusunun ona bir tahmin yerine mesajın tamamını vermesinin nedeni de budur — bir yapay zekâ ajanının okuyabileceği bir gelen kutusu bunu ele alır.
Sorular
Metin parçasını mı yoksa HTML parçasını mı okumalıyım?
Var olduğunda metin parçasını: sabittir ve çözülecek hiçbir şeyi yoktur. Yine de yardımcıların yaptığı gibi ikisini de arayın, böylece yalnızca HTML gönderen bir şablon yine de çalışır ve yalnızca metin gönderen bir şablon boş HTML'e asla takılmaz.
Kodumda harfler var. Desen değişir mi?
Yalnızca karakter sınıfı: ([A-Z0-9]{6}), ya da göndereninin kullandığı hangi alfabe olursa olsun, yine de öncesindeki ifadeye sabitlenmiş şekilde. Büyük/küçük harf garanti değilse i bayrağını ekleyin ve sınıfın, çapadan sonra gelen bir İngilizce kelimeyle de eşleşmediğinden emin olun.
Kod yerine magic link'ler ne olacak?
Aynı disiplin, farklı desen: URL'yi "ilk bağlantı" yerine bildiğiniz bir yol parçası — /confirm/, /auth/magic/ — üzerinden eşleştirin, çünkü işlemsel bir e-posta genellikle beş bağlantı taşır ve istediğiniz nadiren ilk sıradadır. Ziyaret etmeden önce &'i çözün.
Bir mesaj ne kadar süre okunabilir durumda kalır?
Ulaşmasından sonra, okunsun ya da okunmasın, 5 gün boyunca. Bu, herhangi bir kodun geçerli kaldığından çok daha uzundur, bu yüzden bir testin okumayı hiçbir zaman aceleye getirmesi gerekmez — yalnızca yazmayı.
Posta kutusunu yoklamadan kodu alabilir miyim?
REST üzerinden, hayır: bir son tarihle saniyede bir yoklarsınız; bu, belgelenmiş ritimdir ve hiçbir zaman kısıtlanmaz. MCP üzerinden ise mesaj gelene kadar çağrıyı açık tutan bir wait_for_message aracı vardır, ki bir yapay zekâ ajanının ihtiyaç duyduğu şekil de budur.
Bir API anahtarına ihtiyacım var mı?
Hayır. Genel alan adları anahtar, hesap ya da başlık gerektirmez. Yalnızca tek kullanımlık posta kara listelerinin dışında tutulan ücretli alan adı havuzu bir bearer token kullanır ve extractor kodu her iki durumda da aynıdır.


