mirror of
https://github.com/hexastack/hexabot
synced 2025-06-26 18:27:28 +00:00
137 lines
4.8 KiB
TypeScript
137 lines
4.8 KiB
TypeScript
/*
|
|
* Copyright © 2025 Hexastack. All rights reserved.
|
|
*
|
|
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
|
|
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
|
|
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
|
|
*/
|
|
|
|
import { Injectable } from '@nestjs/common';
|
|
import { EventEmitter2, OnEvent } from '@nestjs/event-emitter';
|
|
|
|
import { Subscriber } from '@/chat/schemas/subscriber.schema';
|
|
import { config } from '@/config';
|
|
import { LoggerService } from '@/logger/logger.service';
|
|
import { BaseService } from '@/utils/generics/base-service';
|
|
|
|
import { BotStatsRepository } from '../repositories/bot-stats.repository';
|
|
import { BotStats, BotStatsType } from '../schemas/bot-stats.schema';
|
|
|
|
@Injectable()
|
|
export class BotStatsService extends BaseService<BotStats> {
|
|
constructor(
|
|
readonly repository: BotStatsRepository,
|
|
private readonly eventEmitter: EventEmitter2,
|
|
private readonly logger: LoggerService,
|
|
) {
|
|
super(repository);
|
|
}
|
|
|
|
/**
|
|
* Retrieves statistics for messages within a specified time range and of specified types.
|
|
*
|
|
* @param from - The start date for filtering messages.
|
|
* @param to - The end date for filtering messages.
|
|
* @param types - An array of message types (of type BotStatsType) to filter the statistics.
|
|
*
|
|
* @returns A promise that resolves to an array of `BotStats` objects representing the message statistics.
|
|
*/
|
|
async findMessages(
|
|
from: Date,
|
|
to: Date,
|
|
types: BotStatsType[],
|
|
): Promise<BotStats[]> {
|
|
return await this.repository.findMessages(from, to, types);
|
|
}
|
|
|
|
/**
|
|
* Retrieves the most popular blocks within a specified time range.
|
|
* Popular blocks are those triggered the most frequently.
|
|
*
|
|
* @param from - The start date of the time range.
|
|
* @param to - The end date of the time range.
|
|
* @returns A promise that resolves with an array of popular blocks, each containing an `id` and the number of times it was triggered (`value`).
|
|
*/
|
|
async findPopularBlocks(
|
|
from: Date,
|
|
to: Date,
|
|
): Promise<{ id: string; value: number }[]> {
|
|
return await this.repository.findPopularBlocks(from, to);
|
|
}
|
|
|
|
/**
|
|
* Handles the event to track user activity and emit statistics for loyalty, returning users, and retention.
|
|
*
|
|
* This method checks the last visit of the subscriber and emits relevant analytics events
|
|
* based on configured thresholds for loyalty, returning users, and retention.
|
|
*
|
|
* @param {Subscriber} subscriber - The subscriber object that contains last visit and retention data.
|
|
*/
|
|
@OnEvent('hook:user:lastvisit')
|
|
handleLastVisit(subscriber: Subscriber) {
|
|
const now = +new Date();
|
|
if (subscriber.lastvisit) {
|
|
// A loyal subscriber is a subscriber that comes back after some inactivity
|
|
if (now - +subscriber.lastvisit > config.analytics.thresholds.loyalty) {
|
|
this.eventEmitter.emit(
|
|
'hook:stats:entry',
|
|
BotStatsType.returning_users,
|
|
'Loyalty',
|
|
subscriber,
|
|
);
|
|
}
|
|
|
|
// Returning subscriber is a subscriber that comes back after some inactivity
|
|
if (now - +subscriber.lastvisit > config.analytics.thresholds.returning) {
|
|
this.eventEmitter.emit(
|
|
'hook:stats:entry',
|
|
BotStatsType.returning_users,
|
|
'Returning users',
|
|
subscriber,
|
|
);
|
|
}
|
|
}
|
|
// Retention
|
|
if (
|
|
subscriber.retainedFrom &&
|
|
now - +subscriber.retainedFrom > config.analytics.thresholds.retention
|
|
) {
|
|
this.eventEmitter.emit(
|
|
'hook:stats:entry',
|
|
'retention',
|
|
'Retentioned users',
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handles the event to update bot statistics.
|
|
*
|
|
* @param type - The type of bot statistics being tracked (e.g., user messages, bot responses).
|
|
* @param name - The name or identifier of the statistics entry (e.g., a specific feature or component being tracked).
|
|
*/
|
|
@OnEvent('hook:stats:entry')
|
|
async handleStatEntry(type: BotStatsType, name: string): Promise<void> {
|
|
const day = new Date();
|
|
day.setMilliseconds(0);
|
|
day.setSeconds(0);
|
|
day.setMinutes(0);
|
|
day.setHours(0);
|
|
|
|
try {
|
|
const insight = await this.findOneOrCreate(
|
|
{ day: { $lte: day, $gte: day }, type, name },
|
|
{ day, type, name, value: 0 },
|
|
);
|
|
|
|
try {
|
|
await this.updateOne(insight.id, { value: insight.value + 1 });
|
|
} catch (err) {
|
|
this.logger.error('Unable to update insight', err);
|
|
}
|
|
} catch (err) {
|
|
this.logger.error('Unable to find or create insight', err);
|
|
}
|
|
}
|
|
}
|