diff --git a/src/database/models/userArticleLink.js b/src/database/models/userArticleLink.js index 300cc126..e2f07323 100644 --- a/src/database/models/userArticleLink.js +++ b/src/database/models/userArticleLink.js @@ -33,10 +33,14 @@ class UserArticleLink extends Base { * * @param {string} userId * @param {string} articleId - * @param {object} data + * @param {object?} data * @returns {Promise} */ - static async createOrUpdateByUserIdAndArticleId(userId, articleId, data) { + static async createOrUpdateByUserIdAndArticleId( + userId, + articleId, + data = {} + ) { const setOnInsert = Object.assign({}, this.DEFAULT_DATA); for (let key in data) { diff --git a/src/webhook/handlers/askingArticleSubmissionConsent.js b/src/webhook/handlers/askingArticleSubmissionConsent.ts similarity index 70% rename from src/webhook/handlers/askingArticleSubmissionConsent.js rename to src/webhook/handlers/askingArticleSubmissionConsent.ts index 339d069f..06d8848a 100644 --- a/src/webhook/handlers/askingArticleSubmissionConsent.js +++ b/src/webhook/handlers/askingArticleSubmissionConsent.ts @@ -1,4 +1,7 @@ import { t } from 'ttag'; +import { Message } from '@line/bot-sdk'; + +import { ChatbotStateHandler } from 'src/types/chatbotState'; import ga from 'src/lib/ga'; import gql from 'src/lib/gql'; import { getArticleURL } from 'src/lib/sharedUtils'; @@ -15,9 +18,21 @@ import { } from './utils'; import UserSettings from 'src/database/models/userSettings'; import UserArticleLink from 'src/database/models/userArticleLink'; +import { + ArticleTypeEnum, + SubmitMediaArticleUnderConsentMutation, + SubmitMediaArticleUnderConsentMutationVariables, + SubmitTextArticleUnderConsentMutation, + SubmitTextArticleUnderConsentMutationVariables, +} from 'typegen/graphql'; + +function uppercase(s: T) { + return s.toUpperCase() as Uppercase; +} -export default async function askingArticleSubmissionConsent(params) { - let { data, state, event, userId, replies } = params; +const askingArticleSubmissionConsent: ChatbotStateHandler = async (params) => { + const { data, event, userId } = params; + let { state, replies } = params; const visitor = ga(userId, state, data.searchedText); @@ -41,22 +56,43 @@ export default async function askingArticleSubmissionConsent(params) { let article; if (isTextArticle) { const result = await gql` - mutation ($text: String!) { + mutation SubmitTextArticleUnderConsent($text: String!) { CreateArticle(text: $text, reference: { type: LINE }) { id } } - `({ text: data.searchedText }, { userId }); + `< + SubmitTextArticleUnderConsentMutation, + SubmitTextArticleUnderConsentMutationVariables + >({ text: data.searchedText ?? '' }, { userId }); article = result.data.CreateArticle; } else { + /* istanbul ignore if */ if (!data.messageId) { // Should not be here throw new Error('No message ID found, cannot submit message.'); } + const articleType: ArticleTypeEnum = (() => { + switch (data.messageType) { + case 'image': + case 'audio': + case 'video': + return uppercase(data.messageType); + default: + throw new Error( + `[askingArticleSubmissionConsent] unsupported message type ${data.messageType}` + ); + } + })(); + const proxyUrl = getLineContentProxyURL(data.messageId); + const result = await gql` - mutation ($mediaUrl: String!, $articleType: ArticleTypeEnum!) { + mutation SubmitMediaArticleUnderConsent( + $mediaUrl: String! + $articleType: ArticleTypeEnum! + ) { CreateMediaArticle( mediaUrl: $mediaUrl articleType: $articleType @@ -65,13 +101,20 @@ export default async function askingArticleSubmissionConsent(params) { id } } - `( - { mediaUrl: proxyUrl, articleType: data.messageType.toUpperCase() }, - { userId } - ); + `< + SubmitMediaArticleUnderConsentMutation, + SubmitMediaArticleUnderConsentMutationVariables + >({ mediaUrl: proxyUrl, articleType }, { userId }); article = result.data.CreateMediaArticle; } + /* istanbul ignore if */ + if (!article?.id) { + throw new Error( + '[askingARticleSubmissionConsent] article is not created successfully' + ); + } + await UserArticleLink.createOrUpdateByUserIdAndArticleId( userId, article.id @@ -86,7 +129,7 @@ export default async function askingArticleSubmissionConsent(params) { userId ); - let maybeAIReplies = [ + let maybeAIReplies: Message[] = [ createTextMessage({ text: t`In the meantime, you can:`, }), @@ -150,7 +193,7 @@ export default async function askingArticleSubmissionConsent(params) { !allowNewReplyUpdate && createNotificationSettingsBubble(), createArticleShareBubble(articleUrl), - ].filter((m) => m), + ].filter(Boolean), }, }, ]; @@ -160,4 +203,6 @@ export default async function askingArticleSubmissionConsent(params) { visitor.send(); return { data, event, userId, replies }; -} +}; + +export default askingArticleSubmissionConsent; diff --git a/typegen/gql.ts b/typegen/gql.ts index 5e428242..6461e361 100644 --- a/typegen/gql.ts +++ b/typegen/gql.ts @@ -13,6 +13,8 @@ import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/ * Therefore it is highly recommended to use the babel or swc plugin for production. */ const documents = { + "\n mutation SubmitTextArticleUnderConsent($text: String!) {\n CreateArticle(text: $text, reference: { type: LINE }) {\n id\n }\n }\n ": types.SubmitTextArticleUnderConsentDocument, + "\n mutation SubmitMediaArticleUnderConsent(\n $mediaUrl: String!\n $articleType: ArticleTypeEnum!\n ) {\n CreateMediaArticle(\n mediaUrl: $mediaUrl\n articleType: $articleType\n reference: { type: LINE }\n ) {\n id\n }\n }\n ": types.SubmitMediaArticleUnderConsentDocument, "\n query GetArticleInChoosingArticle($id: String!) {\n GetArticle(id: $id) {\n text\n replyCount\n articleType\n articleReplies(status: NORMAL) {\n reply {\n id\n type\n text\n }\n positiveFeedbackCount\n negativeFeedbackCount\n }\n }\n }\n ": types.GetArticleInChoosingArticleDocument, "\n mutation SubmitReplyRequestWithoutReason($id: String!) {\n CreateOrUpdateReplyRequest(articleId: $id) {\n replyRequestCount\n }\n }\n ": types.SubmitReplyRequestWithoutReasonDocument, "\n query GetReplyRelatedData($id: String!, $articleId: String!) {\n GetReply(id: $id) {\n type\n text\n reference\n createdAt\n }\n GetArticle(id: $articleId) {\n replyCount\n }\n }\n ": types.GetReplyRelatedDataDocument, @@ -36,6 +38,14 @@ const documents = { */ export function graphql(source: string): unknown; +/** + * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. + */ +export function graphql(source: "\n mutation SubmitTextArticleUnderConsent($text: String!) {\n CreateArticle(text: $text, reference: { type: LINE }) {\n id\n }\n }\n "): (typeof documents)["\n mutation SubmitTextArticleUnderConsent($text: String!) {\n CreateArticle(text: $text, reference: { type: LINE }) {\n id\n }\n }\n "]; +/** + * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. + */ +export function graphql(source: "\n mutation SubmitMediaArticleUnderConsent(\n $mediaUrl: String!\n $articleType: ArticleTypeEnum!\n ) {\n CreateMediaArticle(\n mediaUrl: $mediaUrl\n articleType: $articleType\n reference: { type: LINE }\n ) {\n id\n }\n }\n "): (typeof documents)["\n mutation SubmitMediaArticleUnderConsent(\n $mediaUrl: String!\n $articleType: ArticleTypeEnum!\n ) {\n CreateMediaArticle(\n mediaUrl: $mediaUrl\n articleType: $articleType\n reference: { type: LINE }\n ) {\n id\n }\n }\n "]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ diff --git a/typegen/graphql.ts b/typegen/graphql.ts index 50ebd421..15f67948 100644 --- a/typegen/graphql.ts +++ b/typegen/graphql.ts @@ -1379,6 +1379,21 @@ export type YdocVersion = { snapshot: Maybe; }; +export type SubmitTextArticleUnderConsentMutationVariables = Exact<{ + text: Scalars['String']; +}>; + + +export type SubmitTextArticleUnderConsentMutation = { CreateArticle: { id: string | null } | null }; + +export type SubmitMediaArticleUnderConsentMutationVariables = Exact<{ + mediaUrl: Scalars['String']; + articleType: ArticleTypeEnum; +}>; + + +export type SubmitMediaArticleUnderConsentMutation = { CreateMediaArticle: { id: string | null } | null }; + export type GetArticleInChoosingArticleQueryVariables = Exact<{ id: Scalars['String']; }>; @@ -1434,6 +1449,8 @@ export const CreateReferenceWordsReplyFragmentDoc = {"kind":"Document","definiti export const CreateReplyMessagesReplyFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CreateReplyMessagesReply"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Reply"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"text"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"CreateReferenceWordsReply"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CreateReferenceWordsReply"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Reply"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"reference"}},{"kind":"Field","name":{"kind":"Name","value":"type"}}]}}]} as unknown as DocumentNode; export const CreateReplyMessagesArticleFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CreateReplyMessagesArticle"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Article"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"replyCount"}}]}}]} as unknown as DocumentNode; export const CreateHighlightContentsHighlightFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CreateHighlightContentsHighlight"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Highlights"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"text"}},{"kind":"Field","name":{"kind":"Name","value":"hyperlinks"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"summary"}}]}}]}}]} as unknown as DocumentNode; +export const SubmitTextArticleUnderConsentDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"SubmitTextArticleUnderConsent"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"text"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"CreateArticle"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"text"},"value":{"kind":"Variable","name":{"kind":"Name","value":"text"}}},{"kind":"Argument","name":{"kind":"Name","value":"reference"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"type"},"value":{"kind":"EnumValue","value":"LINE"}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]} as unknown as DocumentNode; +export const SubmitMediaArticleUnderConsentDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"SubmitMediaArticleUnderConsent"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"mediaUrl"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"articleType"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ArticleTypeEnum"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"CreateMediaArticle"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"mediaUrl"},"value":{"kind":"Variable","name":{"kind":"Name","value":"mediaUrl"}}},{"kind":"Argument","name":{"kind":"Name","value":"articleType"},"value":{"kind":"Variable","name":{"kind":"Name","value":"articleType"}}},{"kind":"Argument","name":{"kind":"Name","value":"reference"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"type"},"value":{"kind":"EnumValue","value":"LINE"}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]} as unknown as DocumentNode; export const GetArticleInChoosingArticleDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetArticleInChoosingArticle"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"GetArticle"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"text"}},{"kind":"Field","name":{"kind":"Name","value":"replyCount"}},{"kind":"Field","name":{"kind":"Name","value":"articleType"}},{"kind":"Field","name":{"kind":"Name","value":"articleReplies"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"status"},"value":{"kind":"EnumValue","value":"NORMAL"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"reply"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"text"}}]}},{"kind":"Field","name":{"kind":"Name","value":"positiveFeedbackCount"}},{"kind":"Field","name":{"kind":"Name","value":"negativeFeedbackCount"}}]}}]}}]}}]} as unknown as DocumentNode; export const SubmitReplyRequestWithoutReasonDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"SubmitReplyRequestWithoutReason"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"CreateOrUpdateReplyRequest"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"articleId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"replyRequestCount"}}]}}]}}]} as unknown as DocumentNode; export const GetReplyRelatedDataDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetReplyRelatedData"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"articleId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"GetReply"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"text"}},{"kind":"Field","name":{"kind":"Name","value":"reference"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}},{"kind":"Field","name":{"kind":"Name","value":"GetArticle"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"articleId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"replyCount"}}]}}]}}]} as unknown as DocumentNode;