Initial open source commit
This commit is contained in:
@@ -0,0 +1,415 @@
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user