Skip to content
Snippets Groups Projects
Commit bc7ade96 authored by psy's avatar psy
Browse files

initial

parents
Branches
No related tags found
No related merge requests found
.idea/
__pycache__/
store/
venv/
config.toml
session.txt
\ No newline at end of file
config.py 0 → 100644
import simplematrixbotlib as botlib
from dataclasses import dataclass
@dataclass
class MyConfig(botlib.Config):
_homeserver: str = ""
_account: str = ""
_user: str = ""
_user_formatted: str = ""
_access_token: str = ""
_ratelimit_calls: int = 10
_ratelimit_period: int = 60
_report_maxlength: int = 500
_report_reaction: str = "👀"
_report_destination: str = ""
_ack_reaction: str = "✅️"
_metrics_port: int = 31337
@property
def homeserver(self) -> str:
return self._homeserver
@homeserver.setter
def homeserver(self, value: str) -> None:
self._homeserver = value
self._make_formatted_user()
@property
def user(self) -> str:
return self._user
@user.setter
def user(self, value: str) -> None:
self._user = value
self._account = value[1:].split(':')[0] # extract account from @account:homeserver
self._make_formatted_user()
def _make_formatted_user(self):
self._user_formatted: str = \
f"<a href=\"https://matrix.to/#/{self._user}\">{self._account}</a>"
@property
def account(self) -> str:
return self._account
@account.setter
def account(self, value: str) -> None:
self._account = value
@property
def user_formatted(self) -> str:
return self._user_formatted
@user_formatted.setter
def user_formatted(self, value: str) -> None:
self._user_formatted = value
@property
def access_token(self) -> str:
return self._access_token
@access_token.setter
def access_token(self, value: str) -> None:
self._access_token = value
@property
def ratelimit_calls(self) -> int:
return self._ratelimit_calls
@ratelimit_calls.setter
def ratelimit_calls(self, value: int) -> None:
self._ratelimit_calls = value
@property
def ratelimit_period(self) -> int:
return self._ratelimit_period
@ratelimit_period.setter
def ratelimit_period(self, value: int) -> None:
self._ratelimit_period = value
@property
def report_maxlength(self) -> int:
return self._report_maxlength
@report_maxlength.setter
def report_maxlength(self, value: int) -> None:
self._report_maxlength = value
@property
def report_reaction(self) -> str:
return self._report_reaction
@report_reaction.setter
def report_reaction(self, value: str) -> None:
self._report_reaction = value
@property
def report_destination(self) -> str:
return self._report_destination
@report_destination.setter
def report_destination(self, value: str) -> None:
self._report_destination = value
@property
def ack_reaction(self) -> str:
return self._ack_reaction
@ack_reaction.setter
def ack_reaction(self, value: str) -> None:
self._ack_reaction = value
@property
def metrics_port(self) -> int:
return self._metrics_port
@metrics_port.setter
def metrics_port(self, value: int) -> None:
self._metrics_port = value
#! /usr/bin/env python3
import simplematrixbotlib as botlib
from config import MyConfig
import time
from prometheus_client import Counter, start_http_server
from pprint import pprint
from nio import InviteMemberEvent
metrics_reports = Counter(
'draupnir_reports_total',
'Number of reports received',
['type', 'user', 'channel']
)
metrics_ratelimit = Counter(
'draupnir_reports_ratelimited_total',
'Number of reports not sent due to ratelimiting',
['type', 'user', 'channel']
)
config = MyConfig()
config.load_toml('config.toml')
start_http_server(config.metrics_port, addr='::1')
creds = botlib.Creds(
homeserver=config.homeserver,
username=config.account,
access_token=config.access_token
)
bot = botlib.Bot(creds, config)
calls_per_user = {}
own_user = ''
own_user_formatted = ''
class RateLimitException(Exception):
pass
def rate_limit_per_user(func):
def wrapper(user, *args, **kwargs):
global calls_per_user
current_time = int(time.time())
if user not in calls_per_user:
calls_per_user[user] = []
calls_per_user[user] = [timestamp for timestamp in calls_per_user[user] if
current_time - timestamp < config.ratelimit_period]
if len(calls_per_user[user]) >= config.ratelimit_calls:
raise RateLimitException('Rate Limited!')
calls_per_user[user].append(current_time)
return func(user, *args, **kwargs)
return wrapper
@rate_limit_per_user
async def send_to_mgmt_room(user, message):
await bot.api.send_markdown_message(config.report_destination, message)
@bot.listener.on_message_event
async def on_dm(room, message):
match = botlib.MessageMatch(room, message, bot, '!')
if match.is_not_from_this_bot() and match.is_from_allowed_user() and room.name is None:
print(f"DM from {message.sender}:\n> {message.body.replace('\n', '\n> ')}")
report = f"DM from [{message.sender}](https://matrix.to/#/{message.sender}):\n" \
f"> {message.body[:config.report_maxlength].replace('\n', '\n > ')}"
try:
await send_to_mgmt_room(message.sender, report)
metrics_reports.labels(type='dm', user=message.sender, channel="").inc()
await bot.api.send_reaction(room.room_id, message, config.report_reaction)
except RateLimitException:
print(f"RATELIMIT: {message.sender} exceeded rate limit.")
metrics_ratelimit.labels(type='dm', user=message.sender, channel="").inc()
await bot.api.send_text_message(
room.room_id,
"Hey, calm down a bit.\n\n"
"Please don't spam us with messages. If you need more space for your report, use a multiline message.\n\n"
"Wait a few minutes and try again."
)
@bot.listener.on_message_event
async def on_channel_message(room, message):
match = botlib.MessageMatch(room, message, bot, '?')
if (match.is_not_from_this_bot() and
match.is_from_allowed_user() and
room.name is not None and
room.room_id != config.report_destination and
(config.user in message.body or
(message.formatted_body is not None and config.user_formatted in message.formatted_body)
)
):
print(f"Mention in {room.name} ({room.room_id}) by {message.sender}:\n> {message.body.replace('\n', '\n> ')}")
report = f"Mention in [{room.name}](https://matrix.to/#/{room.room_id}) by " \
f"[{message.sender}](https://matrix.to/#/{message.sender}):\n" \
f"> {message.body[:config.report_maxlength].replace('\n', '\n > ')}"
try:
await send_to_mgmt_room(message.sender, report)
metrics_reports.labels(type='room', user=message.sender, channel=room.name).inc()
await bot.api.send_reaction(room.room_id, message, config.report_reaction)
except RateLimitException:
print(f"RATELIMIT: {message.sender} exceeded rate limit.")
metrics_ratelimit.labels(type='dm', user=message.sender, channel=room.name).inc()
pass
@bot.listener.on_message_event
async def block(room, message):
match = botlib.MessageMatch(room, message, bot, prefix='+')
if (room.room_id == config.report_destination
and match.is_not_from_this_bot()
and match.command() == 'block'
and 0 < len(match.args())):
blocklist = set()
for arg in match.args():
print(f"adding {arg} to blocklist")
blocklist.add(arg)
config.add_blocklist(blocklist)
pprint(config.blocklist)
config.save_toml('config.toml')
await bot.api.send_reaction(room.room_id, message, config.ack_reaction)
@bot.listener.on_message_event
async def unblock(room, message):
pprint(config.blocklist)
match = botlib.MessageMatch(room, message, bot, prefix='-')
if (room.room_id == config.report_destination
and match.is_not_from_this_bot()
and match.command() == 'block'
and 0 < len(match.args())):
blocklist = set()
for arg in match.args():
print(f"removing {arg} from blocklist")
blocklist.add(arg)
config.remove_blocklist(blocklist)
pprint(config.blocklist)
config.save_toml('config.toml')
await bot.api.send_reaction(room.room_id, message, config.ack_reaction)
@bot.listener.on_custom_event(InviteMemberEvent)
async def custom(room, event):
print(f"Invite from {event.sender} to {room.room_id}")
if event.content.get('is_direct', False):
print('.. joining ..')
await bot.api.async_client.join(room.room_id)
if __name__ == '__main__':
bot.run()
#simplematrixbotlib==2.12.0
git+https://codeberg.org/psy/simplematrixbotlib.git
argparse
prometheus_client
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment