fix: issues
This commit is contained in:
4
.vscode/launch.json
vendored
4
.vscode/launch.json
vendored
@@ -11,7 +11,9 @@
|
|||||||
"mode": "auto",
|
"mode": "auto",
|
||||||
"program": "${workspaceFolder}/main.go",
|
"program": "${workspaceFolder}/main.go",
|
||||||
"args": [
|
"args": [
|
||||||
"login"
|
"export",
|
||||||
|
"--alias",
|
||||||
|
"yunpanshare"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
33
internal/channel_message.go
Normal file
33
internal/channel_message.go
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
package internal
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
type ChannelMessage struct {
|
||||||
|
ID int
|
||||||
|
Message string
|
||||||
|
Medias []ChannelMessageMedia
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChannelMessageMedia struct {
|
||||||
|
Photo string
|
||||||
|
Video string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewChannelMessage(id int) *ChannelMessage {
|
||||||
|
return &ChannelMessage{ID: id}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ChannelMessage) WithMessage(message string) *ChannelMessage {
|
||||||
|
c.Message = message
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ChannelMessage) WithPhoto(assetID int64, ext string) *ChannelMessage {
|
||||||
|
c.Medias = append(c.Medias, ChannelMessageMedia{Photo: fmt.Sprintf("%d.%s", assetID, ext)})
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ChannelMessage) WithVideo(video string) *ChannelMessage {
|
||||||
|
c.Medias = append(c.Medias, ChannelMessageMedia{Video: video})
|
||||||
|
return c
|
||||||
|
}
|
||||||
@@ -20,16 +20,10 @@ type TClient struct {
|
|||||||
logger *zap.Logger
|
logger *zap.Logger
|
||||||
api *tg.Client
|
api *tg.Client
|
||||||
|
|
||||||
downloadMedia bool
|
|
||||||
|
|
||||||
waitLogin chan error
|
waitLogin chan error
|
||||||
block chan struct{}
|
block chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// func (t *TClient) Close() {
|
|
||||||
// t.block <- struct{}{}
|
|
||||||
// }
|
|
||||||
|
|
||||||
func NewClient(logger *zap.Logger, config *config.Config) *TClient {
|
func NewClient(logger *zap.Logger, config *config.Config) *TClient {
|
||||||
c := &TClient{
|
c := &TClient{
|
||||||
Config: config,
|
Config: config,
|
||||||
@@ -46,11 +40,6 @@ func NewClient(logger *zap.Logger, config *config.Config) *TClient {
|
|||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TClient) WithMedia() *TClient {
|
|
||||||
t.downloadMedia = true
|
|
||||||
return t
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *TClient) Login(ctx context.Context) error {
|
func (t *TClient) Login(ctx context.Context) error {
|
||||||
t.logger.Info("login phone", zap.String("phone", t.Config.Phone))
|
t.logger.Info("login phone", zap.String("phone", t.Config.Phone))
|
||||||
flow := auth.NewFlow(Terminal{PhoneNumber: client.Config.Phone}, auth.SendCodeOptions{})
|
flow := auth.NewFlow(Terminal{PhoneNumber: client.Config.Phone}, auth.SendCodeOptions{})
|
||||||
|
|||||||
@@ -2,24 +2,29 @@ package internal
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"github.com/gotd/td/telegram/downloader"
|
"github.com/gotd/td/telegram/downloader"
|
||||||
"github.com/gotd/td/tg"
|
"github.com/gotd/td/tg"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (t *TClient) Channel(ctx context.Context, channel *tg.Channel, offset int) error {
|
func (t *TClient) Channel(ctx context.Context, channel *tg.Channel, cfg *ChannelConfig, modeHistory bool) error {
|
||||||
inputPeer := &tg.InputPeerChannel{ChannelID: channel.ID, AccessHash: channel.AccessHash}
|
inputPeer := &tg.InputPeerChannel{ChannelID: channel.ID, AccessHash: channel.AccessHash}
|
||||||
messages, err := t.api.MessagesGetHistory(ctx, &tg.MessagesGetHistoryRequest{
|
|
||||||
Peer: inputPeer,
|
request := &tg.MessagesGetHistoryRequest{
|
||||||
Limit: 10,
|
Peer: inputPeer,
|
||||||
OffsetID: offset,
|
Limit: 1,
|
||||||
})
|
}
|
||||||
|
|
||||||
|
if modeHistory { // 提供此ID供遍历历史消息
|
||||||
|
request.OffsetID = cfg.Offset
|
||||||
|
} else {
|
||||||
|
request.MinID = cfg.MinID // 提供此ID供新增加的消息
|
||||||
|
}
|
||||||
|
|
||||||
|
messages, err := t.Client.API().MessagesGetHistory(ctx, request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "messages.getHistory")
|
return errors.Wrap(err, "messages.getHistory")
|
||||||
}
|
}
|
||||||
@@ -28,13 +33,19 @@ func (t *TClient) Channel(ctx context.Context, channel *tg.Channel, offset int)
|
|||||||
lo.ForEach(messages.(*tg.MessagesChannelMessages).GetMessages(), func(item tg.MessageClass, index int) {
|
lo.ForEach(messages.(*tg.MessagesChannelMessages).GetMessages(), func(item tg.MessageClass, index int) {
|
||||||
msg, ok := item.(*tg.Message)
|
msg, ok := item.(*tg.Message)
|
||||||
if !ok {
|
if !ok {
|
||||||
fmt.Println("ID: get failed")
|
t.logger.Error("convert msg to *tg.Message failed")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
defer func() {
|
||||||
|
if err := cfg.Update(ctx, msg.ID); err != nil {
|
||||||
|
t.logger.Error("update config failed", zap.Error(err))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
if !t.downloadMedia {
|
channelMessage := NewChannelMessage(msg.ID)
|
||||||
return
|
defer cfg.SaveMessage(channelMessage)
|
||||||
}
|
|
||||||
|
channelMessage.WithMessage(msg.GetMessage())
|
||||||
|
|
||||||
if mediaClass, ok := msg.GetMedia(); ok {
|
if mediaClass, ok := msg.GetMedia(); ok {
|
||||||
if photoClass, ok := mediaClass.(*tg.MessageMediaPhoto).GetPhoto(); ok {
|
if photoClass, ok := mediaClass.(*tg.MessageMediaPhoto).GetPhoto(); ok {
|
||||||
@@ -52,47 +63,17 @@ func (t *TClient) Channel(ctx context.Context, channel *tg.Channel, offset int)
|
|||||||
ThumbSize: thumbSize,
|
ThumbSize: thumbSize,
|
||||||
}
|
}
|
||||||
|
|
||||||
saveTo := lo.Must(filepath.Abs(fmt.Sprintf("./photos/%d.jpg", photo.GetID())))
|
saveTo := cfg.Asset(photo.GetID(), "jpg")
|
||||||
storage, err := downloader.Download(t.api, location).ToPath(ctx, saveTo)
|
_, err := downloader.Download(t.Client.API(), location).ToPath(ctx, saveTo)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
t.logger.Error("download failed", zap.Error(err))
|
||||||
} else {
|
return
|
||||||
fmt.Println("Downloaded : ", storage)
|
|
||||||
}
|
}
|
||||||
|
channelMessage.WithPhoto(photo.GetID(), "jpg")
|
||||||
|
t.logger.Info("download failed", zap.String("asset", saveTo))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type ChannelConfig struct {
|
|
||||||
ID int64 `json:"id"`
|
|
||||||
Offset int `json:"offset"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *TClient) SaveChannelConfig(ctx context.Context, channelID int64, offset int) (*ChannelConfig, error) {
|
|
||||||
channelConfigFile := fmt.Sprintf("outputs/%d/config.json", channelID)
|
|
||||||
// if file not exists then create it
|
|
||||||
if _, err := os.Stat(channelConfigFile); os.IsNotExist(err) {
|
|
||||||
// create config file
|
|
||||||
data, _ := json.Marshal(&ChannelConfig{ID: channelID})
|
|
||||||
if err := os.WriteFile(channelConfigFile, data, 0o644); err != nil {
|
|
||||||
return nil, errors.Wrap(err, "write channel config")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// read config file
|
|
||||||
data, err := os.ReadFile(channelConfigFile)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "read channel config")
|
|
||||||
}
|
|
||||||
|
|
||||||
var config *ChannelConfig
|
|
||||||
if err := json.Unmarshal(data, config); err != nil {
|
|
||||||
return nil, errors.Wrap(err, "unmarshal channel config")
|
|
||||||
}
|
|
||||||
|
|
||||||
return config, nil
|
|
||||||
}
|
|
||||||
|
|||||||
116
internal/client_channel_config.go
Normal file
116
internal/client_channel_config.go
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
package internal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ChannelConfig struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
Offset int `json:"offset"`
|
||||||
|
MinID int `json:"min_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewChannelConfig(channelID int64) *ChannelConfig {
|
||||||
|
return &ChannelConfig{ID: channelID}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ChannelConfig) Asset(assetID int64, ext string) string {
|
||||||
|
assetFile := fmt.Sprintf("outputs/%d/assets/%d.%s", c.ID, assetID, ext)
|
||||||
|
|
||||||
|
// if file dir not exists then create it
|
||||||
|
if _, err := os.Stat(filepath.Dir(assetFile)); os.IsNotExist(err) {
|
||||||
|
if err := os.MkdirAll(filepath.Dir(assetFile), 0o755); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if file exists then delete it
|
||||||
|
if _, err := os.Stat(assetFile); err == nil {
|
||||||
|
if err := os.Remove(assetFile); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return assetFile
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ChannelConfig) file(_ context.Context) (string, error) {
|
||||||
|
channelConfigFile := fmt.Sprintf("outputs/%d/config.json", c.ID)
|
||||||
|
|
||||||
|
// if file dir not exists then create it
|
||||||
|
if _, err := os.Stat(filepath.Dir(channelConfigFile)); os.IsNotExist(err) {
|
||||||
|
if err := os.MkdirAll(filepath.Dir(channelConfigFile), 0o755); err != nil {
|
||||||
|
return "", errors.Wrap(err, "create channel config dir")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if file not exists then create it
|
||||||
|
if _, err := os.Stat(channelConfigFile); os.IsNotExist(err) {
|
||||||
|
// create config file
|
||||||
|
data, _ := json.Marshal(&ChannelConfig{ID: channelID})
|
||||||
|
if err := os.WriteFile(channelConfigFile, data, 0o644); err != nil {
|
||||||
|
return "", errors.Wrap(err, "write channel config")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return channelConfigFile, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ChannelConfig) Read(ctx context.Context) error {
|
||||||
|
channelConfigFile, err := c.file(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// read config file
|
||||||
|
data, err := os.ReadFile(channelConfigFile)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "read channel config")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.Unmarshal(data, &c); err != nil {
|
||||||
|
return errors.Wrap(err, "unmarshal channel config")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ChannelConfig) Update(ctx context.Context, offsetID int) error {
|
||||||
|
channelConfigFile, err := c.file(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Offset = offsetID
|
||||||
|
|
||||||
|
if c.MinID < offsetID {
|
||||||
|
c.MinID = offsetID
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := json.Marshal(c)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "marshal channel config")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.WriteFile(channelConfigFile, b, 0o644); err != nil {
|
||||||
|
return errors.Wrap(err, "write channel config")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ChannelConfig) SaveMessage(msg *ChannelMessage) {
|
||||||
|
// save message
|
||||||
|
saveTo := fmt.Sprintf("outputs/%d/%d.json", c.ID, msg.ID)
|
||||||
|
b, err := json.Marshal(msg)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if err := os.WriteFile(saveTo, b, 0o644); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,10 +9,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
channelID int64
|
channelID int64
|
||||||
offsetID int
|
modeHistory bool
|
||||||
channelAlias string
|
channelAlias string
|
||||||
downloadMedia bool
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func ExportCmd() *cobra.Command {
|
func ExportCmd() *cobra.Command {
|
||||||
@@ -23,9 +22,8 @@ func ExportCmd() *cobra.Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cmd.Flags().Int64Var(&channelID, "channel", 0, "channel id")
|
cmd.Flags().Int64Var(&channelID, "channel", 0, "channel id")
|
||||||
cmd.Flags().IntVar(&offsetID, "offset", 0, "offset id")
|
cmd.Flags().BoolVar(&modeHistory, "history", false, "history mode")
|
||||||
cmd.Flags().StringVar(&channelAlias, "alias", "", "channel alias")
|
cmd.Flags().StringVar(&channelAlias, "alias", "", "channel alias")
|
||||||
cmd.Flags().BoolVar(&downloadMedia, "media", false, "download media")
|
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
@@ -49,15 +47,11 @@ func exportCmd(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if downloadMedia {
|
cfg := NewChannelConfig(channel.ID)
|
||||||
client.WithMedia()
|
if err := cfg.Read(ctx); err != nil {
|
||||||
}
|
|
||||||
|
|
||||||
cfg, err := client.SaveChannelConfig(ctx, channel.ID, offsetID)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
|
|
||||||
return client.Channel(ctx, channel, cfg.Offset)
|
// https://t.me/yunpanshare/37426
|
||||||
|
return client.Channel(ctx, channel, cfg, modeHistory)
|
||||||
}
|
}
|
||||||
|
|||||||
8
main.go
8
main.go
@@ -39,6 +39,14 @@ func main() {
|
|||||||
internal.ExportCmd(),
|
internal.ExportCmd(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// rootCmd.SilenceErrors = true
|
||||||
|
rootCmd.SilenceUsage = true
|
||||||
|
rootCmd.SetFlagErrorFunc(func(cmd *cobra.Command, err error) error {
|
||||||
|
cmd.Println(err)
|
||||||
|
cmd.Println(cmd.UsageString())
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
|
||||||
err := rootCmd.Execute()
|
err := rootCmd.Execute()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|||||||
2
outputs/.gitignore
vendored
Normal file
2
outputs/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
*
|
||||||
|
!.gitignore
|
||||||
Reference in New Issue
Block a user