@@ -137,22 +137,10 @@ async fn cmd_channel_setup(channel_name: &str) -> Result<()> {
137137
138138 match channel_name {
139139 "whatsapp_web" => setup_whatsapp_web ( & mut config) ?,
140- "telegram" => {
141- println ! ( "Use 'zeptoclaw onboard' to configure Telegram." ) ;
142- return Ok ( ( ) ) ;
143- }
144- "discord" => {
145- println ! ( "Use 'zeptoclaw onboard' to configure Discord." ) ;
146- return Ok ( ( ) ) ;
147- }
148- "slack" => {
149- println ! ( "Use 'zeptoclaw onboard' to configure Slack." ) ;
150- return Ok ( ( ) ) ;
151- }
152- "webhook" => {
153- println ! ( "Use 'zeptoclaw onboard' to configure Webhook." ) ;
154- return Ok ( ( ) ) ;
155- }
140+ "telegram" => setup_telegram ( & mut config) ?,
141+ "discord" => setup_discord ( & mut config) ?,
142+ "slack" => setup_slack ( & mut config) ?,
143+ "webhook" => setup_webhook ( & mut config) ?,
156144 _ => unreachable ! ( ) ,
157145 }
158146
@@ -209,6 +197,163 @@ fn setup_whatsapp_web(config: &mut Config) -> Result<()> {
209197 Ok ( ( ) )
210198}
211199
200+ /// Interactive Telegram channel setup.
201+ fn setup_telegram ( config : & mut Config ) -> Result < ( ) > {
202+ println ! ( ) ;
203+ println ! ( "Telegram Bot Setup" ) ;
204+ println ! ( "------------------" ) ;
205+ println ! ( "To create a bot: Open Telegram, message @BotFather, send /newbot" ) ;
206+ println ! ( ) ;
207+ print ! ( "Enter Telegram bot token (or press Enter to skip): " ) ;
208+ io:: stdout ( ) . flush ( ) ?;
209+
210+ let token = read_line ( ) ?;
211+ if token. is_empty ( ) {
212+ println ! ( " Skipped." ) ;
213+ return Ok ( ( ) ) ;
214+ }
215+
216+ let tg = config
217+ . channels
218+ . telegram
219+ . get_or_insert_with ( Default :: default) ;
220+ tg. token = token;
221+ tg. enabled = true ;
222+
223+ print ! ( "Allowlist user IDs/usernames (comma-separated, or Enter for all): " ) ;
224+ io:: stdout ( ) . flush ( ) ?;
225+ let allowlist = read_line ( ) ?;
226+ if !allowlist. is_empty ( ) {
227+ tg. allow_from = allowlist
228+ . split ( ',' )
229+ . map ( |s| s. trim ( ) . to_string ( ) )
230+ . filter ( |s| !s. is_empty ( ) )
231+ . collect ( ) ;
232+ }
233+
234+ println ! ( " Telegram bot configured." ) ;
235+ println ! ( " Run 'zeptoclaw gateway' to start the bot." ) ;
236+ Ok ( ( ) )
237+ }
238+
239+ /// Interactive Discord channel setup.
240+ fn setup_discord ( config : & mut Config ) -> Result < ( ) > {
241+ println ! ( ) ;
242+ println ! ( "Discord Bot Setup" ) ;
243+ println ! ( "-----------------" ) ;
244+ println ! ( "To create a bot:" ) ;
245+ println ! ( " 1. Go to https://discord.com/developers/applications" ) ;
246+ println ! ( " 2. Create New Application → Bot → Reset Token → copy it" ) ;
247+ println ! ( " 3. Enable MESSAGE CONTENT intent under Bot → Privileged Intents" ) ;
248+ println ! ( " 4. Invite bot to your server with OAuth2 URL Generator" ) ;
249+ println ! ( ) ;
250+ print ! ( "Enter Discord bot token (or press Enter to skip): " ) ;
251+ io:: stdout ( ) . flush ( ) ?;
252+
253+ let token = read_line ( ) ?;
254+ if token. is_empty ( ) {
255+ println ! ( " Skipped." ) ;
256+ return Ok ( ( ) ) ;
257+ }
258+
259+ let dc = config. channels . discord . get_or_insert_with ( Default :: default) ;
260+ dc. token = token;
261+ dc. enabled = true ;
262+
263+ print ! ( "Allowlist user IDs (comma-separated, or Enter for all): " ) ;
264+ io:: stdout ( ) . flush ( ) ?;
265+ let allowlist = read_line ( ) ?;
266+ if !allowlist. is_empty ( ) {
267+ dc. allow_from = allowlist
268+ . split ( ',' )
269+ . map ( |s| s. trim ( ) . to_string ( ) )
270+ . filter ( |s| !s. is_empty ( ) )
271+ . collect ( ) ;
272+ }
273+
274+ println ! ( " Discord bot configured." ) ;
275+ println ! ( " Run 'zeptoclaw gateway' to start the bot." ) ;
276+ Ok ( ( ) )
277+ }
278+
279+ /// Interactive Slack channel setup.
280+ fn setup_slack ( config : & mut Config ) -> Result < ( ) > {
281+ println ! ( ) ;
282+ println ! ( "Slack Bot Setup" ) ;
283+ println ! ( "---------------" ) ;
284+ println ! ( "To create a bot:" ) ;
285+ println ! ( " 1. Go to https://api.slack.com/apps → Create New App" ) ;
286+ println ! ( " 2. Add Bot Token Scopes: chat:write, app_mentions:read" ) ;
287+ println ! ( " 3. Install to Workspace → copy Bot User OAuth Token (xoxb-...)" ) ;
288+ println ! ( " 4. Generate App-Level Token with connections:write scope" ) ;
289+ println ! ( ) ;
290+ print ! ( "Enter Slack bot token (xoxb-..., or press Enter to skip): " ) ;
291+ io:: stdout ( ) . flush ( ) ?;
292+
293+ let bot_token = read_line ( ) ?;
294+ if bot_token. is_empty ( ) {
295+ println ! ( " Skipped." ) ;
296+ return Ok ( ( ) ) ;
297+ }
298+
299+ print ! ( "Enter Slack app-level token (xapp-...): " ) ;
300+ io:: stdout ( ) . flush ( ) ?;
301+ let app_token = read_line ( ) ?;
302+
303+ let sl = config. channels . slack . get_or_insert_with ( Default :: default) ;
304+ sl. bot_token = bot_token;
305+ sl. app_token = app_token;
306+ sl. enabled = true ;
307+
308+ println ! ( " Slack bot configured." ) ;
309+ println ! ( " Run 'zeptoclaw gateway' to start the bot." ) ;
310+ Ok ( ( ) )
311+ }
312+
313+ /// Interactive Webhook channel setup.
314+ fn setup_webhook ( config : & mut Config ) -> Result < ( ) > {
315+ println ! ( ) ;
316+ println ! ( "Webhook Channel Setup" ) ;
317+ println ! ( "---------------------" ) ;
318+ println ! ( "Receives messages via HTTP POST to a local endpoint." ) ;
319+ println ! ( ) ;
320+
321+ let wh = config. channels . webhook . get_or_insert_with ( Default :: default) ;
322+
323+ print ! ( "Bind address [{}]: " , wh. bind_address) ;
324+ io:: stdout ( ) . flush ( ) ?;
325+ let bind = read_line ( ) ?;
326+ if !bind. is_empty ( ) {
327+ wh. bind_address = bind;
328+ }
329+
330+ print ! ( "Port [{}]: " , wh. port) ;
331+ io:: stdout ( ) . flush ( ) ?;
332+ let port_str = read_line ( ) ?;
333+ if !port_str. is_empty ( ) {
334+ if let Ok ( p) = port_str. parse :: < u16 > ( ) {
335+ wh. port = p;
336+ } else {
337+ println ! ( " Invalid port, keeping default {}." , wh. port) ;
338+ }
339+ }
340+
341+ print ! ( "Bearer auth token (or Enter for none): " ) ;
342+ io:: stdout ( ) . flush ( ) ?;
343+ let auth = read_line ( ) ?;
344+ if !auth. is_empty ( ) {
345+ wh. auth_token = Some ( auth) ;
346+ }
347+
348+ wh. enabled = true ;
349+ println ! (
350+ " Webhook configured at {}:{}{}" ,
351+ wh. bind_address, wh. port, wh. path
352+ ) ;
353+ println ! ( " Run 'zeptoclaw gateway' to start listening." ) ;
354+ Ok ( ( ) )
355+ }
356+
212357// ---------------------------------------------------------------------------
213358// channel test
214359// ---------------------------------------------------------------------------
0 commit comments