├── .gitignore ├── README.md ├── cmd ├── datastores │ ├── profilestore │ │ └── profilestore.go │ ├── proxystore │ │ └── proxystore.go │ └── taskstore │ │ ├── customunmarshal.go │ │ └── taskstore.go ├── init │ └── init.go └── taskengine │ ├── entities.go │ ├── helpers.go │ └── taskengine.go ├── data ├── profiles.json ├── proxies.json └── tasks.json ├── go.mod ├── go.sum ├── internal ├── activityapi │ ├── entities.go │ ├── statusLogger.go │ └── statusLogger_test.go ├── certs │ └── rootcerts.go ├── client │ ├── client.go │ ├── jar.go │ ├── request.go │ └── response.go ├── constants │ └── sites.go ├── errors │ └── Handler.go ├── flags │ └── flags.go ├── helpers │ ├── datastore │ │ └── store.go │ └── utilities │ │ ├── arrays.go │ │ ├── cardidentification │ │ └── card.go │ │ ├── print.go │ │ ├── random │ │ ├── random.go │ │ └── slices.go │ │ └── taskParsing.go ├── monitorengine │ ├── entities.go │ └── monitorengine.go ├── monitortask │ └── monitortask.go ├── products │ └── product.go ├── profiles │ └── profile.go ├── proxies │ └── proxy.go └── tasks │ └── task.go ├── main.go ├── monitors └── .gitkeep └── sites ├── louisvuitton ├── entities.go └── louisvuitton.go └── walmart ├── headers.go ├── walmart.go └── walmartentities.go /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CicadaAIO 2 | 3 | Go task framework used for Cicada AIO. 4 | 5 | Finally decided to open source this, as it's a little shame for this code to go to waste. Apologies for some of the mess, the code was essentially in-between versions and hastily patched to be deployable for the LV drop. 6 | 7 | Uses goroutines and custom networking solutions (TLS and HTTP/2 client not open sourced, there are plenty already out there) to automate purchasing on a variety of websites. Highly extensible to new sites/features, and capable of running thousands of concurrent tasks while still remaining highly performant. 8 | 9 | Used to have a UI/GRPC integration as well, but this was removed prior to open sourcing. Not meant to be run as some imports are in private repositories, although this can be easily fixed. I've removed all features/packages not relevant to the task framework. 10 | 11 | All modules removed except Louis Vuitton and Walmart. 12 | 13 | # Commands 14 | 15 | ``` 16 | go run main.go -r group_id 17 | ``` 18 | 19 | Group_ID should be set for a task group in `Data/tasks.json`. 20 | 21 | On Linux: 22 | 23 | ``` 24 | go build . && ./framework -r "all" 25 | ``` 26 | 27 | Credits to https://github.com/g4cko, https://github.com/trrai, as well as https://github.com/Epacity for the LV module code. 28 | 29 | Also big shoutout to https://github.com/amcode21 for teaching me what I know. 30 | -------------------------------------------------------------------------------- /cmd/datastores/profilestore/profilestore.go: -------------------------------------------------------------------------------- 1 | package profilestore 2 | 3 | import ( 4 | "fmt" 5 | 6 | store "github.com/umasii/bot-framework/internal/helpers/datastore" 7 | profiles "github.com/umasii/bot-framework/internal/profiles" 8 | 9 | goErrors "errors" 10 | 11 | errors "github.com/umasii/bot-framework/internal/errors" 12 | ) 13 | 14 | var profileGroupList []*profiles.ProfileGroup 15 | 16 | func AddProfile(profile *profiles.Profile) error { 17 | profileGroup, err := GetProfileGroupByID(profile.GroupID) 18 | if err != nil { 19 | return errors.Handler(goErrors.New("Failed to Add Profile")) 20 | } 21 | profile.ProfileID = getNextProfileID(&profileGroup.Profiles) 22 | profileGroup.Profiles = append(profileGroup.Profiles, *profile) 23 | store.Write(&profileGroupList, "profiles") 24 | return nil 25 | } 26 | 27 | func AddProfileGroup(profileGroup *profiles.ProfileGroup) { 28 | profileGroups := GetProfileGroups() 29 | profileGroup.GroupID = getNextProfileGroupID(&profileGroups) 30 | if profileGroup.Profiles == nil { 31 | profileGroup.Profiles = []profiles.Profile{} 32 | } 33 | profileGroups = append(profileGroups, *profileGroup) 34 | store.Write(&profileGroups, "profiles") 35 | } 36 | 37 | func GetProfileGroups() []profiles.ProfileGroup { 38 | var existingProfileGroups []profiles.ProfileGroup 39 | store.Read(&existingProfileGroups, "profiles") 40 | return existingProfileGroups 41 | } 42 | 43 | func GetProfileByID(groupID, profileID int) (*profiles.Profile, error) { 44 | profileGroup, err := GetProfileGroupByID(groupID) 45 | 46 | if err != nil { 47 | return nil, errors.Handler(goErrors.New(fmt.Sprintf("Could not locate Profile Group (GroupID: %v)", groupID))) 48 | } 49 | 50 | for i := range profileGroup.Profiles { 51 | if profileGroup.Profiles[i].ProfileID == profileID { 52 | return &profileGroup.Profiles[i], nil 53 | } 54 | } 55 | 56 | return nil, errors.Handler(goErrors.New(fmt.Sprintf("Could not locate Profile (GroupID: %v, ProfileID: %v)", groupID, profileID))) 57 | 58 | } 59 | 60 | func GetProfileGroupByID(profileGroupID int) (*profiles.ProfileGroup, error) { 61 | store.Read(&profileGroupList, "profiles") 62 | for i := range profileGroupList { 63 | if profileGroupList[i].GroupID == profileGroupID { 64 | return profileGroupList[i], nil 65 | } 66 | } 67 | return nil, errors.Handler(goErrors.New(fmt.Sprintf("Profile Group with ID %v was not found", profileGroupID))) 68 | } 69 | 70 | func getNextProfileID(profiles *[]profiles.Profile) int { 71 | if len(*profiles) > 0 { 72 | lastProfile := (*profiles)[len(*profiles)-1] 73 | return lastProfile.ProfileID + 1 74 | } 75 | return 1 76 | } 77 | 78 | func getNextProfileGroupID(profileGroups *[]profiles.ProfileGroup) int { 79 | if len(*profileGroups) > 0 { 80 | lastProfileGroup := (*profileGroups)[len(*profileGroups)-1] 81 | return lastProfileGroup.GroupID + 1 82 | } else { 83 | return 1 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /cmd/datastores/proxystore/proxystore.go: -------------------------------------------------------------------------------- 1 | package proxystore 2 | 3 | import ( 4 | goErrors "errors" 5 | "fmt" 6 | 7 | errors "github.com/umasii/bot-framework/internal/errors" 8 | store "github.com/umasii/bot-framework/internal/helpers/datastore" 9 | proxies "github.com/umasii/bot-framework/internal/proxies" 10 | ) 11 | 12 | var proxyGroupList []*proxies.ProxyGroup 13 | 14 | func AddProxy(groupID int, proxy string) error { 15 | proxyGroup, err := GetProxyGroupByID(groupID) 16 | if err != nil { 17 | return errors.Handler(goErrors.New("Failed to Add Proxy")) 18 | } 19 | proxyGroup.Proxies = append(proxyGroup.Proxies, proxy) 20 | store.Write(&proxyGroupList, "proxies") 21 | return nil 22 | 23 | } 24 | 25 | func AddProxyGroup(proxyGroup *proxies.ProxyGroup) { 26 | proxyGroups := GetProxyGroups() 27 | proxyGroup.GroupID = getNextProxyGroupID(&proxyGroups) 28 | if proxyGroup.Proxies == nil { 29 | proxyGroup.Proxies = []string{} 30 | } 31 | proxyGroups = append(proxyGroups, *proxyGroup) 32 | store.Write(&proxyGroups, "proxies") 33 | } 34 | 35 | func GetProxyGroups() []proxies.ProxyGroup { 36 | var existingProxyGroups []proxies.ProxyGroup 37 | store.Read(&existingProxyGroups, "proxies") 38 | return existingProxyGroups 39 | } 40 | 41 | func GetProxyGroupByID(proxyGroupID int) (*proxies.ProxyGroup, error) { 42 | store.Read(&proxyGroupList, "proxies") 43 | for i := range proxyGroupList { 44 | if proxyGroupList[i].GroupID == proxyGroupID { 45 | return proxyGroupList[i], nil 46 | } 47 | } 48 | return nil, errors.Handler(goErrors.New(fmt.Sprintf("Proxy Group with ID %v was not found", proxyGroupID))) 49 | } 50 | 51 | func GetMonitoringProxies() []string { 52 | var existingProxyGroups []proxies.ProxyGroup 53 | var monitoringProxies []string 54 | store.Read(&existingProxyGroups, "proxies") 55 | 56 | for i := range existingProxyGroups { 57 | if existingProxyGroups[i].IsMonitoringProxies { 58 | monitoringProxies = append(monitoringProxies, existingProxyGroups[i].Proxies...) 59 | } 60 | } 61 | 62 | return monitoringProxies 63 | } 64 | 65 | func getNextProxyGroupID(proxyGroups *[]proxies.ProxyGroup) int { 66 | if len(*proxyGroups) > 0 { 67 | lastProxyGroup := (*proxyGroups)[len(*proxyGroups)-1] 68 | return lastProxyGroup.GroupID + 1 69 | } 70 | return 1 71 | } 72 | -------------------------------------------------------------------------------- /cmd/datastores/taskstore/customunmarshal.go: -------------------------------------------------------------------------------- 1 | package taskstore 2 | 3 | import ( 4 | "encoding/json" 5 | "io/ioutil" 6 | "os" 7 | "reflect" 8 | 9 | constants "github.com/umasii/bot-framework/internal/constants" 10 | errors "github.com/umasii/bot-framework/internal/errors" 11 | store "github.com/umasii/bot-framework/internal/helpers/datastore" 12 | tasks "github.com/umasii/bot-framework/internal/tasks" 13 | ) 14 | 15 | type TaskGroupUnpacker struct { 16 | GroupName string 17 | GroupID int 18 | Tasks []TaskUnpacker 19 | } 20 | 21 | type TaskUnpacker struct { 22 | Site string 23 | Value tasks.IBotTask 24 | } 25 | 26 | func (c *TaskUnpacker) UnmarshalJSON(data []byte) error { 27 | 28 | value, err := UnmarshalCustomValue(data, "Site", constants.SITES) 29 | 30 | if err != nil { 31 | return errors.Handler(err) 32 | } 33 | 34 | c.Value = value 35 | 36 | return nil 37 | } 38 | 39 | func UnmarshalCustomValue(data []byte, typeJsonField string, customTypes map[string]reflect.Type) (tasks.IBotTask, error) { 40 | m := map[string]interface{}{} 41 | 42 | if err := json.Unmarshal(data, &m); err != nil { 43 | return nil, errors.Handler(err) 44 | } 45 | 46 | typeName := m[typeJsonField].(string) 47 | 48 | var value tasks.IBotTask 49 | if ty, found := customTypes[typeName]; found { 50 | value = reflect.New(ty).Interface().(tasks.IBotTask) 51 | } 52 | 53 | valueBytes, err := json.Marshal(m) 54 | 55 | if err != nil { 56 | return nil, errors.Handler(err) 57 | } 58 | 59 | if err = json.Unmarshal(valueBytes, &value); err != nil { 60 | return nil, errors.Handler(err) 61 | } 62 | 63 | return value, errors.Handler(err) 64 | } 65 | 66 | func readTasksJSONFile() ([]byte, error) { 67 | jsonFile, err := os.Open(store.GetStoreFilePath("tasks")) 68 | 69 | if err != nil { 70 | return []byte{}, errors.Handler(err) 71 | } 72 | 73 | defer jsonFile.Close() 74 | 75 | byteValue, _ := ioutil.ReadAll(jsonFile) 76 | 77 | return byteValue, nil 78 | } 79 | 80 | func CustomUnmarshalTasksFile() ([]tasks.TaskGroup, error) { 81 | taskGroups := []tasks.TaskGroup{} 82 | 83 | jsonData, err := readTasksJSONFile() 84 | 85 | if err != nil { 86 | return taskGroups, errors.Handler(err) 87 | } 88 | 89 | var unpackedTaskGroups []TaskGroupUnpacker 90 | 91 | err = json.Unmarshal(jsonData, &unpackedTaskGroups) 92 | 93 | if err != nil { 94 | return taskGroups, errors.Handler(err) 95 | } 96 | 97 | for i := range unpackedTaskGroups { 98 | 99 | currentTaskGroup := tasks.TaskGroup{ 100 | GroupName: unpackedTaskGroups[i].GroupName, 101 | GroupID: unpackedTaskGroups[i].GroupID, 102 | Tasks: []tasks.IBotTask{}, 103 | } 104 | 105 | for j := range unpackedTaskGroups[i].Tasks { 106 | currentTaskGroup.Tasks = append(currentTaskGroup.Tasks, unpackedTaskGroups[i].Tasks[j].Value) 107 | } 108 | 109 | taskGroups = append(taskGroups, currentTaskGroup) 110 | 111 | } 112 | 113 | return taskGroups, nil 114 | } 115 | -------------------------------------------------------------------------------- /cmd/datastores/taskstore/taskstore.go: -------------------------------------------------------------------------------- 1 | package taskstore 2 | 3 | import ( 4 | goErrors "errors" 5 | "fmt" 6 | "reflect" 7 | 8 | errors "github.com/umasii/bot-framework/internal/errors" 9 | store "github.com/umasii/bot-framework/internal/helpers/datastore" 10 | tasks "github.com/umasii/bot-framework/internal/tasks" 11 | ) 12 | 13 | var taskGroupList []tasks.TaskGroup 14 | 15 | func AddTask(task tasks.IBotTask) error { 16 | taskGroup, err := GetTaskGroupByID(task.Get().GroupID) 17 | if err != nil { 18 | return errors.Handler(goErrors.New("Failed to Add Task")) 19 | } 20 | 21 | setNewTaskID(taskGroup, &task) 22 | 23 | taskGroup.Tasks = append(taskGroup.Tasks, task) 24 | store.Write(&taskGroupList, "tasks") 25 | return nil 26 | } 27 | 28 | func AddTaskGroup(taskGroup *tasks.TaskGroup) { 29 | taskGroups := GetTaskGroups() 30 | taskGroup.GroupID = getNextTaskGroupID(&taskGroups) 31 | if taskGroup.Tasks == nil { 32 | taskGroup.Tasks = []tasks.IBotTask{} 33 | } 34 | taskGroups = append(taskGroups, *taskGroup) 35 | store.Write(&taskGroups, "tasks") 36 | } 37 | 38 | func GetTaskByID(groupID, taskID int) (*tasks.IBotTask, error) { 39 | taskGroup, err := GetTaskGroupByID(groupID) 40 | 41 | if err != nil { 42 | return nil, errors.Handler(goErrors.New(fmt.Sprintf("Could not locate Task Group (GroupID: %v)", groupID))) 43 | } 44 | 45 | for i := range taskGroup.Tasks { 46 | if taskGroup.Tasks[i].Get().TaskID == taskID { 47 | return &taskGroup.Tasks[i], nil 48 | } 49 | } 50 | 51 | return nil, errors.Handler(goErrors.New(fmt.Sprintf("Could not locate Task (GroupID: %v, TaskID: %v)", groupID, taskID))) 52 | 53 | } 54 | 55 | func GetTaskGroups() []tasks.TaskGroup { 56 | taskGroups, err := CustomUnmarshalTasksFile() 57 | 58 | if err != nil { 59 | errors.Handler(err) 60 | } 61 | 62 | return taskGroups 63 | } 64 | 65 | func GetTaskGroupByID(taskGroupID int) (*tasks.TaskGroup, error) { 66 | taskGroupList = GetTaskGroups() 67 | 68 | for i := range taskGroupList { 69 | if taskGroupList[i].GroupID == taskGroupID { 70 | return &taskGroupList[i], nil 71 | } 72 | } 73 | 74 | return nil, errors.Handler(goErrors.New(fmt.Sprintf("Task Group with ID %v was not found", taskGroupID))) 75 | } 76 | 77 | func setNewTaskID(taskGroup *tasks.TaskGroup, task *tasks.IBotTask) { 78 | taskID := reflect.ValueOf(*task).Elem() 79 | field := taskID.FieldByName("TaskID") 80 | if field.IsValid() { 81 | field.SetInt(int64(getNextTaskID(&taskGroup.Tasks))) 82 | } 83 | } 84 | 85 | func getNextTaskID(tasks *[]tasks.IBotTask) int { 86 | if len(*tasks) > 0 { 87 | lastTask := (*tasks)[len(*tasks)-1] 88 | return lastTask.Get().TaskID + 1 89 | } 90 | return 1 91 | } 92 | 93 | func getNextTaskGroupID(taskGroups *[]tasks.TaskGroup) int { 94 | if len(*taskGroups) > 0 { 95 | lastTaskGroup := (*taskGroups)[len(*taskGroups)-1] 96 | return lastTaskGroup.GroupID + 1 97 | } else { 98 | return 1 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /cmd/init/init.go: -------------------------------------------------------------------------------- 1 | package init 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "log" 7 | "math/rand" 8 | "os" 9 | "strconv" 10 | "strings" 11 | "time" 12 | 13 | "github.com/getsentry/sentry-go" 14 | taskengine "github.com/umasii/bot-framework/cmd/taskengine" 15 | errors "github.com/umasii/bot-framework/internal/errors" 16 | flags "github.com/umasii/bot-framework/internal/flags" 17 | ) 18 | 19 | func InitSentry() { 20 | err := sentry.Init(sentry.ClientOptions{ 21 | Dsn: "", 22 | AttachStacktrace: true, 23 | }) 24 | if err != nil { 25 | log.Fatalf("sentry.Init: %s", err) 26 | } 27 | defer sentry.Flush(2 * time.Second) 28 | } 29 | 30 | func InitCicada() { 31 | rand.Seed(time.Now().Unix()) 32 | 33 | InitSentry() 34 | defer sentry.Recover() 35 | 36 | log.SetPrefix("framework: ") 37 | log.SetFlags(0) 38 | flag.Usage = flags.Usage 39 | flag.Parse() 40 | 41 | if *flags.FlagVersion { 42 | fmt.Fprintln(os.Stderr, flags.Version) 43 | os.Exit(2) 44 | } 45 | if *flags.FlagHelp { 46 | flag.Usage() 47 | os.Exit(2) 48 | } 49 | if flag.NArg() != 0 { 50 | fmt.Fprintln(os.Stderr, "does not take any operands") 51 | flag.Usage() 52 | os.Exit(2) 53 | } 54 | if *flags.FlagRun != "" { 55 | 56 | TEngine := taskengine.TaskEngine{} 57 | TEngine.InitializeEngine() 58 | 59 | if *flags.FlagRun == "all" { 60 | TEngine.StartAllTasks() 61 | } else { 62 | groups := strings.Split(*flags.FlagRun, ",") 63 | for _, group := range groups { 64 | igroup, err := strconv.Atoi(group) 65 | if err != nil { 66 | errors.Handler(err) 67 | log.Fatal(err) 68 | } 69 | TEngine.StartTasksInGroup(igroup) 70 | } 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /cmd/taskengine/entities.go: -------------------------------------------------------------------------------- 1 | package taskengine 2 | 3 | import ( 4 | "sync" 5 | 6 | tasks "github.com/umasii/bot-framework/internal/tasks" 7 | ) 8 | 9 | type TaskEngine struct { 10 | TaskGroups []BotTaskGroup 11 | } 12 | 13 | type BotTaskGroup struct { 14 | GroupID int 15 | GroupName string 16 | GroupStatus string 17 | QtyTasks int 18 | QtyActive int 19 | QtyCheckedOut int 20 | QtyFailed int 21 | Tasks []tasks.IBotTask 22 | TaskWaitGroup sync.WaitGroup 23 | } 24 | 25 | func (btg *BotTaskGroup) StartTasks(wg *sync.WaitGroup) { 26 | for i := range btg.Tasks { 27 | wg.Add(1) 28 | go btg.Tasks[i].WrapExecutor(btg.Tasks[i].Execute, wg) 29 | } 30 | wg.Wait() 31 | //TODO: Make sure we call wg.Done() whenever the task is done or stopped! 32 | } 33 | 34 | func (btg *BotTaskGroup) StopTasks() { 35 | for i := range btg.Tasks { 36 | go btg.Tasks[i].Stop() 37 | } 38 | } -------------------------------------------------------------------------------- /cmd/taskengine/helpers.go: -------------------------------------------------------------------------------- 1 | package taskengine 2 | 3 | import ( 4 | "github.com/umasii/bot-framework/cmd/datastores/taskstore" 5 | "github.com/umasii/bot-framework/internal/tasks" 6 | ) 7 | 8 | func (te *TaskEngine) loadTasksIntoEngine(taskGroups *[]tasks.TaskGroup) { 9 | var botTaskGroups = []BotTaskGroup{} 10 | 11 | for tgIndex := range *taskGroups { 12 | 13 | currentTaskGroup := BotTaskGroup{ 14 | GroupName: (*taskGroups)[tgIndex].GroupName, 15 | GroupID: (*taskGroups)[tgIndex].GroupID, 16 | QtyTasks: len((*taskGroups)[tgIndex].Tasks), 17 | GroupStatus: "idle", 18 | Tasks: (*taskGroups)[tgIndex].Tasks, 19 | } 20 | 21 | botTaskGroups = append(botTaskGroups, currentTaskGroup) 22 | } 23 | 24 | te.TaskGroups = botTaskGroups 25 | } 26 | 27 | func createTasksFromJSON() *[]tasks.TaskGroup { 28 | taskGroupsData := taskstore.GetTaskGroups() 29 | taskGroups := []tasks.TaskGroup{} 30 | 31 | for i := range taskGroupsData { 32 | 33 | currentTaskGroup := tasks.TaskGroup{ 34 | GroupName: taskGroupsData[i].GroupName, 35 | GroupID: taskGroupsData[i].GroupID, 36 | Tasks: taskGroupsData[i].Tasks, 37 | } 38 | 39 | taskGroups = append(taskGroups, currentTaskGroup) 40 | } 41 | 42 | return &taskGroups 43 | } 44 | -------------------------------------------------------------------------------- /cmd/taskengine/taskengine.go: -------------------------------------------------------------------------------- 1 | package taskengine 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "sync" 7 | 8 | "github.com/umasii/bot-framework/internal/tasks" 9 | ) 10 | 11 | 12 | 13 | 14 | func (te *TaskEngine) InitializeEngine() { 15 | createdTaskGroups := createTasksFromJSON() 16 | te.loadTasksIntoEngine(createdTaskGroups) 17 | } 18 | 19 | func (te *TaskEngine) StartAllTasks() { 20 | for i := range te.TaskGroups { 21 | te.TaskGroups[i].StartTasks(&te.TaskGroups[i].TaskWaitGroup) 22 | } 23 | } 24 | 25 | func (te *TaskEngine) StopAllTasks() { 26 | for i := range te.TaskGroups { 27 | te.TaskGroups[i].StopTasks() 28 | } 29 | } 30 | 31 | func (te *TaskEngine) GetRunningTaskCount() int { 32 | taskCount := 0 33 | for i := range te.TaskGroups { 34 | taskCount += len(te.TaskGroups[i].Tasks) 35 | } 36 | 37 | return taskCount 38 | } 39 | 40 | func (te *TaskEngine) StartSelectedTasks(wg *sync.WaitGroup, tasks *[]tasks.IBotTask) { 41 | for i := range *tasks { 42 | wg.Add(1) 43 | go (*tasks)[i].WrapExecutor((*tasks)[i].Execute, wg) 44 | } 45 | wg.Wait() 46 | } 47 | 48 | func (te *TaskEngine) StartTasksInGroup(groupID int) { 49 | for i := range te.TaskGroups { 50 | if te.TaskGroups[i].GroupID == groupID { 51 | te.TaskGroups[i].StartTasks(&te.TaskGroups[i].TaskWaitGroup) 52 | } 53 | } 54 | } 55 | 56 | func (te *TaskEngine) GetTasksInTaskGroup(groupID int) ([]tasks.IBotTask, error){ 57 | for i := range te.TaskGroups { 58 | if te.TaskGroups[i].GroupID == groupID { 59 | return te.TaskGroups[i].Tasks, nil 60 | } 61 | } 62 | return nil, errors.New(fmt.Sprintf("Task Group with ID %v not found!", groupID)) 63 | } -------------------------------------------------------------------------------- /data/profiles.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "GroupName": "test", 4 | "GroupID": 1, 5 | "Profiles": [ 6 | { 7 | "ProfileID": 1, 8 | "GroupName": "test", 9 | "GroupID": 1, 10 | "ProfileName": "TestProfile", 11 | "Email": "", 12 | "Billing": { 13 | "CardHolder": "", 14 | "CardNumber": "", 15 | "ExpMonth": "", 16 | "ExpYear": "", 17 | "CVC": "", 18 | "BillingAddress": { 19 | "FirstName": "", 20 | "LastName": "", 21 | "Phone": "", 22 | "Address": "", 23 | "Address2": "", 24 | "City": "", 25 | "State": "", 26 | "Country": "", 27 | "PostalCode": "" 28 | } 29 | }, 30 | "Shipping": { 31 | "FirstName": "", 32 | "LastName": "", 33 | "Phone": "", 34 | "Address": "", 35 | "Address2": "", 36 | "City": "", 37 | "State": "", 38 | "Country": "", 39 | "PostalCode": "" 40 | }, 41 | "Options": { 42 | "SameBillingAsShiping": true, 43 | "OnlyOneCheckout": false 44 | } 45 | } 46 | ] 47 | } 48 | ] 49 | -------------------------------------------------------------------------------- /data/proxies.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "GroupName": "test", 4 | "GroupID": 1, 5 | "IsMonitoringProxies": true, 6 | "Proxies": [] 7 | } 8 | ] 9 | -------------------------------------------------------------------------------- /data/tasks.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "GroupName": "af1", 4 | "GroupID": 1, 5 | "Tasks": [ 6 | { 7 | "Mode": "Litty1", 8 | "TaskID": 1, 9 | "GroupID": 1, 10 | "ProfileGroupID": 1, 11 | "ProfileID": 1, 12 | "ProxyGroupID": 1, 13 | "Site": "Louis Vuitton", 14 | "Product": { 15 | "ProductName": "", 16 | "Identifier": "1A9V8A", 17 | "Color": "", 18 | "Size": "", 19 | "Qty": 1, 20 | "PriceCheck": 600000 21 | }, 22 | "MonitorDelay": 3000, 23 | "RetryDelay": 3000 24 | } 25 | ] 26 | } 27 | ] 28 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/umasii/bot-framework 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/PrismAIO/go.uuid v1.2.0 7 | github.com/andersfylling/snowflake v1.3.0 8 | github.com/umasii/2cap v0.0.0-20210618050852-831eef3e1732 9 | github.com/umasii/captcha v0.0.0-20211220051322-9228b5f60e6b 10 | github.com/umasii/httpclient v0.0.0-20220716025509-7d0125691e89 11 | github.com/umasii/utls v0.0.0-20220716022947-a3fef602e29e 12 | github.com/umasii/walmart-encryption v0.0.0-20210607175137-e88a145a1ed4 13 | github.com/durango/go-credit-card v0.0.0-20220404131259-a9e175ba4082 14 | github.com/getsentry/sentry-go v0.13.0 15 | github.com/gwatts/rootcerts v0.0.0-20220501184621-6eac2dff0b8d 16 | github.com/json-iterator/go v1.1.12 17 | github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 18 | ) 19 | 20 | require ( 21 | github.com/PuerkitoBio/goquery v1.8.0 // indirect 22 | github.com/andybalholm/brotli v1.0.4 // indirect 23 | github.com/andybalholm/cascadia v1.3.1 // indirect 24 | github.com/dsnet/compress v0.0.1 // indirect 25 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 26 | github.com/modern-go/reflect2 v1.0.2 // indirect 27 | github.com/sirupsen/logrus v1.8.1 // indirect 28 | github.com/streadway/amqp v1.0.0 // indirect 29 | github.com/tidwall/gjson v1.14.1 // indirect 30 | github.com/tidwall/match v1.1.1 // indirect 31 | github.com/tidwall/pretty v1.2.0 // indirect 32 | gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect 33 | gitlab.com/yawning/utls.git v0.0.12-1 // indirect 34 | golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect 35 | golang.org/x/net v0.0.0-20220708220712-1185a9018129 // indirect 36 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect 37 | golang.org/x/text v0.3.7 // indirect 38 | ) 39 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | 9fans.net/go v0.0.0-20180727211846-5d4fa602e1e8/go.mod h1:diCsxrliIURU9xsYtjCp5AbpQKqdhKmf0ujWDUSkfoY= 2 | github.com/PrismAIO/go.uuid v1.2.0 h1:GoBkY+BifwaWAlxJBDm7kQMXJuEuaHW322vYYxbnEt8= 3 | github.com/PrismAIO/go.uuid v1.2.0/go.mod h1:Et9qAbb1BAMdosEEr7tWT8iWuuXxAOFT19vlMY1+XBs= 4 | github.com/PuerkitoBio/goquery v1.8.0 h1:PJTF7AmFCFKk1N6V6jmKfrNH9tV5pNE6lZMkG0gta/U= 5 | github.com/PuerkitoBio/goquery v1.8.0/go.mod h1:ypIiRMtY7COPGk+I/YbZLbxsxn9g5ejnI2HSMtkjZvI= 6 | github.com/alecthomas/gometalinter v2.0.10+incompatible/go.mod h1:qfIpQGGz3d+NmgyPBqv+LSh50emm1pt72EtcX2vKYQk= 7 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 8 | github.com/andersfylling/snowflake v1.3.0 h1:dobYwnkDhe3EzvX3FMZlrRygQ1CVj1W+s3wlJuiuqwQ= 9 | github.com/andersfylling/snowflake v1.3.0/go.mod h1:mePGbOEexjod5BUkgAfCoccI//TzrywM4S25u4unsQM= 10 | github.com/andybalholm/brotli v1.0.3/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= 11 | github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= 12 | github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= 13 | github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c= 14 | github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA= 15 | github.com/umasii/2cap v0.0.0-20210618050852-831eef3e1732 h1:j+VhMbWriLba8ieBOsgeux39CSTmHjLaHJiWyB5iHLg= 16 | github.com/umasii/2cap v0.0.0-20210618050852-831eef3e1732/go.mod h1:ReoqMDT7bEdtEhzcFzdJ2fQrnd4QZ55Q1UbIE1DOJs4= 17 | github.com/umasii/captcha v0.0.0-20211220051322-9228b5f60e6b h1:rOE2bEKNXxFu8Q8KfA2nObL5PMOuRA6zEVsMKnBmvQ0= 18 | github.com/umasii/captcha v0.0.0-20211220051322-9228b5f60e6b/go.mod h1:uQLm7GCaGq0eEjCspuzX8/5X/wg5X8zbfWd11mzbnTQ= 19 | github.com/umasii/httpclient v0.0.0-20220716025509-7d0125691e89 h1:BzBl2TscqCqHvvIZz5BvEk8BlqgOMaVEQTo6zVJ0VU0= 20 | github.com/umasii/httpclient v0.0.0-20220716025509-7d0125691e89/go.mod h1:W7q1E2DiMvtFMRC6vcGL2NFyZzhy7zyKFBLVQMbLm88= 21 | github.com/umasii/utls v0.0.0-20220716022947-a3fef602e29e h1:6FKVEW499yYoWVvIUGVnSKPhLdsQOe+xGkuIth6mYEk= 22 | github.com/umasii/utls v0.0.0-20220716022947-a3fef602e29e/go.mod h1:od7jopzwF+wieKRQv9QVGhkLPI4HqFVLaeZFbTqFT80= 23 | github.com/umasii/walmart-encryption v0.0.0-20210607175137-e88a145a1ed4 h1:tx5wItImPleoBG6i7l+IHCP23twQc5c39A/z5f9c7Lw= 24 | github.com/umasii/walmart-encryption v0.0.0-20210607175137-e88a145a1ed4/go.mod h1:37otd/6eNh0iBNqq407csaWPOogc3Ty9xX1QqAfsBRc= 25 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 26 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 27 | github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q= 28 | github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo= 29 | github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= 30 | github.com/durango/go-credit-card v0.0.0-20220404131259-a9e175ba4082 h1:3RgcPZrUWhrxqhtDkuRmDTRVXGkxrVMZtJMic7cEtGA= 31 | github.com/durango/go-credit-card v0.0.0-20220404131259-a9e175ba4082/go.mod h1:jKPLGXGRR3v90kZOLs/kUMffoEot8IrRDGmmzI3kOtg= 32 | github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= 33 | github.com/fatih/gomodifytags v0.0.0-20180826164257-7987f52a7108/go.mod h1:p2/x7bnOQsbq/deXsDIlj2yLiKFGPkD2nuoYqwn8R4Y= 34 | github.com/fatih/structtag v1.0.0/go.mod h1:IKitwq45uXL/yqi5mYghiD3w9H6eTOvI9vnk8tXMphA= 35 | github.com/getsentry/sentry-go v0.13.0 h1:20dgTiUSfxRB/EhMPtxcL9ZEbM1ZdR+W/7f7NWD+xWo= 36 | github.com/getsentry/sentry-go v0.13.0/go.mod h1:EOsfu5ZdvKPfeHYV6pTVQnsjfp30+XA7//UooKNumH0= 37 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 38 | github.com/google/shlex v0.0.0-20150127133951-6f45313302b9/go.mod h1:RpwtwJQFrIEPstU94h88MWPXP2ektJZ8cZ0YntAmXiE= 39 | github.com/gwatts/rootcerts v0.0.0-20210602134037-977e162fa4a7/go.mod h1:5Kt9XkWvkGi2OHOq0QsGxebHmhCcqJ8KCbNg/a6+n+g= 40 | github.com/gwatts/rootcerts v0.0.0-20220501184621-6eac2dff0b8d h1:Kp5G1kHMb2fAD9OiqWDXro4qLB8bQ2NusoorYya4Lbo= 41 | github.com/gwatts/rootcerts v0.0.0-20220501184621-6eac2dff0b8d/go.mod h1:5Kt9XkWvkGi2OHOq0QsGxebHmhCcqJ8KCbNg/a6+n+g= 42 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 43 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 44 | github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA= 45 | github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= 46 | github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= 47 | github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= 48 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 49 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 50 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 51 | github.com/mdempsky/gocode v0.0.0-20180727200127-00e7f5ac290a/go.mod h1:hltEC42XzfMNgg0S1v6JTywwra2Mu6F6cLR03debVQ8= 52 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 53 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 54 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 55 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 56 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 57 | github.com/nicksnyder/go-i18n v1.10.0/go.mod h1:HrK7VCrbOvQoUAQ7Vpy7i87N7JZZZ7R2xBGjv0j365Q= 58 | github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= 59 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 60 | github.com/rogpeppe/godef v0.0.0-20170920080713-b692db1de522/go.mod h1:ZBiG/aArN4rqpDocvOY8PVZIVqBTm7A2uOasqHjXt9Y= 61 | github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= 62 | github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= 63 | github.com/sqs/goreturns v0.0.0-20180302073349-83e02874ec12/go.mod h1:CKI4AZ4XmGV240rTHfO0hfE83S6/a3/Q1siZJ/vXf7A= 64 | github.com/streadway/amqp v1.0.0 h1:kuuDrUJFZL1QYL9hUNuCxNObNzB0bV/ZG5jV3RWAQgo= 65 | github.com/streadway/amqp v1.0.0/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= 66 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 67 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 68 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 69 | github.com/tidwall/gjson v1.14.1 h1:iymTbGkQBhveq21bEvAQ81I0LEBork8BFe1CUZXdyuo= 70 | github.com/tidwall/gjson v1.14.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= 71 | github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= 72 | github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= 73 | github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= 74 | github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= 75 | github.com/tpng/gopkgs v0.0.0-20180428091733-81e90e22e204/go.mod h1:MwY6Iwya1EJi2InQFe7g/J3Qr7eik70yycN5+sw03hQ= 76 | github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= 77 | github.com/zmb3/goaddimport v0.0.0-20170810013102-4ab94a07ab86/go.mod h1:sFvxt8Ocey4CxNasKkwAzQFtzBRW+tyzTLq4llZlZN0= 78 | github.com/zmb3/gogetdoc v0.0.0-20180522031303-10095872a7c5/go.mod h1:ypSS7d5A7vLlcgpQpCK+OHYJ2X3jZSKrEOmspZcOSHI= 79 | gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec h1:FpfFs4EhNehiVfzQttTuxanPIT43FtkkCFypIod8LHo= 80 | gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec/go.mod h1:BZ1RAoRPbCxum9Grlv5aeksu2H8BiKehBYooU2LFiOQ= 81 | gitlab.com/yawning/utls.git v0.0.12-1 h1:RL6O0MP2YI0KghuEU/uGN6+8b4183eqNWoYgx7CXD0U= 82 | gitlab.com/yawning/utls.git v0.0.12-1/go.mod h1:3ONKiSFR9Im/c3t5RKmMJTVdmZN496FNyk3mjrY1dyo= 83 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 84 | golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 85 | golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 86 | golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 87 | golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY= 88 | golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 89 | golang.org/x/net v0.0.0-20190328230028-74de082e2cca/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 90 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 91 | golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 92 | golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 93 | golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 94 | golang.org/x/net v0.0.0-20220708220712-1185a9018129 h1:vucSRfWwTsoXro7P+3Cjlr6flUMtzCwzlvkxEQtHHB0= 95 | golang.org/x/net v0.0.0-20220708220712-1185a9018129/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 96 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 97 | golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 98 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 99 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 100 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 101 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 102 | golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 103 | golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 104 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ= 105 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 106 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 107 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 108 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 109 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 110 | golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= 111 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 112 | golang.org/x/tools v0.0.0-20180824175216-6c1c5e93cdc1/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 113 | golang.org/x/tools v0.0.0-20180904205237-0aa4b8830f48/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 114 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 115 | gopkg.in/alecthomas/kingpin.v3-unstable v3.0.0-20180810215634-df19058c872c/go.mod h1:3HH7i1SgMqlzxCcBmUHW657sD4Kvv9sC3HpL3YukzwA= 116 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 117 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 118 | -------------------------------------------------------------------------------- /internal/activityapi/entities.go: -------------------------------------------------------------------------------- 1 | package activityapi 2 | 3 | const ( 4 | LogLevel = "log" 5 | WarningLevel = "warning" 6 | ErrorLevel = "error" 7 | SuccessLevel = "success" 8 | NotificationLevel = "notification" 9 | ) 10 | 11 | type UserData struct { 12 | UserID string `json:"userID"` 13 | ShippingState string `json:"State"` 14 | } 15 | 16 | type TaskSettings struct { 17 | Site string `json:"Site"` 18 | Product string `json:"product"` 19 | //ProductIDType string `json:"PIDType"` // ie: sku, offerid, keywords, etc.. *this is assuming this will be a task input 20 | Mode string `json:"Mode"` 21 | } 22 | 23 | type TaskResults struct { 24 | CheckedOut bool `json:"checkedOut"` 25 | CheckoutStatusCode int `json:"StatusCode"` 26 | CheckoutMessage string `json:"CheckoutMessage,omitempty"` 27 | FailureReason string `json:"FailureReason,omitempty"` 28 | CheckoutTime int `json:"CheckoutTime"` 29 | } 30 | 31 | type InstanceInfo struct { 32 | OS string `json:"OS"` 33 | TotalTasks int `json:"totalTasks"` 34 | TotalTasksForProduct int `json:"totalTasksPerProd"` 35 | Time int64 `json:"time"` 36 | } 37 | 38 | type RecpData struct { 39 | UserInfo UserData `json:"userInfo"` 40 | Settings TaskSettings `json:"settings"` 41 | Results TaskResults `json:"results"` 42 | Instance InstanceInfo `json:"instance"` 43 | AdditionalData interface{} `json:"AddInfo,omitempty"` 44 | } 45 | 46 | -------------------------------------------------------------------------------- /internal/activityapi/statusLogger.go: -------------------------------------------------------------------------------- 1 | package activityapi 2 | 3 | import ( 4 | "fmt" 5 | "sort" 6 | "sync" 7 | ) 8 | 9 | var statusLogger *TaskStatusLogger 10 | 11 | // Sets a single global status logger object to maintain instead of many as in previous implementation 12 | // Let this be declared during runtime. 13 | func SetStatusLogger(logger *TaskStatusLogger) { 14 | statusLogger = logger 15 | } 16 | 17 | // Gets the status logger 18 | func GetStatusLogger() *TaskStatusLogger { 19 | return statusLogger 20 | } 21 | 22 | func CreateEmptyLogger() *TaskStatusLogger { 23 | return &TaskStatusLogger{ 24 | tasksIdStatusMap: make(map[int]string), 25 | taskStatuses: []TaskStatus{}, 26 | } 27 | } 28 | 29 | type TaskStatus struct { 30 | Id int 31 | Status string 32 | Level string 33 | } 34 | 35 | type internalStatus struct { 36 | Key string 37 | Value int 38 | } 39 | 40 | // For: Sorting map[string]int (ideally should be moved somewhere else) 41 | // Taken from https://stackoverflow.com/questions/18695346/how-to-sort-a-mapstringint-by-its-values 42 | type internalStatusArr []internalStatus 43 | 44 | func (p internalStatusArr) Len() int { return len(p) } 45 | func (p internalStatusArr) Less(i, j int) bool { return p[i].Value < p[j].Value } 46 | func (p internalStatusArr) Swap(i, j int) { p[i], p[j] = p[j], p[i] } 47 | 48 | func sortByOccurance(taskStatusToSort map[string]int) internalStatusArr { 49 | pl := make(internalStatusArr, len(taskStatusToSort)) 50 | i := 0 51 | for k, v := range taskStatusToSort { 52 | pl[i] = internalStatus{k, v} 53 | i++ 54 | } 55 | 56 | sort.Sort(sort.Reverse(pl)) 57 | return pl 58 | } 59 | 60 | // Single instance checkout stats and task statuses logger. 61 | // 62 | // This is mainly used for CLI, but can be implemented into GRPC/UI 63 | type TaskStatusLogger struct { 64 | // session checkout stats 65 | checkoutMutex sync.Mutex 66 | CheckoutCounter int 67 | DeclineCounter int 68 | CaptchaCounter int 69 | ThreeDSCounter int 70 | CartedCounter int 71 | QueuePassCounter int 72 | 73 | // status logging 74 | statusMutex sync.Mutex 75 | statusMapMutex sync.Mutex 76 | tasksIdStatusMap map[int]string 77 | taskStatuses []TaskStatus 78 | } 79 | 80 | // +1 to checkout counter 81 | func (l *TaskStatusLogger) AddCheckout() { 82 | l.checkoutMutex.Lock() 83 | defer l.checkoutMutex.Unlock() 84 | 85 | l.CheckoutCounter++ 86 | } 87 | 88 | // +1 to decline counter 89 | func (l *TaskStatusLogger) AddDecline() { 90 | l.checkoutMutex.Lock() 91 | defer l.checkoutMutex.Unlock() 92 | 93 | l.DeclineCounter++ 94 | } 95 | 96 | // +1 to cart counter 97 | func (l *TaskStatusLogger) AddCart() { 98 | l.checkoutMutex.Lock() 99 | defer l.checkoutMutex.Unlock() 100 | 101 | l.CartedCounter++ 102 | } 103 | 104 | // -1 to cart counter 105 | func (l *TaskStatusLogger) RemoveCart() { 106 | l.checkoutMutex.Lock() 107 | defer l.checkoutMutex.Unlock() 108 | 109 | l.CartedCounter-- 110 | } 111 | 112 | // +1 to queue pass counter 113 | func (l *TaskStatusLogger) AddQueuePass() { 114 | l.checkoutMutex.Lock() 115 | defer l.checkoutMutex.Unlock() 116 | 117 | l.QueuePassCounter++ 118 | } 119 | 120 | // -1 to queue pass counter 121 | func (l *TaskStatusLogger) RemoveQueuePass() { 122 | l.checkoutMutex.Lock() 123 | defer l.checkoutMutex.Unlock() 124 | 125 | l.QueuePassCounter-- 126 | } 127 | 128 | // +1 to captcha counter 129 | func (l *TaskStatusLogger) AddCaptcha() { 130 | l.checkoutMutex.Lock() 131 | defer l.checkoutMutex.Unlock() 132 | 133 | l.CaptchaCounter++ 134 | } 135 | 136 | // +1 to decline counter 137 | func (l *TaskStatusLogger) Add3DS() { 138 | l.checkoutMutex.Lock() 139 | defer l.checkoutMutex.Unlock() 140 | 141 | l.ThreeDSCounter++ 142 | } 143 | 144 | // -1 to captcha counter 145 | func (l *TaskStatusLogger) RemoveCaptcha() { 146 | l.checkoutMutex.Lock() 147 | defer l.checkoutMutex.Unlock() 148 | 149 | l.CaptchaCounter-- 150 | } 151 | 152 | // -1 to decline counter 153 | func (l *TaskStatusLogger) Remove3DS() { 154 | l.checkoutMutex.Lock() 155 | defer l.checkoutMutex.Unlock() 156 | 157 | l.ThreeDSCounter-- 158 | } 159 | 160 | // Returns task status array and empties the array 161 | func (l *TaskStatusLogger) GetStatuses() []TaskStatus { 162 | l.statusMutex.Lock() 163 | defer l.statusMutex.Unlock() 164 | 165 | copiedTaskStatuses := l.taskStatuses 166 | l.taskStatuses = []TaskStatus{} 167 | return copiedTaskStatuses 168 | } 169 | 170 | // Pushes new task status update to array 171 | func (l *TaskStatusLogger) PushTaskStatusUpdate(id int, status string, level string) { 172 | l.statusMutex.Lock() 173 | defer l.statusMutex.Unlock() 174 | l.taskStatuses = append(l.taskStatuses, TaskStatus{ 175 | Id: id, 176 | Status: status, 177 | Level: level, 178 | }) 179 | } 180 | 181 | // Gets task status array and returns it as formatted string 182 | func (l *TaskStatusLogger) GetTaskStatusArray() []string { 183 | l.statusMapMutex.Lock() 184 | defer l.statusMapMutex.Unlock() 185 | 186 | tasksMap := map[string]int{} 187 | 188 | statuses := l.GetStatuses() 189 | // Map task statuses by ID in case of multiple status updates 190 | for i := 0; i < len(statuses); i++ { 191 | l.tasksIdStatusMap[statuses[i].Id] = statuses[i].Status 192 | } 193 | 194 | // Count occurance of each status message 195 | for _, status := range l.tasksIdStatusMap { 196 | if val, ok := tasksMap[status]; ok { 197 | tasksMap[status] = val + 1 198 | } else { 199 | tasksMap[status] = 1 200 | } 201 | } 202 | 203 | // Sort by occurance 204 | sorted := sortByOccurance(tasksMap) 205 | 206 | arr := []string{} 207 | for _, val := range sorted { 208 | arr = append(arr, fmt.Sprintf("[%d] %s", val.Value, val.Key)) 209 | } 210 | 211 | return arr 212 | } 213 | -------------------------------------------------------------------------------- /internal/activityapi/statusLogger_test.go: -------------------------------------------------------------------------------- 1 | package activityapi 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | "testing" 7 | ) 8 | 9 | func TestLoggerInitialization(t *testing.T) { 10 | logger := CreateEmptyLogger() 11 | 12 | logger.AddCheckout() 13 | 14 | if logger.CheckoutCounter != 1 { 15 | t.Errorf("Add Checkout does not add properly") 16 | } 17 | 18 | logger.AddDecline() 19 | 20 | if logger.DeclineCounter != 1 { 21 | t.Errorf("AddDecline does not add properly") 22 | } 23 | 24 | logger.PushTaskStatusUpdate(1, "DAB", LogLevel) 25 | 26 | if logger.taskStatuses[0].Id != 1 && logger.taskStatuses[0].Status != "DAB" { 27 | t.Errorf("status was not pushed correctly") 28 | } 29 | 30 | out := logger.GetTaskStatusArray() 31 | 32 | if out[0] != fmt.Sprintf("[%d] %s", 1, "DAB") { 33 | t.Errorf("GetTaskStatusArray is missing a pushed status for arr: [%s]", strings.Join(out, ",")) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /internal/client/client.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "io" 5 | 6 | "github.com/cicadaaio/httpclient/net/http" 7 | ) 8 | 9 | type Client struct { 10 | Client *http.Client 11 | LastUrl string 12 | } 13 | 14 | func (c *Client) NewRequest() *Request { 15 | return &Request{ 16 | Client: c, 17 | } 18 | } 19 | 20 | func (c *Client) Do(r *http.Request) (*Response, error) { 21 | 22 | resp, err := c.Client.Do(r) 23 | if err != nil { 24 | 25 | return nil, err 26 | } 27 | response := &Response{ 28 | Headers: resp.Header, 29 | Status: resp.Status, 30 | StatusCode: resp.StatusCode, 31 | Url: resp.Request.URL, 32 | } 33 | 34 | body, err := io.ReadAll(resp.Body) 35 | resp.Body.Close() 36 | if err != nil { 37 | return nil, err 38 | } 39 | 40 | response.Body = string(body) 41 | 42 | r.Close = true 43 | 44 | return response, nil 45 | } 46 | -------------------------------------------------------------------------------- /internal/client/jar.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "net/url" 5 | "sync" 6 | "time" 7 | 8 | "github.com/cicadaaio/httpclient/net/http" 9 | ) 10 | 11 | type FJar struct { 12 | // mu locks the remaining fields. 13 | mu sync.Mutex 14 | 15 | // entries is a set of entries, keyed by their eTLD+1 and subkeyed by 16 | // their name/domain/path. 17 | entries map[string]*http.Cookie 18 | } 19 | 20 | func (F *FJar) SetCookies(u *url.URL, cookies []*http.Cookie) { 21 | now := time.Now() 22 | 23 | F.mu.Lock() 24 | defer F.mu.Unlock() 25 | for _, cookie := range cookies { 26 | if cookie.MaxAge > 0 { 27 | cookie.Expires = now.Add(time.Duration(cookie.MaxAge) * time.Second) 28 | } else if cookie.Expires.IsZero() { 29 | cookie.Expires = endOfTime 30 | } 31 | 32 | F.entries[cookie.Name] = cookie 33 | } 34 | } 35 | 36 | func New() *FJar { 37 | jar := &FJar{ 38 | entries: make(map[string]*http.Cookie), 39 | } 40 | return jar 41 | } 42 | 43 | var endOfTime = time.Date(9999, 12, 31, 23, 59, 59, 0, time.UTC) 44 | 45 | func (F *FJar) Cookies(_ *url.URL) []*http.Cookie { 46 | now := time.Now() 47 | F.mu.Lock() 48 | defer F.mu.Unlock() 49 | var cookies []*http.Cookie 50 | for _, cookie := range F.entries { 51 | if !cookie.Expires.After(now) { 52 | delete(F.entries, cookie.Name) 53 | continue 54 | } 55 | cookies = append(cookies, cookie) 56 | } 57 | return cookies 58 | } 59 | -------------------------------------------------------------------------------- /internal/client/request.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "io" 7 | "net/url" 8 | "strings" 9 | 10 | "github.com/cicadaaio/httpclient/net/http" 11 | tls "github.com/cicadaaio/utls" 12 | ) 13 | 14 | type Request struct { 15 | Client *Client 16 | Headers []map[string]string 17 | Method string 18 | Url string 19 | Body io.Reader 20 | Host string 21 | } 22 | 23 | func (r *Request) SetJSONBody(body interface{}) { 24 | b, _ := json.Marshal(body) 25 | r.Body = bytes.NewBuffer(b) 26 | } 27 | 28 | func (r *Request) SetFormBody(body url.Values) { 29 | r.Body = strings.NewReader(body.Encode()) 30 | } 31 | 32 | func (r *Request) SetHost(Host string) { 33 | r.Host = Host 34 | } 35 | 36 | func (r *Request) Do() (*Response, error) { 37 | req, err := http.NewRequest(r.Method, r.Url, r.Body) 38 | if err != nil { 39 | return nil, err 40 | } 41 | var Headers = map[string][]string{} 42 | var Order = []string{} 43 | var spoof bool 44 | 45 | for _, header := range r.Headers { 46 | for key, val := range header { 47 | Headers[key] = []string{val} 48 | if key != ":authority" { 49 | Order = append(Order, key) 50 | } else { 51 | spoof = true 52 | } 53 | } 54 | } 55 | Order = append(Order, "Cookie", "Content-Length") 56 | if r.Client.Client.Transport.(*http.Transport).ClientHelloID != &tls.HelloRandomizedNoALPN { 57 | Headers["HEADERORDER"] = Order 58 | if spoof { 59 | Headers["PSEUDOORDER"] = []string{ 60 | ":method", 61 | ":authority", 62 | ":scheme", 63 | ":path", 64 | } 65 | 66 | } 67 | } 68 | req.Header = Headers 69 | r.Client.LastUrl = r.Url 70 | req.Host = r.Host 71 | req.Close = true 72 | resp, err := r.Client.Do(req) 73 | 74 | return resp, err 75 | } 76 | -------------------------------------------------------------------------------- /internal/client/response.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "net/url" 5 | 6 | "github.com/cicadaaio/httpclient/net/http" 7 | ) 8 | 9 | type Response struct { 10 | Headers http.Header 11 | Body string 12 | Status string 13 | StatusCode int 14 | Url *url.URL 15 | } 16 | -------------------------------------------------------------------------------- /internal/constants/sites.go: -------------------------------------------------------------------------------- 1 | package constants 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/umasii/bot-framework/sites/louisvuitton" 7 | ) 8 | 9 | var SITES = map[string]reflect.Type{ 10 | "Louis Vuitton": reflect.TypeOf(louisvuitton.LVTask{}), 11 | } -------------------------------------------------------------------------------- /internal/errors/Handler.go: -------------------------------------------------------------------------------- 1 | package errors 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/getsentry/sentry-go" 7 | ) 8 | 9 | func Handler(err error) error { 10 | if err != nil { 11 | sentry.CaptureException(err) 12 | // if Args.Args.IsDebug { 13 | fmt.Println(err) 14 | // } 15 | } 16 | return err 17 | } 18 | -------------------------------------------------------------------------------- /internal/flags/flags.go: -------------------------------------------------------------------------------- 1 | package flags 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "os" 7 | ) 8 | 9 | var Version = "v0.0.1" 10 | 11 | const help = ` 12 | CicadaAIO` 13 | 14 | var ( 15 | FlagHelp = flag.Bool("help", false, "print usage and help, and exit") 16 | FlagVersion = flag.Bool("version", false, "print version and exit") 17 | FlagDebug = flag.Bool("v", false, "verbose mode") 18 | FlagRun = flag.String("r", "", "run a test") 19 | ) 20 | 21 | func Usage() { 22 | fmt.Fprintf(os.Stderr, "Usage of framework:\n") 23 | fmt.Fprintf(os.Stderr, "\tframework [-v] [-r]\n") 24 | if *FlagHelp { 25 | fmt.Fprintln(os.Stderr, help) 26 | } 27 | fmt.Fprintf(os.Stderr, "Flags:\n") 28 | flag.PrintDefaults() 29 | } 30 | -------------------------------------------------------------------------------- /internal/helpers/datastore/store.go: -------------------------------------------------------------------------------- 1 | package store 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "path" 8 | 9 | "github.com/kardianos/osext" 10 | "github.com/umasii/bot-framework/internal/errors" 11 | ) 12 | 13 | // Data should be in the same directory as the built.exe 14 | func GetStoreFilePath(storeName string) string { 15 | b, _ := osext.Executable() 16 | storePath := path.Join(path.Dir(b), "Data", fmt.Sprintf("%s.json", storeName)) 17 | return storePath 18 | } 19 | 20 | func Write(data interface{}, storeName string) { 21 | file, _ := json.MarshalIndent(data, "", " ") 22 | 23 | err := ioutil.WriteFile(GetStoreFilePath(storeName), file, 0644) 24 | 25 | if err != nil { 26 | errors.Handler(err) 27 | } 28 | } 29 | 30 | func Read(data interface{}, storeName string) { 31 | file, err := ioutil.ReadFile(GetStoreFilePath(storeName)) 32 | 33 | if err != nil { 34 | errors.Handler(err) 35 | } 36 | 37 | json.Unmarshal([]byte(file), data) 38 | } 39 | -------------------------------------------------------------------------------- /internal/helpers/utilities/arrays.go: -------------------------------------------------------------------------------- 1 | package utilities 2 | 3 | func StrArrcontains(arr []string, s string) bool { 4 | for _, a := range arr { 5 | if a == s { 6 | return true 7 | } 8 | } 9 | return false 10 | } 11 | -------------------------------------------------------------------------------- /internal/helpers/utilities/cardidentification/card.go: -------------------------------------------------------------------------------- 1 | package cardidentification 2 | 3 | import ( 4 | goErrors "errors" 5 | "strings" 6 | 7 | ccID "github.com/durango/go-credit-card" 8 | "github.com/umasii/bot-framework/internal/errors" 9 | profiles "github.com/umasii/bot-framework/internal/profiles" 10 | ) 11 | 12 | func CreditCardType(profile *profiles.Profile) (string, error) { 13 | card := ccID.Card{Number: strings.ReplaceAll(profile.Billing.CardNumber, " ", ""), Cvv: profile.Billing.CVC, Month: profile.Billing.ExpMonth, Year: profile.Billing.ExpYear} 14 | err := card.Method() 15 | 16 | if err != nil { 17 | return "", errors.Handler(goErrors.New("Failed to ID card")) 18 | } 19 | 20 | return card.Company.Short, nil 21 | 22 | } 23 | -------------------------------------------------------------------------------- /internal/helpers/utilities/print.go: -------------------------------------------------------------------------------- 1 | package utilities 2 | 3 | import "encoding/json" 4 | 5 | // Pretty prints a struct 6 | // 7 | // Better than fmt.Sprintf("%#v", i) 8 | func PrettyPrint(i interface{}) string { 9 | s, _ := json.MarshalIndent(i, "", "\t") 10 | return string(s) + "\n" 11 | } 12 | -------------------------------------------------------------------------------- /internal/helpers/utilities/random/random.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "math/rand" 5 | "time" 6 | ) 7 | 8 | var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") 9 | 10 | func RandStringRunes(n int, customAlphabet string) string { 11 | runeSet := letterRunes 12 | if customAlphabet != "" { 13 | runeSet = []rune(customAlphabet) 14 | } 15 | 16 | b := make([]rune, n) 17 | rand.Seed(time.Now().UnixNano()) 18 | 19 | for i := range b { 20 | b[i] = runeSet[rand.Intn(len(runeSet))] 21 | } 22 | return string(b) 23 | } 24 | -------------------------------------------------------------------------------- /internal/helpers/utilities/random/slices.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | func FindInSlice(slice []string, val string) (int, bool) { 4 | for i, item := range slice { 5 | if item == val { 6 | return i, true 7 | } 8 | } 9 | return -1, false 10 | } 11 | -------------------------------------------------------------------------------- /internal/helpers/utilities/taskParsing.go: -------------------------------------------------------------------------------- 1 | package utilities 2 | 3 | import "strings" 4 | 5 | func IsRandomSize(size string) bool { 6 | lowercaseSize := strings.ToLower(size) 7 | return lowercaseSize == "random" || lowercaseSize == "rand" || lowercaseSize == "any" 8 | } -------------------------------------------------------------------------------- /internal/monitorengine/entities.go: -------------------------------------------------------------------------------- 1 | package monitorengine 2 | 3 | type MonitorResp struct { 4 | CurrentStock bool 5 | Info interface{} // Modules may need info returned via monitor (ie PID / ATC ids, so they can just pass int) 6 | } 7 | 8 | type MonitorInfo struct { 9 | Site string 10 | Identifier string 11 | Task MonitorTask 12 | } 13 | 14 | type MonitorTask interface { 15 | Create(Site, Identifier string) 16 | Initialize() 17 | CheckStock() 18 | Start(checkStock func()) 19 | Stop() 20 | } 21 | -------------------------------------------------------------------------------- /internal/monitorengine/monitorengine.go: -------------------------------------------------------------------------------- 1 | package monitorengine 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | type MonitorEngine struct { 9 | CurrentlyMonitoring map[string][]chan MonitorResp 10 | } 11 | 12 | type StockAlert struct { 13 | StockStatus bool `json:"StockStatus"` 14 | Pid string `json:"Pid"` 15 | Site string `json:"Site"` 16 | Endpoint string `json:"Endpoint"` 17 | FormData string `json:"FormData"` 18 | Method string `json:"Method"` 19 | GoodResponse string `json:"Good Response"` 20 | } 21 | 22 | var MEngine *MonitorEngine 23 | var once sync.Once 24 | 25 | func init() { 26 | once.Do(func() { 27 | MEngine = &MonitorEngine{} 28 | MEngine.Initialize() 29 | }) 30 | } 31 | 32 | func (m *MonitorEngine) Initialize() { 33 | m.CurrentlyMonitoring = make(map[string][]chan MonitorResp) 34 | } 35 | 36 | func (m *MonitorEngine) Monitor(info MonitorInfo) chan MonitorResp { 37 | // unique identifier to lookup tasks in future 38 | key := fmt.Sprintf("%s:%s", info.Site, info.Identifier) 39 | fmt.Println(key) 40 | 41 | newChan := make(chan MonitorResp) 42 | 43 | if _, ok := m.CurrentlyMonitoring[key]; ok { 44 | fmt.Println(fmt.Sprintf("Already monitoring for [%s]!", key)) 45 | 46 | m.CurrentlyMonitoring[key] = append(m.CurrentlyMonitoring[key], newChan) 47 | return newChan 48 | 49 | } 50 | 51 | m.CurrentlyMonitoring[key] = append(m.CurrentlyMonitoring[key], newChan) 52 | 53 | info.Task.Create(info.Identifier, info.Site) 54 | 55 | go info.Task.Start(info.Task.CheckStock) 56 | 57 | 58 | 59 | fmt.Println(fmt.Sprintf("Started new Monitor Task for [%s]!", key)) 60 | 61 | return newChan 62 | } 63 | 64 | func (m *MonitorEngine) CheckForMonitoring(site, identifier string) bool { 65 | key := fmt.Sprintf("%s:%s", site, identifier) 66 | 67 | fmt.Println(m.CurrentlyMonitoring[key]) 68 | 69 | if _, ok := m.CurrentlyMonitoring[key]; ok { 70 | return true 71 | } 72 | return false 73 | } 74 | 75 | func (m *MonitorEngine) AlertStock(site, identifier string, status bool, info StockAlert) { 76 | pushDown := MonitorResp{ 77 | CurrentStock: status, 78 | Info: info, 79 | } 80 | key := fmt.Sprintf("%s:%s", site, identifier) 81 | 82 | fmt.Printf("LEN: %d \n ", len(m.CurrentlyMonitoring[key])) 83 | fmt.Println(m.CurrentlyMonitoring) 84 | for iter, task := range m.CurrentlyMonitoring[key] { 85 | fmt.Printf("iter %d \n", iter) 86 | taskX := task 87 | go func () { 88 | taskX <- pushDown 89 | } () 90 | 91 | } 92 | } -------------------------------------------------------------------------------- /internal/monitortask/monitortask.go: -------------------------------------------------------------------------------- 1 | package monitortask 2 | 3 | import ( 4 | "github.com/umasii/bot-framework/cmd/datastores/proxystore" 5 | Client3 "github.com/umasii/bot-framework/internal/client" 6 | Monitor "github.com/umasii/bot-framework/internal/monitorengine" 7 | "github.com/umasii/bot-framework/internal/proxies" 8 | 9 | //"github.com/umasii/bot-framework/Internal/MonitorEngine" 10 | "net/url" 11 | "time" 12 | 13 | "github.com/cicadaaio/httpclient/net/http" 14 | tls "github.com/cicadaaio/utls" 15 | utls "github.com/cicadaaio/utls" 16 | "github.com/gwatts/rootcerts" 17 | 18 | "github.com/cicadaaio/httpclient/net/http/cookiejar" 19 | ) 20 | 21 | type RunningStatus string 22 | 23 | const ( 24 | Active RunningStatus = "Active" 25 | Paused = "Paused" 26 | Stopped = "Stopped" 27 | ) 28 | 29 | type MonitorTask struct { 30 | Client Client3.Client `json:"-"` 31 | Jar *cookiejar.Jar `json:"-"` 32 | Proxy proxies.Proxy `json:"-"` 33 | ProxyList []string `json:"-"` 34 | Delay time.Duration 35 | StockStatus *Monitor.MonitorResp 36 | Running RunningStatus 37 | } 38 | 39 | func (t *MonitorTask) WaitDelay() { 40 | time.Sleep(t.Delay * time.Millisecond) 41 | } 42 | 43 | func (t *MonitorTask) InitializeClient() { 44 | var err error 45 | for { 46 | t.Jar, err = cookiejar.New(nil) 47 | if err != nil { 48 | continue 49 | } 50 | tr := &http.Transport{ 51 | MaxIdleConns: 10, 52 | IdleConnTimeout: 30 * time.Second, 53 | DisableCompression: false, 54 | Proxy: http.ProxyURL(nil), 55 | ClientHelloID: &tls.HelloChrome_83, 56 | ForceAttemptHTTP2: true, 57 | } 58 | tr.TLSClientConfig = &utls.Config{ 59 | InsecureSkipVerify: false, 60 | RootCAs: rootcerts.ServerCertPool(), 61 | } 62 | t.Client.Client = &http.Client{Transport: tr, Jar: t.Jar} 63 | if err != nil { 64 | continue 65 | } 66 | return 67 | } 68 | } 69 | 70 | func (t *MonitorTask) RotateProxy() { 71 | if t.ProxyList == nil || (t.ProxyList)[0] == "" { 72 | return 73 | } else { 74 | for { 75 | if t.Proxy.Raw != "" { 76 | proxies.SafeProxy.ReleaseProxy(t.Proxy.Raw) 77 | } 78 | 79 | rawProxy := proxies.SafeProxy.GetProxy(t.ProxyList) 80 | 81 | // Proxy is empty wait some time then get new 82 | if rawProxy == "" { 83 | t.WaitDelay() 84 | continue 85 | } 86 | 87 | parsedProxy, err := proxies.SafeProxy.ParseProxy(rawProxy) 88 | if err != nil { 89 | continue 90 | } 91 | 92 | t.Proxy = parsedProxy 93 | 94 | t.Client.Client.Transport.(*http.Transport).Proxy = http.ProxyURL(t.Proxy.Formatted) 95 | t.Client.Client.Transport.(*http.Transport).CloseIdleConnections() 96 | 97 | return 98 | } 99 | } 100 | } 101 | 102 | func (t *MonitorTask) Charles() { 103 | charles, _ := url.Parse("http://localhost:8888") 104 | t.Client.Client.Transport.(*http.Transport).Proxy = http.ProxyURL(charles) 105 | t.Client.Client.Transport.(*http.Transport).CloseIdleConnections() 106 | 107 | } 108 | 109 | func (m *MonitorTask) Initialize() { 110 | 111 | m.ProxyList = proxystore.GetMonitoringProxies() 112 | m.InitializeClient() 113 | m.RotateProxy() 114 | } 115 | 116 | func (m *MonitorTask) Start(checkStock func()) { 117 | m.Running = Active 118 | 119 | for { 120 | 121 | m.WaitDelay() 122 | 123 | if m.Running == Stopped { 124 | return 125 | } 126 | 127 | if m.Running == Paused { 128 | continue 129 | } 130 | 131 | checkStock() 132 | } 133 | 134 | } 135 | 136 | func (m *MonitorTask) Stop() { 137 | m.Running = Stopped 138 | } 139 | -------------------------------------------------------------------------------- /internal/products/product.go: -------------------------------------------------------------------------------- 1 | package products 2 | 3 | type Product struct { 4 | ProductName string 5 | Identifier string // (this could be keyworks, id, or link) 6 | Color string 7 | Size string 8 | Qty int 9 | PriceCheck int 10 | Image string 11 | } 12 | -------------------------------------------------------------------------------- /internal/profiles/profile.go: -------------------------------------------------------------------------------- 1 | package profiles 2 | 3 | type Profile struct { 4 | ProfileID int 5 | GroupID int 6 | ProfileName string 7 | GroupName string 8 | Email string 9 | Billing Billing 10 | Shipping Address 11 | Options Options 12 | } 13 | 14 | type Billing struct { 15 | CardHolder string 16 | CardNumber string 17 | ExpMonth string 18 | ExpYear string 19 | CVC string 20 | CardType string 21 | BillingAddress Address 22 | } 23 | 24 | type Address struct { 25 | FirstName string 26 | LastName string 27 | Phone string 28 | Address string 29 | Address2 string 30 | City string 31 | State string 32 | Country string 33 | PostalCode string 34 | } 35 | 36 | type Options struct { 37 | SameBillingAsShiping bool 38 | OnlyOneCheckout bool 39 | } 40 | 41 | type ProfileGroup struct { 42 | GroupName string 43 | GroupID int 44 | Profiles []Profile 45 | } 46 | -------------------------------------------------------------------------------- /internal/proxies/proxy.go: -------------------------------------------------------------------------------- 1 | package proxies 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "math/rand" 7 | "net/url" 8 | "strings" 9 | "sync" 10 | "time" 11 | ) 12 | 13 | type SafeProxyHandler struct { 14 | cacheMutex sync.Mutex 15 | proxyCache map[string]Proxy 16 | usedProxiesMutex sync.Mutex 17 | usedProxies map[string]bool 18 | } 19 | 20 | var SafeProxy *SafeProxyHandler 21 | var once sync.Once 22 | 23 | func init() { 24 | once.Do(func() { 25 | SafeProxy = &SafeProxyHandler{} 26 | SafeProxy.proxyCache = map[string]Proxy{} 27 | SafeProxy.usedProxies = map[string]bool{} 28 | }) 29 | } 30 | 31 | type Proxy struct { 32 | GroupID int 33 | GroupName string 34 | Ip string 35 | Port string 36 | Username string 37 | Password string 38 | RequiresAuth bool 39 | Formatted *url.URL 40 | Raw string 41 | } 42 | 43 | type ProxyGroup struct { 44 | GroupName string 45 | GroupID int 46 | IsMonitoringProxies bool 47 | Proxies []string 48 | } 49 | 50 | var illegalProxies = []string{ 51 | "127.0.0.", 52 | "localhost", 53 | "0.0.0.0", 54 | } 55 | 56 | func (sph *SafeProxyHandler) ParseProxy(proxy string) (Proxy, error) { 57 | sph.cacheMutex.Lock() 58 | defer func() { 59 | sph.cacheMutex.Unlock() 60 | }() 61 | 62 | if val, ok := sph.proxyCache[proxy]; ok { 63 | return val, nil 64 | } 65 | 66 | var proxyInstance Proxy 67 | var proxyUrl *url.URL 68 | var err error 69 | 70 | switch split := strings.Split(proxy, ":"); len(split) { 71 | case 2: 72 | 73 | proxyInstance = Proxy{ 74 | Ip: split[0], 75 | Port: split[1], 76 | Raw: proxy, 77 | } 78 | 79 | proxyUrl, err = url.Parse(fmt.Sprintf("http://%s:%s", proxyInstance.Ip, proxyInstance.Port)) 80 | if err != nil { 81 | return Proxy{}, errors.New("failed to parse proxy") 82 | } 83 | 84 | proxyInstance.Formatted = proxyUrl 85 | case 4: 86 | 87 | proxyInstance = Proxy{ 88 | Raw: proxy, 89 | Ip: split[0], 90 | Port: split[1], 91 | Username: split[2], 92 | Password: split[3], 93 | RequiresAuth: true, 94 | } 95 | 96 | proxyUrl, err = url.Parse(fmt.Sprintf("http://%s:%s@%s:%s", proxyInstance.Username, proxyInstance.Password, proxyInstance.Ip, proxyInstance.Port)) 97 | if err != nil { 98 | return Proxy{}, errors.New("failed to parse proxy") 99 | } 100 | 101 | proxyInstance.Formatted = proxyUrl 102 | case 1: 103 | proxyUrl = nil 104 | default: 105 | return Proxy{}, errors.New("failed to parse proxy") 106 | } 107 | 108 | sph.proxyCache[proxy] = proxyInstance 109 | 110 | return proxyInstance, nil 111 | } 112 | 113 | func (sph *SafeProxyHandler) GetProxy(proxyList []string) string { 114 | 115 | sph.usedProxiesMutex.Lock() 116 | rand.Seed(time.Now().UnixNano()) 117 | 118 | defer func() { 119 | sph.usedProxiesMutex.Unlock() 120 | }() 121 | 122 | rand.Shuffle(len(proxyList), func(i, j int) { proxyList[i], proxyList[j] = proxyList[j], proxyList[i] }) 123 | 124 | var unusedProxyList = []string{} 125 | for _, proxy := range proxyList { 126 | if _, ok := sph.usedProxies[proxy]; !ok { 127 | unusedProxyList = append(unusedProxyList, proxy) 128 | } 129 | } 130 | 131 | if len(unusedProxyList) == 0 { 132 | return "" 133 | } 134 | 135 | proxyToUse := unusedProxyList[rand.Intn(len(unusedProxyList)-0)+0] 136 | sph.usedProxies[proxyToUse] = true 137 | return proxyToUse 138 | } 139 | 140 | func (sph *SafeProxyHandler) ReleaseProxy(proxy string) { 141 | sph.usedProxiesMutex.Lock() 142 | 143 | defer func() { 144 | sph.usedProxiesMutex.Unlock() 145 | }() 146 | 147 | delete(sph.usedProxies, proxy) 148 | } 149 | -------------------------------------------------------------------------------- /internal/tasks/task.go: -------------------------------------------------------------------------------- 1 | package tasks 2 | 3 | import ( 4 | "context" 5 | goErrors "errors" 6 | "fmt" 7 | "log" 8 | "net/url" 9 | "strconv" 10 | 11 | "github.com/umasii/bot-framework/cmd/datastores/profilestore" 12 | "github.com/umasii/bot-framework/cmd/datastores/proxystore" 13 | "github.com/umasii/bot-framework/internal/activityapi" 14 | "github.com/umasii/bot-framework/internal/certs" 15 | "github.com/umasii/bot-framework/internal/client" 16 | "github.com/umasii/bot-framework/internal/errors" 17 | "github.com/umasii/bot-framework/internal/helpers/utilities/cardidentification" 18 | "github.com/umasii/bot-framework/internal/products" 19 | "github.com/umasii/bot-framework/internal/profiles" 20 | "github.com/umasii/bot-framework/internal/proxies" 21 | 22 | tls "github.com/cicadaaio/utls" 23 | 24 | "sync" 25 | "time" 26 | 27 | "github.com/cicadaaio/httpclient/net/http" 28 | ) 29 | 30 | type Stage string 31 | 32 | const ( 33 | Start Stage = "Start" 34 | Stop = "Stop" 35 | InStock = "InStock" 36 | Success = "Success" 37 | ) 38 | 39 | type Task struct { 40 | Stage Stage `json:"-"` 41 | Client client.Client `json:"-"` 42 | Proxy proxies.Proxy `json:"-"` 43 | ProxyList []string `json:"-"` 44 | Status string `json:"-"` 45 | Profile *profiles.Profile `json:"-"` 46 | Jar *client.FJar `json:"-"` 47 | Tries int `json:"-"` 48 | Ctx context.Context `json:"-"` 49 | Cancel context.CancelFunc `json:"-"` 50 | DeclineReason string `json:"-"` 51 | OrderId string `json:"-"` 52 | 53 | TaskID int 54 | GroupID int 55 | GroupName string 56 | ProfileGroupID int 57 | ProfileID int 58 | ProxyGroupID int 59 | ProxyGroupName string 60 | Site string 61 | Product products.Product 62 | MonitorDelay time.Duration 63 | RetryDelay time.Duration 64 | } 65 | 66 | type IBotTask interface { 67 | Get() *Task 68 | Initialize() 69 | InjectTaskData() 70 | //Start(wg *sync.WaitGroup) 71 | Stop() 72 | Execute() 73 | WrapExecutor(f func(), wg *sync.WaitGroup) 74 | } 75 | 76 | type TaskGroup struct { 77 | GroupName string 78 | GroupID int 79 | Tasks []IBotTask 80 | } 81 | 82 | func (t Task) Get() *Task { 83 | return &t 84 | 85 | } 86 | 87 | func (t *Task) Stop() { 88 | t.Cancel() 89 | } 90 | 91 | func (t *Task) WrapExecutor(f func(), wg *sync.WaitGroup) { 92 | stage := Start 93 | 94 | defer func() { 95 | if e := recover(); e != nil { 96 | if err, ok := e.(error); ok { 97 | log.Println(err) 98 | } 99 | } 100 | }() 101 | defer wg.Done() 102 | 103 | t.Initialize() 104 | for { 105 | select { 106 | case <-t.Ctx.Done(): 107 | panic(goErrors.New("stopped")) 108 | 109 | default: 110 | f() 111 | if stage == t.Stage { 112 | t.Tries++ 113 | //handle tries in the methods themselves 114 | continue 115 | } 116 | if t.Stage == Success { 117 | //TODO: Webhook func here that access t.Product 118 | return 119 | } 120 | t.Tries = 0 121 | stage = t.Stage 122 | } 123 | } 124 | } 125 | 126 | func (t *Task) Restart() { 127 | t.UpdateStatus("Restarting", activityapi.LogLevel) 128 | t.Stage = Start 129 | t.Initialize() 130 | } 131 | 132 | func (t *Task) UpdateStatus(status string, level string) { 133 | 134 | t.Status = status 135 | fmt.Println(fmt.Sprintf("Task ID: %s | Status: %s | Product: %s | Status: %s", strconv.Itoa(t.TaskID), t.Status, t.Product.Identifier, t.Status)) 136 | } 137 | 138 | func (t Task) WaitM() { 139 | time.Sleep(t.MonitorDelay * time.Millisecond) 140 | } 141 | 142 | func (t Task) WaitR() { 143 | time.Sleep(t.RetryDelay * time.Millisecond) 144 | } 145 | 146 | func (t *Task) Charles() { 147 | charles, _ := url.Parse("http://localhost:8888") 148 | t.Client.Client.Transport.(*http.Transport).Proxy = http.ProxyURL(charles) 149 | t.Client.Client.Transport.(*http.Transport).CloseIdleConnections() 150 | 151 | } 152 | 153 | func (t *Task) RotateProxy() { 154 | //support localhost through nil proxylist 155 | if t.ProxyList == nil || (t.ProxyList)[0] == "" { 156 | return 157 | } else { 158 | for { 159 | if t.Proxy.Raw != "" { 160 | proxies.SafeProxy.ReleaseProxy(t.Proxy.Raw) 161 | } 162 | 163 | rawProxy := proxies.SafeProxy.GetProxy((t.ProxyList)) 164 | 165 | // Proxy is empty wait some time then get new 166 | if rawProxy == "" { 167 | t.UpdateStatus("Waiting for Proxy", activityapi.WarningLevel) 168 | t.WaitR() 169 | continue 170 | } 171 | 172 | parsedProxy, err := proxies.SafeProxy.ParseProxy(rawProxy) 173 | if err != nil { 174 | t.UpdateStatus(err.Error()+", retrying", activityapi.ErrorLevel) 175 | continue 176 | } 177 | 178 | t.Proxy = parsedProxy 179 | t.Client.Client.Transport.(*http.Transport).Proxy = http.ProxyURL(t.Proxy.Formatted) 180 | t.Client.Client.Transport.(*http.Transport).CloseIdleConnections() 181 | return 182 | } 183 | } 184 | } 185 | 186 | func (t *Task) InitializeClient() { 187 | var err error 188 | for { 189 | t.Jar = client.New() 190 | if err != nil { 191 | continue 192 | } 193 | tr := &http.Transport{ 194 | MaxIdleConns: 5, 195 | MaxConnsPerHost: 5, 196 | IdleConnTimeout: 60 * time.Second, 197 | ResponseHeaderTimeout: 30 * time.Second, 198 | DisableCompression: false, 199 | Proxy: http.ProxyURL(nil), 200 | ClientHelloID: &tls.HelloChrome_99, 201 | ForceAttemptHTTP2: true, 202 | TLSClientConfig: &tls.Config{ 203 | InsecureSkipVerify: false, 204 | RootCAs: certs.ServerCertPool(), 205 | }, 206 | } 207 | t.Client.Client = &http.Client{Transport: tr, Jar: t.Jar} 208 | if err != nil { 209 | continue 210 | } 211 | 212 | return 213 | } 214 | } 215 | 216 | func (t *Task) Initialize() { 217 | Ctx, Cancel := context.WithCancel(context.Background()) 218 | t.Ctx = Ctx 219 | t.Cancel = Cancel 220 | 221 | t.InjectTaskData() 222 | t.InitializeClient() 223 | //t.RotateProxy() 224 | t.Stage = Start 225 | } 226 | 227 | func (t *Task) InjectTaskData() { 228 | for { 229 | profileData, err := profilestore.GetProfileByID((*t).ProfileGroupID, (*t).ProfileID) 230 | proxyGroup, err := proxystore.GetProxyGroupByID((*t).ProxyGroupID) 231 | proxy, err := proxies.SafeProxy.ParseProxy(proxies.SafeProxy.GetProxy(proxyGroup.Proxies)) 232 | 233 | if err != nil { 234 | continue 235 | } 236 | 237 | (*t).Profile = profileData 238 | 239 | (*t).Profile.Billing.CardType, err = cardidentification.CreditCardType((*t).Profile) 240 | 241 | if err != nil { 242 | errors.Handler(err) 243 | return 244 | } 245 | 246 | t.ProxyGroupName = proxyGroup.GroupName 247 | proxy.GroupID = (*t).ProxyGroupID 248 | proxy.GroupName = (*t).ProxyGroupName 249 | (*t).Proxy = proxy 250 | (*t).ProxyList = (proxyGroup.Proxies) 251 | return 252 | } 253 | } 254 | 255 | func (t *Task) SendCheckoutData(checkoutStatus bool, checkoutResponse *client.Response, additionalInfo interface{}) { 256 | req := t.Client.NewRequest() 257 | req.Url = "http://127.0.0.1:3000/activity/" 258 | payload := activityapi.RecpData{ 259 | UserInfo: activityapi.UserData{ 260 | UserID: "TESTING", // TODO: get this from bot 261 | ShippingState: t.Profile.Shipping.State, 262 | }, 263 | Settings: activityapi.TaskSettings{ 264 | Site: t.Site, 265 | Product: t.Product.Identifier, 266 | Mode: "Mode one", 267 | }, 268 | Results: activityapi.TaskResults{ 269 | CheckedOut: checkoutStatus, 270 | CheckoutStatusCode: checkoutResponse.StatusCode, 271 | CheckoutMessage: checkoutResponse.Body, 272 | }, 273 | Instance: activityapi.InstanceInfo{ 274 | OS: "Mac", 275 | TotalTasks: 0, 276 | TotalTasksForProduct: 0, 277 | Time: time.Now().Unix(), 278 | }, 279 | } 280 | 281 | if additionalInfo != nil { 282 | payload.AdditionalData = additionalInfo 283 | } 284 | req.SetJSONBody(payload) 285 | 286 | resp, err := req.Do() 287 | 288 | if err != nil { 289 | errors.Handler(err) 290 | } 291 | 292 | if resp.StatusCode != 200 { 293 | errors.Handler(goErrors.New("Failed to send checkout stat!, resp code" + resp.Status)) 294 | } 295 | } 296 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | Init "github.com/umasii/bot-framework/cmd/init" 5 | ) 6 | 7 | func main() { 8 | Init.InitCicada() 9 | } 10 | -------------------------------------------------------------------------------- /monitors/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/umasii/bot-framework/3e969939b65da96d28afdfd05c596785ab884368/monitors/.gitkeep -------------------------------------------------------------------------------- /sites/louisvuitton/entities.go: -------------------------------------------------------------------------------- 1 | package louisvuitton 2 | 3 | const ( 4 | GetSecure = "GetSecure" 5 | Account = "Account" 6 | Product = "Product" 7 | Variants = "Variants" 8 | Login = "Login" 9 | GetCart = "GetCart" 10 | PrepareCart = "PrepareCart" 11 | GetBilling = "GetBilling" 12 | GetCard = "GetCard" 13 | GetSensor = "GetSensor" 14 | GetU = "GetU" 15 | Akamai = "Akamai" 16 | AkamaiPixel = "AkamaiPixel" 17 | SubmitPayment = "SubmitPayment" 18 | Order = "Order" 19 | LittyATC = "LittyATC" 20 | ProductUrl = "ProductUrl" 21 | 22 | AK_API_KEY = "[redacted]" 23 | ) 24 | 25 | type AccountData struct { 26 | email string 27 | password string 28 | } 29 | 30 | type genSensorData struct { 31 | UserAgent string `json:"userAgent"` 32 | Site string `json:"site"` 33 | Cookie string `json:"abck"` 34 | } 35 | 36 | type genPixelData struct { 37 | Site string `json:"site"` 38 | GVal string `json:"gVal"` 39 | UniqueVal string `json:"bazadebezolkohpepadr"` 40 | UserAgent string `json:"userAgent"` 41 | Version string `json:"version"` 42 | } 43 | 44 | type sensorPayload struct { 45 | SensorData string `json:"sensor_data"` 46 | } 47 | 48 | type loginPayload struct { 49 | Email string `json:"login"` 50 | Password string `json:"password"` 51 | } 52 | 53 | type Create struct { 54 | CreditCardType string `json:"creditCardType"` 55 | CreditCardName string `json:"creditCardName"` 56 | CardVerificationNumber string `json:"cardVerificationNumber"` 57 | CreatedFromSavedCard bool `json:"createdFromSavedCard"` 58 | UseExistingCreditCard bool `json:"useExistingCreditCard"` 59 | UseExistingAddress bool `json:"useExistingAddress"` 60 | ValidateCreditCard bool `json:"validateCreditCard"` 61 | BillingAddressName string `json:"billingAddressName"` 62 | } 63 | 64 | type Apply struct { 65 | ThirdPartyPaymentTypeName string `json:"thirdPartyPaymentTypeName"` 66 | ApplyDefaultPaymentGroup bool `json:"applyDefaultPaymentGroup"` 67 | } 68 | 69 | type cardPayload struct { 70 | Createstruct Create `json:"create"` 71 | Applystruct Apply `json:"apply"` 72 | } 73 | 74 | type littyLoad struct { 75 | SkuId string `json:"skuId"` 76 | CatalogRefId []string `json:"catalogRefIds"` 77 | CatalogRefIdKeys []string `json:"catalogRefIdKeys"` 78 | ProductId string `json:"productId"` 79 | Quantity int `json:"quantity"` 80 | } 81 | -------------------------------------------------------------------------------- /sites/louisvuitton/louisvuitton.go: -------------------------------------------------------------------------------- 1 | package louisvuitton 2 | 3 | import ( 4 | "bytes" 5 | //"encoding/base64" 6 | "fmt" 7 | "log" 8 | "regexp" 9 | "strconv" 10 | "strings" 11 | 12 | "net/url" 13 | 14 | "github.com/tidwall/gjson" 15 | "github.com/umasii/bot-framework/internal/activityapi" 16 | "github.com/umasii/bot-framework/internal/tasks" 17 | 18 | api2captcha "github.com/cicadaaio/2cap" 19 | ) 20 | 21 | var lvUrl *url.URL 22 | 23 | func checkPage(shtml string) bool { 24 | if strings.Contains(shtml, "Thank You for Your Interest") { 25 | return false 26 | } else { 27 | return true 28 | } 29 | } 30 | 31 | type LVTask struct { 32 | tasks.Task 33 | aVal string `json:"-"` 34 | uVal string `json:"-"` 35 | tVal string `json:"-"` 36 | 37 | ItemId string `json:"-"` 38 | SkuId string `json:"-"` 39 | CatalogRefId string `json:"-"` 40 | UserAgent string `json:"-"` // we used website UA rather than LV app UA 41 | checkoutRetry int32 `json:"-"` 42 | PrevStage tasks.Stage `json:"-"` 43 | Region string `json:"-"` // for use on Asia/Europe drops (we only ran US though) 44 | Mode string `json:"-"` 45 | SensorData string `json:"-"` 46 | PixelData string `json:"-"` 47 | 48 | CaptchaClient *api2captcha.Client 49 | CaptchaCount int 50 | 51 | Username string `json:"username"` 52 | Password string `json:"password"` 53 | 54 | SensorURL string `json:"-"` 55 | CardId string `json:"-"` 56 | 57 | PaymentRetries int `json:"-"` 58 | OrderRetries int `json:"-"` 59 | PixelScriptURL string `json:"-"` 60 | PixelURL string `json:"-"` 61 | } 62 | 63 | func (e *LVTask) Execute() { 64 | 65 | e.SensorURL = "https://secure.louisvuitton.com/Q9qt2k/lHAJ/mu/aofS/G8ehiyCHsvg/3Q1tXbwSESG5/aVNaAQ/HU/gCBTFTHEo" 66 | 67 | // for the sake of time, account information was hardcoded for Litty2 mode 68 | // Litty1 mode does not require accounts and was to be used on a larger scale 69 | e.Username = "[redacted]" 70 | e.Password = "[redacted]" 71 | 72 | //e.UserAgent = "Mozilla/5.0 (Nintendo Switch; WebApplet) AppleWebKit/609.4 (KHTML, like Gecko) NF/6.0.2.20.5 NintendoBrowser/5.1.0.22023" 73 | e.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36 Edg/105.0.1320.0" 74 | 75 | e.Mode = "Litty2" 76 | 77 | // A nested switch was used to handle mode selection and each step of the task flow 78 | switch e.Mode { 79 | case "Litty2": 80 | switch e.Stage { 81 | case tasks.Start: // tasks.Start is part of the Cicada framework, but no initialization is required 82 | var err error 83 | 84 | if err != nil { 85 | fmt.Println(err) 86 | } 87 | e.Stage = GetSecure 88 | 89 | case GetSecure: // sets initial Akamai cookies 90 | e.getSecure() 91 | 92 | case GetU: // gets Akamai pixel 93 | e.getU() 94 | 95 | case GetSensor: 96 | e.getAkamaiSensor() 97 | 98 | case Akamai: 99 | e.submitSensor() 100 | 101 | case AkamaiPixel: 102 | e.submitPixel() 103 | 104 | case Account: // Litty2 mode used accounts with the Air Forces precarted already 105 | e.login() 106 | 107 | case PrepareCart: 108 | e.prepareCart() 109 | 110 | case GetCart: 111 | e.getCart() 112 | 113 | case GetCard: 114 | e.getCard() 115 | 116 | case SubmitPayment: 117 | e.submitPayment() 118 | 119 | case Order: 120 | e.order() 121 | } 122 | 123 | case "Litty1": 124 | switch e.Stage { 125 | case tasks.Start: 126 | 127 | var err error 128 | 129 | if err != nil { 130 | fmt.Println(err) 131 | } 132 | 133 | e.Stage = GetSecure 134 | 135 | case GetSecure: 136 | e.getSecure() 137 | 138 | case GetU: 139 | e.getU() 140 | 141 | case GetSensor: 142 | e.getAkamaiSensor() 143 | 144 | case Akamai: 145 | e.submitSensor() 146 | 147 | case AkamaiPixel: 148 | e.submitPixel() 149 | 150 | case ProductUrl: 151 | e.productUrl() 152 | 153 | case LittyATC: // this was the ATC bypass 154 | e.littyATC() 155 | 156 | case GetCart: 157 | e.getCart() 158 | 159 | case GetCard: 160 | e.getCard() 161 | 162 | case SubmitPayment: 163 | e.submitPayment() 164 | 165 | case Order: 166 | e.order() 167 | 168 | } 169 | } 170 | 171 | } 172 | 173 | // We chose to fetch product information through an individual LVTask instead of a monitor task for simplicity 174 | // and due to there being 9 different products 175 | func (e *LVTask) getProduct() { 176 | e.UpdateStatus("Fetching product", activityapi.LogLevel) 177 | 178 | req := e.Client.NewRequest() 179 | req.Url = "https://pass.louisvuitton.com/api/facade/api/eng-us/catalog/product/" + e.Product.Identifier // fetching through facade bypassed LV's blocking of web endpoints during drop time 180 | req.Method = "GET" 181 | req.Headers = []map[string]string{ 182 | {"accept": "application/json, text/plain, */*"}, 183 | {"accept-encoding": "gzip, deflate, br"}, 184 | {"accept-language": "en-US,en;q=0.9"}, 185 | {"origin": "https://us.louisvuitton.com"}, 186 | {"referer": "https://us.louisvuitton.com/eng-us/new/for-men/louis-vuitton-and-nike-air-force-1-by-virgil-abloh/_/N-t1769r8n"}, 187 | {"sec-ch-ua": `".Not/A)Brand";v="99", "Google Chrome";v="103", "Chromium";v="103"`}, 188 | {"sec-ch-ua-mobile": "?0"}, 189 | {"sec-ch-ua-platform": `"Windows"`}, 190 | {"sec-fetch-dest": "empty"}, 191 | {"sec-fetch-mode": "cors"}, 192 | {"sec-fetch-site": "same-site"}, 193 | {"user-agent": e.UserAgent}, 194 | } 195 | 196 | resp, err := req.Do() 197 | if err != nil { 198 | // handle error, set next stage 199 | return 200 | } 201 | 202 | if resp.StatusCode == 200 { 203 | productResp := string(resp.Body) 204 | e.Product.ProductName = gjson.Get(productResp, "name").String() 205 | e.Product.Qty = 1 206 | e.Product.Image = gjson.Get(productResp, "model.0.images.0.contentUrl").String() 207 | 208 | return 209 | } else { 210 | e.UpdateStatus(fmt.Sprintf("Bad status code: %s", resp.Status), activityapi.LogLevel) 211 | e.WaitR() 212 | return 213 | } 214 | } 215 | 216 | func (e *LVTask) getVariant() { 217 | e.UpdateStatus("Fetching random in-stock variant", activityapi.LogLevel) 218 | 219 | req := e.Client.NewRequest() 220 | req.Url = "https://pass.louisvuitton.com/api/facade/api/eng-us/catalog/product/" + e.Product.Identifier 221 | req.Method = "GET" 222 | req.Headers = []map[string]string{ 223 | {"accept": "application/json, text/plain, */*"}, 224 | {"accept-encoding": "gzip, deflate, br"}, 225 | {"accept-language": "en-US,en;q=0.9"}, 226 | {"origin": "https://us.louisvuitton.com"}, 227 | {"referer": "https://us.louisvuitton.com/eng-us/new/for-men/louis-vuitton-and-nike-air-force-1-by-virgil-abloh/_/N-t1769r8n"}, 228 | {"sec-ch-ua": `".Not/A)Brand";v="99", "Google Chrome";v="103", "Chromium";v="103"`}, 229 | {"sec-ch-ua-mobile": "?0"}, 230 | {"sec-ch-ua-platform": `"Windows"`}, 231 | {"sec-fetch-dest": "empty"}, 232 | {"sec-fetch-mode": "cors"}, 233 | {"sec-fetch-site": "same-site"}, 234 | {"user-agent": e.UserAgent}, 235 | } 236 | 237 | resp, err := req.Do() 238 | if err != nil { 239 | // handle error, set next stage 240 | return 241 | } 242 | 243 | if resp.StatusCode == 200 { 244 | productResp := string(resp.Body) 245 | e.Product.ProductName = gjson.Get(productResp, "name").String() 246 | e.Product.Qty = 1 247 | e.Product.Image = gjson.Get(productResp, "model.0.images.0.contentUrl").String() 248 | 249 | return 250 | } else { 251 | e.UpdateStatus(fmt.Sprintf("Bad status code: %s", resp.Status), activityapi.LogLevel) 252 | e.WaitR() 253 | return 254 | } 255 | } 256 | 257 | func (e *LVTask) getU() { 258 | 259 | e.UpdateStatus("Getting Pixel", activityapi.LogLevel) 260 | req := e.Client.NewRequest() 261 | req.Url = e.PixelScriptURL 262 | req.Method = "GET" 263 | req.Headers = []map[string]string{ 264 | {"accept": "application/json, text/plain, */*"}, 265 | {"accept-encoding": "gzip, deflate, br"}, 266 | {"accept-language": "en-US,en;q=0.9"}, 267 | {"origin": "https://us.louisvuitton.com"}, 268 | {"sec-ch-ua": `".Not/A)Brand";v="99", "Google Chrome";v="103", "Chromium";v="103"`}, 269 | {"sec-ch-ua-mobile": "?0"}, 270 | {"sec-ch-ua-platform": `"Windows"`}, 271 | {"sec-fetch-dest": "empty"}, 272 | {"sec-fetch-mode": "cors"}, 273 | {"sec-fetch-site": "same-site"}, 274 | {"user-agent": e.UserAgent}, 275 | } 276 | 277 | resp, err := req.Do() 278 | if err != nil { 279 | // handle error, set next stage 280 | log.Println(err) 281 | e.UpdateStatus("Getting Pixel Error", activityapi.LogLevel) 282 | 283 | return 284 | } 285 | 286 | if resp.StatusCode != 200 { 287 | return 288 | } 289 | 290 | body := resp.Body 291 | 292 | var uVal string 293 | 294 | re := regexp.MustCompile("g=_\\[(\\d{1,3})\\]") 295 | matches := re.FindAllStringSubmatch(body, -1) 296 | 297 | if len(matches) == 0 || len(matches[0]) < 2 { 298 | return 299 | } 300 | 301 | index, err := strconv.Atoi(matches[0][1]) 302 | if err != nil { 303 | return 304 | } 305 | 306 | re = regexp.MustCompile("\\[(.*?)\\]") 307 | matchesTwo := re.FindStringSubmatch(body) 308 | 309 | if len(matchesTwo) == 0 { 310 | return 311 | } 312 | 313 | match := matchesTwo[0] 314 | 315 | split := strings.Split(match, ",") 316 | 317 | formatted := split[index] 318 | formatted = strings.ReplaceAll(formatted, `"`, "") 319 | 320 | splitU := strings.Split(formatted, "\\x")[1:] 321 | 322 | for _, s := range splitU { 323 | s = strings.Trim(s, " \r\t\n[]") 324 | 325 | p, err := strconv.ParseInt("0x"+s, 0, 0) 326 | if err != nil { 327 | return 328 | } 329 | 330 | uVal += string(rune(p)) 331 | } 332 | e.uVal = uVal 333 | e.Stage = AkamaiPixel 334 | return 335 | } 336 | 337 | func (e *LVTask) genPixelData() { 338 | e.UpdateStatus("Submitting Pixel", activityapi.LogLevel) 339 | 340 | payload := genPixelData{ 341 | "https://us.louisvuitton.com/", 342 | e.uVal, 343 | e.aVal, 344 | e.UserAgent, 345 | "13", 346 | } 347 | 348 | req := e.Client.NewRequest() 349 | req.Url = "[redacted]" 350 | req.Method = "POST" 351 | req.Headers = []map[string]string{ 352 | {"x-api-key": AK_API_KEY}, 353 | } 354 | req.SetJSONBody(payload) 355 | 356 | resp, err := req.Do() 357 | if err != nil { 358 | // handle error, set next stage 359 | return 360 | } 361 | 362 | if resp.StatusCode == 200 { 363 | sensorResp := string(resp.Body) 364 | e.PixelData = gjson.Get(sensorResp, "pixel_payload").String() 365 | 366 | e.Stage = AkamaiPixel 367 | return 368 | } else { 369 | e.UpdateStatus(fmt.Sprintf("Bad status code: %s", resp.Status), activityapi.LogLevel) 370 | e.WaitR() 371 | return 372 | } 373 | } 374 | 375 | func (e *LVTask) getAkamaiSensor() { 376 | e.UpdateStatus("Generating Akamai sensor", activityapi.LogLevel) 377 | 378 | cookies := e.Jar.Cookies(lvUrl) 379 | 380 | var ak_cookie string 381 | 382 | for _, cookie := range cookies { 383 | if cookie.Name == "_abck" { 384 | ak_cookie = cookie.Value 385 | break 386 | } 387 | } 388 | 389 | if ak_cookie == "" { 390 | e.getSecure() 391 | return 392 | } 393 | 394 | payload := genSensorData{ 395 | e.UserAgent, 396 | "https://secure.louisvuitton.com/", 397 | ak_cookie, 398 | } 399 | 400 | req := e.Client.NewRequest() 401 | req.Url = "[redacted]" 402 | req.Method = "POST" 403 | req.Headers = []map[string]string{ 404 | {"x-api-key": AK_API_KEY}, 405 | } 406 | req.SetJSONBody(payload) 407 | 408 | resp, err := req.Do() 409 | if err != nil { 410 | // handle error, set next stage 411 | return 412 | } 413 | 414 | respBody := string(resp.Body) 415 | fmt.Println(respBody) 416 | if resp.StatusCode == 200 { 417 | sensorResp := string(resp.Body) 418 | e.SensorData = gjson.Get(sensorResp, "sensor_data").String() 419 | 420 | e.Stage = Akamai 421 | return 422 | 423 | } else { 424 | e.UpdateStatus(fmt.Sprintf("Bad status code: %s", resp.Status), activityapi.LogLevel) 425 | e.WaitR() 426 | return 427 | } 428 | } 429 | 430 | func (e *LVTask) getSecure() { 431 | e.UpdateStatus("Getting homepage", activityapi.LogLevel) 432 | 433 | req := e.Client.NewRequest() 434 | req.Url = e.SensorURL 435 | req.Method = "GET" 436 | req.Headers = []map[string]string{ 437 | {"sec-ch-ua": `".Not/A)Brand";v="99", "Google Chrome";v="103", "Chromium";v="103"`}, 438 | {"sec-ch-ua-mobile": "?0"}, 439 | {"sec-ch-ua-platform": `"Windows"`}, 440 | {"upgrade-insecure-requests": "1"}, 441 | {"user-agent": e.UserAgent}, 442 | {"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"}, 443 | {"sec-fetch-site": "same-origin"}, 444 | {"sec-fetch-mode": "navigate"}, 445 | {"sec-fetch-user": "?1"}, 446 | {"sec-fetch-dest": "document"}, 447 | {"accept-encoding": "gzip, deflate, br"}, 448 | {"accept-language": "en-US,en;q=0.9"}, 449 | } 450 | 451 | resp, err := req.Do() 452 | if err != nil { 453 | // handle error, set next stage 454 | return 455 | } 456 | 457 | if resp.StatusCode == 200 { 458 | lvUrl = resp.Url 459 | if e.Mode == "Litty1" { 460 | e.Stage = GetU 461 | } else { 462 | e.Stage = GetSensor 463 | } 464 | 465 | return 466 | } 467 | 468 | e.UpdateStatus(fmt.Sprintf("Bad status code: %s", resp.Status), activityapi.LogLevel) 469 | e.WaitR() 470 | 471 | return 472 | } 473 | 474 | func (e *LVTask) submitSensor() { 475 | e.UpdateStatus("Submitting Akamai sensor", activityapi.LogLevel) 476 | 477 | payload := sensorPayload{ 478 | e.SensorData, 479 | } 480 | 481 | req := e.Client.NewRequest() 482 | req.Url = e.SensorURL 483 | req.Method = "POST" 484 | req.Headers = []map[string]string{ 485 | {"accept": "*/*"}, 486 | {"accept-encoding": "gzip, deflate, br"}, 487 | {"accept-language": "en-US,en;q=0.9"}, 488 | //{"content-length":""}, 489 | {"content-type": "text/plain;charset=UTF-8"}, 490 | {"origin": "https://secure.louisvuitton.com"}, 491 | {"referer": "https://secure.louisvuitton.com/eng-us/mylv"}, 492 | {"sec-ch-ua": `".Not/A)Brand";v="99", "Google Chrome";v="103", "Chromium";v="103"`}, 493 | {"sec-ch-ua-mobile": "?0"}, 494 | {"sec-ch-ua-platform": `"Windows"`}, 495 | {"sec-fetch-dest": "empty"}, 496 | {"sec-fetch-mode": "cors"}, 497 | {"sec-fetch-site": "same-site"}, 498 | {"user-agent": e.UserAgent}, 499 | } 500 | req.SetJSONBody(payload) 501 | 502 | resp, err := req.Do() 503 | if err != nil { 504 | return 505 | } 506 | 507 | respBody := string(resp.Body) 508 | fmt.Println(respBody) 509 | 510 | if resp.StatusCode == 200 || resp.StatusCode == 201 { 511 | var ak_cookie string 512 | cookies := e.Jar.Cookies(lvUrl) 513 | for _, cookie := range cookies { 514 | if cookie.Name == "_abck" { 515 | ak_cookie = cookie.Value 516 | break 517 | } 518 | } 519 | 520 | if ak_cookie == "" { 521 | e.getSecure() 522 | e.getAkamaiSensor() 523 | e.Stage = Akamai 524 | return 525 | } else if strings.Contains(ak_cookie, "~0~") { 526 | if e.Mode == "Litty1" { 527 | e.Stage = ProductUrl 528 | 529 | } else { 530 | e.Stage = Account 531 | } 532 | 533 | return 534 | } else { 535 | e.getAkamaiSensor() 536 | } 537 | 538 | // set next stage 539 | return 540 | } else { 541 | e.UpdateStatus(fmt.Sprintf("Bad status code: %s", resp.Status), activityapi.LogLevel) 542 | e.WaitR() 543 | return 544 | } 545 | } 546 | 547 | func (e *LVTask) submitPixel() { 548 | e.UpdateStatus("Submitting Akamai pixel", activityapi.LogLevel) 549 | 550 | req := e.Client.NewRequest() 551 | req.Url = e.PixelURL 552 | req.Method = "POST" 553 | req.Headers = []map[string]string{ 554 | {"accept": "*/*"}, 555 | {"accept-encoding": "gzip, deflate, br"}, 556 | {"accept-language": "en-US,en;q=0.9"}, 557 | //{"content-length":""}, 558 | {"content-type": "application/x-www-form-urlencoded"}, 559 | {"origin": "https://secure.louisvuitton.com"}, 560 | {"referer": "https://secure.louisvuitton.com/eng-us/mylv"}, 561 | {"sec-ch-ua": `".Not/A)Brand";v="99", "Google Chrome";v="103", "Chromium";v="103"`}, 562 | {"sec-ch-ua-mobile": "?0"}, 563 | {"sec-ch-ua-platform": `"Windows"`}, 564 | {"sec-fetch-dest": "empty"}, 565 | {"sec-fetch-mode": "cors"}, 566 | {"sec-fetch-site": "same-site"}, 567 | {"user-agent": e.UserAgent}, 568 | } 569 | req.Body = bytes.NewBuffer([]byte(e.PixelData)) 570 | 571 | resp, err := req.Do() 572 | if err != nil { 573 | return 574 | } 575 | 576 | respBody := string(resp.Body) 577 | fmt.Println(respBody) 578 | 579 | if resp.StatusCode == 200 || resp.StatusCode == 201 { 580 | e.Stage = Akamai 581 | return 582 | } else { 583 | e.UpdateStatus(fmt.Sprintf("Bad status code: %s", resp.Status), activityapi.LogLevel) 584 | e.WaitR() 585 | return 586 | } 587 | } 588 | 589 | func (e *LVTask) login() { 590 | e.UpdateStatus("Logging into account", activityapi.LogLevel) 591 | 592 | payload := loginPayload{ 593 | e.Username, 594 | e.Password, 595 | } 596 | 597 | req := e.Client.NewRequest() 598 | req.Url = "https://api.louisvuitton.com/api/eng-us/account/login/" 599 | req.Method = "POST" 600 | req.Headers = []map[string]string{ 601 | {"accept": "application/json, text/plain, */*"}, 602 | {"accept-encoding": "gzip, deflate, br"}, 603 | {"accept-language": "en-US,en;q=0.9"}, 604 | //{"content-length":""}, 605 | {"content-type": "application/json"}, 606 | {"origin": "https://us.louisvuitton.com"}, 607 | {"referer": "https://us.louisvuitton.com/eng-us/homepage"}, 608 | {"sec-ch-ua": `".Not/A)Brand";v="99", "Google Chrome";v="103", "Chromium";v="103"`}, 609 | {"sec-ch-ua-mobile": "?0"}, 610 | {"sec-ch-ua-platform": `"Windows"`}, 611 | {"sec-fetch-dest": "empty"}, 612 | {"sec-fetch-mode": "cors"}, 613 | {"sec-fetch-site": "same-site"}, 614 | {"user-agent": e.UserAgent}, 615 | } 616 | req.SetJSONBody(payload) 617 | 618 | resp, err := req.Do() 619 | if err != nil { 620 | return 621 | } 622 | 623 | respBody := string(resp.Body) 624 | fmt.Println(respBody) 625 | 626 | if resp.StatusCode == 200 || resp.StatusCode == 201 { 627 | ok := checkPage(resp.Body) 628 | if ok { 629 | e.Stage = PrepareCart 630 | return 631 | } else { 632 | e.WaitR() 633 | return 634 | } 635 | // set next stage 636 | return 637 | } else if resp.StatusCode == 403 { 638 | e.getAkamaiSensor() 639 | e.submitSensor() 640 | e.Stage = Account 641 | return 642 | 643 | } else { 644 | e.UpdateStatus(fmt.Sprintf("Bad status code: %s", resp.Status), activityapi.LogLevel) 645 | e.WaitR() 646 | return 647 | } 648 | } 649 | 650 | func (e *LVTask) prepareCart() { 651 | e.UpdateStatus("Preparing cart", activityapi.LogLevel) 652 | 653 | req := e.Client.NewRequest() 654 | req.Url = "https://api.louisvuitton.com/api/eng-us/cart/prepare-order" 655 | req.Method = "PUT" 656 | req.Headers = []map[string]string{ 657 | {"accept": "application/json, text/plain, */*"}, 658 | {"accept-encoding": "gzip, deflate, br"}, 659 | {"accept-language": "en-US,en;q=0.9"}, 660 | //{"content-length":""}, 661 | {"content-type": "application/json"}, 662 | {"origin": "https://us.louisvuitton.com"}, 663 | {"referer": "https://us.louisvuitton.com/eng-us/cart"}, 664 | {"sec-ch-ua": `".Not/A)Brand";v="99", "Google Chrome";v="103", "Chromium";v="103"`}, 665 | {"sec-ch-ua-mobile": "?0"}, 666 | {"sec-ch-ua-platform": `"Windows"`}, 667 | {"sec-fetch-dest": "empty"}, 668 | {"sec-fetch-mode": "cors"}, 669 | {"sec-fetch-site": "same-site"}, 670 | {"user-agent": e.UserAgent}, 671 | } 672 | req.Body = strings.NewReader("{}") 673 | 674 | resp, err := req.Do() 675 | if err != nil { 676 | return 677 | } 678 | 679 | respBody := string(resp.Body) 680 | fmt.Println(respBody) 681 | 682 | if resp.StatusCode == 200 || resp.StatusCode == 201 { 683 | ok := checkPage(resp.Body) 684 | if ok { 685 | e.Stage = GetCart 686 | return 687 | } else { 688 | e.WaitR() 689 | 690 | return 691 | } 692 | 693 | } else if resp.StatusCode == 403 { 694 | e.getAkamaiSensor() 695 | e.submitSensor() 696 | e.Stage = PrepareCart 697 | return 698 | 699 | } else { 700 | e.UpdateStatus(fmt.Sprintf("Bad status code: %s", resp.Status), activityapi.LogLevel) 701 | e.WaitR() 702 | return 703 | } 704 | } 705 | 706 | func (e *LVTask) getCard() { 707 | e.UpdateStatus("Fetching payment method", activityapi.LogLevel) 708 | 709 | req := e.Client.NewRequest() 710 | req.Url = "https://api.louisvuitton.com/api/eng-us/account/credit-cards" 711 | req.Method = "GET" 712 | req.Headers = []map[string]string{ 713 | {"accept": "application/json, text/plain, */*"}, 714 | {"accept-encoding": "gzip, deflate, br"}, 715 | {"accept-language": "en-US,en;q=0.9"}, 716 | {"content-type": "application/json"}, 717 | {"origin": "https://us.louisvuitton.com"}, 718 | {"referer": "https://us.louisvuitton.com/eng-us/checkout/payment"}, 719 | {"sec-ch-ua": `".Not/A)Brand";v="99", "Google Chrome";v="103", "Chromium";v="103"`}, 720 | {"sec-ch-ua-mobile": "?0"}, 721 | {"sec-ch-ua-platform": `"Windows"`}, 722 | {"sec-fetch-dest": "empty"}, 723 | {"sec-fetch-mode": "cors"}, 724 | {"sec-fetch-site": "same-site"}, 725 | {"user-agent": e.UserAgent}, 726 | } 727 | 728 | resp, err := req.Do() 729 | if err != nil { 730 | return 731 | } 732 | 733 | respBody := string(resp.Body) 734 | fmt.Println(respBody) 735 | 736 | if resp.StatusCode == 200 || resp.StatusCode == 201 { 737 | cardResp := string(resp.Body) 738 | e.CardId = gjson.Get(cardResp, "creditCardList.0.creditCardName").String() 739 | e.Stage = SubmitPayment 740 | return 741 | 742 | } else if resp.StatusCode == 403 { 743 | e.getAkamaiSensor() 744 | e.submitSensor() 745 | e.Stage = GetCard 746 | return 747 | 748 | } else { 749 | e.UpdateStatus(fmt.Sprintf("Bad status code: %s", resp.Status), activityapi.LogLevel) 750 | e.WaitR() 751 | return 752 | } 753 | } 754 | 755 | func (e *LVTask) getCart() { 756 | e.UpdateStatus("Fetching cart", activityapi.LogLevel) 757 | 758 | req := e.Client.NewRequest() 759 | req.Url = "https://api.louisvuitton.com/api/eng-us/cart/full" 760 | req.Method = "GET" 761 | req.Headers = []map[string]string{ 762 | {"accept": "application/json, text/plain, */*"}, 763 | {"accept-encoding": "gzip, deflate, br"}, 764 | {"accept-language": "en-US,en;q=0.9"}, 765 | {"content-type": "application/json"}, 766 | {"origin": "https://us.louisvuitton.com"}, 767 | {"referer": "https://us.louisvuitton.com/eng-us/checkout"}, 768 | {"sec-ch-ua": `".Not/A)Brand";v="99", "Google Chrome";v="103", "Chromium";v="103"`}, 769 | {"sec-ch-ua-mobile": "?0"}, 770 | {"sec-ch-ua-platform": `"Windows"`}, 771 | {"sec-fetch-dest": "empty"}, 772 | {"sec-fetch-mode": "cors"}, 773 | {"sec-fetch-site": "same-site"}, 774 | {"user-agent": e.UserAgent}, 775 | } 776 | 777 | resp, err := req.Do() 778 | if err != nil { 779 | return 780 | } 781 | 782 | respBody := string(resp.Body) 783 | fmt.Println(respBody) 784 | 785 | if resp.StatusCode == 200 || resp.StatusCode == 201 { 786 | ok := checkPage(resp.Body) 787 | if ok { 788 | e.Stage = GetCard 789 | return 790 | } else { 791 | e.WaitR() 792 | 793 | return 794 | } 795 | 796 | } else if resp.StatusCode == 403 { 797 | e.getAkamaiSensor() 798 | e.submitSensor() 799 | e.Stage = GetCart 800 | return 801 | 802 | } else { 803 | e.UpdateStatus(fmt.Sprintf("Bad status code: %s", resp.Status), activityapi.LogLevel) 804 | e.WaitR() 805 | return 806 | } 807 | } 808 | 809 | func (e *LVTask) productUrl() { 810 | e.UpdateStatus("Fetching product URL", activityapi.LogLevel) 811 | 812 | req := e.Client.NewRequest() 813 | req.Url = "https://us.louisvuitton.com/eng-us/products/lv-trainer-sneaker-nvprod3710063v/1AAHS3" 814 | req.Method = "GET" 815 | req.Headers = []map[string]string{ 816 | {"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"}, 817 | {"accept-encoding": "gzip, deflate, br"}, 818 | {"accept-language": "en-US,en;q=0.9"}, 819 | {"content-type": "application/json"}, 820 | {"origin": "https://us.louisvuitton.com"}, 821 | {"referer": "https://us.louisvuitton.com/eng-us/homepage"}, 822 | {"sec-ch-ua": `".Not/A)Brand";v="99", "Google Chrome";v="103", "Chromium";v="103"`}, 823 | {"sec-ch-ua-mobile": "?0"}, 824 | {"sec-ch-ua-platform": `"Windows"`}, 825 | {"sec-fetch-dest": "empty"}, 826 | {"sec-fetch-mode": "cors"}, 827 | {"sec-fetch-site": "same-site"}, 828 | {"user-agent": e.UserAgent}, 829 | } 830 | 831 | resp, err := req.Do() 832 | if err != nil { 833 | return 834 | } 835 | 836 | respBody := string(resp.Body) 837 | fmt.Println(respBody) 838 | 839 | if resp.StatusCode == 200 || resp.StatusCode == 201 { 840 | e.Stage = LittyATC 841 | 842 | return 843 | 844 | } else if resp.StatusCode == 403 { 845 | e.getAkamaiSensor() 846 | e.submitSensor() 847 | e.Stage = ProductUrl 848 | return 849 | 850 | } else { 851 | e.UpdateStatus(fmt.Sprintf("Bad status code: %s", resp.Status), activityapi.LogLevel) 852 | e.WaitR() 853 | return 854 | } 855 | } 856 | 857 | func (e *LVTask) submitPayment() { 858 | e.UpdateStatus("Submitting payment method", activityapi.LogLevel) 859 | 860 | var cardType string 861 | 862 | if e.Profile.Billing.CardType == "amex" { 863 | cardType = "americanExpress" 864 | } else if e.Profile.Billing.CardType == "mastercard" { 865 | cardType = "masterCard" 866 | 867 | } else if e.Profile.Billing.CardType == "visa" { 868 | cardType = "visa" 869 | } else { 870 | cardType = strings.ToLower(e.Profile.Billing.CardType) 871 | } 872 | 873 | createStruct := Create{ 874 | cardType, 875 | e.CardId, 876 | e.Profile.Billing.CVC, 877 | true, 878 | true, 879 | true, 880 | true, 881 | "main", 882 | } 883 | 884 | applyStruct := Apply{ 885 | "", 886 | true, 887 | } 888 | 889 | payload := cardPayload{ 890 | createStruct, 891 | applyStruct, 892 | } 893 | 894 | req := e.Client.NewRequest() 895 | req.Url = "https://api.louisvuitton.com/api/eng-us/checkout/payment/creditcard" 896 | req.Method = "POST" 897 | req.Headers = []map[string]string{ 898 | {"accept": "application/json, text/plain, */*"}, 899 | {"accept-encoding": "gzip, deflate, br"}, 900 | {"accept-language": "en-US,en;q=0.9"}, 901 | //{"content-length":""}, 902 | {"content-type": "application/json"}, 903 | {"origin": "https://us.louisvuitton.com"}, 904 | {"referer": "https://us.louisvuitton.com/eng-us/checkout/payment"}, 905 | {"sec-ch-ua": `".Not/A)Brand";v="99", "Google Chrome";v="103", "Chromium";v="103"`}, 906 | {"sec-ch-ua-mobile": "?0"}, 907 | {"sec-ch-ua-platform": `"Windows"`}, 908 | {"sec-fetch-dest": "empty"}, 909 | {"sec-fetch-mode": "cors"}, 910 | {"sec-fetch-site": "same-site"}, 911 | {"user-agent": e.UserAgent}, 912 | } 913 | 914 | req.SetJSONBody(payload) 915 | 916 | resp, err := req.Do() 917 | if err != nil { 918 | // handle error, set next stage 919 | return 920 | } 921 | 922 | respBody := string(resp.Body) 923 | fmt.Println(respBody) 924 | 925 | if resp.StatusCode == 200 || resp.StatusCode == 201 { 926 | ok := checkPage(resp.Body) 927 | if ok { 928 | e.Stage = Order 929 | return 930 | } else { 931 | e.WaitR() 932 | 933 | return 934 | } 935 | 936 | } else if resp.StatusCode == 403 { 937 | e.PaymentRetries++ 938 | if e.PaymentRetries > 4 { 939 | e.PaymentRetries = 0 940 | e.RotateProxy() 941 | } 942 | e.getAkamaiSensor() 943 | e.submitSensor() 944 | e.Stage = SubmitPayment 945 | return 946 | 947 | } else if resp.StatusCode == 500 { 948 | errorResp := string(resp.Body) 949 | errorMsg := gjson.Get(errorResp, "errors.0.errorCode").String() 950 | e.UpdateStatus(errorMsg, activityapi.LogLevel) 951 | e.WaitM() 952 | return 953 | 954 | } else { 955 | 956 | e.UpdateStatus(fmt.Sprintf("Bad status code: %s", resp.Status), activityapi.LogLevel) 957 | e.WaitR() 958 | return 959 | } 960 | } 961 | 962 | func (e *LVTask) order() { 963 | e.UpdateStatus("Submitting order", activityapi.LogLevel) 964 | 965 | req := e.Client.NewRequest() 966 | req.Url = "https://api.louisvuitton.com/api/eng-us/checkout/order/commit" 967 | req.Method = "POST" 968 | req.Headers = []map[string]string{ 969 | {"accept": "application/json, text/plain, */*"}, 970 | {"accept-encoding": "gzip, deflate, br"}, 971 | {"accept-language": "en-US,en;q=0.9"}, 972 | //{"content-length":""}, 973 | {"content-type": "application/json"}, 974 | {"origin": "https://us.louisvuitton.com"}, 975 | {"referer": "https://us.louisvuitton.com/eng-us/checkout/review"}, 976 | {"sec-ch-ua": `".Not/A)Brand";v="99", "Google Chrome";v="103", "Chromium";v="103"`}, 977 | {"sec-ch-ua-mobile": "?0"}, 978 | {"sec-ch-ua-platform": `"Windows"`}, 979 | {"sec-fetch-dest": "empty"}, 980 | {"sec-fetch-mode": "cors"}, 981 | {"sec-fetch-site": "same-site"}, 982 | {"user-agent": e.UserAgent}, 983 | } 984 | req.Body = strings.NewReader("{}") 985 | 986 | resp, err := req.Do() 987 | if err != nil { 988 | // handle error, set next stage 989 | return 990 | } 991 | 992 | respBody := string(resp.Body) 993 | fmt.Println(respBody) 994 | 995 | if resp.StatusCode == 200 || resp.StatusCode == 201 { 996 | ok := checkPage(resp.Body) 997 | if ok { 998 | e.Stop() 999 | return 1000 | } else { 1001 | e.WaitR() 1002 | 1003 | return 1004 | } 1005 | 1006 | } else if resp.StatusCode == 403 { 1007 | e.OrderRetries++ 1008 | if e.OrderRetries > 4 { 1009 | e.OrderRetries = 0 1010 | e.RotateProxy() 1011 | } 1012 | e.getAkamaiSensor() 1013 | e.submitSensor() 1014 | e.Stage = Order 1015 | return 1016 | 1017 | } else if resp.StatusCode == 500 { 1018 | errorResp := string(resp.Body) 1019 | errorMsg := gjson.Get(errorResp, "errors.0.errorCode").String() 1020 | e.UpdateStatus(errorMsg, activityapi.LogLevel) 1021 | return 1022 | 1023 | } else { 1024 | e.UpdateStatus(fmt.Sprintf("Bad status code: %s", resp.Status), activityapi.LogLevel) 1025 | e.WaitR() 1026 | return 1027 | } 1028 | } 1029 | 1030 | func (e *LVTask) littyATC() { 1031 | e.UpdateStatus("Inverting cart marticies", activityapi.LogLevel) // quasi professional cart matrix inverter moment 1032 | 1033 | payload := littyLoad{ // only SkuId and Quantity are necessary, other keys from default ATC were included as a precautionary measure 1034 | e.Product.Identifier, 1035 | []string{e.Product.Identifier}, 1036 | []string{"c21b0b857c24b1925dc02c7070e066b3"}, 1037 | e.ItemId, 1038 | 1, 1039 | } 1040 | 1041 | req := e.Client.NewRequest() 1042 | req.Url = "https://secure.louisvuitton.com/rest/bean/vuitton/commerce/services/cart/1_0/CartService/addToCart?storeLang=eng-us" 1043 | req.Method = "POST" 1044 | req.Headers = []map[string]string{ 1045 | {"sec-ch-ua": `".Not/A)Brand";v="99", "Google Chrome";v="103", "Chromium";v="103"`}, 1046 | {"accept": "application/json, text/plain, */*"}, 1047 | {"content-type": "application/json"}, 1048 | {"sec-ch-ua-mobile": "?0"}, 1049 | {"user-agent": e.UserAgent}, 1050 | {"sec-ch-ua-platform": `"Windows"`}, 1051 | {"origin": "https://us.louisvuitton.com"}, 1052 | {"sec-fetch-site": "same-site"}, 1053 | {"sec-fetch-mode": "cors"}, 1054 | {"sec-fetch-dest": "empty"}, 1055 | {"referer": "https://us.louisvuitton.com/eng-us/products/lv-trainer-sneaker-nvprod3710063v/1AAHS3"}, 1056 | {"accept-encoding": "gzip, deflate, br"}, 1057 | {"accept-language": "en-US,en;q=0.9"}, 1058 | //{"content-length":""}, 1059 | } 1060 | req.SetJSONBody(payload) 1061 | 1062 | resp, err := req.Do() 1063 | if err != nil { 1064 | // handle error, set next stage 1065 | return 1066 | } 1067 | 1068 | respBody := string(resp.Body) 1069 | fmt.Println(respBody) 1070 | 1071 | if resp.StatusCode == 200 || resp.StatusCode == 201 { 1072 | 1073 | e.Stage = GetCart 1074 | return 1075 | 1076 | } else if resp.StatusCode == 403 { 1077 | e.getAkamaiSensor() 1078 | e.submitSensor() 1079 | e.Stage = LittyATC 1080 | return 1081 | 1082 | } else { 1083 | e.UpdateStatus(fmt.Sprintf("Bad status code: %s", resp.Status), activityapi.LogLevel) 1084 | e.WaitR() 1085 | return 1086 | } 1087 | } 1088 | -------------------------------------------------------------------------------- /sites/walmart/headers.go: -------------------------------------------------------------------------------- 1 | package walmart 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "time" 7 | ) 8 | 9 | var acceptList = []string{ 10 | ",application/xhtml+xml", 11 | ",application/xml", 12 | ",image/webp", 13 | ",image/apng", 14 | ",text/html", 15 | } 16 | 17 | func randomAccept() string { 18 | rand.Seed(time.Now().UnixNano()) 19 | return fmt.Sprintf(`application/json%s`, acceptList[rand.Intn(len(acceptList))]) 20 | } 21 | 22 | func randomChUa() string { 23 | rand.Seed(time.Now().UnixNano()) 24 | return fmt.Sprintf(`" Not A;Brand";v="%d", "Chromium";v="%d", "Google Chrome";v="%d"`, rand.Intn(10)+90, rand.Intn(12)+80, rand.Intn(12)+80) 25 | } 26 | 27 | var langList = []string{ 28 | "fr-CH", 29 | "fr", 30 | "de", 31 | "*", 32 | "en-GB", 33 | "de-CH", 34 | "it", 35 | "es", 36 | } 37 | 38 | func randomAcceptLanguage() string { 39 | rand.Seed(time.Now().UnixNano()) 40 | return fmt.Sprintf("en-US,en;q=0.9,%s;q=0.%d", langList[rand.Intn(len(langList))], rand.Intn(9)+1) 41 | } 42 | 43 | func randomizeHeaders(headers []map[string]string) []map[string]string { 44 | rand.Seed(time.Now().UnixNano()) 45 | rand.Shuffle(len(headers), func(i, j int) { 46 | headers[i], headers[j] = headers[j], headers[i] 47 | }) 48 | 49 | return headers 50 | 51 | } 52 | -------------------------------------------------------------------------------- /sites/walmart/walmart.go: -------------------------------------------------------------------------------- 1 | package walmart 2 | 3 | import ( 4 | "encoding/hex" 5 | "encoding/json" 6 | goErrors "errors" 7 | "fmt" 8 | 9 | "github.com/umasii/bot-framework/internal/activityapi" 10 | "github.com/umasii/bot-framework/internal/client" 11 | errors "github.com/umasii/bot-framework/internal/errors" 12 | "github.com/umasii/bot-framework/internal/tasks" 13 | 14 | api2captcha "github.com/cicadaaio/2cap" 15 | 16 | WmMonitor "github.com/umasii/bot-framework/monitors/walmart" 17 | 18 | uuid "github.com/PrismAIO/go.uuid" 19 | 20 | encrypt "github.com/cicadaaio/walmart-encryption" 21 | 22 | "math/rand" 23 | "net/url" 24 | "regexp" 25 | "strconv" 26 | "strings" 27 | "sync" 28 | "time" 29 | 30 | "github.com/cicadaaio/httpclient/net/http" 31 | ) 32 | 33 | type WalmartTask struct { 34 | tasks.Task 35 | Px PxInfo `json:"-"` 36 | Mode WmMonitor.Mode 37 | ItemId string `json:"-"` 38 | UserAgent string `json:"-"` // Set specifically for WM as it requires PX cookies which are UA bound 39 | secCookieWg *sync.WaitGroup 40 | checkoutRetry int `json:"-"` 41 | PrevStage tasks.Stage `json:"-"` 42 | 43 | StoreData SavedShippingRates `json:"-"` 44 | 45 | PieKeyId1 string `json:"-"` 46 | PieKeyId2 string `json:"-"` 47 | PiePhase1 string `json:"-"` 48 | PiePhase2 string `json:"-"` 49 | CardData1 [3]string `json:"-"` //TODO: Need to check if objects are actually strings 50 | CardData2 [3]string `json:"-"` 51 | PiHash string `json:"-"` 52 | StockStatus bool `json:"-"` 53 | CaptchaClient *api2captcha.Client 54 | CaptchaCount int 55 | Language string 56 | SechUa string 57 | Accept string 58 | } 59 | 60 | func (e *WalmartTask) Execute() { 61 | if e.CaptchaCount > 3 && e.Stage != Cart { 62 | e.RotateProxy() 63 | return 64 | } 65 | switch e.Stage { 66 | case tasks.Start: 67 | var err error 68 | 69 | if err != nil { 70 | fmt.Println(err) 71 | } 72 | e.Language = randomAcceptLanguage() 73 | e.Accept = randomAccept() 74 | e.SechUa = randomChUa() 75 | 76 | e.setWmCookie() 77 | 78 | e.Mode = WmMonitor.ModeTwo 79 | e.secCookieWg = &sync.WaitGroup{} 80 | e.pxLoop() 81 | 82 | case Account: 83 | e.Stage = ShippingRates 84 | return 85 | e.genAccount() 86 | 87 | case ShippingRates: 88 | e.genShippingRates() 89 | 90 | case GetCart: 91 | e.getCart() 92 | 93 | case PreEncrypt: 94 | e.preEncrypt() 95 | 96 | case CC: 97 | e.Stage = Cart 98 | return 99 | e.submitCC() 100 | 101 | case Cart: 102 | e.cart() 103 | 104 | case tasks.InStock: 105 | e.startCheckout() 106 | 107 | case Checkout: 108 | 109 | e.startCheckout() 110 | 111 | case Shipping: 112 | e.submitShipping() 113 | 114 | case Payment: 115 | e.submitPayment() 116 | case Order: 117 | e.submitOrder() 118 | 119 | case DeleteItem: 120 | e.deleteItem() 121 | } 122 | } 123 | 124 | func (e *WalmartTask) AutoSolve() string { 125 | e.UpdateStatus("Solving captcha...", activityapi.LogLevel) 126 | 127 | cap := api2captcha.ReCaptcha{ 128 | Version: "RecaptchaV3TaskProxyless", 129 | SiteKey: "6Lc8-RIaAAAAAPWSm2FVTyBg-Zkz2UjsWWfrkgYN", 130 | Url: "https://www.walmart.com/cart", 131 | Invisible: false, 132 | Action: "handleCaptcha", 133 | Score: 0.9, 134 | } 135 | 136 | for { 137 | req := cap.ToRequest() 138 | 139 | code, err := e.CaptchaClient.Solve(req) 140 | 141 | if err == nil { 142 | e.UpdateStatus("SOLVED Captcha "+code, activityapi.LogLevel) 143 | return code 144 | } else { 145 | errors.Handler(err) 146 | } 147 | 148 | } 149 | } 150 | func (e *WalmartTask) pxLoop() { 151 | 152 | pxWait := make(chan bool) // We do not want to progress until the initial PX cookie has been set, so a channel is set after the first loop 153 | i := false 154 | 155 | go func() { 156 | for { 157 | if i == true { 158 | time.Sleep(270 * time.Second) 159 | } 160 | 161 | err := e.getPxCookies() 162 | 163 | if err == nil { 164 | 165 | pxWait <- true 166 | i = true 167 | 168 | continue 169 | } else { 170 | 171 | errors.Handler(err) 172 | 173 | continue 174 | } 175 | } 176 | }() 177 | 178 | <-pxWait 179 | 180 | e.Stage = Account 181 | 182 | } 183 | 184 | func (e *WalmartTask) getPxCookies() error { 185 | 186 | uuid := string(hex.EncodeToString(uuid.NewV1().Bytes())) 187 | uuidstr := uuid[:8] + "-" + uuid[8:12] + "-" + uuid[12:16] + "-" + uuid[16:20] + "-" + uuid[20:] 188 | e.UpdateStatus("Getting PX cookies", activityapi.LogLevel) 189 | 190 | pxPacket := PxInfo{ 191 | Uuid: &uuidstr, 192 | Site: "walmart", 193 | Proxy: e.Proxy.Formatted.String(), 194 | Key: "CDDF7993D938682E9D319592E79EC"} 195 | 196 | req := e.Client.NewRequest() 197 | req.Method = "POST" 198 | 199 | req.Url = "https://px.cicadabots.com/px" 200 | 201 | req.SetJSONBody(pxPacket) 202 | 203 | req.Headers = randomizeHeaders([]map[string]string{ 204 | {"content-type": "application/json"}, 205 | }) 206 | resp, err := req.Do() 207 | if err != nil { 208 | 209 | return goErrors.New("failed to send px request") 210 | } 211 | 212 | if resp.StatusCode == 200 { 213 | 214 | var pxApiResp pxResp 215 | 216 | err = json.Unmarshal([]byte(resp.Body), &pxApiResp) 217 | if err != nil { 218 | 219 | return goErrors.New("failed to process px data") 220 | } 221 | 222 | e.setPxCookie(pxApiResp.Px3) 223 | 224 | e.UserAgent = pxApiResp.Ua 225 | 226 | e.Px.SetID = &pxApiResp.SetID 227 | e.Px.Uuid = &pxApiResp.Uuid 228 | e.Px.Vid = &pxApiResp.Vid 229 | 230 | e.UpdateStatus("Successfully genned PX cookies", activityapi.LogLevel) 231 | return nil 232 | 233 | } else { 234 | 235 | e.UpdateStatus("Failed to gen PX cookie", activityapi.ErrorLevel) 236 | 237 | e.WaitR() 238 | return goErrors.New("failed to gen px cookie") 239 | } 240 | 241 | } 242 | 243 | func (e *WalmartTask) PxCheck(resp *client.Response) bool { 244 | 245 | if resp.StatusCode == 200 || resp.StatusCode == 201 { // I figure we should do this first thing so we don't waste time doing ioutil on fine requests 246 | return false 247 | } 248 | 249 | if (resp.StatusCode == 412 || resp.StatusCode == 307) && strings.Contains(string(resp.Body), "blocked") { 250 | e.UpdateStatus("PX Captcha blocked ["+strconv.Itoa(resp.StatusCode)+"]", activityapi.ErrorLevel) 251 | e.CaptchaCount++ 252 | e.RotateProxy() 253 | e.getPxCookies() 254 | 255 | } 256 | 257 | return true 258 | } 259 | 260 | func (e *WalmartTask) pxCap( /*captchaToken string*/) error { 261 | 262 | e.UpdateStatus("Getting PX captcha cookies", activityapi.LogLevel) 263 | 264 | e.Px.Site = "walmart" 265 | e.Px.Proxy = e.Proxy.Formatted.String() 266 | e.Px.Key = "CDDF7993D938682E9D319592E79EC" 267 | pxPacket := e.Px 268 | 269 | req := e.Client.NewRequest() 270 | 271 | req.Method = "POST" 272 | req.SetJSONBody(pxPacket) 273 | req.Url = "https://px.cicadabots.com/px" 274 | req.Headers = randomizeHeaders([]map[string]string{ 275 | 276 | {"content-type": "application/json"}, 277 | }) 278 | 279 | resp, err := req.Do() 280 | 281 | if err != nil { 282 | 283 | return goErrors.New("failed to send PX Captcha request") 284 | } 285 | 286 | if resp.StatusCode == 200 { 287 | 288 | var pxApiResp pxResp 289 | 290 | err = json.Unmarshal([]byte(resp.Body), &pxApiResp) 291 | if err != nil { 292 | 293 | errors.Handler(err) 294 | 295 | return goErrors.New("failed to process PX Captcha request") 296 | } 297 | 298 | e.setPxCookie(pxApiResp.Px3) 299 | 300 | e.UpdateStatus("Successfully solved PX captcha", activityapi.LogLevel) 301 | return nil 302 | 303 | } else { 304 | 305 | e.UpdateStatus("Failed to process PX Captcha data", activityapi.ErrorLevel) 306 | 307 | e.WaitR() 308 | return goErrors.New("") 309 | } 310 | 311 | } 312 | 313 | func (e *WalmartTask) setPxCookie(px3 string) { 314 | wmUrl, _ := url.Parse("https://www.walmart.com/") 315 | 316 | px3Cookie := &http.Cookie{ 317 | Name: "_px3", 318 | Value: px3, 319 | MaxAge: 999999, 320 | Secure: true, 321 | HttpOnly: true, 322 | } 323 | 324 | var newCookies []*http.Cookie // 325 | newCookies = append(newCookies, px3Cookie) 326 | 327 | e.Jar.SetCookies(wmUrl, newCookies) 328 | 329 | } 330 | 331 | func (e *WalmartTask) genShippingRates() { 332 | 333 | shippingInfoToSend := ShippingData{ 334 | e.Profile.Shipping.PostalCode, 335 | "STOREMETAPLUS", 336 | true, 337 | true, 338 | "Web-Checkout-ShippingAddress", 339 | true, 340 | true, 341 | } 342 | 343 | req := e.Client.NewRequest() 344 | req.Url = "https://www.walmart.com/account/api/location" 345 | req.Method = "PUT" 346 | req.SetJSONBody(shippingInfoToSend) 347 | 348 | req.Headers = randomizeHeaders([]map[string]string{ 349 | {"sec-ch-ua": e.SechUa}, 350 | {"Accept": e.Accept}, 351 | {"sec-ch-ua-mobile": "?0"}, 352 | {"user-agent": e.UserAgent}, 353 | {"Content-Type": "application/json"}, 354 | {"origin": "https://www.walmart.com"}, 355 | {"sec-fetch-site": "same-origin"}, 356 | {"sec-fetch-mode": "cors"}, 357 | {"sec-fetch-dest": "empty"}, 358 | {"referer": "https://www.walmart.com/checkout/"}, 359 | {"Accept-Language": e.Language}, 360 | }) 361 | 362 | resp, err := req.Do() 363 | 364 | if err != nil { 365 | e.UpdateStatus("Failed to send shipping rates request", activityapi.ErrorLevel) 366 | errors.Handler(err) 367 | return 368 | } 369 | 370 | shippingReqResp := ShippingResp{} 371 | 372 | err = json.Unmarshal([]byte(resp.Body), &shippingReqResp) 373 | 374 | if err != nil { 375 | e.UpdateStatus("Failed to unpack shipping rate response", activityapi.ErrorLevel) 376 | errors.Handler(err) 377 | e.WaitR() 378 | return 379 | } 380 | 381 | if len(shippingReqResp.Stores) == 0 { 382 | e.UpdateStatus("Failed to get valid shipping rates response... Rotating proxy", activityapi.ErrorLevel) // I've found this happens on bad ips for whatever reason 383 | e.RotateProxy() 384 | e.WaitR() 385 | return 386 | } 387 | 388 | var goodStore string 389 | 390 | for _, store := range shippingReqResp.Stores { 391 | if strings.Contains(store.Types[0], "gsf_store") { 392 | goodStore = store.StoreId 393 | } 394 | } 395 | 396 | if goodStore == "" { 397 | e.UpdateStatus("Failed to get valid shipping rates... Please make sure your shipping address is supported by US Walmart.", activityapi.ErrorLevel) 398 | e.WaitR() 399 | return // We need to stop this task here 400 | } 401 | 402 | storeList := SavedStoreList{goodStore} 403 | 404 | e.StoreData = SavedShippingRates{ 405 | []SavedStoreList{storeList}, 406 | e.Profile.Shipping.PostalCode, 407 | e.Profile.Shipping.City, 408 | e.Profile.Shipping.State, 409 | true, 410 | "", 411 | "", 412 | "", 413 | "", 414 | } 415 | 416 | e.UpdateStatus("Successfully genned shipping rates", activityapi.LogLevel) 417 | e.Stage = GetCart 418 | 419 | } 420 | 421 | func (e *WalmartTask) genAccount() { 422 | 423 | e.UpdateStatus("Genning Walmart account", activityapi.LogLevel) 424 | emailSplit := strings.Split(e.Profile.Email, "@") 425 | rand.Seed(time.Now().UnixNano()) 426 | 427 | accountEmail := fmt.Sprintf("%s+%d@%s", emailSplit[0], rand.Intn(100000), emailSplit[1]) 428 | 429 | capToSend := signupCaptcha{""} 430 | 431 | Person := personName{ 432 | e.Profile.Shipping.FirstName, 433 | e.Profile.Shipping.LastName, 434 | } 435 | accountToSend := accountData{ 436 | Person, 437 | accountEmail, 438 | "TempPassword", 439 | false, 440 | "true", 441 | false, 442 | capToSend, 443 | } 444 | 445 | req := e.Client.NewRequest() 446 | 447 | req.Method = "POST" 448 | req.Url = "https://www.walmart.com/account/electrode/api/signup?ref=domain" 449 | req.SetJSONBody(accountToSend) 450 | 451 | req.Headers = []map[string]string{ 452 | {"content-length": ""}, 453 | {"sec-ch-ua": `" Not;A Brand";v="99", "Google Chrome";v="91", "Chromium";v="91"`}, 454 | {"sec-ch-ua-mobile": "?0"}, 455 | {"user-agent": e.UserAgent}, 456 | {"content-type": "application/json; charset=utf-8"}, 457 | {"accept": "*/*"}, 458 | {"origin": "https://www.walmart.com"}, 459 | {"sec-fetch-site": "same-origin"}, 460 | {"sec-fetch-mode": "cors"}, 461 | {"sec-fetch-dest": "empty"}, 462 | {"referer": "https://www.walmart.com/account/signup?ref=domain"}, 463 | {"accept-encoding": "gzip, deflate, br"}, 464 | {"accept-language": "en-US,en;q=0.9"}, 465 | } 466 | 467 | resp, err := req.Do() 468 | 469 | if err != nil { 470 | errors.Handler(err) 471 | e.UpdateStatus("Failed to send account request", activityapi.ErrorLevel) 472 | e.WaitR() 473 | return 474 | } 475 | 476 | if e.PxCheck(resp) == false { 477 | e.Stage = ShippingRates 478 | e.UpdateStatus("Successfully genned account", activityapi.LogLevel) 479 | } else { 480 | errors.Handler(err) 481 | 482 | e.UpdateStatus("Failed to gen account", activityapi.ErrorLevel) 483 | e.WaitR() 484 | return 485 | } 486 | 487 | } 488 | 489 | func (e *WalmartTask) getCart() { 490 | e.UpdateStatus("Getting payment cookies..", activityapi.LogLevel) 491 | 492 | req := e.Client.NewRequest() 493 | req.Url = "https://www.walmart.com/cart" 494 | req.Method = "GET" 495 | 496 | req.Headers = []map[string]string{ 497 | 498 | {"accept-encoding": "gzip, deflate, br"}, 499 | {"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"}, 500 | {"accept-language": "en-US,en;q=0.9"}, 501 | {"cache-control": "max-age=0"}, 502 | {"dnt": "1"}, 503 | {"sec-ch-ua": `" Not A;Brand";v="99", "Chromium";v="90", "Google Chrome";v="90"`}, 504 | {"sec-ch-ua-mobile": "?0"}, 505 | {"sec-fetch-dest": "document"}, 506 | {"sec-fetch-mode": "navigate"}, 507 | {"sec-fetch-site": "none"}, 508 | {"sec-fetch-user": "?1"}, 509 | {"service-worker-navigation-preload": "true"}, 510 | {"upgrade-insecure-requests": "1"}, 511 | {"user-agent": e.UserAgent}, 512 | } 513 | 514 | resp, err := req.Do() 515 | 516 | if err != nil { 517 | errors.Handler(err) 518 | e.WaitR() 519 | return 520 | } 521 | 522 | if e.PxCheck(resp) == false { 523 | e.Stage = PreEncrypt 524 | e.UpdateStatus("Successfully got payment cookies", activityapi.LogLevel) 525 | } 526 | return 527 | 528 | } 529 | 530 | func (e *WalmartTask) getCheckout(wg *sync.WaitGroup) { 531 | e.UpdateStatus("Getting security cookies...", activityapi.LogLevel) 532 | req := e.Client.NewRequest() 533 | req.Url = "https://www.walmart.com/checkout" 534 | req.Method = "GET" 535 | req.Body = nil 536 | 537 | req.Headers = []map[string]string{ 538 | 539 | {"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"}, 540 | {"accept-language": "en-US,en;q=0.9"}, 541 | {"cache-control": "no-cache"}, 542 | {"dnt": "1"}, 543 | {"pragma": "no-cache"}, 544 | {"referer": "https://www.walmart.com"}, 545 | {"sec-ch-ua": `" Not A;Brand";v="99", "Chromium";v="90", "Google Chrome";v="90"`}, 546 | {"sec-ch-ua-mobile": "?0"}, 547 | {"sec-fetch-dest": "document"}, 548 | {"sec-fetch-mode": "navigate"}, 549 | {"sec-fetch-site": "same-origin"}, 550 | {"sec-fetch-user": "?1"}, 551 | {"service-worker-navigation-preload": "true"}, 552 | {"upgrade-insecure-requests": "1"}, 553 | {"user-agent": e.UserAgent}, 554 | } 555 | 556 | resp, err := req.Do() 557 | 558 | if err != nil { 559 | errors.Handler(err) 560 | e.WaitR() 561 | return 562 | } 563 | 564 | if e.PxCheck(resp) == false { 565 | 566 | wg.Done() 567 | e.UpdateStatus("Successfully got security cookies!", activityapi.LogLevel) 568 | return 569 | 570 | } else { 571 | 572 | return 573 | } 574 | 575 | } 576 | 577 | func (e *WalmartTask) setWmCookie() { 578 | wmUrl, _ := url.Parse("https://www.walmart.com/") 579 | 580 | wmValue := fmt.Sprintf("reflectorid:0000000000000000000000@lastupd:%d@firstcreate:%d", time.Now().Unix(), time.Now().Unix()) 581 | 582 | px3Cookie := &http.Cookie{ 583 | Name: "com.wm.reflector", 584 | Value: wmValue, 585 | MaxAge: 999999, 586 | Secure: true, 587 | HttpOnly: true, 588 | } 589 | gCookie := &http.Cookie{ 590 | Name: "g", 591 | Value: "0", 592 | MaxAge: 999999, 593 | Secure: true, 594 | HttpOnly: true, 595 | } 596 | 597 | var newCookies []*http.Cookie // 598 | newCookies = append(newCookies, px3Cookie) 599 | newCookies = append(newCookies, gCookie) 600 | 601 | e.Jar.SetCookies(wmUrl, newCookies) 602 | 603 | } 604 | 605 | func (e *WalmartTask) getPieKeys() (error, PieKeyResp) { 606 | 607 | req := e.Client.NewRequest() 608 | req.Url = "https://securedataweb.walmart.com/pie/v1/wmcom_us_vtg_pie/getkey.js?bust=" + strconv.FormatInt(time.Now().UnixNano(), 10) 609 | 610 | req.Method = "GET" 611 | 612 | req.Headers = []map[string]string{ 613 | {"accept": "application/json"}, 614 | {"accept-language": "en-US,en;q=0.9"}, 615 | {"cache-control": "no-cache"}, 616 | {"content-type": "application/json"}, 617 | {"dnt": "1"}, 618 | {"origin": "https://www.walmart.com"}, 619 | {"pragma": "no-cache"}, 620 | {"referer": "https://www.walmart.com/checkout/"}, 621 | {"sec-ch-ua": `" Not A;Brand";v="99", "Chromium";v="90", "Google Chrome";v="90"`}, 622 | {"sec-ch-ua-mobile": "?0"}, 623 | {"sec-fetch-dest": "empty"}, 624 | {"sec-fetch-mode": "cors"}, 625 | {"sec-fetch-site": "same-origin"}, 626 | {"user-agent": e.UserAgent}, 627 | } 628 | 629 | resp, err := req.Do() 630 | 631 | if err != nil { 632 | 633 | errors.Handler(err) 634 | 635 | return goErrors.New("failed to send payment keys request"), PieKeyResp{} 636 | e.WaitR() 637 | } 638 | 639 | res := PieKeyResp{} 640 | 641 | kRegx := regexp.MustCompile(`PIE.K = "(.*?)";`) // This repetive regex matching is very ugly, does anyone know a better way? 642 | kMatch := kRegx.FindStringSubmatch(resp.Body) 643 | res.K = kMatch[1] 644 | 645 | LRegx := regexp.MustCompile(`PIE.L = (\d*?);`) 646 | LMatch := LRegx.FindStringSubmatch(resp.Body) 647 | res.L, _ = strconv.Atoi(LMatch[1]) 648 | 649 | eRegx := regexp.MustCompile(`PIE.E = (\d*?);`) 650 | eMatch := eRegx.FindStringSubmatch(resp.Body) 651 | res.E, _ = strconv.Atoi(eMatch[1]) 652 | 653 | keyRegx := regexp.MustCompile(`PIE.key_id = "(.*?)";`) 654 | keyMatch := keyRegx.FindStringSubmatch(resp.Body) 655 | res.KeyId = keyMatch[1] 656 | 657 | phaseRegx := regexp.MustCompile(`PIE.phase = (.*?);`) 658 | phaseMatch := phaseRegx.FindStringSubmatch(resp.Body) 659 | res.PhaseId, _ = strconv.Atoi(phaseMatch[1]) 660 | 661 | return nil, res 662 | 663 | } 664 | 665 | func (e *WalmartTask) preEncrypt() { 666 | e.UpdateStatus("Encrypting payment", activityapi.LogLevel) 667 | 668 | for i := 1; i <= 2; i++ { 669 | err, res := e.getPieKeys() 670 | 671 | if err != nil { 672 | errors.Handler(err) 673 | return 674 | } 675 | 676 | PIE := encrypt.Pie{ 677 | L: res.L, 678 | E: res.E, 679 | K: res.K, 680 | } 681 | 682 | if i == 1 { 683 | cardData := encrypt.ProtectPanAndCvv("4111111111111111", e.Profile.Billing.CVC, PIE) 684 | e.PieKeyId2 = res.KeyId 685 | e.PiePhase2 = strconv.Itoa(res.PhaseId) 686 | e.CardData2 = cardData 687 | 688 | } else if i == 2 { 689 | cardData := encrypt.ProtectPanAndCvv(e.Profile.Billing.CardNumber, e.Profile.Billing.CVC, PIE) 690 | 691 | e.PieKeyId1 = res.KeyId 692 | e.PiePhase1 = strconv.Itoa(res.PhaseId) 693 | e.CardData1 = cardData 694 | 695 | } 696 | 697 | } 698 | 699 | e.UpdateStatus("Successfully encrypted payment", activityapi.LogLevel) 700 | 701 | e.Stage = CC 702 | 703 | } 704 | 705 | func (e *WalmartTask) submitCC() { 706 | 707 | e.UpdateStatus("Submitting first round of payment", activityapi.LogLevel) 708 | 709 | submitCC := ccdata{ 710 | e.Profile.Shipping.Address, 711 | e.Profile.Shipping.Address2, 712 | strings.ToUpper(e.Profile.Billing.CardType), 713 | e.CardData1[1], 714 | e.CardData1[0], 715 | e.Profile.Billing.ExpMonth, 716 | e.Profile.Billing.ExpYear, 717 | e.Profile.Billing.BillingAddress.FirstName, 718 | e.CardData1[2], 719 | true, 720 | e.PieKeyId1, 721 | e.Profile.Billing.BillingAddress.LastName, 722 | e.PiePhase1, 723 | e.Profile.Billing.BillingAddress.Phone, 724 | e.Profile.Billing.BillingAddress.PostalCode, 725 | e.Profile.Billing.BillingAddress.State, 726 | } 727 | req := e.Client.NewRequest() 728 | req.Method = "POST" 729 | req.SetJSONBody(submitCC) 730 | req.Url = "https://www.walmart.com/api/checkout-customer/:CID/credit-card" 731 | req.Headers = []map[string]string{ 732 | {"content-length": ""}, 733 | {"sec-ch-ua": `" Not A;Brand";v="99", "Chromium";v="90", "Google Chrome";v="90"`}, 734 | {"accept": "application/json"}, 735 | {"sec-ch-ua-mobile": "?0"}, 736 | {"user-agent": e.UserAgent}, 737 | {"content-type": "application/json"}, 738 | {"sec-fetch-site": "same-origin"}, 739 | {"sec-fetch-mode": "cors"}, 740 | {"sec-fetch-dest": "empty"}, 741 | {"referer": "https://www.walmart.com/checkout/"}, 742 | {"accept-encoding": "gzip, deflate, br"}, 743 | {"accept-language": "en-US,en;q=0.9"}, 744 | } 745 | 746 | resp, err := req.Do() 747 | 748 | if err != nil { 749 | errors.Handler(err) 750 | e.UpdateStatus("Failed to send round 1 payment request", activityapi.ErrorLevel) 751 | e.WaitR() 752 | 753 | return 754 | } 755 | 756 | if e.PxCheck(resp) == false { 757 | 758 | e.UpdateStatus("Successfully submitted round 1 payment", activityapi.LogLevel) 759 | var ccResp PaymentResp 760 | 761 | err = json.Unmarshal([]byte(resp.Body), &ccResp) 762 | 763 | e.PiHash = ccResp.PiHash 764 | 765 | e.Stage = Cart 766 | 767 | } else { 768 | 769 | e.UpdateStatus("Failed to submit round 1 payment", activityapi.ErrorLevel) 770 | 771 | e.WaitR() 772 | return 773 | } 774 | 775 | } 776 | 777 | func (e *WalmartTask) preAdd() { 778 | 779 | addToListData := atcParams{e.Product.Identifier, e.Product.Qty} 780 | 781 | e.UpdateStatus("Pre-adding product", activityapi.LogLevel) 782 | 783 | req := e.Client.NewRequest() 784 | req.Url = "https://www.walmart.com/api/v3/cart/:CRT/items" 785 | req.Method = "POST" 786 | req.SetJSONBody(addToListData) 787 | 788 | req.Headers = []map[string]string{ 789 | 790 | {"content-length": ""}, 791 | {"sec-ch-ua": `" Not A;Brand";v="99", "Chromium";v="90", "Google Chrome";v="90"`}, 792 | {"accept": "application/json"}, 793 | {"sec-ch-ua-mobile": "?0"}, 794 | {"user-agent": e.UserAgent}, 795 | {"content-type": "application/json"}, 796 | {"sec-fetch-site": "same-origin"}, 797 | {"sec-fetch-mode": "cors"}, 798 | {"sec-fetch-dest": "empty"}, 799 | {"referer": "https://www.walmart.com/checkout/"}, 800 | {"accept-encoding": "gzip, deflate, br"}, 801 | {"accept-language": "en-US,en;q=0.9"}, 802 | } 803 | 804 | resp, err := req.Do() 805 | 806 | if err != nil { 807 | errors.Handler(err) 808 | e.UpdateStatus("Failed to send pre-add request", activityapi.ErrorLevel) 809 | 810 | e.WaitR() 811 | 812 | return 813 | } 814 | 815 | if e.PxCheck(resp) == false { 816 | 817 | preAddResp := preAdd{} 818 | err = json.Unmarshal([]byte(resp.Body), &preAddResp) 819 | e.ItemId = preAddResp.SavedItems[0].ItemId 820 | e.UpdateStatus("Successfully pre-added product", activityapi.LogLevel) 821 | e.Stage = Cart 822 | } else { 823 | 824 | e.UpdateStatus("Failed to pre-add product", activityapi.ErrorLevel) 825 | if e.Tries > 3 { 826 | e.Stop() 827 | return 828 | } 829 | e.WaitR() 830 | 831 | return 832 | } 833 | 834 | } 835 | 836 | func (e *WalmartTask) cart() { 837 | 838 | e.UpdateStatus("Adding to cart", activityapi.LogLevel) 839 | 840 | atcData := atcParams{e.Product.Identifier, 1} 841 | 842 | req := e.Client.NewRequest() 843 | req.Url = "https://www.walmart.com/api/v3/cart/:CID/items" 844 | req.SetJSONBody(atcData) 845 | req.Method = "POST" 846 | req.Headers = []map[string]string{ 847 | {"content-length": ""}, 848 | {"sec-ch-ua": `" Not A;Brand";v="99", "Chromium";v="90", "Google Chrome";v="90"`}, 849 | {"accept": "application/json"}, 850 | {"sec-ch-ua-mobile": "?0"}, 851 | {"user-agent": e.UserAgent}, 852 | {"content-type": "application/json"}, 853 | {"sec-fetch-site": "same-origin"}, 854 | {"sec-fetch-mode": "cors"}, 855 | {"sec-fetch-dest": "empty"}, 856 | {"referer": "https://www.walmart.com/checkout/"}, 857 | {"accept-encoding": "gzip, deflate, br"}, 858 | {"accept-language": "en-US,en;q=0.9"}, 859 | } 860 | 861 | resp, err := req.Do() 862 | if err != nil { 863 | errors.Handler(err) 864 | e.UpdateStatus("Failed to send pre-cart request", activityapi.ErrorLevel) 865 | e.WaitR() 866 | return 867 | } 868 | 869 | if resp.StatusCode == 200 || resp.StatusCode == 201 { 870 | 871 | var atcJsonResp AtcResp 872 | 873 | err = json.Unmarshal([]byte(resp.Body), &atcJsonResp) 874 | 875 | if err != nil { 876 | e.UpdateStatus("Failed to get valid cart resp... ["+strconv.Itoa(resp.StatusCode)+"]", activityapi.ErrorLevel) 877 | e.WaitR() 878 | return 879 | } 880 | 881 | e.ItemId = atcJsonResp.Items[0].Id 882 | 883 | if int(atcJsonResp.Cart.Totals.SubTotal) > e.Product.PriceCheck { 884 | e.UpdateStatus("Added to cart but exceeded price limit! ["+strconv.Itoa(resp.StatusCode)+"]", activityapi.ErrorLevel) 885 | 886 | e.PrevStage = Cart 887 | e.Stage = DeleteItem 888 | return 889 | } 890 | 891 | e.PrevStage = Cart 892 | e.Stage = Checkout 893 | 894 | e.UpdateStatus("Successfully pre Carted ["+strconv.Itoa(resp.StatusCode)+"]", activityapi.LogLevel) 895 | 896 | } else if strings.Contains(resp.Body, "ITEM_COUNT_MAX_LIMIT") { 897 | 898 | e.Product.Qty = 1 // TODO: Should this be an option? IE: if the bot can't add the qty a user specified, should we set the qty to 1 or just not go for it? 899 | e.UpdateStatus("Failed to add to cart due to item limit, retrying with qty 1 ["+strconv.Itoa(resp.StatusCode)+"]", activityapi.ErrorLevel) 900 | e.WaitR() 901 | return 902 | 903 | } else if strings.Contains(resp.Body, "No fulfillment option has availability") { 904 | 905 | e.UpdateStatus("Product is out of stock ["+strconv.Itoa(resp.StatusCode)+"]", activityapi.ErrorLevel) 906 | e.WaitR() 907 | return 908 | 909 | } else if e.PxCheck(resp) { 910 | e.UpdateStatus("Failed to add to cart... ["+strconv.Itoa(resp.StatusCode)+"]", activityapi.ErrorLevel) 911 | 912 | e.WaitR() 913 | return 914 | } 915 | 916 | } 917 | 918 | func (e *WalmartTask) startCheckout() { 919 | 920 | e.UpdateStatus("Starting checkout", activityapi.LogLevel) 921 | req := e.Client.NewRequest() 922 | req.SetJSONBody(e.StoreData) 923 | req.Method = "POST" 924 | req.Url = "https://www.walmart.com/api/checkout/v3/contract?page=CHECKOUT_VIEW" 925 | 926 | req.Headers = []map[string]string{ 927 | {"accept-encoding": "gzip, deflate, br"}, 928 | {"accept": "application/json, text/javascript, */*; q=0.01"}, 929 | {"accept-language": "en-US,en;q=0.9"}, 930 | {"content-type": "application/json; charset=UTF-8"}, 931 | {"host": "www.walmart.com"}, 932 | {"origin": "https://www.walmart.com"}, 933 | {"referer": "https://www.walmart.com/checkout/"}, 934 | {"sec-ch-ua-mobile": "?0"}, 935 | {"sec-fetch-dest": "empty"}, 936 | {"sec-fetch-mode": "cors"}, 937 | {"sec-fetch-site": "same-origin"}, 938 | {"user-agent": e.UserAgent}, 939 | {"wm_cvv_in_session": "true"}, 940 | {"wm_vertical_id": "0"}, 941 | {"content-length": ""}, 942 | {"sec-ch-ua": `" Not A;Brand";v="99", "Chromium";v="90", "Google Chrome";v="90"`}, 943 | {"accept": "application/json"}, 944 | {"sec-ch-ua-mobile": "?0"}, 945 | {"user-agent": e.UserAgent}, 946 | {"content-type": "application/json"}, 947 | {"sec-fetch-site": "same-origin"}, 948 | {"sec-fetch-mode": "cors"}, 949 | {"sec-fetch-dest": "empty"}, 950 | {"referer": "https://www.walmart.com/checkout/"}, 951 | {"accept-encoding": "gzip, deflate, br"}, 952 | {"accept-language": "en-US,en;q=0.9"}, 953 | } 954 | 955 | resp, err := req.Do() 956 | 957 | if err != nil { 958 | errors.Handler(err) 959 | e.WaitR() 960 | 961 | return 962 | } 963 | 964 | if e.PxCheck(resp) == false { 965 | 966 | var checkoutViewRespJson CheckoutViewResp 967 | 968 | err = json.Unmarshal([]byte(resp.Body), &checkoutViewRespJson) 969 | 970 | if err != nil { 971 | e.UpdateStatus("Failed to get valid checkout resp... ["+strconv.Itoa(resp.StatusCode)+"]", activityapi.ErrorLevel) 972 | e.WaitR() 973 | return 974 | } 975 | 976 | e.UpdateStatus("Successfully loaded checkout ["+strconv.Itoa(resp.StatusCode)+"]", activityapi.LogLevel) 977 | 978 | if int(checkoutViewRespJson.Summary.SubTotal) > e.Product.PriceCheck { 979 | e.UpdateStatus("Got to checkout but exceeded price check! restarting session ["+strconv.Itoa(resp.StatusCode)+"]", activityapi.ErrorLevel) 980 | e.PrevStage = Checkout 981 | e.Stage = DeleteItem 982 | return 983 | 984 | } 985 | 986 | e.Stage = Shipping 987 | 988 | e.secCookieWg.Add(1) 989 | 990 | go e.getCheckout(e.secCookieWg) 991 | return 992 | 993 | } else { 994 | e.UpdateStatus("Failed to go to checkout... Retrying... ["+strconv.Itoa(resp.StatusCode)+"]", activityapi.ErrorLevel) 995 | e.WaitR() 996 | return 997 | } 998 | 999 | } 1000 | 1001 | func (e *WalmartTask) submitShipping() { 1002 | 1003 | e.UpdateStatus("Submitting shipping", activityapi.LogLevel) 1004 | shippingData := shippingInfo{ 1005 | e.Profile.Shipping.Address, 1006 | e.Profile.Shipping.Address2, 1007 | e.Profile.Shipping.City, 1008 | e.Profile.Shipping.FirstName, 1009 | e.Profile.Shipping.LastName, 1010 | e.Profile.Shipping.Phone, 1011 | e.Profile.Email, 1012 | false, 1013 | e.Profile.Shipping.PostalCode, 1014 | e.Profile.Shipping.State, 1015 | e.Profile.Shipping.Country, 1016 | "RESIDENTIAL", 1017 | []string{}, // TODO: Check if this actuall 1018 | 1019 | } 1020 | 1021 | req := e.Client.NewRequest() 1022 | req.Url = "https://www.walmart.com/api/checkout/v3/contract/:PCID/shipping-address" 1023 | req.Method = "POST" 1024 | req.SetJSONBody(shippingData) 1025 | 1026 | req.Headers = []map[string]string{ 1027 | {"content-length": ""}, 1028 | {"sec-ch-ua": `" Not A;Brand";v="99", "Chromium";v="90", "Google Chrome";v="90"`}, 1029 | {"accept": "application/json"}, 1030 | {"sec-ch-ua-mobile": "?0"}, 1031 | {"user-agent": e.UserAgent}, 1032 | {"content-type": "application/json"}, 1033 | {"sec-fetch-site": "same-origin"}, 1034 | {"sec-fetch-mode": "cors"}, 1035 | {"sec-fetch-dest": "empty"}, 1036 | {"referer": "https://www.walmart.com/checkout/"}, 1037 | {"accept-encoding": "gzip, deflate, br"}, 1038 | {"accept-language": "en-US,en;q=0.9"}, 1039 | } 1040 | 1041 | resp, err := req.Do() 1042 | 1043 | if err != nil { 1044 | errors.Handler(err) 1045 | e.UpdateStatus("Failed to send shipping request", activityapi.ErrorLevel) 1046 | e.WaitR() 1047 | return 1048 | } 1049 | 1050 | if e.PxCheck(resp) == false { 1051 | 1052 | e.UpdateStatus("Successfully submitted shipping", activityapi.LogLevel) 1053 | e.Stage = Payment 1054 | 1055 | } else if e.Tries == 3 { 1056 | e.UpdateStatus("Failed to submit shipping 3 times... ", activityapi.ErrorLevel) 1057 | e.WaitR() 1058 | 1059 | } else { 1060 | e.UpdateStatus("Failed to submit shipping... Retrying...", activityapi.ErrorLevel) 1061 | e.WaitR() 1062 | return 1063 | } 1064 | 1065 | } 1066 | 1067 | func (e *WalmartTask) submitPayment() { 1068 | 1069 | e.UpdateStatus("Submitting payment", activityapi.LogLevel) 1070 | 1071 | myPayment := payments{"CREDITCARD", 1072 | strings.ToUpper(e.Profile.Billing.CardType), 1073 | e.Profile.Billing.BillingAddress.LastName, 1074 | e.Profile.Billing.BillingAddress.FirstName, 1075 | e.Profile.Billing.BillingAddress.Address, 1076 | e.Profile.Billing.BillingAddress.Address2, 1077 | e.Profile.Billing.BillingAddress.City, 1078 | e.Profile.Billing.BillingAddress.State, 1079 | e.Profile.Billing.BillingAddress.PostalCode, 1080 | e.Profile.Billing.ExpMonth, 1081 | e.Profile.Billing.ExpYear, 1082 | e.Profile.Email, 1083 | e.Profile.Billing.BillingAddress.Phone, 1084 | e.CardData1[0], 1085 | e.CardData1[1], 1086 | e.CardData1[2], 1087 | e.PieKeyId1, 1088 | e.PiePhase1, 1089 | 1090 | "", 1091 | } 1092 | paymentToSend := paymentInfo{[]payments{myPayment}, true} 1093 | 1094 | req := e.Client.NewRequest() 1095 | req.Url = "https://www.walmart.com/api/checkout/v3/contract/:PCID/payment" 1096 | req.Method = "POST" 1097 | req.SetJSONBody(paymentToSend) 1098 | 1099 | req.Headers = []map[string]string{ 1100 | {"content-length": ""}, 1101 | {"sec-ch-ua": `" Not A;Brand";v="99", "Chromium";v="90", "Google Chrome";v="90"`}, 1102 | {"accept": "application/json"}, 1103 | {"sec-ch-ua-mobile": "?0"}, 1104 | {"user-agent": e.UserAgent}, 1105 | {"content-type": "application/json"}, 1106 | {"sec-fetch-site": "same-origin"}, 1107 | {"sec-fetch-mode": "cors"}, 1108 | {"sec-fetch-dest": "empty"}, 1109 | {"referer": "https://www.walmart.com/checkout/"}, 1110 | {"accept-encoding": "gzip, deflate, br"}, 1111 | {"accept-language": "en-US,en;q=0.9"}, 1112 | } 1113 | resp, err := req.Do() 1114 | 1115 | if err != nil { 1116 | errors.Handler(err) 1117 | e.UpdateStatus("Failed to send payment request", activityapi.ErrorLevel) 1118 | e.WaitR() 1119 | 1120 | return 1121 | } 1122 | 1123 | if e.PxCheck(resp) == false { 1124 | 1125 | e.UpdateStatus("Successfully submitted payment", activityapi.LogLevel) 1126 | e.Stage = Order 1127 | 1128 | } else if e.Tries == 3 { 1129 | 1130 | e.UpdateStatus("Failed to submit payment 3 times... ", activityapi.ErrorLevel) 1131 | e.WaitR() 1132 | 1133 | } else { 1134 | e.UpdateStatus("Failed to submit payment.. Retrying..", activityapi.ErrorLevel) 1135 | e.WaitR() 1136 | return 1137 | } 1138 | 1139 | } 1140 | 1141 | func (e *WalmartTask) submitOrder() { 1142 | e.checkoutRetry = e.checkoutRetry + 1 1143 | 1144 | e.UpdateStatus("Submitting order", activityapi.LogLevel) 1145 | 1146 | paymentInfo := voltagePayments{ 1147 | "CREDITCARD", 1148 | e.CardData1[1], 1149 | e.CardData1[0], 1150 | e.CardData1[2], 1151 | e.PieKeyId1, 1152 | e.PiePhase1, 1153 | } 1154 | 1155 | OrderData := orderInfo{ 1156 | true, 1157 | []voltagePayments{paymentInfo}, 1158 | } 1159 | 1160 | req := e.Client.NewRequest() 1161 | req.SetJSONBody(OrderData) 1162 | req.Url = "https://www.walmart.com/api/checkout/v3/contract/:PCID/order" 1163 | req.Method = "PUT" 1164 | 1165 | req.Headers = []map[string]string{ 1166 | {"content-length": ""}, 1167 | {"sec-ch-ua": `" Not A;Brand";v="99", "Chromium";v="90", "Google Chrome";v="90"`}, 1168 | {"accept": "application/json"}, 1169 | {"sec-ch-ua-mobile": "?0"}, 1170 | {"user-agent": e.UserAgent}, 1171 | {"content-type": "application/json"}, 1172 | {"sec-fetch-site": "same-origin"}, 1173 | {"sec-fetch-mode": "cors"}, 1174 | {"sec-fetch-dest": "empty"}, 1175 | {"referer": "https://www.walmart.com/checkout/"}, 1176 | {"accept-encoding": "gzip, deflate, br"}, 1177 | {"accept-language": "en-US,en;q=0.9"}, 1178 | } 1179 | 1180 | e.secCookieWg.Wait() // Waits until we have gotten cookies from getting /checkout 1181 | 1182 | resp, err := req.Do() 1183 | 1184 | if err != nil { 1185 | e.UpdateStatus("Failed to send order request", activityapi.ErrorLevel) 1186 | e.WaitR() 1187 | 1188 | return 1189 | } 1190 | 1191 | if e.PxCheck(resp) == false { 1192 | 1193 | e.UpdateStatus("Successfully submitted order", activityapi.LogLevel) 1194 | // e.FireWebHook(true, []*discordhook.EmbedField{{ 1195 | // Name: "Mode", 1196 | // Value: string(e.Mode), 1197 | // Inline: false, 1198 | // }}) 1199 | e.Stop() 1200 | 1201 | } else { 1202 | 1203 | var orderFail OrderResp 1204 | 1205 | err = json.Unmarshal([]byte(resp.Body), &orderFail) 1206 | 1207 | e.UpdateStatus(fmt.Sprintf("Failed to submit order. Reason: %s . Attempt %d out of 3 [%d]", orderFail.FailedReason, e.checkoutRetry, resp.StatusCode), activityapi.ErrorLevel) 1208 | e.WaitR() 1209 | 1210 | if e.Tries == 2 { 1211 | // e.FireWebHook(false, []*discordhook.EmbedField{{ // you can pass additional fields if you want to 1212 | // Name: "Failed Reason", 1213 | // Value: orderFail.FailedReason, 1214 | // Inline: false, 1215 | // }, { 1216 | // Name: "Mode", 1217 | // Value: string(e.Mode), 1218 | // Inline: false, 1219 | // }, 1220 | // { // you can pass additional fields if you want to 1221 | // Name: "Resp status code", 1222 | // Value: strconv.Itoa(resp.StatusCode), 1223 | // Inline: false, 1224 | // }, 1225 | // }) 1226 | 1227 | if strings.Contains(orderFail.FailedReason, "Item is no longer in stock") { 1228 | e.UpdateStatus("Failed to checkout due to out of stock error.", activityapi.ErrorLevel) 1229 | e.WaitR() 1230 | 1231 | } else { 1232 | e.Stop() 1233 | 1234 | } 1235 | 1236 | } 1237 | return 1238 | } 1239 | 1240 | } 1241 | 1242 | func (e *WalmartTask) deleteItem() { 1243 | req := e.Client.NewRequest() 1244 | req.Url = "https://www.walmart.com/api/v3/cart/:CRT/items/" + e.ItemId 1245 | req.Method = "DELETE" 1246 | req.Headers = []map[string]string{ 1247 | {"authority": "www.walmart.com"}, 1248 | {"pragma": "no-cache"}, 1249 | {"cache-control": "no-cache"}, 1250 | {"sec-ch-ua": `" Not A;Brand";v="99", "Chromium";v="90", "Google Chrome";v="90"`}, 1251 | {"dnt": "1"}, 1252 | {"sec-ch-ua-mobile": "?0"}, 1253 | {"user-agent": e.UserAgent}, 1254 | {"credentials": "include"}, 1255 | {"content-type": "application/json"}, 1256 | {"accept": "application/json, text/javascript, */*; q=0.01"}, 1257 | {"omitcsrfjwt": "true"}, 1258 | {"origin": "https://www.walmart.com"}, 1259 | {"sec-fetch-site": "same-origin"}, 1260 | {"sec-fetch-mode": "cors"}, 1261 | {"sec-fetch-dest": "empty"}, 1262 | {"referer": "https://www.walmart.com/cart?action=SignIn&rm=true"}, 1263 | {"accept-language": "en-US,en;q=0.9"}, 1264 | } 1265 | resp, err := req.Do() 1266 | 1267 | if err != nil { 1268 | e.UpdateStatus("Failed to send delete req", activityapi.ErrorLevel) 1269 | errors.Handler(err) 1270 | return 1271 | } 1272 | 1273 | if resp.StatusCode != 200 { 1274 | e.UpdateStatus("Failed to delete item... retrying", activityapi.ErrorLevel) 1275 | 1276 | errors.Handler(goErrors.New("Failed to delete item, resp:" + resp.Body)) 1277 | e.WaitR() 1278 | return 1279 | } 1280 | 1281 | e.UpdateStatus("Succesfully deleted item out of price range", activityapi.LogLevel) 1282 | e.Stage = e.PrevStage 1283 | return 1284 | 1285 | } 1286 | -------------------------------------------------------------------------------- /sites/walmart/walmartentities.go: -------------------------------------------------------------------------------- 1 | package walmart 2 | 3 | import "time" 4 | 5 | type Mode string 6 | 7 | const ( 8 | ModeOne Mode = "ModeOne" 9 | ModeTwo Mode = "ModeTwo" 10 | ) 11 | 12 | const ( 13 | Account = "Account" 14 | ShippingRates = "Shipping rates" 15 | GetCart = "Get Cart" 16 | PreEncrypt = "PreEncrypt" 17 | CC = "Cc" 18 | MonitorStage = "Monitor" 19 | PreAdd = "Pre Add" 20 | Cart = "cart" 21 | Checkout = "Checkout" 22 | Shipping = "Shipping" 23 | Payment = "Payment" 24 | Order = "Order" 25 | StartMonitors = "Start monitors" 26 | WaitForStock = "Wait for stock" 27 | DeleteItem = "Delete item" 28 | ) 29 | 30 | type PxInfo struct { 31 | Site string `json:"site"` 32 | Proxy string `json:"proxy"` 33 | Key string `json:"key"` 34 | SetID *string `json:"setID,omitempty"` 35 | Uuid *string `json:"uuid,omitempty"` 36 | Vid *string `json:"vid,omitempty"` 37 | Token *string `json:"token,omitempty"` 38 | } 39 | type personName struct { 40 | FirstName string `json:"firstName"` 41 | LastName string `json:"lastName"` 42 | } 43 | 44 | type signupCaptcha struct { 45 | SensorData string `json:"sensorData"` 46 | } 47 | type accountData struct { 48 | PersonName personName `json:"personName"` 49 | Email string `json:"email"` 50 | Password string `json:"password"` 51 | Rememberme bool `json:"rememberme"` 52 | ShowRememberme string `json:"showRememberme"` 53 | EmailNotificationAccepted bool `json:"emailNotificationAccepted"` 54 | Captcha signupCaptcha `json:"captcha"` 55 | } 56 | type ccdata struct { 57 | AddressLineOne string `json:"addressLineOne"` 58 | AddressLineTwo string `json:"addressLineTwo"` 59 | CardType string `json:"cardType"` 60 | EncryptedCvv string `json:"encryptedCvv"` 61 | EncryptedPan string `json:"encryptedPan"` 62 | ExpiryMonth string `json:"expiryMonth"` 63 | ExpiryYear string `json:"expiryYear"` 64 | FirstName string `json:"firstName"` 65 | IntegrityCheck string `json:"integrityCheck"` 66 | IsGuest bool `json:"isGuest"` 67 | KeyId string `json:"keyId"` 68 | LastName string `json:"lastName"` 69 | Phase string `json:"phase"` 70 | Phone string `json:"phone"` 71 | PostalCode string `json:"postalCode"` 72 | State string `json:"state"` 73 | } 74 | 75 | type atcParams struct { 76 | OfferId string `json:"offerId"` 77 | Quantity int `json:"quantity"` 78 | } 79 | 80 | type shippingInfo struct { 81 | AddressLineOne string `json:"addressLineOne"` 82 | AddressLineTwo string `json:"addressLineTwo"` 83 | City string `json:"city"` 84 | FirstName string `json:"firstName"` 85 | LastName string `json:"lastName"` 86 | Phone string `json:"phone"` 87 | Email string `json:"email"` 88 | MarketingEmailPref bool `json:"marketingEmailPref"` 89 | PostalCode string `json:"postalCode"` 90 | State string `json:"state"` 91 | CountryCode string `json:"countryCode"` 92 | AddressType string `json:"addressType"` 93 | ChangedFields []string `json:"changedFields"` 94 | } 95 | 96 | type payments struct { 97 | PaymentType string `json:"paymentType"` 98 | CardType string `json:"cardType"` 99 | FirstName string `json:"firstName"` 100 | LastName string `json:"lastName"` 101 | AddressLineOne string `json:"addressLineOne"` 102 | AddressLineTwo string `json:"addressLineTwo"` 103 | City string `json:"city"` 104 | State string `json:"state"` 105 | PostalCode string `json:"postalCode"` 106 | ExpiryMonth string `json:"expiryMonth"` 107 | ExpiryYear string `json:"expiryYear"` 108 | Email string `json:"email"` 109 | Phone string `json:"phone"` 110 | EncryptedPan string `json:"encryptedPan"` 111 | EncryptedCvv string `json:"encryptedCvv"` 112 | IntegrityCheck string `json:"integrityCheck"` 113 | KeyId string `json:"keyId"` 114 | Phase string `json:"phase"` 115 | PiHash string `json:"piHash"` 116 | } 117 | 118 | type paymentInfo struct { 119 | Payments []payments `json:"payments"` 120 | CvvInSession bool `json:"cvvInSession"` 121 | } 122 | type voltagePayments struct { 123 | PaymentType string `json:"paymentType"` 124 | EncryptedCvv string `json:"encryptedCvv"` 125 | EncryptedPan string `json:"encryptedPan"` 126 | IntegrityCheck string `json:"integrityCheck"` 127 | KeyId string `json:"keyId"` 128 | Phase string `json:"phase"` 129 | } 130 | 131 | type orderInfo struct { 132 | CvvInSession bool `json:"cvvInSession"` 133 | VoltagePayments []voltagePayments `json:"voltagePayments"` 134 | } 135 | type pxResp struct { 136 | Px3 string `json:"_px3"` 137 | Ua string `json:"useragent"` 138 | SetID string `json:"setID"` 139 | Uuid string `json:"uuid"` 140 | Vid string `json:"vid"` 141 | } 142 | 143 | type preAdd struct { 144 | SavedItems []SavedItems `json:"savedItems"` 145 | } 146 | 147 | type SavedItems struct { 148 | ItemId string `json:"id"` 149 | Pid string `json:"USItemId"` 150 | } 151 | 152 | type ShippingData struct { 153 | PostalCode string `json:"postalCode"` 154 | ResponseGroup string `json:"responseGroup"` 155 | IncludePickUpLocation bool `json:"includePickUpLocation"` 156 | PersistLocation bool `json:"persistLocation"` 157 | ClientName string `json:"clientName"` 158 | StoreMeta bool `json:"storeMeta"` 159 | Plus bool `json:"plus"` 160 | } 161 | 162 | type ShippingResp struct { 163 | Stores []StoreResp `json:"stores"` 164 | } 165 | 166 | type StoreResp struct { 167 | Types []string `json:"types"` 168 | StoreId string `json:"storeId"` 169 | } 170 | 171 | type SavedShippingRates struct { 172 | StoreList []SavedStoreList `json:"storeList"` 173 | PostalCode string `json:"postalCode"` 174 | City string `json:"city"` 175 | State string `json:"state"` 176 | IsZipLocated bool `json:"isZipLocated"` 177 | Crt string `json:"crt:CRT"` 178 | CustomerId string `json:"customerId:CID"` 179 | CustomerType string `json:"customerType:type"` 180 | Affiliate string `json:"affiliateInfo:com.wm.reflector"` 181 | } 182 | 183 | type SavedStoreList struct { 184 | Id string `json:"id"` 185 | } 186 | 187 | type PieKeyResp struct { 188 | L int 189 | E int 190 | K string 191 | KeyId string 192 | PhaseId int 193 | } 194 | 195 | type PaymentResp struct { 196 | PiHash string `json:"piHash"` 197 | } 198 | 199 | type OrderResp struct { 200 | FailedReason string `json:"message"` 201 | } 202 | 203 | type TerraFirmaResp struct { 204 | Errors []interface{} `json:"errors"` 205 | Data struct { 206 | ItemByOfferId struct { 207 | OfferList []struct { 208 | ProductAvailability struct { 209 | AvailabilityStatus string `json:"availabilityStatus"` 210 | } `json:"productAvailability"` 211 | } `json:"offerList"` 212 | } `json:"itemByOfferId"` 213 | } `json:"data"` 214 | } 215 | 216 | type SFLResp struct { 217 | Checkoutable bool `json:"checkoutable"` 218 | Cart struct { 219 | ID string `json:"id"` 220 | Type string `json:"type"` 221 | Preview bool `json:"preview"` 222 | Customerid string `json:"customerId"` 223 | Location struct { 224 | Postalcode string `json:"postalCode"` 225 | City string `json:"city"` 226 | Stateorprovincecode string `json:"stateOrProvinceCode"` 227 | Countrycode string `json:"countryCode"` 228 | Isdefault bool `json:"isDefault"` 229 | } `json:"location"` 230 | Storeids []int `json:"storeIds"` 231 | Itemcount int `json:"itemCount"` 232 | Currencycode string `json:"currencyCode"` 233 | Entityerrors []interface{} `json:"entityErrors"` 234 | Itemcountbytype struct { 235 | Regular int `json:"regular"` 236 | Group int `json:"group"` 237 | } `json:"itemCountByType"` 238 | Saveditemcountbytype struct { 239 | Regular int `json:"regular"` 240 | Group int `json:"group"` 241 | } `json:"savedItemCountByType"` 242 | Hassubmaptypeitem bool `json:"hasSubmapTypeItem"` 243 | Fulfillmenttotals struct { 244 | } `json:"fulfillmentTotals"` 245 | Totals struct { 246 | } `json:"totals"` 247 | Saveditemcount int `json:"savedItemCount"` 248 | Tenantid int `json:"tenantId"` 249 | Verticalid int `json:"verticalId"` 250 | Localeid string `json:"localeId"` 251 | Fulfillmentgroups []interface{} `json:"fulfillmentGroups"` 252 | Hasalcoholicitem bool `json:"hasAlcoholicItem"` 253 | } `json:"cart"` 254 | Sfllist struct { 255 | Itemcount int `json:"itemCount"` 256 | Name string `json:"name"` 257 | Status string `json:"status"` 258 | Itemcountbytype struct { 259 | Regular int `json:"regular"` 260 | Group int `json:"group"` 261 | } `json:"itemCountByType"` 262 | } `json:"sflList"` 263 | Items []interface{} `json:"items"` 264 | Saveditems []struct { 265 | Quantity int `json:"quantity"` 266 | Price float64 `json:"price"` 267 | Unitvaluepricetype string `json:"unitValuePriceType"` 268 | Pickupunitprice float64 `json:"pickupUnitPrice"` 269 | Usitemid string `json:"USItemId"` 270 | Ussellerid string `json:"USSellerId"` 271 | Legacyitemid string `json:"legacyItemId"` 272 | Upc string `json:"upc"` 273 | Wupc string `json:"wupc"` 274 | Offerid string `json:"offerId"` 275 | Gtin string `json:"gtin"` 276 | Alternatewupcs []interface{} `json:"alternateWupcs"` 277 | Productid string `json:"productId"` 278 | Name string `json:"name"` 279 | Manufacturerproductid string `json:"manufacturerProductId"` 280 | Productclasstype string `json:"productClassType"` 281 | Seller struct { 282 | Name string `json:"name"` 283 | Type string `json:"type"` 284 | ID string `json:"id"` 285 | } `json:"seller"` 286 | Currentpricetype string `json:"currentPriceType"` 287 | Comparisonprice struct { 288 | Isstrikethrough bool `json:"isStrikethrough"` 289 | Iseligibleforassociatediscount bool `json:"isEligibleForAssociateDiscount"` 290 | Isrollback bool `json:"isRollback"` 291 | Isreducedprice bool `json:"isReducedPrice"` 292 | Isclearance bool `json:"isClearance"` 293 | Hidepriceforsoi bool `json:"hidePriceForSOI"` 294 | } `json:"comparisonPrice"` 295 | Assets struct { 296 | Primary []struct { 297 | Num60 string `json:"60"` 298 | Num100 string `json:"100"` 299 | } `json:"primary"` 300 | } `json:"assets"` 301 | Productmarketattributes struct { 302 | GpcWithFc string `json:"gpc_with_fc"` 303 | KarfSalesUnit string `json:"karf_sales_unit"` 304 | BrandCode string `json:"brand_code"` 305 | RhPath string `json:"rh_path"` 306 | AlternateShelves string `json:"alternate_shelves"` 307 | KarfSubstitutionsAllowed string `json:"karf_substitutions_allowed"` 308 | PrimaryCategoryPath string `json:"primary_category_path"` 309 | VerticalEligibility string `json:"vertical_eligibility"` 310 | ShelfDescription string `json:"shelf_description"` 311 | KarfMaximumQuantityFactor string `json:"karf_maximum_quantity_factor"` 312 | IsPrivateLabelUnbranded string `json:"is_private_label_unbranded"` 313 | ProductURLText string `json:"product_url_text"` 314 | PrimaryShelfID string `json:"primary_shelf_id"` 315 | CharPrimaryCategoryPath string `json:"char_primary_category_path"` 316 | Segregation string `json:"segregation"` 317 | } `json:"productMarketAttributes"` 318 | Marketingattributes struct { 319 | RhPath string `json:"rh_path"` 320 | AlignChannelPrice string `json:"align_channel_price"` 321 | WmDeptNum string `json:"wm_dept_num"` 322 | ItemClassID string `json:"item_class_id"` 323 | AvailabilityMsgFlag string `json:"availability_msg_flag"` 324 | } `json:"marketingAttributes"` 325 | Offertype string `json:"offerType"` 326 | Offerpublishstatus string `json:"offerPublishStatus"` 327 | Availablequantity int `json:"availableQuantity"` 328 | Maxitemcountperorder float64 `json:"maxItemCountPerOrder"` 329 | Twodayshippingeligible bool `json:"twoDayShippingEligible"` 330 | Pickupdiscounteligible bool `json:"pickupDiscountEligible"` 331 | Shippingtier string `json:"shippingTier"` 332 | Shippingslatiers []string `json:"shippingSlaTiers"` 333 | Shippingsladetail struct { 334 | Slatier string `json:"slaTier"` 335 | Geoitemclassification string `json:"geoItemClassification"` 336 | } `json:"shippingSlaDetail"` 337 | Tooverrideiroprice bool `json:"toOverrideIROPrice"` 338 | Itemclassid string `json:"itemClassId"` 339 | Manufacturername string `json:"manufacturerName"` 340 | Brand string `json:"brand"` 341 | Productsegment string `json:"productSegment"` 342 | Producttype string `json:"productType"` 343 | Isconsumable bool `json:"isConsumable"` 344 | Type string `json:"type"` 345 | Entityerrors []interface{} `json:"entityErrors"` 346 | Shippingeligible bool `json:"shippingEligible"` 347 | Edeliveryeligible bool `json:"eDeliveryEligible"` 348 | Groupcomponents []interface{} `json:"groupComponents"` 349 | } `json:"savedItems"` 350 | } 351 | 352 | type CheckoutViewResp struct { 353 | DbSessionTokenMap struct { 354 | CXOPCST string `json:"CXO_PC_ST"` 355 | } `json:"dbSessionTokenMap"` 356 | Id string `json:"id"` 357 | CheckoutFlowType string `json:"checkoutFlowType"` 358 | CartId string `json:"cartId"` 359 | Items []struct { 360 | Id string `json:"id"` 361 | OfferId string `json:"offerId"` 362 | ProductId string `json:"productId"` 363 | ProductName string `json:"productName"` 364 | ItemId int `json:"itemId"` 365 | SellerId string `json:"sellerId"` 366 | ThumbnailUrl string `json:"thumbnailUrl"` 367 | LegacySellerId int `json:"legacySellerId"` 368 | ProductClassType string `json:"productClassType"` 369 | Quantity int `json:"quantity"` 370 | UnitPrice float64 `json:"unitPrice"` 371 | Type string `json:"type"` 372 | Price float64 `json:"price"` 373 | UnitOfMeasure string `json:"unitOfMeasure"` 374 | HasCarePlan bool `json:"hasCarePlan"` 375 | Brand string `json:"brand"` 376 | Discount struct { 377 | } `json:"discount"` 378 | RhPath string `json:"rhPath"` 379 | IsWarrantyEligible bool `json:"isWarrantyEligible"` 380 | Category string `json:"category"` 381 | PrimaryCategory string `json:"primaryCategory"` 382 | IsCarePlan bool `json:"isCarePlan"` 383 | IsEgiftCard bool `json:"isEgiftCard"` 384 | IsAssociateDiscountEligible bool `json:"isAssociateDiscountEligible"` 385 | IsShippingPassEligible bool `json:"isShippingPassEligible"` 386 | ShippingTier string `json:"shippingTier"` 387 | IsTwoDayShippingEligible bool `json:"isTwoDayShippingEligible"` 388 | MeetsSla bool `json:"meetsSla"` 389 | ClassId string `json:"classId"` 390 | MaxQuantityPerOrder int `json:"maxQuantityPerOrder"` 391 | IsSubstitutable bool `json:"isSubstitutable"` 392 | IsInstaWatch bool `json:"isInstaWatch"` 393 | IsAlcoholic bool `json:"isAlcoholic"` 394 | IsSnapEligible bool `json:"isSnapEligible"` 395 | IsAgeRestricted bool `json:"isAgeRestricted"` 396 | IsSubstitutionsAllowed bool `json:"isSubstitutionsAllowed"` 397 | FulfillmentSelection struct { 398 | FulfillmentOption string `json:"fulfillmentOption"` 399 | ShipMethod string `json:"shipMethod"` 400 | AvailableQuantity int `json:"availableQuantity"` 401 | } `json:"fulfillmentSelection"` 402 | ServicePlanType string `json:"servicePlanType"` 403 | Errors []interface{} `json:"errors"` 404 | WfsEnabled bool `json:"wfsEnabled"` 405 | IsAlcohol bool `json:"isAlcohol"` 406 | } `json:"items"` 407 | Shipping struct { 408 | PostalCode string `json:"postalCode"` 409 | City string `json:"city"` 410 | State string `json:"state"` 411 | } `json:"shipping"` 412 | Summary struct { 413 | SubTotal float64 `json:"subTotal"` 414 | ShippingIsEstimate bool `json:"shippingIsEstimate"` 415 | TaxIsEstimate bool `json:"taxIsEstimate"` 416 | GrandTotal float64 `json:"grandTotal"` 417 | QuantityTotal int `json:"quantityTotal"` 418 | AmountOwed float64 `json:"amountOwed"` 419 | MerchandisingFeesTotal int `json:"merchandisingFeesTotal"` 420 | ShippingCosts []struct { 421 | Label string `json:"label"` 422 | Type string `json:"type"` 423 | Cost float64 `json:"cost"` 424 | Method string `json:"method"` 425 | } `json:"shippingCosts"` 426 | ShippingTotal float64 `json:"shippingTotal"` 427 | HasSurcharge bool `json:"hasSurcharge"` 428 | PreTaxTotal float64 `json:"preTaxTotal"` 429 | AddOnServicesTotal int `json:"addOnServicesTotal"` 430 | ItemsSubTotal float64 `json:"itemsSubTotal"` 431 | } `json:"summary"` 432 | PickupPeople []interface{} `json:"pickupPeople"` 433 | Email string `json:"email"` 434 | Buyer struct { 435 | CustomerAccountId string `json:"customerAccountId"` 436 | FirstName string `json:"firstName"` 437 | LastName string `json:"lastName"` 438 | Email string `json:"email"` 439 | IsGuest bool `json:"isGuest"` 440 | IsAssociate bool `json:"isAssociate"` 441 | ApplyAssociateDiscount bool `json:"applyAssociateDiscount"` 442 | HasCapOneCard bool `json:"hasCapOneCard"` 443 | } `json:"buyer"` 444 | AllowedPaymentTypes []struct { 445 | Type string `json:"type"` 446 | CvvRequired bool `json:"cvvRequired"` 447 | } `json:"allowedPaymentTypes"` 448 | Registries []interface{} `json:"registries"` 449 | Payments []interface{} `json:"payments"` 450 | CardsToDisable []interface{} `json:"cardsToDisable"` 451 | AllowedPaymentPreferences []interface{} `json:"allowedPaymentPreferences"` 452 | IsRCFEligible bool `json:"isRCFEligible"` 453 | IsMarketPlaceItemsExist bool `json:"isMarketPlaceItemsExist"` 454 | Version string `json:"version"` 455 | SharedCategory struct { 456 | ShippingGroups []struct { 457 | ItemIds []string `json:"itemIds"` 458 | Seller string `json:"seller"` 459 | ShippingTier string `json:"shippingTier"` 460 | MeetSla bool `json:"meetSla"` 461 | DefaultSelection bool `json:"defaultSelection"` 462 | FulfillmentOption string `json:"fulfillmentOption"` 463 | ShippingGroupOptions []struct { 464 | Method string `json:"method"` 465 | MethodDisplay string `json:"methodDisplay"` 466 | Selected bool `json:"selected"` 467 | Charge float64 `json:"charge"` 468 | DeliveryDate int64 `json:"deliveryDate"` 469 | AvailableDate int64 `json:"availableDate"` 470 | FulfillmentOption string `json:"fulfillmentOption"` 471 | OnlineStoreId int `json:"onlineStoreId"` 472 | IsThresholdShipMethod bool `json:"isThresholdShipMethod"` 473 | } `json:"shippingGroupOptions"` 474 | IsEdelivery bool `json:"isEdelivery"` 475 | HasWFSItem bool `json:"hasWFSItem"` 476 | ItemSellerGroups []interface{} `json:"itemSellerGroups"` 477 | } `json:"shippingGroups"` 478 | PickupGroupsByStore []struct { 479 | PickupGroups []struct { 480 | ItemIds []string `json:"itemIds"` 481 | Seller string `json:"seller"` 482 | DefaultSelection bool `json:"defaultSelection"` 483 | FulfillmentOption string `json:"fulfillmentOption"` 484 | PickupGroupOptions []struct { 485 | Method string `json:"method"` 486 | MethodDisplay string `json:"methodDisplay"` 487 | Selected bool `json:"selected"` 488 | Charge int `json:"charge"` 489 | DeliveryDate int64 `json:"deliveryDate"` 490 | AvailableDate int64 `json:"availableDate"` 491 | FulfillmentOption string `json:"fulfillmentOption"` 492 | Type string `json:"type"` 493 | AvailabilityStatus string `json:"availabilityStatus"` 494 | } `json:"pickupGroupOptions"` 495 | } `json:"pickupGroups"` 496 | StoreId int `json:"storeId"` 497 | Address struct { 498 | PostalCode string `json:"postalCode"` 499 | AddressLineOne string `json:"addressLineOne"` 500 | City string `json:"city"` 501 | State string `json:"state"` 502 | Address1 string `json:"address1"` 503 | Country string `json:"country"` 504 | } `json:"address"` 505 | StoreType string `json:"storeType"` 506 | StoreTypeId int `json:"storeTypeId"` 507 | StoreName string `json:"storeName"` 508 | Selected bool `json:"selected"` 509 | StoreServices []struct { 510 | ServiceName string `json:"serviceName"` 511 | ServiceId int `json:"serviceId"` 512 | Active bool `json:"active"` 513 | } `json:"storeServices"` 514 | PickupTogetherEligibleStore bool `json:"pickupTogetherEligibleStore"` 515 | } `json:"pickupGroupsByStore"` 516 | IsShippingEnabled bool `json:"isShippingEnabled"` 517 | } `json:"sharedCategory"` 518 | BalanceToReachThreshold float64 `json:"balanceToReachThreshold"` 519 | EntityErrors []interface{} `json:"entityErrors"` 520 | OneDaySelected bool `json:"oneDaySelected"` 521 | PaymentWithBagFee bool `json:"paymentWithBagFee"` 522 | GiftDetails struct { 523 | GiftOrder bool `json:"giftOrder"` 524 | HasGiftEligibleItem bool `json:"hasGiftEligibleItem"` 525 | XoGiftingOptIn bool `json:"xoGiftingOptIn"` 526 | } `json:"giftDetails"` 527 | CanApplyDetails []interface{} `json:"canApplyDetails"` 528 | DbName string `json:"dbName"` 529 | Jwt string `json:"jwt"` 530 | } 531 | 532 | type AtcResp struct { 533 | DbSessionMap struct { 534 | CXOCARTST string `json:"CXO_CART_ST"` 535 | } `json:"dbSessionMap"` 536 | Checkoutable bool `json:"checkoutable"` 537 | Cart struct { 538 | Id string `json:"id"` 539 | Type string `json:"type"` 540 | Preview bool `json:"preview"` 541 | CustomerId string `json:"customerId"` 542 | Location struct { 543 | PostalCode string `json:"postalCode"` 544 | City string `json:"city"` 545 | StateOrProvinceCode string `json:"stateOrProvinceCode"` 546 | CountryCode string `json:"countryCode"` 547 | IsDefault bool `json:"isDefault"` 548 | IsZipLocated bool `json:"isZipLocated"` 549 | } `json:"location"` 550 | StoreIds []int `json:"storeIds"` 551 | ItemCount int `json:"itemCount"` 552 | CurrencyCode string `json:"currencyCode"` 553 | EntityErrors []interface{} `json:"entityErrors"` 554 | ItemCountByType struct { 555 | Regular int `json:"regular"` 556 | Group int `json:"group"` 557 | } `json:"itemCountByType"` 558 | SavedItemCountByType struct { 559 | Regular int `json:"regular"` 560 | Group int `json:"group"` 561 | } `json:"savedItemCountByType"` 562 | HasSubmapTypeItem bool `json:"hasSubmapTypeItem"` 563 | FulfillmentTotals struct { 564 | S2H struct { 565 | Name string `json:"name"` 566 | SellerId string `json:"sellerId"` 567 | Type string `json:"type"` 568 | SellerName string `json:"sellerName"` 569 | Methods []struct { 570 | Method string `json:"method"` 571 | Price float64 `json:"price"` 572 | ItemIds []interface{} `json:"itemIds"` 573 | ThresholdOrderTotal float64 `json:"thresholdOrderTotal"` 574 | BalanceToReachThreshold float64 `json:"balanceToReachThreshold"` 575 | } `json:"methods"` 576 | } `json:"S2H"` 577 | } `json:"fulfillmentTotals"` 578 | Totals struct { 579 | SubTotal float64 `json:"subTotal"` 580 | SellerShippingTotals []struct { 581 | SellerId string `json:"sellerId"` 582 | SellerName string `json:"sellerName"` 583 | Methods []struct { 584 | Price float64 `json:"price"` 585 | Name string `json:"name"` 586 | Method string `json:"method"` 587 | } `json:"methods"` 588 | } `json:"sellerShippingTotals"` 589 | ShippingTotal float64 `json:"shippingTotal"` 590 | HasSurcharge bool `json:"hasSurcharge"` 591 | GrandTotal float64 `json:"grandTotal"` 592 | AddOnServicesTotal int `json:"addOnServicesTotal"` 593 | ItemsSubTotal float64 `json:"itemsSubTotal"` 594 | OriginalShippingTotal float64 `json:"originalShippingTotal"` 595 | MemberShipDiscount int `json:"memberShipDiscount"` 596 | } `json:"totals"` 597 | SavedItemCount int `json:"savedItemCount"` 598 | ShipMethodDefaultRule string `json:"shipMethodDefaultRule"` 599 | TenantId int `json:"tenantId"` 600 | VerticalId int `json:"verticalId"` 601 | LocaleId string `json:"localeId"` 602 | FulfillmentGroups []interface{} `json:"fulfillmentGroups"` 603 | HasAlcoholicItem bool `json:"hasAlcoholicItem"` 604 | GiftOrder bool `json:"giftOrder"` 605 | HasGiftEligibleItem bool `json:"hasGiftEligibleItem"` 606 | } `json:"cart"` 607 | Items []struct { 608 | Id string `json:"id"` 609 | Quantity int `json:"quantity"` 610 | Price float64 `json:"price"` 611 | LinePrice float64 `json:"linePrice"` 612 | UnitValuePrice float64 `json:"unitValuePrice,omitempty"` 613 | UnitValuePriceType string `json:"unitValuePriceType"` 614 | StoreFrontType string `json:"storeFrontType"` 615 | StoreFrontId struct { 616 | StoreUUID string `json:"storeUUID"` 617 | StoreId string `json:"storeId"` 618 | Preferred bool `json:"preferred"` 619 | SemStore bool `json:"semStore"` 620 | OnlineStoreFront bool `json:"onlineStoreFront"` 621 | USStoreId int `json:"USStoreId"` 622 | } `json:"storeFrontId"` 623 | USItemId string `json:"USItemId"` 624 | USSellerId string `json:"USSellerId"` 625 | LegacyItemId string `json:"legacyItemId"` 626 | Upc string `json:"upc,omitempty"` 627 | Wupc string `json:"wupc"` 628 | OfferId string `json:"offerId"` 629 | Gtin string `json:"gtin"` 630 | AlternateWupcs []interface{} `json:"alternateWupcs"` 631 | ProductId string `json:"productId"` 632 | Name string `json:"name"` 633 | ProductClassType string `json:"productClassType"` 634 | Seller struct { 635 | Name string `json:"name"` 636 | Type string `json:"type"` 637 | Id string `json:"id"` 638 | } `json:"seller"` 639 | CurrentPriceType string `json:"currentPriceType"` 640 | ComparisonPrice struct { 641 | IsStrikethrough bool `json:"isStrikethrough"` 642 | IsEligibleForAssociateDiscount bool `json:"isEligibleForAssociateDiscount"` 643 | IsRollback bool `json:"isRollback"` 644 | IsReducedPrice bool `json:"isReducedPrice"` 645 | IsClearance bool `json:"isClearance"` 646 | HidePriceForSOI bool `json:"hidePriceForSOI"` 647 | } `json:"comparisonPrice"` 648 | Assets struct { 649 | Primary []struct { 650 | Field1 string `json:"100"` 651 | Field2 string `json:"60"` 652 | } `json:"primary"` 653 | } `json:"assets"` 654 | ProductMarketAttributes struct { 655 | KarfSalesUnit string `json:"karf_sales_unit,omitempty"` 656 | BrandCode string `json:"brand_code,omitempty"` 657 | New string `json:"new,omitempty"` 658 | KarfMaximumOrderQuantity string `json:"karf_maximum_order_quantity,omitempty"` 659 | RhPath string `json:"rh_path"` 660 | AlternateShelves string `json:"alternate_shelves"` 661 | KarfSubstitutionsAllowed string `json:"karf_substitutions_allowed,omitempty"` 662 | PrimaryCategoryPath string `json:"primary_category_path"` 663 | VerticalEligibility string `json:"vertical_eligibility,omitempty"` 664 | ShelfDescription string `json:"shelf_description"` 665 | KarfMaximumQuantityFactor string `json:"karf_maximum_quantity_factor,omitempty"` 666 | IsPrivateLabelUnbranded string `json:"is_private_label_unbranded"` 667 | ProductUrlText string `json:"product_url_text"` 668 | Newuntildate time.Time `json:"newuntildate,omitempty"` 669 | PrimaryShelfId string `json:"primary_shelf_id"` 670 | CharPrimaryCategoryPath string `json:"char_primary_category_path"` 671 | Segregation string `json:"segregation"` 672 | DisplayStatus string `json:"display_status,omitempty"` 673 | } `json:"productMarketAttributes"` 674 | MarketingAttributes struct { 675 | LastUpdatedBy string `json:"lastUpdatedBy,omitempty"` 676 | RhPath string `json:"rh_path"` 677 | WmtDeptNum string `json:"wmt_dept_num,omitempty"` 678 | WmDeptNum string `json:"wm_dept_num"` 679 | ItemClassId string `json:"item_class_id"` 680 | } `json:"marketingAttributes"` 681 | OfferType string `json:"offerType"` 682 | OfferPublishStatus string `json:"offerPublishStatus"` 683 | AvailableQuantity int `json:"availableQuantity"` 684 | MaxItemCountPerOrder int `json:"maxItemCountPerOrder"` 685 | TwoDayShippingEligible bool `json:"twoDayShippingEligible"` 686 | PickupDiscountEligible bool `json:"pickupDiscountEligible"` 687 | ShippingTier string `json:"shippingTier"` 688 | WfsEnabled bool `json:"wfsEnabled"` 689 | ShippingSlaTiers []string `json:"shippingSlaTiers"` 690 | ToOverrideIROPrice bool `json:"toOverrideIROPrice"` 691 | ItemClassId string `json:"itemClassId"` 692 | ManufacturerName string `json:"manufacturerName"` 693 | Brand string `json:"brand,omitempty"` 694 | ProductSegment string `json:"productSegment"` 695 | ProductType string `json:"productType"` 696 | IsConsumable bool `json:"isConsumable"` 697 | PrimaryCategoryPath string `json:"primaryCategoryPath"` 698 | CharPrimaryCategoryPath string `json:"charPrimaryCategoryPath"` 699 | Type string `json:"type"` 700 | UnitOfMeasure string `json:"unitOfMeasure"` 701 | Sort int64 `json:"sort"` 702 | EntityErrors []interface{} `json:"entityErrors"` 703 | ShippingOptions struct { 704 | S2H []struct { 705 | Status string `json:"status"` 706 | Quantity float64 `json:"quantity"` 707 | Methods []struct { 708 | Price float64 `json:"price"` 709 | Name string `json:"name"` 710 | Method string `json:"method"` 711 | ShippingTier string `json:"shippingTier"` 712 | } `json:"methods"` 713 | StoreAddress struct { 714 | City string `json:"city"` 715 | Country string `json:"country"` 716 | PostalCode string `json:"postalCode"` 717 | State string `json:"state"` 718 | } `json:"storeAddress"` 719 | } `json:"S2H"` 720 | PUT []struct { 721 | Status string `json:"status"` 722 | Quantity float64 `json:"quantity"` 723 | Methods []struct { 724 | Price float64 `json:"price"` 725 | Name string `json:"name"` 726 | Method string `json:"method"` 727 | ShippingTier string `json:"shippingTier"` 728 | } `json:"methods"` 729 | StoreAddress struct { 730 | Address1 string `json:"address1"` 731 | City string `json:"city"` 732 | Country string `json:"country"` 733 | PostalCode string `json:"postalCode"` 734 | State string `json:"state"` 735 | } `json:"storeAddress"` 736 | UsstoreId int `json:"usstoreId"` 737 | } `json:"PUT,omitempty"` 738 | } `json:"shippingOptions"` 739 | IsWarrantyEligible bool `json:"isWarrantyEligible"` 740 | IsServicePlansEligible bool `json:"isServicePlansEligible"` 741 | IsSnapEligible bool `json:"isSnapEligible"` 742 | WeightIncrement int `json:"weightIncrement"` 743 | ManufacturerProductId string `json:"manufacturerProductId,omitempty"` 744 | GiftDetails struct { 745 | Eligibility string `json:"eligibility"` 746 | GiftOpts struct { 747 | GiftOverboxEligible bool `json:"giftOverboxEligible"` 748 | } `json:"giftOpts"` 749 | } `json:"giftDetails,omitempty"` 750 | } `json:"items"` 751 | NextDayEligible bool `json:"nextDayEligible"` 752 | EDeliveryCart bool `json:"eDeliveryCart"` 753 | CanAddWARPItemToCart bool `json:"canAddWARPItemToCart"` 754 | CartVersion int `json:"cartVersion"` 755 | LastModifiedTime int64 `json:"lastModifiedTime"` 756 | } 757 | --------------------------------------------------------------------------------