github.com/hungdoo/bot@v0.0.0-20240325145135-dd1f386f7b81/src/services/telecommands/factory.go (about)

     1  package telecommands
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"fmt"
     7  	"strings"
     8  	"time"
     9  
    10  	"github.com/hungdoo/bot/src/common"
    11  	"github.com/hungdoo/bot/src/packages/command/balance"
    12  	"github.com/hungdoo/bot/src/packages/command/bybitido"
    13  	command "github.com/hungdoo/bot/src/packages/command/common"
    14  	"github.com/hungdoo/bot/src/packages/command/contract"
    15  	"github.com/hungdoo/bot/src/packages/command/tomb"
    16  	"github.com/hungdoo/bot/src/packages/db"
    17  	"github.com/hungdoo/bot/src/packages/log"
    18  	"go.mongodb.org/mongo-driver/bson"
    19  )
    20  
    21  type CommandMap map[string]command.ICommand
    22  
    23  func (cm *CommandMap) Search(nameSubString string) []command.ICommand {
    24  	list := make([]command.ICommand, 0)
    25  	for _, v := range *cm {
    26  		if strings.Contains(v.GetName(), nameSubString) {
    27  			list = append(list, v)
    28  		}
    29  	}
    30  	return list
    31  }
    32  func (cm *CommandMap) ToList() []command.ICommand {
    33  	list := make([]command.ICommand, 0)
    34  	for _, v := range *cm {
    35  		list = append(list, v)
    36  	}
    37  	return list
    38  }
    39  func (cm *CommandMap) Filter(cmdType command.CommandType) *CommandMap {
    40  	filtered := CommandMap{}
    41  	for _, v := range *cm {
    42  		if v.GetType() == cmdType {
    43  			filtered[v.GetName()] = v
    44  		}
    45  	}
    46  	return &filtered
    47  }
    48  func (cm *CommandMap) ToActiveList() []command.ICommand {
    49  	actives := make([]command.ICommand, 0)
    50  	for _, v := range *cm {
    51  		if v.IsEnabled() {
    52  			actives = append(actives, v)
    53  		}
    54  	}
    55  	return actives
    56  }
    57  
    58  // @dev internal facing controller
    59  // Responsibility: validates, processes commands & handles errors
    60  type CommandFactory struct {
    61  	commands CommandMap
    62  }
    63  
    64  func NewCommandFactory() CommandFactory {
    65  	return CommandFactory{commands: map[string]command.ICommand{}}
    66  }
    67  
    68  func (c *CommandFactory) Add(cmdType command.CommandType, messages []string) string {
    69  	if len(messages) < 1 {
    70  		return "Add needs at least 1 params"
    71  	}
    72  	name := messages[0]
    73  	var data []string
    74  	if len(messages) > 1 {
    75  		data = messages[1:]
    76  	}
    77  	if v, ok := c.commands[name]; ok {
    78  		if err := v.SetData(data); err != nil {
    79  			return err.Error()
    80  		}
    81  		if err := UpdateCmd(v); err != nil {
    82  			return err.Error()
    83  		}
    84  		return fmt.Sprintf("Command [%v] updated", name)
    85  	} else {
    86  		var newCommand command.ICommand
    87  
    88  		switch cmdType {
    89  		case command.ContractCall:
    90  			newCommand = &contract.ContractCommand{
    91  				Id: name,
    92  				Command: command.Command{
    93  					Name:     name,
    94  					Enabled:  true,
    95  					Type:     cmdType,
    96  					IdleTime: time.Second * 60,
    97  				},
    98  			}
    99  		case command.Tomb:
   100  			newCommand = &tomb.TombCommand{
   101  				Id: name,
   102  				Command: command.Command{
   103  					Name:     name,
   104  					Enabled:  true,
   105  					Type:     cmdType,
   106  					IdleTime: time.Minute * 1,
   107  				},
   108  			}
   109  		case command.Balance:
   110  			newCommand = &balance.BalanceCommand{
   111  				Id: name,
   112  				Command: command.Command{
   113  					Name:     name,
   114  					Enabled:  true,
   115  					Type:     cmdType,
   116  					IdleTime: time.Second * 30,
   117  				},
   118  			}
   119  		case command.BybitIdo:
   120  			newCommand = &bybitido.IdoCommand{
   121  				Id: name,
   122  				Command: command.Command{
   123  					Name:     name,
   124  					Enabled:  true,
   125  					Type:     cmdType,
   126  					IdleTime: time.Hour * 1,
   127  				},
   128  			}
   129  		}
   130  		if newCommand == nil {
   131  			return fmt.Sprintf("Command [%v] failed to add", name)
   132  		}
   133  
   134  		if err := newCommand.SetData(data); err != nil {
   135  			return err.Error()
   136  		}
   137  
   138  		if err := StoreCmd(newCommand); err != nil {
   139  			return err.Error()
   140  		}
   141  		c.commands[name] = newCommand
   142  		return fmt.Sprintf("Command [%v] added", name)
   143  	}
   144  }
   145  
   146  func (c *CommandFactory) Remove(name string) string {
   147  	if _, ok := c.commands[name]; ok {
   148  		deleteQuery := bson.M{"_id": name}
   149  		if err := db.GetDb().Delete("commands", deleteQuery); err != nil {
   150  			return err.Error()
   151  		}
   152  		delete(c.commands, name)
   153  		return fmt.Sprintf("Command [%v] removed", name)
   154  	}
   155  	return fmt.Sprintf("Command [%v] not found", name)
   156  }
   157  
   158  func (c *CommandFactory) Show(name string) string {
   159  	searchedList := c.commands.Search(name)
   160  	if len(searchedList) != 0 {
   161  		b, err := json.MarshalIndent(searchedList, "", "  ")
   162  		if err != nil {
   163  			return err.Error()
   164  		}
   165  		return string(b)
   166  	}
   167  	return fmt.Sprintf("Command [%v] not found", name)
   168  }
   169  
   170  func (c *CommandFactory) Exec(cmdType command.CommandType, task string, opts ...string) (res string, err error) {
   171  	filtered := c.commands.Filter(cmdType)
   172  	var executedCmds []command.ICommand
   173  	var executedResults []string
   174  
   175  	switch cmdType {
   176  	case command.Tomb, command.BybitIdo:
   177  		subCmd := ""
   178  		if len(opts) != 0 {
   179  			subCmd = opts[0]
   180  		}
   181  		searchedList := filtered.Search(task)
   182  		if len(searchedList) == 0 {
   183  			return fmt.Sprintf("Task [%v] not found", task), nil
   184  		}
   185  
   186  		for _, cmd := range searchedList {
   187  			result, execErr := cmd.Execute(true, subCmd)
   188  			if execErr != nil && execErr.Level >= common.Error {
   189  				log.GeneralLogger.Printf("Job [%s] exec failed: [%s]", cmd.GetName(), execErr.Error())
   190  				if execErr.Level >= common.Critical {
   191  					executedResults = append(executedResults, fmt.Sprintf("%v with reason %s", c.Off(cmd.GetName()), execErr.Error()))
   192  				} else {
   193  					executedResults = append(executedResults, fmt.Sprintf("%v failed with [%s:%s]", cmd.GetName(), execErr.Level, execErr.Error()))
   194  				}
   195  				continue
   196  			}
   197  
   198  			// exec seccessfully -> update db
   199  			if result != "" {
   200  				cmd.SetDisplayMsg(result)
   201  				executedResults = append(executedResults, fmt.Sprintf("[%s]\n%s", cmd.GetName(), result))
   202  			}
   203  			executedCmds = append(executedCmds, cmd)
   204  		}
   205  
   206  	default:
   207  		searchedList := filtered.Search(task)
   208  		if len(searchedList) == 0 {
   209  			return fmt.Sprintf("Task [%v] not found", task), nil
   210  		}
   211  
   212  		for _, cmd := range searchedList {
   213  			result, execErr := cmd.Execute(true, "")
   214  			if execErr != nil && execErr.Level >= common.Error {
   215  				log.GeneralLogger.Printf("Job [%s] exec failed: [%s]", cmd.GetName(), execErr.Error())
   216  				if execErr.Level >= common.Critical {
   217  					executedResults = append(executedResults, fmt.Sprintf("%v with reason %s", c.Off(cmd.GetName()), execErr.Error()))
   218  				} else {
   219  					executedResults = append(executedResults, execErr.Error())
   220  				}
   221  				continue
   222  			}
   223  
   224  			// exec seccessfully -> update db
   225  			if result != "" {
   226  				cmd.SetDisplayMsg(result)
   227  				executedResults = append(executedResults, fmt.Sprintf("[%s]\n%s", cmd.GetName(), result))
   228  			}
   229  			executedCmds = append(executedCmds, cmd)
   230  		}
   231  	}
   232  	if len(executedCmds) != 0 {
   233  		UpdateMultiCmd(executedCmds)
   234  	}
   235  	return string(strings.Join(executedResults, "\n")), nil
   236  }
   237  
   238  func (c *CommandFactory) List(showAll bool) string {
   239  	enabledCMD := []string{}
   240  	disabledCMD := []string{}
   241  	for _, cmd := range c.commands {
   242  		if cmd.IsEnabled() {
   243  			enabledCMD = append(enabledCMD, fmt.Sprintf("[+] %v", cmd.GetOverview()))
   244  		} else {
   245  			disabledCMD = append(disabledCMD, fmt.Sprintf("[-] %v", cmd.GetOverview()))
   246  		}
   247  	}
   248  
   249  	if showAll {
   250  		return strings.Join(append(append(enabledCMD, ""), disabledCMD...), "\n")
   251  	} else {
   252  		return strings.Join(enabledCMD, "\n")
   253  	}
   254  }
   255  
   256  func (c *CommandFactory) On(name string) string {
   257  	if v, ok := c.commands[name]; ok {
   258  		v.SetEnabled(true)
   259  		if err := UpdateCmd(v); err != nil {
   260  			return err.Error()
   261  		}
   262  		return fmt.Sprintf("Command [%v] on", name)
   263  	}
   264  	return fmt.Sprintf("Command [%v] not found", name)
   265  }
   266  
   267  func (c *CommandFactory) Off(name string) string {
   268  	if v, ok := c.commands[name]; ok {
   269  		v.SetEnabled(false)
   270  		if err := UpdateCmd(v); err != nil {
   271  			return err.Error()
   272  		}
   273  		return fmt.Sprintf("Command [%v] off", name)
   274  	}
   275  	return fmt.Sprintf("Command [%v] not found", name)
   276  }
   277  
   278  func (c *CommandFactory) SetInterval(name string, interval time.Duration) string {
   279  	if v, ok := c.commands[name]; ok {
   280  		v.SetIdleTime(interval)
   281  		if err := UpdateCmd(v); err != nil {
   282  			return err.Error()
   283  		}
   284  		return fmt.Sprintf("Command [%v] interval: [%v]", name, interval)
   285  	}
   286  	return fmt.Sprintf("Command [%v] not found", name)
   287  }
   288  
   289  func (c *CommandFactory) GetJobs() ([]command.ICommand, error) {
   290  	cursor, err := db.GetDb().Find("commands", bson.D{})
   291  	if err != nil {
   292  		return nil, err
   293  	}
   294  	defer cursor.Close(context.TODO())
   295  
   296  	for cursor.Next(context.TODO()) {
   297  		// cmd := command.Command{}
   298  		var result bson.M
   299  		if err := cursor.Decode(&result); err != nil {
   300  			return nil, err
   301  		}
   302  
   303  		// Unmarshal the BSON document into the custom command object
   304  		// Convert the result map to BSON representation
   305  		resultBytes, err := bson.Marshal(result)
   306  		if err != nil {
   307  			return nil, err
   308  		}
   309  
   310  		cmd := &command.CustomCommand{}
   311  		err = bson.Unmarshal(resultBytes, cmd)
   312  		if err != nil {
   313  			return nil, err
   314  		}
   315  
   316  		var iCmd interface{}
   317  		switch cmd.Type {
   318  		case command.ContractCall:
   319  			iCmd = &contract.ContractCommand{}
   320  		case command.Tomb:
   321  			iCmd = &tomb.TombCommand{}
   322  		case command.Balance:
   323  			iCmd = &balance.BalanceCommand{}
   324  		case command.BybitIdo:
   325  			iCmd = &bybitido.IdoCommand{}
   326  		default:
   327  			log.GeneralLogger.Printf("unsupported cmd[%+v] ", cmd)
   328  			continue
   329  		}
   330  		err = bson.Unmarshal(resultBytes, iCmd)
   331  		if err != nil {
   332  			return nil, err
   333  		}
   334  
   335  		_command, ok := iCmd.(command.ICommand)
   336  		if !ok {
   337  			return nil, fmt.Errorf("cannot typecast cmd[%v] to ICommand", cmd)
   338  		}
   339  
   340  		name := _command.GetName()
   341  		c.commands[name] = _command
   342  		b, err := json.MarshalIndent(_command, "", "  ")
   343  		if err != nil {
   344  			return nil, fmt.Errorf("cannot MarshalIndent _command[%v]", _command)
   345  		}
   346  		log.GeneralLogger.Printf("Loaded Command [%v]\n", string(b))
   347  	}
   348  
   349  	return c.commands.ToActiveList(), nil
   350  }