Google官方支持的NodeJS集成客户端,用以访问Google APIs, 支持OAuth 2.0授信及登录认证。登录以后即可在后台访问例如 Google Drive(云存储), Google Calendar, Gmail等服务。
公司业务需求,需要对公司google的会议日历做一些处理,如已离职人员预约的周期性google会议日历,需要删除,或者给周期性会议日历一个截止日期,周期性会议改成截止到当天的会议日历等。
首先创建一个NodeJS项目
创建项目目录
mkdir node-meeting
初始化
npm init
一直回车确认:
It only covers the most common items, and tries to guess sensible defaults.
See `npm help json` for definitive documentation on these fields
and exactly what they do.
Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.
Press ^C at any time to quit.
package name: (emojiimages)
version: (1.0.0)
description:
entry point: (index.js)
test command:
git repository:
keywords:
author:
license: (ISC)
About to write to /Users/diyuanwang/github/node-meeting/package.json:
{
"name": "node-meeting",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}
Is this OK? (yes)
复制代码
这时候我们已经完成了nodejs项目的初始化工作。
我们得到了一个目录和一个文件node-meeting/package.json 剩下的工作就是编写js文件了。
通过以下命令安装googleapis及其依赖
$ npm install googleapis
google完整的API支持列表 https://developers.google.com/apis-explorer
index.js中引入googleapi
const {google} = require('googleapis');
在引入googleapi之前,请设置创建google服务帐户时的json文件,下载后的json中包含type,service_account,private_key_id,private_key,client_email,client_id,auth_uri,token_uri,auth_provider_x509_cert_url以及client_x509_cert_url,我们要用到的主要是private_key,用此获取到用户的访问Token并刷新此Token防止会话过期。
如果公司已有google的权限json文件,请忽略以下步骤,直接使用已有的json文件,如没有按照下面步骤获取到json文件:
1.在Google Cloud Platform中创建一个项目 2.创建一个服务帐号 3.为服务帐户启用了域范围委派 4.以JSON格式下载了服务帐户的密钥 5.API Manager>我创建了OAuth 2.0客户端ID的凭据 6.为项目启用了Gmail API 在Google Apps管理控制台中:
在“安全性”>“高级设置”>“管理API客户端访问”中,添加了上述步骤4中的客户端ID 为客户端ID添加了所有可能的范围 下载后的json内容如下图:
google.json
google.json放入到node项目中
以下正式开始对某个google周期性会议日历操作,先进行get方法获取到周期性会议的周期规则(recurrence: [ 'RRULE:FREQ=WEEKLY;BYDAY=FR' ]),如每个星期三、五开一次会议,再用获取到的周期规则加上截止日期,通过google api的patch(附上patch api url developers.google.com/calendar/v3… )加上截止日期,那么已离职人员组织的周期性会议会截止到当天日期,已开过的会议不会被删除,已预约的周期性会议截止到当天:
const {google} = require('googleapis');
var dateTime = require('./util/dateTime');
var util = require('./util/index');
var config = require('./config/config');
const googleKey = require('./google.json');
const jwtClient = new google.auth.JWT(googleKey.client_email,null,googleKey.private_key ,['https://www.googleapis.com/auth/calendar'],config.googleAuth);
async function main(item) {
const calendar = google.calendar('v3');
let res = await getRecurrence(calendar, item);
let isNotSetRecurrence = false;
let resRecurrence = res? res.data.recurrence: '',
recurrenceSplit = [],
recurrenceRRULE = [],
recurrence = '',
//UNTIL = '20200411T145959Z';
UNTIL = dateTime.getYearMonthDay() + 'T155959Z';
console.log("周期性会议修改成功",JSON.stringify(res),resRecurrence);
//规则资料 https://blog.csdn.net/csdn15002274757/article/details/94557330
if(!res) return;
if(resRecurrence.length > 1){
resRecurrence.forEach(e => {
let temp = '';
e.split(';').forEach((item,index) => {
//您可以使用COUNT或UNTIL来指定事件重复的结束。请勿在同一规则中同时使用两者。
if(item.indexOf('UNTIL') < 0 && item.indexOf('COUNT') < 0){
// [ 'EXDATE;TZID=Asia/Hong_Kong:20200511T170000,20200518T170000,20200525T170000','RRULE:FREQ=WEEKLY;WKST=MO;UNTIL=20200530T000000Z;BYDAY=FR,MO,TH,WE' ]
temp += item + (item.indexOf('TZID') > -1 ? '': ';');
}
});
if(temp.indexOf('EXDATE') < 0){
temp = temp + 'UNTIL=' + UNTIL;
}
recurrenceSplit = recurrenceSplit.concat([temp]);
});
}else{
if(resRecurrence[0].indexOf('WEEKLY') > -1 && resRecurrence[0].indexOf('INTERVAL') > -1 && resRecurrence[0].indexOf('BYDAY') > -1 && resRecurrence[0].indexOf('WKST') < 0){
if(resRecurrence[0].indexOf('WKST') < 0){
resRecurrence[0] += ';WKST=MO';
}
}
recurrenceSplit = resRecurrence[0].split(';');
recurrenceSplit.forEach(e => {
//您可以使用COUNT或UNTIL来指定事件重复的结束。请勿在同一规则中同时使用两者。
if(e.split('=')[0] != 'UNTIL' && e.split('=')[0] != 'COUNT'){
recurrenceRRULE.push(e);
}
//原本就有截止日期的,不再进行UNTIL赋值删除
if(e.split('=')[0] == 'UNTIL'){
let UNTILInit = e.split('=')[1];
if(UNTILInit < UNTIL){
UNTIL = UNTILInit;
isNotSetRecurrence = true;
}
}
});
recurrenceRRULE = recurrenceRRULE.join(';');
recurrence = `${recurrenceRRULE};UNTIL=${UNTIL}`;
console.log("周期性会议修改成功3333333",resRecurrence.length,recurrence);
}
if(isNotSetRecurrence) return;
calendar.events.patch(
{
auth: jwtClient,
calendarId: item.room,
eventId: item.recurringEventId,
resource: {
recurrence: resRecurrence.length > 1 ? recurrenceSplit: [ recurrence ]
}
}, (err, res) => {
if (err) {
console.error("sss",err);
config.logger.error( '修改会议截止日期失败', item );
} else {
let log = resRecurrence.length > 1 ? recurrenceSplit: [ recurrence ];
config.logger.info( '修改会议截止日期成功' + log, item );
console.log("周期性会议修改成功",res,log);
}
}
);
}
async function getRecurrence(calendar, item){
try{
let recurrence = await calendar.events.get(
{
auth: jwtClient,
calendarId: item.organizer_email,
eventId: item.recurringEventId,
}
)
return recurrence
}catch(e){
calendar.events.delete(
{
auth: jwtClient,
calendarId: item.room,
eventId: item.recurringEventId,
}, (err, res) => {
if (err) {
console.error("sss",err.data);
config.logger.error( '会议室email释放会议失败', item );
}else{
console.log("shanchu",res);
config.logger.info( '会议室email释放会议成功', item );
}
}
)
return false
}
}
let recurrenceGoogleData = [{organizer_email: '....@anker.com',room: '...@resource.calendar.google.com',recurringEventId:'...'}];
for(let item of recurrenceGoogleData){
main(item).catch(e => {
console.error(e);
throw e;
});
}
复制代码
recurringEventId为周期性会议id; config.googleAuth是一个email,config.googleAuth不能为null。 如config.googleAuth为null,运行时会报错:
{[Error: Bad Request]
code: 400,
errors:
[{
domain: 'global',
reason: 'failedPrecondition',
message: 'Bad Request'
}]
}
复制代码
例外一个需要注意的点是,如果用会议组织者的organizer_email通过get方法查找不到此会议,那么只能通过此会议的会议室email通过delete方法释放此会议室资源。 如有其他疑问,请留言,看到后会回复,谢谢