meetfy 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc +13 -0
- package/README.md +158 -0
- package/dist/features/auth/authService.d.ts +3 -0
- package/dist/features/auth/authService.d.ts.map +1 -0
- package/dist/features/auth/authService.js +86 -0
- package/dist/features/auth/authService.js.map +1 -0
- package/dist/features/meeting/meetingService.d.ts +16 -0
- package/dist/features/meeting/meetingService.d.ts.map +1 -0
- package/dist/features/meeting/meetingService.js +108 -0
- package/dist/features/meeting/meetingService.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +63 -0
- package/dist/index.js.map +1 -0
- package/dist/utils/cliUtils.d.ts +4 -0
- package/dist/utils/cliUtils.d.ts.map +1 -0
- package/dist/utils/cliUtils.js +37 -0
- package/dist/utils/cliUtils.js.map +1 -0
- package/package.json +51 -0
- package/src/constants.ts +3 -0
- package/src/index.ts +76 -0
- package/src/services/authService.ts +106 -0
- package/src/services/configService.ts +8 -0
- package/src/services/meetingService.ts +139 -0
- package/src/services/webServer.ts +35 -0
- package/src/utils/cliUtils.ts +43 -0
- package/tsconfig.json +20 -0
package/.eslintrc
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# Meetfy CLI
|
|
2
|
+
|
|
3
|
+
A command-line tool for creating instant meetings and reserving 30 minutes in Google Calendar.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- š Create instant meetings with Google Meet
|
|
8
|
+
- š
Automatically reserve 30 minutes in your Google Calendar
|
|
9
|
+
- š„ Add participants via email
|
|
10
|
+
- š Secure Google OAuth2 authentication
|
|
11
|
+
- š¾ Persistent authentication tokens
|
|
12
|
+
- šØ Beautiful CLI interface with colors and spinners
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
1. Clone the repository:
|
|
17
|
+
```bash
|
|
18
|
+
git clone <repository-url>
|
|
19
|
+
cd meetfy
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
2. Install dependencies:
|
|
23
|
+
```bash
|
|
24
|
+
npm install
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
3. Build the project:
|
|
28
|
+
```bash
|
|
29
|
+
npm run build
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
4. Link globally (optional):
|
|
33
|
+
```bash
|
|
34
|
+
npm link
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Setup
|
|
38
|
+
|
|
39
|
+
### 1. Google Calendar API Setup
|
|
40
|
+
|
|
41
|
+
1. Go to [Google Cloud Console](https://console.cloud.google.com)
|
|
42
|
+
2. Create a new project or select an existing one
|
|
43
|
+
3. Enable the Google Calendar API:
|
|
44
|
+
- Go to "APIs & Services" ā "Library"
|
|
45
|
+
- Search for "Google Calendar API"
|
|
46
|
+
- Click "Enable"
|
|
47
|
+
4. Create OAuth 2.0 credentials:
|
|
48
|
+
- Go to "APIs & Services" ā "Credentials"
|
|
49
|
+
- Click "Create Credentials" ā "OAuth 2.0 Client IDs"
|
|
50
|
+
- Choose "Desktop application"
|
|
51
|
+
- Download the credentials file
|
|
52
|
+
5. Rename the downloaded file to `credentials.json` and place it in the project root
|
|
53
|
+
|
|
54
|
+
The `credentials.json` file should look like this:
|
|
55
|
+
```json
|
|
56
|
+
{
|
|
57
|
+
"installed": {
|
|
58
|
+
"client_id": "your-client-id.apps.googleusercontent.com",
|
|
59
|
+
"project_id": "your-project-id",
|
|
60
|
+
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
|
|
61
|
+
"token_uri": "https://oauth2.googleapis.com/token",
|
|
62
|
+
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
|
|
63
|
+
"client_secret": "your-client-secret",
|
|
64
|
+
"redirect_uris": ["http://localhost"]
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 2. Authentication
|
|
70
|
+
|
|
71
|
+
Run the authentication command:
|
|
72
|
+
```bash
|
|
73
|
+
npm run dev auth
|
|
74
|
+
# or if linked globally:
|
|
75
|
+
meetfy auth
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Follow the authentication flow to authorize the application.
|
|
79
|
+
|
|
80
|
+
## Usage
|
|
81
|
+
|
|
82
|
+
### Create an Instant Meeting
|
|
83
|
+
|
|
84
|
+
Basic usage:
|
|
85
|
+
```bash
|
|
86
|
+
npm run dev create
|
|
87
|
+
# or if linked globally:
|
|
88
|
+
meetfy create
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
With options:
|
|
92
|
+
```bash
|
|
93
|
+
meetfy create -t "Team Standup" -d "Daily team sync" -p "john@example.com,jane@example.com"
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Available Options
|
|
97
|
+
|
|
98
|
+
- `-t, --title <title>`: Meeting title
|
|
99
|
+
- `-d, --description <description>`: Meeting description
|
|
100
|
+
- `-p, --participants <emails>`: Comma-separated list of participant emails
|
|
101
|
+
|
|
102
|
+
### Authentication
|
|
103
|
+
|
|
104
|
+
To authenticate or re-authenticate:
|
|
105
|
+
```bash
|
|
106
|
+
meetfy auth
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Development
|
|
110
|
+
|
|
111
|
+
### Run in Development Mode
|
|
112
|
+
```bash
|
|
113
|
+
npm run dev
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Build for Production
|
|
117
|
+
```bash
|
|
118
|
+
npm run build
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Run Built Version
|
|
122
|
+
```bash
|
|
123
|
+
npm start
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Project Structure
|
|
127
|
+
|
|
128
|
+
```
|
|
129
|
+
src/
|
|
130
|
+
āāā features/
|
|
131
|
+
ā āāā auth/
|
|
132
|
+
ā ā āāā authService.ts # Google OAuth2 authentication
|
|
133
|
+
ā āāā meeting/
|
|
134
|
+
ā āāā meetingService.ts # Meeting creation and calendar integration
|
|
135
|
+
āāā utils/
|
|
136
|
+
ā āāā cliUtils.ts # CLI utilities and formatting
|
|
137
|
+
āāā index.ts # Main CLI entry point
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Features
|
|
141
|
+
|
|
142
|
+
- **Instant Meeting Creation**: Creates meetings that start in 5 minutes
|
|
143
|
+
- **30-Minute Duration**: Automatically sets 30-minute duration
|
|
144
|
+
- **Google Meet Integration**: Includes Google Meet link in calendar events
|
|
145
|
+
- **Participant Management**: Add participants via email addresses
|
|
146
|
+
- **Calendar Reminders**: Sets up email and popup reminders
|
|
147
|
+
- **Persistent Authentication**: Saves and reuses authentication tokens
|
|
148
|
+
- **Interactive CLI**: User-friendly prompts and colorful output
|
|
149
|
+
|
|
150
|
+
## Requirements
|
|
151
|
+
|
|
152
|
+
- Node.js 18+
|
|
153
|
+
- Google Calendar API access
|
|
154
|
+
- Google OAuth2 credentials
|
|
155
|
+
|
|
156
|
+
## License
|
|
157
|
+
|
|
158
|
+
ISC
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authService.d.ts","sourceRoot":"","sources":["../../../src/features/auth/authService.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAyBnD,eAAO,MAAM,kBAAkB,QAAa,OAAO,CAAC,YAAY,GAAG,IAAI,CA+CtE,CAAC"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { google } from 'googleapis';
|
|
2
|
+
import { readFileSync, existsSync } from 'fs';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import inquirer from 'inquirer';
|
|
6
|
+
import Conf from 'conf';
|
|
7
|
+
const SCOPES = [
|
|
8
|
+
'https://www.googleapis.com/auth/calendar',
|
|
9
|
+
'https://www.googleapis.com/auth/calendar.events'
|
|
10
|
+
];
|
|
11
|
+
const TOKEN_PATH = join(process.cwd(), '.meetfy-token.json');
|
|
12
|
+
const CREDENTIALS_PATH = join(process.cwd(), 'credentials.json');
|
|
13
|
+
const config = new Conf();
|
|
14
|
+
export const authenticateGoogle = async () => {
|
|
15
|
+
try {
|
|
16
|
+
// Check if credentials file exists
|
|
17
|
+
if (!existsSync(CREDENTIALS_PATH)) {
|
|
18
|
+
console.log(chalk.yellow('ā ļø Google Calendar credentials not found.'));
|
|
19
|
+
console.log(chalk.cyan('š Please follow these steps:'));
|
|
20
|
+
console.log(chalk.cyan('1. Go to https://console.cloud.google.com'));
|
|
21
|
+
console.log(chalk.cyan('2. Create a new project or select existing one'));
|
|
22
|
+
console.log(chalk.cyan('3. Enable Google Calendar API'));
|
|
23
|
+
console.log(chalk.cyan('4. Create OAuth 2.0 credentials'));
|
|
24
|
+
console.log(chalk.cyan('5. Download credentials.json and place it in the project root'));
|
|
25
|
+
console.log(chalk.cyan('6. Run: meetfy auth'));
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
const credentials = JSON.parse(readFileSync(CREDENTIALS_PATH, 'utf-8'));
|
|
29
|
+
const { client_secret, client_id, redirect_uris } = credentials.installed || credentials.web;
|
|
30
|
+
const oAuth2Client = new google.auth.OAuth2(client_id, client_secret, redirect_uris[0]);
|
|
31
|
+
// Check if we have stored tokens
|
|
32
|
+
const storedTokens = config.get('googleTokens');
|
|
33
|
+
if (storedTokens) {
|
|
34
|
+
oAuth2Client.setCredentials(storedTokens);
|
|
35
|
+
// Check if token is expired
|
|
36
|
+
if (storedTokens.expiry_date && Date.now() > storedTokens.expiry_date) {
|
|
37
|
+
console.log(chalk.yellow('š Token expired, refreshing...'));
|
|
38
|
+
const { credentials: newTokens } = await oAuth2Client.refreshAccessToken();
|
|
39
|
+
oAuth2Client.setCredentials(newTokens);
|
|
40
|
+
config.set('googleTokens', newTokens);
|
|
41
|
+
}
|
|
42
|
+
return oAuth2Client;
|
|
43
|
+
}
|
|
44
|
+
// Get new tokens
|
|
45
|
+
return await getNewTokens(oAuth2Client);
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
console.error(chalk.red('ā Authentication error:'), error);
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
const getNewTokens = async (oAuth2Client) => {
|
|
53
|
+
try {
|
|
54
|
+
const authUrl = oAuth2Client.generateAuthUrl({
|
|
55
|
+
access_type: 'offline',
|
|
56
|
+
scope: SCOPES,
|
|
57
|
+
});
|
|
58
|
+
console.log(chalk.cyan('š Authorize this app by visiting this url:'));
|
|
59
|
+
console.log(chalk.blue(authUrl));
|
|
60
|
+
console.log(chalk.cyan('\nš Copy the authorization code from the URL and paste it below.'));
|
|
61
|
+
const { code } = await inquirer.prompt([
|
|
62
|
+
{
|
|
63
|
+
type: 'input',
|
|
64
|
+
name: 'code',
|
|
65
|
+
message: 'Enter the authorization code:',
|
|
66
|
+
validate: (input) => {
|
|
67
|
+
if (!input.trim()) {
|
|
68
|
+
return 'Please enter the authorization code';
|
|
69
|
+
}
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
]);
|
|
74
|
+
const { tokens } = await oAuth2Client.getToken(code);
|
|
75
|
+
oAuth2Client.setCredentials(tokens);
|
|
76
|
+
// Store tokens
|
|
77
|
+
config.set('googleTokens', tokens);
|
|
78
|
+
console.log(chalk.green('ā
Authentication successful!'));
|
|
79
|
+
return oAuth2Client;
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
console.error(chalk.red('ā Error getting tokens:'), error);
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
//# sourceMappingURL=authService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authService.js","sourceRoot":"","sources":["../../../src/features/auth/authService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { OAuth2Client } from 'google-auth-library';
|
|
2
|
+
interface MeetingOptions {
|
|
3
|
+
title?: string;
|
|
4
|
+
description?: string;
|
|
5
|
+
participants?: string;
|
|
6
|
+
}
|
|
7
|
+
interface MeetingResult {
|
|
8
|
+
id: string;
|
|
9
|
+
hangoutLink: string;
|
|
10
|
+
startTime: string;
|
|
11
|
+
endTime: string;
|
|
12
|
+
title: string;
|
|
13
|
+
}
|
|
14
|
+
export declare const createInstantMeeting: (auth: OAuth2Client, options: MeetingOptions) => Promise<MeetingResult | null>;
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=meetingService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"meetingService.d.ts","sourceRoot":"","sources":["../../../src/features/meeting/meetingService.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAKnD,UAAU,cAAc;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,UAAU,aAAa;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,eAAO,MAAM,oBAAoB,GAC/B,MAAM,YAAY,EAClB,SAAS,cAAc,KACtB,OAAO,CAAC,aAAa,GAAG,IAAI,CAoE9B,CAAC"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { google } from 'googleapis';
|
|
2
|
+
import inquirer from 'inquirer';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import ora from 'ora';
|
|
5
|
+
export const createInstantMeeting = async (auth, options) => {
|
|
6
|
+
try {
|
|
7
|
+
const calendar = google.calendar({ version: 'v3', auth });
|
|
8
|
+
// Get meeting details from user if not provided
|
|
9
|
+
const meetingDetails = await getMeetingDetails(options);
|
|
10
|
+
// Calculate meeting times (30 minutes from now)
|
|
11
|
+
const now = new Date();
|
|
12
|
+
const startTime = new Date(now.getTime() + 5 * 60 * 1000); // Start in 5 minutes
|
|
13
|
+
const endTime = new Date(startTime.getTime() + 30 * 60 * 1000); // 30 minutes duration
|
|
14
|
+
const spinner = ora('Creating meeting and reserving calendar...').start();
|
|
15
|
+
// Create calendar event
|
|
16
|
+
const event = {
|
|
17
|
+
summary: meetingDetails.title,
|
|
18
|
+
description: meetingDetails.description,
|
|
19
|
+
start: {
|
|
20
|
+
dateTime: startTime.toISOString(),
|
|
21
|
+
timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
22
|
+
},
|
|
23
|
+
end: {
|
|
24
|
+
dateTime: endTime.toISOString(),
|
|
25
|
+
timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
26
|
+
},
|
|
27
|
+
attendees: meetingDetails.participants.map((email) => ({ email: email.trim() })),
|
|
28
|
+
conferenceData: {
|
|
29
|
+
createRequest: {
|
|
30
|
+
requestId: `meetfy-${Date.now()}`,
|
|
31
|
+
conferenceSolutionKey: {
|
|
32
|
+
type: 'hangoutsMeet'
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
reminders: {
|
|
37
|
+
useDefault: false,
|
|
38
|
+
overrides: [
|
|
39
|
+
{ method: 'email', minutes: 10 },
|
|
40
|
+
{ method: 'popup', minutes: 5 }
|
|
41
|
+
]
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
const response = await calendar.events.insert({
|
|
45
|
+
calendarId: 'primary',
|
|
46
|
+
requestBody: event,
|
|
47
|
+
conferenceDataVersion: 1,
|
|
48
|
+
sendUpdates: 'all'
|
|
49
|
+
});
|
|
50
|
+
spinner.succeed('Meeting created successfully!');
|
|
51
|
+
if (!response.data.id || !response.data.hangoutLink) {
|
|
52
|
+
throw new Error('Failed to create meeting with Google Meet link');
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
id: response.data.id,
|
|
56
|
+
hangoutLink: response.data.hangoutLink,
|
|
57
|
+
startTime: startTime.toLocaleString(),
|
|
58
|
+
endTime: endTime.toLocaleString(),
|
|
59
|
+
title: meetingDetails.title
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
console.error(chalk.red('ā Error creating meeting:'), error);
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
const getMeetingDetails = async (options) => {
|
|
68
|
+
const questions = [];
|
|
69
|
+
if (!options.title) {
|
|
70
|
+
questions.push({
|
|
71
|
+
type: 'input',
|
|
72
|
+
name: 'title',
|
|
73
|
+
message: 'Meeting title:',
|
|
74
|
+
default: 'Instant Meeting',
|
|
75
|
+
validate: (input) => {
|
|
76
|
+
if (!input.trim()) {
|
|
77
|
+
return 'Please enter a meeting title';
|
|
78
|
+
}
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
if (!options.description) {
|
|
84
|
+
questions.push({
|
|
85
|
+
type: 'input',
|
|
86
|
+
name: 'description',
|
|
87
|
+
message: 'Meeting description (optional):',
|
|
88
|
+
default: 'Instant meeting created via Meetfy CLI'
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
if (!options.participants) {
|
|
92
|
+
questions.push({
|
|
93
|
+
type: 'input',
|
|
94
|
+
name: 'participants',
|
|
95
|
+
message: 'Participant emails (comma-separated, optional):',
|
|
96
|
+
default: ''
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
const answers = questions.length > 0 ? await inquirer.prompt(questions) : {};
|
|
100
|
+
return {
|
|
101
|
+
title: options.title || answers.title,
|
|
102
|
+
description: options.description || answers.description,
|
|
103
|
+
participants: (options.participants || answers.participants || '')
|
|
104
|
+
.split(',')
|
|
105
|
+
.filter((email) => email.trim() !== '')
|
|
106
|
+
};
|
|
107
|
+
};
|
|
108
|
+
//# sourceMappingURL=meetingService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"meetingService.js","sourceRoot":"","sources":["../../../src/features/meeting/meetingService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEpC,OAAO,QAAQ,MAAM,UAAU,CAAC;AAChC,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,GAAG,MAAM,KAAK,CAAC;AAgBtB,MAAM,CAAC,MAAM,oBAAoB,GAAG,KAAK,EACvC,IAAkB,EAClB,OAAuB,EACQ,EAAE;IACjC,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QAE1D,gDAAgD;QAChD,MAAM,cAAc,GAAG,MAAM,iBAAiB,CAAC,OAAO,CAAC,CAAC;QAExD,gDAAgD;QAChD,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,qBAAqB;QAChF,MAAM,OAAO,GAAG,IAAI,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,sBAAsB;QAEtF,MAAM,OAAO,GAAG,GAAG,CAAC,4CAA4C,CAAC,CAAC,KAAK,EAAE,CAAC;QAE1E,wBAAwB;QACxB,MAAM,KAAK,GAAG;YACZ,OAAO,EAAE,cAAc,CAAC,KAAK;YAC7B,WAAW,EAAE,cAAc,CAAC,WAAW;YACvC,KAAK,EAAE;gBACL,QAAQ,EAAE,SAAS,CAAC,WAAW,EAAE;gBACjC,QAAQ,EAAE,IAAI,CAAC,cAAc,EAAE,CAAC,eAAe,EAAE,CAAC,QAAQ;aAC3D;YACD,GAAG,EAAE;gBACH,QAAQ,EAAE,OAAO,CAAC,WAAW,EAAE;gBAC/B,QAAQ,EAAE,IAAI,CAAC,cAAc,EAAE,CAAC,eAAe,EAAE,CAAC,QAAQ;aAC3D;YACD,SAAS,EAAE,cAAc,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,KAAa,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YACxF,cAAc,EAAE;gBACd,aAAa,EAAE;oBACb,SAAS,EAAE,UAAU,IAAI,CAAC,GAAG,EAAE,EAAE;oBACjC,qBAAqB,EAAE;wBACrB,IAAI,EAAE,cAAc;qBACrB;iBACF;aACF;YACD,SAAS,EAAE;gBACT,UAAU,EAAE,KAAK;gBACjB,SAAS,EAAE;oBACT,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE;oBAChC,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE;iBAChC;aACF;SACF,CAAC;QAEF,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC;YAC5C,UAAU,EAAE,SAAS;YACrB,WAAW,EAAE,KAAK;YAClB,qBAAqB,EAAE,CAAC;YACxB,WAAW,EAAE,KAAK;SACnB,CAAC,CAAC;QAEH,OAAO,CAAC,OAAO,CAAC,+BAA+B,CAAC,CAAC;QAEjD,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACpD,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;QACpE,CAAC;QAED,OAAO;YACL,EAAE,EAAE,QAAQ,CAAC,IAAI,CAAC,EAAE;YACpB,WAAW,EAAE,QAAQ,CAAC,IAAI,CAAC,WAAW;YACtC,SAAS,EAAE,SAAS,CAAC,cAAc,EAAE;YACrC,OAAO,EAAE,OAAO,CAAC,cAAc,EAAE;YACjC,KAAK,EAAE,cAAc,CAAC,KAAK;SAC5B,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,2BAA2B,CAAC,EAAE,KAAK,CAAC,CAAC;QAC7D,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,iBAAiB,GAAG,KAAK,EAAE,OAAuB,EAAE,EAAE;IAC1D,MAAM,SAAS,GAAG,EAAE,CAAC;IAErB,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QACnB,SAAS,CAAC,IAAI,CAAC;YACb,IAAI,EAAE,OAAO;YACb,IAAI,EAAE,OAAO;YACb,OAAO,EAAE,gBAAgB;YACzB,OAAO,EAAE,iBAAiB;YAC1B,QAAQ,EAAE,CAAC,KAAa,EAAE,EAAE;gBAC1B,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;oBAClB,OAAO,8BAA8B,CAAC;gBACxC,CAAC;gBACD,OAAO,IAAI,CAAC;YACd,CAAC;SACF,CAAC,CAAC;IACL,CAAC;IAED,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;QACzB,SAAS,CAAC,IAAI,CAAC;YACb,IAAI,EAAE,OAAO;YACb,IAAI,EAAE,aAAa;YACnB,OAAO,EAAE,iCAAiC;YAC1C,OAAO,EAAE,wCAAwC;SAClD,CAAC,CAAC;IACL,CAAC;IAED,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC;QAC1B,SAAS,CAAC,IAAI,CAAC;YACb,IAAI,EAAE,OAAO;YACb,IAAI,EAAE,cAAc;YACpB,OAAO,EAAE,iDAAiD;YAC1D,OAAO,EAAE,EAAE;SACZ,CAAC,CAAC;IACL,CAAC;IAED,MAAM,OAAO,GAAG,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAE7E,OAAO;QACL,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC,KAAK;QACrC,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,OAAO,CAAC,WAAW;QACvD,YAAY,EAAE,CAAC,OAAO,CAAC,YAAY,IAAI,OAAO,CAAC,YAAY,IAAI,EAAE,CAAC;aAC/D,KAAK,CAAC,GAAG,CAAC;aACV,MAAM,CAAC,CAAC,KAAa,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;KAClD,CAAC;AACJ,CAAC,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import { createInstantMeeting } from './features/meeting/meetingService.js';
|
|
5
|
+
import { authenticateGoogle } from './features/auth/authService.js';
|
|
6
|
+
import { showWelcomeMessage } from './utils/cliUtils.js';
|
|
7
|
+
const program = new Command();
|
|
8
|
+
program
|
|
9
|
+
.name('meetfy')
|
|
10
|
+
.description('CLI tool for creating instant meetings and reserving time in Google Calendar')
|
|
11
|
+
.version('1.0.0');
|
|
12
|
+
program
|
|
13
|
+
.command('create')
|
|
14
|
+
.description('Create an instant meeting and reserve 30 minutes in your Google Calendar')
|
|
15
|
+
.option('-t, --title <title>', 'Meeting title')
|
|
16
|
+
.option('-d, --description <description>', 'Meeting description')
|
|
17
|
+
.option('-p, --participants <emails>', 'Comma-separated list of participant emails')
|
|
18
|
+
.action(async (options) => {
|
|
19
|
+
try {
|
|
20
|
+
showWelcomeMessage();
|
|
21
|
+
// Authenticate with Google
|
|
22
|
+
const auth = await authenticateGoogle();
|
|
23
|
+
if (!auth) {
|
|
24
|
+
console.log(chalk.red('ā Authentication failed. Please try again.'));
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
// Create instant meeting
|
|
28
|
+
const meeting = await createInstantMeeting(auth, options);
|
|
29
|
+
if (meeting) {
|
|
30
|
+
console.log(chalk.green('ā
Meeting created successfully!'));
|
|
31
|
+
console.log(chalk.cyan(`š
Meeting ID: ${meeting.id}`));
|
|
32
|
+
console.log(chalk.cyan(`š Join URL: ${meeting.hangoutLink}`));
|
|
33
|
+
console.log(chalk.cyan(`ā° Duration: 30 minutes`));
|
|
34
|
+
console.log(chalk.cyan(`š
Start Time: ${meeting.startTime}`));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
console.error(chalk.red('ā Error creating meeting:'), error);
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
program
|
|
43
|
+
.command('auth')
|
|
44
|
+
.description('Authenticate with Google Calendar')
|
|
45
|
+
.action(async () => {
|
|
46
|
+
try {
|
|
47
|
+
showWelcomeMessage();
|
|
48
|
+
const auth = await authenticateGoogle();
|
|
49
|
+
if (auth) {
|
|
50
|
+
console.log(chalk.green('ā
Authentication successful!'));
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
console.log(chalk.red('ā Authentication failed.'));
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
console.error(chalk.red('ā Authentication error:'), error);
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
program.parse();
|
|
63
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cliUtils.d.ts","sourceRoot":"","sources":["../../src/utils/cliUtils.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,kBAAkB,QAAO,IAGrC,CAAC;AAEF,eAAO,MAAM,QAAQ,QAAO,IAiB3B,CAAC;AAEF,eAAO,MAAM,qBAAqB,QAAO,IAgBxC,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
export const showWelcomeMessage = () => {
|
|
3
|
+
console.log(chalk.cyan.bold('\nš Meetfy - Instant Meeting Creator'));
|
|
4
|
+
console.log(chalk.gray('Create instant meetings and reserve time in Google Calendar\n'));
|
|
5
|
+
};
|
|
6
|
+
export const showHelp = () => {
|
|
7
|
+
console.log(chalk.cyan.bold('\nš Meetfy CLI Help'));
|
|
8
|
+
console.log(chalk.gray('\nCommands:'));
|
|
9
|
+
console.log(chalk.white(' create Create an instant meeting'));
|
|
10
|
+
console.log(chalk.white(' auth Authenticate with Google Calendar'));
|
|
11
|
+
console.log(chalk.white(' --help Show this help message'));
|
|
12
|
+
console.log(chalk.gray('\nExamples:'));
|
|
13
|
+
console.log(chalk.white(' meetfy create'));
|
|
14
|
+
console.log(chalk.white(' meetfy create -t "Team Standup" -d "Daily team sync"'));
|
|
15
|
+
console.log(chalk.white(' meetfy create -p "john@example.com,jane@example.com"'));
|
|
16
|
+
console.log(chalk.white(' meetfy auth'));
|
|
17
|
+
console.log(chalk.gray('\nOptions for create command:'));
|
|
18
|
+
console.log(chalk.white(' -t, --title <title> Meeting title'));
|
|
19
|
+
console.log(chalk.white(' -d, --description <desc> Meeting description'));
|
|
20
|
+
console.log(chalk.white(' -p, --participants <emails> Comma-separated participant emails'));
|
|
21
|
+
};
|
|
22
|
+
export const showSetupInstructions = () => {
|
|
23
|
+
console.log(chalk.yellow.bold('\nš§ Setup Instructions'));
|
|
24
|
+
console.log(chalk.gray('\n1. Google Calendar API Setup:'));
|
|
25
|
+
console.log(chalk.white(' ⢠Go to https://console.cloud.google.com'));
|
|
26
|
+
console.log(chalk.white(' ⢠Create a new project or select existing one'));
|
|
27
|
+
console.log(chalk.white(' ⢠Enable Google Calendar API'));
|
|
28
|
+
console.log(chalk.white(' ⢠Create OAuth 2.0 credentials'));
|
|
29
|
+
console.log(chalk.white(' ⢠Download credentials.json to project root'));
|
|
30
|
+
console.log(chalk.gray('\n2. Authentication:'));
|
|
31
|
+
console.log(chalk.white(' ⢠Run: meetfy auth'));
|
|
32
|
+
console.log(chalk.white(' ⢠Follow the authentication flow'));
|
|
33
|
+
console.log(chalk.gray('\n3. Create Meetings:'));
|
|
34
|
+
console.log(chalk.white(' ⢠Run: meetfy create'));
|
|
35
|
+
console.log(chalk.white(' ⢠Or with options: meetfy create -t "Meeting Title"'));
|
|
36
|
+
};
|
|
37
|
+
//# sourceMappingURL=cliUtils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cliUtils.js","sourceRoot":"","sources":["../../src/utils/cliUtils.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "meetfy",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "CLI tool for creating instant meetings and reserving time in Google Calendar",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"meetfy": "./dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"dev": "tsx src/index.ts",
|
|
13
|
+
"start": "node dist/index.js",
|
|
14
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
15
|
+
"lint": "eslint src/**/*.ts"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"cli",
|
|
19
|
+
"meeting",
|
|
20
|
+
"google-calendar",
|
|
21
|
+
"typescript"
|
|
22
|
+
],
|
|
23
|
+
"author": "",
|
|
24
|
+
"license": "ISC",
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"chalk": "^5.3.0",
|
|
27
|
+
"commander": "^11.1.0",
|
|
28
|
+
"conf": "^12.0.0",
|
|
29
|
+
"express": "^5.1.0",
|
|
30
|
+
"google-auth-library": "^9.0.0",
|
|
31
|
+
"googleapis": "^128.0.0",
|
|
32
|
+
"inquirer": "^9.2.12",
|
|
33
|
+
"ora": "^7.0.1"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/express": "^5.0.3",
|
|
37
|
+
"@types/inquirer": "^9.0.7",
|
|
38
|
+
"@types/node": "^20.10.0",
|
|
39
|
+
"@typescript-eslint/eslint-plugin": "^7.18.0",
|
|
40
|
+
"@typescript-eslint/parser": "^7.18.0",
|
|
41
|
+
"eslint": "^8.57.1",
|
|
42
|
+
"eslint-config-airbnb": "^19.0.4",
|
|
43
|
+
"eslint-config-airbnb-typescript": "^18.0.0",
|
|
44
|
+
"eslint-plugin-import": "^2.32.0",
|
|
45
|
+
"eslint-plugin-jsx-a11y": "^6.10.2",
|
|
46
|
+
"eslint-plugin-react": "^7.37.5",
|
|
47
|
+
"eslint-plugin-react-hooks": "^4.6.2",
|
|
48
|
+
"tsx": "^4.20.3",
|
|
49
|
+
"typescript": "5.5"
|
|
50
|
+
}
|
|
51
|
+
}
|
package/src/constants.ts
ADDED
package/src/index.ts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import { createInstantMeeting } from './services/meetingService.js';
|
|
6
|
+
import { authenticateGoogle, logoutGoogle } from './services/authService.js';
|
|
7
|
+
import { showWelcomeMessage } from './utils/cliUtils.js';
|
|
8
|
+
|
|
9
|
+
const program = new Command();
|
|
10
|
+
|
|
11
|
+
program
|
|
12
|
+
.name('meetfy')
|
|
13
|
+
.description('CLI tool for creating instant meetings and reserving time in Google Calendar')
|
|
14
|
+
.version('1.0.0');
|
|
15
|
+
|
|
16
|
+
program
|
|
17
|
+
.command('create')
|
|
18
|
+
.description('Create an instant meeting and reserve 30 minutes in your Google Calendar')
|
|
19
|
+
.option('-t, --title <title>', 'Meeting title')
|
|
20
|
+
.option('-d, --description <description>', 'Meeting description')
|
|
21
|
+
.option('-p, --participants <emails>', 'Comma-separated list of participant emails')
|
|
22
|
+
.action(async (options) => {
|
|
23
|
+
try {
|
|
24
|
+
showWelcomeMessage();
|
|
25
|
+
|
|
26
|
+
// Authenticate with Google
|
|
27
|
+
const auth = await authenticateGoogle();
|
|
28
|
+
if (!auth) {
|
|
29
|
+
console.log(chalk.red('ā Authentication failed. Please try again.'));
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Create instant meeting
|
|
34
|
+
const meeting = await createInstantMeeting(auth, options);
|
|
35
|
+
|
|
36
|
+
if (meeting) {
|
|
37
|
+
console.log(chalk.green('ā
Meeting created successfully!'));
|
|
38
|
+
console.log(chalk.cyan(`š
Meeting ID: ${meeting.id}`));
|
|
39
|
+
console.log(chalk.cyan(`š Join URL: ${meeting.hangoutLink}`));
|
|
40
|
+
console.log(chalk.cyan('ā° Duration: 30 minutes'));
|
|
41
|
+
console.log(chalk.cyan(`š
Start Time: ${meeting.startTime}`));
|
|
42
|
+
}
|
|
43
|
+
} catch (error) {
|
|
44
|
+
console.error(chalk.red('ā Error creating meeting:'), error);
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
program
|
|
50
|
+
.command('auth')
|
|
51
|
+
.description('Authenticate with Google Calendar')
|
|
52
|
+
.action(async () => {
|
|
53
|
+
try {
|
|
54
|
+
showWelcomeMessage();
|
|
55
|
+
const auth = await authenticateGoogle();
|
|
56
|
+
if (auth) {
|
|
57
|
+
console.log(chalk.green('ā
Authentication successful!'));
|
|
58
|
+
} else {
|
|
59
|
+
console.log(chalk.red('ā Authentication failed.'));
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
} catch (error) {
|
|
63
|
+
console.error(chalk.red('ā Authentication error:'), error);
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
program
|
|
69
|
+
.command('logout')
|
|
70
|
+
.description('Logout from Google Calendar')
|
|
71
|
+
.action(async () => {
|
|
72
|
+
await logoutGoogle();
|
|
73
|
+
console.log(chalk.green('ā
Logged out successfully!'));
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
program.parse();
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { google } from 'googleapis';
|
|
2
|
+
import { OAuth2Client } from 'google-auth-library';
|
|
3
|
+
import { readFileSync, existsSync } from 'fs';
|
|
4
|
+
import { join } from 'path';
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
import config from './configService.js';
|
|
7
|
+
import { getCodeServer } from './webServer.js';
|
|
8
|
+
|
|
9
|
+
const SCOPES = [
|
|
10
|
+
'https://www.googleapis.com/auth/calendar',
|
|
11
|
+
'https://www.googleapis.com/auth/calendar.events',
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
const CREDENTIALS_PATH = join(process.cwd(), 'credentials.json');
|
|
15
|
+
|
|
16
|
+
interface AuthTokens {
|
|
17
|
+
access_token: string;
|
|
18
|
+
refresh_token: string;
|
|
19
|
+
scope: string;
|
|
20
|
+
token_type: string;
|
|
21
|
+
expiry_date: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const getNewTokens = async (oAuth2Client: OAuth2Client): Promise<OAuth2Client | null> => {
|
|
25
|
+
try {
|
|
26
|
+
const authUrl = oAuth2Client.generateAuthUrl({
|
|
27
|
+
access_type: 'offline',
|
|
28
|
+
scope: SCOPES,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
console.log(chalk.cyan('š Authorize this app by visiting this url:'), chalk.blue(authUrl));
|
|
32
|
+
|
|
33
|
+
console.log(chalk.cyan('š Waiting for code...'));
|
|
34
|
+
|
|
35
|
+
const code = await getCodeServer();
|
|
36
|
+
|
|
37
|
+
const { tokens } = await oAuth2Client.getToken(code);
|
|
38
|
+
oAuth2Client.setCredentials(tokens);
|
|
39
|
+
|
|
40
|
+
// Store tokens
|
|
41
|
+
config.set('googleTokens', tokens);
|
|
42
|
+
|
|
43
|
+
console.log(chalk.green('ā
Authentication successful!'));
|
|
44
|
+
return oAuth2Client;
|
|
45
|
+
} catch (error) {
|
|
46
|
+
console.error(chalk.red('ā Error getting tokens:'), error);
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export const authenticateGoogle = async (): Promise<OAuth2Client | null> => {
|
|
52
|
+
try {
|
|
53
|
+
// Check if credentials file exists
|
|
54
|
+
if (!existsSync(CREDENTIALS_PATH)) {
|
|
55
|
+
console.log(chalk.yellow('ā ļø Google Calendar credentials not found.'));
|
|
56
|
+
console.log(chalk.cyan('š Please follow these steps:'));
|
|
57
|
+
console.log(chalk.cyan('1. Go to https://console.cloud.google.com'));
|
|
58
|
+
console.log(chalk.cyan('2. Create a new project or select existing one'));
|
|
59
|
+
console.log(chalk.cyan('3. Enable Google Calendar API'));
|
|
60
|
+
console.log(chalk.cyan('4. Create OAuth 2.0 credentials'));
|
|
61
|
+
console.log(chalk.cyan('5. Download credentials.json and place it in the project root'));
|
|
62
|
+
console.log(chalk.cyan('6. Run: meetfy auth'));
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const credentials = JSON.parse(readFileSync(CREDENTIALS_PATH, 'utf-8'));
|
|
67
|
+
const {
|
|
68
|
+
client_secret: clientSecret,
|
|
69
|
+
client_id: clientId,
|
|
70
|
+
redirect_uris: redirectUris,
|
|
71
|
+
} = credentials.installed || credentials.web;
|
|
72
|
+
|
|
73
|
+
const oAuth2Client = new google.auth.OAuth2(
|
|
74
|
+
clientId,
|
|
75
|
+
clientSecret,
|
|
76
|
+
redirectUris[0],
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
// Check if we have stored tokens
|
|
80
|
+
const storedTokens = config.get('googleTokens') as AuthTokens | undefined;
|
|
81
|
+
|
|
82
|
+
if (storedTokens) {
|
|
83
|
+
oAuth2Client.setCredentials(storedTokens);
|
|
84
|
+
|
|
85
|
+
// Check if token is expired
|
|
86
|
+
if (storedTokens.expiry_date && Date.now() > storedTokens.expiry_date) {
|
|
87
|
+
console.log(chalk.yellow('š Token expired, refreshing...'));
|
|
88
|
+
const { credentials: newTokens } = await oAuth2Client.refreshAccessToken();
|
|
89
|
+
oAuth2Client.setCredentials(newTokens);
|
|
90
|
+
config.set('googleTokens', newTokens);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return oAuth2Client;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Get new tokens
|
|
97
|
+
return await getNewTokens(oAuth2Client);
|
|
98
|
+
} catch (error) {
|
|
99
|
+
console.error(chalk.red('ā Authentication error:'), error);
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
export const logoutGoogle = async () => {
|
|
105
|
+
config.clear();
|
|
106
|
+
};
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { google } from 'googleapis';
|
|
2
|
+
import { OAuth2Client } from 'google-auth-library';
|
|
3
|
+
import inquirer from 'inquirer';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import ora from 'ora';
|
|
6
|
+
|
|
7
|
+
interface MeetingOptions {
|
|
8
|
+
title?: string;
|
|
9
|
+
description?: string;
|
|
10
|
+
participants?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface MeetingResult {
|
|
14
|
+
id: string;
|
|
15
|
+
hangoutLink: string;
|
|
16
|
+
startTime: string;
|
|
17
|
+
endTime: string;
|
|
18
|
+
title: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const getMeetingDetails = async (options: MeetingOptions) => {
|
|
22
|
+
const questions = [];
|
|
23
|
+
|
|
24
|
+
if (!options.title) {
|
|
25
|
+
questions.push({
|
|
26
|
+
type: 'input',
|
|
27
|
+
name: 'title',
|
|
28
|
+
message: 'Meeting title:',
|
|
29
|
+
default: 'Instant Meeting',
|
|
30
|
+
validate: (input: string) => {
|
|
31
|
+
if (!input.trim()) {
|
|
32
|
+
return 'Please enter a meeting title';
|
|
33
|
+
}
|
|
34
|
+
return true;
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (!options.description) {
|
|
40
|
+
questions.push({
|
|
41
|
+
type: 'input',
|
|
42
|
+
name: 'description',
|
|
43
|
+
message: 'Meeting description (optional):',
|
|
44
|
+
default: 'Instant meeting created via Meetfy CLI',
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (!options.participants) {
|
|
49
|
+
questions.push({
|
|
50
|
+
type: 'input',
|
|
51
|
+
name: 'participants',
|
|
52
|
+
message: 'Participant emails (comma-separated, optional):',
|
|
53
|
+
default: '',
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const answers = questions.length > 0 ? await inquirer.prompt(questions) : {};
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
title: options.title || answers.title,
|
|
61
|
+
description: options.description || answers.description,
|
|
62
|
+
participants: (options.participants || answers.participants || '')
|
|
63
|
+
.split(',')
|
|
64
|
+
.filter((email: string) => email.trim() !== ''),
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export const createInstantMeeting = async (
|
|
69
|
+
auth: OAuth2Client,
|
|
70
|
+
options: MeetingOptions,
|
|
71
|
+
): Promise<MeetingResult | null> => {
|
|
72
|
+
try {
|
|
73
|
+
const calendar = google.calendar({ version: 'v3', auth });
|
|
74
|
+
|
|
75
|
+
// Get meeting details from user if not provided
|
|
76
|
+
const meetingDetails = await getMeetingDetails(options);
|
|
77
|
+
|
|
78
|
+
// Calculate meeting times (30 minutes from now)
|
|
79
|
+
const now = new Date();
|
|
80
|
+
const startTime = new Date(now.getTime() + 5 * 60 * 1000); // Start in 5 minutes
|
|
81
|
+
const endTime = new Date(startTime.getTime() + 30 * 60 * 1000); // 30 minutes duration
|
|
82
|
+
|
|
83
|
+
const spinner = ora('Creating meeting and reserving calendar...').start();
|
|
84
|
+
|
|
85
|
+
// Create calendar event
|
|
86
|
+
const event = {
|
|
87
|
+
summary: meetingDetails.title,
|
|
88
|
+
description: meetingDetails.description,
|
|
89
|
+
start: {
|
|
90
|
+
dateTime: startTime.toISOString(),
|
|
91
|
+
timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
92
|
+
},
|
|
93
|
+
end: {
|
|
94
|
+
dateTime: endTime.toISOString(),
|
|
95
|
+
timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
96
|
+
},
|
|
97
|
+
attendees: meetingDetails.participants.map((email: string) => ({ email: email.trim() })),
|
|
98
|
+
conferenceData: {
|
|
99
|
+
createRequest: {
|
|
100
|
+
requestId: `meetfy-${Date.now()}`,
|
|
101
|
+
conferenceSolutionKey: {
|
|
102
|
+
type: 'hangoutsMeet',
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
reminders: {
|
|
107
|
+
useDefault: false,
|
|
108
|
+
overrides: [
|
|
109
|
+
{ method: 'email', minutes: 10 },
|
|
110
|
+
{ method: 'popup', minutes: 5 },
|
|
111
|
+
],
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const response = await calendar.events.insert({
|
|
116
|
+
calendarId: 'primary',
|
|
117
|
+
requestBody: event,
|
|
118
|
+
conferenceDataVersion: 1,
|
|
119
|
+
sendUpdates: 'all',
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
spinner.succeed('Meeting created successfully!');
|
|
123
|
+
|
|
124
|
+
if (!response.data.id || !response.data.hangoutLink) {
|
|
125
|
+
throw new Error('Failed to create meeting with Google Meet link');
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
id: response.data.id,
|
|
130
|
+
hangoutLink: response.data.hangoutLink,
|
|
131
|
+
startTime: startTime.toLocaleString(),
|
|
132
|
+
endTime: endTime.toLocaleString(),
|
|
133
|
+
title: meetingDetails.title,
|
|
134
|
+
};
|
|
135
|
+
} catch (error) {
|
|
136
|
+
console.error(chalk.red('ā Error creating meeting:'), error);
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import http from 'node:http';
|
|
2
|
+
import express, { Request, Response } from 'express';
|
|
3
|
+
import { PORT_NUMBER } from '../constants.js';
|
|
4
|
+
|
|
5
|
+
const server = express();
|
|
6
|
+
|
|
7
|
+
export const getCodeServer = async (): Promise<string> => new Promise<string>((resolve, reject) => {
|
|
8
|
+
let instance: http.Server;
|
|
9
|
+
|
|
10
|
+
server.get('/', (req: Request, res: Response) => {
|
|
11
|
+
console.log(req.query);
|
|
12
|
+
res.send(`
|
|
13
|
+
<html>
|
|
14
|
+
<body>
|
|
15
|
+
<h1>Meetfy</h1>
|
|
16
|
+
<p>You can close this window now.</p>
|
|
17
|
+
</body>
|
|
18
|
+
</html>
|
|
19
|
+
`);
|
|
20
|
+
|
|
21
|
+
const { code } = req.query;
|
|
22
|
+
if (code) {
|
|
23
|
+
instance.close();
|
|
24
|
+
resolve(code as string);
|
|
25
|
+
} else {
|
|
26
|
+
reject(new Error('No code found'));
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
instance = server.listen(PORT_NUMBER, (error) => {
|
|
31
|
+
if (error) {
|
|
32
|
+
console.error(error);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
|
|
3
|
+
export const showWelcomeMessage = (): void => {
|
|
4
|
+
console.log(chalk.cyan.bold('\nš Meetfy - Instant Meeting Creator'));
|
|
5
|
+
console.log(chalk.gray('Create instant meetings and reserve time in Google Calendar\n'));
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export const showHelp = (): void => {
|
|
9
|
+
console.log(chalk.cyan.bold('\nš Meetfy CLI Help'));
|
|
10
|
+
console.log(chalk.gray('\nCommands:'));
|
|
11
|
+
console.log(chalk.white(' create Create an instant meeting'));
|
|
12
|
+
console.log(chalk.white(' auth Authenticate with Google Calendar'));
|
|
13
|
+
console.log(chalk.white(' --help Show this help message'));
|
|
14
|
+
|
|
15
|
+
console.log(chalk.gray('\nExamples:'));
|
|
16
|
+
console.log(chalk.white(' meetfy create'));
|
|
17
|
+
console.log(chalk.white(' meetfy create -t "Team Standup" -d "Daily team sync"'));
|
|
18
|
+
console.log(chalk.white(' meetfy create -p "john@example.com,jane@example.com"'));
|
|
19
|
+
console.log(chalk.white(' meetfy auth'));
|
|
20
|
+
|
|
21
|
+
console.log(chalk.gray('\nOptions for create command:'));
|
|
22
|
+
console.log(chalk.white(' -t, --title <title> Meeting title'));
|
|
23
|
+
console.log(chalk.white(' -d, --description <desc> Meeting description'));
|
|
24
|
+
console.log(chalk.white(' -p, --participants <emails> Comma-separated participant emails'));
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const showSetupInstructions = (): void => {
|
|
28
|
+
console.log(chalk.yellow.bold('\nš§ Setup Instructions'));
|
|
29
|
+
console.log(chalk.gray('\n1. Google Calendar API Setup:'));
|
|
30
|
+
console.log(chalk.white(' ⢠Go to https://console.cloud.google.com'));
|
|
31
|
+
console.log(chalk.white(' ⢠Create a new project or select existing one'));
|
|
32
|
+
console.log(chalk.white(' ⢠Enable Google Calendar API'));
|
|
33
|
+
console.log(chalk.white(' ⢠Create OAuth 2.0 credentials'));
|
|
34
|
+
console.log(chalk.white(' ⢠Download credentials.json to project root'));
|
|
35
|
+
|
|
36
|
+
console.log(chalk.gray('\n2. Authentication:'));
|
|
37
|
+
console.log(chalk.white(' ⢠Run: meetfy auth'));
|
|
38
|
+
console.log(chalk.white(' ⢠Follow the authentication flow'));
|
|
39
|
+
|
|
40
|
+
console.log(chalk.gray('\n3. Create Meetings:'));
|
|
41
|
+
console.log(chalk.white(' ⢠Run: meetfy create'));
|
|
42
|
+
console.log(chalk.white(' ⢠Or with options: meetfy create -t "Meeting Title"'));
|
|
43
|
+
};
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "Node16",
|
|
5
|
+
"moduleResolution": "node16",
|
|
6
|
+
"outDir": "./dist",
|
|
7
|
+
"rootDir": "./src",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"allowSyntheticDefaultImports": true,
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"forceConsistentCasingInFileNames": true,
|
|
13
|
+
"declaration": true,
|
|
14
|
+
"declarationMap": true,
|
|
15
|
+
"sourceMap": true,
|
|
16
|
+
"resolveJsonModule": true
|
|
17
|
+
},
|
|
18
|
+
"include": ["src/**/*"],
|
|
19
|
+
"exclude": ["node_modules", "dist"]
|
|
20
|
+
}
|