このコミットが含まれているのは:
2026-06-09 23:05:37 +09:00
コミット ae1deaac8c
14個のファイルの変更505行の追加38行の削除
+45 -11
ファイルの表示
@@ -22,6 +22,12 @@ export type GekanatorQuestionKind =
| 'title'
| 'original_date'
export type GekanatorQuestionSource =
| 'default'
| 'user_suggested'
| 'ai_generated'
| 'admin_curated'
export type GekanatorQuestionCondition =
| { type: 'tag'; key: string }
| { type: 'source'; host: string }
@@ -32,17 +38,21 @@ export type GekanatorQuestionCondition =
| { type: 'title-has-ascii' }
export type StoredGekanatorQuestion = {
id: string
text: string
kind: GekanatorQuestionKind
condition: GekanatorQuestionCondition }
id: string
text: string
kind: GekanatorQuestionKind
condition: GekanatorQuestionCondition
source?: GekanatorQuestionSource
priorityWeight?: number }
export type GekanatorQuestion = {
id: string
text: string
kind: GekanatorQuestionKind
condition: GekanatorQuestionCondition
test: (post: Post) => boolean }
id: string
text: string
kind: GekanatorQuestionKind
condition: GekanatorQuestionCondition
source: GekanatorQuestionSource
priorityWeight: number
test: (post: Post) => boolean }
const countBy = <T extends string | number> (values: T[]): Map<T, number> => {
const counts = new Map<T, number> ()
@@ -188,6 +198,8 @@ export const restoreGekanatorQuestion = (
question: StoredGekanatorQuestion,
): GekanatorQuestion => ({
...question,
source: question.source ?? 'default',
priorityWeight: question.priorityWeight ?? 1,
test: (post: Post) => questionMatches (post, question.condition) })
@@ -197,7 +209,9 @@ export const storeGekanatorQuestion = (
id: question.id,
text: question.text,
kind: question.kind,
condition: question.condition })
condition: question.condition,
source: question.source,
priorityWeight: question.priorityWeight })
export const fetchGekanatorPosts = async (): Promise<Post[]> => {
@@ -206,6 +220,12 @@ export const fetchGekanatorPosts = async (): Promise<Post[]> => {
}
export const fetchGekanatorQuestions = async (): Promise<StoredGekanatorQuestion[]> => {
const data = await apiGet<{ questions: StoredGekanatorQuestion[] }> ('/gekanator/questions')
return data.questions
}
export const buildGekanatorQuestions = (posts: Post[]): GekanatorQuestion[] => {
const tagCounts = countBy (posts.flatMap (post =>
post.tags
@@ -248,6 +268,8 @@ export const buildGekanatorQuestions = (posts: Post[]): GekanatorQuestion[] => {
text: tagQuestionText (category, label),
kind: 'tag' as const,
condition: { type: 'tag' as const, key: String (key) },
source: 'default' as const,
priorityWeight: 1,
test: (post: Post) => questionableTag (post, String (key)) }
})
@@ -259,6 +281,8 @@ export const buildGekanatorQuestions = (posts: Post[]): GekanatorQuestion[] => {
text: `${ host } の投稿を思ひ浮かべてゐる?`,
kind: 'source' as const,
condition: { type: 'source' as const, host },
source: 'default' as const,
priorityWeight: 1,
test: (post: Post) => hostOf (post) === host }))
const originalYearQuestions = usefulEntries (originalYears)
@@ -269,6 +293,8 @@ export const buildGekanatorQuestions = (posts: Post[]): GekanatorQuestion[] => {
text: `オリジナルの投稿年は ${ year } 年?`,
kind: 'original_date' as const,
condition: { type: 'original-year' as const, year },
source: 'default' as const,
priorityWeight: 1,
test: (post: Post) => originalYearOf (post) === year }))
const originalMonthQuestions = usefulEntries (originalMonths)
@@ -279,6 +305,8 @@ export const buildGekanatorQuestions = (posts: Post[]): GekanatorQuestion[] => {
text: `オリジナルの投稿月は ${ month } 月?`,
kind: 'original_date' as const,
condition: { type: 'original-month' as const, month },
source: 'default' as const,
priorityWeight: 1,
test: (post: Post) => originalMonthOf (post) === month }))
const originalMonthDayQuestions = usefulEntries (originalMonthDays)
@@ -292,6 +320,8 @@ export const buildGekanatorQuestions = (posts: Post[]): GekanatorQuestion[] => {
text: `オリジナルの投稿日は ${ month }${ day } 日?`,
kind: 'original_date' as const,
condition: { type: 'original-month-day' as const, monthDay: String (monthDay) },
source: 'default' as const,
priorityWeight: 1,
test: (post: Post) => originalMonthDayOf (post) === monthDay }
})
@@ -303,12 +333,16 @@ export const buildGekanatorQuestions = (posts: Post[]): GekanatorQuestion[] => {
condition: {
type: 'title-length-greater-than' as const,
length: titleLengthMedian },
source: 'default' as const,
priorityWeight: 1,
test: (post: Post) => (post.title?.length ?? 0) > titleLengthMedian },
{
id: 'title:ascii',
text: '題名に英数字が混じってゐる?',
kind: 'title' as const,
condition: { type: 'title-has-ascii' as const },
source: 'default' as const,
priorityWeight: 1,
test: (post: Post) => /[A-Za-z0-9]/.test (post.title ?? '') }]
.filter (question => {
const yes = posts.filter (post => question.test (post)).length
@@ -354,7 +388,7 @@ export const saveGekanatorQuestionSuggestion = async ({
gekanatorGameId: number
questionText: string
answer: GekanatorAnswerValue
}): Promise<{ id: number }> =>
}): Promise<{ id: number; count: number }> =>
await apiPost ('/gekanator/question_suggestions', {
gekanator_game_id: gekanatorGameId,
question_text: questionText,