12 Commits

Author SHA1 Message Date
AlberLC
7ad4c4052e Remove twitch bot at main 2022-06-06 02:31:16 +02:00
AlberLC
641dc72738 Update requirements.txt 2022-06-04 03:33:59 +02:00
AlberLC
cafa7ce1c5 Fix recover original message 2022-06-04 02:58:32 +02:00
AlberLC
c9a46b2b07 Fix roles mentions in weather 2022-06-01 03:16:45 +02:00
AlberLC
92ee0e405d Fix env variable names 2022-05-31 23:08:36 +02:00
AlberLC
dc6bf7accb Refactor punishments and optimize chat objects 2022-05-31 22:11:16 +02:00
AlberLC
9e5f7a81ff Add user who pressed the button 2022-05-26 05:45:16 +02:00
AlberLC
28ff804d5a Improve discord user name printing 2022-05-25 05:30:09 +02:00
AlberLC
d47fdfb57e Fix bot insults ratio 2022-05-25 04:00:47 +02:00
AlberLC
20fb6e3223 Fix bot insults ratio 2022-05-25 02:55:28 +02:00
AlberLC
952672946b Add Discord bot 2022-05-25 00:46:32 +02:00
AlberLC
7c43db0867 Fix warning at implicit long weather request 2022-03-31 20:06:06 +02:00
16 changed files with 316 additions and 470 deletions

View File

@@ -1,149 +1,137 @@
__all__ = ['FlanaBot']
import asyncio import asyncio
import datetime import datetime
import itertools
import pprint
import random import random
import time as time_module import time as time_module
from abc import ABC from abc import ABC
from asyncio import Future from asyncio import Future
from typing import Iterable, Iterator, Type from typing import Iterable
import flanaapis.geolocation.functions import flanaapis.geolocation.functions
import flanaapis.weather.functions import flanaapis.weather.functions
import flanautils import flanautils
import plotly.graph_objects import plotly.graph_objects
import pymongo
from flanaapis import InstagramLoginError, MediaNotFoundError, Place, PlaceNotFoundError, WeatherEmoji, instagram, tiktok, twitter 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 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 import constants
from flanabot.exceptions import BadRoleError, UserDisconnectedError from flanabot.models import ButtonsMessageType, Chat, Message, Punishment, WeatherChart
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
# ----------------------------------------------------------------------------------------------------- # # ----------------------------------------------------------------------------------------------------- #
# --------------------------------------------- FLANA_BOT --------------------------------------------- # # --------------------------------------------- FLANA_BOT --------------------------------------------- #
# ----------------------------------------------------------------------------------------------------- # # ----------------------------------------------------------------------------------------------------- #
class FlanaBot(MultiBot, ABC): class FlanaBot(MultiBot, ABC):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.lock = asyncio.Lock()
# ----------------------------------------------------------- # # ----------------------------------------------------------- #
# -------------------- PROTECTED METHODS -------------------- # # -------------------- PROTECTED METHODS -------------------- #
# ----------------------------------------------------------- # # ----------------------------------------------------------- #
def _add_handlers(self): def _add_handlers(self):
super()._add_handlers() 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, multibot_constants.KEYWORDS['config'])
self.register(self._on_config_list_show, constants.KEYWORDS['help']) self.register(self._on_config_list_show, (multibot_constants.KEYWORDS['show'], multibot_constants.KEYWORDS['config']))
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_covid_chart, constants.KEYWORDS['covid_chart']) 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['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, (multibot_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'], 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, (multibot_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'], 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, (multibot_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'], 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['currency_chart'], multibot_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, (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, (multibot_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, (multibot_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, (multibot_constants.KEYWORDS['activate'], multibot_constants.KEYWORDS['delete'], multibot_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'], 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, (multibot_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, (multibot_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, (multibot_constants.KEYWORDS['change'], multibot_constants.KEYWORDS['delete'], multibot_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'], 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, (multibot_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, (multibot_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, (multibot_constants.KEYWORDS['deactivate'], multibot_constants.KEYWORDS['delete'], multibot_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'], 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['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, (multibot_constants.KEYWORDS['delete'], multibot_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, (multibot_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['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'], 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, (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'], multibot_constants.KEYWORDS['message'], multibot_constants.KEYWORDS['config']))
self.register(self._on_hello, constants.KEYWORDS['hello']) self.register(self._on_hello, multibot_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_new_message_default, default=True) 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, (multibot_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, (multibot_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, (multibot_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['deactivate'], multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['message']))
self.register(self._on_punish, constants.KEYWORDS['punish']) self.register(self._on_punish, constants.KEYWORDS['punish'])
self.register(self._on_punish, (constants.KEYWORDS['deactivate'], constants.KEYWORDS['unpunish'])) self.register(self._on_punish, (multibot_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'], 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, constants.KEYWORDS['scraping'])
self.register(self._on_scraping_config_activate, (constants.KEYWORDS['activate'], constants.KEYWORDS['scraping'])) self.register(self._on_scraping_config_activate, (multibot_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'], multibot_constants.KEYWORDS['config']))
self.register(self._on_scraping_config_change, (constants.KEYWORDS['change'], constants.KEYWORDS['scraping'])) self.register(self._on_scraping_config_change, (multibot_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'], multibot_constants.KEYWORDS['config']))
self.register(self._on_scraping_config_deactivate, (constants.KEYWORDS['deactivate'], constants.KEYWORDS['scraping'])) self.register(self._on_scraping_config_deactivate, (multibot_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'], multibot_constants.KEYWORDS['config']))
self.register(self._on_scraping_config_show, (constants.KEYWORDS['show'], constants.KEYWORDS['scraping'])) self.register(self._on_scraping_config_show, (multibot_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['scraping'], multibot_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'], multibot_constants.KEYWORDS['config']))
self.register(self._on_song_info, constants.KEYWORDS['song_info']) 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['unpunish'])
self.register(self._on_unpunish, (constants.KEYWORDS['deactivate'], constants.KEYWORDS['punish'])) self.register(self._on_unpunish, (multibot_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['activate'], multibot_constants.KEYWORDS['permission']))
self.register(self._on_weather_chart, constants.KEYWORDS['weather_chart']) 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, (multibot_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'], 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, (multibot_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'], 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, (multibot_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'], 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['weather_chart'], multibot_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, (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): async def _change_config(self, config_name: str, message: Message, value: bool = None):
if value is None: if value is None:
@@ -157,78 +145,50 @@ class FlanaBot(MultiBot, ABC):
@admin(False) @admin(False)
@group @group
async def _check_message_flood(self, message: Message): async def _check_message_flood(self, message: Message):
if message.author.is_punished: if await self.is_punished(message.author, message.chat):
return return
last_2s_messages = Message.find({ last_2s_messages = Message.find({
'platform': self.platform.value,
'author': message.author.object_id, 'author': message.author.object_id,
'last_update': { '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({ last_7s_messages = Message.find({
'platform': self.platform.value,
'author': message.author.object_id, 'author': message.author.object_id,
'last_update': { 'last_update': {
'$gte': datetime.datetime.now() - datetime.timedelta(seconds=7), '$gte': datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(seconds=7),
'$lt': datetime.datetime.now() - datetime.timedelta(seconds=2) '$lt': datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(seconds=2)
} }
}) })
if len(last_2s_messages) >= 5 or len(last_7s_messages) >= 7: 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.platform.value,
'user_id': message.author.id,
'group_id': message.chat.group_id
}))
punishment_seconds = (n_punishments + 2) ** constants.PUNISHMENT_INCREMENT_EXPONENT punishment_seconds = (n_punishments + 2) ** constants.PUNISHMENT_INCREMENT_EXPONENT
try: try:
await self.punish(message.author.id, message.chat.group_id, punishment_seconds, message) await self.punish(message.author.id, message.chat.group_id, punishment_seconds, message)
except BadRoleError as e: except BadRoleError as e:
await self._manage_exceptions(e, message) await self._manage_exceptions(e, message)
else: else:
# noinspection PyTypeChecker await self.send(f'Castigado durante {TimeUnits(seconds=punishment_seconds).to_words()}.', message)
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 @staticmethod
async def _check_messages(): def _get_config_buttons(config: dict | Chat | Message) -> list[list[str]]:
Message.collection.delete_many({'last_update': {'$lte': datetime.datetime.now() - multibot_constants.MESSAGE_EXPIRATION_TIME}}) match config:
case Chat() as chat:
config = chat.config
case Message() as message:
config = message.chat.config
async def _check_mutes(self): buttons_texts = [f"{'' if v else ''} {k}" for k, v in config.items()]
mute_groups = self._get_grouped_punishments(Mute) # noinspection PyTypeChecker
return flanautils.chunks(buttons_texts, 3)
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()
async def _check_punishments(self):
punishment_groups = self._get_grouped_punishments(Punishment)
now = datetime.datetime.now()
for (user_id, group_id), sorted_punishments in punishment_groups:
if now < (last_punishment := sorted_punishments[-1]).until:
continue
if last_punishment.until + constants.PUNISHMENTS_RESET <= now:
for old_punishment in sorted_punishments:
old_punishment.delete()
if last_punishment.is_active:
await self.unpunish(user_id, group_id)
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)
@return_if_first_empty(exclude_self_types='FlanaBot', globals_=globals()) @return_if_first_empty(exclude_self_types='FlanaBot', globals_=globals())
async def _manage_exceptions(self, exceptions: BaseException | Iterable[BaseException], message: Message): async def _manage_exceptions(self, exceptions: BaseException | Iterable[BaseException], message: Message):
@@ -288,10 +248,7 @@ class FlanaBot(MultiBot, ABC):
new_line = ' ' if len(medias_sended_info) == 1 else '\n' new_line = ' ' if len(medias_sended_info) == 1 else '\n'
return f'{new_line}{medias_sended_info_joined}:' return f'{new_line}{medias_sended_info_joined}:'
async def _mute(self, user_id: int, group_id: int): async def _punish(self, user: int | str | User, group_: int | str | Chat | Message, message: Message = None):
pass
async def _punish(self, user_id: int, group_id: int):
pass pass
async def _search_and_send_medias(self, message: Message, send_song_info=False) -> list[Message]: async def _search_and_send_medias(self, message: Message, send_song_info=False) -> list[Message]:
@@ -333,15 +290,52 @@ class FlanaBot(MultiBot, ABC):
async def _show_config(self, config_name: str, message: Message): 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) 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): async def _unpunish(self, user: int | str | User, group_: int | str | Chat | Message, message: Message = None):
pass
async def _unpunish(self, user_id: int, group_id: int):
pass pass
# ---------------------------------------------- # # ---------------------------------------------- #
# HANDLERS # # HANDLERS #
# ---------------------------------------------- # # ---------------------------------------------- #
async def _on_button_press(self, message: Message):
await self._accept_button_event(message)
match message.button_pressed_text:
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 message.button_pressed_user.is_admin and 'auto_' in (config := message.button_pressed_text.split()[1]):
buttons_message_type = ButtonsMessageType.CONFIG
message.chat.config[config] = not message.chat.config[config]
message.save()
await self.edit('<b>Estos son los ajustes del grupo:</b>\n\n', self._get_config_buttons(message), message)
case _:
return
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): async def _on_bye(self, message: Message):
if not message.chat.is_group or self.is_bot_mentioned(message): if not message.chat.is_group or self.is_bot_mentioned(message):
await self.send_bye(message) await self.send_bye(message)
@@ -349,11 +343,13 @@ class FlanaBot(MultiBot, ABC):
@group @group
@bot_mentioned @bot_mentioned
async def _on_config_list_show(self, message: Message): async def _on_config_list_show(self, message: Message):
config_info = pprint.pformat(message.chat.config) # config_info = pprint.pformat(message.chat.config)
config_info = flanautils.translate(config_info, {'{': None, '}': None, ',': None, 'True': '', "'": None, 'False': ''}) # config_info = flanautils.translate(config_info, {'{': None, '}': None, ',': None, 'True': '', "'": None, 'False': '❌'})
config_info = config_info.splitlines() # config_info = config_info.splitlines()
config_info = '\n'.join(config_info_.strip() for config_info_ in config_info) # config_info = '\n'.join(config_info_.strip() for config_info_ in config_info)
await self.send(f'<b>Estos son los ajustes del grupo:</b>\n\n<code>{config_info}</code>', message) # await self.send(f'<b>Estos son los ajustes del grupo:</b>\n\n<code>{config_info}</code>', message)
await self.send('<b>Estos son los ajustes del grupo:</b>\n\n', self._get_config_buttons(message), message)
async def _on_covid_chart(self, message: Message): # todo2 async def _on_covid_chart(self, message: Message): # todo2
pass pass
@@ -413,31 +409,42 @@ class FlanaBot(MultiBot, ABC):
if not message.chat.is_group or self.is_bot_mentioned(message): if not message.chat.is_group or self.is_bot_mentioned(message):
await self.send_hello(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): async def _on_new_message_default(self, message: Message):
if message.is_inline: if message.is_inline:
await self._on_scraping(message) await self._on_scraping(message)
elif ( 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 and
not self.is_bot_mentioned(message) (
and message.author.id != self.owner_id
not message.chat.config['auto_scraping'] and
or (
not await self._on_scraping(message) self.is_bot_mentioned(message)
or
(
message.chat.config['auto_insult']
and
random.random() < constants.INSULT_PROBABILITY
)
)
)
): ):
await self.send_bad_phrase(message) await self.send_insult(message)
@ignore_self_message @ignore_self_message
async def _on_new_message_raw(self, message: Message): async def _on_new_message_raw(self, message: Message):
await super()._on_new_message_raw(message) await super()._on_new_message_raw(message)
if not message.is_inline: 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): async def _on_no_delete_original(self, message: Message):
if not await self._on_scraping(message, delete_original=False): if not await self._on_scraping(message, delete_original=False):
@@ -447,34 +454,35 @@ class FlanaBot(MultiBot, ABC):
@group @group
@admin(send_negative=True) @admin(send_negative=True)
async def _on_punish(self, message: Message): async def _on_punish(self, message: Message):
await self._update_punishment(self.punish, message, time=flanautils.words_to_time(message.text)) for user in await self._find_users_to_punish(message):
await self.punish(user, message, flanautils.words_to_time(message.text), message)
async def _on_ready(self): async def _on_ready(self):
await super()._on_ready() await super()._on_ready()
await flanautils.do_every(constants.CHECK_MUTES_EVERY_SECONDS, self._check_mutes) await flanautils.do_every(constants.CHECK_PUNISHMENTS_EVERY_SECONDS, Punishment.check_olds, self._unpunish, self.platform)
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) @inline(False)
async def _on_recover_message(self, message: Message): async def _on_recover_message(self, message: Message):
if message.replied_message: 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}) message_deleted_bot_action = BotAction.find_one({'action': Action.MESSAGE_DELETED.value, 'chat': message.chat.object_id, 'affected_objects': message.replied_message.object_id})
elif self.is_bot_mentioned(message): 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': Action.MESSAGE_DELETED.value,
'chat': message.chat.object_id,
'date': {'$gt': datetime.datetime.now(datetime.timezone.utc) - constants.RECOVERY_DELETED_MESSAGE_BEFORE}
}, sort_keys=(('date', pymongo.DESCENDING),))
else: else:
return return
if not message_deleted_bot_action: if not message_deleted_bot_action:
await self.send_error(random.choice(constants.RECOVER_PHRASES), message) await self.send_error('No hay nada que recuperar.', message)
return return
affected_object_ids = [affected_message_object_id for affected_message_object_id in message_deleted_bot_action.affected_objects] 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.platform.value, '_id': affected_object_id})).author.id != self.id]
for deleted_message in deleted_messages: for deleted_message in deleted_messages:
await self.send(deleted_message.text, message) await self.send(deleted_message.text, message)
message_deleted_bot_action.delete()
async def _on_scraping(self, message: Message, delete_original: bool = None) -> OrderedSet[Media]: async def _on_scraping(self, message: Message, delete_original: bool = None) -> OrderedSet[Media]:
sended_media_messages = await self._search_and_send_medias(message.replied_message) if message.replied_message else OrderedSet() sended_media_messages = await self._search_and_send_medias(message.replied_message) if message.replied_message else OrderedSet()
sended_media_messages += await self._search_and_send_medias(message) sended_media_messages += await self._search_and_send_medias(message)
@@ -539,17 +547,12 @@ class FlanaBot(MultiBot, ABC):
elif self.is_bot_mentioned(message) or not message.chat.is_group: 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) 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 @group
@bot_mentioned @bot_mentioned
@admin(send_negative=True) @admin(send_negative=True)
async def _on_unpunish(self, message: Message): async def _on_unpunish(self, message: Message):
await self._update_punishment(self.unpunish, message) for user in await self._find_users_to_punish(message):
await self.unpunish(user, message, message)
async def _on_weather_chart(self, message: Message): async def _on_weather_chart(self, message: Message):
bot_state_message: Message | None = None bot_state_message: Message | None = None
@@ -557,7 +560,7 @@ class FlanaBot(MultiBot, ABC):
show_progress_state = False show_progress_state = False
elif message.chat.is_group and not self.is_bot_mentioned(message): elif message.chat.is_group and not self.is_bot_mentioned(message):
if message.chat.config['auto_weather_chart']: 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': Action.AUTO_WEATHER_CHART.value, 'chat': message.chat.object_id, 'date': {'$gt': datetime.datetime.now(datetime.timezone.utc) - constants.AUTO_WEATHER_EVERY}}):
return return
show_progress_state = False show_progress_state = False
else: else:
@@ -565,8 +568,16 @@ class FlanaBot(MultiBot, ABC):
else: else:
show_progress_state = True show_progress_state = True
user_names_with_at_sign = {user.name.lower() for user in message.chat.users} possible_mentioned_ids = []
user_names_without_at_sign = {user.name.lower().replace('@', '') for user in message.chat.users} 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}')
if roles := await self.get_group_roles(message):
for role in roles:
possible_mentioned_ids.append(f'@{role.id}')
original_text_words = flanautils.remove_accents(message.text.lower()) original_text_words = flanautils.remove_accents(message.text.lower())
original_text_words = original_text_words.replace(',', ' ').replace(';', ' ').replace('-', ' -') original_text_words = original_text_words.replace(',', ' ').replace(';', ' ').replace('-', ' -')
original_text_words = flanautils.translate( original_text_words = flanautils.translate(
@@ -575,12 +586,11 @@ class FlanaBot(MultiBot, ABC):
).split() ).split()
place_words = ( place_words = (
OrderedSet(original_text_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['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, multibot_constants.KEYWORDS['date'], min_ratio=0.85).keys()
- flanautils.cartesian_product_string_matching(original_text_words, constants.KEYWORDS['thanks'], min_ratio=0.85).keys() - flanautils.cartesian_product_string_matching(original_text_words, multibot_constants.KEYWORDS['thanks'], min_ratio=0.85).keys()
- user_names_with_at_sign - possible_mentioned_ids
- user_names_without_at_sign
- flanautils.CommonWords.all_words - flanautils.CommonWords.all_words
) )
if not place_words: if not place_words:
@@ -592,8 +602,8 @@ class FlanaBot(MultiBot, ABC):
place_words.insert(0, 'calle') place_words.insert(0, 'calle')
place_query = ' '.join(place_words) place_query = ' '.join(place_words)
if len(place_query) >= constants.MAX_PLACE_QUERY_LENGTH: if len(place_query) >= constants.MAX_PLACE_QUERY_LENGTH:
if not message.is_inline: if show_progress_state:
await self.send_error(Media(str(flanautils.resolve_path('resources/mucho_texto.png'))), message, send_as_file=False) await self.send_error(Media(str(flanautils.resolve_path('resources/mucho_texto.png')), MediaType.IMAGE, Source.LOCAL), message, send_as_file=False)
return return
if show_progress_state: if show_progress_state:
bot_state_message = await self.send(f'Buscando "{place_query}" en el mapa 🧐...', message) bot_state_message = await self.send(f'Buscando "{place_query}" en el mapa 🧐...', message)
@@ -714,55 +724,13 @@ class FlanaBot(MultiBot, ABC):
# -------------------------------------------------------- # # -------------------------------------------------------- #
# -------------------- PUBLIC METHODS -------------------- # # -------------------- 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 | Message) -> bool:
pass pass
async def is_muted(self, user_id: int, group_id: int) -> bool: async def punish(self, user: int | str | User, group_: int | str | Chat | Message, time: int | datetime.timedelta, message: Message = None):
pass # noinspection PyTypeChecker
punish = Punishment(self.platform, self.get_user_id(user), self.get_group_id(group_), time)
async def mute(self, user_id: int, group_id: int, time: int | datetime.timedelta, message: Message = None): await punish.punish(self._punish, self._unpunish, message)
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):
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)))
else:
if time:
# noinspection PyTypeChecker
Punishment(user_id, group_id, until=datetime.datetime.now() + time).save()
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)
async def send_bye(self, message: Message) -> multibot_constants.ORIGINAL_MESSAGE: 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) return await self.send(random.choice((*constants.BYE_PHRASES, flanautils.CommonWords.random_time_greeting())), message)
@@ -770,6 +738,10 @@ class FlanaBot(MultiBot, ABC):
async def send_hello(self, message: Message) -> multibot_constants.ORIGINAL_MESSAGE: 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) 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()) @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]: async def send_medias(self, medias: OrderedSet[Media], message: Message, send_song_info=False) -> tuple[list[Message], int]:
sended_media_messages = [] sended_media_messages = []
@@ -781,7 +753,7 @@ class FlanaBot(MultiBot, ABC):
bot_state_message: Message = await self.send('Descargando...', message) bot_state_message: Message = await self.send('Descargando...', message)
if message.chat.is_group: if message.chat.is_group:
sended_info_message = await self.send(f'{message.author.name} compartió{self._medias_sended_info(medias)}', message) sended_info_message = await self.send(f"{message.author.name.split('#')[0]} compartió{self._medias_sended_info(medias)}", message)
for media in medias: for media in medias:
if not media.content: if not media.content:
@@ -792,10 +764,7 @@ class FlanaBot(MultiBot, ABC):
message.song_infos.add(media.song_info) message.song_infos.add(media.song_info)
message.save() message.save()
try: if not (bot_message := await self.send(media, message)):
bot_message = await self.send(media, message)
except SendError as e:
await self._manage_exceptions(e, message)
fails += 1 fails += 1
else: else:
sended_media_messages.append(bot_message) sended_media_messages.append(bot_message)
@@ -819,21 +788,14 @@ class FlanaBot(MultiBot, ABC):
attributes = ( attributes = (
f'Título: {song_info.title}\n' if song_info.title else '', f'Título: {song_info.title}\n' if song_info.title else '',
f'Autor: {song_info.author}\n' if song_info.author 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:' f'Previa:'
) )
await self.send(''.join(attributes), message) await self.send(''.join(attributes), message)
if song_info: if song_info:
await self.send(song_info, message) await self.send(song_info, message)
async def unmute(self, user_id: int, group_id: int, message: Message = None): async def unpunish(self, user: int | str | User, group_: int | str | Chat | Message, message: Message = None):
try: # noinspection PyTypeChecker
await self._unmute(user_id, group_id) punish = Punishment(self.platform, self.get_user_id(user), self.get_group_id(group_))
except UserDisconnectedError as e: await punish.unpunish(self._unpunish, message)
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):
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)))

View File

@@ -1,13 +1,14 @@
__all__ = ['FlanaDiscBot']
import asyncio import asyncio
import os import os
import discord import discord
from multibot import DiscordBot from multibot import BadRoleError, DiscordBot, User
from flanabot import constants from flanabot import constants
from flanabot.bots.flana_bot import FlanaBot from flanabot.bots.flana_bot import FlanaBot
from flanabot.exceptions import BadRoleError, UserDisconnectedError from flanabot.models import Chat, Message, Punishment
from flanabot.models import User
HEAT_NAMES = [ HEAT_NAMES = [
'Canal Congelado', 'Canal Congelado',
@@ -20,18 +21,9 @@ HEAT_NAMES = [
'Verano Cordobés al Sol', 'Verano Cordobés al Sol',
'Canal Ardiendo', 'Canal Ardiendo',
'abrid las putas ventanas y traed el extintor', 'abrid las putas ventanas y traed el extintor',
'Canal INFIERNO', 'Canal INFIERNO'
'La Palma 🌋'
] ]
HOT_CHANNEL_ID = 493530483045564417 HOT_CHANNEL_ID = 493530483045564417
ROLES = {
'Administrador': 387344390030360587,
'Carroñero': 493523298429435905,
'al lol': 881238165476741161,
'Persona': 866046517998387220,
'Castigado': 877662459568209921,
'Bot': 493784221085597706
}
# ---------------------------------------------------------------------------------------------------- # # ---------------------------------------------------------------------------------------------------- #
@@ -48,7 +40,7 @@ class FlanaDiscBot(DiscordBot, FlanaBot):
# ----------------------------------------------------------- # # ----------------------------------------------------------- #
def _add_handlers(self): def _add_handlers(self):
super()._add_handlers() super()._add_handlers()
self.bot_client.add_listener(self._on_voice_state_update, 'on_voice_state_update') self.client.add_listener(self._on_voice_state_update, 'on_voice_state_update')
async def _heat_channel(self, channel: discord.VoiceChannel): async def _heat_channel(self, channel: discord.VoiceChannel):
while True: while True:
@@ -67,33 +59,19 @@ class FlanaDiscBot(DiscordBot, FlanaBot):
await channel.edit(name=HEAT_NAMES[int(self.heat_level)]) await channel.edit(name=HEAT_NAMES[int(self.heat_level)])
async def _mute(self, user_id: int, group_id: int): async def _punish(self, user: int | str | User, group_: int | str | Chat | Message, message: Message = None):
user = await self.get_user(user_id, group_id) user_id = self.get_user_id(user)
try: try:
await user.original_object.edit(mute=True) await self.add_role(user_id, group_, 'Castigado')
except discord.errors.HTTPException: await self.remove_role(user_id, group_, 'Persona')
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))
except AttributeError: except AttributeError:
raise BadRoleError(str(self._punish)) raise BadRoleError(str(self._punish))
async def _unmute(self, user_id: int, group_id: int): async def _unpunish(self, user: int | str | User, group_: int | str | Chat | Message, message: Message = None):
user = await self.get_user(user_id, group_id) user_id = self.get_user_id(user)
try: try:
await user.original_object.edit(mute=False) await self.add_role(user_id, group_, 'Persona')
except discord.errors.HTTPException: await self.remove_role(user_id, group_, 'Castigado')
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))
except AttributeError: except AttributeError:
raise BadRoleError(str(self._unpunish)) raise BadRoleError(str(self._unpunish))
@@ -116,18 +94,12 @@ class FlanaDiscBot(DiscordBot, FlanaBot):
# -------------------------------------------------------- # # -------------------------------------------------------- #
# -------------------- PUBLIC METHODS -------------------- # # -------------------- 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 | Message):
user = await self.get_user(user_id, group_id) user = await self.get_user(user, group_)
return user.original_object.voice.deaf group_id = self.get_group_id(group_)
return group_id in {punishment.group_id for punishment in Punishment.find({
async def is_muted(self, user_id: int, group_id: int) -> bool: 'platform': self.platform.value,
user = await self.get_user(user_id, group_id) 'user_id': user.id,
return user.original_object.voice.mute 'group_id': group_id,
'is_active': True
@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

View File

@@ -1,18 +1,16 @@
from __future__ import annotations # todo0 remove in 3.11 from __future__ import annotations # todo0 remove in 3.11
__all__ = ['whitelisted_event', 'FlanaTeleBot']
import functools import functools
import os import os
from typing import Callable from typing import Callable
import telethon.tl.functions import telethon.tl.functions
from flanaapis.weather.constants import WeatherEmoji from multibot import TelegramBot, find_message, user_client
from flanautils import Media, MediaType, return_if_first_empty
from multibot import TelegramBot, constants as multibot_constants, find_message, user_client
from flanabot.bots.flana_bot import FlanaBot from flanabot.bots.flana_bot import FlanaBot
from flanabot.models.chat import Chat from flanabot.models import Message
from flanabot.models.message import Message
from flanabot.models.user import User
# ---------------------------------------------------------- # # ---------------------------------------------------------- #
@@ -48,20 +46,6 @@ class FlanaTeleBot(TelegramBot, FlanaBot):
# ----------------------------------------------------------- # # ----------------------------------------------------------- #
# -------------------- PROTECTED METHODS -------------------- # # -------------------- 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 @user_client
async def _get_contacts_ids(self) -> list[int]: async def _get_contacts_ids(self) -> list[int]:
async with self.user_client: async with self.user_client:
@@ -71,43 +55,11 @@ class FlanaTeleBot(TelegramBot, FlanaBot):
@user_client @user_client
async def _update_whitelist(self): async def _update_whitelist(self):
self.whitelist_ids = [self.owner_id, self.bot_id] + await self._get_contacts_ids() self.whitelist_ids = [self.owner_id, self.id] + await self._get_contacts_ids()
# ---------------------------------------------- # # ---------------------------------------------- #
# HANDLERS # # 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 @whitelisted_event
async def _on_inline_query_raw(self, message: Message): async def _on_inline_query_raw(self, message: Message):
await super()._on_new_message_raw(message) await super()._on_new_message_raw(message)

View File

@@ -3,19 +3,22 @@ import datetime
import flanautils import flanautils
AUTO_WEATHER_EVERY = datetime.timedelta(hours=6) 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() CHECK_PUNISHMENTS_EVERY_SECONDS = datetime.timedelta(hours=1).total_seconds()
HEAT_PERIOD_SECONDS = datetime.timedelta(minutes=15).total_seconds() HEAT_PERIOD_SECONDS = datetime.timedelta(minutes=15).total_seconds()
INSULT_PROBABILITY = 0.00166666667
MAX_PLACE_QUERY_LENGTH = 50 MAX_PLACE_QUERY_LENGTH = 50
PUNISHMENT_INCREMENT_EXPONENT = 6 PUNISHMENT_INCREMENT_EXPONENT = 6
PUNISHMENTS_RESET = datetime.timedelta(weeks=6 * flanautils.WEEKS_IN_A_MONTH) PUNISHMENTS_RESET = datetime.timedelta(weeks=6 * flanautils.WEEKS_IN_A_MONTH)
RECOVERY_DELETED_MESSAGE_BEFORE = datetime.timedelta(hours=1) 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 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.', 'Cállate ya anda.',
'¿Quién te ha preguntado?', '¿Quién te ha preguntado?',
'¿Tú eres así o te dan apagones cerebrales?', '¿Tú eres así o te dan apagones cerebrales?',
@@ -42,55 +45,23 @@ BAD_PHRASES = (
'Eres más malo que pegarle a un padre.' '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 = { 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', 'covid_chart': ('case', 'caso', 'contagiado', 'contagio', 'corona', 'coronavirus', 'covid', 'covid19', 'death',
'disease', 'enfermedad', 'enfermos', 'fallecido', 'incidencia', 'jacovid', 'mascarilla', 'muerte', 'disease', 'enfermedad', 'enfermos', 'fallecido', 'incidencia', 'jacovid', 'mascarilla', 'muerte',
'muerto', 'pandemia', 'sick', 'virus'), 'muerto', 'pandemia', 'sick', 'virus'),
'currency_chart': ('argentina', 'bitcoin', 'cardano', 'cripto', 'crypto', 'criptodivisa', 'cryptodivisa', 'currency_chart': ('argentina', 'bitcoin', 'cardano', 'cripto', 'crypto', 'criptodivisa', 'cryptodivisa',
'cryptomoneda', 'cryptocurrency', 'currency', 'dinero', 'divisa', 'ethereum', 'inversion', 'cryptomoneda', 'cryptocurrency', 'currency', 'dinero', 'divisa', 'ethereum', 'inversion',
'moneda', 'pasta'), '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', 'punish': ('acaba', 'aprende', 'ataca', 'atalo', 'azota', 'boss', 'castiga', 'castigo', 'condena', 'controla',
'destroy', 'destroza', 'duro', 'ejecuta', 'enseña', 'escarmiento', 'execute', 'finish', 'fuck', 'fusila', 'destroy', 'destroza', 'duro', 'ejecuta', 'enseña', 'escarmiento', 'execute', 'finish', 'fuck', 'fusila',
'hell', 'humos', 'infierno', 'jefe', 'jode', 'learn', 'leccion', 'lesson', 'manda', 'purgatorio', 'hell', 'humos', 'infierno', 'jefe', 'jode', 'learn', 'leccion', 'lesson', 'manda', 'purgatorio',
'sancion', 'shoot', 'teach', 'termina', 'whip'), 'sancion', 'shoot', 'teach', 'termina', 'whip'),
'reset': ('recover', 'recovery', 'recupera', 'reinicia', 'reset', 'resetea', 'restart'),
'scraping': ('api', 'aqui', 'busca', 'contenido', 'content', 'descarga', 'descargar', 'download', 'envia', 'habia', 'scraping': ('api', 'aqui', 'busca', 'contenido', 'content', 'descarga', 'descargar', 'download', 'envia', 'habia',
'media', 'redes', 'scrap', 'scraping', 'search', 'send', 'social', 'sociales', 'tenia', 'video', 'media', 'redes', 'scrap', 'scraping', 'search', 'send', 'social', 'sociales', 'tenia', 'video',
'videos'), 'videos'),
'show': ('actual', 'enseña', 'estado', 'how', 'muestra', 'show', 'como'),
'song_info': ('aqui', 'cancion', 'data', 'datos', 'info', 'informacion', 'information', 'llama', 'media', 'name', 'song_info': ('aqui', 'cancion', 'data', 'datos', 'info', 'informacion', 'information', 'llama', 'media', 'name',
'nombre', 'sonaba', 'sonando', 'song', 'sono', 'sound', 'suena', 'title', 'titulo', 'nombre', 'sonaba', 'sonando', 'song', 'sono', 'sound', 'suena', 'title', 'titulo',
'video'), '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'), 'unpunish': ('absolve', 'forgive', 'innocent', 'inocente', 'perdona', 'spare'),
'weather_chart': ('atmosfera', 'atmosferico', 'calle', 'calor', 'caloret', 'clima', 'climatologia', 'cloud', 'weather_chart': ('atmosfera', 'atmosferico', 'calle', 'calor', 'caloret', 'clima', 'climatologia', 'cloud',
'cloudless', 'cloudy', 'cold', 'congelar', 'congelado', 'denbora', 'despejado', 'diluvio', 'frio', 'cloudless', 'cloudy', 'cold', 'congelar', 'congelado', 'denbora', 'despejado', 'diluvio', 'frio',
@@ -100,14 +71,5 @@ KEYWORDS = {
'wetter') 'wetter')
} }
RECOVER_PHRASES = (
'No hay nada que recuperar.',
'Ya lo he recuperado y enviado, así que callate ya.',
'Ya lo he recuperado y enviado, así que mejor estás antento antes de dar por culo.',
'Ya lo he recuperado y enviado, no lo voy a hacer dos veces.',
'Ya lo he recuperado y enviado. A ver si leemos más y jodemos menos.',
'Ya lo he reenviado.'
)
SCRAPING_PHRASES = ('Analizando...', 'Buscando...', 'Hackeando internet... 👀', 'Rebuscando en la web...', SCRAPING_PHRASES = ('Analizando...', 'Buscando...', 'Hackeando internet... 👀', 'Rebuscando en la web...',
'Robando cosas...', 'Scrapeando...', 'Scraping...') 'Robando cosas...', 'Scrapeando...', 'Scraping...')

View File

@@ -1,6 +0,0 @@
class BadRoleError(Exception):
pass
class UserDisconnectedError(Exception):
pass

View File

@@ -5,14 +5,15 @@ import flanautils
os.environ |= flanautils.find_environment_variables('../.env') os.environ |= flanautils.find_environment_variables('../.env')
import asyncio import asyncio
from flanabot.bots.flana_disc_bot import FlanaDiscBot
from flanabot.bots.flana_tele_bot import FlanaTeleBot from flanabot.bots.flana_tele_bot import FlanaTeleBot
async def main(): async def main():
flana_disc_bot = FlanaDiscBot()
flana_tele_bot = FlanaTeleBot() flana_tele_bot = FlanaTeleBot()
await asyncio.gather( await asyncio.gather(
flana_disc_bot.start(),
flana_tele_bot.start() flana_tele_bot.start()
) )

View File

@@ -1,5 +1,5 @@
from flanabot.models.chat import * from flanabot.models.chat import *
from flanabot.models.enums import *
from flanabot.models.message import * from flanabot.models.message import *
from flanabot.models.punishments import * from flanabot.models.punishment import *
from flanabot.models.user import *
from flanabot.models.weather_chart import * from flanabot.models.weather_chart import *

View File

@@ -1,21 +1,19 @@
from dataclasses import dataclass, field __all__ = ['Chat']
from multibot import Chat as MultiBotChat from dataclasses import dataclass
from flanabot.models.user import User from multibot.models import Chat as MultiBotChat
@dataclass(eq=False) @dataclass(eq=False)
class Chat(MultiBotChat): class Chat(MultiBotChat):
DEFAULT_CONFIG = {'auto_clear': False, DEFAULT_CONFIG = {'auto_covid_chart': True,
'auto_covid_chart': True,
'auto_currency_chart': True, 'auto_currency_chart': True,
'auto_delete_original': True, 'auto_delete_original': True,
'auto_insult': True,
'auto_scraping': True, 'auto_scraping': True,
'auto_weather_chart': True} 'auto_weather_chart': True}
users: list[User] = field(default_factory=list)
def __post_init__(self): def __post_init__(self):
super().__post_init__() super().__post_init__()
if not self.config: self.config = self.DEFAULT_CONFIG | self.config
self.config = self.DEFAULT_CONFIG

10
flanabot/models/enums.py Normal file
View File

@@ -0,0 +1,10 @@
__all__ = ['ButtonsMessageType']
from enum import auto
from flanautils import FlanaEnum
class ButtonsMessageType(FlanaEnum):
CONFIG = auto()
WEATHER = auto()

View File

@@ -1,19 +1,21 @@
from __future__ import annotations # todo0 remove in 3.11 from __future__ import annotations # todo0 remove in 3.11
__all__ = ['Message']
from dataclasses import dataclass, field from dataclasses import dataclass, field
from typing import Iterable from typing import Iterable
from flanautils import Media, OrderedSet 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.chat import Chat
from flanabot.models.user import User
from flanabot.models.weather_chart import WeatherChart from flanabot.models.weather_chart import WeatherChart
@dataclass(eq=False) @dataclass(eq=False)
class Message(MultiBotMessage): class Message(MultiBotMessage):
author: User = None author: User = None
button_pressed_user: User = None
mentions: Iterable[User] = field(default_factory=list) mentions: Iterable[User] = field(default_factory=list)
chat: Chat = None chat: Chat = None
replied_message: Message = None replied_message: Message = None

View File

@@ -0,0 +1,32 @@
__all__ = ['Punishment']
import datetime
from dataclasses import dataclass
from typing import Callable
from multibot.models import Platform, PunishmentBase, db
from flanabot import constants
@dataclass(eq=False)
class Punishment(PunishmentBase):
collection = db.punishment
@classmethod
async def check_olds(cls, unpunishment_method: Callable, platform: Platform):
punishment_groups = cls._get_grouped_punishments(platform)
now = datetime.datetime.now(datetime.timezone.utc)
for (_, _), sorted_punishments in punishment_groups:
if not (last_punishment := sorted_punishments[-1]).until or now < last_punishment.until:
continue
if last_punishment.until + constants.PUNISHMENTS_RESET <= now:
for old_punishment in sorted_punishments:
old_punishment.delete()
if last_punishment.is_active:
await last_punishment.unpunish(unpunishment_method)
last_punishment.is_active = False
last_punishment.save()

View File

@@ -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

View File

@@ -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})}

View File

@@ -1,3 +1,5 @@
__all__ = ['Direction', 'WeatherChart']
import datetime import datetime
from dataclasses import dataclass, field from dataclasses import dataclass, field
@@ -44,7 +46,7 @@ class WeatherChart(DateChart):
if self.show_now_vertical_line and first_dt <= now <= last_dt: 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_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: 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) date_time = datetime.datetime(year=day_weather.date.year, month=day_weather.date.month, day=day_weather.date.day, tzinfo=self.timezone)

View File

@@ -1,9 +1,9 @@
aiohttp==3.7.4.post0 aiohttp==3.8.1
aiosignal==1.2.0 aiosignal==1.2.0
anyio==3.5.0 anyio==3.5.0
asgiref==3.4.1 asgiref==3.4.1
async-generator==1.10 async-generator==1.10
async-timeout==3.0.1 async-timeout==4.0.2
attrs==21.4.0 attrs==21.4.0
beautifulsoup4==4.10.0 beautifulsoup4==4.10.0
certifi==2021.10.8 certifi==2021.10.8
@@ -14,7 +14,7 @@ click==8.0.3
colorama==0.4.4 colorama==0.4.4
cryptg==0.2.post4 cryptg==0.2.post4
cryptography==36.0.1 cryptography==36.0.1
discord.py==1.7.3 discord.py @ git+https://github.com/Rapptz/discord.py@b7e25645dc68bbb828bf1ede711d098c6b183237
fastapi==0.71.0 fastapi==0.71.0
flanaapis flanaapis
flanautils flanautils
@@ -47,6 +47,7 @@ selenium-stealth==1.0.6
six==1.16.0 six==1.16.0
sniffio==1.2.0 sniffio==1.2.0
sortedcontainers==2.4.0 sortedcontainers==2.4.0
soupsieve==2.3.1
starlette==0.17.1 starlette==0.17.1
sympy==1.9 sympy==1.9
Telethon==1.24.0 Telethon==1.24.0
@@ -54,7 +55,7 @@ tenacity==8.0.1
TikTokApi==4.1.0 TikTokApi==4.1.0
trio==0.19.0 trio==0.19.0
trio-websocket==0.9.2 trio-websocket==0.9.2
twitchio==2.1.4 twitchio==2.3.0
typing_extensions==4.0.1 typing_extensions==4.0.1
ujson==5.1.0 ujson==5.1.0
urllib3==1.26.8 urllib3==1.26.8

View File

@@ -32,7 +32,10 @@ class TestParseCallbacks(unittest.TestCase):
'flanabot ajustes', 'flanabot ajustes',
'Flanabot ajustes', 'Flanabot ajustes',
'Flanabot qué puedo ajustar?', '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) self._test_no_always_callbacks(phrases, self.flana_tele_bot._on_config_list_show)