Backlogのwiki記事をAPIで自動作成するGAS

2021年10月30日土曜日

(前記事の流れを完全に無視して) 
backlogのwiki記事を自動作成するGASです! 

概要

  • Backlogプロジェクトのwikiについて、指定URL配下に記事を作成する。
  • テンプレートを元に作成記事単位でシートを作成、必要情報記入する(Spreadsheet例については後述)。
  • 月~金曜日朝に実行され(トリガー自体は毎朝実行設定)、シート内容通りのタイミングで記事を作成する。
  • 定例MTGの議事録定期作成が前提のため、ページ名はすべて日付で作成される。
  • 作成結果はGoogleChatへ投稿される。

コード

コードはこんな感じ、技術的に特別なことはしていないので説明は省略。
function createLogPageMain() {
let nowDate = new Date()
//月~金のみ処理する。
if(!(isExecutionDay(nowDate))){
return
}
//設定内容シート群を取得
let targetSheets = SpreadsheetApp.getActiveSpreadsheet().getSheets()
let createdLogStrings = ""
//シートごとにループ
targetSheets.forEach(function(targetSheet){
if (targetSheet.getName() == "説明書" || targetSheet.getName() == "テンプレート"){
return
}
let parametersBuf = targetSheet.getRange(1,2,8,1).getValues()
let parameters = {
projectID:parametersBuf[0][0],
span:parametersBuf[1][0],
weekScaleDate1:parametersBuf[2][0],
weekScaleDate2:parametersBuf[3][0],
monthScaleDate:parametersBuf[4][0],
rootPath:parametersBuf[5][0],
notice:parametersBuf[6][0],
contents:parametersBuf[7][0]
}
//処理対象日か判定
if (isCreateDate(nowDate,parameters)){
//対象日の場合記事を作成
let logURL = createLogPage(nowDate,parameters)
createdLogStrings = "<" + logURL + "|" + targetSheet.getName() + ">" + "\n"
}
})
//作成結果をチャットへ投稿
postResult(createdLogStrings)
}
function isExecutionDay(nowDate){
//処理当日が月~金か判定
if ( nowDate.getDay() >= 1 && nowDate.getDay() <= 5 ){
return true
} else {
return false
}
}
function isCreateDate(nowDate,parameters){
//シートの設定内容から、処理当日が作成対象日か判定する。
//日次
if(parameters.span == "日次"){
return true
}
//週次
if(parameters.span == "週次" && isWeek1(nowDate,parameters.weekScaleDate1)){
return true
}
//隔週
if(parameters.span == "隔週" && isWeek2(nowDate,parameters.weekScaleDate2)){
return true
}
//月末
if(parameters.span == "月次" && isMonth(nowDate,parameters.monthScaleDate)){
return true
}
}
function createLogPage(nowDate,parameters){
//BacklogAPIで議事録を作成
var url = "https://hoge.backlog.com/api/v2/wikis?apiKey=hoge";
var payload = {
"projectId" : Number(parameters.projectID),
"name" : String(parameters.rootPath + "/" + getDateStrings(nowDate)),
"content" : String(parameters.contents),
"mailNotify" : parameters.notice
}
var options = {
"method" : "POST",
"contentType": "application/json",
"payload" : JSON.stringify(payload)
}
var response = UrlFetchApp.fetch(url, options);
return response.getAllHeaders().Location
}
function isWeek1(nowDate,weekScaleDate1){
let dayDictionary = {
"月":1,
"火":2,
"水":3,
"木":4,
"金":5
}
if ( nowDate.getDay() == dayDictionary[weekScaleDate1] ){
return true
} else {
return false
}
}
function isWeek2(nowDate,weekScaleDate2){
//処理当日が基準日から数えて14の倍数日目の場合true
let dateTo = new Date(nowDate.getFullYear(),nowDate.getMonth(),nowDate.getDate())
let dateFrom = new Date(weekScaleDate2.getFullYear(),weekScaleDate2.getMonth(),weekScaleDate2.getDate())
let termDay = (dateTo - dateFrom) / 86400000;
if (termDay % 14 == 0) {
return true
}else{
return false
}
}
function isMonth(nowDate,monthScaleDate){
//基準日の設定に基づいて日付を算出、処理当日か判定
let dateYYYYMMDD = new Date(nowDate.getFullYear(),nowDate.getMonth(),nowDate.getDate())
//各指定日を算出
let targetDate
switch(monthScaleDate){
case "1":
case "初月":
case "初火":
case "初水":
case "初木":
case "初金":
let monthStartDate = getMonthStartDate(nowDate)
targetDate = getMonthTargetDate(monthScaleDate,monthStartDate)
break;
case "末月":
case "末火":
case "末水":
case "末木":
case "末金":
let monthEndDate = getMonthEndDate(nowDate)
targetDate = getMonthTargetDate(monthScaleDate,monthEndDate)
break;
default:
targetDate = null
}
//算出した日付と処理当日が等しければtrue
if(dateYYYYMMDD.getTime() === targetDate.getTime()){
return true
}else{
return false
}
}
function getMonthStartDate(nowDate){
return new Date(nowDate.getFullYear(),nowDate.getMonth(),1)
}
function getMonthEndDate(nowDate){
return new Date(nowDate.getFullYear(),nowDate.getMonth()+1,0)
}
function getMonthTargetDate(monthScaleDate,monthDate){
let dayDictionary = {
"月":1,
"火":2,
"水":3,
"木":4,
"金":5
}
if(monthScaleDate == "1"){
return monthDate
}else if(monthScaleDate.slice(0,1)=="初"){
let targetDayNumber = dayDictionary[monthScaleDate.slice(-1)]
let monthStartDayNumber = monthDate.getDay()
if(targetDayNumber >= monthStartDayNumber){
return new Date(monthDate.getFullYear(),monthDate.getMonth(),1 + targetDayNumber - monthStartDayNumber )
}else{
return new Date(monthDate.getFullYear(),monthDate.getMonth(),1 + targetDayNumber - monthStartDayNumber + 7)
}
}else{
let targetDayNumber = dayDictionary[monthScaleDate.slice(-1)]
let monthEndDayNumber = monthDate.getDay()
if(targetDayNumber >= monthEndDayNumber){
return new Date(monthDate.getFullYear(),monthDate.getMonth() + 1,0 + targetDayNumber - monthEndDayNumber - 7 )
}else{
return new Date(monthDate.getFullYear(),monthDate.getMonth() + 1,0 + targetDayNumber - monthEndDayNumber)
}
}
}
function getDateStrings(nowDate){
return nowDate.getFullYear() + "/" + String(nowDate.getMonth() + 1) + "/" + nowDate.getDate()
}
function postResult(createdLogStrings){
//作成した議事録が無ければチャットへ投稿しない
if (createdLogStrings == "") {
return
}
var message = "【本日下記の議事録をBacklogへ作成しました。】\n\n" + createdLogStrings
var url = "webhookのURL";
var payload = {
"text" : message
}
var json = JSON.stringify(payload);
var options = {
'method': 'POST',
'contentType': 'application/json; charset=UTF-8',
"payload" : json
};
var response = UrlFetchApp.fetch(url, options);
}
view raw createLog.gs hosted with ❤ by GitHub
強いて言えば、月次処理の日付算出方法は少し考えました。
APIキー方式なのでOAuth方式に変更できると安心かな? 
エラー処理も入れてないし、ツギハギな上にコードレビュー一切してないので、きれいにしたい方はご自由にー。
今後はパラメーターバリデーションを入れる予定。 

なお設定するパラメーターは下記の通り、Spreadsheetも公開しておきますー。
使用時はテンプレートをコピーしてください。
「説明書」シートと「テンプレート」シート以外を読み取って記事を作成します。
  • プロジェクトID:作成先プロジェクトのIDを設定。IDは課題一覧のURLやソースから取れます。
  • スパン:作成スパンの設定、「日次、週次、隔週、月次」が設定可能。
    • 日次の場合は月~金曜日のみ作成する。
  • 週次基準曜日:週次設定時の作成曜日。
  • 隔週基準日:隔週設定時の初回作成日。これを基準日として、処理当日との日数を算出、日数が14の倍数日目だった場合に作成する。
    • 例:2021/4/1(木)を基準日とした場合、4/15(木)は14日目、4/29(木)は28日目と14の倍数日目のため作成する。
  • 月次基準日:1日か、月初各曜日(月~金)、月末最終各曜日(月~金)が設定可能。
  • 親ページ:作成する記事の格納先を設定。設定必須。
    • 設定例:議事録/改善MTG"
  • (ページ名):自動設定。定期作成が前提のため、ページ名単独での設定はなく、処理当日でページ名を作成する。
  • 記事内容:Backlogのwiki記法に沿う、Backlog上で要確認。
  • メール通知:True/False、Backlog本体からの作成通知メールを送信するかしないか。