airship-server
Version:
Airship is a framework for Node.JS & TypeScript that helps you to write big, scalable and maintainable API servers.
155 lines • 7.77 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const VKApiError_1 = require("../../vkApi/VKApiError");
/**
* BroadcastSender tries to send messages as 2500 item batches per call,
* but if there is any error (and because of api we cant exactly say what message caused an error)
* it sends that messages to NotificationsSender which sends them in 25 item batches and handles errors for every message
*/
class HHEventsBroadcastSender {
constructor(tokensProvider, notificationsProvider, outputMessageSender, outputMessageTransmitter, logger) {
this._tokensProvider = tokensProvider;
this._notificationsProvider = notificationsProvider;
this._outputMessageSender = outputMessageSender;
this._outputMessageTransmitter = outputMessageTransmitter;
this._logger = logger;
this._groupsMessagesQueues = [];
this._groupsTimeouts = [];
this._notificationsProvider.getMessages((outputMessage, ack) => {
ack();
this.handleMessage(outputMessage, () => { });
});
}
handleMessage(message, ack) {
return __awaiter(this, void 0, void 0, function* () {
let groupId = message.groupId;
if (!this._groupsMessagesQueues[groupId])
this._groupsMessagesQueues[groupId] = [];
this._groupsMessagesQueues[groupId].push({ message, ack });
if (this._groupsMessagesQueues[groupId].length == 1) {
this._groupsTimeouts[groupId] = setTimeout(() => {
clearTimeout(this._groupsTimeouts[message.groupId]);
this._groupsTimeouts[message.groupId] = null;
let batch = this._groupsMessagesQueues[message.groupId].slice(0, 2500);
this._groupsMessagesQueues[message.groupId] = this._groupsMessagesQueues[message.groupId].slice(2500);
this.sendByTimeout(batch);
}, 1000);
}
else if (this._groupsMessagesQueues[message.groupId].length >= 2500) {
let batch = this._groupsMessagesQueues[message.groupId].slice(0, 2500);
this._groupsMessagesQueues[message.groupId] = this._groupsMessagesQueues[message.groupId].slice(2500);
clearTimeout(this._groupsTimeouts[message.groupId]);
this._groupsTimeouts[message.groupId] = null;
let token = yield this._tokensProvider.getToken(message);
yield this.sendBatch(batch, token);
}
});
}
sendByTimeout(messages) {
return __awaiter(this, void 0, void 0, function* () {
let token = yield this._tokensProvider.getToken(messages[0].message);
yield this.sendBatch(messages, token);
});
}
sendBatch(messages, token) {
return __awaiter(this, void 0, void 0, function* () {
function retry(message, ack) {
setTimeout(() => {
this.handleMessage(message, ack);
}, 1000);
}
try {
let response = yield this._outputMessageSender.broadcast(messages.map(m => m.message), token);
if (response['execute_errors']) {
let errorIndex = 0;
let i = 0;
for (let responseCode of response['response']) {
// vk api sometimes returns 0 for some reason
if (responseCode === false) {
let error = response['execute_errors'][errorIndex];
// too many reqs & server error
if (error['error_code'] == 6 ||
//error['error_code'] == 9 ||
error['error_code'] == 10) {
retry.bind(this)(messages[i].message, messages[i].ack);
}
else {
console.log('execute error', error);
console.log('resending batch message by message');
let batch = messages.slice((i * 100), (i * 100) + 100);
for (let item of batch) {
this._outputMessageTransmitter.transmit(item.message);
item.ack();
}
}
// else if (error['error_code'] == 901) {
// // no permissions to send to user
// this._logger.warn(`no permissions to send to user userId: ${messages[i].message.userId} groupId: ${messages[i].message.groupId}`)
// //await this._bannedEndpointsCache.cache(messages[i].message.userId, true, 1000 * 60 * 5)
// messages[i].ack()
// }
// else if (error['error_code'] == 5) {
// // group is banned
// messages[i].ack()
// }
// else {
// messages[i].ack()
// }
errorIndex++;
}
else {
messages[i].ack();
}
i++;
}
}
else {
for (let i = 0; i <= response['response'].length - 1; i++) {
messages[i].ack();
}
}
}
catch (e) {
console.log(e);
if (e instanceof VKApiError_1.default) {
if (e.errorCode == 6) {
// too many requests
setTimeout(() => {
for (let i = 0; i <= messages.length - 1; i++) {
this.handleMessage(messages[i].message, messages[i].ack);
}
}, 300);
}
else if (e.errorCode == 5) {
// group banned
for (let i = 0; i <= messages.length - 1; i++) {
messages[i].ack();
}
}
else {
this._logger.error('Strange vk bug');
for (let i = 0; i <= messages.length - 1; i++) {
messages[i].ack();
}
}
}
else {
this._logger.error('Very bad bug');
for (let i = 0; i <= messages.length - 1; i++) {
messages[i].ack();
}
}
}
});
}
}
exports.default = HHEventsBroadcastSender;
//# sourceMappingURL=HHEventsBroadcastSender.js.map