init: Initial commit
This commit is contained in:
123
app/bot/auth.go
Normal file
123
app/bot/auth.go
Normal file
@@ -0,0 +1,123 @@
|
||||
package bot
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.secnex.io/secnex/masterlog"
|
||||
)
|
||||
|
||||
var AUTH *Auth
|
||||
|
||||
type Token struct {
|
||||
Data map[string]interface{}
|
||||
ExpiresIn int `json:"expires_in"`
|
||||
ExpiresAt time.Time `json:"expires_at"`
|
||||
AccessToken string `json:"access_token"`
|
||||
}
|
||||
|
||||
type Auth struct {
|
||||
TenantId string
|
||||
AppId string
|
||||
AppSecret string
|
||||
token *Token
|
||||
}
|
||||
|
||||
func NewAuth(tenantId, appId, appSecret string) *Auth {
|
||||
auth := &Auth{
|
||||
TenantId: tenantId,
|
||||
AppId: appId,
|
||||
AppSecret: appSecret,
|
||||
}
|
||||
AUTH = auth
|
||||
return auth
|
||||
}
|
||||
|
||||
func (a *Auth) getRequestUrl() string {
|
||||
return fmt.Sprintf("https://login.microsoftonline.com/%s/oauth2/v2.0/token", a.TenantId)
|
||||
}
|
||||
|
||||
func (a *Auth) requestToken() (*Token, error) {
|
||||
if a.TenantId == "" {
|
||||
return nil, fmt.Errorf("tenant ID is required but not set")
|
||||
}
|
||||
if a.AppId == "" {
|
||||
return nil, fmt.Errorf("app ID is required but not set")
|
||||
}
|
||||
if a.AppSecret == "" {
|
||||
return nil, fmt.Errorf("app secret is required but not set")
|
||||
}
|
||||
|
||||
requestUrl := a.getRequestUrl()
|
||||
masterlog.Debug("Requesting token", map[string]interface{}{
|
||||
"url": requestUrl,
|
||||
"tenantId": a.TenantId,
|
||||
"appId": a.AppId,
|
||||
})
|
||||
|
||||
body := url.Values{
|
||||
"grant_type": []string{"client_credentials"},
|
||||
"client_id": []string{a.AppId},
|
||||
"client_secret": []string{a.AppSecret},
|
||||
"scope": []string{"https://api.botframework.com/.default"},
|
||||
}
|
||||
req, err := http.NewRequest("POST", requestUrl, strings.NewReader(body.Encode()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
bodyBytes, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||
masterlog.Error("Token request failed", map[string]interface{}{
|
||||
"statusCode": resp.StatusCode,
|
||||
})
|
||||
return nil, fmt.Errorf("token request failed with status %d: %s", resp.StatusCode, string(bodyBytes))
|
||||
}
|
||||
|
||||
masterlog.Debug("Token request successful", map[string]interface{}{
|
||||
"statusCode": resp.StatusCode,
|
||||
})
|
||||
|
||||
if len(bodyBytes) == 0 {
|
||||
return nil, fmt.Errorf("empty response body from token endpoint")
|
||||
}
|
||||
|
||||
var token Token
|
||||
err = json.Unmarshal(bodyBytes, &token)
|
||||
if err != nil {
|
||||
masterlog.Error("Failed to unmarshal token response", map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
})
|
||||
return nil, fmt.Errorf("failed to unmarshal token response: %w", err)
|
||||
}
|
||||
token.ExpiresAt = time.Now().Add(time.Duration(token.ExpiresIn) * time.Second)
|
||||
masterlog.Debug("Requested token", map[string]interface{}{"accessToken": token.AccessToken, "expiresIn": token.ExpiresIn, "expiresAt": token.ExpiresAt})
|
||||
return &token, nil
|
||||
}
|
||||
|
||||
func (a *Auth) GetToken() (string, error) {
|
||||
if a.token == nil || a.token.ExpiresAt.Before(time.Now()) {
|
||||
token, err := a.requestToken()
|
||||
if err != nil {
|
||||
masterlog.Error("Failed to refresh token", map[string]interface{}{"error": err.Error()})
|
||||
return "", err
|
||||
}
|
||||
a.token = token
|
||||
masterlog.Debug("Refreshed token", map[string]interface{}{"token": a.token.AccessToken})
|
||||
}
|
||||
return a.token.AccessToken, nil
|
||||
}
|
||||
56
app/bot/conversation.go
Normal file
56
app/bot/conversation.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package bot
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"git.secnex.io/secnex/masterlog"
|
||||
"git.secnex.io/secnex/taro-bot/config"
|
||||
)
|
||||
|
||||
type Conversation struct {
|
||||
auth Auth
|
||||
}
|
||||
|
||||
func NewConversation(auth Auth) *Conversation {
|
||||
return &Conversation{
|
||||
auth: auth,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Conversation) SendMessage(message *Message) error {
|
||||
token, err := c.auth.GetToken()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
json, err := message.ToJSON()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
trafficManagerUrl := config.CONFIG.MicrosoftTeamsBotUrl
|
||||
requestUrl := fmt.Sprintf("%s/v3/conversations", trafficManagerUrl)
|
||||
req, err := http.NewRequest("POST", requestUrl, strings.NewReader(json))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
statusCode := resp.StatusCode
|
||||
masterlog.Info("Sent message via Teams Bot successfully", map[string]interface{}{"statusCode": statusCode})
|
||||
if statusCode >= 300 {
|
||||
return errors.New(string(body))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
53
app/bot/message.go
Normal file
53
app/bot/message.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package bot
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
type Message struct {
|
||||
IsGroup bool `json:"isGroup"`
|
||||
ChannelData ChannelData `json:"channelData"`
|
||||
Activity Activity `json:"activity"`
|
||||
}
|
||||
|
||||
type ChannelData struct {
|
||||
Channel Channel `json:"channel"`
|
||||
}
|
||||
|
||||
type Channel struct {
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
type Activity struct {
|
||||
Type string `json:"type"`
|
||||
Text string `json:"text"`
|
||||
}
|
||||
|
||||
func NewMessage(isGroup bool, channelData ChannelData, activity Activity) *Message {
|
||||
return &Message{
|
||||
IsGroup: isGroup,
|
||||
ChannelData: channelData,
|
||||
Activity: activity,
|
||||
}
|
||||
}
|
||||
|
||||
func NewTextPost(channelId, text string) *Message {
|
||||
return &Message{
|
||||
IsGroup: true,
|
||||
ChannelData: ChannelData{
|
||||
Channel: Channel{
|
||||
ID: channelId,
|
||||
},
|
||||
},
|
||||
Activity: Activity{
|
||||
Type: "message",
|
||||
Text: text,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Message) ToJSON() (string, error) {
|
||||
jsonBytes, err := json.Marshal(m)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(jsonBytes), nil
|
||||
}
|
||||
Reference in New Issue
Block a user