diff --git a/__tests__/triggers/greeting-and-acquaintance.js b/__tests__/triggers/greeting-and-acquaintance.js new file mode 100644 index 0000000..c28b0b5 --- /dev/null +++ b/__tests__/triggers/greeting-and-acquaintance.js @@ -0,0 +1,65 @@ +const { trigger: greetingAndAcquaintanceTrigger, combinedResponses, containsGreetingAndAcquaintance } = require('../../triggers/greeting-and-acquaintance'); +const { enqueueMessage } = require('../../outgoing-messages'); +jest.mock('../../outgoing-messages'); + +const triggerDescription = 'greeting and acquaintance trigger'; + +describe(triggerDescription, () => { + beforeEach(() => { + enqueueMessage.mockClear(); + }); + + test.each([ + ['Привет! Мы знакомы?'], + ['Привет. Мы знакомы?'], + ['Привет, мы знакомы с тобой?'], + ['Приветик! Мы знакомы?'], + ['Здравствуйте! Мы знакомы?'], + ['🖐 Мы знакомы?'], + ['👋 Мы знакомы с тобой?'], + ['Мы знакомы? Привет!'], + ['Знакомы мы? Привет'], + ['Привет, знакомы мы?'], + ['Салют! Мы знакомы?'], + ['Хай! Мы знакомы с тобой?'], + ])(`"%s" message matches ${triggerDescription} and gives expected response`, (incomingMessage) => { + const context = { request: { isFromUser: true, isOutbox: false, text: incomingMessage, attachments: [] } }; + expect(greetingAndAcquaintanceTrigger.condition(context)).toBe(true); + if (greetingAndAcquaintanceTrigger.condition(context)) { + greetingAndAcquaintanceTrigger.action(context); + } + expect(enqueueMessage).toHaveBeenCalled(); + const callArg = enqueueMessage.mock.calls[0][0]; + expect(callArg).toEqual(expect.objectContaining(context)); + expect(combinedResponses).toContain(callArg.response.message); + }); + + test.each([ + ['Привет'], + ['Мы знакомы?'], + ['Как дела?'], + ['Здравствуйте, как у вас дела?'], + ])(`"%s" message does not match ${triggerDescription} (missing greeting or acquaintance)`, (incomingMessage) => { + const context = { request: { isFromUser: true, isOutbox: false, text: incomingMessage, attachments: [] } }; + expect(greetingAndAcquaintanceTrigger.condition(context)).toBe(false); + }); + + test('greeting sticker + acquaintance text matches trigger', () => { + const context = { + request: { + isFromUser: true, + isOutbox: false, + text: 'Мы знакомы?', + attachments: [{ id: 72789 }] // greeting sticker + } + }; + expect(greetingAndAcquaintanceTrigger.condition(context)).toBe(true); + }); + + test('containsGreetingAndAcquaintance function works correctly', () => { + expect(containsGreetingAndAcquaintance('Привет! Мы знакомы?', [])).toBe(true); + expect(containsGreetingAndAcquaintance('Привет', [])).toBe(false); + expect(containsGreetingAndAcquaintance('Мы знакомы?', [])).toBe(false); + expect(containsGreetingAndAcquaintance('Мы знакомы?', [{ id: 72789 }])).toBe(true); + }); +}); diff --git a/triggers/acquaintance.js b/triggers/acquaintance.js index e72483e..ca2bd82 100644 --- a/triggers/acquaintance.js +++ b/triggers/acquaintance.js @@ -50,5 +50,6 @@ const trigger = { module.exports = { trigger, - acquaintanceSuggestions + acquaintanceSuggestions, + acquaintedRegex }; \ No newline at end of file diff --git a/triggers/greeting-and-acquaintance.js b/triggers/greeting-and-acquaintance.js new file mode 100644 index 0000000..1d34f9a --- /dev/null +++ b/triggers/greeting-and-acquaintance.js @@ -0,0 +1,63 @@ +const { getRandomElement, hasSticker } = require('../utils'); +const { enqueueMessage } = require('../outgoing-messages'); +const { DateTime } = require('luxon'); +const { incomingGreetingStickersIds } = require('./greeting'); + +// Combined responses for greeting + acquaintance question +const combinedResponses = [ + "Привет! Ещё нет, но давай исправим. Я программист. А ты? (можно на ты?)", + "Привет! Мы не знакомы, но можем познакомиться. Я программист, а ты? (можем на ты?)", + "Привет! Нет, ещё не знакомы. Я программист :)", + "Привет! Нет, мы не знакомы. Я программист, а ты? (не против, что на ты?)", + "Привет! Ещё нет, я программист, предлагаю дружбу :)", + "Привет! Ещё нет, я программист, а ты? (можем перейти на ты?)", + "Привет! Не встречались ранее. Давай познакомимся. Я программист, а ты? (переходим на ты?)", + "Привет! Пока не знакомы, но можно это исправить. Я программист. А ты? (можно на ты?)", + "Привет! Ещё нет, но я всегда рад новым знакомствам. Я программист, а ты? (продолжим на ты?)", + "Привет! Пока что нет. Давай познакомимся? Я программист, а ты чем занимаешься? (мы на ты?)", + "Привет! Пока еще не знакомы. Исправим это? Я программист. А ты? (можно на ты?)", + "Привет! Мы еще не знакомы. Давай это исправим? Я программист. А ты? (перейдем на ты?)", +]; + +// Partial regex patterns to match within a message (not requiring full message match) +const greetingPartialRegex = /(трям|🖖|👋|🖐|мо[иё] приветстви[ея]|салам|салют|з?д[ао]ров[ао]?|ку|q+|шалом|хай|хэллоу|йоу?|привет(ствую|ики?)?|здрав?с(твуй|ь)?(те)?|дд|((день|вечер)[^\p{L}]+)?добр(ый([^\p{L}]*(день|вечер))?|ое[^\p{L}]*утро|ой[^\p{L}]*ночи|ого[^\p{L}]*времени[^\p{L}]*суток))/ui; +// Match acquaintance question - look for "знакомы" followed by "?" anywhere in the text +const acquaintancePartialRegex = /знакомы[^\?]*\?/ui; + +// Check if message contains both greeting and acquaintance question +function containsGreetingAndAcquaintance(text, attachments) { + const hasGreeting = greetingPartialRegex.test(text) || hasSticker({ attachments }, incomingGreetingStickersIds); + const hasAcquaintance = acquaintancePartialRegex.test(text); + return hasGreeting && hasAcquaintance; +} + +const trigger = { + name: "GreetingAndAcquaintanceTrigger", + condition: (context) => { + if (!context?.request?.isFromUser) { + return false; + } + + const now = DateTime.now(); + const lastTriggered = context?.state?.triggers?.[trigger.name]?.lastTriggered; + const lastTriggeredDiff = lastTriggered ? now.diff(lastTriggered, 'days').days : Number.MAX_SAFE_INTEGER; + + return lastTriggeredDiff >= 1 + && !context?.request?.isOutbox + && containsGreetingAndAcquaintance(context.request.text || '', context.request.attachments); + }, + action: (context) => { + enqueueMessage({ + ...context, + response: { + message: getRandomElement(combinedResponses) + } + }); + } +}; + +module.exports = { + trigger, + combinedResponses, + containsGreetingAndAcquaintance +};