Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
00323f99fb | ||
|
|
6237ccf8b6 | ||
|
|
f402293c99 | ||
|
|
99effad710 | ||
|
|
54cada0649 | ||
|
|
0f5d3c333a | ||
|
|
5fb55404cf | ||
|
|
2768d8e949 | ||
|
|
b0ca5a2ded | ||
|
|
697ba9e89e | ||
|
|
09df75ea0b | ||
|
|
c2e7e68619 | ||
|
|
f12d0c18c1 | ||
|
|
b55f933a32 |
@@ -7,6 +7,8 @@ services:
|
|||||||
|
|
||||||
mongodb:
|
mongodb:
|
||||||
image: mongo
|
image: mongo
|
||||||
|
ports:
|
||||||
|
- "27017:27017"
|
||||||
environment:
|
environment:
|
||||||
- MONGO_INITDB_ROOT_USERNAME=${MONGO_USER}
|
- MONGO_INITDB_ROOT_USERNAME=${MONGO_USER}
|
||||||
- MONGO_INITDB_ROOT_PASSWORD=${MONGO_PASSWORD}
|
- MONGO_INITDB_ROOT_PASSWORD=${MONGO_PASSWORD}
|
||||||
|
|||||||
@@ -2,11 +2,13 @@ __all__ = ['FlanaBot']
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import datetime
|
import datetime
|
||||||
|
import math
|
||||||
import random
|
import random
|
||||||
|
import re
|
||||||
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
|
from typing import Iterable, Sequence
|
||||||
|
|
||||||
import flanaapis.geolocation.functions
|
import flanaapis.geolocation.functions
|
||||||
import flanaapis.weather.functions
|
import flanaapis.weather.functions
|
||||||
@@ -15,7 +17,7 @@ import plotly.graph_objects
|
|||||||
import pymongo
|
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, BadRoleError, BotAction, MultiBot, SendError, User, admin, bot_mentioned, constants as multibot_constants, group, ignore_self_message, inline, reply
|
from multibot import Action, BadRoleError, BotAction, ButtonsGroup, 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.models import Chat, Message, Punishment, WeatherChart
|
from flanabot.models import Chat, Message, Punishment, WeatherChart
|
||||||
@@ -37,6 +39,10 @@ class FlanaBot(MultiBot, ABC):
|
|||||||
|
|
||||||
self.register(self._on_bye, multibot_constants.KEYWORDS['bye'])
|
self.register(self._on_bye, multibot_constants.KEYWORDS['bye'])
|
||||||
|
|
||||||
|
self.register(self._on_choose, constants.KEYWORDS['choose'])
|
||||||
|
self.register(self._on_choose, constants.KEYWORDS['random'])
|
||||||
|
self.register(self._on_choose, (constants.KEYWORDS['choose'], constants.KEYWORDS['random']))
|
||||||
|
|
||||||
self.register(self._on_config_list_show, multibot_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_config_list_show, (multibot_constants.KEYWORDS['show'], multibot_constants.KEYWORDS['config']))
|
||||||
|
|
||||||
@@ -79,6 +85,8 @@ class FlanaBot(MultiBot, ABC):
|
|||||||
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['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_delete_original_config_show, (multibot_constants.KEYWORDS['show'], multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['message'], multibot_constants.KEYWORDS['config']))
|
||||||
|
|
||||||
|
self.register(self._on_dice, constants.KEYWORDS['dice'])
|
||||||
|
|
||||||
self.register(self._on_hello, multibot_constants.KEYWORDS['hello'])
|
self.register(self._on_hello, multibot_constants.KEYWORDS['hello'])
|
||||||
|
|
||||||
self.register(self._on_new_message_default, default=True)
|
self.register(self._on_new_message_default, default=True)
|
||||||
@@ -88,6 +96,8 @@ class FlanaBot(MultiBot, ABC):
|
|||||||
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['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_no_delete_original, (multibot_constants.KEYWORDS['deactivate'], multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['message']))
|
||||||
|
|
||||||
|
self.register(self._on_poll, constants.KEYWORDS['poll'])
|
||||||
|
|
||||||
self.register(self._on_punish, constants.KEYWORDS['punish'])
|
self.register(self._on_punish, constants.KEYWORDS['punish'])
|
||||||
self.register(self._on_punish, (multibot_constants.KEYWORDS['deactivate'], constants.KEYWORDS['unpunish']))
|
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_punish, (multibot_constants.KEYWORDS['deactivate'], multibot_constants.KEYWORDS['permission']))
|
||||||
@@ -111,6 +121,11 @@ class FlanaBot(MultiBot, ABC):
|
|||||||
|
|
||||||
self.register(self._on_song_info, constants.KEYWORDS['song_info'])
|
self.register(self._on_song_info, constants.KEYWORDS['song_info'])
|
||||||
|
|
||||||
|
self.register(self._on_stop_poll, multibot_constants.KEYWORDS['deactivate'])
|
||||||
|
self.register(self._on_stop_poll, (multibot_constants.KEYWORDS['deactivate'], constants.KEYWORDS['poll']))
|
||||||
|
self.register(self._on_stop_poll, multibot_constants.KEYWORDS['stop'])
|
||||||
|
self.register(self._on_stop_poll, (multibot_constants.KEYWORDS['stop'], constants.KEYWORDS['poll']))
|
||||||
|
|
||||||
self.register(self._on_unpunish, constants.KEYWORDS['unpunish'])
|
self.register(self._on_unpunish, constants.KEYWORDS['unpunish'])
|
||||||
|
|
||||||
self.register(self._on_unpunish, (multibot_constants.KEYWORDS['deactivate'], constants.KEYWORDS['punish']))
|
self.register(self._on_unpunish, (multibot_constants.KEYWORDS['deactivate'], constants.KEYWORDS['punish']))
|
||||||
@@ -131,8 +146,9 @@ class FlanaBot(MultiBot, ABC):
|
|||||||
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['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(self._on_weather_chart_config_show, (multibot_constants.KEYWORDS['show'], constants.KEYWORDS['weather_chart'], multibot_constants.KEYWORDS['config']))
|
||||||
|
|
||||||
self.register_button(self._on_config_button_press, list(Chat.DEFAULT_CONFIG.keys()))
|
self.register_button(self._on_config_button_press, ButtonsGroup.CONFIG)
|
||||||
self.register_button(self._on_weather_button_press, WeatherEmoji.values)
|
self.register_button(self._on_poll_button_press, ButtonsGroup.POLL)
|
||||||
|
self.register_button(self._on_weather_button_press, ButtonsGroup.WEATHER)
|
||||||
|
|
||||||
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:
|
||||||
@@ -152,20 +168,15 @@ class FlanaBot(MultiBot, ABC):
|
|||||||
last_2s_messages = Message.find({
|
last_2s_messages = Message.find({
|
||||||
'platform': self.platform.value,
|
'platform': self.platform.value,
|
||||||
'author': message.author.object_id,
|
'author': message.author.object_id,
|
||||||
'last_update': {
|
'last_update': {'$gte': datetime.datetime.now(datetime.timezone.utc) - 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,
|
'platform': self.platform.value,
|
||||||
'author': message.author.object_id,
|
'author': message.author.object_id,
|
||||||
'last_update': {
|
'last_update': {'$gte': datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(seconds=7)}
|
||||||
'$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:
|
if len(last_2s_messages) >= constants.FLOOD_2s_LIMIT or len(last_7s_messages) >= constants.FLOOD_7s_LIMIT:
|
||||||
n_punishments = len(Punishment.find({
|
n_punishments = len(Punishment.find({
|
||||||
'platform': self.platform.value,
|
'platform': self.platform.value,
|
||||||
'user_id': message.author.id,
|
'user_id': message.author.id,
|
||||||
@@ -179,18 +190,6 @@ class FlanaBot(MultiBot, ABC):
|
|||||||
else:
|
else:
|
||||||
await self.send(f'Castigado durante {TimeUnits(seconds=punishment_seconds).to_words()}.', message)
|
await self.send(f'Castigado durante {TimeUnits(seconds=punishment_seconds).to_words()}.', message)
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
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()]
|
|
||||||
# noinspection PyTypeChecker
|
|
||||||
return flanautils.chunks(buttons_texts, 3)
|
|
||||||
|
|
||||||
@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], context: Chat | Message):
|
async def _manage_exceptions(self, exceptions: BaseException | Iterable[BaseException], context: Chat | Message):
|
||||||
if not isinstance(exceptions, Iterable):
|
if not isinstance(exceptions, Iterable):
|
||||||
@@ -271,7 +270,7 @@ class FlanaBot(MultiBot, ABC):
|
|||||||
return_exceptions=True
|
return_exceptions=True
|
||||||
)
|
)
|
||||||
|
|
||||||
if not message.is_inline and (self.is_bot_mentioned(message) or not message.chat.is_group):
|
if not message.is_inline and (self.is_bot_mentioned(message) or message.chat.is_private):
|
||||||
while not results.done():
|
while not results.done():
|
||||||
if constants.SCRAPING_MESSAGE_WAITING_TIME <= time_module.perf_counter() - start_time:
|
if constants.SCRAPING_MESSAGE_WAITING_TIME <= time_module.perf_counter() - start_time:
|
||||||
bot_state_message = await self.send(random.choice(constants.SCRAPING_PHRASES), message)
|
bot_state_message = await self.send(random.choice(constants.SCRAPING_PHRASES), message)
|
||||||
@@ -298,23 +297,63 @@ class FlanaBot(MultiBot, ABC):
|
|||||||
# HANDLERS #
|
# HANDLERS #
|
||||||
# ---------------------------------------------- #
|
# ---------------------------------------------- #
|
||||||
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 message.chat.is_private or self.is_bot_mentioned(message):
|
||||||
await self.send_bye(message)
|
await self.send_bye(message)
|
||||||
|
|
||||||
async def _on_config_button_press(self, message: Message):
|
async def _on_choose(self, message: Message):
|
||||||
await self._accept_button_event(message)
|
if message.chat.is_group and not self.is_bot_mentioned(message):
|
||||||
|
|
||||||
if not message.button_pressed_user.is_admin or 'auto_' not in (config := message.button_pressed_text.split()[1]):
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
discarded_words = {
|
||||||
|
*constants.KEYWORDS['choose'],
|
||||||
|
*constants.KEYWORDS['random'],
|
||||||
|
self.name.lower(), f'<@{self.id}>',
|
||||||
|
*flanautils.CommonWords.get('conjunctions'),
|
||||||
|
'entre', 'between'
|
||||||
|
}
|
||||||
|
|
||||||
|
if final_words := [word for word in message.text.split() if not flanautils.cartesian_product_string_matching(word.lower(), discarded_words, min_ratio=multibot_constants.PARSE_CALLBACKS_MIN_RATIO_DEFAULT)]:
|
||||||
|
for i in range(1, len(final_words) - 1):
|
||||||
|
try:
|
||||||
|
n1 = flanautils.cast_number(final_words[i - 1])
|
||||||
|
except ValueError:
|
||||||
|
try:
|
||||||
|
n1 = flanautils.words_to_numbers(final_words[i - 1], ignore_no_numbers=False)
|
||||||
|
except KeyError:
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
n2 = flanautils.cast_number(final_words[i + 1])
|
||||||
|
except ValueError:
|
||||||
|
try:
|
||||||
|
n2 = flanautils.words_to_numbers(final_words[i + 1], ignore_no_numbers=False)
|
||||||
|
except KeyError:
|
||||||
|
continue
|
||||||
|
if final_words[i] in ('al', 'el', 'to'):
|
||||||
|
await self.send(random.randint(math.ceil(n1), math.floor(n2)), message)
|
||||||
|
return
|
||||||
|
await self.send(random.choice(final_words), message)
|
||||||
|
else:
|
||||||
|
await self.send(random.choice(('¿Que elija el qué?', '¿Y las opciones?', '?', '🤔')), message)
|
||||||
|
|
||||||
|
async def _on_config_button_press(self, message: Message):
|
||||||
|
await self.accept_button_event(message)
|
||||||
|
|
||||||
|
if message.buttons_info.presser_user.is_admin is False:
|
||||||
|
return
|
||||||
|
|
||||||
|
config = message.buttons_info.pressed_text.split()[1]
|
||||||
message.chat.config[config] = not message.chat.config[config]
|
message.chat.config[config] = not message.chat.config[config]
|
||||||
message.save()
|
pressed_button = message.buttons_info.pressed_button
|
||||||
await self.edit('<b>Estos son los ajustes del grupo:</b>\n\n', self._get_config_buttons(message), message)
|
pressed_button.is_checked = not pressed_button.is_checked
|
||||||
|
pressed_button.text = f"{'✔' if pressed_button.is_checked else '❌'} {config}"
|
||||||
|
|
||||||
|
await self.edit('<b>Estos son los ajustes del grupo:</b>\n\n', message.buttons_info.buttons, message)
|
||||||
|
|
||||||
@group
|
@group
|
||||||
@bot_mentioned
|
@bot_mentioned
|
||||||
async def _on_config_list_show(self, message: Message):
|
async def _on_config_list_show(self, message: Message):
|
||||||
await self.send('<b>Estos son los ajustes del grupo:</b>\n\n', self._get_config_buttons(message), message)
|
buttons_texts = [(f"{'✔' if v else '❌'} {k}", v) for k, v in message.chat.config.items()]
|
||||||
|
await self.send('<b>Estos son los ajustes del grupo:</b>\n\n', flanautils.chunks(buttons_texts, 3), message, buttons_key=ButtonsGroup.CONFIG)
|
||||||
|
|
||||||
async def _on_covid_chart(self, message: Message): # todo2
|
async def _on_covid_chart(self, message: Message): # todo2
|
||||||
pass
|
pass
|
||||||
@@ -370,8 +409,17 @@ class FlanaBot(MultiBot, ABC):
|
|||||||
async def _on_delete_original_config_show(self, message: Message):
|
async def _on_delete_original_config_show(self, message: Message):
|
||||||
await self._show_config('auto_delete_original', message)
|
await self._show_config('auto_delete_original', message)
|
||||||
|
|
||||||
|
async def _on_dice(self, message: Message):
|
||||||
|
if message.chat.is_group and not self.is_bot_mentioned(message):
|
||||||
|
return
|
||||||
|
|
||||||
|
if top_number := flanautils.sum_numbers_in_text(message.text):
|
||||||
|
await self.send(random.randint(1, math.floor(top_number)), message)
|
||||||
|
else:
|
||||||
|
await self.send(random.choice(('¿De cuántas caras?', '¿Y el número?', '?', '🤔')), message)
|
||||||
|
|
||||||
async def _on_hello(self, message: Message):
|
async def _on_hello(self, message: Message):
|
||||||
if not message.chat.is_group or self.is_bot_mentioned(message):
|
if message.chat.is_private or self.is_bot_mentioned(message):
|
||||||
await self.send_hello(message)
|
await self.send_hello(message)
|
||||||
|
|
||||||
async def _on_new_message_default(self, message: Message):
|
async def _on_new_message_default(self, message: Message):
|
||||||
@@ -415,6 +463,55 @@ class FlanaBot(MultiBot, ABC):
|
|||||||
if not await self._on_scraping(message, delete_original=False):
|
if not await self._on_scraping(message, delete_original=False):
|
||||||
await self._on_recover_message(message)
|
await self._on_recover_message(message)
|
||||||
|
|
||||||
|
def _distribute_poll_buttons(self, texts: Sequence[str]) -> list[list[str]]:
|
||||||
|
pass
|
||||||
|
|
||||||
|
async def _on_poll(self, message: Message):
|
||||||
|
if message.chat.is_group and not self.is_bot_mentioned(message):
|
||||||
|
return
|
||||||
|
|
||||||
|
discarded_words = {*constants.KEYWORDS['poll'], self.name.lower(), f'<@{self.id}>'}
|
||||||
|
if final_options := [option.title() for option in message.text.split() if not flanautils.cartesian_product_string_matching(option.lower(), discarded_words, min_ratio=multibot_constants.PARSE_CALLBACKS_MIN_RATIO_DEFAULT)]:
|
||||||
|
await self.send('Encuesta en curso...', self._distribute_poll_buttons(final_options), message, buttons_key=ButtonsGroup.POLL, contents={'poll': {'is_active': True, 'votes': {option: [] for option in final_options}}})
|
||||||
|
else:
|
||||||
|
await self.send(random.choice(('¿Y las opciones?', '?', '🤔')), message)
|
||||||
|
|
||||||
|
async def _on_poll_button_press(self, message: Message):
|
||||||
|
await self.accept_button_event(message)
|
||||||
|
if not message.contents['poll']['is_active']:
|
||||||
|
return
|
||||||
|
|
||||||
|
option_name = results[0] if (results := re.findall('(.*?) ➜.+', message.buttons_info.pressed_text)) else message.buttons_info.pressed_text
|
||||||
|
selected_option_votes = message.contents['poll']['votes'][option_name]
|
||||||
|
presser_id = message.buttons_info.presser_user.id
|
||||||
|
presser_name = message.buttons_info.presser_user.name.split('#')[0]
|
||||||
|
total_votes = sum(len(option_votes) for option_votes in message.contents['poll']['votes'].values())
|
||||||
|
if [presser_id, presser_name] in selected_option_votes:
|
||||||
|
selected_option_votes.remove([presser_id, presser_name])
|
||||||
|
total_votes -= 1
|
||||||
|
else:
|
||||||
|
for option_votes in message.contents['poll']['votes'].values():
|
||||||
|
try:
|
||||||
|
option_votes.remove([presser_id, presser_name])
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
total_votes -= 1
|
||||||
|
break
|
||||||
|
selected_option_votes.append((presser_id, presser_name))
|
||||||
|
total_votes += 1
|
||||||
|
|
||||||
|
if total_votes:
|
||||||
|
buttons = []
|
||||||
|
for option, option_votes in message.contents['poll']['votes'].items():
|
||||||
|
percent = f'{round(len(option_votes) / total_votes * 100)}%'
|
||||||
|
names = f"({', '.join(option_vote[1] for option_vote in option_votes)})" if option_votes else ''
|
||||||
|
buttons.append(f'{option} ➜ {percent} {names}')
|
||||||
|
else:
|
||||||
|
buttons = list(message.contents['poll']['votes'].keys())
|
||||||
|
|
||||||
|
await self.edit(self._distribute_poll_buttons(buttons), message)
|
||||||
|
|
||||||
@bot_mentioned
|
@bot_mentioned
|
||||||
@group
|
@group
|
||||||
@admin(send_negative=True)
|
@admin(send_negative=True)
|
||||||
@@ -515,9 +612,48 @@ class FlanaBot(MultiBot, ABC):
|
|||||||
if song_infos:
|
if song_infos:
|
||||||
for song_info in song_infos:
|
for song_info in song_infos:
|
||||||
await self.send_song_info(song_info, message)
|
await self.send_song_info(song_info, message)
|
||||||
elif self.is_bot_mentioned(message) or not message.chat.is_group:
|
elif self.is_bot_mentioned(message) or message.chat.is_private:
|
||||||
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)
|
||||||
|
|
||||||
|
async def _on_stop_poll(self, message: Message):
|
||||||
|
if poll_message := message.replied_message:
|
||||||
|
if poll_message.contents.get('poll') is None:
|
||||||
|
return
|
||||||
|
elif (
|
||||||
|
(message.chat.is_private or self.is_bot_mentioned(message))
|
||||||
|
and
|
||||||
|
flanautils.cartesian_product_string_matching(message.text, constants.KEYWORDS['poll'], min_ratio=multibot_constants.PARSE_CALLBACKS_MIN_RATIO_DEFAULT)
|
||||||
|
and
|
||||||
|
(poll_message := Message.find_one({'contents.poll.is_active': True}, sort_keys=(('date', pymongo.DESCENDING),)))
|
||||||
|
):
|
||||||
|
poll_message = await self.get_message(poll_message.chat.id, poll_message.id)
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
|
||||||
|
winners = []
|
||||||
|
max_votes = 1
|
||||||
|
for option, votes in poll_message.contents['poll']['votes'].items():
|
||||||
|
if len(votes) > max_votes:
|
||||||
|
winners = [option]
|
||||||
|
max_votes = len(votes)
|
||||||
|
elif len(votes) == max_votes:
|
||||||
|
winners.append(option)
|
||||||
|
|
||||||
|
match winners:
|
||||||
|
case [_, _, *_]:
|
||||||
|
winners = [f'<b>{winner}</b>' for winner in winners]
|
||||||
|
text = f"Encuesta finalizada. Los ganadores son: {flanautils.join_last_separator(winners, ', ', ' y ')}."
|
||||||
|
case [winner]:
|
||||||
|
text = f'Encuesta finalizada. Ganador: <b>{winner}</b>.'
|
||||||
|
case _:
|
||||||
|
text = 'Encuesta finalizada.'
|
||||||
|
|
||||||
|
poll_message.contents['poll']['is_active'] = False
|
||||||
|
|
||||||
|
await self.edit(text, poll_message)
|
||||||
|
if not message.replied_message:
|
||||||
|
await self.send(text, reply_to=poll_message)
|
||||||
|
|
||||||
@group
|
@group
|
||||||
@bot_mentioned
|
@bot_mentioned
|
||||||
@admin(send_negative=True)
|
@admin(send_negative=True)
|
||||||
@@ -526,9 +662,9 @@ class FlanaBot(MultiBot, ABC):
|
|||||||
await self.unpunish(user, message, message)
|
await self.unpunish(user, message, message)
|
||||||
|
|
||||||
async def _on_weather_button_press(self, message: Message):
|
async def _on_weather_button_press(self, message: Message):
|
||||||
await self._accept_button_event(message)
|
await self.accept_button_event(message)
|
||||||
|
|
||||||
match message.button_pressed_text:
|
match message.buttons_info.pressed_text:
|
||||||
case WeatherEmoji.ZOOM_IN.value:
|
case WeatherEmoji.ZOOM_IN.value:
|
||||||
message.weather_chart.zoom_in()
|
message.weather_chart.zoom_in()
|
||||||
case WeatherEmoji.ZOOM_OUT.value:
|
case WeatherEmoji.ZOOM_OUT.value:
|
||||||
@@ -548,7 +684,6 @@ class FlanaBot(MultiBot, ABC):
|
|||||||
|
|
||||||
message.weather_chart.apply_zoom()
|
message.weather_chart.apply_zoom()
|
||||||
message.weather_chart.draw()
|
message.weather_chart.draw()
|
||||||
message.save()
|
|
||||||
|
|
||||||
image_bytes = message.weather_chart.to_image()
|
image_bytes = message.weather_chart.to_image()
|
||||||
await self.edit(Media(image_bytes, MediaType.IMAGE), message)
|
await self.edit(Media(image_bytes, MediaType.IMAGE), message)
|
||||||
@@ -590,7 +725,7 @@ class FlanaBot(MultiBot, ABC):
|
|||||||
- 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['date'], min_ratio=0.85).keys()
|
||||||
- flanautils.cartesian_product_string_matching(original_text_words, multibot_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()
|
||||||
- possible_mentioned_ids
|
- possible_mentioned_ids
|
||||||
- flanautils.CommonWords.all_words
|
- flanautils.CommonWords.get()
|
||||||
)
|
)
|
||||||
if not place_words:
|
if not place_words:
|
||||||
if not message.is_inline:
|
if not message.is_inline:
|
||||||
@@ -682,6 +817,7 @@ class FlanaBot(MultiBot, ABC):
|
|||||||
[WeatherEmoji.HUMIDITY.value, WeatherEmoji.PRECIPITATION_PROBABILITY.value, WeatherEmoji.PRECIPITATION_VOLUME.value, WeatherEmoji.PRESSURE.value, WeatherEmoji.WIND_SPEED.value]
|
[WeatherEmoji.HUMIDITY.value, WeatherEmoji.PRECIPITATION_PROBABILITY.value, WeatherEmoji.PRECIPITATION_VOLUME.value, WeatherEmoji.PRESSURE.value, WeatherEmoji.WIND_SPEED.value]
|
||||||
],
|
],
|
||||||
message,
|
message,
|
||||||
|
buttons_key=ButtonsGroup.WEATHER,
|
||||||
send_as_file=False
|
send_as_file=False
|
||||||
)
|
)
|
||||||
await self.send_inline_results(message)
|
await self.send_inline_results(message)
|
||||||
|
|||||||
@@ -2,9 +2,11 @@ __all__ = ['FlanaDiscBot']
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import os
|
import os
|
||||||
|
from typing import Sequence
|
||||||
|
|
||||||
import discord
|
import discord
|
||||||
from multibot import BadRoleError, DiscordBot, User
|
import flanautils
|
||||||
|
from multibot import BadRoleError, DiscordBot, User, constants as multibot_constants
|
||||||
|
|
||||||
from flanabot import constants
|
from flanabot import constants
|
||||||
from flanabot.bots.flana_bot import FlanaBot
|
from flanabot.bots.flana_bot import FlanaBot
|
||||||
@@ -19,9 +21,10 @@ HEAT_NAMES = [
|
|||||||
'Canal Caliente',
|
'Canal Caliente',
|
||||||
'Canal Olor a Vasco',
|
'Canal Olor a Vasco',
|
||||||
'Verano Cordobés al Sol',
|
'Verano Cordobés al Sol',
|
||||||
'Canal Ardiendo',
|
|
||||||
'abrid las putas ventanas y traed el extintor',
|
'abrid las putas ventanas y traed el extintor',
|
||||||
'Canal INFIERNO'
|
'Canal Ardiendo',
|
||||||
|
'Canal INFIERNO',
|
||||||
|
'🔥🔥🔥🔥🔥🔥🔥'
|
||||||
]
|
]
|
||||||
HOT_CHANNEL_ID = 493530483045564417
|
HOT_CHANNEL_ID = 493530483045564417
|
||||||
|
|
||||||
@@ -42,6 +45,13 @@ class FlanaDiscBot(DiscordBot, FlanaBot):
|
|||||||
super()._add_handlers()
|
super()._add_handlers()
|
||||||
self.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')
|
||||||
|
|
||||||
|
# noinspection PyTypeChecker
|
||||||
|
def _distribute_poll_buttons(self, texts: Sequence[str]) -> list[list[str]]:
|
||||||
|
if len(texts) <= multibot_constants.DISCORD_BUTTONS_MAX:
|
||||||
|
return flanautils.chunks(texts, 1)
|
||||||
|
else:
|
||||||
|
return flanautils.chunks(texts, multibot_constants.DISCORD_BUTTONS_MAX)
|
||||||
|
|
||||||
async def _heat_channel(self, channel: discord.VoiceChannel):
|
async def _heat_channel(self, channel: discord.VoiceChannel):
|
||||||
while True:
|
while True:
|
||||||
await asyncio.sleep(constants.HEAT_PERIOD_SECONDS)
|
await asyncio.sleep(constants.HEAT_PERIOD_SECONDS)
|
||||||
|
|||||||
@@ -4,8 +4,9 @@ __all__ = ['whitelisted_event', 'FlanaTeleBot']
|
|||||||
|
|
||||||
import functools
|
import functools
|
||||||
import os
|
import os
|
||||||
from typing import Callable
|
from typing import Callable, Sequence
|
||||||
|
|
||||||
|
import flanautils
|
||||||
import telethon.tl.functions
|
import telethon.tl.functions
|
||||||
from multibot import TelegramBot, find_message, user_client
|
from multibot import TelegramBot, find_message, user_client
|
||||||
|
|
||||||
@@ -46,6 +47,10 @@ class FlanaTeleBot(TelegramBot, FlanaBot):
|
|||||||
# ----------------------------------------------------------- #
|
# ----------------------------------------------------------- #
|
||||||
# -------------------- PROTECTED METHODS -------------------- #
|
# -------------------- PROTECTED METHODS -------------------- #
|
||||||
# ----------------------------------------------------------- #
|
# ----------------------------------------------------------- #
|
||||||
|
def _distribute_poll_buttons(self, texts: Sequence[str]) -> list[list[str]]:
|
||||||
|
# noinspection PyTypeChecker
|
||||||
|
return flanautils.chunks(texts, 1)
|
||||||
|
|
||||||
@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:
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import flanautils
|
|||||||
|
|
||||||
AUTO_WEATHER_EVERY = datetime.timedelta(hours=6)
|
AUTO_WEATHER_EVERY = datetime.timedelta(hours=6)
|
||||||
CHECK_PUNISHMENTS_EVERY_SECONDS = datetime.timedelta(hours=1).total_seconds()
|
CHECK_PUNISHMENTS_EVERY_SECONDS = datetime.timedelta(hours=1).total_seconds()
|
||||||
|
FLOOD_2s_LIMIT = 4
|
||||||
|
FLOOD_7s_LIMIT = 7
|
||||||
HEAT_PERIOD_SECONDS = datetime.timedelta(minutes=15).total_seconds()
|
HEAT_PERIOD_SECONDS = datetime.timedelta(minutes=15).total_seconds()
|
||||||
INSULT_PROBABILITY = 0.00166666667
|
INSULT_PROBABILITY = 0.00166666667
|
||||||
MAX_PLACE_QUERY_LENGTH = 50
|
MAX_PLACE_QUERY_LENGTH = 50
|
||||||
@@ -19,43 +21,84 @@ BYE_PHRASES = ('Adiós.', 'adieu', 'adio', 'adioh', 'adios', 'adió', 'adiós',
|
|||||||
HELLO_PHRASES = ('alo', 'aloh', 'buenas', 'Hola.', 'hello', 'hey', 'hi', 'hola', 'holaaaa', 'holaaaaaaa', 'ola',
|
HELLO_PHRASES = ('alo', 'aloh', 'buenas', 'Hola.', 'hello', 'hey', 'hi', 'hola', 'holaaaa', 'holaaaaaaa', 'ola',
|
||||||
'ola k ase', 'pa ti mi cola', 'saludos')
|
'ola k ase', 'pa ti mi cola', 'saludos')
|
||||||
INSULTS = (
|
INSULTS = (
|
||||||
'Cállate ya anda.',
|
'._.',
|
||||||
'¿Quién te ha preguntado?',
|
'aha',
|
||||||
'¿Tú eres así o te dan apagones cerebrales?',
|
'Aléjate de mi.',
|
||||||
'Ante la duda mi dedo corazón te saluda.',
|
'Ante la duda mi dedo corazón te saluda.',
|
||||||
'Enjoy cancer brain.',
|
|
||||||
'Calla noob.',
|
|
||||||
'Hablas tanta mierda que tu culo tiene envidia de tu boca.',
|
|
||||||
'jAJjajAJjajAJjajAJajJAJajJA',
|
|
||||||
'enjoy xd',
|
|
||||||
'Reported.',
|
|
||||||
'Baneito pa ti en breve.',
|
'Baneito pa ti en breve.',
|
||||||
'Despídete de tu cuenta.',
|
'Calla noob.',
|
||||||
'Flanagan es más guapo que tú.',
|
'Cansino.',
|
||||||
'jajaj',
|
'Cuéntame menos.',
|
||||||
'xd',
|
'Cuéntame más.',
|
||||||
'Hay un concurso de hostias y tienes todas las papeletas.',
|
'Cállate ya anda.',
|
||||||
'¿Por qué no te callas?',
|
'Cállate.',
|
||||||
'Das penilla.',
|
'Das penilla.',
|
||||||
|
'De verdad. Estás para encerrarte.',
|
||||||
'Deberían hacerte la táctica del C4.',
|
'Deberían hacerte la táctica del C4.',
|
||||||
'Te voy romper las pelotas.',
|
'Despídete de tu cuenta.',
|
||||||
'Más tonto y no naces.',
|
'Déjame tranquilo.',
|
||||||
|
'Enjoy cancer brain.',
|
||||||
|
'Eres cortito, ¿eh?',
|
||||||
|
'Eres más malo que pegarle a un padre.',
|
||||||
'Eres más tonto que peinar bombillas.',
|
'Eres más tonto que peinar bombillas.',
|
||||||
'Eres más tonto que pellizcar cristales.',
|
'Eres más tonto que pellizcar cristales.',
|
||||||
'Eres más malo que pegarle a un padre.'
|
'Estás mal de la azotea.',
|
||||||
|
'Estás mal de la cabeza.',
|
||||||
|
'Flanagan es más guapo que tú.',
|
||||||
|
'Hablas tanta mierda que tu culo tiene envidia de tu boca.',
|
||||||
|
'Hay un concurso de hostias y tienes todas las papeletas.',
|
||||||
|
'Loco.',
|
||||||
|
'Más tonto y no naces.',
|
||||||
|
'No eres muy avispado tú...',
|
||||||
|
'Pesado.',
|
||||||
|
'Qué bien, ¿eh?',
|
||||||
|
'Que me dejes en paz.',
|
||||||
|
'Qué pesado.',
|
||||||
|
'Quita bicho.',
|
||||||
|
'Reportaito mi arma.',
|
||||||
|
'Reported.',
|
||||||
|
'Retard.',
|
||||||
|
'Te voy romper las pelotas.',
|
||||||
|
'Tú... no estás muy bien, ¿no?',
|
||||||
|
'Ya estamos otra vez...',
|
||||||
|
'Ya estamos...',
|
||||||
|
'enjoy xd',
|
||||||
|
'jAJjajAJjajAJjajAJajJAJajJA',
|
||||||
|
'jajaj',
|
||||||
|
'o_O',
|
||||||
|
'xd',
|
||||||
|
'¿Otra vez tú?',
|
||||||
|
'¿Pero cuándo te vas a callar?',
|
||||||
|
'¿Por qué no te callas?',
|
||||||
|
'¿Quién te ha preguntado?',
|
||||||
|
'¿Qúe quieres?',
|
||||||
|
'¿Te callas o te callo?',
|
||||||
|
'¿Te imaginas que me interesa?',
|
||||||
|
'¿Te quieres callar?',
|
||||||
|
'¿Todo bien?',
|
||||||
|
'¿Tú eres así o te dan apagones cerebrales?',
|
||||||
|
'🖕',
|
||||||
|
'😑',
|
||||||
|
'🙄',
|
||||||
|
'🤔',
|
||||||
|
'🤨'
|
||||||
)
|
)
|
||||||
|
|
||||||
KEYWORDS = {
|
KEYWORDS = {
|
||||||
|
'choose': ('choose', 'elige', 'escoge'),
|
||||||
'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'),
|
||||||
|
'dice': ('dado', 'dice'),
|
||||||
|
'poll': ('encuesta', 'poll', 'quiz'),
|
||||||
'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', 'fuck', 'fusila', 'hell',
|
||||||
'hell', 'humos', 'infierno', 'jefe', 'jode', 'learn', 'leccion', 'lesson', 'manda', 'purgatorio',
|
'humos', 'infierno', 'jefe', 'jode', 'learn', 'leccion', 'lesson', 'manda', 'purgatorio', 'sancion',
|
||||||
'sancion', 'shoot', 'teach', 'termina', 'whip'),
|
'shoot', 'teach', 'whip'),
|
||||||
|
'random': ('aleatorio', 'azar', 'random'),
|
||||||
'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'),
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ 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
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ os.environ |= flanautils.find_environment_variables('../.env')
|
|||||||
import unittest
|
import unittest
|
||||||
from typing import Iterable
|
from typing import Iterable
|
||||||
|
|
||||||
from multibot import constants as multibot_constants
|
|
||||||
from flanabot.bots.flana_tele_bot import FlanaTeleBot
|
from flanabot.bots.flana_tele_bot import FlanaTeleBot
|
||||||
|
|
||||||
|
|
||||||
@@ -15,7 +14,7 @@ class TestParseCallbacks(unittest.TestCase):
|
|||||||
def _test_no_always_callbacks(self, phrases: Iterable[str], callback: callable):
|
def _test_no_always_callbacks(self, phrases: Iterable[str], callback: callable):
|
||||||
for i, phrase in enumerate(phrases):
|
for i, phrase in enumerate(phrases):
|
||||||
with self.subTest(phrase):
|
with self.subTest(phrase):
|
||||||
callbacks = [registered_callback.callback for registered_callback in self.flana_tele_bot._parse_callbacks(phrase, multibot_constants.RATIO_REWARD_EXPONENT, multibot_constants.KEYWORDS_LENGHT_PENALTY, multibot_constants.MINIMUM_RATIO_TO_MATCH)
|
callbacks = [registered_callback.callback for registered_callback in self.flana_tele_bot._parse_callbacks(phrase, self.flana_tele_bot._registered_callbacks)
|
||||||
if not registered_callback.always]
|
if not registered_callback.always]
|
||||||
self.assertEqual(1, len(callbacks))
|
self.assertEqual(1, len(callbacks))
|
||||||
self.assertEqual(callback, callbacks[0], f'\n\nExpected: {callback.__name__}\nActual: {callbacks[0].__name__}')
|
self.assertEqual(callback, callbacks[0], f'\n\nExpected: {callback.__name__}\nActual: {callbacks[0].__name__}')
|
||||||
@@ -92,8 +91,7 @@ class TestParseCallbacks(unittest.TestCase):
|
|||||||
def test_on_delete_original_config_activate(self):
|
def test_on_delete_original_config_activate(self):
|
||||||
phrases = [
|
phrases = [
|
||||||
'activa el borrado automatico',
|
'activa el borrado automatico',
|
||||||
'flanabot pon el auto delete activado',
|
'flanabot pon el auto delete activado'
|
||||||
'flanabot activa el autodelete'
|
|
||||||
]
|
]
|
||||||
self._test_no_always_callbacks(phrases, self.flana_tele_bot._on_delete_original_config_activate)
|
self._test_no_always_callbacks(phrases, self.flana_tele_bot._on_delete_original_config_activate)
|
||||||
|
|
||||||
@@ -104,8 +102,7 @@ class TestParseCallbacks(unittest.TestCase):
|
|||||||
def test_on_delete_original_config_deactivate(self):
|
def test_on_delete_original_config_deactivate(self):
|
||||||
phrases = [
|
phrases = [
|
||||||
'desactiva el borrado automatico',
|
'desactiva el borrado automatico',
|
||||||
'flanabot pon el auto delete desactivado',
|
'flanabot pon el auto delete desactivado'
|
||||||
'flanabot desactiva el autodelete'
|
|
||||||
]
|
]
|
||||||
self._test_no_always_callbacks(phrases, self.flana_tele_bot._on_delete_original_config_deactivate)
|
self._test_no_always_callbacks(phrases, self.flana_tele_bot._on_delete_original_config_deactivate)
|
||||||
|
|
||||||
@@ -119,9 +116,9 @@ class TestParseCallbacks(unittest.TestCase):
|
|||||||
|
|
||||||
def test_on_mute(self):
|
def test_on_mute(self):
|
||||||
phrases = [
|
phrases = [
|
||||||
# 'silencia',
|
'silencia',
|
||||||
# 'silencia al pavo ese',
|
'silencia al pavo ese',
|
||||||
# 'calla a ese pesao',
|
'calla a ese pesao',
|
||||||
'haz que se calle',
|
'haz que se calle',
|
||||||
'quitale el microfono a ese',
|
'quitale el microfono a ese',
|
||||||
'quitale el micro',
|
'quitale el micro',
|
||||||
|
|||||||
Reference in New Issue
Block a user