adding
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
# بله Bot API تلگرامسازگار است. اگر base_url حساب تو فرق دارد، همین یک
|
||||
# مقدار را عوض کن — بقیهی منطق در bot_api.py مشترک است.
|
||||
from django.conf import settings
|
||||
|
||||
from .bot_api import send_bot_message
|
||||
|
||||
BALE_BASE_URL = "https://tapi.bale.ai"
|
||||
|
||||
|
||||
def send_bale(chat_id: str, text: str) -> bool:
|
||||
return send_bot_message(BALE_BASE_URL, settings.BALE_BOT_TOKEN, chat_id, text)
|
||||
@@ -0,0 +1,39 @@
|
||||
# apps/messaging/providers/bot_api.py
|
||||
"""
|
||||
پایهی مشترک ارسال از طریق Bot API — تلگرام، بله و ایتا هرکدام یک Bot API
|
||||
تلگرامسازگار دارند (endpoint یکسان: POST {base}/bot{token}/sendMessage با
|
||||
بدنهی {chat_id, text}). این یعنی این سه پلتفرم فقط base_url و token
|
||||
متفاوت دارند؛ منطق ارسال یکی است.
|
||||
|
||||
نکتهی مهم — opt-in: بات فقط میتواند به chat_id هایی پیام بدهد که قبلا
|
||||
خودشان چت با بات را با /start شروع کردهاند (دقیقا مثل قانون خودِ تلگرام).
|
||||
یعنی این تابع فقط برای مخاطبینی کار میکند که chat_id شان قبلا (از طریق
|
||||
webhook خود بات، خارج از این فایل) در Contact.channel_ids ثبت شده باشد؛ برای
|
||||
بقیه، صدازننده باید needs_manual برگرداند — نه اینکه اینجا حدس بزند.
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
||||
import requests
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
REQUEST_TIMEOUT_SECONDS = 10
|
||||
|
||||
|
||||
def send_bot_message(base_url: str, token: str, chat_id: str, text: str) -> bool:
|
||||
if not base_url or not token:
|
||||
logger.error("توکن یا آدرس Bot API تنظیم نشده است")
|
||||
return False
|
||||
|
||||
url = f"{base_url.rstrip('/')}/bot{token}/sendMessage"
|
||||
try:
|
||||
response = requests.post(
|
||||
url, json={"chat_id": chat_id, "text": text}, timeout=REQUEST_TIMEOUT_SECONDS
|
||||
)
|
||||
response.raise_for_status()
|
||||
body = response.json()
|
||||
return bool(body.get("ok", True))
|
||||
except (requests.RequestException, ValueError) as exc:
|
||||
logger.error(f"❌ خطا در ارسال Bot API به {url}: {exc}")
|
||||
return False
|
||||
@@ -0,0 +1,11 @@
|
||||
# ایتا هم یک Bot API تلگرامسازگار (Eitaa Payam) دارد. اگر ساختار حساب تو
|
||||
# فرق دارد (مثلا آدرس دیگری برای endpoint)، همین base_url را عوض کن.
|
||||
from django.conf import settings
|
||||
|
||||
from .bot_api import send_bot_message
|
||||
|
||||
EITAA_BASE_URL = "https://eitaayar.ir/api"
|
||||
|
||||
|
||||
def send_eitaa(chat_id: str, text: str) -> bool:
|
||||
return send_bot_message(EITAA_BASE_URL, settings.EITAA_BOT_TOKEN, chat_id, text)
|
||||
@@ -0,0 +1,64 @@
|
||||
# apps/messaging/providers/sms.py
|
||||
"""
|
||||
ارسال پیامک گروهی با متن آزاد (نه پترن OTP) — از همان کاوهنگاری که
|
||||
apps/account/send_sms.py برای OTP استفاده میکند، ولی از API متفاوتی: پترن
|
||||
OTP فقط اجازهی ارسال یک قالب از پیش تاییدشده را میدهد، برای متن دلخواه باید
|
||||
از API عادی ارسال (sms/sendarray.json برای چندگیرندهی همزمان با متنهای
|
||||
مجزا) استفاده کرد. این دو مسیر کاملا مستقلاند؛ send_sms.py دستنخورده ماند.
|
||||
|
||||
فقط دو مقدار از .env لازم است: KAVENEGAR_API_KEY و KAVENEGAR_SENDER_LINE.
|
||||
اگر ساختار پاسخ/درخواست کاوهنگار حساب تو فرق دارد، فقط تابع
|
||||
`_call_sendarray` را با مستندات https://kavenegar.com/rest.html هماهنگ کن.
|
||||
"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
|
||||
import requests
|
||||
from django.conf import settings
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
REQUEST_TIMEOUT_SECONDS = 15
|
||||
KAVENEGAR_BASE = "https://api.kavenegar.com/v1"
|
||||
|
||||
|
||||
def _call_sendarray(receptors: list[str], messages: list[str]) -> dict[str, bool]:
|
||||
"""ارسال بالک؛ خروجی: {receptor: True/False} برای هر گیرنده."""
|
||||
api_key = settings.KAVENEGAR_API_KEY
|
||||
sender = settings.KAVENEGAR_SENDER_LINE
|
||||
result = {r: False for r in receptors}
|
||||
|
||||
if not api_key or not sender:
|
||||
logger.error("KAVENEGAR_API_KEY یا KAVENEGAR_SENDER_LINE در .env تنظیم نشده است")
|
||||
return result
|
||||
|
||||
url = f"{KAVENEGAR_BASE}/{api_key}/sms/sendarray.json"
|
||||
payload = {
|
||||
"receptor": json.dumps(receptors),
|
||||
"sender": json.dumps([sender] * len(receptors)),
|
||||
"message": json.dumps(messages),
|
||||
}
|
||||
|
||||
try:
|
||||
response = requests.post(url, data=payload, timeout=REQUEST_TIMEOUT_SECONDS)
|
||||
response.raise_for_status()
|
||||
body = response.json()
|
||||
if body.get("return", {}).get("status") == 200:
|
||||
for r in receptors:
|
||||
result[r] = True
|
||||
else:
|
||||
logger.error(f"❌ کاوهنگار خطا برگرداند: {body.get('return')}")
|
||||
except (requests.RequestException, ValueError) as exc:
|
||||
logger.error(f"❌ خطا در ارسال پیامک بالک: {exc}")
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def send_bulk_sms(recipients: list[tuple[str, str]]) -> dict[str, bool]:
|
||||
"""recipients: [(phone, message), ...] -> {phone: sent_ok}"""
|
||||
if not recipients:
|
||||
return {}
|
||||
phones = [p for p, _ in recipients]
|
||||
messages = [m for _, m in recipients]
|
||||
return _call_sendarray(phones, messages)
|
||||
@@ -0,0 +1,9 @@
|
||||
from django.conf import settings
|
||||
|
||||
from .bot_api import send_bot_message
|
||||
|
||||
TELEGRAM_BASE_URL = "https://api.telegram.org"
|
||||
|
||||
|
||||
def send_telegram(chat_id: str, text: str) -> bool:
|
||||
return send_bot_message(TELEGRAM_BASE_URL, settings.TELEGRAM_BOT_TOKEN, chat_id, text)
|
||||
@@ -0,0 +1,63 @@
|
||||
# apps/messaging/providers/whatsapp.py
|
||||
"""
|
||||
ارسال از طریق WhatsApp Business Cloud API رسمی (Meta) —
|
||||
https://developers.facebook.com/docs/whatsapp/cloud-api
|
||||
|
||||
برای ارسال گروهی/تبلیغاتی (business-initiated، نه پاسخ به پیام کاربر)،
|
||||
واتساپ فقط اجازهی ارسال «Template Message» از قبل تاییدشده را میدهد؛ پیام
|
||||
آزاد فقط داخل پنجرهی ۲۴ ساعتهی پاسخ به مشتری مجاز است. یعنی این تابع یک
|
||||
Template با یک متغیر ({{1}}) صدا میزند که همان متن رندرشدهی قالب داخل اپ
|
||||
در آن جای میگیرد — این Template باید از قبل در Meta Business Manager ثبت و
|
||||
تایید شده باشد.
|
||||
|
||||
سه مقدار از .env لازم است: WHATSAPP_ACCESS_TOKEN، WHATSAPP_PHONE_NUMBER_ID،
|
||||
WHATSAPP_TEMPLATE_NAME. اگر ساختار Template حساب تو (تعداد متغیرها، زبان)
|
||||
فرق دارد، فقط بدنهی payload در send_whatsapp را هماهنگ کن.
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
||||
import requests
|
||||
from django.conf import settings
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
REQUEST_TIMEOUT_SECONDS = 15
|
||||
GRAPH_API_VERSION = "v20.0"
|
||||
|
||||
|
||||
def send_whatsapp(phone: str, text: str) -> bool:
|
||||
token = settings.WHATSAPP_ACCESS_TOKEN
|
||||
phone_number_id = settings.WHATSAPP_PHONE_NUMBER_ID
|
||||
template_name = settings.WHATSAPP_TEMPLATE_NAME
|
||||
|
||||
if not token or not phone_number_id or not template_name:
|
||||
logger.error("تنظیمات WhatsApp Business API کامل نیست")
|
||||
return False
|
||||
|
||||
url = f"https://graph.facebook.com/{GRAPH_API_VERSION}/{phone_number_id}/messages"
|
||||
payload = {
|
||||
"messaging_product": "whatsapp",
|
||||
"to": phone,
|
||||
"type": "template",
|
||||
"template": {
|
||||
"name": template_name,
|
||||
"language": {"code": "fa"},
|
||||
"components": [
|
||||
{"type": "body", "parameters": [{"type": "text", "text": text}]},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
try:
|
||||
response = requests.post(
|
||||
url,
|
||||
json=payload,
|
||||
headers={"Authorization": f"Bearer {token}"},
|
||||
timeout=REQUEST_TIMEOUT_SECONDS,
|
||||
)
|
||||
response.raise_for_status()
|
||||
return True
|
||||
except requests.RequestException as exc:
|
||||
logger.error(f"❌ خطا در ارسال WhatsApp به {phone}: {exc}")
|
||||
return False
|
||||
Reference in New Issue
Block a user