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