-
Notifications
You must be signed in to change notification settings - Fork 1
/
main.go
executable file
·150 lines (125 loc) · 3.32 KB
/
main.go
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
package main
import (
"bufio"
"bytes"
"github.com/joho/godotenv"
tb "gopkg.in/tucnak/telebot.v2"
"log"
"net/url"
"os"
"os/exec"
"path/filepath"
"regexp"
"time"
)
var YOUTUBE_DL = "yt-dlp"
var FFMPEG = "ffmpeg"
func main() {
if !commandExists(YOUTUBE_DL) || !commandExists(FFMPEG) {
log.Fatal("youtube-dl and ffmpeg need to be installed")
return
}
err := os.MkdirAll("download", os.ModePerm)
token := os.Getenv("TELEGRAM_API_TOKEN")
if token == "" {
err = godotenv.Load()
token = os.Getenv("TELEGRAM_API_TOKEN")
if err != nil {
log.Fatal("Error loading .env file")
}
}
bot, err := tb.NewBot(tb.Settings{
Token: token,
Poller: &tb.LongPoller{Timeout: 10 * time.Second},
})
if err != nil {
log.Fatal(err)
return
}
bot.Handle(tb.OnText, func(message *tb.Message) {
handleMessage(bot, message)
})
log.Println("Starting bot")
bot.Start()
}
func handleMessage(bot *tb.Bot, message *tb.Message) {
uri, err := url.Parse(message.Text)
if err != nil {
panic(err)
}
if uri.Host == "www.youtube.com" || uri.Host == "m.youtube.com" || uri.Host == "youtu.be" {
log.Printf("Downloading: %s for %s: %d", uri, message.Sender.FirstName, message.Sender.ID)
cmd := exec.Command(YOUTUBE_DL, "--extract-audio", "--audio-format", "mp3", "-o", "download/%(title)s.%(ext)s", uri.String())
stdout, _ := cmd.StdoutPipe()
cmd.Start()
scanner := bufio.NewScanner(stdout)
scanner.Split(ScanLinesWithCarriageReturn)
filename := ""
filenameFound := false
var response *tb.Message
firstResponse := true
lastUpdate := time.Now()
for scanner.Scan() {
line := scanner.Text()
re := regexp.MustCompile(`\[download] (.*)`)
match := re.FindStringSubmatch(line)
log.Printf(" %s: %s", YOUTUBE_DL, line)
if len(match) > 0 {
if firstResponse {
response, _ = bot.Reply(message, match[1])
firstResponse = false
} else {
if time.Now().After(lastUpdate.Add(time.Millisecond * 50)) {
response, _ = bot.Edit(response, match[1])
lastUpdate = time.Now()
}
}
}
if !filenameFound {
re := regexp.MustCompile(`\[ExtractAudio] Destination: (.*).mp3`)
match := re.FindStringSubmatch(line)
if len(match) > 0 {
filename = match[1] + ".mp3"
filenameFound = true
}
}
}
cmd.Wait()
if _, err := os.Stat(filename); err == nil {
mp3 := &tb.Audio{File: tb.FromDisk(filename), FileName: filepath.Base(filename)}
bot.Delete(response)
bot.Reply(message, mp3)
}
} else {
bot.Reply(message, "Host: "+uri.Host+" is not a youtube site")
}
}
func ScanLinesWithCarriageReturn(data []byte, atEOF bool) (advance int, token []byte, err error) {
if atEOF && len(data) == 0 {
return 0, nil, nil
}
if i := bytes.IndexByte(data, '\r'); i >= 0 {
// We have a full newline-terminated line.
return i + 1, dropCR(data[0:i]), nil
}
if i := bytes.IndexByte(data, '\n'); i >= 0 {
// We have a full newline-terminated line.
return i + 1, dropCR(data[0:i]), nil
}
// If we're at EOF, we have a final, non-terminated line. Return it.
if atEOF {
return len(data), dropCR(data), nil
}
// Request more data.
return 0, nil, nil
}
func dropCR(data []byte) []byte {
if len(data) > 0 && data[len(data)-1] == '\r' {
return data[0 : len(data)-1]
}
return data
}
func commandExists(cmd string) bool {
_, err := exec.LookPath(cmd)
return err == nil
}