import { Bot, Context } from 'grammy'
import { ActionCodesClient } from '@actioncodes/sdk'
const bot = new Bot(process.env.BOT_TOKEN!)
const client = new ActionCodesClient({
authToken: process.env.ACTION_CODES_TOKEN
})
// /start command
bot.command('start', (ctx) => {
ctx.reply(
'Welcome! I can help you sign messages with your Solana wallet.\n\n' +
'Commands:\n' +
'/sign - Sign a message\n' +
'/verify - Verify wallet ownership'
)
})
// /sign command
bot.command('sign', async (ctx) => {
await ctx.reply(
'To sign a message:\n\n' +
'1. Open actioncode.app in your Solana wallet\n' +
'2. Connect and get your code\n' +
'3. Send me the 8-digit code'
)
// Store state that we're expecting a code for signing
await setUserState(ctx.from!.id, { action: 'sign' })
})
// Handle code input
bot.on('message:text', async (ctx) => {
const text = ctx.message.text
// Check if it looks like an action code (8 digits)
if (!/^\d{8}$/.test(text)) {
return // Not a code, ignore or handle other messages
}
const userState = await getUserState(ctx.from!.id)
if (!userState?.action) {
await ctx.reply('Send /sign first to start a signing session.')
return
}
await ctx.reply('Verifying code...')
try {
// Resolve the code
const actionCode = await client.resolve(text)
await ctx.reply(
`Code valid!\n` +
`Wallet: ${actionCode.pubkey.slice(0, 8)}...\n\n` +
`Attaching message for you to sign...`
)
// Create and attach message
const message = `Signed via Telegram bot\nUser: @${ctx.from!.username}\nTime: ${new Date().toISOString()}`
await client.attachMessage(text, message, {
description: 'Telegram signature request'
})
await ctx.reply(
'Message attached!\n\n' +
'Now go to actioncode.app and approve the signing request.'
)
// Wait for signature
for await (const status of client.observeStatus(text, { timeout: 120000 })) {
if (status.signedMessage) {
await ctx.reply(
`Message signed!\n\n` +
`Signature: ${status.signedMessage.slice(0, 20)}...`
)
await clearUserState(ctx.from!.id)
return
}
}
// If we get here, it timed out
await ctx.reply('Code expired. Use /sign to try again.')
} catch (error: any) {
await ctx.reply(`Error: ${error.message}\n\nUse /sign to try again.`)
}
await clearUserState(ctx.from!.id)
})
// Start bot
bot.start()