Add initial flood prevention
This commit is contained in:
parent
8e16575d3b
commit
85a7967888
3 changed files with 39 additions and 2 deletions
|
@ -29,6 +29,10 @@ templates:
|
||||||
default_flags:
|
default_flags:
|
||||||
- ignorecase
|
- ignorecase
|
||||||
|
|
||||||
|
antispam:
|
||||||
|
max: 2
|
||||||
|
delay: 60
|
||||||
|
|
||||||
rules:
|
rules:
|
||||||
twim_cookies:
|
twim_cookies:
|
||||||
rooms: ["!FPUfgzXYWTKgIrwKxW:matrix.org"]
|
rooms: ["!FPUfgzXYWTKgIrwKxW:matrix.org"]
|
||||||
|
|
|
@ -13,9 +13,12 @@
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
from typing import Type, Tuple
|
from typing import Type, Tuple, Dict
|
||||||
|
import time
|
||||||
|
|
||||||
from mautrix.types import EventType, MessageType
|
from attr import dataclass
|
||||||
|
|
||||||
|
from mautrix.types import EventType, MessageType, UserID, RoomID
|
||||||
from mautrix.util.config import BaseProxyConfig
|
from mautrix.util.config import BaseProxyConfig
|
||||||
|
|
||||||
from maubot import Plugin, MessageEvent
|
from maubot import Plugin, MessageEvent
|
||||||
|
@ -24,8 +27,27 @@ from maubot.handlers import event
|
||||||
from .config import Config, ConfigError
|
from .config import Config, ConfigError
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class FloodInfo:
|
||||||
|
rb: 'ReactBot'
|
||||||
|
count: int
|
||||||
|
last_message: int
|
||||||
|
|
||||||
|
def bump(self) -> bool:
|
||||||
|
now = int(time.time())
|
||||||
|
if self.last_message + self.rb.config["antispam.delay"] < now:
|
||||||
|
self.count = 0
|
||||||
|
self.count += 1
|
||||||
|
if self.count > self.rb.config["antispam.max"]:
|
||||||
|
return True
|
||||||
|
self.last_message = now
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
class ReactBot(Plugin):
|
class ReactBot(Plugin):
|
||||||
allowed_msgtypes: Tuple[MessageType, ...] = (MessageType.TEXT, MessageType.EMOTE)
|
allowed_msgtypes: Tuple[MessageType, ...] = (MessageType.TEXT, MessageType.EMOTE)
|
||||||
|
user_flood: Dict[UserID, FloodInfo]
|
||||||
|
room_flood: Dict[RoomID, FloodInfo]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_config_class(cls) -> Type[BaseProxyConfig]:
|
def get_config_class(cls) -> Type[BaseProxyConfig]:
|
||||||
|
@ -33,6 +55,8 @@ class ReactBot(Plugin):
|
||||||
|
|
||||||
async def start(self) -> None:
|
async def start(self) -> None:
|
||||||
await super().start()
|
await super().start()
|
||||||
|
self.user_flood = {}
|
||||||
|
self.room_flood = {}
|
||||||
self.on_external_config_update()
|
self.on_external_config_update()
|
||||||
|
|
||||||
def on_external_config_update(self) -> None:
|
def on_external_config_update(self) -> None:
|
||||||
|
@ -42,6 +66,11 @@ class ReactBot(Plugin):
|
||||||
except ConfigError:
|
except ConfigError:
|
||||||
self.log.exception("Failed to load config")
|
self.log.exception("Failed to load config")
|
||||||
|
|
||||||
|
def is_flood(self, evt: MessageEvent) -> bool:
|
||||||
|
uf = self.user_flood.setdefault(evt.sender, FloodInfo(rb=self, count=0, last_message=0))
|
||||||
|
rf = self.room_flood.setdefault(evt.room_id, FloodInfo(rb=self, count=0, last_message=0))
|
||||||
|
return uf.bump() or rf.bump()
|
||||||
|
|
||||||
@event.on(EventType.ROOM_MESSAGE)
|
@event.on(EventType.ROOM_MESSAGE)
|
||||||
async def event_handler(self, evt: MessageEvent) -> None:
|
async def event_handler(self, evt: MessageEvent) -> None:
|
||||||
if evt.sender == self.client.mxid or evt.content.msgtype not in self.allowed_msgtypes:
|
if evt.sender == self.client.mxid or evt.content.msgtype not in self.allowed_msgtypes:
|
||||||
|
@ -49,6 +78,8 @@ class ReactBot(Plugin):
|
||||||
for name, rule in self.config.rules.items():
|
for name, rule in self.config.rules.items():
|
||||||
match = rule.match(evt)
|
match = rule.match(evt)
|
||||||
if match is not None:
|
if match is not None:
|
||||||
|
if self.is_flood(evt):
|
||||||
|
return
|
||||||
try:
|
try:
|
||||||
await rule.execute(evt, match)
|
await rule.execute(evt, match)
|
||||||
except Exception:
|
except Exception:
|
||||||
|
|
|
@ -37,6 +37,8 @@ class Config(BaseProxyConfig):
|
||||||
helper.copy("rules")
|
helper.copy("rules")
|
||||||
helper.copy("templates")
|
helper.copy("templates")
|
||||||
helper.copy("default_flags")
|
helper.copy("default_flags")
|
||||||
|
helper.copy("antispam.max")
|
||||||
|
helper.copy("antispam.delay")
|
||||||
|
|
||||||
def parse_data(self) -> None:
|
def parse_data(self) -> None:
|
||||||
self.default_flags = re.RegexFlag(0)
|
self.default_flags = re.RegexFlag(0)
|
||||||
|
|
Loading…
Reference in a new issue