Removed unnecessary triggers
This commit is contained in:
@@ -1,6 +0,0 @@
|
|||||||
## RUN
|
|
||||||
```bash
|
|
||||||
cd shaffuru/
|
|
||||||
dev_appserver.py --port=5001 --host=0.0.0.0 --enable_host_checking=false .
|
|
||||||
```
|
|
||||||
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
main.go
|
|
||||||
*.swo
|
|
||||||
*.swp
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
# Local testing
|
|
||||||
1. Change hook.go package to main
|
|
||||||
```bash
|
|
||||||
mv ../main.go .
|
|
||||||
go run main.go hook.go
|
|
||||||
```
|
|
||||||
|
|
||||||
# Deploy local
|
|
||||||
```bash
|
|
||||||
gcloud functions deploy webhook --runtime go111 --entry-point Authorization --trigger-http --project shuffle-241517 --memory=128 --set-env-vars=FUNCTION_APIKEY=asdasd,CALLBACKURL=shuffler.io,HOOKID=test123
|
|
||||||
```
|
|
||||||
|
|
||||||
# Build and deploy from gui
|
|
||||||
1. rm webhook.zip
|
|
||||||
2. zip webhook.zip hook.go
|
|
||||||
3. Upload to bucket https://console.cloud.google.com/storage/browser/shuffle-241517.appspot.com?project=shuffle-241517
|
|
||||||
4. Restart hook(s) (https://shuffler.io/webhooks)
|
|
||||||
@@ -1,416 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
// APPS:
|
|
||||||
// apps.dev.microsoft.com
|
|
||||||
|
|
||||||
// REMOVE ACCESS:
|
|
||||||
// https://portal.office.com/account/#
|
|
||||||
|
|
||||||
// Developer:
|
|
||||||
// https://developer.microsoft.com/en-us/graph/docs/concepts/permissions_reference
|
|
||||||
|
|
||||||
// Bots:
|
|
||||||
// https://dev.botframework.com/bots
|
|
||||||
|
|
||||||
// Connectors
|
|
||||||
// https://outlook.office.com/connectors/home/login/#/new
|
|
||||||
// https://go.microsoft.com/fwlink/?linkid=857599
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Info struct {
|
|
||||||
Url string `json:"url" datastore:"url"`
|
|
||||||
Name string `json:"name" datastore:"name"`
|
|
||||||
Description string `json:"description" datastore:"description"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Actions to be done by webhooks etc
|
|
||||||
// Field is the actual field to use from json
|
|
||||||
type HookAction struct {
|
|
||||||
Type string `json:"type" datastore:"type"`
|
|
||||||
Name string `json:"name" datastore:"name"`
|
|
||||||
Id string `json:"id" datastore:"id"`
|
|
||||||
Field string `json:"field" datastore:"field"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Hook struct {
|
|
||||||
Id string `json:"id" datastore:"id"`
|
|
||||||
Info Info `json:"info" datastore:"info"`
|
|
||||||
Actions []HookAction `json:"actions" datastore:"actions"`
|
|
||||||
Type string `json:"type" datastore:"type"`
|
|
||||||
Status string `json:"status" datastore:"status"`
|
|
||||||
Running bool `json:"running" datastore:"running"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type TeamsHook struct {
|
|
||||||
MembersAdded []struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
} `json:"membersAdded"`
|
|
||||||
Type string `json:"type"`
|
|
||||||
Timestamp time.Time `json:"timestamp"`
|
|
||||||
LocalTimestamp string `json:"localTimestamp"`
|
|
||||||
ID string `json:"id"`
|
|
||||||
ChannelID string `json:"channelId"`
|
|
||||||
ServiceURL string `json:"serviceUrl"`
|
|
||||||
From struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
} `json:"from"`
|
|
||||||
Conversation struct {
|
|
||||||
IsGroup bool `json:"isGroup"`
|
|
||||||
ConversationType string `json:"conversationType"`
|
|
||||||
ID string `json:"id"`
|
|
||||||
TenantID string `json:"tenantId"`
|
|
||||||
} `json:"conversation"`
|
|
||||||
Recipient struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
} `json:"recipient"`
|
|
||||||
ChannelData struct {
|
|
||||||
Team struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
} `json:"team"`
|
|
||||||
EventType string `json:"eventType"`
|
|
||||||
Tenant struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
} `json:"tenant"`
|
|
||||||
} `json:"channelData"`
|
|
||||||
}
|
|
||||||
|
|
||||||
var hook Hook
|
|
||||||
var baseUrl = "https://shuffler.io"
|
|
||||||
|
|
||||||
type OauthToken struct {
|
|
||||||
TokenType string `json:"token_type"`
|
|
||||||
ExpiresIn int `json:"expires_in"`
|
|
||||||
ExtExpiresIn int `json:"ext_expires_in"`
|
|
||||||
AccessToken string `json:"access_token"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type TeamsResponse struct {
|
|
||||||
Conversation struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
} `json:"conversation"`
|
|
||||||
From struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
} `json:"from"`
|
|
||||||
Recipient struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
} `json:"recipient"`
|
|
||||||
ReplyToId string `json:"replyToId"`
|
|
||||||
Type string `json:"type"`
|
|
||||||
Text string `json:"text"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// This should be in a token thingy, to be controlled in workflow
|
|
||||||
func sendRequest(token OauthToken, message TeamsHook) error {
|
|
||||||
//POST https://smba.trafficmanager.net/apis/v3/conversations/12345/activities
|
|
||||||
//Authorization: Bearer eyJhbGciOiJIUzI1Ni...
|
|
||||||
//
|
|
||||||
//(JSON-serialized Activity message goes here)
|
|
||||||
|
|
||||||
tmpData := TeamsResponse{}
|
|
||||||
tmpData.Conversation.ID = message.Conversation.ID
|
|
||||||
tmpData.From = message.Recipient
|
|
||||||
tmpData.Recipient = message.From
|
|
||||||
tmpData.ReplyToId = message.ID
|
|
||||||
tmpData.Type = "message"
|
|
||||||
tmpData.Text = "HELO"
|
|
||||||
|
|
||||||
data, err := json.Marshal(tmpData)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// /v3/conversations/{conversationId}/activities/{activityId}
|
|
||||||
fullurl := fmt.Sprintf("%sv3/conversations/%s/activities", message.ServiceURL, message.Conversation.ID)
|
|
||||||
log.Println(fullurl)
|
|
||||||
log.Println(string(data))
|
|
||||||
req, err := http.NewRequest(
|
|
||||||
http.MethodPost,
|
|
||||||
fullurl,
|
|
||||||
bytes.NewBuffer([]byte(data)),
|
|
||||||
)
|
|
||||||
|
|
||||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token.AccessToken))
|
|
||||||
req.Header.Add("Content-Type", "application/json")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
client := http.Client{}
|
|
||||||
res, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("Status: %d", res.StatusCode)
|
|
||||||
body, err := ioutil.ReadAll(res.Body)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Println(string(body))
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// If you're finding this: its from a test project :)
|
|
||||||
func get_accesstoken() (OauthToken, error) {
|
|
||||||
client_id := "9a2a2a63-c63c-4487-baf0-4ff3f4873a7f"
|
|
||||||
client_secret := ":3]D6oFimiXbuV20xH?Dzu@LR*6IFVbq"
|
|
||||||
fullurl := fmt.Sprintf("https://login.microsoftonline.com/botframework.com/oauth2/v2.0/token")
|
|
||||||
data := fmt.Sprintf("grant_type=client_credentials&client_id=%s&client_secret=%s&scope=https://api.botframework.com/.default", client_id, client_secret)
|
|
||||||
|
|
||||||
log.Println(data)
|
|
||||||
|
|
||||||
req, err := http.NewRequest(
|
|
||||||
http.MethodPost,
|
|
||||||
fullurl,
|
|
||||||
bytes.NewBuffer([]byte(data)),
|
|
||||||
)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return OauthToken{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
client := http.Client{}
|
|
||||||
res, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return OauthToken{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("Status: %d", res.StatusCode)
|
|
||||||
body, err := ioutil.ReadAll(res.Body)
|
|
||||||
if err != nil {
|
|
||||||
return OauthToken{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
token := OauthToken{}
|
|
||||||
err = json.Unmarshal(body, &token)
|
|
||||||
if err != nil {
|
|
||||||
return OauthToken{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return token, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
//func CheckTenantId(message TeamsHook) {
|
|
||||||
// fullurl := fmt.Sprintf("%s/api/v1/functions/tenants/%s", baseUrl, message.Conversation.TenantID)
|
|
||||||
// req, err := http.NewRequest(
|
|
||||||
// http.MethodPost,
|
|
||||||
// fullurl,
|
|
||||||
// bytes.NewBuffer([]byte(data)),
|
|
||||||
// )
|
|
||||||
//
|
|
||||||
// req.Header.Add("Authorization", fmt.Sprintf(`Bearer %s`, baseApikey))
|
|
||||||
// req.Header.Add("Content-Type", "application/json")
|
|
||||||
// if err != nil {
|
|
||||||
// return []string{}, err
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// client := http.Client{}
|
|
||||||
// res, err := client.Do(req)
|
|
||||||
// if err != nil {
|
|
||||||
// return []string{}, err
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// log.Printf("Status: %d", res.StatusCode)
|
|
||||||
// body, err := ioutil.ReadAll(res.Body)
|
|
||||||
// if err != nil {
|
|
||||||
// return []string{}, err
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
func Authorization(resp http.ResponseWriter, request *http.Request) {
|
|
||||||
// FIXME - don't have this here, but before loops etc
|
|
||||||
// How to keep it refreshed?
|
|
||||||
token, err := get_accesstoken()
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Failed: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
body, err := ioutil.ReadAll(request.Body)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Println("Data")
|
|
||||||
log.Println(string(body))
|
|
||||||
|
|
||||||
hook := TeamsHook{}
|
|
||||||
err = json.Unmarshal(body, &hook)
|
|
||||||
if err != nil {
|
|
||||||
resp.WriteHeader(200)
|
|
||||||
resp.Write([]byte(`{"success": false}`))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only handle messages currently
|
|
||||||
if hook.Type != "message" {
|
|
||||||
resp.WriteHeader(200)
|
|
||||||
resp.Write([]byte(`{"success": false}`))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the ORG based on the above info. How?
|
|
||||||
// MSTeams hook should have it attached somehow?
|
|
||||||
|
|
||||||
log.Printf(string(body))
|
|
||||||
//log.Printf(hook.ServiceURL)
|
|
||||||
//log.Printf(hook.ChannelID)
|
|
||||||
//log.Printf(hook.ID)
|
|
||||||
//log.Printf("%#v", hook.Conversation)
|
|
||||||
|
|
||||||
err = sendRequest(token, hook)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Failed: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
resp.WriteHeader(200)
|
|
||||||
resp.Write([]byte(fmt.Sprintf(`{"success": true}`)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadConfiguration(fullUrl string, apikey string) (Hook, error) {
|
|
||||||
client := &http.Client{}
|
|
||||||
|
|
||||||
req, err := http.NewRequest(
|
|
||||||
"GET",
|
|
||||||
fullUrl,
|
|
||||||
nil,
|
|
||||||
)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error making http request: %s", req)
|
|
||||||
return Hook{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
req.Header.Add("Authorization", fmt.Sprintf(`Bearer %s`, apikey))
|
|
||||||
req.Header.Add("Content-Type", "application/json")
|
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error in http request: %s", req)
|
|
||||||
return Hook{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
body, err := ioutil.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error reading response: %s", req)
|
|
||||||
return Hook{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = json.Unmarshal(body, &hook)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Failed unmarshaling hook API", req)
|
|
||||||
return Hook{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return hook, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetUserDetails - Get one user's details from randomuser.me API
|
|
||||||
func ForwardRequest(resp http.ResponseWriter, request *http.Request) error {
|
|
||||||
callbackUrl := os.Getenv("CALLBACKURL")
|
|
||||||
hookId := os.Getenv("HOOKID")
|
|
||||||
apikey := os.Getenv("FUNCTION_APIKEY")
|
|
||||||
|
|
||||||
hook, err := loadConfiguration(
|
|
||||||
fmt.Sprintf("%s/api/v1/hooks/%s", callbackUrl, hookId),
|
|
||||||
apikey,
|
|
||||||
)
|
|
||||||
|
|
||||||
log.Println("Done loading!")
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("%#v", hook)
|
|
||||||
|
|
||||||
// Find all things to execute
|
|
||||||
workflowUrls := []string{}
|
|
||||||
for _, item := range hook.Actions {
|
|
||||||
if item.Type == "" {
|
|
||||||
log.Printf("CONTINUE AAS EMPTY ITEM: %#v", item)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if item.Type == "workflow" {
|
|
||||||
workflowUrls = append(workflowUrls, item.Id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(workflowUrls) == 0 {
|
|
||||||
return errors.New("No actions to do yet")
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("Should send data to the following: %s", strings.Join(workflowUrls, ", "))
|
|
||||||
|
|
||||||
randomUserClient := http.Client{
|
|
||||||
Timeout: time.Second * 3,
|
|
||||||
}
|
|
||||||
|
|
||||||
body, err := ioutil.ReadAll(request.Body)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare data
|
|
||||||
type arg struct {
|
|
||||||
ExecutionArgument string `json:"execution_argument"`
|
|
||||||
}
|
|
||||||
data := arg{
|
|
||||||
ExecutionArgument: string(body),
|
|
||||||
}
|
|
||||||
|
|
||||||
newjson, err := json.Marshal(data)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loop all executions to run
|
|
||||||
for _, item := range workflowUrls {
|
|
||||||
fullUrl := fmt.Sprintf("%s/api/v1/workflows/%s/execute", callbackUrl, item)
|
|
||||||
log.Printf("Sending data to %s", fullUrl)
|
|
||||||
req, err := http.NewRequest(
|
|
||||||
http.MethodPost,
|
|
||||||
fullUrl,
|
|
||||||
bytes.NewBuffer(newjson),
|
|
||||||
)
|
|
||||||
|
|
||||||
req.Header.Add("Authorization", fmt.Sprintf(`Bearer %s`, apikey))
|
|
||||||
req.Header.Add("Content-Type", "application/json")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
res, err := randomUserClient.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("Status: %d", res.StatusCode)
|
|
||||||
body, err := ioutil.ReadAll(res.Body)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf(string(body))
|
|
||||||
}
|
|
||||||
|
|
||||||
//log.Println(string(newbody))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/gorilla/handlers"
|
|
||||||
"github.com/gorilla/mux"
|
|
||||||
)
|
|
||||||
|
|
||||||
func webhook() {
|
|
||||||
// FIXME - remove static
|
|
||||||
port := ":8080"
|
|
||||||
baseFilePath := "/"
|
|
||||||
|
|
||||||
mux := mux.NewRouter()
|
|
||||||
mux.SkipClean(true)
|
|
||||||
|
|
||||||
// FIXME - Add path for updating the hook? Can be a specific POST requeuest from backend
|
|
||||||
mux.HandleFunc(baseFilePath, Authorization).Methods("POST")
|
|
||||||
mux.HandleFunc("/test", Authorization).Methods("POST")
|
|
||||||
|
|
||||||
handlers.LoggingHandler(os.Stdout, mux)
|
|
||||||
loggedRouter := handlers.LoggingHandler(os.Stdout, mux)
|
|
||||||
|
|
||||||
log.Printf("Starting on http://localhost%s", port)
|
|
||||||
err := http.ListenAndServe(
|
|
||||||
port,
|
|
||||||
loggedRouter,
|
|
||||||
)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("ListenAndServer: ", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
webhook()
|
|
||||||
}
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.5/MicrosoftTeams.schema.json",
|
|
||||||
"manifestVersion": "1.5",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"id": "9a2a2a63-c63c-4487-baf0-4ff3f4873a7f",
|
|
||||||
"packageName": "com.example.myapp",
|
|
||||||
"devicePermissions" : [],
|
|
||||||
"developer": {
|
|
||||||
"name": "@frikkylikeme",
|
|
||||||
"websiteUrl": "https://shuffler.io/",
|
|
||||||
"privacyUrl": "https://shuffler.io/privacy",
|
|
||||||
"termsOfUseUrl": "https://shuffler.io/tos"
|
|
||||||
},
|
|
||||||
"localizationInfo": {
|
|
||||||
"defaultLanguageTag": "en-us"
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"short": "Shuffle",
|
|
||||||
"full": "Shuffle"
|
|
||||||
},
|
|
||||||
"description": {
|
|
||||||
"short": "Shuffle is a workflow automation platform",
|
|
||||||
"full": "Shuffle is a workflow automation platform. Find more info at https://shuffler.io"
|
|
||||||
},
|
|
||||||
"icons": {
|
|
||||||
"outline": "outline.png",
|
|
||||||
"color": "color.png"
|
|
||||||
},
|
|
||||||
"accentColor": "#15202b",
|
|
||||||
"bots": [
|
|
||||||
{
|
|
||||||
"botId": "9a2a2a63-c63c-4487-baf0-4ff3f4873a7f",
|
|
||||||
"needsChannelSelector": false,
|
|
||||||
"isNotificationOnly": false,
|
|
||||||
"scopes": [ "team", "personal", "groupchat" ],
|
|
||||||
"supportsFiles": false,
|
|
||||||
"commandLists": [
|
|
||||||
{
|
|
||||||
"scopes": [ "team", "groupchat", "personal" ],
|
|
||||||
"commands": [
|
|
||||||
{
|
|
||||||
"title": "test",
|
|
||||||
"description": "THIS IS FOR TESTING"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
curl -XPOST http://localhost:8080 -d '{
|
|
||||||
"membersAdded": [
|
|
||||||
{
|
|
||||||
"id": "28:f5d48856-5b42-41a0-8c3a-c5f944b679b0"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"type": "conversationUpdate",
|
|
||||||
"timestamp": "2017-02-23T19:38:35.312Z",
|
|
||||||
"localTimestamp": "2017-02-23T12:38:35.312-07:00",
|
|
||||||
"id": "f:5f85c2ad",
|
|
||||||
"channelId": "msteams",
|
|
||||||
"serviceUrl": "https://smba.trafficmanager.net/amer-client-ss.msg/",
|
|
||||||
"from": {
|
|
||||||
"id": "29:1I9Is_Sx0OIy2rQ7Xz1lcaPKlO9eqmBRTBuW6XzkFtcjqxTjPaCMij8BVMdBcL9L_RwWNJyAHFQb0TRzXgyQvA"
|
|
||||||
},
|
|
||||||
"conversation": {
|
|
||||||
"isGroup": true,
|
|
||||||
"conversationType": "channel",
|
|
||||||
"id": "19:efa9296d959346209fea44151c742e73@thread.skype"
|
|
||||||
},
|
|
||||||
"recipient": {
|
|
||||||
"id": "28:f5d48856-5b42-41a0-8c3a-c5f944b679b0",
|
|
||||||
"name": "SongsuggesterBot"
|
|
||||||
},
|
|
||||||
"channelData": {
|
|
||||||
"team": {
|
|
||||||
"id": "19:efa9296d959346209fea44151c742e73@thread.skype"
|
|
||||||
},
|
|
||||||
"eventType": "teamMemberAdded",
|
|
||||||
"tenant": {
|
|
||||||
"id": "72f988bf-86f1-41af-91ab-2d7cd011db47"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}'
|
|
||||||
#{"type":"message","id":"4oN7bHB4dit7scwHygF1pf-h|0000000","timestamp":"2019-09-06T15:21:21.9035613Z","serviceUrl":"https://webchat.botframework.com/","channelId":"webchat","from":{"id":"4ccfb6b9-5755-426e-914d-641dd74f5e0f"},"conversation":{"id":"4oN7bHB4dit7scwHygF1pf-h"},"recipient":{"id":"Shuffle@qKw6tMx9fE8","name":"Shuffler"},"textFormat":"plain","locale":"en-US","text":"hi","entities":[{"type":"ClientCapabilities","requiresBotState":true,"supportsListening":true,"supportsTts":true}],"channelData":{"clientActivityID":"15677832808420.ishosdmdfbd"}}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
main.go
|
|
||||||
*.swo
|
|
||||||
*.swp
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
# Local testing
|
|
||||||
1. Change hook.go package to main
|
|
||||||
```bash
|
|
||||||
mv ../main.go .
|
|
||||||
go run main.go hook.go
|
|
||||||
```
|
|
||||||
|
|
||||||
# Deploy local
|
|
||||||
```bash
|
|
||||||
gcloud functions deploy webhook --runtime go111 --entry-point Authorization --trigger-http --project shuffler --memory=128 --set-env-vars=FUNCTION_APIKEY=asdasd,CALLBACKURL=shuffler.io,HOOKID=test123
|
|
||||||
```
|
|
||||||
|
|
||||||
# Build and deploy from gui
|
|
||||||
1. rm webhook.zip
|
|
||||||
2. zip webhook.zip hook.go
|
|
||||||
3. Upload to bucket https://console.cloud.google.com/storage/browser/shuffler.appspot.com?project=shuffler
|
|
||||||
4. Restart hook(s) (https://shuffler.io/webhooks)
|
|
||||||
@@ -1,249 +0,0 @@
|
|||||||
package function
|
|
||||||
|
|
||||||
// BOTS
|
|
||||||
// https://dev.botframework.com/bots/channels?id=Shuffle
|
|
||||||
|
|
||||||
// APPS:
|
|
||||||
// apps.dev.microsoft.com
|
|
||||||
|
|
||||||
// REMOVE ACCESS:
|
|
||||||
// https://portal.office.com/account/#
|
|
||||||
|
|
||||||
// Developer:
|
|
||||||
// https://developer.microsoft.com/en-us/graph/docs/concepts/permissions_reference
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Info struct {
|
|
||||||
Url string `json:"url" datastore:"url"`
|
|
||||||
Name string `json:"name" datastore:"name"`
|
|
||||||
Description string `json:"description" datastore:"description"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Actions to be done by webhooks etc
|
|
||||||
// Field is the actual field to use from json
|
|
||||||
type HookAction struct {
|
|
||||||
Type string `json:"type" datastore:"type"`
|
|
||||||
Name string `json:"name" datastore:"name"`
|
|
||||||
Id string `json:"id" datastore:"id"`
|
|
||||||
Field string `json:"field" datastore:"field"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Hook struct {
|
|
||||||
Id string `json:"id" datastore:"id"`
|
|
||||||
Info Info `json:"info" datastore:"info"`
|
|
||||||
Actions []HookAction `json:"actions" datastore:"actions"`
|
|
||||||
Type string `json:"type" datastore:"type"`
|
|
||||||
Status string `json:"status" datastore:"status"`
|
|
||||||
Running bool `json:"running" datastore:"running"`
|
|
||||||
}
|
|
||||||
|
|
||||||
var hook Hook
|
|
||||||
|
|
||||||
func Authorization(resp http.ResponseWriter, request *http.Request) {
|
|
||||||
apikey := os.Getenv("FUNCTION_APIKEY")
|
|
||||||
callbackUrl := os.Getenv("CALLBACKURL")
|
|
||||||
hookId := os.Getenv("HOOKID")
|
|
||||||
if len(apikey) == 0 {
|
|
||||||
log.Println("Env FUNCTION_APIKEY not set")
|
|
||||||
resp.WriteHeader(401)
|
|
||||||
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Internal error"}`)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(callbackUrl) == 0 {
|
|
||||||
log.Println("Env CALLBACKURL not set")
|
|
||||||
resp.WriteHeader(401)
|
|
||||||
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Internal error"}`)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(hookId) == 0 {
|
|
||||||
log.Println("Env HOOKID not set")
|
|
||||||
resp.WriteHeader(401)
|
|
||||||
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Internal error"}`)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
authorization := request.Header.Get("Authorization")
|
|
||||||
if len(authorization) == 0 {
|
|
||||||
resp.WriteHeader(401)
|
|
||||||
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Authorization header required"}`)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if !strings.HasPrefix(authorization, "Bearer") {
|
|
||||||
resp.WriteHeader(401)
|
|
||||||
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Authorization header must start with Bearer"}`)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
apikeyCheck := strings.Split(authorization, " ")
|
|
||||||
if len(apikeyCheck) != 2 {
|
|
||||||
log.Println("Length is not 2 for apikey: %s vs %s", apikeyCheck[1], apikey)
|
|
||||||
resp.WriteHeader(401)
|
|
||||||
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Invalid Apikey"}`)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if apikeyCheck[1] != apikey {
|
|
||||||
log.Printf("Apikeys are not equal. Failed authentication.")
|
|
||||||
resp.WriteHeader(401)
|
|
||||||
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Invalid Apikey"}`)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err := ForwardRequest(resp, request)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error: %s", err)
|
|
||||||
resp.WriteHeader(401)
|
|
||||||
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Println("Success?")
|
|
||||||
resp.WriteHeader(200)
|
|
||||||
resp.Write([]byte(fmt.Sprintf(`{"success": true}`)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadConfiguration(fullUrl string, apikey string) (Hook, error) {
|
|
||||||
client := &http.Client{}
|
|
||||||
|
|
||||||
req, err := http.NewRequest(
|
|
||||||
"GET",
|
|
||||||
fullUrl,
|
|
||||||
nil,
|
|
||||||
)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error making http request: %s", req)
|
|
||||||
return Hook{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
req.Header.Add("Authorization", fmt.Sprintf(`Bearer %s`, apikey))
|
|
||||||
req.Header.Add("Content-Type", "application/json")
|
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error in http request: %s", req)
|
|
||||||
return Hook{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
body, err := ioutil.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error reading response: %s", req)
|
|
||||||
return Hook{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = json.Unmarshal(body, &hook)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Failed unmarshaling hook API", req)
|
|
||||||
return Hook{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return hook, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetUserDetails - Get one user's details from randomuser.me API
|
|
||||||
func ForwardRequest(resp http.ResponseWriter, request *http.Request) error {
|
|
||||||
callbackUrl := os.Getenv("CALLBACKURL")
|
|
||||||
hookId := os.Getenv("HOOKID")
|
|
||||||
apikey := os.Getenv("FUNCTION_APIKEY")
|
|
||||||
|
|
||||||
hook, err := loadConfiguration(
|
|
||||||
fmt.Sprintf("%s/api/v1/hooks/%s", callbackUrl, hookId),
|
|
||||||
apikey,
|
|
||||||
)
|
|
||||||
|
|
||||||
log.Println("Done loading!")
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("%#v", hook)
|
|
||||||
|
|
||||||
// Find all things to execute
|
|
||||||
workflowUrls := []string{}
|
|
||||||
for _, item := range hook.Actions {
|
|
||||||
if item.Type == "" {
|
|
||||||
log.Printf("CONTINUE AAS EMPTY ITEM: %#v", item)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if item.Type == "workflow" {
|
|
||||||
workflowUrls = append(workflowUrls, item.Id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(workflowUrls) == 0 {
|
|
||||||
return errors.New("No actions to do yet")
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("Should send data to the following: %s", strings.Join(workflowUrls, ", "))
|
|
||||||
|
|
||||||
randomUserClient := http.Client{
|
|
||||||
Timeout: time.Second * 3,
|
|
||||||
}
|
|
||||||
|
|
||||||
body, err := ioutil.ReadAll(request.Body)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare data
|
|
||||||
type arg struct {
|
|
||||||
ExecutionArgument string `json:"execution_argument"`
|
|
||||||
}
|
|
||||||
data := arg{
|
|
||||||
ExecutionArgument: string(body),
|
|
||||||
}
|
|
||||||
|
|
||||||
newjson, err := json.Marshal(data)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loop all executions to run
|
|
||||||
for _, item := range workflowUrls {
|
|
||||||
fullUrl := fmt.Sprintf("%s/api/v1/workflows/%s/execute", callbackUrl, item)
|
|
||||||
log.Printf("Sending data to %s", fullUrl)
|
|
||||||
req, err := http.NewRequest(
|
|
||||||
http.MethodPost,
|
|
||||||
fullUrl,
|
|
||||||
bytes.NewBuffer(newjson),
|
|
||||||
)
|
|
||||||
|
|
||||||
req.Header.Add("Authorization", fmt.Sprintf(`Bearer %s`, apikey))
|
|
||||||
req.Header.Add("Content-Type", "application/json")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
res, err := randomUserClient.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("Status: %d", res.StatusCode)
|
|
||||||
body, err := ioutil.ReadAll(res.Body)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf(string(body))
|
|
||||||
}
|
|
||||||
|
|
||||||
//log.Println(string(newbody))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/gorilla/handlers"
|
|
||||||
"github.com/gorilla/mux"
|
|
||||||
)
|
|
||||||
|
|
||||||
func webhook() {
|
|
||||||
// FIXME - remove static
|
|
||||||
port := ":8080"
|
|
||||||
baseFilePath := "/"
|
|
||||||
|
|
||||||
mux := mux.NewRouter()
|
|
||||||
mux.SkipClean(true)
|
|
||||||
|
|
||||||
// FIXME - Add path for updating the hook? Can be a specific POST requeuest from backend
|
|
||||||
mux.HandleFunc(baseFilePath, Authorization).Methods("POST")
|
|
||||||
|
|
||||||
handlers.LoggingHandler(os.Stdout, mux)
|
|
||||||
loggedRouter := handlers.LoggingHandler(os.Stdout, mux)
|
|
||||||
|
|
||||||
err := http.ListenAndServe(
|
|
||||||
port,
|
|
||||||
loggedRouter,
|
|
||||||
)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("ListenAndServer: ", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
webhook()
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user