Zulip bots with Eio

Add fallback channel lookup by searching channel list

When get_id fails with Bad_request (channel not found), fall back to
fetching the full channel list and searching by name. This handles
cases where the direct API lookup fails but the channel is visible
in the list. If the channel is truly not found, the error now includes
the list of available channel names for debugging.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

+28 -8
+28 -8
lib/zulip/channels.ml
··· 43 43 in 44 44 Error.decode_or_raise streams_codec json "parsing channels list" 45 45 46 + (* Search for a channel by name in the list of all channels *) 47 + let find_channel_by_name channels ~name = 48 + match List.find_opt (fun ch -> Channel.name ch = name) channels with 49 + | Some ch -> Channel.stream_id ch 50 + | None -> None 51 + 46 52 let get_id client ~name = 47 53 let response_codec = 48 54 Jsont.Object.( ··· 50 56 |> mem "stream_id" Jsont.int ~enc:Fun.id 51 57 |> finish) 52 58 in 53 - let json = 54 - Client.request client ~method_:`GET 55 - ~path:"/api/v1/get_stream_id" 56 - ~params:[("stream", name)] 57 - () 58 - in 59 - Error.decode_or_raise response_codec json 60 - (Printf.sprintf "getting stream id for %s" name) 59 + try 60 + let json = 61 + Client.request client ~method_:`GET 62 + ~path:"/api/v1/get_stream_id" 63 + ~params:[("stream", name)] 64 + () 65 + in 66 + Error.decode_or_raise response_codec json 67 + (Printf.sprintf "getting stream id for %s" name) 68 + with Eio.Io (Error.E { code = Bad_request; _ }, _) -> 69 + (* Fallback: search through channel list for exact name match *) 70 + let channels = list client in 71 + match find_channel_by_name channels ~name with 72 + | Some id -> id 73 + | None -> 74 + (* Re-raise with helpful context about available channels *) 75 + let available = List.map Channel.name channels |> String.concat ", " in 76 + Error.raise_with_context 77 + (Error.make ~code:Bad_request 78 + ~message:(Printf.sprintf "Channel '%s' not found. Available: %s" name available) 79 + ()) 80 + "getting stream id for %s" name 61 81 62 82 let get_by_id client ~stream_id = 63 83 let response_codec =