From 7000c7176a791a707c068cff2e5d0d753f7bf745 Mon Sep 17 00:00:00 2001 From: Paul-Louis Hery Date: Mon, 26 Jun 2023 20:59:44 +0200 Subject: [PATCH] feat(workers): improve error management --- unfollow-ninja-server/src/tasks/notifyUser.ts | 60 +++++------------ .../src/tasks/sendDailyDM.ts | 67 ++++++------------- .../src/tasks/sendWelcomeMessage.ts | 65 +++++------------- .../src/workers/checkAllFollowers.ts | 52 ++++---------- 4 files changed, 64 insertions(+), 180 deletions(-) diff --git a/unfollow-ninja-server/src/tasks/notifyUser.ts b/unfollow-ninja-server/src/tasks/notifyUser.ts index 805276e..95a725f 100644 --- a/unfollow-ninja-server/src/tasks/notifyUser.ts +++ b/unfollow-ninja-server/src/tasks/notifyUser.ts @@ -6,7 +6,6 @@ import logger from '../utils/logger'; import { IUnfollowerInfo, Lang } from '../utils/types'; import Task from './task'; import metrics from '../utils/metrics'; -import { NotificationEvent } from '../dao/userEventDao'; import { SUPPORTED_LANGUAGES } from '../utils/utils'; import { ApiResponseError, UsersV2Result } from 'twitter-api-v2'; import { sendRevokedEmailToUserId } from '../utils/emailSender'; @@ -319,50 +318,21 @@ export default class extends Task { const userDao = this.dao.getUserDao(userId); - for (const { code, message } of [err]) { - switch (code) { - case 17: // no user matches the specified terms (users/lookup) - case 50: // user not found (friendship/show) - break; - // app-related - case 32: - throw new Error( - 'Authentication problems.' + 'Please check that your consumer key & secret are correct.' - ); - case 416: - throw new Error('Oops, it looks like the application has been suspended :/...'); - // user-related - case 89: - case 401: // since V2? but not clear message - logger.warn('@%s revoked the token. removing them from the list...', username); - await userDao.setCategory(UserCategory.revoked); - await sendRevokedEmailToUserId(userId); - return true; - case 326: - case 64: - case 403: // since V2? but not clear message - logger.warn('@%s is suspended. removing them from the list...', username); - await userDao.setCategory(UserCategory.suspended); - return true; - case 150: // dm closed to non-followers - case 349: // user blocked? - logger.warn('@%s does not accept DMs. removing them from the list...', username); - await userDao.setCategory(UserCategory.dmclosed); - return true; - case 292: - throw new Error('Notification blocked because "it seems automated".'); - // twitter errors - case 130: // over capacity - case 131: // internal error` - throw new Error('Twitter has problems at the moment, skipping this action.'); - case 88: // rate limit - throw new Error('the user reached its rate-limit (notifyUser)'); - default: - throw new Error( - `An unexpected twitter error occured: ${code} ${message} ${err.data.title} ${err.data.detail}` - ); - } + switch (err?.data?.detail) { + case 'Unauthorized': // since V2? but not clear message + logger.warn('@%s revoked the token. Removing them from the list...', await userDao.getUsername()); + await userDao.setCategory(UserCategory.revoked); + await sendRevokedEmailToUserId(userId); + return true; + case 'Forbidden': // since V2? but not clear message + logger.warn('@%s is suspended. Removing them from the list...', await userDao.getUsername()); + await userDao.setCategory(UserCategory.suspended); + return true; + default: + throw new Error( + `[checkFollowers] An unexpected twitter error occured: ${err?.code} ${err?.message} ${err?.data?.title} ${err?.data?.detail}` + ); } - return false; + // return false; } } diff --git a/unfollow-ninja-server/src/tasks/sendDailyDM.ts b/unfollow-ninja-server/src/tasks/sendDailyDM.ts index c84a1b9..e425a79 100644 --- a/unfollow-ninja-server/src/tasks/sendDailyDM.ts +++ b/unfollow-ninja-server/src/tasks/sendDailyDM.ts @@ -10,6 +10,8 @@ import { NotificationEvent } from '../dao/userEventDao'; import { SUPPORTED_LANGUAGES } from '../utils/utils'; import { ApiResponseError } from 'twitter-api-v2'; import * as Sentry from '@sentry/node'; +import UserDao from '../dao/userDao'; +import { sendRevokedEmailToUserId } from '../utils/emailSender'; i18n.configure({ locales: SUPPORTED_LANGUAGES, @@ -201,59 +203,28 @@ export default class extends Task { // throw an error if it's a twitter problem // return true if we can't continue to process the user - private async manageTwitterErrors(err: unknown, username: string, userId: string): Promise { + private async manageTwitterErrors(err: unknown, username: string, userId: string): Promise { if (!(err instanceof ApiResponseError)) { throw err; } - // const userDao = this.dao.getUserDao(userId); let error; - for (const { code, message } of [err]) { - switch (code) { - /* case 17: // no user matches the specified terms (users/lookup) - case 50: // user not found (friendship/show) - break; - // app-related - case 32: - throw new Error( - 'Authentication problems.' + 'Please check that your consumer key & secret are correct.' - ); - case 416: - throw new Error('Oops, it looks like the application has been suspended :/...'); - // user-related - case 89: - case 401: // since V2? but not clear message - logger.warn('@%s revoked the token. removing them from the list...', username); - await userDao.setCategory(UserCategory.revoked); - await sendRevokedEmailToUserId(userId); - return true; - case 326: - case 64: - case 403: // since V2? but not clear message - logger.warn('@%s is suspended. removing them from the list...', username); - await userDao.setCategory(UserCategory.suspended); - return true; - case 150: // dm closed to non-followers - case 349: // user blocked? - logger.warn('@%s does not accept DMs. removing them from the list...', username); - await userDao.setCategory(UserCategory.dmclosed); - return true; - case 292: - throw new Error('Notification blocked because "it seems automated".'); - // twitter errors - case 130: // over capacity - case 131: // internal error` - throw new Error('Twitter has problems at the moment, skipping this action.'); - case 88: // rate limit - throw new Error('the user reached its rate-limit (notifyUser)');*/ - default: - error = new Error( - `An unexpected twitter error occured: @${username}/${userId} - ${code} ${message} ${err.data.title} ${err.data.detail}` - ); - logger.error(error); - Sentry.captureException(error); - } + switch (err?.data?.detail) { + /*case 'Unauthorized': // since V2? but not clear message + logger.warn('@%s revoked the token. Removing them from the list...', await userDao.getUsername()); + await userDao.setCategory(UserCategory.revoked); + await sendRevokedEmailToUserId(userId); + break; + case 'Forbidden': // since V2? but not clear message + logger.warn('@%s is suspended. Removing them from the list...', await userDao.getUsername()); + await userDao.setCategory(UserCategory.suspended); + break;*/ + default: + error = new Error( + `[checkFollowers] An unexpected twitter error occured @${username}/${userId}: ${err?.code} ${err?.message} ${err?.data?.title} ${err?.data?.detail}` + ); + logger.error(error); + Sentry.captureException(error); } - return false; } } diff --git a/unfollow-ninja-server/src/tasks/sendWelcomeMessage.ts b/unfollow-ninja-server/src/tasks/sendWelcomeMessage.ts index e1762bb..92622ec 100644 --- a/unfollow-ninja-server/src/tasks/sendWelcomeMessage.ts +++ b/unfollow-ninja-server/src/tasks/sendWelcomeMessage.ts @@ -7,6 +7,7 @@ import { NotificationEvent } from '../dao/userEventDao'; import { SUPPORTED_LANGUAGES } from '../utils/utils'; import { ApiResponseError } from 'twitter-api-v2'; import { sendRevokedEmailToUserId } from '../utils/emailSender'; +import UserDao from '../dao/userDao'; i18n.configure({ locales: SUPPORTED_LANGUAGES, @@ -56,55 +57,21 @@ export default class extends Task { .catch((err) => this.manageTwitterErrors(err, username, userId)); */ } - private async manageTwitterErrors(err: unknown, username: string, userId: string): Promise { - if (!(err instanceof ApiResponseError)) { - throw err; - } - - const userDao = this.dao.getUserDao(userId); - - for (const { code, message } of [err]) { - switch (code) { - // app-related - case 32: - throw new Error( - 'Authentication problems.' + 'Please check that your consumer key & secret are correct.' - ); - case 416: - throw new Error('Oops, it looks like the application has been suspended :/...'); - // user-related - case 89: - case 401: // since V2? but not clear message - logger.warn('@%s revoked the token. removing them from the list...', username); - await userDao.setCategory(UserCategory.revoked); - await sendRevokedEmailToUserId(userId); - break; - case 326: - case 64: - case 403: // since V2? but not clear message - logger.warn('@%s is suspended. removing them from the list...', username); - await userDao.setCategory(UserCategory.suspended); - break; - // twitter errors - case 130: // over capacity - case 131: // internal error` - case 88: // rate limit - // retry in 15 minutes - await this.queue.add( - 'sendWelcomeMessage', - { - id: Date.now(), // otherwise some seem stuck?? - userId, - username, - }, - { delay: 15 * 60 * 1000 } - ); - break; - default: - throw new Error( - `An unexpected twitter error occured: ${code} ${message} ${err.data.title} ${err.data.detail}` - ); - } + private async manageTwitterErrors(err: ApiResponseError, userDao: UserDao, userId: string): Promise { + switch (err?.data?.detail) { + case 'Unauthorized': // since V2? but not clear message + logger.warn('@%s revoked the token. Removing them from the list...', await userDao.getUsername()); + await userDao.setCategory(UserCategory.revoked); + await sendRevokedEmailToUserId(userId); + break; + case 'Forbidden': // since V2? but not clear message + logger.warn('@%s is suspended. Removing them from the list...', await userDao.getUsername()); + await userDao.setCategory(UserCategory.suspended); + break; + default: + throw new Error( + `[checkFollowers] An unexpected twitter error occured: ${err?.code} ${err?.message} ${err?.data?.title} ${err?.data?.detail}` + ); } } } diff --git a/unfollow-ninja-server/src/workers/checkAllFollowers.ts b/unfollow-ninja-server/src/workers/checkAllFollowers.ts index 4899951..1ae3b0c 100644 --- a/unfollow-ninja-server/src/workers/checkAllFollowers.ts +++ b/unfollow-ninja-server/src/workers/checkAllFollowers.ts @@ -286,43 +286,19 @@ async function detectUnfollows(userId: string, followers: string[], formerFollow // Manage or rethrow twitter errors async function manageTwitterErrors(err: ApiResponseError, userDao: UserDao, userId: string): Promise { - for (const { code, message } of [err]) { - switch (code) { - // app-related - case 32: - throw new Error(`[checkFollowers] Authentication problems. Please check that your consumer key.`); - case 416: - throw new Error(`[checkFollowers] Oops, it looks like the application has been suspended :/...`); - // user-related - case 89: - case 401: // since V2? but not clear message - logger.warn('@%s revoked the token. Removing them from the list...', await userDao.getUsername()); - await userDao.setCategory(UserCategory.revoked); - await sendRevokedEmailToUserId(userId); - break; - case 326: - case 64: - case 403: // since V2? but not clear message - logger.warn('@%s is suspended. Removing them from the list...', await userDao.getUsername()); - await userDao.setCategory(UserCategory.suspended); - break; - case 34: // 404 - the user closed his account? - logger.warn( - "@%s this account doesn't exist. Removing them from the list...", - await userDao.getUsername() - ); - await userDao.setCategory(UserCategory.accountClosed); - break; - // twitter errors - case 130: // over capacity - case 131: // internal error - throw new Error(`[checkFollowers] internal Twitter error`); - case 88: // rate limit - throw new Error(`[checkFollowers] the user reached its rate-limit.`); - default: - throw new Error( - `[checkFollowers] An unexpected twitter error occured: ${code} ${message} ${err.data.title} ${err.data.detail}` - ); - } + switch (err?.data?.detail) { + case 'Unauthorized': // since V2? but not clear message + logger.warn('@%s revoked the token. Removing them from the list...', await userDao.getUsername()); + await userDao.setCategory(UserCategory.revoked); + await sendRevokedEmailToUserId(userId); + break; + case 'Forbidden': // since V2? but not clear message + logger.warn('@%s is suspended. Removing them from the list...', await userDao.getUsername()); + await userDao.setCategory(UserCategory.suspended); + break; + default: + throw new Error( + `[checkFollowers] An unexpected twitter error occured: ${err?.code} ${err?.message} ${err?.data?.title} ${err?.data?.detail}` + ); } }