1
1
import { google } from "googleapis" ;
2
2
import fs from "fs" ;
3
3
import path from "path" ;
4
+ import {
5
+ parse ,
6
+ addHours ,
7
+ isValid ,
8
+ startOfHour ,
9
+ addDays ,
10
+ setHours ,
11
+ } from "date-fns" ;
4
12
5
- //const KEYFILEPATH = "auth.json";
13
+ const SERVICE_ACCOUNT_PATH = "auth.json" ;
14
+ const TIME_ZONE = "America/New_York" ;
15
+ const APPOINTMENT_DURATION_HOURS = 1 ;
6
16
const CALENDAR_ID = "primary" ;
17
+ const CHECK_DAYS_AHEAD = 7 ;
18
+ const BUSINESS_START_HOUR = 9 ; // Business hours start at 9 AM
19
+ const BUSINESS_END_HOUR = 17 ; // Business hours end at 5 PM
7
20
21
+ // Load service account credentials
8
22
const serviceAccountCredentials = JSON . parse (
9
- fs . readFileSync ( path . join ( "auth.json" ) , "utf8" )
23
+ fs . readFileSync ( path . join ( SERVICE_ACCOUNT_PATH ) , "utf8" )
10
24
) ;
11
25
12
26
// Google Calendar API client setup
@@ -24,81 +38,89 @@ const auth = new google.auth.JWT(
24
38
"111370419970452146902"
25
39
) ;
26
40
const calendar = google . calendar ( { version : "v3" , auth } ) ;
27
- console . log ( "Auth setup complete, proceeding with API calls..." ) ;
28
- // Function to check calendar availability
29
- export async function checkDateTimeAvailability ( dateTimeToCheck ) {
30
- const startDateTime = new Date ( dateTimeToCheck ) ;
31
- const endDateTime = new Date ( startDateTime . getTime ( ) + 60 * 60000 ) ; // Check 1 hour range
32
-
33
- const res = await calendar . freebusy . query ( {
34
- requestBody : {
35
- timeMin : startDateTime . toISOString ( ) ,
36
- timeMax : endDateTime . toISOString ( ) ,
37
- items : [ { id : CALENDAR_ID } ] ,
38
- } ,
39
- } ) ;
40
41
41
- const isAvailable = res . data . calendars [ CALENDAR_ID ] . busy . length === 0 ;
42
- console . log (
43
- `Checking availability for: ${ startDateTime . toISOString ( ) } to ${ endDateTime . toISOString ( ) } `
44
- ) ;
45
- console . log ( `Availability: ${ isAvailable ? "Available" : "Not available" } ` ) ;
46
- return isAvailable ;
47
- }
48
-
49
- // // Function to create a calendar event
50
- async function createAppointment ( dateTimeToCheck , summary , description , email ) {
51
- if ( await checkDateTimeAvailability ( dateTimeToCheck ) ) {
52
- const event = {
53
- summary,
54
- description,
55
- start : {
56
- dateTime : dateTimeToCheck ,
57
- timeZone : "America/New_York" ,
58
- } ,
59
- end : {
60
- dateTime : new Date (
61
- new Date ( dateTimeToCheck ) . getTime ( ) + 3600000
62
- ) . toISOString ( ) , // Adds one hour to the start time
63
- timeZone : "America/New_York" ,
64
- } ,
65
- attendees : [ { email : email } ] ,
66
- } ;
67
-
68
- try {
69
- const { data } = await calendar . events . insert ( {
70
- calendarId : CALENDAR_ID ,
71
- resource : event ,
72
- } ) ;
73
-
74
- return data . htmlLink ; // Return the link to the created calendar event
75
- } catch ( error ) {
76
- console . error ( "Error booking appointment:" , error ) ;
77
- throw error ;
42
+ export const checkDateTimeAvailability = async (
43
+ date ,
44
+ numberOfDays = CHECK_DAYS_AHEAD
45
+ ) => {
46
+ let availableSlots = [ ] ;
47
+ let currentDate = startOfHour ( new Date ( date ) ) ; // Start from the beginning of the hour of the provided date
48
+
49
+ for ( let day = 0 ; day < numberOfDays ; day ++ ) {
50
+ for ( let hour = BUSINESS_START_HOUR ; hour < BUSINESS_END_HOUR ; hour ++ ) {
51
+ let startDateTime = setHours ( addDays ( currentDate , day ) , hour ) ;
52
+ let endDateTime = addHours ( startDateTime , APPOINTMENT_DURATION_HOURS ) ;
53
+
54
+ try {
55
+ const response = await calendar . freebusy . query ( {
56
+ requestBody : {
57
+ timeMin : startDateTime . toISOString ( ) ,
58
+ timeMax : endDateTime . toISOString ( ) ,
59
+ items : [ { id : CALENDAR_ID } ] ,
60
+ timeZone : TIME_ZONE ,
61
+ } ,
62
+ } ) ;
63
+
64
+ if ( response . data . calendars [ CALENDAR_ID ] . busy . length === 0 ) {
65
+ availableSlots . push ( startDateTime ) ;
66
+ }
67
+ } catch ( error ) {
68
+ console . error ( "Error checking availability:" , error ) ;
69
+ throw new Error ( "Failed to check availability" ) ;
70
+ }
78
71
}
79
- } else {
72
+ }
73
+
74
+ return availableSlots ;
75
+ } ;
76
+
77
+ // Function to create a calendar event
78
+ export const createAppointment = async (
79
+ dateTime ,
80
+ summary ,
81
+ description ,
82
+ email
83
+ ) => {
84
+ const isAvailable = await checkDateTimeAvailability ( dateTime ) ;
85
+ if ( ! isAvailable ) {
80
86
console . log (
81
87
"Time slot not available, looking for the next available slot..."
82
88
) ;
83
- // Find the next available time slot, add an hour and try again:
84
- const nextAttempt = new Date ( new Date ( dateTimeToCheck ) . getTime ( ) + 3600000 ) ;
85
- return createAppointment (
86
- nextAttempt . toISOString ( ) ,
87
- summary ,
88
- description ,
89
- email
90
- ) ;
89
+ const nextAttempt = addHours ( dateTime , APPOINTMENT_DURATION_HOURS ) ;
90
+ return createAppointment ( nextAttempt , summary , description , email ) ;
91
+ }
92
+
93
+ const event = {
94
+ summary,
95
+ description,
96
+ start : { dateTime : dateTime . toISOString ( ) , timeZone : TIME_ZONE } ,
97
+ end : {
98
+ dateTime : addHours ( dateTime , APPOINTMENT_DURATION_HOURS ) . toISOString ( ) ,
99
+ timeZone : TIME_ZONE ,
100
+ } ,
101
+ attendees : [ { email } ] ,
102
+ } ;
103
+
104
+ try {
105
+ const { data } = await calendar . events . insert ( {
106
+ calendarId : CALENDAR_ID ,
107
+ resource : event ,
108
+ } ) ;
109
+ return data . htmlLink ;
110
+ } catch ( error ) {
111
+ console . error ( "Error booking appointment:" , error ) ;
112
+ throw new Error ( "Failed to book appointment" ) ;
91
113
}
92
- }
114
+ } ;
93
115
94
- // // Example usage of booking an appointment
95
- export async function setupMeeting ( dateTime , summary , description , email ) {
116
+ // Function to setup a meeting
117
+ export const setupMeeting = async ( date , time , summary , description , email ) => {
118
+ const dateTime = parseDateTime ( date , time ) ;
96
119
try {
97
120
const bookingLink = await createAppointment (
98
121
dateTime ,
99
122
summary ,
100
123
description ,
101
-
102
124
email
103
125
) ;
104
126
console . log (
@@ -107,4 +129,70 @@ export async function setupMeeting(dateTime, summary, description, email) {
107
129
} catch ( error ) {
108
130
console . error ( "Failed to schedule the meeting:" , error ) ;
109
131
}
110
- }
132
+ } ;
133
+
134
+ const parseDateTime = ( date , time ) => {
135
+ const dateTimeFormats = [
136
+ "dd/MM/yyyy HH:mm" ,
137
+ "dd/MM/yyyy hh:mm a" ,
138
+ "dd-MM-yyyy HH:mm" ,
139
+ "dd-MM-yyyy hh:mm a" ,
140
+ "yyyy-MM-dd'T'HH:mm:ssX" ,
141
+ ] ;
142
+
143
+ for ( const formatString of dateTimeFormats ) {
144
+ const parsedDate = parse ( `${ date } ${ time } ` , formatString , new Date ( ) ) ;
145
+ if ( isValid ( parsedDate ) ) {
146
+ return parsedDate ;
147
+ }
148
+ }
149
+
150
+ throw new Error ( "Invalid date-time value" ) ;
151
+ } ;
152
+
153
+ // Function to send meeting details to user's email
154
+ export const sendMeetingDetails = async (
155
+ meetingLink ,
156
+ email ,
157
+ meetingDetails
158
+ ) => {
159
+ const nodemailer = require ( "nodemailer" ) ;
160
+
161
+ // Create a transport object
162
+ const transporter = nodemailer . createTransport ( {
163
+ host : "smtp.gmail.com" ,
164
+ port : 587 ,
165
+ secure : false , // or 'STARTTLS'
166
+ auth : {
167
+
168
+ pass : "Gil@sapir2309" ,
169
+ } ,
170
+ } ) ;
171
+
172
+ // Define the email message
173
+ const mailOptions = {
174
+
175
+ to : email ,
176
+ subject : "Meeting Details" ,
177
+ html : `
178
+ <p>Dear Participant,</p>
179
+ <p>We are pleased to invite you to the following meeting:</p>
180
+ <p><strong>Meeting Details:</strong></p>
181
+ <ul>
182
+ <li><strong>Date:</strong> ${ meetingDetails . date } </li>
183
+ <li><strong>Time:</strong> ${ meetingDetails . time } </li>
184
+ <li><strong>Agenda:</strong> ${ meetingDetails . agenda } </li>
185
+ </ul>
186
+ <p>You can join the meeting using this link: <a href="${ meetingLink } ">${ meetingLink } </a></p>
187
+ <p>We look forward to your participation.</p>
188
+ ` ,
189
+ } ;
190
+
191
+ // Send the email
192
+ transporter . sendMail ( mailOptions , ( error , info ) => {
193
+ if ( error ) {
194
+ return console . log ( error ) ;
195
+ }
196
+ console . log ( "Email sent: " + info . response ) ;
197
+ } ) ;
198
+ } ;
0 commit comments