Created
July 7, 2022 12:40
-
-
Save subomi/11b06fa0558e063afdfd5e2883ece9ee to your computer and use it in GitHub Desktop.
Set up Twitter webhooks in Golang.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Generate webhook URL. | |
package main | |
import ( | |
"errors" | |
"fmt" | |
"io/ioutil" | |
"net/http" | |
"net/url" | |
"time" | |
"github.com/dghubble/oauth1" | |
log "github.com/sirupsen/logrus" | |
) | |
type webhookResource struct { | |
ID string `json:"id"` | |
URL string `json:"url"` | |
Valid bool `json:"valid"` | |
CreatedAt time.Time `json:"created_at"` | |
} | |
func main() { | |
err := configWebhookURL() | |
if err != nil { | |
log.WithError(err).Errorf("Failed to config webhooks") | |
} | |
} | |
const consumerKey = "<insert-consumer-key>" | |
const consumerSecret = "<insert-consumer-secret>" | |
const accessToken = "<insert-access-token>" | |
const accessSecret = "<insert-access-secret>" | |
const format = "https://api.twitter.com/1.1/account_activity/all/%s/webhooks.json?url=%s" | |
const environment = "staging" | |
const webhookURL = "<insert-webhook-url>" | |
func configWebhookURL() error { | |
config := oauth1.NewConfig(consumerKey, consumerSecret) | |
token := oauth1.NewToken(accessToken, accessSecret) | |
client := config.Client(oauth1.NoContext, token) | |
tURL := fmt.Sprintf(format, environment, url.QueryEscape(webhookURL)) | |
// build webhooks http request. | |
req, err := http.NewRequest("POST", tURL, nil) | |
if err != nil { | |
return err | |
} | |
resp, err := client.Do(req) | |
if err != nil { | |
return err | |
} | |
body, err := ioutil.ReadAll(resp.Body) | |
if err != nil { | |
return err | |
} | |
err = resp.Body.Close() | |
if err != nil { | |
return err | |
} | |
if resp.StatusCode != 200 { | |
fmt.Printf("Failure Body: %+v\n", string(body)) | |
return errors.New("Failed to create webhook resource") | |
} | |
// success case. | |
fmt.Printf("Success Body: %+v\n", string(body)) | |
return nil | |
} | |
// Validate CRC | |
package main | |
import ( | |
"crypto/hmac" | |
"crypto/sha256" | |
"encoding/base64" | |
"encoding/json" | |
"fmt" | |
"net/http" | |
log "github.com/sirupsen/logrus" | |
) | |
const port = 3000 | |
const consumerSecret = "<insert-consumer-secret>" | |
type response struct { | |
ResponseToken string `json:"response_token"` | |
} | |
func main() { | |
log.Info("Starting server...") | |
http.HandleFunc("/", crcHandler) | |
portString := fmt.Sprintf(":%d", port) | |
http.ListenAndServe(portString, nil) | |
} | |
func crcHandler(w http.ResponseWriter, r *http.Request) { | |
params := r.URL.Query() | |
mac := hmac.New(sha256.New, []byte(consumerSecret)) | |
mac.Write([]byte(params.Get("crc_token"))) | |
crcHash := mac.Sum(nil) | |
base64CrcHash := base64.StdEncoding.EncodeToString(crcHash) | |
resp := response{ResponseToken: fmt.Sprintf("sha256=%s", base64CrcHash)} | |
data, err := json.Marshal(resp) | |
if err != nil { | |
log.WithError(err).Error("Could not marshal") | |
} | |
fmt.Fprint(w, string(data)) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment