├── 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 |
--------------------------------------------------------------------------------