diff --git a/internal/executor/thread-mate/service.go b/internal/executor/thread-mate/service.go index 05fa0035d..11d72603e 100644 --- a/internal/executor/thread-mate/service.go +++ b/internal/executor/thread-mate/service.go @@ -16,10 +16,10 @@ import ( "github.com/sirupsen/logrus" "gopkg.in/yaml.v3" - "github.com/kubeshop/botkube/internal/executor/x/mathx" "github.com/kubeshop/botkube/internal/loggerx" "github.com/kubeshop/botkube/pkg/api" "github.com/kubeshop/botkube/pkg/api/executor" + "github.com/kubeshop/botkube/pkg/mathx" ) const ( diff --git a/internal/executor/x/output/message_parser.go b/internal/executor/x/output/message_parser.go index 1b10ca84d..e9e4eea6f 100644 --- a/internal/executor/x/output/message_parser.go +++ b/internal/executor/x/output/message_parser.go @@ -10,15 +10,15 @@ import ( "github.com/sirupsen/logrus" "github.com/kubeshop/botkube/internal/executor/x" - "github.com/kubeshop/botkube/internal/executor/x/parser" "github.com/kubeshop/botkube/internal/executor/x/state" "github.com/kubeshop/botkube/internal/executor/x/template" "github.com/kubeshop/botkube/pkg/api" + "github.com/kubeshop/botkube/pkg/formatx" ) // Parser defines type Parser interface { - TableSeparated(in string) parser.TableOutput + TableSeparated(in string) formatx.TableOutput } // TableCommandParser allows to render table command output into interactive message based on registered templates. @@ -32,7 +32,7 @@ func NewTableCommandParser(log logrus.FieldLogger) *TableCommandParser { return &TableCommandParser{ log: log, parsers: map[string]Parser{ - "space": &parser.TableSpace{}, + "space": &formatx.TableSpace{}, }, } } @@ -78,7 +78,7 @@ func (p *TableCommandParser) RenderMessage(cmd, output string, state *state.Cont }, nil } -func (p *TableCommandParser) renderActions(msgCtx template.ParseMessage, table parser.Table, cmd string, idx int) (api.Section, error) { +func (p *TableCommandParser) renderActions(msgCtx template.ParseMessage, table formatx.Table, cmd string, idx int) (api.Section, error) { if idx >= len(table.Rows) { idx = len(table.Rows) - 1 } @@ -120,7 +120,7 @@ func (p *TableCommandParser) renderActions(msgCtx template.ParseMessage, table p }, nil } -func (p *TableCommandParser) renderPreview(msgCtx template.ParseMessage, out parser.TableOutput, requestedRow int) (api.Section, error) { +func (p *TableCommandParser) renderPreview(msgCtx template.ParseMessage, out formatx.TableOutput, requestedRow int) (api.Section, error) { headerLine := out.Lines[0] if requestedRow >= len(out.Table.Rows) { @@ -161,7 +161,7 @@ func (*TableCommandParser) getPreviewLine(lines []string, idx int) string { return lines[1] // otherwise default first line } -func (p *TableCommandParser) renderDropdowns(selects []template.Select, commandData parser.Table, cmd string, state *state.Container) (api.Section, int) { +func (p *TableCommandParser) renderDropdowns(selects []template.Select, commandData formatx.Table, cmd string, state *state.Container) (api.Section, int) { var ( dropdowns []api.Select lastSelectedIdx int @@ -187,7 +187,7 @@ func (p *TableCommandParser) renderDropdowns(selects []template.Select, commandD }, lastSelectedIdx } -func (p *TableCommandParser) selectDropdown(name, cmd, keyTpl string, table parser.Table, state *state.Container) (*api.Select, int) { +func (p *TableCommandParser) selectDropdown(name, cmd, keyTpl string, table formatx.Table, state *state.Container) (*api.Select, int) { log := p.log.WithField("selectName", name) var options []api.OptionItem for idx, row := range table.Rows { diff --git a/internal/executor/x/output/message_tutorial.go b/internal/executor/x/output/message_tutorial.go index 5eeb1aa8f..9870022c0 100644 --- a/internal/executor/x/output/message_tutorial.go +++ b/internal/executor/x/output/message_tutorial.go @@ -4,10 +4,10 @@ import ( "fmt" "github.com/kubeshop/botkube/internal/executor/x" - "github.com/kubeshop/botkube/internal/executor/x/mathx" "github.com/kubeshop/botkube/internal/executor/x/state" "github.com/kubeshop/botkube/internal/executor/x/template" "github.com/kubeshop/botkube/pkg/api" + "github.com/kubeshop/botkube/pkg/mathx" ) // TutorialWrapper allows constructing interactive message with predefined steps. diff --git a/pkg/api/cloudteams/cloud_teams.pb.go b/pkg/api/cloudteams/cloud_teams.pb.go index 78cc0f7ea..fd0fbba8f 100644 --- a/pkg/api/cloudteams/cloud_teams.pb.go +++ b/pkg/api/cloudteams/cloud_teams.pb.go @@ -20,6 +20,52 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) +type MessageType int32 + +const ( + MessageType_MESSAGE_EXECUTOR MessageType = 0 + MessageType_MESSAGE_SOURCE MessageType = 1 +) + +// Enum value maps for MessageType. +var ( + MessageType_name = map[int32]string{ + 0: "MESSAGE_EXECUTOR", + 1: "MESSAGE_SOURCE", + } + MessageType_value = map[string]int32{ + "MESSAGE_EXECUTOR": 0, + "MESSAGE_SOURCE": 1, + } +) + +func (x MessageType) Enum() *MessageType { + p := new(MessageType) + *p = x + return p +} + +func (x MessageType) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (MessageType) Descriptor() protoreflect.EnumDescriptor { + return file_cloud_teams_proto_enumTypes[0].Descriptor() +} + +func (MessageType) Type() protoreflect.EnumType { + return &file_cloud_teams_proto_enumTypes[0] +} + +func (x MessageType) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use MessageType.Descriptor instead. +func (MessageType) EnumDescriptor() ([]byte, []int) { + return file_cloud_teams_proto_rawDescGZIP(), []int{0} +} + type AgentActivity struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -73,10 +119,11 @@ type Message struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TeamId string `protobuf:"bytes,1,opt,name=teamId,proto3" json:"teamId,omitempty"` - ActivityId string `protobuf:"bytes,2,opt,name=activityId,proto3" json:"activityId,omitempty"` - ConversationId string `protobuf:"bytes,3,opt,name=conversationId,proto3" json:"conversationId,omitempty"` - Data []byte `protobuf:"bytes,4,opt,name=data,proto3" json:"data,omitempty"` + TeamId string `protobuf:"bytes,1,opt,name=teamId,proto3" json:"teamId,omitempty"` + ActivityId string `protobuf:"bytes,2,opt,name=activityId,proto3" json:"activityId,omitempty"` + ConversationId string `protobuf:"bytes,3,opt,name=conversationId,proto3" json:"conversationId,omitempty"` + MessageType MessageType `protobuf:"varint,4,opt,name=messageType,proto3,enum=cloudteams.MessageType" json:"messageType,omitempty"` + Data []byte `protobuf:"bytes,5,opt,name=data,proto3" json:"data,omitempty"` } func (x *Message) Reset() { @@ -132,6 +179,13 @@ func (x *Message) GetConversationId() string { return "" } +func (x *Message) GetMessageType() MessageType { + if x != nil { + return x.MessageType + } + return MessageType_MESSAGE_EXECUTOR +} + func (x *Message) GetData() []byte { if x != nil { return x.Data @@ -195,24 +249,32 @@ var file_cloud_teams_proto_rawDesc = []byte{ 0x12, 0x2d, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x74, 0x65, 0x61, 0x6d, 0x73, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, - 0x7d, 0x0a, 0x07, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x65, - 0x61, 0x6d, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, - 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x49, 0x64, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, - 0x49, 0x64, 0x12, 0x26, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x49, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x76, - 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, - 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x25, - 0x0a, 0x0d, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x12, - 0x14, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, - 0x65, 0x76, 0x65, 0x6e, 0x74, 0x32, 0x5a, 0x0a, 0x0a, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x54, 0x65, - 0x61, 0x6d, 0x73, 0x12, 0x4c, 0x0a, 0x0e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x41, 0x63, 0x74, - 0x69, 0x76, 0x69, 0x74, 0x79, 0x12, 0x19, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x74, 0x65, 0x61, - 0x6d, 0x73, 0x2e, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, - 0x1a, 0x19, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x74, 0x65, 0x61, 0x6d, 0x73, 0x2e, 0x43, 0x6c, - 0x6f, 0x75, 0x64, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x22, 0x00, 0x28, 0x01, 0x30, - 0x01, 0x42, 0x14, 0x5a, 0x12, 0x70, 0x6b, 0x67, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x63, 0x6c, 0x6f, - 0x75, 0x64, 0x74, 0x65, 0x61, 0x6d, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0xb8, 0x01, 0x0a, 0x07, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x74, + 0x65, 0x61, 0x6d, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, + 0x6d, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x49, + 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, + 0x79, 0x49, 0x64, 0x12, 0x26, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x6f, 0x6e, + 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x39, 0x0a, 0x0b, 0x6d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x17, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x74, 0x65, 0x61, 0x6d, 0x73, 0x2e, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0b, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x25, 0x0a, 0x0d, 0x43, 0x6c, + 0x6f, 0x75, 0x64, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x65, + 0x76, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x65, 0x76, 0x65, 0x6e, + 0x74, 0x2a, 0x37, 0x0a, 0x0b, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, + 0x12, 0x14, 0x0a, 0x10, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x5f, 0x45, 0x58, 0x45, 0x43, + 0x55, 0x54, 0x4f, 0x52, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, + 0x45, 0x5f, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x10, 0x01, 0x32, 0x5a, 0x0a, 0x0a, 0x43, 0x6c, + 0x6f, 0x75, 0x64, 0x54, 0x65, 0x61, 0x6d, 0x73, 0x12, 0x4c, 0x0a, 0x0e, 0x53, 0x74, 0x72, 0x65, + 0x61, 0x6d, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x12, 0x19, 0x2e, 0x63, 0x6c, 0x6f, + 0x75, 0x64, 0x74, 0x65, 0x61, 0x6d, 0x73, 0x2e, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x41, 0x63, 0x74, + 0x69, 0x76, 0x69, 0x74, 0x79, 0x1a, 0x19, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x74, 0x65, 0x61, + 0x6d, 0x73, 0x2e, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, + 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, 0x42, 0x14, 0x5a, 0x12, 0x70, 0x6b, 0x67, 0x2f, 0x61, 0x70, + 0x69, 0x2f, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x74, 0x65, 0x61, 0x6d, 0x73, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -227,21 +289,24 @@ func file_cloud_teams_proto_rawDescGZIP() []byte { return file_cloud_teams_proto_rawDescData } +var file_cloud_teams_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_cloud_teams_proto_msgTypes = make([]protoimpl.MessageInfo, 3) var file_cloud_teams_proto_goTypes = []interface{}{ - (*AgentActivity)(nil), // 0: cloudteams.AgentActivity - (*Message)(nil), // 1: cloudteams.Message - (*CloudActivity)(nil), // 2: cloudteams.CloudActivity + (MessageType)(0), // 0: cloudteams.MessageType + (*AgentActivity)(nil), // 1: cloudteams.AgentActivity + (*Message)(nil), // 2: cloudteams.Message + (*CloudActivity)(nil), // 3: cloudteams.CloudActivity } var file_cloud_teams_proto_depIdxs = []int32{ - 1, // 0: cloudteams.AgentActivity.message:type_name -> cloudteams.Message - 0, // 1: cloudteams.CloudTeams.StreamActivity:input_type -> cloudteams.AgentActivity - 2, // 2: cloudteams.CloudTeams.StreamActivity:output_type -> cloudteams.CloudActivity - 2, // [2:3] is the sub-list for method output_type - 1, // [1:2] is the sub-list for method input_type - 1, // [1:1] is the sub-list for extension type_name - 1, // [1:1] is the sub-list for extension extendee - 0, // [0:1] is the sub-list for field type_name + 2, // 0: cloudteams.AgentActivity.message:type_name -> cloudteams.Message + 0, // 1: cloudteams.Message.messageType:type_name -> cloudteams.MessageType + 1, // 2: cloudteams.CloudTeams.StreamActivity:input_type -> cloudteams.AgentActivity + 3, // 3: cloudteams.CloudTeams.StreamActivity:output_type -> cloudteams.CloudActivity + 3, // [3:4] is the sub-list for method output_type + 2, // [2:3] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name } func init() { file_cloud_teams_proto_init() } @@ -292,13 +357,14 @@ func file_cloud_teams_proto_init() { File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_cloud_teams_proto_rawDesc, - NumEnums: 0, + NumEnums: 1, NumMessages: 3, NumExtensions: 0, NumServices: 1, }, GoTypes: file_cloud_teams_proto_goTypes, DependencyIndexes: file_cloud_teams_proto_depIdxs, + EnumInfos: file_cloud_teams_proto_enumTypes, MessageInfos: file_cloud_teams_proto_msgTypes, }.Build() File_cloud_teams_proto = out.File diff --git a/pkg/api/executor/grpc_adapter.go b/pkg/api/executor/grpc_adapter.go index 64d8dcf28..64fa58d1a 100644 --- a/pkg/api/executor/grpc_adapter.go +++ b/pkg/api/executor/grpc_adapter.go @@ -10,8 +10,8 @@ import ( "google.golang.org/grpc" "google.golang.org/protobuf/types/known/emptypb" - "github.com/kubeshop/botkube/internal/executor/x/mathx" "github.com/kubeshop/botkube/pkg/api" + "github.com/kubeshop/botkube/pkg/mathx" ) const maxMessageNumberForSingleCommandExecution = 15 diff --git a/pkg/bot/interactive/plugin_help.go b/pkg/bot/interactive/plugin_help.go index 483a16741..9579b082e 100644 --- a/pkg/bot/interactive/plugin_help.go +++ b/pkg/bot/interactive/plugin_help.go @@ -16,7 +16,8 @@ var pluginHelpProvider = map[string]pluginHelpProviderFn{ } }, "botkube/kubectl": func(platform config.CommPlatformIntegration, btnBuilder *api.ButtonBuilder) api.Section { - if platform.IsInteractive() { + // TODO(https://github.com/kubeshop/botkube-cloud/issues/645): add support for kubectl builder + if platform.IsInteractive() && platform != config.CloudTeamsCommPlatformIntegration { return api.Section{ Base: api.Base{ Header: "Run kubectl commands", @@ -30,11 +31,8 @@ var pluginHelpProvider = map[string]pluginHelpProviderFn{ // without the kubectl command builder return api.Section{ - Base: api.Base{ - Header: "Run kubectl commands (if enabled)", - }, Buttons: []api.Button{ - btnBuilder.ForCommandWithoutDesc("kubectl help", "kubectl help"), + btnBuilder.ForCommandWithBoldDesc("kubectl help", "Run kubectl commands (if enabled)", "kubectl help"), }, } }, diff --git a/pkg/bot/teams_cloud.go b/pkg/bot/teams_cloud.go index 287ddd4a7..7474cfb44 100644 --- a/pkg/bot/teams_cloud.go +++ b/pkg/bot/teams_cloud.go @@ -5,12 +5,14 @@ import ( "encoding/json" "fmt" "regexp" + "strings" "sync" "time" "github.com/avast/retry-go/v4" "github.com/infracloudio/msbotbuilder-go/core/activity" "github.com/infracloudio/msbotbuilder-go/schema" + "github.com/mitchellh/mapstructure" "github.com/sirupsen/logrus" "golang.org/x/sync/errgroup" @@ -26,8 +28,6 @@ import ( "github.com/kubeshop/botkube/pkg/sliceutil" ) -const channelIDKeyName = "teamsChannelId" - var _ Bot = &CloudTeams{} // CloudTeams listens for user's messages, execute commands and sends back the response. @@ -92,7 +92,6 @@ func NewCloudTeams( if err != nil { return nil, err } - return &CloudTeams{ log: log, executorFactory: executorFactory, @@ -254,9 +253,12 @@ func (b *CloudTeams) handleStreamMessage(ctx context.Context, data *pb.CloudActi return nil, fmt.Errorf("while unmarshaling activity event: %w", err) } switch act.Type { - case schema.Message: + case schema.Message, schema.Invoke: b.log.WithField("message", formatx.StructDumper().Sdump(act)).Debug("Processing Cloud message...") - channel, exists := b.getChannelForActivity(act) + channel, exists, err := b.getChannelForActivity(act) + if err != nil { + b.log.WithError(err).Error("cannot extract message channel id, processing with empty...") + } msg := b.processMessage(ctx, act, channel, exists) if msg.IsEmpty() { @@ -264,7 +266,8 @@ func (b *CloudTeams) handleStreamMessage(ctx context.Context, data *pb.CloudActi return nil, nil } - raw, err := json.Marshal(msg.Message) + msg.ReplaceBotNamePlaceholder(b.BotName(), api.BotNameWithClusterName(b.clusterName)) + raw, err := json.Marshal(msg) if err != nil { return nil, fmt.Errorf("while marshaling message to trasfer it via gRPC: %w", err) } @@ -272,6 +275,7 @@ func (b *CloudTeams) handleStreamMessage(ctx context.Context, data *pb.CloudActi conversationRef := activity.GetCoversationReference(act) return &pb.AgentActivity{ Message: &pb.Message{ + MessageType: pb.MessageType_MESSAGE_EXECUTOR, TeamId: channel.teamID, ConversationId: conversationRef.Conversation.ID, ActivityId: conversationRef.ActivityID, // activity ID allows us to send it as a thread message @@ -300,7 +304,8 @@ func (b *CloudTeams) processMessage(ctx context.Context, act schema.Activity, ch }, Message: trimmedMsg, User: execute.UserInput{ - //Mention: "", // TODO(https://github.com/kubeshop/botkube-cloud/issues/677): set when adding interactivity support. + // TODO: we need to add support for mentions on cloud side. + //Mention: "", DisplayName: act.From.Name, }, }) @@ -313,8 +318,7 @@ func (b *CloudTeams) sendAgentActivity(ctx context.Context, msg interactive.Core b.log.Debugf("Sending message to channel %q: %+v", channel.ID, msg) msg.ReplaceBotNamePlaceholder(b.BotName(), api.BotNameWithClusterName(b.clusterName)) - - raw, err := json.Marshal(msg.Message) + raw, err := json.Marshal(msg) if err != nil { errs = multierror.Append(errs, fmt.Errorf("while proxing message via agent for channel id %q: %w", channel.ID, err)) continue @@ -322,6 +326,7 @@ func (b *CloudTeams) sendAgentActivity(ctx context.Context, msg interactive.Core act := &pb.AgentActivity{ Message: &pb.Message{ + MessageType: pb.MessageType_MESSAGE_SOURCE, TeamId: channel.teamID, ActivityId: "", // empty so it will be sent on root instead of sending as a thread message ConversationId: channel.ID, @@ -338,19 +343,31 @@ func (b *CloudTeams) sendAgentActivity(ctx context.Context, msg interactive.Core return errs.ErrorOrNil() } -func (b *CloudTeams) getChannelForActivity(act schema.Activity) (teamsCloudChannelConfigByID, bool) { - rawChannelID, exists := act.ChannelData[channelIDKeyName] - if !exists { - return teamsCloudChannelConfigByID{}, false +type channelData struct { + Channel struct { + ID string `mapstructure:"id"` + } `mapstructure:"channel"` + TeamsChannelID string `mapstructure:"teamsChannelId"` +} + +func (b *CloudTeams) getChannelForActivity(act schema.Activity) (teamsCloudChannelConfigByID, bool, error) { + var data channelData + err := mapstructure.Decode(act.ChannelData, &data) + if err != nil { + return teamsCloudChannelConfigByID{}, false, fmt.Errorf("while decoding data: %w", err) } - channelID, ok := rawChannelID.(string) - if !ok { - return teamsCloudChannelConfigByID{}, false + if data.Channel.ID == "" && data.TeamsChannelID == "" { + return teamsCloudChannelConfigByID{}, false, fmt.Errorf("cannot find channel id in: %s", formatx.StructDumper().Sdump(act.ChannelData)) } - channel, exists := b.getChannels()[channelID] - return channel, exists + id := data.TeamsChannelID + if id == "" { + id = data.Channel.ID + } + + channel, exists := b.getChannels()[id] + return channel, exists, nil } func (b *CloudTeams) getChannelsToNotify(sourceBindings []string) []teamsCloudChannelConfigByID { @@ -383,6 +400,7 @@ func (b *CloudTeams) setChannels(channels map[string]teamsCloudChannelConfigByID } func (b *CloudTeams) trimBotMention(msg string) string { + msg = strings.TrimSpace(msg) return b.botMentionRegex.ReplaceAllString(msg, "") } diff --git a/pkg/config/config.go b/pkg/config/config.go index 348661f57..c2d23b536 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -101,7 +101,7 @@ const ( ) func (c CommPlatformIntegration) IsInteractive() bool { - return c == SocketSlackCommPlatformIntegration || c == CloudSlackCommPlatformIntegration + return c == SocketSlackCommPlatformIntegration || c == CloudSlackCommPlatformIntegration || c == CloudTeamsCommPlatformIntegration } // String returns string platform name. diff --git a/pkg/execute/plugin_executor.go b/pkg/execute/plugin_executor.go index e8c65816d..22728faf9 100644 --- a/pkg/execute/plugin_executor.go +++ b/pkg/execute/plugin_executor.go @@ -102,7 +102,7 @@ func (e *PluginExecutor) Execute(ctx context.Context, bindings []string, slackSt Command: cmdCtx.CleanCmd, Configs: configs, Context: executor.ExecuteInputContext{ - IsInteractivitySupported: cmdCtx.Platform.IsInteractive(), + IsInteractivitySupported: e.isInteractivitySupported(cmdCtx), SlackState: slackState, KubeConfig: kubeconfig, Message: executor.Message{ @@ -146,6 +146,15 @@ func (e *PluginExecutor) Execute(ctx context.Context, bindings []string, slackSt return out, nil } +func (e *PluginExecutor) isInteractivitySupported(cmdCtx CommandContext) bool { + // TODO(https://github.com/kubeshop/botkube-cloud/issues/645): add support for kubectl builder + if strings.EqualFold(cmdCtx.CleanCmd, "kubectl") && cmdCtx.Platform == config.CloudTeamsCommPlatformIntegration { + // event though the cloud Teams support some interactivity, we do not support the command builder yet. + return false + } + return cmdCtx.Platform.IsInteractive() +} + func allMessagesMarkedAsSkip(msgs []api.Message) bool { for _, item := range msgs { if item.Type != api.SkipMessage { diff --git a/internal/executor/x/parser/space_table.go b/pkg/formatx/space_table.go similarity index 97% rename from internal/executor/x/parser/space_table.go rename to pkg/formatx/space_table.go index a61ec1d3a..4bcb79783 100644 --- a/internal/executor/x/parser/space_table.go +++ b/pkg/formatx/space_table.go @@ -1,11 +1,11 @@ -package parser +package formatx import ( "bufio" "strings" "unicode" - "github.com/kubeshop/botkube/internal/executor/x/mathx" + "github.com/kubeshop/botkube/pkg/mathx" ) // Table holds table data. diff --git a/internal/executor/x/parser/space_table_test.go b/pkg/formatx/space_table_test.go similarity index 99% rename from internal/executor/x/parser/space_table_test.go rename to pkg/formatx/space_table_test.go index b421a3d9d..ab06579ff 100644 --- a/internal/executor/x/parser/space_table_test.go +++ b/pkg/formatx/space_table_test.go @@ -1,4 +1,4 @@ -package parser +package formatx import ( "testing" diff --git a/internal/executor/x/mathx/int.go b/pkg/mathx/int.go similarity index 100% rename from internal/executor/x/mathx/int.go rename to pkg/mathx/int.go diff --git a/proto/cloud_teams.proto b/proto/cloud_teams.proto index fbf0d2482..b7b7f9913 100644 --- a/proto/cloud_teams.proto +++ b/proto/cloud_teams.proto @@ -9,11 +9,17 @@ message AgentActivity { Message message = 2; } +enum MessageType { + MESSAGE_EXECUTOR = 0; + MESSAGE_SOURCE = 1; +} + message Message { string teamId = 1; string activityId = 2; string conversationId = 3; - bytes data = 4; + MessageType messageType = 4; + bytes data = 5; } message CloudActivity {