You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过Google App Script访问他人主日历并实现人员配对及空闲查询?

Hey Sebastian, 我之前在Google Workspace环境下开发过类似的日程配对工具,刚好能解答你的问题。下面分两部分来说明实现方案:

一、无需订阅即可查询用户空闲状态的方法

要查询同事的空闲状态,核心是用Google Calendar API的Free/Busy查询接口,这个接口不需要预先订阅对方的日历,只要满足以下条件:

  • 你们处于同一个Google Workspace域(如果是个人账号,对方需要在日历设置里允许你查看他们的空闲信息);
  • 你的脚本拥有正确的API权限和OAuth2授权范围。

关键细节:

  1. 权限配置:
    你的Google Apps Script需要启用Calendar API,并且授权范围至少包含https://www.googleapis.com/auth/calendar.readonly(如果还要创建会议,需要https://www.googleapis.com/auth/calendar)。在脚本编辑器里可以通过「资源」→「高级Google服务」开启Calendar API。

  2. Free/Busy查询示例:
    下面是调用接口获取多个用户空闲状态的代码片段:

    function getFreeBusyStatus(userEmails, startTime, endTime) {
      // 构建查询参数
      var request = {
        timeMin: startTime.toISOString(),
        timeMax: endTime.toISOString(),
        items: userEmails.map(email => ({ id: email }))
      };
    
      // 调用Calendar API的freebusy.query方法
      var response = Calendar.Freebusy.query(request);
      var freeBusyData = response.calendars;
    
      // 整理结果:返回每个用户的忙碌时段
      let result = {};
      for (let email in freeBusyData) {
        result[email] = freeBusyData[email].busy || [];
      }
      return result;
    }
    
二、开发自动配对并发送会议邀请的脚本

整个流程分为四个核心步骤,下面逐步说明:

1. 准备用户列表和时间范围

首先定义要配对的用户邮箱列表,以及你要查询的时间范围(比如未来一周的工作日):

const USER_LIST = ["colleague1@yourdomain.com", "colleague2@yourdomain.com", "colleague3@yourdomain.com"];
const START_DATE = new Date(); // 今天
const END_DATE = new Date(START_DATE.getTime() + 7 * 24 * 60 * 60 * 1000); // 7天后

2. 获取所有用户的空闲状态

调用上面的getFreeBusyStatus方法,拿到每个用户的忙碌时段,然后计算出他们的空闲时段:

function calculateFreeTimes(userBusyTimes, startTime, endTime) {
  // 初始化空闲时段为整个查询范围
  let freeTimes = [{ start: startTime, end: endTime }];

  // 遍历每个忙碌时段,切割空闲时段
  userBusyTimes.forEach(busy => {
    let busyStart = new Date(busy.start);
    let busyEnd = new Date(busy.end);
    let newFreeTimes = [];

    freeTimes.forEach(free => {
      let freeStart = free.start;
      let freeEnd = free.end;

      // 忙碌时段在空闲时段之前,保留原空闲时段
      if (busyEnd <= freeStart) {
        newFreeTimes.push(free);
      }
      // 忙碌时段在空闲时段之后,保留原空闲时段
      else if (busyStart >= freeEnd) {
        newFreeTimes.push(free);
      }
      // 忙碌时段覆盖部分空闲时段,拆分出前后两段
      else {
        if (freeStart < busyStart) {
          newFreeTimes.push({ start: freeStart, end: busyStart });
        }
        if (busyEnd < freeEnd) {
          newFreeTimes.push({ start: busyEnd, end: freeEnd });
        }
      }
    });

    freeTimes = newFreeTimes;
  });

  // 过滤掉短于30分钟的空闲时段(可自定义时长)
  return freeTimes.filter(free => (free.end - free.start) >= 30 * 60 * 1000);
}

3. 配对用户并查找共同空闲时段

接下来可以写一个配对逻辑(比如随机配对,或者按特定规则配对),然后对比每对用户的空闲时段,找到交集:

function findCommonFreeTime(user1Free, user2Free) {
  let commonTimes = [];
  let i = 0, j = 0;

  while (i < user1Free.length && j < user2Free.length) {
    let u1Start = user1Free[i].start;
    let u1End = user1Free[i].end;
    let u2Start = user2Free[j].start;
    let u2End = user2Free[j].end;

    // 计算交集的开始和结束时间
    let overlapStart = new Date(Math.max(u1Start.getTime(), u2Start.getTime()));
    let overlapEnd = new Date(Math.min(u1End.getTime(), u2End.getTime()));

    // 如果有有效交集(时长>=30分钟)
    if (overlapEnd > overlapStart && (overlapEnd - overlapStart) >= 30 * 60 * 1000) {
      commonTimes.push({ start: overlapStart, end: overlapEnd });
    }

    // 移动指针:结束时间早的先推进
    if (u1End <= u2End) {
      i++;
    } else {
      j++;
    }
  }

  return commonTimes;
}

// 示例:随机配对用户
function pairUsers(userList) {
  let shuffled = [...userList].sort(() => Math.random() - 0.5);
  let pairs = [];
  for (let i = 0; i < shuffled.length; i += 2) {
    if (i + 1 < shuffled.length) {
      pairs.push([shuffled[i], shuffled[i+1]]);
    }
  }
  return pairs;
}

4. 创建并发送会议邀请

找到共同空闲时段后,就可以调用Calendar API创建会议,邀请对应的用户:

function createMeeting(attendees, startTime, endTime, title = "自动配对会议") {
  let event = {
    summary: title,
    start: {
      dateTime: startTime.toISOString(),
      timeZone: Session.getScriptTimeZone()
    },
    end: {
      dateTime: endTime.toISOString(),
      timeZone: Session.getScriptTimeZone()
    },
    attendees: attendees.map(email => ({ email: email })),
    sendUpdates: "all" // 发送邀请给所有参会者
  };

  // 创建会议
  try {
    Calendar.Events.insert(event, "primary");
    console.log(`已为${attendees.join(", ")}创建会议:${title}`);
  } catch (e) {
    console.error(`创建会议失败:${e.message}`);
  }
}

整合所有流程

最后把这些函数整合起来,运行主函数即可:

function runMatchingAndMeeting() {
  // 1. 获取所有用户的忙碌状态
  let busyStatus = getFreeBusyStatus(USER_LIST, START_DATE, END_DATE);

  // 2. 计算每个用户的空闲时段
  let userFreeTimes = {};
  for (let email in busyStatus) {
    userFreeTimes[email] = calculateFreeTimes(busyStatus[email], START_DATE, END_DATE);
  }

  // 3. 配对用户
  let pairs = pairUsers(USER_LIST);

  // 4. 为每对用户查找共同空闲并创建会议
  pairs.forEach(pair => {
    let [user1, user2] = pair;
    let commonFree = findCommonFreeTime(userFreeTimes[user1], userFreeTimes[user2]);

    if (commonFree.length > 0) {
      // 取第一个可用的共同空闲时段
      let firstFree = commonFree[0];
      createMeeting(pair, firstFree.start, firstFree.end);
    } else {
      console.log(`${user1}和${user2}在指定时间范围内没有共同空闲时段`);
    }
  });
}
注意事项
  • 权限验证:第一次运行脚本时,需要授权访问Calendar API,确保你有足够的权限(如果是Workspace管理员,可以设置域内的日历共享权限,让所有用户默认允许域内成员查看空闲状态);
  • 时间时区:脚本中使用Session.getScriptTimeZone()获取时区,确保和用户的日历时区一致;
  • 错误处理:可以根据需要添加更多错误处理逻辑,比如处理API调用失败、用户邮箱无效等情况。

内容的提问来源于stack exchange,提问作者Sebastian

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 09:28:56