view webhook.go @ 35:fc1164117cb5

Revert last commit
author Lewin Bormann <lbo@spheniscida.de>
date Sat, 10 Dec 2016 13:52:56 +0100
parents d35d389fa9fb
children
line wrap: on
line source

package main

import (
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"time"
)

// Register webhook
func registerWebhook() error {
	setWebhhokRq := setWebhook{URL: *flagMyURL + "?" + *flagWebhookToken}

	body, err := json.Marshal(setWebhhokRq)

	if err != nil {
		log.Println("Couldn't serialize setWebhook request:", err)
		return err
	}

	rp, err := defaultClient.Post(buildURL(setWebhookMethod), jsonBodyType, bytes.NewBuffer(body))

	if err != nil {
		log.Println("Couldn't register webhook:", err)
		return err
	}

	if rp.StatusCode == http.StatusOK {
		return nil
	} else {
		return errors.New(fmt.Sprint("Bad return code for setWebhook:", rp.StatusCode))
	}
}

func deleteWebhook() error {
	rp, err := defaultClient.Post(buildURL(deleteWebhookMethod), jsonBodyType, bytes.NewBuffer(nil))

	if err != nil {
		return err
	}

	if rp.StatusCode == http.StatusOK {
		return nil
	} else {
		errMsg := fmt.Sprintf("Bad return code for deleteWebhook:", rp.StatusCode)
		log.Println(errMsg)
		return errors.New(errMsg)
	}
}

func getWebhookInfo(ctx context.Context) (string, error) {
	srvStatus.apiCalls++

	rq, err := http.NewRequest(http.MethodGet, buildURL(getWebhookInfoMethod), bytes.NewBuffer(nil))

	if err != nil {
		log.Println("Couldn't create request for getWebhookInfo:", err)
		return "", err
	}

	rq = rq.WithContext(ctx)

	rp, err := defaultClient.Do(rq)

	if err != nil {
		log.Println("Couldn't getWebhookInfo:", err)
		return "", err
	}

	if rp.StatusCode == http.StatusOK {
		body, err := ioutil.ReadAll(rp.Body)

		if err != nil {
			return "", err
		}

		info := webhookInfo{}
		err = json.Unmarshal(body, &info)

		if err != nil {
			log.Println("Couldn't parse getWebhookInfo result:", err)
			return "", err
		}

		return fmt.Sprintf("pending=%d last-error='%s'", info.Pending_Update_Count, info.Last_Error_Message), nil
	} else {
		srvStatus.apiErrors++
		log.Println("Couldn't getWebhookInfo:", rp.StatusCode)
		return "", errors.New("Bad response code")
	}

}

// Webhook handler (push method; /hook)
// Receives `update`s as JSON
func updatesHandler(rp http.ResponseWriter, rq *http.Request) {
	ctx, cancel := context.WithTimeout(rq.Context(), time.Duration(*flagDeadline)*time.Second)
	defer cancel()

	body, err := ioutil.ReadAll(rq.Body)

	if err != nil {
		rp.WriteHeader(400)
		log.Println("Couldn't read body for webhook request:", err)
		return
	}

	log.Println("Received update:", string(body))

	upd := update{}
	err = json.Unmarshal(body, &upd)

	if err != nil {
		rp.WriteHeader(400)
		log.Println("Couldn't deserialize webhook update:", err)
		return
	}

	if !checkAuthToken(rq) {
		rp.WriteHeader(http.StatusUnauthorized)
		return
	}

	responseMsg, err := dispatch(ctx, upd)

	if err != nil {
		rp.WriteHeader(http.StatusInternalServerError)
		log.Println("Error from dispatch; returning 500")
		return
	}

	// Empty response, e.g. for callback handlers
	if responseMsg.Chat_ID == 0 {
		rp.WriteHeader(http.StatusOK)
		return
	}

	responseMsg.Method = sendMessageMethod
	responseBody, err := json.Marshal(responseMsg)

	if err != nil {
		rp.WriteHeader(http.StatusInternalServerError)
		log.Println("Couldn't serialize webhook response:", err)
		return
	}

	log.Println("Replied:", string(responseBody))

	rp.Header().Set("Content-Type", jsonBodyType)
	rp.WriteHeader(http.StatusOK)
	rp.Write(responseBody)
}

// Checks if the supplied auth token is correct. Only we and Telegram should know it.
// The webhook URL has the format https://some.hook.com/?TOKENTOKENTOKEN
// If the token is empty, no check is done.
func checkAuthToken(rq *http.Request) bool {
	if *flagWebhookToken == "" {
		return true
	}
	if err := rq.ParseForm(); err != nil {
		log.Println("Error parsing request form:", err)
		return false
	}
	if _, ok := rq.Form[*flagWebhookToken]; !ok {
		log.Println("Bad token on webhook request!")
		return false
	}
	return true
}