- なろう小説 API
- Google Apps Script
- Google スプレッドシート
- Discord
const main = () => {
/** スプレッドシート */
const spreadsheet = SpreadsheetApp.getActive()
/** リストシート */
const sheet = spreadsheet.getSheetByName('リスト')
/** 取得するデータの範囲(2行目以降) */
const range = sheet.getRange(2, 1, sheet.getLastRow() - 1, 3)
/** リストシートのデータ */
const data = range.getValues().map((item) => ({
/** タイトル */
title: item[0] || '',
/** Nコード */
ncode: (item[1] || '').toLowerCase(),
/** エピソード数 */
latest: Number(item[2] || 0),
}))
}
/**
* なろう小説APIから情報を取得する
* @param ncodes Nコードの配列
* @returns 小説情報の配列
*/
const getNarouInfo = (ncodes = []) => {
/** APIのベースURL */
const base = 'https://api.syosetu.com/novelapi/api/'
/** 出力形式 */
const output = 'json'
/** 取得する項目 */
const key = [
'n', // Nコード
's', // 作品のあらすじ
'ga', // エピソード数
].join('-')
/** 取得する件数 */
const limit = ncodes.length
/** Nコード */
const ncode = ncodes.join('-')
try {
const response = UrlFetchApp
.fetch(`${base}?out=${output}&of=${key}&lim=${limit}&ncode=${ncode}`)
const data = JSON.parse(response.getContentText())
.filter((item) => item.ncode)
.map((item) => ({
ncode: item.ncode.toLowerCase(),
description: item.story.substring(0, 100),
latest: Number(item.general_all_no),
}))
return data
} catch (err) {
Logger.log(err.toString())
}
}
- base - API の URL。
- output - API の
out
パラメータに渡す値。Google Apps Script で扱いやすいよう JSON 形式を指定。 - key - API の
of
パラメータに渡す値。転送量を減らすため、不要なデータは取得しないようにここで指定します。複数の項目を取得する場合は、ハイフン(-
)で区切ります。
n
- N コードs
- 作品のあらすじga
- エピソード数
- limit - API の
lim
パラメータに渡す、取得する小説の件数。ここでは引数として渡された配列の要素数を指定しています。 - ncode - API の
ncode
パラメータに渡す、取得する小説の N コード。複数の小説を指定する場合はハイフン(-
)で区切ります。
const main = () => {
/** スプレッドシート */
const spreadsheet = SpreadsheetApp.getActive()
/** リストシート */
const sheet = spreadsheet.getSheetByName('リスト')
/** 取得するデータの範囲(2行目以降) */
const range = sheet.getRange(2, 1, sheet.getLastRow() - 1, 3)
/** リストシートのデータ */
const data = range.getValues().map((item) => ({
/** タイトル */
title: item[0] || '',
/** Nコード */
ncode: (item[1] || '').toLowerCase(),
/** エピソード数 */
latest: Number(item[2] || 0),
}))
/** なろう小説APIから取得した情報 */
const response = getNarouInfo(
data.filter((item) => item.ncode).map((item) => item.ncode)
)
}
/**
* Discordへ通知を送信する
* @params novels 通知する小説の情報
*/
const postDiscord = (novels = []) => {
/** 送信先URL */
const webhookUrl = 'https://discord.com/api/webhooks/xxxxxxxxxxxxxxxxxxxx'
const items = novels.reduce((prev, current) => {
if (!prev.length || prev[prev.length - 1].length === 10) {
return [...prev, [current]]
} else {
const [last, ...rest] = prev.reverse()
return [...rest.reverse(), [...last, current]]
}
}, [])
try {
items.forEach((item) => {
UrlFetchApp.fetch(webhookUrl, {
'method': 'post',
'contentType': 'application/json',
'payload': JSON.stringify({
content: '小説家になろうの作品に更新がありました。',
embeds: item,
})
})
})
} catch (err) {
Logger.log(err.toString())
}
}
const main = () => {
/** スプレッドシート */
const spreadsheet = SpreadsheetApp.getActive()
/** リストシート */
const sheet = spreadsheet.getSheetByName('リスト')
/** 取得するデータの範囲(2行目以降) */
const range = sheet.getRange(2, 1, sheet.getLastRow() - 1, 3)
/** リストシートのデータ */
const data = range.getValues().map((item) => ({
/** タイトル */
title: item[0] || '',
/** Nコード */
ncode: (item[1] || '').toLowerCase(),
/** エピソード数 */
latest: Number(item[2] || 0),
}))
/** なろう小説APIから取得した情報 */
const response = getNarouInfo(
data.filter((item) => item.ncode).map((item) => item.ncode)
)
/** 通知する小説の情報 */
const postItems = []
data.forEach((item, index) => {
try {
const current = response.find((novel) => novel.ncode === item.ncode)
// 見つからない場合はエラー
if (!current) {
throw new Error('小説が見つかりません')
}
if (Number(item.latest) < current.latest) {
// スプレッドシートのエピソード数を更新
sheet.getRange(`C${index + 2}`).setValue(current.latest)
// 通知する項目として配列に追加
postItems.push({
title: item.title,
description: current.description,
url: `https://ncode.syosetu.com/${item.ncode}/`,
})
}
} catch (err) {
Logger.log(err.toString())
}
})
// Discordへ通知を送信する
postDiscord(postItems)
}
const main = () => {
/** スプレッドシート */
const spreadsheet = SpreadsheetApp.getActive()
/** リストシート */
const sheet = spreadsheet.getSheetByName('リスト')
/** 取得するデータの範囲(2行目以降) */
const range = sheet.getRange(2, 1, sheet.getLastRow() - 1, 3)
/** リストシートのデータ */
const data = range.getValues().map((item) => ({
/** タイトル */
title: item[0] || '',
/** Nコード */
ncode: (item[1] || '').toLowerCase(),
/** エピソード数 */
latest: Number(item[2] || 0),
}))
/** なろう小説APIから取得した情報 */
const response = getNarouInfo(
data.filter((item) => item.ncode).map((item) => item.ncode)
)
/** 通知する小説の情報 */
const postItems = []
data.forEach((item, index) => {
try {
const current = response.find((novel) => novel.ncode === item.ncode)
// 見つからない場合はエラー
if (!current) {
throw new Error('小説が見つかりません')
}
if (Number(item.latest) < current.latest) {
// スプレッドシートのエピソード数を更新
sheet.getRange(`C${index + 2}`).setValue(current.latest)
// 通知する項目として配列に追加
postItems.push({
title: item.title,
description: current.description,
url: `https://ncode.syosetu.com/${item.ncode}/`,
})
}
} catch (err) {
Logger.log(err.toString())
}
})
// Discordへ通知を送信する
postDiscord(postItems)
}
/**
* なろう小説APIから情報を取得する
* @param ncodes Nコードの配列
* @returns 小説情報の配列
*/
const getNarouInfo = (ncodes = []) => {
/** APIのベースURL */
const base = 'https://api.syosetu.com/novelapi/api/'
/** 出力形式 */
const output = 'json'
/** 取得する項目 */
const key = [
'n', // Nコード
's', // 作品のあらすじ
'ga', // エピソード数
].join('-')
/** 取得する件数 */
const limit = ncodes.length
/** Nコード */
const ncode = ncodes.join('-')
try {
const response = UrlFetchApp
.fetch(`${base}?out=${output}&of=${key}&lim=${limit}&ncode=${ncode}`)
const data = JSON.parse(response.getContentText())
.filter((item) => item.ncode)
.map((item) => ({
ncode: item.ncode.toLowerCase(),
description: item.story.substring(0, 100),
latest: Number(item.general_all_no),
}))
return data
} catch (err) {
Logger.log(err.toString())
}
}
/**
* Discordへ通知を送信する
* @params novels 通知する小説の情報
*/
const postDiscord = (novels = []) => {
/** 送信先URL */
const webhookUrl = 'https://discord.com/api/webhooks/xxxxxxxxxxxxxxxxxxxx'
const items = novels.reduce((prev, current) => {
if (!prev.length || prev[prev.length - 1].length === 10) {
return [...prev, [current]]
} else {
const [last, ...rest] = prev.reverse()
return [...rest.reverse(), [...last, current]]
}
}, [])
try {
items.forEach((item) => {
UrlFetchApp.fetch(webhookUrl, {
'method': 'post',
'contentType': 'application/json',
'payload': JSON.stringify({
content: '小説家になろうの作品に更新がありました。',
embeds: item,
})
})
})
} catch (err) {
Logger.log(err.toString())
}
}