go-ayo/common을 gocommon으로 분리
This commit is contained in:
456
azure/bot.go
Normal file
456
azure/bot.go
Normal file
@ -0,0 +1,456 @@
|
||||
package azure
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ChannelAccount struct {
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name,omitempty"`
|
||||
AadObjectId string `json:"aadObjectId,omitempty"`
|
||||
Role string `json:"role,omitempty"`
|
||||
}
|
||||
|
||||
type AttachmentData struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
OriginalBase64 string `json:"originalBase64,omitempty"`
|
||||
ThumbnailBase64 string `json:"thumbnailBase64,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
}
|
||||
|
||||
type MessageActivity struct {
|
||||
Type string `json:"type,omitempty"`
|
||||
Timestamp time.Time `json:"timestamp,omitempty"`
|
||||
LocalTimestamp time.Time `json:"localTimestamp,omitempty"`
|
||||
Id string `json:"id"`
|
||||
ChannelId string `json:"channelId,omitempty"`
|
||||
ServiceUrl string `json:"serviceUrl,omitempty"`
|
||||
Message string
|
||||
Mentions []string
|
||||
From ChannelAccount `json:"from,omitempty"`
|
||||
Conversation *struct {
|
||||
ConversationType string `json:"conversationType,omitempty"`
|
||||
TenantId string `json:"tenantId,omitempty"`
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
} `json:"conversation,omitempty"`
|
||||
Recipient *struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Id string `json:"id"`
|
||||
} `json:"recipient,omitempty"`
|
||||
Entities []interface{} `json:"entities,omitempty"`
|
||||
ChannelData *struct {
|
||||
TeamsChannelId string `json:"teamsChannelId,omitempty"`
|
||||
TeamsTeamId string `json:"teamsTeamId,omitempty"`
|
||||
Channel *struct {
|
||||
Id string `json:"id"`
|
||||
} `json:"channel,omitempty"`
|
||||
Team *struct {
|
||||
Id string `json:"id"`
|
||||
} `json:"team,omitempty"`
|
||||
Tenant *struct {
|
||||
Id string `json:"id,omitempty"`
|
||||
} `json:"tenant,omitempty"`
|
||||
} `json:"channelData,omitempty"`
|
||||
ReplyToId string `json:"replyToId,omitempty"`
|
||||
Value map[string]string `json:"value,omitempty"`
|
||||
Locale string `json:"Locale,omitempty"`
|
||||
LocalTimezone string `json:"LocalTimezone,omitempty"`
|
||||
}
|
||||
|
||||
type AdaptiveCard struct {
|
||||
Body []interface{}
|
||||
Actions []interface{}
|
||||
}
|
||||
|
||||
type tempMessageActivity struct {
|
||||
MessageActivity
|
||||
RawText string `json:"text,omitempty"`
|
||||
}
|
||||
|
||||
var botFrameworkAuth = accessToken{}
|
||||
|
||||
func ParseMessageActivity(src []byte) (MessageActivity, error) {
|
||||
var message tempMessageActivity
|
||||
err := json.Unmarshal(src, &message)
|
||||
if err != nil {
|
||||
return MessageActivity{}, err
|
||||
}
|
||||
|
||||
for len(message.RawText) > 0 {
|
||||
s := strings.Index(message.RawText, "<at>")
|
||||
if s < 0 {
|
||||
break
|
||||
}
|
||||
message.Message += message.RawText[:s]
|
||||
message.RawText = message.RawText[s+4:]
|
||||
e := strings.Index(message.RawText, "</at>")
|
||||
if e < 0 {
|
||||
break
|
||||
}
|
||||
mention := message.RawText[:e]
|
||||
message.RawText = message.RawText[e+5:]
|
||||
message.Mentions = append(message.Mentions, mention)
|
||||
}
|
||||
message.Message += strings.Trim(message.RawText, " ")
|
||||
message.Message = strings.TrimSpace(message.Message)
|
||||
if !strings.HasSuffix(message.ServiceUrl, "/") {
|
||||
message.ServiceUrl += "/"
|
||||
}
|
||||
return message.MessageActivity, nil
|
||||
}
|
||||
|
||||
func sendRequireMessage(requrl string, method string, stream io.Reader) (string, error) {
|
||||
req, err := http.NewRequest(method, requrl, stream)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
auth, err := botFrameworkAuth.getAuthoizationToken()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
req.Header.Set("Authorization", auth)
|
||||
req.Header.Set("Content-type", "application/json")
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
bt, _ := io.ReadAll(resp.Body)
|
||||
var resultdoc map[string]interface{}
|
||||
json.Unmarshal(bt, &resultdoc)
|
||||
if len(resultdoc) > 0 {
|
||||
if idraw, ok := resultdoc["id"]; ok {
|
||||
return idraw.(string), nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", err
|
||||
}
|
||||
|
||||
func putMessage(requrl string, stream io.Reader) (string, error) {
|
||||
return sendRequireMessage(requrl, "PUT", stream)
|
||||
}
|
||||
|
||||
func postMessage(requrl string, stream io.Reader) (string, error) {
|
||||
return sendRequireMessage(requrl, "POST", stream)
|
||||
}
|
||||
|
||||
func unescapeUnicodeCharactersInJSON(src []byte) (string, error) {
|
||||
str, err := strconv.Unquote(strings.Replace(strconv.Quote(string(src)), `\\u`, `\u`, -1))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return str, nil
|
||||
}
|
||||
|
||||
func (m *MessageActivity) SendStreamOnChannel(reader io.Reader) (string, error) {
|
||||
serviceUrl := m.ServiceUrl
|
||||
|
||||
// mention 없이 여기 왔으면 private 채팅이다. reply를 해야함
|
||||
var conversationId string
|
||||
if m.ChannelData.Channel == nil {
|
||||
conversationId = m.Conversation.Id
|
||||
} else {
|
||||
conversationId = m.ChannelData.Channel.Id
|
||||
}
|
||||
|
||||
requrl := fmt.Sprintf("%sv3/conversations/%s/activities", serviceUrl, conversationId)
|
||||
|
||||
return postMessage(requrl, reader)
|
||||
}
|
||||
|
||||
func (m *MessageActivity) UploadFile(attachment AttachmentData) (string, error) {
|
||||
// POST /v3/conversations/{conversationId}/attachments
|
||||
// Request body An AttachmentData object.
|
||||
// Returns A ResourceResponse object. The id property specifies the attachment ID that can be used with the Get Attachment Info operation and the Get Attachment operation.
|
||||
serviceUrl := m.ServiceUrl
|
||||
conversationId := m.Conversation.Id
|
||||
bt, _ := json.Marshal(attachment)
|
||||
|
||||
requrl := fmt.Sprintf("%sv3/conversations/%s/attachments", serviceUrl, conversationId)
|
||||
return postMessage(requrl, bytes.NewReader([]byte(bt)))
|
||||
}
|
||||
|
||||
func makeAttachmentDocs(attachments []AdaptiveCard) []interface{} {
|
||||
var attachconv []interface{}
|
||||
for _, attachment := range attachments {
|
||||
attachconv = append(attachconv, map[string]interface{}{
|
||||
"contentType": "application/vnd.microsoft.card.adaptive",
|
||||
"content": map[string]interface{}{
|
||||
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
|
||||
"type": "AdaptiveCard",
|
||||
"version": "1.3",
|
||||
"body": attachment.Body,
|
||||
},
|
||||
})
|
||||
}
|
||||
return attachconv
|
||||
}
|
||||
|
||||
// func (m *MessageActivity) update(activityId string, message string, attachments ...AdaptiveCard) (string, error) {
|
||||
// serviceUrl := m.ServiceUrl
|
||||
// conversationId := m.Conversation.Id
|
||||
|
||||
// attachconv := makeAttachmentDocs(attachments)
|
||||
// activity := map[string]interface{}{
|
||||
// "type": "message",
|
||||
// "text": strings.ReplaceAll(message, `"`, `\"`),
|
||||
// }
|
||||
// if len(attachconv) > 0 {
|
||||
// activity["attachments"] = attachconv
|
||||
// }
|
||||
|
||||
// reqdoc, _ := json.Marshal(activity)
|
||||
// unq, _ := unescapeUnicodeCharactersInJSON(reqdoc)
|
||||
|
||||
// requrl := fmt.Sprintf("%sv3/conversations/%s/activities/%s", serviceUrl, conversationId, activityId)
|
||||
// return putMessage(requrl, bytes.NewReader([]byte(unq)))
|
||||
// }
|
||||
|
||||
// func (m *MessageActivity) reply(message string, mention bool, attachments ...AdaptiveCard) (string, error) {
|
||||
// serviceUrl := m.ServiceUrl
|
||||
// conversationId := m.Conversation.Id
|
||||
|
||||
// attachconv := makeAttachmentDocs(attachments)
|
||||
|
||||
// var activity map[string]interface{}
|
||||
// if mention {
|
||||
// mentionText := fmt.Sprintf(`<at>%s</at>`, m.From.Name)
|
||||
// activity = map[string]interface{}{
|
||||
// "type": "message",
|
||||
// "text": mentionText + " " + strings.ReplaceAll(message, `"`, `\"`),
|
||||
// "entities": []interface{}{
|
||||
// map[string]interface{}{
|
||||
// "mentioned": m.From,
|
||||
// "text": mentionText,
|
||||
// "type": "mention",
|
||||
// },
|
||||
// },
|
||||
// }
|
||||
// } else {
|
||||
// activity = map[string]interface{}{
|
||||
// "type": "message",
|
||||
// "text": strings.ReplaceAll(message, `"`, `\"`),
|
||||
// }
|
||||
// }
|
||||
|
||||
// activity["replyToId"] = m.Id
|
||||
// if len(attachconv) > 0 {
|
||||
// activity["attachments"] = attachconv
|
||||
// }
|
||||
|
||||
// reqdoc, _ := json.Marshal(activity)
|
||||
// unq, _ := unescapeUnicodeCharactersInJSON(reqdoc)
|
||||
|
||||
// requrl := fmt.Sprintf("%sv3/conversations/%s/activities", serviceUrl, conversationId)
|
||||
// return postMessage(requrl, bytes.NewReader([]byte(unq)))
|
||||
// }
|
||||
|
||||
// func (m *MessageActivity) ReplyWithMentionf(format string, val ...interface{}) (string, error) {
|
||||
// return m.ReplyWithMention(fmt.Sprintf(format, val...))
|
||||
// }
|
||||
|
||||
// func (m *MessageActivity) ReplyWithMention(message string, attachments ...AdaptiveCard) (string, error) {
|
||||
// return m.reply(message, true, attachments...)
|
||||
// }
|
||||
|
||||
// func (m *MessageActivity) Reply(message string, attachments ...AdaptiveCard) (string, error) {
|
||||
// return m.reply(message, false, attachments...)
|
||||
// }
|
||||
|
||||
// func (m *MessageActivity) Replyf(format string, val ...interface{}) (string, error) {
|
||||
// return m.Reply(fmt.Sprintf(format, val...))
|
||||
// }
|
||||
|
||||
// func (m *MessageActivity) Update(activityId string, message string, attachments ...AdaptiveCard) (string, error) {
|
||||
// return m.update(activityId, message, attachments...)
|
||||
// }
|
||||
|
||||
// func (m *MessageActivity) Updatef(activityId string, format string, val ...interface{}) (string, error) {
|
||||
// return m.Update(activityId, fmt.Sprintf(format, val...))
|
||||
// }
|
||||
|
||||
type MessageWrap struct {
|
||||
*MessageActivity
|
||||
Prefix string
|
||||
Mention bool
|
||||
Attachments []AdaptiveCard
|
||||
}
|
||||
|
||||
func (m *MessageActivity) MakeWrap() *MessageWrap {
|
||||
return &MessageWrap{MessageActivity: m}
|
||||
}
|
||||
|
||||
func (m *MessageWrap) WithPrefix(prefix string) *MessageWrap {
|
||||
m.Prefix = prefix
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *MessageWrap) WithMention() *MessageWrap {
|
||||
m.Mention = true
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *MessageWrap) WithAttachments(attachments ...AdaptiveCard) *MessageWrap {
|
||||
m.Attachments = attachments
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *MessageWrap) Reply(text string) (string, error) {
|
||||
serviceUrl := m.ServiceUrl
|
||||
conversationId := m.Conversation.Id
|
||||
attachconv := makeAttachmentDocs(m.Attachments)
|
||||
|
||||
var activity map[string]interface{}
|
||||
if m.Mention {
|
||||
var mentionText string
|
||||
if len(m.Prefix) > 0 {
|
||||
mentionText = fmt.Sprintf(`**[%s]** <at>%s</at>`, m.Prefix, m.From.Name)
|
||||
} else {
|
||||
mentionText = fmt.Sprintf(`<at>%s</at>`, m.From.Name)
|
||||
}
|
||||
activity = map[string]interface{}{
|
||||
"type": "message",
|
||||
"text": mentionText + " " + strings.ReplaceAll(text, `"`, `\"`),
|
||||
"entities": []interface{}{
|
||||
map[string]interface{}{
|
||||
"mentioned": m.From,
|
||||
"text": mentionText,
|
||||
"type": "mention",
|
||||
},
|
||||
},
|
||||
}
|
||||
} else {
|
||||
if len(m.Prefix) > 0 {
|
||||
text = fmt.Sprintf("**[%s]** %s", m.Prefix, text)
|
||||
}
|
||||
|
||||
activity = map[string]interface{}{
|
||||
"type": "message",
|
||||
"text": strings.ReplaceAll(text, `"`, `\"`),
|
||||
}
|
||||
}
|
||||
|
||||
activity["replyToId"] = m.Id
|
||||
if len(attachconv) > 0 {
|
||||
activity["attachments"] = attachconv
|
||||
}
|
||||
|
||||
activity["from"] = m.Recipient
|
||||
activity["channelId"] = m.ChannelId
|
||||
reqdoc, _ := json.Marshal(activity)
|
||||
unq, _ := unescapeUnicodeCharactersInJSON(reqdoc)
|
||||
|
||||
requrl := fmt.Sprintf("%sv3/conversations/%s/activities", serviceUrl, conversationId)
|
||||
return postMessage(requrl, bytes.NewReader([]byte(unq)))
|
||||
}
|
||||
|
||||
func (m *MessageWrap) Replyf(format string, args ...interface{}) (string, error) {
|
||||
return m.Reply(fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
func (m *MessageActivity) Update(reader io.Reader) {
|
||||
serviceUrl := m.ServiceUrl
|
||||
conversationId := m.Conversation.Id
|
||||
activityId := m.ReplyToId
|
||||
|
||||
requrl := fmt.Sprintf("%sv3/conversations/%s/activities/%s", serviceUrl, conversationId, activityId)
|
||||
putMessage(requrl, reader)
|
||||
}
|
||||
|
||||
func (m *MessageWrap) Update(activityId string, text string) (string, error) {
|
||||
serviceUrl := m.ServiceUrl
|
||||
conversationId := m.Conversation.Id
|
||||
if len(m.Prefix) > 0 {
|
||||
text = fmt.Sprintf("**[%s]** %s", m.Prefix, text)
|
||||
}
|
||||
attachconv := makeAttachmentDocs(m.Attachments)
|
||||
activity := map[string]interface{}{
|
||||
"type": "message",
|
||||
"text": strings.ReplaceAll(text, `"`, `\"`),
|
||||
}
|
||||
if len(attachconv) > 0 {
|
||||
activity["attachments"] = attachconv
|
||||
}
|
||||
|
||||
reqdoc, _ := json.Marshal(activity)
|
||||
unq, _ := unescapeUnicodeCharactersInJSON(reqdoc)
|
||||
|
||||
requrl := fmt.Sprintf("%sv3/conversations/%s/activities/%s", serviceUrl, conversationId, activityId)
|
||||
return putMessage(requrl, bytes.NewReader([]byte(unq)))
|
||||
}
|
||||
|
||||
func (m *MessageWrap) Updatef(activityId string, format string, args ...interface{}) (string, error) {
|
||||
return m.Update(activityId, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
type MessageReplyCache struct {
|
||||
sourceActivity *MessageActivity
|
||||
ReplyWrap *MessageWrap
|
||||
Replyaid string
|
||||
}
|
||||
|
||||
func (c *MessageReplyCache) Serialize() string {
|
||||
bt, err := json.Marshal(c)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
linkfilename := fmt.Sprintf("updatelink_%s.json", time.Now().Format("2006-01-02T15-04-05"))
|
||||
if os.WriteFile(linkfilename, bt, 0666) == nil {
|
||||
return linkfilename
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func DeserializeMessageReplyCache(filename string) *MessageReplyCache {
|
||||
bt, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var out MessageReplyCache
|
||||
if json.Unmarshal(bt, &out) == nil {
|
||||
return &out
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func MakeMessageReplyCache(src *MessageActivity) MessageReplyCache {
|
||||
return MessageReplyCache{
|
||||
sourceActivity: src,
|
||||
}
|
||||
}
|
||||
|
||||
func (ch *MessageReplyCache) SourceActivity() *MessageActivity {
|
||||
return ch.sourceActivity
|
||||
}
|
||||
|
||||
func (ch *MessageReplyCache) Reply(text string) {
|
||||
if ch.ReplyWrap == nil {
|
||||
hostname, _ := os.Hostname()
|
||||
ch.ReplyWrap = ch.sourceActivity.MakeWrap().WithPrefix(hostname)
|
||||
}
|
||||
|
||||
if len(ch.Replyaid) == 0 {
|
||||
ch.Replyaid, _ = ch.ReplyWrap.Reply(text)
|
||||
} else {
|
||||
ch.ReplyWrap.Update(ch.Replyaid, text)
|
||||
}
|
||||
}
|
||||
|
||||
func (ch *MessageReplyCache) Replyf(format string, args ...interface{}) {
|
||||
ch.Reply(fmt.Sprintf(format, args...))
|
||||
}
|
||||
Reference in New Issue
Block a user