mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-27 09:15:15 +00:00
slack: add reply and session struct
This commit is contained in:
parent
68a544383a
commit
b6f05cf772
|
@ -45,7 +45,7 @@ func (m *PositionInteraction) Commands(i *interact.Interact) {
|
|||
// send symbol options
|
||||
reply.Message("Choose your position")
|
||||
for _, symbol := range []string{"BTCUSDT", "ETHUSDT"} {
|
||||
reply.AddButton(symbol)
|
||||
reply.AddButton(symbol, symbol, symbol)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -68,7 +68,7 @@ func (m *PositionInteraction) Commands(i *interact.Interact) {
|
|||
|
||||
reply.Message("Choose or enter the percentage to close")
|
||||
for _, symbol := range []string{"25%", "50%", "100%"} {
|
||||
reply.AddButton(symbol)
|
||||
reply.AddButton(symbol, symbol, symbol)
|
||||
}
|
||||
|
||||
// send percentage options
|
||||
|
@ -85,7 +85,7 @@ func (m *PositionInteraction) Commands(i *interact.Interact) {
|
|||
|
||||
// send confirmation
|
||||
reply.Message("Are you sure to close the position?")
|
||||
reply.AddButton("Yes")
|
||||
reply.AddButton("Yes", "confirm", "yes")
|
||||
return nil
|
||||
}).Next(func(reply interact.Reply, confirm string) error {
|
||||
switch strings.ToLower(confirm) {
|
||||
|
|
|
@ -44,6 +44,12 @@ func NewCoreInteraction(environment *Environment, trader *Trader) *CoreInteracti
|
|||
|
||||
func (it *CoreInteraction) Commands(i *interact.Interact) {
|
||||
i.PrivateCommand("/sessions", "List Exchange Sessions", func(reply interact.Reply) error {
|
||||
switch r := reply.(type) {
|
||||
case *interact.SlackReply:
|
||||
// call slack specific api to build the reply object
|
||||
_ = r
|
||||
}
|
||||
|
||||
message := "Your connected sessions:\n"
|
||||
for name, session := range it.environment.Sessions() {
|
||||
message += "- " + name + " (" + session.ExchangeName.String() + ")\n"
|
||||
|
@ -56,7 +62,7 @@ func (it *CoreInteraction) Commands(i *interact.Interact) {
|
|||
i.PrivateCommand("/balances", "Show balances", func(reply interact.Reply) error {
|
||||
reply.Message("Please select an exchange session")
|
||||
for name := range it.environment.Sessions() {
|
||||
reply.AddButton(name)
|
||||
reply.AddButton(name, "session", name)
|
||||
}
|
||||
return nil
|
||||
}).Next(func(sessionName string, reply interact.Reply) error {
|
||||
|
@ -86,7 +92,7 @@ func (it *CoreInteraction) Commands(i *interact.Interact) {
|
|||
found := false
|
||||
for signature, strategy := range it.exchangeStrategies {
|
||||
if _, ok := strategy.(PositionReader); ok {
|
||||
reply.AddButton(signature)
|
||||
reply.AddButton(signature, "strategy", signature)
|
||||
found = true
|
||||
}
|
||||
}
|
||||
|
@ -131,7 +137,7 @@ func (it *CoreInteraction) Commands(i *interact.Interact) {
|
|||
found := false
|
||||
for signature, strategy := range it.exchangeStrategies {
|
||||
if _, ok := strategy.(PositionCloser); ok {
|
||||
reply.AddButton(signature)
|
||||
reply.AddButton(signature, strategy, signature)
|
||||
found = true
|
||||
}
|
||||
}
|
||||
|
@ -173,8 +179,8 @@ func (it *CoreInteraction) Commands(i *interact.Interact) {
|
|||
}
|
||||
|
||||
reply.Message("Choose or enter the percentage to close")
|
||||
for _, symbol := range []string{"5%", "25%", "50%", "80%", "100%"} {
|
||||
reply.AddButton(symbol)
|
||||
for _, p := range []string{"5%", "25%", "50%", "80%", "100%"} {
|
||||
reply.AddButton(p, "percentage", p)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
@ -1,8 +1,33 @@
|
|||
package interact
|
||||
|
||||
type Button struct {
|
||||
Text string
|
||||
Name string
|
||||
Value string
|
||||
}
|
||||
|
||||
type Reply interface {
|
||||
// Send sends the message directly to the client's session
|
||||
Send(message string)
|
||||
|
||||
// Message sets the message to the reply
|
||||
Message(message string)
|
||||
AddButton(text string)
|
||||
|
||||
// AddButton adds the button to the reply
|
||||
AddButton(text string, name, value string)
|
||||
|
||||
// RemoveKeyboard hides the keyboard from the client user interface
|
||||
RemoveKeyboard()
|
||||
}
|
||||
|
||||
// ButtonReply can be used if your reply needs button user interface.
|
||||
type ButtonReply interface {
|
||||
// AddButton adds the button to the reply
|
||||
AddButton(text string)
|
||||
}
|
||||
|
||||
// DialogReply can be used if your reply needs Dialog user interface
|
||||
type DialogReply interface {
|
||||
// AddButton adds the button to the reply
|
||||
Dialog(title, text string, buttons []string)
|
||||
}
|
||||
|
|
|
@ -3,6 +3,10 @@ package interact
|
|||
// Responder defines the logic of responding the message
|
||||
type Responder func(session Session, message string, reply Reply, ctxObjects ...interface{}) error
|
||||
|
||||
type CallbackResponder interface {
|
||||
SetCallbackResponder(responder Responder)
|
||||
}
|
||||
|
||||
type TextMessageResponder interface {
|
||||
SetTextMessageResponder(responder Responder)
|
||||
}
|
||||
|
|
|
@ -6,14 +6,108 @@ import (
|
|||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/slack-go/slack"
|
||||
"github.com/slack-go/slack/slackevents"
|
||||
"github.com/slack-go/slack/socketmode"
|
||||
)
|
||||
|
||||
|
||||
type SlackReply struct {
|
||||
// uuid is the unique id of this question
|
||||
// can be used as the callback id
|
||||
uuid string
|
||||
|
||||
session *SlackSession
|
||||
|
||||
client *slack.Client
|
||||
|
||||
message string
|
||||
|
||||
accessories []*slack.Accessory
|
||||
}
|
||||
|
||||
func (reply *SlackReply) Send(message string) {
|
||||
cID, tsID, err := reply.client.PostMessage(
|
||||
reply.session.ChannelID,
|
||||
slack.MsgOptionText(message, false),
|
||||
slack.MsgOptionAsUser(false), // Add this if you want that the bot would post message as a user, otherwise it will send response using the default slackbot
|
||||
)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Errorf("slack post message error: channel=%s thread=%s", cID, tsID)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (reply *SlackReply) Message(message string) {
|
||||
reply.message = message
|
||||
}
|
||||
|
||||
// RemoveKeyboard is not supported by Slack
|
||||
func (reply *SlackReply) RemoveKeyboard() {}
|
||||
|
||||
func (reply *SlackReply) AddButton(text string, name string, value string) {
|
||||
actionID := reply.uuid + ":" + value
|
||||
reply.accessories = append(reply.accessories, slack.NewAccessory(
|
||||
slack.NewButtonBlockElement(
|
||||
// action id should be unique
|
||||
actionID,
|
||||
value,
|
||||
&slack.TextBlockObject{
|
||||
Type: slack.PlainTextType,
|
||||
Text: text,
|
||||
},
|
||||
),
|
||||
))
|
||||
}
|
||||
|
||||
func (reply *SlackReply) build() map[string]interface{} {
|
||||
var blocks []slack.Block
|
||||
|
||||
blocks = append(blocks, slack.NewSectionBlock(
|
||||
&slack.TextBlockObject{
|
||||
Type: slack.MarkdownType,
|
||||
Text: reply.message,
|
||||
},
|
||||
nil, // fields
|
||||
nil, // accessory
|
||||
nil, // options
|
||||
))
|
||||
|
||||
blocks = append(blocks, slack.NewActionBlock("",
|
||||
slack.NewButtonBlockElement(
|
||||
"actionID",
|
||||
"value",
|
||||
slack.NewTextBlockObject(
|
||||
slack.PlainTextType, "text", true, false),
|
||||
)))
|
||||
|
||||
var payload = map[string]interface{}{
|
||||
"blocks": blocks,
|
||||
}
|
||||
return payload
|
||||
}
|
||||
|
||||
type SlackSession struct {
|
||||
BaseSession
|
||||
|
||||
ChannelID string
|
||||
UserID string
|
||||
|
||||
// questions is used to store the questions that we added in the reply
|
||||
// the key is the client generated callback id
|
||||
questions map[string]interface{}
|
||||
}
|
||||
|
||||
func NewSlackSession() *SlackSession {
|
||||
return &SlackSession{
|
||||
questions: make(map[string]interface{}),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SlackSession) ID() string {
|
||||
return fmt.Sprintf("%s-%s", s.UserID, s.ChannelID)
|
||||
}
|
||||
|
||||
type SlackSessionMap map[int64]*SlackSession
|
||||
|
@ -31,6 +125,8 @@ type Slack struct {
|
|||
textMessageResponder Responder
|
||||
|
||||
authorizedCallbacks []func(userSession *SlackSession)
|
||||
|
||||
eventsApiCallbacks []func(slackevents.EventsAPIEvent)
|
||||
}
|
||||
|
||||
func NewSlack(client *slack.Client) *Slack {
|
||||
|
@ -68,15 +164,15 @@ func (s *Slack) listen() {
|
|||
case socketmode.EventTypeEventsAPI:
|
||||
eventsAPIEvent, ok := evt.Data.(slackevents.EventsAPIEvent)
|
||||
if !ok {
|
||||
fmt.Printf("Ignored %+v\n", evt)
|
||||
|
||||
logrus.Debugf("ignored %+v", evt)
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Printf("Event received: %+v\n", eventsAPIEvent)
|
||||
|
||||
logrus.Debugf("event received: %+v", eventsAPIEvent)
|
||||
s.socket.Ack(*evt.Request)
|
||||
|
||||
s.EmitEventsApi(eventsAPIEvent)
|
||||
|
||||
switch eventsAPIEvent.Type {
|
||||
case slackevents.CallbackEvent:
|
||||
innerEvent := eventsAPIEvent.InnerEvent
|
||||
|
@ -95,20 +191,19 @@ func (s *Slack) listen() {
|
|||
case socketmode.EventTypeInteractive:
|
||||
callback, ok := evt.Data.(slack.InteractionCallback)
|
||||
if !ok {
|
||||
fmt.Printf("Ignored %+v\n", evt)
|
||||
|
||||
logrus.Debugf("ignored %+v", evt)
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Printf("Interaction received: %+v\n", callback)
|
||||
logrus.Debugf("interaction received: %+v", callback)
|
||||
|
||||
var payload interface{}
|
||||
|
||||
switch callback.Type {
|
||||
case slack.InteractionTypeBlockActions:
|
||||
// See https://api.slack.com/apis/connections/socket-implement#button
|
||||
logrus.Debugf("button clicked!")
|
||||
|
||||
s.socket.Debugf("button clicked!")
|
||||
case slack.InteractionTypeShortcut:
|
||||
case slack.InteractionTypeViewSubmission:
|
||||
// See https://api.slack.com/apis/connections/socket-implement#modal
|
||||
|
@ -118,47 +213,44 @@ func (s *Slack) listen() {
|
|||
}
|
||||
|
||||
s.socket.Ack(*evt.Request, payload)
|
||||
|
||||
case socketmode.EventTypeSlashCommand:
|
||||
cmd, ok := evt.Data.(slack.SlashCommand)
|
||||
if !ok {
|
||||
fmt.Printf("Ignored %+v\n", evt)
|
||||
|
||||
logrus.Debugf("ignored %+v", evt)
|
||||
continue
|
||||
}
|
||||
|
||||
s.socket.Debugf("Slash command received: %+v", cmd)
|
||||
logrus.Debugf("slash command received: %+v", cmd)
|
||||
|
||||
payload := map[string]interface{}{
|
||||
"blocks": []slack.Block{
|
||||
slack.NewSectionBlock(
|
||||
&slack.TextBlockObject{
|
||||
Type: slack.MarkdownType,
|
||||
Text: "foo",
|
||||
},
|
||||
nil,
|
||||
slack.NewAccessory(
|
||||
slack.NewButtonBlockElement(
|
||||
"",
|
||||
"somevalue",
|
||||
&slack.TextBlockObject{
|
||||
Type: slack.PlainTextType,
|
||||
Text: "bar",
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
}}
|
||||
session := s.newSession(evt)
|
||||
reply := s.newReply(session)
|
||||
if err := s.textMessageResponder(session, "", reply); err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
payload := reply.build()
|
||||
s.socket.Ack(*evt.Request, payload)
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "Unexpected event type received: %s\n", evt.Type)
|
||||
logrus.Debugf("unexpected event type received: %s", evt.Type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Slack) newSession(evt socketmode.Event) *SlackSession {
|
||||
return NewSlackSession()
|
||||
}
|
||||
|
||||
func (s *Slack) newReply(session *SlackSession) *SlackReply {
|
||||
return &SlackReply{
|
||||
uuid: uuid.New().String(),
|
||||
session: session,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Slack) Start(ctx context.Context) {
|
||||
go s.listen()
|
||||
if err := s.socket.Run() ; err != nil {
|
||||
if err := s.socket.Run(); err != nil {
|
||||
logrus.WithError(err).Errorf("slack socketmode error")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,7 +70,7 @@ func (r *TelegramReply) RemoveKeyboard() {
|
|||
r.set = true
|
||||
}
|
||||
|
||||
func (r *TelegramReply) AddButton(text string) {
|
||||
func (r *TelegramReply) AddButton(text string, name string, value string) {
|
||||
var button = r.menu.Text(text)
|
||||
if len(r.buttons) == 0 {
|
||||
r.buttons = append(r.buttons, []telebot.Btn{})
|
||||
|
@ -101,6 +101,8 @@ type Telegram struct {
|
|||
// textMessageResponder is used for interact to register its message handler
|
||||
textMessageResponder Responder
|
||||
|
||||
callbackResponder CallbackResponder
|
||||
|
||||
commands []*Command
|
||||
|
||||
authorizedCallbacks []func(s *TelegramSession)
|
||||
|
@ -114,8 +116,12 @@ func NewTelegram(bot *telebot.Bot) *Telegram {
|
|||
}
|
||||
}
|
||||
|
||||
func (tm *Telegram) SetTextMessageResponder(textMessageResponder Responder) {
|
||||
tm.textMessageResponder = textMessageResponder
|
||||
func (tm *Telegram) SetCallbackResponder(responder CallbackResponder) {
|
||||
tm.callbackResponder = responder
|
||||
}
|
||||
|
||||
func (tm *Telegram) SetTextMessageResponder(responder Responder) {
|
||||
tm.textMessageResponder = responder
|
||||
}
|
||||
|
||||
func (tm *Telegram) Start(context.Context) {
|
||||
|
|
Loading…
Reference in New Issue
Block a user