diff --git a/.gitignore b/.gitignore index 4535f44..870f346 100644 --- a/.gitignore +++ b/.gitignore @@ -117,4 +117,5 @@ toxic/ fuck/ coffin/ 1800/ -*.csv \ No newline at end of file +*.csv +User ID Parser/.vscode/launch.json diff --git a/Locate ID/LocateID b/Locate ID/LocateID new file mode 100755 index 0000000..6f7a879 Binary files /dev/null and b/Locate ID/LocateID differ diff --git a/Locate ID/LocateID.go b/Locate ID/LocateID.go new file mode 100644 index 0000000..ccba48e --- /dev/null +++ b/Locate ID/LocateID.go @@ -0,0 +1,141 @@ +package main + +import ( + "encoding/csv" + "flag" + "io/fs" + "io/ioutil" + "log" + "os" +) + +var ( + CsvInput string + UID string + CsvOutput string +) + +// struct for getting just user ID out of csv file +type DiscordCSV struct { + UserID string + Author string + Date string + Content string + Attachments string +} + +func init() { + + flag.StringVar(&UID, "id", "", "User ID to locate") + flag.StringVar(&CsvInput, "i", "", "The csv or folder to search") + flag.StringVar(&CsvOutput, "o", "", "Output CSV to save mesages from user") + flag.Parse() +} + +func main() { + // Opens input and gets info + fileInfo, err := os.Stat(CsvInput) + if err != nil { + // error handling + println(err.Error()) + } + + if CsvOutput != "" { + OutFile, err := os.OpenFile(CsvOutput, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600) + if err != nil { + panic(err) + } + OutFile.WriteString("UID, Author, Date, Content, Attachments, Found in\n") + OutFile.Close() + } + + // checks if input is directory or file + if fileInfo.IsDir() { + // file is a directory + files, err := ioutil.ReadDir(CsvInput) // CsvInput + if err != nil { + log.Fatal(err) + } + + // For each file in folder + for _, file := range files { + if !file.IsDir() { + file_path := CsvInput + file.Name() + process(file_path, file) + } + + } + + } else { + // file is not a directory + files, err := os.Stat(CsvInput) // CsvInput + if err != nil { + log.Fatal(err) + } + process(CsvInput, files) + + } +} + +func process(inFile string, fileInfo fs.FileInfo) { + var messages []string + + // Read CSV file + lines, err := ReadCsv(inFile) + if err != nil { + panic(err) + } + + for _, line := range lines { + data := DiscordCSV{ + UserID: line[0], + Author: line[1], + Date: line[2], + Content: line[3], + Attachments: line[4], + } + if UID == data.UserID { + messages = append(messages, data.UserID+","+data.Author+","+data.Date+","+data.Content+","+data.Attachments+","+fileInfo.Name()) + println(data.UserID + "," + data.Author + "," + data.Date + "," + data.Content + "," + data.Attachments + "," + fileInfo.Name()) + } + } + + if CsvOutput != "" { + OutFile, err := os.OpenFile(CsvOutput, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600) + if err != nil { + panic(err) + } + for _, message := range messages { + OutFile.WriteString(message + "\n") + } + } + +} + +func ReadCsv(filename string) ([][]string, error) { + + // Open CSV file + f, err := os.Open(filename) + if err != nil { + return [][]string{}, err + } + defer f.Close() + + // Read File into a Variable + lines, err := csv.NewReader(f).ReadAll() + if err != nil { + return [][]string{}, err + } + + return lines, nil +} + +func contains(s []string, str string) bool { + for _, v := range s { + if v == str { + return true + } + } + + return false +} diff --git a/Locate ID/build.sh b/Locate ID/build.sh new file mode 100755 index 0000000..0b276a2 --- /dev/null +++ b/Locate ID/build.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash + +package="./LocateID.go" + +package_split=(${package//\// }) +package_name="LocateID" + +platforms=("windows/amd64" "windows/386" "linux/amd64" "linux/386" "linux/arm64" "linux/arm") + +for platform in "${platforms[@]}" +do + platform_split=(${platform//\// }) + GOOS=${platform_split[0]} + GOARCH=${platform_split[1]} + output_name=$package_name'-'$GOOS'-'$GOARCH + if [ $GOOS = "windows" ]; then + output_name+='.exe' + fi + + env GOOS=$GOOS GOARCH=$GOARCH go build -o ./build/$output_name $package + if [ $? -ne 0 ]; then + echo 'An error has occurred! Aborting the script execution...' + exit 1 + fi +done \ No newline at end of file diff --git a/Locate ID/go.mod b/Locate ID/go.mod new file mode 100644 index 0000000..c0ecf8b --- /dev/null +++ b/Locate ID/go.mod @@ -0,0 +1,3 @@ +module main + +go 1.17 diff --git a/README.md b/README.md index 348abc5..4dd50d0 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,29 @@ -# Discord Chat Exporter csv parser -[![wakatime](https://wakatime.com/badge/github/BuyMyMojo/discord-chat-parser.svg)](https://wakatime.com/badge/github/BuyMyMojo/discord-chat-parser) +# Discord Chat Exporter csv parsers + Now with a much faster version written in go! -this tool will take a csv (or a folder of them) from Discord Chat Exporter and create a new list of unique IDs. -Made to get all active users from a discord server. +## How to use the go version +To run the go versions all you need to do is download the executable for your platform and run it from the terminal/powershell: + +```bash +./IdParser[.exe] -i [Path to folder/file] -o [Path to output .csv (Defaults to ./out.csv)] +``` -> py ./main.py [csv file or dir] [output csv] - Use the same output between multiple runs works and takes into account old data to ensure no dupes + +```bash +./LocateID[.exe] -i [Path to folder/fil] -id "[User ID in quotes]" -o [Path to output csv (Optional)] +``` + +## How to use the Python version + +```bash +py ./main.py [csv file or dir] [output csv] +``` + +Use the same output between multiple runs works and takes into account old data to ensure no dupes + +``` bash +py ./locate_id.py [User ID] [csv file or dir] +``` diff --git a/User ID Parser/IdParser.go b/User ID Parser/IdParser.go new file mode 100644 index 0000000..d4d9570 --- /dev/null +++ b/User ID Parser/IdParser.go @@ -0,0 +1,169 @@ +package main + +import ( + "encoding/csv" + "errors" + "flag" + "fmt" + "io/ioutil" + "log" + "os" +) + +// Variables used for command line parameters +var ( + CsvInput string + CsvOutput string + unique_old []string + unique_ids []string +) + +func init() { + + flag.StringVar(&CsvInput, "i", "", "Input csv or folder [required]") + flag.StringVar(&CsvOutput, "o", "out.csv", "Output CSV") + flag.Parse() +} + +// struct for getting just user ID out of csv file +type DiscordCSV struct { + UserID string +} + +func main() { + + // Check if there is no input and exits if true + if CsvInput == "" { + println("No input given, use -h for help on how to use this program") + os.Exit(0) + } + + // Opens input and gets info + fileInfo, err := os.Stat(CsvInput) + if err != nil { + // error handling + println(err.Error()) + } + + // checks if input is directory or file + if fileInfo.IsDir() { + // file is a directory + files, err := ioutil.ReadDir(CsvInput) + if err != nil { + log.Fatal(err) + } + + // For each file in folder + for _, file := range files { + if !file.IsDir() { + process(CsvInput + file.Name()) + } + + } + + } else { + // file is not a directory + process(CsvInput) + + } + +} + +func process(CsvPath string) { + + if _, err := os.Stat(CsvOutput); err == nil { + // path/to/whatever exists + + // Read CSV file + lines, err := ReadCsv(CsvOutput) + if err != nil { + panic(err) + } + // Loop through lines & turn into object + for _, line := range lines { + data := DiscordCSV{ + UserID: line[0], + } + unique_old = append(unique_old, data.UserID) + } + + WriteCSV(CsvPath) + + } else if errors.Is(err, os.ErrNotExist) { + // path/to/whatever does *not* exist + OutFile, err := os.Create(CsvOutput) + if err != nil { + fmt.Println(err) + return + } + OutFile.Close() + + WriteCSV(CsvPath) + + } else { + // Schrodinger: file may or may not exist. See err for details. + + // Therefore, do *NOT* use !os.IsNotExist(err) to test for file existence + println(err) + } + +} + +func WriteCSV(InFile string) { + // Create DiscordIDs list + var DiscordIDs []string + + // Read CSV file + lines, err := ReadCsv(InFile) + if err != nil { + panic(err) + } + + OutFile, err := os.OpenFile(CsvOutput, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600) + + // Loop through lines & add to DiscordIDs list + for _, line := range lines { + data := DiscordCSV{ + UserID: line[0], + } + if !contains(DiscordIDs, data.UserID) { + DiscordIDs = append(DiscordIDs, data.UserID) + } + } + + // Checks if ID is already in list + // if it isnt then it gets writen to list + for _, ID := range DiscordIDs { + if !contains(unique_old, ID) { + OutFile.WriteString(ID + "\n") + } + } +} + +func ReadCsv(filename string) ([][]string, error) { + + // Open CSV file + f, err := os.Open(filename) + if err != nil { + return [][]string{}, err + } + defer f.Close() + + // Read File into a Variable + lines, err := csv.NewReader(f).ReadAll() + if err != nil { + return [][]string{}, err + } + + return lines, nil +} + +func contains(s []string, str string) bool { + for _, v := range s { + if v == str { + return true + } + } + + return false +} diff --git a/User ID Parser/build.sh b/User ID Parser/build.sh new file mode 100755 index 0000000..ad2e3a5 --- /dev/null +++ b/User ID Parser/build.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash + +package="./IdParser.go" + +package_split=(${package//\// }) +package_name="IdParser" + +platforms=("windows/amd64" "windows/386" "linux/amd64" "linux/386" "linux/arm64" "linux/arm") + +for platform in "${platforms[@]}" +do + platform_split=(${platform//\// }) + GOOS=${platform_split[0]} + GOARCH=${platform_split[1]} + output_name=$package_name'-'$GOOS'-'$GOARCH + if [ $GOOS = "windows" ]; then + output_name+='.exe' + fi + + env GOOS=$GOOS GOARCH=$GOARCH go build -o ./build/$output_name $package + if [ $? -ne 0 ]; then + echo 'An error has occurred! Aborting the script execution...' + exit 1 + fi +done \ No newline at end of file diff --git a/User ID Parser/go.mod b/User ID Parser/go.mod new file mode 100644 index 0000000..c0ecf8b --- /dev/null +++ b/User ID Parser/go.mod @@ -0,0 +1,3 @@ +module main + +go 1.17