func (x *opBot) markAsPendingCaptcha(bot tgbotInterface, user tgbotapi.User, code int) {
	log.Printf("Adding user to the pending-captcha list: %q, uid=%d\n", formatName(user), user.ID)
	x.pendingCaptcha.set(user.ID, botCaptcha{
		code:       code,
		expiration: time.Now().Add(x.captchaTime),
	})
}
markAsPendingCaptcha marks the user status as pending Captcha response.
package main
import (
	"bytes"
	"fmt"
	"github.com/dchest/captcha"
	"github.com/go-telegram-bot-api/telegram-bot-api"
	"image/png"
	"log"
	"math/rand"
	"regexp"
	"strconv"
	"strings"
	"sync"
	"time"
	"unicode"
)
const (
	captchaWidth  = 400
	captchaHeight = 240
)
var captchaRegex *regexp.Regexp
func init() {
	captchaRegex = regexp.MustCompile(`\d{4}`)
}
type botCaptcha struct {
	code       int
	expiration time.Time
}
type pendingCaptchaType struct {
	sync.RWMutex
	users map[int]botCaptcha
}
func (x *pendingCaptchaType) set(userID int, captcha botCaptcha) {
	x.Lock()
	x.users[userID] = captcha
	x.Unlock()
}
func (x *pendingCaptchaType) get(userID int) (botCaptcha, bool) {
	x.Lock()
	exp, ok := x.users[userID]
	x.Unlock()
	return exp, ok
}
func (x *pendingCaptchaType) del(userID int) {
	x.Lock()
	delete(x.users, userID)
	x.Unlock()
}
func newPendingCaptchaType() *pendingCaptchaType {
	return &pendingCaptchaType{
		users: map[int]botCaptcha{},
	}
}
func (x *opBot) sendCaptcha(bot tgbotInterface, update tgbotapi.Update, user tgbotapi.User) {
	promCaptchaCount.Inc()
	if user.IsBot {
		return
	}
	name := nameRef(user)
	code := rand.Int() % 10000
	x.markAsPendingCaptcha(bot, user, code)
	log.Printf("Sending captcha %04.4d to user %s (uid=%d)", code, name, user.ID)
	fb, err := genCaptchaImage(code)
	if err != nil {
		log.Printf("Warning: Unable to generate captcha image. Ignoring")
		return
	}
	msg, err := sendPhotoReply(bot, update.Message.Chat.ID, update.Message.MessageID, fb, fmt.Sprintf(T("enter_captcha"), name))
	if err != nil {
		log.Printf("Warning: Unable to send captcha message: %v", err)
		return
	}
	selfDestructMessage(bot, msg.Chat.ID, msg.MessageID, x.captchaTime+time.Duration(10*time.Second))
}
func genCaptchaImage(code int) (tgbotapi.FileBytes, error) {
	var ret tgbotapi.FileBytes
	if code < 0 || code > 9999 {
		return ret, fmt.Errorf("captcha code must be between 0 and 9999, got %d", code)
	}
	codeStr := fmt.Sprintf("%04.4d", code)
	img := captcha.NewImage(codeStr, bincode(codeStr), captchaWidth, captchaHeight)
	var buf bytes.Buffer
	if err := png.Encode(&buf, img); err != nil {
		log.Printf("Unable to generate image captcha: %v", err)
		return ret, err
	}
	ret = tgbotapi.FileBytes{
		Name:  "captcha",
		Bytes: buf.Bytes(),
	}
	buf.Reset()
	return ret, nil
}
func (x *opBot) captchaReaper(bot tgbotInterface, update tgbotapi.Update, user tgbotapi.User) {
	time.AfterFunc(x.captchaTime, func() {
		_, ok := x.pendingCaptcha.get(user.ID)
		if !ok {
			return
		}
		promCaptchaFailedCount.Inc()
		name := nameRef(user)
		_, err := sendMessage(bot, update.Message.Chat.ID, fmt.Sprintf(T("no_captcha_received"), name))
		if err != nil {
			log.Printf("Warning: Unable to send 'invalid captcha' message.")
		}
		banned, err := isBanned(bot, update.Message.Chat.ID, user.ID)
		if err != nil {
			log.Printf("Warning: Unable to get information for user %s (uid=%d): %v", name, user.ID, err)
		}
		if err = kickUser(bot, update.Message.Chat.ID, user.ID); err != nil {
			log.Printf("Warning: Unable to kick user %s (uid=%d) out of the channel.", name, user.ID)
		}
		x.pendingCaptcha.del(user.ID)
		if banned {
			log.Printf("User %s (uid=%d) has already been banned (by admin?) Not unbanning.", name, user.ID)
			return
		}
		if err = unBanUser(bot, update.Message.Chat.ID, user.ID); err != nil {
			log.Printf("Warning: Unable to UNBAN user %s (uid=%d) (May be locked out.)", name, user.ID)
		}
	})
}