vknode
Version:
Library for working with VK API
420 lines (341 loc) • 12.2 kB
JavaScript
const rp = require('request-promise')
const qs = require('qs')
const fs = require('fs')
const md5 = require('md5')
const LongPoll = require('./longpoll')
const BotLongPoll = require('./longpoll/bot')
const CallbackAPI = require('./callback-api')
const MessageConstructor = require('./messages/constructor')
const Upload = require('./upload')
const
apiUrl = {
protocol: 'https',
domain: 'api.vk.com',
path: 'method',
version: '5.69'
},
apiPackedUrl = `${apiUrl.protocol}://${apiUrl.domain}/${apiUrl.path}`
module.exports = class API {
constructor(token) {
this.token = token || ''
this.queries = []
this.limit = 3
this.rejection = true
this.info = {}
this._version = apiUrl.version
this.errorHandler = () => {}
this.upload = new Upload(this)
Object.defineProperty(this, "longpoll", {
enumerable: false,
get: () => {
if (!this.lp) {
this.lp = new LongPoll(this)
}
return this.lp
}
})
Object.defineProperty(this, "bot", {
enumerable: false,
get: () => {
if (!this.botlp) {
this.botlp = new BotLongPoll(this)
}
return this.botlp
}
})
this.loadErrors('en')
}
version(version) {
this._version = version.toString()
return this
}
pack() {
this.packer = !this.packer
return this
}
lang(language) {
this.language = language
switch (language) {
case 'ru':
case 'en':
{
this.loadErrors(language)
break
}
}
return this
}
check(app_data = {}) {
this.call('groups.getById')
.then(response => {
if (response.length) {
this.info = {
group_id: response[0].gid,
name: response[0].name,
screen_name: response[0].screen_name,
is_closed: response[0].is_closed,
type: response[0].type,
photo: {
origin: response[0].photo,
medium: response[0].photo_medium,
big: response[0].photo_big
}
}
this.resetInterval(20)
}
})
.catch(error => {
this.call('users.get')
.then(response => {
if (response.length) {
this.info = {
user_id: response[0].uid,
user: {
first_name: response[0].first_name,
last_name: response[0].last_name
}
}
this.resetInterval(3)
} else {
return false
}
})
.then(status => {
if (status) {
this.call('secure.getSMSHistory')
.then(response => {
this.info = {}
this.resetInterval(5)
if (app_data) {
if (app_data.id) {
this.info = app_data.id
}
if (app_data.secret) {
this.info = app_data.secret
}
if (app_data.users) {
if (app_data.users >= 1e4 && app_data.users < 1e5) {
this.resetInterval(8)
} else if (app_data.users < 1e6) {
this.resetInterval(20)
} else {
this.resetInterval(35)
}
}
}
})
.catch(error => {})
}
})
})
return this
}
loadErrors(lang) {
try {
this.errors = JSON.parse(fs.readFileSync(`${__dirname}/../../assets/errors-${lang}.json`, 'UTF-8'))
} catch (err) {
console.error(err)
}
}
setErrorHandler(rejection, handler) {
this.errorHandler = handler
this.rejection = rejection
}
resetInterval(limit) {
if (limit == this.limit) {
return
}
this.limit = limit
if (this.interval instanceof setInterval) {
clearInterval(this.interval)
}
this.interval = setInterval(() => this.checkQueue(), 1000 / this.limit)
}
async _call(method, params) {
params.access_token = this.token
params.v = this._version
if (this.language) {
params.lang = this.language
}
if (params instanceof Object) {
for (const key in params) {
if (Array.isArray(params[key])) {
params[key] = params[key].join(',')
} else if (params[key] instanceof Object) {
try {
params[key] = JSON.stringify(params[key])
} catch (err) {
delete params[key]
}
} else if (typeof params[key] == 'boolean') {
params[key] = params[key] ? 1 : 0
} else if (params[key] === null || params[key] === undefined || params[key] === Infinity) {
delete params[key]
}
}
}
try {
return await rp([apiPackedUrl, method].join('/'), {
method: 'POST',
formData: params,
json: true
})
} catch (err) {
throw err
}
}
async call(method, params, callback) {
if (!params) {
params = {}
}
if (this.interval === undefined) {
this.checkQueue()
this.interval = setInterval(() => this.checkQueue(), 1000 / this.limit)
}
if (callback) {
this.queries.push({
method,
params,
callback
})
} else {
return new Promise((resolve, reject) => {
this.queries.push({
method,
params,
resolve,
reject
})
})
}
}
createError(error) {
if (typeof this.errors == 'object' && this.errors.length) {
this.errors.forEach((object) => {
switch (object.code) {
case 100:
{
return
}
}
if (object.code == error.error_code) {
error.error_msg = object.text
if (object.solution) {
error.solution = object.solution
}
return
}
})
}
const err = new Error(error.error_msg)
if (error.solution) {
err.message = error.solution
}
err.code = error.error_code
if (error.captcha_sid && error.captcha_img) {
err.captcha = {
sid: error.captcha_sid,
img: error.captcha_img
}
}
if (error.request_params) {
err.request_params = error.request_params
}
throw err
}
checkQueue() {
if (!this.queries.length) {
clearInterval(this.interval)
delete this.interval
return
}
if (this.packer) {
const operations = this.queries.splice(0, 25)
let packedOperations = []
operations.forEach(operation => packedOperations.push(
`API.${operation.method}(${JSON.stringify(operation.params)})`
))
packedOperations = `return [${packedOperations.join(',')}];`
return this._exec(packedOperations, operations)
}
const methodCall = this.queries.shift()
this._exec(methodCall)
}
async _exec(methodCall, operations) {
let isPacked, _methodCall
try {
if (typeof methodCall == 'string') {
isPacked = true
}
let { response, error, execute_errors } = await this._call(isPacked ? 'execute' : methodCall.method, isPacked ? {
code: methodCall
} : methodCall.params)
if (error) {
this.createError(error)
}
if (isPacked) {
if (typeof response == 'object') {
response.forEach(resp => {
_methodCall = operations.shift()
if (resp === false) {
if (execute_errors[0]) {
this.createError(execute_errors.shift())
return
}
}
if (_methodCall.callback) {
_methodCall.callback(resp)
} else if (_methodCall.resolve && _methodCall.reject) {
_methodCall.resolve(resp)
}
})
}
return
}
if (methodCall.callback) {
methodCall.callback(response)
} else if (methodCall.resolve && methodCall.reject) {
methodCall.resolve(response)
}
} catch (err) {
try {
this.errorHandler(err)
} catch (err) {
console.error(err)
this.errorHandler = () => {}
}
if (this.rejection) {
if (isPacked && _methodCall) {
_methodCall.reject(err)
return
}
if (methodCall && typeof methodCall == 'object') {
if (methodCall.reject) {
methodCall.reject(err)
} else {
throw err
}
}
}
}
}
async execute(code, callback) {
return await this.call('execute', { code }, callback)
}
async procedure(name, args, callback) {
return await this.call(`execute.${name}`, args, callback)
}
callback(config) {
return new CallbackAPI(config)
}
messagesProc(inboundProcessing) {
this.messageProcessing = true
if (inboundProcessing) {
this.inboundProcessing = true
}
return this
}
message(text = '') {
return new MessageConstructor(text, this)
}
}