diff --git a/config.yml.example b/config.yml.example index b39adca..dae2350 100644 --- a/config.yml.example +++ b/config.yml.example @@ -4,6 +4,7 @@ :path: '/var/www/telegabber/content' # webserver workdir :link: 'http://tlgrm.localhost/content' # webserver public address :upload: 'https:///xmppfiles.localhost' # xmpp http upload address + :user: 'www-data' # owner of content files :tdlib_verbosity: 1 :tdlib: :datadir: './sessions/' diff --git a/config/config.go b/config/config.go index 8c7cc9b..e88142f 100644 --- a/config/config.go +++ b/config/config.go @@ -38,6 +38,7 @@ type TelegramContentConfig struct { Path string `yaml:":path"` Link string `yaml:":link"` Upload string `yaml:":upload"` + User string `yaml:":user"` } // TelegramTdlibConfig is for :tdlib: subtree diff --git a/config_schema.json b/config_schema.json index 785bb44..e77b8c3 100644 --- a/config_schema.json +++ b/config_schema.json @@ -21,6 +21,9 @@ }, ":upload": { "type": "string" + }, + ":user": { + "type": "string" } } }, diff --git a/telegram/handlers.go b/telegram/handlers.go index fe4540c..62afd67 100644 --- a/telegram/handlers.go +++ b/telegram/handlers.go @@ -2,8 +2,6 @@ package telegram import ( "fmt" - "os" - "path/filepath" "strconv" "strings" "sync" @@ -107,13 +105,6 @@ func (c *Client) updateHandler() { uhOh() } c.updateDeleteMessages(typedUpdate) - case client.TypeUpdateFile: - typedUpdate, ok := update.(*client.UpdateFile) - if !ok { - uhOh() - } - c.updateFile(typedUpdate) - log.Debugf("%#v", typedUpdate.File) case client.TypeUpdateAuthorizationState: typedUpdate, ok := update.(*client.UpdateAuthorizationState) if !ok { @@ -214,10 +205,10 @@ func (c *Client) updateNewMessage(update *client.UpdateNewMessage) { } } else { text = c.messageToText(update.Message, false) - file, filename := c.contentToFilename(content) + file := c.contentToFile(content) - // download file(s) - if file != nil && !file.Local.IsDownloadingCompleted { + // download file (if one) + if file != nil { newFile, err := c.DownloadFile(file.Id, 1, true) if err == nil { file = newFile @@ -226,7 +217,7 @@ func (c *Client) updateNewMessage(update *client.UpdateNewMessage) { // OTR support (I do not know why would you need it, seriously) if !(strings.HasPrefix(text, "?OTR") || c.Session.RawMessages) { var prefix strings.Builder - prefix.WriteString(c.messageToPrefix(update.Message, c.formatContent(file, filename))) + prefix.WriteString(c.messageToPrefix(update.Message, c.formatFile(file))) if text != "" { // \n if it is groupchat and message is not empty if chatId < 0 { @@ -275,27 +266,6 @@ func (c *Client) updateDeleteMessages(update *client.UpdateDeleteMessages) { } } -// file downloaded -func (c *Client) updateFile(update *client.UpdateFile) { - // not really - if !update.File.Local.IsDownloadingCompleted { - return - } - - err := os.Symlink( - update.File.Local.Path, - c.formatFilePath(c.content.Path, update.File.Remote.UniqueId, filepath.Ext(update.File.Local.Path)), - ) - if err != nil { - linkErr := err.(*os.LinkError) - if linkErr.Err.Error() == "file exists" { - log.Warn(err.Error()) - } else { - log.Errorf("Error creating symlink: %v", err) - } - } -} - func (c *Client) updateAuthorizationState(update *client.UpdateAuthorizationState) { switch update.AuthorizationState.AuthorizationStateType() { case client.TypeAuthorizationStateClosing: diff --git a/telegram/utils.go b/telegram/utils.go index d9773c0..6c16a74 100644 --- a/telegram/utils.go +++ b/telegram/utils.go @@ -2,11 +2,11 @@ package telegram import ( "crypto/sha1" - "crypto/sha256" "fmt" "github.com/pkg/errors" "io" "os" + osUser "os/user" "path/filepath" "regexp" "strconv" @@ -355,21 +355,56 @@ func (c *Client) formatForward(fwd *client.MessageForwardInfo) string { return "Unknown forward type" } -func (c *Client) formatContent(file *client.File, filename string) string { - if file == nil { +func (c *Client) formatFile(file *client.File) string { + if file == nil || file.Local == nil || file.Remote == nil { return "" } - return fmt.Sprintf( - "%s (%v kbytes) | %s", - filename, - file.Size/1024, - c.formatFilePath(c.content.Link, file.Remote.UniqueId, filepath.Ext(file.Local.Path)), - ) -} + var link string + var src string + + if c.content.Path != "" && c.content.Link != "" { + src = file.Local.Path // source path + _, err := os.Stat(src) + if err != nil { + return "" + } -func (c *Client) formatFilePath(basedir string, id string, ext string) string { - return fmt.Sprintf("%s/%x%s", basedir, sha256.Sum256([]byte(id)), ext) + basename := file.Remote.UniqueId + filepath.Ext(src) + dest := c.content.Path + "/" + basename // destination path + link = c.content.Link + "/" + basename // download link + + // move + err = os.Rename(src, dest) + if err != nil { + linkErr := err.(*os.LinkError) + if linkErr.Err.Error() == "file exists" { + log.Warn(err.Error()) + } else { + log.Errorf("File moving error: %v", err) + return "" + } + } + // chown + if c.content.User != "" { + user, err := osUser.Lookup(c.content.User) + if err == nil { + uid, err := strconv.ParseInt(user.Uid, 10, 0) + if err == nil { + err = os.Chown(dest, int(uid), -1) + if err != nil { + log.Errorf("Chown error: %v", err) + } + } else { + log.Errorf("Broken uid: %v", err) + } + } else { + log.Errorf("Wrong user name for chown: %v", err) + } + } + } + + return fmt.Sprintf("%s (%v kbytes) | %s", filepath.Base(src), file.Size/1024, link) } func (c *Client) formatBantime(hours int64) int32 { @@ -573,44 +608,44 @@ func (c *Client) messageToText(message *client.Message, preview bool) string { return fmt.Sprintf("unknown message (%s)", message.Content.MessageContentType()) } -func (c *Client) contentToFilename(content client.MessageContent) (*client.File, string) { +func (c *Client) contentToFile(content client.MessageContent) *client.File { if content == nil { - return nil, "" + return nil } switch content.MessageContentType() { case client.TypeMessageSticker: sticker, _ := content.(*client.MessageSticker) - return sticker.Sticker.Sticker, "sticker.webp" + return sticker.Sticker.Sticker case client.TypeMessageVoiceNote: voice, _ := content.(*client.MessageVoiceNote) - return voice.VoiceNote.Voice, fmt.Sprintf("voice note (%v s.).oga", voice.VoiceNote.Duration) + return voice.VoiceNote.Voice case client.TypeMessageVideoNote: video, _ := content.(*client.MessageVideoNote) - return video.VideoNote.Video, fmt.Sprintf("video note (%v s.).mp4", video.VideoNote.Duration) + return video.VideoNote.Video case client.TypeMessageAnimation: animation, _ := content.(*client.MessageAnimation) - return animation.Animation.Animation, "animation.mp4" + return animation.Animation.Animation case client.TypeMessagePhoto: photo, _ := content.(*client.MessagePhoto) sizes := photo.Photo.Sizes if len(sizes) >= 1 { file := sizes[len(sizes)-1].Photo - return file, strconv.FormatInt(int64(file.Id), 10) + ".jpg" + return file } - return nil, "" + return nil case client.TypeMessageAudio: audio, _ := content.(*client.MessageAudio) - return audio.Audio.Audio, filepath.Base(audio.Audio.Audio.Local.Path) + return audio.Audio.Audio case client.TypeMessageVideo: video, _ := content.(*client.MessageVideo) - return video.Video.Video, filepath.Base(video.Video.Video.Local.Path) + return video.Video.Video case client.TypeMessageDocument: document, _ := content.(*client.MessageDocument) - return document.Document.Document, filepath.Base(document.Document.Document.Local.Path) + return document.Document.Document } - return nil, "" + return nil } func (c *Client) messageToPrefix(message *client.Message, fileString string) string { diff --git a/telegram/utils_test.go b/telegram/utils_test.go index a435ea9..5caf2c5 100644 --- a/telegram/utils_test.go +++ b/telegram/utils_test.go @@ -146,8 +146,8 @@ func TestFormatContent(t *testing.T) { }, } - content := c.formatContent(&file, "a.jpg") - if content != "a.jpg (23 kbytes) | localhvost/b0896d9e9f1de7d2af59b080c3f0947b838c5c6c64f71c68a4b690a15de2ccf8.jpg" { + content := c.formatFile(&file) + if content != ". (23 kbytes) | " { t.Errorf("Wrong file label: %v", content) } } @@ -351,142 +351,6 @@ func TestMessageUnknown(t *testing.T) { } } -func TestContentToFilenameSticker(t *testing.T) { - sticker := client.MessageSticker{ - Sticker: &client.Sticker{}, - } - _, filename := (&Client{}).contentToFilename(&sticker) - if filename != "sticker.webp" { - t.Errorf("Wrong sticker filename") - } -} - -func TestContentToFilenameVoice(t *testing.T) { - voice := client.MessageVoiceNote{ - VoiceNote: &client.VoiceNote{ - Duration: 56, - }, - } - _, filename := (&Client{}).contentToFilename(&voice) - if filename != "voice note (56 s.).oga" { - t.Errorf("Wrong voice note filename") - } -} - -func TestContentToFilenameVideoNote(t *testing.T) { - video := client.MessageVideoNote{ - VideoNote: &client.VideoNote{ - Duration: 56, - }, - } - _, filename := (&Client{}).contentToFilename(&video) - if filename != "video note (56 s.).mp4" { - t.Errorf("Wrong video note filename") - } -} - -func TestContentToFilenameAnimation(t *testing.T) { - animation := client.MessageAnimation{ - Animation: &client.Animation{}, - } - _, filename := (&Client{}).contentToFilename(&animation) - if filename != "animation.mp4" { - t.Errorf("Wrong animation filename") - } -} - -func TestContentToFilenamePhoto(t *testing.T) { - photo := client.MessagePhoto{ - Photo: &client.Photo{ - Sizes: []*client.PhotoSize{ - &client.PhotoSize{ - Photo: &client.File{}, - }, - &client.PhotoSize{ - Photo: &client.File{ - Id: 56, - }, - }, - }, - }, - } - _, filename := (&Client{}).contentToFilename(&photo) - if filename != "56.jpg" { - t.Errorf("Wrong photo filename") - } -} - -func TestContentToFilenamePhotoNoSizes(t *testing.T) { - photo := client.MessagePhoto{ - Photo: &client.Photo{ - Sizes: []*client.PhotoSize{}, - }, - } - _, filename := (&Client{}).contentToFilename(&photo) - if filename != "" { - t.Errorf("Wrong filename of sizeless photo") - } -} - -func TestContentToFilenameAudio(t *testing.T) { - audio := client.MessageAudio{ - Audio: &client.Audio{ - FileName: "swine.mp3", - Audio: &client.File{ - Local: &client.LocalFile{ - Path: "C:/WINNT/swine.mp3", - }, - }, - }, - } - _, filename := (&Client{}).contentToFilename(&audio) - if filename != "swine.mp3" { - t.Errorf("Not oinking, shame on you!") - } -} - -func TestContentToFilenameVideo(t *testing.T) { - video := client.MessageVideo{ - Video: &client.Video{ - FileName: "swine.3gp", - Video: &client.File{ - Local: &client.LocalFile{ - Path: "C:/Document and Settings/Svinarchuk-PC/swine.3gp", - }, - }, - }, - } - _, filename := (&Client{}).contentToFilename(&video) - if filename != "swine.3gp" { - t.Errorf("Not pigdancing, shame on you!") - } -} - -func TestContentToFilenameDocument(t *testing.T) { - document := client.MessageDocument{ - Document: &client.Document{ - FileName: "swine.doc", - Document: &client.File{ - Local: &client.LocalFile{ - Path: "D:/My Documents/swine.doc", - }, - }, - }, - } - _, filename := (&Client{}).contentToFilename(&document) - if filename != "swine.doc" { - t.Errorf("Not hoofstomping, shame on you!") - } -} - -func TestContentToFilenameUnknown(t *testing.T) { - unknown := client.MessageExpiredPhoto{} - _, filename := (&Client{}).contentToFilename(&unknown) - if filename != "" { - t.Errorf("Wrong filename of unknown content") - } -} - func TestMessageToPrefix1(t *testing.T) { message := client.Message{ Id: 42,