[core] ChatWoot - timestamp for old messages

This commit is contained in:
devlikepro committed 2025-11-04 14:00:33 +07:00
1 parent 63890dc499
commit 9394509a07
25 files changed
+289 -38

No files matched your search

+2 -1
View File
@@ -4,6 +4,7 @@ import { BufferedOutput } from '@waha/apps/chatwoot/cli/utils/BufferedOutput';
import { CommandContext } from '@waha/apps/chatwoot/cli/types';
import { ChatWootCommandsConfig } from '@waha/apps/chatwoot/dto/config.dto';
import { BuildProgram } from '@waha/apps/chatwoot/cli/program.a';
import { CHATWOOT_COMMAND_PREFIX } from '@waha/apps/chatwoot/env';
export async function runText(
commands: ChatWootCommandsConfig,
@@ -31,4 +32,4 @@ export async function runText(
return output;
}
export const CommandPrefix = process.env.WAHA_CHATWOOT_COMMAND_PREFIX || 'wa/';
export const CommandPrefix = CHATWOOT_COMMAND_PREFIX;
@@ -467,6 +467,10 @@ class MessageAnyHistoryHandler extends MessageAnyHandler {
public force: boolean = false;
public shouldLogUnsupported = true;
protected get historyMessage() {
return true;
}
protected get shouldAddFromTag() {
return false;
}
@@ -481,22 +485,6 @@ class MessageAnyHistoryHandler extends MessageAnyHandler {
}
return await super.ShouldProcessMessage(payload);
}
protected finalizeContent(content: string, payload: WAMessage): string {
let ack: any = null;
if (payload.fromMe) {
ack = {
emoji: MessageAckEmoji(payload.ack),
name: this.l.r(payload.ackName || 'UNKNOWN'),
};
}
return this.l.r('whatsapp.history.message.wrapper', {
content: content,
payload: payload,
timestamp: this.l.FormatTimestamp(payload.timestamp, false),
ack: ack,
});
}
}
function period(options: MessagesPullOptions) {
+32 -1
View File
@@ -32,6 +32,8 @@ import { PinoLogger } from 'nestjs-pino';
import { TKey } from '@waha/apps/chatwoot/i18n/templates';
import { isJidBroadcast, isJidGroup, toCusFormat } from '@waha/core/utils/jids';
import { EngineHelper } from '@waha/apps/chatwoot/waha';
import { EnsureSeconds } from '@waha/utils/timehelper';
import { CHATWOOT_MESSAGE_CALENDAR_THRESHOLD_SECONDS } from '@waha/apps/chatwoot/env';
export function ListenEventsForChatWoot() {
return [
@@ -209,8 +211,37 @@ export abstract class MessageBaseHandler<Payload extends WAMessageBase> {
payload: Payload,
): Promise<ChatWootMessagePartial>;
protected get historyMessage() {
return false;
}
protected finalizeContent(content: string, payload: Payload): string {
return content;
if (!content) {
return content;
}
if (!payload.timestamp) {
return content;
}
if (!this.historyMessage) {
// Check if the message is recent enough to be considered current
const now = EnsureSeconds(Date.now() / 1000);
const messageAge = now - EnsureSeconds(payload.timestamp);
const recent = messageAge <= CHATWOOT_MESSAGE_CALENDAR_THRESHOLD_SECONDS;
if (recent) {
return content;
}
}
const date = this.l.ParseTimestamp(payload.timestamp);
if (!date) {
return content;
}
const timestamp = this.l.FormatHumanDate(date);
return this.l.r('whatsapp.history.message.wrapper', {
content: content,
payload: payload,
history: this.historyMessage,
timestamp: timestamp,
});
}
abstract getReplyToWhatsAppID(payload: Payload): string | undefined;
+10
View File
@@ -0,0 +1,10 @@
export const CHATWOOT_COMMAND_PREFIX =
process.env.WAHA_CHATWOOT_COMMAND_PREFIX || 'wa/';
export const CHATWOOT_MESSAGE_CALENDAR_THRESHOLD_SECONDS =
parseInt(process.env.WAHA_CHATWOOT_MESSAGE_CALENDAR_THRESHOLD_SECONDS) || 600;
export const APPS_CHATWOOT_LANGUAGES_FOLDER =
process.env.WAHA_APPS_CHATWOOT_LANGUAGES_FOLDER?.trim();
export const TZ = process.env.TZ;
+2 -1
View File
@@ -1,6 +1,7 @@
import * as path from 'path';
import { YamlLocaleLoader } from '@waha/apps/chatwoot/i18n/loader';
import { I18N } from '@waha/apps/chatwoot/i18n/i18n';
import { APPS_CHATWOOT_LANGUAGES_FOLDER } from '@waha/apps/chatwoot/env';
const i18n = new I18N();
const localesDir = path.join(__dirname, 'locales');
@@ -9,7 +10,7 @@ i18n.load(new YamlLocaleLoader(localesDir, 'yml').load());
i18n.load(new YamlLocaleLoader(localesDir, 'yaml').load());
// Resolve env var (supports '.' and '~'); fallback to default
const additional = process.env.WAHA_APPS_CHATWOOT_LANGUAGES_FOLDER?.trim();
const additional = APPS_CHATWOOT_LANGUAGES_FOLDER;
if (additional) {
const resolved = path.resolve(additional);
i18n.load(new YamlLocaleLoader(resolved, 'yml').load());
+86
View File
@@ -0,0 +1,86 @@
import { i18n } from '@waha/apps/chatwoot/i18n/index';
const enLocale = i18n.locale('en-US');
// @ts-ignore
enLocale.strings.datetime.timeZone = 'America/New_York';
const ptBrLocale = i18n.locale('pt-BR');
// @ts-ignore
ptBrLocale.strings.datetime.timeZone = 'America/Sao_Paulo';
describe('Locale', () => {
describe('FormatHumanDate', () => {
beforeEach(() => {
jest.useFakeTimers();
});
afterEach(() => {
jest.useRealTimers();
});
it('formats dates occurring today with the Today prefix', () => {
const now = new Date('2024-03-15T10:30:00Z');
jest.setSystemTime(now);
const result = enLocale.FormatHumanDate(new Date(now));
expect(result).toBe('Today, Mar 15 • 06:30 AM EDT');
});
it('formats dates occurring yesterday with the Yesterday prefix', () => {
const now = new Date('2024-03-15T10:30:00Z');
const yesterday = new Date('2024-03-14T10:30:00Z');
jest.setSystemTime(now);
const result = enLocale.FormatHumanDate(yesterday);
expect(result).toBe('Yesterday, Mar 14 • 06:30 AM EDT');
});
it('formats dates from the current year without the year', () => {
const now = new Date('2024-05-01T09:00:00Z');
const date = new Date('2024-02-10T14:45:00Z');
jest.setSystemTime(now);
const result = enLocale.FormatHumanDate(date);
expect(result).toBe('Sat, Feb 10 • 09:45 AM EST');
});
it('formats dates from previous years including the year', () => {
const now = new Date('2024-05-01T09:00:00Z');
const date = new Date('2023-07-10T12:15:00Z');
jest.setSystemTime(now);
const result = enLocale.FormatHumanDate(date);
expect(result).toBe('Mon, Jul 10, 2023 • 08:15 AM EDT');
});
});
describe('FormatHumanDate (pt-BR)', () => {
beforeEach(() => {
jest.useFakeTimers();
});
afterEach(() => {
jest.useRealTimers();
});
it('uses localized Today and Yesterday labels', () => {
const now = new Date('2024-03-15T10:30:00Z');
const yesterday = new Date('2024-03-14T10:30:00Z');
jest.setSystemTime(now);
const todayResult = ptBrLocale.FormatHumanDate(now);
const yesterdayResult = ptBrLocale.FormatHumanDate(yesterday);
expect(todayResult).toBe('Hoje, 15 de mar. • 07:30 BRT');
expect(yesterdayResult).toBe('Ontem, 14 de mar. • 07:30 BRT');
});
});
});
+45 -2
View File
@@ -4,9 +4,11 @@ import { TemplatePayloads, TKey } from '@waha/apps/chatwoot/i18n/templates';
import Long from 'long';
import { ensureNumber } from '@waha/core/engines/noweb/utils';
import { EnsureMilliseconds } from '@waha/utils/timehelper';
import { TZ } from '@waha/apps/chatwoot/env';
import { isToday, isYesterday, isSameYear } from '@waha/utils/datehelper';
export class Locale {
constructor(private readonly strings: Record<string, string>) {}
constructor(public readonly strings: Record<string, string>) {}
/**
* Return Base Locale
@@ -101,7 +103,7 @@ export class Locale {
}
const opts: any = this.strings['datetime'] || {};
// Copy timezone if any
options.timeZone = opts.timeZone || opts.timezone || process.env.TZ;
options.timeZone = opts.timeZone || opts.timezone || TZ;
if (!year && date.getFullYear() === new Date().getFullYear()) {
// Hide year if current year
options.year = undefined;
@@ -117,6 +119,47 @@ export class Locale {
const date = this.ParseTimestamp(timestamp);
return this.FormatDatetimeOpts(date, options, year);
}
FormatHumanDate(date: Date): string {
const options: any = lodash.clone(this.strings['datetime'] || {});
const now = new Date();
const today = isToday(date, now);
const yesterday = isYesterday(date, now);
const sameYear = isSameYear(date, now);
const dateOptions: Intl.DateTimeFormatOptions = {
...options,
hour: undefined,
minute: undefined,
second: undefined,
timeZoneName: undefined,
year: sameYear ? undefined : options.year,
weekday: today || yesterday ? undefined : options.weekday,
};
const timeOptions: Intl.DateTimeFormatOptions = {
...options,
year: undefined,
month: undefined,
day: undefined,
weekday: undefined,
};
const dateStr = date.toLocaleDateString(this.locale, dateOptions);
const timeStr = date.toLocaleTimeString(this.locale, timeOptions);
if (today) {
const todayLabel = this.r('Today');
return `${todayLabel}, ${dateStr} • ${timeStr}`;
}
if (yesterday) {
const yesterdayLabel = this.r('Yesterday');
return `${yesterdayLabel}, ${dateStr} • ${timeStr}`;
}
// weekday already included in dateStr
return `${dateStr} • ${timeStr}`;
}
}
export class Template<K extends TKey> {
+4 -1
View File
@@ -25,6 +25,9 @@ datetime:
# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
# timeZone: 'Asia/Dubai'
Today: 'اليوم'
Yesterday: 'أمس'
job.report.error: |-
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
@@ -521,7 +524,7 @@ whatsapp.to.chatwoot.message.facebook.ad: |-
{{{payload.body}}}
whatsapp.history.message.wrapper: |-
{{{content}}}
`🗓️ {{{timestamp}}}`
`{{#history}}📥{{/history}}🗓️ {{{timestamp}}}`
UNKNOWN: غير معروف
ERROR: خطأ
+4 -1
View File
@@ -25,6 +25,9 @@ datetime:
# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
# timeZone: 'Asia/Dhaka'
Today: 'আজ'
Yesterday: 'গতকাল'
job.report.error: |-
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
@@ -521,7 +524,7 @@ whatsapp.to.chatwoot.message.facebook.ad: |-
{{{payload.body}}}
whatsapp.history.message.wrapper: |-
{{{content}}}
`🗓️ {{{timestamp}}}`
`{{#history}}📥{{/history}}🗓️ {{{timestamp}}}`
UNKNOWN: অজানা
ERROR: ত্রুটি
+4 -1
View File
@@ -25,6 +25,9 @@ datetime:
# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
# timeZone: 'Europe/Berlin'
Today: 'Heute'
Yesterday: 'Gestern'
job.report.error: |-
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
@@ -524,7 +527,7 @@ whatsapp.to.chatwoot.message.facebook.ad: |-
{{{payload.body}}}
whatsapp.history.message.wrapper: |-
{{{content}}}
`🗓️ {{{timestamp}}}`
`{{#history}}📥{{/history}}🗓️ {{{timestamp}}}`
UNKNOWN: Unbekannt
ERROR: Fehler
+4 -1
View File
@@ -24,6 +24,9 @@ datetime:
# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
# timeZone: 'America/New_York'
Today: Today
Yesterday: Yesterday
job.report.error: |-
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
@@ -76,7 +79,7 @@ whatsapp.group.message: |-
whatsapp.history.message.wrapper: |-
{{{content}}}
`🗓️ {{{timestamp}}}`
`{{#history}}📥{{/history}}🗓️ {{{timestamp}}}`
# WhatsApp Messages Ack
UNKNOWN: |-
+4 -1
View File
@@ -25,6 +25,9 @@ datetime:
# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
# timeZone: 'Europe/Madrid'
Today: 'Hoy'
Yesterday: 'Ayer'
job.report.error: |-
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
@@ -521,7 +524,7 @@ whatsapp.to.chatwoot.message.facebook.ad: |-
{{{payload.body}}}
whatsapp.history.message.wrapper: |-
{{{content}}}
`🗓️ {{{timestamp}}}`
`{{#history}}📥{{/history}}🗓️ {{{timestamp}}}`
UNKNOWN: Desconocido
ERROR: Error
+4 -1
View File
@@ -25,6 +25,9 @@ datetime:
# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
# timeZone: 'Asia/Tehran'
Today: 'امروز'
Yesterday: 'دیروز'
job.report.error: |-
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
@@ -521,7 +524,7 @@ whatsapp.to.chatwoot.message.facebook.ad: |-
{{{payload.body}}}
whatsapp.history.message.wrapper: |-
{{{content}}}
`🗓️ {{{timestamp}}}`
`{{#history}}📥{{/history}}🗓️ {{{timestamp}}}`
UNKNOWN: ناشناخته
ERROR: خطا
+4 -1
View File
@@ -25,6 +25,9 @@ datetime:
# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
# timeZone: 'Europe/Paris'
Today: "Aujourd'hui"
Yesterday: 'Hier'
job.report.error: |-
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
@@ -521,7 +524,7 @@ whatsapp.to.chatwoot.message.facebook.ad: |-
{{{payload.body}}}
whatsapp.history.message.wrapper: |-
{{{content}}}
`🗓️ {{{timestamp}}}`
`{{#history}}📥{{/history}}🗓️ {{{timestamp}}}`
UNKNOWN: Inconnu
ERROR: Erreur
+4 -1
View File
@@ -25,6 +25,9 @@ datetime:
# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
# timeZone: 'Asia/Jerusalem'
Today: 'היום'
Yesterday: 'אתמול'
job.report.error: |-
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
@@ -521,7 +524,7 @@ whatsapp.to.chatwoot.message.facebook.ad: |-
{{{payload.body}}}
whatsapp.history.message.wrapper: |-
{{{content}}}
`🗓️ {{{timestamp}}}`
`{{#history}}📥{{/history}}🗓️ {{{timestamp}}}`
UNKNOWN: לא ידוע
ERROR: שגיאה
+4 -1
View File
@@ -25,6 +25,9 @@ datetime:
# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
# timeZone: 'Asia/Kolkata'
Today: 'आज'
Yesterday: 'कल'
job.report.error: |-
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
@@ -521,7 +524,7 @@ whatsapp.to.chatwoot.message.facebook.ad: |-
{{{payload.body}}}
whatsapp.history.message.wrapper: |-
{{{content}}}
`🗓️ {{{timestamp}}}`
`{{#history}}📥{{/history}}🗓️ {{{timestamp}}}`
UNKNOWN: अज्ञात
ERROR: त्रुटि
+4 -1
View File
@@ -25,6 +25,9 @@ datetime:
# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
# timeZone: 'Asia/Jakarta'
Today: 'Hari ini'
Yesterday: 'Kemarin'
job.report.error: |-
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
@@ -521,7 +524,7 @@ whatsapp.to.chatwoot.message.facebook.ad: |-
{{{payload.body}}}
whatsapp.history.message.wrapper: |-
{{{content}}}
`🗓️ {{{timestamp}}}`
`{{#history}}📥{{/history}}🗓️ {{{timestamp}}}`
UNKNOWN: Tidak diketahui
ERROR: Kesalahan
+4 -1
View File
@@ -25,6 +25,9 @@ datetime:
# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
# timeZone: 'Asia/Karachi'
Today: 'ਅੱਜ'
Yesterday: 'ਕੱਲ੍ਹ'
job.report.error: |-
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
@@ -521,7 +524,7 @@ whatsapp.to.chatwoot.message.facebook.ad: |-
{{{payload.body}}}
whatsapp.history.message.wrapper: |-
{{{content}}}
`🗓️ {{{timestamp}}}`
`{{#history}}📥{{/history}}🗓️ {{{timestamp}}}`
UNKNOWN: ਅਣਜਾਣ
ERROR: ਗਲਤੀ
+4 -1
View File
@@ -23,6 +23,9 @@ datetime:
# https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat#timezonename
timeZoneName: short
Today: Hoje
Yesterday: Ontem
job.report.error: |-
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
@@ -526,7 +529,7 @@ whatsapp.to.chatwoot.message.facebook.ad: |-
{{{payload.body}}}
whatsapp.history.message.wrapper: |-
{{{content}}}
`🗓️ {{{timestamp}}}`
`{{#history}}📥{{/history}}🗓️ {{{timestamp}}}`
UNKNOWN: Desconhecido
ERROR: Erro
+4 -1
View File
@@ -25,6 +25,9 @@ datetime:
# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
# timeZone: 'Europe/Moscow'
Today: 'Сегодня'
Yesterday: 'Вчера'
job.report.error: |-
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
@@ -521,7 +524,7 @@ whatsapp.to.chatwoot.message.facebook.ad: |-
{{{payload.body}}}
whatsapp.history.message.wrapper: |-
{{{content}}}
`🗓️ {{{timestamp}}}`
`{{#history}}📥{{/history}}🗓️ {{{timestamp}}}`
UNKNOWN: Неизвестно
ERROR: Ошибка
+4 -1
View File
@@ -25,6 +25,9 @@ datetime:
# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
# timeZone: 'Europe/Istanbul'
Today: 'Bugün'
Yesterday: 'Dün'
job.report.error: |-
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
@@ -521,7 +524,7 @@ whatsapp.to.chatwoot.message.facebook.ad: |-
{{{payload.body}}}
whatsapp.history.message.wrapper: |-
{{{content}}}
`🗓️ {{{timestamp}}}`
`{{#history}}📥{{/history}}🗓️ {{{timestamp}}}`
UNKNOWN: Bilinmiyor
ERROR: Hata
+4 -1
View File
@@ -25,6 +25,9 @@ datetime:
# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
# timeZone: 'Europe/Kyiv'
Today: 'Сьогодні'
Yesterday: 'Вчора'
job.report.error: |-
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
@@ -521,7 +524,7 @@ whatsapp.to.chatwoot.message.facebook.ad: |-
{{{payload.body}}}
whatsapp.history.message.wrapper: |-
{{{content}}}
`🗓️ {{{timestamp}}}`
`{{#history}}📥{{/history}}🗓️ {{{timestamp}}}`
UNKNOWN: Невідомо
ERROR: Помилка
+4 -1
View File
@@ -25,6 +25,9 @@ datetime:
# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
# timeZone: 'Asia/Karachi'
Today: 'آج'
Yesterday: 'کل'
job.report.error: |-
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
@@ -521,7 +524,7 @@ whatsapp.to.chatwoot.message.facebook.ad: |-
{{{payload.body}}}
whatsapp.history.message.wrapper: |-
{{{content}}}
`🗓️ {{{timestamp}}}`
`{{#history}}📥{{/history}}🗓️ {{{timestamp}}}`
UNKNOWN: نامعلوم
ERROR: خرابی
+4 -1
View File
@@ -25,6 +25,9 @@ datetime:
# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
# timeZone: 'Asia/Shanghai'
Today: '今天'
Yesterday: '昨天'
job.report.error: |-
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
@@ -521,7 +524,7 @@ whatsapp.to.chatwoot.message.facebook.ad: |-
{{{payload.body}}}
whatsapp.history.message.wrapper: |-
{{{content}}}
`🗓️ {{{timestamp}}}`
`{{#history}}📥{{/history}}🗓️ {{{timestamp}}}`
UNKNOWN: 未知
ERROR: 错误
+40
View File
@@ -0,0 +1,40 @@
export function isToday(
input: Date | null | undefined,
reference: Date = new Date(),
): boolean {
if (!input || !(input instanceof Date) || Number.isNaN(input.getTime())) {
return false;
}
return (
input.getFullYear() === reference.getFullYear() &&
input.getMonth() === reference.getMonth() &&
input.getDate() === reference.getDate()
);
}
export function isYesterday(
input: Date | null | undefined,
reference: Date = new Date(),
): boolean {
if (!input || !(input instanceof Date) || Number.isNaN(input.getTime())) {
return false;
}
const yesterday = new Date(reference);
yesterday.setHours(0, 0, 0, 0);
yesterday.setDate(yesterday.getDate() - 1);
const candidate = new Date(input);
candidate.setHours(0, 0, 0, 0);
return candidate.getTime() === yesterday.getTime();
}
export function isSameYear(
input: Date | null | undefined,
reference: Date = new Date(),
): boolean {
if (!input || !(input instanceof Date) || Number.isNaN(input.getTime())) {
return false;
}
return input.getFullYear() === reference.getFullYear();
}