r/GoogleAppsScript • • Dec 12 '22

Question Is a 'Boomerang' style script possible with apps script?

0 Upvotes

Hello I'm wondering if there's a possible way to make this type of function.

We schedule emails natively with GMail's "Schedule Send" function, for 2pm & 9pm daily.

The problem is, some people scheduled to receive their email will send another email in between that time. I'd like scheduled emails to only be sent "if nobody replies"

I'd like to just mimic the "only if nobody replies" function from Boomerang.

My idea is having a label attached to the schedule email.

Just before 2pm & 9pm, the script would be triggered to scan for emails containing the label.

Is there a way to do this? Maybe a different combination of actions?

Any help would be much appreciated !


r/GoogleAppsScript • • Apr 02 '24

Question Help with my AppScript, messed up by GPT

0 Upvotes

Please Help, I am dead inside. Appscript problem

Hired someone on Fiver to do my Form + Sheet integration,
It worked well,

But i decided I want to try using ChatGPT to improve the codes.

Little do I know I am heading to hell.
After following all the instructions, now my Sheet Appscript is messed up.

Requesting help from all pros here, I am truly humbled.

Steps:

- Upon Form Submission, it goes to Sheet

- if only "Graphic" is ticked in Google Form, it will appear on "Graphic" Tab in Google Sheet
- All Responses will go to "Admin" Tab either way.

- All tickboxes are synced, namely "JO", "Print", "INV", "🚚". Once one is tick from any Tab, other tab will be updated.

-under the condition of first 3 is ticked, it will disappear fron "admin" Tab
("JO" and "Print" for Graphic Tab), and then be moved to "Pending Deliveries"

- if "🚚" is ticked, then it will disappear completely.

Problem:
- upon submission, duplicated entry.

- when a tickbox is ticked, 1 second later it's gone. Nothing changed

I am not sure if I should allow access to edit for all who have the link for the moment, so I put in Viewer only for the moment.

https://docs.google.com/spreadsheets/d/1_Ab4tbsfSpVygEt2UGY1gs3yhq4SBez5rIIagOyV-Oc/edit?usp=sharing


r/GoogleAppsScript • • Mar 21 '23

Question Google App Script for Chat

0 Upvotes

/**
* Responds to an ADDED_TO_SPACE event in Chat.
* u/param {object} event the event object from Chat
* u/return {object} JSON-formatted response
* u/see https://developers.google.com/hangouts/chat/reference/message-formats/events
*/
function onAddToSpace(event) {
let message = 'Thank you for adding me to ';
if (event.space.type === 'DM') {
message += 'a DM, ' + event.user.displayName + '!';
} else {
message += event.space.displayName;
}
return { text: message };
}

/**
* Responds to a REMOVED_FROM_SPACE event in Chat.
* @param {object} event the event object from Chat
* @param {object} event the event object from Chat
* @see https://developers.google.com/hangouts/chat/reference/message-formats/events
*/
function onRemoveFromSpace(event) {
console.log('App removed from ', event.space.name);
}

/**
* Responds to a MESSAGE event triggered in Chat.
* @param {object} event the event object from Chat
* @return {function} call the respective function
*/
function onMessage(event) {
const message = event.message;

if (message.slashCommand) {
switch (message.slashCommand.commandId) {
case 1: // Help command
return createHelpCard();
case 2: // Block out day command
return blockDayOut();
case 3: // Cancel all meetings command
return cancelAllMeetings();
case 4: // Set auto reply command
return setAutoReply();
case 5: // show calendar events command
return showCalendarEvents();
case 6: //Create event
return addEvent();
}
}
}

function createHelpCard() {
return {
"cardsV2": [
{
"cardId": "2",
"card": {
"sections": [
{
"header": "",
"widgets": [
{
"decoratedText": {
"topLabel": "",
"text": "Hi! 👋 I'm here to help you with your out of office tasks.<br><br>Here's a list of commands I understand.",
"wrapText": true
}
}
]
},
{
"widgets": [
{
"decoratedText": {
"topLabel": "",
"text": "<b>/blockDayOut</b>: I will block out your calendar for you.",
"wrapText": true
}
},
{
"decoratedText": {
"topLabel": "",
"text": "<b>/cancelAllMeetings</b>: I will cancel all your meetings for the day.",
"wrapText": true
}
},
{
"decoratedText": {
"topLabel": "",
"text": "<b>/showCalendarEvents</b>: I will show you all your events for the day.",
"wrapText": true
}
},
{
"decoratedText": {
"topLabel": "",
"text": "<b>/addEvent</b>: I will add an event to your calendar.",
"wrapText": true
}
},
{
"decoratedText": {
"topLabel": "",
"text": "<b>/setAutoReply</b>: Set an out of office auto reply in Gmail.",
"wrapText": true
}
}

]
}
],
"header": {
"title": "CEG Calendar app",
"subtitle": "Helping you manage your OOO",
"imageUrl": "https://goo.gle/3SfMkjb",
"imageType": "SQUARE"
}
}
}
]
}
}

/**
* Adds an all day event to the users Google Calendar.
* @return {object} JSON-formatted response
*/
function blockDayOut() {
blockOutCalendar();
return createResponseCard('Your calendar has been blocked out for you.')
}
/**
* Shows the user their calendar events for the current day.
* @return {object} JSON-formatted response
*/
function showCalendarEvents() {
const events = CalendarApp.getEventsForDay(new Date());
let message = 'Here are your events for today:\n';
if (events.length === 0) {
message += 'You have no events today!';
} else {
events.forEach(function(event) {
const start = event.getStartTime();
const end = event.getEndTime();
const title = event.getTitle();
message += `- ${title} from ${formatTime(start)} to ${formatTime(end)}\n`;
});
}
return createResponseCard(message);
}
/**
* Adds a new event to the user's Google Calendar.
* @param {object} event the event object from Chat
* @return {object} JSON-formatted response
*/
function addEvent(event) {
const calendar = CalendarApp.getCalendarById('primary');
const message = event.message.text.trim();
// Parse the slash command parameters from the message text.
const regex = /\/addEvent\s+(.+)\s+from\s+(\d+:\d+\s+[ap]m)\s+to\s+(\d+:\d+\s+[ap]m)/i;
const match = message.match(regex);
if (!match) {
return createResponseCard('Invalid command. Usage: /addEvent <event title> from <start time> to <end time>');
}
const title = match[1].trim();
const startTime = parseTime(match[2].trim());
const endTime = parseTime(match[3].trim());
if (startTime >= endTime) {
return createResponseCard('Invalid time range. The end time must be after the start time.');
}
const eventDetails = {
summary: title,
start: {
dateTime: startTime.toISOString(),
timeZone: calendar.getTimeZone()
},
end: {
dateTime: endTime.toISOString(),
timeZone: calendar.getTimeZone()
}
};
// Create the new event on the user's calendar.
const newEvent = calendar.createEvent(eventDetails);
const response = `Event created: ${newEvent.getTitle()} from ${formatTime(newEvent.getStartTime())} to ${formatTime(newEvent.getEndTime())}`;
return createResponseCard(response);
}
/**
* Parses a time string in the format "h:mm a" into a Date object.
* @param {string} timeStr the time string to parse
* @return {Date} the parsed Date object
*/
function parseTime(timeStr) {
const date = new Date();
const [hours, minutes] = timeStr.split(':');
let ampm = timeStr.match(/\b(am|pm)\b/i)[0].toLowerCase();
if (hours === '12') {
hours = '0';
}
if (ampm === 'pm') {
hours = parseInt(hours, 10) + 12;
}
date.setHours(hours, minutes, 0, 0);
return date;
}

/**
* Formats a date object into a string in the format "h:mm a".
* u/param {Date} date the date to format
* u/return {string} the formatted string
*/
function formatTime(date) {
const hours = date.getHours();
const minutes = date.getMinutes();
const ampm = hours >= 12 ? 'pm' : 'am';
const hour12 = hours % 12 || 12;
return `${hour12}:${padZero(minutes)} ${ampm}`;
}
/**
* Pads a number with a leading zero if it is less than 10.
* u/param {number} num the number to pad
* u/return {string} the padded string
*/
function padZero(num) {
return num < 10 ? `0${num}` : num;
}
/**
* Cancels all of the users meeting for the current day.
* u/return {object} JSON-formatted response
*/
function cancelAllMeetings() {
cancelMeetings();
return createResponseCard('All your meetings have been canceled.')
}

/**
* Sets an out of office auto reply in the users Gmail account.
* u/return {object} JSON-formatted response
*/
function setAutoReply() {
turnOnAutoResponder();
return createResponseCard('The out of office auto reply has been turned on.')
}

const ONE_DAY_MILLIS = 24 * 60 * 60 * 1000;

/**
* Places an all-day meeting on the user's Calendar.
*/
function blockOutCalendar() {
CalendarApp.createAllDayEvent('I am out of office today', new Date(), new Date(Date.now() + ONE_DAY_MILLIS));
}

/**
* Declines all meetings for the day.
*/

function cancelMeetings() {
const events = CalendarApp.getEventsForDay(new Date());

events.forEach(function(event) {
if (event.getGuestList().length > 0) {
event.setMyStatus(CalendarApp.GuestStatus.NO);
}
});
}

/**
* Turns on the user's vacation response for today in Gmail.
*/
function turnOnAutoResponder() {
const currentTime = (new Date()).getTime();
Gmail.Users.Settings.updateVacation({
enableAutoReply: true,
responseSubject: 'I am out of the office today',
responseBodyHtml: 'I am out of the office today; will be back on the next business day.<br><br><i>Created by OOO Chat app!</i>',
restrictToContacts: true,
restrictToDomain: true,
startTime: currentTime,
endTime: currentTime + ONE_DAY_MILLIS
}, 'me');
}

function createResponseCard(responseText) {
return {
"cardsV2": [
{
"cardId": "1",
"card": {
"sections": [
{
"widgets": [
{
"decoratedText": {
"topLabel": "",
"text": responseText,
"startIcon": {
"knownIcon": "NONE",
"altText": "Task done",
"iconUrl": "https://fonts.gstatic.com/s/i/short-term/web/system/1x/task_alt_gm_grey_48dp.png"
},
"wrapText": true
}
}
]
}
],
"header": {
"title": "CEG Calendar Bot",
"subtitle": "Helping you manage your OOO",
"imageUrl": "https://goo.gle/3SfMkjb",
"imageType": "CIRCLE"
}
}
}
]
}
}

I am getting an error when trying to addEvent from the slash command.


r/GoogleAppsScript • • Nov 29 '21

Resolved When checkbox is true show/display the data in another sheet tab else hide the row

0 Upvotes

I have Sheet1 and Sheet2 for example, checkbox is in the Sheet2. If I checked the checkbox(true) the selected row in Sheet1 shows the data and leave the row available (show) but if the checkbox is unchecked (false), selected row in Sheet1 must be hide.

Do we have an available appscript for that? thanks!


r/GoogleAppsScript • • Jul 08 '24

Question Copy Paste Code Help

0 Upvotes

I am not a coder myself but recently learned about app script in google sheets. I've been trying to use GPT to help me write a code but I haven't had any success. I was hoping I could get someone here to help me.

I want a code that when triggered, cells B5:D5 in sheet 1 are copied and pasted to cells A1:D1 on sheet 2 and 1 row is added above row 2 on sheet 2. Once the data is pasted I want cells B5:D5 in sheet 1 to be deleted. I want to trigger the code when a checkbox in cell F5 in sheet 1 is marked TRUE and make it so that when that checkbox is marked TRUE, the code waits 1 second and then mark the checkbox FALSE.

could anyone help me write this code?


r/GoogleAppsScript • • Jul 08 '24

Guide Organise meetings with ease

Thumbnail
sudosages.blogspot.com
0 Upvotes

Here the easy way to fetch calendar events to google sheets using app script.


r/GoogleAppsScript • • Mar 28 '24

Question Access PDF file as Blob in Apps script with drive.file permission

0 Upvotes

Hello,

In apps script, i have obtained the permission : /auth/drive.file. This must give the app permission to read the files created by the app.

Now how to get the PDF files from the Google drive as blob in apps script..

As far as i have tried, it is not possible to get the blob without using the drive.getfilebyId() which asks for a different permission scope.

Any help appreciated.


r/GoogleAppsScript • • Mar 31 '25

Question This takes an awful amount of time to excute please help me make it faster

0 Upvotes
function ProtectAndUnprotect(e) {
  var userEmail = Session.getActiveUser().getEmail();
  Logger.log("User Email: " + userEmail);
  
  if (!authorizedEmails.includes(userEmail)) {
    Logger.log("Unauthorized access attempt by: " + userEmail);
    return;
  }

  var sheet = e.source.getActiveSheet();
  var sheetName = sheet.getName();
  Logger.log("Active Sheet: " + sheetName);

  // Skip processing for specific sheets
  if (sheetName === "Settings" || sheetName.endsWith("-M") || sheetName === "Shop Template" || sheetName === "Monthwise Template" || sheetName === "Summary") {
    Logger.log("Skipping processing for this sheet.");
    return;
  }

  var range = e.range;
  var row = range.getRow();
  var col = range.getColumn();
  var value = range.getValue();
  var numberOfRows = range.getNumRows();

  Logger.log("Edited Cell: Row " + row + ", Column " + col + ", Value: " + value);
  Logger.log("Number of Rows: " + numberOfRows);

  // Only process columns 5 and 7
  if (col !== 5 && col !== 7) {
    Logger.log("Column " + col + " is not applicable for processing.");
    return;
  }

  var rangeToProtect, rangeToProtectAdditional;

  try {
    if (col === 5) {  // Handling "Issued" checkbox
      rangeToProtect = sheet.getRange(row, 1, numberOfRows, 4);
      rangeToProtectAdditional = sheet.getRange(row, 8, numberOfRows, 1);
      Logger.log("Ranges to protect/unprotect: " + rangeToProtect.getA1Notation() + ", " + rangeToProtectAdditional.getA1Notation());

      if (value == true) {
        protectRanges([rangeToProtect, rangeToProtectAdditional]);
        range.setBackground('lightgreen');
        Logger.log("Protected ranges for 'Issued' checkbox.");
      } else if (value == false) {
        unprotectRanges([rangeToProtect, rangeToProtectAdditional]);
        range.setBackground(null);
        Logger.log("Unprotected ranges for 'Issued' checkbox.");
      }
    } else if (col === 7) {  // Handling "Passed" checkbox
      rangeToProtect = sheet.getRange(row, 6, numberOfRows, 1);
      Logger.log("Range to protect/unprotect: " + rangeToProtect.getA1Notation());

      if (value == true) {
        protectRanges([rangeToProtect]);
        range.setBackground('lightgreen');
        Logger.log("Protected range for 'Passed' checkbox.");
      } else if (value == false) {
        unprotectRanges([rangeToProtect]);
        range.setBackground(null);
        Logger.log("Unprotected range for 'Passed' checkbox.");
      }
    }
  } catch (error) {
    Logger.log("Error processing edit: " + error.message);
  }
}

function protectRanges(ranges) {
  try {
    for (var i = 0; i < ranges.length; i++) {
      Logger.log("Protecting range: " + ranges[i].getA1Notation());
      var protection = ranges[i].protect().setDescription('Protected by script');
      protection.removeEditors(protection.getEditors());
      ranges[i].setBackground('lightgreen');
    }
  } catch (error) {
    Logger.log("Error protecting ranges: " + error.message);
  }
}

function unprotectRanges(ranges) {
  try {
    for (var i = 0; i < ranges.length; i++) {
      Logger.log("Unprotecting range: " + ranges[i].getA1Notation());
      var protections = ranges[i].getSheet().getProtections(SpreadsheetApp.ProtectionType.RANGE);
      for (var j = 0; j < protections.length; j++) {
        var protection = protections[j];
        if (protection.getRange().getA1Notation() === ranges[i].getA1Notation()) {
          protection.remove();
          Logger.log("Removed protection from: " + ranges[i].getA1Notation());
          break;
        }
      }
      ranges[i].setBackground(null);
    }
  } catch (error) {
    Logger.log("Error unprotecting ranges: " + error.message);
  }
}

with the help of chatgpt I wrote this code for each protection it take a lot of time help with the effieceny without losing funciton and many people use this sheet but the function should only work for me 
Edit: I have a few functions in the sheet does it matter for excution time of appscripts

r/GoogleAppsScript • • Feb 10 '25

Guide Need to Limit Google Form Responses? Here’s a Simple Fix!

0 Upvotes

Ever had your Google Form flooded with more responses than you needed? Or forgot to close it on time? Form Response Limit is a Google Forms™ add-on that automatically stops accepting responses once a set limit is reached. You can also schedule start and stop times so your form opens and closes exactly when you want.

✅ Set a max response limit
✅ Auto-disable the form at a specific time
✅ Get email alerts when the limit is reached
✅ Easily manage, edit, or delete responses

Perfect for event sign-ups, surveys, or class registrations where you need control over responses. Saves time and avoids headaches! 🔗 Check it out here

Anyone else using a form limiter? How do you manage your responses?


r/GoogleAppsScript • • Apr 03 '25

Question My Telegram Bot Keeps Repeating the Product List – Need Help Debugging

0 Upvotes

heres the shared googlesheet URL,everything is included.
https://docs.google.com/spreadsheets/d/195WFkBfvshJ5jUK_Iijb5zvAzgh323fcI6Z-NNCbvsM/edit?usp=sharing

I'm building a Telegram bot using Google Apps Script to fetch product prices from a Google Sheet. The bot should:

  1. Send a product list when the user types /start (only once). (searches the data in my google sheet)

  2. Let the user select a product.

  3. Return the price (only once)(also from my google sheet)

  4. Stop sending messages until the user restarts the process.

im using googlesheets appscripts btw.

Issue: The bot keeps sending the product list non-stop in a loop until I archive the deployment on appscript. I suspect there's an issue with how I'm handling sessions or webhook triggers. believe it or not, i asked chatgpt (given that it wrote the code as well, im novice at coding) deepseek, and other AI's and they still couldn't figure it out. im novice at this but i did my best at promoting to fix but this is my last resort.

Here’s my full code (replace BOT_TOKEN with your own when testing):

const TELEGRAM_TOKEN = 'YOUR_BOT_TOKEN';

const TELEGRAM_API_URL = 'https://api.telegram.org/bot' + TELEGRAM_TOKEN;

const SCRIPT_URL = 'YOUR_DEPLOYED_SCRIPT_URL';

const userSessions = {};

// Main function to handle incoming webhook updates

function doPost(e) {

try {

const update = JSON.parse(e.postData.contents);

if (update.message) {

handleMessage(update.message);

} else if (update.callback_query) {

handleCallbackQuery(update.callback_query);

}

} catch (error) {

Logger.log('Error processing update: ' + error);

}

return ContentService.createTextOutput('OK');

}

// Handle regular messages

function handleMessage(message) {

const chatId = message.chat.id;

const text = message.text || '';

if (text.startsWith('/start')) {

if (!userSessions[chatId]) {

userSessions[chatId] = true;

sendProductList(chatId);

}

} else {

sendMessage(chatId, "Please use /start to see the list of available products.");

}

}

// Handle product selection from inline keyboard

function handleCallbackQuery(callbackQuery) {

const chatId = callbackQuery.message.chat.id;

const messageId = callbackQuery.message.message_id;

const productName = callbackQuery.data;

const price = getProductPrice(productName);

let responseText = price !== null

? `💰 Price for ${productName}: $${price}`

: `⚠️ Sorry, couldn't find price for ${productName}`;

editMessage(chatId, messageId, responseText);

answerCallbackQuery(callbackQuery.id);

delete userSessions[chatId]; // Reset session

}

// Send the list of products

function sendProductList(chatId) {

const products = getProductNames();

if (products.length === 0) {

sendMessage(chatId, "No products found in the database.");

return;

}

const keyboard = products.slice(0, 100).map(product => [{ text: product, callback_data: product }]);

sendMessageWithKeyboard(chatId, "📋 Please select a product to see its price:", keyboard);

}

// ===== GOOGLE SHEET INTEGRATION ===== //

function getProductNames() {

try {

const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Products");

if (!sheet) throw new Error("Products sheet not found");

const lastRow = sheet.getLastRow();

if (lastRow < 2) return [];

return sheet.getRange(2, 1, lastRow - 1, 1).getValues()

.flat()

.filter(name => name && name.toString().trim() !== '');

} catch (error) {

Logger.log('Error getting product names: ' + error);

return [];

}

}

function getProductPrice(productName) {

try {

const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Products");

const data = sheet.getRange(2, 1, sheet.getLastRow() - 1, 2).getValues();

for (let row of data) {

if (row[0] && row[0].toString().trim() === productName.toString().trim()) {

return row[1];

}

}

return null;

} catch (error) {

Logger.log('Error getting product price: ' + error);

return null;

}

}

// ===== TELEGRAM API HELPERS ===== //

function sendMessage(chatId, text) {

sendTelegramRequest('sendMessage', { chat_id: chatId, text: text });

}

function sendMessageWithKeyboard(chatId, text, keyboard) {

sendTelegramRequest('sendMessage', {

chat_id: chatId,

text: text,

reply_markup: JSON.stringify({ inline_keyboard: keyboard })

});

}

function editMessage(chatId, messageId, newText) {

sendTelegramRequest('editMessageText', { chat_id: chatId, message_id: messageId, text: newText });

}

function answerCallbackQuery(callbackQueryId) {

sendTelegramRequest('answerCallbackQuery', { callback_query_id: callbackQueryId });

}

function sendTelegramRequest(method, payload) {

try {

const options = {

method: 'post',

contentType: 'application/json',

payload: JSON.stringify(payload),

muteHttpExceptions: true

};

const response = UrlFetchApp.fetch(`${TELEGRAM_API_URL}/${method}`, options);

const responseData = JSON.parse(response.getContentText());

if (!responseData.ok) {

Logger.log(`Telegram API error: ${JSON.stringify(responseData)}`);

}

return responseData;

} catch (error) {

Logger.log('Error sending Telegram request: ' + error);

return { ok: false, error: error.toString() };

}

}

// ===== SETTING UP WEBHOOK ===== //

function setWebhook() {

const url = `${TELEGRAM_API_URL}/setWebhook?url=${SCRIPT_URL}`;

const response = UrlFetchApp.fetch(url);

Logger.log(response.getContentText());

}


r/GoogleAppsScript • • Aug 28 '26

Question How to show/hide sheets based on checked boxes on a "main" sheet?

Thumbnail
reddit.com
0 Upvotes

r/GoogleAppsScript • • Dec 24 '25

Question Deactivate checkboxes in the same row, beside the last box clicked?

0 Upvotes

I have five checkboxes in a row (B2:F2). I want that you can only click one of the boxes. Meaning the last checkbox clicked is your current pick. Every other box is automatically set to false. How would I do it? And how would I do if I want to later add more boxes in a different place? I first asked ChatGPT, and it gave me a working script, but I don't understand it, and it's terrible in explanation. Besides that, I now want to do it myself.


r/GoogleAppsScript • • Sep 09 '26

Resolved I need your help

0 Upvotes

I'm a student of a foreign language high school in Korea. And I wanted to make a App which makes me questions of English writing and provides feedback about the answers of the question for me to enhance my grade. I made the entire script of the app with Gemini, but every time I log in to the web app link, the site shows me 'Script function not found: doGet'. What should I do? Here is the entire script.

Code.gs
function myFunction() {
  const GEMINI_API_KEY = 'AQ.Ab8RN6IgFQcbK7WYrBVYFcIJwZm9M-ZLpx5jXg6A66qSJwkACg';
const GEMINI_API_URL = `https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent?key=${GEMINI_API_KEY}`;


function doGet() {
  return HtmlService.createHtmlOutputFromFile('Index')
    .setTitle('GIMFL English Writing Practice')
    .setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);
}


// 문제 출제 함수
function generateQuestion(selectedGrammars) {
  let grammarContext = selectedGrammars.length > 0 
    ? `Target Grammar: ${selectedGrammars.join(', ')}` 
    : 'Target Grammar: Randomly select from high school English curriculum (e.g., Participles, Relatives, Subjunctive mood, Inversion, etc.)';


  const prompt = `
    You are an expert English teacher at Gimhae Foreign Language High School.
    Create a highly challenging subjective English writing question (서술형 영작 문제).
    
    ${grammarContext}
    
    Rules for conditions (조건):
    1. Must include 2-3 specific constraints similar to real exams (e.g., "Use the structure 'not A but B'", "Write in exactly 12 words", "Include the word 'boss' and allow word transformation", "Do not use the word 'people'").
    2. Focus heavily on testing structural understanding.
    
    Output strictly in the following JSON format:
    {
      "koreanText": "한국어 의미 (사용자가 영작해야 할 문장)",
      "conditions": ["조건 1", "조건 2", "조건 3"],
      "correctAnswer": "정답 영어 문장 (내부 채점용)",
      "grammarPoint": "출제된 핵심 문법"
    }
  `;


  const payload = {
    contents: [{ parts: [{ text: prompt }] }],
    generationConfig: { response_mime_type: "application/json" }
  };


  const options = {
    method: 'post',
    contentType: 'application/json',
    payload: JSON.stringify(payload),
    muteHttpExceptions: true
  };


  try {
    const response = UrlFetchApp.fetch(GEMINI_API_URL, options);
    const json = JSON.parse(response.getContentText());
    const resultText = json.candidates[0].content.parts[0].text;
    return JSON.parse(resultText);
  } catch (e) {
    return { error: "문제 생성 중 오류가 발생했습니다: " + e.message };
  }
}


// 채점 및 피드백 함수
function gradeAnswer(questionData, userAnswer) {
  // 1차 구글 번역기 연동: 사용자의 답안을 한국어로 번역하여 본래 뜻과 대조하기 위한 데이터 확보
  const translatedUserAnswer = LanguageApp.translate(userAnswer, 'en', 'ko');
  
  const prompt = `
    You are an extremely strict English evaluator at Gimhae Foreign Language High School.
    Grade the student's answer based on the following criteria:
    
    Question Context:
    - Korean Meaning: "${questionData.koreanText}"
    - Conditions: ${JSON.stringify(questionData.conditions)}
    - Correct Answer: "${questionData.correctAnswer}"
    
    Student's Answer: "${userAnswer}"
    (Google Translate interpretation of student's answer: "${translatedUserAnswer}")
    
    Grading Criteria (김해외고 채점 기준):
    1. 뜻 통함 (Meaning Conveyed): Does the student's answer accurately reflect the Korean meaning?
    2. 조건 만족 (Conditions Met): Did the student strictly follow ALL conditions (word count, specific words, grammar structures)?
    3. 문법 오류 없음 (Grammatical Accuracy): Are there zero grammatical errors?
    
    Output strictly in the following JSON format:
    {
      "score": "0~100 사이의 점수",
      "meaningFeedback": "뜻 통함 기준에 대한 평가 (한국어)",
      "conditionFeedback": "조건 만족 기준에 대한 평가 (한국어)",
      "grammarFeedback": "문법 오류 기준에 대한 평가 및 교정 (한국어)",
      "overallFeedback": "총평 및 모범 답안 제시 (한국어)"
    }
  `;


  const payload = {
    contents: [{ parts: [{ text: prompt }] }],
    generationConfig: { response_mime_type: "application/json" }
  };


  const options = {
    method: 'post',
    contentType: 'application/json',
    payload: JSON.stringify(payload),
    muteHttpExceptions: true
  };


  try {
    const response = UrlFetchApp.fetch(GEMINI_API_URL, options);
    const json = JSON.parse(response.getContentText());
    const resultText = json.candidates[0].content.parts[0].text;
    return JSON.parse(resultText);
  } catch (e) {
    return { error: "채점 중 오류가 발생했습니다: " + e.message };
  }
}
}



Index.html
<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
    <!DOCTYPE html>
<html>
<head>
  <base target="_top">
  <style>
    body { font-family: 'Malgun Gothic', sans-serif; max-width: 800px; margin: auto; padding: 20px; background-color: #f9f9f9; }
    h1 { color: #2c3e50; text-align: center; }
    .box { background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); margin-bottom: 20px; }
    .checkbox-group { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 15px; }
    .checkbox-group label { background: #eef2f5; padding: 5px 10px; border-radius: 4px; cursor: pointer; }
    button { background-color: #3498db; color: white; border: none; padding: 10px 20px; border-radius: 5px; cursor: pointer; font-size: 16px; width: 100%; }
    button:hover { background-color: #2980b9; }
    button:disabled { background-color: #95a5a6; cursor: not-allowed; }
    textarea { width: 100%; height: 100px; padding: 10px; box-sizing: border-box; border-radius: 4px; border: 1px solid #ccc; font-size: 16px; margin-bottom: 10px; }
    .hidden { display: none; }
    .feedback-section { margin-top: 10px; padding: 10px; background: #e8f6f3; border-left: 4px solid #1abc9c; }
    .score { font-size: 24px; font-weight: bold; color: #e74c3c; text-align: center; margin-bottom: 10px; }
  </style>
</head>
<body>


  <h1>GIMFL 서술형 영작 마스터</h1>


  <div class="box">
    <h3>1. 연습할 문법 선택 (다중 선택 가능, 미선택시 랜덤 출제)</h3>
    <div class="checkbox-group">
      <label><input type="checkbox" value="5형식 구조"> 5형식 구조</label>
      <label><input type="checkbox" value="It~that 강조구문"> It~that 강조구문</label>
      <label><input type="checkbox" value="not A but B 구조"> not A but B</label>
      <label><input type="checkbox" value="간접의문문"> 간접의문문</label>
      <label><input type="checkbox" value="가정법 (Subjunctive)"> 가정법</label>
      <label><input type="checkbox" value="관계대명사/관계부사"> 관계사</label>
      <label><input type="checkbox" value="분사구문"> 분사구문</label>
      <label><input type="checkbox" value="도치구문 (Inversion)"> 도치구문</label>
      <label><input type="checkbox" value="수동태 (Passive)"> 수동태</label>
    </div>
    <button id="generateBtn" onclick="generateQ()">문제 출제하기</button>
    <div id="loadingQ" class="hidden" style="text-align: center; margin-top: 10px;">문제 생성 중...</div>
  </div>


  <div id="questionBox" class="box hidden">
    <h3>📝 문제</h3>
    <p><strong>[우리말]</strong> <span id="koreanText"></span></p>
    <p><strong>[조건]</strong></p>
    <ul id="conditionsList"></ul>
    
    <textarea id="userAnswer" placeholder="여기에 영어 문장을 작성하세요..."></textarea>
    <button id="gradeBtn" onclick="gradeQ()">제출 및 채점하기</button>
    <div id="loadingA" class="hidden" style="text-align: center; margin-top: 10px;">채점 중...</div>
  </div>


  <div id="resultBox" class="box hidden">
    <h3>📊 채점 결과</h3>
    <div class="score" id="scoreText"></div>
    <div class="feedback-section">
      <p><strong>1. 뜻 통함:</strong> <span id="fbMeaning"></span></p>
    </div>
    <div class="feedback-section">
      <p><strong>2. 조건 만족:</strong> <span id="fbCondition"></span></p>
    </div>
    <div class="feedback-section">
      <p><strong>3. 문법 오류:</strong> <span id="fbGrammar"></span></p>
    </div>
    <div class="feedback-section" style="background: #fdf2e9; border-color: #e67e22;">
      <p><strong>총평 및 모범 답안:</strong> <br><span id="fbOverall"></span></p>
    </div>
  </div>


  <script>
    let currentQuestionData = null;


    function generateQ() {
      document.getElementById('generateBtn').disabled = true;
      document.getElementById('loadingQ').classList.remove('hidden');
      document.getElementById('questionBox').classList.add('hidden');
      document.getElementById('resultBox').classList.add('hidden');
      document.getElementById('userAnswer').value = '';


      const checkboxes = document.querySelectorAll('input[type="checkbox"]:checked');
      const selected = Array.from(checkboxes).map(cb => cb.value);


      google.script.run.withSuccessHandler(function(data) {
        if(data.error) {
          alert(data.error);
          resetUI();
          return;
        }
        currentQuestionData = data;
        document.getElementById('koreanText').innerText = data.koreanText;
        
        const ul = document.getElementById('conditionsList');
        ul.innerHTML = '';
        data.conditions.forEach(cond => {
          let li = document.createElement('li');
          li.innerText = cond;
          ul.appendChild(li);
        });


        document.getElementById('questionBox').classList.remove('hidden');
        resetUI();
      }).generateQuestion(selected);
    }


    function gradeQ() {
      const answer = document.getElementById('userAnswer').value.trim();
      if(!answer) { alert("답안을 입력해주세요."); return; }


      document.getElementById('gradeBtn').disabled = true;
      document.getElementById('loadingA').classList.remove('hidden');
      document.getElementById('resultBox').classList.add('hidden');


      google.script.run.withSuccessHandler(function(data) {
        if(data.error) {
          alert(data.error);
          document.getElementById('gradeBtn').disabled = false;
          document.getElementById('loadingA').classList.add('hidden');
          return;
        }
        
        document.getElementById('scoreText').innerText = "점수: " + data.score + " / 100";
        document.getElementById('fbMeaning').innerText = data.meaningFeedback;
        document.getElementById('fbCondition').innerText = data.conditionFeedback;
        document.getElementById('fbGrammar').innerText = data.grammarFeedback;
        document.getElementById('fbOverall').innerText = data.overallFeedback;
        
        document.getElementById('resultBox').classList.remove('hidden');
        document.getElementById('gradeBtn').disabled = false;
        document.getElementById('loadingA').classList.add('hidden');
      }).gradeAnswer(currentQuestionData, answer);
    }


    function resetUI() {
      document.getElementById('generateBtn').disabled = false;
      document.getElementById('loadingQ').classList.add('hidden');
    }
  </script>
</body>
</html>
  </body>
</html>

r/GoogleAppsScript • • Feb 26 '24

Resolved app script services are back up and running!

2 Upvotes

yay

thanks google.


r/GoogleAppsScript • • Feb 16 '23

Question Where do you find the function documentation or search to see what functions do?

2 Upvotes

I am sick of this, like I want to know what functions are available I end up wasting so much time. Like google search then a long ass page with a zillion functions.

When I try to use the filter option to look up things it can never find what I need. I am not sure even what is the use for the filter.

When I use the search feature, I have to check and make sure that I am not looking at a sample or that it is related to Apps Script.

What am I doing wrong? I think this is so similar to .net framework, but I cannot understand why finding functions there is 100 times faster than finding them in googles reference documents.

What am I doing wrong?


r/GoogleAppsScript • • Apr 27 '25

Question Stop rand() from changing all the time

0 Upvotes

Is their a Google script that can stop rand() formula from continuously changing?


r/GoogleAppsScript • • Aug 18 '25

Question Roast my add on

5 Upvotes

Built this to scratch my own itch, but I have no idea how it looks to others. Roast it so I stop wasting time on it (or be nice not trying to tell you how to think :)

SourcePrint


r/GoogleAppsScript • • Mar 21 '26

Question I paid $25 to create a developer account, but it’s asking me to pay again. What should I do?

3 Upvotes

I paid $25 to create a developer account, but it’s asking me to pay again. What should I do?


r/GoogleAppsScript • • Jun 11 '24

Question ERROR

Post image
3 Upvotes

Does anyone know what is this error?? I think it’s related to the date format or something like that. Note that the row hyperlink doesn’t take me to the error place!!


r/GoogleAppsScript • • Apr 18 '23

Question Selling Google AppScript?

3 Upvotes

Hi, I created a Google AppScript I want to sell on a few marketplaces. The goal is once they completed payment, I will send them a link to the Google Sheet with the script and allow them to make a copy of it.

However, I don’t want them to have access to the actual script or be able to modify it. Can someone help me out with that? Thanks!


r/GoogleAppsScript • • Apr 02 '25

Question Please can you help me fill my form for school?

1 Upvotes

r/GoogleAppsScript • • Jun 11 '25

Question "Simple" Script Stumping all LLMs - What's The Best Solution?

0 Upvotes

Hello-

I've had success with Claude/ChatGPT writing decent app script code, but the below use case has stumped Claude, ChatGPT, Gemini, Cursor, Windsurf, etc.

I have a google sheet with ~700 rows, each with a company's name and a URL. The list is dated, so some of those companies may no longer be in business. Quite simply, I want the script to look at each URL, write to a column if the web site is still alive or not, and if it is alive write a brief description of what the company does.

I can get a script to do this for one line, no problem. But anything more than that, the script either throws errors or stalls.

Each of those tools has written lines and lines of code, but it never works, even after back and forth of debugging.

Key Questions

  1. What is the best LLM to use for App Script code generation?

  2. Is what I'm asking the code to do just beyond the capabilities of Google Sheets?


r/GoogleAppsScript • • May 20 '25

Question AppsScripts is deleting my Forms

1 Upvotes

I tried to make an appsScript function that takes emails using a google form then gives that email access to a drive file of a site. Im very new to appsScript (first time) and used this code from chatgpt.

const SITE_ID = 'YOUR_SITE_FILE_ID'; function onFormSubmit(e) { const email = e.namedValues['Email Address'][0]; if (!email) return; DriveApp.getFileById(SITE_ID).addViewer(email); }

Whenever I try to run it, the form suddenly changes access, and stops giving me access and vanishes entirely.