@@ -27,6 +27,14 @@ const (
2727
2828// ── TelegramApprover ───────────────────────────────────────────────────────
2929
30+ // pendingRequest holds the response channel and the message ID for a
31+ // pending approval request, so HandleCallback can edit the message
32+ // text and remove the inline keyboard after the user responds.
33+ type pendingRequest struct {
34+ resp chan string
35+ messageID int
36+ }
37+
3038// TelegramApprover implements danger.Approver by sending approval requests
3139// via Telegram inline keyboards. The agent loop calls PromptCommand which:
3240//
@@ -41,7 +49,7 @@ const (
4149// Thread-safe: PromptCommand and HandleCallback are safe to call concurrently.
4250type TelegramApprover struct {
4351 bot * Bot
44- pending map [string ]chan string // requestID -> response channel
52+ pending map [string ]* pendingRequest // requestID -> pending request
4553 mu sync.Mutex
4654 trusted map [danger.RiskClass ]bool
4755 trustAll bool // when true, all PromptCommand calls auto-approve
@@ -57,7 +65,7 @@ func NewTelegramApprover(bot *Bot, chatID int64) *TelegramApprover {
5765 return & TelegramApprover {
5866 bot : bot ,
5967 ChatID : chatID ,
60- pending : make (map [string ]chan string ),
68+ pending : make (map [string ]* pendingRequest ),
6169 trusted : make (map [danger.RiskClass ]bool ),
6270 log : NewNopLogger (),
6371 cancel : make (chan struct {}),
@@ -129,18 +137,18 @@ func (a *TelegramApprover) PromptCommand(cls danger.RiskClass, cmd, description
129137 },
130138 }
131139
132- _ , err := a .bot .SendMessage (a .ChatID , text , & SendOpts {
140+ msg , err := a .bot .SendMessage (a .ChatID , text , & SendOpts {
133141 ParseMode : ParseModeMarkdownV2 ,
134142 ReplyMarkup : & markup ,
135143 })
136144 if err != nil {
137145 return fmt .Errorf ("telegram approver: send prompt: %w" , err )
138146 }
139147
140- // Register the pending request.
141- resp := make (chan string , 1 )
148+ // Register the pending request with message ID .
149+ pr := & pendingRequest { resp : make (chan string , 1 ), messageID : msg . ID }
142150 a .mu .Lock ()
143- a .pending [id ] = resp
151+ a .pending [id ] = pr
144152 a .mu .Unlock ()
145153
146154 defer func () {
@@ -151,20 +159,31 @@ func (a *TelegramApprover) PromptCommand(cls danger.RiskClass, cmd, description
151159
152160 // Wait for response, cancellation, or timeout.
153161 select {
154- case action := <- resp :
162+ case action := <- pr .resp :
163+ // Edit the approval message to remove buttons and show the user's decision.
164+ answerText := ""
165+ switch action {
166+ case "approve" :
167+ answerText = "✅ *Approved*"
168+ case "trust" :
169+ answerText = fmt .Sprintf ("🔒 *Trusted:* `%s`" , cls )
170+ case "deny" :
171+ answerText = "❌ *Denied*"
172+ default :
173+ answerText = "⏭ *Skipped*"
174+ }
175+ if answerText != "" {
176+ a .bot .EditMessageText (a .ChatID , pr .messageID , answerText ,
177+ & SendOpts {ParseMode : ParseModeMarkdownV2 , ReplyMarkup : & InlineKeyboardMarkup {InlineKeyboard : [][]InlineKeyboardButton {}}})
178+ }
179+
155180 switch action {
156181 case "approve" :
157182 return nil
158183 case "trust" :
159184 a .mu .Lock ()
160185 a .trusted [cls ] = true
161186 a .mu .Unlock ()
162- // Confirm trust to the user
163- if _ , err := a .bot .SendMessage (a .ChatID ,
164- fmt .Sprintf ("🔒 Class `%s` trusted for this session." , cls ),
165- & SendOpts {ParseMode : ParseModeMarkdownV2 }); err != nil {
166- a .log .Error ("confirm trust message failed" , "chat_id" , a .ChatID , "error" , err )
167- }
168187 return nil
169188 case "deny" :
170189 return fmt .Errorf ("operation denied by user: %s" , cmd )
@@ -208,11 +227,11 @@ func (a *TelegramApprover) HandleCallback(data string) bool {
208227 }
209228
210229 a .mu .Lock ()
211- resp , ok := a .pending [id ]
230+ pr , ok := a .pending [id ]
212231 a .mu .Unlock ()
213232
214233 if ok {
215- resp <- action
234+ pr . resp <- action
216235 }
217236
218237 return true
0 commit comments