195 lines
6.7 KiB
PHP
195 lines
6.7 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Api;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Services\ZaloBotService;
|
|
use App\Services\SettingService;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\Request;
|
|
|
|
class ZaloWebhookController extends Controller
|
|
{
|
|
protected ZaloBotService $zaloBotService;
|
|
protected SettingService $settingService;
|
|
|
|
public function __construct(
|
|
ZaloBotService $zaloBotService,
|
|
SettingService $settingService
|
|
) {
|
|
$this->zaloBotService = $zaloBotService;
|
|
$this->settingService = $settingService;
|
|
}
|
|
|
|
/**
|
|
* Handle webhook request from Zalo Bot Platform.
|
|
*/
|
|
public function handle(Request $request): JsonResponse
|
|
{
|
|
$configuredSecret = $this->settingService->get('zalo_webhook_secret');
|
|
|
|
if (!empty($configuredSecret)) {
|
|
$incomingSecret = $request->header('X-Bot-Api-Secret-Token')
|
|
?? $request->header('x-bot-api-secret-token');
|
|
|
|
if ($incomingSecret !== $configuredSecret) {
|
|
return response()->json([
|
|
'ok' => false,
|
|
'error' => 'Unauthorized. Secret token mismatch.'
|
|
], 401);
|
|
}
|
|
}
|
|
|
|
$payload = $request->all();
|
|
|
|
// Pass payload to ZaloBotService
|
|
$this->zaloBotService->handleWebhook($payload);
|
|
|
|
return response()->json([
|
|
'ok' => true,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Process a Shopee link for the Zalo user-bot (self-bot) and return the formatted reply.
|
|
*/
|
|
public function processLink(Request $request): JsonResponse
|
|
{
|
|
$request->validate([
|
|
'url' => 'required|string',
|
|
'sender_name' => 'nullable|string',
|
|
'thread_id' => 'nullable|string',
|
|
'chat_name' => 'nullable|string'
|
|
]);
|
|
|
|
$url = $request->input('url');
|
|
$fromName = $request->input('sender_name') ?? 'Bạn';
|
|
$threadId = $request->input('thread_id');
|
|
$chatName = $request->input('chat_name');
|
|
|
|
// 1. Add/Update this thread into discovered groups pool so Admin can whitelist it
|
|
if ($threadId) {
|
|
\App\Models\ZaloDiscoveredGroup::updateOrCreate(
|
|
['group_id' => $threadId],
|
|
['name' => $chatName ?: ($fromName ? "Chat với {$fromName}" : "Trò chuyện cá nhân")]
|
|
);
|
|
}
|
|
|
|
// 2. Whitelist Filtering: Bot is ONLY allowed to respond to whitelisted threads (Default Deny)
|
|
$isAllowed = \App\Models\ZaloChannel::where('thread_id', $threadId)
|
|
->where('is_allowed', true)
|
|
->exists();
|
|
|
|
$productTitle = $request->input('product_title');
|
|
$productImage = $request->input('product_image');
|
|
|
|
if (!$isAllowed) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'error' => 'Thread is not whitelisted. Bot response restricted.'
|
|
], 403);
|
|
}
|
|
|
|
try {
|
|
// 1. Resolve redirect if short link
|
|
$resolvedUrl = $this->zaloBotService->resolveShortUrl($url);
|
|
|
|
// 2. Convert link to short redirect affiliate link and log details
|
|
$convertResult = app(\App\Services\LinkConverterService::class)->process(
|
|
$resolvedUrl,
|
|
$threadId,
|
|
$chatName,
|
|
'zalo_bot',
|
|
null,
|
|
null,
|
|
$productTitle,
|
|
$productImage
|
|
);
|
|
|
|
if ($convertResult && $convertResult->isSuccess) {
|
|
$shortLink = $convertResult->affiliateUrl;
|
|
|
|
// Fetch the logged history to extract cào details
|
|
$history = \App\Models\ConversionHistory::where('original_url', $resolvedUrl)
|
|
->where('zalo_thread_id', $threadId)
|
|
->latest()
|
|
->first();
|
|
|
|
$productName = $history && $history->product_title ? $history->product_title : ($productTitle ?: ($this->zaloBotService->extractProductName($resolvedUrl) ?? 'Sản phẩm Shopee'));
|
|
|
|
// 3. Construct the message template
|
|
$replyText = "Hi @{$fromName}, Link của bạn đây ạ:\n\n" .
|
|
"📦 Tên sản phẩm Shopee:\n{$productName}\n\n" .
|
|
"🔗 Link: {$shortLink}\n\n" .
|
|
"⚠️ Lưu ý: Để đơn hàng được ghi nhận, KHÔNG mua qua Livestream, KHÔNG xem video/live bất kỳ sau khi click & XÓA sản phẩm ra khỏi giỏ hàng TRƯỚC KHI bấm link!";
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'reply_text' => $replyText,
|
|
'short_link' => $shortLink
|
|
]);
|
|
}
|
|
|
|
return response()->json([
|
|
'success' => false,
|
|
'error' => $convertResult->errorMessage ?? 'Conversion failed'
|
|
], 400);
|
|
|
|
} catch (\Exception $e) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'error' => $e->getMessage()
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update Zalo user-bot (self-bot) status in settings table.
|
|
*/
|
|
public function updateStatus(Request $request): JsonResponse
|
|
{
|
|
$request->validate([
|
|
'status' => 'required|string',
|
|
'user_name' => 'nullable|string'
|
|
]);
|
|
|
|
$this->settingService->set('zalo_bot_status', $request->input('status'));
|
|
|
|
if ($request->has('user_name')) {
|
|
$this->settingService->set('zalo_bot_user', $request->input('user_name') ?? '');
|
|
} else if ($request->input('status') === 'disconnected') {
|
|
$this->settingService->set('zalo_bot_user', '');
|
|
}
|
|
|
|
return response()->json(['ok' => true]);
|
|
}
|
|
|
|
/**
|
|
* Sync Zalo groups from userbot into discovered pool.
|
|
*/
|
|
public function syncGroups(Request $request): JsonResponse
|
|
{
|
|
$request->validate([
|
|
'groups' => 'required|array',
|
|
'groups.*.id' => 'required|string',
|
|
'groups.*.name' => 'nullable|string'
|
|
]);
|
|
|
|
$groups = $request->input('groups');
|
|
$fetchedIds = array_column($groups, 'id');
|
|
|
|
// Delete groups no longer joined
|
|
\App\Models\ZaloDiscoveredGroup::whereNotIn('group_id', $fetchedIds)->delete();
|
|
|
|
// Update or insert current groups
|
|
foreach ($groups as $group) {
|
|
\App\Models\ZaloDiscoveredGroup::updateOrCreate(
|
|
['group_id' => $group['id']],
|
|
['name' => $group['name'] ?? 'Nhóm Zalo']
|
|
);
|
|
}
|
|
|
|
return response()->json(['ok' => true]);
|
|
}
|
|
}
|