Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[3] Move selectedArticleId from context to action payload #369

Merged
merged 2 commits into from
Nov 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions src/types/chatbotState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ export type ChatbotState =
export type Context = {
/** Used to differientiate different search sessions (searched text or media) */
sessionId: number;
/** User selected article in DB */
selectedArticleId?: string;
} & (
| {
/** Searched multi-media message that started this search session */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,6 @@ exports[`should select article and choose the only one reply for user 1`] = `
Object {
"data": Object {
"searchedText": "Just One Reply Just One Reply Just One Reply Just One Reply Just One Reply",
"selectedArticleId": "article-id",
"sessionId": 0,
},
"replies": Array [
Expand Down Expand Up @@ -328,7 +327,6 @@ exports[`should select article and have OPINIONATED and NOT_ARTICLE replies 1`]
Object {
"data": Object {
"searchedText": "老榮民九成存款全部捐給慈濟,如今窮了卻得不到慈濟醫院社工的幫忙,竟翻臉不認人",
"selectedArticleId": "article-id",
"sessionId": 1497994017447,
},
"replies": Array [
Expand Down Expand Up @@ -675,7 +673,6 @@ exports[`should select article and slice replies when over 10 1`] = `
Object {
"data": Object {
"searchedText": "老榮民九成存款全部捐給慈濟,如今窮了卻得不到慈濟醫院社工的幫忙,竟翻臉不認人",
"selectedArticleId": "article-id",
"sessionId": 0,
},
"replies": Array [
Expand Down Expand Up @@ -1500,7 +1497,6 @@ Object {
因為最近已經發現- 好多病人因為吃了生魚片,胃壁附著《海獸胃腺蟲》,大小隻不一定,有的病人甚至胃壁上滿滿都是無法夾出來,驅蟲藥也很難根治,罹患機率每個國家的人都一樣。
尤其;鮭魚的含蟲量最高、最可怕!
請傳給朋友,讓他們有所警惕!",
"selectedArticleId": "article-id",
"sessionId": 0,
},
"replies": Array [
Expand Down Expand Up @@ -1689,7 +1685,6 @@ exports[`should select article with no replies: has AI reply 1`] = `
Object {
"data": Object {
"searchedText": "老司機車裡總備一塊香皂,知道內情的新手默默也準備了一塊",
"selectedArticleId": "article-id",
"sessionId": 0,
},
"replies": Array [
Expand Down Expand Up @@ -1869,7 +1864,6 @@ exports[`should select article with no replies: has no AI reply 1`] = `
Object {
"data": Object {
"searchedText": "老司機車裡總備一塊香皂,知道內情的新手默默也準備了一塊",
"selectedArticleId": "article-id",
"sessionId": 0,
},
"replies": Array [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,6 @@ exports[`should select reply by replyId should handle the case with just one rep
Object {
"data": Object {
"searchedText": "貼圖",
"selectedArticleId": "AWDZYXxAyCdS-nWhumlz",
"sessionId": 0,
},
"replies": Array [
Expand Down Expand Up @@ -373,7 +372,6 @@ exports[`should select reply by replyId should handle the case with multiple rep
Object {
"data": Object {
"searchedText": "貼圖",
"selectedArticleId": "AWDZYXxAyCdS-nWhumlz",
"sessionId": 0,
},
"replies": Array [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -463,7 +463,6 @@ exports[`only one article found with high similarity and choose for user 1`] = `
Object {
"data": Object {
"searchedText": "YouTube · 寻找健康人生",
"selectedArticleId": "AVvY-yizyCdS-nWhuYWx",
"sessionId": 1497994017447,
},
"replies": Array [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,6 @@ Object {
"messageId": "6270464463537",
"messageType": "image",
"searchedText": "",
"selectedArticleId": "image-article-1",
"sessionId": 1577836800000,
},
},
Expand Down
20 changes: 13 additions & 7 deletions src/webhook/handlers/__tests__/choosingReply.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ jest.mock('src/lib/gql');
jest.mock('src/lib/ga');

import MockDate from 'mockdate';
import choosingReply from '../choosingReply';
import choosingReply, { Input } from '../choosingReply';
import * as apiResult from '../__fixtures__/choosingReply';
import UserSettings from 'src/database/models/userSettings';
import originalGql from 'src/lib/gql';
Expand All @@ -25,15 +25,19 @@ afterEach(() => {
});

describe('should select reply by replyId', () => {
const input: Input = {
a: 'AWDZYXxAyCdS-nWhumlz',
r: 'AWDZeeV0yCdS-nWhuml8',
};

const params: ChatbotPostbackHandlerParams = {
data: {
sessionId: 0,
searchedText: '貼圖',
selectedArticleId: 'AWDZYXxAyCdS-nWhumlz',
},
postbackData: {
sessionId: 0,
input: 'AWDZeeV0yCdS-nWhuml8',
input,
state: 'CHOOSING_REPLY',
},
userId: 'Uaddc74df8a3a176b901d9d648b0fc4fe',
Expand Down Expand Up @@ -100,12 +104,11 @@ it('should block invalid postback input', async () => {
data: {
sessionId: 0,
searchedText: '貼圖',
selectedArticleId: 'AWDZYXxAyCdS-nWhumlz',
},
postbackData: {
sessionId: 0,
state: 'CHOOSING_REPLY',
input: undefined,
input: 'Some wrong string',
},
userId: 'Uaddc74df8a3a176b901d9d648b0fc4fe',
};
Expand All @@ -117,16 +120,19 @@ it('should block invalid postback input', async () => {

it('should handle graphql error gracefully', async () => {
gql.__push({ errors: [] });
const input: Input = {
a: 'AWDZYXxAyCdS-nWhumlz',
r: 'AWDZeeV0yCdS-nWhuml8',
};

const params: ChatbotPostbackHandlerParams = {
data: {
sessionId: 0,
searchedText: '貼圖',
selectedArticleId: 'AWDZYXxAyCdS-nWhumlz',
},
postbackData: {
sessionId: 0,
input: 'AWDZeeV0yCdS-nWhuml8',
input,
state: 'CHOOSING_REPLY',
},
userId: 'Uaddc74df8a3a176b901d9d648b0fc4fe',
Expand Down
10 changes: 8 additions & 2 deletions src/webhook/handlers/choosingArticle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {

import UserArticleLink from '../../database/models/userArticleLink';
import choosingReply from './choosingReply';
import type { Input as ChoosingReplyInput } from './choosingReply';
import { ChatbotPostbackHandler } from 'src/types/chatbotState';
import { FlexBubble, Message } from '@line/bot-sdk';

Expand Down Expand Up @@ -116,7 +117,7 @@ const choosingArticle: ChatbotPostbackHandler = async (params) => {
};
}

const selectedArticleId = (data.selectedArticleId = input);
const selectedArticleId = input;

await UserArticleLink.createOrUpdateByUserIdAndArticleId(
userId,
Expand Down Expand Up @@ -171,13 +172,18 @@ const choosingArticle: ChatbotPostbackHandler = async (params) => {
if (articleReplies.length === 1) {
visitor.send();

const input: ChoosingReplyInput = {
a: selectedArticleId,
r: articleReplies[0].reply?.id ?? '',
};

// choose reply for user
return await choosingReply({
data,
postbackData: {
sessionId,
state: 'CHOOSING_REPLY',
input: articleReplies[0].reply?.id ?? '',
input,
},
userId,
});
Expand Down
30 changes: 20 additions & 10 deletions src/webhook/handlers/choosingReply.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { t } from 'ttag';
import { z } from 'zod';
import gql from 'src/lib/gql';
import {
ManipulationError,
Expand All @@ -17,6 +18,14 @@ import {
ReplyTypeEnum,
} from 'typegen/graphql';

const inputSchema = z.object({
a: z.string().describe('Article ID'),
r: z.string().describe('Reply ID'),
});

/** Postback input type for CHOOSING_REPLY state handler */
export type Input = z.infer<typeof inputSchema>;

/**
* @param {string} articleId - Article ID of the article-reply to feedback
* @param {string} replyId - Reply ID of the article-reply to feedback
Expand Down Expand Up @@ -133,13 +142,17 @@ function createShareBubble(
const choosingReply: ChatbotPostbackHandler = async ({
data,
userId,
postbackData: { input, state },
postbackData: { input: postbackInput, state },
}) => {
if (typeof input !== 'string') {
let input: Input;
try {
input = inputSchema.parse(postbackInput);
} catch (e) {
console.error('[choosingReply]', e);
throw new ManipulationError(t`Please choose from provided options.`);
}

const selectedReplyId = input;
const { a: selectedArticleId, r: selectedReplyId } = input;

const { data: getReplyData, errors } = await gql`
query GetReplyRelatedData($id: String!, $articleId: String!) {
Expand All @@ -156,7 +169,7 @@ const choosingReply: ChatbotPostbackHandler = async ({
}
`<GetReplyRelatedDataQuery, GetReplyRelatedDataQueryVariables>({
id: selectedReplyId,
articleId: data.selectedArticleId ?? '',
articleId: selectedArticleId,
});

/* istanbul ignore if */
Expand All @@ -177,25 +190,22 @@ const choosingReply: ChatbotPostbackHandler = async ({
);

const replies: Message[] = [
...createReplyMessages(GetReply, GetArticle, data.selectedArticleId ?? ''),
...createReplyMessages(GetReply, GetArticle, selectedArticleId),
{
type: 'flex',
altText: t`Is the reply helpful?`,
contents: {
type: 'carousel',
contents: [
createAskReplyFeedbackBubble(
data.selectedArticleId ?? '',
selectedReplyId
),
createAskReplyFeedbackBubble(selectedArticleId, selectedReplyId),

// Ask user to turn on notification if the user did not turn it on
process.env.NOTIFY_METHOD &&
!allowNewReplyUpdate &&
createNotificationSettingsBubble(),

createShareBubble(
data.selectedArticleId ?? '',
selectedArticleId,
getReplyData.GetArticle.text ?? '',
GetReply.type
),
Expand Down
Loading