Google Workspace 연결하기
Google Apps Script로 Gmail, 캘린더, Drive 등을 텔레그램에서 자동화하세요
연결하면 할 수 있는 것
Gmail 검색/발송/답장/임시저장
캘린더 일정 추가/수정/삭제/검색
Google Drive 파일 업로드/이동/공유/복사
Sheets 읽기/쓰기/행 삭제/검색/탭 추가
Docs 문서 작성/읽기/텍스트 치환
Forms 설문 생성/응답 조회
Tasks 할 일 추가/완료/삭제
Apps Script 프로젝트 만들기
drive.google.com에 접속 → + 새로 만들기 → 더보기 → Google Apps Script를 선택하세요. 프로젝트 이름을 "MyBotAutomation"으로 변경합니다.

코드 붙여넣기
코드.gs 파일에 아래 코드를 전체 복사해서 붙여넣으세요.
// ==========================================
// Google Workspace 통합 API (Full Edition)
// Sheets, Calendar, Gmail, Drive, Docs, Forms, Tasks
// ==========================================
function doPost(e) {
try {
var data = JSON.parse(e.postData.contents);
return handleRequest(data);
} catch(err) {
return jsonResponse({status: 'error', message: err.toString()});
}
}
function doGet(e) {
try {
if (e.parameter.action) {
var data = {};
for (var key in e.parameter) { data[key] = e.parameter[key]; }
if (e.parameter.values) data.values = JSON.parse(e.parameter.values);
if (e.parameter.headers) data.headers = JSON.parse(e.parameter.headers);
if (e.parameter.questions) data.questions = JSON.parse(e.parameter.questions);
if (e.parameter.guests) data.guests = JSON.parse(e.parameter.guests);
if (e.parameter.choices) data.choices = JSON.parse(e.parameter.choices);
return handleRequest(data);
}
return jsonResponse({
status: 'ok',
message: 'Google Workspace API is running!',
services: ['sheets', 'calendar', 'gmail', 'drive', 'docs', 'forms', 'tasks'],
time: new Date().toISOString()
});
} catch(err) {
return jsonResponse({status: 'error', message: err.toString()});
}
}
function handleRequest(data) {
var action = data.action;
switch(action) {
// GOOGLE SHEETS
case 'sheet_append': return handleSheetAppend(data);
case 'sheet_read': return handleSheetRead(data);
case 'sheet_update': return handleSheetUpdate(data);
case 'sheet_create': return handleSheetCreate(data);
case 'sheet_delete_row': return handleSheetDeleteRow(data);
case 'sheet_clear': return handleSheetClear(data);
case 'sheet_get_names': return handleSheetGetNames(data);
case 'sheet_add_tab': return handleSheetAddTab(data);
case 'sheet_find': return handleSheetFind(data);
// GOOGLE CALENDAR
case 'calendar_add': return handleCalendarAdd(data);
case 'calendar_list': return handleCalendarList(data);
case 'calendar_delete': return handleCalendarDelete(data);
case 'calendar_bulk_delete': return handleCalendarBulkDelete(data);
case 'calendar_update': return handleCalendarUpdate(data);
case 'calendar_allday': return handleCalendarAllDay(data);
case 'calendar_search': return handleCalendarSearch(data);
// GMAIL
case 'gmail_send': return handleGmailSend(data);
case 'gmail_read': return handleGmailRead(data);
case 'gmail_search': return handleGmailSearch(data);
case 'gmail_trash': return handleGmailTrash(data);
case 'gmail_label': return handleGmailLabel(data);
case 'gmail_mark_read': return handleGmailMarkRead(data);
case 'gmail_draft': return handleGmailDraft(data);
case 'gmail_reply': return handleGmailReply(data);
// GOOGLE DRIVE
case 'drive_upload': return handleDriveUpload(data);
case 'drive_list': return handleDriveList(data);
case 'drive_share': return handleDriveShare(data);
case 'drive_delete': return handleDriveDelete(data);
case 'drive_create_folder': return handleDriveCreateFolder(data);
case 'drive_move': return handleDriveMove(data);
case 'drive_rename': return handleDriveRename(data);
case 'drive_get_info': return handleDriveGetInfo(data);
case 'drive_copy': return handleDriveCopy(data);
// GOOGLE DOCS
case 'doc_create': return handleDocCreate(data);
case 'doc_append': return handleDocAppend(data);
case 'doc_read': return handleDocRead(data);
case 'doc_replace': return handleDocReplace(data);
// GOOGLE FORMS
case 'form_create': return handleFormCreate(data);
case 'form_responses': return handleFormResponses(data);
// GOOGLE TASKS
case 'tasks_list': return handleTasksList(data);
case 'tasks_add': return handleTasksAdd(data);
case 'tasks_complete': return handleTasksComplete(data);
case 'tasks_delete': return handleTasksDelete(data);
default:
return jsonResponse({status: 'error', message: 'Unknown action: ' + action});
}
}
// ==========================================
// GOOGLE SHEETS
// ==========================================
function handleSheetAppend(data) {
var sheet = SpreadsheetApp.openById(data.sheetId).getSheetByName(data.sheetName || 'Sheet1');
sheet.appendRow(data.values);
return jsonResponse({status: 'success', message: 'Row appended'});
}
function handleSheetRead(data) {
var sheet = SpreadsheetApp.openById(data.sheetId).getSheetByName(data.sheetName || 'Sheet1');
var range = sheet.getRange(data.range || 'A1:D10');
return jsonResponse({status: 'success', data: range.getValues()});
}
function handleSheetUpdate(data) {
var sheet = SpreadsheetApp.openById(data.sheetId).getSheetByName(data.sheetName || 'Sheet1');
if (data.values) {
sheet.getRange(data.range).setValues(data.values);
} else {
sheet.getRange(data.range).setValue(data.value);
}
return jsonResponse({status: 'success', message: 'Cell updated'});
}
function handleSheetCreate(data) {
var ss = SpreadsheetApp.create(data.title || 'New Spreadsheet');
if (data.headers) {
ss.getActiveSheet().appendRow(data.headers);
}
return jsonResponse({status: 'success', sheetId: ss.getId(), url: ss.getUrl()});
}
function handleSheetDeleteRow(data) {
var sheet = SpreadsheetApp.openById(data.sheetId).getSheetByName(data.sheetName || 'Sheet1');
sheet.deleteRow(data.row);
return jsonResponse({status: 'success', message: 'Row ' + data.row + ' deleted'});
}
function handleSheetClear(data) {
var sheet = SpreadsheetApp.openById(data.sheetId).getSheetByName(data.sheetName || 'Sheet1');
if (data.range) {
sheet.getRange(data.range).clear();
} else {
sheet.clear();
}
return jsonResponse({status: 'success', message: 'Cleared'});
}
function handleSheetGetNames(data) {
var ss = SpreadsheetApp.openById(data.sheetId);
var names = ss.getSheets().map(function(s) { return s.getName(); });
return jsonResponse({status: 'success', sheets: names});
}
function handleSheetAddTab(data) {
var ss = SpreadsheetApp.openById(data.sheetId);
var newSheet = ss.insertSheet(data.tabName);
if (data.headers) newSheet.appendRow(data.headers);
return jsonResponse({status: 'success', message: 'Tab "' + data.tabName + '" created'});
}
function handleSheetFind(data) {
var sheet = SpreadsheetApp.openById(data.sheetId).getSheetByName(data.sheetName || 'Sheet1');
var allData = sheet.getDataRange().getValues();
var results = [];
var query = (data.query || '').toLowerCase();
for (var i = 0; i < allData.length; i++) {
for (var j = 0; j < allData[i].length; j++) {
if (String(allData[i][j]).toLowerCase().indexOf(query) !== -1) {
results.push({row: i + 1, col: j + 1, value: allData[i][j], rowData: allData[i]});
}
}
}
return jsonResponse({status: 'success', results: results});
}
// ==========================================
// GOOGLE CALENDAR
// ==========================================
function handleCalendarAdd(data) {
var calendar = CalendarApp.getDefaultCalendar();
var event = calendar.createEvent(
data.title,
new Date(data.startTime),
new Date(data.endTime),
{description: data.description || '', location: data.location || ''}
);
if (data.guests) {
data.guests.forEach(function(g) { event.addGuest(g); });
}
return jsonResponse({status: 'success', eventId: event.getId()});
}
function handleCalendarList(data) {
var calendar = CalendarApp.getDefaultCalendar();
var start = new Date(data.startDate || new Date());
var end = new Date(data.endDate || new Date(start.getTime() + 7*24*60*60*1000));
var events = calendar.getEvents(start, end);
var result = events.map(function(ev) {
return {
title: ev.getTitle(), start: ev.getStartTime().toISOString(),
end: ev.getEndTime().toISOString(), description: ev.getDescription(),
location: ev.getLocation(), id: ev.getId(), isAllDay: ev.isAllDayEvent()
};
});
return jsonResponse({status: 'success', events: result});
}
function handleCalendarDelete(data) {
var event = CalendarApp.getDefaultCalendar().getEventById(data.eventId);
if (!event) return jsonResponse({status: 'error', message: 'Event not found'});
event.deleteEvent();
return jsonResponse({status: 'success', message: 'Event deleted'});
}
function handleCalendarBulkDelete(data) {
var calendar = CalendarApp.getDefaultCalendar();
var start = new Date(data.startDate || '2020-01-01');
var end = new Date(data.endDate || '2030-01-01');
var query = (data.query || '').toLowerCase();
var events = calendar.getEvents(start, end, query ? {search: query} : {});
var deleted = 0;
events.forEach(function(event) {
if (!query || event.getTitle().toLowerCase().indexOf(query) !== -1) {
event.deleteEvent();
deleted++;
}
});
return jsonResponse({status: 'success', deleted: deleted});
}
function handleCalendarUpdate(data) {
var event = CalendarApp.getDefaultCalendar().getEventById(data.eventId);
if (!event) return jsonResponse({status: 'error', message: 'Event not found'});
if (data.title) event.setTitle(data.title);
if (data.description) event.setDescription(data.description);
if (data.location) event.setLocation(data.location);
if (data.startTime && data.endTime) event.setTime(new Date(data.startTime), new Date(data.endTime));
return jsonResponse({status: 'success', message: 'Event updated'});
}
function handleCalendarAllDay(data) {
var event = CalendarApp.getDefaultCalendar().createAllDayEvent(
data.title, new Date(data.date),
{description: data.description || '', location: data.location || ''}
);
return jsonResponse({status: 'success', eventId: event.getId()});
}
function handleCalendarSearch(data) {
var calendar = CalendarApp.getDefaultCalendar();
var start = new Date(data.startDate || new Date());
var end = new Date(data.endDate || new Date(start.getTime() + 30*24*60*60*1000));
var events = calendar.getEvents(start, end, {search: data.query});
var result = events.map(function(ev) {
return {title: ev.getTitle(), start: ev.getStartTime().toISOString(), id: ev.getId()};
});
return jsonResponse({status: 'success', events: result});
}
// ==========================================
// GMAIL
// ==========================================
function handleGmailSend(data) {
GmailApp.sendEmail(data.to, data.subject, data.body, {
htmlBody: data.htmlBody || null, cc: data.cc || '', bcc: data.bcc || '',
name: data.senderName || '', replyTo: data.replyTo || ''
});
return jsonResponse({status: 'success', message: 'Email sent'});
}
function handleGmailRead(data) {
var threads = GmailApp.getInboxThreads(0, data.count || 10);
var result = threads.map(function(thread) {
var msgs = thread.getMessages();
var last = msgs[msgs.length - 1];
return {
threadId: thread.getId(), subject: last.getSubject(),
from: last.getFrom(), to: last.getTo(),
date: last.getDate().toISOString(), body: (last.getPlainBody() || last.getBody() || '').substring(0, 500),
isUnread: thread.isUnread(), messageCount: msgs.length
};
});
return jsonResponse({status: 'success', emails: result});
}
function handleGmailSearch(data) {
var threads = GmailApp.search(data.query, 0, data.count || 10);
var result = threads.map(function(thread) {
var msg = thread.getMessages()[0];
return {
threadId: thread.getId(), subject: msg.getSubject(),
from: msg.getFrom(), date: msg.getDate().toISOString(),
snippet: (msg.getPlainBody() || msg.getBody() || '').substring(0, 300), isUnread: thread.isUnread()
};
});
return jsonResponse({status: 'success', emails: result});
}
function handleGmailTrash(data) {
var thread = GmailApp.getThreadById(data.threadId);
thread.moveToTrash();
return jsonResponse({status: 'success', message: 'Moved to trash'});
}
function handleGmailLabel(data) {
var thread = GmailApp.getThreadById(data.threadId);
var label = GmailApp.getUserLabelByName(data.label) || GmailApp.createLabel(data.label);
if (data.remove) {
thread.removeLabel(label);
} else {
thread.addLabel(label);
}
return jsonResponse({status: 'success', message: 'Label updated'});
}
function handleGmailMarkRead(data) {
var thread = GmailApp.getThreadById(data.threadId);
if (data.unread) { thread.markUnread(); } else { thread.markRead(); }
return jsonResponse({status: 'success', message: 'Read status updated'});
}
function handleGmailDraft(data) {
GmailApp.createDraft(data.to, data.subject, data.body, {
htmlBody: data.htmlBody || null, cc: data.cc || ''
});
return jsonResponse({status: 'success', message: 'Draft created'});
}
function handleGmailReply(data) {
var thread = GmailApp.getThreadById(data.threadId);
var msgs = thread.getMessages();
var lastMsg = msgs[msgs.length - 1];
lastMsg.reply(data.body, {htmlBody: data.htmlBody || null});
return jsonResponse({status: 'success', message: 'Reply sent'});
}
// ==========================================
// GOOGLE DRIVE
// ==========================================
function handleDriveUpload(data) {
var blob = Utilities.newBlob(
Utilities.base64Decode(data.content), data.mimeType || 'text/plain', data.fileName
);
var file = DriveApp.createFile(blob);
if (data.folderId) {
DriveApp.getFolderById(data.folderId).addFile(file);
DriveApp.getRootFolder().removeFile(file);
}
return jsonResponse({status: 'success', fileId: file.getId(), url: file.getUrl()});
}
function handleDriveList(data) {
var items = [];
if (data.folderId) {
var folder = DriveApp.getFolderById(data.folderId);
var files = folder.getFiles();
while (files.hasNext() && items.length < (data.count || 20)) items.push(files.next());
var folders = folder.getFolders();
while (folders.hasNext() && items.length < (data.count || 20)) items.push(folders.next());
} else if (data.query) {
var it = DriveApp.searchFiles(data.query);
while (it.hasNext() && items.length < (data.count || 20)) items.push(it.next());
} else {
var all = DriveApp.getFiles();
while (all.hasNext() && items.length < (data.count || 20)) items.push(all.next());
}
var output = items.map(function(f) {
return {name: f.getName(), id: f.getId(), url: f.getUrl(), mimeType: f.getMimeType(), size: f.getSize()};
});
return jsonResponse({status: 'success', files: output});
}
function handleDriveShare(data) {
var file = DriveApp.getFileById(data.fileId);
if (data.role === 'viewer') { file.addViewer(data.email); }
else { file.addEditor(data.email); }
return jsonResponse({status: 'success', message: 'Shared with ' + data.email});
}
function handleDriveDelete(data) {
DriveApp.getFileById(data.fileId).setTrashed(true);
return jsonResponse({status: 'success', message: 'File moved to trash'});
}
function handleDriveCreateFolder(data) {
var parent = data.parentId ? DriveApp.getFolderById(data.parentId) : DriveApp.getRootFolder();
var folder = parent.createFolder(data.name);
return jsonResponse({status: 'success', folderId: folder.getId(), url: folder.getUrl()});
}
function handleDriveMove(data) {
var file = DriveApp.getFileById(data.fileId);
var dest = DriveApp.getFolderById(data.folderId);
var parents = file.getParents();
if (parents.hasNext()) parents.next().removeFile(file);
dest.addFile(file);
return jsonResponse({status: 'success', message: 'File moved'});
}
function handleDriveRename(data) {
DriveApp.getFileById(data.fileId).setName(data.newName);
return jsonResponse({status: 'success', message: 'Renamed to ' + data.newName});
}
function handleDriveGetInfo(data) {
var file = DriveApp.getFileById(data.fileId);
return jsonResponse({
status: 'success',
name: file.getName(), mimeType: file.getMimeType(), size: file.getSize(),
url: file.getUrl(), created: file.getDateCreated().toISOString(),
updated: file.getLastUpdated().toISOString(),
owner: file.getOwner() ? file.getOwner().getEmail() : 'unknown',
sharingAccess: file.getSharingAccess().toString()
});
}
function handleDriveCopy(data) {
var file = DriveApp.getFileById(data.fileId);
var copy = file.makeCopy(data.newName || file.getName());
if (data.folderId) {
DriveApp.getFolderById(data.folderId).addFile(copy);
DriveApp.getRootFolder().removeFile(copy);
}
return jsonResponse({status: 'success', fileId: copy.getId(), url: copy.getUrl()});
}
// ==========================================
// GOOGLE DOCS
// ==========================================
function handleDocCreate(data) {
var doc = DocumentApp.create(data.title || 'New Document');
if (data.content) doc.getBody().appendParagraph(data.content);
return jsonResponse({status: 'success', docId: doc.getId(), url: doc.getUrl()});
}
function handleDocAppend(data) {
var doc = DocumentApp.openById(data.docId);
doc.getBody().appendParagraph(data.content);
doc.saveAndClose();
return jsonResponse({status: 'success', message: 'Content appended'});
}
function handleDocRead(data) {
var doc = DocumentApp.openById(data.docId);
var text = doc.getBody().getText();
return jsonResponse({status: 'success', title: doc.getName(), content: text});
}
function handleDocReplace(data) {
var doc = DocumentApp.openById(data.docId);
doc.getBody().replaceText(data.searchPattern, data.replacement);
doc.saveAndClose();
return jsonResponse({status: 'success', message: 'Text replaced'});
}
// ==========================================
// GOOGLE FORMS
// ==========================================
function handleFormCreate(data) {
var form = FormApp.create(data.title || 'New Form');
if (data.description) form.setDescription(data.description);
if (data.questions) {
data.questions.forEach(function(q) {
switch(q.type) {
case 'text':
form.addTextItem().setTitle(q.title).setRequired(q.required || false); break;
case 'paragraph':
form.addParagraphTextItem().setTitle(q.title).setRequired(q.required || false); break;
case 'multiple':
var mc = form.addMultipleChoiceItem().setTitle(q.title);
mc.setChoiceValues(q.choices); mc.setRequired(q.required || false); break;
case 'checkbox':
var cb = form.addCheckboxItem().setTitle(q.title);
cb.setChoiceValues(q.choices); cb.setRequired(q.required || false); break;
case 'dropdown':
var dd = form.addListItem().setTitle(q.title);
dd.setChoiceValues(q.choices); dd.setRequired(q.required || false); break;
case 'scale':
form.addScaleItem().setTitle(q.title)
.setBounds(q.low || 1, q.high || 5)
.setRequired(q.required || false); break;
case 'date':
form.addDateItem().setTitle(q.title).setRequired(q.required || false); break;
case 'time':
form.addTimeItem().setTitle(q.title).setRequired(q.required || false); break;
}
});
}
return jsonResponse({
status: 'success', formId: form.getId(),
editUrl: form.getEditUrl(), publishedUrl: form.getPublishedUrl()
});
}
function handleFormResponses(data) {
var form = FormApp.openById(data.formId);
var responses = form.getResponses();
var result = responses.slice(-(data.count || 50)).map(function(r) {
var answers = r.getItemResponses().map(function(ir) {
return {question: ir.getItem().getTitle(), answer: ir.getResponse()};
});
return {timestamp: r.getTimestamp().toISOString(), answers: answers};
});
return jsonResponse({status: 'success', count: responses.length, responses: result});
}
// ==========================================
// GOOGLE TASKS
// ==========================================
function checkTasksEnabled() {
if (typeof Tasks === 'undefined') {
throw new Error('Tasks API not enabled. Add it via Services (+) in the sidebar.');
}
}
function handleTasksList(data) {
checkTasksEnabled();
var taskLists = Tasks.Tasklists.list();
if (data.listId) {
var tasks = Tasks.Tasks.list(data.listId, {showCompleted: data.showCompleted || false});
return jsonResponse({status: 'success', tasks: tasks.items || []});
}
var lists = (taskLists.items || []).map(function(l) {
return {id: l.id, title: l.title};
});
return jsonResponse({status: 'success', taskLists: lists});
}
function handleTasksAdd(data) {
checkTasksEnabled();
var listId = data.listId || Tasks.Tasklists.list().items[0].id;
var task = Tasks.Tasks.insert({
title: data.title,
notes: data.notes || '',
due: data.due || null
}, listId);
return jsonResponse({status: 'success', taskId: task.id, title: task.title});
}
function handleTasksComplete(data) {
checkTasksEnabled();
var listId = data.listId || Tasks.Tasklists.list().items[0].id;
Tasks.Tasks.patch({status: 'completed'}, listId, data.taskId);
return jsonResponse({status: 'success', message: 'Task completed'});
}
function handleTasksDelete(data) {
checkTasksEnabled();
var listId = data.listId || Tasks.Tasklists.list().items[0].id;
Tasks.Tasks.remove(listId, data.taskId);
return jsonResponse({status: 'success', message: 'Task deleted'});
}
// ==========================================
// Utility
// ==========================================
function jsonResponse(data) {
return ContentService
.createTextOutput(JSON.stringify(data))
.setMimeType(ContentService.MimeType.JSON);
}
// ==========================================
// Run this once from the editor to grant all permissions
// ==========================================
function authorize() {
CalendarApp.getDefaultCalendar();
GmailApp.getInboxThreads(0, 1);
DriveApp.getFiles();
var doc = DocumentApp.create('_auth_test');
DriveApp.getFileById(doc.getId()).setTrashed(true);
var form = FormApp.create('_auth_test');
DriveApp.getFileById(form.getId()).setTrashed(true);
Logger.log('All permissions granted!');
}권한 승인하기
상단 드롭다운에서 authorize를 선택하고 실행(▶)을 클릭하세요. 권한 팝업이 여러 번 나올 수 있습니다. 매번 "고급" → "안전하지 않은 페이지로 이동"을 클릭해 허용하고, 팝업이 더 이상 안 나올 때까지 다시 실행하세요.

배포하기 (Deploy)
오른쪽 상단의 배포 버튼 → 새 배포를 클릭하세요. 종류는 "웹 앱", 실행 사용자는 "나", 액세스 권한은 "모든 사용자"로 설정하고 배포를 클릭하면 URL이 생성됩니다.

SupaClaw에 연결하기
텔레그램에서 여러분의 봇을 열고, 생성된 URL을 보내면서 "구글 워크스페이스 설정해줘"라고 요청하세요. SupaClaw가 자동으로 연결합니다.

이런 걸 할 수 있어요
"오늘 온 중요 메일 요약해줘"
"내일 오후 3시에 팀 미팅 잡아줘"
"Drive에서 '분기 보고서' 파일 찾아줘"
"매주 월요일 아침에 이번 주 일정 알려줘"
"새 스프레드시트 만들어서 매출 데이터 기록해줘"
꿀팁
배포 후 처음 실행할 때 권한 승인 팝업이 나옵니다. "고급" → "안전하지 않은 페이지로 이동"을 클릭해 권한을 허용하세요. Tasks(할 일) 기능을 사용하려면 Apps Script 에디터 왼쪽의 서비스(+) → Tasks API를 추가하세요.