UNPKG

line-api

Version:

Simple LINE api client for Node.js written in TypeScript

185 lines (156 loc) 4.88 kB
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) } }