2016-11-15 22:15:57 +00:00
package btelegram
import (
2022-03-25 20:58:52 +00:00
"fmt"
2018-06-18 21:38:52 +00:00
"html"
Support Telegram animated stickers (tgs) format (#1173)
This is half a fix for #874
This patch introduces a new config flag:
- MediaConvertTgs
These need to be treated independently from the existing
MediaConvertWebPToPNG flag because Tgs→WebP results in an
*animated* WebP, and the WebP→PNG converter can't handle
animated WebP files yet.
Furthermore, some platforms (like discord) don't even support
animated WebP files, so the user may want to fall back to
static PNGs (not APNGs).
The final reason why this is only half a fix is that this
introduces an external dependency, namely lottie, to be
installed like this:
$ pip3 install lottie cairosvg
This patch works by writing the tgs to a temporary file in /tmp,
calling lottie to convert it (this conversion may take several seconds!),
and then deleting the temporary file.
The temporary file is absolutely necessary, as lottie refuses to
work on non-seekable files.
If anyone comes up with a reasonable use case where /tmp is
unavailable, I can add yet another config option for that, if desired.
Telegram will bail out if the option is configured but lottie isn't found.
2020-08-23 20:34:28 +00:00
"log"
2017-01-06 22:32:17 +00:00
"strconv"
2017-11-12 21:04:35 +00:00
"strings"
2017-01-06 22:32:17 +00:00
2018-02-26 23:33:21 +00:00
"github.com/42wim/matterbridge/bridge"
2016-11-15 22:15:57 +00:00
"github.com/42wim/matterbridge/bridge/config"
2017-11-04 13:50:01 +00:00
"github.com/42wim/matterbridge/bridge/helper"
2023-03-14 22:03:04 +00:00
tgbotapi "github.com/matterbridge/telegram-bot-api/v6"
2016-11-15 22:15:57 +00:00
)
2018-11-08 21:20:03 +00:00
const (
unknownUser = "unknown"
HTMLFormat = "HTML"
HTMLNick = "htmlnick"
2020-07-12 20:40:22 +00:00
MarkdownV2 = "MarkdownV2"
2018-11-08 21:20:03 +00:00
)
2016-11-15 22:15:57 +00:00
type Btelegram struct {
2017-12-19 22:15:03 +00:00
c * tgbotapi . BotAPI
2018-03-04 22:52:14 +00:00
* bridge . Config
2018-02-19 23:54:35 +00:00
avatarMap map [ string ] string // keep cache of userid and avatar sha
2016-11-15 22:15:57 +00:00
}
2018-03-04 22:52:14 +00:00
func New ( cfg * bridge . Config ) bridge . Bridger {
Support Telegram animated stickers (tgs) format (#1173)
This is half a fix for #874
This patch introduces a new config flag:
- MediaConvertTgs
These need to be treated independently from the existing
MediaConvertWebPToPNG flag because Tgs→WebP results in an
*animated* WebP, and the WebP→PNG converter can't handle
animated WebP files yet.
Furthermore, some platforms (like discord) don't even support
animated WebP files, so the user may want to fall back to
static PNGs (not APNGs).
The final reason why this is only half a fix is that this
introduces an external dependency, namely lottie, to be
installed like this:
$ pip3 install lottie cairosvg
This patch works by writing the tgs to a temporary file in /tmp,
calling lottie to convert it (this conversion may take several seconds!),
and then deleting the temporary file.
The temporary file is absolutely necessary, as lottie refuses to
work on non-seekable files.
If anyone comes up with a reasonable use case where /tmp is
unavailable, I can add yet another config option for that, if desired.
Telegram will bail out if the option is configured but lottie isn't found.
2020-08-23 20:34:28 +00:00
tgsConvertFormat := cfg . GetString ( "MediaConvertTgs" )
if tgsConvertFormat != "" {
err := helper . CanConvertTgsToX ( )
if err != nil {
2021-08-24 20:32:50 +00:00
log . Fatalf ( "Telegram bridge configured to convert .tgs files to '%s', but %s does not appear to work:\n%#v" , tgsConvertFormat , helper . LottieBackend ( ) , err )
Support Telegram animated stickers (tgs) format (#1173)
This is half a fix for #874
This patch introduces a new config flag:
- MediaConvertTgs
These need to be treated independently from the existing
MediaConvertWebPToPNG flag because Tgs→WebP results in an
*animated* WebP, and the WebP→PNG converter can't handle
animated WebP files yet.
Furthermore, some platforms (like discord) don't even support
animated WebP files, so the user may want to fall back to
static PNGs (not APNGs).
The final reason why this is only half a fix is that this
introduces an external dependency, namely lottie, to be
installed like this:
$ pip3 install lottie cairosvg
This patch works by writing the tgs to a temporary file in /tmp,
calling lottie to convert it (this conversion may take several seconds!),
and then deleting the temporary file.
The temporary file is absolutely necessary, as lottie refuses to
work on non-seekable files.
If anyone comes up with a reasonable use case where /tmp is
unavailable, I can add yet another config option for that, if desired.
Telegram will bail out if the option is configured but lottie isn't found.
2020-08-23 20:34:28 +00:00
}
2021-08-24 20:32:50 +00:00
if ! helper . SupportsFormat ( tgsConvertFormat ) {
log . Fatalf ( "Telegram bridge configured to convert .tgs files to '%s', but %s doesn't support it." , tgsConvertFormat , helper . LottieBackend ( ) )
Support Telegram animated stickers (tgs) format (#1173)
This is half a fix for #874
This patch introduces a new config flag:
- MediaConvertTgs
These need to be treated independently from the existing
MediaConvertWebPToPNG flag because Tgs→WebP results in an
*animated* WebP, and the WebP→PNG converter can't handle
animated WebP files yet.
Furthermore, some platforms (like discord) don't even support
animated WebP files, so the user may want to fall back to
static PNGs (not APNGs).
The final reason why this is only half a fix is that this
introduces an external dependency, namely lottie, to be
installed like this:
$ pip3 install lottie cairosvg
This patch works by writing the tgs to a temporary file in /tmp,
calling lottie to convert it (this conversion may take several seconds!),
and then deleting the temporary file.
The temporary file is absolutely necessary, as lottie refuses to
work on non-seekable files.
If anyone comes up with a reasonable use case where /tmp is
unavailable, I can add yet another config option for that, if desired.
Telegram will bail out if the option is configured but lottie isn't found.
2020-08-23 20:34:28 +00:00
}
}
2018-03-04 22:52:14 +00:00
return & Btelegram { Config : cfg , avatarMap : make ( map [ string ] string ) }
2016-11-15 22:15:57 +00:00
}
func ( b * Btelegram ) Connect ( ) error {
var err error
2018-02-26 23:33:21 +00:00
b . Log . Info ( "Connecting" )
2018-03-04 22:52:14 +00:00
b . c , err = tgbotapi . NewBotAPI ( b . GetString ( "Token" ) )
2016-11-15 22:15:57 +00:00
if err != nil {
2018-02-26 23:33:21 +00:00
b . Log . Debugf ( "%#v" , err )
2016-11-15 22:15:57 +00:00
return err
}
2018-01-29 11:07:26 +00:00
u := tgbotapi . NewUpdate ( 0 )
u . Timeout = 60
2021-12-11 23:35:32 +00:00
updates := b . c . GetUpdatesChan ( u )
2018-02-26 23:33:21 +00:00
b . Log . Info ( "Connection succeeded" )
2016-11-15 22:15:57 +00:00
go b . handleRecv ( updates )
return nil
}
2017-02-14 20:12:02 +00:00
func ( b * Btelegram ) Disconnect ( ) error {
return nil
}
2017-08-12 12:51:41 +00:00
func ( b * Btelegram ) JoinChannel ( channel config . ChannelInfo ) error {
2016-11-15 22:15:57 +00:00
return nil
}
2021-04-03 21:15:19 +00:00
func TGGetParseMode ( b * Btelegram , username string , text string ) ( textout string , parsemode string ) {
textout = username + text
if b . GetString ( "MessageFormat" ) == HTMLFormat {
b . Log . Debug ( "Using mode HTML" )
parsemode = tgbotapi . ModeHTML
}
if b . GetString ( "MessageFormat" ) == "Markdown" {
b . Log . Debug ( "Using mode markdown" )
parsemode = tgbotapi . ModeMarkdown
}
if b . GetString ( "MessageFormat" ) == MarkdownV2 {
b . Log . Debug ( "Using mode MarkdownV2" )
parsemode = MarkdownV2
}
if strings . ToLower ( b . GetString ( "MessageFormat" ) ) == HTMLNick {
b . Log . Debug ( "Using mode HTML - nick only" )
textout = username + html . EscapeString ( text )
parsemode = tgbotapi . ModeHTML
}
return textout , parsemode
}
2023-03-14 22:03:04 +00:00
func ( b * Btelegram ) getIds ( channel string ) ( int64 , int , error ) {
var chatid int64
topicid := 0
// get the chatid
if strings . Contains ( channel , "/" ) { //nolint:nestif
s := strings . Split ( channel , "/" )
if len ( s ) < 2 {
b . Log . Errorf ( "Invalid channel format: %#v\n" , channel )
return 0 , 0 , nil
}
id , err := strconv . ParseInt ( s [ 0 ] , 10 , 64 )
if err != nil {
return 0 , 0 , err
}
chatid = id
tid , err := strconv . Atoi ( s [ 1 ] )
if err != nil {
return 0 , 0 , err
}
topicid = tid
} else {
id , err := strconv . ParseInt ( channel , 10 , 64 )
if err != nil {
return 0 , 0 , err
}
chatid = id
}
return chatid , topicid , nil
}
2017-08-27 20:59:37 +00:00
func ( b * Btelegram ) Send ( msg config . Message ) ( string , error ) {
2018-02-28 21:23:29 +00:00
b . Log . Debugf ( "=> Receiving %#v" , msg )
2018-02-25 22:54:20 +00:00
2023-03-14 22:03:04 +00:00
chatid , topicid , err := b . getIds ( msg . Channel )
2016-11-15 22:15:57 +00:00
if err != nil {
2017-08-27 20:59:37 +00:00
return "" , err
2016-11-15 22:15:57 +00:00
}
2017-01-06 22:32:17 +00:00
2018-02-19 23:54:35 +00:00
// map the file SHA to our user (caches the avatar)
2018-11-15 19:43:43 +00:00
if msg . Event == config . EventAvatarDownload {
2018-02-25 22:54:20 +00:00
return b . cacheAvatar ( & msg )
2018-02-19 23:54:35 +00:00
}
2018-11-08 21:20:03 +00:00
if b . GetString ( "MessageFormat" ) == HTMLFormat {
2022-07-06 22:47:50 +00:00
msg . Text = makeHTML ( html . EscapeString ( msg . Text ) )
2017-02-24 17:49:52 +00:00
}
2017-08-28 18:38:37 +00:00
2018-02-25 22:54:20 +00:00
// Delete message
2018-11-15 19:43:43 +00:00
if msg . Event == config . EventMsgDelete {
2018-12-12 22:50:08 +00:00
return b . handleDelete ( & msg , chatid )
2017-09-11 21:12:33 +00:00
}
2022-03-25 20:58:52 +00:00
// Handle prefix hint for unthreaded messages.
if msg . ParentNotFound ( ) {
msg . ParentID = ""
msg . Text = fmt . Sprintf ( "[reply]: %s" , msg . Text )
}
2022-03-30 20:23:52 +00:00
var parentID int
if msg . ParentID != "" {
parentID , _ = b . intParentID ( msg . ParentID )
}
2018-02-25 22:54:20 +00:00
// Upload a file if it exists
if msg . Extra != nil {
for _ , rmsg := range helper . HandleExtra ( & msg , b . General ) {
2023-03-14 22:03:04 +00:00
if _ , msgErr := b . sendMessage ( chatid , topicid , rmsg . Username , rmsg . Text , parentID ) ; msgErr != nil {
2020-01-09 17:14:01 +00:00
b . Log . Errorf ( "sendMessage failed: %s" , msgErr )
2018-11-28 09:57:59 +00:00
}
2018-02-25 22:54:20 +00:00
}
// check if we have files to upload (from slack, telegram or mattermost)
if len ( msg . Extra [ "file" ] ) > 0 {
2023-03-14 22:03:04 +00:00
return b . handleUploadFile ( & msg , chatid , topicid , parentID )
2018-02-25 22:54:20 +00:00
}
}
2017-08-28 18:38:37 +00:00
// edit the message if we have a msg ID
if msg . ID != "" {
2018-12-12 22:50:08 +00:00
return b . handleEdit ( & msg , chatid )
2017-08-28 18:38:37 +00:00
}
2018-02-25 22:54:20 +00:00
// Post normal message
2020-01-09 17:14:01 +00:00
// TODO: recheck it.
// Ignore empty text field needs for prevent double messages from whatsapp to telegram
// when sending media with text caption
if msg . Text != "" {
2023-03-14 22:03:04 +00:00
return b . sendMessage ( chatid , topicid , msg . Username , msg . Text , parentID )
2020-01-09 17:14:01 +00:00
}
return "" , nil
2016-11-15 22:15:57 +00:00
}
2017-06-17 16:25:17 +00:00
func ( b * Btelegram ) getFileDirectURL ( id string ) string {
res , err := b . c . GetFileDirectURL ( id )
if err != nil {
return ""
}
return res
}
2017-11-04 13:50:01 +00:00
2023-03-14 22:03:04 +00:00
func ( b * Btelegram ) sendMessage ( chatid int64 , topicid int , username , text string , parentID int ) ( string , error ) {
2018-02-28 22:25:00 +00:00
m := tgbotapi . NewMessage ( chatid , "" )
2021-04-03 21:15:19 +00:00
m . Text , m . ParseMode = TGGetParseMode ( b , username , text )
2023-03-14 22:03:04 +00:00
if topicid != 0 {
m . BaseChat . MessageThreadID = topicid
}
2022-03-30 20:23:52 +00:00
m . ReplyToMessageID = parentID
2020-02-02 17:53:04 +00:00
m . DisableWebPagePreview = b . GetBool ( "DisableWebPagePreview" )
2017-11-12 23:20:31 +00:00
res , err := b . c . Send ( m )
if err != nil {
return "" , err
}
return strconv . Itoa ( res . MessageID ) , nil
}
2018-02-25 22:54:20 +00:00
2022-03-30 20:23:52 +00:00
// sendMediaFiles native upload media files via media group
2023-03-14 22:03:04 +00:00
func ( b * Btelegram ) sendMediaFiles ( msg * config . Message , chatid int64 , threadid int , parentID int , media [ ] interface { } ) ( string , error ) {
2022-03-30 20:23:52 +00:00
if len ( media ) == 0 {
return "" , nil
}
2023-03-14 22:03:04 +00:00
mg := tgbotapi . MediaGroupConfig {
BaseChat : tgbotapi . BaseChat {
ChatID : chatid ,
MessageThreadID : threadid ,
ChannelUsername : msg . Username ,
ReplyToMessageID : parentID ,
} ,
Media : media ,
}
2022-03-30 20:23:52 +00:00
messages , err := b . c . SendMediaGroup ( mg )
if err != nil {
return "" , err
}
// return first message id
return strconv . Itoa ( messages [ 0 ] . MessageID ) , nil
}
// intParentID return integer parent id for telegram message
func ( b * Btelegram ) intParentID ( parentID string ) ( int , error ) {
pid , err := strconv . Atoi ( parentID )
if err != nil {
return 0 , err
}
return pid , nil
}
2018-02-25 22:54:20 +00:00
func ( b * Btelegram ) cacheAvatar ( msg * config . Message ) ( string , error ) {
fi := msg . Extra [ "file" ] [ 0 ] . ( config . FileInfo )
/ * if we have a sha we have successfully uploaded the file to the media server ,
so we can now cache the sha * /
if fi . SHA != "" {
2018-02-26 23:33:21 +00:00
b . Log . Debugf ( "Added %s to %s in avatarMap" , fi . SHA , msg . UserID )
2018-02-25 22:54:20 +00:00
b . avatarMap [ msg . UserID ] = fi . SHA
}
return "" , nil
}