From 952672946b01de13fdf0bcaf7ada25397401a505 Mon Sep 17 00:00:00 2001 From: AlberLC Date: Wed, 25 May 2022 00:46:32 +0200 Subject: [PATCH] Add Discord bot --- flanabot/bots/flana_bot.py | 419 +++++++++++++++-------------- flanabot/bots/flana_disc_bot.py | 70 ++--- flanabot/bots/flana_tele_bot.py | 54 +--- flanabot/constants.py | 44 +-- flanabot/exceptions.py | 6 - flanabot/main.py | 3 + flanabot/models/__init__.py | 4 +- flanabot/models/chat.py | 11 +- flanabot/models/enums.py | 8 + flanabot/models/message.py | 3 +- flanabot/models/punishment.py | 8 + flanabot/models/punishments.py | 23 -- flanabot/models/user.py | 22 -- flanabot/models/weather_chart.py | 2 +- requirements.txt | 2 +- tests/unit/test_parse_callbacks.py | 5 +- 16 files changed, 273 insertions(+), 411 deletions(-) delete mode 100644 flanabot/exceptions.py create mode 100644 flanabot/models/enums.py create mode 100644 flanabot/models/punishment.py delete mode 100644 flanabot/models/punishments.py delete mode 100644 flanabot/models/user.py diff --git a/flanabot/bots/flana_bot.py b/flanabot/bots/flana_bot.py index 44d004c..02d4a6d 100644 --- a/flanabot/bots/flana_bot.py +++ b/flanabot/bots/flana_bot.py @@ -1,12 +1,10 @@ import asyncio import datetime -import itertools -import pprint import random import time as time_module from abc import ABC from asyncio import Future -from typing import Iterable, Iterator, Type +from typing import Iterable import flanaapis.geolocation.functions import flanaapis.weather.functions @@ -14,136 +12,123 @@ import flanautils import plotly.graph_objects from flanaapis import InstagramLoginError, MediaNotFoundError, Place, PlaceNotFoundError, WeatherEmoji, instagram, tiktok, twitter from flanautils import Media, MediaType, NotFoundError, OrderedSet, Source, TimeUnits, TraceMetadata, return_if_first_empty -from multibot import Action, BotAction, MultiBot, SendError, admin, bot_mentioned, constants as multibot_constants, group, ignore_self_message, inline, reply +from multibot import Action, BadRoleError, BotAction, MultiBot, SendError, User, admin, bot_mentioned, constants as multibot_constants, group, ignore_self_message, inline, reply from flanabot import constants -from flanabot.exceptions import BadRoleError, UserDisconnectedError -from flanabot.models.chat import Chat -from flanabot.models.message import Message -from flanabot.models.punishments import Mute, Punishment, PunishmentBase -from flanabot.models.weather_chart import WeatherChart +from flanabot.models import ButtonsMessageType, Chat, Message, Punishment, WeatherChart # ----------------------------------------------------------------------------------------------------- # # --------------------------------------------- FLANA_BOT --------------------------------------------- # # ----------------------------------------------------------------------------------------------------- # - - class FlanaBot(MultiBot, ABC): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.lock = asyncio.Lock() + # ----------------------------------------------------------- # # -------------------- PROTECTED METHODS -------------------- # # ----------------------------------------------------------- # def _add_handlers(self): super()._add_handlers() - self.register(self._on_bye, constants.KEYWORDS['bye']) + self.register(self._on_bye, multibot_constants.KEYWORDS['bye']) - self.register(self._on_config_list_show, constants.KEYWORDS['config']) - self.register(self._on_config_list_show, constants.KEYWORDS['help']) - self.register(self._on_config_list_show, (constants.KEYWORDS['show'], constants.KEYWORDS['config'])) - self.register(self._on_config_list_show, (constants.KEYWORDS['help'], constants.KEYWORDS['config'])) - self.register(self._on_config_list_show, (constants.KEYWORDS['show'], constants.KEYWORDS['help'])) - self.register(self._on_config_list_show, (constants.KEYWORDS['show'], constants.KEYWORDS['help'], constants.KEYWORDS['config'])) + self.register(self._on_config_list_show, multibot_constants.KEYWORDS['config']) + self.register(self._on_config_list_show, (multibot_constants.KEYWORDS['show'], multibot_constants.KEYWORDS['config'])) self.register(self._on_covid_chart, constants.KEYWORDS['covid_chart']) self.register(self._on_currency_chart, constants.KEYWORDS['currency_chart']) - self.register(self._on_currency_chart, (constants.KEYWORDS['show'], constants.KEYWORDS['currency_chart'])) + self.register(self._on_currency_chart, (multibot_constants.KEYWORDS['show'], constants.KEYWORDS['currency_chart'])) - self.register(self._on_currency_chart_config_activate, (constants.KEYWORDS['activate'], constants.KEYWORDS['currency_chart'])) - self.register(self._on_currency_chart_config_activate, (constants.KEYWORDS['activate'], constants.KEYWORDS['currency_chart'], constants.KEYWORDS['config'])) + self.register(self._on_currency_chart_config_activate, (multibot_constants.KEYWORDS['activate'], constants.KEYWORDS['currency_chart'])) + self.register(self._on_currency_chart_config_activate, (multibot_constants.KEYWORDS['activate'], constants.KEYWORDS['currency_chart'], multibot_constants.KEYWORDS['config'])) - self.register(self._on_currency_chart_config_change, (constants.KEYWORDS['change'], constants.KEYWORDS['currency_chart'])) - self.register(self._on_currency_chart_config_change, (constants.KEYWORDS['change'], constants.KEYWORDS['currency_chart'], constants.KEYWORDS['config'])) + self.register(self._on_currency_chart_config_change, (multibot_constants.KEYWORDS['change'], constants.KEYWORDS['currency_chart'])) + self.register(self._on_currency_chart_config_change, (multibot_constants.KEYWORDS['change'], constants.KEYWORDS['currency_chart'], multibot_constants.KEYWORDS['config'])) - self.register(self._on_currency_chart_config_deactivate, (constants.KEYWORDS['deactivate'], constants.KEYWORDS['currency_chart'])) - self.register(self._on_currency_chart_config_deactivate, (constants.KEYWORDS['deactivate'], constants.KEYWORDS['currency_chart'], constants.KEYWORDS['config'])) + self.register(self._on_currency_chart_config_deactivate, (multibot_constants.KEYWORDS['deactivate'], constants.KEYWORDS['currency_chart'])) + self.register(self._on_currency_chart_config_deactivate, (multibot_constants.KEYWORDS['deactivate'], constants.KEYWORDS['currency_chart'], multibot_constants.KEYWORDS['config'])) - self.register(self._on_currency_chart_config_show, (constants.KEYWORDS['currency_chart'], constants.KEYWORDS['config'])) - self.register(self._on_currency_chart_config_show, (constants.KEYWORDS['show'], constants.KEYWORDS['currency_chart'], constants.KEYWORDS['config'])) + self.register(self._on_currency_chart_config_show, (constants.KEYWORDS['currency_chart'], multibot_constants.KEYWORDS['config'])) + self.register(self._on_currency_chart_config_show, (multibot_constants.KEYWORDS['show'], constants.KEYWORDS['currency_chart'], multibot_constants.KEYWORDS['config'])) - self.register(self._on_delete_original_config_activate, (constants.KEYWORDS['activate'], multibot_constants.KEYWORDS['delete'])) - self.register(self._on_delete_original_config_activate, (constants.KEYWORDS['activate'], multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['message'])) - self.register(self._on_delete_original_config_activate, (constants.KEYWORDS['activate'], multibot_constants.KEYWORDS['delete'], constants.KEYWORDS['config'])) - self.register(self._on_delete_original_config_activate, (constants.KEYWORDS['activate'], multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['message'], constants.KEYWORDS['config'])) + self.register(self._on_delete_original_config_activate, (multibot_constants.KEYWORDS['activate'], multibot_constants.KEYWORDS['delete'])) + self.register(self._on_delete_original_config_activate, (multibot_constants.KEYWORDS['activate'], multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['message'])) + self.register(self._on_delete_original_config_activate, (multibot_constants.KEYWORDS['activate'], multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['config'])) + self.register(self._on_delete_original_config_activate, (multibot_constants.KEYWORDS['activate'], multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['message'], multibot_constants.KEYWORDS['config'])) - self.register(self._on_delete_original_config_change, (constants.KEYWORDS['change'], multibot_constants.KEYWORDS['delete'])) - self.register(self._on_delete_original_config_change, (constants.KEYWORDS['change'], multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['message'])) - self.register(self._on_delete_original_config_change, (constants.KEYWORDS['change'], multibot_constants.KEYWORDS['delete'], constants.KEYWORDS['config'])) - self.register(self._on_delete_original_config_change, (constants.KEYWORDS['change'], multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['message'], constants.KEYWORDS['config'])) + self.register(self._on_delete_original_config_change, (multibot_constants.KEYWORDS['change'], multibot_constants.KEYWORDS['delete'])) + self.register(self._on_delete_original_config_change, (multibot_constants.KEYWORDS['change'], multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['message'])) + self.register(self._on_delete_original_config_change, (multibot_constants.KEYWORDS['change'], multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['config'])) + self.register(self._on_delete_original_config_change, (multibot_constants.KEYWORDS['change'], multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['message'], multibot_constants.KEYWORDS['config'])) - self.register(self._on_delete_original_config_deactivate, (constants.KEYWORDS['deactivate'], multibot_constants.KEYWORDS['delete'])) - self.register(self._on_delete_original_config_deactivate, (constants.KEYWORDS['deactivate'], multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['message'])) - self.register(self._on_delete_original_config_deactivate, (constants.KEYWORDS['deactivate'], multibot_constants.KEYWORDS['delete'], constants.KEYWORDS['config'])) - self.register(self._on_delete_original_config_deactivate, (constants.KEYWORDS['deactivate'], multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['message'], constants.KEYWORDS['config'])) + self.register(self._on_delete_original_config_deactivate, (multibot_constants.KEYWORDS['deactivate'], multibot_constants.KEYWORDS['delete'])) + self.register(self._on_delete_original_config_deactivate, (multibot_constants.KEYWORDS['deactivate'], multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['message'])) + self.register(self._on_delete_original_config_deactivate, (multibot_constants.KEYWORDS['deactivate'], multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['config'])) + self.register(self._on_delete_original_config_deactivate, (multibot_constants.KEYWORDS['deactivate'], multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['message'], multibot_constants.KEYWORDS['config'])) - self.register(self._on_delete_original_config_show, (constants.KEYWORDS['show'], multibot_constants.KEYWORDS['delete'])) - self.register(self._on_delete_original_config_show, (multibot_constants.KEYWORDS['delete'], constants.KEYWORDS['config'])) - self.register(self._on_delete_original_config_show, (constants.KEYWORDS['show'], multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['message'])) - self.register(self._on_delete_original_config_show, (constants.KEYWORDS['show'], multibot_constants.KEYWORDS['delete'], constants.KEYWORDS['config'])) - self.register(self._on_delete_original_config_show, (multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['message'], constants.KEYWORDS['config'])) - self.register(self._on_delete_original_config_show, (constants.KEYWORDS['show'], multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['message'], constants.KEYWORDS['config'])) + self.register(self._on_delete_original_config_show, (multibot_constants.KEYWORDS['show'], multibot_constants.KEYWORDS['delete'])) + self.register(self._on_delete_original_config_show, (multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['config'])) + self.register(self._on_delete_original_config_show, (multibot_constants.KEYWORDS['show'], multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['message'])) + self.register(self._on_delete_original_config_show, (multibot_constants.KEYWORDS['show'], multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['config'])) + self.register(self._on_delete_original_config_show, (multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['message'], multibot_constants.KEYWORDS['config'])) + self.register(self._on_delete_original_config_show, (multibot_constants.KEYWORDS['show'], multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['message'], multibot_constants.KEYWORDS['config'])) - self.register(self._on_hello, constants.KEYWORDS['hello']) - - self.register(self._on_mute, constants.KEYWORDS['mute']) - self.register(self._on_mute, (('haz', 'se'), constants.KEYWORDS['mute'])) - self.register(self._on_mute, (constants.KEYWORDS['deactivate'], constants.KEYWORDS['unmute'])) - self.register(self._on_mute, (constants.KEYWORDS['deactivate'], constants.KEYWORDS['sound'])) + self.register(self._on_hello, multibot_constants.KEYWORDS['hello']) self.register(self._on_new_message_default, default=True) - self.register(self._on_no_delete_original, (constants.KEYWORDS['negate'], multibot_constants.KEYWORDS['delete'])) - self.register(self._on_no_delete_original, (constants.KEYWORDS['negate'], multibot_constants.KEYWORDS['message'])) - self.register(self._on_no_delete_original, (constants.KEYWORDS['negate'], multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['message'])) - self.register(self._on_no_delete_original, (constants.KEYWORDS['deactivate'], multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['message'])) + self.register(self._on_no_delete_original, (multibot_constants.KEYWORDS['negate'], multibot_constants.KEYWORDS['delete'])) + self.register(self._on_no_delete_original, (multibot_constants.KEYWORDS['negate'], multibot_constants.KEYWORDS['message'])) + self.register(self._on_no_delete_original, (multibot_constants.KEYWORDS['negate'], multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['message'])) + self.register(self._on_no_delete_original, (multibot_constants.KEYWORDS['deactivate'], multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['message'])) self.register(self._on_punish, constants.KEYWORDS['punish']) - self.register(self._on_punish, (constants.KEYWORDS['deactivate'], constants.KEYWORDS['unpunish'])) - self.register(self._on_punish, (constants.KEYWORDS['deactivate'], constants.KEYWORDS['permission'])) + self.register(self._on_punish, (multibot_constants.KEYWORDS['deactivate'], constants.KEYWORDS['unpunish'])) + self.register(self._on_punish, (multibot_constants.KEYWORDS['deactivate'], multibot_constants.KEYWORDS['permission'])) - self.register(self._on_recover_message, (constants.KEYWORDS['reset'], multibot_constants.KEYWORDS['message'])) + self.register(self._on_recover_message, (multibot_constants.KEYWORDS['reset'], multibot_constants.KEYWORDS['message'])) self.register(self._on_scraping, constants.KEYWORDS['scraping']) - self.register(self._on_scraping_config_activate, (constants.KEYWORDS['activate'], constants.KEYWORDS['scraping'])) - self.register(self._on_scraping_config_activate, (constants.KEYWORDS['activate'], constants.KEYWORDS['scraping'], constants.KEYWORDS['config'])) + self.register(self._on_scraping_config_activate, (multibot_constants.KEYWORDS['activate'], constants.KEYWORDS['scraping'])) + self.register(self._on_scraping_config_activate, (multibot_constants.KEYWORDS['activate'], constants.KEYWORDS['scraping'], multibot_constants.KEYWORDS['config'])) - self.register(self._on_scraping_config_change, (constants.KEYWORDS['change'], constants.KEYWORDS['scraping'])) - self.register(self._on_scraping_config_change, (constants.KEYWORDS['change'], constants.KEYWORDS['scraping'], constants.KEYWORDS['config'])) + self.register(self._on_scraping_config_change, (multibot_constants.KEYWORDS['change'], constants.KEYWORDS['scraping'])) + self.register(self._on_scraping_config_change, (multibot_constants.KEYWORDS['change'], constants.KEYWORDS['scraping'], multibot_constants.KEYWORDS['config'])) - self.register(self._on_scraping_config_deactivate, (constants.KEYWORDS['deactivate'], constants.KEYWORDS['scraping'])) - self.register(self._on_scraping_config_deactivate, (constants.KEYWORDS['deactivate'], constants.KEYWORDS['scraping'], constants.KEYWORDS['config'])) + self.register(self._on_scraping_config_deactivate, (multibot_constants.KEYWORDS['deactivate'], constants.KEYWORDS['scraping'])) + self.register(self._on_scraping_config_deactivate, (multibot_constants.KEYWORDS['deactivate'], constants.KEYWORDS['scraping'], multibot_constants.KEYWORDS['config'])) - self.register(self._on_scraping_config_show, (constants.KEYWORDS['show'], constants.KEYWORDS['scraping'])) - self.register(self._on_scraping_config_show, (constants.KEYWORDS['scraping'], constants.KEYWORDS['config'])) - self.register(self._on_scraping_config_show, (constants.KEYWORDS['show'], constants.KEYWORDS['scraping'], constants.KEYWORDS['config'])) + self.register(self._on_scraping_config_show, (multibot_constants.KEYWORDS['show'], constants.KEYWORDS['scraping'])) + self.register(self._on_scraping_config_show, (constants.KEYWORDS['scraping'], multibot_constants.KEYWORDS['config'])) + self.register(self._on_scraping_config_show, (multibot_constants.KEYWORDS['show'], constants.KEYWORDS['scraping'], multibot_constants.KEYWORDS['config'])) self.register(self._on_song_info, constants.KEYWORDS['song_info']) - self.register(self._on_unmute, constants.KEYWORDS['unmute']) - self.register(self._on_unmute, (constants.KEYWORDS['deactivate'], constants.KEYWORDS['mute'])) - self.register(self._on_unmute, (constants.KEYWORDS['activate'], constants.KEYWORDS['sound'])) - self.register(self._on_unpunish, constants.KEYWORDS['unpunish']) - self.register(self._on_unpunish, (constants.KEYWORDS['deactivate'], constants.KEYWORDS['punish'])) - self.register(self._on_unpunish, (constants.KEYWORDS['activate'], constants.KEYWORDS['permission'])) + self.register(self._on_unpunish, (multibot_constants.KEYWORDS['deactivate'], constants.KEYWORDS['punish'])) + self.register(self._on_unpunish, (multibot_constants.KEYWORDS['activate'], multibot_constants.KEYWORDS['permission'])) self.register(self._on_weather_chart, constants.KEYWORDS['weather_chart']) - self.register(self._on_weather_chart, (constants.KEYWORDS['show'], constants.KEYWORDS['weather_chart'])) + self.register(self._on_weather_chart, (multibot_constants.KEYWORDS['show'], constants.KEYWORDS['weather_chart'])) - self.register(self._on_weather_chart_config_activate, (constants.KEYWORDS['activate'], constants.KEYWORDS['weather_chart'])) - self.register(self._on_weather_chart_config_activate, (constants.KEYWORDS['activate'], constants.KEYWORDS['weather_chart'], constants.KEYWORDS['config'])) + self.register(self._on_weather_chart_config_activate, (multibot_constants.KEYWORDS['activate'], constants.KEYWORDS['weather_chart'])) + self.register(self._on_weather_chart_config_activate, (multibot_constants.KEYWORDS['activate'], constants.KEYWORDS['weather_chart'], multibot_constants.KEYWORDS['config'])) - self.register(self._on_weather_chart_config_change, (constants.KEYWORDS['change'], constants.KEYWORDS['weather_chart'])) - self.register(self._on_weather_chart_config_change, (constants.KEYWORDS['change'], constants.KEYWORDS['weather_chart'], constants.KEYWORDS['config'])) + self.register(self._on_weather_chart_config_change, (multibot_constants.KEYWORDS['change'], constants.KEYWORDS['weather_chart'])) + self.register(self._on_weather_chart_config_change, (multibot_constants.KEYWORDS['change'], constants.KEYWORDS['weather_chart'], multibot_constants.KEYWORDS['config'])) - self.register(self._on_weather_chart_config_deactivate, (constants.KEYWORDS['deactivate'], constants.KEYWORDS['weather_chart'])) - self.register(self._on_weather_chart_config_deactivate, (constants.KEYWORDS['deactivate'], constants.KEYWORDS['weather_chart'], constants.KEYWORDS['config'])) + self.register(self._on_weather_chart_config_deactivate, (multibot_constants.KEYWORDS['deactivate'], constants.KEYWORDS['weather_chart'])) + self.register(self._on_weather_chart_config_deactivate, (multibot_constants.KEYWORDS['deactivate'], constants.KEYWORDS['weather_chart'], multibot_constants.KEYWORDS['config'])) - self.register(self._on_weather_chart_config_show, (constants.KEYWORDS['weather_chart'], constants.KEYWORDS['config'])) - self.register(self._on_weather_chart_config_show, (constants.KEYWORDS['show'], constants.KEYWORDS['weather_chart'], constants.KEYWORDS['config'])) + self.register(self._on_weather_chart_config_show, (constants.KEYWORDS['weather_chart'], multibot_constants.KEYWORDS['config'])) + self.register(self._on_weather_chart_config_show, (multibot_constants.KEYWORDS['show'], constants.KEYWORDS['weather_chart'], multibot_constants.KEYWORDS['config'])) + + self.register_button(self._on_button_press) async def _change_config(self, config_name: str, message: Message, value: bool = None): if value is None: @@ -157,52 +142,43 @@ class FlanaBot(MultiBot, ABC): @admin(False) @group async def _check_message_flood(self, message: Message): - if message.author.is_punished: + if await self.is_punished(message.author, message.chat): return last_2s_messages = Message.find({ + 'platform': self.bot_platform.value, 'author': message.author.object_id, 'last_update': { - '$gte': datetime.datetime.now() - datetime.timedelta(seconds=2) + '$gte': datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(seconds=2) } }) last_7s_messages = Message.find({ + 'platform': self.bot_platform.value, 'author': message.author.object_id, 'last_update': { - '$gte': datetime.datetime.now() - datetime.timedelta(seconds=7), - '$lt': datetime.datetime.now() - datetime.timedelta(seconds=2) + '$gte': datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(seconds=7), + '$lt': datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(seconds=2) } }) if len(last_2s_messages) >= 5 or len(last_7s_messages) >= 7: - n_punishments = len(Punishment.find({'user_id': message.author.id, 'group_id': message.chat.group_id})) + n_punishments = len(Punishment.find({ + 'platform': self.bot_platform.value, + 'user_id': message.author.id, + 'group_id': message.chat.group_id + })) punishment_seconds = (n_punishments + 2) ** constants.PUNISHMENT_INCREMENT_EXPONENT try: await self.punish(message.author.id, message.chat.group_id, punishment_seconds, message) except BadRoleError as e: await self._manage_exceptions(e, message) else: - # noinspection PyTypeChecker - Punishment(message.author.id, message.author.group_id, datetime.datetime.now() + datetime.timedelta(punishment_seconds)).save() - await self.send(f'Castigado durante {TimeUnits(punishment_seconds).to_words()}.', message) - - @staticmethod - async def _check_messages(): - Message.collection.delete_many({'last_update': {'$lte': datetime.datetime.now() - multibot_constants.MESSAGE_EXPIRATION_TIME}}) - - async def _check_mutes(self): - mute_groups = self._get_grouped_punishments(Mute) - - now = datetime.datetime.now() - for (user_id, group_id), sorted_mutes in mute_groups: - if (last_mute := sorted_mutes[-1]).until <= now: - await self.unmute(user_id, group_id) - last_mute.delete() + await self.send(f'Castigado durante {TimeUnits(seconds=punishment_seconds).to_words()}.', message) async def _check_punishments(self): punishment_groups = self._get_grouped_punishments(Punishment) - now = datetime.datetime.now() + now = datetime.datetime.now(datetime.timezone.utc) for (user_id, group_id), sorted_punishments in punishment_groups: if now < (last_punishment := sorted_punishments[-1]).until: continue @@ -216,19 +192,16 @@ class FlanaBot(MultiBot, ABC): last_punishment.is_active = False last_punishment.save() - async def _create_empty_message(self) -> Message: - return Message() - @staticmethod - def _get_grouped_punishments(PunishmentClass: Type[PunishmentBase]) -> tuple[tuple[tuple[int, int], list[PunishmentBase]]]: - sorted_punishments = PunishmentClass.find(sort_keys=('user_id', 'group_id', 'until')) - group_iterator: Iterator[ - tuple[ - tuple[int, int], - Iterator[PunishmentClass] - ] - ] = itertools.groupby(sorted_punishments, key=lambda punishment: (punishment.user_id, punishment.group_id)) - return tuple(((user_id, group_id), list(group_)) for (user_id, group_id), group_ in group_iterator) + def _get_config_buttons(config: dict | Chat | Message) -> list[list[str]]: + match config: + case Chat() as chat: + config = chat.config + case Message() as message: + config = message.chat.config + + buttons_texts = [f"{'✔' if v else '❌'} {k}" for k, v in config.items()] + return flanautils.chunks(buttons_texts, 3) @return_if_first_empty(exclude_self_types='FlanaBot', globals_=globals()) async def _manage_exceptions(self, exceptions: BaseException | Iterable[BaseException], message: Message): @@ -288,10 +261,7 @@ class FlanaBot(MultiBot, ABC): new_line = ' ' if len(medias_sended_info) == 1 else '\n' return f'{new_line}{medias_sended_info_joined}:' - async def _mute(self, user_id: int, group_id: int): - pass - - async def _punish(self, user_id: int, group_id: int): + async def _punish(self, user: int | str | User, group_: int | str | Chat): pass async def _search_and_send_medias(self, message: Message, send_song_info=False) -> list[Message]: @@ -333,15 +303,54 @@ class FlanaBot(MultiBot, ABC): async def _show_config(self, config_name: str, message: Message): await self.send(f"{config_name} está {'activado ✔' if message.chat.config.get(config_name) else 'desactivado ❌'}", message) - async def _unmute(self, user_id: int, group_id: int): - pass - - async def _unpunish(self, user_id: int, group_id: int): + async def _unpunish(self, user: int | str | User, group_: int | str | Chat): pass # ---------------------------------------------- # # HANDLERS # # ---------------------------------------------- # + async def _on_button_press(self, message: Message): + await self._accept_button_event(message) + if message.author.is_admin is False: + return + + match message.last_button_pressed: + case WeatherEmoji.ZOOM_IN.value: + buttons_message_type = ButtonsMessageType.WEATHER + message.weather_chart.zoom_in() + case WeatherEmoji.ZOOM_OUT.value: + buttons_message_type = ButtonsMessageType.WEATHER + message.weather_chart.zoom_out() + case WeatherEmoji.LEFT.value: + buttons_message_type = ButtonsMessageType.WEATHER + message.weather_chart.move_left() + case WeatherEmoji.RIGHT.value: + buttons_message_type = ButtonsMessageType.WEATHER + message.weather_chart.move_right() + case WeatherEmoji.PRECIPITATION_VOLUME.value: + buttons_message_type = ButtonsMessageType.WEATHER + message.weather_chart.trace_metadatas['rain_volume'].show = not message.weather_chart.trace_metadatas['rain_volume'].show + message.weather_chart.trace_metadatas['snow_volume'].show = not message.weather_chart.trace_metadatas['snow_volume'].show + case emoji if emoji in WeatherEmoji.values: + buttons_message_type = ButtonsMessageType.WEATHER + trace_metadata_name = WeatherEmoji(emoji).name.lower() + message.weather_chart.trace_metadatas[trace_metadata_name].show = not message.weather_chart.trace_metadatas[trace_metadata_name].show + case _ if 'auto_' in (config := message.last_button_pressed.split()[1]): + buttons_message_type = ButtonsMessageType.CONFIG + message.chat.config[config] = not message.chat.config[config] + message.save() + await self.edit('Estos son los ajustes del grupo:\n\n', self._get_config_buttons(message), message) + case _: + buttons_message_type = None + + if buttons_message_type is ButtonsMessageType.WEATHER: + message.weather_chart.apply_zoom() + message.weather_chart.draw() + message.save() + + image_bytes = message.weather_chart.to_image() + await self.edit(Media(image_bytes, MediaType.IMAGE), message) + async def _on_bye(self, message: Message): if not message.chat.is_group or self.is_bot_mentioned(message): await self.send_bye(message) @@ -349,11 +358,13 @@ class FlanaBot(MultiBot, ABC): @group @bot_mentioned async def _on_config_list_show(self, message: Message): - config_info = pprint.pformat(message.chat.config) - config_info = flanautils.translate(config_info, {'{': None, '}': None, ',': None, 'True': '✔', "'": None, 'False': '❌'}) - config_info = config_info.splitlines() - config_info = '\n'.join(config_info_.strip() for config_info_ in config_info) - await self.send(f'Estos son los ajustes del grupo:\n\n{config_info}', message) + # config_info = pprint.pformat(message.chat.config) + # config_info = flanautils.translate(config_info, {'{': None, '}': None, ',': None, 'True': '', "'": None, 'False': '❌'}) + # config_info = config_info.splitlines() + # config_info = '\n'.join(config_info_.strip() for config_info_ in config_info) + # await self.send(f'Estos son los ajustes del grupo:\n\n{config_info}', message) + + await self.send('Estos son los ajustes del grupo:\n\n', self._get_config_buttons(message), message) async def _on_covid_chart(self, message: Message): # todo2 pass @@ -413,31 +424,42 @@ class FlanaBot(MultiBot, ABC): if not message.chat.is_group or self.is_bot_mentioned(message): await self.send_hello(message) - @group - @bot_mentioned - @admin(send_negative=True) - async def _on_mute(self, message: Message): - await self._update_punishment(self.mute, message, time=flanautils.words_to_time(message.text)) - async def _on_new_message_default(self, message: Message): if message.is_inline: await self._on_scraping(message) elif ( - message.chat.is_group + ( + message.chat.is_group + and + not self.is_bot_mentioned(message) + and + not message.chat.config['auto_scraping'] + or + not await self._on_scraping(message) + ) and - not self.is_bot_mentioned(message) - and - not message.chat.config['auto_scraping'] - or - not await self._on_scraping(message) + ( + message.author.id != self.owner_id + and + ( + self.is_bot_mentioned(message) + or + ( + message.chat.config['auto_insult'] + and + random.random() < 0.5 + ) + ) + ) ): - await self.send_bad_phrase(message) + await self.send_insult(message) @ignore_self_message async def _on_new_message_raw(self, message: Message): await super()._on_new_message_raw(message) if not message.is_inline: - await self._check_message_flood(message) + async with self.lock: + await self._check_message_flood(message) async def _on_no_delete_original(self, message: Message): if not await self._on_scraping(message, delete_original=False): @@ -451,8 +473,6 @@ class FlanaBot(MultiBot, ABC): async def _on_ready(self): await super()._on_ready() - await flanautils.do_every(constants.CHECK_MUTES_EVERY_SECONDS, self._check_mutes) - await flanautils.do_every(constants.CHECK_MESSAGE_EVERY_SECONDS, self._check_messages) await flanautils.do_every(constants.CHECK_PUNISHMENTS_EVERY_SECONDS, self._check_punishments) @inline(False) @@ -460,7 +480,7 @@ class FlanaBot(MultiBot, ABC): if message.replied_message: message_deleted_bot_action = BotAction.find_one({'action': bytes(Action.MESSAGE_DELETED), 'chat': message.chat.object_id, 'affected_objects': message.replied_message.object_id}) elif self.is_bot_mentioned(message): - message_deleted_bot_action = BotAction.find_one({'action': bytes(Action.MESSAGE_DELETED), 'chat': message.chat.object_id, 'date': {'$gt': datetime.datetime.now() - constants.RECOVERY_DELETED_MESSAGE_BEFORE}}) + message_deleted_bot_action = BotAction.find_one({'action': bytes(Action.MESSAGE_DELETED), 'chat': message.chat.object_id, 'date': {'$gt': datetime.datetime.now(datetime.timezone.utc) - constants.RECOVERY_DELETED_MESSAGE_BEFORE}}) else: return @@ -469,7 +489,7 @@ class FlanaBot(MultiBot, ABC): return affected_object_ids = [affected_message_object_id for affected_message_object_id in message_deleted_bot_action.affected_objects] - deleted_messages: list[Message] = [affected_message for affected_object_id in affected_object_ids if (affected_message := Message.find_one({'_id': affected_object_id})).author.id != self.bot_id] + deleted_messages: list[Message] = [affected_message for affected_object_id in affected_object_ids if (affected_message := Message.find_one({'platform': self.bot_platform.value, '_id': affected_object_id})).author.id != self.bot_id] for deleted_message in deleted_messages: await self.send(deleted_message.text, message) @@ -539,12 +559,6 @@ class FlanaBot(MultiBot, ABC): elif self.is_bot_mentioned(message) or not message.chat.is_group: await self._manage_exceptions(SendError('No hay información musical en ese mensaje.'), message) - @group - @bot_mentioned - @admin(send_negative=True) - async def _on_unmute(self, message: Message): - await self._update_punishment(self.unmute, message) - @group @bot_mentioned @admin(send_negative=True) @@ -557,7 +571,7 @@ class FlanaBot(MultiBot, ABC): show_progress_state = False elif message.chat.is_group and not self.is_bot_mentioned(message): if message.chat.config['auto_weather_chart']: - if BotAction.find_one({'action': bytes(Action.AUTO_WEATHER_CHART), 'chat': message.chat.object_id, 'date': {'$gt': datetime.datetime.now() - constants.AUTO_WEATHER_EVERY}}): + if BotAction.find_one({'action': bytes(Action.AUTO_WEATHER_CHART), 'chat': message.chat.object_id, 'date': {'$gt': datetime.datetime.now(datetime.timezone.utc) - constants.AUTO_WEATHER_EVERY}}): return show_progress_state = False else: @@ -565,8 +579,14 @@ class FlanaBot(MultiBot, ABC): else: show_progress_state = True - user_names_with_at_sign = {user.name.lower() for user in message.chat.users} - user_names_without_at_sign = {user.name.lower().replace('@', '') for user in message.chat.users} + possible_mentioned_ids = [] + for user in message.mentions: + possible_mentioned_ids.append(user.name.lower()) + possible_mentioned_ids.append(user.name.split('#')[0].lower()) + possible_mentioned_ids.append(f'@{user.id}') + for role in message.chat.roles: + possible_mentioned_ids.append(f'@{role.id}') + original_text_words = flanautils.remove_accents(message.text.lower()) original_text_words = original_text_words.replace(',', ' ').replace(';', ' ').replace('-', ' -') original_text_words = flanautils.translate( @@ -575,12 +595,11 @@ class FlanaBot(MultiBot, ABC): ).split() place_words = ( OrderedSet(original_text_words) - - flanautils.cartesian_product_string_matching(original_text_words, constants.KEYWORDS['show'], min_ratio=0.85).keys() + - flanautils.cartesian_product_string_matching(original_text_words, multibot_constants.KEYWORDS['show'], min_ratio=0.85).keys() - flanautils.cartesian_product_string_matching(original_text_words, constants.KEYWORDS['weather_chart'], min_ratio=0.85).keys() - - flanautils.cartesian_product_string_matching(original_text_words, constants.KEYWORDS['date'], min_ratio=0.85).keys() - - flanautils.cartesian_product_string_matching(original_text_words, constants.KEYWORDS['thanks'], min_ratio=0.85).keys() - - user_names_with_at_sign - - user_names_without_at_sign + - flanautils.cartesian_product_string_matching(original_text_words, multibot_constants.KEYWORDS['date'], min_ratio=0.85).keys() + - flanautils.cartesian_product_string_matching(original_text_words, multibot_constants.KEYWORDS['thanks'], min_ratio=0.85).keys() + - possible_mentioned_ids - flanautils.CommonWords.all_words ) if not place_words: @@ -592,8 +611,8 @@ class FlanaBot(MultiBot, ABC): place_words.insert(0, 'calle') place_query = ' '.join(place_words) if len(place_query) >= constants.MAX_PLACE_QUERY_LENGTH: - if not show_progress_state: - await self.send_error(Media(str(flanautils.resolve_path('resources/mucho_texto.png'))), message, send_as_file=False) + if show_progress_state: + await self.send_error(Media(str(flanautils.resolve_path('resources/mucho_texto.png')), MediaType.IMAGE, Source.LOCAL), message, send_as_file=False) return if show_progress_state: bot_state_message = await self.send(f'Buscando "{place_query}" en el mapa 🧐...', message) @@ -714,55 +733,31 @@ class FlanaBot(MultiBot, ABC): # -------------------------------------------------------- # # -------------------- PUBLIC METHODS -------------------- # # -------------------------------------------------------- # - async def is_deaf(self, user_id: int, group_id: int) -> bool: + async def is_punished(self, user: int | str | User, group_: int | str | Chat) -> bool: pass - async def is_muted(self, user_id: int, group_id: int) -> bool: - pass - - async def mute(self, user_id: int, group_id: int, time: int | datetime.timedelta, message: Message = None): - if isinstance(time, int): - time = datetime.timedelta(seconds=time) - - try: - await self._mute(user_id, group_id) - except UserDisconnectedError as e: - await self._manage_exceptions(e, message if message else Message(chat=Chat(group_id=group_id))) - else: - if time: - # noinspection PyTypeChecker - Mute(user_id, group_id, until=datetime.datetime.now() + time).save() - if datetime.timedelta() < time <= constants.TIME_THRESHOLD_TO_MANUAL_UNMUTE: - await flanautils.do_later(time, self._check_mutes) - else: - # noinspection PyTypeChecker - Mute(user_id, group_id).save() - - async def punish(self, user_id: int, group_id: int, time: int | datetime.timedelta, message: Message = None): + async def punish(self, user: int | str | User, group_: int | str | Chat, time: int | datetime.timedelta, message: Message = None): + user_id = self._get_user_id(user) + group_id = self._get_group_id(group_) if isinstance(time, int): time = datetime.timedelta(seconds=time) try: await self._punish(user_id, group_id) except BadRoleError as e: - await self._manage_exceptions(e, message if message else Message(chat=Chat(group_id=group_id))) + if message and message.chat.original_object: + await self._manage_exceptions(e, message) + else: + raise e else: if time: - # noinspection PyTypeChecker - Punishment(user_id, group_id, until=datetime.datetime.now() + time).save() + until = datetime.datetime.now(datetime.timezone.utc) + time if datetime.timedelta() < time <= constants.TIME_THRESHOLD_TO_MANUAL_UNPUNISH: await flanautils.do_later(time, self._check_punishments) else: - # noinspection PyTypeChecker - Punishment(user_id, group_id).save() - - async def send_bad_phrase(self, message: Message, probability=0.00166666667) -> multibot_constants.ORIGINAL_MESSAGE | None: - if not self.is_bot_mentioned(message) and random.random() >= probability or message.author.id == self.owner_id: - return - - await self.typing_delay(message) - - return await self.send(random.choice(constants.BAD_PHRASES), message) + until = None + # noinspection PyTypeChecker + Punishment(self.bot_platform, user_id, group_id, until=until).save() async def send_bye(self, message: Message) -> multibot_constants.ORIGINAL_MESSAGE: return await self.send(random.choice((*constants.BYE_PHRASES, flanautils.CommonWords.random_time_greeting())), message) @@ -770,6 +765,10 @@ class FlanaBot(MultiBot, ABC): async def send_hello(self, message: Message) -> multibot_constants.ORIGINAL_MESSAGE: return await self.send(random.choice((*constants.HELLO_PHRASES, flanautils.CommonWords.random_time_greeting())), message) + async def send_insult(self, message: Message) -> multibot_constants.ORIGINAL_MESSAGE | None: + await self.typing_delay(message) + return await self.send(random.choice(constants.INSULTS), message) + @return_if_first_empty(exclude_self_types='FlanaBot', globals_=globals()) async def send_medias(self, medias: OrderedSet[Media], message: Message, send_song_info=False) -> tuple[list[Message], int]: sended_media_messages = [] @@ -819,21 +818,25 @@ class FlanaBot(MultiBot, ABC): attributes = ( f'Título: {song_info.title}\n' if song_info.title else '', f'Autor: {song_info.author}\n' if song_info.author else '', - f"{f'Álbum: {song_info.album}'}\n" if song_info.album else '', + f'Álbum: {song_info.album}\n' if song_info.album else '', f'Previa:' ) await self.send(''.join(attributes), message) if song_info: await self.send(song_info, message) - async def unmute(self, user_id: int, group_id: int, message: Message = None): - try: - await self._unmute(user_id, group_id) - except UserDisconnectedError as e: - await self._manage_exceptions(e, message if message else Message(chat=Chat(group_id=group_id))) - - async def unpunish(self, user_id: int, group_id: int, message: Message = None): + async def unpunish(self, user: int | str | User, group_: int | str | Chat, message: Message = None): + user_id = self._get_user_id(user) + group_id = self._get_group_id(group_) try: await self._unpunish(user_id, group_id) except BadRoleError as e: - await self._manage_exceptions(e, message if message else Message(chat=Chat(group_id=group_id))) + if message and message.chat.original_object: + await self._manage_exceptions(e, message) + else: + raise e + else: + try: + Punishment.find_one({'platform': self.bot_platform.value, 'user_id': user_id, 'group_id': group_id, 'until': None}).delete() + except AttributeError: + pass diff --git a/flanabot/bots/flana_disc_bot.py b/flanabot/bots/flana_disc_bot.py index 7f05b2b..12b5c9f 100644 --- a/flanabot/bots/flana_disc_bot.py +++ b/flanabot/bots/flana_disc_bot.py @@ -2,12 +2,11 @@ import asyncio import os import discord -from multibot import DiscordBot +from multibot import BadRoleError, DiscordBot, User from flanabot import constants from flanabot.bots.flana_bot import FlanaBot -from flanabot.exceptions import BadRoleError, UserDisconnectedError -from flanabot.models import User +from flanabot.models import Chat, Punishment HEAT_NAMES = [ 'Canal Congelado', @@ -20,18 +19,9 @@ HEAT_NAMES = [ 'Verano Cordobés al Sol', 'Canal Ardiendo', 'abrid las putas ventanas y traed el extintor', - 'Canal INFIERNO', - 'La Palma 🌋' + 'Canal INFIERNO' ] HOT_CHANNEL_ID = 493530483045564417 -ROLES = { - 'Administrador': 387344390030360587, - 'Carroñero': 493523298429435905, - 'al lol': 881238165476741161, - 'Persona': 866046517998387220, - 'Castigado': 877662459568209921, - 'Bot': 493784221085597706 -} # ---------------------------------------------------------------------------------------------------- # @@ -67,33 +57,19 @@ class FlanaDiscBot(DiscordBot, FlanaBot): await channel.edit(name=HEAT_NAMES[int(self.heat_level)]) - async def _mute(self, user_id: int, group_id: int): - user = await self.get_user(user_id, group_id) + async def _punish(self, user: int | str | User, group_: int | str | Chat): + user_id = self._get_user_id(user) try: - await user.original_object.edit(mute=True) - except discord.errors.HTTPException: - raise UserDisconnectedError - - async def _punish(self, user_id: int, group_id: int): - user = await self.get_user(user_id, group_id) - try: - await user.original_object.remove_roles(self._find_role_by_id(ROLES['Persona'], user.original_object.guild.roles)) - await user.original_object.add_roles(self._find_role_by_id(ROLES['Castigado'], user.original_object.guild.roles)) + await self.add_role(user_id, group_, 'Castigado') + await self.remove_role(user_id, group_, 'Persona') except AttributeError: raise BadRoleError(str(self._punish)) - async def _unmute(self, user_id: int, group_id: int): - user = await self.get_user(user_id, group_id) + async def _unpunish(self, user: int | str | User, group_: int | str | Chat): + user_id = self._get_user_id(user) try: - await user.original_object.edit(mute=False) - except discord.errors.HTTPException: - raise UserDisconnectedError - - async def _unpunish(self, user_id: int, group_id: int): - user = await self.get_user(user_id, group_id) - try: - await user.original_object.remove_roles(self._find_role_by_id(ROLES['Castigado'], user.original_object.guild.roles)) - await user.original_object.add_roles(self._find_role_by_id(ROLES['Persona'], user.original_object.guild.roles)) + await self.add_role(user_id, group_, 'Persona') + await self.remove_role(user_id, group_, 'Castigado') except AttributeError: raise BadRoleError(str(self._unpunish)) @@ -116,18 +92,12 @@ class FlanaDiscBot(DiscordBot, FlanaBot): # -------------------------------------------------------- # # -------------------- PUBLIC METHODS -------------------- # # -------------------------------------------------------- # - async def is_deaf(self, user_id: int, group_id: int) -> bool: - user = await self.get_user(user_id, group_id) - return user.original_object.voice.deaf - - async def is_muted(self, user_id: int, group_id: int) -> bool: - user = await self.get_user(user_id, group_id) - return user.original_object.voice.mute - - @staticmethod - def is_self_deaf(user: User) -> bool: - return user.original_object.voice.self_deaf - - @staticmethod - def is_self_muted(user: User) -> bool: - return user.original_object.voice.self_mute + async def is_punished(self, user: int | str | User, group_: int | str | Chat): + user = await self.get_user(user, group_) + group_id = self._get_group_id(group_) + return group_id in {punishment.group_id for punishment in Punishment.find({ + 'platform': self.bot_platform.value, + 'user_id': user.id, + 'group_id': group_id, + 'is_active': True + })} diff --git a/flanabot/bots/flana_tele_bot.py b/flanabot/bots/flana_tele_bot.py index 5301b8a..39aba97 100644 --- a/flanabot/bots/flana_tele_bot.py +++ b/flanabot/bots/flana_tele_bot.py @@ -5,14 +5,10 @@ import os from typing import Callable import telethon.tl.functions -from flanaapis.weather.constants import WeatherEmoji -from flanautils import Media, MediaType, return_if_first_empty -from multibot import TelegramBot, constants as multibot_constants, find_message, user_client +from multibot import TelegramBot, find_message, user_client from flanabot.bots.flana_bot import FlanaBot -from flanabot.models.chat import Chat -from flanabot.models.message import Message -from flanabot.models.user import User +from flanabot.models import Message # ---------------------------------------------------------- # @@ -48,20 +44,6 @@ class FlanaTeleBot(TelegramBot, FlanaBot): # ----------------------------------------------------------- # # -------------------- PROTECTED METHODS -------------------- # # ----------------------------------------------------------- # - def _add_handlers(self): - super()._add_handlers() - self.register_button(self._on_button_press) - - @return_if_first_empty(exclude_self_types='FlanaTeleBot', globals_=globals()) - async def _create_chat_from_telegram_chat(self, telegram_chat: multibot_constants.TELEGRAM_CHAT) -> Chat | None: - chat = await super()._create_chat_from_telegram_chat(telegram_chat) - chat.config = Chat.DEFAULT_CONFIG - return Chat.from_dict(chat.to_dict()) - - @return_if_first_empty(exclude_self_types='FlanaTeleBot', globals_=globals()) - async def _create_user_from_telegram_user(self, original_user: multibot_constants.TELEGRAM_USER, group_id: int = None) -> User | None: - return User.from_dict((await super()._create_user_from_telegram_user(original_user, group_id)).to_dict()) - @user_client async def _get_contacts_ids(self) -> list[int]: async with self.user_client: @@ -76,38 +58,6 @@ class FlanaTeleBot(TelegramBot, FlanaBot): # ---------------------------------------------- # # HANDLERS # # ---------------------------------------------- # - @whitelisted_event - async def _on_button_press(self, message: Message): - await message.original_event.answer() - - match message.button_text: - case WeatherEmoji.ZOOM_IN.value: - message.weather_chart.zoom_in() - case WeatherEmoji.ZOOM_OUT.value: - message.weather_chart.zoom_out() - case WeatherEmoji.LEFT.value: - message.weather_chart.move_left() - case WeatherEmoji.RIGHT.value: - message.weather_chart.move_right() - case WeatherEmoji.PRECIPITATION_VOLUME.value: - message.weather_chart.trace_metadatas['rain_volume'].show = not message.weather_chart.trace_metadatas['rain_volume'].show - message.weather_chart.trace_metadatas['snow_volume'].show = not message.weather_chart.trace_metadatas['snow_volume'].show - case emoji if emoji in WeatherEmoji.values: - trace_metadata_name = WeatherEmoji(emoji).name.lower() - message.weather_chart.trace_metadatas[trace_metadata_name].show = not message.weather_chart.trace_metadatas[trace_metadata_name].show - - message.weather_chart.apply_zoom() - message.weather_chart.draw() - message.save() - - image_bytes = message.weather_chart.to_image() - file = await self._prepare_media_to_send(Media(image_bytes, MediaType.IMAGE)) - - try: - await message.original_object.edit(file=file) - except telethon.errors.rpcerrorlist.MessageNotModifiedError: - pass - @whitelisted_event async def _on_inline_query_raw(self, message: Message): await super()._on_new_message_raw(message) diff --git a/flanabot/constants.py b/flanabot/constants.py index db9dbf3..56cf724 100644 --- a/flanabot/constants.py +++ b/flanabot/constants.py @@ -3,19 +3,23 @@ import datetime import flanautils AUTO_WEATHER_EVERY = datetime.timedelta(hours=6) -CHECK_MESSAGE_EVERY_SECONDS = datetime.timedelta(days=1).total_seconds() -CHECK_MUTES_EVERY_SECONDS = datetime.timedelta(hours=1).total_seconds() CHECK_PUNISHMENTS_EVERY_SECONDS = datetime.timedelta(hours=1).total_seconds() HEAT_PERIOD_SECONDS = datetime.timedelta(minutes=15).total_seconds() +INSULT_PROBABILITY = 0.00166666667 MAX_PLACE_QUERY_LENGTH = 50 PUNISHMENT_INCREMENT_EXPONENT = 6 PUNISHMENTS_RESET = datetime.timedelta(weeks=6 * flanautils.WEEKS_IN_A_MONTH) RECOVERY_DELETED_MESSAGE_BEFORE = datetime.timedelta(hours=1) -TIME_THRESHOLD_TO_MANUAL_UNMUTE = datetime.timedelta(days=3) TIME_THRESHOLD_TO_MANUAL_UNPUNISH = datetime.timedelta(days=3) SCRAPING_MESSAGE_WAITING_TIME = 0.1 -BAD_PHRASES = ( +BYE_PHRASES = ('Adiós.', 'adieu', 'adio', 'adioh', 'adios', 'adió', 'adiós', 'agur', 'bye', 'byyeeee', 'chao', + 'hasta la vista', 'hasta luego', 'hasta nunca', ' hasta pronto', 'hasta la próxima', + 'nos vemos', 'taluego') + +HELLO_PHRASES = ('alo', 'aloh', 'buenas', 'Hola.', 'hello', 'hey', 'hi', 'hola', 'holaaaa', 'holaaaaaaa', 'ola', + 'ola k ase', 'pa ti mi cola', 'saludos') +INSULTS = ( 'Cállate ya anda.', '¿Quién te ha preguntado?', '¿Tú eres así o te dan apagones cerebrales?', @@ -42,55 +46,23 @@ BAD_PHRASES = ( 'Eres más malo que pegarle a un padre.' ) -BYE_PHRASES = ('Adiós.', 'adieu', 'adio', 'adioh', 'adios', 'adió', 'adiós', 'agur', 'bye', 'byyeeee', 'chao', - 'hasta la vista', 'hasta luego', 'hasta nunca', ' hasta pronto', 'hasta la próxima', - 'nos vemos', 'taluego') - -HELLO_PHRASES = ('alo', 'aloh', 'buenas', 'Hola.', 'hello', 'hey', 'hi', 'hola', 'holaaaa', 'holaaaaaaa', 'ola', - 'ola k ase', 'pa ti mi cola', 'saludos') - KEYWORDS = { - 'activate': ('activa', 'activar', 'activate', 'deja', 'dejale', 'devuelve', 'devuelvele', 'enable', 'encender', - 'enciende', 'habilita', 'habilitar'), - 'bye': ('adieu', 'adio', 'adiooooo', 'adios', 'agur', 'buenas', 'bye', 'cama', 'chao', 'farewell', 'goodbye', - 'hasta', 'luego', 'noches', 'pronto', 'taluego', 'taluegorl', 'tenga', 'vemos', 'vista', 'voy'), - 'change': ('alter', 'alternar', 'alternate', 'cambiar', 'change', 'default', 'defecto', 'edit', 'editar', - 'exchange', 'modificar', 'modify', 'permutar', 'predeterminado', 'shift', 'swap', 'switch', 'turn', - 'vary'), - 'config': ('ajustar', 'ajuste', 'ajustes', 'automatico', 'automatic', 'config', 'configs', 'configuracion', - 'configuration', 'default', 'defecto', 'setting', 'settings'), 'covid_chart': ('case', 'caso', 'contagiado', 'contagio', 'corona', 'coronavirus', 'covid', 'covid19', 'death', 'disease', 'enfermedad', 'enfermos', 'fallecido', 'incidencia', 'jacovid', 'mascarilla', 'muerte', 'muerto', 'pandemia', 'sick', 'virus'), 'currency_chart': ('argentina', 'bitcoin', 'cardano', 'cripto', 'crypto', 'criptodivisa', 'cryptodivisa', 'cryptomoneda', 'cryptocurrency', 'currency', 'dinero', 'divisa', 'ethereum', 'inversion', 'moneda', 'pasta'), - 'date': ('ayer', 'de', 'domingo', 'fin', 'finde', 'friday', 'hoy', 'jueves', 'lunes', 'martes', 'mañana', - 'miercoles', 'monday', 'pasado', 'sabado', 'saturday', 'semana', 'sunday', 'thursday', 'today', 'tomorrow', - 'tuesday', 'viernes', 'wednesday', 'week', 'weekend', 'yesterday'), - 'deactivate': ('apaga', 'apagar', 'deactivate', 'deactivate', 'desactivar', 'deshabilita', 'deshabilitar', - 'disable', 'forbids', 'prohibe', 'quita', 'remove', 'return'), - 'hello': ('alo', 'aloh', 'buenas', 'dias', 'hello', 'hey', 'hi', 'hola', 'holaaaaaa', 'ola', 'saludos', 'tardes'), - 'help': ('ayuda', 'help'), - 'mute': ('calla', 'calle', 'cierra', 'close', 'mute', 'mutea', 'mutealo', 'noise', 'ruido', 'shut', 'silence', - 'silencia'), - 'negate': ('no', 'ocurra', 'ocurre'), - 'permission': ('permiso', 'permission'), 'punish': ('acaba', 'aprende', 'ataca', 'atalo', 'azota', 'boss', 'castiga', 'castigo', 'condena', 'controla', 'destroy', 'destroza', 'duro', 'ejecuta', 'enseña', 'escarmiento', 'execute', 'finish', 'fuck', 'fusila', 'hell', 'humos', 'infierno', 'jefe', 'jode', 'learn', 'leccion', 'lesson', 'manda', 'purgatorio', 'sancion', 'shoot', 'teach', 'termina', 'whip'), - 'reset': ('recover', 'recovery', 'recupera', 'reinicia', 'reset', 'resetea', 'restart'), 'scraping': ('api', 'aqui', 'busca', 'contenido', 'content', 'descarga', 'descargar', 'download', 'envia', 'habia', 'media', 'redes', 'scrap', 'scraping', 'search', 'send', 'social', 'sociales', 'tenia', 'video', 'videos'), - 'show': ('actual', 'enseña', 'estado', 'how', 'muestra', 'show', 'como'), 'song_info': ('aqui', 'cancion', 'data', 'datos', 'info', 'informacion', 'information', 'llama', 'media', 'name', 'nombre', 'sonaba', 'sonando', 'song', 'sono', 'sound', 'suena', 'title', 'titulo', 'video'), - 'sound': ('hablar', 'hable', 'micro', 'microfono', 'microphone', 'sonido', 'sound', 'talk', 'volumen'), - 'thanks': ('gracia', 'gracias', 'grasia', 'grasias', 'grax', 'thank', 'thanks', 'ty'), - 'unmute': ('desilencia', 'desmutea', 'desmutealo', 'unmute'), 'unpunish': ('absolve', 'forgive', 'innocent', 'inocente', 'perdona', 'spare'), 'weather_chart': ('atmosfera', 'atmosferico', 'calle', 'calor', 'caloret', 'clima', 'climatologia', 'cloud', 'cloudless', 'cloudy', 'cold', 'congelar', 'congelado', 'denbora', 'despejado', 'diluvio', 'frio', diff --git a/flanabot/exceptions.py b/flanabot/exceptions.py deleted file mode 100644 index 4f55fc5..0000000 --- a/flanabot/exceptions.py +++ /dev/null @@ -1,6 +0,0 @@ -class BadRoleError(Exception): - pass - - -class UserDisconnectedError(Exception): - pass diff --git a/flanabot/main.py b/flanabot/main.py index a29c1d6..2e4dfc8 100644 --- a/flanabot/main.py +++ b/flanabot/main.py @@ -6,13 +6,16 @@ os.environ |= flanautils.find_environment_variables('../.env') import asyncio +from flanabot.bots.flana_disc_bot import FlanaDiscBot from flanabot.bots.flana_tele_bot import FlanaTeleBot async def main(): + flana_disc_bot = FlanaDiscBot() flana_tele_bot = FlanaTeleBot() await asyncio.gather( + flana_disc_bot.start(), flana_tele_bot.start() ) diff --git a/flanabot/models/__init__.py b/flanabot/models/__init__.py index 3fa55be..32e91cd 100644 --- a/flanabot/models/__init__.py +++ b/flanabot/models/__init__.py @@ -1,5 +1,5 @@ from flanabot.models.chat import * +from flanabot.models.enums import * from flanabot.models.message import * -from flanabot.models.punishments import * -from flanabot.models.user import * +from flanabot.models.punishment import * from flanabot.models.weather_chart import * diff --git a/flanabot/models/chat.py b/flanabot/models/chat.py index 273cc2a..6c0c792 100644 --- a/flanabot/models/chat.py +++ b/flanabot/models/chat.py @@ -1,21 +1,18 @@ from dataclasses import dataclass, field -from multibot import Chat as MultiBotChat - -from flanabot.models.user import User +from multibot.models import Chat as MultiBotChat, User @dataclass(eq=False) class Chat(MultiBotChat): - DEFAULT_CONFIG = {'auto_clear': False, - 'auto_covid_chart': True, + DEFAULT_CONFIG = {'auto_covid_chart': True, 'auto_currency_chart': True, 'auto_delete_original': True, + 'auto_insult': True, 'auto_scraping': True, 'auto_weather_chart': True} users: list[User] = field(default_factory=list) def __post_init__(self): super().__post_init__() - if not self.config: - self.config = self.DEFAULT_CONFIG + self.config = self.DEFAULT_CONFIG | self.config diff --git a/flanabot/models/enums.py b/flanabot/models/enums.py new file mode 100644 index 0000000..8812e6d --- /dev/null +++ b/flanabot/models/enums.py @@ -0,0 +1,8 @@ +from enum import auto + +from flanautils import FlanaEnum + + +class ButtonsMessageType(FlanaEnum): + CONFIG = auto() + WEATHER = auto() diff --git a/flanabot/models/message.py b/flanabot/models/message.py index 4f42a96..7e8caf1 100644 --- a/flanabot/models/message.py +++ b/flanabot/models/message.py @@ -4,10 +4,9 @@ from dataclasses import dataclass, field from typing import Iterable from flanautils import Media, OrderedSet -from multibot import Message as MultiBotMessage +from multibot.models import Message as MultiBotMessage, User from flanabot.models.chat import Chat -from flanabot.models.user import User from flanabot.models.weather_chart import WeatherChart diff --git a/flanabot/models/punishment.py b/flanabot/models/punishment.py new file mode 100644 index 0000000..5526345 --- /dev/null +++ b/flanabot/models/punishment.py @@ -0,0 +1,8 @@ +from dataclasses import dataclass + +from multibot.models import Mute, db + + +@dataclass(eq=False) +class Punishment(Mute): + collection = db.punishment diff --git a/flanabot/models/punishments.py b/flanabot/models/punishments.py deleted file mode 100644 index bba5160..0000000 --- a/flanabot/models/punishments.py +++ /dev/null @@ -1,23 +0,0 @@ -import datetime -from dataclasses import dataclass - -from flanautils import DCMongoBase, FlanaBase -from multibot.models.database import db - - -@dataclass(eq=False) -class PunishmentBase(DCMongoBase, FlanaBase): - user_id: int = None - group_id: int = None - until: datetime.datetime = None - is_active: bool = True - - -@dataclass(eq=False) -class Punishment(PunishmentBase): - collection = db.punishment - - -@dataclass(eq=False) -class Mute(PunishmentBase): - collection = db.mute diff --git a/flanabot/models/user.py b/flanabot/models/user.py deleted file mode 100644 index 18f74f8..0000000 --- a/flanabot/models/user.py +++ /dev/null @@ -1,22 +0,0 @@ -from dataclasses import dataclass - -from multibot import User as MultiBotUser - -from flanabot.models.punishments import Mute, Punishment - - -@dataclass(eq=False) -class User(MultiBotUser): - def is_muted_on(self, group_id: int): - return group_id in self.muted_on - - def is_punished_on(self, group_id: int): - return group_id in self.punished_on - - @property - def muted_on(self): - return {mute.group_id for mute in Mute.find({'user_id': self.id, 'is_active': True})} - - @property - def punished_on(self): - return {punishment for punishment in Punishment.find({'user_id': self.id, 'is_active': True})} diff --git a/flanabot/models/weather_chart.py b/flanabot/models/weather_chart.py index 9650db5..00cb52a 100644 --- a/flanabot/models/weather_chart.py +++ b/flanabot/models/weather_chart.py @@ -44,7 +44,7 @@ class WeatherChart(DateChart): if self.show_now_vertical_line and first_dt <= now <= last_dt: self.figure.add_vline(x=now, line_width=1, line_dash='dot') - self.figure.add_annotation(text="Ahora", yref="paper", x=now, y=0.01, showarrow=False) + self.figure.add_annotation(text='Ahora', yref='paper', x=now, y=0.01, showarrow=False) for day_weather in self.day_weathers: date_time = datetime.datetime(year=day_weather.date.year, month=day_weather.date.month, day=day_weather.date.day, tzinfo=self.timezone) diff --git a/requirements.txt b/requirements.txt index 72a4d60..7fc835a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,7 +14,7 @@ click==8.0.3 colorama==0.4.4 cryptg==0.2.post4 cryptography==36.0.1 -discord.py==1.7.3 +discord.py==2.0.0a4269+gb7e25645 fastapi==0.71.0 flanaapis flanautils diff --git a/tests/unit/test_parse_callbacks.py b/tests/unit/test_parse_callbacks.py index 6ba7b6c..66d47e4 100644 --- a/tests/unit/test_parse_callbacks.py +++ b/tests/unit/test_parse_callbacks.py @@ -32,7 +32,10 @@ class TestParseCallbacks(unittest.TestCase): 'flanabot ajustes', 'Flanabot ajustes', 'Flanabot qué puedo ajustar?', - 'flanabot ayuda' + 'config', + 'configuracion', + 'configuración', + 'configuration' ] self._test_no_always_callbacks(phrases, self.flana_tele_bot._on_config_list_show)