Skip to content

Commit

Permalink
add info on the original message as forwarded to admin chat for ban
Browse files Browse the repository at this point in the history
  • Loading branch information
umputun committed Dec 18, 2023
1 parent 737656c commit ef72a6d
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 24 deletions.
26 changes: 21 additions & 5 deletions app/events/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ func (l *TelegramListener) procEvents(update tbapi.Update) error {
}

log.Printf("[DEBUG] incoming msg: %+v", strings.ReplaceAll(msg.Text, "\n", " "))
l.Locator.AddMessage(update.Message.Text, fromChat, msg.From.ID, msg.ID) // save message to locator
l.Locator.AddMessage(update.Message.Text, fromChat, msg.From.ID, msg.From.Username, msg.ID) // save message to locator
resp := l.Bot.OnMessage(*msg)

// send response to the channel if allowed
Expand Down Expand Up @@ -259,9 +259,8 @@ func (l *TelegramListener) adminChatMsgHandler(update tbapi.Update) error {
log.Printf("[DEBUG] message from admin chat: msg id: %d, update id: %d, from: %s, sender: %s",
update.Message.MessageID, update.UpdateID, update.Message.From.UserName, update.Message.ForwardSenderName)

if update.Message.ForwardSenderName == "" && update.FromChat() == nil {
if update.Message.ForwardSenderName == "" && update.Message.ForwardFrom == nil {
// this is a regular message from admin chat, not the forwarded one, ignore it
log.Printf("[DEBUG] message from admin chat, but not forwarded, ignore it, %+v", update.Message)
return nil
}

Expand All @@ -279,10 +278,29 @@ func (l *TelegramListener) adminChatMsgHandler(update tbapi.Update) error {
}

log.Printf("[DEBUG] locator found message %s", info)
errs := new(multierror.Error)

// remove user from the approved list
l.Bot.RemoveApprovedUsers(info.userID)

// make message with spam info and send to admin chat
spamInfo := []string{}
resp := l.Bot.OnMessage(bot.Message{Text: update.Message.Text, From: bot.User{ID: info.userID}})
spamInfoText := "**can't get spam info**"
for _, check := range resp.CheckResults {
spamInfo = append(spamInfo, "- "+check.String())
}
if len(spamInfo) > 0 {
spamInfoText = strings.Join(spamInfo, "\n")
}
newMsgText := fmt.Sprintf("**original detection results for %q (%d)**\n\n%s\n\n\n*the user banned and message deleted*",
info.userName, info.userID, spamInfoText)
msg := tbapi.NewMessage(l.adminChatID, newMsgText)
msg.ParseMode = tbapi.ModeMarkdown
if _, err := l.TbAPI.Send(msg); err != nil {
errs = multierror.Append(errs, fmt.Errorf("failed to send spap detection results to admin chat: %w", err))
}

// update spam samples
if !l.Dry {
if err := l.Bot.UpdateSpam(msgTxt); err != nil {
Expand All @@ -295,8 +313,6 @@ func (l *TelegramListener) adminChatMsgHandler(update tbapi.Update) error {
return nil
}

errs := new(multierror.Error)

// delete message
if _, err := l.TbAPI.Request(tbapi.DeleteMessageConfig{ChatID: l.chatID, MessageID: info.msgID}); err != nil {
errs = multierror.Append(errs, fmt.Errorf("failed to delete message %d: %w", info.msgID, err))
Expand Down
9 changes: 6 additions & 3 deletions app/events/events_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,7 @@ func TestTelegramListener_DoWithForwarded(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 500*time.Minute)
defer cancel()

l.Locator.AddMessage("text 123", 123, 88, 999999) // add message to locator
l.Locator.AddMessage("text 123", 123, 88, "user", 999999) // add message to locator

updMsg := tbapi.Update{
Message: &tbapi.Message{
Expand All @@ -418,15 +418,18 @@ func TestTelegramListener_DoWithForwarded(t *testing.T) {
err := l.Do(ctx)
assert.EqualError(t, err, "telegram update chan closed")
assert.Equal(t, 0, len(mockLogger.SaveCalls()))
require.Equal(t, 1, len(mockAPI.SendCalls()))

require.Equal(t, 2, len(mockAPI.SendCalls()))
assert.Equal(t, "startup", mockAPI.SendCalls()[0].C.(tbapi.MessageConfig).Text)
assert.Contains(t, mockAPI.SendCalls()[1].C.(tbapi.MessageConfig).Text, "detection results")
assert.Equal(t, int64(123), mockAPI.SendCalls()[1].C.(tbapi.MessageConfig).ChatID)

require.Equal(t, 1, len(b.UpdateSpamCalls()))
assert.Equal(t, "text 123", b.UpdateSpamCalls()[0].Msg)

assert.Equal(t, 2, len(mockAPI.RequestCalls()))
assert.Equal(t, int64(123), mockAPI.RequestCalls()[0].C.(tbapi.DeleteMessageConfig).ChatID)
assert.Equal(t, 999999, mockAPI.RequestCalls()[0].C.(tbapi.DeleteMessageConfig).MessageID)

assert.Equal(t, int64(123), mockAPI.RequestCalls()[1].C.(tbapi.RestrictChatMemberConfig).ChatID)
assert.Equal(t, int64(88), mockAPI.RequestCalls()[1].C.(tbapi.RestrictChatMemberConfig).UserID)

Expand Down
23 changes: 13 additions & 10 deletions app/events/locator.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,11 @@ type Locator struct {

// MsgMeta stores message metadata
type MsgMeta struct {
time time.Time
chatID int64
userID int64
msgID int
time time.Time
chatID int64
userID int64
userName string
msgID int
}

// SpamData stores spam data for a given user
Expand All @@ -42,7 +43,8 @@ type SpamData struct {
}

func (m MsgMeta) String() string {
return fmt.Sprintf("{chatID: %d, userID: %d, msgID: %d, time: %s}", m.chatID, m.userID, m.msgID, m.time.Format(time.RFC3339))
return fmt.Sprintf("{chatID: %d, user name: %s, userID: %d, msgID: %d, time: %s}",
m.chatID, m.userName, m.userID, m.msgID, m.time.Format(time.RFC3339))
}

func (s SpamData) String() string {
Expand Down Expand Up @@ -80,12 +82,13 @@ func (l *Locator) MsgHash(msg string) string {
// Messages are removed the total number of messages exceeds minSize and the last cleanup was performed more than cleanupDuration ago.
// The reason for minSize is to avoid removing messages on low-traffic chats where admin visits are rare.
// Note: removes old messages only once per cleanupDuration and only if a new message is added
func (l *Locator) AddMessage(msg string, chatID, userID int64, msgID int) {
func (l *Locator) AddMessage(msg string, chatID, userID int64, userName string, msgID int) {
l.msgs.data[l.MsgHash(msg)] = MsgMeta{
time: time.Now(),
chatID: chatID,
userID: userID,
msgID: msgID,
time: time.Now(),
chatID: chatID,
userID: userID,
userName: userName,
msgID: msgID,
}

if time.Since(l.msgs.lastRemoval) < l.cleanupDuration || len(l.msgs.data) <= l.minSize {
Expand Down
14 changes: 8 additions & 6 deletions app/events/locator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,15 @@ func TestGetMessage(t *testing.T) {
chatID := int64(123)
userID := int64(456)
msgID := 7890
locator.AddMessage(msg, chatID, userID, msgID)
locator.AddMessage(msg, chatID, userID, "user", msgID)

// test retrieval of existing message
info, found := locator.Message("test message")
require.True(t, found)
assert.Equal(t, msgID, info.msgID)
assert.Equal(t, chatID, info.chatID)
assert.Equal(t, userID, info.userID)
assert.Equal(t, "user", info.userName)

// test retrieval of non-existing message
_, found = locator.Message("no such message") // non-existing msgID
Expand Down Expand Up @@ -96,18 +97,19 @@ func TestAddMessageAndCleanup(t *testing.T) {
chatID := int64(123)
userID := int64(456)
msgID := 7890
locator.AddMessage(msg, chatID, userID, msgID)
locator.AddMessage(msg, chatID, userID, "user1", msgID)

hash := locator.MsgHash(msg)
meta, exists := locator.msgs.data[hash]
require.True(t, exists)
assert.Equal(t, chatID, meta.chatID)
assert.Equal(t, userID, meta.userID)
assert.Equal(t, "user1", meta.userName)
assert.Equal(t, msgID, meta.msgID)

// wait for cleanup duration and add another message to trigger cleanup
time.Sleep(cleanupDuration + time.Second)
locator.AddMessage("another message", 789, 555, 1011)
locator.AddMessage("another message", 789, 555, "user2", 1011)

_, existsAfterCleanup := locator.msgs.data[hash]
assert.False(t, existsAfterCleanup)
Expand All @@ -124,7 +126,7 @@ func TestAddAndCleanup_withMinSize(t *testing.T) {
chatID := int64(123)
userID := int64(456)
msgID := 7890
locator.AddMessage(msg, chatID, userID, msgID) // add first message
locator.AddMessage(msg, chatID, userID, "user1", msgID) // add first message

hash := locator.MsgHash(msg)
meta, exists := locator.msgs.data[hash]
Expand All @@ -135,13 +137,13 @@ func TestAddAndCleanup_withMinSize(t *testing.T) {

// wait for cleanup duration and add another message to trigger cleanup
time.Sleep(cleanupDuration + time.Millisecond*200)
locator.AddMessage("second message", 789, 555, 1011)
locator.AddMessage("second message", 789, 555, "user2", 1011)
_, existsAfterCleanup := locator.msgs.data[hash]
assert.True(t, existsAfterCleanup, "minSize should prevent cleanup")

// wait for cleanup duration and add another message to trigger cleanup
time.Sleep(cleanupDuration + +time.Millisecond*200)
locator.AddMessage("third message", chatID, 9000, 2000)
locator.AddMessage("third message", chatID, 9000, "user3", 2000)
_, existsAfterCleanup = locator.msgs.data[hash]
assert.False(t, existsAfterCleanup, "minSize should allow cleanup")

Expand Down

0 comments on commit ef72a6d

Please sign in to comment.