Skip to content
This repository has been archived by the owner on Mar 8, 2024. It is now read-only.

Commit

Permalink
Merge pull request #39 from proshunsuke/v4
Browse files Browse the repository at this point in the history
他のカレンダーを登録出来るように抽象化
  • Loading branch information
proshunsuke authored Mar 11, 2021
2 parents f1d178d + 1694b1d commit ad9066c
Show file tree
Hide file tree
Showing 14 changed files with 297 additions and 128 deletions.
27 changes: 23 additions & 4 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,30 @@ module.exports = {
'prettier',
],
rules: {
quotes: ['error', 'single', { 'avoidEscape': true, 'allowTemplateLiterals': false }],
quotes: ['error', 'single', {'avoidEscape': true, 'allowTemplateLiterals': false}],
semi: ['error', 'always'],
'import/no-extraneous-dependencies': 'off',
'no-void': ['error', { allowAsStatement: true }],
'no-console': ['error', { allow: ['info', 'error'] }],
'@typescript-eslint/unbound-method': 'off'
'no-void': ['error', {allowAsStatement: true}],
'no-console': ['error', {allow: ['info', 'error']}],
'@typescript-eslint/unbound-method': 'off',
'no-restricted-syntax': [
'error',
{
selector: 'ForInStatement',
message: 'for..in loops iterate over the entire prototype chain, which is virtually never what you want. Use Object.{keys,values,entries}, and iterate over the resulting array.',
},
// {
// selector: 'ForOfStatement',
// message: 'iterators/generators require regenerator-runtime, which is too heavyweight for this guide to allow them. Separately, loops should be avoided in favor of array iterations.',
// },
{
selector: 'LabeledStatement',
message: 'Labels are a form of GOTO; using them makes code confusing and hard to maintain and understand.',
},
{
selector: 'WithStatement',
message: '`with` is disallowed in strict mode because it makes code impossible to predict and optimize.',
},
],
},
};
10 changes: 9 additions & 1 deletion gcpFunctions/getKeyakiSchedule.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,15 @@ exports.getKeyakiSchedule = async (req, res) => {

await page.goto('https://www.keyakizaka46.com/s/k46o/media/list?dy=' + req.query['date']);

const result = await page.evaluate("scheduleEvents");
const response = await page.evaluate("scheduleEvents");

const result = response.map((scheduleEvent) => {
return {
title: scheduleEvent.title,
date: scheduleEvent.start,
type: scheduleEvent.className
}
});

res.send(result);
};
40 changes: 23 additions & 17 deletions src/calendar.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
import {
ScheduleObj,
KeyakiCalendarObj,
keyakiCalendarIds,
} from './keyakizaka/keyakiObjects';
import { ScheduleInterface, SiteCalendarInterface } from './calendarInterface';
import Retry from './lib/retry';

export default class Calendar {
Expand All @@ -21,32 +17,42 @@ export default class Calendar {

/**
*
* @param {ScheduleObj} schedule
* @param {ScheduleInterface} schedule
* @param calendarIds
*/
static createEvent(schedule: ScheduleObj): void {
const keyakiCalendarId:
| KeyakiCalendarObj
| undefined = keyakiCalendarIds.find(
(id) => id.kind === schedule.className
static createEvent(
schedule: ScheduleInterface,
calendarIds: SiteCalendarInterface[]
): void {
const siteCalendarId: SiteCalendarInterface | undefined = calendarIds.find(
(id) => id.type === schedule.type
);
if (typeof keyakiCalendarId === 'undefined') {
if (typeof siteCalendarId === 'undefined') {
console.info('スケジュールの内容: ');
console.info(schedule);
throw new Error(
`存在しない種類のスケジュールです。className: ${schedule.className}`
`存在しない種類のスケジュールです。type: ${schedule.type}`
);
}
const { calendarId } = keyakiCalendarId;
const { calendarId } = siteCalendarId;
Retry.retryable(3, () => {
if (process.env.ENV === 'production') {
if (process.env.ENV !== 'production') return;
if (schedule.startTime && schedule.endTime) {
CalendarApp.getCalendarById(calendarId).createEvent(
schedule.title,
new Date(schedule.startTime),
new Date(schedule.endTime),
{ description: schedule.description }
);
} else {
CalendarApp.getCalendarById(calendarId).createAllDayEvent(
schedule.title,
new Date(schedule.start)
new Date(schedule.date)
);
}
});
console.info(
`予定を作成しました。日付: ${schedule.start}, タイトル: ${schedule.title}`
`予定を作成しました。日付: ${schedule.date}, タイトル: ${schedule.title}`
);
if (process.env.ENV === 'production') {
Utilities.sleep(500); // API制限に引っかかってそうなのでsleepする
Expand Down
13 changes: 13 additions & 0 deletions src/calendarInterface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export interface ScheduleInterface {
title: string;
date: string;
startTime?: string;
endTime?: string;
description?: string;
type: string;
}

export interface SiteCalendarInterface {
type: string;
calendarId: string;
}
File renamed without changes.
53 changes: 30 additions & 23 deletions src/keyakizaka/keyakiSchedule.ts → src/oneMonthSchedule.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,31 @@
import dayjs from 'dayjs';
import Calendar from '../calendar';
import {
ScheduleObj,
KeyakiCalendarObj,
getKeyakiCalendarUrl,
keyakiCalendarIds,
} from './keyakiObjects';
import Retry from '../lib/retry';
import Calendar from './calendar';
import { ScheduleInterface, SiteCalendarInterface } from './calendarInterface';
import Retry from './lib/retry';
import 'regenerator-runtime';

export default class KeyakiSchedule {
export default class OneMonthSchedule {
/**
*
* @param {dayjs.Dayjs} date
* @param calendarUrl
* @param siteCalendarIds
* @returns {Promise<void>}
*/
static async setSchedule(date: dayjs.Dayjs): Promise<void> {
const customUrl: string = getKeyakiCalendarUrl + date.format('YYYYMMDD');
static async setSchedule(
date: dayjs.Dayjs,
calendarUrl: string,
siteCalendarIds: SiteCalendarInterface[]
): Promise<void> {
const customUrl: string = calendarUrl + date.format('YYYYMMDD');

const scheduleJson = await KeyakiSchedule.getScheduleJson(customUrl);
const scheduleJson = await OneMonthSchedule.getScheduleJson(customUrl);

const scheduleList = JSON.parse(scheduleJson) as ScheduleObj[];
const scheduleList = JSON.parse(scheduleJson) as ScheduleInterface[];

console.info(`${date.format('YYYY年MM月')}分の予定を更新します`);
KeyakiSchedule.delete1MonthCalendarEvents(date);
KeyakiSchedule.create1MonthEvents(scheduleList, date);
OneMonthSchedule.delete1MonthCalendarEvents(date, siteCalendarIds);
OneMonthSchedule.create1MonthEvents(scheduleList, date, siteCalendarIds);
console.info(`${date.format('YYYY年MM月')}分の予定を更新しました`);
}

Expand All @@ -50,13 +51,17 @@ export default class KeyakiSchedule {
/**
*
* @param {dayjs.Dayjs} date
* @param siteCalendarIds
*/
private static delete1MonthCalendarEvents(date: dayjs.Dayjs) {
private static delete1MonthCalendarEvents(
date: dayjs.Dayjs,
siteCalendarIds: SiteCalendarInterface[]
) {
let deleteEventCallCount = 0;
keyakiCalendarIds.forEach((keyakiCalendarObj: KeyakiCalendarObj) => {
siteCalendarIds.forEach((siteCalendarId) => {
if (process.env.ENV !== 'production') return;
const calendarApp = Retry.retryable(3, () =>
CalendarApp.getCalendarById(keyakiCalendarObj.calendarId)
CalendarApp.getCalendarById(siteCalendarId.calendarId)
);
const targetDateBeginningOfMonth = date;
const targetDateBeginningOfNextMonth = date.add(1, 'month');
Expand Down Expand Up @@ -87,18 +92,20 @@ export default class KeyakiSchedule {

/**
*
* @param {ScheduleObj[]} scheduleList
* @param {ScheduleInterface[]} scheduleList
* @param {dayjs.Dayjs} date
* @param calendarIds
*/
private static create1MonthEvents(
scheduleList: ScheduleObj[],
date: dayjs.Dayjs
scheduleList: ScheduleInterface[],
date: dayjs.Dayjs,
calendarIds: SiteCalendarInterface[]
) {
let createEventCallCount = 0;
scheduleList.forEach((schedule: ScheduleObj) => {
scheduleList.forEach((schedule: ScheduleInterface) => {
createEventCallCount += 1;
try {
Calendar.createEvent(schedule);
Calendar.createEvent(schedule, calendarIds);
} catch (e) {
console.error(
`カレンダー作成に失敗しました。失敗するまでに実行された回数: ${createEventCallCount.toString()}`
Expand Down
41 changes: 8 additions & 33 deletions src/schedule.ts
Original file line number Diff line number Diff line change
@@ -1,39 +1,14 @@
import dayjs from 'dayjs';
import KeyakiSchedule from './keyakizaka/keyakiSchedule';
import Trigger, { TERMINATION_MINUTES } from './lib/trigger';
import KeyakiSiteSchedule from './sites/keyakizaka/keyakiSiteSchedule';
import { SiteScheduleInterface } from './sites/siteSchedule';

export default class Schedule {
static async setSchedule(): Promise<void> {
const beginningOfNexYearMonth = dayjs().startOf('month').add(1, 'year');
let targetBeginningOfMonth = Schedule.getTargetBeginningOfMonth();
const startDate = dayjs();

while (targetBeginningOfMonth.isBefore(beginningOfNexYearMonth)) {
// eslint-disable-next-line no-await-in-loop
await KeyakiSchedule.setSchedule(targetBeginningOfMonth);
targetBeginningOfMonth = targetBeginningOfMonth.add(1, 'month');
if (Trigger.hasExceededTerminationMinutes(startDate)) {
Trigger.setTrigger(targetBeginningOfMonth);
console.info(
`${TERMINATION_MINUTES}分以上経過したので次のトリガーをセットして終了します。次実行開始する月: ${targetBeginningOfMonth.format(
'YYYY-MM-DD'
)}`
);
return;
}
const siteScheduleList: SiteScheduleInterface[] = [
new KeyakiSiteSchedule(),
// new KeyakiSiteSchedule
];
for await (const siteSchedule of siteScheduleList) {
await siteSchedule.setSiteSchedule();
}
Trigger.deleteTargetDateProperty();
Trigger.deleteTriggers();
}

/**
*
* @returns {dayjs.Dayjs}
*/
private static getTargetBeginningOfMonth(): dayjs.Dayjs {
const targetDateStr: string | null = Trigger.getTargetDateProperty();
return targetDateStr
? dayjs(targetDateStr).startOf('month')
: dayjs().startOf('month');
}
}
Original file line number Diff line number Diff line change
@@ -1,51 +1,41 @@
export interface ScheduleObj {
title: string;
start: string;
description: string;
className: string;
}

export interface KeyakiCalendarObj {
kind: string;
calendarId: string;
}
import { SiteCalendarInterface } from '../../calendarInterface';

export const getKeyakiCalendarUrl =
process.env.ENV === 'production'
? 'https://us-central1-augc-260709.cloudfunctions.net/getKeyakiSchedule?date='
: 'http://localhost:8080?date=';

export const keyakiCalendarIds: KeyakiCalendarObj[] = [
export const keyakiCalendarIds: SiteCalendarInterface[] = [
{
kind: 'shakehands',
type: 'shakehands',
calendarId: 'jdnc8uf21242be7qjm5nmj7uok@group.calendar.google.com',
},
{
kind: 'event',
type: 'event',
calendarId: 'eh0boh68ai7r2v15m38k2ms1lg@group.calendar.google.com',
},
{
kind: 'goods',
type: 'goods',
calendarId: '8l4srrnd9c6vge51k6cclsdsmc@group.calendar.google.com',
},
{
kind: 'release',
type: 'release',
calendarId: '8tc88j0j9gmr95qa81r8t2210c@group.calendar.google.com',
},
{
kind: 'ticket',
type: 'ticket',
calendarId: 'f4bcp8sqv66sugk9m06gb1ioeg@group.calendar.google.com',
},
{
kind: 'media',
type: 'media',
calendarId: '9beck0tqd2096b3b5utkh0jg8g@group.calendar.google.com',
},
{
kind: 'birthday',
type: 'birthday',
calendarId: 'lihum5fsldhsspa3r8altr01ns@group.calendar.google.com',
},
{
kind: 'other',
type: 'other',
calendarId: 'efhfvac7iii073suf8v16tlmic@group.calendar.google.com',
},
];
15 changes: 15 additions & 0 deletions src/sites/keyakizaka/keyakiSiteSchedule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import SiteSchedule from '../siteSchedule';
import { getKeyakiCalendarUrl, keyakiCalendarIds } from './keyakiObjects';
import { SiteCalendarInterface } from '../../calendarInterface';

export default class KeyakiSiteSchedule extends SiteSchedule {
// eslint-disable-next-line class-methods-use-this
siteCalendarUrl(): string {
return getKeyakiCalendarUrl;
}

// eslint-disable-next-line class-methods-use-this
siteCalendarIds(): SiteCalendarInterface[] {
return keyakiCalendarIds;
}
}
Loading

0 comments on commit ad9066c

Please sign in to comment.