Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
V4NSH4J
GitHub Repository: V4NSH4J/discord-mass-DM-GO
Path: blob/main/discord/scraper.go
310 views
1
// Copyright (C) 2021 github.com/V4NSH4J
2
//
3
// This source code has been released under the GNU Affero General Public
4
// License v3.0. A copy of this license is available at
5
// https://www.gnu.org/licenses/agpl-3.0.en.html
6
7
package discord
8
9
import (
10
"bufio"
11
"encoding/json"
12
"fmt"
13
"os"
14
"os/exec"
15
"strings"
16
"time"
17
18
"github.com/V4NSH4J/discord-mass-dm-GO/instance"
19
"github.com/V4NSH4J/discord-mass-dm-GO/utilities"
20
)
21
22
func LaunchScraperMenu() {
23
cfg, _, err := instance.GetEverything()
24
if err != nil {
25
utilities.LogErr("Error while getting neccessary information %v", err)
26
utilities.ExitSafely()
27
}
28
utilities.PrintMenu([]string{"Online Scraper (Opcode 14)", "Scrape from Reactions (REST API)", "Offline Scraper (Opcode 8)"})
29
options := utilities.UserInputInteger("Select an option: ")
30
if options == 1 {
31
token := utilities.UserInput("Enter the token: ")
32
serverid := utilities.UserInput("Enter the server ID: ")
33
channelid := utilities.UserInput("Enter the channel ID: ")
34
var botsFile, avatarFile, nameFile, path, rolePath, scrapedFile, userDataFile string
35
if cfg.OtherSettings.Logs {
36
path = fmt.Sprintf(`logs/online_scraper/DMDGO-OS-%s-%s-%s-%s`, serverid, channelid, time.Now().Format(`2006-01-02 15-04-05`), utilities.RandStringBytes(5))
37
rolePath = fmt.Sprintf(`%s/roles`, path)
38
err := os.MkdirAll(path, 0755)
39
if err != nil && !os.IsExist(err) {
40
utilities.LogErr("Error creating logs directory: %s", err)
41
utilities.ExitSafely()
42
}
43
err = os.MkdirAll(rolePath, 0755)
44
if err != nil && !os.IsExist(err) {
45
utilities.LogErr("Error creating roles directory: %s", err)
46
utilities.ExitSafely()
47
}
48
botsFileX, err := os.Create(fmt.Sprintf(`%s/bots.txt`, path))
49
if err != nil {
50
utilities.LogErr("Error creating bots file: %s", err)
51
utilities.ExitSafely()
52
}
53
botsFileX.Close()
54
AvatarFileX, err := os.Create(fmt.Sprintf(`%s/avatars.txt`, path))
55
if err != nil {
56
utilities.LogErr("Error creating avatars file: %s", err)
57
utilities.ExitSafely()
58
}
59
AvatarFileX.Close()
60
NameFileX, err := os.Create(fmt.Sprintf(`%s/names.txt`, path))
61
if err != nil {
62
utilities.LogErr("Error creating names file: %s", err)
63
utilities.ExitSafely()
64
}
65
NameFileX.Close()
66
ScrapedFileX, err := os.Create(fmt.Sprintf(`%s/scraped.txt`, path))
67
if err != nil {
68
utilities.LogErr("Error creating scraped file: %s", err)
69
utilities.ExitSafely()
70
}
71
UserDataFileX, err := os.Create(fmt.Sprintf(`%s/user_data.txt`, path))
72
if err != nil {
73
utilities.LogErr("Error creating user data file: %s", err)
74
utilities.ExitSafely()
75
}
76
botsFile, avatarFile, nameFile, scrapedFile, userDataFile = botsFileX.Name(), AvatarFileX.Name(), NameFileX.Name(), ScrapedFileX.Name(), UserDataFileX.Name()
77
}
78
Is := instance.Instance{Token: token}
79
title := make(chan bool)
80
go func() {
81
Out:
82
for {
83
select {
84
case <-title:
85
break Out
86
default:
87
if Is.Ws != nil {
88
if Is.Ws.Conn != nil {
89
cmd := exec.Command("cmd", "/C", "title", fmt.Sprintf(`DMDGO [%v Scraped]`, len(Is.Ws.Members)))
90
_ = cmd.Run()
91
}
92
}
93
}
94
}
95
}()
96
t := 0
97
for {
98
if t >= 5 {
99
utilities.LogErr("Couldn't connect to websocket after retrying.")
100
break
101
}
102
err := Is.StartWS()
103
if err != nil {
104
utilities.LogFailed("Error while opening websocket: %v", err)
105
} else {
106
break
107
}
108
t++
109
}
110
111
utilities.LogErr("Websocket opened %v", Is.Token)
112
i := 0
113
for {
114
err := instance.Scrape(Is.Ws, serverid, channelid, i)
115
if err != nil {
116
utilities.LogErr("Error while scraping: %v", err)
117
}
118
utilities.LogSuccess("Token %v Scrape Count: %v", Is.Token, len(Is.Ws.Members))
119
if Is.Ws.Complete {
120
break
121
}
122
i++
123
time.Sleep(time.Duration(cfg.ScraperSettings.SleepSc) * time.Millisecond)
124
}
125
if Is.Ws != nil {
126
Is.Ws.Close()
127
}
128
utilities.LogSuccess("Scraping finished. Scraped %v members", len(Is.Ws.Members))
129
if cfg.OtherSettings.Logs {
130
for i := 0; i < len(Is.Ws.Members); i++ {
131
if Is.Ws.Members[i].User.Bot {
132
utilities.WriteLinesPath(botsFile, fmt.Sprintf("%v %v %v", Is.Ws.Members[i].User.ID, Is.Ws.Members[i].User.Username, Is.Ws.Members[i].User.Discriminator))
133
}
134
if Is.Ws.Members[i].User.Avatar != "" {
135
utilities.WriteLinesPath(avatarFile, fmt.Sprintf("%v:%v", Is.Ws.Members[i].User.ID, Is.Ws.Members[i].User.Avatar))
136
}
137
if Is.Ws.Members[i].User.Username != "" {
138
utilities.WriteLinesPath(nameFile, fmt.Sprintf("%v", Is.Ws.Members[i].User.Username))
139
}
140
for x := 0; x < len(Is.Ws.Members[i].Roles); x++ {
141
utilities.WriteRoleFile(Is.Ws.Members[i].User.ID, rolePath, Is.Ws.Members[i].Roles[x])
142
}
143
if Is.Ws.Members[i].User.Discriminator != "" && Is.Ws.Members[i].User.Username != "" {
144
utilities.WriteLinesPath(userDataFile, fmt.Sprintf("%v#%v", Is.Ws.Members[i].User.Username, Is.Ws.Members[i].User.Discriminator))
145
}
146
utilities.WriteLinesPath(scrapedFile, Is.Ws.Members[i].User.ID)
147
}
148
}
149
var memberids []string
150
for _, member := range Is.Ws.Members {
151
memberids = append(memberids, member.User.ID)
152
}
153
clean := utilities.RemoveDuplicateStr(memberids)
154
utilities.LogSuccess("Removed Duplicates. Scraped %v members", len(clean))
155
write := utilities.UserInput("Write to memberids.txt? (y/n)")
156
title <- true
157
if write == "y" {
158
for k := 0; k < len(clean); k++ {
159
err := utilities.WriteLines("memberids.txt", clean[k])
160
if err != nil {
161
utilities.LogErr("Error while writing to file: %v", err)
162
}
163
}
164
utilities.LogSuccess("Wrote %v members to memberids.txt", len(clean))
165
}
166
167
}
168
if options == 2 {
169
token := utilities.UserInput("Enter the token: ")
170
channelid := utilities.UserInput("Enter the channel ID: ")
171
messageid := utilities.UserInput("Enter the message ID: ")
172
utilities.PrintMenu([]string{"Get Emoji from Message", "Enter Emoji Manually"})
173
option := utilities.UserInputInteger("Select an option: ")
174
var send string
175
if option == 2 {
176
send = utilities.UserInput("Enter the emoji [Format emojiName or emojiName:emojiID for nitro emojis]: ")
177
} else {
178
msg, err := instance.GetRxn(channelid, messageid, token)
179
if err != nil {
180
utilities.LogErr("Error while getting message: %v", err)
181
}
182
var selection []string
183
for i := 0; i < len(msg.Reactions); i++ {
184
selection = append(selection, fmt.Sprintf("Emoji: %v | Count: %v", msg.Reactions[i].Emojis.Name, msg.Reactions[i].Count))
185
}
186
utilities.PrintMenu2(selection)
187
index := utilities.UserInputInteger("Select an option: ")
188
if msg.Reactions[index].Emojis.ID == "" {
189
send = msg.Reactions[index].Emojis.Name
190
191
} else if msg.Reactions[index].Emojis.ID != "" {
192
send = msg.Reactions[index].Emojis.Name + ":" + msg.Reactions[index].Emojis.ID
193
}
194
}
195
var allUIDS []string
196
var m string
197
title := make(chan bool)
198
go func() {
199
Out:
200
for {
201
select {
202
case <-title:
203
break Out
204
default:
205
cmd := exec.Command("cmd", "/C", "title", fmt.Sprintf(`DMDGO [%v Scraped]`, len(allUIDS)))
206
_ = cmd.Run()
207
}
208
209
}
210
}()
211
for {
212
if len(allUIDS) == 0 {
213
m = ""
214
} else {
215
m = allUIDS[len(allUIDS)-1]
216
}
217
rxn, err := instance.GetReactions(channelid, messageid, token, send, m)
218
if err != nil {
219
utilities.LogErr("Error while getting reactions: %v", err)
220
continue
221
}
222
if len(rxn) == 0 {
223
break
224
}
225
utilities.LogInfo("Scraped %v members", len(rxn))
226
allUIDS = append(allUIDS, rxn...)
227
228
}
229
utilities.LogInfo("Scraping finished. Scraped %v lines - Removing Duplicates", len(allUIDS))
230
clean := utilities.RemoveDuplicateStr(allUIDS)
231
path := fmt.Sprintf(`logs/reaction_scraper/DMDGO-RS-%s-%s-%s-%s`, channelid, messageid, time.Now().Format(`2006-01-02 15-04-05`), utilities.RandStringBytes(5))
232
err := os.MkdirAll(path, 0755)
233
if err != nil && !os.IsExist(err) {
234
utilities.LogErr("Error creating logs directory: %s", err)
235
utilities.ExitSafely()
236
}
237
scrapedFileX, err := os.Create(fmt.Sprintf(`%s/scraped.txt`, path))
238
if err != nil {
239
utilities.LogErr("Error creating scraped file: %s", err)
240
utilities.ExitSafely()
241
}
242
defer scrapedFileX.Close()
243
scrapedFile := scrapedFileX.Name()
244
for i := 0; i < len(clean); i++ {
245
utilities.WriteLinesPath(scrapedFile, clean[i])
246
}
247
write := utilities.UserInput("Write to memberids.txt? (y/n)")
248
if write == "y" {
249
for k := 0; k < len(clean); k++ {
250
err := utilities.WriteLines("memberids.txt", clean[k])
251
if err != nil {
252
utilities.LogErr("Error while writing to file: %v", err)
253
}
254
}
255
utilities.LogSuccess("Wrote %v members to memberids.txt", len(clean))
256
}
257
title <- true
258
utilities.LogSuccess("Finished")
259
}
260
if options == 3 {
261
cfg, instances, err := instance.GetEverything()
262
if err != nil {
263
utilities.LogErr("Error while getting instances: %v", err)
264
return
265
}
266
var scraped []string
267
var queriesCompleted []string
268
// Input the number of tokens to be used
269
title := make(chan bool)
270
go func() {
271
Out:
272
for {
273
select {
274
case <-title:
275
break Out
276
default:
277
cmd := exec.Command("cmd", "/C", "title", fmt.Sprintf(`DMDGO [%v Scraped %v Queries Completed]`, len(scraped), len(queriesCompleted)))
278
_ = cmd.Run()
279
}
280
281
}
282
}()
283
numTokens := utilities.UserInputInteger("How many tokens do you wish to use? You have %v ", len(instances))
284
quit := make(chan bool)
285
var allQueries []string
286
var chars string
287
rawChars := " !\"#$%&'()*+,-./0123456789:;<=>?@[]^_`abcdefghijklmnopqrstuvwxyz{|}~" + cfg.ScraperSettings.ExtendedChars
288
// Removing duplicates
289
for i := 0; i < len(rawChars); i++ {
290
if !strings.Contains(rawChars[0:i], string(rawChars[i])) {
291
chars += string(rawChars[i])
292
}
293
}
294
295
queriesLeft := make(chan string)
296
297
for i := 0; i < len(chars); i++ {
298
go func(i int) {
299
queriesLeft <- string(chars[i])
300
}(i)
301
}
302
303
if numTokens > len(instances) {
304
utilities.LogWarn("You only have %v tokens in your tokens.txt Using the maximum number of tokens possible", len(instances))
305
} else if numTokens <= 0 {
306
utilities.LogErr("You must atleast use 1 token")
307
utilities.ExitSafely()
308
} else if numTokens <= len(instances) {
309
utilities.LogInfo("You have %v tokens in your tokens.txt Using %v tokens", len(instances), numTokens)
310
instances = instances[:numTokens]
311
} else {
312
utilities.LogErr("Invalid input")
313
}
314
315
serverid := utilities.UserInput("Enter the server ID: ")
316
var tokenFile, botsFile, avatarFile, nameFile, path, rolePath, scrapedFile, userDataFile string
317
if cfg.OtherSettings.Logs {
318
path = fmt.Sprintf(`logs/offline_scraper/DMDGO-OS-%s-%s-%s`, serverid, time.Now().Format(`2006-01-02 15-04-05`), utilities.RandStringBytes(5))
319
rolePath = fmt.Sprintf(`%s/roles`, path)
320
err := os.MkdirAll(path, 0755)
321
if err != nil && !os.IsExist(err) {
322
utilities.LogErr("Error creating logs directory: %s", err)
323
utilities.ExitSafely()
324
}
325
err = os.MkdirAll(rolePath, 0755)
326
if err != nil && !os.IsExist(err) {
327
utilities.LogErr("Error creating roles directory: %s", err)
328
utilities.ExitSafely()
329
}
330
tokenFileX, err := os.Create(fmt.Sprintf(`%s/token.txt`, path))
331
if err != nil {
332
utilities.LogErr("Error creating token file: %s", err)
333
utilities.ExitSafely()
334
}
335
tokenFileX.Close()
336
botsFileX, err := os.Create(fmt.Sprintf(`%s/bots.txt`, path))
337
if err != nil {
338
utilities.LogErr("Error creating bots file: %s", err)
339
utilities.ExitSafely()
340
}
341
botsFileX.Close()
342
AvatarFileX, err := os.Create(fmt.Sprintf(`%s/avatars.txt`, path))
343
if err != nil {
344
utilities.LogErr("Error creating avatars file: %s", err)
345
utilities.ExitSafely()
346
}
347
AvatarFileX.Close()
348
NameFileX, err := os.Create(fmt.Sprintf(`%s/names.txt`, path))
349
if err != nil {
350
utilities.LogErr("Error creating names file: %s", err)
351
utilities.ExitSafely()
352
}
353
NameFileX.Close()
354
ScrapedFileX, err := os.Create(fmt.Sprintf(`%s/scraped.txt`, path))
355
if err != nil {
356
utilities.LogErr("Error creating scraped file: %s", err)
357
utilities.ExitSafely()
358
}
359
defer ScrapedFileX.Close()
360
UserDataFileX, err := os.Create(fmt.Sprintf(`%s/userdata.txt`, path))
361
if err != nil {
362
utilities.LogErr("Error creating userdata file: %s", err)
363
utilities.ExitSafely()
364
}
365
defer UserDataFileX.Close()
366
tokenFile, botsFile, avatarFile, nameFile, scrapedFile, userDataFile = tokenFileX.Name(), botsFileX.Name(), AvatarFileX.Name(), NameFileX.Name(), ScrapedFileX.Name(), UserDataFileX.Name()
367
for i := 0; i < len(instances); i++ {
368
instances[i].WriteInstanceToFile(tokenFile)
369
}
370
}
371
utilities.LogInfo("Press ENTER to START and STOP scraping")
372
bufio.NewReader(os.Stdin).ReadBytes('\n')
373
var namesScraped []string
374
var avatarsScraped []string
375
// Starting the instances as GOroutines
376
for i := 0; i < len(instances); i++ {
377
go func(i int) {
378
instances[i].ScrapeCount = 0
379
for {
380
381
// Start websocket, reconnect if disconnected.
382
if instances[i].ScrapeCount%5 == 0 || instances[i].LastCount%100 == 0 {
383
if instances[i].Ws != nil {
384
instances[i].Ws.Close()
385
}
386
time.Sleep(2 * time.Second)
387
err := instances[i].StartWS()
388
if err != nil {
389
fmt.Println(err)
390
continue
391
}
392
time.Sleep(2 * time.Second)
393
394
}
395
instances[i].ScrapeCount++
396
397
// Get a query from the channel / Await for close response
398
select {
399
case <-quit:
400
if instances[i].Ws != nil {
401
instances[i].Ws.Close()
402
}
403
return
404
default:
405
query := <-queriesLeft
406
allQueries = append(allQueries, query)
407
if instances[i].Ws == nil {
408
continue
409
}
410
if instances[i].Ws.Conn == nil {
411
continue
412
}
413
err := instance.ScrapeOffline(instances[i].Ws, serverid, query)
414
if err != nil {
415
utilities.LogErr("%v Error while scraping: %v", instances[i].CensorToken(), err)
416
go func() {
417
queriesLeft <- query
418
}()
419
continue
420
}
421
422
memInfo := <-instances[i].Ws.OfflineScrape
423
queriesCompleted = append(queriesCompleted, query)
424
var MemberInfo instance.Event
425
err = json.Unmarshal(memInfo, &MemberInfo)
426
if err != nil {
427
utilities.LogErr("Error while unmarshalling: %v", err)
428
queriesLeft <- query
429
continue
430
}
431
432
if len(MemberInfo.Data.Members) == 0 {
433
instances[i].LastCount = -1
434
continue
435
}
436
instances[i].LastCount = len(MemberInfo.Data.Members)
437
for _, member := range MemberInfo.Data.Members {
438
// Avoiding Duplicates
439
if !utilities.Contains(scraped, member.User.ID) {
440
scraped = append(scraped, member.User.ID)
441
}
442
}
443
utilities.LogSuccess("Token %v Query %v Scraped %v [+%v]", instances[i].CensorToken(), query, len(scraped), len(MemberInfo.Data.Members))
444
445
for i := 0; i < len(MemberInfo.Data.Members); i++ {
446
id := MemberInfo.Data.Members[i].User.ID
447
err := utilities.WriteLines("memberids.txt", id)
448
if err != nil {
449
utilities.LogErr("Error while writing to file: %v", err)
450
continue
451
}
452
if cfg.OtherSettings.Logs {
453
utilities.WriteLinesPath(scrapedFile, id)
454
if MemberInfo.Data.Members[i].User.Bot {
455
utilities.WriteLinesPath(botsFile, fmt.Sprintf("%v %v %v", id, MemberInfo.Data.Members[i].User.Username, MemberInfo.Data.Members[i].User.Discriminator))
456
}
457
if MemberInfo.Data.Members[i].User.Avatar != "" {
458
utilities.WriteLinesPath(avatarFile, fmt.Sprintf("%v:%v", id, MemberInfo.Data.Members[i].User.Avatar))
459
}
460
if MemberInfo.Data.Members[i].User.Username != "" {
461
utilities.WriteLinesPath(nameFile, fmt.Sprintf("%v", MemberInfo.Data.Members[i].User.Username))
462
}
463
for x := 0; x < len(MemberInfo.Data.Members[i].Roles); x++ {
464
utilities.WriteRoleFile(id, rolePath, MemberInfo.Data.Members[i].Roles[x])
465
}
466
if MemberInfo.Data.Members[i].User.Username != "" && MemberInfo.Data.Members[i].User.Discriminator != "" {
467
utilities.WriteLinesPath(userDataFile, fmt.Sprintf("%v#%v", MemberInfo.Data.Members[i].User.Username, MemberInfo.Data.Members[i].User.Discriminator))
468
}
469
}
470
471
if cfg.ScraperSettings.ScrapeUsernames {
472
nom := MemberInfo.Data.Members[i].User.Username
473
if !utilities.Contains(namesScraped, nom) {
474
err := utilities.WriteLines("names.txt", nom)
475
if err != nil {
476
utilities.LogErr("Error while writing to file: %v", err)
477
continue
478
}
479
}
480
}
481
if cfg.ScraperSettings.ScrapeAvatars {
482
av := MemberInfo.Data.Members[i].User.Avatar
483
if !utilities.Contains(avatarsScraped, av) {
484
err := utilities.ProcessAvatar(av, id)
485
if err != nil {
486
utilities.LogErr("Error while processing avatar: %v", err)
487
continue
488
}
489
}
490
}
491
}
492
if len(MemberInfo.Data.Members) < 100 {
493
time.Sleep(time.Duration(cfg.ScraperSettings.SleepSc) * time.Millisecond)
494
continue
495
}
496
lastName := MemberInfo.Data.Members[len(MemberInfo.Data.Members)-1].User.Username
497
498
nextQueries := instance.FindNextQueries(query, lastName, queriesCompleted, chars)
499
for i := 0; i < len(nextQueries); i++ {
500
go func(i int) {
501
queriesLeft <- nextQueries[i]
502
}(i)
503
}
504
505
}
506
507
}
508
}(i)
509
}
510
511
bufio.NewReader(os.Stdin).ReadBytes('\n')
512
utilities.LogInfo("Stopping All instances")
513
title <- true
514
for i := 0; i < len(instances); i++ {
515
go func() {
516
quit <- true
517
}()
518
}
519
520
utilities.LogInfo("Scraping Complete. %v members scraped.", len(scraped))
521
choice := utilities.UserInput("Do you wish to write to file again? (y/n) [This will remove pre-existing IDs from memberids.txt]")
522
if choice == "y" || choice == "Y" {
523
clean := utilities.RemoveDuplicateStr(scraped)
524
err := utilities.TruncateLines("memberids.txt", clean)
525
if err != nil {
526
utilities.LogErr("Error while truncating file: %v", err)
527
}
528
}
529
530
}
531
}
532
533