import axios , { AxiosError , AxiosInstance , AxiosRequestConfig , AxiosResponse } from 'axios' ; import { camelCaseObjKeys , snakeCaseObjKeys , pluralize } from './utils' ; import { Announce , Channel , Guild , Member , Mute , Role , Schedule , User } from './common' ; type TwoParamsMethod = 'get' | 'delete' | 'head' | 'options' ; type ThreeParamsMethod = 'post' | 'put' | 'patch' ; interface TwoParamsRequest { < T , D = any > ( url , config ? ) : Promise < T > ; } interface ThreeParamsRequest { < T , D = any > ( url , data ? , config ? ) : Promise < T > ; } export type InnerAxiosInstance = Omit < AxiosInstance , 'request' | TwoParamsMethod | ThreeParamsMethod > & { request < T = any , D = any > ( config ) : Promise < T > ; } & { [ K in TwoParamsMethod ] : TwoParamsRequest ; } & { [ K in ThreeParamsMethod ] : ThreeParamsRequest ; } ; export type D < T > = { info : T ; filter : { [ K in keyof T ] ? : boolean ; } ; } ; example export interface Api { get guilds ( ) : Promise < Guild [ ] > ; guild ( id ) : Promise < Guild > & { get mute ( ) : { upd ( d ) : Promise < void > ; } ; get roles ( ) : Promise < { roles : Role [ ] ; guildId : string ; /** 默认分组上限 */ roleNumLimit : number ; } > & { add ( d ) : Promise < { roleId : string ; role : Role ; } > ; } ; role ( id ) : { del ( ) : Promise < void > ; upd ( d ) : Promise < { role : Role ; roleId : string ; guildId : string ; } > ; } ; get members ( ) : Promise < Member [ ] > ; member ( id ) : Promise < Member > & { get mute ( ) : { upd ( d ) : Promise < void > ; } ; role ( id ) : Promise < Role > ; } ; get channels ( ) : Promise < Channel [ ] > ; } ; channel ( id ) : Promise < Channel > & { get announces ( ) : { add ( d ) : Promise < Announce > ; } ; announce ( msgId ) : { del ( ) : Promise < void > ; } ; get schedules ( ) : Promise < Schedule [ ] > & { add ( d ) : Promise < Schedule > ; } ; schedule ( id ) : Promise < Schedule > & { upd ( d ) : Promise < Schedule > ; del ( ) : Promise < void > ; } ; } ; } export class Api { readonly host : string ; readonly token : string ; $request : InnerAxiosInstance ; constructor ( host , token , isSandbox ) { this . host = host ; this . token = token ; if ( isSandbox ) this . host = this . host . replace ( /^(https?:\/\/)/ , '$1sandbox.' ) ; this . $request = this . getRequest ( ) ; } protected getRequest ( ) { const a = axios . create ( { baseURL : this . host , headers : { 'Content-Type' : 'application/json' , 'Authorization' : this . token } } ) ; a . interceptors . request . use ( ( config ) => { config . data && ( config . data = snakeCaseObjKeys ( config . data ) ) ; return config ; } , ( error ) => Promise . reject ( error ) ) ; a . interceptors . response . use ( ( response ) => { return camelCaseObjKeys ( response . data ) ; } , async ( error ) => { const response = error ?. response ; switch ( response ?. status ) { case 401 : case 403 : break ; } throw error ; } ) ; return a ; } get me ( ) { return this . $request . get < User > ( '/users/@me' ) ; } } type promiseMethod = 'then' | 'catch' | 'finally' ; const promiseMethods = [ 'then' , 'catch' , 'finally' ] ; const getPromiseProp = ( p , prop ) => p [ prop ] . bind ( p ) ; const requestProxy = ( a , path , cPath = '' ) : Function => new Proxy ( ( ) => { } , { get ( _ , prop ) { if ( promiseMethods . includes ( prop ) ) return getPromiseProp ( a . $request . get ( path + cPath ) , prop as promiseMethod ) ; else { switch ( prop as 'add' | 'del' | 'upd' ) { case 'add' : return ( d ) => a . $request . post ( path + cPath , d ) ; case 'del' : return ( ) => a . $request . delete ( path + cPath ) ; case 'upd' : return ( d ) => a . $request . patch ( path + cPath , d ) ; } return requestProxy ( a , path , `/${ prop }` ) ; } } , apply ( _ , __ , [ id , ... _args ] ) { return requestProxy ( a , `${ path + pluralize ( cPath ) }/${ id }` ) ; } } ) ; export const attachApi = < T extends Api > ( a ) => new Proxy ( a , { get ( target , path ) { if ( path in target ) return target [ path ] ; return new Proxy ( ( ) => { } , { get ( _ , prop ) { if ( promiseMethods . includes ( prop ) ) return getPromiseProp ( a . $request . get ( `/users/@me/${ path }` ) , prop as promiseMethod ) ; } , apply ( _ , __ , [ id , ... _args ] ) { return requestProxy ( a , `/${ pluralize ( path ) }/${ id }` ) ; } } ) ; } } ) ;