Anti-spam bot for Telegram in Go

Posted by Pavel on Friday, April 23, 2021

Introduction

This article in Russian

Before starting to write my bot, I tried to use the existing ones, but they all had their own shortcomings. Since I was the admin of the Russian-language geo-chat in Telegram, I needed a solution that would cope with a large number of bots without polluting the message feed with other messages that one user was blocked or deleted

Cons of existing bots:

  • Large informational messages of the type: “The message was deleted because …", which does not work well where there are a lot of spam bots

  • Checking for captcha does not solve the problems of English-speaking merchant bots that solve a simple captcha and then insert their spam message. I needed a solution that would only leave Russian-speaking users

Decision

The solution was to write your own bot, which asked questions like:

example

The user needs to answer correctly, other messages will be deleted

How do I add this bot to a chat?

First you need to invite this bot to chat by its nickname: @spam_fighter_bot

Bot link: https://t.me/spam_fighter_bot

  1. Add the bot to the chat:

add-bot-to-chat

  1. After that, add the bot as an admin in the chat settings:

add-bot-as-admin

  1. Give the bot access rights to delete messages and ban users:

add-bot-as-admin

Done!

Code

I chose Go as the language for bot and to work with messages in telegrams, I decided to use this library 🔗tucnak/telebot I have worked with before and which seems to me very convenient

First, you need to configure and launch a new bot:

You need to add to the imports:

import (
	tb "gopkg.in/tucnak/telebot.v2"
)

And this is how our bot is launched

	// initialize bot
	b, err := tb.NewBot(tb.Settings{
		Token:  "YOUR_TOKEN",
		Poller: &tb.LongPoller{Timeout: 10 * time.Second},
	})
	if err != nil {
		fmt.Printf("error while initializing bot, %v", err)
	}

In order to track when a new user enters the chat, you need to add handling of these events:

    b.Handle(tb.OnUserJoined, UserJoined(l, b, memoryStorage))

And this is how the UserJoined function itself looks like:

func UserJoined(l *zap.SugaredLogger, b *tb.Bot, s data.Storage) func(m *tb.Message) {
	return func(m *tb.Message) {
		r := rand.New(rand.NewSource(time.Now().UnixNano()))

		// generating two random small number
		firstNumber := r.Intn(4) + 1
		secondNumber := r.Intn(4) + 1
		fistNumberInWords := ntw.IntegerToRuRu(firstNumber)
		secondNumberInWords := ntw.IntegerToRuRu(secondNumber)

		username := getUsername(m.Sender)
		welcomeMessageText := getWelcomeMessageText(username, m.Chat.Title, fistNumberInWords, secondNumberInWords)
		welcomeMessage, err := b.Send(m.Chat, welcomeMessageText)
		if err != nil {
			l.Error("error while sending welcome message", err)
			return
		}
		s.Add(m.Chat, m.Sender, data.Info{WelcomeMessage: welcomeMessage, RightAnswer: firstNumber * secondNumber})

		// Goroutine to delete message after 2 minutes
		// and block user if he or she still in the list
		go checkAndBanUser(l, b, welcomeMessage, s, m, username)
	}
}

It will generate two random numbers and convert them to text. The correct answer is recorded in memory. Then, in the goroutine, 2 minutes are expected from the message sending, if the user still has not solved the correct answer, then he is blocked and removed from the chat

This is what the code looks like to check the user’s response:

func Text(l *zap.SugaredLogger, b *tb.Bot, s data.Storage) func(m *tb.Message) {
	return func(m *tb.Message) {
		info, ok := s.Exist(m.Chat, m.Sender)
		if !ok {
			return
		}
		if m.Text != strconv.Itoa(info.RightAnswer) {
			err := b.Delete(m)
			if err != nil {
				l.Errorf("error while deleting (spam) user message: %v", err)
			}
			return
		}
		// in case of correct answer:
		s.Remove(m.Chat, m.Sender)

		// Correct! Tell us about yourself
		approveMessage, err := b.Send(m.Chat, "Верно! Расскажите нам о себе 🙂")
		if err != nil {
			l.Errorf("error while sending: %v", err)
		}
		go deleteWelcomeMessages(l, b, m, approveMessage, info.WelcomeMessage)
	}
}

Feedback

As a result, the bot very successfully bans other bots and spammers, preventing them from writing a message with an advertisement. No one translates messages into English and therefore does not know exactly what to do. Thus, for now, it turns out to block all bots, although before with other anti-spam bots 50% just solved captcha and wrote spam

feedback

Source

You can find the source code, which is then deployed to the server, here:

🔗GitHub

You can suggest your changes and improvements, the code is open source

After the merge, the code is automatically deployed to production (if the build is successful)


comments powered by Disqus