تخطَّ إلى المستندات
Python

إرسال رسالة

`emails.send`: رسالة واحدة، الآن أو لاحقًا.

emails.send

send_email.py
from openemail import openemail email = openemail.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': pdf_bytes}],    'threadId': 'thread_…',    'scheduledAt': 'PT1H',    'tags': {'order': '4021'},    'tracking': {'opens': True, 'clicks': True},})

تأخذ to وcc وbcc مستلمًا واحدًا أو عدة مستلمين، والمستلم المفرد يُلفّ لك. وقد يكون كل منهم عنوانًا مجردًا أو بالشكل Name <addr@host> أو {'email': ..., 'name': ...}.

المعاملات

fromRecipientInputمطلوب
المُرسِل. عنوان مجرّد، أو `Name <addr@host>`، أو قاموس. ويجب أن يكون أحد العناوين التي يجوز لهذا المفتاح الإرسال باسمها. ولا يوجد مُرسِل احتياطي، فكل إرسال يسمّي دائمًا العنوان الذي يخرج منه.
toRecipientInput | list[RecipientInput]مطلوب
مستلم واحد أو عدة؛ والمفرد يُلفّ لك. وبحد أقصى 50 عبر to وcc وbcc مجتمعة.
ccRecipientInput | list[RecipientInput]
تُحسب ضمن حد الخمسين مستلمًا.
bccRecipientInput | list[RecipientInput]
لا يُسمّى أبدًا في البايتات التي يتلقاها أي شخص آخر، لأن مُغلَّفًا واحدًا يُبثّ لكل مستلم.
replyToRecipientInput
عنوان واحد، يُرسل كترويسة Reply-To.
subjectstr
بحد أقصى 998 حرفًا، وهو حد السطر في RFC 5322. والافتراضي فارغ.
htmlstr
يلزم واحد من html أو text أو draftId أو template. وHTML هو ما يراه المستلمون عندما يُعطى html وtext معًا.
textstr
جزء النص العادي.
templateEmailSendTemplate
اعرض قالبًا مخزَّنًا على الخادم. و`version` تثبّت المراجعة؛ احذفها لاستخدام ما يكون منشورًا عند قبول الطلب. وخاصية مجهولة أو ناقصة ترد 422 بدل فراغ في الرسالة.
draftIdstr
أرسل مسودة محفوظة تحت هذا المُغلَّف.
headersdict[str, str]
`X-*` و`List-*` وReply-To وPrecedence وAuto-Submitted وImportance وPriority وFeedback-Id. وأي ترويسة يضبطها النقل بنفسه تُرفض بدل أن تُسقط بصمت.
attachmentslist[AttachmentInput]
`{'filename': ..., 'content': ...}` مع `'contentType'` اختياري، أو `{'fileId': ...}` يسمي ملفًا موجودًا بالفعل في مساحة العمل، مثل ملف من `files.upload`. مرّر بايتات في content وتُرمَّز لك بترميز base64. 20 ملفًا، بحد أقصى 5 ميغابايت للملفات المضمّنة مجتمعة بعد فك الترميز. والملف المخزَّن يجوز أن يكون أكبر ويسافر كرابط تنزيل.
attachmentDeliveryAttachmentDeliveryMode
`mime` أو `link` أو `auto`. تحمل `auto` الملفات كروابط تنزيل متى تجاوزت 2 ميغابايت على نطاق له نطاق ملفات نشط، وداخل الرسالة فيما عدا ذلك. وإذا تُركت، طُبّق إعداد صندوق البريد، وهو `auto` افتراضيًا.
threadIdstr
الرد داخل محادثة قائمة. ويكتب النقل ترويستي In-Reply-To وReferences.
scheduledAtdatetime | str
`datetime` أو لحظة بصيغة ISO-8601 أو مدة مثل `PT1H`. حتى سنة من الآن، ولا تكون في الماضي أبدًا. ولا يمكن الجمع بينها وبين cancellableForSeconds.
cancellableForSecondsint
من 0 إلى 900. نافذة تراجع على إرسال فوري: آلية التراجع في محرّر الرسائل، مكشوفة بدل أن تكون مثبتة في الشيفرة.
tagsdict[str, str]
حتى 10 وسوم، تُعاد كما هي وقابلة للترشيح. ولا تُفسَّر أبدًا.
signaturebool
هل تحمل هذه الرسالة توقيع العنوان المرسِل: توقيعه الخاص، وإلا فتوقيع الالتقاط الشامل لعنوان التقطه، وإلا فتذييل OpenEmail ما لم يوقفه ذلك العنوان. إن لم يُحدَّد، يخرج متن `html` كما كُتب تمامًا بلا توقيع، ويحمله متن `text` وحده. اضبطه على `False` للبريد الذي يرسله برنامج نيابةً عن شخص، مثل إيصال أو إعادة تعيين كلمة مرور أو ملخص، فلا أحد منها يحتاج توقيع شخص أسفله.
trackingTrackingRequest
ما إذا كان سيُضاف بكسل فتح وتُعاد كتابة الروابط لهذه الرسالة. معطّل ما لم يكن التتبع قد فُعِّل للعنوان المرسَلة منه (أو للالتقاط الشامل الذي التقطه)، وأي من الحقلين المذكور هنا يحسم تلك الرسالة الواحدة أيًّا كان ضبط العنوان.
translateSendTranslateOptions
أرسلها بلغة المستلم. تأخذ `to` رمزًا أو اسمًا إنجليزيًا أو اسم اللغة بلغتها؛ و`subject` و`includeOriginal` كلاهما true افتراضيًا. ويُحل ذلك عند قبول الطلب، فالرسالة المجدولة تحمل الكلمات التي اعتُمدت. ويُرفض مع `draftId`.

الاستجابة

idstr
معرّف الإرسال، `msg_…`. استخدمه مع `get` و`cancel` و`reschedule` و`get_tracking`.
statusEmailStatus
queued أو scheduled أو sending أو sent أو partial أو bounced أو cancelled أو failed. اقرأ هذا بدل الاكتفاء بأن الاستدعاء قد عاد. و`partial` حالة قائمة بذاتها: فبعض المستلمين لديهم الرسالة ولا يمكن سحبها منهم، ومن ثَم فإعادة المحاولة خطأ والإبلاغ عن فشل كذب.
modeApiKeyMode
أي نوع من المفاتيح أرسلها. والإرسال في وضع الاختبار يُسجَّل ولا يُبثّ أبدًا.
fromstr
العنوان الذي أُذن به فعلًا ووُضع على الشبكة، وهو ليس دائمًا العنوان المطلوب.
subjectstr | None
كما أُرسل.
messageIdstr | None
ترويسة Message-ID بحسب RFC 5322. تكون null إلى أن توجد رسالة MIME. وتعيد خدمة الإرسال كتابة الترويسة عند الخروج، فلا يحمل أي ارتداد أو تقرير تسليم هذه القيمة. و`id` هو ما يعود به أي حدث.
threadIdstr | None
المحادثة التي حطّت فيها.
transportEmailTransport | str | None
كيف خرجت الرسالة. تكون null حتى الإرسال.
attemptsint
كم مرة جُرّب الإرسال.
lastErrorstr | None
لماذا فشلت المحاولة الأخيرة، حرفيًا.
scheduledAtstr | None
اللحظة بصيغة ISO التي يُتوقع أن تنطلق فيها.
cancellableUntilstr | None
ما دام الآن قبل هذه اللحظة، فالإلغاء ما زال يعمل.
sentAtstr | None
اللحظة بصيغة ISO التي انطلقت فيها.
tagsdict[str, str]
ما أرسلته، معادًا كما هو.
sourceEmailSource | str
composer أو api أو mcp أو ai أو oauth أو form: أي واجهة طلبت الإرسال. و`api` هو هذا العميل بمفتاح API، و`oauth` هو هذا العميل برمز وصول.
createdAtstr
اللحظة بصيغة ISO التي كُتب فيها السجل.
replayedbool
تكون True عندما يطابق Idempotency-Key عملية إرسال موجودة أصلًا. فلا شيء جديد أُرسل، وهذه هي الرسالة الأصلية.
translationNotRequired[EmailTranslationResource]
يظهر فقط على رسالة تُرجمت، وفقط حيث يُحمل الطلب المخزَّن كاملًا: أي هذه الاستجابة و`get`. وهو قاموس فيه `language` و`languageName` و`detectedSourceLanguage` و`subject` و`includeOriginal`، كلها رموز لا سجلات لغات. وسجل القائمة لا يحمله أبدًا، فغيابه هناك لا يقول شيئًا في أي اتجاه. اقرأه بـ `email.get('translation')`.

بلغة المستلِم

يكتب translate الرسالة بلغة شخص آخر قبل أن تنطلق. فالجسم، والموضوع ما لم توقف ذلك، يُترجمان عند قبول واجهة API للطلب، وما خرج هو ما يُرسل: فترجمة تعذّر إنتاجها ترفض الإرسال بدل أن تنشره باللغة التي كتبته بها.

translate.py
from openemail import openemail email = openemail.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(email.get('translation'))

لم يقرأ أحد ذلك قبل أن ينطلق. وemails.translate هو الرحلة نفسها موقوفة قبل خطوة واحدة من نهايتها. اعرضها على شخص، ودعه يغيّرها، ثم أرسل ما اعتمده دون أي translate في الاستدعاء إطلاقًا. فتمريره مجددًا كان سيترجم مرة ثانية ويطرح تعديلاته.

preview_translation.py
from openemail import openemail preview = openemail.emails.translate({    'subject': 'Your September invoice',    'html': '<p>Invoice attached. Payment is due on the 14th.</p>',    'to': 'de',}) print(preview['language']['native'], preview['detectedSourceLanguage'])print(preview['html']) approved_subject = input(f"Subject [{preview['subject']}]: ") or preview['subject'] or '' openemail.emails.send({    'from': '[email protected]',    'to': '[email protected]',    'subject': approved_subject,    'html': preview['html'] or '',})
render_picker.py
from openemail import LANGUAGES, is_rtl_language, language_by_code, openemail, resolve_language current = openemail.languages.list() german = resolve_language('Deutsch')traditional = resolve_language('zh-TW')upper = language_by_code('DE') assert len(LANGUAGES) == 200assert german is not None and german['code'] == 'de'assert traditional is not None and traditional['code'] == 'zh-Hant'assert upper is not None and upper['native'] == 'Deutsch'assert is_rtl_language('ar')

الجدول مضمّن في الحزمة، بترتيب المنتقي، فيمكن ملء منتقٍ قبل أول طلب. وlanguages.list() يعيد السجلات نفسها من الشبكة كقائمة عادية، لمن يفضّل السجلات الحالية على تلك التي شُحنت مع هذا الإصدار. ويأخذ resolve_language رمزًا أو اسمًا إنجليزيًا أو اسمًا محليًا أو اسمًا بديلًا (فـ zh-TW اسم بديل لرمز لم يعد مدرجًا)، ويطابق language_by_code رمزًا تامًا دون حساسية لحالة الأحرف، وستة عشر من السجلات لغات تُكتب من اليمين إلى اليسار. ابحث في native وlabel وcode معًا، واعرض native أولًا، واحفظ الرمز.

لا يُعاد استدعاء emails.translate تلقائيًا. فهو ينفق استدعاءات للنموذج ولا يكتب شيئًا، فلا شيء هناك لجعله عديم أثر التكرار، وإعادة المحاولة بعد طلب بلا إجابة لن تشتري سوى الإجابة نفسها مرتين.

  • لغة لا تُحلّ إلى شيء تعطي validation_error على translate.to، قبل إرسال أي شيء.
  • translation_too_long فوق 30,000 حرف، وtranslation_not_configured عندما لا يكون في التثبيت أي إعداد للذكاء الاصطناعي، و429 ai_quota_exceeded عندما تكون مساحة العمل قد استنفدت إجراءات الذكاء الاصطناعي لهذا اليوم (يُصفَّر عند منتصف الليل بتوقيت UTC ولا تُعاد المحاولة)، وtranslation_failed عندما لا يجيب المزوّد. ولا يرسل أي منها الرسالة دون ترجمة كحل بديل.
  • يعمل مع template: فالمخرَج المعروض هو ما يُترجم، فيخدم جسم مخزَّن واحد كل لغة يقرأ بها عملاؤك. والقالب الذي يعرض مستندًا كاملًا يحتفظ بـ doctype وكتل <style> وقواعد @font-face الخاصة به: إذ لا يذهب إلى النموذج سوى الجسم ويُعاد الباقي حوله. ويُترك <title> كما هو، وهو ما لا يعرضه شيء على أي حال.
  • إعادة المحاولة لا تكلّف شيئًا إضافيًا. فالترجمة ليست جزءًا من بصمة منع التكرار (الطلب هو الجزء، بما فيه translate)، لذا فإن إعادة إرسال طلب بلا إجابة بالـ Idempotency-Key نفسه تعيد تشغيل الرسالة الموجودة أصلًا بدل أن تترجم وترسل ثانية.
  • الرسالة المترجمة التي تكون في الطابور أو مجدولة مجمّدة ضد تغيير الصياغة. وemails.reschedule ما زال ينقلها؛ أما تغيير ما تقوله فيعني الإلغاء وإعادة الإرسال.

المرفقات

الحقل content يُرسل بترميز base64 على الشبكة. مرّر بايتات وتُرمَّز لك.

attachment.py
from pathlib import Path from openemail.types import AttachmentInput attachments: list[AttachmentInput] = [    {        'filename': 'invoice.pdf',        'content': Path('invoice.pdf').read_bytes(),        'contentType': 'application/pdf',    },]

الدالة to_base64 مُصدَّرة إن احتجت إليها في مكان آخر. وقيمة str في content تُرسل كما هي، لذا يجب أن تكون بترميز base64 مسبقًا.

المرجع