package httpserver import ( "context" "encoding/json" "net/http" "strings" "github.com/TelegramExchange/tgex-backend/tg_parser/internal/usecase" "github.com/gotd/td/tgerr" "github.com/rs/zerolog/log" ) type Server struct { httpServer *http.Server } func New(uc *usecase.UseCase, addr string) *Server { mux := http.NewServeMux() mux.HandleFunc("/fetch-telegram-channel", func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) return } username := strings.TrimSpace(r.URL.Query().Get("username")) username = strings.TrimPrefix(username, "@") if username == "" { http.Error(w, "missing username", http.StatusBadRequest) return } channel, err := uc.FetchChannelMeta(r.Context(), username) if err != nil { if isNotFoundError(err) { http.Error(w, "channel not found", http.StatusNotFound) return } log.Error().Err(err).Str("username", username).Msg("fetch channel meta failed") http.Error(w, "internal error", http.StatusInternalServerError) return } resp := struct { ID string `json:"id"` TelegramID int64 `json:"telegram_id"` Username string `json:"username"` Title string `json:"title"` AccessHash int64 `json:"access_hash"` Pts int `json:"pts"` }{ ID: channel.ID.String(), TelegramID: channel.TelegramID, Username: channel.Username, Title: channel.Title, AccessHash: channel.AccessHash, Pts: channel.Pts, } w.Header().Set("Content-Type", "application/json") if err := json.NewEncoder(w).Encode(resp); err != nil { log.Error().Err(err).Msg("encode channel response") } }) mux.HandleFunc("/resolve-channel-by-invite", func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) return } var payload struct { InviteLink string `json:"invite_link"` } if err := json.NewDecoder(r.Body).Decode(&payload); err != nil { http.Error(w, "invalid payload", http.StatusBadRequest) return } inviteLink := strings.TrimSpace(payload.InviteLink) if inviteLink == "" { http.Error(w, "missing invite_link", http.StatusBadRequest) return } channel, err := uc.FetchChannelMetaByInvite(r.Context(), inviteLink) if err != nil { if isNotFoundError(err) { http.Error(w, "channel not found", http.StatusNotFound) return } log.Error().Err(err).Msg("resolve channel by invite failed") http.Error(w, "internal error", http.StatusInternalServerError) return } resp := struct { ID string `json:"id"` TelegramID int64 `json:"telegram_id"` Username string `json:"username"` Title string `json:"title"` AccessHash int64 `json:"access_hash"` Pts int `json:"pts"` }{ ID: channel.ID.String(), TelegramID: channel.TelegramID, Username: channel.Username, Title: channel.Title, AccessHash: channel.AccessHash, Pts: channel.Pts, } w.Header().Set("Content-Type", "application/json") if err := json.NewEncoder(w).Encode(resp); err != nil { log.Error().Err(err).Msg("encode channel response") } }) return &Server{ httpServer: &http.Server{ Addr: addr, Handler: mux, }, } } func (s *Server) ListenAndServe() error { return s.httpServer.ListenAndServe() } func (s *Server) Shutdown(ctx context.Context) error { return s.httpServer.Shutdown(ctx) } func isNotFoundError(err error) bool { if tgerr.Is( err, "USERNAME_NOT_OCCUPIED", "USERNAME_INVALID", "CHANNEL_INVALID", "CHANNEL_PRIVATE", "INVITE_HASH_INVALID", "INVITE_HASH_EXPIRED", "INVITE_HASH_EMPTY", ) { return true } msg := err.Error() return strings.Contains(msg, "no chats in resolve result") || strings.Contains(msg, "not a channel") || strings.Contains(msg, "invite link") }