この記事は株式会社サイバー・バズ Advent calendar 2021の記事です。
前置き
GASって便利ですよね。
ちょっとした業務改善に、サイバー・バズでもよく使われています。
claspを使えばTypeScriptで書けるしGitHubで管理できるので、保守性も高いです。
ただ、GASで何回も同じコードを使ってるなあと思ったので
汎用的に役に立ちそうなコード片をまとめてみることにしました。
- 外部APIから値取得
- Slackへ投稿
- メールの送信
- シートの取得または作成
- シートへの値追加
- カスタムメニューの追加
- トリガーの追加
- フォーム回答内容取得
- ファイルの取得
- プロパティの取得
1. 外部APIから値取得
おそらくGASで最も使われていてもおかしくない、UrlFetchAppでのデータ取得です。
headersはobject型、dataはany型でこのままでは使いづらいので、
好みに応じて型情報を設定して使うのが良いでしょう。
export const fetch = (url: string, accessToken: String) => {
const res: GoogleAppsScript.URL_Fetch.HTTPResponse = UrlFetchApp.fetch(url, {
headers: { Authorization: `Bearer ${accessToken}` }
})
const data = JSON.parse(res.getContentText())
const headers = res.getHeaders()
return {
headers,
data
}
}
2. Slackへ投稿
何らかのトリガーの実行後、成功/失敗の通知などにSlackを活用することが多いです。
attachmentsも駆使することで、より綺麗で見やすいフォーマットで送信可能です。
export type SlackAttachment = {
title: string
title_link: string
fields: SlackAttachmentField[]
}
export type SlackAttachmentField = {
title: string
value: string
short: boolean
}
export type SlackUser = {
username: String
icon_url?: string
}
export const postToSlack = (params: {
channel: string
webhookUrl: string
user: SlackUser
attachments: SlackAttachment[]
}): string => {
const res = UrlFetchApp.fetch(params.webhookUrl, {
method: 'post',
headers: { 'Content-Type': 'application/json' },
payload: JSON.stringify({
channel: params.channel,
username: params.user.username,
icon_url: params.user.icon_url,
attachments: params.attachments.length > 0 ? params.attachments : undefined
})
})
return res.getContentText()
}
3. メールの送信
定番中の定番ですが。toやcc,bccにはカンマ区切りで好きな値を設定できます。
システムから送られるメールとして、noReply設定は重宝します。
const to = 'to1@gmail.com,to2@gmail.com'
const cc = 'cc1@gmail.com,cc2@gmail.com'
const subject = '件名件名件名件名件名件名件名'
const body = `
お世話になっております。
テストメールです。
`
GmailApp.sendEmail(to, subject, body, {
cc,
noReply: true
})
4. シートの取得または作成
シート名とテンプレート名を指定して新規作成、もしくは当てはまる名前のシートを取得します。
export const createSheetIfNotExists = (
sheetName: string,
templateName?: string
): GoogleAppsScript.Spreadsheet.Sheet => {
const spreadsheet = SpreadsheetApp.getActiveSpreadsheet()
const existSheet = spreadsheet.getSheetByName(sheetName)
const sheet = existSheet
? existSheet
: templateName
? spreadsheet.insertSheet(sheetName, { template: spreadsheet.getSheetByName(templateName) })
: spreadsheet.insertSheet(sheetName)
return sheet.showSheet()
}
5. シートへのデータ挿入
取得したデータをとにかくシートの末尾に一括挿入する。
APIからの取得データをrawデータとしてシートに貯めておく際に使ったりします。
export const insertRows = (sheet: GoogleAppsScript.Spreadsheet.Sheet) => (data: Object[][]) => {
const lastIndex = sheet.getLastRow()
const insertRange = sheet.getRange(
lastIndex + 1,
1,
data.length,
Math.max(...data.map(d => d.length))
)
return insertRange.setValues(data)
}
6. カスタムメニューの追加
スプレッドシートやフォーム、ドキュメントのメニューを追加したいときに使う。
export type MenuParam = { title?: string; items: MenuListItem[] }
export type MenuItemParam = { label: string; functionName: string }
export type MenuListItem = MenuParam | MenuItemParam
const isMenuItem = (value): value is MenuItemParam => 'label' in value && 'functionName' in value
export const createMemu = (
{ title = 'カスタムメニュー', items }: MenuParam,
ui: GoogleAppsScript.Base.Ui = SpreadsheetApp.getUi()
): GoogleAppsScript.Base.Menu => {
const menu = items.reduce((m, item) => {
return isMenuItem(item)
? m.addItem(item.label, item.functionName)
: m.addSubMenu(createMemu(item, ui))
}, ui.createMenu(title))
menu.addToUi()
return menu
}
// Usage
global.onOpen = () => {
createMemu({
items: [
{ label: '実行する', functionName: 'myFunction1' },
{ title: 'サブメニュー', items: [{ label: '実行する', functionName: 'myFunction2' }] }
]
})
}
7. トリガーの更新
特に時間ベーストリガーを自分ではない誰かの権限で設定したい時。
担当者に設定方法を案内するよりも、設定する関数を書いて、メニュー等から設定してもらいましょう。
トリガーの設定は冪等になるようにしておくと尚良いです。
export const updateTrigger = (functionName: string): GoogleAppsScript.Script.Trigger => {
ScriptApp.getProjectTriggers()
.filter((_) => _.getHandlerFunction() === functionName)
.forEach((_) => ScriptApp.deleteTrigger(_))
// トリガー設定値は要調整
return ScriptApp.newTrigger(functionName).timeBased().everyDays(1).atHour(9).create()
}
8. フォーム回答内容取得
フォームの回答のタイミングで、フォームの回答内容を取得して何らかの処理を行う。
フォームの回答TriggerのハンドラとしてonSubmit関数を設定して使いましょう。
type FormSubmitEvent = {
authMode: GoogleAppsScript.Script.AuthMode
response: GoogleAppsScript.Forms.FormResponse
source: GoogleAppsScript.Forms.Form
triggerUid: string
}
export const onSubmit = (event: FormSubmitEvent) => {
const sender = event.response.getRespondentEmail()
const itemResponses = event.response.getItemResponses()
const qAndA = itemResponses.map((r) => ({
question: r.getItem().getTitle(),
answer: r.getResponse().toString()
}))
}
9. ファイルの取得
HTMLメールのテンプレート取得であったり、
指定フォルダ内の該当ファイル取得であったり
const id: string = 'idididid'
const folder = DriveApp.getFolderById(id)
const file = DriveApp.getFileById(id)
const name: string = 'namename'
const folders = DriveApp.getFoldersByName(name)
const files = DriveApp.getFilesByName(name)
10. プロパティの取得
ScriptProperty
はユーザー間で共有されつつ、GASからのみ利用可能なので使い勝手が良いです。
export const properties = (
prop: GoogleAppsScript.Properties.Properties = PropertiesService.getScriptProperties()
) => {
return {
set: (key: string, value: string) => prop.setProperty(key, value),
get: (key: string) => prop.getProperty(key),
delete: (key: string) => prop.deleteProperty(key)
}
}
// Usage
const prop = properties()
prop.set('EXAMPLE_KEY', 'hoge')
prop.get('EXAMPLE_KEY') // => 'hoge'
prop.delete('EXAMPLE_KEY')
終わりに
GASの効率化のパワーは侮れません。
うまく使って、快適な業務環境を作りましょう
初学者にも手軽に試せて、TypeScriptの練習にもなりますね!