├── go.mod ├── README.md └── main.go /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/vsec7/gothreads 2 | 3 | go 1.18 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gothreads 2 | GoThreads is a simple GO tool for likefeed threads.net 3 | 4 | Crafted By viloid (github.com/vsec7) 5 | 6 | > *** NOTE : USE AT YOUR OWN RISK! *** 7 | 8 |

9 | 10 | 11 | 12 | 13 | 14 | 15 |

16 | 17 | ```bash 18 | 19 | GoThreads Bot 20 | 21 | Is a simple GO tool for threads.net 22 | 23 | Crafted By : github.com/vsec7 24 | 25 | Basic Usage : 26 | ▶ gothreads -m login -u username -p password 27 | ▶ gothreads -m like 28 | Options : 29 | -m, --m Set Mode: login, like 30 | -d, --d Set Delay *Seconds (default: 120) 31 | -u, --u Set Instagram Username 32 | -p, --p Set Instagram Password 33 | 34 | ``` 35 | 36 | ## • Features 37 | - Auto Like Feed 38 | 39 | ## • Requirement 40 | > go version: go1.18+ 41 | 42 | ## • Installation 43 | ```bash 44 | go install -v github.com/vsec7/gothreads@latest 45 | ``` 46 | 47 | ## Run 48 | ```bash 49 | # Login 50 | gothreads -m login -u -p 51 | 52 | # Auto Like Feed 53 | gothreads -m like -d 180 54 | ``` 55 | 56 | ## Original Code : 57 | Saya recode dari : https://github.com/MrFatoni/threads 58 | 59 | Terima kasih untuk base code nya mas Fatoni . 60 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "flag" 6 | "fmt" 7 | "io/ioutil" 8 | "math/rand" 9 | "net/http" 10 | "os" 11 | "regexp" 12 | "strings" 13 | "time" 14 | ) 15 | 16 | func main() { 17 | 18 | switch mode := Mode; mode { 19 | case "login": 20 | login(Username, Password) 21 | case "like": 22 | for true { 23 | likeFeed() 24 | fmt.Printf("----------[ Delay for %d Seconds]----------\n", Delay) 25 | time.Sleep(time.Duration(Delay) * time.Second) 26 | } 27 | 28 | } 29 | } 30 | 31 | var ( 32 | Username string 33 | Password string 34 | Delay int 35 | Mode string 36 | ) 37 | 38 | func init() { 39 | flag.StringVar(&Username, "u", "", "Username") 40 | flag.StringVar(&Password, "p", "", "Password") 41 | flag.IntVar(&Delay, "d", 120, "Delay *Seconds") 42 | flag.StringVar(&Mode, "m", "", "Mode") 43 | 44 | flag.Usage = func() { 45 | h := []string{ 46 | "", 47 | "GoThreads Bot", 48 | "", 49 | "Is a simple GO tool for threads.net", 50 | "", 51 | "Crafted By : github.com/vsec7", 52 | "", 53 | "Basic Usage :", 54 | " ▶ gothreads -m login -u username -p password", 55 | " ▶ gothreads -m like", 56 | "Options :", 57 | " -m, --m Set Mode: login, like", 58 | " -d, --d Set Delay *Seconds (default: 120)", 59 | " -u, --u Set Instagram Username", 60 | " -p, --p Set Instagram Password", 61 | "", 62 | "", 63 | } 64 | fmt.Fprintf(os.Stderr, strings.Join(h, "\n")) 65 | } 66 | flag.Parse() 67 | } 68 | 69 | func likeFeed() { 70 | tokenBytes, err := ioutil.ReadFile("token.txt") 71 | if err != nil { 72 | fmt.Println("[x] Failed to read token.txt") 73 | fmt.Println("[▶] gothreads -m login -u username -p password") 74 | os.Exit(0) 75 | } 76 | 77 | token := strings.TrimSpace(string(tokenBytes)) 78 | 79 | uuid := uuid() 80 | 81 | headers := map[string]string{ 82 | "X-Bloks-Version-Id": "5f56efad68e1edec7801f630b5c122704ec5378adbee6609a448f105f34a9c73", 83 | "X-Ig-Www-Claim": "hmac.AR2jr3_r-N6PqPM09G7tetqnPfD9P_Ux_HFjJvwyPwksRLqR", 84 | "X-Ig-Device-Id": uuid, 85 | "X-Ig-Android-Id": "android-6be35fa278d92525", 86 | "User-Agent": "Barcelona 289.0.0.77.109 Android (31/12; 440dpi; 1080x2148; Google/google; sdk_gphone64_arm64; emulator64_arm64; ranchu; en_US; 489720145)", 87 | "Accept-Language": "en-US", 88 | "Authorization": "Bearer " + token, 89 | "Host": "i.instagram.com", 90 | } 91 | 92 | getFeedsURL := "https://i.instagram.com/api/v1/feed/text_post_app_timeline/" 93 | postData := "feed_view_info=[]&max_id=&pagination_source=text_post_feed_threads&is_pull_to_refresh=0&_uuid=" + uuid + "&bloks_versioning_id=5f56efad68e1edec7801f630b5c122704ec5378adbee6609a448f105f34a9c73" 94 | 95 | getFeedsResp, err := request(getFeedsURL, postData, headers, true) 96 | if err != nil { 97 | fmt.Println("[x] Failed to get feeds:", err) 98 | return 99 | } 100 | 101 | parsegetFeeds := parseGetFeedsResponse(getFeedsResp) 102 | if parsegetFeeds == nil { 103 | fmt.Println("[x] Failed to parse feeds response.") 104 | return 105 | } 106 | 107 | if parsegetFeeds["message"] == "login_required" { 108 | fmt.Println("[x] Auth expired, please get token again.") 109 | fmt.Println("[▶] gothreads -m login -u username -p password") 110 | os.Exit(0) 111 | } 112 | 113 | items, ok := parsegetFeeds["items"].([]interface{}) 114 | if !ok { 115 | fmt.Println("[x] Failed to extract items from feeds response.") 116 | return 117 | } 118 | 119 | for _, item := range items { 120 | postingan, ok := item.(map[string]interface{}) 121 | if !ok { 122 | fmt.Println("[x] Failed to extract postingan from feeds response.") 123 | continue 124 | } 125 | 126 | threadItems, ok := postingan["thread_items"].([]interface{}) 127 | if !ok { 128 | fmt.Println("[x] Failed to extract thread_items from postingan.") 129 | continue 130 | } 131 | 132 | if len(threadItems) > 0 { 133 | threadItem, ok := threadItems[0].(map[string]interface{}) 134 | if !ok { 135 | fmt.Println("[x] Failed to extract threadItem from thread_items.") 136 | continue 137 | } 138 | 139 | post, ok := threadItem["post"].(map[string]interface{}) 140 | if !ok { 141 | fmt.Println("[x] Failed to extract post from threadItem.") 142 | continue 143 | } 144 | 145 | mediaid, ok := post["id"].(string) 146 | if !ok { 147 | fmt.Println("[x] Failed to extract mediaid from post.") 148 | continue 149 | } 150 | 151 | fmt.Println("media_id:", mediaid) 152 | 153 | likeURL := "https://i.instagram.com/api/v1/media/" + mediaid + "/like/" 154 | likeData := "signed_body=SIGNATURE.%7B%22delivery_class%22%3A%22organic%22%2C%22tap_source%22%3A%22button%22%2C%22media_id%22%3A%22" + mediaid + "%22%2C%22radio_type%22%3A%22wifi-none%22%2C%22_uuid%22%3A%22" + uuid + "%2C%22recs_ix%22%3A%221%22%2C%22is_carousel_bumped_post%22%3A%22false%22%2C%22container_module%22%3A%22ig_text_feed_timeline%22%2C%22feed_position%22%3A%221%22%7D&d=0" 155 | 156 | likeResp, err := request(likeURL, likeData, headers, false) 157 | if err != nil { 158 | fmt.Println("[x] Failed to like post:", err) 159 | continue 160 | } 161 | 162 | fmt.Println(likeResp) // Status "ok" or "fail" 163 | } 164 | } 165 | } 166 | 167 | func login(username string, password string) { 168 | uuid := uuid() 169 | 170 | headers := map[string]string{ 171 | "X-Bloks-Version-Id": "5f56efad68e1edec7801f630b5c122704ec5378adbee6609a448f105f34a9c73", 172 | "X-Ig-Www-Claim": "hmac.AR2jr3_r-N6PqPM09G7tetqnPfD9P_Ux_HFjJvwyPwksRLqR", 173 | "X-Ig-Device-Id": uuid, 174 | "X-Ig-Android-Id": "android-6be35fa278d92525", 175 | "User-Agent": "Barcelona 289.0.0.77.109 Android (31/12; 440dpi; 1080x2148; Google/google; sdk_gphone64_arm64; emulator64_arm64; ranchu; en_US; 489720145)", 176 | "Accept-Language": "en-US", 177 | "Host": "i.instagram.com", 178 | } 179 | 180 | getTokenURL := "https://i.instagram.com/api/v1/bloks/apps/com.bloks.www.bloks.caa.login.async.send_login_request/" 181 | postData := fmt.Sprintf(`params={"client_input_params":{"device_id":"android-6be35fa278d92525","login_attempt_count":1,"secure_family_device_id":"","machine_id":"ZKoroAABAAGyzN_tN5j5gN3Q0kpR","accounts_list":[],"auth_secure_device_id":"","password":"%s","family_device_id":"621af360-a821-4229-9e3a-678d59eb7d37","fb_ig_device_id":[],"device_emails":[],"try_num":1,"event_flow":"login_manual","event_step":"home_page","openid_tokens":{},"client_known_key_hash":"","contact_point":"%s","encrypted_msisdn":""},"server_params":{"username_text_input_id":"wktbih:48","device_id":"android-6be35fa278d92525","should_trigger_override_login_success_action":0,"server_login_source":"login","waterfall_id":"6b2356be-c2a1-41ac-8f81-9af5ec0aee87","login_source":"Login","INTERNAL__latency_qpl_instance_id":196987789700164,"is_platform_login":0,"credential_type":"password","family_device_id":"621af360-a821-4229-9e3a-678d59eb7d37","INTERNAL__latency_qpl_marker_id":36707139,"offline_experiment_group":"caa_iteration_v3_perf_ig_4","INTERNAL_INFRA_THEME":"harm_f","password_text_input_id":"wktbih:49","ar_event_source":"login_home_page"}}`, 182 | password, username) 183 | 184 | resp, err := request(getTokenURL, postData, headers, true) 185 | if err != nil { 186 | fmt.Println("[x] Failed to get auth token. Try again.\n[▶] gothreads -m login -u username -p password") 187 | os.Exit(0) 188 | } 189 | 190 | authToken := extractAuthToken(resp) 191 | if authToken == "" { 192 | fmt.Println("[x] Failed to get auth token. Try again.\n[▶] gothreads -m login -u username -p password") 193 | os.Exit(0) 194 | } 195 | 196 | err = ioutil.WriteFile("token.txt", []byte(authToken), 0644) 197 | if err != nil { 198 | fmt.Println("[x] Failed to write token to file:", err) 199 | os.Exit(0) 200 | } 201 | 202 | fmt.Println("[+] Successfully Login\n[+] Bearer Token Saved to token.txt") 203 | } 204 | 205 | func uuid() string { 206 | return fmt.Sprintf("%04x%04x-%04x-%04x-%04x-%04x%04x%04x", 207 | rand.Intn(0xffff), 208 | rand.Intn(0xffff), 209 | rand.Intn(0xffff), 210 | rand.Intn(0x0fff)|0x4000, 211 | rand.Intn(0x3fff)|0x8000, 212 | rand.Intn(0xffff), 213 | rand.Intn(0xffff), 214 | rand.Intn(0xffff), 215 | ) 216 | } 217 | 218 | func parseGetFeedsResponse(response string) map[string]interface{} { 219 | var result map[string]interface{} 220 | err := json.Unmarshal([]byte(response), &result) 221 | if err != nil { 222 | fmt.Println("Failed to parse Get Feeds response:", err) 223 | return nil 224 | } 225 | return result 226 | } 227 | 228 | func request(url, data string, headers map[string]string, outputHeader bool) (string, error) { 229 | client := &http.Client{ 230 | Timeout: 15 * time.Second, 231 | } 232 | 233 | req, err := http.NewRequest("POST", url, strings.NewReader(data)) 234 | if err != nil { 235 | return "", err 236 | } 237 | 238 | if headers != nil { 239 | for key, value := range headers { 240 | req.Header.Set(key, value) 241 | } 242 | } 243 | 244 | if outputHeader { 245 | req.Header.Set("Content-Type", "application/x-www-form-urlencoded") 246 | req.Header.Set("Content-Length", fmt.Sprintf("%d", len(data))) 247 | } 248 | 249 | resp, err := client.Do(req) 250 | if err != nil { 251 | return "", err 252 | } 253 | defer resp.Body.Close() 254 | 255 | body, err := ioutil.ReadAll(resp.Body) 256 | if err != nil { 257 | return "", err 258 | } 259 | 260 | return string(body), nil 261 | } 262 | 263 | func extractAuthToken(response string) string { 264 | re := regexp.MustCompile(`Bearer (.*?)\\\\`) 265 | matches := re.FindStringSubmatch(response) 266 | if len(matches) < 2 { 267 | return "" 268 | } 269 | 270 | return matches[1] 271 | } 272 | --------------------------------------------------------------------------------