src/Utils/HootsuiteHelper.php line 485

Open in your IDE?
  1. <?php
  2. namespace App\Utils;
  3. use Symfony\Component\HttpFoundation\RequestStack;
  4. use Doctrine\ORM\EntityManagerInterface;
  5. use App\Entity\HootsuiteToken;
  6. use App\Entity\HootsuiteSocialProfile;
  7. use DateTimeImmutable;
  8. /**
  9.  * Class HootsuiteHelper
  10.  * 
  11.  * This class provides helper functions for interacting with Hootsuite.
  12.  */
  13. class HootsuiteHelper
  14. {
  15.   protected $requestStack;
  16.   protected $entityManager;
  17.   public function __construct(RequestStack $requestStackEntityManagerInterface $entityManager)
  18.   {
  19.     $this->requestStack $requestStack;
  20.     $this->entityManager $entityManager;
  21.   }
  22.   protected $baseUrl "https://platform.hootsuite.com/";
  23.   protected $clientID "a760a0ab-04d5-4ff3-b86b-1412f0def467";
  24.   protected $clientSecret "hws7jUvYPweg";
  25.   protected $authHeader "YTc2MGEwYWItMDRkNS00ZmYzLWI4NmItMTQxMmYwZGVmNDY3Omh3czdqVXZZUHdlZw==";
  26.   protected $redirectUri "https://staging.rooferscoffeeshop.com/admin/apps/hootsuite";
  27.   public function getAccessToken($code null)
  28.   {
  29.     if ($code == null) {
  30.       $accessToken $this->getAccessTokenFromDB();
  31.       if ($accessToken == false) {
  32.         $refreshToken $this->getRefreshTokenFromDB();
  33.         $refreshedTokens $this->refreshAccessToken($refreshToken);
  34.         $refreshedTokens json_decode($refreshedTokenstrue);
  35.         $accessToken $refreshedTokens['access_token'];
  36.         return $accessToken;
  37.       } else {
  38.         return $accessToken;
  39.       }
  40.     } else {
  41.       return $this->getAccessTokenFromCode($code);
  42.     }
  43.   }
  44.   /**
  45.    * Retrieves the access token from the database.
  46.    *
  47.    * @return string|false The access token if it is still valid, false otherwise.
  48.    */
  49.   public function getAccessTokenFromDB()
  50.   {
  51.     $tokens $this->entityManager->getRepository(HootsuiteToken::class)->find(1);
  52.     $result $tokens->getAccessToken();
  53.     $expires_in $tokens->getExpiresIn();
  54.     $created_at $tokens->getCreatedAt();
  55.     $now = new \DateTime();
  56.     $now $now->getTimestamp();
  57.     $created_at $created_at->getTimestamp();
  58.     if ($now $created_at $expires_in) {
  59.       return false;
  60.     }
  61.     return $result;
  62.   }
  63.   /**
  64.    * Retrieves the refresh token from the database.
  65.    *
  66.    * @return string The refresh token.
  67.    */
  68.   public function getRefreshTokenFromDB()
  69.   {
  70.     $tokens $this->entityManager->getRepository(HootsuiteToken::class)->find(1);
  71.     return $tokens->getRefreshToken();
  72.   }
  73.   /**
  74.    * Retrieves an access token from the provided authorization code.
  75.    *
  76.    * @param string $code The authorization code.
  77.    * @return string The response from the API containing the access token.
  78.    * @throws \Exception If there is an error getting the access token.
  79.    */
  80.   public function getAccessTokenFromCode($code)
  81.   {
  82.     $uri $this->baseUrl "oauth2/token";
  83.     $headers = array(
  84.       'Accept: application/json;charset=utf-8',
  85.       'Content-Type: application/x-www-form-urlencoded',
  86.       "Authorization: Basic $this->authHeader"
  87.     );
  88.     $body "grant_type=authorization_code&code=$code&redirect_uri=$this->redirectUri";
  89.     $curl curl_init();
  90.     curl_setopt_array(
  91.       $curl,
  92.       array(
  93.         CURLOPT_URL => $uri,
  94.         CURLOPT_RETURNTRANSFER => true,
  95.         CURLOPT_ENCODING => "",
  96.         CURLOPT_MAXREDIRS => 10,
  97.         CURLOPT_TIMEOUT => 0,
  98.         CURLOPT_FOLLOWLOCATION => true,
  99.         CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  100.         CURLOPT_CUSTOMREQUEST => "POST",
  101.         CURLOPT_POSTFIELDS => $body,
  102.         CURLOPT_HTTPHEADER => $headers
  103.       )
  104.     );
  105.     $response curl_exec($curl);
  106.     curl_close($curl);
  107.     if (isset($response['error'])) {
  108.       throw new \Exception("Error getting access token: " $response['error']);
  109.     }
  110.     $this->saveTokensToDB(json_decode($responsetrue));
  111.     return $response;
  112.   }
  113.   /**
  114.    * Refreshes the access token using the provided refresh token.
  115.    *
  116.    * @param string $refreshToken The refresh token.
  117.    * @return string The response from the API.
  118.    * @throws \Exception If there is an error refreshing the access token.
  119.    */
  120.   public function refreshAccessToken($refreshToken)
  121.   {
  122.     $uri $this->baseUrl "oauth2/token";
  123.     $headers = array(
  124.       'Accept: application/json;charset=utf-8',
  125.       'Content-Type: application/x-www-form-urlencoded',
  126.       "Authorization: Basic $this->authHeader"
  127.     );
  128.     $body "grant_type=refresh_token&refresh_token=$refreshToken";
  129.     $curl curl_init();
  130.     curl_setopt_array(
  131.       $curl,
  132.       array(
  133.         CURLOPT_URL => $uri,
  134.         CURLOPT_RETURNTRANSFER => true,
  135.         CURLOPT_ENCODING => "",
  136.         CURLOPT_MAXREDIRS => 10,
  137.         CURLOPT_TIMEOUT => 0,
  138.         CURLOPT_FOLLOWLOCATION => true,
  139.         CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  140.         CURLOPT_CUSTOMREQUEST => "POST",
  141.         CURLOPT_POSTFIELDS => $body,
  142.         CURLOPT_HTTPHEADER => $headers
  143.       )
  144.     );
  145.     $response curl_exec($curl);
  146.     curl_close($curl);
  147.     if (isset($response['error'])) {
  148.       throw new \Exception("Error refreshing access token: " $response['error']);
  149.     }
  150.     $this->saveTokensToDB(json_decode($responsetrue));
  151.     return $response;
  152.   }
  153.   /**
  154.    * Saves the tokens to the database.
  155.    *
  156.    * @param array $tokens The tokens to be saved.
  157.    * @throws \Exception If any of the required tokens are not found in the response.
  158.    */
  159.   private function saveTokensToDB($tokens)
  160.   {
  161.     if (!isset($tokens['access_token'])) {
  162.       throw new \Exception("Access token not found in response");
  163.     }
  164.     if (!isset($tokens['expires_in'])) {
  165.       throw new \Exception("Expires in not found in response");
  166.     }
  167.     if (!isset($tokens['refresh_token'])) {
  168.       throw new \Exception("Refresh token not found in response");
  169.     }
  170.     // update THE ACCESS TOKEN
  171.     $accessToken $tokens['access_token'];
  172.     $expiresIn $tokens['expires_in'];
  173.     $refreshToken $tokens['refresh_token'];
  174.     $eTokens $this->entityManager->getRepository(HootsuiteToken::class)->find(1);
  175.     $eTokens->setAccessToken($accessToken);
  176.     $eTokens->setExpiresIn($expiresIn);
  177.     $eTokens->setRefreshToken($refreshToken);
  178.     $createdAt = new DateTimeImmutable();
  179.     $eTokens->setCreatedAt($createdAt);
  180.     $this->entityManager->persist($eTokens);
  181.     $this->entityManager->flush();
  182.   }
  183.   /**
  184.    * Retrieves the social profiles from Hootsuite.
  185.    *
  186.    * @return string The response containing the social profiles.
  187.    * @throws \Exception If there is an error getting the social profiles.
  188.    */
  189.   public function getSocialProfiles()
  190.   {
  191.     $uri $this->baseUrl "v1/socialProfiles";
  192.     $accessToken $this->getAccessToken();
  193.     $headers = array(
  194.       "Accept: application/json;charset=utf-8",
  195.       "Content-Type: application/json;charset=utf-8",
  196.       "Authorization: Bearer $accessToken"
  197.     );
  198.     $curl curl_init();
  199.     curl_setopt_array(
  200.       $curl,
  201.       array(
  202.         CURLOPT_URL => $uri,
  203.         CURLOPT_RETURNTRANSFER => true,
  204.         CURLOPT_ENCODING => "",
  205.         CURLOPT_MAXREDIRS => 10,
  206.         CURLOPT_TIMEOUT => 0,
  207.         CURLOPT_FOLLOWLOCATION => true,
  208.         CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  209.         CURLOPT_CUSTOMREQUEST => "GET",
  210.         CURLOPT_HTTPHEADER => $headers
  211.       )
  212.     );
  213.     $response curl_exec($curl);
  214.     curl_close($curl);
  215.     // throw error if response is not 200
  216.     if (isset($response['error'])) {
  217.       throw new \Exception("Error getting social profiles: " $response['error']);
  218.     }
  219.     // these should be refreshed reguarly but stored in the database
  220.     return $response;
  221.   }
  222.   public function updateSocialProfiles()
  223.   {
  224.     $socialProfiles $this->getSocialProfiles();
  225.     $socialProfiles json_decode($socialProfilestrue);
  226.     $socialProfiles $socialProfiles['data'];
  227.     $allIds = [];
  228.     foreach ($socialProfiles as $profile) {
  229.       $id $profile['id'];
  230.       $type $profile['type'] ?? "";
  231.       $socialNetworkId $profile['socialNetworkId'] ?? "";
  232.       $socialNetworkUsername $profile['socialNetworkUsername'] ?? "";
  233.       $avatarUrl $profile['avatarUrl'] ?? "";
  234.       $owner $profile['owner'] ?? "";
  235.       $ownerId $profile['ownerId'] ?? "";
  236.       $eProfile $this->entityManager->getRepository(HootsuiteSocialProfile::class)->find($id);
  237.       if ($eProfile == null) {
  238.         $eProfile = new HootsuiteSocialProfile();
  239.       }
  240.       $eProfile->setId($id);
  241.       $eProfile->setType($type);
  242.       $eProfile->setSocialNetworkId($socialNetworkId);
  243.       $eProfile->setSocialNetworkUsername($socialNetworkUsername);
  244.       $eProfile->setAvatarUrl($avatarUrl);
  245.       $eProfile->setOwner($owner);
  246.       $eProfile->setOwnerId($ownerId);
  247.       $eProfile->setStatus(1);
  248.       $this->entityManager->persist($eProfile);
  249.       $allIds[] = $id;
  250.     }
  251.     $results $this->entityManager
  252.       ->createQuery(
  253.         implode(
  254.           " ",
  255.           array(
  256.             "UPDATE",
  257.             "App\Entity\HootsuiteSocialProfile p",
  258.             "SET",
  259.             "p.status = 0",
  260.             "WHERE",
  261.             "p.id NOT IN (",
  262.             implode(", "$allIds),
  263.             ")",
  264.           )
  265.         )
  266.       )
  267.       ->getResult();
  268.     $this->entityManager->flush();
  269.   }
  270.   // actual meat and potatoes
  271.   /**
  272.    * Creates a media upload URI for Hootsuite.
  273.    *
  274.    * @param array $media.
  275.    * @return string The response from the API call.
  276.    */
  277.   public function createMediaUploadUri($media)
  278.   {
  279.     $uri $this->baseUrl "v1/media";
  280.     $accessToken $this->getAccessToken();
  281.     $imageSize $media['size'];
  282.     $imageMime $media['mime'];
  283.     $postBody json_encode(
  284.       array(
  285.         "sizeBytes" => $imageSize,
  286.         "mimeType" => $imageMime
  287.       )
  288.     );
  289.     $curl curl_init();
  290.     curl_setopt_array(
  291.       $curl,
  292.       array(
  293.         CURLOPT_URL => $uri,
  294.         CURLOPT_RETURNTRANSFER => true,
  295.         CURLOPT_ENCODING => '',
  296.         CURLOPT_MAXREDIRS => 10,
  297.         CURLOPT_TIMEOUT => 0,
  298.         CURLOPT_FOLLOWLOCATION => true,
  299.         CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  300.         CURLOPT_CUSTOMREQUEST => 'POST',
  301.         CURLOPT_POSTFIELDS => $postBody,
  302.         CURLOPT_HTTPHEADER => array(
  303.           "Accept: application/json;charset=utf-8",
  304.           "Content-Type: application/json;charset=utf-8",
  305.           "Authorization: Bearer $accessToken"
  306.         ),
  307.       )
  308.     );
  309.     $response curl_exec($curl);
  310.     curl_close($curl);
  311.     if (isset($response['error'])) {
  312.       throw new \Exception("Error creating media upload URI: " $response['error']);
  313.     }
  314.     // should return a respone like:
  315.     /*{
  316.       "data": {
  317.         "id": "aHR0cHM6Ly9ob290c3VpdGUtdmlkZW8uczMuYW1hem9uYXdzLmNvbS9wcm9kdWN0aW9uLzEyMjU1MjQ0XzgyOTVmZjllLWFkOWYtNGNlNy1iOGE3LTgwNzI0NDAwYTBhZS5tcDQ=",
  318.         "uploadUrl": "https://hootsuite-video.s3.amazonaws.com/production/12255244_01942650-3d42-42b8-a191-aa84eb45d105.mp4?AWSAccessKeyId=AKIAIM7ASX2JTE3ZFAAA&Expires=1471978770&Signature=b%2B196oEHxySdmE%2FC34ZRL6pXSAI%3D",
  319.         "uploadUrlDurationSeconds": 1799
  320.       }
  321.     }*/
  322.     return $response;
  323.   }
  324.   /**
  325.    * Uploads media to a specified URL using cURL.
  326.    *
  327.    * @param string $uploadUrl The URL to upload the media to.
  328.    * @param array $media The path of the media file to be uploaded.
  329.    * @return string The response from the cURL request.
  330.    */
  331.   public function uploadMedia($uploadUrl$media)
  332.   {
  333.     $accessToken $this->getAccessToken();
  334.     $imageMime $media['mime'];
  335.     $imageSize $media['size'];
  336.     $headers = array(
  337.       "Content-Type: $imageMime",
  338.       "Content-Length: $imageSize",
  339.     );
  340.     $curl curl_init();
  341.     curl_setopt_array(
  342.       $curl,
  343.       array(
  344.         CURLOPT_URL => $uploadUrl,
  345.         CURLOPT_RETURNTRANSFER => true,
  346.         CURLOPT_ENCODING => "",
  347.         CURLOPT_MAXREDIRS => 10,
  348.         CURLOPT_TIMEOUT => 0,
  349.         CURLOPT_FOLLOWLOCATION => true,
  350.         CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  351.         CURLOPT_CUSTOMREQUEST => "PUT",
  352.         CURLOPT_POSTFIELDS => file_get_contents($media['image']),
  353.         CURLOPT_HTTPHEADER => $headers
  354.       )
  355.     );
  356.     $response curl_exec($curl);
  357.     curl_close($curl);
  358.     if (isset($response['error'])) {
  359.       throw new \Exception("Error uploading media: " $response['error']);
  360.     }
  361.     return $response;
  362.   }
  363.   /**
  364.    * Confirm the media upload by checking the status of the uploaded media.
  365.    *
  366.    * @param string|null $mediaID The ID of the media to be confirmed.
  367.    * @return string|bool The response from the API or false if the mediaID is null.
  368.    */
  369.   public function confirmMediaUpload($mediaID null)
  370.   {
  371.     if ($mediaID == null) {
  372.       return false;
  373.     }
  374.     $accessToken $this->getAccessToken();
  375.     $uri $this->baseUrl "v1/media/$mediaID";
  376.     $curl curl_init();
  377.     curl_setopt_array(
  378.       $curl,
  379.       array(
  380.         CURLOPT_URL => $uri,
  381.         CURLOPT_RETURNTRANSFER => true,
  382.         CURLOPT_ENCODING => "",
  383.         CURLOPT_MAXREDIRS => 10,
  384.         CURLOPT_TIMEOUT => 0,
  385.         CURLOPT_FOLLOWLOCATION => true,
  386.         CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  387.         CURLOPT_CUSTOMREQUEST => "GET",
  388.         CURLOPT_HTTPHEADER => array(
  389.           "Accept: application/json;charset=utf-8",
  390.           "Content-Type: application/json;charset=utf-8",
  391.           "Authorization: Bearer $accessToken"
  392.         ),
  393.       )
  394.     );
  395.     $response curl_exec($curl);
  396.     curl_close($curl);
  397.     return $response;
  398.   }
  399.   /**
  400.    * Schedule a post on Hootsuite.
  401.    *
  402.    * @param string $text The text content of the post.
  403.    * @param array $mediaIDs An array of media IDs to be attached to the post (optional).
  404.    * @param array $socialProfileIDs An array of social profile IDs to which the post will be sent (optional).
  405.    * @param string $scheduledTime The scheduled time for the post in ISO 8601 format.
  406.    * @return string The response from the Hootsuite API.
  407.    */
  408.   public function schedulePost($text$mediaIDs = [], $socialProfileIDs = [], $scheduledTime)
  409.   {
  410.     if(!$scheduledTime || !$text || count($socialProfileIDs) == 0) {
  411.       return false;
  412.     }
  413.     $scheduledDateTimeObj \DateTime::createFromFormat('Y-m-d\TH:i:s\Z'$scheduledTime, new \DateTimeZone('UTC'));
  414.     $dateTimeNow = new \DateTime();
  415.         $dateTimeNow->setTimezone(new \DateTimeZone('UTC'));
  416.     if($scheduledDateTimeObj $dateTimeNow) {
  417.       return false;
  418.     }
  419.     $uri $this->baseUrl "v1/messages";
  420.     $accessToken $this->getAccessToken();
  421.     $rawPostBody = array(
  422.       "text" => $text,
  423.       "scheduledSendTime" => $scheduledTime,
  424.       "socialProfileIds" => $socialProfileIDs
  425.     );
  426.     if (count($mediaIDs) > 0) {
  427.       // each mediaid should be an object {id: "mediaID"}
  428.       $mediaIDs array_map(function ($mediaID) {
  429.         return array ("id" => $mediaID);
  430.       }, $mediaIDs);
  431.       $rawPostBody["media"] = $mediaIDs;
  432.     }
  433.     $postBody json_encode($rawPostBody);
  434.     $curl curl_init();
  435.     curl_setopt_array(
  436.       $curl,
  437.       array(
  438.         CURLOPT_URL => $uri,
  439.         CURLOPT_RETURNTRANSFER => true,
  440.         CURLOPT_ENCODING => "",
  441.         CURLOPT_MAXREDIRS => 10,
  442.         CURLOPT_TIMEOUT => 0,
  443.         CURLOPT_FOLLOWLOCATION => true,
  444.         CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  445.         CURLOPT_CUSTOMREQUEST => "POST",
  446.         CURLOPT_POSTFIELDS => $postBody,
  447.         CURLOPT_HTTPHEADER => array(
  448.           "Accept: application/json;charset=utf-8",
  449.           "Content-Type: application/json;charset=utf-8",
  450.           "Authorization: Bearer $accessToken"
  451.         ),
  452.       )
  453.     );
  454.     $response curl_exec($curl);
  455.     curl_close($curl);
  456.     return $response;
  457.   }
  458.   /**
  459.    * Retrieves a message from Hootsuite based on the provided message ID.
  460.    *
  461.    * @param int $messageID The ID of the message to retrieve.
  462.    * @return string The response from the Hootsuite API.
  463.    */
  464.   public function getMessage($messageID)
  465.   {
  466.     $uri $this->baseUrl "v1/messages/$messageID";
  467.     $accessToken $this->getAccessToken();
  468.     $curl curl_init();
  469.     curl_setopt_array(
  470.       $curl,
  471.       array(
  472.         CURLOPT_URL => $uri,
  473.         CURLOPT_RETURNTRANSFER => true,
  474.         CURLOPT_ENCODING => "",
  475.         CURLOPT_MAXREDIRS => 10,
  476.         CURLOPT_TIMEOUT => 0,
  477.         CURLOPT_FOLLOWLOCATION => true,
  478.         CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  479.         CURLOPT_CUSTOMREQUEST => "GET",
  480.         CURLOPT_HTTPHEADER => array(
  481.           "Accept: application/json;charset=utf-8",
  482.           "Content-Type: application/json;charset=utf-8",
  483.           "Authorization: Bearer $accessToken"
  484.         ),
  485.       )
  486.     );
  487.     $response curl_exec($curl);
  488.     curl_close($curl);
  489.     return $response;
  490.   }
  491.   /**
  492.    * Deletes a message from Hootsuite based on the provided message ID.
  493.    *
  494.    * @param int $messageID The ID of the message to delete.
  495.    * @return string The response from the Hootsuite API.
  496.    */
  497.   public function deleteMessage($messageID)
  498.   {
  499.     $uri $this->baseUrl "v1/messages/$messageID";
  500.     $accessToken $this->getAccessToken();
  501.     $curl curl_init();
  502.     curl_setopt_array(
  503.       $curl,
  504.       array(
  505.         CURLOPT_URL => $uri,
  506.         CURLOPT_RETURNTRANSFER => true,
  507.         CURLOPT_ENCODING => "",
  508.         CURLOPT_MAXREDIRS => 10,
  509.         CURLOPT_TIMEOUT => 0,
  510.         CURLOPT_FOLLOWLOCATION => true,
  511.         CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  512.         CURLOPT_CUSTOMREQUEST => "DELETE",
  513.         CURLOPT_HTTPHEADER => array(
  514.           "Accept: application/json;charset=utf-8",
  515.           "Content-Type: application/json;charset=utf-8",
  516.           "Authorization: Bearer $accessToken"
  517.         ),
  518.       )
  519.     );
  520.     $response curl_exec($curl);
  521.     curl_close($curl);
  522.     return $response;
  523.   }
  524. }