zappyzep/backend/src/plugins/Stats.ts
2019-12-01 15:57:35 +02:00

155 lines
4.6 KiB
TypeScript

import { ZeppelinPlugin } from "./ZeppelinPlugin";
import * as t from "io-ts";
import { convertDelayStringToMS, DAYS, HOURS, tAlphanumeric, tDateTime, tDeepPartial, tDelayString } from "../utils";
import { IPluginOptions } from "knub";
import moment from "moment-timezone";
import { GuildStats } from "../data/GuildStats";
import { Message } from "eris";
import escapeStringRegexp from "escape-string-regexp";
import { SavedMessage } from "../data/entities/SavedMessage";
import { GuildSavedMessages } from "../data/GuildSavedMessages";
const tBaseSource = t.type({
name: tAlphanumeric,
track: t.boolean,
retention_period: tDelayString,
});
const tMemberMessagesSource = t.intersection([
tBaseSource,
t.type({
type: t.literal("member_messages"),
}),
]);
type TMemberMessagesSource = t.TypeOf<typeof tMemberMessagesSource>;
const tChannelMessagesSource = t.intersection([
tBaseSource,
t.type({
type: t.literal("channel_messages"),
}),
]);
type TChannelMessagesSource = t.TypeOf<typeof tChannelMessagesSource>;
const tKeywordsSource = t.intersection([
tBaseSource,
t.type({
type: t.literal("keywords"),
keywords: t.array(t.string),
}),
]);
type TKeywordsSource = t.TypeOf<typeof tKeywordsSource>;
const tSource = t.union([tMemberMessagesSource, tChannelMessagesSource, tKeywordsSource]);
type TSource = t.TypeOf<typeof tSource>;
const tConfigSchema = t.type({
sources: t.record(tAlphanumeric, tSource),
});
type TConfigSchema = t.TypeOf<typeof tConfigSchema>;
const tPartialConfigSchema = tDeepPartial(tConfigSchema);
const DEFAULT_RETENTION_PERIOD = "4w";
export class StatsPlugin extends ZeppelinPlugin<TConfigSchema> {
public static pluginName = "stats";
public static configSchema = tConfigSchema;
public static showInDocs = false;
protected stats: GuildStats;
protected savedMessages: GuildSavedMessages;
private onMessageCreateFn;
private cleanStatsInterval;
public static getStaticDefaultOptions(): IPluginOptions<TConfigSchema> {
return {
config: {
sources: {},
},
};
}
protected static preprocessStaticConfig(config: t.TypeOf<typeof tPartialConfigSchema>) {
// TODO: Limit min period, min period start date
if (config.sources) {
for (const [key, source] of Object.entries(config.sources)) {
source.name = key;
if (source.track == null) {
source.track = true;
}
if (source.retention_period == null) {
source.retention_period = DEFAULT_RETENTION_PERIOD;
}
}
}
return config;
}
protected onLoad() {
this.stats = GuildStats.getGuildInstance(this.guildId);
this.savedMessages = GuildSavedMessages.getGuildInstance(this.guildId);
this.onMessageCreateFn = this.savedMessages.events.on("create", msg => this.onMessageCreate(msg));
this.cleanOldStats();
this.cleanStatsInterval = setInterval(() => this.cleanOldStats(), 1 * DAYS);
}
protected onUnload() {
this.savedMessages.events.off("create", this.onMessageCreateFn);
clearInterval(this.cleanStatsInterval);
}
protected async cleanOldStats() {
const config = this.getConfig();
for (const source of Object.values(config.sources)) {
const cutoffMS = convertDelayStringToMS(source.retention_period);
const cutoff = moment()
.subtract(cutoffMS, "ms")
.format("YYYY-MM-DD HH:mm:ss");
await this.stats.deleteOldValues(source.name, cutoff);
}
}
protected saveMemberMessagesStats(source: TMemberMessagesSource, msg: SavedMessage) {
this.stats.saveValue(source.name, msg.user_id, 1);
}
protected saveChannelMessagesStats(source: TChannelMessagesSource, msg: SavedMessage) {
this.stats.saveValue(source.name, msg.channel_id, 1);
}
protected saveKeywordsStats(source: TKeywordsSource, msg: SavedMessage) {
const content = msg.data.content;
if (!content) return;
for (const keyword of source.keywords) {
const regex = new RegExp(`\\b${escapeStringRegexp(keyword)}\\b`, "i");
if (content.match(regex)) {
this.stats.saveValue(source.name, "keyword", 1);
break;
}
}
}
onMessageCreate(msg: SavedMessage) {
const config = this.getConfigForMemberIdAndChannelId(msg.user_id, msg.channel_id);
for (const source of Object.values(config.sources)) {
if (!source.track) continue;
if (source.type === "member_messages") {
this.saveMemberMessagesStats(source, msg);
} else if (source.type === "channel_messages") {
this.saveChannelMessagesStats(source, msg);
} else if (source.type === "keywords") {
this.saveKeywordsStats(source, msg);
}
}
}
}