466 lines
19 KiB
PHP
466 lines
19 KiB
PHP
<?php
|
|
|
|
namespace App\Classes;
|
|
|
|
use App\Models\Setting;
|
|
use Carbon\Carbon;
|
|
use DateTime;
|
|
use Exception;
|
|
use GuzzleHttp\Client;
|
|
use Illuminate\Support\Facades\Cache;
|
|
use Illuminate\Support\Facades\Log;
|
|
use GuzzleHttp\HandlerStack;
|
|
use Kevinrob\GuzzleCache\CacheMiddleware;
|
|
use Kevinrob\GuzzleCache\Strategy\GreedyCacheStrategy;
|
|
|
|
class Calendly
|
|
{
|
|
|
|
protected $clientId = "1SW4eLp_g-nFim1XOORcAh-2lH2H-dfUVCiB1tLpkvo";
|
|
protected $clientSecret = "qXtkthnPxdCoHzVekWsgKr1QEH2q5nm5B3R81x6IG28";
|
|
//protected $redirectUri = 'https://hgh.codelfi.com/calendly/redirect-code/';
|
|
public function authUrl()
|
|
{
|
|
$url = 'https://auth.calendly.com/oauth/authorize';
|
|
$url .= '?client_id=' . urlencode($this->clientId);
|
|
$url .= '&response_type=code';
|
|
$url .= '&redirect_uri=' . urlencode(route('redirectURI'));
|
|
//$url .= '&redirect_uri=' . urlencode('https://app.example.com/calendly/redirect-code/');
|
|
|
|
return $url;
|
|
}
|
|
public function authorize($code)
|
|
{
|
|
$tokenUrl = 'https://auth.calendly.com/oauth/token';
|
|
|
|
$client = new Client();
|
|
|
|
try {
|
|
$response = $client->post($tokenUrl, [
|
|
'form_params' => [
|
|
'grant_type' => 'authorization_code',
|
|
'client_id' => $this->clientId,
|
|
'client_secret' => $this->clientSecret,
|
|
'redirect_uri' => route("redirectURI"),
|
|
//'redirect_uri' => 'https://app.example.com/calendly/redirect-code/',
|
|
'code' => $code,
|
|
],
|
|
'headers' => [
|
|
'Content-Type' => 'application/x-www-form-urlencoded',
|
|
],
|
|
]);
|
|
|
|
$data = json_decode($response->getBody()->getContents(), true);
|
|
Log::info('Info from function authorize(): ', $data);
|
|
$setting = Setting::find(1);
|
|
$setting->calendly_access_token = $data['access_token'];
|
|
$setting->calendly_refresh_token = $data['refresh_token'];
|
|
$setting->save();
|
|
Cache::forget('calendly_access_token');
|
|
Cache::put('calendly_access_token', $data['access_token'], now()->addSeconds($data['expires_in']));
|
|
|
|
return [
|
|
'access_token' => $data['access_token'],
|
|
'refresh_token' => $data['refresh_token'],
|
|
'token_type' => $data['token_type'],
|
|
'expires_in' => $data['expires_in']
|
|
];
|
|
} catch (\Exception $e) {
|
|
throw $e;
|
|
}
|
|
}
|
|
public function accessToken()
|
|
{
|
|
|
|
if (Cache::has('calendly_access_token')) {
|
|
return Cache::get('calendly_access_token');
|
|
}
|
|
|
|
$setting = Setting::find(1);
|
|
|
|
$tokenUrl = 'https://auth.calendly.com/oauth/token';
|
|
|
|
$client = new Client();
|
|
|
|
$response = $client->post($tokenUrl, [
|
|
'form_params' => [
|
|
'grant_type' => 'refresh_token',
|
|
'client_id' => $this->clientId,
|
|
'client_secret' => $this->clientSecret,
|
|
'refresh_token' => $setting->calendly_refresh_token,
|
|
],
|
|
'headers' => [
|
|
'Content-Type' => 'application/x-www-form-urlencoded',
|
|
],
|
|
]);
|
|
|
|
// Decode the response
|
|
$data = json_decode($response->getBody()->getContents(), true);
|
|
Log::info('Info from function accessToken(): ', $data);
|
|
// Store the new access token and refresh token in cache
|
|
Cache::put('calendly_access_token', $data['access_token'], now()->addSeconds($data['expires_in']));
|
|
|
|
return $data['access_token'];
|
|
}
|
|
public function getUserUri()
|
|
{
|
|
// 1. Call the /users/me API to get user information
|
|
$client = new Client();
|
|
try {
|
|
$response = $client->request('GET', 'https://api.calendly.com/users/me', [
|
|
'headers' => [
|
|
'Authorization' => 'Bearer ' . $this->accessToken(),
|
|
'Content-Type' => 'application/json',
|
|
]
|
|
]);
|
|
|
|
$data = json_decode($response->getBody(), true);
|
|
return $userUri = $data['resource']['uri'];
|
|
} catch (\Exception $e) {
|
|
return false;
|
|
}
|
|
}
|
|
public function getUserSlug()
|
|
{
|
|
// 1. Call the /users/me API to get user information
|
|
$client = new Client();
|
|
try {
|
|
$response = $client->request('GET', 'https://api.calendly.com/users/me', [
|
|
'headers' => [
|
|
'Authorization' => 'Bearer ' . $this->accessToken(),
|
|
'Content-Type' => 'application/json',
|
|
]
|
|
]);
|
|
|
|
$data = json_decode($response->getBody(), true);
|
|
return $userUri = $data['resource']['slug'];
|
|
} catch (\Exception $e) {
|
|
return false;
|
|
}
|
|
}
|
|
public function eventTypes()
|
|
{
|
|
// 1. Call the /users/me API to get user information
|
|
$client = new Client();
|
|
$responseEvent = $client->request('GET', 'https://api.calendly.com/event_types?user=' . $this->getUserUri(), [
|
|
'headers' => [
|
|
'Authorization' => 'Bearer ' . $this->accessToken(),
|
|
'Content-Type' => 'application/json',
|
|
]
|
|
]);
|
|
$dataEvent = json_decode($responseEvent->getBody(), true);
|
|
return $dataEvent['collection'];
|
|
}
|
|
public function setEventUri($even_type_url)
|
|
{
|
|
$setting = Setting::find(1);
|
|
$setting->event_type = $even_type_url;
|
|
$setting->save();
|
|
}
|
|
public function resetEventUri()
|
|
{
|
|
$setting = Setting::find(1);
|
|
$setting->event_type = null;
|
|
$setting->calendly_access_token = null;
|
|
$setting->calendly_refresh_token = null;
|
|
|
|
$setting->save();
|
|
}
|
|
function mapTimezone($tz){
|
|
$timezone_maping = [
|
|
"PST"=> "America/Los_Angeles",
|
|
"EST"=> "America/New_York",
|
|
"CST"=> "America/Chicago",
|
|
"MST"=> "America/Denver",
|
|
"PST"=> "America/Los_Angeles",
|
|
"AST"=> "America/Halifax",
|
|
];
|
|
return data_get($timezone_maping, $tz, $tz);
|
|
}
|
|
|
|
function getAvailableDates($even_type_url, $month, $tz = "UTC"){
|
|
$user_slug = $this->getUserSlug();
|
|
//https://calendly.com/api/booking/event_types/lookup?event_type_slug=15-minute&profile_slug=vini-peptidewebmd
|
|
//https://api.calendly.com/event_types/527074d7-c194-419a-9575-60de51220a8e/calendar/range?timezone=America/Chicago&diagnostics=false&range_start=2024-10-01&range_end=2024-10-31&scheduling_link_uuid=cmm8-rtf-7q6
|
|
//https://calendly.com/api/booking/event_types/527074d7-c194-419a-9575-60de51220a8e/calendar/range?timezone=Asia%2FKarachi&diagnostics=false&range_start=2024-10-26&range_end=2024-10-31&scheduling_link_uuid=cmm8-rtf-7q6
|
|
// $user_slug = explode('/', $userUri)[4];
|
|
$client = new Client();
|
|
|
|
|
|
$response = $client->request('GET', $even_type_url, [
|
|
'headers' => [
|
|
'Authorization' => 'Bearer ' . $this->accessToken(),
|
|
'Content-Type' => 'application/json',
|
|
],
|
|
// 'query' => $queryParams
|
|
]);
|
|
$event = json_decode($response->getBody(), true);
|
|
$web_event = json_decode($this->makeRequest("https://calendly.com/api/booking/event_types/lookup?event_type_slug={$event['resource']['slug']}&profile_slug={$user_slug}"),true);
|
|
// var_Dump("https://calendly.com/api/booking/event_types/lookup?event_type_slug={$event['resource']['slug']}&profile_slug={$user_slug}");
|
|
$event_slug = data_get($web_event,"uuid");
|
|
// $eventDetails = json_decode($this->makeRequest($eventDetailsUrl));
|
|
// $eventDetailsUrl = "https://calendly.com/api/booking/event_types/lookup?event_type_slug=$event_type&profile_slug=$owner_id";
|
|
$current_month = Carbon::now()->month;
|
|
$current_date = Carbon::now()->day;
|
|
$timezone = $this->mapTimezone($tz);
|
|
// if ($month == $current_month && $current_date > 15) {}
|
|
$date = Carbon::createFromDate(Carbon::now()->year, $month, 1);
|
|
// $start_date = Carbon::now()->startOfMonth()->tz("UTC");
|
|
$start_date = $date->startOfMonth()->format('Y-m-d');
|
|
$end_date = $date->endOfMonth()->format('Y-m-d');
|
|
$uid = data_get($web_event,"scheduling_link.uid");
|
|
$url = "https://calendly.com/api/booking/event_types/{$event_slug}/calendar/range?timezone=$timezone&diagnostics=false&range_start=$start_date&range_end=$end_date&scheduling_link_uuid={$uid}";
|
|
$response = $client->request('GET', $url, [
|
|
'headers' => [
|
|
'Authorization' => 'Bearer ' . $this->accessToken(),
|
|
'Content-Type' => 'application/json',
|
|
],
|
|
]);
|
|
|
|
$eventDetails = json_decode($this->makeRequest($url),true);
|
|
$availableSlots = [];
|
|
foreach ($eventDetails['days'] as $_event) {
|
|
if($_event['status'] == 'available') {
|
|
$slots = [
|
|
// 'date' => $event['date'],
|
|
|
|
];
|
|
foreach ($_event['spots'] as $slot) {
|
|
if($slot['status'] == 'available'){
|
|
$slotDateTime = Carbon::parse($slot['start_time'])->tz($timezone);
|
|
$slot['formatted_datetime'] = $slotDateTime->format('Y-m-d g:i:00');
|
|
$slot['scheduling_url'] = "https://calendly.com/$user_slug/{$event['resource']['slug']}/{$slot['start_time']}";
|
|
}
|
|
$slots[] = $slot;
|
|
}
|
|
$availableSlots[$_event['date']] = $slots;
|
|
}
|
|
}
|
|
|
|
// var_dump($eventDetails);
|
|
return $availableSlots;
|
|
|
|
}
|
|
function getAvailableDatesOld($even_type_url, $month, $tz = "UTC")
|
|
{
|
|
$cacheKey = $even_type_url . $month . $tz;
|
|
return Cache::remember($cacheKey, now()->addMinutes(2), function () use ($even_type_url, $month, $tz) {
|
|
return $this->getAvailableDates2($even_type_url, $month, $tz);
|
|
});
|
|
}
|
|
function getAvailableDates2($even_type_url, $month, $tz = "UTC")
|
|
{
|
|
|
|
try {
|
|
$availableTimes = [];
|
|
|
|
$date = Carbon::createFromDate(Carbon::now()->year, $month, 1, $tz);
|
|
$date = $date->startOfMonth()->tz("UTC");
|
|
$endMonthDate = Carbon::createFromDate(Carbon::now()->year, $month, 1, $tz);
|
|
$endMonthDate = $endMonthDate->endOfMonth()->tz("UTC");
|
|
|
|
|
|
while ($date < $endMonthDate) {
|
|
$start_time = $date->startOfDay()->format('Y-m-d\T24:00:00.000000\Z');
|
|
$end_time = $date->addDays(7)->endOfDay()->format('Y-m-d\T24:00:00.000000\Z');
|
|
|
|
$client = new Client();
|
|
try {
|
|
|
|
// Prepare API endpoint with the required parameters
|
|
$eventTypeUrl = 'https://api.calendly.com/event_type_available_times';
|
|
$queryParams = [
|
|
'event_type' => $even_type_url,
|
|
'start_time' => $start_time,
|
|
'end_time' => $end_time
|
|
];
|
|
// dd($queryParams);
|
|
$str = "event_type=" . urlencode($queryParams['event_type']) . "&" . "start_time=" . urlencode($queryParams['start_time']) . "&" . "end_time=" . urlencode($queryParams['end_time']);
|
|
$eventTypeUrl = $eventTypeUrl . "?" . ($str);
|
|
// Send the request to Calendly
|
|
$response = $client->request('GET', $eventTypeUrl, [
|
|
'headers' => [
|
|
'Authorization' => 'Bearer ' . $this->accessToken(),
|
|
'Content-Type' => 'application/json',
|
|
],
|
|
// 'query' => $queryParams
|
|
]);
|
|
|
|
|
|
$data = json_decode($response->getBody(), true);
|
|
//$availableTimes += $data['collection'];
|
|
foreach ($data['collection'] as $slot) {
|
|
$slotDate = Carbon::parse($slot['start_time'])->tz($tz)->format('Y-m-d');
|
|
if (!isset($availableTimes[$slotDate])) {
|
|
$availableTimes[$slotDate] = [];
|
|
}
|
|
$slotDateTime = Carbon::parse($slot['start_time'])->tz($tz);
|
|
$dateKey = $slotDateTime->format('Y-m-d');
|
|
$slot['formatted_datetime'] = $slotDateTime->format('Y-m-d g:i:00');
|
|
$availableTimes[$slotDate][] = $slot;
|
|
}
|
|
} catch (Exception $e) {
|
|
Log::error('Error from function getAvailableDates(): ' . $e->getMessage(), [
|
|
'eventTypeUrl' => $eventTypeUrl,
|
|
'queryParams' => $queryParams,
|
|
'full_rul' => $eventTypeUrl,
|
|
|
|
'Authorization' => 'Bearer ' . $this->accessToken(),
|
|
]);
|
|
continue;
|
|
}
|
|
$date->addSecond();
|
|
}
|
|
|
|
return $availableTimes;
|
|
} catch (\Exception $e) {
|
|
return response()->json(['error' => 'Failed to fetch available times: ' . $e->getMessage()], 500);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// write function to do php get and post request and add json support
|
|
function makeRequest($url, $method = 'GET', $data = [], $headers = [], $json = false, $proxy = "iproyal15202:pb86ljih495_country-us@geo.iproyal.com:12321")
|
|
{
|
|
$ch = curl_init($url);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_ENCODING, '',);
|
|
if ($proxy) {
|
|
curl_setopt($ch, CURLOPT_PROXY, 'http://geo.iproyal.com:12321'); // Set the proxy address and port
|
|
curl_setopt($ch, CURLOPT_PROXYUSERPWD, 'iproyal15202:Pb86ljiH495_country-us'); // Set the proxy authentication credentials
|
|
}
|
|
$headers = ['User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36', ...$headers];
|
|
if ($method === 'POST') {
|
|
|
|
if ($json) {
|
|
$headers = ['Content-Type: application/json', ...$headers];
|
|
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
|
|
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
|
|
} else {
|
|
curl_setopt($ch, CURLOPT_POST, true);
|
|
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
|
|
}
|
|
}
|
|
if (!empty($headers)) {
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
|
}
|
|
|
|
$response = curl_exec($ch);
|
|
curl_close($ch);
|
|
|
|
return $response;
|
|
}
|
|
|
|
|
|
function getCrfToken($url)
|
|
{
|
|
try {
|
|
$response = $this->makeRequest($url);
|
|
preg_match('/<meta name="csrf-token" content="(.*?)" \/>/', $response, $matches);
|
|
return $matches[1];
|
|
} catch (Exception $e) {
|
|
$response = $this->makeRequest($url);
|
|
preg_match('/<meta name="csrf-token" content="(.*?)" \/>/', $response, $matches);
|
|
return $matches[1];
|
|
}
|
|
}
|
|
|
|
function getEventDetails($url)
|
|
{
|
|
$response = $this->makeRequest($url);
|
|
return $response;
|
|
}
|
|
|
|
//random string function
|
|
function generateRandomString($length = 10)
|
|
{
|
|
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
|
$randomString = '';
|
|
for ($i = 0; $i < $length; $i++) {
|
|
$randomString .= $characters[rand(0, strlen($characters) - 1)];
|
|
}
|
|
return $randomString;
|
|
}
|
|
|
|
|
|
public function bookEvent($url, $name, $email, $timezone = "UTC")
|
|
{
|
|
$url_parts = explode("/", $url);
|
|
$bookingDate = $url_parts[5];
|
|
$event_type = $url_parts[4];
|
|
$owner_id = $url_parts[3];
|
|
$eventDetailsUrl = "https://calendly.com/api/booking/event_types/lookup?event_type_slug=$event_type&profile_slug=$owner_id";
|
|
$eventDetails = json_decode($this->makeRequest($eventDetailsUrl));
|
|
// var_dump($eventDetails);
|
|
$crfToken = $this->getCrfToken($url);
|
|
$timezone = $this->mapTimezone($timezone);
|
|
// $eventUuid = $eventDetails->uuid;
|
|
$eventUuid = data_get($eventDetails, 'eventDetails.uid');
|
|
$link_uuid = data_get($eventDetails, 'scheduling_link.uid');
|
|
// $custom_fields_id = $eventDetails->custom_fields[0]->id;
|
|
$custom_fields_id = data_get($eventDetails, 'custom_fields.0.id');
|
|
$booking_request_id = urlencode($this->generateRandomString(36) . "|$bookingDate|$eventUuid|$name");
|
|
//convert to php array
|
|
$bookingData = [
|
|
"analytics" => [
|
|
//php date with 10 sec gap
|
|
"invitee_landed_at" => date("Y-m-d\TH:i:s.uP", strtotime($bookingDate) - 200),
|
|
"browser" => "Chrome 129",
|
|
"device" => "undefined Mac OS X 10.15.7",
|
|
"fields_filled" => 1,
|
|
"fields_presented" => 1,
|
|
"booking_flow" => "v3",
|
|
"seconds_to_convert" => 200
|
|
],
|
|
"embed" => (object)[],
|
|
"event" => [
|
|
"start_time" => $bookingDate,
|
|
"location_configuration" => data_get($eventDetails,"location_configurations.0",[
|
|
"location" => "",
|
|
"phone_number" => "",
|
|
"additional_info" => ""
|
|
]),
|
|
"guests" => []
|
|
],
|
|
"event_fields" => [
|
|
[
|
|
"id" => $custom_fields_id,
|
|
"name" => "Please share anything that will help prepare for our meeting.",
|
|
"format" => "text",
|
|
"required" => false,
|
|
"position" => 0,
|
|
"answer_choices" => null,
|
|
"include_other" => false,
|
|
"value" => ""
|
|
]
|
|
],
|
|
"invitee" => [
|
|
"timezone" => $timezone,
|
|
"time_notation" => "24h",
|
|
"full_name" => $name,
|
|
"email" => $email
|
|
],
|
|
"payment_token" => (object)[],
|
|
"tracking" => [
|
|
"fingerprint" => $this->generateRandomString(32)
|
|
],
|
|
"scheduling_link_uuid" => $link_uuid,
|
|
"locale" => "en",
|
|
"verification_code" => null,
|
|
"remember_device" => false
|
|
];
|
|
|
|
|
|
$response = $this->makeRequest("https://calendly.com/api/booking/invitees", 'POST', $bookingData, [
|
|
'x-csrf-token: ' . $crfToken,
|
|
'x-page-rendered-at: ' . date('Y-m-d\TH:i:s'),
|
|
'x-requested-with: XMLHttpRequest',
|
|
'referer: ' . $url,
|
|
"booking-request-id: $booking_request_id"
|
|
], true);
|
|
|
|
return $response;
|
|
}
|
|
}
|