Extra contents beyond WaniKani
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

232 lines
4.6 KiB

import axios from 'axios'
import rateLimit, { RateLimitedAxiosInstance } from 'axios-rate-limit'
/**
* https://docs.api.wanikani.com/20170710/#rate-limit
*
* Requests per minute 60
*
* @param apiKey @default process.env['WANIKANI_API_KEY']
* @returns
*/
export function makeWanikani(
apiKey = process.env['WANIKANI_API_KEY']!
): WaniKaniAxiosInstance {
const wkApi = rateLimit(
axios.create({
baseURL: 'https://api.wanikani.com/v2/',
headers: {
Authorization: `Bearer ${apiKey}`
},
validateStatus: function () {
return true
}
}),
{
/**
* Per second
*/
maxRequests: 1,
perMilliseconds: 1000
}
)
return Object.assign(wkApi, {
async *kanji(params = {}) {
let nextUrl = '/subjects'
while (true) {
const r = await wkApi.get<
ICollection<
IResource<{
characters: string
level: number
}>
>
>(nextUrl, {
params: {
...params,
types: 'kanji'
}
})
for (const d of r.data.data) {
yield {
id: d.id,
level: d.data.level,
characters: d.data.characters
}
}
console.error(r.data.url)
nextUrl = r.data.pages.next_url || ''
if (!nextUrl) {
break
}
}
},
async *vocabulary(params = {}) {
let nextUrl = '/subjects'
while (true) {
const r = await wkApi.get<
ICollection<
IResource<{
characters: string
level: number
context_sentences: {
ja: string
en: string
}[]
}>
>
>(nextUrl, {
params: {
...params,
types: 'vocabulary'
}
})
for (const d of r.data.data) {
yield {
id: d.id,
level: d.data.level,
characters: d.data.characters,
sentences: d.data.context_sentences
}
}
console.error(r.data.url)
nextUrl = r.data.pages.next_url || ''
if (!nextUrl) {
break
}
}
},
async *subjects<T = any>(params = {}) {
let nextUrl = '/subjects'
while (true) {
const r = await wkApi.get<
ICollection<
IResource<any> & {
id: number
object: string
data_updated_at: string
url: string
data: T
}
>
>(nextUrl, { params })
for (const d of r.data.data) {
yield {
id: d.id,
data_updated_at: d.data_updated_at,
object: d.object,
url: d.url,
data: d.data
}
}
console.error(r.data.url)
nextUrl = r.data.pages.next_url || ''
if (!nextUrl) {
break
}
}
},
async *assignments(
params = {
unlocked: 'true'
}
) {
let nextUrl = '/assignments'
while (true) {
const r = await wkApi.get<
ICollection<
IResource<{
subject_id: number
srs_stage: number
}>
>
>(nextUrl, {
params
})
console.error(r.data.url)
for (const d of r.data.data) {
yield {
id: d.data.subject_id,
srsLevel: d.data.srs_stage
}
}
nextUrl = r.data.pages.next_url || ''
if (!nextUrl) {
break
}
}
}
})
}
export interface WaniKaniAxiosInstance extends RateLimitedAxiosInstance {
kanji(params?: any): AsyncGenerator<{
id: number
level: number
characters: string
}>
vocabulary(params?: any): AsyncGenerator<{
id: number
level: number
characters: string
sentences: {
ja: string
en: string
}[]
}>
subjects<T = any>(
params?: any
): AsyncGenerator<{
id: number
object: string
data_updated_at: string
url: string
data: T
}>
assignments(params?: any): AsyncGenerator<{
id: number
srsLevel: number
}>
}
export interface IResource<T = any> {
id: number
url: string
data_updated_at: string // Date
data: T
}
export interface ICollection<T = any> {
object: string
url: string
pages: {
next_url?: string
previous_url?: string
per_page: number
}
total_count: number
data_updated_at: string // Date
data: T[]
}
export interface IError {
error: string
code: number
}