mirror of
https://github.com/whekin/household-bot.git
synced 2026-03-31 17:44:03 +00:00
feat(bot): use typing indicator for dm assistant
This commit is contained in:
@@ -397,7 +397,7 @@ describe('registerDmAssistant', () => {
|
|||||||
|
|
||||||
await bot.handleUpdate(privateMessageUpdate('How much do I still owe this month?') as never)
|
await bot.handleUpdate(privateMessageUpdate('How much do I still owe this month?') as never)
|
||||||
|
|
||||||
expect(calls).toHaveLength(3)
|
expect(calls).toHaveLength(2)
|
||||||
expect(calls[0]).toMatchObject({
|
expect(calls[0]).toMatchObject({
|
||||||
method: 'sendChatAction',
|
method: 'sendChatAction',
|
||||||
payload: {
|
payload: {
|
||||||
@@ -409,14 +409,6 @@ describe('registerDmAssistant', () => {
|
|||||||
method: 'sendMessage',
|
method: 'sendMessage',
|
||||||
payload: {
|
payload: {
|
||||||
chat_id: 123456,
|
chat_id: 123456,
|
||||||
text: 'Working on it...'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
expect(calls[2]).toMatchObject({
|
|
||||||
method: 'editMessageText',
|
|
||||||
payload: {
|
|
||||||
chat_id: 123456,
|
|
||||||
message_id: 2,
|
|
||||||
text: 'You still owe 350.00 GEL this cycle.'
|
text: 'You still owe 350.00 GEL this cycle.'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -555,7 +547,7 @@ describe('registerDmAssistant', () => {
|
|||||||
await bot.handleUpdate(update as never)
|
await bot.handleUpdate(update as never)
|
||||||
await bot.handleUpdate(update as never)
|
await bot.handleUpdate(update as never)
|
||||||
|
|
||||||
expect(calls).toHaveLength(3)
|
expect(calls).toHaveLength(2)
|
||||||
expect(usageTracker.listHouseholdUsage('household-1')).toEqual([
|
expect(usageTracker.listHouseholdUsage('household-1')).toEqual([
|
||||||
{
|
{
|
||||||
householdId: 'household-1',
|
householdId: 'household-1',
|
||||||
|
|||||||
@@ -257,57 +257,6 @@ function paymentProposalReplyMarkup(locale: BotLocale, proposalId: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PendingAssistantReply {
|
|
||||||
chatId: number
|
|
||||||
messageId: number
|
|
||||||
}
|
|
||||||
|
|
||||||
async function sendAssistantProcessingReply(
|
|
||||||
ctx: Context,
|
|
||||||
text: string
|
|
||||||
): Promise<PendingAssistantReply | null> {
|
|
||||||
const message = await ctx.reply(text)
|
|
||||||
|
|
||||||
if (!message?.chat?.id || typeof message.message_id !== 'number') {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
chatId: message.chat.id,
|
|
||||||
messageId: message.message_id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function finalizeAssistantReply(
|
|
||||||
ctx: Context,
|
|
||||||
pendingReply: PendingAssistantReply | null,
|
|
||||||
text: string,
|
|
||||||
replyMarkup?: {
|
|
||||||
inline_keyboard: Array<
|
|
||||||
Array<{
|
|
||||||
text: string
|
|
||||||
callback_data: string
|
|
||||||
}>
|
|
||||||
>
|
|
||||||
}
|
|
||||||
): Promise<void> {
|
|
||||||
if (!pendingReply) {
|
|
||||||
await ctx.reply(text, replyMarkup ? { reply_markup: replyMarkup } : undefined)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await ctx.api.editMessageText(
|
|
||||||
pendingReply.chatId,
|
|
||||||
pendingReply.messageId,
|
|
||||||
text,
|
|
||||||
replyMarkup ? { reply_markup: replyMarkup } : {}
|
|
||||||
)
|
|
||||||
} catch {
|
|
||||||
await ctx.reply(text, replyMarkup ? { reply_markup: replyMarkup } : undefined)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function parsePaymentProposalPayload(
|
function parsePaymentProposalPayload(
|
||||||
payload: Record<string, unknown>
|
payload: Record<string, unknown>
|
||||||
): PaymentProposalPayload | null {
|
): PaymentProposalPayload | null {
|
||||||
@@ -768,7 +717,6 @@ export function registerDmAssistant(options: {
|
|||||||
|
|
||||||
const memory = options.memoryStore.get(telegramUserId)
|
const memory = options.memoryStore.get(telegramUserId)
|
||||||
const typingIndicator = startTypingIndicator(ctx)
|
const typingIndicator = startTypingIndicator(ctx)
|
||||||
let pendingReply: PendingAssistantReply | null = null
|
|
||||||
const assistantStartedAt = Date.now()
|
const assistantStartedAt = Date.now()
|
||||||
let stage: 'household_context' | 'assistant_response' = 'household_context'
|
let stage: 'household_context' | 'assistant_response' = 'household_context'
|
||||||
let contextBuildMs: number | null = null
|
let contextBuildMs: number | null = null
|
||||||
@@ -785,7 +733,6 @@ export function registerDmAssistant(options: {
|
|||||||
financeService
|
financeService
|
||||||
})
|
})
|
||||||
contextBuildMs = Date.now() - contextStartedAt
|
contextBuildMs = Date.now() - contextStartedAt
|
||||||
pendingReply = await sendAssistantProcessingReply(ctx, t.processing)
|
|
||||||
stage = 'assistant_response'
|
stage = 'assistant_response'
|
||||||
const assistantResponseStartedAt = Date.now()
|
const assistantResponseStartedAt = Date.now()
|
||||||
const reply = await options.assistant.respond({
|
const reply = await options.assistant.respond({
|
||||||
@@ -830,7 +777,7 @@ export function registerDmAssistant(options: {
|
|||||||
'DM assistant reply generated'
|
'DM assistant reply generated'
|
||||||
)
|
)
|
||||||
|
|
||||||
await finalizeAssistantReply(ctx, pendingReply, reply.text)
|
await ctx.reply(reply.text)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
options.logger?.error(
|
options.logger?.error(
|
||||||
{
|
{
|
||||||
@@ -846,7 +793,7 @@ export function registerDmAssistant(options: {
|
|||||||
},
|
},
|
||||||
'DM assistant reply failed'
|
'DM assistant reply failed'
|
||||||
)
|
)
|
||||||
await finalizeAssistantReply(ctx, pendingReply, t.unavailable)
|
await ctx.reply(t.unavailable)
|
||||||
} finally {
|
} finally {
|
||||||
typingIndicator.stop()
|
typingIndicator.stop()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -114,7 +114,6 @@ export const enBotTranslations: BotTranslationCatalog = {
|
|||||||
},
|
},
|
||||||
assistant: {
|
assistant: {
|
||||||
unavailable: 'The assistant is temporarily unavailable. Try again in a moment.',
|
unavailable: 'The assistant is temporarily unavailable. Try again in a moment.',
|
||||||
processing: 'Working on it...',
|
|
||||||
noHousehold:
|
noHousehold:
|
||||||
'I can help after your Telegram account is linked to a household. Open the household group and complete the join flow first.',
|
'I can help after your Telegram account is linked to a household. Open the household group and complete the join flow first.',
|
||||||
multipleHouseholds:
|
multipleHouseholds:
|
||||||
|
|||||||
@@ -117,7 +117,6 @@ export const ruBotTranslations: BotTranslationCatalog = {
|
|||||||
},
|
},
|
||||||
assistant: {
|
assistant: {
|
||||||
unavailable: 'Ассистент сейчас недоступен. Попробуйте ещё раз чуть позже.',
|
unavailable: 'Ассистент сейчас недоступен. Попробуйте ещё раз чуть позже.',
|
||||||
processing: 'Сейчас разберусь...',
|
|
||||||
noHousehold:
|
noHousehold:
|
||||||
'Я смогу помочь после того, как ваш Telegram-профиль будет привязан к дому. Сначала откройте группу дома и завершите вступление.',
|
'Я смогу помочь после того, как ваш Telegram-профиль будет привязан к дому. Сначала откройте группу дома и завершите вступление.',
|
||||||
multipleHouseholds:
|
multipleHouseholds:
|
||||||
|
|||||||
@@ -122,7 +122,6 @@ export interface BotTranslationCatalog {
|
|||||||
}
|
}
|
||||||
assistant: {
|
assistant: {
|
||||||
unavailable: string
|
unavailable: string
|
||||||
processing: string
|
|
||||||
noHousehold: string
|
noHousehold: string
|
||||||
multipleHouseholds: string
|
multipleHouseholds: string
|
||||||
rateLimited: (retryDelay: string) => string
|
rateLimited: (retryDelay: string) => string
|
||||||
|
|||||||
Reference in New Issue
Block a user