mirror of
https://github.com/Neur0toxine/simla-bot-quickreplies-test.git
synced 2024-11-23 13:26:07 +03:00
initial
This commit is contained in:
commit
646caf2cab
18
.editorconfig
Normal file
18
.editorconfig
Normal file
@ -0,0 +1,18 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.{less,css,yml,json}]
|
||||
indent_size = 2
|
||||
|
||||
[{*.md,go.mod,go.sum}]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[Makefile]
|
||||
indent_style = tab
|
4
.env.dist
Normal file
4
.env.dist
Normal file
@ -0,0 +1,4 @@
|
||||
API_URL=https://test.retailcrm.pro
|
||||
API_KEY=key
|
||||
MESSAGE_SCOPE=public
|
||||
TEXT_OPTIONS="Text 1,Text 2,Text 3,Text 4,Text 5"
|
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
vendor
|
||||
.env
|
||||
main
|
||||
.idea
|
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Neur0toxine
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
3
README.md
Normal file
3
README.md
Normal file
@ -0,0 +1,3 @@
|
||||
This bot will automatically respond to any Simla message with preconfigured quick replies.
|
||||
Build: `go build -o main ./...`
|
||||
Usage: copy `.env.dist` to `.env` then replace placeholders with your data. After that you can run the bot.
|
11
go.mod
Normal file
11
go.mod
Normal file
@ -0,0 +1,11 @@
|
||||
module bot-quick-replies
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/gorilla/websocket v1.4.2 // indirect
|
||||
github.com/joho/godotenv v1.4.0 // indirect
|
||||
github.com/retailcrm/api-client-go v1.3.8 // indirect
|
||||
github.com/retailcrm/mg-bot-api-client-go v1.2.9 // indirect
|
||||
)
|
22
go.sum
Normal file
22
go.sum
Normal file
@ -0,0 +1,22 @@
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
|
||||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||
github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg=
|
||||
github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/retailcrm/api-client-go v1.3.8 h1:oWyxmm2YB2bEz/F+0XrP0k6QhOVv9gICKv4A/3sDWS0=
|
||||
github.com/retailcrm/api-client-go v1.3.8/go.mod h1:QRoPE2SM6ST7i2g0yEdqm7Iw98y7cYuq3q14Ot+6N8c=
|
||||
github.com/retailcrm/mg-bot-api-client-go v1.2.9 h1:eUDrD20ysNCb4oAOmbJ4BTkXcHKZKU4Fq+aVKOx7Ttc=
|
||||
github.com/retailcrm/mg-bot-api-client-go v1.2.9/go.mod h1:kWDUiT5pvUtWZrb/mpkJDgddU5ezW8EuwduJKJ35lPs=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/h2non/gock.v1 v1.1.0/go.mod h1:n7UGz/ckNChHiK05rDoiC4MYSunEC/lyaUm2WWaDva0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
56
main.go
Normal file
56
main.go
Normal file
@ -0,0 +1,56 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/joho/godotenv"
|
||||
v1 "github.com/retailcrm/mg-bot-api-client-go/v1"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := godotenv.Load()
|
||||
if err != nil {
|
||||
log.Fatal("Error loading .env file:", err)
|
||||
}
|
||||
|
||||
apiURL := os.Getenv("API_URL")
|
||||
apiKey := os.Getenv("API_KEY")
|
||||
|
||||
if apiURL == "" {
|
||||
log.Fatal("API_URL environment variable must be set")
|
||||
}
|
||||
if apiKey == "" {
|
||||
log.Fatal("API_KEY environment variable must be set")
|
||||
}
|
||||
|
||||
botCode := os.Getenv("BOT_CODE")
|
||||
botName := os.Getenv("BOT_NAME")
|
||||
msgScope := os.Getenv("MESSAGE_SCOPE")
|
||||
options := os.Getenv("TEXT_OPTIONS")
|
||||
|
||||
if botCode == "" {
|
||||
botCode = "test-quick-reply-bot"
|
||||
}
|
||||
if botName == "" {
|
||||
botName = "Test Quick Reply Bot"
|
||||
}
|
||||
if msgScope == "" {
|
||||
msgScope = v1.MessageScopePrivate
|
||||
}
|
||||
if options == "" {
|
||||
options = "Text reply"
|
||||
}
|
||||
|
||||
endpoint, token, err := updateIntegrationModule(apiURL, apiKey, botCode, botName)
|
||||
if err != nil {
|
||||
log.Fatal("Error updating integration module: ", err)
|
||||
}
|
||||
|
||||
log.Println("Integration module has been updated. Endpoint: ", endpoint, ", token: ", token)
|
||||
|
||||
if err := NewWebsocketListener(endpoint, token, msgScope, strings.Split(options, ",")).Listen(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
62
settings.go
Normal file
62
settings.go
Normal file
@ -0,0 +1,62 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/retailcrm/api-client-go/errs"
|
||||
"github.com/retailcrm/api-client-go/v5"
|
||||
)
|
||||
|
||||
func buildIntegrationModule(code, name string) v5.IntegrationModule {
|
||||
return v5.IntegrationModule{
|
||||
Code: code,
|
||||
IntegrationCode: code,
|
||||
Active: true,
|
||||
Name: name,
|
||||
ClientID: code,
|
||||
BaseURL: "https://example.com",
|
||||
Integrations: &v5.Integrations{
|
||||
MgBot: &v5.MgBot{},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func updateIntegrationModule(apiURL, apiKey, code, name string) (string, string, error) {
|
||||
client := v5.New(apiURL, apiKey)
|
||||
resp, _, err := client.IntegrationModuleEdit(buildIntegrationModule(code, name))
|
||||
if err != nil {
|
||||
if nErr := normalizeAPIError(err); nErr != nil {
|
||||
return "", "", nErr
|
||||
}
|
||||
}
|
||||
return resp.Info.MgBotInfo.EndpointUrl, resp.Info.MgBotInfo.Token, nil
|
||||
}
|
||||
|
||||
func normalizeAPIError(err *errs.Failure) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err.Error() != "" {
|
||||
return errors.New(err.Error())
|
||||
}
|
||||
|
||||
if err.ApiError() != "" {
|
||||
return errors.New(err.ApiError())
|
||||
}
|
||||
|
||||
if len(err.ApiErrors()) > 0 {
|
||||
var sb strings.Builder
|
||||
sb.Grow(128)
|
||||
|
||||
for field, value := range err.ApiErrors() {
|
||||
sb.WriteString(fmt.Sprintf("[%s: %s]", field, value))
|
||||
}
|
||||
|
||||
return errors.New(sb.String())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
97
websocket.go
Normal file
97
websocket.go
Normal file
@ -0,0 +1,97 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
v1 "github.com/retailcrm/mg-bot-api-client-go/v1"
|
||||
)
|
||||
|
||||
type WebsocketListener struct {
|
||||
mg *v1.MgClient
|
||||
ws *websocket.Conn
|
||||
scope string
|
||||
suggestions []v1.Suggestion
|
||||
}
|
||||
|
||||
func NewWebsocketListener(endpoint, token, scope string, textOptions []string) *WebsocketListener {
|
||||
suggestions := []v1.Suggestion{
|
||||
{
|
||||
Type: v1.SuggestionTypePhone,
|
||||
Title: "Phone",
|
||||
},
|
||||
{
|
||||
Type: v1.SuggestionTypeEmail,
|
||||
Title: "E-Mail",
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range textOptions {
|
||||
suggestions = append(suggestions, v1.Suggestion{
|
||||
Type: v1.SuggestionTypeText,
|
||||
Title: s,
|
||||
})
|
||||
}
|
||||
|
||||
return &WebsocketListener{
|
||||
mg: v1.New(endpoint, token),
|
||||
scope: scope,
|
||||
suggestions: suggestions,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *WebsocketListener) Listen() error {
|
||||
data, header, err := l.mg.WsMeta([]string{v1.WsEventMessageNew})
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot get meta for connection: %w", err)
|
||||
}
|
||||
|
||||
ws, _, err := websocket.DefaultDialer.Dial(data, header)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot estabilish WebSocket connection to %s: %w", data, err)
|
||||
}
|
||||
|
||||
log.Println("Listening for the new messages...")
|
||||
|
||||
for {
|
||||
var wsEvent v1.WsEvent
|
||||
if err := ws.ReadJSON(&wsEvent); err != nil {
|
||||
log.Fatal("unexpected websocket error:", err)
|
||||
}
|
||||
|
||||
var event v1.WsEventMessageNewData
|
||||
err = json.Unmarshal(wsEvent.Data, &event)
|
||||
if err != nil {
|
||||
log.Printf("cannot unmarshal payload: %s\n", err)
|
||||
continue
|
||||
}
|
||||
|
||||
if event.Message == nil {
|
||||
log.Print("invalid payload - nil message")
|
||||
continue
|
||||
}
|
||||
|
||||
if event.Message.From != nil && event.Message.From.Type != "customer" {
|
||||
continue
|
||||
}
|
||||
|
||||
log.Printf("Received message from %s with id=%d\n", event.Message.From.Name, event.Message.ID)
|
||||
|
||||
_, _, err := l.mg.MessageSend(v1.MessageSendRequest{
|
||||
Type: v1.MsgTypeText,
|
||||
Content: "The quick brown fox jumps over the lazy dog.",
|
||||
// Items: nil,
|
||||
Scope: l.scope,
|
||||
ChatID: event.Message.ChatID,
|
||||
QuoteMessageId: event.Message.ID,
|
||||
TransportAttachments: &v1.TransportAttachments{
|
||||
Suggestions: l.suggestions,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
log.Printf("error: cannot respond to the message: %s\n", err)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user