Files
bunny-bot/internal/moon/telegram.go
eleksir 0c34b6e781 Global code ovehaul - remove CAS and other, add 15 minute greet check
- Remove CAS, as it no longer works for us
- Remove other checks
- Implement 15 minute greet timeout check
- Fix chat logos
- Ditch thirdparty logger, use stdlib log facility wrapper for logging
2026-10-20 12:20:16 +03:00

112 lines
3.3 KiB
Go

package moon
import (
"bunny-bot/internal/elog"
"bunny-bot/internal/mylog"
"github.com/NicoNex/echotron/v3"
"github.com/davecgh/go-spew/spew"
)
// Telegram main goroutine that implements bot.
//
// It blocks until a termination signal stops it, so the update listener can be
// shut down in a defined order rather than simply abandoned when the process
// exits.
func Telegram(cfg MyConfig) {
// A signal can arrive during startup; then this must not start at all.
if !startWorker() {
mylog.Info("Telegram not started: shutdown already in progress")
return
}
defer stopWorker()
Tg = echotron.NewAPI(cfg.Token)
mylog.Info("Starting api update poller loop")
// The sweeper calls the API too, so it may only start once Tg exists.
mylog.Info("Starting greeting sweeper periodic task")
go SweepGreetings()
updates := echotron.PollingUpdatesOptions(cfg.Token, true, echotron.UpdateOptions{
AllowedUpdates: allowedUpdates,
})
for {
select {
// Stop between updates, never inside one: the handler currently running
// finishes, so nothing is left half-processed when we return.
case <-stopRequested:
mylog.Info("Telegram update listener stopped")
return
case u, ok := <-updates:
if !ok {
mylog.Info("Telegram update poller ended")
return
}
telegramMsgParser(u)
}
}
}
// telegramMsgParser parses events generated by echotron.
func telegramMsgParser(msg *echotron.Update) {
// The one place an update enters the program, and therefore the one place an
// entity with no id is caught. Everything downstream — the caches, the sweeper,
// the bans — may then assume the ids it holds are real.
if !usableUpdate(msg) {
reportBrokenUpdate(msg)
return
}
switch {
// Membership status changes: joins, leaves, bans, promotions. Logged only —
// moderation is the sweeper's job, on a timer, not the dispatcher's.
//
// This also catches the membership updates caused by our own bans, so every
// ban leaves a second record in the chat log.
case msg.ChatMember != nil:
elog.LogUserStatus(msg.ChatMember)
// New members entered to chat. Checked before the text case so that a
// join service message is never mistaken for ordinary chatter.
case msg.Message != nil && msg.Message.NewChatMembers != nil:
newChatMembers(msg)
// Ordinary text messages, used as greeting answers.
case msg.Message != nil && msg.Message.Text != "":
textMessage(msg)
// Everything else that is still a message: a photo with no caption, a
// sticker, a location, a leave, a title change. Logged, and deliberately NOT
// recorded as a greeting answer — an attachment is not an introduction, and
// letting one satisfy the detector would be a one-key bypass for a spammer.
//
// The condition is deliberately just "there is a message": elog already
// renders every media and service field, and already writes nothing for a
// message with no describable content, so emptiness is its decision to make.
// Testing for a field being non-empty here is what lost these messages.
case msg.Message != nil:
logOnlyMessage(msg)
// An edit revises a message already recorded, so it is a chat-log event
// rather than a moderation one.
case msg.EditedMessage != nil:
elog.LogEditedMessage(msg.EditedMessage)
default:
mylog.Debug(spew.Sdump(msg))
}
}
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */