Add all files
This commit is contained in:
398
extensions/Others/SocialBase/Services/MessagingService.php
Normal file
398
extensions/Others/SocialBase/Services/MessagingService.php
Normal file
@@ -0,0 +1,398 @@
|
||||
<?php
|
||||
|
||||
namespace Paymenter\Extensions\Others\SocialBase\Services;
|
||||
|
||||
use Paymenter\Extensions\Others\SocialBase\Models\Conversation;
|
||||
use Paymenter\Extensions\Others\SocialBase\Models\Message;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
|
||||
class MessagingService
|
||||
{
|
||||
/**
|
||||
* Send a message from one user to another
|
||||
*/
|
||||
public function sendDirectMessage($senderId, $recipientId, string $content, array $metadata = []): Message
|
||||
{
|
||||
$conversation = Conversation::findOrCreateDirectConversation($senderId, $recipientId);
|
||||
|
||||
return $this->sendMessage($conversation->id, $senderId, $content, $metadata);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message to a conversation
|
||||
*/
|
||||
public function sendMessage($conversationId, $senderId, string $content, array $metadata = []): Message
|
||||
{
|
||||
$message = Message::create([
|
||||
'conversation_id' => $conversationId,
|
||||
'sender_id' => $senderId,
|
||||
'content' => $content,
|
||||
'type' => 'text',
|
||||
'is_system' => false,
|
||||
'metadata' => $metadata,
|
||||
]);
|
||||
|
||||
// Update conversation last message time
|
||||
Conversation::where('id', $conversationId)->update([
|
||||
'last_message_at' => now(),
|
||||
]);
|
||||
|
||||
// Fire event for notifications
|
||||
Event::dispatch('socialbase.message.sent', [$message]);
|
||||
|
||||
// Fire moderation hook - allows extensions to auto-moderate
|
||||
$this->runModerationHooks($message);
|
||||
|
||||
return $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run moderation hooks to allow extensions to auto-moderate messages
|
||||
*/
|
||||
private function runModerationHooks(Message $message): void
|
||||
{
|
||||
// Dispatch moderation event - extensions can return actions
|
||||
$actions = Event::dispatch('socialbase.message.moderation', [$message]);
|
||||
|
||||
// Process actions from extensions
|
||||
foreach ($actions as $action) {
|
||||
if (empty($action) || !is_array($action)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$actionType = $action['action'] ?? null;
|
||||
$reason = $action['reason'] ?? 'Automated moderation';
|
||||
|
||||
switch ($actionType) {
|
||||
case 'flag':
|
||||
$this->flagMessage($message->id, null, $reason);
|
||||
break;
|
||||
case 'delete':
|
||||
$this->deleteMessage($message->id, null, $reason);
|
||||
break;
|
||||
case 'approve':
|
||||
$this->approveMessage($message->id, null);
|
||||
break;
|
||||
// 'keep' or null means do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a system message to a user
|
||||
*/
|
||||
public function sendSystemMessage($userId, string $content, string $systemType = 'general', array $metadata = []): Message
|
||||
{
|
||||
// Validate user exists
|
||||
if (!User::find($userId)) {
|
||||
throw new \InvalidArgumentException("User with ID {$userId} does not exist");
|
||||
}
|
||||
|
||||
// Find or create system conversation for THIS specific user
|
||||
// We need to find a system conversation that has ONLY this user as participant
|
||||
$conversation = Conversation::where('type', 'system')
|
||||
->whereNull('created_by')
|
||||
->whereHas('participants', function ($query) use ($userId) {
|
||||
$query->where('user_id', $userId);
|
||||
})
|
||||
->withCount('participants')
|
||||
->having('participants_count', '=', 1)
|
||||
->first();
|
||||
|
||||
if (!$conversation) {
|
||||
// Check if there are any orphaned system conversations for this user
|
||||
$this->cleanupOrphanedSystemConversations($userId);
|
||||
|
||||
// Create a new system conversation for this user
|
||||
$conversation = Conversation::create([
|
||||
'type' => 'system',
|
||||
'created_by' => null,
|
||||
'subject' => 'System Messages',
|
||||
]);
|
||||
|
||||
// Add user as participant
|
||||
$conversation->participants()->attach($userId);
|
||||
|
||||
// Log the creation for debugging
|
||||
\Log::info("Created new system conversation {$conversation->id} for user {$userId}");
|
||||
}
|
||||
|
||||
$message = Message::create([
|
||||
'conversation_id' => $conversation->id,
|
||||
'sender_id' => null,
|
||||
'content' => $content,
|
||||
'type' => 'system',
|
||||
'is_system' => true,
|
||||
'system_type' => $systemType,
|
||||
'metadata' => $metadata,
|
||||
]);
|
||||
|
||||
// Update conversation last message time
|
||||
$conversation->update(['last_message_at' => now()]);
|
||||
|
||||
// Fire event for notifications
|
||||
Event::dispatch('socialbase.system_message.sent', [$message, $userId]);
|
||||
|
||||
return $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up orphaned system conversations for a user
|
||||
*/
|
||||
private function cleanupOrphanedSystemConversations($userId): void
|
||||
{
|
||||
// Find all system conversations for this user
|
||||
$conversations = Conversation::where('type', 'system')
|
||||
->whereNull('created_by')
|
||||
->whereHas('participants', function ($query) use ($userId) {
|
||||
$query->where('user_id', $userId);
|
||||
})
|
||||
->with(['participants', 'messages'])
|
||||
->get();
|
||||
|
||||
if ($conversations->count() <= 1) {
|
||||
return; // No cleanup needed
|
||||
}
|
||||
|
||||
\Log::warning("Found {$conversations->count()} system conversations for user {$userId}, consolidating...");
|
||||
|
||||
// Sort by creation date to keep the oldest as the main conversation
|
||||
$conversations = $conversations->sortBy('created_at');
|
||||
$mainConversation = $conversations->first();
|
||||
$conversationsToMerge = $conversations->skip(1);
|
||||
|
||||
foreach ($conversationsToMerge as $conversationToMerge) {
|
||||
// Move all messages from the conversation to merge to the main conversation
|
||||
$messages = $conversationToMerge->messages;
|
||||
|
||||
foreach ($messages as $message) {
|
||||
$message->update(['conversation_id' => $mainConversation->id]);
|
||||
}
|
||||
|
||||
// Update the main conversation's last message time if needed
|
||||
$latestMessage = $mainConversation->messages()->latest()->first();
|
||||
if ($latestMessage) {
|
||||
$mainConversation->update(['last_message_at' => $latestMessage->created_at]);
|
||||
}
|
||||
|
||||
// Delete the conversation to merge
|
||||
$conversationToMerge->delete();
|
||||
|
||||
\Log::info("Merged conversation {$conversationToMerge->id} into {$mainConversation->id} for user {$userId}");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send system message to multiple users
|
||||
*/
|
||||
public function sendSystemMessageToUsers(array $userIds, string $content, string $systemType = 'general', array $metadata = []): array
|
||||
{
|
||||
$messages = [];
|
||||
|
||||
foreach ($userIds as $userId) {
|
||||
$messages[] = $this->sendSystemMessage($userId, $content, $systemType, $metadata);
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user's conversations
|
||||
*/
|
||||
public function getUserConversations($userId, $includeArchived = false)
|
||||
{
|
||||
$query = Conversation::whereHas('participants', function ($q) use ($userId, $includeArchived) {
|
||||
$q->where('user_id', $userId);
|
||||
|
||||
if (!$includeArchived) {
|
||||
$q->where('is_archived', false);
|
||||
}
|
||||
})
|
||||
->with(['lastMessage', 'participants'])
|
||||
->orderBy('last_message_at', 'desc');
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get unread messages count for user
|
||||
*/
|
||||
public function getUnreadCount($userId): int
|
||||
{
|
||||
$conversations = Conversation::whereHas('participants', function ($q) use ($userId) {
|
||||
$q->where('user_id', $userId);
|
||||
})->get();
|
||||
|
||||
$total = 0;
|
||||
foreach ($conversations as $conversation) {
|
||||
$total += $conversation->getUnreadCountForUser($userId);
|
||||
}
|
||||
|
||||
return $total;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flag message for moderation
|
||||
*/
|
||||
public function flagMessage($messageId, $flaggedBy, string $reason = null): void
|
||||
{
|
||||
$message = Message::findOrFail($messageId);
|
||||
|
||||
$message->update([
|
||||
'is_moderated' => true,
|
||||
'moderation_status' => 'flagged',
|
||||
'moderation_reason' => $reason,
|
||||
]);
|
||||
|
||||
Event::dispatch('socialbase.message.flagged', [$message, $flaggedBy]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Approve message
|
||||
*/
|
||||
public function approveMessage($messageId, $moderatorId): void
|
||||
{
|
||||
$message = Message::findOrFail($messageId);
|
||||
|
||||
$message->update([
|
||||
'is_moderated' => true,
|
||||
'moderation_status' => 'approved',
|
||||
'moderated_by' => $moderatorId,
|
||||
'moderated_at' => now(),
|
||||
]);
|
||||
|
||||
Event::dispatch('socialbase.message.approved', [$message, $moderatorId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete message (soft delete)
|
||||
*/
|
||||
public function deleteMessage($messageId, $moderatorId, string $reason = null): void
|
||||
{
|
||||
$message = Message::findOrFail($messageId);
|
||||
|
||||
$message->update([
|
||||
'is_moderated' => true,
|
||||
'moderation_status' => 'deleted',
|
||||
'moderated_by' => $moderatorId,
|
||||
'moderated_at' => now(),
|
||||
'moderation_reason' => $reason,
|
||||
]);
|
||||
|
||||
$message->delete();
|
||||
|
||||
Event::dispatch('socialbase.message.deleted', [$message, $moderatorId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Report a message
|
||||
*/
|
||||
public function reportMessage($messageId, $reportedBy, string $reason, string $description = null)
|
||||
{
|
||||
$report = \Paymenter\Extensions\Others\SocialBase\Models\MessageReport::create([
|
||||
'message_id' => $messageId,
|
||||
'reported_by' => $reportedBy,
|
||||
'reason' => $reason,
|
||||
'description' => $description,
|
||||
'status' => 'pending',
|
||||
]);
|
||||
|
||||
// Auto-flag the message if it gets multiple reports
|
||||
$message = Message::findOrFail($messageId);
|
||||
$reportCount = $message->reports()->where('status', 'pending')->count();
|
||||
|
||||
if ($reportCount >= 3 && $message->moderation_status !== 'flagged') {
|
||||
$this->flagMessage($messageId, null, "Automatically flagged after {$reportCount} user reports");
|
||||
}
|
||||
|
||||
Event::dispatch('socialbase.message.reported', [$message, $report]);
|
||||
|
||||
return $report;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get message reports
|
||||
*/
|
||||
public function getMessageReports($messageId)
|
||||
{
|
||||
return \Paymenter\Extensions\Others\SocialBase\Models\MessageReport::where('message_id', $messageId)
|
||||
->with(['reporter', 'reviewer'])
|
||||
->orderBy('created_at', 'desc')
|
||||
->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for and consolidate duplicate system conversations for all users
|
||||
*/
|
||||
public function consolidateSystemConversations(): array
|
||||
{
|
||||
$results = [];
|
||||
|
||||
// Get all users
|
||||
$users = User::all();
|
||||
|
||||
foreach ($users as $user) {
|
||||
$conversations = Conversation::where('type', 'system')
|
||||
->whereNull('created_by')
|
||||
->whereHas('participants', function ($query) use ($user) {
|
||||
$query->where('user_id', $user->id);
|
||||
})
|
||||
->with(['participants', 'messages'])
|
||||
->get();
|
||||
|
||||
if ($conversations->count() <= 1) {
|
||||
continue; // No consolidation needed
|
||||
}
|
||||
|
||||
$this->cleanupOrphanedSystemConversations($user->id);
|
||||
|
||||
$results[] = [
|
||||
'user_id' => $user->id,
|
||||
'user_name' => $user->name,
|
||||
'conversations_found' => $conversations->count(),
|
||||
'consolidated' => true
|
||||
];
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get system conversation statistics
|
||||
*/
|
||||
public function getSystemConversationStats(): array
|
||||
{
|
||||
$systemConversations = Conversation::where('type', 'system')
|
||||
->whereNull('created_by')
|
||||
->with(['participants', 'messages'])
|
||||
->get();
|
||||
|
||||
$stats = [
|
||||
'total_conversations' => $systemConversations->count(),
|
||||
'total_messages' => $systemConversations->sum(function ($conv) {
|
||||
return $conv->messages->count();
|
||||
}),
|
||||
'users_with_system_conversations' => 0,
|
||||
'conversations_by_user' => []
|
||||
];
|
||||
|
||||
// Group by user
|
||||
foreach ($systemConversations as $conversation) {
|
||||
foreach ($conversation->participants as $participant) {
|
||||
$userId = $participant->pivot->user_id; // Get user_id from pivot table
|
||||
if ($userId) { // Only count if user_id is not null/empty
|
||||
if (!isset($stats['conversations_by_user'][$userId])) {
|
||||
$stats['conversations_by_user'][$userId] = 0;
|
||||
}
|
||||
$stats['conversations_by_user'][$userId]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$stats['users_with_system_conversations'] = count($stats['conversations_by_user']);
|
||||
|
||||
return $stats;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user