المستودع: GitHub - overgrow/discourse-bounce-guard · GitHub
الرخصة: MIT
&tldr; ما ستحصل عليه: سجل أخطاء أنظف (تتوقف إعادة المحاولة الساعة إلى العناوين الميتة). يتوقف Discourse عن إرسال البريد الذي لا يمكن وصوله. ومستخدمون أكثر سعادة: يمكنهم العودة إلى حساباتهم، لأن العنوان الميت يُستبدل بعنوان يعمل في الوقت المناسب.
إذا كان البريد الصادر لديك يمر عبر وسيطك الخاص (Postfix، Exim، معظم الإعدادات المستضافة ذاتيًا)، فربما لاحظت أن /logs تمتلئ بأزواج مثل هذا:
SMTP Error Net::SMTPServerBusy with message: 450 4.1.2 <someone@gone-domain.com>: Recipient address rejected: Domain not found
Job exception: Net::SMTPServerBusy
النطاق قد اختفى ولن يعود. لكن الوسيط يرد برمز خطأ مؤقت (450)، لأنه نظريًا يمكن أن يفشل بحث DNS لفترة وجيزة. يتعامل Discourse مع أي خطأ مؤقت على أنه “حاول مرة أخرى بعد ساعة”، ويواصل Sidekiq المحاولة لأسابيع. لا يعمل أبدًا اكتشاف الارتداد في النواة. فهو يستجيب فقط عندما يعود رسالة ارتداد، إما كبريد إلكتروني (VERP) أو كـ webhook من مزود البريد الخاص بك، وهنا تُرفض الرسالة في الموقع، لذا لا توجد أبدًا رسالة ارتداد. يبقى مؤشر الارتداد عند الصفر، وتواصل إعادة المحاولة، ويحتفظ المستخدم بعنوان لا يمكنه استلام أي شيء، بما في ذلك إعادة تعيين كلمة المرور.
ظهرت هذه المشكلة هنا عدة مرات دون إجابة مدمجة، على سبيل المثال التعامل مع رسائل البريد إلى نطاق غير موجود، تعطيل مستخدم مع ارتداد صلب، وكيفية تعطيل حسابات المستخدمين الذين لا يستلمون رسائل البريد. عانينا من نفس المشكلة في منتدانا، لذا بنينا إضافة.
ما تفعله
تتصل Bounce Guard بمسار الإرسال نفسه وتصنف كل رفض SMTP:
- الردود 5xx مع حالة محسنة للمستلم غير صالح (
5.1.1،5.1.2،5.2.1وأقرانهم)
تُحتسب كفشل صلب. - أي رد يطابق قائمة عبارات قابلة للضبط (“النطاق غير موجود”، “المستخدم غير معروف”، …)
يُحتسب كصلب بغض النظر عن الرمز. هذا يلتقط الوسطاء الذين يفشلون بشكل ناعم في حالات دائمة
مع 450. - يُترك Greylisting والبريد الممتلئ وحدود المعدل كما هي. سلوك إعادة المحاولة في النواة
لم يُمس.
ثم تتسلل الفشل الصلبة المسجلة سلّمًا:
- كل فشل يغذي مؤشر الارتداد في النواة كارتداد صلب (مفعّل افتراضيًا). بعد فشلين، يعمل عتبة النواة الخاصة بها ويتوقف Discourse عن إرسال البريد إلى المستخدم. ينتهي ضجيج السجل هنا، حتى لو لم تفعّل أي شيء إضافي أبدًا.
- بعد عدد قابل للضبط من الفشل موزع على فترة زمنية قابلة للضبط (افتراضيًا: فشلان على الأقل بفارق 48 ساعة، حتى لا يستطيع انقطاع قصير إخراج أي شخص)، تتدخل الإضافة. الإجراء الافتراضي هو
log_only: إدخال في سجل إجراءات الموظفين يسجل أن المستخدم كان سيُعطَّل. انتقل إلىdeactivateعندما تثق بها. - توجيه التعطيل للمستخدم عبر مسار التفعيل القياسي عند تسجيل الدخول التالي، حيث يتم بناء تغيير العنوان فيه. يتحققون من بريد إلكتروني يعمل ويواصلون بحسابهم سليمًا. النقطة هي القابلية للاسترداد: حساب نشط يكون قناة الاسترداد الوحيدة له صندوق بريد ميت هي قفل في انتظار الحدوث.
إذا كان موقعك يستقبل ارتدادات (VERP أو webhooks)، فيمكنك أيضًا السماح للإضافة بالتصرف عندما يتجاوز مؤشر الارتداد في النواة مستوى تختاره، أعلى من الدرجة التي يتوقف عندها النواة عن الإرسال. تنطبق بعض قواعد السلامة في كل مكان. لا يُلمس أبدًا الموظفون والروبوتات. بريد واحد عالق يعيد المحاولة كل ساعة يُحتسب كفشل واحد واحد، بفضل نافذة تبريد. لا يُحتسب فشل ضد مستخدم إلا عندما يكون العنوان الذي رفضه الخادم هو العنوان الحالي لذلك المستخدم. وتُحفظ كل فشل مسجلة لمدة 90 يومًا حتى تتمكن من التحقق مما حدث (استعلام Data Explorer في README).
التثبيت
تثبيت الإضافة القياسي:
hooks:
after_code:
- exec:
cd: $home/plugins
cmd:
- git clone https://github.com/discourse/docker_manager.git
- git clone https://github.com/overgrow/discourse-bounce-guard.git
فعّل bounce_guard_enabled، واترك bounce_guard_action على log_only لمدة أسبوع أو أسبوعين، راجع ما يرفعه، ثم قرر بشأن التعطيل. مرجع الإعدادات موجود في README.
جانب الخادم فقط، لا مكونات موضوع أو JS. بُنِي واختُبر مقابل النواة الحالية (2026.8)، 31 مواصفة، وتشغل CI سير عمل discourse-plugin القياسي. الترحيب بالتغذية الراجعة، خاصة عبارات الرفض من وسطاء آخرين تفقدها قائمة العبارات الافتراضية.