تنويه: نعم، استخدمت Claude لمساعدتي في إصلاح هذه المشكلة:
أقوم بتشغيل Discourse core عند الإصدار 5ee8e24 (2026-08-19). تكتمل مزامنة Patreon دون أخطاء، لكنها تزامن صفر مستخدم:
Patreon sync complete: 63 pledges, 0 users synced
جميع استجابات الواجهة البرمجية (API) هي status=200، لذا فإن الطلبات نفسها سليمة. عند تتبع المشكلة في سطر أوامر Rails، يتضح أن طلب الأعضاء الذي يبنيه الإضافة صحيح ويطلب بالفعل البريد الإلكتروني في مورد العضو (member resource):
fields[member]=full_name,last_charge_date,last_charge_status,currently_entitled_amount_cents,patron_status,email
عند استدعاء هذا الطرف (endpoint) مباشرةً باستخدام رمز منشئ (creator token) مُنح نطاقًا مناسبًا (campaigns.members[email] مُنح)، يكون البريد الإلكتروني موجودًا في كائن member، وليس في كائن user داخل included. يؤكد جلب عضو واحد ذلك:
data.attributes.email => "patron@example.com" # موجود
included[].attributes.email => (غير موجود) # كائنات المستخدم تحمل الاسم الكامل فقط
لذا، في قائمة أعضاء الحملة، يتم تعبئة member.attributes.email (46 من أصل 63 عضوًا لديّ؛ والباقي لديهم مشاركة مقيدة أو هم متابعون مجانيون)، بينما لا تحتوي إدخالات المستخدم في included على حقل بريد إلكتروني على الإطلاق.
المشكلة موجودة في Patreon::ApiVersion::V2.extract (lib/api_version/v2.rb). يقوم ببناء خريطة users من كائنات المستخدم في included:
ruby
(member_data["included"] || []).each do |entry|
if entry["type"] == "user" && entry["attributes"]["email"].present?
users[entry["id"]] = entry["attributes"]["email"].downcase
end
end
بما أن كائنات المستخدم هذه لا تحمل أبدًا البريد الإلكتروني، تخرج الخريطة فارغة، ويفشل كل عضو في مطابقة البريد الإلكتروني، ومن هنا يأتي العدد صفرًا في المزامنة.
قراءة البريد الإلكتروني من إدخال العضو بدلاً من ذلك (مُفهرسة باستخدام معرّف المستخدم المتاح بالفعل في member.relationships.user.data.id، وهو نفس patron_id المستخدم للتعهدات) أعاد إصلاح المشكلة في تثبيتي: تحوّل 0 إلى 46 users synced. التغيير الذي طبقته:
diff
+ users[patron_id] = attrs["email"].downcase if attrs["email"].present?
pledges[patron_id] = attrs["currently_entitled_amount_cents"]
declines[patron_id] = attrs["last_charge_date"] if attrs["last_charge_status"] == "Declined"
end
-
- (member_data["included"] || []).each do |entry|
- if entry["type"] == "user" && entry["attributes"]["email"].present?
- users[entry["id"]] = entry["attributes"]["email"].downcase
- end
- end
لست متأكدًا من أن إزالة حلقة included بالكامل هي القرار الصحيح في كل حالة — إذا كانت بعض الإعدادات تستقبل البريد الإلكتروني في كائن المستخدم، فقد ترغب في إبقائها كحل بديل بدلاً من استبدالها. أنشر السلوك المرصود والتغيير الأدنى الذي نجح هنا في حال كان مفيدًا أو يشير إلى الإصلاح الصحيح.