दस्तावेज़ पर जाएँ
Ruby

थ्रेड

`threads.list`, `list_all`, `iterate`, `get`, `update`, `trash`, `snooze`, `unsnooze` और `list_attachments`।

पढ़ना

read_threads.rb
page = client.threads.list(  folder: "inbox",  query: "from:ada",  label_ids: ["INBOX", "IMPORTANT"],  limit: 25) if page.next_cursor  next_page = client.threads.list(folder: "inbox", cursor: page.next_cursor)  puts next_page.items.sizeend thread = client.threads.get("CAHk7pQ2x9LmZ4-mail.example.com")puts thread[:messageCount], thread[:hasUnread], thread[:totalReplies]

API थ्रेड को pageToken से पेज करता है। क्लाइंट इसे आपको next_cursor के रूप में देता है और cursor: के रूप में वापस लेता है, हर दूसरी सूची की तरह, और list_all तथा iterate आपके लिए इसका पीछा करते हैं। यह अपारदर्शी है: जो मिला वही वापस भेजें और ख़ुद कभी न बनाएँ।

सूची के फ़िल्टर snake_case में Ruby keywords हैं (label_ids:, date_from:), जबकि रिक्वेस्ट बॉडी के फ़ील्ड API के camelCase नाम बनाए रखते हैं (update पर addLabelIds:)। थ्रेड Symbol कुंजियों वाले Hash के रूप में लौटता है, इसलिए thread[:messageCount] गिनती पढ़ता है।

sort_threads.rb
last_week = client.threads.list_all(  sort: "oldest",  date_from: Time.now - (7 * 86_400),  date_to: Time.now,  from_contacts: true)puts last_week.size client.threads.iterate(sort: "sender") do |thread|  puts thread[:id]end

sort:, date_from:, date_to: और from_contacts: थ्रेड सूची के अपने नियंत्रण हैं। sort: newest, oldest, sender या subject है, और OpenEmail::THREAD_SORTS उनके नाम बताता है। तारीख़ें Time, DateTime या समय और offset वाली ISO 8601 स्ट्रिंग लेती हैं, और दोनों सिरे शामिल हैं। Ruby Date सादी तारीख़ के रूप में भेजी जाती है, जिसे ये फ़ील्ड 422 के साथ अस्वीकार करते हैं। from_contacts: true वह मेल रखता है जिसका सबसे नया संदेश किसी सहेजे गए संपर्क से आया हो। हर क्रम बिना कोई थ्रेड छोड़े या दोहराए अंत तक पेज होता है।

आख़िरी पेज आ जाने पर list_all एक Array लौटाता है। iterate हर थ्रेड को block में yield करता है और अगला पेज तभी लाता है जब लूप को उसकी ज़रूरत हो। block के बिना यह एक Enumerator लौटाता है, इसलिए first(10) या lazy ज़रूरत भर मिलते ही रुक जाते हैं।

व्यवस्थित करना

organise_threads.rb
thread_id = "CAHk7pQ2x9LmZ4-mail.example.com" client.threads.update(thread_id, read: true, addLabelIds: ["USER_DONE"], removeLabelIds: ["INBOX"]) client.threads.trash(thread_id)client.threads.snooze(thread_id, Time.now + 86_400)client.threads.unsnooze(thread_id)

पढ़े जाने की स्थिति यहाँ हर backend पर एक लेबल है, इसलिए यह लेबल सूचियों के साथ चलती है, और दोनों सेट करने पर क्रम तय है: हटाना जोड़ने से पहले लागू होता है, इसलिए दोनों सूचियों में मौजूद id अंत में थ्रेड पर रहती है। तीनों फ़ील्ड में से कम से कम एक मौजूद होना चाहिए।

addLabelIds labels.list से ids और ARCHIVE तथा STARRED जैसी सिस्टम ids लेता है। जो id किसी लेबल का नाम नहीं लेती उसे बनाया नहीं जाता, बल्कि 422 label_not_found के साथ अस्वीकार किया जाता है, इसलिए पहले labels.create से लेबल बनाएँ। client.threads.list(folder: "USER_DONE") लेबल वाला हर थ्रेड सूचीबद्ध करता है, चाहे वह किसी भी फ़ोल्डर में हो।

किसी संदेश पर attachment

attachments.rb
files = client.threads.list_attachments("CAHk7pQ2x9LmZ4-mail.example.com", "message_4c1b257a") files.each do |file|  puts "#{file[:filename]} #{file[:contentType]} #{file[:size]}"  File.binwrite(file[:filename], file[:content].unpack1("m")) unless file[:content].to_s.empty?end

list_attachments Hashes की एक Array लौटाता है। content base64 है, जिसे unpack1("m") binary String में बदल देता है, और जब सहेजे गए बाइट्स न मिलें तो यह ख़ाली स्ट्रिंग होती है, इसलिए डिकोड करने से पहले उसकी लंबाई जाँचें। एन्क्रिप्टेड संदेश का ciphertext इस सूची में है और किसी भी दूसरी फ़ाइल की तरह डाउनलोड होता है। PGP/MIME version हिस्सा और कोई भी अलग सिग्नेचर इसमें नहीं हैं। वे अपनी ids सिर्फ़ encryption.parts में रखते हैं, और कुछ नहीं।

ऐसा संदेश जो एन्क्रिप्टेड आया

यह gem न एन्क्रिप्ट करता है न डिक्रिप्ट। यह किसी और के एन्क्रिप्ट किए संदेश को नहीं खोल सकता, और एन्क्रिप्टेड संदेश भेज भी नहीं सकता। अगर send रिक्वेस्ट में एन्क्रिप्शन का मार्कर हो तो उसे अस्वीकार किया जाता है, क्योंकि बिना कुंजी वाले क्लाइंट का ऐसा दावा करने का कोई हक़ नहीं। OpenEmail ऐप में बनी कुंजियाँ उसी ब्राउज़र में रहती हैं जिसने उन्हें बनाया और यहाँ किसी चीज़ तक नहीं पहुँचतीं। जब वह ब्राउज़र कोई sealed संदेश खोलता है तो plaintext उसी में रहता है, और यह कॉल जो सहेजा गया संदेश पढ़ती है वह अब भी ciphertext है। threads.get आपको जो देता है वह पहचाना हुआ envelope है। PGP या S/MIME में लिपटकर आए संदेश में एक encryption Hash होता है, ताकि ख़ाली decodedBody ही आपको मिलने वाली इकलौती चीज़ न रहे। encryption संदेश का वह इकलौता फ़ील्ड है जिसकी API प्रतिबद्धता लेता है, क्योंकि यही वह फ़ील्ड है जिसकी अनुपस्थिति का अंदाज़ा लगाकर आप बच नहीं सकते।

encrypted_mail.rb
thread = client.threads.get("CAHk7pQ2x9LmZ4-mail.example.com") thread[:messages].each do |message|  next unless message[:encryption]  next unless OpenEmail.sealed?(message)   warn "cannot read this one: #{message[:encryption][:format]}"end

शाखा OpenEmail.sealed? से बनाएँ, फ़ील्ड की मौजूदगी पर कभी नहीं। पाँच में से दो फ़ॉर्मेट, pgp-signed और smime-signed, ऐसी बॉडी बताते हैं जो एक अलग सिग्नेचर के साथ खुले रूप में आई, इसलिए मौजूदगी पर रोक लगाना ऐसा मेल छिपा देता है जिसे छिपाने की किसी को ज़रूरत नहीं थी, और उपयोगकर्ता न उसे देख पाता है न समझा पाता है। OpenEmail.sealed? ठीक इसी कारण मौजूद है। सर्वर sealed सेट एक बार बताता है, gem की कॉपी उसी स्रोत से बनती है, और हाथ से लिखी तीसरी कॉपी ही वह है जो भटक जाती है। OpenEmail::MESSAGE_ENCRYPTION_FORMATS पाँचों फ़ॉर्मेट के नाम बताता है।

अनुपस्थिति का अर्थ plaintext नहीं है। detection शिप होने से पहले संग्रहीत हर संदेश पर, और हर उस चीज़ पर जो मेलबॉक्स तक ऐसे रास्ते से पहुँची जहाँ detector कभी नहीं चला, encryption अनुपस्थित है। यह दर्ज करता है कि किसी ने देखा ही नहीं। यह तथ्य हमारी coverage के बारे में है, मेल के बारे में नहीं, और इसे कुछ भी backfill नहीं करता।

ये बाकियों से कहाँ अलग हैं

  • किसी थ्रेड के messages की हर प्रविष्टि वही Hash है जो मेलबॉक्स ने सहेजा, फ़ील्ड की किसी तय सूची के बिना। इससे ज़्यादा का वादा करना क्लाइंट का ऐसे normalisation का दावा होता जो कोई करता ही नहीं। encryption फिर भी वह इकलौता फ़ील्ड है जिसकी API प्रतिबद्धता लेता है, क्योंकि जो क्लाइंट उस पर शाखा नहीं बना सकता वह sealed संदेश को ख़ाली संदेश पढ़ता है।
  • जिस रिक्वेस्ट को ईमानदारी से पूरा नहीं किया जा सकता वह 422 capability_unsupported है, जो OpenEmail::ValidationError के रूप में raise होता है, ऐसा जवाब नहीं जो सही दिखे और चुपचाप ग़लत हो।

पैरामीटर: threads.list

folderString
कौन-सा फ़ोल्डर सूचीबद्ध करना है। सर्वर इसका डिफ़ॉल्ट `inbox` रखता है, इसलिए इसे छोड़ने से सूची सब कुछ तक फैलने के बजाय सिमटती है। यह `query:` खोज पर भी लागू होता है, जब तक कि query ख़ुद `in:` से या `is:sent` जैसे फ़ोल्डर वाले `is:` से कोई फ़ोल्डर न बताए।
queryString
मेलबॉक्स की खोज सिंटैक्स। सादे शब्द सभी मौजूद होने चाहिए, और हर एक ढीले ढंग से मेल खाता है: case, accents और विभाजक अनदेखे होते हैं और किसी लंबे शब्द का हिस्सा भी गिना जाता है, इसलिए `min` और `ben jamin` दोनों “Benjamin” ढूँढ लेते हैं। उद्धरण-चिह्नों वाला वाक्यांश case और accents को छोड़कर लिखे अनुसार मिलाया जाता है, इसलिए `"ben jamin"` “Ben-Jamin” नहीं ढूँढता, और भराव वाले शब्द तब हटा दिए जाते हैं जब खोजने को कुछ और बचा हो। जब कुछ भी सटीक मेल न खाए तो इसके बजाय मिलती-जुलती वर्तनियाँ लौटाई जाती हैं, इसलिए `benjimin` “Benjamin” ढूँढ लेता है: कोई सादा शब्द, या `from:`, `to:`, `cc:`, `subject:`, `body:`, `filename:` या `label:` का मान, किसी शब्द की शुरुआत से एक टाइपो (बदला हुआ, छूटा हुआ, अतिरिक्त या अदला-बदला अक्षर) से अलग हो सकता है जब उसमें चार से सात अक्षर हों, और दो से जब आठ या ज़्यादा हों। उद्धरण-चिह्नों वाला वाक्यांश, अंक वाला शब्द, छोटा शब्द और बाहर रखा गया शब्द अब भी सटीक ही मेल खाते हैं, और आगे के पेज भी उसी तरह मिलान करते रहते हैं। `from:ada`, `label:Invoices`, `is:unread`, `has:pdf`, `before:2026/01/31` और `older_than:1y` जैसे operators से सीमित करें, और उन्हें `OR`, कोष्ठकों और आगे लगे `-` से जोड़ें। जिस मान को खोज इस्तेमाल नहीं कर सकती उसे सीमित करने के बजाय अनदेखा कर दिया जाता है। शब्द और `from:`, `to:`, `cc:`, `subject:` तथा `body:` operators नवीनतम संदेश के प्रेषक, प्राप्तकर्ता, विषय और markup हटाकर उसकी बॉडी के पहले 4,000 वर्ण पढ़ते हैं, जबकि `filename:` और `has:` पूरी बातचीत का हर अटैचमेंट पढ़ते हैं, और लेबल तथा फ़ोल्डर पूरी बातचीत पढ़ते हैं। यह उसी index को सीमित करता है जिसे बिना फ़िल्टर वाली सूची पढ़ती है। sealed संदेश कोई बॉडी टेक्स्ट नहीं सहेजते, इसलिए सिर्फ़ उनके प्रेषक, प्राप्तकर्ता और विषय मेल खा सकते हैं। सादा शब्द बातचीत के किसी भी अटैचमेंट के नाम से भी मेल खाता है, चाहे वह किसी भी संदेश के साथ आया हो।
label_idsString or Array<String>
सूची को उन थ्रेड तक सीमित करें जिन पर ये लेबल हैं। endpoint कॉमा से अलग स्ट्रिंग लेता है, और क्लाइंट आपके लिए Array या Set को जोड़कर एक बना देता है। आप कितने नाम दें इसकी कोई सीमा नहीं है।
limitInteger
कितने थ्रेड लौटाने हैं, 1 से 100। छोड़ने पर handler 25 इस्तेमाल करता है। डिफ़ॉल्ट schema में नहीं, handler में रहता है, इसलिए अनुपस्थित मान और स्पष्ट 25 एक जैसा व्यवहार करते हैं।
cursorString
पिछले पेज का `next_cursor`, जैसा का तैसा वापस भेजा गया। यह API का `pageToken` है, उस नाम से जो हर दूसरी सूची इस्तेमाल करती है, और यह अपारदर्शी है, इसलिए इसे कभी ख़ुद न बनाएँ और न बदलें।

जवाब: OpenEmail::Page

itemsArray<Hash>
इस पेज में हर थ्रेड के लिए एक Hash, API के `data` envelope से बाहर निकाला गया। हर एक में सिर्फ़ एक `object` मार्कर और एक `id` है। सूची में न विषय, न झलक, न प्रतिभागी, न लेबल होते हैं, इसलिए इससे ज़्यादा के लिए अपने चाहे थ्रेड पर `threads.get` कॉल करना होगा।
items[].idString
थ्रेड की id, `item[:id]` के रूप में पढ़ी जाती है, जिसे `threads.get`, `threads.update` और बाक़ी को बिना बदले देना है। पंक्ति चाहे फ़िल्टर की गई सूची से आई हो या `query:` खोज से, id वही रहती है।
has_more?Boolean
क्या आगे कोई पेज है, जहाँ API बताता है वहाँ उससे लिया जाता है और जहाँ नहीं बताता वहाँ `next_cursor` से निकाला जाता है।
next_cursorString or nil
API का `nextPageToken`, जिसे अगले पेज के लिए `cursor:` के रूप में वापस भेजना है, या आगे कोई पेज न हो तो nil। ख़ाली टोकन nil में बदल दिया जाता है, इसलिए `if page.next_cursor` और nil जाँच एक ही नतीजा देते हैं।