كيفية دفع جهات اتصال Excel إلى Brevo باستخدام ماكرو VBA (وبديل Office Scripts)

ماكرو VBA عامل ينشر جهات الاتصال من ورقة Excel إلى واجهة Brevo API بنقرة واحدة، إضافة إلى متى تستخدم Office Scripts + Power Automate بدلاً من ذلك، ومقارنات مع إعداد Google Apps Script.

Featured image for article: كيفية دفع جهات اتصال Excel إلى Brevo باستخدام ماكرو VBA (وبديل Office Scripts)

لديك جهات اتصال في Excel وتريدها في Brevo. الإجابة السريعة والقذرة هي حفظ الملف كـ .csv واستيراده، وهو أمر جيد لمرة واحدة. لأي شيء تفعله بشكل متكرر (تسليم مبيعات أسبوعي، مصنّف يحدّثه فريقك يومياً، قائمة شركاء يتم تحديثها)، تريد زراً مباشرة داخل Excel يقوم بالمزامنة.

يغطي هذا الدليل المسارين اللذين يعنيان حقاً لذلك:

  1. ماكرو VBA مدمج في المصنّف. بدون ترخيص، بدون سحابة، يعمل بدون اتصال، يعمل في اللحظة التي ينقر فيها المستخدم على زر. الإجابة الصحيحة لحوالي 80% من حالات “Excel-إلى-Brevo”.
  2. Office Scripts + Power Automate. TypeScript بدلاً من VBA، يعمل في السحابة، يدعم المشغّلات المجدولة. الإجابة الصحيحة إذا كان المصنّف في OneDrive/SharePoint وتريد مزامنة بدون إشراف، لكن كن على دراية بترخيص Power Automate.

إذا كنت تبحث عن مكافئ Google Sheets، انظر المقالة المرافقة عن Apps Script. وإذا كنت تريد فقط استيراد CSV لمرة واحدة من سكريبت على جهازك المحمول، فإن دليل استيراد CSV لديه إصدارات Python و Node.js و cURL.

ماذا يفعل الماكرو

عندما ينقر المستخدم على زر “Sync to Brevo” في الورقة:

  1. يقرأ كل صف من ورقة العمل النشطة (صف الرأس أولاً، جهة اتصال واحدة لكل صف).
  2. يبني مصفوفة JSON مهيأة لمعامل jsonBody الخاص بـ Brevo.
  3. يقوم بـ POST لها إلى https://api.brevo.com/v3/contacts/import بمفتاح API المخزن في المصنّف.
  4. يعرض مربع رسالة بالنتيجة.

هذا كل شيء. حوالي 120 سطراً من VBA. أدناه الوحدة الكاملة العاملة.

تخطيط الورقة الذي يتوقعه الماكرو

emailfirstNamelastNamecompanycity
[email protected]JaneDoeAcmeBerlin
[email protected]JohnSmithGlobexParis

email إلزامي. كل عمود آخر يصبح سمة جهة اتصال Brevo، يتم ربطه برأس العمود (بأحرف كبيرة) إلى اسم السمة. لذلك firstNameFIRSTNAME، companyCOMPANY. السمات المخصصة (أي شيء خارج المجموعة القياسية) تحتاج إلى أن توجد في حسابك Brevo أولاً. عرّفها تحت Contacts → Settings → Contact attributes.

الخطوة 1: افتح محرر VBA

في Excel: اضغط Alt + F11. يفتح محرر VBA. في لوحة Project على اليسار، انقر بزر الماوس الأيمن على المصنّف الخاص بك واختر Insert → Module. سيظهر Module1 فارغ.

الخطوة 2: الصق الماكرو الكامل

استبدل محتويات Module1 بهذا:

' ===========================================================================
' Brevo contact sync for Excel
' Reads the active sheet's rows and POSTs them to Brevo's import API.
' ===========================================================================
Option Explicit
Private Const BREVO_API_BASE As String = "https://api.brevo.com/v3"
Private Const BREVO_LIST_ID As Long = 42 ' <- your Brevo list ID
Private Const BATCH_SIZE As Long = 1000
' --- Public entry points (the ones you assign to ribbon buttons) -----------
Public Sub SyncSheetToBrevo()
Dim apiKey As String
apiKey = GetApiKey()
If apiKey = "" Then
MsgBox "No API key configured. Run ConfigureApiKey first.", _
vbExclamation, "Brevo Sync"
Exit Sub
End If
Dim ws As Worksheet
Set ws = ActiveSheet
Dim emailCol As Long
emailCol = FindEmailColumn(ws)
If emailCol = 0 Then
MsgBox "Sheet must have an 'email' column in row 1.", _
vbExclamation, "Brevo Sync"
Exit Sub
End If
Dim lastRow As Long, lastCol As Long
lastRow = ws.Cells(ws.Rows.Count, emailCol).End(xlUp).Row
lastCol = ws.Cells(1, ws.Columns.Count).End(xlToLeft).Column
If lastRow < 2 Then
MsgBox "No contact rows found.", vbInformation, "Brevo Sync"
Exit Sub
End If
Dim contacts As Collection
Set contacts = New Collection
Dim r As Long, c As Long
For r = 2 To lastRow
Dim email As String
email = LCase(Trim(CStr(ws.Cells(r, emailCol).Value)))
If email <> "" And InStr(email, "@") > 0 Then
Dim json As String
json = "{""email"":""" & EscapeJson(email) & """,""attributes"":{"
Dim attrFirst As Boolean
attrFirst = True
For c = 1 To lastCol
If c <> emailCol Then
Dim val As String
val = CStr(ws.Cells(r, c).Value)
If val <> "" Then
If Not attrFirst Then json = json & ","
Dim attrName As String
attrName = UCase(Trim(CStr(ws.Cells(1, c).Value)))
json = json & """" & attrName & """:""" & EscapeJson(val) & """"
attrFirst = False
End If
End If
Next c
json = json & "}}"
contacts.Add json
End If
Next r
If contacts.Count = 0 Then
MsgBox "No valid contact rows found.", vbInformation, "Brevo Sync"
Exit Sub
End If
Dim totalSent As Long
Dim batchNum As Long
Dim okCount As Long, failCount As Long
Dim batchStart As Long
For batchStart = 1 To contacts.Count Step BATCH_SIZE
batchNum = batchNum + 1
Dim batchEnd As Long
batchEnd = batchStart + BATCH_SIZE - 1
If batchEnd > contacts.Count Then batchEnd = contacts.Count
Dim payload As String
payload = "{""jsonBody"":["
Dim i As Long
For i = batchStart To batchEnd
If i > batchStart Then payload = payload & ","
payload = payload & contacts(i)
Next i
payload = payload & "],""listIds"":[" & BREVO_LIST_ID & _
"],""updateExistingContacts"":true,""emptyContactsAttributes"":false}"
Dim ok As Boolean
ok = PostToBrevo(apiKey, payload)
If ok Then
okCount = okCount + 1
totalSent = totalSent + (batchEnd - batchStart + 1)
Else
failCount = failCount + 1
End If
Next batchStart
MsgBox "Sent " & totalSent & " contact(s) in " & batchNum & " batch(es)." _
& vbCrLf & "Successful batches: " & okCount _
& vbCrLf & "Failed batches: " & failCount, _
vbInformation, "Brevo Sync"
End Sub
Public Sub ConfigureApiKey()
Dim key As String
key = InputBox("Paste your Brevo API key (xkeysib-...):", "Brevo API Key")
If key = "" Then Exit Sub
key = Trim(key)
If Left(key, 8) <> "xkeysib-" Then
MsgBox "That doesn't look like a Brevo API key (should start with xkeysib-).", _
vbExclamation, "Brevo API Key"
Exit Sub
End If
On Error Resume Next
ThisWorkbook.CustomDocumentProperties("BrevoApiKey").Delete
On Error GoTo 0
ThisWorkbook.CustomDocumentProperties.Add _
Name:="BrevoApiKey", _
LinkToContent:=False, _
Type:=msoPropertyTypeString, _
Value:=key
ThisWorkbook.Save
MsgBox "API key saved inside the workbook.", vbInformation, "Brevo API Key"
End Sub
' --- Private helpers --------------------------------------------------------
Private Function GetApiKey() As String
On Error Resume Next
GetApiKey = ThisWorkbook.CustomDocumentProperties("BrevoApiKey").Value
On Error GoTo 0
End Function
Private Function FindEmailColumn(ws As Worksheet) As Long
Dim lastCol As Long, c As Long
lastCol = ws.Cells(1, ws.Columns.Count).End(xlToLeft).Column
For c = 1 To lastCol
If LCase(Trim(CStr(ws.Cells(1, c).Value))) = "email" Then
FindEmailColumn = c
Exit Function
End If
Next c
FindEmailColumn = 0
End Function
Private Function PostToBrevo(apiKey As String, payload As String) As Boolean
Dim http As Object
Set http = CreateObject("MSXML2.XMLHTTP")
http.Open "POST", BREVO_API_BASE & "/contacts/import", False
http.SetRequestHeader "api-key", apiKey
http.SetRequestHeader "Content-Type", "application/json"
http.SetRequestHeader "Accept", "application/json"
http.Send payload
PostToBrevo = (http.Status = 202)
If Not PostToBrevo Then
Debug.Print "Brevo error " & http.Status & ": " & http.responseText
End If
End Function
Private Function EscapeJson(s As String) As String
Dim r As String
r = Replace(s, "\", "\\")
r = Replace(r, """", "\""")
r = Replace(r, vbCrLf, "\n")
r = Replace(r, vbLf, "\n")
r = Replace(r, vbCr, "\n")
r = Replace(r, vbTab, "\t")
EscapeJson = r
End Function

الخطوة 3: احفظ المصنّف كـ .xlsm

تستمر ماكرو VBA فقط في المصنّفات الممكّنة للماكرو. Save As، اختر Excel Macro-Enabled Workbook (.xlsm). صيغة .xlsx العادية تجرّد الماكرو بصمت. كثير من الناس يفقدون الكود بهذه الطريقة في المرة الأولى.

الخطوة 4: قم بتكوين مفتاح API الخاص بك

شغّل ConfigureApiKey مرة واحدة. إما:

  • في محرر VBA، انقر في أي مكان داخل sub ConfigureApiKey واضغط F5، أو
  • في Excel، Developer → Macros، اختر ConfigureApiKey، Run.

الصق مفتاحك xkeysib-.... يخزنه الماكرو كخاصية مستند مخصصة داخل المصنّف نفسه. ليس في الكود المصدري، ليس في السجل، ويسافر مع الملف (لذا انتبه: إذا أرسلت .xlsm بالبريد الإلكتروني لشخص ما، فإن مفتاح API يذهب معه).

إذا كنت تفضل وضع المفتاح في مكان خارج المصنّف، استبدل التخزين بسجل Windows:

' Replace the body of ConfigureApiKey with:
SaveSetting "Brevo", "Sync", "ApiKey", key
' And GetApiKey with:
GetApiKey = GetSetting("Brevo", "Sync", "ApiKey", "")

SaveSetting/GetSetting يكتب تحت HKCU\Software\VB and VBA Program Settings\Brevo\Sync. لكل مستخدم، وليس لكل مصنّف. استخدم هذا إذا كانت مصنّفات متعددة يجب أن تشارك مفتاحاً واحداً، أو إذا لم ترد المفتاح في الملف.

الخطوة 5: أضف زراً على الورقة

هذا ما يحوّله إلى تجربة بنقرة واحدة للمستخدمين غير التقنيين.

Insert → Shapes → Rectangle، أسقط واحداً على الورقة، سمّه “Sync to Brevo”. انقر بزر الماوس الأيمن على الشكل → Assign Macro → اختر SyncSheetToBrevo. تم.

أو، لواجهة مستخدم أكثر صقلاً، أضف علامة تبويب شريط مخصصة عبر Office Custom UI Editor. لكن لمعظم الأدوات الداخلية، الشكل-كزر كافٍ تماماً.

الخطوة 6: شغّله

انقر على الزر. يقرأ الماكرو الصفوف، ويقسمها إلى دفعات، وينشر كل دفعة إلى Brevo، ويعرض مربع رسالة ملخصاً. استيراد Brevo غير متزامن، لذلك تعني رسالة النجاح “قبل Brevo الدفعة”. إنشاء جهة الاتصال الفعلي يحدث من جانب الخادم في الثواني القليلة التالية. ستحصل على ملخص بالبريد الإلكتروني من Brevo عند انتهائه (إلا إذا ضبطت disableNotification: true).

المزالق الشائعة

الزر لا يفعل شيئاً ولا يوجد خطأ. الماكرو معطل. انظر إلى شريط الأمان الأصفر في أعلى الورقة، انقر على Enable Content. إذا كانت مؤسستك تحظر الماكرو، انظر مسار trust-center / code-signing أدناه.

Compile error: User-defined type not defined. أنت على Mac Excel، الذي لا يحتوي على MSXML2.XMLHTTP. لا يمكن لـ Mac VBA إجراء طلبات HTTPS مباشرة؛ استخدم مسار Office Scripts أدناه بدلاً من ذلك.

400 Bad Request من Brevo بدون سبب واضح. غالباً ما يكون أحد: (أ) سمة مخصصة في ورقتك غير موجودة في Brevo بعد، أنشئها أولاً؛ (ب) خطأ في JSON escaping، علامات اقتباس أو شرطات مائلة عكسية في قيم الخلايا لم يتم escape لها. تتعامل دالة EscapeJson في الكود مع الحالات القياسية؛ إذا كانت بياناتك تحتوي على أحرف غريبة، سجّل payload إلى نافذة Immediate (Debug.Print payload) وافحصها.

401 Unauthorized. رأس خاطئ. إنه api-key (أحرف صغيرة، شرطة)، وليس Authorization. يستخدم الماكرو الصحيح، ولكن إذا نسخت مقتطفاً من مكان آخر، تحقق مرة أخرى.

يتجمد Excel على الاستيرادات الكبيرة. يعمل الماكرو بشكل متزامن على خيط واجهة المستخدم. لـ 50,000+ صف، ستشاهد Excel يتعلق لمدة 10 إلى 30 ثانية بينما يبني JSON وينتظر Brevo. إما تقبل ذلك، أو بدّل MSXML2.XMLHTTP إلى متغيره غير المتزامن. ولكن على هذا المقياس، أنت أفضل في Power Automate (القسم التالي).

عندما لا يكفي VBA: Office Scripts + Power Automate

لا يستطيع VBA القيام بمزامنة سحابية مجدولة. إذا احتجت:

  • المصنّف يتزامن مع Brevo كل ساعة دون أن يفتحه أحد
  • المصنّف في OneDrive/SharePoint، يتم تحريره من الويب
  • قسم تكنولوجيا المعلومات يحظر ماكرو سطح المكتب

…فأنت تريد Office Scripts (مكافئ Microsoft السحابي لـ Apps Script) بالإضافة إلى Power Automate (طبقة الجدولة و HTTP الخاصة بهم).

التقسيم: يقرأ Office Scripts الورقة ويعيد بيانات جهات الاتصال. يأخذ Power Automate تلك البيانات ويقوم بـ POST لها إلى Brevo بناءً على مشغّل.

Office Script (Excel for the web → Automate → New Script):

function main(workbook: ExcelScript.Workbook): {email: string, attributes: Record<string, string>}[] {
const sheet = workbook.getActiveWorksheet();
const range = sheet.getUsedRange();
if (!range) return [];
const values = range.getValues() as string[][];
if (values.length < 2) return [];
const headers = values[0].map(h => String(h).trim());
const emailIdx = headers.findIndex(h => h.toLowerCase() === "email");
if (emailIdx === -1) throw new Error("Sheet must have an 'email' column");
const contacts: {email: string, attributes: Record<string, string>}[] = [];
for (let r = 1; r < values.length; r++) {
const row = values[r];
const email = String(row[emailIdx] ?? "").trim().toLowerCase();
if (!email || !email.includes("@")) continue;
const attributes: Record<string, string> = {};
for (let c = 0; c < headers.length; c++) {
if (c === emailIdx) continue;
const v = row[c];
if (v === null || v === "") continue;
attributes[headers[c].toUpperCase()] = String(v);
}
contacts.push({ email, attributes });
}
return contacts;
}

flow Power Automate:

  1. Trigger: Recurrence (كل ساعة)، أو زر يدوي، أو “When a row is modified” إذا أردت مزامنة مدفوعة بالتغيير.
  2. Action: Excel Online → Run script، وجّهه إلى مصنّفك والسكريبت أعلاه. احفظ قيمته المرتجعة كـ contacts.
  3. Action: HTTP (هذا هو موصّل Premium، انظر ملاحظة الترخيص أدناه).
    • Method: POST
    • URI: https://api.brevo.com/v3/contacts/import
    • Headers: api-key: xkeysib-...، Content-Type: application/json
    • Body:
      {
      "jsonBody": @{outputs('Run_script')?['body/result']},
      "listIds": [42],
      "updateExistingContacts": true
      }
  4. Action: Condition → إذا كان كود الحالة ≠ 202، أرسل تنبيه Teams/email.

فحص واقع الترخيص: إجراء HTTP هو موصّل Power Automate Premium. على خطط Microsoft 365 Business Basic/Standard، تحصل على الموصّلات القياسية ولكن ليس Premium. أرخص حل بديل هو إضافة Power Automate Premium (حوالي 15 دولاراً/مستخدم/شهر في وقت كتابة هذا)، أو نقل HTTP إلى Azure Function صغيرة يمكن للـ flow القياسي استدعاؤها. إذا كنت بالفعل على E3/E5 مع Premium مضمّن، فأنت جاهز.

هذا هو السبب الرئيسي لكون قصة Apps Script أنظف: UrlFetchApp الخاص بـ Apps Script مجاني وغير مقيد، بينما المعادل من Microsoft يضع المكالمة الشبكية خلف طبقة موصّل مدفوعة.

VBA مقابل Office Scripts مقابل Apps Script: متى تختار ماذا

الحاجةالخيار الأفضل
زر بنقرة واحدة في مصنّف يفتحه فريقك يومياً بالفعلماكرو VBA (هذا الدليل، النصف العلوي)
المصنّف في OneDrive/SharePoint، مزامنة تلقائية كل ساعةOffice Scripts + Power Automate (يحتاج Premium لـ HTTP)
Mac Excel فقط، لا يمكن استخدام VBAOffice Scripts + Power Automate
البيانات تعيش في Google Sheets، ليس ExcelApps Script (مجاني، مشغّلات مجدولة مدمجة)
استيراد لمرة واحدة، لن يحتاج إليه مرة أخرىSave As → CSV واستخدم سكريبت استيراد CSV
استيراد جماعي من ملف >10 ميجابايتCSV مع fileUrl، انظر دليل CSV

لماذا يتفوق هذا على Zapier / منصات no-code

لمهمة Excel-إلى-Brevo المتكررة، تفرض أدوات الأتمتة من طرف ثالث (Zapier، Make، n8n) رسوماً لكل مهمة وتضع طرفاً ثالثاً بين بياناتك و Brevo. نهج VBA لديه تكلفة جارية صفرية، بدون تدفق بيانات لطرف ثالث، ويعيش داخل الملف. عندما يتحرك المصنّف، يتحرك التكامل معه. Office Scripts + Power Automate مماثل لكن مع Microsoft كطرف ثالث (موجود بالفعل في stack الخاص بك إذا كنت على M365).

النقطة الكاملة لنقطة نهاية POST /v3/contacts/import الخاصة بـ Brevo هي أنك لا تحتاج إلى منصة لاصقة. أدواتك تعرف بالفعل كيفية إجراء طلبات HTTP.

قراءات إضافية

Frequently Asked Questions

هل يمكن لماكرو VBA حقاً استدعاء Brevo API من داخل Excel؟
نعم. يمكن لـ VBA إجراء طلبات HTTP عبر MSXML2.XMLHTTP، الذي يأتي مع كل تثبيت حديث لـ Windows. يقوم الماكرو بـ POST لـ JSON إلى api.brevo.com/v3/contacts/import، نفس نقطة النهاية التي يصلها سكريبت Python أو Node. يمكن لـ Mac Excel فعل ذلك أيضاً عبر Office Scripts + Power Automate، لكن VBA على سطح مكتب Mac لا يستطيع (لا يوجد MSXML).
هل يمكنني تشغيل هذا وفق جدول مثل مشغّلات Apps Script الزمنية؟
ليس من VBA وحده. يجب أن يكون Excel مفتوحاً ليعمل Application.OnTime. للمزامنة المجدولة بدون إشراف لديك خياران: (1) Windows Task Scheduler يفتح المصنّف ويشغّل الماكرو، أو (2) Office Scripts المُشغّل بواسطة flow Power Automate المجدول (يُغطى لاحقاً في هذا الدليل).
هل VBA آمن؟ شركتي تحظر الماكرو.
تعمل ماكرو VBA بصلاحيات نظام الملفات والسجل، لذا فإن حظرها افتراضياً سياسة معقولة. مساران للالتفاف: (1) وقّع الماكرو بشهادة code-signing وضع المصنّف في Trusted Location، أو (2) تخطّ VBA كلياً واستخدم Office Scripts (TypeScript، sandboxed، بدون وصول لنظام الملفات). Office Scripts هو المسار الحديث المُبارك من Microsoft.
كيف يقارن هذا بنهج Google Apps Script؟
Apps Script أقرب إلى set-and-forget. يعمل في سحابة Google، مشغّلات مجدولة مدمجة، بدون رخصة Excel/Power Automate. يفوز VBA للعمل بدون اتصال والمزامنة اليدوية بنقرة واحدة من مصنّف فريقك يفتحه بالفعل. Office Scripts + Power Automate هو المعادل السحابي/المجدول من جانب Microsoft، لكن Power Automate يحتاج غالباً إلى موصّل premium مدفوع لـ HTTP الصادر.
ابدأ مجانًا مع Brevo