<?php
namespace App\Service;
use App\Entity\DiscussionGroup;
use App\Entity\Campaign;
use App\Entity\FileMessage;
use App\Entity\Message;
use App\Entity\Mission;
use App\Entity\User;
use App\Enum\AdminMail;
use App\Event\Chat\MessageSentEvent;
use App\Event\User\UserMentionnedEvent;
use App\Event\FirebaseCloudeMessaging\FcmNotificationEvent;
use App\Repository\CampaignRepository;
use App\Repository\MessageRepository;
use App\Repository\UserRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bridge\Twig\Mime\NotificationEmail;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mime\Address;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use App\Service\DynamicHostService;
use Twig\Environment;
class MessageService
{
public function __construct(
private MessageRepository $messageRepository,
private EntityManagerInterface $entityManagerInterface,
private CampaignRepository $campaignRepository,
private UserRepository $userRepository,
private Security $security,
private ParameterBagInterface $parameterBagInterface,
private MailerInterface $mailer,
private EventDispatcherInterface $dispatcher,
private GoogleStorageService $googleStorageService,
private DynamicHostService $dynamicHostService,
private EntityManagerInterface $entityManager,
private Environment $twig,
) {
}
public function addMessage(Mission $mission, Message $message, $files, $idMessageToReply, $idMessageToEdit, $pathFileMessage){
foreach ($files as $file) {
$nameSplitByDot = explode('.', $file->getClientOriginalName());
$extension = sizeof($nameSplitByDot) > 1 ? end($nameSplitByDot) : $file->guessExtension();
$type = preg_replace('/\/[a-zA-Z-_,.]*\s?/',"",$file->getMimeType());
$nameUniqueCompany = strtolower("company-" . $mission->getCampaign()->getCompany()->getId());
$originalFilename = pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME);
$newFilename = $originalFilename . '-' . uniqid() . '.' . $extension;
//upload file in google storage
$this->googleStorageService->uploadFile($nameUniqueCompany, $file, $newFilename, 'Campaigns/' . $mission->getCampaign()->getId());
$fileMessage = new FileMessage();
$fileMessage->setName($newFilename);
$fileMessage->setIsNew(1);
$fileMessage->setType($type);
$fileMessage->setIsNotInfected(true);
$message->addFileMessage($fileMessage);
}
if ($idMessageToEdit != "" && $idMessageToEdit!=null) {
$messagePersisted = $this->messageRepository->findOneBy(['id' => $idMessageToEdit]);
$messagePersisted->setIsModified(true);
$messagePersisted->setContent($message->getContent(), false)
->setOriginalContent($message->getContent())
;
$message = $messagePersisted;
} else {
$message->setUser($this->security->getUser())
->setCampaign($mission->getCampaign())
->setMission(mission: $mission)
->setUserListWhoRead([$this->security->getUser()->getId()])
->setOriginalContent($message->getContent());
;
$this->entityManagerInterface->persist($message);
}
if ($idMessageToReply != "" && $idMessageToReply!=null) {
$replyedMessage = $this->messageRepository->findOneBy(['id' => $idMessageToReply]);
$message->setReplyToWithMessage($replyedMessage);
}
$this->entityManagerInterface->flush();
$this->sendEmailForParticipantMentionedInTchat($mission, $message);
return $message;
}
public function addAtTheLine($content){
$pattern = "/\r/";
return preg_replace($pattern,'<br>',$content);
}
public function sendEmailForParticipantMentionedInTchat(Mission $mission, Message $message)
{
$rawMessage = $message->getOriginalContent();
$messageContent = $message->getContent();
$messageId = $message->getId();
$messageStripTag = strip_tags($messageContent);
$missionReference = $message->getMission()->getReference();
$idUserToExclure = [];
$idUserToAlreadyHaveMessage = [];
$usersMentionned = [];
$userMentionnedAllMembre = false;
if ($rawMessage != null && $message->getContent() != null) {
$pattern = '/@\w*\S/u';
preg_match_all($pattern, $messageContent, $matches);
$users = $matches[0];
if (in_array("@tous", $users)) {
$message->setContent($this->putBoldUserMentioned($message->getContent(), '@tous', '@tous'), false, false);
$this->entityManagerInterface->flush();
$userMentionnedAllMembre = true;
}
foreach ($users as $userIdentifier) {
$identifier = str_replace('@', '',rtrim($userIdentifier,'.,;:/!?'));
$company = $this->dynamicHostService->getCompany();
$user = $this->userRepository->getUserByIdentifier($identifier,$company);
if ($user != null) {
$message->setContent($this->putBoldUserMentioned($message->getContent(), $userIdentifier, $user->getFirstname()), false, false);
$this->entityManagerInterface->flush();
$idUserToExclure = [...$idUserToExclure, $user->getId()];
$usersMentionned = [...$usersMentionned,$user ];
}
}
if($userMentionnedAllMembre){
$event = new UserMentionnedEvent($mission, null, $message->getContent(), $messageId, null);
$this->dispatcher->dispatch($event, UserMentionnedEvent::NAME);
}
else{
foreach ($usersMentionned as $user) {
foreach ($mission->getCampaign()->getMissions() as $mission) {
foreach ($mission->getParticipants() as $participant) {
if(
!in_array($participant->getUser()->getId(), $idUserToAlreadyHaveMessage) and
$participant->getUser()->getId() == $user->getId()
){
$event = new UserMentionnedEvent($mission, $user, $message->getContent(), $messageId, $user->getEmail());
$this->dispatcher->dispatch($event, UserMentionnedEvent::NAME);
$idUserToAlreadyHaveMessage = [...$idUserToAlreadyHaveMessage, $user->getId()];
}
}
}
}
}
$event = new MessageSentEvent($message, $idUserToExclure);
$this->dispatcher->dispatch($event, MessageSentEvent::NAME);
}
}
public function putBoldUserMentioned($content, $search, $remplace){
$htmlToRemplace = "<strong data-origin='$search'>$remplace</strong>";
return preg_replace("/$search\b/", $htmlToRemplace, $this->addAtTheLine($content));
}
public function sendEmailError(string $errorType, string $messageType, string $message)
{
// $content = "
// <p>Bonjour</p>
// <p>Une erreur à été survenue lors d'enregistrement d'un message du {$this->security->getUser()->getFullname()}</p>
// <p>type de l'erreur : $errorType</p>
// <p>type de message : $messageType</p>
// <p>Message : $message</p>
// ";
// $admins = ['r.rado@softibox.com', 'anthony@softibox.com'];
// foreach ($admins as $email) {
// $this->sendEmail(
// new Address('caroline.b@my-flow.fr', 'myFlow'),
// new Address($email),
// 'Erreur dans le tchat',
// $content
// );
// }
}
public function getNumberMessageUnread(Mission $mission,DiscussionGroup $groupedMessage = null,$user = null)
{
$user = !is_null($user) ? $user : $this->security->getUser();
$listMessage = $this->messageRepository->findBy(["campaign" => $mission->getCampaign(),"discussionGroup"=>null]);
if (!is_null($groupedMessage)) {
$listMessage = $this->messageRepository->findBy(["campaign" => $mission->getCampaign(),"discussionGroup"=>$groupedMessage]);
}
$numberMessageUnread = 0;
foreach ($listMessage as $message) {
if ( $user !== null && $message->getOrigin() !=2 && ($message->getUserListWhoRead() === null || !in_array($this->security->getUser()->getId(), $message->getUserListWhoRead()))
and !$message->isDeleted() and $this->haveMessage($message))
{
if ($message->getDiscussionGroup() !== null) {
foreach ($message->getDiscussionGroup()->getUsers() as $userInGroup) {
if ($this->security->getUser()->getId() == $userInGroup->getId() and !$message->getDiscussionGroup()->isDeleted()) {
$numberMessageUnread++;
}
}
}else{
$numberMessageUnread++;
}
}
}
return $numberMessageUnread;
}
public function haveMessage($message){
return $message->getContent() == null && count($message->getFileMessages()) > 0 || $message->getContent() != null ;
}
public function getNumberAllMessageUnread(Mission $mission,DiscussionGroup $groupedMessage = null)
{
$listMessage = $this->messageRepository->findBy(["campaign" => $mission->getCampaign()]);
$numberMessageUnread = 0;
foreach ($listMessage as $message) {
if ($this->security->getUser() !== null && $message->getOrigin() !=2 && ($message->getUserListWhoRead() === null || !in_array($this->security->getUser()->getId(), $message->getUserListWhoRead()))) {
if (!$message->isDeleted()) {
if ($message->getDiscussionGroup() !== null) {
foreach ($message->getDiscussionGroup()->getUsers() as $userInGroup) {
if ($this->security->getUser()->getId() == $userInGroup->getId() and !$message->getDiscussionGroup()->isDeleted()) {
if ($message->getContent() == null) {
foreach ($message->getFileMessages() as $file) {
if ($file->getSharedWithUsers()->isEmpty() or ( in_array("ROLE_ADMIN", $this->security->getUser()->getRoles()) or in_array("ROLE_CLIENT", $this->security->getUser()->getRoles()))) {
$numberMessageUnread++;
}else{
foreach ($file->getSharedWithUsers() as $user) {
if ($user->getId() == $this->security->getUser()->getId()) {
$numberMessageUnread++;
}
}
}
}
}else{
$numberMessageUnread++;
}
}
}
}else{
if ($message->getContent() == null) {
foreach ($message->getFileMessages() as $file) {
if ($file->getSharedWithUsers()->isEmpty() or ( in_array("ROLE_ADMIN", $this->security->getUser()->getRoles()) or in_array("ROLE_CLIENT", $this->security->getUser()->getRoles()))) {
$numberMessageUnread++;
}else{
foreach ($file->getSharedWithUsers() as $user) {
if ($user->getId() == $this->security->getUser()->getId()) {
$numberMessageUnread++;
}
}
}
}
}else{
$numberMessageUnread++;
}
}
}
}
}
return $numberMessageUnread;
}
public function marqueAllInMissionMessageRead(Mission $mission, User $user = null)
{
$user = $user !== null ? $user : $this->security->getUser();
$messages = $this->messageRepository->findBy(["campaign" => $mission->getCampaign()]);
$message = null;
// $messages = $mission->getMessages();
if($user != null ){
foreach ($messages as $message) {
try {
$this->marqueMessageRead(message : $message,user: $user);
} catch (\Throwable $th) {
// mail("erreurmessageanthonmyfolo@yopmail.com", "erreur", "errdsf");
}
}
}
}
public function marqueAllInMissionMessageReadWithGroup(Mission $mission, User $user = null,$groupedMessage = null)
{
$user = $user !== null ? $user : $this->security->getUser();
$messages = $this->messageRepository->getMessageWithGroup($mission->getCampaign(),$groupedMessage);
$message = null;
// $messages = $mission->getMessages();
if($user != null ){
foreach ($messages as $message) {
try {
$this->marqueMessageRead(message : $message,user: $user);
} catch (\Throwable $th) {
// mail("erreurmessageanthonmyfolo@yopmail.com", "erreur", "errdsf");
}
}
}
}
public function marqueMessageRead(Message $message, User $user)
{
if ($message->getUserListWhoRead() == null || sizeof($message->getUserListWhoRead()) == 0) {
$message->setUserListWhoRead([$user->getId()]);
} elseif (!in_array($user->getId(), $message->getUserListWhoRead())) {
$message->setUserListWhoRead([...$message->getUserListWhoRead(), $user->getId()]);
}
$this->entityManagerInterface->flush();
}
public function getUsersWhoReadMessage(Message $message)
{
return $this->userRepository->findUsersWhoAllreadyRead($message);
}
public function allFileMessageIsDeleted(Message $message){
$numberFileMessageDeleted = 0;
$numberMessageDeleted = 0;
foreach ($message->getFileMessages() as $key => $filemessage) {
if($filemessage->getIsDeleted()){
$numberFileMessageDeleted++;
}
$numberMessageDeleted ++ ;
}
return $numberFileMessageDeleted == $numberMessageDeleted ? true : false ;
}
public function getReactionsHtml(Message $message){
$reactions = $message->getMessageReactions();
$reactionsReduced=[];
$users = [];
foreach ($reactions as $reaction) {
$isMyReaction = $reaction->getUser()->getId() == $this->security->getUser()->getId() ? true : false;
if(array_key_exists($reaction->getEmojiId(), $reactionsReduced)){
$numberReaction = $reactionsReduced[$reaction->getEmojiId()]['number'] ;
$users = $reactionsReduced[$reaction->getEmojiId()]['users'] ;
$reactionsReduced[$reaction->getEmojiId()]['number'] = $numberReaction +1 ;
$reactionsReduced[$reaction->getEmojiId()]['users'] = [...$users,$reaction->getUser()];
if( $reactionsReduced[$reaction->getEmojiId()]['my_reaction'] == false){
$reactionsReduced[$reaction->getEmojiId()]['my_reaction'] = $isMyReaction;
}
}
else{
$reactionsReduced = [
...$reactionsReduced,
$reaction->getEmojiId()=>[
'id'=> $reaction->getEmojiId(),
'message_id'=>$message->getId(),
'emoji'=> $reaction->getEmoji(),
'users'=>[$reaction->getUser()],
'number'=>1,
'my_reaction'=>$isMyReaction,
]
];
}
}
return $this->twig->render('services/messages/reaction.html.twig',[
'reactionsReduced'=> $reactionsReduced
]
) ;
}
public function getFileMissionUrlByCampaign(Campaign $campaign, $fileName){
$filesystem = new Filesystem();
foreach ($campaign->getMissions() as $mission) {
$destination = "{$this->parameterBagInterface->get('file_message_directory')}/{$mission->getId()}/message/$fileName";
if ($filesystem->exists($destination)) {
return "uploads/mission/{$mission->getId()}/message/$fileName";
}
}
return "";
}
public function getFileMissionUrl(Mission $missionCampaign, string $fileName)
{
return $this->getFileMissionUrlByCampaign($missionCampaign->getCampaign(), $fileName);
}
/**
* @param User $user
* @param string $message=""
*
* @return string
*/
public function generateMessageByAction(User $user = null, string $action = "", mixed $option = null, ?Mission $mission=null): string
{
$user ??= "Système";
$mission = $mission !=null ? $mission->getProduct()->getName(): '';
$userOptionFullName ="";
$jobsOptionFullName = "";
try {
$userOptionFullName = $option != null && $option->getUser() != null ? $option->getUser()->getFullName() : '';
$jobsOptionFullName = $option != null && $option->getJob() != null ? $option->getJob()->getName() : '-';
} catch (\Throwable $th) {
//throw $th;
}
if (preg_match('/^relancelaunch_/', $action)) {
$explodeValue = explode("_", $action);
return "Une relance a été envoyée à " . $explodeValue[1] . " " . $explodeValue[2];
}
switch ($action) {
case 'save_file';
return "Téléchargement de ce fichier $option.";
case 'accept_mission';
return " $user a accepté de prendre en charge la mission.";
case 'decline_mission';
return "$user a rejeté la mission $mission.";
case 'title_campagne_modified';
return 'Le titre de la mission a été modifié.';
case 'new_project':
return "Passation de la commande.";
case 'add_validator_list':
return "Utilisateur(s) qui valide la mission : {$this->stringParticipant(participants:$option)}.";
case 'archive':
return "Mission archivée par $user.";
case 'add_subcontractor':
return "$user a ajouté {$userOptionFullName} en tant que {$jobsOptionFullName} sur la mission.";
case 'quantity_update':
return "$user a modifié la quantité.";
case 'deadline_change':
return "Mise à jour de l’échéance par $user.";
case 'modif_subcontractor':
return "Modification de l’intervenant opérationnel pour le métier $option.";
case 'initial_time':
return "$user a apporté(e) une modification à la demande initiale.";
case 'mission_evaluate':
return "$user a fait évoluer le budget.";
case 'mission_evaluate_time':
return "Mise à jour du temps passé par $user.";
case 'add_client':
return "$user a ajouté {$userOptionFullName} en tant que contact client";
case 'edit_role_participant':
return "Le role de : {$userOptionFullName} a changé en {$option->getRole()->label()}.";
case 'delete_participant':
return "Suppression d’un contact sur la mission : {$userOptionFullName} par $user.";
case 'mission_reactived':
return "La mission {$option->getProduct()->getName()} a été réactivé.";
case 'mission_deleted':
return "La mission {$option->getProduct()->getName()} a été supprimé.";
case 'sub_activate_mission':
return "Mission démarrée par $user.";
case 'activation':
return 'Mission activée.';
case 'activation_campaign':
return 'Mission activée.';
case 'pause':
return "Mission en pause par $user.";
case 'unpause':
return "Mission est redémarrée par $user.";
case 'cancel':
return "Mission annulée par $user.";
case 'refus':
return "$user a refusé la mission.";
case 'closed':
return "Mission clôturée par $user.";
case 'evaluation_request':
return "Envoi de demande d'évaluation.";
case 'resoumission':
return "resoumission du panier avec la dernière action de $user.";
case 'resend':
return "Mission resoumise par $user.";
case 'validate':
return "Mission validée.";
case 'accepter':
return "Panier validé.";
case 'start':
return "Mission démarrée.";
case 'automatic_close':
return "Mission cloturée automatiquement.";
case 'credit_campagne_modified':
return "$user a modifié le mode de facturation de la mission.";
case 'campaign_initiate_by_admin':
return "Commande par l'administrateur.";
case 'campaign_initiate_by_tunnel_client':
return "Mission déposée à partir du tunnel de commande.";
case 'devis_public':
return "Mission déposée à partir du devis public.";
case 'campaign_initiate_by_tunnel_devis':
return "Mission déposée à partir du tunnel de demande de devis.";
case 'campaign_initiate_by_recurrent':
return "Mission récurrente.";
case 'campaign_initiate_by_mail':
return "Mission déposée par email";
case 'livrable_send_have_file':
return "$user a partagé le(s) livrable(s) suivant(s) : $option";
case 'livrable_send_not_file':
return "$user n'a aucun livrable";
default:
return "Une action est faite par $user.";
}
}
public function stringParticipant(array|string|null $participants=''): string
{
if(!is_string($participants) && !is_null($participants)){
$separate = ",";
$validators = "";
foreach ($participants as $key => $participant) {
if ($key + 1 == sizeof($participants)) {
$separate = "";
}
$validators .= "{$participant->getUser()->getFullName()} $separate";
}
return $validators;
}
return '-';
}
public function sendEmail(Address $from, Address $to, string $subject, string $content, $attachments = null)
{
$notification = (new NotificationEmail())
->from($from)
->to($to)
->subject($subject)
->content($content)
->markAsPublic();
if (null !== $attachments) {
foreach ($attachments as $file) {
$notification->attachFromPath($this->parameterBagInterface->get('kernel.project_dir') . '/public/uploads/mission/' . $file->getMission()->getId() . '/' . $file->getName());
}
}
try {
$this->mailer->send($notification);
} catch (\Exception $e) {
}
}
public function getBullUsersWhoReadMessage(Message $message, ?Message $nextMessage = null){
$users=[];
if($nextMessage!=null){
$userIds = array_diff($message->getUserListWhoRead(), $nextMessage->getUserListWhoRead());
//delete my Id of sender
// $userIds = array_diff($userIds, [$message->getUser()->getId()]);
if(sizeof(value: $userIds)>0){
$users = $this->userRepository->findByIds($userIds);
}
}else{
$userIds = $message->getUserListWhoRead();
// $userIds = array_diff($userIds, [$message->getUser()->getId()]);
$users = $this->userRepository->findByIds($userIds);
}
return $users;
}
public function postUserTypingMessage(string $campaignId){
$campaign= $this->campaignRepository->findOneBy(['id'=>$campaignId]);
if($campaign){
$entityManager = $this->entityManager;
$aboutThisUser = [];
$dateUserTyping = new \DateTime();
$user = $this->security->getUser();
$userId = $user->getId();
$userName = $user->getFirstname();
$userProfilePicture = $user->getPictureName();
$campaignIsUserWriting = $campaign->getUserWriting();
$aboutThisUser = [
[
'id'=> $userId,
'name' => $userName,
'picture_name'=> $userProfilePicture,
'date_typing' => $dateUserTyping,
]
];
if(is_null($campaignIsUserWriting)){
$campaignIsUserWriting = $aboutThisUser;
}else{
$isUserAlreadyTyping = $this->checkIfUserIsAlreadyTyping($userId,$campaignIsUserWriting);
if($isUserAlreadyTyping === false ){
$campaignIsUserWriting = array_merge($campaignIsUserWriting,$aboutThisUser);
}else{
$campaignIsUserWriting[$isUserAlreadyTyping]['date_typing'] = $dateUserTyping;
}
}
$campaign->setUserWriting($campaignIsUserWriting);
$entityManager->persist($campaign);
$entityManager->flush();
}
}
public function getUserTypingWithoutCurrentUser(Mission $mission) {
$campaign= $mission->getCampaign();
$allUserTyping = $campaign->getUserWriting();
if(!is_null($allUserTyping)){
$allUserTyping = array_filter($allUserTyping, function ($idUser) {
return !in_array($idUser['id'], [$this->security->getUser()->getId()]);
});
}
return $allUserTyping;
}
private function checkIfUserIsAlreadyTyping($userId,$listOfUser){
return array_search($userId, array_column($listOfUser, 'id'));
}
public function calculaTimeTypingOfEachUser($listOfUserTyping) {
if(!is_null($listOfUserTyping)){
$listOfUserTyping = array_map(function ($user) {
$currentDate = new \DateTime();
$maxNumberSeconds = 5;
$timeTyping = $currentDate->diff($user['date_typing']);
$date1 = $user['date_typing'];
$timeSecondDiff = $currentDate->format('U') - $date1->format('U');
if($timeSecondDiff<5){
$user['date_typing'] = 'is_typing';
}else{
$user['date_typing'] = "";
}
return $user;
}, $listOfUserTyping);
}
return $listOfUserTyping ?? [];
}
}