إرسال رسالة واتساب بلغة Python
ترسل هذه الصفحة رسالة واتساب من Python بمكتبة requests، ثم تستقبل الرد في رابط استقبال صغير مكتوب بـ Flask. الرسالة الأولى تذهب إلى هاتفك أنت، فتراها وهي تصل.
ما الذي تحتاجه
- حساب على Threadwire.
- رقم مربوط. في لوحة التحكم، اضغط «إضافة رقم» وامسح رمز QR بواتساب على الهاتف (الإعدادات ← الأجهزة المرتبطة). ثم انسخ معرّف الرقم من صفحة الرقم.
- مفتاح API. أنشئه من للمطورين ← مفاتيح API ← مفتاح جديد (للمسؤولين فقط).
- Python مع
requests. يقرأ المثال المفتاح من متغير بيئة اسمهTHREADWIRE_KEY. ورابط الاستقبال أسفل الصفحة يستخدم Flask أيضًا.
«الرقم» في Threadwire هو رقم واتساب واحد مربوط به. يُربط كجهاز، بالطريقة نفسها التي يُربط بها WhatsApp Web، فهذه ليست الواجهة الرسمية من Meta. صفحة WhatsApp API بدون موافقة Meta تشرح معنى ذلك.
أرسل الرسالة
أرسل الرسالة الأولى إلى نفسك. اكتب «مرحبًا» إلى رقمك المربوط من واتساب على هاتف آخر لك. «رابط المحادثة» في صفحة «الأرقام» يفتح تلك المحادثة. ثم أرسل إلى ذلك الهاتف:
# Python, with requests
import os, requests
response = requests.post(
'https://threadwire.net/api/v1/messages',
headers={'Authorization': f"Bearer {os.environ['THREADWIRE_KEY']}", 'Accept': 'application/json'},
json={'instance_id': 'YOUR_INSTANCE_ID', 'to': 'YOUR_PHONE', 'text': 'Hello from Threadwire'},
)
response.raise_for_status()
message = response.json()['data']
print(message['id'], message['status'])
headers: مفتاح API الخاص بك كرمز Bearer فيAuthorization، ويُرسل مع كل طلب.json: حقول الرسالة. ترسلهاrequestsكجسم طلب بصيغة JSON.instance_id: معرّف الرقم الذي نسخته.to: رقم ذلك الهاتف بالصيغة الدولية، مثل201012345678: رمز الدولة، بلا حاجة إلى+، وبلا صفر في البداية.text: نص الرسالة، حتى 4,096 حرفًا.
اقرأ رد الـ API
يصل الرد فورًا، وresponse.json()['data'] هو الرسالة:
202 {"data": {"id": "01K…", "status": "queued", "phone": "201012345678", "body": "Hello from Threadwire", "…": "…"}}
id: احتفظ به.GET /v1/messages/{id}يعيد الرسالة بحالتها في تلك اللحظة.status: القيمةqueuedتعني أن الرسالة قُبلت وهي في الانتظار حتى يأتي دورها. لم تصل إلى الهاتف بعد.phoneوbody: من تُرسل إليه الرسالة، ونصها.
الرسالة التي في الانتظار تُرسل كما يرسلها الإنسان. بعدها تنتقل status إلى sent ثم delivered ثم read، أو إلى failed مع السبب في error.
أنت كتبت أولًا، فهذه الرسالة رد، وتصل خلال ثوانٍ. أما سكربت يراسل من لم يكتبوا إلى رقمك من قبل فحالة مختلفة. تلك الرسائل الأولى تُرسل متباعدة، ولها حد يومي، ولا تُرسل ليلًا. دليل كيف يحمي الإرسال رقمك فيه كل قاعدة بأرقامها.
استقبل الرد على رسالتك
من للمطورين ← الويب هوك (للمسؤولين فقط)، حدّد رابط الاستقبال لديك: عنوان عام يبدأ بـ https://. يُرسل إليه كل رد وكل تغيّر في الحالة بصيغة JSON:
{"id": "evt_01K…", "type": "message.received", "created_at": "2026-10-01T10:00:00+00:00",
"data": {"id": "01K…", "instance_id": "01K…", "phone": "201012345678", "body": "Hi!", "…": "…"}}
يصل الرد كحدث message.received، ويصل تحديث التسليم كحدث message.status.
كل حدث موقّع وفق مواصفة Standard Webhooks. يبدأ سر التوقيع بـ whsec_، ويظهر عند حفظ الرابط. هذه الدالة، من دليل الويب هوك، تتحقق من التوقيع بمكتبة Python القياسية:
# Python: headers as lower-case names, raw_body as bytes (in Flask, request.get_data()).
import base64, hashlib, hmac, time
def is_genuine(headers: dict, raw_body: bytes, secret: str) -> bool:
# Reject old or future timestamps: a replayed request is not a new event.
if abs(time.time() - int(headers['webhook-timestamp'])) > 300:
return False
key = base64.b64decode(secret.removeprefix('whsec_'))
signed = f"{headers['webhook-id']}.{headers['webhook-timestamp']}.".encode() + raw_body
expected = 'v1,' + base64.b64encode(hmac.new(key, signed, hashlib.sha256).digest()).decode()
return any(hmac.compare_digest(expected.encode(), signature.encode()) for signature in headers.get('webhook-signature', '').split(' '))
تحتاج الدالة إلى Python 3.9 أو أحدث، بسبب removeprefix(). أعطها جسم الطلب الخام كبايتات، قبل أي تحليل لـ JSON. في Flask هو request.get_data():
# A Flask endpoint that uses is_genuine() from above.
import json, os
from flask import Flask, request
app = Flask(__name__)
@app.post('/webhooks/whatsapp')
def whatsapp():
headers = {name.lower(): value for name, value in request.headers.items()}
raw_body = request.get_data()
try:
genuine = is_genuine(headers, raw_body, os.environ['THREADWIRE_WEBHOOK_SECRET'])
except (KeyError, ValueError):
genuine = False # a header is missing, or the timestamp is not a number
if not genuine:
return '', 401
event = json.loads(raw_body)
if event['type'] == 'message.received':
print(f"{event['data']['phone']}: {event['data']['body']}")
return '', 200
ردّ بأي رمز 2xx خلال 15 ثانية. الحدث الذي يُرسل مرة أخرى يحتفظ بـ webhook-id، فاستخدم هذا المعرّف لتجاهل المكرر.
من المفيد أن تعرف
- ليس لسكربتك رابط عام؟ اجلب الأحداث نفسها عبر
GET /v1/eventsبدلًا من ذلك. راجع الجلب. - إعادة المحاولة آمنة مع ترويسة
Idempotency-Key. أضف'Idempotency-Key': 'order-1042-shipped'إلىheaders: أي نص فريد من عندك. إعادة المحاولة بالمفتاح نفسه تعيد الرسالة الأولى بدلًا من إرسال ثانية. raise_for_status()يرمي استثناءً عند رفض الطلب. في الردmessageبكلمات بسيطة وcodeثابت، مثلvalidation_failed. اقرأهما منerror.response.json()حين تلتقطrequests.HTTPError.- لا تُعد إرسال رسالة فشلت في حلقة تكرار أبدًا. الرسالة التي لا يمكن إرسالها يُرد عليها بـ
202، ثم تصبحfailedلاحقًا. اقرأerrorوتصرّف بناءً عليه. راجع ما الذي يعيد نظامك محاولته.
التالي
- إرسال الرسائل: الملفات والرسائل الصوتية والاستطلاعات وغيرها، كحقول في الطلب نفسه.
- الويب هوك والأحداث: كل الأحداث، وإعادة المحاولة، والجلب.
- مرجع الـ API: كل نقاط الاتصال، والأخطاء.
- الأدوات وأمثلة الكود: مجموعة Postman، وهذا الطلب بلغات أخرى.