پرش به مستندات
Ruby

ارسال یک ایمیل

`emails.send`: یک پیام، حالا یا بعداً.

emails.send

send_email.rb
email = client.emails.send(  from: {email: "[email protected]", name: "Acme Billing"},  to: ["[email protected]", "Grace <[email protected]>"],  cc: "[email protected]",  bcc: [{email: "[email protected]"}],  replyTo: "[email protected]",  subject: "Your September invoice",  html: "<p>Invoice attached.</p>",  text: "Invoice attached.",  headers: {"X-Campaign" => "invoices"},  attachments: [{filename: "invoice.pdf", content: Pathname("invoice.pdf")}],  threadId: "CAHk7pQ2x9LmZ4-mail.example.com",  scheduledAt: "PT1H",  tags: {order: "4021"},  tracking: {opens: true, clicks: true}) puts email[:id], email[:status]

to، cc و bcc یک گیرنده یا یک Array از گیرندگان می‌گیرند، و اگر یکی تنها بدهید برایتان در Array پیچیده می‌شود. هرکدام می‌تواند نشانی خام، Name <addr@host> یا یک Hash با email و name باشد.

پیام به‌صورت آرگومان‌های کلیدواژه‌ای یا به‌صورت یک Hash داده می‌شود. آرگومان‌های کلیدواژه‌ای که کنار یک Hash بیایند در آن ادغام می‌شوند و جایی که هر دو یک فیلد را نام ببرند مقدم‌اند، پس client.emails.send(message, subject: "Re: your invoice") یک فیلد از پیامی را که پیش‌تر ساخته‌اید تغییر می‌دهد. کلیدها نام‌های API را نگه می‌دارند، و به همین دلیل replyTo و scheduledAt به شکل camelCase می‌مانند، در حالی که idempotency_key: و api_key: گزینه‌های فراخوانی‌اند و هرگز بخشی از پیام نیستند.

پارامترها

fromString or Hashالزامی
فرستنده. یک نشانی خام، `Name <addr@host>` یا یک Hash با `email` و `name`. باید نشانی‌ای باشد که این کلید اجازهٔ ارسال با آن را دارد، وگرنه فراخوانی یک 403 `from_address_forbidden` را raise می‌کند. هیچ فرستندهٔ جایگزینی وجود ندارد، پس هر ارسال همیشه نشانی‌ای را که از آن فرستاده می‌شود نام می‌برد.
toString, Hash or Arrayالزامی
یک گیرنده یا یک Array از گیرندگان، و اگر یکی تنها بدهید برایتان در Array پیچیده می‌شود. حداکثر 50 نشانی روی هم در `to`، `cc` و `bcc`، و بیشتر از آن یک 422 `too_many_recipients` است.
ccString, Hash or Array
به سقف 50 گیرنده شمرده می‌شود.
bccString, Hash or Array
هرگز در بایت‌هایی که دیگران دریافت می‌کنند نام برده نمی‌شود، چون به ازای هر گیرنده یک پاکت منتقل می‌شود. در شمار همان 50 هم حساب می‌شود.
replyToString or Hash
یک نشانی تکی، که به شکل هدر Reply-To فرستاده می‌شود.
subjectString
حداکثر 998 نویسه، همان حد طول خط RFC 5322. پیش‌فرض خالی است، و موضوع خالی به موضوع قالب یا پیش‌نویس بازمی‌گردد.
htmlString
یکی از `html`، `text`، `draftId` یا `template` الزامی است. وقتی هر دو `html` و `text` داده شوند، آنچه گیرندگان می‌بینند HTML است. حداکثر 1,000,000 نویسه.
textString
بخش متن ساده، حداکثر 1,000,000 نویسه.
templateHash
یک قالب ذخیره‌شده را سمت سرور رندر می‌کند: یک Hash با `id`، که شناسه یا slug می‌گیرد، و `version` اختیاری (یک Integer)، `props` و `slots`. `version` یک نسخه را سنجاق می‌کند. آن را ننویسید تا هرچه هنگام پذیرش درخواست منتشر شده باشد به کار رود. prop ناشناخته یا نیامده، به‌جای جای خالی در پیام، یک 422 است.
draftIdString
یک پیش‌نویس ذخیره‌شده را همان‌طور که نوشته شده، زیر این پاکت بفرستید. نمی‌توان آن را با `template` یا `translate` ترکیب کرد.
headersHash
نام سرآیند به مقدار String، محدود به `X-*`، `List-*`، Reply-To، Precedence، Auto-Submitted، Importance، Priority و Feedback-ID. هر چیزی که خودِ لایهٔ انتقال تنظیم می‌کند، به‌جای حذف بی‌سروصدا، با یک 422 `reserved_header` رد می‌شود.
attachmentsArray<Hash>
هرکدام یک Hash با `filename`، `content` و `contentType` اختیاری، یا یک Hash فقط با `fileId` که فایلی را نام می‌برد که از پیش در فضای کاری هست، مثلاً فایلی از `files.upload`. برای `content` بایت بدهید تا برایتان با base64 رمزگذاری شود. 20 فایل، با سقف مجموع 5 MB برای فایل‌های درون‌خطی پس از رمزگشایی. فایل ذخیره‌شده می‌تواند بزرگ‌تر باشد و به شکل پیوند دانلود می‌رود.
attachmentDeliveryString
`mime`، `link` یا `auto`. `auto` فایل‌ها را وقتی روی دامنه‌ای با files domain فعال از 2 MB بگذرند به شکل پیوند دانلود می‌برد و در غیر این صورت درون پیام. اگر ننویسید، تنظیم صندوق اعمال می‌شود، که پیش‌فرضش `auto` است.
threadIdString
پاسخ‌دادن درون یک thread موجود. حمل‌ونقل In-Reply-To و References را می‌نویسد.
scheduledAtTime, DateTime or String
یک Time یا DateTime که به‌صورت یک لحظهٔ ISO 8601 با UTC فرستاده می‌شود، یک لحظهٔ ISO 8601 به‌صورت String، یا مدتی مانند `PT1H`. تا یک سال بعد، هرگز در گذشته. نمی‌توان آن را با `cancellableForSeconds` ترکیب کرد. یک Date در Ruby به‌صورت تاریخ خالی فرستاده می‌شود که API آن را نیمه‌شب UTC همان روز می‌خواند، پس وقتی ساعت مهم است یک Time بدهید.
cancellableForSecondsInteger
0 تا 900. پنجرهٔ لغو روی یک ارسال فوری: همان سازوکار لغوِ composer، که به‌جای hardcode شدن در دسترس گذاشته شده است.
tagsHash
حداکثر 10 برچسب، با کلیدهای 1 تا 64 نویسه از حروف، ارقام، `_` یا `-` و مقدارهای String تا 256 نویسه. در هر خواندن همان‌طور بازگردانده می‌شوند و هرگز تفسیر نمی‌شوند.
signatureBoolean
اینکه این پیام امضای نشانی فرستنده را داشته باشد یا نه: امضای خودِ آن نشانی، وگرنه امضای catch-all برای نشانی‌ای که catch-all دریافت کرده، وگرنه پانویس OpenEmail، مگر اینکه آن نشانی خاموشش کرده باشد. اگر مشخص نشود، بدنهٔ `html` دقیقاً همان‌طور که نوشته شده و بدون امضا می‌رود و بدنهٔ فقط `text` آن را دارد. برای نامه‌ای که برنامه‌ای از طرف کسی می‌فرستد، مثل رسید، بازنشانی گذرواژه یا خلاصه، `false` بگذارید، چون هیچ‌کدام امضای یک شخص را زیر خود نمی‌خواهند. ارسال‌های قالبی و ارسال‌های رمزگذاری‌شده هرگز امضا ندارند.
trackingHash
یک Hash با `opens` و `clicks` بولی و اختیاری: اینکه آیا برای این پیام pixel باز شدن افزوده و پیوندها بازنویسی شوند یا نه. خاموش است مگر ردیابی برای نشانی‌ای که پیام از آن می‌رود (یا catch-allی که آن را گرفته) روشن شده باشد، و هر یک از این دو کلید که اینجا بیاید تکلیف همان یک پیام را، هرطور که نشانی تنظیم شده باشد، روشن می‌کند.
translateHash
آن را به زبان گیرنده بفرستید: یک Hash با `to` و `from`، `subject` و `includeOriginal` اختیاری. `to` یک کد، یک نام انگلیسی یا نام خودِ زبان می‌گیرد، و `subject` و `includeOriginal` هر دو پیش‌فرض true دارند. هنگام پذیرفته‌شدن درخواست تعیین می‌شود، پس پیام زمان‌بندی‌شده همان کلماتی را می‌برد که تأیید شده‌اند. در کنار `draftId` رد می‌شود.
idempotency_keyString
کلید خودتان برای این ارسال، 1 تا 255 نویسه از حروف، ارقام، `_`، `.`، `:` یا `-`. بدون آن، کلاینت برای هر فراخوانی کلیدی تولید می‌کند، پس تلاش‌های دوبارهٔ خودش هرگز دو بار نمی‌فرستند، و با آن، ارسالی که در فرایندی دیگر دوباره اجرا شود به‌جای تکرار، بازپخش می‌شود.
api_keyString
به‌جای کلید کلاینت با این کلید می‌فرستد، برای فرایندی که از طرف چند فضای کاری ارسال می‌کند.

پاسخ

یک Hash با کلیدهای Symbol، پس email[:status] وضعیت را می‌خواند.

idString
شناسهٔ ارسال، `msg_` و به دنبالش 24 نویسهٔ hex. برای `get`، `cancel`، `reschedule` و `get_tracking` از آن استفاده کنید.
statusString
queued، scheduled، sending، sent، partial، bounced، cancelled یا failed. به‌جای این واقعیت که فراخوانی بازگشت، همین را بخوانید: ارسال فوری درون همان درخواست روانه می‌شود و معمولاً با `sent`، `partial` یا `failed` برمی‌گردد، و ارسالی که نگه داشته شده با `queued` یا `scheduled` برمی‌گردد. `partial` وضعیتی از آنِ خودش است: بعضی گیرندگان آن را دریافت کرده‌اند و نمی‌شود پس گرفت، پس تلاش دوباره اشتباه است و گزارش شکست دروغ.
modeString
`live` یا `test`: کدام نوع کلید آن را فرستاده. ارسال در حالت test ثبت می‌شود و هرگز منتقل نمی‌شود. وضعیتش `sent` است و `transport` برابر `test`، پس روی پاسخ assert کنید نه روی یک صندوق ورودی.
fromString
نشانی‌ای که واقعاً مجاز شمرده و روی سیم گذاشته شد، که همیشه همان نشانی درخواست‌شده نیست.
subjectString or nil
همان‌طور که فرستاده شد.
messageIdString or nil
همان Message-ID مربوط به RFC 5322. تا وقتی MIME وجود نداشته باشد nil است. سرویس ارسال سرآیند را در مسیر خروج بازنویسی می‌کند، پس هیچ bounce یا گزارش تحویلی این مقدار را حمل نمی‌کند. چیزی که رویداد با آن برمی‌گردد `id` است.
threadIdString or nil
thread ای که در آن نشست.
transportString or nil
پیام چگونه رفت. تا پیش از ارسال nil است.
attemptsInteger
چند بار ارسال تلاش شده است.
lastErrorString or nil
چرا آخرین تلاش شکست خورد، عیناً.
scheduledAtString or nil
لحظهٔ ISO 8601 که قرار است برود.
cancellableUntilString or nil
تا وقتی اکنون پیش از این لحظه است، `cancel` هنوز کار می‌کند.
sentAtString or nil
لحظهٔ ISO 8601 که رفت.
tagsHash
آنچه فرستادید، بازتاب‌داده‌شده.
sourceString
composer، api، mcp، ai یا queue: کدام سطح درخواست داده است. `api` همین کلاینت است.
createdAtString
لحظهٔ ISO 8601 که رکورد نوشته شد.
replayedBoolean
وقتی true است که یک Idempotency-Key با ارسالی که از پیش وجود داشته مطابقت کرده باشد. چیز تازه‌ای فرستاده نشده، و این همان پیام اصلی در وضعیت کنونی‌اش است.
translationHash
فقط روی پیامی حاضر است که ترجمه شده، و تنها جایی که کل درخواست ذخیره‌شده حمل می‌شود: همین پاسخ و `get`. شامل `language`، `languageName`، `detectedSourceLanguage`، `subject` و `includeOriginal` است، به شکل کد نه ردیف کامل زبان. ردیف فهرست هرگز آن را ندارد، پس غیابش آنجا به هیچ سمتی چیزی نمی‌گوید.

به زبان گیرنده

translate پیام را پیش از رفتنش به زبان کسی دیگر می‌نویسد. بدنه (و موضوع، مگر آن را خاموش کنید) وقتی API درخواست را می‌پذیرد ترجمه می‌شود، و آنچه بیرون آمد همان است که بیرون می‌رود: ترجمه‌ای که نشود تولیدش کرد، به‌جای فرستادن پیام به زبانی که نوشته‌اید، ارسال را رد می‌کند.

translate.rb
email = client.emails.send(  from: "[email protected]",  to: "[email protected]",  subject: "Your September invoice",  html: "<p>Invoice attached. Payment is due on the 14th.</p>",  translate: {to: "de"}) p email[:translation]

آنگاه email[:translation] این را می‌خواند: {language: "de", languageName: "German", detectedSourceLanguage: "en", subject: true, includeOriginal: true}.

کسی پیش از رفتنش آن را نخواند. emails.translate همان رفت‌وبرگشت است که یک گام زودتر متوقف شده. آن را به یک آدم نشان دهید، بگذارید تغییرش دهد، و بعد آنچه را تأیید کرده بدون هیچ translateای روی فراخوانی بفرستید. دادن دوبارهٔ آن، متن را بار دوم ترجمه می‌کرد و ویرایش‌هایش را دور می‌ریخت.

preview_translation.rb
preview = client.emails.translate(  subject: "Your September invoice",  html: "<p>Invoice attached. Payment is due on the 14th.</p>",  to: "de") puts preview.dig(:language, :native), preview[:subject], preview[:html]print "Send it as it is? [y/N] " if $stdin.gets.to_s.strip.casecmp?("y")  client.emails.send(    from: "[email protected]",    to: "[email protected]",    subject: preview[:subject],    html: preview[:html]  )end
languages.rb
p OpenEmail::LANGUAGES.size current = client.languages.listp current.size p OpenEmail.resolve_language("Deutsch")&.fetch(:code)p OpenEmail.resolve_language("zh-TW")&.fetch(:code)p OpenEmail.language_by_code("DE")&.fetch(:native)p OpenEmail.rtl_language?("ar")

این خط‌ها 200 را چاپ می‌کنند، یعنی ردیف‌هایی که این نسخه با آن‌ها عرضه می‌شود، سپس تعدادی که API اکنون دارد، سپس "de"، "zh-Hant"، "Deutsch" و true. این جدول به ترتیب picker، به شکل OpenEmail::LANGUAGES در بسته هست، یک Array منجمد از Hashهایی با code، label، native، flag و rtl، پس می‌شود یک picker را پیش از نخستین درخواست پر کرد. languages.list همان ردیف‌ها را از روی سیم به شکل یک Array ساده برمی‌گرداند، برای فراخواننده‌ای که ردیف‌های کنونی را به ردیف‌هایی که این نسخه با آن‌ها عرضه شده ترجیح می‌دهد. OpenEmail.resolve_language یک کد، یک نام انگلیسی، یک نام بومی یا یک نام مستعار می‌گیرد (zh-TW نام مستعار کدی است که دیگر فهرست نمی‌شود) و وقتی چیزی مطابقت نکند nil برمی‌گرداند، OpenEmail.language_by_code کد را دقیقاً و بدون حساسیت به بزرگی و کوچکی حروف تطبیق می‌دهد، و شانزده ردیف از راست به چپ‌اند. native، label و code را با هم جست‌وجو کنید، native را اول نشان دهید، و کد را ذخیره کنید.

emails.translate به‌طور خودکار دوباره تلاش نمی‌شود. فراخوانی مدل خرج می‌کند و چیزی نمی‌نویسد، پس چیزی برای idempotent کردن نیست و تلاش دوباره پس از درخواستی بی‌پاسخ فقط همان پاسخ را دو بار می‌خرد.

  • زبانی که API نتواند تطبیقش دهد یک validation_error روی translate.to است، پیش از آنکه چیزی فرستاده شود.
  • translation_too_long برای بیش از 30,000 نویسه، translation_not_configured وقتی نصب هیچ AI پیکربندی‌شده‌ای ندارد، یک 429 ai_quota_exceeded وقتی آن فضای کاری کنش‌های هوش مصنوعی امروز را مصرف کرده باشد (در نیمه‌شب UTC بازنشانی می‌شود و دوباره تلاش نمی‌شود)، translation_failed وقتی ارائه‌دهنده پاسخ نداده است. هیچ‌کدام به‌عنوان جایگزین، پیام را ترجمه‌نشده نمی‌فرستند.
  • با template کار می‌کند: آنچه ترجمه می‌شود خروجیِ RENDER شده است، پس یک بدنهٔ ذخیره‌شده به هر زبانی که مشتریانتان می‌خوانند خدمت می‌کند. قالبی که یک سند کامل رندر می‌کند، doctype، بلوک‌های <style> و قاعده‌های @font-face خود را نگه می‌دارد: فقط بدنه به مدل می‌رود و بقیه دوباره دورش گذاشته می‌شود. <title> آن دست‌نخورده می‌ماند، که به‌هرحال چیزی نمایشش نمی‌دهد.
  • تلاش دوباره هزینهٔ اضافه ندارد. ترجمه بخشی از اثرانگشت idempotency نیست (خودِ درخواست هست، با translate و همه)، پس بازفرستادن یک ارسال بی‌پاسخ با همان Idempotency-Key پیامی را که از پیش وجود دارد بازپخش می‌کند، نه اینکه بار دوم ترجمه و ارسال کند.
  • پیام ترجمه‌شده‌ای که queued یا scheduled باشد متن تأییدشده‌اش را نگه می‌دارد. emails.reschedule هنوز جابه‌جایش می‌کند، در حالی که emails.update متن تازه را با یک 409 translation_locked رد می‌کند، پس تغییر آنچه می‌گوید یعنی لغو و ارسال دوباره.

پیوست‌ها

content روی سیم base64 است. بایت‌ها را بدهید تا برایتان رمزگذاری شوند: یک String دودویی مانند آنچه File.binread برمی‌گرداند، یک IO مانند یک File باز، یا یک Pathname که برایتان خوانده می‌شود.

attachments.rb
attachments = [  {filename: "invoice.pdf", content: File.binread("invoice.pdf"), contentType: "application/pdf"},  {filename: "report.pdf", content: Pathname("report.pdf")},  {fileId: "file_6bb640f5b99e47deb758f1f5"}] client.emails.send(  from: "[email protected]",  to: "[email protected]",  subject: "Your documents",  text: "Both are attached.",  attachments:)

Stringی که به‌عنوان متن برچسب خورده، مانند آنچه File.read برمی‌گرداند، از پیش base64 فرض می‌شود، و اگر base64 نباشد پیش از فرستادن هر چیزی ArgumentError را raise می‌کند. فایل‌ها را با File.binread بخوانید، یا روی بایت‌هایی که با برچسب متن رسیده‌اند .b را فراخوانی کنید.

اگر همین رمزگذاری را جای دیگری لازم دارید، OpenEmail.to_base64 هست. یک String دودویی، یک IO یا یک Pathname می‌گیرد و base64 سخت‌گیرانه (strict)، بدون شکست خط، برمی‌گرداند.