From 35c0a6680c79b289dde2ba9f9f2aa404d096fa4d Mon Sep 17 00:00:00 2001 From: Hamza-Ayed Date: Sun, 5 Jul 2026 00:06:55 +0300 Subject: [PATCH] Update: 2026-07-05 00:06:55 --- .../marketing_engine/cron_weekly_report.php | 77 ++++++++++ backend/marketing_engine/index.php | 28 ++++ backend/marketing_engine/schema.sql | 8 + .../lib/controller/auth/otp_helper.dart | 5 + .../controller/firebase/firbase_messge.dart | 14 ++ siro_admin/lib/routes.dart | 2 + .../lib/views/admin/admin_home_page.dart | 2 + .../marketing/social_intelligence_screen.dart | 139 ++++++++++++++++++ siro_admin/pubspec.lock | 24 +++ siro_admin/pubspec.yaml | 1 + 10 files changed, 300 insertions(+) create mode 100644 backend/marketing_engine/cron_weekly_report.php create mode 100644 siro_admin/lib/views/admin/marketing/social_intelligence_screen.dart diff --git a/backend/marketing_engine/cron_weekly_report.php b/backend/marketing_engine/cron_weekly_report.php new file mode 100644 index 00000000..5131a37a --- /dev/null +++ b/backend/marketing_engine/cron_weekly_report.php @@ -0,0 +1,77 @@ +prepare(" + SELECT platform, report_html, created_at + FROM marketing_reports + WHERE is_weekly = 0 + AND created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY) + ORDER BY created_at ASC + "); + $stmt->execute(); + $reports = $stmt->fetchAll(PDO::FETCH_ASSOC); + + if (empty($reports)) { + echo "No reports generated in the last 7 days to summarize.\n"; + exit; + } + + // Combine all report data for Gemini + $combinedData = []; + foreach ($reports as $report) { + $combinedData[] = [ + 'platform' => $report['platform'], + 'date' => $report['created_at'], + 'content' => strip_tags($report['report_html']) // Send stripped HTML to save tokens + ]; + } + + $gemini = new SiroGeminiService(); + + // Create a new method in GeminiService or use evaluatePostsForReporting with a specific prompt wrapper + $prompt = "You are a Chief Marketing Officer AI. Below are all the daily/individual social media intelligence reports collected over the last 7 days from our scraper bots. Please read through all of them and generate one comprehensive, executive 'Weekly Market Intelligence Summary'. Format it beautifully in HTML. Highlight key market trends, recurrent complaints, competitor activities, and actionable advice for the week.\n\nData:\n" . json_encode($combinedData); + + $response = $gemini->callGemini($prompt, 'gemini-1.5-flash'); + + if (isset($response['candidates'][0]['content']['parts'][0]['text'])) { + $weeklyHtml = $response['candidates'][0]['content']['parts'][0]['text']; + + // Save the weekly report + $insert = $con->prepare("INSERT INTO marketing_reports (platform, report_html, is_weekly) VALUES ('all', ?, 1)"); + $insert->execute([$weeklyHtml]); + $reportId = $con->lastInsertId(); + + // Notify Admins + $fcm = new FcmService(); + $fcm->send( + token: '/topics/admin_alerts', + title: 'Weekly Market Intelligence Summary 📈', + body: 'The comprehensive weekly social media report has been generated.', + data: [ + 'type' => 'marketing_report', + 'report_id' => $reportId, + 'is_weekly' => 1 + ], + category: 'marketing_report' + ); + + echo "Weekly report generated and saved successfully. ID: $reportId\n"; + } else { + echo "Failed to generate report from Gemini.\n"; + } + +} catch (Exception $e) { + echo "Error generating weekly report: " . $e->getMessage() . "\n"; +} diff --git a/backend/marketing_engine/index.php b/backend/marketing_engine/index.php index 08123802..1a46350b 100644 --- a/backend/marketing_engine/index.php +++ b/backend/marketing_engine/index.php @@ -7,6 +7,7 @@ require_once __DIR__ . '/../core/bootstrap.php'; require_once __DIR__ . '/../functions.php'; require_once __DIR__ . '/../core/Services/SiroGeminiService.php'; +require_once __DIR__ . '/../core/Services/FcmService.php'; header('Content-Type: application/json'); @@ -87,11 +88,38 @@ try { // Save to database $stmt = $con->prepare("INSERT INTO marketing_reports (platform, report_html) VALUES (?, ?)"); $stmt->execute([$platform, $reportHtml]); + $reportId = $con->lastInsertId(); + + // Send FCM notification to admins + $fcm = new FcmService(); + $fcm->send( + token: '/topics/admin_alerts', + title: 'New Social Media Intelligence Report 📊', + body: "A new autonomous analysis report for {$platform} has been generated.", + data: [ + 'type' => 'marketing_report', + 'report_id' => $reportId, + 'platform' => $platform + ], + category: 'marketing_report' + ); } echo json_encode(['status' => 'success', 'message' => 'Posts evaluated and report saved']); break; + case 'get_reports': + $stmt = $con->prepare(" + SELECT id, platform, report_html, is_weekly, created_at + FROM marketing_reports + ORDER BY created_at DESC + LIMIT 50 + "); + $stmt->execute(); + $reports = $stmt->fetchAll(PDO::FETCH_ASSOC); + echo json_encode(['status' => 'success', 'data' => $reports]); + break; + default: echo json_encode(['status' => 'error', 'message' => 'Invalid action']); } diff --git a/backend/marketing_engine/schema.sql b/backend/marketing_engine/schema.sql index 49b57e78..f4c26f60 100644 --- a/backend/marketing_engine/schema.sql +++ b/backend/marketing_engine/schema.sql @@ -68,3 +68,11 @@ INSERT IGNORE INTO `api_quotas` (`service_name`, `daily_usage`, `quota_limit`, ` ('gemini', 0, 100, CURDATE()), ('elevenlabs', 0, 5, CURDATE()), ('creatomate', 0, 2, CURDATE()); + +CREATE TABLE IF NOT EXISTS marketing_reports ( + id INT AUTO_INCREMENT PRIMARY KEY, + platform ENUM('facebook', 'instagram', 'tiktok', 'twitter', 'youtube', 'all') NOT NULL, + report_html TEXT NOT NULL, + is_weekly TINYINT(1) DEFAULT 0, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP +); diff --git a/siro_admin/lib/controller/auth/otp_helper.dart b/siro_admin/lib/controller/auth/otp_helper.dart index b26fad08..ac16b447 100644 --- a/siro_admin/lib/controller/auth/otp_helper.dart +++ b/siro_admin/lib/controller/auth/otp_helper.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:jwt_decoder/jwt_decoder.dart'; import 'package:get/get.dart'; +import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:siro_admin/controller/functions/device_info.dart'; import 'package:siro_admin/views/auth/login_page.dart'; @@ -184,6 +185,8 @@ class OtpHelper extends GetxController { await box.write(BoxName.phoneVerified, true); await box.write('admin_password', password); + FirebaseMessaging.instance.subscribeToTopic('admin_alerts'); + mySnackbarSuccess('تم تسجيل الدخول بنجاح'); return true; } @@ -238,6 +241,7 @@ class OtpHelper extends GetxController { String token = r(rawJwt.toString()).split(AppInformation.addd)[0]; if (!JwtDecoder.isExpired(token)) { Log.print('Valid JWT found, skipping login.php (no OTP needed)'); + FirebaseMessaging.instance.subscribeToTopic('admin_alerts'); Get.offAll(() => const AdminHomePage()); return; } @@ -275,6 +279,7 @@ class OtpHelper extends GetxController { } else if (response['jwt'] != null) { box.write(BoxName.jwt, c(response['jwt'])); } + FirebaseMessaging.instance.subscribeToTopic('admin_alerts'); try { if (Get.isRegistered()) { Get.find().getDashBoard(); diff --git a/siro_admin/lib/controller/firebase/firbase_messge.dart b/siro_admin/lib/controller/firebase/firbase_messge.dart index 59f43c7d..2a53f6a7 100644 --- a/siro_admin/lib/controller/firebase/firbase_messge.dart +++ b/siro_admin/lib/controller/firebase/firbase_messge.dart @@ -241,6 +241,20 @@ class FirebaseMessagesController extends GetxController { fireBaseTitles(message); } }); + + FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) { + if (message.data['type'] == 'marketing_report') { + Get.toNamed('/social-intelligence'); + } + }); + + FirebaseMessaging.instance.getInitialMessage().then((RemoteMessage? message) { + if (message != null && message.data['type'] == 'marketing_report') { + Future.delayed(const Duration(seconds: 2), () { + Get.toNamed('/social-intelligence'); + }); + } + }); } void fireBaseTitles(RemoteMessage message) { diff --git a/siro_admin/lib/routes.dart b/siro_admin/lib/routes.dart index 644c841b..733ced70 100644 --- a/siro_admin/lib/routes.dart +++ b/siro_admin/lib/routes.dart @@ -8,6 +8,7 @@ import 'views/admin/complaints/complaint_list_page.dart'; import 'views/admin/drivers/driver_documents_review_page.dart'; import 'views/admin/marketing/marketing_page.dart'; import 'views/admin/marketing/heatmap_page.dart'; +import 'views/admin/marketing/social_intelligence_screen.dart'; List> routes = [ GetPage(name: "/", page: () => const AdminHomePage()), @@ -19,4 +20,5 @@ List> routes = [ GetPage(name: "/driver-docs", page: () => DriverDocsReviewPage()), GetPage(name: "/marketing", page: () => const MarketingPage()), GetPage(name: "/heatmap", page: () => const HeatmapPage()), + GetPage(name: "/social-intelligence", page: () => const SocialIntelligenceScreen()), ]; diff --git a/siro_admin/lib/views/admin/admin_home_page.dart b/siro_admin/lib/views/admin/admin_home_page.dart index d3e3e180..ec47521d 100644 --- a/siro_admin/lib/views/admin/admin_home_page.dart +++ b/siro_admin/lib/views/admin/admin_home_page.dart @@ -757,6 +757,8 @@ class _AdminHomePageState extends State () => Get.toNamed('/complaints')), ActionItem('مراجعة الوثائق', Icons.assignment_ind_rounded, _info, () => Get.toNamed('/driver-docs')), + ActionItem('استخبارات السوشيال', Icons.insights_rounded, const Color(0xFFC792EA), + () => Get.toNamed('/social-intelligence')), ], ), ActionCategory( diff --git a/siro_admin/lib/views/admin/marketing/social_intelligence_screen.dart b/siro_admin/lib/views/admin/marketing/social_intelligence_screen.dart new file mode 100644 index 00000000..29b9f4c1 --- /dev/null +++ b/siro_admin/lib/views/admin/marketing/social_intelligence_screen.dart @@ -0,0 +1,139 @@ +import 'dart:convert'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:http/http.dart' as http; +import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart'; +import '../../../constant/colors.dart'; +import '../../../constant/links.dart'; + +class SocialIntelligenceController extends GetxController { + var isLoading = true.obs; + var reports = [].obs; + + @override + void onInit() { + super.onInit(); + fetchReports(); + } + + Future fetchReports() async { + try { + isLoading(true); + final url = Uri.parse( + '${AppLink.server}/marketing_engine/index.php?action=get_reports'); + final response = await http.get(url); + + if (response.statusCode == 200) { + final data = json.decode(response.body); + if (data['status'] == 'success') { + reports.value = data['data'] ?? []; + } + } + } catch (e) { + print('Error fetching reports: $e'); + } finally { + isLoading(false); + } + } +} + +class SocialIntelligenceScreen extends StatelessWidget { + const SocialIntelligenceScreen({super.key}); + + @override + Widget build(BuildContext context) { + final controller = Get.put(SocialIntelligenceController()); + + return Scaffold( + backgroundColor: AppColor.bg, + appBar: AppBar( + title: const Text( + 'استخبارات السوشيال ميديا (Social Intelligence)', + style: TextStyle(fontWeight: FontWeight.w700, fontSize: 16), + ), + backgroundColor: AppColor.bg, + elevation: 0, + foregroundColor: AppColor.textPrimary, + actions: [ + IconButton( + icon: const Icon(Icons.refresh), + onPressed: () => controller.fetchReports(), + ) + ], + ), + body: Obx(() { + if (controller.isLoading.value) { + return const Center( + child: CircularProgressIndicator(color: AppColor.accent)); + } + + if (controller.reports.isEmpty) { + return const Center( + child: Text( + 'لا توجد تقارير حالياً', + style: TextStyle(color: AppColor.textSecondary), + ), + ); + } + + return ListView.builder( + padding: const EdgeInsets.all(16), + itemCount: controller.reports.length, + itemBuilder: (context, index) { + final report = controller.reports[index]; + final isWeekly = report['is_weekly'].toString() == '1'; + + return Card( + color: AppColor.surface, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + side: BorderSide( + color: isWeekly ? AppColor.accent : AppColor.divider, + width: isWeekly ? 1.5 : 1.0), + ), + margin: const EdgeInsets.only(bottom: 16), + child: ExpansionTile( + iconColor: AppColor.accent, + collapsedIconColor: AppColor.textSecondary, + title: Text( + isWeekly + ? '📈 تقرير السوق الأسبوعي الشامل' + : '📊 تقرير ${report['platform'].toString().toUpperCase()}', + style: TextStyle( + fontWeight: FontWeight.bold, + color: isWeekly ? AppColor.accent : AppColor.textPrimary, + fontSize: 14, + ), + ), + subtitle: Text( + report['created_at'].toString(), + style: const TextStyle( + color: AppColor.textSecondary, fontSize: 11), + ), + children: [ + Container( + padding: const EdgeInsets.all(16), + width: double.infinity, + decoration: const BoxDecoration( + border: Border(top: BorderSide(color: AppColor.divider)), + ), + child: Directionality( + textDirection: TextDirection.rtl, + child: HtmlWidget( + report['report_html'] ?? '', + textStyle: const TextStyle( + color: AppColor.textPrimary, + fontSize: 13, + height: 1.6), + ), + ), + ) + ], + ), + ); + }, + ); + }), + ); + } +} diff --git a/siro_admin/pubspec.lock b/siro_admin/pubspec.lock index 225f75ed..fa70eb66 100644 --- a/siro_admin/pubspec.lock +++ b/siro_admin/pubspec.lock @@ -185,6 +185,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.7" + csslib: + dependency: transitive + description: + name: csslib + sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e" + url: "https://pub.dev" + source: hosted + version: "1.0.2" dart_earcut: dependency: transitive description: @@ -552,6 +560,14 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_widget_from_html_core: + dependency: "direct main" + description: + name: flutter_widget_from_html_core + sha256: b1048fd119a14762e2361bd057da608148a895477846d6149109b2151d2f7abf + url: "https://pub.dev" + source: hosted + version: "0.15.2" get: dependency: "direct main" description: @@ -592,6 +608,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.2" + html: + dependency: transitive + description: + name: html + sha256: "6d1264f2dffa1b1101c25a91dff0dc2daee4c18e87cd8538729773c073dbf602" + url: "https://pub.dev" + source: hosted + version: "0.15.6" http: dependency: "direct main" description: diff --git a/siro_admin/pubspec.yaml b/siro_admin/pubspec.yaml index a8467f36..96a74950 100644 --- a/siro_admin/pubspec.yaml +++ b/siro_admin/pubspec.yaml @@ -68,6 +68,7 @@ dependencies: flutter_staggered_animations: ^1.1.1 jailbreak_root_detection: ^1.1.5 package_info_plus: ^4.0.2 + flutter_widget_from_html_core: ^0.15.1 image: any path_provider: any