Compare commits

...

68 commits

Author SHA1 Message Date
9e5fb7afec
Merge pull request #85 from vragovR/add-dialog-utm
Add `Utm` field to the `DialogResponseItem` struct
2025-04-10 11:42:28 +03:00
Vragov Roman
3b56a36b3b Add Utm field to the DialogResponseItem struct 2025-04-07 13:25:47 +03:00
1ba7024ccc
Merge pull request #83 from vragovR/add-message-utm
Add `Utm` field to the `Dialog` struct
2025-03-06 15:20:40 +03:00
Vragov Roman
fe5c8e9676 Add Utm field to the Dialog struct 2025-03-06 15:05:48 +03:00
e2205fb4d9
fix UploadFile dependency on external URL 2025-03-06 14:59:25 +03:00
223e63b268 update linter (2) 2025-03-06 14:58:13 +03:00
7cec2bb237 update linter 2025-03-06 14:46:36 +03:00
c338d2e19e ci update 2025-03-06 14:42:46 +03:00
a101bd502a fix UploadFile dependency on external URL 2025-03-06 14:40:01 +03:00
021568afd8
Merge pull request #79 from ilyavlasoff/add_transcription_status_field
Added transcription status field
2024-11-15 14:39:28 +03:00
f7e352a622
Merge pull request #80 from opheugene/add_from_is_system
Added IsSystem flag to UserRef struct
2024-11-15 14:39:09 +03:00
5466d2d1ad
Merge pull request #82 from ilyavlasoff/added_message_dialog_field
Added Message.Dialog field for all types of messages
2024-11-15 14:38:50 +03:00
0e970386ce
Merge pull request #81 from kifril-ltd/bot-role-hidden
Add new bot role `hidden`
2024-11-15 14:38:21 +03:00
Vlasov
8b0193186b Added Message.Dialog field for all types of messages 2024-11-15 12:02:57 +03:00
Kirill Sukhorukov
c7b7f73c4f Add new bot role hidden 2024-11-08 13:33:19 +03:00
Opheugene
22d56ef2dc -mod 2024-11-06 20:06:49 +01:00
Opheugene
f6affa2063 mod 2024-11-06 19:59:49 +01:00
Opheugene
d5aa1a0794 Added Last Dialog to Chat struct 2024-11-06 19:32:35 +01:00
Opheugene
bc9702741e Added IsSystem flag to UserRef struct 2024-11-06 19:05:27 +01:00
Vlasov
46a262a5dc Added transcription status field 2024-10-21 12:27:34 +03:00
7b80d3d542
Add LastUserMessage field to the Chat struct 2024-10-14 17:44:28 +03:00
Vragov Roman
b0e374f88e Add LastUserMessage field to the Chat struct 2024-10-14 15:28:26 +03:00
8eb473d7d2
update file metadata request 2024-09-04 17:35:34 +03:00
Vlasov
bbea93671e Added update file metadata request 2024-09-04 16:11:38 +03:00
Alexey
ca83218213
Добавлен параметр IncludeMassCommunication в запрос списка чатов, диалогов, сообщений (#76) 2024-08-30 16:42:04 +03:00
074f787d0f
Add websocket options 2024-07-17 12:36:41 +03:00
d01abbc1bb
Added since_id field to ChatsRequest 2024-07-16 11:47:23 +03:00
Vlasov
88eeda804b Added since_id field to ChatsRequest 2024-07-16 11:40:08 +03:00
Alexey Chelnakov
e090080325 fix 2024-07-11 14:04:49 +03:00
Alexey Chelnakov
3e9078ee86 fix 2024-07-11 14:02:42 +03:00
Alexey Chelnakov
de3eca3a55 fix 2024-07-11 13:49:29 +03:00
Alexey Chelnakov
73b06d5de4 Add websocket options 2024-06-28 18:04:49 +03:00
3d84ddaee6
Add customer_external_id to ChatsRequest 2024-06-24 15:34:18 +03:00
Kirill Sukhorukov
6090ceebcf Add customer_external_id to ChatsRequest 2024-06-17 17:45:21 +03:00
506a6a1d61
add waiting levels to chat events 2024-05-30 13:38:06 +03:00
Vlasov
272cdaa60c Added waiting levels in chat events 2024-05-30 13:26:13 +03:00
edf67afb08
Add functional options for MGClient 2024-02-28 14:21:39 +03:00
Kirill Sukhorukov
9ecd6f85a1 Add functional options for MGClient 2024-02-28 14:13:02 +03:00
9ee3653827
Add request DialogsTagsAdd and DialogTagsDelete 2024-02-27 15:56:29 +03:00
Vragov Roman
ae8c5793dc Add request DialogsTagsAdd and DialogTagsDelete 2024-02-20 19:09:05 +03:00
4058284e32
Merge pull request #65 from RenCurs/update-linter
update linter and it's rules
2024-02-06 15:02:40 +03:00
1c8f6885e6
Add ChannelType field to the CustomersRequest struct 2023-11-28 16:21:28 +03:00
Vragov Roman
8a0219a962 Add ChannelType field to the CustomersRequest struct 2023-11-28 15:19:29 +03:00
5190445cc4
Add ChannelId field to the CustomersRequest struct 2023-11-28 11:10:52 +03:00
Vragov Roman
632153073d Add ChannelId field to the CustomersRequest struct 2023-11-21 16:50:01 +03:00
3fb19e388b
add sinceId to DialogsRequest 2023-08-15 15:45:43 +03:00
c68299f924 add sinceId to DialogsRequest 2023-08-15 15:43:53 +03:00
942cf81ce6
fix for limit parameter serialized name 2023-02-13 10:01:46 +03:00
Kirill Sukhorukov
2c5a6ef463 fixing the name of the limit parameter 2023-02-02 14:49:10 +03:00
Ruslan Efanov
56bf4df310 update linter and it's rules 2022-12-30 10:42:29 +03:00
21e9551b32
Add UsersResponseItem field to the Available struct 2022-12-29 09:03:16 +03:00
582acf20eb
Add Utm field to the CustomersResponseItem struct 2022-11-23 12:15:01 +03:00
Vragov Roman
1ea6a6ec9d Add Utm field to the CustomersResponseItem struct 2022-11-23 11:36:00 +03:00
6674d05af5
Add test for GET chats by customer_id 2022-11-17 13:17:15 +03:00
Ruslan Efanov
a9d74ceee4 Add test for GET chats by customer_id 2022-11-16 15:29:08 +03:00
d21f159a57
Add CustomerID field to the ChatsRequest struct 2022-11-16 10:01:59 +03:00
Ivan Lutokhin
d74d2fea2a Add CustomerID field to the ChatsRequest struct 2022-11-15 18:00:44 +03:00
Vragov Roman
f3ea711138 Add UsersResponseItem field to the Available struct 2022-11-08 14:13:27 +03:00
77f3af2445
Add IsActive field to the WsEventUserUpdatedData struct 2022-10-20 16:34:26 +03:00
Vragov Roman
3cf9c28f51 Add IsActive field to the WsEventUserUpdatedData struct 2022-10-20 12:43:03 +03:00
9400a2b045
Add IsTechnicalAccount field to the UserRef struct 2022-08-18 17:26:29 +03:00
Vragov Roman
68d7defc91 Add IsTechnicalAccount field to the UserRef struct 2022-08-17 12:14:54 +03:00
a42575b51c
add IsTechnicalAccount field to the UsersResponseItem struct 2022-08-16 14:30:12 +03:00
Vragov Roman
431c55576f Add UsersResponseItem field IsTechnicalAccount 2022-08-16 13:26:35 +03:00
78201aa2d3
correct type for the ExternalID field in UserRef 2022-07-26 12:15:53 +03:00
Shchedrin Andrei
3555b34ad3 fix 2022-07-26 11:34:53 +03:00
f2c1365a51
add channel updated data 2022-07-25 15:07:44 +03:00
Shchedrin Andrei
2e4ba1e681 add channel updated data 2022-07-21 16:57:40 +03:00
5 changed files with 734 additions and 158 deletions

View file

@ -19,33 +19,33 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Set up latest Go 1.x version
uses: actions/setup-go@v2
uses: actions/checkout@v4
- name: Set up Go 1.24
uses: actions/setup-go@v5
with:
go-version: '1.17'
go-version: '1.24'
- name: Get dependencies
run: |
go mod tidy
cp .env.dist .env
- name: Lint code with golangci-lint
uses: golangci/golangci-lint-action@v3
uses: golangci/golangci-lint-action@v6
with:
version: v1.36
version: v1.62.2
only-new-issues: true
tests:
name: Tests
runs-on: ubuntu-latest
strategy:
matrix:
go-version: ['1.13', '1.14', '1.15', '1.16']
go-version: ['1.13', '1.14', '1.15', '1.16', '1.17', '1.18', '1.19', '1.20', '1.21', '1.22', '1.23', '1.24']
steps:
- name: Set up Go ${{ matrix.go-version }}
uses: actions/setup-go@v2
uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go-version }}
- name: Check out code into the Go module directory
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: Get dependencies
run: |
go mod tidy

View file

@ -1,30 +1,28 @@
run:
skip-dirs-use-default: true
allow-parallel-runners: true
modules-download-mode: readonly
output:
format: colored-line-number
formats:
- format: colored-line-number
sort-results: true
linters:
disable-all: true
enable:
- deadcode
- errcheck
- gosimple
- govet
- ineffassign
- staticcheck
- structcheck
- unused
- unparam
- varcheck
- bodyclose
- dogsled
- dupl
- errorlint
- exhaustive
- exportloopref
- copyloopvar
- funlen
- gocognit
- goconst
@ -32,26 +30,23 @@ linters:
- gocyclo
- godot
- goimports
- golint
- gomnd
- revive
- mnd
- gosec
- ifshort
- interfacer
- lll
- makezero
- maligned
- misspell
- nestif
- prealloc
- predeclared
- scopelint
- sqlclosecheck
- unconvert
- whitespace
- unused
- testifylint
linters-settings:
govet:
check-shadowing: false
disable-all: true
enable:
- assign
@ -70,7 +65,6 @@ linters-settings:
- unmarshal
- unreachable
- unsafeptr
- unused
settings:
printf:
funcs:
@ -119,8 +113,6 @@ linters-settings:
- log.Printf
- log.Println
- runtime/trace.Logf
unused:
check-exported: false
unparam:
check-exported: false
dogsled:
@ -129,11 +121,13 @@ linters-settings:
threshold: 200
errorlint:
errorf: true
asserts: true
comparison: true
exhaustive:
check-generated: false
default-signifies-exhaustive: false
funlen:
lines: 60
lines: 90
statements: 40
gocognit:
min-complexity: 25
@ -143,8 +137,6 @@ linters-settings:
local-prefixes: github.com/retailcrm/mg-bot-api-client-go
lll:
line-length: 120
maligned:
suggest-new: true
misspell:
locale: US
nestif:
@ -157,7 +149,8 @@ issues:
exclude-rules:
- path: _test\.go
linters:
- gomnd
- dupl
- mnd
- lll
- bodyclose
- errcheck
@ -183,7 +176,4 @@ issues:
severity:
default-severity: error
case-sensitive: false
service:
golangci-lint-version: 1.36.x
case-sensitive: false

View file

@ -14,16 +14,46 @@ import (
"github.com/google/go-querystring/query"
)
type Option func(*MgClient)
// OptionHTTPClient set custom http.Client for MgClient.
func OptionHTTPClient(client *http.Client) func(*MgClient) {
return func(c *MgClient) {
c.httpClient = client
}
}
// OptionLogger sets the provided logger instance into the MgClient.
func OptionLogger(logger BasicLogger) func(*MgClient) {
return func(c *MgClient) {
c.logger = logger
}
}
// OptionDebug enables debug mode for MgClient.
func OptionDebug() func(*MgClient) {
return func(c *MgClient) {
c.Debug = true
}
}
// New initialize client
func New(url string, token string) *MgClient {
return &MgClient{
func New(url string, token string, opts ...Option) *MgClient {
c := &MgClient{
URL: url,
Token: token,
httpClient: &http.Client{Timeout: time.Minute},
}
for _, opt := range opts {
opt(c)
}
return c
}
// WithLogger sets the provided logger instance into the Client.
// Deprecated: Use functional option OptionLogger instead.
func (c *MgClient) WithLogger(logger BasicLogger) *MgClient {
c.logger = logger
return c
@ -427,6 +457,50 @@ func (c *MgClient) DialogClose(request uint64) (map[string]interface{}, int, err
return resp, status, err
}
// DialogsTagsAdd allows to assign dialog to Bot or User
//
// Example:
//
// var client = v1.New("https://demo.url", "09jIJ")
//
// data, status, err := client.DialogsTagsAdd(DialogTagsAddRequest{DialogID: uint64(1),Tags: []TagsAdd{{Name: "foo"}}})
func (c *MgClient) DialogsTagsAdd(request DialogTagsAddRequest) (int, error) {
outgoing, _ := json.Marshal(&request)
data, status, err := c.PatchRequest(fmt.Sprintf("/dialogs/%d/tags/add", request.DialogID), outgoing)
if err != nil {
return status, err
}
if status != http.StatusOK {
return status, c.Error(data)
}
return status, err
}
// DialogTagsDelete allows to assign dialog to Bot or User
//
// Example:
//
// var client = v1.New("https://demo.url", "09jIJ")
//
// data, status, err := client.DialogsTagsAdd(DialogTagsDelete{DialogID: uint64(1),Tags: []TagsDelete{{Name: "foo"}}})
func (c *MgClient) DialogTagsDelete(request DialogTagsDeleteRequest) (int, error) {
outgoing, _ := json.Marshal(&request)
data, status, err := c.PatchRequest(fmt.Sprintf("/dialogs/%d/tags/delete", request.DialogID), outgoing)
if err != nil {
return status, err
}
if status != http.StatusOK {
return status, c.Error(data)
}
return status, err
}
// Messages get all available messages
//
// Example:
@ -838,8 +912,57 @@ func (c *MgClient) UploadFileByURL(request UploadFileByUrlRequest) (UploadFileRe
return resp, status, err
}
// UpdateFileMetadata update file metadata
//
// Example:
//
// response, status, err := c.UpdateFileMetadata(UploadFileByUrlRequest{
// ID: "e038aa39-2338-4285-be86-e2a0bb424daa"
// Transcription: "demo transcription",
// })
//
// if err != nil {
// fmt.Printf("%v", err)
// }
//
// fmt.Printf("%s\n%s", response.ID, status)
func (c *MgClient) UpdateFileMetadata(request UpdateFileMetadataRequest) (UploadFileResponse, int, error) {
var resp UploadFileResponse
outgoing, err := json.Marshal(&request)
if err != nil {
return resp, 0, err
}
data, status, err := c.PutRequest(fmt.Sprintf("/files/%s/meta", request.ID), outgoing)
if err != nil {
return resp, status, err
}
if status != http.StatusOK {
return resp, status, c.Error(data)
}
if e := json.Unmarshal(data, &resp); e != nil {
return resp, status, e
}
return resp, status, err
}
type wsParams struct {
options []string
}
type WsParams interface {
apply(*wsParams)
}
func (c WsOption) apply(opts *wsParams) {
opts.options = append(opts.options, string(c))
}
// WsMeta let you receive url & headers to open web socket connection
func (c *MgClient) WsMeta(events []string) (string, http.Header, error) {
func (c *MgClient) WsMeta(events []string, urlParams ...WsParams) (string, http.Header, error) {
var url string
if len(events) < 1 {
@ -849,6 +972,14 @@ func (c *MgClient) WsMeta(events []string) (string, http.Header, error) {
url = fmt.Sprintf("%s%s%s%s", strings.Replace(c.URL, "https", "wss", 1), prefix, "/ws?events=", strings.Join(events[:], ","))
var params wsParams
for _, param := range urlParams {
param.apply(&params)
}
if len(params.options) > 0 {
url = fmt.Sprintf("%s&options=%s", url, strings.Join(params.options, ","))
}
if url == "" {
err := errors.New("empty WS URL")
return url, nil, err

View file

@ -2,8 +2,12 @@ package v1
import (
"bytes"
"compress/gzip"
"encoding/base64"
"encoding/json"
"fmt"
"github.com/stretchr/testify/require"
"io"
"log"
"math/rand"
"net/http"
@ -44,14 +48,12 @@ var (
debug, _ = strconv.ParseBool(os.Getenv("DEBUG"))
)
func client() *MgClient {
c := New(mgURL, mgToken)
func client(opts ...Option) *MgClient {
if debug != false {
c.Debug = true
opts = append(opts, OptionDebug())
}
return c
return New(mgURL, mgToken, opts...)
}
func TestMgClient_Bots(t *testing.T) {
@ -71,7 +73,7 @@ func TestMgClient_Bots(t *testing.T) {
t.Errorf("%d %v", status, err)
}
assert.NoError(t, err)
require.NoError(t, err)
assert.NotEmpty(t, data)
for _, bot := range data {
@ -150,7 +152,7 @@ func TestMgClient_Channels(t *testing.T) {
]`)
channels, status, err := c.Channels(ChannelsRequest{Active: 1})
assert.NoError(t, err)
require.NoError(t, err)
assert.Equal(t, 200, status)
assert.Len(t, channels, 1)
@ -214,7 +216,7 @@ func TestMgClient_Users(t *testing.T) {
gock.New(mgURL).
Get("/api/bot/v1/users").
Reply(200).
BodyString(`[{"id": 1, "external_id":"1", "username": "Test", "first_name":"Test", "last_name":"Test", "created_at": "2018-01-01T00:00:00.000000Z", "is_active": true, "is_online": true}]`)
BodyString(`[{"id": 1, "external_id":"1", "username": "Test", "first_name":"Test", "last_name":"Test", "created_at": "2018-01-01T00:00:00.000000Z", "is_active": true, "is_online": true, "is_technical_account": true}]`)
req := UsersRequest{Active: 1}
@ -223,11 +225,19 @@ func TestMgClient_Users(t *testing.T) {
t.Errorf("%d %v", status, err)
}
assert.NoError(t, err)
require.NoError(t, err)
assert.NotEmpty(t, data)
for _, user := range data {
assert.NotEmpty(t, user.FirstName)
assert.Equal(t, uint64(1), user.ID)
assert.Equal(t, "1", user.ExternalID)
assert.Equal(t, "Test", user.Username)
assert.Equal(t, "Test", user.FirstName)
assert.Equal(t, "Test", user.LastName)
assert.Equal(t, "2018-01-01T00:00:00.000000Z", user.CreatedAt)
assert.True(t, user.IsActive)
assert.True(t, user.IsOnline)
assert.True(t, user.IsTechnicalAccount)
}
}
@ -236,10 +246,44 @@ func TestMgClient_Customers(t *testing.T) {
defer gock.Off()
response := `
[
{
"id": 1,
"channel_id": 1,
"created_at":
"2018-01-01T00:00:00.000000Z",
"utm": {
"source": "test"
}
},
{
"id": 2,
"channel_id": 1,
"created_at":
"2018-01-01T00:00:00.000000Z",
"utm": {
"source": null
}
},
{
"id": 3,
"channel_id": 1,
"created_at":
"2018-01-01T00:00:00.000000Z",
"utm": null
},
{
"id": 4,
"channel_id": 1,
"created_at": "2018-01-01T00:00:00.000000Z"
}
]`
gock.New(mgURL).
Get("/api/bot/v1/customers").
Reply(200).
BodyString(`[{"id": 1,"channel_id": 1, "created_at": "2018-01-01T00:00:00.000000Z"}]`)
BodyString(response)
req := CustomersRequest{}
@ -248,12 +292,17 @@ func TestMgClient_Customers(t *testing.T) {
t.Errorf("%d %v", status, err)
}
assert.NoError(t, err)
require.NoError(t, err)
assert.NotEmpty(t, data)
for _, customer := range data {
assert.NotEmpty(t, customer.ChannelId)
}
assert.Equal(t, "test", data[0].Utm.Source)
assert.Equal(t, "", data[1].Utm.Source)
assert.Nil(t, data[2].Utm)
assert.Nil(t, data[3].Utm)
}
func TestMgClient_Chats(t *testing.T) {
@ -264,16 +313,22 @@ func TestMgClient_Chats(t *testing.T) {
gock.New(mgURL).
Get("/api/bot/v1/chats").
Reply(200).
BodyString(`[{"id": 1,"customer": {"id": 1, "name": "Test"}, "created_at": "2018-01-01T00:00:00.000000Z"}]`)
BodyString(`[
{"id": 2,"customer": {"id": 2, "name": "Foo"}, "created_at": "2018-01-01T00:00:00.000000Z"},
{"id": 3,"customer": {"id": 3, "name": "Bar"}, "created_at": "2018-01-02T00:00:00.000000Z"}
]`)
req := ChatsRequest{ChannelType: ChannelTypeTelegram}
req := ChatsRequest{
ChannelType: ChannelTypeTelegram,
SinceID: 1,
}
data, status, err := c.Chats(req)
if err != nil {
t.Errorf("%d %v", status, err)
}
assert.NoError(t, err)
require.NoError(t, err)
assert.NotEmpty(t, data)
for _, chat := range data {
@ -298,7 +353,7 @@ func TestMgClient_Members(t *testing.T) {
t.Errorf("%d %v", status, err)
}
assert.NoError(t, err)
require.NoError(t, err)
for _, member := range data {
assert.NotEmpty(t, member.ChatID)
@ -315,14 +370,14 @@ func TestMgClient_Dialogs(t *testing.T) {
Reply(200).
BodyString(`[{"id": 1, "chat_id": 1, "created_at": "2018-01-01T00:00:00.000000Z"}]`)
req := DialogsRequest{Active: 0}
req := DialogsRequest{Active: 0, SinceID: 1}
data, status, err := c.Dialogs(req)
if err != nil {
t.Errorf("%d %v", status, err)
}
assert.NoError(t, err)
require.NoError(t, err)
assert.NotEmpty(t, data)
for _, dialog := range data {
@ -364,7 +419,7 @@ func TestMgClient_DialogUnassign(t *testing.T) {
resp, status, err := c.DialogUnassign(777)
assert.NoError(t, err)
require.NoError(t, err)
assert.Equal(t, http.StatusOK, status)
assert.Equal(t, int64(111), resp.PreviousResponsible.ID)
@ -426,6 +481,59 @@ func TestMgClient_DialogClose(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, status)
}
func TestMgClient_DialogsTagsAdd(t *testing.T) {
c := client()
color := ColorBlue
req := DialogTagsAddRequest{
DialogID: uint64(1),
Tags: []TagsAdd{
{Name: "foo", ColorCode: nil},
{Name: "bar", ColorCode: &color},
},
}
r, _ := json.Marshal(req)
defer gock.Off()
gock.New(mgURL).
Patch("/api/bot/v1/dialogs/1/tags/add").
JSON(r).
Reply(200).
BodyString(`{}`)
status, err := c.DialogsTagsAdd(req)
require.NoError(t, err)
assert.Equal(t, http.StatusOK, status)
}
func TestMgClient_DialogsTagsDelete(t *testing.T) {
c := client()
req := DialogTagsDeleteRequest{
DialogID: uint64(1),
Tags: []TagsDelete{
{Name: "foo"},
{Name: "bar"},
},
}
r, _ := json.Marshal(req)
defer gock.Off()
gock.New(mgURL).
Patch("/api/bot/v1/dialogs/1/tags/delete").
JSON(r).
Reply(200).
BodyString(`{}`)
status, err := c.DialogTagsDelete(req)
require.NoError(t, err)
assert.Equal(t, http.StatusOK, status)
}
func TestMgClient_Messages(t *testing.T) {
c := client()
@ -443,7 +551,7 @@ func TestMgClient_Messages(t *testing.T) {
t.Errorf("%d %v", status, err)
}
assert.NoError(t, err)
require.NoError(t, err)
assert.NotEmpty(t, data)
for _, message := range data {
@ -451,6 +559,83 @@ func TestMgClient_Messages(t *testing.T) {
}
}
func TestMgClient_MessagesDialog(t *testing.T) {
t.Parallel()
c := client()
defer gock.Off()
gock.New(mgURL).
Get("/api/bot/v1/messages").
Reply(200).
BodyString(`[
{
"id": 1,
"time": "2024-11-14T20:48:21Z",
"type": "system",
"scope": "private",
"chat_id": 1,
"is_read": false,
"is_edit": false,
"status": "received",
"dialog": {
"id": 2054
},
"action": "dialog_closed",
"channel_id": 71,
"created_at": "2024-11-14T20:48:21.907566Z",
"updated_at": "2024-11-14T20:48:21.907566Z"
},
{
"id": 2,
"time": "2024-11-14T19:58:42Z",
"type": "text",
"scope": "public",
"chat_id": 1,
"is_read": false,
"is_edit": false,
"status": "failed",
"from": {
"id": 1,
"external_id": "1",
"type": "user",
"avatar": "http://avatars-test.jpeg",
"name": "John Smith",
"first_name": "John",
"last_name": "Smith",
"available": true
},
"dialog": {
"id": 2054
},
"error": {
"code": "malformed_response"
},
"content": "Message from user John Smith",
"quote": null,
"channel_id": 71,
"created_at": "2024-11-14T19:58:42.933025Z",
"updated_at": "2024-11-14T19:58:45.01619Z"
}
]`)
req := MessagesRequest{}
data, status, err := c.Messages(req)
if err != nil {
t.Errorf("%d %v", status, err)
}
require.NoError(t, err)
assert.Len(t, data, 2)
for _, m := range data {
assert.NotNil(t, m.Message.Dialog)
assert.Equal(t, uint64(2054), m.Message.Dialog.ID)
}
}
func TestMgClient_MessageSendText(t *testing.T) {
c := client()
@ -475,7 +660,7 @@ func TestMgClient_MessageSendText(t *testing.T) {
t.Errorf("%d %v", status, err)
}
assert.NoError(t, err)
require.NoError(t, err)
assert.NotEmpty(t, data.MessageID)
}
@ -513,7 +698,7 @@ func TestMgClient_MessageSendTextWithSuggestions(t *testing.T) {
t.Errorf("%d %v", status, err)
}
assert.NoError(t, err)
require.NoError(t, err)
assert.NotEmpty(t, data.MessageID)
}
@ -554,7 +739,7 @@ func TestMgClient_MessageSendProduct(t *testing.T) {
t.Errorf("%v", err)
}
assert.NoError(t, err)
require.NoError(t, err)
t.Logf("%v", msg)
}
@ -614,7 +799,7 @@ func TestMgClient_MessageSendOrder(t *testing.T) {
t.Errorf("%v", err)
}
assert.NoError(t, err)
require.NoError(t, err)
t.Logf("%v", msg)
}
@ -683,7 +868,7 @@ func TestMgClient_Info(t *testing.T) {
t.Errorf("%d %v", status, err)
}
assert.NoError(t, err)
require.NoError(t, err)
}
func TestMgClient_Commands(t *testing.T) {
@ -703,7 +888,7 @@ func TestMgClient_Commands(t *testing.T) {
t.Errorf("%d %v", status, err)
}
assert.NoError(t, err)
require.NoError(t, err)
assert.NotEmpty(t, data)
for _, command := range data {
@ -731,7 +916,7 @@ func TestMgClient_CommandEditDelete(t *testing.T) {
t.Errorf("%d %v", status, err)
}
assert.NoError(t, err)
require.NoError(t, err)
assert.NotEmpty(t, n.ID)
gock.New(mgURL).
@ -744,10 +929,28 @@ func TestMgClient_CommandEditDelete(t *testing.T) {
t.Errorf("%d %v", status, err)
}
assert.NoError(t, err)
require.NoError(t, err)
t.Logf("%v", d)
}
func TestMgClient_WsMeta_With_Options(t *testing.T) {
c := client()
events := []string{"user_updated", "user_join_chat"}
params := []WsParams{WsOptionIncludeMassCommunication}
url, headers, err := c.WsMeta(events, params...)
if err != nil {
t.Errorf("%v", err)
}
resURL := "wss://api.example.com/api/bot/v1/ws?events=user_updated,user_join_chat&options=include_mass_communication"
resToken := c.Token
assert.Equal(t, resURL, url)
assert.Equal(t, resToken, headers["X-Bot-Token"][0])
}
func TestMgClient_WsMeta(t *testing.T) {
c := client()
events := []string{"user_updated", "user_join_chat"}
@ -757,23 +960,56 @@ func TestMgClient_WsMeta(t *testing.T) {
t.Errorf("%v", err)
}
resUrl := fmt.Sprintf("%s%s%s%s", strings.Replace(c.URL, "https", "wss", 1), prefix, "/ws?events=", strings.Join(events[:], ","))
resURL := fmt.Sprintf("%s%s%s%s", strings.Replace(c.URL, "https", "wss", 1), prefix, "/ws?events=", strings.Join(events, ","))
resToken := c.Token
assert.Equal(t, resUrl, url)
assert.Equal(t, resURL, url)
assert.Equal(t, resToken, headers["X-Bot-Token"][0])
}
func TestMgClient_UploadFile(t *testing.T) {
c := client()
defer gock.Off()
gock.New("https://via.placeholder.com").
Get("/300").
Reply(http.StatusOK).
SetHeader("Content-Type", "image/jpeg").
Body(func() io.Reader {
res, err := base64.StdEncoding.DecodeString(`
H4sIAAAAAAACA/t/4/8DBgEvN083BkZGBgZGIGT4f5vBmYGVmYWFhZkVSLCysrKx83CwAwE/Nzcn
jyC/kJAgv6CgsJiMuLCIlKigoISihJSsrLy8vLC4koqSnIqMnLwcyBBGoFZ2NnY+Dg4+ORFBETmS
wf8DDIIcDAoMCsyMSgxMgozMgoz/jzDIA93JyggGDFDAyMTMAnQlBycXN1DBVgEGJkZmZiag+4Ee
AMrWAuUZWARZhRQNHdmEAxPZlQpFjBonLuRQdtp4UDTo4gcV46SiJk4uMXEJSSlVNXUNTS0TUzNz
C0srZxdXN3cPT6/gkNCw8IjIqOSU1LT0jMys4pLSsvKKyqrmlta29o7OrkmTp0ydNn3GzFmLFi9Z
umz5ipWrNm3esnXb9h07dx06fOToseMnTp66dPnK1WvXb9y89fDR4ydPnz1/8fLVx0+fv3z99v3H
z18gfzEyMDPCAFZ/CQL9xQSMFhZ2kL8YmcpBCgRZWBUN2YQcA9kTC4WVjBo5RJwmLtx4kFPZOOiD
aFLRRS4xFZOHqh9BXgP7jDiPNZHlM7jHEP66xcDDzAiMPGZBBnuGH/fuKWs3sItefBlWa7FqV+h8
P+01l9b8KnQQ27Labk545NLIL49WZwLl1m322vzyKEPFu6npl7temwAlQ3O1zi8XvQaSXMBtBdcZ
itDYYP//JgDowAia0AIAAA==`)
if err != nil {
t.Errorf("%v", err)
t.FailNow()
return nil
}
unpacker, err := gzip.NewReader(bytes.NewReader(res))
if err != nil {
t.Errorf("%v", err)
t.FailNow()
return nil
}
return unpacker
}())
resp, err := http.Get("https://via.placeholder.com/300")
if err != nil {
t.Errorf("%v", err)
t.FailNow()
}
defer resp.Body.Close()
defer gock.Off()
gock.New(mgURL).
Post("/api/bot/v1/files/upload").
@ -811,7 +1047,7 @@ func TestMgClient_UploadFileByUrl(t *testing.T) {
t.Logf("File %+v is upload", uploadFileResponse.ID)
assert.NoError(t, err)
require.NoError(t, err)
}
func RandStringBytesMaskImprSrc(n int) string {
@ -851,11 +1087,110 @@ func TestMgClient_DebugWithLogger(t *testing.T) {
var buf bytes.Buffer
logger := log.New(&buf, "Custom log prefix ", 0)
c := client()
c.Debug = true
c.WithLogger(logger)
c := client(OptionDebug(), OptionLogger(logger))
c.writeLog("Test log string")
assert.Contains(t, buf.String(), "Custom log prefix Test log string")
}
func TestMgClient_SuccessChatsByCustomerId(t *testing.T) {
defer gock.Off()
customerID := uint64(191140)
gock.New(mgURL).
Path("/api/bot/v1/chats").
MatchParam("customer_id", fmt.Sprintf("%d", customerID)).
MatchHeader("X-Bot-Token", mgToken).
Reply(http.StatusOK).
JSON(getJSONResponseChats())
apiClient := client()
chatsRequest := ChatsRequest{
CustomerID: customerID,
}
resp, statusCode, err := apiClient.Chats(chatsRequest)
require.NoError(t, err)
assert.Equal(t, http.StatusOK, statusCode)
assert.Len(t, resp, 1)
assert.Equal(t, uint64(9000), resp[0].ID)
assert.Equal(t, uint64(8000), resp[0].Channel.ID)
assert.Equal(t, customerID, resp[0].Customer.ID)
assert.Equal(t, "Имя Фамилия", resp[0].Customer.Name)
assert.Equal(t, "Имя", resp[0].Customer.FirstName)
assert.Equal(t, "Фамилия", resp[0].Customer.LastName)
}
func getJSONResponseChats() string {
return `[
{
"id": 9000,
"channel": {
"id": 8000,
"avatar": "",
"transport_id": 555,
"type": "transport",
"settings": {
"status": {
"delivered": "send"
},
"text": {
"creating": "both",
"editing": "both",
"quoting": "both",
"deleting": "receive",
"max_chars_count": 4096
},
"product": {
"creating": "receive",
"editing": "receive"
},
"order": {
"creating": "receive",
"editing": "receive"
},
"image": {
"creating": "both",
"editing": "both",
"quoting": "both",
"deleting": "receive",
"max_items_count": 10
},
"file": {
"creating": "both",
"editing": "both",
"quoting": "both",
"deleting": "receive",
"max_items_count": 1
},
"audio": {
"creating": "both",
"quoting": "both",
"deleting": "receive",
"max_items_count": 1
},
"suggestions": {
"text": "both",
"email": "both",
"phone": "both"
}
},
"name": "@test_bot123",
"is_active": false
},
"customer": {
"id": 191140,
"external_id": "",
"type": "customer",
"avatar": "",
"name": "Имя Фамилия",
"username": "Имя",
"first_name": "Имя",
"last_name": "Фамилия"
},
"last_activity": "2022-10-28T13:17:38+03:00",
"created_at": "2022-10-07T14:00:24.795382Z",
"updated_at": "2022-10-28T12:19:04.834592Z"
}
]`
}

View file

@ -43,6 +43,8 @@ const (
WsEventSettingsUpdated string = "settings_updated"
WsEventChatsDeleted string = "chats_deleted"
WsOptionIncludeMassCommunication WsOption = "include_mass_communication"
ChannelFeatureNone string = "none"
ChannelFeatureReceive string = "receive"
ChannelFeatureSend string = "send"
@ -50,6 +52,7 @@ const (
BotRoleDistributor string = "distributor"
BotRoleResponsible string = "responsible"
BotRoleHidden string = "hidden"
MsgTypeText string = "text"
MsgTypeSystem string = "system"
@ -76,6 +79,23 @@ const (
SuggestionTypeText = "text"
SuggestionTypeEmail = "email"
SuggestionTypePhone = "phone"
ColorLightRed = "light-red"
ColorLightBlue = "light-blue"
ColorLightGreen = "light-green"
ColorLightOrange = "light-orange"
ColorLightGray = "light-gray"
ColorLightGrayishBlue = "light-grayish-blue"
ColorRed = "red"
ColorBlue = "blue"
ColorGreen = "green"
ColorOrange = "orange"
ColorGray = "gray"
ColorGrayishBlue = "grayish-blue"
WaitingLevelNone = "none"
WaitingLevelWarning = "warning"
WaitingLevelDanger = "danger"
)
// MgClient type
@ -96,7 +116,7 @@ type (
Role string `url:"role,omitempty"`
Since string `url:"since,omitempty"`
Until string `url:"until,omitempty"`
Limit int `url:"int,omitempty"`
Limit int `url:"limit,omitempty"`
}
ChannelsRequest struct {
@ -105,7 +125,7 @@ type (
Active uint8 `url:"active,omitempty"`
Since string `url:"since,omitempty"`
Until string `url:"until,omitempty"`
Limit int `url:"int,omitempty"`
Limit int `url:"limit,omitempty"`
}
UsersRequest struct {
@ -115,24 +135,30 @@ type (
Active uint8 `url:"active,omitempty"`
Since string `url:"since,omitempty"`
Until string `url:"until,omitempty"`
Limit int `url:"int,omitempty"`
Limit int `url:"limit,omitempty"`
}
CustomersRequest struct {
ID uint64 `url:"id,omitempty"`
ExternalID string `url:"external_id,omitempty" json:"external_id"`
Since string `url:"since,omitempty"`
Until string `url:"until,omitempty"`
Limit int `url:"int,omitempty"`
}
ChatsRequest struct {
ID uint64 `url:"id,omitempty"`
ChannelID uint64 `url:"channel_id,omitempty" json:"channel_id"`
ChannelType string `url:"channel_type,omitempty" json:"channel_type"`
ExternalID string `url:"external_id,omitempty" json:"external_id"`
Since string `url:"since,omitempty"`
Until string `url:"until,omitempty"`
Limit int `url:"int,omitempty"`
Limit int `url:"limit,omitempty"`
}
ChatsRequest struct {
ID uint64 `url:"id,omitempty"`
ChannelID uint64 `url:"channel_id,omitempty" json:"channel_id"`
ChannelType string `url:"channel_type,omitempty" json:"channel_type"`
CustomerID uint64 `url:"customer_id,omitempty" json:"customer_id"`
CustomerExternalID string `url:"customer_external_id,omitempty" json:"customer_external_id"`
Since string `url:"since,omitempty"`
Until string `url:"until,omitempty"`
Limit int `url:"limit,omitempty"`
SinceID int `url:"since_id,omitempty"`
IncludeMassCommunication uint8 `url:"include_mass_communication,omitempty"`
}
MembersRequest struct {
@ -141,19 +167,21 @@ type (
State string `url:"state,omitempty"`
Since string `url:"since,omitempty"`
Until string `url:"until,omitempty"`
Limit int `url:"int,omitempty"`
Limit int `url:"limit,omitempty"`
}
DialogsRequest struct {
ID uint64 `url:"id,omitempty"`
ChatID string `url:"chat_id,omitempty" json:"chat_id"`
UserID string `url:"user_id,omitempty" json:"user_id"`
BotID string `url:"bot_id,omitempty" json:"bot_id"`
Assign uint8 `url:"assign,omitempty"`
Active uint8 `url:"active,omitempty"`
Since string `url:"since,omitempty"`
Until string `url:"until,omitempty"`
Limit int `url:"int,omitempty"`
ID uint64 `url:"id,omitempty"`
ChatID string `url:"chat_id,omitempty" json:"chat_id"`
UserID string `url:"user_id,omitempty" json:"user_id"`
BotID string `url:"bot_id,omitempty" json:"bot_id"`
Assign uint8 `url:"assign,omitempty"`
Active uint8 `url:"active,omitempty"`
Since string `url:"since,omitempty"`
SinceID int `url:"since_id,omitempty"`
Until string `url:"until,omitempty"`
Limit int `url:"limit,omitempty"`
IncludeMassCommunication uint8 `url:"include_mass_communication,omitempty"`
}
DialogAssignRequest struct {
@ -162,20 +190,40 @@ type (
BotID uint64 `url:"bot_id,omitempty" json:"bot_id"`
}
DialogTagsAddRequest struct {
DialogID uint64 `url:"dialog_id,omitempty"`
Tags []TagsAdd `json:"tags"`
}
TagsAdd struct {
Name string `json:"name"`
ColorCode *string `json:"color_code"`
}
DialogTagsDeleteRequest struct {
DialogID uint64 `url:"dialog_id,omitempty"`
Tags []TagsDelete `json:"tags"`
}
TagsDelete struct {
Name string `json:"name"`
}
MessagesRequest struct {
ID []int `url:"id,omitempty"`
ChatID uint64 `url:"chat_id,omitempty" json:"chat_id"`
DialogID uint64 `url:"dialog_id,omitempty" json:"dialog_id"`
UserID uint64 `url:"user_id,omitempty" json:"user_id"`
CustomerID uint64 `url:"customer_id,omitempty" json:"customer_id"`
BotID uint64 `url:"bot_id,omitempty" json:"bot_id"`
ChannelID uint64 `url:"channel_id,omitempty" json:"channel_id"`
ChannelType string `url:"channel_type,omitempty" json:"channel_type"`
Scope string `url:"scope,omitempty"`
Type string `url:"type,omitempty"`
Since string `url:"since,omitempty"`
Until string `url:"until,omitempty"`
Limit int `url:"int,omitempty"`
ID []int `url:"id,omitempty"`
ChatID uint64 `url:"chat_id,omitempty" json:"chat_id"`
DialogID uint64 `url:"dialog_id,omitempty" json:"dialog_id"`
UserID uint64 `url:"user_id,omitempty" json:"user_id"`
CustomerID uint64 `url:"customer_id,omitempty" json:"customer_id"`
BotID uint64 `url:"bot_id,omitempty" json:"bot_id"`
ChannelID uint64 `url:"channel_id,omitempty" json:"channel_id"`
ChannelType string `url:"channel_type,omitempty" json:"channel_type"`
Scope string `url:"scope,omitempty"`
Type string `url:"type,omitempty"`
Since string `url:"since,omitempty"`
Until string `url:"until,omitempty"`
Limit int `url:"limit,omitempty"`
IncludeMassCommunication uint8 `url:"include_mass_communication,omitempty"`
}
MessageSendRequest struct {
@ -215,7 +263,7 @@ type (
Name string `url:"name,omitempty"`
Since string `url:"since,omitempty"`
Until string `url:"until,omitempty"`
Limit int `url:"int,omitempty"`
Limit int `url:"limit,omitempty"`
}
CommandEditRequest struct {
@ -226,6 +274,13 @@ type (
UploadFileByUrlRequest struct {
Url string `json:"url"`
}
UpdateFileMetadataRequest struct {
ID string `json:"-"`
Transcription string `json:"transcription,omitempty"`
// Current status of transcription process. Available values: "in_progress", "ready", "error"
TranscriptionStatus string `json:"transcription_status,omitempty"`
}
)
// Response types
@ -256,17 +311,20 @@ type (
}
UsersResponseItem struct {
ID uint64 `json:"id"`
ExternalID string `json:"external_id,omitempty"`
Username string `json:"username,omitempty"`
FirstName string `json:"first_name,omitempty"`
LastName string `json:"last_name,omitempty"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at,omitempty"`
RevokedAt string `json:"revoked_at,omitempty"`
IsOnline bool `json:"is_online"`
IsActive bool `json:"is_active"`
Avatar string `json:"avatar_url,omitempty"`
ID uint64 `json:"id"`
ExternalID string `json:"external_id,omitempty"`
Username string `json:"username,omitempty"`
FirstName string `json:"first_name,omitempty"`
LastName string `json:"last_name,omitempty"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at,omitempty"`
RevokedAt string `json:"revoked_at,omitempty"`
Available bool `json:"available"`
IsOnline bool `json:"is_online"`
Connected bool `json:"connected"`
IsActive bool `json:"is_active"`
IsTechnicalAccount bool `json:"is_technical_account"`
Avatar string `json:"avatar_url,omitempty"`
}
CustomersResponseItem struct {
@ -285,19 +343,22 @@ type (
Language string `json:"language,omitempty"`
Phone string `json:"phone,omitempty"`
Email string `json:"email,omitempty"`
Utm *Utm `json:"utm,omitempty"`
}
ChatResponseItem struct {
ID uint64 `json:"id"`
Avatar string `json:"avatar"`
Name string `json:"name"`
Channel Channel `json:"channel,omitempty"`
Customer UserRef `json:"customer"`
AuthorID uint64 `json:"author_id"`
LastMessage Message `json:"last_message"`
LastActivity string `json:"last_activity"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
ID uint64 `json:"id"`
Avatar string `json:"avatar"`
Name string `json:"name"`
Channel Channel `json:"channel,omitempty"`
Customer UserRef `json:"customer"`
AuthorID uint64 `json:"author_id"`
LastMessage Message `json:"last_message"`
LastUserMessage MessageID `json:"last_user_message"`
LastActivity string `json:"last_activity"`
LastDialog Dialog `json:"last_dialog"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
}
MemberResponseItem struct {
@ -322,6 +383,7 @@ type (
IsAssigned bool `json:"is_assigned"`
Responsible Responsible `json:"responsible,omitempty"`
IsActive bool `json:"is_active"`
Utm *Utm `json:"utm,omitempty"`
}
DialogAssignResponse struct {
@ -389,6 +451,11 @@ type (
}
)
// WS options.
type (
WsOption string
)
// Single entity types
type (
Message struct {
@ -404,11 +471,16 @@ type (
From *UserRef `json:"from"`
Product *MessageProduct `json:"product,omitempty"`
Order *MessageOrder `json:"order,omitempty"`
Dialog *MessageDialog `json:"dialog,omitempty"`
*TextMessage
*SystemMessage
*AttachmentList
}
MessageID struct {
ID uint64 `json:"id"`
}
TextMessage struct {
Content string `json:"content"`
Quote *QuoteMessage `json:"quote"`
@ -416,16 +488,22 @@ type (
}
SystemMessage struct {
Action string `json:"action"`
Action string `json:"action"`
// Deprecated: Use Message.Dialog.ID instead.
Dialog *SystemMessageDialog `json:"dialog,omitempty"`
User *UserRef `json:"user,omitempty"`
Responsible *UserRef `json:"responsible,omitempty"`
}
// Deprecated: Use MessageDialog instead.
SystemMessageDialog struct {
ID uint64 `json:"id"`
}
MessageDialog struct {
ID uint64 `json:"id"`
}
QuoteMessage struct {
ID uint64 `json:"id"`
Content string `json:"content"`
@ -439,13 +517,21 @@ type (
}
Attachment struct {
ID string `json:"id"`
Mime string `json:"type"`
Caption string `json:"caption"`
Size uint64 `json:"size"`
PreviewURL *string `json:"preview_url,omitempty"`
Height *uint64 `json:"height,omitempty"`
Width *uint64 `json:"width,omitempty"`
File
Caption string `json:"caption"`
}
File struct {
PreviewURL *string `json:"preview_url,omitempty"`
Height *uint64 `json:"height,omitempty"`
Width *uint64 `json:"width,omitempty"`
Transcription string `json:"transcription,omitempty"`
ID string `json:"id"`
Mime string `json:"type"`
Type string `json:"kind"`
Duration int `json:"duration,omitempty"`
Size uint64 `json:"size"`
}
MessageProduct struct {
@ -511,17 +597,19 @@ type (
}
UserRef struct {
ID uint64 `json:"id"`
ExternalID uint64 `json:"external_id"`
Avatar string `json:"avatar"`
Type string `json:"type"`
Name string `json:"name"`
FirstName string `json:"first_name,omitempty"`
LastName string `json:"last_name,omitempty"`
Phone string `json:"phone,omitempty"`
Email string `json:"email,omitempty"`
IsAdmin bool `json:"is_admin"`
Available bool `json:"available"`
ID uint64 `json:"id"`
ExternalID string `json:"external_id"`
Avatar string `json:"avatar"`
Type string `json:"type"`
Name string `json:"name"`
FirstName string `json:"first_name,omitempty"`
LastName string `json:"last_name,omitempty"`
Phone string `json:"phone,omitempty"`
Email string `json:"email,omitempty"`
IsAdmin bool `json:"is_admin"`
Available bool `json:"available"`
IsTechnicalAccount bool `json:"is_technical_account"`
IsSystem bool `json:"is_system"`
}
Channel struct {
@ -553,15 +641,22 @@ type (
}
Chat struct {
ID uint64 `json:"id"`
Avatar string `json:"avatar"`
Name string `json:"name"`
Channel *Channel `json:"channel,omitempty"`
Members []Member `json:"members"`
Customer *UserRef `json:"customer"`
AuthorID uint64 `json:"author_id"`
LastMessage *Message `json:"last_message"`
LastActivity string `json:"last_activity"`
ID uint64 `json:"id"`
Avatar string `json:"avatar"`
Name string `json:"name"`
Channel *Channel `json:"channel,omitempty"`
Members []Member `json:"members"`
Customer *UserRef `json:"customer"`
AuthorID uint64 `json:"author_id"`
LastMessage *Message `json:"last_message"`
LastUserMessage *MessageID `json:"last_user_message"`
LastActivity string `json:"last_activity"`
}
WaitingChat struct {
Chat
WaitingLevel string `json:"waiting_level"`
}
Member struct {
@ -578,6 +673,7 @@ type (
Responsible *Responsible `json:"responsible"`
CreatedAt string `json:"created_at"`
ClosedAt *string `json:"closed_at"`
Utm *Utm `json:"utm,omitempty"`
}
FileMeta struct {
@ -589,6 +685,14 @@ type (
ID string `json:"id"`
Caption string `json:"caption"`
}
Utm struct {
Source string `json:"source"`
Medium string `json:"medium"`
Campaign string `json:"campaign"`
Term string `json:"term"`
Content string `json:"content"`
}
)
// Channel settings
@ -674,10 +778,12 @@ type (
Message *Message `json:"message"`
}
// Deprecated: Use WsEventWaitingChatCreatedData instead.
WsEventChatCreatedData struct {
Chat *Chat `json:"chat"`
}
// Deprecated: Use WsEventWaitingChatUpdatedData instead.
WsEventChatUpdatedData struct {
Chat *Chat `json:"chat"`
}
@ -702,6 +808,7 @@ type (
WsEventUserUpdatedData struct {
*UserRef
IsActive bool `json:"is_active"`
}
WsEventCustomerUpdatedData struct {
@ -723,11 +830,24 @@ type (
}
WsEventUserOnlineUpdatedData struct {
User *UserRef `json:"user"`
Online bool `json:"online"`
User *UserRef `json:"user"`
Online bool `json:"online"`
Connected bool `json:"connected"`
}
WsEventChatsDeletedData struct {
ChatIds []int64 `json:"chat_ids"`
}
WsEventChannelUpdatedData struct {
Channel *ChannelResponseItem `json:"channel"`
}
WsEventWaitingChatCreatedData struct {
Chat *WaitingChat `json:"chat"`
}
WsEventWaitingChatUpdatedData struct {
Chat *WaitingChat `json:"chat"`
}
)