إرسال رسالة
`emails->send`: رسالة واحدة، الآن أو لاحقًا.
emails->send
$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' => new \SplFileInfo('invoice.pdf')]], 'threadId' => 'CAHk7pQ2x9LmZ4-mail.example.com', 'scheduledAt' => 'PT1H', 'tags' => ['order' => '4021'], 'tracking' => ['opens' => true, 'clicks' => true],]); echo $email['id'], ' ', $email['status'], PHP_EOL;تأخذ to وcc وbcc مستلمًا واحدًا أو قائمة منهم، والمستلم المنفرد يُغلَّف لك. وكل منهم قد يكون عنوانًا مجرّدًا، أو Name <addr@host>، أو مصفوفة فيها email وname.
الرسالة مصفوفة واحدة مفاتيحها أسماء حقول API، ولهذا يبقى replyTo وscheduledAt بصيغة camelCase، بينما idempotencyKey: وapiKey: وسيطان مسمّيان للاستدعاء وليسا جزءًا من الرسالة أبدًا. ولتغيير حقل واحد من رسالة بنيتها سابقًا، انشرها في مصفوفة جديدة: يحتفظ $client->emails->send([...$message, 'subject' => 'Re: your invoice']) بكل الحقول الأخرى ويستبدل الموضوع.
المعاملات
fromstring or arrayمطلوب- المُرسِل. عنوان مجرّد، أو `Name <addr@host>`، أو مصفوفة فيها `email` و`name`. ويجب أن يكون أحد العناوين التي يجوز لهذا المفتاح الإرسال باسمها، وإلا رمى الاستدعاء 403 `from_address_forbidden`. ولا يوجد مُرسِل احتياطي، فكل إرسال يسمّي دائمًا العنوان الذي يخرج منه.
tostring or arrayمطلوب- مستلم واحد أو قائمة منهم، والمستلم المنفرد يُغلَّف لك. بحد أقصى 50 في `to` و`cc` و`bcc` مجتمعة، وما زاد يعطي 422 `too_many_recipients`.
ccstring or array- تُحسب ضمن حد الخمسين مستلمًا.
bccstring or array- لا يُسمّى أبدًا في البايتات التي يتلقاها أي شخص آخر، لأن مُغلَّفًا واحدًا يُبثّ لكل مستلم. ويُحتسب ضمن الخمسين أيضًا.
replyTostring or array- عنوان واحد، يُرسل كترويسة Reply-To.
subjectstring- بحد أقصى 998 حرفًا، وهو حد السطر في RFC 5322. والافتراضي فارغ، والموضوع الفارغ يُستعاض عنه بموضوع القالب أو المسودة.
htmlstring- يلزم واحد من `html` أو `text` أو `draftId` أو `template`. وHTML هو ما يراه المستلمون عندما يُعطى `html` و`text` معًا. بحد أقصى 1,000,000 حرف.
textstring- جزء النص العادي، بحد أقصى 1,000,000 حرف.
templatearray- اعرض قالبًا مخزَّنًا على الخادم: مصفوفة فيها `id`، الذي يأخذ معرّفًا أو slug، و`version` اختياري (عدد صحيح) و`props` و`slots`. ويثبّت `version` مراجعة بعينها. اتركه لاستخدام ما يكون منشورًا عند قبول الطلب. والخاصية المجهولة أو الناقصة تعطي 422 بدل فراغ في الرسالة.
draftIdstring- أرسل مسودة محفوظة تحت هذا المُغلَّف، كما كُتبت. ولا يمكن جمعها مع `template` أو `translate`.
headersarray- اسم ترويسة مقابل قيمة نصية، مقصور على `X-*` و`List-*` وReply-To وPrecedence وAuto-Submitted وImportance وPriority وFeedback-ID. وأي ترويسة يضبطها النقل بنفسه تُرفض بالخطأ 422 `reserved_header` بدل أن تُحذف بصمت.
attachmentsarray- قائمة، كل عنصر فيها مصفوفة فيها `filename` و`content` و`contentType` اختياري، أو مصفوفة فيها `fileId` وحده يسمّي ملفًا موجودًا بالفعل في مساحة العمل، مثل ملف من `files->upload`. و`content` بترميز base64: التدفق من `fopen` أو `SplFileInfo` أو تدفق PSR-7 يُقرأ ويُرمَّز لك، أما السلسلة النصية فيجب أن تكون بترميز base64 مسبقًا. 20 ملفًا، بحد أقصى 5 ميغابايت للملفات المضمّنة مجتمعة بعد فك الترميز. والملف المخزَّن يمكن أن يكون أكبر ويُرسَل كرابط تنزيل.
attachmentDeliverystring- `mime` أو `link` أو `auto`. تحمل `auto` الملفات كروابط تنزيل متى تجاوزت 2 ميغابايت على نطاق له نطاق ملفات نشط، وداخل الرسالة فيما عدا ذلك. وإذا تُركت، طُبّق إعداد صندوق البريد، وهو `auto` افتراضيًا.
threadIdstring- الرد داخل محادثة قائمة. ويكتب النقل ترويستي In-Reply-To وReferences.
scheduledAtDateTimeInterface or string- `DateTimeInterface` يُرسَل كلحظة ISO 8601 بتوقيت UTC، أو لحظة ISO 8601 في صورة سلسلة نصية، أو مدة مثل `PT1H`. حتى سنة واحدة إلى الأمام، ولا يكون في الماضي أبدًا. ولا يمكن جمعه مع `cancellableForSeconds`. وسلسلة التاريخ التي لا وقت فيها، مثل `2027-01-01`، تُقرأ على أنها منتصف الليل UTC في ذلك اليوم، فمرّر لحظة حين تهمّ الساعة.
cancellableForSecondsint- من 0 إلى 900. نافذة تراجع على إرسال فوري: آلية التراجع في محرّر الرسائل، مكشوفة بدل أن تكون مثبتة في الشيفرة.
tagsarray- حتى 10 وسوم، بمفاتيح من 1 إلى 64 حرفًا من الحروف أو الأرقام أو `_` أو `-`، وقيم نصية حتى 256 حرفًا. تُعاد كما هي في كل قراءة ولا تُفسَّر أبدًا.
signaturebool- هل تحمل هذه الرسالة توقيع العنوان المرسِل: توقيعه الخاص، وإلا فتوقيع الالتقاط الشامل لعنوان التقطه، وإلا فتذييل OpenEmail ما لم يوقفه ذلك العنوان. إن لم يُحدَّد، يخرج متن `html` كما كُتب تمامًا بلا توقيع، ويحمله متن `text` وحده. اضبطه على false للبريد الذي يرسله برنامج نيابةً عن شخص، مثل إيصال أو إعادة تعيين كلمة مرور أو ملخص، فلا أحد منها يحتاج توقيع شخص أسفله. ولا تحمل الإرسالات بالقوالب ولا الإرسالات المشفّرة توقيعًا أبدًا.
trackingarray- مصفوفة فيها `opens` و`clicks` اختياريان، كل منهما من نوع bool: هل يُضاف بكسل فتح وتُعاد كتابة الروابط لهذه الرسالة. معطَّل ما لم يُفعَّل التتبّع للعنوان الذي تُرسل منه (أو للالتقاط الشامل الذي التقطه)، وأي مفتاح يُذكر هنا يحسم أمر تلك الرسالة وحدها أيًّا كان ضبط العنوان.
translatearray- أرسلها بلغة المستلم: مصفوفة فيها `to`، ومعه `from` و`subject` و`includeOriginal` اختياريًا. تأخذ `to` رمزًا أو اسمًا إنجليزيًا أو اسم اللغة بلغتها، و`subject` و`includeOriginal` كلاهما true افتراضيًا. ويُحسم ذلك عند قبول الطلب، فالرسالة المجدولة تحمل الكلمات التي اعتُمدت. ويُرفض مع `draftId`.
idempotencyKeystring- وسيط مسمّى للاستدعاء لا حقل في الرسالة. مفتاحك الخاص لهذا الإرسال، من 1 إلى 255 حرفًا من الحروف أو الأرقام أو `_` أو `.` أو `:` أو `-`. ودونه يولّد العميل مفتاحًا لكل استدعاء، فلا ترسل إعادات محاولته مرتين أبدًا، ومعه يُعاد تشغيل الإرسال الذي يعمل مجددًا في عملية أخرى بدل تكراره.
apiKeystring- وسيط مسمّى أيضًا. يرسل بهذا المفتاح بدل مفتاح العميل، لعملية ترسل بالنيابة عن عدة مساحات عمل.
الاستجابة
مصفوفة مفاتيحها أسماء API بصيغة camelCase، فيقرأ $email['status'] الحالة.
idstring- معرّف الإرسال، `msg_` متبوعًا بـ 24 حرفًا ست عشريًا. استخدمه مع `get` و`cancel` و`reschedule` و`getTracking`.
statusstring- queued أو scheduled أو sending أو sent أو partial أو bounced أو cancelled أو failed. اقرأ هذا بدل الاكتفاء بأن الاستدعاء قد عاد: فالإرسال الفوري يُنفَّذ داخل الطلب ويعود عادةً بالحالة `sent` أو `partial` أو `failed`، والإرسال المؤجَّل يعود بالحالة `queued` أو `scheduled`. و`partial` حالة قائمة بذاتها: فبعض المستلمين لديهم الرسالة ولا يمكن سحبها منهم، ومن ثَم فإعادة المحاولة خطأ والإبلاغ عن فشل كذب.
modestring- `live` أو `test`: نوع المفتاح الذي أرسلها. والإرسال التجريبي يُسجَّل ولا يُبثّ أبدًا. وتظهر حالته `sent`، مع ضبط `transport` على `test`، فتحقق من الاستجابة لا من صندوق وارد.
fromstring- العنوان الذي أُذن به فعلًا ووُضع على الشبكة، وهو ليس دائمًا العنوان المطلوب.
subjectstring or null- كما أُرسل.
messageIdstring or null- ترويسة Message-ID بحسب RFC 5322. تكون null إلى أن توجد رسالة MIME. وتعيد خدمة الإرسال كتابة الترويسة عند الخروج، فلا يحمل أي ارتداد أو تقرير تسليم هذه القيمة. و`id` هو ما يعود به أي حدث.
threadIdstring or null- المحادثة التي حطّت فيها.
transportstring or null- كيف غادرت الرسالة. null حتى الإرسال الفعلي.
attemptsint- كم مرة جُرّب الإرسال.
lastErrorstring or null- لماذا فشلت المحاولة الأخيرة، حرفيًا.
scheduledAtstring or null- لحظة ISO 8601 المقرر أن تنطلق فيها.
cancellableUntilstring or null- ما دام الوقت الحالي قبل هذه اللحظة، فإن `cancel` ما زال يعمل.
sentAtstring or null- لحظة ISO 8601 التي غادرت فيها.
tagsarray- ما أرسلته، معادًا كما هو.
sourcestring- composer أو api أو mcp أو ai أو queue: أي واجهة طلبت الإرسال. و`api` هو هذا العميل.
createdAtstring- لحظة ISO 8601 التي كُتب فيها السجل.
replayedbool- true حين يطابق Idempotency-Key إرسالًا موجودًا بالفعل. لم يُرسَل شيء جديد، وهذه هي الرسالة الأصلية كما هي الآن.
translationarray- يظهر فقط على رسالة تُرجمت، وفقط حيث يُحمل الطلب المخزَّن كاملًا: أي هذه الاستجابة و`get`. ويحمل `language` و`languageName` و`detectedSourceLanguage` و`subject` و`includeOriginal`، برموز لا بسجلات لغات كاملة. وسجل القائمة لا يحمله أبدًا، فغيابه هناك لا يقول شيئًا في أي اتجاه.
بلغة المستلِم
يكتب translate الرسالة بلغة شخص آخر قبل أن تنطلق. فالجسم، والموضوع ما لم توقف ذلك، يُترجمان عند قبول واجهة API للطلب، وما خرج هو ما يُرسل: فترجمة تعذّر إنتاجها ترفض الإرسال بدل أن تنشره باللغة التي كتبته بها.
$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'],]); print_r($email['translation'] ?? []);يحمل $email['translation'] حينها language بقيمة de، وlanguageName بقيمة German، وdetectedSourceLanguage بقيمة en، وsubject وincludeOriginal كلاهما true.
لم يقرأ أحد ذلك قبل أن ينطلق. وemails->translate هو الرحلة نفسها موقوفة قبل خطوة واحدة من نهايتها. اعرضها على شخص، ودعه يغيّرها، ثم أرسل ما اعتمده دون أي translate في الاستدعاء إطلاقًا. فتمريره مجددًا كان سيترجم مرة ثانية ويتخلص من تعديلاته.
$preview = $client->emails->translate([ 'subject' => 'Your September invoice', 'html' => '<p>Invoice attached. Payment is due on the 14th.</p>', 'to' => 'de',]); echo $preview['language']['native'], PHP_EOL, $preview['subject'], PHP_EOL, $preview['html'], PHP_EOL;echo 'Send it as it is? [y/N] '; $answer = fgets(STDIN); if ($answer !== false && strtolower(trim($answer)) === 'y') { $client->emails->send([ 'from' => '[email protected]', 'to' => '[email protected]', 'subject' => $preview['subject'], 'html' => $preview['html'], ]);}use OpenEmail\Constants\Languages;use OpenEmail\OpenEmail; echo count(Languages::ALL), PHP_EOL; $current = $client->languages->list();echo count($current), PHP_EOL; echo OpenEmail::resolveLanguage('Deutsch')['code'] ?? 'none', PHP_EOL;echo OpenEmail::resolveLanguage('zh-TW')['code'] ?? 'none', PHP_EOL;echo OpenEmail::languageByCode('DE')['native'] ?? 'none', PHP_EOL;var_dump(OpenEmail::isRtlLanguage('ar'));تطبع تلك الأسطر 200، وهي السجلات التي يأتي بها هذا الإصدار، ثم عدد ما تحمله API الآن، ثم de وzh-Hant وDeutsch وbool(true). والجدول مضمَّن، بترتيب أداة الاختيار، بوصفه OpenEmail\Constants\Languages::ALL، وهو قائمة من المصفوفات فيها code وlabel وnative وflag وrtl، فيمكن ملء أداة اختيار اللغة قبل أول طلب. ويعيد languages->list السجلات نفسها من الشبكة في صورة قائمة عادية، لمن يفضّل الحالية على تلك التي جاء بها هذا الإصدار. ويأخذ OpenEmail::resolveLanguage() رمزًا أو اسمًا إنجليزيًا أو اسم اللغة بلغتها أو اسمًا بديلًا (zh-TW اسم بديل لرمز لم يعد مدرجًا) ويعيد null حين لا يطابق شيء، ويطابق OpenEmail::languageByCode() رمزًا مطابقًا تمامًا بأي حالة أحرف، ويقول OpenEmail::isRtlLanguage() ما إذا كانت اللغة تُقرأ من اليمين إلى اليسار، كما هو حال ستة عشر من السجلات. ابحث في native وlabel وcode معًا، واعرض native أولًا، واحفظ الرمز.
لا يُعاد استدعاء emails->translate تلقائيًا. فهو ينفق استدعاءات للنموذج ولا يكتب شيئًا، فلا شيء هناك لجعله عديم أثر التكرار، وإعادة المحاولة بعد طلب بلا إجابة لن تشتري سوى الإجابة نفسها مرتين.
- اللغة التي لا تستطيع API مطابقتها تعطي
validation_errorعلىtranslate.to، قبل إرسال أي شيء. translation_too_longفوق 30,000 حرف، وtranslation_not_configuredعندما لا يكون في التثبيت أي إعداد للذكاء الاصطناعي، و429ai_quota_exceededعندما تكون مساحة العمل قد استنفدت إجراءات الذكاء الاصطناعي لهذا اليوم (يُصفَّر عند منتصف الليل بتوقيت UTC ولا تُعاد المحاولة)، وtranslation_failedعندما لا يجيب المزوّد. ولا يرسل أي منها الرسالة دون ترجمة كحل بديل.- يعمل مع
template: فالمخرَج المعروض هو ما يُترجم، فيخدم جسم مخزَّن واحد كل لغة يقرأ بها عملاؤك. والقالب الذي يعرض مستندًا كاملًا يحتفظ بـ doctype وكتل<style>وقواعد@font-faceالخاصة به: إذ لا يذهب إلى النموذج سوى الجسم ويُعاد الباقي حوله. ويُترك<title>كما هو، وهو ما لا يعرضه شيء على أي حال. - إعادة المحاولة لا تكلّف شيئًا إضافيًا. فالترجمة ليست جزءًا من بصمة منع التكرار (الطلب هو الجزء، بما فيه
translate)، لذا فإن إعادة إرسال طلب بلا إجابة بالـIdempotency-Keyنفسه تعيد تشغيل الرسالة الموجودة أصلًا بدل أن تترجم وترسل ثانية. - الرسالة المترجمة التي تكون في الطابور أو مجدولة تحتفظ بصياغتها المعتمدة. و
emails->rescheduleما زال ينقلها، بينما يرفضemails->updateأي صياغة جديدة بالخطأ 409translation_locked، فتغيير ما تقوله يعني الإلغاء وإعادة الإرسال.
المرفقات
content بترميز base64 عند النقل. أعطِ العميل شيئًا يستطيع قراءته فيرمّز البايتات لك: مورد تدفق من fopen، أو SplFileInfo، أو تدفق PSR-7 أو ملف مرفوع. أما السلسلة النصية فتُرسل كما هي، فيجب أن تكون بترميز base64 مسبقًا، وهذا ما يصنعه OpenEmail::toBase64() من البايتات التي تحتفظ بها في الذاكرة.
use OpenEmail\OpenEmail; $attachments = [ ['filename' => 'invoice.pdf', 'content' => OpenEmail::toBase64(file_get_contents('invoice.pdf')), 'contentType' => 'application/pdf'], ['filename' => 'report.csv', 'content' => new \SplFileInfo('report.csv')], ['filename' => 'contacts.csv', 'content' => fopen('contacts.csv', 'rb')], ['fileId' => 'file_6bb640f5b99e47deb758f1f5'],]; $client->emails->send([ 'from' => '[email protected]', 'to' => '[email protected]', 'subject' => 'Your documents', 'text' => 'All three are attached.', 'attachments' => $attachments,]);السلسلة النصية في content التي ليست بترميز base64 ترمي OpenEmail\Exception\InvalidArgumentException قبل إرسال أي شيء. أما البايتات الخام التي تصادف أنها تُقرأ كـ base64 فستخرج مشوّهة بدلًا من ذلك، فلا تمرّر بايتات ملف كما هي أبدًا: غلّفها بـ OpenEmail::toBase64()، أو مرّر الملف نفسه.
يتوفر OpenEmail::toBase64() إن احتجت إلى الترميز نفسه في مكان آخر. يأخذ سلسلة من البايتات أو مورد تدفق أو SplFileInfo أو تدفق PSR-7 ويعيد base64 دون فواصل أسطر.