Skip to content

Commit 94d7203

Browse files
committed
2 parents 8ccfeb9 + 1d27e33 commit 94d7203

14 files changed

Lines changed: 1757 additions & 791 deletions

File tree

examples/download/main.go

Lines changed: 85 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
package main
22

33
import (
4+
"bytes"
5+
"fmt"
6+
"os"
7+
48
"github.com/amarnathcjd/gogram/telegram"
59
)
610

@@ -12,32 +16,101 @@ const (
1216

1317
func main() {
1418
client, _ := telegram.NewClient(telegram.ClientConfig{
15-
AppID: appID,
16-
AppHash: appHash,
17-
LogLevel: telegram.LogInfo,
19+
AppID: appID,
20+
AppHash: appHash,
1821
})
19-
2022
client.LoginBot(botToken)
2123

22-
client.On(telegram.OnMessage, func(message *telegram.NewMessage) error {
23-
if !message.IsMedia() {
24+
// Download to disk (default)
25+
// File is saved to the current directory using the server-provided filename.
26+
// Workers write directly to the file in parallel — no intermediate buffer.
27+
client.On("message:/dl", func(m *telegram.NewMessage) error {
28+
if !m.IsMedia() {
2429
return nil
2530
}
2631

27-
pm := telegram.NewProgressManager(5).SetMessage(message)
32+
path, err := m.Download(&telegram.DownloadOptions{
33+
FileName: "downloads/output.mp4", // optional: override save path
34+
ProgressCallback: logProgress,
35+
ProgressInterval: 3,
36+
})
37+
if err != nil {
38+
return err
39+
}
40+
m.Reply("saved to: " + path)
41+
return nil
42+
})
2843

29-
_, err := message.Download(&telegram.DownloadOptions{
30-
ProgressManager: pm,
31-
// Timeout: 2 * time.Minute,
44+
// Download directly into an *os.File (io.WriterAt, fastest)
45+
// *os.File implements io.WriterAt so chunks are written in parallel with zero
46+
// intermediate allocation — you control the file handle yourself.
47+
client.On("message:/dl_file", func(m *telegram.NewMessage) error {
48+
if !m.IsMedia() {
49+
return nil
50+
}
51+
52+
f, err := os.CreateTemp("", "tg-*.mp4")
53+
if err != nil {
54+
return err
55+
}
56+
defer f.Close()
57+
58+
_, err = m.Download(&telegram.DownloadOptions{
59+
Buffer: f, // *os.File satisfies io.WriterAt — no intermediate buffer
60+
ProgressCallback: logProgress,
61+
})
62+
if err != nil {
63+
return err
64+
}
65+
m.Reply("written to temp file: " + f.Name())
66+
return nil
67+
})
68+
69+
// Download into a bytes.Buffer (io.Writer, in-memory)
70+
// Chunks are assembled in an internal []byte then copied to the buffer at the end.
71+
// Useful for small files you want to process in-memory without touching disk.
72+
// Note: allocates the full file size in RAM — avoid for large files.
73+
client.On("message:/dl_mem", func(m *telegram.NewMessage) error {
74+
if !m.IsMedia() {
75+
return nil
76+
}
77+
78+
var buf bytes.Buffer
79+
_, err := m.Download(&telegram.DownloadOptions{
80+
Buffer: &buf, // *bytes.Buffer satisfies io.Writer
3281
})
3382
if err != nil {
34-
message.Reply("download error: " + err.Error())
3583
return err
3684
}
85+
m.Reply(fmt.Sprintf("downloaded %d bytes into memory", buf.Len()))
86+
return nil
87+
})
3788

38-
message.Reply("download finished")
89+
// Download with ProgressManager (edits a live message)
90+
client.On(telegram.OnMessage, func(m *telegram.NewMessage) error {
91+
if !m.IsMedia() {
92+
return nil
93+
}
94+
95+
pm := telegram.NewProgressManager(5).SetMessage(m)
96+
_, err := m.Download(&telegram.DownloadOptions{
97+
ProgressManager: pm,
98+
})
99+
if err != nil {
100+
m.Reply("download error: " + err.Error())
101+
return err
102+
}
103+
m.Reply("done!")
39104
return nil
40105
}, telegram.IsPrivate)
41106

42107
client.Idle()
43108
}
109+
110+
func logProgress(p *telegram.ProgressInfo) {
111+
fmt.Printf("\r%s %.1f%% %.2f MB/s ETA %.0fs",
112+
p.FileName, p.Percentage, p.CurrentSpeed/1024/1024, p.ETA)
113+
if p.Percentage >= 100 {
114+
fmt.Println()
115+
}
116+
}

examples/progress/upload.go

Lines changed: 105 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,124 @@
11
package main
22

3-
// Youtube DL Bot Example;
4-
// https://gist.github.com/AmarnathCJD/bfceefe9efd1a079ab151da54ef3bba2
5-
63
import (
4+
"bytes"
75
"fmt"
6+
"io"
7+
"os"
8+
"strings"
89

910
"github.com/amarnathcjd/gogram/telegram"
1011
)
1112

1213
const (
1314
appID = 6
14-
apiKey = ""
15-
botToken = ""
15+
appHash = "YOUR_APP_HASH"
16+
botToken = "YOUR_BOT_TOKEN"
17+
chatID = "YOUR_CHAT_ID"
1618
)
1719

1820
func main() {
19-
// create a new client object
2021
client, _ := telegram.NewClient(telegram.ClientConfig{
2122
AppID: appID,
22-
AppHash: apiKey,
23+
AppHash: appHash,
2324
})
24-
2525
client.LoginBot(botToken)
2626

27-
chat, _ := client.ResolvePeer("chatId")
28-
m, _ := client.SendMessage(chat, "Starting File Upload...")
29-
30-
client.SendMedia(chat, "<file-name>", &telegram.MediaOptions{
31-
Upload: &telegram.UploadOptions{
32-
ProgressCallback: func(pi *telegram.ProgressInfo) {
33-
m.Edit(fmt.Sprintf("Uploading... %.2f%% complete (%.2f MB/s), ETA: %.2f seconds",
34-
pi.Percentage,
35-
pi.CurrentSpeed/1024/1024,
36-
pi.ETA,
37-
))
38-
},
39-
ProgressInterval: 5, // update every 5 seconds
40-
},
41-
})
42-
43-
// to use custom progress manager
44-
// pm := telegram.NewProgressManager(5)
45-
// pm.EditFunc(MediaDownloadProgress("<file-name>", m, pm))
46-
// client.SendMedia(chat, "<file-name>", &telegram.MediaOptions{
47-
// ProgressManager: pm,
48-
// })
49-
50-
// same goes for download
51-
// &DownloadOptions{ProgressManager: NewProgressManager(5).SetMessage(m)}
52-
// or
53-
// &DownloadOptions{ProgressCallback: func(pi *ProgressInfo) {
54-
// m.Edit(fmt.Sprintf("Downloading... %.2f%% complete (%.2f MB/s), ETA: %.2f seconds",
55-
// pi.Percentage,
56-
// pi.CurrentSpeed/1024/1024,
57-
// pi.ETA,
58-
// ))
59-
// }}
60-
// &SendOptions{ProgressManager: ...}
61-
// &MediaOptions{ProgressManager: ...}
27+
chat, _ := client.ResolvePeer(chatID)
28+
29+
// Upload from a file path (string)
30+
// Parallel upload — file is read at random offsets by multiple workers.
31+
client.SendMedia(chat, "video.mp4", &telegram.MediaOptions{
32+
Upload: &telegram.UploadOptions{
33+
ProgressCallback: logProgress,
34+
ProgressInterval: 3,
35+
},
36+
})
37+
38+
// Upload from *os.File
39+
// Also parallel — *os.File satisfies io.ReaderAt.
40+
f, _ := os.Open("photo.jpg")
41+
defer f.Close()
42+
client.SendMedia(chat, f, &telegram.MediaOptions{
43+
Upload: &telegram.UploadOptions{
44+
ProgressCallback: logProgress,
45+
},
46+
})
47+
48+
// Upload from []byte
49+
// Parallel — bytes.NewReader wraps the slice, supports ReaderAt.
50+
data, _ := os.ReadFile("document.pdf")
51+
client.SendMedia(chat, data, &telegram.MediaOptions{
52+
Upload: &telegram.UploadOptions{
53+
FileName: "document.pdf", // required: no filename is inferred from []byte
54+
},
55+
})
56+
57+
// Upload from *bytes.Reader (parallel)
58+
// bytes.Reader implements io.ReaderAt so uploads are parallelised.
59+
br := bytes.NewReader(data)
60+
client.SendMedia(chat, br, &telegram.MediaOptions{
61+
Upload: &telegram.UploadOptions{
62+
FileName: "document.pdf",
63+
ProgressCallback: logProgress,
64+
},
65+
})
66+
67+
// Upload from io.ReadSeeker (sequential, size known)
68+
// ReadSeeker is NOT safe for concurrent reads, so gogram uses a single worker.
69+
// The seek is used upfront to measure the file size for accurate progress.
70+
var rs io.ReadSeeker = strings.NewReader("hello world")
71+
client.SendMedia(chat, rs, &telegram.MediaOptions{
72+
Upload: &telegram.UploadOptions{
73+
FileName: "hello.txt",
74+
},
75+
})
76+
77+
// Upload from io.Reader (sequential, size unknown)
78+
// No parallelism, no progress percentage (size is unknown).
79+
// Use for streaming sources like network responses.
80+
var r io.Reader = strings.NewReader("streamed content")
81+
client.SendMedia(chat, r, &telegram.MediaOptions{
82+
Upload: &telegram.UploadOptions{
83+
FileName: "stream.txt",
84+
},
85+
})
86+
87+
// Upload from ReaderAtSource (parallel, custom io.ReaderAt)
88+
// Use this when you have a custom source that supports random-access reads,
89+
// e.g. a memory-mapped file, a cloud storage reader, etc.
90+
// You must supply the size explicitly since there is no way to infer it.
91+
rawData := []byte("binary payload")
92+
src := &telegram.ReaderAtSource{
93+
Reader: bytes.NewReader(rawData),
94+
Size: int64(len(rawData)),
95+
Name: "payload.bin",
96+
}
97+
client.SendMedia(chat, src, &telegram.MediaOptions{
98+
Upload: &telegram.UploadOptions{
99+
ProgressCallback: logProgress,
100+
},
101+
})
102+
103+
// Upload with ProgressManager (edits a live message)
104+
status, _ := client.SendMessage(chat, "Uploading...")
105+
pm := telegram.NewProgressManager(5).
106+
WithCallback(func(pi *telegram.ProgressInfo) {
107+
status.Edit(fmt.Sprintf("Uploading... %.1f%% (%.2f MB/s) ETA %.0fs",
108+
pi.Percentage, pi.CurrentSpeed/1024/1024, pi.ETA))
109+
})
110+
client.SendMedia(chat, "bigfile.zip", &telegram.MediaOptions{
111+
Upload: &telegram.UploadOptions{
112+
ProgressManager: pm,
113+
},
114+
})
115+
status.Edit("Upload complete!")
116+
}
117+
118+
func logProgress(p *telegram.ProgressInfo) {
119+
fmt.Printf("\r%s %.1f%% %.2f MB/s ETA %.0fs",
120+
p.FileName, p.Percentage, p.CurrentSpeed/1024/1024, p.ETA)
121+
if p.Percentage >= 100 {
122+
fmt.Println()
123+
}
62124
}

internal/cmd/tlgen/webui.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,9 @@ func startWebUI() {
9292
http.HandleFunc("/api/missing-type/response", handleMissingTypeResponse)
9393

9494
port := ":8080"
95+
if len(os.Args) > 1 {
96+
port = ":" + os.Args[1]
97+
}
9598
log.Printf("INFO: Starting web UI on http://localhost%s\n", port)
9699
fmt.Printf("Open http://localhost%s in your browser\n", port)
97100

0 commit comments

Comments
 (0)