Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose http handler #124

Merged
merged 4 commits into from
Dec 2, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 30 additions & 30 deletions ext/botmapping.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,42 +127,42 @@ func (m *botMapping) getBotFromURL(urlPath string) (botData, bool) {
return bData, ok
}

// ServeHTTP dispatches the request to the handler whose
// pattern most closely matches the request URL.
func (m *botMapping) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.RequestURI == "*" {
if r.ProtoAtLeast(1, 1) {
w.Header().Set("Connection", "close")
func (m *botMapping) getHandlerFunc(prefix string) func(writer http.ResponseWriter, request *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
if r.RequestURI == "*" {
if r.ProtoAtLeast(1, 1) {
w.Header().Set("Connection", "close")
}
w.WriteHeader(http.StatusBadRequest)
return
}
w.WriteHeader(http.StatusBadRequest)
return
}

b, ok := m.getBotFromURL(strings.TrimPrefix(r.URL.Path, "/"))
if !ok {
// If we don't recognise the URL, we return a 404.
w.WriteHeader(http.StatusNotFound)
return
}
b, ok := m.getBotFromURL(strings.TrimPrefix(r.URL.Path, prefix))
if !ok {
// If we don't recognise the URL, we return a 404.
w.WriteHeader(http.StatusNotFound)
return
}

headerSecret := r.Header.Get("X-Telegram-Bot-Api-Secret-Token")
if b.webhookSecret != "" && b.webhookSecret != headerSecret {
// Drop any updates from invalid secret tokens.
w.WriteHeader(http.StatusUnauthorized)
return
}
headerSecret := r.Header.Get("X-Telegram-Bot-Api-Secret-Token")
if b.webhookSecret != "" && b.webhookSecret != headerSecret {
// Drop any updates from invalid secret tokens.
w.WriteHeader(http.StatusUnauthorized)
return
}

bytes, err := io.ReadAll(r.Body)
if err != nil {
if m.errFunc != nil {
m.errFunc(err)
} else {
m.logf("Failed to read incoming update contents: %s", err.Error())
bytes, err := io.ReadAll(r.Body)
if err != nil {
if m.errFunc != nil {
m.errFunc(err)
} else {
m.logf("Failed to read incoming update contents: %s", err.Error())
}
w.WriteHeader(http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusInternalServerError)
return
b.updateChan <- bytes
}
b.updateChan <- bytes
}

func (m *botMapping) logf(format string, args ...interface{}) {
Expand Down
8 changes: 7 additions & 1 deletion ext/updater.go
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,12 @@ func (u *Updater) SetAllBotWebhooks(domain string, opts *gotgbot.SetWebhookOpts)
return nil
}

// GetHandlerFunc returns the http.HandlerFunc responsible for processing incoming webhook updates.
// It is provided to allow for an alternative to the StartServer method using a user-defined http server.
func (u *Updater) GetHandlerFunc(pathPrefix string) http.HandlerFunc {
return u.botMapping.getHandlerFunc(pathPrefix)
}

// StartServer starts the webhook server for all the bots added via AddWebhook.
// It is recommended to call this BEFORE calling setWebhooks.
// The opts parameter allows for specifying TLS settings.
Expand All @@ -370,7 +376,7 @@ func (u *Updater) StartServer(opts WebhookOpts) error {
}

u.webhookServer = &http.Server{
Handler: &u.botMapping,
Handler: u.GetHandlerFunc("/"),
ReadTimeout: opts.ReadTimeout,
ReadHeaderTimeout: opts.ReadHeaderTimeout,
}
Expand Down
30 changes: 26 additions & 4 deletions samples/webappBot/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ func main() {
panic("URL environment variable is empty")
}

// Get the webhook secret from the environment variable.
webhookSecret := os.Getenv("WEBHOOK_SECRET")
if webhookSecret == "" {
panic("WEBHOOK_SECRET environment variable is empty")
}

// Create our bot.
b, err := gotgbot.NewBot(token, nil)
if err != nil {
Expand All @@ -52,27 +58,43 @@ func main() {

// /start command to introduce the bot and send the URL
dispatcher.AddHandler(handlers.NewCommand("start", func(b *gotgbot.Bot, ctx *ext.Context) error {
// We can wrap commands with anonymous functions to pass in extra variables, like the webapp URL, or other
// configuration.
return start(b, ctx, webappURL)
}))

// Start receiving (and handling) updates.
err = updater.StartPolling(b, &ext.PollingOpts{DropPendingUpdates: true})
// We add the bot webhook to our updater, such that we can populate the updater's http.Handler.
err = updater.AddWebhook(b, b.Token, ext.WebhookOpts{SecretToken: webhookSecret})
if err != nil {
panic("failed to start polling: " + err.Error())
panic("Failed to add bot webhooks to updater: " + err.Error())
}

// We select a subpath to specify where the updater handler is found on the http.Server.
updaterSubpath := "/bots/"
err = updater.SetAllBotWebhooks(webappURL+updaterSubpath, &gotgbot.SetWebhookOpts{
MaxConnections: 100,
DropPendingUpdates: true,
SecretToken: webhookSecret,
})
if err != nil {
panic("Failed to set bot webhooks: " + err.Error())
}
log.Printf("%s has been started...\n", b.User.Username)

// Setup new HTTP server mux to handle different paths.
mux := http.NewServeMux()
// This serves the home page.
mux.HandleFunc("/", index(webappURL))
// This serves our "validation" API, which checks if the input data is valid.
mux.HandleFunc("/validate", validate(token))
// This serves the updater's webhook handler.
mux.HandleFunc(updaterSubpath, updater.GetHandlerFunc(updaterSubpath))

server := http.Server{
Handler: mux,
Addr: "0.0.0.0:8080",
}

log.Printf("%s has been started...\n", b.User.Username)
// Start the webserver displaying the page.
// Note: ListenAndServe is a blocking operation, so we don't need to call updater.Idle() here.
if err := server.ListenAndServe(); err != nil {
Expand Down
Loading