Fix Bind Topic Error by making callback handling more robust

- Wrap answerCallbackQuery and editMessageText in try-catch to handle expired queries
- Answer callback queries as early as possible
- Add setup_tracking to allowed pending action types for better type safety
- Ignore 'query is too old' and 'message is not modified' errors gracefully
This commit is contained in:
2026-03-15 02:50:32 +04:00
parent 5e39cdf455
commit 07c5ffb82d
3 changed files with 116 additions and 69 deletions

View File

@@ -945,16 +945,22 @@ export function registerHouseholdSetupCommands(options: {
})
if (result.status === 'rejected') {
await ctx.answerCallbackQuery({
await ctx
.answerCallbackQuery({
text: adminRejectionMessage(locale, result.reason),
show_alert: true
})
.catch(() => {})
return
}
try {
await ctx.answerCallbackQuery({
text: t.setup.approvedMemberToast(result.member.displayName)
})
} catch {
// Ignore stale query
}
if (ctx.msg) {
const refreshed = await options.householdAdminService.listPendingMembers({
@@ -963,6 +969,7 @@ export function registerHouseholdSetupCommands(options: {
})
if (refreshed.status === 'ok') {
try {
if (refreshed.members.length === 0) {
await ctx.editMessageText(t.setup.pendingMembersEmpty(refreshed.householdName))
} else {
@@ -971,6 +978,9 @@ export function registerHouseholdSetupCommands(options: {
reply_markup: reply.reply_markup
})
}
} catch {
// Ignore message edit errors
}
}
}
@@ -989,10 +999,12 @@ export function registerHouseholdSetupCommands(options: {
const t = getBotTranslations(locale).setup
if (!isGroupChat(ctx)) {
await ctx.answerCallbackQuery({
await ctx
.answerCallbackQuery({
text: t.useButtonInGroup,
show_alert: true
})
.catch(() => {})
return
}
@@ -1004,18 +1016,22 @@ export function registerHouseholdSetupCommands(options: {
: null
if (!actorIsAdmin) {
await ctx.answerCallbackQuery({
await ctx
.answerCallbackQuery({
text: t.onlyTelegramAdminsBindTopics,
show_alert: true
})
.catch(() => {})
return
}
if (!household) {
await ctx.answerCallbackQuery({
await ctx
.answerCallbackQuery({
text: t.householdNotConfigured,
show_alert: true
})
.catch(() => {})
return
}
@@ -1031,13 +1047,15 @@ export function registerHouseholdSetupCommands(options: {
})
if (result.status === 'rejected') {
await ctx.answerCallbackQuery({
await ctx
.answerCallbackQuery({
text:
result.reason === 'not_admin'
? t.onlyTelegramAdminsBindTopics
: t.householdNotConfigured,
show_alert: true
})
.catch(() => {})
return
}
@@ -1050,15 +1068,23 @@ export function registerHouseholdSetupCommands(options: {
botUsername: ctx.me.username
})
try {
await ctx.answerCallbackQuery({
text: t.setupTopicCreated(setupTopicRoleLabel(locale, role), topicName)
})
} catch {
// Ignore stale query
}
if (ctx.msg) {
try {
await ctx.editMessageText(
reply.text,
'reply_markup' in reply ? { reply_markup: reply.reply_markup } : {}
)
} catch {
// Ignore message edit errors
}
}
} catch (error) {
const message =
@@ -1067,10 +1093,12 @@ export function registerHouseholdSetupCommands(options: {
? t.setupTopicCreateForbidden
: t.setupTopicCreateFailed
await ctx.answerCallbackQuery({
await ctx
.answerCallbackQuery({
text: message,
show_alert: true
})
.catch(() => {})
}
}
)
@@ -1085,10 +1113,12 @@ export function registerHouseholdSetupCommands(options: {
const t = getBotTranslations(locale).setup
if (!isGroupChat(ctx)) {
await ctx.answerCallbackQuery({
await ctx
.answerCallbackQuery({
text: t.useButtonInGroup,
show_alert: true
})
.catch(() => {})
return
}
@@ -1098,10 +1128,12 @@ export function registerHouseholdSetupCommands(options: {
const actorIsAdmin = await isGroupAdmin(ctx)
if (!actorIsAdmin) {
await ctx.answerCallbackQuery({
await ctx
.answerCallbackQuery({
text: t.onlyTelegramAdminsBindTopics,
show_alert: true
})
.catch(() => {})
return
}
@@ -1113,27 +1145,37 @@ export function registerHouseholdSetupCommands(options: {
})
if (result.status === 'rejected') {
await ctx.answerCallbackQuery({
await ctx
.answerCallbackQuery({
text:
result.reason === 'not_admin'
? t.onlyTelegramAdminsBindTopics
: t.householdNotConfigured,
show_alert: true
})
.catch(() => {})
return
}
try {
await ctx.answerCallbackQuery({
text: t.topicBoundSuccess(
setupTopicRoleLabel(locale, role),
result.household.householdName
)
})
} catch {
// Ignore stale query
}
if (ctx.msg) {
try {
await ctx.editMessageText(
t.topicBoundSuccess(setupTopicRoleLabel(locale, role), result.household.householdName)
)
} catch {
// Ignore message edit errors
}
}
// Try to update the main /setup checklist if it exists

View File

@@ -37,6 +37,10 @@ function parsePendingActionType(raw: string): TelegramPendingActionType {
return raw
}
if (raw === 'setup_tracking') {
return raw
}
throw new Error(`Unexpected telegram pending action type: ${raw}`)
}

View File

@@ -7,7 +7,8 @@ export const TELEGRAM_PENDING_ACTION_TYPES = [
'payment_topic_clarification',
'payment_topic_confirmation',
'reminder_utility_entry',
'setup_topic_binding'
'setup_topic_binding',
'setup_tracking'
] as const
export type TelegramPendingActionType = (typeof TELEGRAM_PENDING_ACTION_TYPES)[number]