diff --git a/package.json b/package.json index cafc08fa..056931ec 100644 --- a/package.json +++ b/package.json @@ -116,7 +116,7 @@ "botframework-connector": "4.18.0", "botlib": "3.0.11", "c3-chart-maker": "0.2.8", - "cd": "0.3.3", + "cd": "^0.3.3", "chalk-animation": "^2.0.3", "chatgpt": "2.4.2", "chrome-remote-interface": "0.31.3", diff --git a/packages/basic.gblib/services/GBVMService.ts b/packages/basic.gblib/services/GBVMService.ts index b6661b75..59ce3582 100644 --- a/packages/basic.gblib/services/GBVMService.ts +++ b/packages/basic.gblib/services/GBVMService.ts @@ -466,7 +466,7 @@ export class GBVMService extends GBService { } } while (include); - let { code, map, metadata, tasks } = await this.convert(filename, mainName, basicCode); + let { code, map, metadata, tasks, systemPrompt } = await this.convert(filename, mainName, basicCode); // Generates function JSON metadata to be used later. @@ -474,7 +474,6 @@ export class GBVMService extends GBService { Fs.writeFileSync(jsonFile, JSON.stringify(metadata)); const mapFile = `${filename}.map`; - Fs.writeFileSync(mapFile, JSON.stringify(map)); // Execute off-line code tasks @@ -836,6 +835,7 @@ export class GBVMService extends GBService { let description; let table = null; // Used for TABLE keyword. let talk = null; + let systemPrompt = null; let connection = null; const tasks = []; let fields = {}; @@ -851,7 +851,7 @@ export class GBVMService extends GBService { line = line.replace(/^\s*\d+\s*/gi, ''); - if (!table && !talk) { + if (!table && !talk && !systemPrompt) { for (let j = 0; j < keywords.length; j++) { line = line.replace(keywords[j][0], keywords[j][1]); // TODO: Investigate delay here. } @@ -874,6 +874,15 @@ export class GBVMService extends GBService { emmit = false; } + const endSystemPromptKeyword = /^\s*END SYSTEM PROMPT\s*/gim; + let endSystemPromptReg = endSystemPromptKeyword.exec(line); + if (endSystemPromptReg && systemPrompt) { + line = systemPrompt + '`})'; + + systemPrompt = null; + emmit = true; + } + const endTalkKeyword = /^\s*END TALK\s*/gim; let endTalkReg = endTalkKeyword.exec(line); if (endTalkReg && talk) { @@ -883,7 +892,6 @@ export class GBVMService extends GBService { emmit = true; } - const endTableKeyword = /^\s*END TABLE\s*/gim; let endTableReg = endTableKeyword.exec(line); if (endTableReg && table) { @@ -906,6 +914,13 @@ export class GBVMService extends GBService { emmit = false; } + // Inside BEGIN SYSTEM PROMPT + + if (systemPrompt) { + systemPrompt += line + '\\n'; + emmit = false; + } + // Inside BEGIN/END table pair containing FIELDS. if (table && line.trim() !== '') { @@ -929,6 +944,13 @@ export class GBVMService extends GBService { emmit = false; } + const systemPromptKeyword = /^\s*BEGIN SYSTEM PROMPT\s*/gim; + let systemPromptReg = systemPromptKeyword.exec(line); + if (systemPromptReg && !systemPrompt) { + systemPrompt = "await sys.setSystemPrompt ({pid: pid, text: `"; + emmit = false; + } + // Add additional lines returned from replacement. let add = emmit ? line.split(/\r\n|\r|\n/).length : 0; @@ -952,7 +974,7 @@ export class GBVMService extends GBService { let metadata = GBVMService.getMetadata(mainName, properties, description); - return { code, map, metadata, tasks }; + return { code, map, metadata, tasks, systemPrompt }; } /** diff --git a/packages/basic.gblib/services/SystemKeywords.ts b/packages/basic.gblib/services/SystemKeywords.ts index 634f16fd..f34a762e 100644 --- a/packages/basic.gblib/services/SystemKeywords.ts +++ b/packages/basic.gblib/services/SystemKeywords.ts @@ -1440,6 +1440,20 @@ export class SystemKeywords { return ret; } + public static async setSystemPrompt({ pid, systemPrompt }) { + + let { min, user } = await DialogKeywords.getProcessInfo(pid); + const sec = new SecService(); + + if (user) { + user['systemPrompt'] = systemPrompt; + const path = DialogKeywords.getGBAIPath(min.botId); + const systemPromptFile = urlJoin(process.cwd(), 'work', path, 'users',user.userSystemId, 'systemPrompt.txt'); + Fs.writeFileSync(systemPromptFile, systemPrompt); + } + } + + /** * Creates a folder in the bot instance drive. * diff --git a/packages/core.gbapp/services/GBConversationalService.ts b/packages/core.gbapp/services/GBConversationalService.ts index 98b63185..06763667 100644 --- a/packages/core.gbapp/services/GBConversationalService.ts +++ b/packages/core.gbapp/services/GBConversationalService.ts @@ -68,6 +68,11 @@ import { GBUtil } from '../../../src/util.js'; * services like NLP or Search. */ export class GBConversationalService { + + public async getNewMobileCode(){ + throw new Error('Method removed.'); + } + /** * Reference to the core service. */ @@ -280,20 +285,6 @@ export class GBConversationalService { return str; } - public getNewMobileCode() { - const passwordGenerator = new PasswordGenerator(); - const options = { - upperCaseAlpha: false, - lowerCaseAlpha: false, - number: true, - specialCharacter: false, - minimumLength: 4, - maximumLength: 4 - }; - let code = passwordGenerator.generatePassword(options); - return code; - } - public getCurrentLanguage(step: GBDialogStep) { return step.context.activity.locale; } diff --git a/packages/core.gbapp/services/GBMinService.ts b/packages/core.gbapp/services/GBMinService.ts index 977feb8b..f9df8743 100644 --- a/packages/core.gbapp/services/GBMinService.ts +++ b/packages/core.gbapp/services/GBMinService.ts @@ -768,9 +768,9 @@ export class GBMinService { min['scheduleMap'] = {}; min['conversationWelcomed'] = {}; if (process.env.OPENAI_API_KEY) { - min['vectorStore'] = await this.deployer.loadOrCreateEmptyVectorStore(min); const gbkbPath = DialogKeywords.getGBAIPath(min.botId, 'gbkb'); min['vectorStorePath'] = Path.join('work', gbkbPath, 'docs-vectorized'); + min['vectorStore'] = await this.deployer.loadOrCreateEmptyVectorStore(min); } min['apiConversations'] = {}; min.packages = sysPackages; diff --git a/packages/gpt.gblib/services/ChatServices.ts b/packages/gpt.gblib/services/ChatServices.ts index 00388dd3..19bb6d74 100644 --- a/packages/gpt.gblib/services/ChatServices.ts +++ b/packages/gpt.gblib/services/ChatServices.ts @@ -30,30 +30,81 @@ 'use strict'; -import { RunnableSequence } from "@langchain/core/runnables"; -import { ChatOpenAI } from "@langchain/openai"; -import { GBMinInstance } from 'botlib'; -import * as Fs from 'fs'; -import { formatXml } from "langchain/agents/format_scratchpad/xml"; -import { jsonSchemaToZod } from "json-schema-to-zod"; -import { renderTextDescription } from "langchain/tools/render"; - -import { AgentExecutor, AgentStep } from "langchain/agents"; -import { BufferWindowMemory } from 'langchain/memory'; -import { AIMessagePromptTemplate, ChatPromptTemplate, HumanMessagePromptTemplate, MessagesPlaceholder } from '@langchain/core/prompts'; -import { Tool } from "langchain/tools"; import { HNSWLib } from '@langchain/community/vectorstores/hnswlib'; +import { StringOutputParser } from "@langchain/core/output_parsers"; +import { AIMessagePromptTemplate, ChatPromptTemplate, HumanMessagePromptTemplate, MessagesPlaceholder } from '@langchain/core/prompts'; +import { RunnableSequence } from "@langchain/core/runnables"; +import { convertToOpenAITool } from "@langchain/core/utils/function_calling"; +import { ChatOpenAI } from "@langchain/openai"; +import { GBLog, GBMinInstance } from 'botlib'; +import * as Fs from 'fs'; +import { jsonSchemaToZod } from "json-schema-to-zod"; +import { BufferWindowMemory } from 'langchain/memory'; import Path from 'path'; import { CollectionUtil } from 'pragmatismo-io-framework'; import { DialogKeywords } from '../../basic.gblib/services/DialogKeywords.js'; import { GBVMService } from '../../basic.gblib/services/GBVMService.js'; import { GBConfigService } from '../../core.gbapp/services/GBConfigService.js'; import { GuaribasSubject } from '../../kb.gbapp/models/index.js'; -import { XMLAgentOutputParser } from "langchain/agents/xml/output_parser"; -import { StringOutputParser } from "@langchain/core/output_parsers"; -import { convertToOpenAITool } from "@langchain/core/utils/function_calling"; +import { z } from "zod"; +import { DynamicStructuredTool } from "@langchain/core/tools"; +import { JsonOutputToolsParser } from "langchain/output_parsers"; +import { + RunnableLambda, + RunnablePassthrough, +} from "@langchain/core/runnables"; +import { + CombiningOutputParser, +} from "langchain/output_parsers"; +import { + BaseLLMOutputParser, + OutputParserException, +} from "@langchain/core/output_parsers"; +import { ChatGeneration, Generation } from "@langchain/core/outputs"; + +export interface CustomOutputParserFields { } + +// This can be more generic, like Record +export type ExpectedOutput = { + greeting: string; +}; + +function isChatGeneration( + llmOutput: ChatGeneration | Generation +): llmOutput is ChatGeneration { + return "message" in llmOutput; +} + +export class CustomLLMOutputParser extends BaseLLMOutputParser { + lc_namespace = ["langchain", "output_parsers"]; + + constructor(fields?: CustomOutputParserFields) { + super(fields); + } + + async parseResult( + llmOutputs: ChatGeneration[] | Generation[] + ): Promise { + if (!llmOutputs.length) { + throw new OutputParserException( + "Output parser did not receive any generations." + ); + } + let parsedOutput; + + + if (isChatGeneration(llmOutputs[0])) { + parsedOutput = llmOutputs[0].message.content; + } else { + parsedOutput = llmOutputs[0].text; + } + let parsedText; + parsedText = parsedOutput; + return parsedText; + } +} export class ChatServices { @@ -62,6 +113,10 @@ export class ChatServices { sanitizedQuestion: string, numDocuments: number ): Promise { + if (sanitizedQuestion === '') { + return ''; + } + const documents = await vectorStore.similaritySearch(sanitizedQuestion, numDocuments); return documents .map((doc) => doc.pageContent) @@ -78,13 +133,13 @@ export class ChatServices { * result = CONTINUE text * */ - public static async continue(min: GBMinInstance, text: string, chatId) { + public static async continue(min: GBMinInstance, question: string, chatId) { } - public static async answerByGPT(min: GBMinInstance, pid, - query: string, + public static async answerByGPT(min: GBMinInstance, user, pid, + question: string, searchScore: number, subjects: GuaribasSubject[] ) { @@ -93,6 +148,7 @@ export class ChatServices { return { answer: undefined, questionId: 0 }; } + const contentLocale = min.core.getParam( min.instance, 'Default Content Language', @@ -102,6 +158,10 @@ export class ChatServices { let tools = await ChatServices.getTools(min); let toolsAsText = ChatServices.getToolsAsText(tools); + const toolMap: Record = { + multiply: tools[0] + }; + const memory = new BufferWindowMemory({ returnMessages: true, memoryKey: 'chat_history', @@ -114,47 +174,56 @@ export class ChatServices { modelName: "gpt-3.5-turbo-0125", temperature: 0, }); - - const contextVectorStore = min['vectorStore']; - let promptTemplate = `Answer in ${contentLocale}. - You have access to the context (RELEVANTDOCS) provided by the user. - - When answering think about whether the question in RELEVANTDOCS, but never mention - to user about the source. - Don’t justify your answers. Don't refer to yourself in any of the created content. - Don´t prefix RESPONSE: when answering the user. - RELEVANTDOCS: {context} - - QUESTION: """{input}""" - - You have the following tools that you can invoke based on the user inquiry. - Tools: - - ${toolsAsText} - - `; - - - const toolMap: Record = { - multiply: ()=>{}, - }; + const context = min['vectorStore']; const modelWithTools = model.bind({ tools: tools.map(convertToOpenAITool), + tool_choice: { + type: "function", + function: { name: "multiply" }, + }, }); + // Function for dynamically constructing the end of the chain based on the model-selected tool. + const callSelectedTool = RunnableLambda.from( + (toolInvocation: Record) => { + const selectedTool = toolMap[toolInvocation.type]; + if (!selectedTool) { + throw new Error( + `No matching tool available for requested type "${toolInvocation.type}".` + ); + } + const toolCallChain = RunnableSequence.from([ + (toolInvocation) => toolInvocation.args, + selectedTool, + ]); + // We use `RunnablePassthrough.assign` here to return the intermediate `toolInvocation` params + // as well, but you can omit if you only care about the answer. + return RunnablePassthrough.assign({ + output: toolCallChain, + }); + }, + + ); + const questionGeneratorTemplate = ChatPromptTemplate.fromMessages([ AIMessagePromptTemplate.fromTemplate( "Given the following conversation about a codebase and a follow up question, rephrase the follow up question to be a standalone question." ), new MessagesPlaceholder("chat_history"), - AIMessagePromptTemplate.fromTemplate(`Follow Up Input: {question} Standalone question:`), + AIMessagePromptTemplate.fromTemplate(`Follow Up Input: {question} + Standalone question:`), ]); const combineDocumentsPrompt = ChatPromptTemplate.fromMessages([ AIMessagePromptTemplate.fromTemplate( - "Use the following pieces of context to answer the question at the end. If you don't know the answer, just say that you don't know, don't try to make up an answer.\n\n{context}\n\n" + `Use the following pieces of context to answer the question at the end. + If you don't know the answer, just say that you don't know, don't try to make up an answer. + \n\n{context}\n\n + You have the following tools to call: + ${toolsAsText}` + ), new MessagesPlaceholder("chat_history"), HumanMessagePromptTemplate.fromTemplate("Question: {question}"), @@ -168,15 +237,15 @@ export class ChatServices { return chat_history; }, context: async (output: string) => { - - return await this.getRelevantContext(contextVectorStore, output, 1); + return await ChatServices.getRelevantContext(context, output, 1); + }, }, combineDocumentsPrompt, modelWithTools, - new StringOutputParser(), + new CustomLLMOutputParser(), ]); - + const conversationalQaChain = RunnableSequence.from([ { question: (i: { question: string }) => i.question, @@ -190,20 +259,31 @@ export class ChatServices { new StringOutputParser(), combineDocumentsChain, ]); + + + + const systemPrompt = user['systemPrompt']; - const question = "How can I initialize a ReAct agent?"; let result = await conversationalQaChain.invoke({ question, }); - - return { answer: result.toString() , questionId: 0 }; + // await memory.saveContext( + // { + // input: query, + // }, + // { + // output: result, + // } + // ); + GBLog.info(`GPT Result: ${result.toString()}`); + return { answer: result.toString(), questionId: 0 }; } private static getToolsAsText(tools) { return Object.keys(tools) - .map((toolname) => `${tools[toolname].function.name}: ${tools[toolname].function.description}`) + .map((toolname) => `${tools[toolname].name}: ${tools[toolname].description}`) .join("\n"); } @@ -217,19 +297,35 @@ export class ChatServices { if (Fs.existsSync(functionJSON)) { const func = JSON.parse(Fs.readFileSync(functionJSON, 'utf8')); - func.schema = jsonSchemaToZod(func.properties, { module: "esm" }); - func.func = async ()=>{ - const name = ''; - const pid = 1; - const text = ''; // TODO: - const result = await GBVMService.callVM(name, min, false, pid,false, [text]); + func.schema = jsonSchemaToZod(func.properties, { module: "esm" }); + func.func = async () => { + const name = ''; + const pid = 1; + const text = ''; // TODO: + const result = await GBVMService.callVM(name, min, false, pid, false, [text]); - } + } functions.push(func); } }); + + const multiplyTool = new DynamicStructuredTool({ + name: "multiply", + description: "Multiply two integers together.", + schema: z.object({ + firstInt: z.number(), + secondInt: z.number(), + }), + func: async ({ firstInt, secondInt }) => { + return (firstInt * secondInt).toString(); + }, + }); + + functions.push(multiplyTool); + + return functions; } } diff --git a/packages/kb.gbapp/dialogs/AskDialog.ts b/packages/kb.gbapp/dialogs/AskDialog.ts index a3ccb2f1..d3c0ef85 100644 --- a/packages/kb.gbapp/dialogs/AskDialog.ts +++ b/packages/kb.gbapp/dialogs/AskDialog.ts @@ -228,7 +228,14 @@ export class AskDialog extends IGBDialog { min.instance.searchScore ? min.instance.searchScore : minBoot.instance.searchScore ); - const results = await service.ask(min,step.context.activity['pid'], text, searchScore, null /* user.subjects */); + // Tries to answer by NLP. + + let handled = await min.conversationalService.routeNLP(step, min, text); + if (handled) { + return; + } + + const results = await service.ask(min, user, step, step.context.activity['pid'], text, searchScore, null /* user.subjects */); // If there is some result, answer immediately. diff --git a/packages/kb.gbapp/services/KBService.ts b/packages/kb.gbapp/services/KBService.ts index 5d633adc..000890e3 100644 --- a/packages/kb.gbapp/services/KBService.ts +++ b/packages/kb.gbapp/services/KBService.ts @@ -269,6 +269,8 @@ export class KBService implements IGBKBService { public async ask( min: GBMinInstance, + user, + step, pid, query: string, searchScore: number, @@ -372,11 +374,11 @@ export class KBService implements IGBKBService { } } GBLog.info( - `SEARCH returned LOW level score, calling GPT + `SEARCH returned LOW level score, calling NLP if any, returnedScore: ${returnedScore} < required (searchScore): ${searchScore}` ); - return await ChatServices.answerByGPT(min,pid, + return await ChatServices.answerByGPT(min, user, pid, query, searchScore, subjects @@ -384,10 +386,6 @@ export class KBService implements IGBKBService { } - - - - public async getSubjectItems(instanceId: number, parentId: number): Promise { const where = { parentSubjectId: parentId, instanceId: instanceId }; diff --git a/packages/security.gbapp/dialogs/SMSAuthDialog.ts b/packages/security.gbapp/dialogs/SMSAuthDialog.ts new file mode 100644 index 00000000..462c0e4f --- /dev/null +++ b/packages/security.gbapp/dialogs/SMSAuthDialog.ts @@ -0,0 +1,103 @@ +/*****************************************************************************\ +| █████ █████ ██ █ █████ █████ ████ ██ ████ █████ █████ ███ ® | +| ██ █ ███ █ █ ██ ██ ██ ██ ██ ██ █ ██ ██ █ █ | +| ██ ███ ████ █ ██ █ ████ █████ ██████ ██ ████ █ █ █ ██ | +| ██ ██ █ █ ██ █ █ ██ ██ ██ ██ ██ ██ █ ██ ██ █ █ | +| █████ █████ █ ███ █████ ██ ██ ██ ██ █████ ████ █████ █ ███ | +| | +| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. | +| Licensed under the AGPL-3.0. | +| | +| According to our dual licensing model, this program can be used either | +| under the terms of the GNU Affero General Public License, version 3, | +| or under a proprietary license. | +| | +| The texts of the GNU Affero General Public License with an additional | +| permission and of our proprietary license can be found at and | +| in the LICENSE file you have received along with this program. | +| | +| This program is distributed in the hope that it will be useful, | +| but WITHOUT ANY WARRANTY, without even the implied warranty of | +| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | +| GNU Affero General Public License for more details. | +| | +| "General Bots" is a registered trademark of pragmatismo.com.br. | +| The licensing of the program under the AGPLv3 does not imply a | +| trademark license. Therefore any rights, title and interest in | +| our trademarks remain entirely with us. | +| | +\*****************************************************************************/ + +/** + * @fileoverview Dialog for handling OAuth scenarios. + */ + +'use strict'; + +import { TokenResponse } from 'botbuilder'; +import { GBLog, GBMinInstance, IGBDialog } from 'botlib'; +import { Messages } from '../strings.js'; +import { GBAdminService } from '../../admin.gbapp/services/GBAdminService.js'; +import { SecService } from '../services/SecService.js'; +/** + * Dialogs for handling Menu control. + */ +export class SMSAuthDialog extends IGBDialog { + public static getSMSAuthDialog(min: GBMinInstance) { + return { + id: '/smsauth', + waterfall: [ + async (step) => { + const msg = 'Por favor, qual o seu celular? Ex: 55 21 99999-0000.'; + step.activeDialog.state.resetInfo = {}; + return await min.conversationalService.prompt(min, step, msg); + + }, + async (step) => { + + await step.context.sendActivity('Por favor, digite o código enviado para seu celular.'); + + const mobile = step.result.replace(/\+|\s|\-/g, ''); + const locale = step.context.activity.locale; + step.activeDialog.state.resetInfo.mobile = mobile; + + // Generates a new mobile code. + + let code = GBAdminService.getMobileCode(); + GBLog.info(`SMS Auth: Generated new code: ${code} is being sent.`); + step.activeDialog.state.resetInfo.sentCode = code; + step.activeDialog.state.resetInfo.mobile = mobile; + + // Sends a confirmation SMS. + + await min.whatsAppDirectLine.sendToDevice( + mobile, + Messages[locale].please_use_code(code) + ); + return await min.conversationalService.prompt(min, step, Messages[locale].confirm_mobile); + }, + async (step) => { + const typed = step.result; + const locale = step.context.activity.locale; + + // Checks if the typed code is equal to the one + // sent to the registered mobile. + + if (typed == step.activeDialog.state.resetInfo.sentCode) { + let sec = new SecService(); + const member = step.context.activity.from; + + GBLog.info(`SMS Auth: User Authenticated.`); + await step.context.sendActivity(Messages[locale].authenticated); + + return await step.endDialog(step.activeDialog.state.resetInfo.mobile); + } + else { + await step.context.sendActivity(Messages[locale].not_authorized); + return await step.endDialog(false); + } + } + ] + }; + } +} diff --git a/packages/security.gbapp/index.ts b/packages/security.gbapp/index.ts index 5f3c0ffe..fcfb7282 100644 --- a/packages/security.gbapp/index.ts +++ b/packages/security.gbapp/index.ts @@ -39,6 +39,7 @@ import { Sequelize } from 'sequelize-typescript'; import { OAuthDialog } from './dialogs/OAuthDialog.js'; import { ProfileDialog } from './dialogs/ProfileDialog.js'; import { GuaribasGroup, GuaribasUser, GuaribasUserGroup } from './models/index.js'; +import { SMSAuthDialog } from './dialogs/SMSAuthDialog.js'; /** * Package for the security module. @@ -50,9 +51,10 @@ export class GBSecurityPackage implements IGBPackage { ProfileDialog.getNameDialog(min), ProfileDialog.getEmailDialog(min), ProfileDialog.getMobileDialog(min), - ProfileDialog.getMobileConfirmDialog(min) + ProfileDialog.getMobileConfirmDialog(min), + SMSAuthDialog.getSMSAuthDialog(min) ]; - + if (process.env.ENABLE_AUTH) { out.push(OAuthDialog.getOAuthDialog(min)); } diff --git a/packages/security.gbapp/services/SecService.ts b/packages/security.gbapp/services/SecService.ts index e34c351f..25f46a82 100644 --- a/packages/security.gbapp/services/SecService.ts +++ b/packages/security.gbapp/services/SecService.ts @@ -6,6 +6,8 @@ import { FindOptions } from 'sequelize'; import { DialogKeywords } from '../../../packages/basic.gblib/services/DialogKeywords.js'; import * as Fs from 'fs'; import mkdirp from 'mkdirp'; +import urlJoin from 'url-join'; + /** * Security service layer. @@ -26,17 +28,19 @@ export class SecService extends GBService { } }); + const gbaiPath = DialogKeywords.getGBAIPath(min.botId); + const dir = urlJoin ('work',gbaiPath, 'users', userSystemId); + if (!user) { user = GuaribasUser.build(); - const gbaiPath = DialogKeywords.getGBAIPath(min.botId); - - const dir = `work/${gbaiPath}/users/${userSystemId}`; if (!Fs.existsSync(dir)) { mkdirp.sync(dir); } + } - - + const systemPromptFile = urlJoin(dir, 'systemPrompt.txt'); + if (Fs.existsSync(systemPromptFile)) { + user[ 'systemPrompt'] = Fs.readFileSync(systemPromptFile); } user.instanceId = min.instance.instanceId; diff --git a/packages/security.gbapp/strings.ts b/packages/security.gbapp/strings.ts index c3cbb678..0940e393 100644 --- a/packages/security.gbapp/strings.ts +++ b/packages/security.gbapp/strings.ts @@ -6,7 +6,11 @@ export const Messages = { whats_email: "What's your E-mail address?", validation_enter_name: 'Please enter your full name.', validation_enter_valid_mobile: 'Please enter a valid mobile number.', - validation_enter_valid_email: 'Please enter a valid e-mail.' + validation_enter_valid_email: 'Please enter a valid e-mail.', + authenticated: 'You are now authenticated.', + not_authorized: 'Wrong verification code. Not authenticated yet. Try again, please.', + please_use_code:(code)=> `Please, answer the Bot with the code: ${code}.` + }, 'pt-BR': { whats_name: 'Qual o seu nome?', @@ -18,6 +22,10 @@ export const Messages = { código enviado para seu celular.`, validation_enter_valid_email: 'Por favor, digite um e-mail válido no formato nome@domínio.com.br.', validation_enter_name: 'Por favor, digite seu nome completo', - validation_enter_valid_mobile: 'Por favor, insira um número de celular válido (ex.: +55 21 98888-7766).' + validation_enter_valid_mobile: 'Por favor, insira um número de celular válido (ex.: +55 21 98888-7766).', + authenticated: 'Você está autenticada(o).', + not_authorized: 'Código de identificação inválido. Não autorizado, tente novamente, por favor.', + please_use_code:(code)=> `Por favor, responda ao bot com o código: ${code}.` + } };