Merge branch 'master' into adhoc

This commit is contained in:
Bohdan Horbeshko 2024-05-09 19:32:57 -04:00
commit 4eae44b9a2
4 changed files with 188 additions and 57 deletions

View file

@ -3,6 +3,7 @@ package persistence
import ( import (
"github.com/pkg/errors" "github.com/pkg/errors"
"io/ioutil" "io/ioutil"
"sync"
"time" "time"
"dev.narayana.im/narayana/telegabber/yamldb" "dev.narayana.im/narayana/telegabber/yamldb"
@ -34,16 +35,18 @@ type SessionsMap struct {
// Session is a key-values subtree // Session is a key-values subtree
type Session struct { type Session struct {
Login string `yaml:":login"` Login string `yaml:":login"`
Timezone string `yaml:":timezone"` Timezone string `yaml:":timezone"`
KeepOnline bool `yaml:":keeponline"` KeepOnline bool `yaml:":keeponline"`
RawMessages bool `yaml:":rawmessages"` RawMessages bool `yaml:":rawmessages"`
AsciiArrows bool `yaml:":asciiarrows"` AsciiArrows bool `yaml:":asciiarrows"`
OOBMode bool `yaml:":oobmode"` OOBMode bool `yaml:":oobmode"`
Carbons bool `yaml:":carbons"` Carbons bool `yaml:":carbons"`
HideIds bool `yaml:":hideids"` HideIds bool `yaml:":hideids"`
Receipts bool `yaml:":receipts"` Receipts bool `yaml:":receipts"`
NativeEdits bool `yaml:":nativeedits"` NativeEdits bool `yaml:":nativeedits"`
IgnoredChats []int64 `yaml:":ignoredchats"`
ignoredChatsMap map[int64]bool `yaml:"-"`
} }
var configKeys = []string{ var configKeys = []string{
@ -59,14 +62,21 @@ var configKeys = []string{
} }
var sessionDB *SessionsYamlDB var sessionDB *SessionsYamlDB
var sessionsLock sync.Mutex
// SessionMarshaller implementation for YamlDB // SessionMarshaller implementation for YamlDB
func SessionMarshaller() ([]byte, error) { func SessionMarshaller() ([]byte, error) {
cleanedMap := SessionsMap{} cleanedMap := SessionsMap{}
emptySessionsMap(&cleanedMap) emptySessionsMap(&cleanedMap)
sessionsLock.Lock()
defer sessionsLock.Unlock()
for jid, session := range sessionDB.Data.Sessions { for jid, session := range sessionDB.Data.Sessions {
if session.Login != "" { 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 cleanedMap.Sessions[jid] = session
} }
} }
@ -108,6 +118,16 @@ func initYamlDB(path string, dataPtr *SessionsMap) (*SessionsYamlDB, error) {
emptySessionsMap(dataPtr) 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{ return &SessionsYamlDB{
YamlDB: yamldb.YamlDB{ YamlDB: yamldb.YamlDB{
Path: path, Path: path,
@ -119,6 +139,13 @@ func initYamlDB(path string, dataPtr *SessionsMap) (*SessionsYamlDB, error) {
// Get retrieves a session value // Get retrieves a session value
func (s *Session) Get(key string) (string, error) { 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 { switch key {
case "timezone": case "timezone":
return s.Timezone, nil return s.Timezone, nil
@ -145,9 +172,12 @@ func (s *Session) Get(key string) (string, error) {
// ToMap converts the session to a map // ToMap converts the session to a map
func (s *Session) ToMap() map[string]string { func (s *Session) ToMap() map[string]string {
sessionsLock.Lock()
defer sessionsLock.Unlock()
m := make(map[string]string) m := make(map[string]string)
for _, configKey := range configKeys { for _, configKey := range configKeys {
value, _ := s.Get(configKey) value, _ := s.get(configKey)
m[configKey] = value m[configKey] = value
} }
@ -156,6 +186,9 @@ func (s *Session) ToMap() map[string]string {
// Set sets a session value // Set sets a session value
func (s *Session) Set(key string, value string) (string, error) { func (s *Session) Set(key string, value string) (string, error) {
sessionsLock.Lock()
defer sessionsLock.Unlock()
switch key { switch key {
case "timezone": case "timezone":
s.Timezone = value s.Timezone = value
@ -232,6 +265,51 @@ func (s *Session) TimezoneToLocation() *time.Location {
return zeroLocation 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 { func fromBool(b bool) string {
if b { if b {
return "true" return "true"

View file

@ -88,3 +88,31 @@ func TestSessionSetAbsent(t *testing.T) {
t.Error("There shouldn't come a donkey!") 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")
}
}

View file

@ -94,8 +94,8 @@ var chatCommands = map[string]command{
"invite": command{1, []string{"id or @username"}, "add user to current chat", &notForPM}, "invite": command{1, []string{"id or @username"}, "add user to current chat", &notForPM},
"link": command{0, []string{}, "get invite link for current chat", &notForPM}, "link": command{0, []string{}, "get invite link for current chat", &notForPM},
"kick": command{1, []string{"id or @username"}, "remove user from current chat", &notForPM}, "kick": command{1, []string{"id or @username"}, "remove user from current chat", &notForPM},
"mute": command{1, []string{"id or @username", "hours"}, "mute user in current chat", &notForPMAndBasic}, "mute": command{0, []string{"id or @username", "hours"}, "mute the whole chat or a user in current chat", &notForPMAndBasic},
"unmute": command{1, []string{"id or @username"}, "unrestrict user from current chat", &notForPMAndBasic}, "unmute": command{0, []string{"id or @username"}, "unmute the whole chat or a user in the current chat", &notForPMAndBasic},
"ban": command{1, []string{"id or @username", "hours"}, "restrict @username from current chat for [hours] or forever", &notForPM}, "ban": command{1, []string{"id or @username", "hours"}, "restrict @username from current chat for [hours] or forever", &notForPM},
"unban": command{1, []string{"id or @username"}, "unbans @username in current chat (and devotes from admins)", &notForPM}, "unban": command{1, []string{"id or @username"}, "unbans @username in current chat (and devotes from admins)", &notForPM},
"promote": command{1, []string{"id or @username", "title"}, "promote user to admin in current chat", &notForPM}, "promote": command{1, []string{"id or @username", "title"}, "promote user to admin in current chat", &notForPM},
@ -847,57 +847,71 @@ func (c *Client) ProcessChatCommand(chatID int64, cmdline string) (string, bool,
if err != nil { if err != nil {
return err.Error(), true, false return err.Error(), true, false
} }
// mute @username [n hours] // mute [@username [n hours]]
case "mute": case "mute":
contact, _, err := c.GetContactByUsername(args[0]) if len(args) > 0 {
if err != nil { contact, _, err := c.GetContactByUsername(args[0])
return err.Error(), true, false
}
if contact == nil {
return "Contact not found", true, false
}
var hours int64
if len(args) > 1 {
hours, err = strconv.ParseInt(args[1], 10, 32)
if err != nil { if err != nil {
return "Invalid number of hours", true, false return err.Error(), true, false
}
if contact == nil {
return "Contact not found", true, false
} }
}
_, err = c.client.SetChatMemberStatus(&client.SetChatMemberStatusRequest{ var hours int64
ChatId: chatID, if len(args) > 1 {
MemberId: &client.MessageSenderUser{UserId: contact.Id}, hours, err = strconv.ParseInt(args[1], 10, 32)
Status: &client.ChatMemberStatusRestricted{ if err != nil {
IsMember: true, return "Invalid number of hours", true, false
RestrictedUntilDate: c.formatBantime(hours), }
Permissions: &permissionsReadonly, }
},
}) _, err = c.client.SetChatMemberStatus(&client.SetChatMemberStatusRequest{
if err != nil { ChatId: chatID,
return err.Error(), true, false MemberId: &client.MessageSenderUser{UserId: contact.Id},
Status: &client.ChatMemberStatusRestricted{
IsMember: true,
RestrictedUntilDate: c.formatBantime(hours),
Permissions: &permissionsReadonly,
},
})
if err != nil {
return err.Error(), true, false
}
} else {
if !c.Session.IgnoreChat(chatID) {
return "Chat is already ignored", true, false
}
gateway.DirtySessions = true
} }
// unmute @username // unmute [@username]
case "unmute": case "unmute":
contact, _, err := c.GetContactByUsername(args[0]) if len(args) > 0 {
if err != nil { contact, _, err := c.GetContactByUsername(args[0])
return err.Error(), true, false if err != nil {
} return err.Error(), true, false
if contact == nil { }
return "Contact not found", true, false if contact == nil {
} return "Contact not found", true, false
}
_, err = c.client.SetChatMemberStatus(&client.SetChatMemberStatusRequest{ _, err = c.client.SetChatMemberStatus(&client.SetChatMemberStatusRequest{
ChatId: chatID, ChatId: chatID,
MemberId: &client.MessageSenderUser{UserId: contact.Id}, MemberId: &client.MessageSenderUser{UserId: contact.Id},
Status: &client.ChatMemberStatusRestricted{ Status: &client.ChatMemberStatusRestricted{
IsMember: true, IsMember: true,
RestrictedUntilDate: 0, RestrictedUntilDate: 0,
Permissions: &permissionsMember, Permissions: &permissionsMember,
}, },
}) })
if err != nil { if err != nil {
return err.Error(), true, false return err.Error(), true, false
}
} else {
if !c.Session.UnignoreChat(chatID) {
return "Chat wasn't ignored", true, false
}
gateway.DirtySessions = true
} }
// ban @username from current chat [for N hours] // ban @username from current chat [for N hours]
case "ban": case "ban":

View file

@ -235,6 +235,9 @@ func (c *Client) updateChatLastMessage(update *client.UpdateChatLastMessage) {
// message received // message received
func (c *Client) updateNewMessage(update *client.UpdateNewMessage) { func (c *Client) updateNewMessage(update *client.UpdateNewMessage) {
chatId := update.Message.ChatId chatId := update.Message.ChatId
if c.Session.IsChatIgnored(chatId) {
return
}
// guarantee sequential message delivering per chat // guarantee sequential message delivering per chat
lock := c.getChatMessageLock(chatId) lock := c.getChatMessageLock(chatId)
@ -261,6 +264,10 @@ func (c *Client) updateNewMessage(update *client.UpdateNewMessage) {
// message content updated // message content updated
func (c *Client) updateMessageContent(update *client.UpdateMessageContent) { func (c *Client) updateMessageContent(update *client.UpdateMessageContent) {
if c.Session.IsChatIgnored(update.ChatId) {
return
}
markupFunction := c.getFormatter() markupFunction := c.getFormatter()
defer c.updateLastMessageHash(update.ChatId, update.MessageId, update.NewContent) defer c.updateLastMessageHash(update.ChatId, update.MessageId, update.NewContent)
@ -353,6 +360,10 @@ func (c *Client) updateMessageContent(update *client.UpdateMessageContent) {
// message(s) deleted // message(s) deleted
func (c *Client) updateDeleteMessages(update *client.UpdateDeleteMessages) { func (c *Client) updateDeleteMessages(update *client.UpdateDeleteMessages) {
if update.IsPermanent { if update.IsPermanent {
if c.Session.IsChatIgnored(update.ChatId) {
return
}
var deleteChar string var deleteChar string
if c.Session.AsciiArrows { if c.Session.AsciiArrows {
deleteChar = "X " deleteChar = "X "