-
-
Notifications
You must be signed in to change notification settings - Fork 26
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Publish to telegram #15
Changes from all commits
d938e0f
908cda8
3ae8267
d8fb331
c741b89
ee439da
635a674
80ca198
be4eeb7
117ef40
bec8544
010a331
d3d0adb
b8312f0
69f08f9
2b98130
336fa15
c5a94a5
c2f5de1
727df45
395831a
ec63f0e
588a63f
e27a045
7193518
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,186 @@ | ||
package proc | ||
|
||
import ( | ||
"fmt" | ||
"io" | ||
"net/http" | ||
"path" | ||
"strings" | ||
"time" | ||
|
||
log "github.com/go-pkgz/lgr" | ||
"github.com/microcosm-cc/bluemonday" | ||
tb "gopkg.in/tucnak/telebot.v2" | ||
|
||
"github.com/umputun/feed-master/app/feed" | ||
) | ||
|
||
const ( | ||
maxTelegramFileSize = 50_000_000 | ||
) | ||
|
||
// TelegramClient client | ||
type TelegramClient struct { | ||
Bot *tb.Bot | ||
Timeout time.Duration | ||
} | ||
|
||
// NewTelegramClient init telegram client | ||
func NewTelegramClient(token string, timeout time.Duration) (*TelegramClient, error) { | ||
if timeout == 0 { | ||
timeout = time.Duration(60 * 10) | ||
sgaynetdinov marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
if token == "" { | ||
return &TelegramClient{ | ||
Bot: nil, | ||
Timeout: timeout, | ||
}, nil | ||
} | ||
|
||
bot, err := tb.NewBot(tb.Settings{ | ||
Token: token, | ||
Client: &http.Client{Timeout: timeout * time.Second}, | ||
}) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
result := TelegramClient{ | ||
Bot: bot, | ||
Timeout: timeout, | ||
} | ||
return &result, err | ||
} | ||
|
||
// Send message, skip if telegram token empty | ||
func (client TelegramClient) Send(channelID string, item feed.Item) error { | ||
if client.Bot == nil { | ||
return nil | ||
} | ||
|
||
if channelID == "" { | ||
return nil | ||
} | ||
|
||
contentLength, err := getContentLength(item.Enclosure.URL) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
var message *tb.Message | ||
|
||
if contentLength < maxTelegramFileSize { | ||
message, err = client.sendAudio(channelID, item) | ||
} else { | ||
message, err = client.sendText(channelID, item) | ||
} | ||
|
||
if err != nil { | ||
return err | ||
} | ||
|
||
log.Printf("[DEBUG] send telegram message: \n%s", message.Text) | ||
return err | ||
} | ||
|
||
func getContentLength(url string) (int64, error) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @umputun how mocking http response? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. see httptest. You can run test server and provide whatever response you need There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is an example of httptest.Server use in real life, from one of my projects https://github.com/go-pkgz/rest/blob/master/httperrors_test.go#L16 |
||
resp, err := http.Head(url) //nolint:gosec | ||
if err != nil { | ||
return 0, err | ||
} | ||
|
||
defer resp.Body.Close() | ||
|
||
if resp.StatusCode != 200 { | ||
return 0, fmt.Errorf("resp.StatusCode: %d, not equal 200", resp.StatusCode) | ||
} | ||
|
||
log.Printf("[DEBUG] Content-Length: %d, url: %s", resp.ContentLength, url) | ||
return resp.ContentLength, err | ||
} | ||
|
||
func (client TelegramClient) sendText(channelID string, item feed.Item) (*tb.Message, error) { | ||
message, err := client.Bot.Send( | ||
recipient{chatID: channelID}, | ||
client.getMessageHTML(item), | ||
tb.ModeHTML, | ||
tb.NoPreview, | ||
) | ||
|
||
return message, err | ||
} | ||
|
||
func (client TelegramClient) sendAudio(channelID string, item feed.Item) (*tb.Message, error) { | ||
httpBody, err := client.downloadAudio(item.Enclosure.URL) | ||
defer httpBody.Close() //nolint:staticcheck | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
audio := tb.Audio{ | ||
File: tb.FromReader(httpBody), | ||
FileName: client.getFilenameByURL(item.Enclosure.URL), | ||
MIME: "audio/mpeg", | ||
Caption: client.getMessageHTML(item), | ||
Title: item.Title, | ||
} | ||
|
||
message, err := audio.Send( | ||
client.Bot, | ||
recipient{chatID: channelID}, | ||
&tb.SendOptions{ | ||
ParseMode: tb.ModeHTML, | ||
}, | ||
) | ||
|
||
return message, err | ||
} | ||
|
||
func (client TelegramClient) downloadAudio(url string) (io.ReadCloser, error) { | ||
clientHTTP := &http.Client{Timeout: client.Timeout * time.Second} | ||
|
||
resp, err := clientHTTP.Get(url) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
log.Printf("[DEBUG] start download audio: %s", url) | ||
|
||
return resp.Body, err | ||
} | ||
|
||
// https://core.telegram.org/bots/api#html-style | ||
func (client TelegramClient) tagLinkOnlySupport(html string) string { | ||
p := bluemonday.NewPolicy() | ||
p.AllowAttrs("href").OnElements("a") | ||
return p.Sanitize(html) | ||
} | ||
|
||
func (client TelegramClient) getMessageHTML(item feed.Item) string { | ||
title := strings.TrimSpace(item.Title) | ||
|
||
description := client.tagLinkOnlySupport(string(item.Description)) | ||
description = strings.TrimSpace(description) | ||
|
||
messageHTML := fmt.Sprintf("%s\n\n%s\n\n%s", title, description, item.Enclosure.URL) | ||
|
||
return messageHTML | ||
} | ||
|
||
func (client TelegramClient) getFilenameByURL(url string) string { | ||
_, filename := path.Split(url) | ||
return filename | ||
} | ||
|
||
type recipient struct { | ||
chatID string | ||
} | ||
|
||
func (r recipient) Recipient() string { | ||
if !strings.HasPrefix(r.chatID, "@") { | ||
return "@" + r.chatID | ||
} | ||
|
||
return r.chatID | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
when starting without
TELEGRAM_TOKEN
there will be an error,There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is not right, telegram support should be optional
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for "need to register this env in docker-compose?" - I think you are asking if it should be passed via docker-compose? Yes, if a user needs to set (or pass)
TELEGRAM_TOKEN
it has to be defined/declared in the composeThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed