diff --git a/src/webhook/handlers/__fixtures__/choosingArticle.js b/src/webhook/handlers/__fixtures__/choosingArticle.js index 22abdbb5..af0adac1 100644 --- a/src/webhook/handlers/__fixtures__/choosingArticle.js +++ b/src/webhook/handlers/__fixtures__/choosingArticle.js @@ -208,6 +208,7 @@ export const noReplies = { data: { GetArticle: { text: '老司機車裡總備一塊香皂,知道內情的新手默默也準備了一塊', + articleType: 'TEXT', replyCount: 0, articleReplies: [], }, diff --git a/src/webhook/handlers/__tests__/__snapshots__/askingArticleSubmissionConsent.test.js.snap b/src/webhook/handlers/__tests__/__snapshots__/askingArticleSubmissionConsent.test.js.snap index 3b5dac6e..d4d53f61 100644 --- a/src/webhook/handlers/__tests__/__snapshots__/askingArticleSubmissionConsent.test.js.snap +++ b/src/webhook/handlers/__tests__/__snapshots__/askingArticleSubmissionConsent.test.js.snap @@ -185,12 +185,36 @@ Object { "type": "flex", }, Object { - "altText": "In the meantime, you can:", + "altText": "這篇文章尚待查核中,請先不要相信這篇文章。 +以下是機器人初步分析此篇訊息的結果,希望能帶給你一些想法。", "contents": Object { "body": Object { "contents": Array [ Object { - "text": "In the meantime, you can:", + "text": "這篇文章尚待查核中,請先不要相信這篇文章。 +以下是機器人初步分析此篇訊息的結果,希望能帶給你一些想法。", + "type": "text", + "wrap": true, + }, + ], + "layout": "vertical", + "type": "box", + }, + "type": "bubble", + }, + "type": "flex", + }, + Object { + "text": "Hello from ChatGPT", + "type": "text", + }, + Object { + "altText": "讀完以上機器人的自動分析後,您可以:", + "contents": Object { + "body": Object { + "contents": Array [ + Object { + "text": "讀完以上機器人的自動分析後,您可以:", "type": "text", "wrap": true, }, diff --git a/src/webhook/handlers/__tests__/__snapshots__/choosingArticle.test.js.snap b/src/webhook/handlers/__tests__/__snapshots__/choosingArticle.test.js.snap index 02e599d7..c9b9d23e 100644 --- a/src/webhook/handlers/__tests__/__snapshots__/choosingArticle.test.js.snap +++ b/src/webhook/handlers/__tests__/__snapshots__/choosingArticle.test.js.snap @@ -1763,8 +1763,7 @@ Don’t trust the message just yet!", "body": Object { "contents": Array [ Object { - "text": "This message has already published on Cofacts, and will soon be fact-checked by volunteers. -Don’t trust the message just yet!", + "text": "此訊息已經被收錄至 Cofacts 有待好心人來查證。", "type": "text", "wrap": true, }, @@ -1786,12 +1785,36 @@ Don’t trust the message just yet!", "type": "flex", }, Object { - "altText": "In the meantime, you can:", + "altText": "這篇文章尚待查核,請先不要相信這篇文章。 +以下是機器人初步分析此篇訊息的結果,希望能帶給你一些想法。", + "contents": Object { + "body": Object { + "contents": Array [ + Object { + "text": "這篇文章尚待查核,請先不要相信這篇文章。 +以下是機器人初步分析此篇訊息的結果,希望能帶給你一些想法。", + "type": "text", + "wrap": true, + }, + ], + "layout": "vertical", + "type": "box", + }, + "type": "bubble", + }, + "type": "flex", + }, + Object { + "text": "Hello from ChatGPT", + "type": "text", + }, + Object { + "altText": "讀完以上機器人的自動分析後,您可以:", "contents": Object { "body": Object { "contents": Array [ Object { - "text": "In the meantime, you can:", + "text": "讀完以上機器人的自動分析後,您可以:", "type": "text", "wrap": true, }, diff --git a/src/webhook/handlers/__tests__/askingArticleSubmissionConsent.test.js b/src/webhook/handlers/__tests__/askingArticleSubmissionConsent.test.js index 4c3ee3a0..3d3db7e2 100644 --- a/src/webhook/handlers/__tests__/askingArticleSubmissionConsent.test.js +++ b/src/webhook/handlers/__tests__/askingArticleSubmissionConsent.test.js @@ -93,6 +93,7 @@ it('should submit article if user agrees to submit', async () => { MockDate.set('2020-01-02'); gql.__push({ data: { CreateArticle: { id: 'new-article-id' } } }); + gql.__push({ data: { CreateAIReply: { text: 'Hello from ChatGPT' } } }); const result = await askingArticleSubmissionConsent(params); MockDate.reset(); expect(gql.__finished()).toBe(true); @@ -168,6 +169,7 @@ it('should create a UserArticleLink when creating a Article', async () => { MockDate.set('2020-01-01'); gql.__push({ data: { CreateArticle: { id: 'new-article-id' } } }); + gql.__push({ data: { CreateAIReply: { text: 'Hello from ChatGPT' } } }); await askingArticleSubmissionConsent(params); MockDate.reset(); @@ -190,6 +192,7 @@ it('should ask user to turn on notification settings if they did not turn it on }; gql.__push({ data: { CreateArticle: { id: 'new-article-id' } } }); + gql.__push({ data: { CreateAIReply: { text: 'Hello from ChatGPT' } } }); process.env.NOTIFY_METHOD = 'LINE_NOTIFY'; await UserSettings.setAllowNewReplyUpdate(userId, false); @@ -197,7 +200,7 @@ it('should ask user to turn on notification settings if they did not turn it on const results = await askingArticleSubmissionConsent(params); MockDate.reset(); - expect(results.replies[2].contents.contents).toMatchSnapshot(); + expect(results.replies[4].contents.contents).toMatchSnapshot(); delete process.env.NOTIFY_METHOD; await UserSettings.setAllowNewReplyUpdate(userId, true); diff --git a/src/webhook/handlers/__tests__/choosingArticle.test.js b/src/webhook/handlers/__tests__/choosingArticle.test.js index 58a1d4f5..f3474f3d 100644 --- a/src/webhook/handlers/__tests__/choosingArticle.test.js +++ b/src/webhook/handlers/__tests__/choosingArticle.test.js @@ -164,6 +164,7 @@ it('should select article and have OPINIONATED and NOT_ARTICLE replies', async ( it('should select article with no replies', async () => { gql.__push(apiGetArticleResult.noReplies); + gql.__push({ data: { CreateAIReply: { text: 'Hello from ChatGPT' } } }); gql.__push(apiGetArticleResult.createOrUpdateReplyRequest); const params = { diff --git a/src/webhook/handlers/askingArticleSubmissionConsent.js b/src/webhook/handlers/askingArticleSubmissionConsent.js index 5658e57e..353aa938 100644 --- a/src/webhook/handlers/askingArticleSubmissionConsent.js +++ b/src/webhook/handlers/askingArticleSubmissionConsent.js @@ -11,6 +11,7 @@ import { createArticleShareBubble, createNotificationSettingsBubble, getLineContentProxyURL, + createAIReply, } from './utils'; import UserSettings from 'src/database/models/userSettings'; import UserArticleLink from 'src/database/models/userArticleLink'; @@ -36,8 +37,9 @@ export default async function askingArticleSubmissionConsent(params) { case POSTBACK_YES: { visitor.event({ ec: 'Article', ea: 'Create', el: 'Yes' }); + const isTextArticle = data.searchedText && !data.messageId; let article; - if (data.searchedText && !data.messageId) { + if (isTextArticle) { const result = await gql` mutation ($text: String!) { CreateArticle(text: $text, reference: { type: LINE }) { @@ -46,7 +48,12 @@ export default async function askingArticleSubmissionConsent(params) { } `({ text: data.searchedText }, { userId }); article = result.data.CreateArticle; - } else if (data.messageId) { + } else { + if (!data.messageId) { + // Should not be here + throw new Error('No message ID found, cannot submit message.'); + } + const proxyUrl = getLineContentProxyURL(data.messageId); const result = await gql` mutation ($mediaUrl: String!, $articleType: ArticleTypeEnum!) { @@ -79,6 +86,31 @@ export default async function askingArticleSubmissionConsent(params) { userId ); + let maybeAIReplies = [ + createTextMessage({ + text: t`In the meantime, you can:`, + }), + ]; + + if (isTextArticle) { + const aiReply = await createAIReply(article.id, userId); + /* istanbul ignore else */ + if (aiReply) { + maybeAIReplies = [ + createTextMessage({ + text: '這篇文章尚待查核中,請先不要相信這篇文章。\n以下是機器人初步分析此篇訊息的結果,希望能帶給你一些想法。', + }), + { + type: 'text', + text: aiReply, + }, + createTextMessage({ + text: '讀完以上機器人的自動分析後,您可以:', + }), + ]; + } + } + replies = [ { type: 'flex', @@ -107,9 +139,7 @@ export default async function askingArticleSubmissionConsent(params) { }, }, }, - createTextMessage({ - text: t`In the meantime, you can:`, - }), + ...maybeAIReplies, { type: 'flex', altText: articleCreatedMsg, diff --git a/src/webhook/handlers/choosingArticle.js b/src/webhook/handlers/choosingArticle.js index 79ebee19..ccadace6 100644 --- a/src/webhook/handlers/choosingArticle.js +++ b/src/webhook/handlers/choosingArticle.js @@ -13,6 +13,7 @@ import { createNotificationSettingsBubble, createArticleShareBubble, createAskArticleSubmissionConsentReply, + createAIReply, } from './utils'; import ga from 'src/lib/ga'; import UserSettings from 'src/database/models/userSettings'; @@ -116,6 +117,7 @@ export default async function choosingArticle(params) { GetArticle(id: $id) { text replyCount + articleType articleReplies(status: NORMAL) { reply { id @@ -326,6 +328,33 @@ export default async function choosingArticle(params) { const { allowNewReplyUpdate } = await UserSettings.findOrInsertByUserId( userId ); + const isTextArticle = GetArticle.articleType === 'TEXT'; + + let maybeAIReplies = [ + createTextMessage({ + text: t`In the meantime, you can:`, + }), + ]; + + if (isTextArticle) { + const aiReply = await createAIReply(selectedArticleId, userId); + /* istanbul ignore else */ + if (aiReply) { + maybeAIReplies = [ + createTextMessage({ + text: '這篇文章尚待查核,請先不要相信這篇文章。\n以下是機器人初步分析此篇訊息的結果,希望能帶給你一些想法。', + }), + { + type: 'text', + text: aiReply, + }, + createTextMessage({ + text: '讀完以上機器人的自動分析後,您可以:', + }), + ]; + } + } + replies = [ { type: 'flex', @@ -340,8 +369,10 @@ Don’t trust the message just yet!`, { type: 'text', wrap: true, - text: t`This message has already published on Cofacts, and will soon be fact-checked by volunteers. -Don’t trust the message just yet!`, + text: isTextArticle + ? '此訊息已經被收錄至 Cofacts 有待好心人來查證。' + : t`This message has already published on Cofacts, and will soon be fact-checked by volunteers. + Don’t trust the message just yet!`, }, { type: 'button', @@ -356,7 +387,7 @@ Don’t trust the message just yet!`, }, }, }, - createTextMessage({ text: t`In the meantime, you can:` }), + ...maybeAIReplies, { type: 'flex', altText: t`Provide more detail`, diff --git a/src/webhook/handlers/utils.js b/src/webhook/handlers/utils.js index 3393ec6e..2fb18acf 100644 --- a/src/webhook/handlers/utils.js +++ b/src/webhook/handlers/utils.js @@ -1,5 +1,6 @@ import { t, msgid, ngettext } from 'ttag'; import GraphemeSplitter from 'grapheme-splitter'; +import gql from 'src/lib/gql'; import { getArticleURL, createTypeWords } from 'src/lib/sharedUtils'; import { sign } from 'src/lib/jwt'; @@ -487,6 +488,18 @@ export function createReplyMessages(reply, article, selectedArticleId) { ]; } +export async function createAIReply(articleId, userId) { + return ( + await gql` + mutation ($articleId: String!) { + CreateAIReply(articleId: $articleId) { + text + } + } + `({ articleId }, { userId }) + ).data.CreateAIReply.text; +} + /** * @param {object} reply `Reply` type from rumors-api * @param {object} article `Article` type from rumors-api