From a74e2bcb7d3262073d05aa89140b1d202b7f179d Mon Sep 17 00:00:00 2001 From: Bohdan Horbeshko Date: Sun, 5 May 2024 13:16:38 -0400 Subject: [PATCH] Mute/unmute whole chats with no arguments --- Makefile | 2 +- persistence/sessions.go | 100 ++++++++++++++++++++++++++++++---- persistence/sessions_test.go | 28 ++++++++++ telegabber.go | 2 +- telegram/commands.go | 102 ++++++++++++++++++----------------- telegram/handlers.go | 11 ++++ 6 files changed, 184 insertions(+), 61 deletions(-) diff --git a/Makefile b/Makefile index a90eb8f..f8d5b73 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ COMMIT := $(shell git rev-parse --short HEAD) TD_COMMIT := "5bbfc1cf5dab94f82e02f3430ded7241d4653551" -VERSION := "v1.9.3" +VERSION := "v1.9.4" MAKEOPTS := "-j4" all: diff --git a/persistence/sessions.go b/persistence/sessions.go index 29c4918..0454d97 100644 --- a/persistence/sessions.go +++ b/persistence/sessions.go @@ -3,6 +3,7 @@ package persistence import ( "github.com/pkg/errors" "io/ioutil" + "sync" "time" "dev.narayana.im/narayana/telegabber/yamldb" @@ -34,16 +35,18 @@ type SessionsMap struct { // Session is a key-values subtree type Session struct { - Login string `yaml:":login"` - Timezone string `yaml:":timezone"` - KeepOnline bool `yaml:":keeponline"` - RawMessages bool `yaml:":rawmessages"` - AsciiArrows bool `yaml:":asciiarrows"` - OOBMode bool `yaml:":oobmode"` - Carbons bool `yaml:":carbons"` - HideIds bool `yaml:":hideids"` - Receipts bool `yaml:":receipts"` - NativeEdits bool `yaml:":nativeedits"` + Login string `yaml:":login"` + Timezone string `yaml:":timezone"` + KeepOnline bool `yaml:":keeponline"` + RawMessages bool `yaml:":rawmessages"` + AsciiArrows bool `yaml:":asciiarrows"` + OOBMode bool `yaml:":oobmode"` + Carbons bool `yaml:":carbons"` + HideIds bool `yaml:":hideids"` + Receipts bool `yaml:":receipts"` + NativeEdits bool `yaml:":nativeedits"` + IgnoredChats []int64 `yaml:":ignoredchats"` + ignoredChatsMap map[int64]bool `yaml:"-"` } var configKeys = []string{ @@ -59,14 +62,21 @@ var configKeys = []string{ } var sessionDB *SessionsYamlDB +var sessionsLock sync.Mutex // SessionMarshaller implementation for YamlDB func SessionMarshaller() ([]byte, error) { cleanedMap := SessionsMap{} emptySessionsMap(&cleanedMap) + sessionsLock.Lock() + defer sessionsLock.Unlock() for jid, session := range sessionDB.Data.Sessions { if session.Login != "" { + session.IgnoredChats = make([]int64, 0, len(session.ignoredChatsMap)) + for chatID := range session.ignoredChatsMap { + session.IgnoredChats = append(session.IgnoredChats, chatID) + } cleanedMap.Sessions[jid] = session } } @@ -108,6 +118,16 @@ func initYamlDB(path string, dataPtr *SessionsMap) (*SessionsYamlDB, error) { emptySessionsMap(dataPtr) } + // convert ignored users slice to map + for jid, session := range dataPtr.Sessions { + session.ignoredChatsMap = make(map[int64]bool) + for _, chatID := range session.IgnoredChats { + session.ignoredChatsMap[chatID] = true + } + session.IgnoredChats = nil + dataPtr.Sessions[jid] = session + } + return &SessionsYamlDB{ YamlDB: yamldb.YamlDB{ Path: path, @@ -119,6 +139,13 @@ func initYamlDB(path string, dataPtr *SessionsMap) (*SessionsYamlDB, error) { // Get retrieves a session value func (s *Session) Get(key string) (string, error) { + sessionsLock.Lock() + defer sessionsLock.Unlock() + + return s.get(key) +} + +func (s *Session) get(key string) (string, error) { switch key { case "timezone": return s.Timezone, nil @@ -145,9 +172,12 @@ func (s *Session) Get(key string) (string, error) { // ToMap converts the session to a map func (s *Session) ToMap() map[string]string { + sessionsLock.Lock() + defer sessionsLock.Unlock() + m := make(map[string]string) for _, configKey := range configKeys { - value, _ := s.Get(configKey) + value, _ := s.get(configKey) m[configKey] = value } @@ -156,6 +186,9 @@ func (s *Session) ToMap() map[string]string { // Set sets a session value func (s *Session) Set(key string, value string) (string, error) { + sessionsLock.Lock() + defer sessionsLock.Unlock() + switch key { case "timezone": s.Timezone = value @@ -232,6 +265,51 @@ func (s *Session) TimezoneToLocation() *time.Location { return zeroLocation } +// IgnoreChat adds a chat id to ignore list, returns false if already ignored +func (s *Session) IgnoreChat(chatID int64) bool { + sessionsLock.Lock() + defer sessionsLock.Unlock() + + if s.ignoredChatsMap == nil { + s.ignoredChatsMap = make(map[int64]bool) + } else if _, ok := s.ignoredChatsMap[chatID]; ok { + return false + } + + s.ignoredChatsMap[chatID] = true + return true +} + +// UnignoreChat removes a chat id from ignore list, returns false if not already ignored +func (s *Session) UnignoreChat(chatID int64) bool { + sessionsLock.Lock() + defer sessionsLock.Unlock() + + if s.ignoredChatsMap == nil { + return false + } + + if _, ok := s.ignoredChatsMap[chatID]; !ok { + return false + } + + delete(s.ignoredChatsMap, chatID) + return true +} + +// IsChatIgnored checks the chat id against the ignore list +func (s *Session) IsChatIgnored(chatID int64) bool { + sessionsLock.Lock() + defer sessionsLock.Unlock() + + if s.ignoredChatsMap == nil { + return false + } + + _, ok := s.ignoredChatsMap[chatID] + return ok +} + func fromBool(b bool) string { if b { return "true" diff --git a/persistence/sessions_test.go b/persistence/sessions_test.go index 0339378..001cfa0 100644 --- a/persistence/sessions_test.go +++ b/persistence/sessions_test.go @@ -88,3 +88,31 @@ func TestSessionSetAbsent(t *testing.T) { t.Error("There shouldn't come a donkey!") } } + +func TestSessionIgnore(t *testing.T) { + session := Session{} + if session.IsChatIgnored(3) { + t.Error("Shouldn't be ignored yet") + } + if !session.IgnoreChat(3) { + t.Error("Shouldn't have been ignored") + } + if session.IgnoreChat(3) { + t.Error("Shouldn't ignore second time") + } + if !session.IsChatIgnored(3) { + t.Error("Should be ignored already") + } + if session.IsChatIgnored(-145) { + t.Error("Wrong chat is ignored") + } + if !session.UnignoreChat(3) { + t.Error("Should successfully unignore") + } + if session.UnignoreChat(3) { + t.Error("Should unignore second time") + } + if session.IsChatIgnored(3) { + t.Error("Shouldn't be ignored already") + } +} diff --git a/telegabber.go b/telegabber.go index c39d91d..9e71887 100644 --- a/telegabber.go +++ b/telegabber.go @@ -16,7 +16,7 @@ import ( goxmpp "gosrc.io/xmpp" ) -var version string = "1.9.3" +var version string = "1.9.4" var commit string var sm *goxmpp.StreamManager diff --git a/telegram/commands.go b/telegram/commands.go index 1ce316b..4730a3f 100644 --- a/telegram/commands.go +++ b/telegram/commands.go @@ -85,8 +85,8 @@ var chatCommands = map[string]command{ "invite": command{"id or @username", "add user to current chat"}, "link": command{"", "get invite link for current chat"}, "kick": command{"id or @username", "remove user to current chat"}, - "mute": command{"id or @username [hours]", "mute user in current chat"}, - "unmute": command{"id or @username", "unrestrict user from current chat"}, + "mute": command{"[id or @username] [hours]", "mute the whole chat or a user in current chat"}, + "unmute": command{"[id or @username]", "unmute the whole chat or a user in the current chat"}, "ban": command{"id or @username [hours]", "restrict @username from current chat for [hours] or forever"}, "unban": command{"id or @username", "unbans @username in current chat (and devotes from admins)"}, "promote": command{"id or @username [title]", "promote user to admin in current chat"}, @@ -771,59 +771,65 @@ func (c *Client) ProcessChatCommand(chatID int64, cmdline string) (string, bool) if err != nil { return err.Error(), true } - // mute @username [n hours] + // mute [@username [n hours]] case "mute": - if len(args) < 1 { - return notEnoughArguments, true - } - - contact, _, err := c.GetContactByUsername(args[0]) - if err != nil { - return err.Error(), true - } - - var hours int64 - if len(args) > 1 { - hours, err = strconv.ParseInt(args[1], 10, 32) + if len(args) > 0 { + contact, _, err := c.GetContactByUsername(args[0]) if err != nil { - return "Invalid number of hours", true + return err.Error(), true } - } - _, err = c.client.SetChatMemberStatus(&client.SetChatMemberStatusRequest{ - ChatId: chatID, - MemberId: &client.MessageSenderUser{UserId: contact.Id}, - Status: &client.ChatMemberStatusRestricted{ - IsMember: true, - RestrictedUntilDate: c.formatBantime(hours), - Permissions: &permissionsReadonly, - }, - }) - if err != nil { - return err.Error(), true + var hours int64 + if len(args) > 1 { + hours, err = strconv.ParseInt(args[1], 10, 32) + if err != nil { + return "Invalid number of hours", true + } + } + + _, err = c.client.SetChatMemberStatus(&client.SetChatMemberStatusRequest{ + ChatId: chatID, + MemberId: &client.MessageSenderUser{UserId: contact.Id}, + Status: &client.ChatMemberStatusRestricted{ + IsMember: true, + RestrictedUntilDate: c.formatBantime(hours), + Permissions: &permissionsReadonly, + }, + }) + if err != nil { + return err.Error(), true + } + } else { + if !c.Session.IgnoreChat(chatID) { + return "Chat is already ignored", true + } + gateway.DirtySessions = true } - // unmute @username + // unmute [@username] case "unmute": - if len(args) < 1 { - return notEnoughArguments, true - } + if len(args) > 0 { + contact, _, err := c.GetContactByUsername(args[0]) + if err != nil { + return err.Error(), true + } - contact, _, err := c.GetContactByUsername(args[0]) - if err != nil { - return err.Error(), true - } - - _, err = c.client.SetChatMemberStatus(&client.SetChatMemberStatusRequest{ - ChatId: chatID, - MemberId: &client.MessageSenderUser{UserId: contact.Id}, - Status: &client.ChatMemberStatusRestricted{ - IsMember: true, - RestrictedUntilDate: 0, - Permissions: &permissionsMember, - }, - }) - if err != nil { - return err.Error(), true + _, err = c.client.SetChatMemberStatus(&client.SetChatMemberStatusRequest{ + ChatId: chatID, + MemberId: &client.MessageSenderUser{UserId: contact.Id}, + Status: &client.ChatMemberStatusRestricted{ + IsMember: true, + RestrictedUntilDate: 0, + Permissions: &permissionsMember, + }, + }) + if err != nil { + return err.Error(), true + } + } else { + if !c.Session.UnignoreChat(chatID) { + return "Chat wasn't ignored", true + } + gateway.DirtySessions = true } // ban @username from current chat [for N hours] case "ban": diff --git a/telegram/handlers.go b/telegram/handlers.go index 6266292..1ccd622 100644 --- a/telegram/handlers.go +++ b/telegram/handlers.go @@ -235,6 +235,9 @@ func (c *Client) updateChatLastMessage(update *client.UpdateChatLastMessage) { // message received func (c *Client) updateNewMessage(update *client.UpdateNewMessage) { chatId := update.Message.ChatId + if c.Session.IsChatIgnored(chatId) { + return + } // guarantee sequential message delivering per chat lock := c.getChatMessageLock(chatId) @@ -261,6 +264,10 @@ func (c *Client) updateNewMessage(update *client.UpdateNewMessage) { // message content updated func (c *Client) updateMessageContent(update *client.UpdateMessageContent) { + if c.Session.IsChatIgnored(update.ChatId) { + return + } + markupFunction := c.getFormatter() defer c.updateLastMessageHash(update.ChatId, update.MessageId, update.NewContent) @@ -353,6 +360,10 @@ func (c *Client) updateMessageContent(update *client.UpdateMessageContent) { // message(s) deleted func (c *Client) updateDeleteMessages(update *client.UpdateDeleteMessages) { if update.IsPermanent { + if c.Session.IsChatIgnored(update.ChatId) { + return + } + var deleteChar string if c.Session.AsciiArrows { deleteChar = "X "