Google apps script 当用户选择范围内的单元格时,Google应用程序脚本执行函数

Google apps script 当用户选择范围内的单元格时,Google应用程序脚本执行函数,google-apps-script,google-sheets,triggers,Google Apps Script,Google Sheets,Triggers,我希望在用户单击/选择给定范围内的单元格时运行一个需要授权的函数。由于授权问题,简单的onSelectionChange(e)触发器不起作用,可安装的触发器显然不包括onSelectionChange 请问还有别的办法吗?谢谢 因此,我最后在每行添加了一个值为“Clear to run function”的列,如果该列中的一个值与“Clear to run function”不同,则使用一个简单的onEdit(e)触发我的函数 从用户体验的角度来看,这意味着清除一个单元格来运行该函数——虽然不理

我希望在用户单击/选择给定范围内的单元格时运行一个需要授权的函数。由于授权问题,简单的onSelectionChange(e)触发器不起作用,可安装的触发器显然不包括onSelectionChange


请问还有别的办法吗?谢谢

因此,我最后在每行添加了一个值为“Clear to run function”的列,如果该列中的一个值与“Clear to run function”不同,则使用一个简单的onEdit(e)触发我的函数


从用户体验的角度来看,这意味着清除一个单元格来运行该函数——虽然不理想,但它是有效的。

对于不涉及
ui
/
HtmlService
的函数,可以使用简单的触发器通过削弱安全性来运行一些需要授权的函数(比如特权函数)

  • 流程:Trigger=>onSelectionChange(没有获取/执行特权函数的授权)=>Trigger自定义函数(获得获取特权函数的授权/没有执行特权函数的授权)=>fetch/post=>webapp(完全授权运行特权函数)

  • 此解决方案的灵感来自,它直接使用可安装触发器和普通访问令牌来授权自定义函数。从安全角度来看,不建议这样做

  • 虽然已采取措施确保执行以下脚本的用户的安全和隐私,但尚未考虑所有攻击向量。脚本在很多方面都可能有漏洞,特别是在平台中缺乏加密模块支持的情况下。如果替代解决方案不可行,请自行承担使用风险

  • 在大多数情况下,首选使用菜单/按钮/时间触发器/可安装触发器(始终在完全授权下运行)的替代解决方案使用OneEdit可安装触发器+复选框可以实现类似的流程

要使用示例脚本,请执行以下步骤:

  • 在清单文件中。对于示例脚本

     "oauthScopes": ["https://www.googleapis.com/auth/script.send_mail"],
    
  • 用于执行需要授权的功能的明确目的

    • 以“我”的身份执行
    • 访问:“任何人”
  • 没有用于从自定义功能授权WebApp的明确目的的角色/权限

  • 并将其复制到示例脚本中的
    creds
    对象

  • 与服务帐户共享您的项目/电子表格(
    client\u email

  • 安装以为服务帐户创建/签署jwt令牌

  • 创建一个用于设置自定义功能的
    hiddenSheet
    ,该自定义功能将被设置为此工作表的A1
    onSelectionChange

  • 当有人触摸电子表格中的任何内容时,以下脚本将发送电子邮件

示例脚本:
复选框会更理想
/**
 * Gets Oauth2 service based on service account with drive scope
 * Drive scope needed to access webapp with access:anyone
 * This does not grant access to the user's drive but the service
 *     account's drive, which will only contain the file shared with it
 */
function getService_() {
  const creds = {
    private_key: '[PRIVATE_KEY]',
    client_email: '[CLIENT_EMAIL]',
  };
  const PRIVATE_KEY = creds['private_key'];
  const CLIENT_EMAIL = creds['client_email'];
  return OAuth2.createService('GoogleDrive:')
    .setTokenUrl('https://oauth2.googleapis.com/token')
    .setPrivateKey(PRIVATE_KEY)
    .setIssuer(CLIENT_EMAIL)
    .setPropertyStore(PropertiesService.getUserProperties())
    .setScope('https://www.googleapis.com/auth/drive');
}
/**
 * @returns {string} base64 encoded string of SHA_512 digest of random uuidstring
 */
const getRandHashKey_ = () =>
  Utilities.base64EncodeWebSafe(
    Utilities.computeDigest(
      Utilities.DigestAlgorithm.SHA_512,
      Utilities.getUuid() //type 4 advertised crypto secure
    )
  );

/**
 * @param {GoogleAppsScript.Events.SheetsOnSelectionChange} e
 */
const onSelectionChange = e => {
  const sCache = CacheService.getScriptCache();
  e.rangestr = e.range.getSheet().getName() + '!' + e.range.getA1Notation();
  const hashRandom = getRandHashKey_();
  sCache.put(hashRandom, JSON.stringify(e), 20);//expires in 20 seconds
  e.source
    .getSheetByName('hiddenSheet')
    .getRange('A1')
    .setValue(`=CALLWEBAPP("${hashRandom}")`);
};
/**
 * Calls published webapp(Access:Anyone) with service account token
 * @customfunction
 * @returns void
 */
const callwebapp = randomHash => {
  const webAppScriptId = '[SCRIPT_ID]';
  UrlFetchApp.fetch(
    `https://script.google.com/macros/s/${webAppScriptId}/exec`,
    {
      method: 'post',
      payload: { e: randomHash },
      headers: { Authorization: `Bearer ${getService_().getAccessToken()}` },
    }
  );
};

/**
 * @param {GoogleAppsScript.Events.AppsScriptHttpRequestEvent} e
 */
const doPost = e => {
  const hashRandom = e.parameter.e;
  const sCache = CacheService.getScriptCache();
  const encodedSelectionEvent = sCache.get(hashRandom);
  if (encodedSelectionEvent) {
    const selectionEvent = JSON.parse(encodedSelectionEvent);
    MailApp.sendEmail(
      '[EMAIL_TO_SEND_NOTIFICATION_TO]',
      'Someone touched your spreadsheet',
      `Wanna take a look? ${selectionEvent.rangestr} was touched without your permission`
    );
  }
};