Initial implementation of on-enter ban
This commit is contained in:
@@ -61,3 +61,8 @@ Cуществует несколько видов пинга:
|
||||
------------------------------------------------------------------------------------------------------------------------
|
||||
Причину бана можно только включить или выключить в конфиге на per-chat основе. Фразу настроить невозможно. (не хотелось
|
||||
реализовывать парсинг темплейтов, как минимум даты-времени)
|
||||
|
||||
|
||||
Парсинг списка и бан здобеев на моменте входа в комнату
|
||||
------------------------------------------------------------------------------------------------------------------------
|
||||
Бан реализован только для real jid-ов, но не для nick-ов. Пока непонятно, как изящно хранить и то и другое.
|
||||
|
||||
19
README.md
19
README.md
@@ -55,25 +55,6 @@ make
|
||||
помещает этот jid в список изгнанных из комнаты (бан-лист конкретной комнаты, в которой присутствует бот) и выгоняет
|
||||
этого jid-а.
|
||||
|
||||
### В чём тонкость?
|
||||
|
||||
Ровно в том, что для исполнения действия, боту надо заметить presence участника, а presence посылается
|
||||
при изменении состояния клиента. То есть, если jid уже был в комнате на момент захода бота в неё, то его не забанят
|
||||
(во всяком случае моментально). Самый простой способ поместить таких персонажей в бан - просто кикнуть их. Ихний
|
||||
affiliation сменится на none, бот заметит presence и, если jid подпадает под правила чёрного списка, то отправит этот
|
||||
jid в бан.
|
||||
|
||||
Но, простите, ведь "по правилам", при заходе клиента в комнату боту присылается presence всех участников, чтобы он мог
|
||||
построить локальный ростер комнаты и начать отслеживать изменения состояния участников конференции. Почему бы в этот
|
||||
момент не банить злодеев?
|
||||
|
||||
Да, так делать можно, однако пока этого нет. Дело в том, что presence самого себя участнику приходит последним (после
|
||||
presence-ов тех, кто находится в комнате на момент появления в ней бота), и, согласно этому событию, можно считать, что
|
||||
клиент зашёл в комнату успешно и формально *только после этого*, клиент может что-то пытаться делать в этой комнате.
|
||||
Соответственно, чтобы кого-то банить исходя из списка presense-ов, который прилетает на входе, необходимо реализовать
|
||||
"одноразовую" очередь тех, кого имеет смысл забанить и разгрести её "когда будет можно". Эта задача факультативная,
|
||||
возможно, она будет решена потом.
|
||||
|
||||
## Как работает белый список?
|
||||
|
||||
Белый список - это список jid-ов, которых банить нельзя ни при каких обстоятельствах. В нём всегда неявно присутствует
|
||||
|
||||
@@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -488,6 +489,9 @@ func parseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
|
||||
// клиента забанят из-за спама "вошёл-вышел".
|
||||
// Однако, это не значит, что такую ситуацию мы не должны корректным образом обрабатывать.
|
||||
if v.Type == "unavailable" {
|
||||
// Считаем, что мы больше не в комнате, поэтому не знаем, кто там есть
|
||||
roomPresencesJid.Delete(v.From)
|
||||
|
||||
log.Error("Presence notification - looks like another instance of client leaves room")
|
||||
|
||||
if slices.Contains(roomsConnected, v.From) {
|
||||
@@ -499,20 +503,72 @@ func parseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
|
||||
log.Errorf(spew.Sdump(v))
|
||||
}
|
||||
} else {
|
||||
log.Infof("Presence notification, Type: %s, From: %s, To: %s Show: %s, Status: %s, Affiliation: %s, Role: %s, JID: %s",
|
||||
v.Type, v.From, v.To, v.Show, v.Status, v.Affiliation, v.Role, v.JID)
|
||||
log.Infof(
|
||||
"Presence notification, Type: %s, From: %s, To: %s Show: %s, Status: %s, Affiliation: %s, Role: %s, JID: %s",
|
||||
v.Type, v.From, v.To, v.Show, v.Status, v.Affiliation, v.Role, v.JID,
|
||||
)
|
||||
|
||||
room := strings.SplitN(v.From, "/", 2)[0]
|
||||
nick := strings.SplitN(v.From, "/", 2)[1]
|
||||
|
||||
if nick == "" {
|
||||
log.Infof("Presence stanza contains incorrect from attribute: %s", v.From)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Это наш собственный Presence
|
||||
if v.Show == "" && v.Status == "" {
|
||||
if from := strings.Split(v.From, "/"); from[1] == config.Jabber.Nick {
|
||||
roomsConnected = append(roomsConnected, from[0])
|
||||
if nick == config.Jabber.Nick {
|
||||
roomsConnected = append(roomsConnected, room)
|
||||
// На всякий случай дедуплицируем список комнат, к которым мы заджойнились.
|
||||
sort.Strings(roomsConnected)
|
||||
slices.Compact(roomsConnected)
|
||||
}
|
||||
}
|
||||
|
||||
if err := buny(v); err != nil {
|
||||
gTomb.Kill(err)
|
||||
switch v.Role {
|
||||
// Участник ушёл
|
||||
case "none":
|
||||
if namesInterface, present := roomPresencesJid.Get(room); present {
|
||||
var newNames []string
|
||||
names := interfaceToStringSlice(namesInterface)
|
||||
|
||||
return
|
||||
for _, name := range names {
|
||||
if name != v.JID {
|
||||
newNames = append(newNames, name)
|
||||
}
|
||||
}
|
||||
|
||||
if len(newNames) == 0 {
|
||||
roomPresencesJid.Delete(room)
|
||||
} else {
|
||||
roomPresencesJid.Set(room, newNames)
|
||||
}
|
||||
}
|
||||
// Участник пришёл
|
||||
default:
|
||||
var newNames []string
|
||||
|
||||
if namesInterface, present := roomPresencesJid.Get(room); present {
|
||||
names := interfaceToStringSlice(namesInterface)
|
||||
names = append(names, v.JID)
|
||||
roomPresencesJid.Set(room, names)
|
||||
} else {
|
||||
newNames = append(newNames, v.JID)
|
||||
roomPresencesJid.Set(room, newNames)
|
||||
}
|
||||
}
|
||||
|
||||
// Проверяем, а не злодей ли зашёл? Сделать это мы можем, только если мы находимся в комнате.
|
||||
// По правилам, мы можем что-то делать, только после того, как нам прилетит наш собственный presence, это
|
||||
// значит, что мы вошли в комнату.
|
||||
if slices.Contains(roomsConnected, room) {
|
||||
if err := buny(v); err != nil {
|
||||
gTomb.Kill(err)
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -66,4 +66,7 @@ var serverPingTimestampRx int64
|
||||
// Объектик для хранения стейта утилизатора горутинок.
|
||||
var gTomb tomb.Tomb
|
||||
|
||||
// sync.Map-ка со списком участников конференций.
|
||||
var roomPresencesJid *Collection
|
||||
|
||||
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */
|
||||
|
||||
43
lib.go
43
lib.go
@@ -6,6 +6,7 @@ import (
|
||||
"math/rand"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
@@ -580,6 +581,30 @@ func joinMuc(room string) {
|
||||
log.Infof("Joined to MUC: %s", room)
|
||||
|
||||
go RotateStatus(room)
|
||||
|
||||
// Время прoверить участников на предмет злобности
|
||||
namesInterface, present := roomPresencesJid.Get(room)
|
||||
|
||||
// Если room есть в списке presence-ов, то фигачим. room там должен быть, просто обязан.
|
||||
if present {
|
||||
for _, name := range interfaceToStringSlice(namesInterface) {
|
||||
var v xmpp.Presence
|
||||
v.From = name
|
||||
v.To = fmt.Sprintf("%s/%s", room, config.Jabber.Nick)
|
||||
v.ID = "fake_id"
|
||||
v.JID = v.From
|
||||
v.Affiliation = "member"
|
||||
v.Role = "participant"
|
||||
v.Priority = ""
|
||||
v.Show = ""
|
||||
v.Status = ""
|
||||
|
||||
// Оно там внутри всё само обработает, если вдруг возникнет wire error, то зарекконетится.
|
||||
_ = buny(v)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: тоже самое и для nick-ов
|
||||
}
|
||||
|
||||
// probeServerLiveness проверяет живость соединения с сервером. Для многих серверов обязательная штука, без которой
|
||||
@@ -822,4 +847,22 @@ func randomPhrase(list []string) string {
|
||||
return phrase
|
||||
}
|
||||
|
||||
// interfaceToStringSlice превращает данный интерфейс в слайс строк.
|
||||
// Если может, конечно :)
|
||||
func interfaceToStringSlice(iface interface{}) []string {
|
||||
var mySlice []string
|
||||
|
||||
// А теперь мы начинаем дурдом, нам надо превратить ёбанный interface{} в []string
|
||||
// Поскольку interface{} может быть чем угодно, перестрахуемся
|
||||
if reflect.TypeOf(iface).Kind() == reflect.Slice {
|
||||
shit := reflect.ValueOf(iface)
|
||||
|
||||
for i := 0; i < shit.Len(); i++ {
|
||||
mySlice = append(mySlice, fmt.Sprint(shit.Index(i)))
|
||||
}
|
||||
}
|
||||
|
||||
return mySlice
|
||||
}
|
||||
|
||||
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */
|
||||
|
||||
Reference in New Issue
Block a user