星期四, 9月 17, 2026

[GAS] Gemini API

在 GAS 內呼叫 Gemini 來進行應用,該筆記分兩部分
  • Google AI Studio 申請 API Key
  • 在 GAS 內透過 UrlFetchApp.fetch 呼叫 Gemini

申請 API Key

進入 Google AI Studio 建立 API Key,步驟依序為

Google AI Studio => MANAGE => Dashboard
PROJECT => API Keys
API Keys 內右上角 [Create API Key]
Create a new Key 頁面,專案選項可以在該畫面新增、選擇現有或是匯入
API Key Detail

呼叫 Gemini

重點
  • 官方文件-互動 API 內有使用模型可以參考,該範例是使用 gemini-3.8-flash
  • Gemini API 內有 response json 範例可以參考,看完後才能比較理解 getGeminiText() 內是在拆解什麼

官方 response json 範例
{
  "id": "v1_ChdPU0F4YWFtNkFwS2kxZThQZ05lbXdROBIXT1NBeGFhbTZBcEtpMWU4UGdOZW13UTg",
  "model": "gemini-3-flash-preview",
  "status": "completed",
  "object": "interaction",
  "created": "2025-11-26T12:25:15Z",
  "updated": "2025-11-26T12:25:15Z",
  "steps": [
    {
      "type": "model_output",
      "content": [
        {
          "type": "text",
          "text": "I'm doing great, thank you for asking! How can I help you today?"
        }
      ]
    }
  ]
}
code.gs
const GEMINI_MODEL = "gemini-3.8-flash";

/**
 * 呼叫 Gemini API
 *
 * @param {string} prompt 使用者輸入的問題
 * @return {string} Gemini 回覆
 */
function askGemini(prompt) {

  // 1. 取得 API Key
  const apiKey = PropertiesService
    .getScriptProperties()
    .getProperty("GEMINI_API_KEY");

  if (!apiKey) {
    throw new Error("找不到 GEMINI_API_KEY,請先設定 Script Properties。");
  }

  // 2. Gemini API URL
  const url = "https://generativelanguage.googleapis.com/v1beta/interactions";

  // 3. 要傳給 Gemini 的資料
  const payload = {
    model: GEMINI_MODEL,
    input: prompt
  };

  // 4. HTTP Request 設定
  const options = {
    method: "post",
    contentType: "application/json",

    headers: {
      "x-goog-api-key": apiKey
    },

    payload: JSON.stringify(payload),

    muteHttpExceptions: true
  };

  // 5. 呼叫 Gemini
  const response = UrlFetchApp.fetch(url, options);

  // 6. 取得 HTTP Status Code
  const statusCode = response.getResponseCode();

  // 7. 取得 JSON 字串
  const responseText = response.getContentText();

  // 8. API 發生錯誤
  if (statusCode < 200 || statusCode >= 300) {

    throw new Error(
      "Gemini API 呼叫失敗\n" +
      "HTTP Status: " + statusCode + "\n" +
      responseText
    );
  }

  // 9. JSON → JavaScript Object
  const data = JSON.parse(responseText);

  // 10. 取得 Gemini 回覆文字
  const result = getGeminiText(data);

  return result;
}


/**
 * 從 Gemini Interaction Response 取得文字
 */
function getGeminiText(data) {

  if (!data.steps) {
    throw new Error("Gemini Response 沒有 steps 資料。");
  }

  let result = [];

  data.steps.forEach(step => {

    if (step.type !== "model_output") {
      return;
    }

    if (!step.content) {
      return;
    }

    step.content.forEach(content => {
      if (content.type === "text" && content.text) {
        result.push(content.text);
      }
    });

  });

  if (result.length === 0) {
    throw new Error("Gemini 沒有回傳文字內容。");
  }

  return result.join("\n");
}
測試
/**
 * 測試 Gemini API
 */
function testGemini() {

  const prompt = "請用 100 個字介紹高雄。";

  const result = askGemini(prompt);

  console.log(result);
}

星期六, 9月 05, 2026

[Docs] 清除格式

從 Gemini 要把對話匯成 Docs 時,常常會發生排版不如預期的情況,這時候可以透過清除格式來重置整份文件後,再依需求去進行排版設計

清除格式快捷鍵為 Ctrl + 反斜線 \

星期三, 9月 02, 2026

[GAS] 自訂側欄

用 agy 寫網頁應用程式時,AI 自行在 Google Sheet 內設計 sidebar 來呈現網頁應用程式,那就來筆記 sidebar 效果囉

code.gs
/**
 * 當試算表開啟時,自動新增自訂功能表
 */
function onOpen() {
  SpreadsheetApp.getUi()
    .createMenu('🛠️ 自訂功能')
    .addItem('開啟側邊欄', 'showSidebar')
    .addToUi();
}

/**
 * 載入 Sidebar.html 並在右側開啟側邊欄
 */
function showSidebar() {
  const htmlOutput = HtmlService.createHtmlOutputFromFile('Sidebar')
    .setTitle('側邊欄說明')
    .setWidth(300); // 側邊欄預設寬度通常為 300px
  
  SpreadsheetApp.getUi().showSidebar(htmlOutput);
}
sidebar.html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <!-- 引入 Google Material/標準風格樣式簡潔化介面 -->
    <link rel="stylesheet" href="https://ssl.gstatic.com/docs/script/css/add-ons1.css">
    <style>
      body {
        padding: 12px;
        font-family: Arial, sans-serif;
        color: #333;
        line-height: 1.5;
      }
      .card {
        background-color: #f8f9fa;
        border: 1px solid #dadce0;
        border-radius: 8px;
        padding: 12px;
        margin-bottom: 12px;
      }
      h2 {
        margin-top: 0;
        color: #1a73e8;
        font-size: 18px;
      }
      ul {
        padding-left: 20px;
        margin: 8px 0;
      }
      li {
        margin-bottom: 6px;
      }
      .footer {
        font-size: 12px;
        color: #70757a;
        margin-top: 20px;
        border-top: 1px solid #eee;
        padding-top: 8px;
      }
    </style>
  </head>
  <body>
    <h2>📌 側邊欄功能說明</h2>
    
    <div class="card">
      <p>歡迎使用自訂側邊欄!這是一個透過 <code>HtmlService</code> 建立的客製化介面。</p>
    </div>

    <h3>主要特色</h3>
    <ul>
      <li><strong>即時操作:</strong> 不影響目前試算表的編輯流程。</li>
      <li><strong>雙向溝通:</strong> 可透過 <code>google.script.run</code> 與後端 Apps Script 進行資料交換。</li>
      <li><strong>豐富介面:</strong> 支援 HTML5、CSS3 及 JavaScript 各種前端框架。</li>
    </ul>

    <div class="footer">
      💡 提示:點擊右上角「✕」即可隨時關閉此側邊欄。
    </div>
  </body>
</html>

gs code 內的 OnOpen() 在 Google Sheet 開啟時會有自訂功能

透過 sidebar 開啟 sitebar.html 網頁

星期二, 9月 01, 2026

[GAS] 網頁應用程式-執行身分、誰可以存取

透過 clasp 來進行部屬時發現 clasp deploy 部屬網頁應用程式,是沒有參數可以指定 [執行身分] 和 [誰可以存取],查發現這兩個設定是要在 appsscript.json 內進行設定

appsscript.json 內的執行身分 (executeAs) 和 誰可以存取 (access)
{
  "timeZone": "Asia/Taipei",
  "dependencies": {},
  "exceptionLogging": "STACKDRIVER",
  "runtimeVersion": "V8",
  "webapp": {
    "executeAs": "USER_DEPLOYING",
    "access": "MYSELF"
  }
}
access 參數
  • MYSELF:只有部署者可以執行
  • ANYONE:任何登入 Google 帳號的使用者
  • ANYONE_ANONYMOUS:任何使用者,即使未登入也適用
  • DOMAIN:只有與部署者位於相同網域的使用者才能執行(限 Google Workspace)
executeAs 參數
  • USER_ACCESSING:網頁應用程式會以存取者的身分執行
  • USER_DEPLOYING:網頁應用程式會以部署者的身分執行

星期一, 8月 31, 2026

[NoteBook] 移除浮水印

之前在 [Gemini] 媒體浮水印 記錄過 Gemini 可以移除媒體浮水印,Gemini Notebook 也有選項可以移除浮水印,不知道何時這類設定才要統一