const scriptProp = PropertiesService.getScriptProperties().getProperties(); const KEY = scriptProp.TRELLO_API_KEY; const TOKEN = scriptProp.TRELLO_TOKEN; const LISTID = scriptProp.TRELLO_LIST_ID; /** * Trello カード */ type Card = { id: string; name: string; idMembers: string[]; }; /** * スプレッドシートのシート名 */ const SheetName = { Settings: 'Settings', Knowledge: 'Knowledge List', } as const; /** * 用途別のセル ID(:は矩形範囲指定) */ const CellId = { BaseUrl: 'B2', Term: 'B3', TitleArea: 'B:B' } as const; /** * 用途別の列番号 */ const ColNumber = { Title: 2, // ナレッジのタイトル列番号(B列) UserName: 4, // 追加した人(D列) Term: 5, // 集計期間(E列) } as const; /** * URLFetchApp.fetch のオプション */ const Option = { Get: { 'method': 'GET' }, Put: { 'method': 'PUT' }, } as const; /** * スプレッドシートのメニューにボタンを追加する * メニューは [ヘルプ] の右に追加される */ const onOpen = () => SpreadsheetApp.getUi() .createMenu('Trelloからインポートする') .addItem('インポート', 'execute') .addToUi(); /** * Settings シートの設定値を取得する * 固定値はこちらから取得 * * @see SheetName.Settings のシート名がなければエラーになるから事前に作ってね * * @param callId - Settings シートのセル ID 'A1', 'B2' など * @return 設定値 */ const getSettingValue = (cellId: string): string => SpreadsheetApp.getActiveSpreadsheet() .getSheetByName(SheetName.Settings)!.getRange(cellId) .getValue(); const BASEURL = getSettingValue(CellId.BaseUrl); const TERM = getSettingValue(CellId.Term); /** * スケジュールトリガーで毎時実行される */ const execute = () => { const cards = getCards(); if (cards.length !== 0) { addSpreadSheet(cards); } } /** * カードを取得する * * @returns カードリスト */ const getCards = (): Card[] => { const url = `${BASEURL}lists/${LISTID}/cards?key=${KEY}&token=${TOKEN}`; return fetchUrl(url, Option.Get); } /** * カードをアーカイブ(削除)する * * @param cardId カード ID */ const archiveCard = (cardId: string) => { const url = `${BASEURL}cards/${cardId}?key=${KEY}&token=${TOKEN}&closed=true`; fetchUrl(url, Option.Put); } /** * HTTP リクエストを送信する * * @param url 対象 URL * @param option HTTP メソッド等 * @returns レスポンス JSON オブジェクト */ const fetchUrl = (url: string, option: object) => { const response = UrlFetchApp.fetch(url, option); return JSON.parse(response.getContentText()); } /** * スプレッドシートにカードの情報を登録する * * @param cards カードリスト */ const addSpreadSheet = (cards: Card[]) => { const activeSpreadSheet = SpreadsheetApp.getActiveSpreadsheet(); const sheet = activeSpreadSheet.getSheetByName(SheetName.Knowledge); if (!sheet) { console.log("Knowledge List シートがないよ。シートを確認してね。"); return; } const titles = sheet.getRange(CellId.TitleArea).getValues(); // B 列(ナレッジタイトル)最終行の次の行から書き込む const lastRowNumber = titles.filter(String).length + 1; cards.map((card, index) => { // 最終行 + 1, 入力したい列を指定して入力セルを決定する sheet.getRange(lastRowNumber + index, ColNumber.Title).setValue(card.name); sheet.getRange(lastRowNumber + index, ColNumber.UserName).setValue(getUserName(card.idMembers[0])); sheet.getRange(lastRowNumber + index, ColNumber.Term).setValue(TERM); // 記録後カードをアーカイブ archiveCard(card.id); }) } /** * ユーザー ID からフルネームを取得 * * @returns ユーザーのフルネーム */ const getUserName = (userId: string): string => { const url = `${BASEURL}members/${userId}?key=${KEY}&token=${TOKEN}`; return fetchUrl(url, Option.Get).fullName; }