-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.py
163 lines (138 loc) · 6.4 KB
/
main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
from bs4 import BeautifulSoup
import requests
import os
import sys
import docker
import getopt
from pathlib import Path
def main(argv):
global _verbose
_verbose = False
inputFilePath = ""
outputDirectory = ""
token = ""
channelsDict = {}
try:
opts, _ = getopt.getopt(
argv, "hi:t:o:v", ["help", "input=", "token=", "output=", "verbose"]
)
except getopt.GetoptError as err:
print(err)
usage()
sys.exit(1)
for opt, arg in opts:
if opt in ("-h", "--help"):
usage()
sys.exit(0)
elif opt in ("-v", "--verbose"):
_verbose = True
print("INFO --- Verbose mode is active")
elif opt in ("-i", "--input"):
inputFilePath = arg
elif opt in ("-o", "--output"):
outputDirectory = arg
elif opt in ("-t", "--token"):
token = arg
# Check if inputFile is specified, exists and valid
# If OK contruct the dictionnary
if inputFilePath == "":
print("ERROR --- Please specify the channel list's file in parameter")
usage()
sys.exit(2)
if not os.path.exists(inputFilePath):
print("ERROR --- The input file specified in parameter doesn't exist")
sys.exit(3)
if not os.path.isfile(inputFilePath):
print("ERROR --- The input file specified in parameter isn't a file")
sys.exit(4)
with open(inputFilePath) as inputFile:
for line_number, line in enumerate(inputFile, 1):
try:
key, value = line.split(":")
except ValueError:
print("ERROR --- Incorrect entry at line " + str(line_number) + " of input file : No ':' found")
sys.exit(5)
value = value.rstrip()
if value == "":
print("ERROR --- Incorrect entry at line " + str(line_number) + " of input file : Empty channel ID")
sys.exit(5)
channelsDict[key.strip().replace(" ", "_")] = value.strip()
# Check outputDirectory, create folder in pwd if not specified
pwd = os.path.dirname(os.path.realpath(__file__))
if outputDirectory == "":
Path(pwd + "/export/").mkdir(exist_ok=True)
outputDirectory = pwd + "/export/"
print("INFO --- Output directory not specified, create 'export' folder in ", pwd) \
if _verbose is True else None
else:
if not os.path.exists(outputDirectory):
print("ERROR --- The output directory specified in parameter doesn't exist")
sys.exit(6)
if not os.path.isdir(outputDirectory):
print("ERROR --- The output directory specified in parameter isn't a directory")
sys.exit(7)
if outputDirectory[-1] != "/":
outputDirectory = outputDirectory + "/"
# Get Discord token from env_variable if not specified in parameter
if token == "":
print("INFO --- The Discord token wasn't specified in parameter, searching in env DISCORD_TOKEN") \
if _verbose is True else None
token: str = os.getenv("DISCORD_TOKEN")
if token is None:
print("ERROR --- The Discord token wasn't specified either in parameter or in environnement variable")
usage()
sys.exit(8)
run(token, channelsDict, outputDirectory)
def run(discordToken, channelsDict, outputDirectory):
# Initialze Docker client
client = docker.from_env()
# Create tmp directory
Path("/tmp/export-discord/").mkdir()
for channelName, channelID in channelsDict.items():
print("INFO --- Exporting " + channelName + " channel (id=" + channelID + ")")
# Create dest folder
Path(outputDirectory + channelName).mkdir(parents=True, exist_ok=True)
# Launch tyrrrz/discordchatexporter container
# https://github.com/Tyrrrz/DiscordChatExporter/
client.containers.run(
"tyrrrz/discordchatexporter:stable",
"export -t " + discordToken + " -c " + channelID,
detach=False,
remove=True,
volumes={"/tmp/export-discord/": {"bind": "/app/out", "mode": "rw"}},
)
_, _, files = next(os.walk("/tmp/export-discord/"))
if len(files) == 0:
print("ERROR --- The container didn't export the channel, meaning that your token or the channel id is invalid")
Path("/tmp/export-discord").rmdir()
sys.exit(9)
sourceFile = open("/tmp/export-discord/" + files[0], "r")
soup = BeautifulSoup(sourceFile, "html.parser")
attachementDivs = soup.find_all("div", class_="chatlog__attachment")
print("INFO --- Found " + str(len(attachementDivs)) + " medias in channel " + channelName)
i = 0
for div in attachementDivs:
link = div.a.get("href")
attachement = link[(link.rindex("/") + 1):]
path = outputDirectory + channelName + "/" + attachement
if not os.path.exists(path):
i = i + 1
with open(path, "wb") as f:
print("INFO --- Downloading ", attachement) \
if _verbose is True else None
f.write(requests.get(link).content)
print("INFO --- " + str(i) + " medias were downloaded")
os.remove("/tmp/export-discord/" + files[0])
print("INFO --- Export successfully completed")
print("INFO --- Files are located in " + outputDirectory)
Path("/tmp/export-discord").rmdir()
def usage():
print("\nUsage: python3 main.py [OPTIONS]")
print("\nOPTION\t\tREQUIRED\tDESCRIPTION")
print("-h, --help\tNO\t\tShow manual")
print("-i, --input\tYES\t\tPath to config file\n\t\t\t\tTo know how to create this file, visit https://github.com/pbrissaud/discord-attachements-exporter/wiki/Usage-guide#create-your-config-file")
print("-o, --output\tNO\t\tPath to directory where attachements will be downloaded ; if not specified, the script will create one in pwd")
print("-t, --token\tNO\t\tDiscord user token ; if not specified, the script will search in DISCORD_TOKEN env\n\t\t\t\tOne of two must be defined (the script always take the parameter first)\n\t\t\t\tTo know how to get a user token, visit https://github.com/pbrissaud/discord-attachements-exporter/wiki/Usage-guide#grab-your-discord-token")
print("-v, --verbose\tNO\t\tVerbose mode (more logs)")
if __name__ == "__main__":
main(sys.argv[1:])