initial commit
This commit is contained in:
357
app/Classes/Calendly.php
Normal file
357
app/Classes/Calendly.php
Normal file
@@ -0,0 +1,357 @@
|
||||
<?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;
|
||||
|
||||
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 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 getAvailableDates($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
|
||||
];
|
||||
$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)
|
||||
{
|
||||
$response = $this->makeRequest($url);
|
||||
|
||||
// var_dump($response);
|
||||
//<meta name="csrf-token" content="mTW6eNk5xiDTzby7yHl_iVTO1_pi-CXZ2U9MYPcXGJGMpksW4iGLJniNxs-T4xrmYlLbXFaLuU8FZ75cuorz8w" />
|
||||
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/profiles/$owner_id/event_types/$event_type";
|
||||
$eventDetails = json_decode($this->makeRequest($eventDetailsUrl));
|
||||
// var_dump($eventDetails);
|
||||
$crfToken = $this->getCrfToken($url);
|
||||
|
||||
$eventUuid = $eventDetails->uuid;
|
||||
$link_uuid = $eventDetails->scheduling_link->uid;
|
||||
$custom_fields_id = $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" => [],
|
||||
"event" => [
|
||||
"start_time" => $bookingDate,
|
||||
"location_configuration" => [
|
||||
"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" => [],
|
||||
"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;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user