alert-manager.ts 5.67 KB
Newer Older
1
import { makeLogger, translate as $t, displayLabel } from '../helpers';
2

3
import { Account, Access, Alert, Transaction } from '../models';
4

5
import getNotifier from './notifications';
6
import getEmailer from './emailer';
7
8
import { I18NObject } from '../shared/helpers';
import { getTranslator } from './translator';
9

10
const log = makeLogger('alert-manager');
11

12
class AlertManager {
13
14
    private wrapContent(i18n: I18NObject, content: string): string {
        return `${$t(i18n, 'server.email.hello')}
15
16
17

${content}

18
19
${$t(i18n, 'server.email.seeyoulater.notifications')},
${$t(i18n, 'server.email.signature')}
20
21
22
`;
    }

23
24
    async send(
        userId: number,
25
        i18n: I18NObject,
26
27
        { subject, text }: { subject: string; text: string }
    ): Promise<void> {
Benjamin Bouvier's avatar
Benjamin Bouvier committed
28
29
        let sentNotifications = false;

30
31
32
        const notifier = getNotifier(userId);
        if (notifier !== null) {
            await notifier.send(subject, text);
Benjamin Bouvier's avatar
Benjamin Bouvier committed
33
            sentNotifications = true;
34
35
36
37
38
        }

        const emailer = getEmailer();
        if (emailer !== null) {
            // Send email notification
39
            const content = this.wrapContent(i18n, text);
40
            const fullSubject = `Kresus - ${subject}`;
41

42
43
44
45
            await emailer.sendToUser(userId, {
                subject: fullSubject,
                content,
            });
46

Benjamin Bouvier's avatar
Benjamin Bouvier committed
47
            sentNotifications = true;
48
        }
49

Benjamin Bouvier's avatar
Benjamin Bouvier committed
50
51
        if (sentNotifications) {
            log.info('Notifications have been sent.');
52
        } else {
Benjamin Bouvier's avatar
Benjamin Bouvier committed
53
            log.info('No notifier or email sender found, no notifications sent.');
54
        }
55
56
    }

57
    async checkAlertsForTransactions(
58
59
60
61
        userId: number,
        access: Access,
        operations: Transaction[]
    ): Promise<void> {
62
        try {
Benjamin Bouvier's avatar
Benjamin Bouvier committed
63
            if (getNotifier(userId) === null && getEmailer() === null) {
64
65
66
67
                log.info('No notifier or emailer found, skipping transactions alerts check.');
                return;
            }

68
69
            const i18n = await getTranslator(userId);

70
            // Map account to names
71
72
73
74
            const accessLabel = access.getLabel();
            const accounts = await Account.byAccess(userId, access);
            const accountsMap = new Map();
            for (const a of accounts) {
75
                accountsMap.set(a.id, {
76
                    label: `${accessLabel}${displayLabel(a)}`,
77
                    formatCurrency: await a.getCurrencyFormatter(),
78
                });
79
80
81
            }

            // Map accounts to alerts
82
            const alertsByAccount = new Map();
83

84
            for (const operation of operations) {
85
                // Memoize alerts by account
86
                let alerts;
87
                if (!alertsByAccount.has(operation.accountId)) {
Benjamin Bouvier's avatar
Benjamin Bouvier committed
88
                    alerts = await Alert.byAccountAndType(
89
90
91
92
                        userId,
                        operation.accountId,
                        'transaction'
                    );
93
                    alertsByAccount.set(operation.accountId, alerts);
94
                } else {
95
                    alerts = alertsByAccount.get(operation.accountId);
96
                }
97
98

                // Skip operations for which the account has no alerts
99
                if (!alerts || !alerts.length) {
100
                    continue;
101
                }
102

103
                // Set the account information
104
                const { label: accountName, formatCurrency } = accountsMap.get(operation.accountId);
105

106
                for (const alert of alerts) {
107
108
109
                    if (!alert.testTransaction(operation)) {
                        continue;
                    }
110

111
                    const text = alert.formatOperationMessage(
112
                        i18n,
113
114
115
116
                        operation,
                        accountName,
                        formatCurrency
                    );
117
118
                    await this.send(userId, i18n, {
                        subject: $t(i18n, 'server.alert.operation.title'),
119
                        text,
120
                    });
121
                }
122
            }
123
        } catch (err) {
124
            log.error(`Error when checking alerts for operations: ${err}`);
125
126
127
        }
    }

128
    async checkAlertsForAccounts(userId: number, access: Access): Promise<void> {
129
        try {
Benjamin Bouvier's avatar
Benjamin Bouvier committed
130
131
            if (getNotifier(userId) === null && getEmailer() === null) {
                log.info('No notifier or emailer found, skipping transactions alerts check.');
132
133
134
                return;
            }

135
136
            const i18n = await getTranslator(userId);

137
138
139
140
            const accounts = await Account.byAccess(userId, access);
            const accessLabel = access.getLabel();
            for (const account of accounts) {
                const alerts = await Alert.byAccountAndType(userId, account.id, 'balance');
141
142
143
                if (!alerts) {
                    continue;
                }
144

145
                const balance = account.balance as number;
146
                for (const alert of alerts) {
147
148
149
                    if (!alert.testBalance(balance)) {
                        continue;
                    }
150

151
                    // Set the currency formatter
152
153
                    const formatCurrency = await account.getCurrencyFormatter();
                    const text = alert.formatAccountMessage(
154
                        i18n,
155
                        `${accessLabel}${displayLabel(account)}`,
156
157
158
                        balance,
                        formatCurrency
                    );
159
160
                    await this.send(userId, i18n, {
                        subject: $t(i18n, 'server.alert.balance.title'),
161
                        text,
162
                    });
163
                }
164
            }
165
        } catch (err) {
166
167
            log.error(`Error when checking alerts for accounts: ${err}`);
        }
168
    }
169
}
170
171

export default new AlertManager();