From b09c370df6dd46ee89b59e79c73999e6159ea5a6 Mon Sep 17 00:00:00 2001 From: Akshett Rai Jindal Date: Wed, 27 Sep 2023 11:01:11 +0530 Subject: [PATCH 1/2] [WhatsApp] send online presence for 10 seconds on new messages --- state/config.go | 1 + utils/telegram.go | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/state/config.go b/state/config.go index 0844194..51e450c 100644 --- a/state/config.go +++ b/state/config.go @@ -55,6 +55,7 @@ type Config struct { SendRevokedMessageUpdates bool `yaml:"send_revoked_message_updates"` WhatsmeowDebugMode bool `yaml:"whatsmeow_debug_mode"` SendMyMessagesFromOtherDevices bool `yaml:"send_my_messages_from_other_devices"` + SendMyPresenceAndReceipts bool `yaml:"send_my_presence_and_receipts"` } `yaml:"whatsapp"` Database map[string]string `yaml:"database"` diff --git a/utils/telegram.go b/utils/telegram.go index 400a35f..fafd604 100644 --- a/utils/telegram.go +++ b/utils/telegram.go @@ -20,6 +20,7 @@ import ( "go.mau.fi/whatsmeow" waProto "go.mau.fi/whatsmeow/binary/proto" waTypes "go.mau.fi/whatsmeow/types" + "go.uber.org/zap" "golang.org/x/exp/slices" "google.golang.org/protobuf/proto" ) @@ -170,6 +171,7 @@ func TgSendToWhatsApp(b *gotgbot.Bot, c *ext.Context, var ( cfg = state.State.Config + logger = state.State.Logger waClient = state.State.WhatsAppClient mentions = []string{} ) @@ -196,6 +198,27 @@ func TgSendToWhatsApp(b *gotgbot.Bot, c *ext.Context, } } + if cfg.WhatsApp.SendMyPresenceAndReceipts { + err := waClient.SendPresence(waTypes.PresenceAvailable) + if err != nil { + logger.Warn("failed to send presence", + zap.Error(err), + zap.String("presence", string(waTypes.PresenceAvailable)), + ) + } + + go func() { + time.Sleep(10 * time.Second) + err := waClient.SendPresence(waTypes.PresenceUnavailable) + if err != nil { + logger.Warn("failed to send presence", + zap.Error(err), + zap.String("presence", string(waTypes.PresenceUnavailable)), + ) + } + }() + } + if msgToForward.Photo != nil && len(msgToForward.Photo) > 0 { bestPhoto := msgToForward.Photo[0] From 8912039152a9458148e64d3359be41ac876214d3 Mon Sep 17 00:00:00 2001 From: Akshett Rai Jindal Date: Tue, 3 Oct 2023 19:55:23 +0530 Subject: [PATCH 2/2] [presence] add configurable option to send read receipts to others --- database/helpers.go | 42 ++++++++++++++++++++++++++++++++++++++++++ database/types.go | 8 +++++++- sample_config.yaml | 3 +++ state/config.go | 3 ++- utils/telegram.go | 28 +++++++++++++++++++++++++++- whatsapp/handlers.go | 11 +++++++++++ 6 files changed, 92 insertions(+), 3 deletions(-) diff --git a/database/helpers.go b/database/helpers.go index cb89e48..541f397 100644 --- a/database/helpers.go +++ b/database/helpers.go @@ -1,6 +1,8 @@ package database import ( + "database/sql" + "watgbridge/state" "go.mau.fi/whatsmeow/types" @@ -22,6 +24,7 @@ func MsgIdAddNewPair(waMsgId, participantId, waChatId string, tgChatId, tgMsgId, bridgePair.TgChatId = tgChatId bridgePair.TgMsgId = tgMsgId bridgePair.TgThreadId = tgThreadId + bridgePair.MarkRead = sql.NullBool{Valid: true, Bool: false} res = db.Save(&bridgePair) return res.Error } @@ -33,6 +36,7 @@ func MsgIdAddNewPair(waMsgId, participantId, waChatId string, tgChatId, tgMsgId, TgChatId: tgChatId, TgMsgId: tgMsgId, TgThreadId: tgThreadId, + MarkRead: sql.NullBool{Valid: true, Bool: false}, }) return res.Error } @@ -57,6 +61,44 @@ func MsgIdGetWaFromTg(tgChatId, tgMsgId, tgThreadId int64) (msgId, participantId return bridgePair.ID, bridgePair.ParticipantId, bridgePair.WaChatId, res.Error } +func MsgIdGetUnread(waChatId string) (map[string]([]string), error) { + + db := state.State.Database + + var bridgePairs []MsgIdPair + res := db.Where("wa_chat_id = ? AND mark_read = false", waChatId).Find(&bridgePairs) + + var msgIds = make(map[string]([]string)) + + for _, pair := range bridgePairs { + if _, found := msgIds[pair.ParticipantId]; !found { + msgIds[pair.ParticipantId] = []string{} + } + msgIds[pair.ParticipantId] = append(msgIds[pair.ParticipantId], pair.ID) + } + + return msgIds, res.Error +} + +func MsgIdMarkRead(waChatId, waMsgId string) error { + + db := state.State.Database + + var bridgePair MsgIdPair + res := db.Where("id = ? AND wa_chat_id = ?", waMsgId, waChatId).Find(&bridgePair) + if res.Error != nil { + return res.Error + } + + if bridgePair.ID == waMsgId { + bridgePair.MarkRead = sql.NullBool{Valid: true, Bool: true} + res = db.Save(&bridgePair) + return res.Error + } + + return nil +} + func MsgIdDeletePair(tgChatId, tgMsgId int64) error { db := state.State.Database diff --git a/database/types.go b/database/types.go index 9998b5d..bb5e32c 100644 --- a/database/types.go +++ b/database/types.go @@ -1,6 +1,10 @@ package database -import "watgbridge/state" +import ( + "database/sql" + + "watgbridge/state" +) type MsgIdPair struct { // WhatsApp @@ -12,6 +16,8 @@ type MsgIdPair struct { TgChatId int64 TgThreadId int64 TgMsgId int64 + + MarkRead sql.NullBool } type ChatThreadPair struct { diff --git a/sample_config.yaml b/sample_config.yaml index 60d68fe..438ac7c 100644 --- a/sample_config.yaml +++ b/sample_config.yaml @@ -17,6 +17,9 @@ telegram: skip_video_stickers: false # Setting this as true will stop trying to convert telegram video stickers to webp and sending them skip_setting_commands: false # This will not show you list of commands when you start typing / in telegram + send_my_presence: false # Setting this to true will show your account as online to others whenever you send a message using Telegram + send_my_read_receipts: false # Setting this to true will mark all unread messages in a chat as read when you send a new message using Telegram + whatsapp: session_name: watgbridge # This will appear in your Linked Devices in mobile app # All these values can be obtained by running /findcontacts and /getwagroups commands diff --git a/state/config.go b/state/config.go index 51e450c..d84b21b 100644 --- a/state/config.go +++ b/state/config.go @@ -26,6 +26,8 @@ type Config struct { SelfHostedAPI bool `yaml:"self_hosted_api"` SkipVideoStickers bool `yaml:"skip_video_stickers"` SkipSettingCommands bool `yaml:"skip_setting_commands"` + SendMyPresence bool `yaml:"send_my_presence"` + SendMyReadReceipts bool `yaml:"send_my_read_receipts"` } `yaml:"telegram"` WhatsApp struct { @@ -55,7 +57,6 @@ type Config struct { SendRevokedMessageUpdates bool `yaml:"send_revoked_message_updates"` WhatsmeowDebugMode bool `yaml:"whatsmeow_debug_mode"` SendMyMessagesFromOtherDevices bool `yaml:"send_my_messages_from_other_devices"` - SendMyPresenceAndReceipts bool `yaml:"send_my_presence_and_receipts"` } `yaml:"whatsapp"` Database map[string]string `yaml:"database"` diff --git a/utils/telegram.go b/utils/telegram.go index fafd604..20ff436 100644 --- a/utils/telegram.go +++ b/utils/telegram.go @@ -198,7 +198,7 @@ func TgSendToWhatsApp(b *gotgbot.Bot, c *ext.Context, } } - if cfg.WhatsApp.SendMyPresenceAndReceipts { + if cfg.Telegram.SendMyPresence { err := waClient.SendPresence(waTypes.PresenceAvailable) if err != nil { logger.Warn("failed to send presence", @@ -894,6 +894,32 @@ func TgSendToWhatsApp(b *gotgbot.Bot, c *ext.Context, } + if cfg.Telegram.SendMyReadReceipts { + unreadMsgs, err := database.MsgIdGetUnread(waChatJID.String()) + if err != nil { + return TgReplyWithErrorByContext(b, c, "Message sent but failed to get unread messages to mark them read", err) + } + + for sender, msgIds := range unreadMsgs { + senderJID, _ := WaParseJID(sender) + err := waClient.MarkRead(msgIds, time.Now(), waChatJID, senderJID) + if err != nil { + logger.Warn( + "failed to mark messages as read", + zap.String("chat_id", waChatJID.String()), + zap.Any("msg_ids", msgIds), + zap.String("sender", senderJID.String()), + ) + } else { + for _, msgId := range msgIds { + database.MsgIdMarkRead(waChatJID.String(), msgId) + } + } + } + + // waClient.MarkRead(unreadMsgs, time.Now(), waChatJID, ) + } + return nil } diff --git a/whatsapp/handlers.go b/whatsapp/handlers.go index a218a8f..4a03e1b 100644 --- a/whatsapp/handlers.go +++ b/whatsapp/handlers.go @@ -26,6 +26,9 @@ func WhatsAppEventHandler(evt interface{}) { switch v := evt.(type) { + case *events.Receipt: + ReceiptEventHandler(v) + case *events.PushName: PushNameEventHandler(v) @@ -1120,6 +1123,14 @@ func CallOfferEventHandler(v *events.CallOffer) { utils.TgSendTextById(tgBot, cfg.Telegram.TargetChatID, callThreadId, bridgeText) } +func ReceiptEventHandler(v *events.Receipt) { + if v.Type == events.ReceiptTypeReadSelf { + for _, msgId := range v.MessageIDs { + database.MsgIdMarkRead(v.Chat.String(), msgId) + } + } +} + func PushNameEventHandler(v *events.PushName) { logger := state.State.Logger defer logger.Sync()