From b6d703d3c20d7457145ede1a244dc83dd5a9eed5 Mon Sep 17 00:00:00 2001 From: fegloff Date: Sat, 6 Jul 2024 00:02:08 -0500 Subject: [PATCH] add error handling to stats, botstats, allstats commands --- src/database/stats.service.ts | 262 ++++++++++++++++------------ src/modules/schedule/bridgeAPI.ts | 45 +++-- src/modules/schedule/exchangeApi.ts | 22 ++- src/modules/schedule/explorerApi.ts | 20 ++- src/modules/schedule/subgraphAPI.ts | 24 ++- 5 files changed, 237 insertions(+), 136 deletions(-) diff --git a/src/database/stats.service.ts b/src/database/stats.service.ts index 09e3d98e..6203adbe 100644 --- a/src/database/stats.service.ts +++ b/src/database/stats.service.ts @@ -2,6 +2,15 @@ import { AppDataSource } from './datasource' import { StatBotCommand } from './entities/StatBotCommand' import moment from 'moment-timezone' import { BotLog } from './entities/Log' +import pino from 'pino' + +const logger = pino({ + name: 'StatsService', + transport: { + target: 'pino-pretty', + options: { colorize: true } + } +}) const statBotCommandRepository = AppDataSource.getRepository(StatBotCommand) const logRepository = AppDataSource.getRepository(BotLog) @@ -37,143 +46,176 @@ export class StatsService { } async getTotalONE (): Promise { - const rows = await logRepository.query('select sum("amountOne") from logs') - return rows.length ? +rows[0].sum : 0 + try { + const rows = await logRepository.query('select sum("amountOne") from logs') + return rows.length ? +rows[0].sum : 0 + } catch (e) { + logger.error(e) + return 0 + } } async getTotalFreeCredits (): Promise { - const rows = await logRepository.query('select sum("amountCredits") from logs') - return rows.length ? +rows[0].sum : 0 + try { + const rows = await logRepository.query('select sum("amountCredits") from logs') + return rows.length ? +rows[0].sum : 0 + } catch (e) { + logger.error(e) + return 0 + } } async getUniqueUsersCount (): Promise { - const rows = await logRepository.query('select count(distinct("tgUserId")) from logs') - return rows.length ? +rows[0].count : 0 + try { + const rows = await logRepository.query('select count(distinct("tgUserId")) from logs') + return rows.length ? +rows[0].count : 0 + } catch (e) { + logger.error(e) + return 0 + } } public async getActiveUsers (daysPeriod = 0): Promise { - const currentTime = moment() - const dateStart = moment() - .tz('America/Los_Angeles') - .set({ hour: 0, minute: 0, second: 0 }) - .subtract(daysPeriod, 'days') - .unix() - - const dateEnd = currentTime.unix() - - const rows = await logRepository - .createQueryBuilder('logs') - .select('count(distinct(logs."tgUserId"))') - .where(`logs.createdAt BETWEEN TO_TIMESTAMP(${dateStart}) and TO_TIMESTAMP(${dateEnd})`) - .execute() - - return rows.length ? +rows[0].count : 0 + try { + const currentTime = moment() + const dateStart = moment() + .tz('America/Los_Angeles') + .set({ hour: 0, minute: 0, second: 0 }) + .subtract(daysPeriod, 'days') + .unix() + const dateEnd = currentTime.unix() + const rows = await logRepository + .createQueryBuilder('logs') + .select('count(distinct(logs."tgUserId"))') + .where(`logs.createdAt BETWEEN TO_TIMESTAMP(${dateStart}) and TO_TIMESTAMP(${dateEnd})`) + .execute() + return rows.length ? +rows[0].count : 0 + } catch (e) { + logger.error(e) + return 0 + } } public async getNewUsers (daysPeriod = 0): Promise { - const currentTime = moment() - const dateStart = moment() - .tz('America/Los_Angeles') - .set({ hour: 0, minute: 0, second: 0 }) - .subtract(daysPeriod, 'days') - .unix() - - const dateEnd = currentTime.unix() - - const query = logRepository - .createQueryBuilder('logs') - .select('distinct("FirstInsertTime")') - .from(subQuery => - subQuery - .select('"tgUserId", MIN("createdAt") AS "FirstInsertTime"') - .from(BotLog, 'logs') - .groupBy('"tgUserId"'), 'first_inserts') - if (daysPeriod > 0) { - query.where(`"FirstInsertTime" BETWEEN TO_TIMESTAMP(${dateStart}) and TO_TIMESTAMP(${dateEnd})`) + try { + const currentTime = moment() + const dateStart = moment() + .tz('America/Los_Angeles') + .set({ hour: 0, minute: 0, second: 0 }) + .subtract(daysPeriod, 'days') + .unix() + const dateEnd = currentTime.unix() + const query = logRepository + .createQueryBuilder('logs') + .select('distinct("FirstInsertTime")') + .from(subQuery => + subQuery + .select('"tgUserId", MIN("createdAt") AS "FirstInsertTime"') + .from(BotLog, 'logs') + .groupBy('"tgUserId"'), 'first_inserts') + if (daysPeriod > 0) { + query.where(`"FirstInsertTime" BETWEEN TO_TIMESTAMP(${dateStart}) and TO_TIMESTAMP(${dateEnd})`) + } + const result = await query.execute() + // console.log(dateStart, dateEnd, result.length) + return result.length + } catch (e) { + logger.error(e) + return 0 } - const result = await query.execute() - // console.log(dateStart, dateEnd, result.length) - return result.length } // Doesn't check last 7 days. public async getOnetimeUsers (): Promise { - const bufferDays = 7 - const bufferDate = moment() - .tz('America/Los_Angeles') - .set({ hour: 0, minute: 0, second: 0 }) - .subtract(bufferDays, 'days') - .unix() - - const query = await logRepository - .createQueryBuilder('logs') - .select('count("tgUserId") AS row_count, "tgUserId", MAX("createdAt") AS max_created') - .where(`"createdAt" < TO_TIMESTAMP(${bufferDate})`) - .groupBy('"tgUserId"') - .getRawMany() - const result = query.filter(row => row.row_count === '1') - return result.length + try { + const bufferDays = 7 + const bufferDate = moment() + .tz('America/Los_Angeles') + .set({ hour: 0, minute: 0, second: 0 }) + .subtract(bufferDays, 'days') + .unix() + const query = await logRepository + .createQueryBuilder('logs') + .select('count("tgUserId") AS row_count, "tgUserId", MAX("createdAt") AS max_created') + .where(`"createdAt" < TO_TIMESTAMP(${bufferDate})`) + .groupBy('"tgUserId"') + .getRawMany() + const result = query.filter(row => row.row_count === '1') + return result.length + } catch (e) { + logger.error(e) + return 0 + } } public async getTotalMessages (daysPeriod = 0, onlySupportedCommands = false): Promise { - const currentTime = moment() - const dateStart = moment() - .tz('America/Los_Angeles') - .set({ hour: 0, minute: 0, second: 0 }) - .subtract(daysPeriod, 'days') - .unix() - - const dateEnd = currentTime.unix() - - const query = logRepository - .createQueryBuilder('logs') - .select('count(*)') - if (daysPeriod > 0) { - query.where(`logs.createdAt BETWEEN TO_TIMESTAMP(${dateStart}) and TO_TIMESTAMP(${dateEnd})`) - } - if (onlySupportedCommands) { - query.andWhere('logs.isSupportedCommand=true') + try { + const currentTime = moment() + const dateStart = moment() + .tz('America/Los_Angeles') + .set({ hour: 0, minute: 0, second: 0 }) + .subtract(daysPeriod, 'days') + .unix() + + const dateEnd = currentTime.unix() + + const query = logRepository + .createQueryBuilder('logs') + .select('count(*)') + if (daysPeriod > 0) { + query.where(`logs.createdAt BETWEEN TO_TIMESTAMP(${dateStart}) and TO_TIMESTAMP(${dateEnd})`) + } + if (onlySupportedCommands) { + query.andWhere('logs.isSupportedCommand=true') + } + const rows = await query.execute() + return rows.length ? +rows[0].count : 0 + } catch (e) { + logger.error(e) + return 0 } - - const rows = await query.execute() - - return rows.length ? +rows[0].count : 0 } public async getUserEngagementByCommand (daysPeriod = 7): Promise { - const currentTime = moment() - const dateStart = moment() - .tz('America/Los_Angeles') - .set({ hour: 0, minute: 0, second: 0 }) - .subtract(daysPeriod, 'days') - .unix() - - const dateEnd = currentTime.unix() - - const rows = await logRepository.createQueryBuilder('logs') - .select('logs.command, count(logs.command) as "commandCount", SUM(logs.amountOne) as "oneAmount"') - .where(`logs.createdAt BETWEEN TO_TIMESTAMP(${dateStart}) and TO_TIMESTAMP(${dateEnd})`) - .groupBy('logs.command') - .orderBy('"commandCount"', 'DESC').execute() - - return rows + try { + const currentTime = moment() + const dateStart = moment() + .tz('America/Los_Angeles') + .set({ hour: 0, minute: 0, second: 0 }) + .subtract(daysPeriod, 'days') + .unix() + const dateEnd = currentTime.unix() + const rows = await logRepository.createQueryBuilder('logs') + .select('logs.command, count(logs.command) as "commandCount", SUM(logs.amountOne) as "oneAmount"') + .where(`logs.createdAt BETWEEN TO_TIMESTAMP(${dateStart}) and TO_TIMESTAMP(${dateEnd})`) + .groupBy('logs.command') + .orderBy('"commandCount"', 'DESC').execute() + return rows + } catch (e) { + logger.error(e) + return [] + } } public async getRevenueFromLog (daysPeriod = 7): Promise { - const currentTime = moment() - const dateStart = moment() - .tz('America/Los_Angeles') - .set({ hour: 0, minute: 0, second: 0 }) - .subtract(daysPeriod, 'days') - .unix() - - const dateEnd = currentTime.unix() - const result = await logRepository.createQueryBuilder('logs') - .select('SUM(CAST(logs.amountCredits AS NUMERIC)) AS revenue') - .where('logs.isSupportedCommand=true') - .andWhere(`logs.createdAt BETWEEN TO_TIMESTAMP(${dateStart}) and TO_TIMESTAMP(${dateEnd})`) - .execute() - return result[0].revenue + try { + const currentTime = moment() + const dateStart = moment() + .tz('America/Los_Angeles') + .set({ hour: 0, minute: 0, second: 0 }) + .subtract(daysPeriod, 'days') + .unix() + const dateEnd = currentTime.unix() + const result = await logRepository.createQueryBuilder('logs') + .select('SUM(CAST(logs.amountCredits AS NUMERIC)) AS revenue') + .where('logs.isSupportedCommand=true') + .andWhere(`logs.createdAt BETWEEN TO_TIMESTAMP(${dateStart}) and TO_TIMESTAMP(${dateEnd})`) + .execute() + return result[0].revenue + } catch (e) { + logger.error(e) + return '' + } } public async addCommandStat ({ tgUserId, rawMessage, command }: { tgUserId: number, rawMessage: string, command: string }): Promise { diff --git a/src/modules/schedule/bridgeAPI.ts b/src/modules/schedule/bridgeAPI.ts index bb379c82..96810f44 100644 --- a/src/modules/schedule/bridgeAPI.ts +++ b/src/modules/schedule/bridgeAPI.ts @@ -2,6 +2,15 @@ import axios from 'axios' import moment from 'moment' import { abbreviateNumber, getPercentDiff } from './utils' import { BigNumber } from 'bignumber.js' +import pino from 'pino' + +const logger = pino({ + name: 'BridgeAPI', + transport: { + target: 'pino-pretty', + options: { colorize: true } + } +}) const bridgeUrl = 'https://hmy-lz-api-token.fly.dev' const stakeApiUrl = 'https://api.stake.hmny.io' @@ -144,8 +153,13 @@ export const getBridgeStats = async (): Promise<{ change: string, value: string } export const getTVL = async (): Promise => { - const tokens = await getTokensList() - return tokens.reduce((acc, item) => acc + +item.totalLockedUSD, 0) + try { + const tokens = await getTokensList() + return tokens.reduce((acc, item) => acc + +item.totalLockedUSD, 0) + } catch (e) { + logger.error(e) + return 0 + } } export const getTotalStakes = async (): Promise => { @@ -154,17 +168,20 @@ export const getTotalStakes = async (): Promise => { } export const getAvgStakes = async (): Promise => { - const { history } = await getStakingStats() - const sortedHistory = Object.values(history).sort((a, b) => b.current_epoch - a.current_epoch) - - const epochCount = 30 - const values = [] - for (let i = 0; i < sortedHistory.length || i < epochCount; i++) { - values.push(sortedHistory[i]['total-staking']) + try { + const { history } = await getStakingStats() + const sortedHistory = Object.values(history).sort((a, b) => b.current_epoch - a.current_epoch) + const epochCount = 30 + const values = [] + for (let i = 0; i < sortedHistory.length || i < epochCount; i++) { + values.push(sortedHistory[i]['total-staking']) + } + // calc avg total / values.length + return values.reduce((acc, item) => { + return acc.plus(item) + }, BigNumber(0)).div(values.length).div(10 ** 18).toNumber() + } catch (e) { + logger.error(e) + return 0 } - - // calc avg total / values.length - return values.reduce((acc, item) => { - return acc.plus(item) - }, BigNumber(0)).div(values.length).div(10 ** 18).toNumber() } diff --git a/src/modules/schedule/exchangeApi.ts b/src/modules/schedule/exchangeApi.ts index 25cd0828..c2bcd96a 100644 --- a/src/modules/schedule/exchangeApi.ts +++ b/src/modules/schedule/exchangeApi.ts @@ -1,4 +1,13 @@ import axios from 'axios' +import pino from 'pino' + +const logger = pino({ + name: 'ExchangeAPI', + transport: { + target: 'pino-pretty', + options: { colorize: true } + } +}) interface CoinGeckoResponse { harmony: { @@ -7,8 +16,13 @@ interface CoinGeckoResponse { } export const getOneRate = async (): Promise => { - const { data } = await axios.get( - 'https://api.coingecko.com/api/v3/simple/price?ids=harmony&vs_currencies=usd' - ) - return +data.harmony.usd + try { + const { data } = await axios.get( + 'https://api.coingecko.com/api/v3/simple/price?ids=harmony&vs_currencies=usd' + ) + return +data.harmony.usd + } catch (e) { + logger.error(e) + return 0 + } } diff --git a/src/modules/schedule/explorerApi.ts b/src/modules/schedule/explorerApi.ts index 916952fa..9db22dde 100644 --- a/src/modules/schedule/explorerApi.ts +++ b/src/modules/schedule/explorerApi.ts @@ -1,6 +1,15 @@ import axios from 'axios' import config from '../../config' import { abbreviateNumber, getPercentDiff } from './utils' +import pino from 'pino' + +const logger = pino({ + name: 'ExplorerAPI', + transport: { + target: 'pino-pretty', + options: { colorize: true } + } +}) export interface MetricsDaily { date: string @@ -18,9 +27,14 @@ export enum MetricsDailyType { } export const getDailyMetrics = async (type: MetricsDailyType, limit: number): Promise => { - const feesUrl = `${apiUrl}/v0/metrics?type=${type}&limit=${limit}` - const { data } = await axios.get(feesUrl, { headers: { 'X-API-KEY': apiKey } }) - return data + try { + const feesUrl = `${apiUrl}/v0/metrics?type=${type}&limit=${limit}` + const { data } = await axios.get(feesUrl, { headers: { 'X-API-KEY': apiKey } }) + return data + } catch (e) { + logger.error('ERROR', e) + return [] + } } export const getFeeStats = async (): Promise<{ change: string, value: string }> => { diff --git a/src/modules/schedule/subgraphAPI.ts b/src/modules/schedule/subgraphAPI.ts index f7e06023..bffb7228 100644 --- a/src/modules/schedule/subgraphAPI.ts +++ b/src/modules/schedule/subgraphAPI.ts @@ -1,5 +1,14 @@ import axios from 'axios' import config from '../../config' +import pino from 'pino' + +const logger = pino({ + name: 'SubgraphAPI', + transport: { + target: 'pino-pretty', + options: { colorize: true } + } +}) export interface TradingVolume { id: string @@ -26,9 +35,14 @@ const generateTradingVolumeQuery = (first = 30): string => { } export const getTradingVolume = async (daysCount = 30): Promise => { - const { data } = await axios.post( - config.schedule.swapSubgraphApiUrl, - { query: generateTradingVolumeQuery(daysCount) } - ) - return data.data.uniswapDayDatas + try { + const { data } = await axios.post( + config.schedule.swapSubgraphApiUrl, + { query: generateTradingVolumeQuery(daysCount) } + ) + return data.data.uniswapDayDatas + } catch (e) { + logger.error(e) + return [] + } }