Code:

var ACCESS_TOKEN = "Your acces token here"; var OPEN_SHEET_ID = "Your sheet id here"; var SCRIPT_PASSOWRD = "Your google apps script password here"; var CLIENT_NAME = "Your 3ds's account name here"; var gas_ver = 6;//Do **NOT** edit this value. function log_save(message, user_name, write_sheet_name, group_or_user_name, pic_url) { var google_pic_url; var previous_pic_url; var response; var sheet_pos = 1; var spreadsheet = SpreadsheetApp.openById(OPEN_SHEET_ID); var write_sheet = spreadsheet.getSheetByName(write_sheet_name); if (!write_sheet) { spreadsheet.insertSheet(write_sheet_name); write_sheet = spreadsheet.getSheetByName(write_sheet_name); } sheet_pos = get_cache_pos(write_sheet); while (true) { var sheet_data = write_sheet.getRange("A" + sheet_pos).getValue(); if (sheet_data == "") { write_sheet.getRange("A" + sheet_pos).setValue("" + user_name + " : " + message); break; } else sheet_pos++; } write_cache_data(write_sheet, (sheet_pos + 1), "B1"); if (group_or_user_name != "Do not save") write_cache_data(write_sheet, group_or_user_name, "B2"); if (pic_url != "Do not save") { previous_pic_url = get_previous_pic_url(write_sheet); if (previous_pic_url != pic_url) { response = UrlFetchApp.fetch(pic_url, { 'headers': { 'Content-Type': 'application/json; charset=UTF-8', 'Authorization': 'Bearer ' + ACCESS_TOKEN, }, muteHttpExceptions: true, }); if (response.getResponseCode() == 200) { google_pic_url = get_profile_pic_google_url(response, write_sheet_name); write_cache_data(write_sheet, google_pic_url, "B3"); write_cache_data(write_sheet, pic_url, "B4"); } } } } function check_folder_exist(input_folders, name) { var folder; while (input_folders.hasNext()) { folder = input_folders.next(); if(name == folder.getName()) return folder; } return undefined; } function log_read(id, num_of_logs) { var sheet_offset = 1; var sheet_start = 1; var return_data; var sheet_data; var spreadsheet = SpreadsheetApp.openById(OPEN_SHEET_ID); var read_sheet = spreadsheet.getSheetByName(id); if(!read_sheet) return "No such sheet.

Please duoble check your ID."; sheet_start = get_cache_pos(read_sheet); while(true) { sheet_data = read_sheet.getRange("A" + sheet_start).getValue(); if(sheet_data == "") { write_cache_data(read_sheet, sheet_start, "B1"); if(sheet_start > num_of_logs) sheet_start = (sheet_start - num_of_logs); else sheet_start = 1; break; } else sheet_start++; } sheet_offset = sheet_start; sheet_data = read_sheet.getRange(sheet_start, 1, num_of_logs).getValues(); return_data = "<0>"; for(var i = 0; i < num_of_logs; i++) { if(sheet_data[i] == "") break; return_data += sheet_data[i]; sheet_offset++; } return_data += "</0>"; return_data += "<1>" + get_cache_name(read_sheet) + "</1>"; return_data += "<2>" + get_google_pic_url(read_sheet) + "</2>"; return_data += "<3>" + sheet_offset + "</3>"; return_data += "<4>Success</4>"; return return_data; } function get_cache_pos(sheet_object) { var cached_sheet_pos = sheet_object.getRange("B1").getValue(); var sheet_data; if(parseInt(cached_sheet_pos) > 0) { cached_sheet_pos = parseInt(cached_sheet_pos); sheet_data = sheet_object.getRange("A" + (cached_sheet_pos - 1)).getValue(); if(sheet_data != "") return cached_sheet_pos; } return 1; } function get_cache_name(sheet_object) { var cached_name = sheet_object.getRange("B2").getValue(); return cached_name; } function get_google_pic_url(sheet_object) { var cached_name = sheet_object.getRange("B3").getValue(); return cached_name; } function get_previous_pic_url(sheet_object) { var cached_name = sheet_object.getRange("B4").getValue(); return cached_name; } function write_cache_data(sheet_object, cache_data, pos) { sheet_object.getRange(pos).setValue(cache_data); } function http_get_content(request_id) { var response; var url = "https://api.line.me/v2/bot/message/" + request_id + "/content"; response = UrlFetchApp.fetch(url, { 'headers': { 'Content-Type': 'application/json; charset=UTF-8', 'Authorization': 'Bearer ' + ACCESS_TOKEN, }, muteHttpExceptions: true, }); return response; } function http_get_user_name(id) { var response; var url = 'https://api.line.me/v2/bot/profile/' + id; response = UrlFetchApp.fetch(url, { 'headers': { 'Content-Type': 'application/json; charset=UTF-8', 'Authorization': 'Bearer ' + ACCESS_TOKEN, }, muteHttpExceptions: true, }); return response; } function http_get_group_user_name(id, group_id) { var response; var url = "https://api.line.me/v2/bot/group/" + group_id + "/member/" + id + "/"; response = UrlFetchApp.fetch(url, { 'headers': { 'Content-Type': 'application/json; charset=UTF-8', 'Authorization': 'Bearer ' + ACCESS_TOKEN, }, muteHttpExceptions: true, }); return response; } function http_get_group_name(group_id) { var response; var url = "https://api.line.me/v2/bot/group/" + group_id +"/summary/"; response = UrlFetchApp.fetch(url, { 'headers': { 'Content-Type': 'application/json; charset=UTF-8', 'Authorization': 'Bearer ' + ACCESS_TOKEN, }, muteHttpExceptions: true, }); return response; } function http_send_text_reply(reply_token, send_msg) { var response; var url = 'https://api.line.me/v2/bot/message/reply/'; response = UrlFetchApp.fetch(url, { 'headers': { 'Content-Type': 'application/json; charset=UTF-8', 'Authorization': 'Bearer ' + ACCESS_TOKEN, }, 'method': 'post', 'payload': JSON.stringify({ 'replyToken': reply_token, 'messages': [{ 'type': 'text', 'text': send_msg , }], 'notificationDisabled': 'true', }), muteHttpExceptions: true, }); return response; } function http_send_text(id, send_msg) { var response; var url = 'https://api.line.me/v2/bot/message/push/'; response = UrlFetchApp.fetch(url, { 'headers': { 'Content-Type': 'application/json; charset=UTF-8', 'Authorization': 'Bearer ' + ACCESS_TOKEN, }, 'method': 'post', 'payload': JSON.stringify({ "to": id, "messages": [{ "text": send_msg, "type": "text", }], 'notificationDisabled': 'false', }), muteHttpExceptions: true, }); return response; } function http_send_sticker(id, package_id, sticker_id) { var response; var url = 'https://api.line.me/v2/bot/message/push/'; response = UrlFetchApp.fetch(url, { 'headers': { 'Content-Type': 'application/json; charset=UTF-8', 'Authorization': 'Bearer ' + ACCESS_TOKEN, }, 'method': 'post', 'payload': JSON.stringify({ "to": id, "messages": [{ "type": "sticker", "packageId": package_id, "stickerId": sticker_id, }], 'notificationDisabled': 'false', }), muteHttpExceptions: true, }); return response; } function http_send_image(id, img_url, preview_url) { var response; var url = 'https://api.line.me/v2/bot/message/push/'; response = UrlFetchApp.fetch(url, { 'headers': { 'Content-Type': 'application/json; charset=UTF-8', 'Authorization': 'Bearer ' + ACCESS_TOKEN, }, 'method': 'post', 'payload': JSON.stringify({ "to": id, "messages": [{ "type": "image", "originalContentUrl": img_url, "previewImageUrl": preview_url, }], 'notificationDisabled': 'false', }), muteHttpExceptions: true, }); return response; } function send_msg(id, send_message, time) { var cache; var response; var return_message = "Success"; response = http_send_text(id, send_message); if(response.getResponseCode() != 200) { cache = "***The message failed to send. Status code = " + response.getResponseCode() + "

" + JSON.parse(response.getContentText()).message + "*** "; cache += send_message; send_message = cache; return_message = "The message failed to send. Status code = " + response.getResponseCode() + "

" + JSON.parse(response.getContentText()).message; } send_message += "(" + time + ")"; log_save(send_message, CLIENT_NAME, id, "Do not save", "Do not save"); return return_message; } function send_sticker(id, package_id, sticker_id, time) { var response; var return_message = "Success"; var send_message = ""; response = http_send_sticker(id, package_id, sticker_id); if(response.getResponseCode() != 200) { send_message = "***The sticker failed to send. Status code = " + response.getResponseCode() + " " + JSON.parse(response.getContentText()).message + "*** "; return_message = "The sticker failed to send. Status code = " + response.getResponseCode() + "

" + JSON.parse(response.getContentText()).message; } send_message += "<sticker>" + sticker_id + "</sticker>(" + time + ")"; log_save(send_message, CLIENT_NAME, id, "Do not save", "Do not save"); return return_message; } function get_cache_data(id, cache_name) { var cache_data = ""; var cache_file; var cache_folder; var exist_cache_folders; var exist_folders; var exist_sub_folders; var folder; var folder_name = "Line_contents"; var sub_folder; exist_folders = DriveApp.searchFolders("'me' in owners"); folder = check_folder_exist(exist_folders, folder_name); if(folder == undefined) return undefined; exist_sub_folders = folder.searchFolders("'me' in owners"); sub_folder = check_folder_exist(exist_sub_folders, id); if(sub_folder == undefined) return undefined; exist_cache_folders = sub_folder.searchFolders("'me' in owners"); cache_folder = check_folder_exist(exist_cache_folders, cache_name); if(cache_folder == undefined) return undefined; for(var i = 0; i < 4096; i++) { try { cache_file = cache_folder.getFilesByName(i).next(); cache_data += cache_file.getBlob().getDataAsString(); } catch(error) { break; } } cache_folder.setTrashed(true); return cache_data; } function upload_content(id, data, count, cache_name, last, time) { var cache_data; var cache_folder; var exist_cache_folders; var exist_folders; var exist_sub_folders; var folder; var folder_name = "Line_contents"; var part_of_content; var part_of_content_data; var return_message = "Success"; var send_message; var sub_folder; if(count == 0 && last == "true") return_message = send_content(id, cache_name, time, data); else if(last == "true") { cache_data = get_cache_data(id, cache_name); if(cache_data == undefined) { send_message = "***The content failed to send. The cache folder does not exist.*** (" + time + ")"; log_save(send_message, CLIENT_NAME, id, "Do not save", "Do not save"); return_message = "The content failed to send.

The cache folder does not exist."; } else { cache_data += data; return_message = send_content(id, cache_name, time, cache_data); } } else { exist_folders = DriveApp.searchFolders("'me' in owners"); folder = check_folder_exist(exist_folders, folder_name); if(folder == undefined) folder = DriveApp.createFolder(folder_name); exist_sub_folders = folder.searchFolders("'me' in owners"); sub_folder = check_folder_exist(exist_sub_folders, id); if(sub_folder == undefined) sub_folder = folder.createFolder(id); exist_cache_folders = sub_folder.searchFolders("'me' in owners"); cache_folder = check_folder_exist(exist_cache_folders, cache_name); if(cache_folder == undefined) cache_folder = sub_folder.createFolder(cache_name); else if(!(cache_folder == undefined) && count == 0) { cache_folder.setTrashed(true); cache_folder = sub_folder.createFolder(cache_name); } part_of_content_data = Utilities.newBlob(data).setName(count); part_of_content = cache_folder.createFile(part_of_content_data); } return return_message; } function send_content(id, cache_name, time, encoded_data) { var content; var content_data; var content_type; var content_url = ""; var exist_folders; var exist_sub_folders; var folder; var folder_name = "Line_contents"; var preview_content_url = ""; var response; var return_message = "Success"; var send_message = ""; var sub_folder; exist_folders = DriveApp.searchFolders("'me' in owners"); folder = check_folder_exist(exist_folders, folder_name); if(folder == undefined) folder = DriveApp.createFolder(folder_name); exist_sub_folders = folder.searchFolders("'me' in owners"); sub_folder = check_folder_exist(exist_sub_folders, id); if(sub_folder == undefined) sub_folder = folder.createFolder(id); try { content_data = Utilities.newBlob(Utilities.base64Decode(encoded_data)).setName(cache_name); } catch (e) { return_message = "The content failed to send.

Couldn't decode base64 data."; return return_message; } content = sub_folder.createFile(content_data); content.setSharing(DriveApp.Access.ANYONE, DriveApp.Permission.VIEW); content_type = content.getMimeType(); if(content_type == MimeType.BMP || content_type == MimeType.GIF || content_type == MimeType.JPEG || content_type == MimeType.PNG || content_type == MimeType.SVG) { content.setTrashed(true); folder_name = "Line_images"; exist_folders = DriveApp.searchFolders("'me' in owners"); folder = check_folder_exist(exist_folders, folder_name); if(folder == undefined) folder = DriveApp.createFolder(folder_name); exist_sub_folders = folder.searchFolders("'me' in owners"); sub_folder = check_folder_exist(exist_sub_folders, id); if(sub_folder == undefined) sub_folder = folder.createFolder(id); content = sub_folder.createFile(content_data); content.setSharing(DriveApp.Access.ANYONE, DriveApp.Permission.VIEW); content_url = "https://lh3.googleusercontent.com/d/" + content.getId() + "=w16383-h16383"; preview_content_url = "https://lh3.googleusercontent.com/d/" + content.getId() + "=w240-h240"; response = http_send_image(id, content_url, preview_content_url); if(response.getResponseCode() != 200) { send_message = "***The image failed to send. Status code = " + response.getResponseCode() + " " + JSON.parse(response.getContentText()).message + "*** "; return_message = "The image failed to send. Status code = " + response.getResponseCode() + "

" + JSON.parse(response.getContentText()).message; } send_message += "<image_url>" + content_url + "</image_url>(" + time + ")"; log_save(send_message, CLIENT_NAME, id, "Do not save", "Do not save"); return return_message; } else { content_url = "https://drive.google.com/file/d/" + content.getId() + "/view"; response = http_send_text(id, content_url); if(response.getResponseCode() != 200) { send_message = "***The content failed to send. Status code = " + response.getResponseCode() + " " + JSON.parse(response.getContentText()).message + "*** "; return_message = "The content failed to send. Status code = " + response.getResponseCode() + "

" + JSON.parse(response.getContentText()).message; } content_url = "https://drive.google.com/uc?export=download&id=" + content.getId(); send_message += "<content_url>" + content_url + "</content_url>(" + time + ")"; log_save(send_message, CLIENT_NAME, id, "Do not save", "Do not save"); return return_message; } } function get_profile_pic_google_url(http_response, group_or_user_id) { var content_data; var content; var folder_name = "Line_images"; var folder; var sub_folder; var exist_folders; var exist_sub_folders; var url = ""; exist_folders = DriveApp.searchFolders("'me' in owners"); folder = check_folder_exist(exist_folders, folder_name); if (folder == undefined) folder = DriveApp.createFolder(folder_name); exist_sub_folders = folder.searchFolders("'me' in owners"); sub_folder = check_folder_exist(exist_sub_folders, group_or_user_id); if (sub_folder == undefined) sub_folder = folder.createFolder(group_or_user_id); content_data = http_response.getBlob().getAs("image/jpeg").setName("icon"); content = sub_folder.createFile(content_data); content.setSharing(DriveApp.Access.ANYONE, DriveApp.Permission.VIEW); url = "https://lh3.googleusercontent.com/d/" + content.getId() + "=w32-h32"; return url; } function get_content_url(request_id, group_or_user_id, type) { var content_data; var content; var exist_folders; var exist_sub_folders; var folder_name; var folder; var sub_folder; var response; var url = ""; if(type == "image") folder_name = "Line_images"; else if(type == "audio") folder_name = "Line_audio"; else if(type == "video") folder_name = "Line_videos"; else if(type == "file") folder_name = "Line_contens"; exist_folders = DriveApp.searchFolders("'me' in owners"); folder = check_folder_exist(exist_folders, folder_name); if(folder == undefined) folder = DriveApp.createFolder(folder_name); exist_sub_folders = folder.searchFolders("'me' in owners"); sub_folder = check_folder_exist(exist_sub_folders, group_or_user_id); if(sub_folder == undefined) sub_folder = folder.createFolder(group_or_user_id); response = http_get_content(request_id); if(response.getResponseCode() != 200) { if(type == "image") url = "***Couldn't generate an image URL. Status code = " + response.getResponseCode() + " " + JSON.parse(response.getContentText()).message + "*** "; else if(type == "audio") url = "***Couldn't generate a audio URL. Status code = " + response.getResponseCode() + " " + JSON.parse(response.getContentText()).message + "*** "; else if(type == "video") url = "***Couldn't generate a video URL. Status code = " + response.getResponseCode() + " " + JSON.parse(response.getContentText()).message + "*** "; } else { if(type == "image") { content_data = response.getBlob().getAs("image/jpeg").setName(request_id); content = sub_folder.createFile(content_data); content.setSharing(DriveApp.Access.ANYONE, DriveApp.Permission.VIEW); url = "https://lh3.googleusercontent.com/d/" + content.getId() + "=w16383-h16383"; } else if(type == "audio" || type == "video" || type == "file") { content_data = response.getBlob().setName(request_id); content = sub_folder.createFile(content_data); content.setSharing(DriveApp.Access.ANYONE, DriveApp.Permission.VIEW); url = "https://drive.google.com/uc?export=download&id=" + content.getId(); } } return url; } function receive_msg_from_line(user_message, user_id, group_id, reply_token, time, reply) { var group_name; var picture_url = [ "", ""]; var response = [ "", ""]; var send_msg; var user_name; if(group_id == "Unknown") response[0] = http_get_user_name(user_id); else { response[0] = http_get_group_user_name(user_id, group_id); response[1] = http_get_group_name(group_id); if(response[1].getResponseCode() != 200) group_name = "Unknown"; else { group_name = JSON.parse(response[1]).groupName; picture_url[1] = JSON.parse(response[1]).pictureUrl; } } if(response[0].getResponseCode() != 200) user_name = "Unknown"; else { user_name = JSON.parse(response[0]).displayName; picture_url[0] = JSON.parse(response[0]).pictureUrl; } if(user_message == "getid" || user_message == "getgroupid") { if(user_message == "getid") send_msg = user_id; else if(user_message == "getgroupid") send_msg = group_id; response[0] = http_send_text_reply(reply_token, send_msg); send_msg = "<id>" + send_msg + "</id>"; if(response[0].getResponseCode() != 200) send_msg += "***The message failed to send. Status code = " + response[0].getResponseCode() + " " + JSON.parse(response[0].getContentText()).message + "*** "; send_msg += "(" + time + ")"; if(user_message == "getid") log_save(send_msg, user_name, "IDs", "BOT", "Do not save"); else if(user_message == "getgroupid") log_save(send_msg, group_name, "IDs", "BOT", "Do not save"); return; } user_message += "(" + time + ")"; if(group_id == "Unknown") log_save(user_message, user_name, user_id, user_name, picture_url[0]); else log_save(user_message, user_name, group_id, group_name, picture_url[1]); } function doPost(post_data) { var time = Utilities.formatDate(new Date(), 'Asia/Tokyo', 'MM/dd hh:mm:ss'); var data_type; var result; var client_auth = "Unknown"; var client_gas_ver = "Unknown"; var id = "Unknown"; var lock = LockService.getScriptLock(); var lock_result = lock.tryLock(100); try { data_type = JSON.parse(post_data.postData.contents).type; } catch(e) { } while(!lock_result) { Utilities.sleep(100); lock_result = lock.tryLock(100); } if(data_type == undefined) { for(var i = 0; i < 50; i++) { var msg_id = ""; var user_message = ""; var user_id = ""; var group_id = ""; var reply_token = ""; var msg_type = ""; var type = ""; try { type = JSON.parse(post_data.postData.contents).events[i].type; user_id = JSON.parse(post_data.postData.contents).events[i].source.userId; group_id = JSON.parse(post_data.postData.contents).events[i].source.groupId; reply_token = JSON.parse(post_data.postData.contents).events[i].replyToken; if(type == "message") { msg_id = JSON.parse(post_data.postData.contents).events[i].message.id; user_message = JSON.parse(post_data.postData.contents).events[i].message.text; msg_type = JSON.parse(post_data.postData.contents).events[i].message.type; } } catch(e) { break; } if(user_id == undefined) user_id = "Unknown"; if(group_id == undefined) group_id = "Unknown"; if(type == "follow") user_message = "getid"; else if(type == "join") user_message = "getgroupid"; if(type == "leave") user_message = "*****Removed by user*****"; else if(type == "unfollow") user_message = "*****Blocked by user*****"; else if(msg_type == "sticker") { var sticker_id = JSON.parse(post_data.postData.contents).events[i].message.stickerId; user_message = "<sticker>" + sticker_id + "</sticker>"; } else if(msg_type == "image") { if(group_id == "Unknown") user_message = "<image_url>" + get_content_url(msg_id, user_id, msg_type) + "</image_url>"; else user_message = "<image_url>" + get_content_url(msg_id, group_id, msg_type) + "</image_url>"; } else if(msg_type == "audio") { if(group_id == "Unknown") user_message = "<audio_url>" + get_content_url(msg_id, user_id, msg_type) + "</audio_url>"; else user_message = "<audio_url>" + get_content_url(msg_id, group_id, msg_type) + "</audio_url>"; } else if(msg_type == "video") { if(group_id == "Unknown") user_message = "<video_url>" + get_content_url(msg_id, user_id, msg_type) + "</video_url>"; else user_message = "<video_url>" + get_content_url(msg_id, group_id, msg_type) + "</video_url>"; } else if(msg_type == "file") { if(group_id == "Unknown") user_message = "<file_url>" + get_content_url(msg_id, user_id, msg_type) + "</file_url>"; else user_message = "<file_url>" + get_content_url(msg_id, group_id, msg_type) + "</file_url>"; } else if(user_message == undefined) user_message += " : " + msg_type + " " + msg_id; receive_msg_from_line(user_message, user_id, group_id, reply_token, time); } lock.releaseLock(); return; } try { client_auth = JSON.parse(post_data.postData.contents).auth; client_gas_ver = JSON.parse(post_data.postData.contents).gas_ver; id = JSON.parse(post_data.postData.contents).id; } catch(e) { } if(SCRIPT_PASSOWRD == client_auth) { if(gas_ver == client_gas_ver) { if(data_type == "send_text") { var send_message = JSON.parse(post_data.postData.contents).message; result = send_msg(id, send_message, time); } else if(data_type == "send_sticker") { var package_id = JSON.parse(post_data.postData.contents).package_id; var sticker_id = JSON.parse(post_data.postData.contents).sticker_id; result = send_sticker(id, package_id, sticker_id, time); } else if(data_type == "upload_content") { var content_data = JSON.parse(post_data.postData.contents).content_data; var count = JSON.parse(post_data.postData.contents).count; var name = JSON.parse(post_data.postData.contents).name; var last = JSON.parse(post_data.postData.contents).last; result = upload_content(id, content_data, count, name, last, time); } else result = "Unknown message type."; } else result = "Google apps script version does not match. Server's gas

ver is " + gas_ver + ", but 3DS's gas ver is " + client_gas_ver + ". Please use the same version."; } else result = "Auth failed. Please set correct password."; lock.releaseLock(); return ContentService.createTextOutput(result); } function doGet(post_data) { var data = ""; var client_auth = ""; var client_gas_ver = ""; var num_of_logs = ""; var id = ""; var lock = LockService.getScriptLock(); var lock_result = lock.tryLock(100); while(!lock_result) { Utilities.sleep(100); lock_result = lock.tryLock(100); } try { client_auth = post_data.parameter.script_auth; client_gas_ver = post_data.parameter.gas_ver; num_of_logs = post_data.parameter.logs; id = post_data.parameter.id; } catch(e) { } if(client_auth == undefined) client_auth = ""; if(client_gas_ver == undefined) client_gas_ver = -1; if(num_of_logs == undefined) num_of_logs = 300; if(id == undefined) id = "unknown"; if(SCRIPT_PASSOWRD == client_auth) { if(gas_ver == client_gas_ver) data = log_read(id, num_of_logs); else data = "Google apps script version does not match. Server's gas

ver is " + gas_ver + ", but 3DS's gas ver is " + client_gas_ver + ". Please use the same version."; } else data = "Auth failed. Please set correct password."; lock.releaseLock(); return ContentService.createTextOutput(data); }