UNPKG

destream-api

Version:

Full featured DeStream API NodeJS wrapper [BETA]

206 lines (183 loc) 7.66 kB
const fetch = require('node-fetch') const formurlencoded = require('form-urlencoded') const sha512 = require('crypto-js/sha512') const WebSocket = require('ws') module.exports = class DeStreamAPI { constructor(config) { this._clientId = config.clientId this._clientSecret = config.clientSecret this.apiVersion = config.apiVersion ?? 2 this.baseURI = `https://destream.net/api/v${this.apiVersion}` this.baseURIwebsocket = `https://destream.net/ws/v${this.apiVersion}` } static UserExistsException(api_response) { this.apiResponse = api_response } static AccessTokenIncorrect(api_response) { this.apiResponse = api_response } _getSignature() { return sha512(this._clientId+getISO8601Date()+this._clientSecret) } async getTokensFromCode(authorizationCode, redirectUri) { const accessTokenEndpoint = `${this.baseURI}/oauth2/token` let params = formurlencoded({ grant_type: 'authorization_code', client_id: this._clientId, client_secret: this._clientSecret, redirect_uri: redirectUri, code: authorizationCode }) let response = await fetch(`${accessTokenEndpoint}?${params}`, { method: 'POST', headers: {'Content-Type': 'application/x-www-form-urlencoded'} }) let parsedAPIResponse = await response.json() return { ...parsedAPIResponse, http_status: response.status } } async refreshAccessToken(scope, refresh_token) { const tokenRefreshingEndpoint = `${this.baseURI}/oauth2/token` let params = formurlencoded({ grant_type: 'refresh_token', client_id: this._clientId, client_secret: this._clientSecret, scope: scope, refresh_token: refresh_token }) let response = await fetch(`${tokenRefreshingEndpoint}?${params}`, { method: 'POST', headers: {'Content-Type': 'application/x-www-form-urlencoded'} }) let parsedAPIResponse = await response.json() return { ...parsedAPIResponse, http_status: response.status } } async getUser(token_type, access_token) { const userInfoEndpoint = `${this.baseURI}/users` let response = await fetch(userInfoEndpoint, { method: 'GET', headers: { 'X-Api-ClientId': this._clientId, 'Authorization': `${token_type} ${access_token}` } }) let parsedAPIResponse = await response.json() if(response.status === 401){ console.error('DeStreamAPI error', 'AccessTokenIncorrect', parsedAPIResponse); throw DeStreamAPI.AccessTokenIncorrect(parsedAPIResponse) } return { ...parsedAPIResponse, http_status: response.status } } async registerUser(email) { const registerUserEndpoint = `${this.baseURI}/users/register` let response = await fetch(registerUserEndpoint, { method: 'POST', body: JSON.stringify({ email: email }), headers: { 'Content-Type': 'application/json', 'X-Api-ClientId': this._clientId, 'X-Api-RequestDate': getISO8601Date(), 'X-Api-Signature': this._getSignature() } }) let parsedAPIResponse = await response.json() const HTTP_STATUS_USER_EXISTS = 409 if(response.status === HTTP_STATUS_USER_EXISTS){ console.error('DeStreamAPI error', 'UserExistsException', parsedAPIResponse); throw new DeStreamAPI.UserExistsException(parsedAPIResponse) } return { ...parsedAPIResponse, http_status: response.status } } async getTips(tokens, offset, limit, sinceDate) { const userInfoEndpoint = `${this.baseURI}/users/tips` let params = formurlencoded({ offset: offset ?? 0, limit: Math.max(0, Math.min(30, limit ?? 10)), after_date: sinceDate instanceof Date ? getISO8601Date(sinceDate) : null }, { ignorenull: true }) let response = await fetch(`${userInfoEndpoint}?${params}`, { method: 'GET', headers: { 'X-Api-ClientId': this._clientId, 'Authorization': `${tokens.token_type} ${tokens.access_token}` } }) let parsedAPIResponse = await response.json() if(response.status === 401){ console.error('DeStreamAPI error', 'AccessTokenIncorrect', parsedAPIResponse); throw DeStreamAPI.AccessTokenIncorrect(parsedAPIResponse) } if(response.status === 200){ parsedAPIResponse.next = async () => await this.getTips(tokens, (offset ?? 0)+(limit ?? 0), limit, sinceDate) parsedAPIResponse.prev = async () => await this.getTips(tokens, Math.max(0, (offset ?? 0)-(limit ?? 0)), limit, sinceDate) } return { ...parsedAPIResponse, http_status: response.status } } async getInvoicesPayments(tokens, offset, limit, sinceDate, arrayOfIds) { const userInfoEndpoint = `${this.baseURI}/payments` let params = formurlencoded({ offset: offset ?? 0, limit: Math.max(0, Math.min(30, limit ?? 10)), after_date: sinceDate instanceof Date ? getISO8601Date(sinceDate) : null, payment_id: arrayOfIds ? arrayOfIds.join(',') : null }, { ignorenull: true }) let response = await fetch(`${userInfoEndpoint}?${params}`, { method: 'GET', headers: { 'X-Api-ClientId': this._clientId, 'X-Api-RequestDate': getISO8601Date(), 'X-Api-Signature': this._getSignature(), 'Authorization': `${tokens.token_type} ${tokens.access_token}` } }) let parsedAPIResponse = await response.json() if(response.status === 401){ console.error('DeStreamAPI error', 'AccessTokenIncorrect', parsedAPIResponse); throw DeStreamAPI.AccessTokenIncorrect(parsedAPIResponse) } if(response.status === 200){ parsedAPIResponse.next = async () => await this.getInvoicesPayments(tokens, offset+limit, limit, sinceDate) parsedAPIResponse.prev = async () => await this.getInvoicesPayments(tokens, Math.max(0, offset-limit), limit, sinceDate) } return { ...parsedAPIResponse, http_status: response.status } } subscribeToEvents(access_token, callback) { const websocketEndpoint = `${this.baseURIwebsocket}/donations` let params = formurlencoded({ client_id: this._clientId, access_token: access_token }) let websocketURL = `${websocketEndpoint}?${params}` console.log(websocketURL) const ws = new WebSocket(websocketURL) ws.on('message', callback) } async createInvoice(user_id, amount, currency, optionalData) { const registerUserEndpoint = `${this.baseURI}/payments` let response = await fetch(registerUserEndpoint, { method: 'POST', body: JSON.stringify({ user_id: user_id, amount: amount, currency: currency, ...optionalData }), headers: { 'Content-Type': 'application/json', 'X-Api-ClientId': this._clientId, 'X-Api-RequestDate': getISO8601Date(), 'X-Api-Signature': this._getSignature() } }) let parsedAPIResponse = await response.json() return { ...parsedAPIResponse, http_status: response.status } } validateSignature(body, receivedSignature) { return sha512(body+this._clientSecret) === receivedSignature } } const getISO8601Date = (date) => { const o0 = s => (`0${s}`).substr(-2) date = date ?? new Date() let year = date.getFullYear() let month = o0(date.getMonth()+1) let day = o0(date.getDate()) let hours = o0(date.getHours()) let minutes = o0(date.getMinutes()) let seconds = o0(date.getSeconds()) let tzOffset = -date.getTimezoneOffset(); let dif = tzOffset >= 0 ? '+' : '-'; let tzH = o0(tzOffset / 60) let tzM = o0(tzOffset % 60) // ISO8601 date + time + timezone example: 2021-05-28T20:34:37+00:00 return `${year}-${month}-${day}T${hours}:${minutes}:${seconds}${dif}${tzH}:${tzM}` }