1010 * - Uses character.md persona if present
1111 */
1212
13- import { Telegraf } from "telegraf" ;
13+ import { Telegraf , Markup } from "telegraf" ;
1414import pc from "picocolors" ;
1515import { runChatTask , getAlpClaw , chunkText } from "./lib/chat-agent.js" ;
16+ import { readGlobalConfig , writeGlobalConfig } from "@alpclaw/config" ;
1617
1718const MAX_MSG = 3800 ;
1819
@@ -49,6 +50,64 @@ async function main() {
4950 console . log ( pc . dim ( "─" . repeat ( 60 ) ) ) ;
5051 console . log ( pc . dim ( "Waiting for messages... (Ctrl+C to stop)\n" ) ) ;
5152
53+ bot . command ( "mode" , async ( ctx ) => {
54+ try {
55+ const cfg = readGlobalConfig ( ) ;
56+ const currentMode = cfg . safety ?. mode || "permissive" ;
57+
58+ const keyboard = Markup . inlineKeyboard ( [
59+ Markup . button . callback ( currentMode === "permissive" ? "✅ Permissive" : "Permissive" , "mode:permissive" ) ,
60+ Markup . button . callback ( currentMode === "strict" ? "✅ Strict" : "Strict" , "mode:strict" ) ,
61+ ] ) ;
62+
63+ await ctx . reply ( "Select safety mode:" , keyboard ) ;
64+ } catch ( e : any ) {
65+ await ctx . reply ( "⚠️ Failed to load modes. Please try again." ) ;
66+ }
67+ } ) ;
68+
69+ bot . command ( "provider" , async ( ctx ) => {
70+ try {
71+ const alpclaw = await getAlpClaw ( ) ;
72+ const providers = alpclaw . router . listProviders ( ) ;
73+ const current = readGlobalConfig ( ) . providers ?. default || "openrouter" ;
74+
75+ const buttons = providers . map ( p => [
76+ Markup . button . callback ( current === p . name ? `✅ ${ p . name } ` : p . name , `provider:${ p . name } ` )
77+ ] ) ;
78+
79+ await ctx . reply ( "Select default provider:" , Markup . inlineKeyboard ( buttons ) ) ;
80+ } catch ( e : any ) {
81+ await ctx . reply ( "⚠️ Failed to load providers. Please try again." ) ;
82+ }
83+ } ) ;
84+
85+ bot . on ( "callback_query" , async ( ctx ) => {
86+ try {
87+ const cb = ctx . callbackQuery as any ;
88+ if ( ! cb . data ) return ;
89+
90+ const [ action , value ] = cb . data . split ( ":" ) ;
91+ const cfg = readGlobalConfig ( ) ;
92+
93+ if ( action === "mode" ) {
94+ cfg . safety = cfg . safety || { mode : "permissive" } ;
95+ cfg . safety . mode = value as "permissive" | "strict" ;
96+ writeGlobalConfig ( cfg ) ;
97+ await ctx . editMessageText ( `✅ Safety mode set to ${ value } .` ) ;
98+ } else if ( action === "provider" ) {
99+ cfg . providers = cfg . providers || { default : value , apiKeys : { } } ;
100+ cfg . providers . default = value ;
101+ writeGlobalConfig ( cfg ) ;
102+ await ctx . editMessageText ( `✅ Provider set to ${ value } .` ) ;
103+ }
104+
105+ await ctx . answerCbQuery ( ) ;
106+ } catch ( e : any ) {
107+ await ctx . answerCbQuery ( "Error updating config." ) . catch ( ( ) => null ) ;
108+ }
109+ } ) ;
110+
52111 bot . on ( "text" , async ( ctx ) => {
53112 const text = ctx . message . text ;
54113 const from = ctx . message . from ;
@@ -65,7 +124,25 @@ async function main() {
65124 } ) ;
66125
67126 const startMs = Date . now ( ) ;
68- const { reply, success } = await runChatTask ( text ) ;
127+ let reply = "" ;
128+ let success = false ;
129+ let attempts = 0 ;
130+ const maxAttempts = 2 ;
131+
132+ while ( attempts < maxAttempts ) {
133+ attempts ++ ;
134+ const res = await runChatTask ( text ) ;
135+ reply = res . reply ;
136+ success = res . success ;
137+ if ( success || attempts >= maxAttempts ) break ;
138+ console . log ( `${ ts ( ) } ${ pc . yellow ( "⚠ RETRY" ) } Attempt ${ attempts } failed, retrying...` ) ;
139+ }
140+
141+ if ( ! success ) {
142+ // Friendly error wrapper — hide stack trace from user
143+ reply = "⚠️ I encountered an internal error while processing your request. Please check the terminal logs or try again later." ;
144+ }
145+
69146 const elapsed = ( ( Date . now ( ) - startMs ) / 1000 ) . toFixed ( 1 ) ;
70147
71148 const pieces = chunkText ( reply , MAX_MSG ) ;
0 commit comments