Build a full-stack AI chatbot that connects to Google Calendar, enabling natural language event creation, querying, and management.
Build a full-stack AI assistant that lets users interact with Google Calendar through natural language. Create events, retrieve past appointments, and answer questions about calendar data using AI + Google Calendar API.
This skill guides you through building a Smart Calendar Chatbot Web App that allows users to:
Create the project structure with frontend and backend:
```bash
mkdir calendar-chatbot && cd calendar-chatbot
mkdir -p client/components client/pages
mkdir -p server/routes server/utils
```
Initialize both frontend and backend:
```bash
cd client
npm create vite@latest . -- --template react
npm install @react-oauth/google tailwindcss react-chat-ui
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
cd ../server
npm init -y
npm install express cors dotenv googleapis openai express-session jsonwebtoken
```
1. Go to [Google Cloud Console](https://console.cloud.google.com/)
2. Create a new project or select existing
3. Enable **Google Calendar API**
4. Create OAuth 2.0 credentials (Web application)
5. Add authorized redirect URIs:
- `http://localhost:3000/auth/callback` (development)
6. Note your `CLIENT_ID` and `CLIENT_SECRET`
Create `.env` in server directory:
```env
GOOGLE_CLIENT_ID=your_client_id
GOOGLE_CLIENT_SECRET=your_client_secret
GOOGLE_REDIRECT_URI=http://localhost:3000/auth/callback
OPENAI_API_KEY=your_openai_api_key
SESSION_SECRET=your_random_secret
PORT=3000
```
Create `server/routes/auth.js`:
```javascript
const express = require('express');
const { google } = require('googleapis');
const router = express.Router();
const oauth2Client = new google.auth.OAuth2(
process.env.GOOGLE_CLIENT_ID,
process.env.GOOGLE_CLIENT_SECRET,
process.env.GOOGLE_REDIRECT_URI
);
// Generate auth URL
router.get('/google', (req, res) => {
const url = oauth2Client.generateAuthUrl({
access_type: 'offline',
scope: ['https://www.googleapis.com/auth/calendar']
});
res.json({ url });
});
// Handle OAuth callback
router.get('/callback', async (req, res) => {
const { code } = req.query;
const { tokens } = await oauth2Client.getToken(code);
req.session.tokens = tokens;
res.redirect('http://localhost:5173/dashboard');
});
module.exports = router;
```
Create `server/routes/calendar.js`:
```javascript
const express = require('express');
const { google } = require('googleapis');
const router = express.Router();
router.post('/create', async (req, res) => {
const { title, start, end, recurrence } = req.body;
const oauth2Client = new google.auth.OAuth2();
oauth2Client.setCredentials(req.session.tokens);
const calendar = google.calendar({ version: 'v3', auth: oauth2Client });
const event = {
summary: title,
start: { dateTime: start },
end: { dateTime: end },
recurrence: recurrence ? [recurrence] : undefined
};
const response = await calendar.events.insert({
calendarId: 'primary',
resource: event
});
res.json({ success: true, event: response.data });
});
router.get('/past', async (req, res) => {
const oauth2Client = new google.auth.OAuth2();
oauth2Client.setCredentials(req.session.tokens);
const calendar = google.calendar({ version: 'v3', auth: oauth2Client });
const response = await calendar.events.list({
calendarId: 'primary',
timeMax: new Date().toISOString(),
maxResults: 100,
orderBy: 'startTime',
singleEvents: true
});
res.json({ events: response.data.items });
});
module.exports = router;
```
Create `server/routes/chatbot.js`:
```javascript
const express = require('express');
const OpenAI = require('openai');
const router = express.Router();
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
router.post('/message', async (req, res) => {
const { message, calendarEvents } = req.body;
// Build RAG context from calendar events
const context = calendarEvents?.map(e =>
`- ${e.summary} - ${e.start.dateTime || e.start.date}`
).join('\n') || 'No calendar events available.';
const prompt = `
You are a calendar assistant. Answer the user's question based on their calendar data.
Calendar Context:
${context}
User Question: "${message}"
Provide a helpful, concise answer. If creating an event, respond with JSON:
{
"action": "create",
"title": "...",
"start": "ISO 8601 datetime",
"end": "ISO 8601 datetime",
"recurrence": "RRULE:..." (optional)
}
`;
const completion = await openai.chat.completions.create({
model: 'gpt-4',
messages: [{ role: 'user', content: prompt }],
temperature: 0.7
});
const response = completion.choices[0].message.content;
// Try parsing as JSON for event creation
try {
const parsed = JSON.parse(response);
if (parsed.action === 'create') {
return res.json({ type: 'create', data: parsed });
}
} catch (e) {
// Not JSON, return as text
}
res.json({ type: 'message', response });
});
module.exports = router;
```
Create `server/server.js`:
```javascript
const express = require('express');
const cors = require('cors');
const session = require('express-session');
require('dotenv').config();
const authRoutes = require('./routes/auth');
const calendarRoutes = require('./routes/calendar');
const chatbotRoutes = require('./routes/chatbot');
const app = express();
app.use(cors({ origin: 'http://localhost:5173', credentials: true }));
app.use(express.json());
app.use(session({
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
cookie: { secure: false }
}));
app.use('/auth', authRoutes);
app.use('/api/calendar', calendarRoutes);
app.use('/api/chatbot', chatbotRoutes);
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
```
Create `client/src/components/ChatInterface.jsx`:
```jsx
import { useState, useEffect } from 'react';
export default function ChatInterface() {
const [messages, setMessages] = useState([]);
const [input, setInput] = useState('');
const [events, setEvents] = useState([]);
useEffect(() => {
// Fetch past events for RAG context
fetch('http://localhost:3000/api/calendar/past', { credentials: 'include' })
.then(r => r.json())
.then(data => setEvents(data.events));
}, []);
const sendMessage = async () => {
const userMessage = { sender: 'user', text: input };
setMessages([...messages, userMessage]);
const response = await fetch('http://localhost:3000/api/chatbot/message', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({ message: input, calendarEvents: events })
});
const data = await response.json();
if (data.type === 'create') {
// Create event
await fetch('http://localhost:3000/api/calendar/create', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify(data.data)
});
setMessages([...messages, userMessage, { sender: 'bot', text: 'Event created!' }]);
} else {
setMessages([...messages, userMessage, { sender: 'bot', text: data.response }]);
}
setInput('');
};
return (
<div className="flex flex-col h-screen max-w-2xl mx-auto p-4">
<div className="flex-1 overflow-y-auto space-y-4">
{messages.map((msg, i) => (
<div key={i} className={`p-3 rounded ${msg.sender === 'user' ? 'bg-blue-100 ml-auto' : 'bg-gray-100'} max-w-[80%]`}>
{msg.text}
</div>
))}
</div>
<div className="flex gap-2 mt-4">
<input
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyPress={(e) => e.key === 'Enter' && sendMessage()}
placeholder="Ask about your calendar..."
className="flex-1 border rounded px-4 py-2"
/>
<button onClick={sendMessage} className="bg-blue-500 text-white px-6 py-2 rounded">
Send
</button>
</div>
</div>
);
}
```
**Test Cases:**
| Scenario | Input | Expected Outcome |
|----------|-------|------------------|
| Create event | "Schedule gym at 6 PM Friday" | Event created with correct time |
| Query next event | "What's next on my calendar?" | Returns upcoming event |
| Past query | "When was my last dentist visit?" | Returns last event matching "dentist" |
| No match | "When did I go to Mars?" | Returns "No match found" message |
**Run the app:**
```bash
cd server && node server.js
cd client && npm run dev
```
Visit `http://localhost:5173`, authenticate with Google, and start chatting with your calendar!
**Creating a recurring event:**
```
User: "Create a daily standup meeting at 9 AM starting tomorrow"
Bot: "Event created! Daily standup will occur every day at 9:00 AM."
```
**Querying past events:**
```
User: "When was my last dentist appointment?"
Bot: "Your last dentist appointment was on March 1, 2025 at 3:00 PM."
```
**Checking upcoming events:**
```
User: "What's on my calendar this week?"
Bot: "You have 5 events this week: Team meeting (Mon 2PM), Dentist (Wed 3PM)..."
```
Leave a review
No reviews yet. Be the first to review this skill!
# Download SKILL.md from killerskills.ai/api/skills/smart-calendar-chatbot-integration/raw