Merge branch 'master' into adhoc
This commit is contained in:
commit
4eae44b9a2
|
@ -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"
|
||||||
|
|
|
@ -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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -94,8 +94,8 @@ var chatCommands = map[string]command{
|
||||||
"invite": command{1, []string{"id or @username"}, "add user to current chat", ¬ForPM},
|
"invite": command{1, []string{"id or @username"}, "add user to current chat", ¬ForPM},
|
||||||
"link": command{0, []string{}, "get invite link for current chat", ¬ForPM},
|
"link": command{0, []string{}, "get invite link for current chat", ¬ForPM},
|
||||||
"kick": command{1, []string{"id or @username"}, "remove user from current chat", ¬ForPM},
|
"kick": command{1, []string{"id or @username"}, "remove user from current chat", ¬ForPM},
|
||||||
"mute": command{1, []string{"id or @username", "hours"}, "mute user in current chat", ¬ForPMAndBasic},
|
"mute": command{0, []string{"id or @username", "hours"}, "mute the whole chat or a user in current chat", ¬ForPMAndBasic},
|
||||||
"unmute": command{1, []string{"id or @username"}, "unrestrict user from current chat", ¬ForPMAndBasic},
|
"unmute": command{0, []string{"id or @username"}, "unmute the whole chat or a user in the current chat", ¬ForPMAndBasic},
|
||||||
"ban": command{1, []string{"id or @username", "hours"}, "restrict @username from current chat for [hours] or forever", ¬ForPM},
|
"ban": command{1, []string{"id or @username", "hours"}, "restrict @username from current chat for [hours] or forever", ¬ForPM},
|
||||||
"unban": command{1, []string{"id or @username"}, "unbans @username in current chat (and devotes from admins)", ¬ForPM},
|
"unban": command{1, []string{"id or @username"}, "unbans @username in current chat (and devotes from admins)", ¬ForPM},
|
||||||
"promote": command{1, []string{"id or @username", "title"}, "promote user to admin in current chat", ¬ForPM},
|
"promote": command{1, []string{"id or @username", "title"}, "promote user to admin in current chat", ¬ForPM},
|
||||||
|
@ -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":
|
||||||
|
|
|
@ -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 "
|
||||||
|
|
Loading…
Reference in a new issue