mirror of
https://github.com/headporter81/DiscordDownloaderBot.git
synced 2026-08-08 15:41:12 +09:00
first commit
This commit is contained in:
+40
@@ -0,0 +1,40 @@
|
|||||||
|
target/
|
||||||
|
!.mvn/wrapper/maven-wrapper.jar
|
||||||
|
!**/src/main/**/target/
|
||||||
|
!**/src/test/**/target/
|
||||||
|
.kotlin
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea/modules.xml
|
||||||
|
.idea/jarRepositories.xml
|
||||||
|
.idea/compiler.xml
|
||||||
|
.idea/libraries/
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
|
||||||
|
### Eclipse ###
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
.sts4-cache
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
/nbproject/private/
|
||||||
|
/nbbuild/
|
||||||
|
/dist/
|
||||||
|
/nbdist/
|
||||||
|
/.nb-gradle/
|
||||||
|
build/
|
||||||
|
!**/src/main/**/build/
|
||||||
|
!**/src/test/**/build/
|
||||||
|
|
||||||
|
### VS Code ###
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
### Mac OS ###
|
||||||
|
.DS_Store
|
||||||
|
/Downloads/
|
||||||
Generated
+10
@@ -0,0 +1,10 @@
|
|||||||
|
# 디폴트 무시된 파일
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# 쿼리 파일을 포함한 무시된 디폴트 폴더
|
||||||
|
/queries/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
||||||
|
# 에디터 기반 HTTP 클라이언트 요청
|
||||||
|
/httpRequests/
|
||||||
Generated
+7
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Encoding">
|
||||||
|
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
|
||||||
|
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
Generated
+14
@@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||||
|
<component name="MavenProjectsManager">
|
||||||
|
<option name="originalFiles">
|
||||||
|
<list>
|
||||||
|
<option value="$PROJECT_DIR$/pom.xml" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="temurin-1.8" project-jdk-type="JavaSDK">
|
||||||
|
<output url="file://$PROJECT_DIR$/out" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
Generated
+6
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<groupId>com.example</groupId>
|
||||||
|
<artifactId>DiscordDownloaderBot</artifactId>
|
||||||
|
<version>1.0</version>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-shade-plugin</artifactId>
|
||||||
|
<version>3.5.1</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>shade</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<transformers>
|
||||||
|
<transformer>
|
||||||
|
<mainClass>DiscordDownloadBot</mainClass>
|
||||||
|
</transformer>
|
||||||
|
</transformers>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
<properties>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<maven.compiler.source>8</maven.compiler.source>
|
||||||
|
<maven.compiler.target>8</maven.compiler.target>
|
||||||
|
</properties>
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>com.example</groupId>
|
||||||
|
<artifactId>DiscordDownloaderBot</artifactId>
|
||||||
|
<version>1.0</version>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>8</maven.compiler.source>
|
||||||
|
<maven.compiler.target>8</maven.compiler.target>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.dv8tion</groupId>
|
||||||
|
<artifactId>JDA</artifactId>
|
||||||
|
<version>5.0.0-beta.19</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>ch.qos.logback</groupId>
|
||||||
|
<artifactId>logback-classic</artifactId>
|
||||||
|
<version>1.2.13</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-shade-plugin</artifactId>
|
||||||
|
<version>3.5.1</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>shade</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<transformers>
|
||||||
|
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||||
|
<mainClass>DiscordDownloadBot</mainClass> </transformer>
|
||||||
|
</transformers>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,173 @@
|
|||||||
|
import net.dv8tion.jda.api.JDABuilder;
|
||||||
|
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||||
|
import net.dv8tion.jda.api.hooks.ListenerAdapter;
|
||||||
|
import net.dv8tion.jda.api.requests.GatewayIntent;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public class DiscordDownloadBot extends ListenerAdapter {
|
||||||
|
|
||||||
|
// 🔴 [필수] 여기에 디스코드 봇 토큰을 붙여넣으세요
|
||||||
|
private static final String BOT_TOKEN = "MTQ0Nzg1NjI1NTY4NjE0ODEzOA.GpvmOr.KRTrSGh1GrmlF_oHY1brKZbAXb3erfHBlPXHEI";
|
||||||
|
|
||||||
|
// yt-dlp.exe 경로 (프로젝트 최상위 폴더 기준)
|
||||||
|
private static final String YTDLP_PATH = System.getProperty("user.dir") + File.separator + "yt-dlp.exe";
|
||||||
|
|
||||||
|
// 저장할 폴더 (프로젝트 폴더 아래 'Downloads')
|
||||||
|
private static final String DOWNLOAD_DIR = System.getProperty("user.dir") + File.separator + "Downloads";
|
||||||
|
|
||||||
|
// 동시에 3개까지만 다운로드 처리 (서버 부하 방지)
|
||||||
|
private final ExecutorService executor = Executors.newFixedThreadPool(3);
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
// 1. 필수 파일 확인
|
||||||
|
if (!new File(YTDLP_PATH).exists()) {
|
||||||
|
System.err.println("❌ 오류: yt-dlp.exe 파일을 프로젝트 폴더(pom.xml 옆)에 넣어주세요!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
File folder = new File(DOWNLOAD_DIR);
|
||||||
|
if (!folder.exists()) {
|
||||||
|
boolean success = folder.mkdirs(); // 폴더가 없으면 생성
|
||||||
|
if (success) {
|
||||||
|
System.out.println("✅ 다운로드 폴더를 새로 만들었습니다: " + DOWNLOAD_DIR);
|
||||||
|
} else {
|
||||||
|
System.out.println("⚠️ 폴더 생성 실패! 권한을 확인하세요.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 봇 실행
|
||||||
|
try {
|
||||||
|
JDABuilder.createDefault(BOT_TOKEN)
|
||||||
|
.enableIntents(GatewayIntent.MESSAGE_CONTENT) // 메시지 내용 읽기 권한
|
||||||
|
.addEventListeners(new DiscordDownloadBot())
|
||||||
|
.build();
|
||||||
|
System.out.println("🤖 봇 실행 완료! 유튜브/인스타 링크를 감지합니다.");
|
||||||
|
System.out.println("📂 저장 경로: " + DOWNLOAD_DIR);
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("❌ 봇 실행 실패: 토큰이 올바른지 확인하세요.");
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMessageReceived(@NotNull MessageReceivedEvent event) {
|
||||||
|
// 1. 봇 자신의 메시지는 무시
|
||||||
|
if (event.getAuthor().isBot()) return;
|
||||||
|
|
||||||
|
// 2. 메시지 내용 가져오기
|
||||||
|
String message = event.getMessage().getContentRaw();
|
||||||
|
System.out.println("👂 감지된 메시지: " + message);
|
||||||
|
|
||||||
|
// 3. URL 추출 (작성하신 extractUrl 함수 사용)
|
||||||
|
String foundUrl = extractUrl(message);
|
||||||
|
|
||||||
|
// 4. 유효한 링크가 발견되면 다운로드 시작
|
||||||
|
if (foundUrl != null) {
|
||||||
|
System.out.println("✅ 다운로드 대상 링크 발견: " + foundUrl);
|
||||||
|
event.getChannel().sendMessage("📥 영상을 감지했습니다! 다운로드를 시도합니다... (" + foundUrl + ")").queue();
|
||||||
|
|
||||||
|
// 5. [핵심] 별도 스레드에서 다운로드 실행 (봇 멈춤 방지)
|
||||||
|
executor.submit(() -> {
|
||||||
|
processDownload(foundUrl, event);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 유튜브, 인스타, 틱톡 등의 URL만 쏙 뽑아내는 함수
|
||||||
|
private String extractUrl(String text) {
|
||||||
|
// http 또는 https로 시작하는 주소 패턴
|
||||||
|
String regex = "(https?://\\S+)";
|
||||||
|
Pattern pattern = Pattern.compile(regex);
|
||||||
|
Matcher matcher = pattern.matcher(text);
|
||||||
|
|
||||||
|
if (matcher.find()) {
|
||||||
|
String foundUrl = matcher.group(1);
|
||||||
|
// 유튜브, 인스타, 틱톡 주소인지 한번 더 체크 (원하면 이 부분 삭제해서 모든 링크 허용 가능)
|
||||||
|
if (foundUrl.contains("youtube.com") || foundUrl.contains("youtu.be") ||
|
||||||
|
foundUrl.contains("instagram.com") || foundUrl.contains("tiktok.com")) {
|
||||||
|
return foundUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processDownload(String url, MessageReceivedEvent event) {
|
||||||
|
// 진행 상황을 표시할 메시지 객체
|
||||||
|
net.dv8tion.jda.api.entities.Message progressMessage = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 1. 처음에 "다운로드 시작" 메시지를 보내고, 그 메시지 객체를 가져옵니다.
|
||||||
|
progressMessage = event.getChannel().sendMessage("⏳ **다운로드 준비 중...**").complete();
|
||||||
|
|
||||||
|
File dir = new File(DOWNLOAD_DIR);
|
||||||
|
if (!dir.exists()) dir.mkdirs();
|
||||||
|
|
||||||
|
String outputPath = DOWNLOAD_DIR + File.separator + "%(title)s.%(ext)s";
|
||||||
|
|
||||||
|
// 2. 명령어 설정 (--newline 옵션 추가가 핵심!)
|
||||||
|
String[] command = {
|
||||||
|
YTDLP_PATH,
|
||||||
|
"--newline", // 👈 [중요] 진행률을 한 줄씩 출력하게 함
|
||||||
|
"--no-warnings",
|
||||||
|
"-f", "bestvideo+bestaudio/best",
|
||||||
|
"--merge-output-format", "mp4",
|
||||||
|
"-o", outputPath,
|
||||||
|
url
|
||||||
|
};
|
||||||
|
|
||||||
|
ProcessBuilder pb = new ProcessBuilder(command);
|
||||||
|
pb.redirectErrorStream(true); // 에러도 같이 읽음
|
||||||
|
Process process = pb.start();
|
||||||
|
|
||||||
|
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
||||||
|
String line;
|
||||||
|
|
||||||
|
// 시간 체크용 변수 (너무 잦은 업데이트 방지)
|
||||||
|
long lastUpdateTime = 0;
|
||||||
|
|
||||||
|
// 3. yt-dlp의 출력을 한 줄씩 읽음
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
System.out.println("[yt-dlp] " + line); // 콘솔에도 출력
|
||||||
|
|
||||||
|
// "[download] 25.4%" 같은 패턴을 찾음
|
||||||
|
if (line.contains("[download]") && line.contains("%")) {
|
||||||
|
long currentTime = System.currentTimeMillis();
|
||||||
|
|
||||||
|
// 3초(3000ms)가 지났을 때만 디스코드 메시지 수정
|
||||||
|
if (currentTime - lastUpdateTime > 3000) {
|
||||||
|
// 정규식으로 숫자만 쏙 뽑기 (예: 25.4%)
|
||||||
|
Matcher matcher = Pattern.compile("(\\d+\\.\\d+%)").matcher(line);
|
||||||
|
if (matcher.find()) {
|
||||||
|
String percent = matcher.group(1);
|
||||||
|
progressMessage.editMessage("📥 **다운로드 중...** " + percent).queue();
|
||||||
|
lastUpdateTime = currentTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int exitCode = process.waitFor();
|
||||||
|
|
||||||
|
// 4. 완료 시 메시지 수정
|
||||||
|
if (exitCode == 0) {
|
||||||
|
progressMessage.editMessage("✅ **다운로드 완료!**\n(파일은 서버 컴퓨터의 `Downloads` 폴더에 저장되었습니다.)").queue();
|
||||||
|
} else {
|
||||||
|
progressMessage.editMessage("❌ **다운로드 실패.** 콘솔 로그를 확인하세요.").queue();
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
if (progressMessage != null) {
|
||||||
|
progressMessage.editMessage("🔥 **오류 발생:** " + e.getMessage()).queue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
Binary file not shown.
Reference in New Issue
Block a user