Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 19 additions & 13 deletions external_plugins/telegram/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,19 @@ function gate(ctx: Context): GateResult {
return { action: 'drop' }
}

// Like gate() but for bot commands: no pairing side effects, just allow/drop.
function dmCommandGate(ctx: Context): { access: Access; senderId: string } | null {
if (ctx.chat?.type !== 'private') return null
if (!ctx.from) return null
const senderId = String(ctx.from.id)
const access = loadAccess()
const pruned = pruneExpired(access)
if (pruned) saveAccess(access)
if (access.dmPolicy === 'disabled') return null
if (access.dmPolicy === 'allowlist' && !access.allowFrom.includes(senderId)) return null
return { access, senderId }
}

function isMentioned(ctx: Context, extraPatterns?: string[]): boolean {
const entities = ctx.message?.entities ?? ctx.message?.caption_entities ?? []
const text = ctx.message?.text ?? ctx.message?.caption ?? ''
Expand Down Expand Up @@ -585,12 +598,7 @@ process.on('SIGINT', shutdown)
// the gate's behavior for unrecognized groups.

bot.command('start', async ctx => {
if (ctx.chat?.type !== 'private') return
const access = loadAccess()
if (access.dmPolicy === 'disabled') {
await ctx.reply(`This bot isn't accepting new connections.`)
return
}
if (!dmCommandGate(ctx)) return
await ctx.reply(
`This bot bridges Telegram to a Claude Code session.\n\n` +
`To pair:\n` +
Expand All @@ -601,7 +609,7 @@ bot.command('start', async ctx => {
})

bot.command('help', async ctx => {
if (ctx.chat?.type !== 'private') return
if (!dmCommandGate(ctx)) return
await ctx.reply(
`Messages you send here route to a paired Claude Code session. ` +
`Text and photos are forwarded; replies and reactions come back.\n\n` +
Expand All @@ -611,14 +619,12 @@ bot.command('help', async ctx => {
})

bot.command('status', async ctx => {
if (ctx.chat?.type !== 'private') return
const from = ctx.from
if (!from) return
const senderId = String(from.id)
const access = loadAccess()
const gated = dmCommandGate(ctx)
if (!gated) return
const { access, senderId } = gated

if (access.allowFrom.includes(senderId)) {
const name = from.username ? `@${from.username}` : senderId
const name = ctx.from!.username ? `@${ctx.from!.username}` : senderId
await ctx.reply(`Paired as ${name}.`)
return
}
Expand Down
Loading