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 }