line-api
Version:
Simple LINE api client for Node.js written in TypeScript
185 lines (156 loc) • 4.88 kB
text/typescript
import qs from 'querystring'
import fetch, { Headers, RequestInit } from 'node-fetch'
import { Readable } from 'stream'
import { FormData } from 'formdata-node'
import { fileFromPath } from 'formdata-node/file-from-path'
import { FormDataEncoder } from 'form-data-encoder'
import { ENDPOINT, STICKER } from './consts'
export type NotifyOption = {
token: string
}
export type RateLimit = {
request: {
limit: number
remaining: number
}
image: {
limit: number
remaining: number
}
reset: Date
}
export type StatusResponse = {
status: 200
message: 'ok'
targetType: string
target: string
}
export type RevokeResponse = {
staus: 200
message: 'OK'
}
export type SendResponse = {
staus: 200
message: 'OK'
}
export type TokenResponse = {
access_token: string
}
export type SendOption = {
message: string
image?: string | { fullsize: string; thumbnail: string }
sticker?: keyof typeof STICKER | { packageId: number; id: number }
notificationDisabled?: boolean
}
export type AuthorizeOption = {
response_type: 'code'
scope: 'notify'
client_id: string
redirect_uri: string
state: string
response_mode?: 'form_post'
}
export type TokenOption = {
grant_type: 'authorization_code'
code: string
redirect_uri: string
client_id: string
client_secret: string
}
export class NotifyError extends Error {
public status: number
constructor(param: { status: number; message?: string }) {
super(param.message)
this.name = 'NotifyError'
this.status = param.status
}
}
export class Notify {
public accessToken: string
public ratelimit?: RateLimit
constructor({ token }: NotifyOption) {
this.accessToken = token
}
private async req(type: 'api' | 'oauth', path: string, param?: RequestInit) {
const url = ENDPOINT.notify[type] + path
const headers = new Headers(param?.headers)
headers.set('Authorization', `Bearer ${this.accessToken}`)
return fetch(url, { ...param, headers })
.then((r) => {
const limit = r.headers.get('x-ratelimit-limit')
const remaining = r.headers.get('x-ratelimit-remaining')
const imageLimit = r.headers.get('x-ratelimit-imagelimit')
const imageRemaining = r.headers.get('x-ratelimit-imageremaining')
const reset = r.headers.get('x-ratelimit-reset')
if (limit && remaining && imageLimit && imageRemaining && reset) {
this.ratelimit = {
request: {
limit: parseInt(limit),
remaining: parseInt(remaining),
},
image: {
limit: parseInt(imageLimit),
remaining: parseInt(imageRemaining),
},
reset: new Date(parseInt(reset) * 1000),
}
}
return r.json()
})
.then((r) => {
if (r.status !== 200) {
throw new NotifyError(r)
}
return r
})
}
private get(type: 'api' | 'oauth', path: string, query?: Record<string, any>) {
const q = query ? '?' + qs.stringify(query) : ''
return this.req(type, path + q, { method: 'get' })
}
private post(type: 'api' | 'oauth', path: string, formData: FormData) {
const encoder = new FormDataEncoder(formData)
return this.req(type, path, { method: 'post', headers: encoder.headers, body: Readable.from(encoder) })
}
status(): Promise<StatusResponse> {
return this.get('api', '/status')
}
revoke(): Promise<RevokeResponse> {
return this.get('api', '/revoke')
}
async send({ message, image, sticker, notificationDisabled }: SendOption): Promise<SendResponse> {
const formData = new FormData()
formData.append('message', message)
if (image) {
if (typeof image === 'string') {
formData.append('imageFile', await fileFromPath(image))
} else {
formData.append('imageFullsize', image.fullsize)
formData.append('imageThumbnail', image.thumbnail)
}
}
if (sticker) {
const { packageId, id } = typeof sticker === 'string' ? STICKER[sticker] || {} : sticker
if (packageId && id) {
formData.append('stickerPackageId', packageId)
formData.append('stickerId', id)
}
}
if (notificationDisabled !== void 0) {
formData.append('notificationDisabled', notificationDisabled)
}
return this.post('api', '/notify', formData)
}
authorize(opt: AuthorizeOption): Promise<void> {
return this.get('oauth', '/authorize', opt)
}
token(opt: TokenOption): Promise<TokenResponse> {
const formData = new FormData()
formData.append('grant_type', opt.grant_type)
formData.append('code', opt.code)
formData.append('redirect_uri', opt.redirect_uri)
formData.append('client_id', opt.client_id)
formData.append('client_secret', opt.client_secret)
return this.post('oauth', '/token', formData)
}
}