Compare commits
82 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b54dd8d944 | ||
|
|
376da6f072 | ||
|
|
f396d2b232 | ||
|
|
6c3a8e5e4a | ||
|
|
d9eb01e5c0 | ||
|
|
a9a79b2705 | ||
|
|
29ebfd5e26 | ||
|
|
cf6b39e262 | ||
|
|
225bad07cc | ||
|
|
336d8f8b8a | ||
|
|
ad98464446 | ||
|
|
94fe902d41 | ||
|
|
b86fc98377 | ||
|
|
fa67733416 | ||
|
|
63e972a774 | ||
|
|
a1434e54d3 | ||
|
|
88b61cd87f | ||
|
|
4fc52128ec | ||
|
|
8788904d44 | ||
|
|
fdb02e9d65 | ||
|
|
8594351229 | ||
|
|
ae7fb1b9b2 | ||
|
|
d21978b2da | ||
|
|
35e8ec5090 | ||
|
|
8703eca5ff | ||
|
|
6be120da17 | ||
|
|
aaf17c6877 | ||
|
|
68a3fde4ed | ||
|
|
edad87b683 | ||
|
|
3be1cf5c47 | ||
|
|
ff934325c5 | ||
|
|
11ef06b1d3 | ||
|
|
d5b51e3f44 | ||
|
|
a6b0e699ea | ||
|
|
e90e081b69 | ||
|
|
8a23baf626 | ||
|
|
7f795e0b73 | ||
|
|
9fbc16df02 | ||
|
|
9da95e5e1c | ||
|
|
a27f86d1b9 | ||
|
|
26b3d714d3 | ||
|
|
afa96325fe | ||
|
|
72a803daec | ||
|
|
7ce6985fa9 | ||
|
|
12d9dd6075 | ||
|
|
cf8ec9182e | ||
|
|
060484396e | ||
|
|
94e8f72245 | ||
|
|
d359ccb39e | ||
|
|
44dcb3d987 | ||
|
|
4ebcade424 | ||
|
|
52e981fc58 | ||
|
|
722c2ffbac | ||
|
|
55e3fe53c2 | ||
|
|
34320079eb | ||
|
|
61daaa387a | ||
|
|
7fee5f382c | ||
|
|
f8be4d3e5e | ||
|
|
89207de059 | ||
|
|
14793e5eb5 | ||
|
|
488db76710 | ||
|
|
a1cefe437d | ||
|
|
506af05ebb | ||
|
|
c17ffe1010 | ||
|
|
81854375d1 | ||
|
|
154f02a1b6 | ||
|
|
786bab4e04 | ||
|
|
d045551db9 | ||
|
|
5021bdd7ae | ||
|
|
e8742eca42 | ||
|
|
a24021c2bd | ||
|
|
d145f70a42 | ||
|
|
73ad75e8b2 | ||
|
|
1da383d5eb | ||
|
|
a3893df34b | ||
|
|
6645bef22f | ||
|
|
d02d5d4df2 | ||
|
|
85b181c598 | ||
|
|
db5a1974b2 | ||
|
|
5e65b5a298 | ||
|
|
4d60281a23 | ||
|
|
0ede68d495 |
@@ -4,4 +4,5 @@ from flanabot.bots.flana_tele_bot import *
|
||||
from flanabot.bots.penalty_bot import *
|
||||
from flanabot.bots.poll_bot import *
|
||||
from flanabot.bots.scraper_bot import *
|
||||
from flanabot.bots.ubereats_bot import *
|
||||
from flanabot.bots.weather_bot import *
|
||||
|
||||
@@ -284,8 +284,8 @@ class Connect4Bot(MultiBot, ABC):
|
||||
return False
|
||||
|
||||
try:
|
||||
message.data['is_active'] = False
|
||||
except AttributeError:
|
||||
message.data['connect_4']['is_active'] = False
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
await self.edit(
|
||||
@@ -448,7 +448,7 @@ class Connect4Bot(MultiBot, ABC):
|
||||
return winners
|
||||
|
||||
def _winning_positions(self, board: list[list[int | None]]) -> defaultdict[int, list[tuple[int, int]]]:
|
||||
winning_positions = defaultdict(list)
|
||||
winning_positions: defaultdict[int, list[tuple[int, int]]] = defaultdict(list)
|
||||
for next_i, next_j in self._available_positions(board):
|
||||
for player_number in self._check_winners(next_i, next_j, board):
|
||||
winning_positions[player_number].append((next_i, next_j))
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
__all__ = ['FlanaBot']
|
||||
|
||||
import asyncio
|
||||
import datetime
|
||||
import random
|
||||
import time
|
||||
from abc import ABC
|
||||
from typing import Iterable
|
||||
|
||||
@@ -10,13 +12,14 @@ import pymongo
|
||||
import pytz
|
||||
from flanaapis import InstagramLoginError, MediaNotFoundError, PlaceNotFoundError
|
||||
from flanautils import return_if_first_empty
|
||||
from multibot import BadRoleError, MultiBot, Role, bot_mentioned, constants as multibot_constants, group, inline, owner
|
||||
from multibot import BadRoleError, MultiBot, RegisteredCallback, Role, User, bot_mentioned, constants as multibot_constants, group, inline, owner
|
||||
|
||||
from flanabot import constants
|
||||
from flanabot.bots.connect_4_bot import Connect4Bot
|
||||
from flanabot.bots.penalty_bot import PenaltyBot
|
||||
from flanabot.bots.poll_bot import PollBot
|
||||
from flanabot.bots.scraper_bot import ScraperBot
|
||||
from flanabot.bots.ubereats_bot import UberEatsBot
|
||||
from flanabot.bots.weather_bot import WeatherBot
|
||||
from flanabot.models import Action, BotAction, ButtonsGroup, Chat, Message
|
||||
|
||||
@@ -24,30 +27,40 @@ from flanabot.models import Action, BotAction, ButtonsGroup, Chat, Message
|
||||
# ----------------------------------------------------------------------------------------------------- #
|
||||
# --------------------------------------------- FLANA_BOT --------------------------------------------- #
|
||||
# ----------------------------------------------------------------------------------------------------- #
|
||||
class FlanaBot(Connect4Bot, PenaltyBot, PollBot, ScraperBot, WeatherBot, MultiBot, ABC):
|
||||
class FlanaBot(Connect4Bot, PenaltyBot, PollBot, ScraperBot, UberEatsBot, WeatherBot, MultiBot, ABC):
|
||||
Chat = Chat
|
||||
Message = Message
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.tunnel_chat = None
|
||||
self.help_calls = {}
|
||||
|
||||
# -------------------------------------------------------- #
|
||||
# ------------------- PROTECTED METHODS ------------------ #
|
||||
# -------------------------------------------------------- #
|
||||
def _add_handlers(self):
|
||||
super()._add_handlers()
|
||||
|
||||
self.register(self._on_activate_tunnel, (multibot_constants.KEYWORDS['activate'], constants.KEYWORDS['tunnel']))
|
||||
|
||||
self.register(self._on_bye, multibot_constants.KEYWORDS['bye'])
|
||||
|
||||
self.register(self._on_config, multibot_constants.KEYWORDS['config'])
|
||||
self.register(self._on_config, (multibot_constants.KEYWORDS['show'], multibot_constants.KEYWORDS['config']))
|
||||
|
||||
self.register(self._on_database_messages, (multibot_constants.KEYWORDS['last'], multibot_constants.KEYWORDS['message']))
|
||||
self.register(lambda message: self._on_database_messages(message, simple=True), (multibot_constants.KEYWORDS['last'], multibot_constants.KEYWORDS['message'], multibot_constants.KEYWORDS['simple']))
|
||||
|
||||
self.register(self._on_database_messages_simple, (multibot_constants.KEYWORDS['last'], multibot_constants.KEYWORDS['message'], multibot_constants.KEYWORDS['simple']))
|
||||
self.register(self._on_deactivate_tunnel, (multibot_constants.KEYWORDS['deactivate'], constants.KEYWORDS['tunnel']))
|
||||
|
||||
self.register(self._on_delete, multibot_constants.KEYWORDS['delete'])
|
||||
self.register(self._on_delete, (multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['message']))
|
||||
|
||||
self.register(self._on_hello, multibot_constants.KEYWORDS['hello'])
|
||||
|
||||
self.register(self._on_help, multibot_constants.KEYWORDS['help'])
|
||||
|
||||
self.register(self._on_new_message_default, default=True)
|
||||
|
||||
self.register(self._on_recover_message, multibot_constants.KEYWORDS['reset'])
|
||||
@@ -61,6 +74,8 @@ class FlanaBot(Connect4Bot, PenaltyBot, PollBot, ScraperBot, WeatherBot, MultiBo
|
||||
self.register(self._on_roles, (multibot_constants.KEYWORDS['activate'], multibot_constants.KEYWORDS['role']))
|
||||
self.register(self._on_roles, (multibot_constants.KEYWORDS['deactivate'], multibot_constants.KEYWORDS['role']))
|
||||
|
||||
self.register(self._on_tunnel_message, always=True)
|
||||
|
||||
self.register(self._on_users, multibot_constants.KEYWORDS['user'])
|
||||
|
||||
self.register_button(self._on_config_button_press, ButtonsGroup.CONFIG)
|
||||
@@ -74,7 +89,7 @@ class FlanaBot(Connect4Bot, PenaltyBot, PollBot, ScraperBot, WeatherBot, MultiBo
|
||||
async def _get_message(
|
||||
self,
|
||||
event: multibot_constants.MESSAGE_EVENT,
|
||||
pull_overwrite_fields: Iterable[str] = ('_id', 'config')
|
||||
pull_overwrite_fields: Iterable[str] = ('_id', 'config', 'date', 'ubereats')
|
||||
) -> Message:
|
||||
return await super()._get_message(event, pull_overwrite_fields)
|
||||
|
||||
@@ -120,29 +135,67 @@ class FlanaBot(Connect4Bot, PenaltyBot, PollBot, ScraperBot, WeatherBot, MultiBo
|
||||
# ---------------------------------------------- #
|
||||
# HANDLERS #
|
||||
# ---------------------------------------------- #
|
||||
async def _on_activate_tunnel(self, message: Message):
|
||||
keywords = (*multibot_constants.KEYWORDS['activate'], *constants.KEYWORDS['tunnel'])
|
||||
try:
|
||||
chat_id_or_name = next(part for part in flanautils.remove_accents(message.text.lower()).split() if not flanautils.cartesian_product_string_matching(part, keywords, multibot_constants.PARSER_MIN_SCORE_DEFAULT))
|
||||
except StopIteration:
|
||||
return
|
||||
|
||||
chat_id_or_name = flanautils.cast_number(chat_id_or_name, raise_exception=False)
|
||||
if (chat := await self.get_chat(chat_id_or_name)) or (chat := await self.get_chat(await self.get_user(chat_id_or_name))):
|
||||
self.tunnel_chat = chat
|
||||
await self.send(f"Túnel abierto con <b>{chat.name}{f' ({chat.group_name})' if chat.group_name else ''}</b>.", message)
|
||||
else:
|
||||
await self.send_error('Chat inválido.', message)
|
||||
|
||||
async def _on_bye(self, message: Message):
|
||||
if message.chat.is_private or self.is_bot_mentioned(message):
|
||||
await self.send_bye(message)
|
||||
|
||||
@group
|
||||
@bot_mentioned
|
||||
async def _on_config(self, message: Message):
|
||||
if not message.chat.config:
|
||||
if message.chat.is_private:
|
||||
config_names = ('auto_insult', 'auto_scraping', 'scraping_delete_original', 'ubereats')
|
||||
elif self.is_bot_mentioned(message):
|
||||
config_names = (
|
||||
'auto_insult',
|
||||
'auto_scraping',
|
||||
'auto_weather_chart',
|
||||
'check_flood',
|
||||
'punish',
|
||||
'scraping_delete_original'
|
||||
)
|
||||
else:
|
||||
return
|
||||
|
||||
buttons_texts = [(f"{'✔' if v else '❌'} {k}", v) for k, v in message.chat.config.items()]
|
||||
buttons_texts = []
|
||||
for k, v in message.chat.config.items():
|
||||
if k not in config_names:
|
||||
continue
|
||||
if k == 'ubereats':
|
||||
k = f"ubereats (cada {flanautils.TimeUnits(seconds=message.chat.ubereats['seconds']).to_words()})"
|
||||
buttons_texts.append((f"{'✔' if v else '❌'} {k}", v))
|
||||
|
||||
await self.send('<b>Estos son los ajustes del chat:</b>\n\n', flanautils.chunks(buttons_texts, 3), message, buttons_key=ButtonsGroup.CONFIG)
|
||||
await self.delete_message(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:
|
||||
if message.buttons_info.presser_user.is_admin is False or not message.buttons_info.pressed_button:
|
||||
return
|
||||
|
||||
config_name = message.buttons_info.pressed_text.split()[1]
|
||||
message.chat.config[config_name] = not message.chat.config[config_name]
|
||||
message.buttons_info.pressed_button.text = f"{'✔' if message.chat.config[config_name] else '❌'} {config_name}"
|
||||
if config_name == 'ubereats':
|
||||
if message.chat.config[config_name]:
|
||||
await self.start_ubereats(message.chat)
|
||||
else:
|
||||
await self.stop_ubereats(message.chat)
|
||||
button_text = f"ubereats (cada {flanautils.TimeUnits(seconds=message.chat.ubereats['seconds']).to_words()})"
|
||||
else:
|
||||
button_text = config_name
|
||||
message.buttons_info.pressed_button.text = f"{'✔' if message.chat.config[config_name] else '❌'} {button_text}"
|
||||
|
||||
await self.edit(message.buttons_info.buttons, message)
|
||||
|
||||
@@ -163,23 +216,27 @@ class FlanaBot(Connect4Bot, PenaltyBot, PollBot, ScraperBot, WeatherBot, MultiBo
|
||||
),
|
||||
message
|
||||
)
|
||||
await self.delete_message(message)
|
||||
|
||||
async def _on_database_messages_simple(self, message: Message):
|
||||
await self._on_database_messages(message, simple=True)
|
||||
async def _on_deactivate_tunnel(self, message: Message):
|
||||
self.tunnel_chat = None
|
||||
await self.send('Túnel cerrado.', message)
|
||||
|
||||
@inline(False)
|
||||
async def _on_delete(self, message: Message):
|
||||
if message.replied_message:
|
||||
if message.replied_message.author.id == self.id:
|
||||
await self.delete_message(message.replied_message)
|
||||
if message.chat.is_group:
|
||||
await self.delete_message(message)
|
||||
elif message.chat.is_group and self.is_bot_mentioned(message):
|
||||
if not self.is_bot_mentioned(message):
|
||||
return
|
||||
|
||||
if message.author.is_admin or message.replied_message.author.id == self.id:
|
||||
flanautils.do_later(flanautils.text_to_time(message.text).total_seconds(), self.delete_message, message.replied_message)
|
||||
await self.delete_message(message)
|
||||
elif message.chat.is_group:
|
||||
await self.send_negative(message)
|
||||
elif (
|
||||
(message.chat.is_private or self.is_bot_mentioned(message))
|
||||
and
|
||||
(n_messages := flanautils.text_to_number(message.text))
|
||||
(message.chat.is_private or self.is_bot_mentioned(message))
|
||||
and
|
||||
(n_messages := flanautils.text_to_number(message.text))
|
||||
):
|
||||
if message.author.is_admin is False:
|
||||
await self.send_negative(message)
|
||||
@@ -195,54 +252,109 @@ class FlanaBot(Connect4Bot, PenaltyBot, PollBot, ScraperBot, WeatherBot, MultiBo
|
||||
if message.chat.is_private or self.is_bot_mentioned(message):
|
||||
await self.send_hello(message)
|
||||
|
||||
async def _on_help(self, message: Message):
|
||||
now = datetime.timedelta(seconds=time.time())
|
||||
if (
|
||||
message.chat.is_group
|
||||
and
|
||||
not self.is_bot_mentioned(message)
|
||||
or
|
||||
self.help_calls.get(message.chat.id)
|
||||
and
|
||||
now - self.help_calls[message.chat.id] <= datetime.timedelta(minutes=1)
|
||||
):
|
||||
return
|
||||
|
||||
self.help_calls[message.chat.id] = now
|
||||
|
||||
await self.send(
|
||||
'<b>Necesita ayuda:</b>\n'
|
||||
'<b>User:</b>\n'
|
||||
f' <b>id:</b> <code>{message.author.id}</code>\n'
|
||||
f' <b>name:</b> <code>{message.author.name}</code>\n'
|
||||
f' <b>is_admin:<b> <code>{message.author.is_admin}</code>\n'
|
||||
f' <b>is_bot:</b> <code>{message.author.is_bot}</code>\n'
|
||||
'\n'
|
||||
'<b>Chat:</b>\n'
|
||||
f' <b>id:</b> <code>{message.chat.id}</code>\n'
|
||||
f' <b>name:</b> <code>{message.chat.name}</code>\n'
|
||||
f' <b>group_id:</b> <code>{message.chat.group_id}</code>\n'
|
||||
f' <b>group_name:</b> <code>{message.chat.group_name}</code>',
|
||||
await self.owner_chat
|
||||
)
|
||||
await self.send('Se ha notificado a Flanagan. Se pondrá en contacto contigo cuando pueda.', message)
|
||||
|
||||
async def _on_new_message_default(self, message: Message):
|
||||
if message.is_inline:
|
||||
await self._scrape_and_send(message)
|
||||
await self._on_scraping(message)
|
||||
elif (
|
||||
(
|
||||
message.chat.is_group
|
||||
and
|
||||
not self.is_bot_mentioned(message)
|
||||
and
|
||||
not message.chat.config['auto_scraping']
|
||||
or
|
||||
not await self._scrape_send_and_delete(message)
|
||||
)
|
||||
(
|
||||
message.chat.is_group
|
||||
and
|
||||
(
|
||||
message.author.id != self.owner_id
|
||||
and
|
||||
(
|
||||
not message.replied_message
|
||||
or
|
||||
message.replied_message.author.id != self.id
|
||||
or
|
||||
not message.replied_message.medias
|
||||
)
|
||||
and
|
||||
(
|
||||
self.is_bot_mentioned(message)
|
||||
or
|
||||
(
|
||||
message.chat.config['auto_insult']
|
||||
and
|
||||
random.random() < constants.INSULT_PROBABILITY
|
||||
)
|
||||
)
|
||||
)
|
||||
not self.is_bot_mentioned(message)
|
||||
and
|
||||
not message.chat.config['auto_scraping']
|
||||
or
|
||||
not await self._on_scraping(message, scrape_replied=False)
|
||||
)
|
||||
and
|
||||
message.author.id != self.owner_id
|
||||
and
|
||||
(
|
||||
not message.replied_message
|
||||
or
|
||||
message.replied_message.author.id != self.id
|
||||
or
|
||||
not message.replied_message.medias
|
||||
)
|
||||
and
|
||||
(
|
||||
self.is_bot_mentioned(message)
|
||||
or
|
||||
message.chat.config['auto_insult']
|
||||
and
|
||||
random.random() < constants.INSULT_PROBABILITY
|
||||
)
|
||||
and
|
||||
(
|
||||
not self.tunnel_chat
|
||||
or
|
||||
self.tunnel_chat != message.chat
|
||||
)
|
||||
):
|
||||
await self.send_insult(message)
|
||||
|
||||
async def _on_ready(self):
|
||||
await super()._on_ready()
|
||||
await flanautils.do_every(multibot_constants.CHECK_OLD_DATABASE_MESSAGES_EVERY_SECONDS, self.check_old_database_actions)
|
||||
flanautils.do_every(multibot_constants.CHECK_OLD_DATABASE_MESSAGES_EVERY_SECONDS, self.check_old_database_actions)
|
||||
for chat in Chat.find({
|
||||
'platform': self.platform.value,
|
||||
'config.ubereats': {"$exists": True, "$eq": True},
|
||||
'ubereats.cookies': {"$exists": True, "$ne": []}
|
||||
}):
|
||||
chat = await self.get_chat(chat.id)
|
||||
chat.pull_from_database(overwrite_fields=('_id', 'config', 'ubereats'))
|
||||
if (
|
||||
chat.ubereats['next_execution']
|
||||
and
|
||||
(delta_time := chat.ubereats['next_execution'] - datetime.datetime.now(datetime.timezone.utc)) > datetime.timedelta()
|
||||
):
|
||||
flanautils.do_later(delta_time, self.start_ubereats, chat)
|
||||
else:
|
||||
await self.start_ubereats(chat)
|
||||
|
||||
@inline(False)
|
||||
async def _on_recover_message(self, message: Message):
|
||||
if message.replied_message and message.replied_message.author.id == self.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})
|
||||
message_deleted_bot_action = BotAction.find_one({
|
||||
'platform': self.platform.value,
|
||||
'action': Action.MESSAGE_DELETED.value,
|
||||
'chat': message.chat.object_id,
|
||||
'affected_objects': message.replied_message.object_id
|
||||
})
|
||||
elif self.is_bot_mentioned(message):
|
||||
message_deleted_bot_action = BotAction.find_one({
|
||||
'platform': self.platform.value,
|
||||
'action': Action.MESSAGE_DELETED.value,
|
||||
'chat': message.chat.object_id,
|
||||
'date': {'$gt': datetime.datetime.now(datetime.timezone.utc) - constants.RECOVERY_DELETED_MESSAGE_BEFORE}
|
||||
@@ -298,6 +410,28 @@ class FlanaBot(Connect4Bot, PenaltyBot, PollBot, ScraperBot, WeatherBot, MultiBo
|
||||
|
||||
message.buttons_info.presser_user.save()
|
||||
|
||||
async def _on_tunnel_message(self, message: Message):
|
||||
if (
|
||||
not self.tunnel_chat
|
||||
or
|
||||
self._parse_callbacks(
|
||||
message.text,
|
||||
[
|
||||
RegisteredCallback(..., (multibot_constants.KEYWORDS['activate'], constants.KEYWORDS['tunnel'])),
|
||||
RegisteredCallback(..., (multibot_constants.KEYWORDS['deactivate'], constants.KEYWORDS['tunnel']))
|
||||
]
|
||||
)
|
||||
):
|
||||
return
|
||||
|
||||
if message.chat == self.tunnel_chat:
|
||||
await self.send(f"<b>{message.author.name.split('#')[0]}:</b> {message.text}", await self.owner_chat)
|
||||
elif message.author.id == self.owner_id and message.chat.is_private:
|
||||
if message.text:
|
||||
await self.send(message.text, self.tunnel_chat)
|
||||
else:
|
||||
await self.send('No puedo enviar un mensaje sin texto.', message)
|
||||
|
||||
@group
|
||||
@bot_mentioned
|
||||
async def _on_users(self, message: Message):
|
||||
@@ -345,17 +479,20 @@ class FlanaBot(Connect4Bot, PenaltyBot, PollBot, ScraperBot, WeatherBot, MultiBo
|
||||
# -------------------------------------------------------- #
|
||||
# -------------------- PUBLIC METHODS -------------------- #
|
||||
# -------------------------------------------------------- #
|
||||
@staticmethod
|
||||
def check_old_database_actions():
|
||||
def check_old_database_actions(self):
|
||||
before_date = datetime.datetime.now(datetime.timezone.utc) - multibot_constants.DATABASE_MESSAGE_EXPIRATION_TIME
|
||||
BotAction.delete_many_raw({'date': {'$lte': before_date}})
|
||||
BotAction.delete_many_raw({'platform': self.platform.value, 'date': {'$lte': before_date}})
|
||||
|
||||
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)
|
||||
async def send_bye(self, chat: int | str | User | Chat | Message) -> multibot_constants.ORIGINAL_MESSAGE:
|
||||
chat = await self.get_chat(chat)
|
||||
return await self.send(random.choice((*constants.BYE_PHRASES, flanautils.CommonWords.random_time_greeting())), chat)
|
||||
|
||||
async def send_hello(self, message: Message) -> multibot_constants.ORIGINAL_MESSAGE:
|
||||
return await self.send(random.choice((*constants.HELLO_PHRASES, flanautils.CommonWords.random_time_greeting())), message)
|
||||
async def send_hello(self, chat: int | str | User | Chat | Message) -> multibot_constants.ORIGINAL_MESSAGE:
|
||||
chat = await self.get_chat(chat)
|
||||
return await self.send(random.choice((*constants.HELLO_PHRASES, flanautils.CommonWords.random_time_greeting())), chat)
|
||||
|
||||
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)
|
||||
async def send_insult(self, chat: int | str | User | Chat | Message) -> multibot_constants.ORIGINAL_MESSAGE | None:
|
||||
chat = await self.get_chat(chat)
|
||||
async with await self.typing(chat):
|
||||
await asyncio.sleep(random.randint(1, 3))
|
||||
return await self.send(random.choice(constants.INSULTS), chat)
|
||||
|
||||
@@ -10,7 +10,7 @@ import discord
|
||||
import flanautils
|
||||
import pytz
|
||||
from flanautils import Media, NotFoundError, OrderedSet
|
||||
from multibot import BadRoleError, DiscordBot, Role, User, bot_mentioned, constants as multibot_constants, group
|
||||
from multibot import BadRoleError, DiscordBot, Platform, Role, User, bot_mentioned, constants as multibot_constants, group
|
||||
|
||||
from flanabot import constants
|
||||
from flanabot.bots.flana_bot import FlanaBot
|
||||
@@ -39,7 +39,8 @@ class FlanaDiscBot(DiscordBot, FlanaBot):
|
||||
|
||||
async def _changeable_roles(self, group_: int | str | Chat | Message) -> list[Role]:
|
||||
group_id = self.get_group_id(group_)
|
||||
return [role for role in await self.get_group_roles(group_) if role.id in constants.CHANGEABLE_ROLES[group_id]]
|
||||
r = await self.get_group_roles(group_)
|
||||
return [role for role in r if role.id in constants.CHANGEABLE_ROLES[Platform.DISCORD][group_id]]
|
||||
|
||||
async def _heat_channel(self, channel: discord.VoiceChannel):
|
||||
async def set_fire_to(channel_key: str, depends_on: str, firewall=0):
|
||||
@@ -60,9 +61,9 @@ class FlanaDiscBot(DiscordBot, FlanaBot):
|
||||
await channels[channel_key]['object'].edit(name=new_name_)
|
||||
|
||||
channels = {}
|
||||
for key in constants.DISCORD_HOT_CHANNEL_IDS:
|
||||
channel_ = flanautils.find(channel.guild.voice_channels, condition=lambda c: c.id == constants.DISCORD_HOT_CHANNEL_IDS[key])
|
||||
channels[key] = {
|
||||
for letter, channel_id in constants.DISCORD_HOT_CHANNEL_IDS.items():
|
||||
channel_ = flanautils.find(channel.guild.voice_channels, condition=lambda c: c.id == channel_id)
|
||||
channels[letter] = {
|
||||
'object': channel_,
|
||||
'original_name': channel_.name,
|
||||
'n_fires': 0
|
||||
@@ -84,7 +85,10 @@ class FlanaDiscBot(DiscordBot, FlanaBot):
|
||||
continue
|
||||
|
||||
i = int(self.heat_level)
|
||||
if i < len(constants.DISCORD_HEAT_NAMES):
|
||||
if not i:
|
||||
n_fires = 0
|
||||
new_name = channels['C']['original_name']
|
||||
elif i < len(constants.DISCORD_HEAT_NAMES):
|
||||
n_fires = 0
|
||||
new_name = constants.DISCORD_HEAT_NAMES[i]
|
||||
else:
|
||||
@@ -113,7 +117,7 @@ class FlanaDiscBot(DiscordBot, FlanaBot):
|
||||
message: Message,
|
||||
force=False,
|
||||
audio_only=False,
|
||||
timeout_for_media: int | float = 15
|
||||
timeout_for_media: int | float = constants.SCRAPING_TIMEOUT_SECONDS
|
||||
) -> OrderedSet[Media]:
|
||||
return await super()._search_medias(message, force, audio_only, timeout_for_media)
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import telethon.tl.functions
|
||||
from flanautils import Media, OrderedSet
|
||||
from multibot import TelegramBot, find_message, user_client
|
||||
|
||||
from flanabot import constants
|
||||
from flanabot.bots.flana_bot import FlanaBot
|
||||
from flanabot.models import Message
|
||||
|
||||
@@ -20,11 +21,11 @@ from flanabot.models import Message
|
||||
def whitelisted(func: Callable) -> Callable:
|
||||
@functools.wraps(func)
|
||||
@find_message
|
||||
async def wrapper(self: FlanaTeleBot, message: Message):
|
||||
async def wrapper(self: FlanaTeleBot, message: Message, *args, **kwargs):
|
||||
if message.author.id not in self.whitelist_ids:
|
||||
return
|
||||
|
||||
return await func(self, message)
|
||||
return await func(self, message, *args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
@@ -57,7 +58,7 @@ class FlanaTeleBot(TelegramBot, FlanaBot):
|
||||
message: Message,
|
||||
force=False,
|
||||
audio_only=False,
|
||||
timeout_for_media: int | float = 15
|
||||
timeout_for_media: int | float = constants.SCRAPING_TIMEOUT_SECONDS
|
||||
) -> OrderedSet[Media]:
|
||||
return await super()._search_medias(message, force, audio_only, timeout_for_media)
|
||||
|
||||
|
||||
@@ -118,7 +118,7 @@ class PenaltyBot(MultiBot, ABC):
|
||||
|
||||
async def _on_ready(self):
|
||||
await super()._on_ready()
|
||||
await flanautils.do_every(constants.CHECK_PUNISHMENTS_EVERY_SECONDS, self.check_old_punishments)
|
||||
flanautils.do_every(constants.CHECK_PUNISHMENTS_EVERY_SECONDS, self.check_old_punishments)
|
||||
|
||||
@bot_mentioned
|
||||
@group
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
__all__ = ['ScraperBot']
|
||||
|
||||
import asyncio
|
||||
import datetime
|
||||
import random
|
||||
from abc import ABC
|
||||
from collections import defaultdict
|
||||
from typing import Iterable
|
||||
|
||||
import flanautils
|
||||
from flanaapis import RedditMediaNotFoundError, instagram, reddit, tiktok, twitter, yt_dlp_wrapper
|
||||
from flanaapis import InstagramMediaNotFoundError, RedditMediaNotFoundError, instagram, reddit, tiktok, twitter, yt_dlp_wrapper
|
||||
from flanautils import Media, MediaType, OrderedSet, return_if_first_empty
|
||||
from multibot import MultiBot, RegisteredCallback, SendError, constants as multibot_constants, reply
|
||||
from multibot import MultiBot, RegisteredCallback, SendError, constants as multibot_constants, owner, reply
|
||||
|
||||
from flanabot import constants
|
||||
from flanabot.models import Action, BotAction, Message
|
||||
@@ -19,27 +20,27 @@ from flanabot.models import Action, BotAction, Message
|
||||
# --------------------------------------------- SCRAPER_BOT --------------------------------------------- #
|
||||
# ------------------------------------------------------------------------------------------------------- #
|
||||
class ScraperBot(MultiBot, ABC):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.instagram_ban_date = None
|
||||
|
||||
# -------------------------------------------------------- #
|
||||
# ------------------- PROTECTED METHODS ------------------ #
|
||||
# -------------------------------------------------------- #
|
||||
def _add_handlers(self):
|
||||
super()._add_handlers()
|
||||
|
||||
self.register(self._on_force_scraping, constants.KEYWORDS['force'])
|
||||
self.register(self._on_force_scraping, (constants.KEYWORDS['force'], constants.KEYWORDS['scraping']))
|
||||
self.register(self._on_no_scraping, (multibot_constants.KEYWORDS['negate'], constants.KEYWORDS['scraping']))
|
||||
|
||||
self.register(self._on_force_scraping_audio, (constants.KEYWORDS['force'], multibot_constants.KEYWORDS['audio']))
|
||||
self.register(self._on_force_scraping_audio, (constants.KEYWORDS['force'], multibot_constants.KEYWORDS['audio'], constants.KEYWORDS['scraping']))
|
||||
|
||||
self.register(self._on_no_delete_original, (multibot_constants.KEYWORDS['negate'], multibot_constants.KEYWORDS['delete']))
|
||||
self.register(self._on_no_delete_original, (multibot_constants.KEYWORDS['negate'], multibot_constants.KEYWORDS['message']))
|
||||
self.register(self._on_no_delete_original, (multibot_constants.KEYWORDS['negate'], multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['message']))
|
||||
self.register(self._on_no_delete_original, (multibot_constants.KEYWORDS['deactivate'], multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['message']))
|
||||
self.register(self._on_reset_instagram_ban, (multibot_constants.KEYWORDS['delete'], 'instagram'))
|
||||
self.register(self._on_reset_instagram_ban, (multibot_constants.KEYWORDS['reset'], 'instagram'))
|
||||
|
||||
self.register(self._on_scraping, constants.KEYWORDS['scraping'])
|
||||
|
||||
self.register(self._on_scraping_audio, multibot_constants.KEYWORDS['audio'])
|
||||
self.register(self._on_scraping_audio, (multibot_constants.KEYWORDS['audio'], constants.KEYWORDS['scraping']))
|
||||
self.register(self._on_scraping, constants.KEYWORDS['force'])
|
||||
self.register(self._on_scraping, multibot_constants.KEYWORDS['audio'])
|
||||
self.register(lambda message: self._on_scraping(message, delete=False), (multibot_constants.KEYWORDS['negate'], multibot_constants.KEYWORDS['delete']))
|
||||
self.register(lambda message: self._on_scraping(message, delete=False), (multibot_constants.KEYWORDS['negate'], multibot_constants.KEYWORDS['message']))
|
||||
self.register(lambda message: self._on_scraping(message, delete=False), (multibot_constants.KEYWORDS['negate'], multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['message']))
|
||||
|
||||
self.register(self._on_song_info, constants.KEYWORDS['song_info'])
|
||||
|
||||
@@ -53,9 +54,32 @@ class ScraperBot(MultiBot, ABC):
|
||||
tiktok.find_download_urls(text)
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _get_keywords(delete=True, force=False, full=False, audio_only=False) -> list[str]:
|
||||
keywords = list(constants.KEYWORDS['scraping'])
|
||||
|
||||
if not delete:
|
||||
keywords += [
|
||||
*multibot_constants.KEYWORDS['negate'],
|
||||
*multibot_constants.KEYWORDS['deactivate'],
|
||||
*multibot_constants.KEYWORDS['delete'],
|
||||
*multibot_constants.KEYWORDS['message']
|
||||
]
|
||||
|
||||
if force:
|
||||
keywords += constants.KEYWORDS['force']
|
||||
|
||||
if full:
|
||||
keywords += ['sin', 'timeout', 'limite', *multibot_constants.KEYWORDS['all']]
|
||||
|
||||
if audio_only:
|
||||
keywords += multibot_constants.KEYWORDS['audio']
|
||||
|
||||
return keywords
|
||||
|
||||
@staticmethod
|
||||
def _medias_sended_info(medias: Iterable[Media]) -> str:
|
||||
medias_count = defaultdict(lambda: defaultdict(int))
|
||||
medias_count: dict = defaultdict(lambda: defaultdict(int))
|
||||
for media in medias:
|
||||
if not media.source or isinstance(media.source, str):
|
||||
medias_count[media.source][media.type_] += 1
|
||||
@@ -89,21 +113,28 @@ class ScraperBot(MultiBot, ABC):
|
||||
new_line = ' ' if len(medias_sended_info) == 1 else '\n'
|
||||
return f'{new_line}{medias_sended_info_joined}:'
|
||||
|
||||
async def _scrape_and_send(self, message: Message, force=False, audio_only=False) -> OrderedSet[Media]:
|
||||
kwargs = {}
|
||||
if self._parse_callbacks(
|
||||
message.text,
|
||||
[
|
||||
RegisteredCallback(..., [['sin'], ['timeout', 'limite']]),
|
||||
RegisteredCallback(..., 'completo entero full todo')
|
||||
]
|
||||
):
|
||||
kwargs['timeout_for_media'] = None
|
||||
async def _scrape_and_send(
|
||||
self,
|
||||
message: Message,
|
||||
force=False,
|
||||
full=False,
|
||||
audio_only=False,
|
||||
send_user_context=True,
|
||||
keywords: list[str] = None,
|
||||
sended_media_messages: OrderedSet[Message] = None
|
||||
) -> OrderedSet[Message]:
|
||||
if not keywords:
|
||||
keywords = []
|
||||
if sended_media_messages is None:
|
||||
sended_media_messages = OrderedSet()
|
||||
|
||||
kwargs = {'timeout_for_media': None} if full else {}
|
||||
|
||||
if not (medias := await self._search_medias(message, force, audio_only, **kwargs)):
|
||||
return OrderedSet()
|
||||
|
||||
sended_media_messages, _ = await self.send_medias(medias, message)
|
||||
sended_media_messages = OrderedSet(sended_media_messages)
|
||||
new_sended_media_messages, _ = await self.send_medias(medias, message, send_user_context=send_user_context, keywords=keywords)
|
||||
sended_media_messages |= new_sended_media_messages
|
||||
|
||||
await self.send_inline_results(message)
|
||||
|
||||
@@ -113,23 +144,22 @@ class ScraperBot(MultiBot, ABC):
|
||||
self,
|
||||
message: Message,
|
||||
force=False,
|
||||
full=False,
|
||||
audio_only=False,
|
||||
sended_media_messages: OrderedSet[Media] = None
|
||||
) -> OrderedSet[Media]:
|
||||
send_user_context=True,
|
||||
keywords: list[str] = None,
|
||||
sended_media_messages: OrderedSet[Message] = None
|
||||
) -> OrderedSet[Message]:
|
||||
if not keywords:
|
||||
keywords = []
|
||||
if sended_media_messages is None:
|
||||
sended_media_messages = OrderedSet()
|
||||
|
||||
sended_media_messages += await self._scrape_and_send(message, force, audio_only)
|
||||
sended_media_messages += await self._scrape_and_send(message, force, full, audio_only, send_user_context, keywords)
|
||||
|
||||
if (
|
||||
sended_media_messages
|
||||
and
|
||||
message.chat.is_group
|
||||
and
|
||||
message.chat.config['scraping_delete_original']
|
||||
):
|
||||
if sended_media_messages and message.chat.config['scraping_delete_original']:
|
||||
# noinspection PyTypeChecker
|
||||
BotAction(Action.MESSAGE_DELETED, message, affected_objects=[message, *sended_media_messages]).save()
|
||||
BotAction(self.platform.value, Action.MESSAGE_DELETED, message, affected_objects=[message, *sended_media_messages]).save()
|
||||
await self.delete_message(message)
|
||||
|
||||
return sended_media_messages
|
||||
@@ -155,9 +185,8 @@ class ScraperBot(MultiBot, ABC):
|
||||
if not any(ids) and flanautils.find_urls(text_part):
|
||||
if force:
|
||||
media_urls.append(text_part)
|
||||
else:
|
||||
if not any(domain.lower() in text_part for domain in multibot_constants.GIF_DOMAINS):
|
||||
media_urls.append(text_part)
|
||||
elif not any(domain.lower() in text_part for domain in multibot_constants.GIF_DOMAINS):
|
||||
media_urls.append(text_part)
|
||||
|
||||
if not any(ids) and not media_urls:
|
||||
return medias
|
||||
@@ -188,18 +217,47 @@ class ScraperBot(MultiBot, ABC):
|
||||
else:
|
||||
media_urls.append(reddit_url)
|
||||
|
||||
gather_result = asyncio.gather(
|
||||
gather_future = asyncio.gather(
|
||||
twitter.get_medias(tweet_ids, audio_only),
|
||||
instagram.get_medias(instagram_ids, audio_only),
|
||||
tiktok.get_medias(tiktok_users_and_ids, tiktok_download_urls, 'h264', 'mp4', force, audio_only, timeout_for_media),
|
||||
yt_dlp_wrapper.get_medias(media_urls, 'h264', 'mp4', force, audio_only, timeout_for_media),
|
||||
return_exceptions=True
|
||||
)
|
||||
|
||||
await gather_result
|
||||
instagram_results = []
|
||||
instagram_restricted_age_ids = []
|
||||
if instagram_ids:
|
||||
can_instagram_v1 = not self.instagram_ban_date or datetime.datetime.now(datetime.timezone.utc) - self.instagram_ban_date >= constants.INSTAGRAM_BAN_SLEEP
|
||||
if can_instagram_v1:
|
||||
try:
|
||||
instagram_results = await instagram.get_medias(instagram_ids, audio_only)
|
||||
except InstagramMediaNotFoundError:
|
||||
pass
|
||||
else:
|
||||
instagram_restricted_age_ids = [media.content for media in instagram_results if media.type_ is MediaType.ERROR]
|
||||
|
||||
if not instagram_results:
|
||||
v2_ids = instagram_ids
|
||||
elif instagram_restricted_age_ids:
|
||||
v2_ids = instagram_restricted_age_ids
|
||||
else:
|
||||
v2_ids = []
|
||||
|
||||
if v2_ids:
|
||||
try:
|
||||
instagram_results = await instagram.get_medias_v2(instagram_ids, audio_only)
|
||||
except InstagramMediaNotFoundError as e:
|
||||
if not (instagram_results := await yt_dlp_wrapper.get_medias(instagram.make_urls(instagram_ids), 'h264', 'mp4', force, audio_only, timeout_for_media)):
|
||||
exceptions.append(e)
|
||||
|
||||
if instagram_results and can_instagram_v1 and not instagram_restricted_age_ids:
|
||||
self.instagram_ban_date = datetime.datetime.now(datetime.timezone.utc)
|
||||
await self.send('Límite de Instagram excedido.', await self.owner_chat)
|
||||
|
||||
gather_results = await gather_future
|
||||
await self.delete_message(bot_state_message)
|
||||
|
||||
gather_medias, gather_exceptions = flanautils.filter_exceptions(gather_result.result())
|
||||
gather_medias, gather_exceptions = flanautils.filter_exceptions(gather_results + instagram_results)
|
||||
await self._manage_exceptions(exceptions + gather_exceptions, message, print_traceback=True)
|
||||
|
||||
return medias | gather_medias
|
||||
@@ -207,31 +265,91 @@ class ScraperBot(MultiBot, ABC):
|
||||
# ---------------------------------------------- #
|
||||
# HANDLERS #
|
||||
# ---------------------------------------------- #
|
||||
async def _on_force_scraping(self, message: Message) -> OrderedSet[Media]:
|
||||
return await self._on_scraping(message, force=True)
|
||||
|
||||
async def _on_force_scraping_audio(self, message: Message) -> OrderedSet[Media]:
|
||||
return await self._on_scraping(message, force=True, audio_only=True)
|
||||
|
||||
async def _on_no_delete_original(self, message: Message):
|
||||
if not await self._scrape_and_send(message):
|
||||
await self._on_recover_message(message)
|
||||
async def _on_no_scraping(self, message: Message):
|
||||
pass
|
||||
|
||||
async def _on_recover_message(self, message: Message):
|
||||
pass
|
||||
|
||||
async def _on_scraping(self, message: Message, force=False, audio_only=False) -> OrderedSet[Media]:
|
||||
@owner
|
||||
async def _on_reset_instagram_ban(self, message: Message):
|
||||
if message.chat.is_group and not self.is_bot_mentioned(message):
|
||||
return
|
||||
|
||||
self.instagram_ban_date = None
|
||||
bot_message = await self.send('Ban de Instagram reseteado.', message)
|
||||
await self.delete_message(message)
|
||||
flanautils.do_later(multibot_constants.COMMAND_MESSAGE_DURATION, self.delete_message, bot_message)
|
||||
|
||||
async def _on_scraping(
|
||||
self,
|
||||
message: Message,
|
||||
delete=True,
|
||||
force: bool = None,
|
||||
full: bool = None,
|
||||
audio_only: bool = None,
|
||||
scrape_replied=True,
|
||||
) -> OrderedSet[Message]:
|
||||
sended_media_messages = OrderedSet()
|
||||
if not message.chat.config['auto_scraping'] and not self.is_bot_mentioned(message):
|
||||
return sended_media_messages
|
||||
|
||||
if message.replied_message:
|
||||
sended_media_messages += await self._scrape_and_send(message.replied_message, force, audio_only)
|
||||
if force is None:
|
||||
force = bool(
|
||||
flanautils.cartesian_product_string_matching(
|
||||
message.text,
|
||||
constants.KEYWORDS['force'],
|
||||
multibot_constants.PARSER_MIN_SCORE_DEFAULT
|
||||
)
|
||||
)
|
||||
|
||||
return await self._scrape_send_and_delete(message, force, audio_only, sended_media_messages)
|
||||
if full is None:
|
||||
full = bool(
|
||||
self._parse_callbacks(
|
||||
message.text,
|
||||
[
|
||||
RegisteredCallback(..., (('sin',), ('timeout', 'limite'))),
|
||||
RegisteredCallback(..., multibot_constants.KEYWORDS['all'])
|
||||
]
|
||||
)
|
||||
)
|
||||
|
||||
async def _on_scraping_audio(self, message: Message) -> OrderedSet[Media]:
|
||||
return await self._on_scraping(message, audio_only=True)
|
||||
if audio_only is None:
|
||||
audio_only = bool(
|
||||
flanautils.cartesian_product_string_matching(
|
||||
message.text,
|
||||
multibot_constants.KEYWORDS['audio'],
|
||||
multibot_constants.PARSER_MIN_SCORE_DEFAULT
|
||||
)
|
||||
)
|
||||
|
||||
keywords = self._get_keywords(delete, force, full, audio_only)
|
||||
|
||||
if scrape_replied and message.replied_message:
|
||||
sended_media_messages += await self._scrape_and_send(
|
||||
message.replied_message,
|
||||
force,
|
||||
full,
|
||||
audio_only,
|
||||
send_user_context=False
|
||||
)
|
||||
|
||||
kwargs = {
|
||||
'message': message,
|
||||
'force': force,
|
||||
'full': full,
|
||||
'audio_only': audio_only,
|
||||
'keywords': keywords,
|
||||
'sended_media_messages': sended_media_messages
|
||||
}
|
||||
if delete:
|
||||
sended_media_messages |= await self._scrape_send_and_delete(**kwargs)
|
||||
else:
|
||||
sended_media_messages |= await self._scrape_and_send(**kwargs)
|
||||
if not sended_media_messages:
|
||||
await self._on_recover_message(message)
|
||||
|
||||
return sended_media_messages
|
||||
|
||||
@reply
|
||||
async def _on_song_info(self, message: Message):
|
||||
@@ -247,7 +365,17 @@ class ScraperBot(MultiBot, ABC):
|
||||
# -------------------- PUBLIC METHODS -------------------- #
|
||||
# -------------------------------------------------------- #
|
||||
@return_if_first_empty(([], 0), exclude_self_types='ScraperBot', 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,
|
||||
send_user_context=True,
|
||||
keywords: list[str] = None
|
||||
) -> tuple[list[Message], int]:
|
||||
if not keywords:
|
||||
keywords = []
|
||||
|
||||
sended_media_messages = []
|
||||
fails = 0
|
||||
bot_state_message: Message | None = None
|
||||
@@ -259,29 +387,22 @@ class ScraperBot(MultiBot, ABC):
|
||||
|
||||
if message.chat.is_group:
|
||||
sended_info_message = await self.send(f"{message.author.name.split('#')[0]} compartió{self._medias_sended_info(medias)}", message, reply_to=message.replied_message)
|
||||
user_text = ' '.join(
|
||||
[word for word in message.text.split()
|
||||
if (
|
||||
if (
|
||||
send_user_context
|
||||
and
|
||||
(user_text := ' '.join(
|
||||
[word for word in message.text.split()
|
||||
if (
|
||||
not any(await self._find_ids(word))
|
||||
and
|
||||
not flanautils.find_urls(word)
|
||||
and
|
||||
not flanautils.cartesian_product_string_matching(
|
||||
word,
|
||||
(
|
||||
*multibot_constants.KEYWORDS['audio'],
|
||||
*multibot_constants.KEYWORDS['delete'],
|
||||
*constants.KEYWORDS['force'],
|
||||
*multibot_constants.KEYWORDS['negate'],
|
||||
*constants.KEYWORDS['scraping']
|
||||
),
|
||||
multibot_constants.PARSER_MIN_SCORE_DEFAULT
|
||||
)
|
||||
not flanautils.cartesian_product_string_matching(word, keywords, multibot_constants.PARSER_MIN_SCORE_DEFAULT)
|
||||
and
|
||||
flanautils.remove_symbols(word).lower() not in (str(self.id), self.name.lower())
|
||||
)]
|
||||
)
|
||||
if user_text:
|
||||
)]
|
||||
))
|
||||
):
|
||||
user_text_bot_message = await self.send(user_text, message, reply_to=message.replied_message)
|
||||
|
||||
for media in medias:
|
||||
@@ -318,10 +439,10 @@ class ScraperBot(MultiBot, ABC):
|
||||
@return_if_first_empty(exclude_self_types='ScraperBot', globals_=globals())
|
||||
async def send_song_info(self, song_info: Media, message: Message):
|
||||
attributes = (
|
||||
f'Título: {song_info.title}\n' if song_info.title else '',
|
||||
f'Autor: {song_info.author}\n' if song_info.author else '',
|
||||
f'Álbum: {song_info.album}\n' if song_info.album else '',
|
||||
f'Previa:'
|
||||
f'<b>Título:</b> {song_info.title}\n' if song_info.title else '',
|
||||
f'<b>Autor:</b> {song_info.author}\n' if song_info.author else '',
|
||||
f'<b>Álbum:</b> {song_info.album}\n' if song_info.album else '',
|
||||
f'<b>Previa:</b>'
|
||||
)
|
||||
await self.send(''.join(attributes), message)
|
||||
if song_info:
|
||||
|
||||
178
flanabot/bots/ubereats_bot.py
Normal file
178
flanabot/bots/ubereats_bot.py
Normal file
@@ -0,0 +1,178 @@
|
||||
__all__ = ['UberEatsBot']
|
||||
|
||||
import asyncio
|
||||
import datetime
|
||||
import random
|
||||
from abc import ABC
|
||||
from collections import defaultdict
|
||||
|
||||
import flanautils
|
||||
import playwright.async_api
|
||||
from multibot import MultiBot, group
|
||||
|
||||
import constants
|
||||
from flanabot.models import Chat, Message
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------------------------------- #
|
||||
# --------------------------------------------- POLL_BOT --------------------------------------------- #
|
||||
# ---------------------------------------------------------------------------------------------------- #
|
||||
class UberEatsBot(MultiBot, ABC):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.playwright: playwright.async_api.Playwright | None = None
|
||||
self.browser: playwright.async_api.Browser | None = None
|
||||
self.task_contexts: dict[int, dict] = defaultdict(lambda: defaultdict(lambda: None))
|
||||
|
||||
# -------------------------------------------------------- #
|
||||
# ------------------- PROTECTED METHODS ------------------ #
|
||||
# -------------------------------------------------------- #
|
||||
def _add_handlers(self):
|
||||
super()._add_handlers()
|
||||
|
||||
self.register(self._on_ubereats, 'ubereats', priority=2)
|
||||
|
||||
async def _cancel_scraping_task(self, chat: Chat):
|
||||
if not (task := self.task_contexts[chat.id]['task']) or task.done():
|
||||
return
|
||||
|
||||
await self._close_playwright(chat)
|
||||
task.cancel()
|
||||
del self.task_contexts[chat.id]
|
||||
|
||||
async def _close_playwright(self, chat: Chat):
|
||||
if browser := self.task_contexts[chat.id]['browser']:
|
||||
await browser.close()
|
||||
if playwright_ := self.task_contexts[chat.id]['playwright']:
|
||||
await playwright_.stop()
|
||||
|
||||
async def _scrape_codes(self, chat: Chat) -> list[str | None]:
|
||||
async def get_code() -> str:
|
||||
return await page.input_value("input[class='code toCopy']")
|
||||
|
||||
codes: list[str | None] = [None] * len(chat.ubereats['cookies'])
|
||||
|
||||
async with playwright.async_api.async_playwright() as playwright_:
|
||||
self.task_contexts[chat.id]['playwright'] = playwright_
|
||||
for i, cookies in enumerate(chat.ubereats['cookies']):
|
||||
for _ in range(3):
|
||||
try:
|
||||
async with await playwright_.chromium.launch() as browser:
|
||||
self.task_contexts[chat.id]['browser'] = browser
|
||||
context: playwright.async_api.BrowserContext = await browser.new_context(
|
||||
storage_state={'cookies': cookies},
|
||||
user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.5615.49 Safari/537.36',
|
||||
screen={
|
||||
'width': 1920,
|
||||
'height': 1080
|
||||
},
|
||||
viewport={
|
||||
'width': 1280,
|
||||
'height': 720
|
||||
},
|
||||
device_scale_factor=1,
|
||||
is_mobile=False,
|
||||
has_touch=False,
|
||||
default_browser_type='chromium',
|
||||
locale='es-ES'
|
||||
)
|
||||
context.set_default_timeout(3000)
|
||||
|
||||
page = await context.new_page()
|
||||
await page.goto('https://www.myunidays.com/ES/es-ES/partners/ubereats/access/online', timeout=30000)
|
||||
|
||||
if button := await page.query_selector("button[class='button highlight']"):
|
||||
await button.click()
|
||||
else:
|
||||
await page.click("'Revelar código'")
|
||||
for _ in range(5):
|
||||
if len(context.pages) == 2:
|
||||
break
|
||||
await asyncio.sleep(0.5)
|
||||
else:
|
||||
continue
|
||||
page = context.pages[1]
|
||||
await page.wait_for_load_state()
|
||||
|
||||
code = await get_code()
|
||||
if not (new_code_button := await page.query_selector("button[class='getNewCode button secondary']")):
|
||||
new_code_button = page.locator("'Obtener nuevo código'")
|
||||
if await new_code_button.is_enabled():
|
||||
await new_code_button.click()
|
||||
for _ in range(5):
|
||||
if (new_code := await get_code()) != code:
|
||||
code = new_code
|
||||
break
|
||||
await asyncio.sleep(0.5)
|
||||
codes[i] = code
|
||||
|
||||
chat.ubereats['cookies'][i] = await context.cookies('https://www.myunidays.com')
|
||||
except playwright.async_api.Error:
|
||||
await asyncio.sleep(1)
|
||||
else:
|
||||
break
|
||||
|
||||
return codes
|
||||
|
||||
# ---------------------------------------------- #
|
||||
# HANDLERS #
|
||||
# ---------------------------------------------- #
|
||||
@group(False)
|
||||
async def _on_ubereats(self, message: Message):
|
||||
if not message.chat.ubereats['cookies']:
|
||||
return
|
||||
|
||||
time = flanautils.text_to_time(message.text)
|
||||
if not time:
|
||||
bot_state_message = await self.send(random.choice(constants.SCRAPING_PHRASES), message)
|
||||
await self.send_ubereats_code(message.chat, update_next_execution=False)
|
||||
await self.delete_message(bot_state_message)
|
||||
return
|
||||
|
||||
if time < datetime.timedelta(days=1, minutes=5):
|
||||
await self.send('El mínimo es 1 día y 5 minutos.', message)
|
||||
return
|
||||
|
||||
seconds = int(time.total_seconds())
|
||||
message.chat.ubereats['seconds'] = seconds
|
||||
message.save()
|
||||
period = flanautils.TimeUnits(seconds=seconds)
|
||||
await self.send(f'A partir de ahora te enviaré un código de UberEats cada <b>{period.to_words()}</b>.', message)
|
||||
await self.start_ubereats(message.chat)
|
||||
|
||||
# -------------------------------------------------------- #
|
||||
# -------------------- PUBLIC METHODS -------------------- #
|
||||
# -------------------------------------------------------- #
|
||||
async def send_ubereats_code(self, chat: Chat, update_next_execution=True):
|
||||
chat.pull_from_database(overwrite_fields=('ubereats',))
|
||||
|
||||
codes = await self._scrape_codes(chat)
|
||||
for i, code in enumerate(codes):
|
||||
if code:
|
||||
if code in chat.ubereats['last_codes']:
|
||||
warning_text = '<i>Código ya enviado anteriormente:</i> '
|
||||
else:
|
||||
warning_text = ''
|
||||
await self.send(f'{warning_text}<code>{code}</code>', chat, silent=True)
|
||||
else:
|
||||
try:
|
||||
codes[i] = chat.ubereats['last_codes'][i]
|
||||
except IndexError:
|
||||
codes[i] = None
|
||||
chat.ubereats['last_codes'] = codes
|
||||
|
||||
if update_next_execution:
|
||||
chat.ubereats['next_execution'] = datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(seconds=chat.ubereats['seconds'])
|
||||
|
||||
chat.save()
|
||||
|
||||
async def start_ubereats(self, chat: Chat, send_code_now=True):
|
||||
await self._cancel_scraping_task(chat)
|
||||
chat.config['ubereats'] = True
|
||||
chat.save(pull_overwrite_fields=('ubereats',))
|
||||
self.task_contexts[chat.id]['task'] = flanautils.do_every(chat.ubereats['seconds'], self.send_ubereats_code, chat, do_first_now=send_code_now)
|
||||
|
||||
async def stop_ubereats(self, chat: Chat):
|
||||
await self._cancel_scraping_task(chat)
|
||||
chat.config['ubereats'] = False
|
||||
chat.save(pull_overwrite_fields=('ubereats',))
|
||||
@@ -40,7 +40,7 @@ class WeatherBot(MultiBot, ABC):
|
||||
show_progress_state = False
|
||||
elif message.chat.is_group and not self.is_bot_mentioned(message):
|
||||
if message.chat.config['auto_weather_chart']:
|
||||
if BotAction.find_one({'action': Action.AUTO_WEATHER_CHART.value, 'chat': message.chat.object_id, 'date': {'$gt': datetime.datetime.now(datetime.timezone.utc) - constants.AUTO_WEATHER_EVERY}}):
|
||||
if BotAction.find_one({'platform': self.platform.value, 'action': Action.AUTO_WEATHER_CHART.value, 'chat': message.chat.object_id, 'date': {'$gt': datetime.datetime.now(datetime.timezone.utc) - constants.AUTO_WEATHER_EVERY}}):
|
||||
return
|
||||
show_progress_state = False
|
||||
else:
|
||||
@@ -161,7 +161,7 @@ class WeatherBot(MultiBot, ABC):
|
||||
|
||||
if bot_message and not self.is_bot_mentioned(message):
|
||||
# noinspection PyTypeChecker
|
||||
BotAction(Action.AUTO_WEATHER_CHART, message, affected_objects=[bot_message]).save()
|
||||
BotAction(self.platform.value, Action.AUTO_WEATHER_CHART, message, affected_objects=[bot_message]).save()
|
||||
|
||||
async def _on_weather_button_press(self, message: Message):
|
||||
await self.accept_button_event(message)
|
||||
|
||||
@@ -26,7 +26,7 @@ FONT_SIZE = 32 * SIZE_MULTIPLIER
|
||||
TABLE_LINE_WIDTH = 4 * SIZE_MULTIPLIER
|
||||
|
||||
BLUE = (66 / 255, 135 / 255, 245 / 255)
|
||||
BACKGROUND_COLOR = (54 / 255, 57 / 255, 63 / 255)
|
||||
BACKGROUND_COLOR = (49 / 255, 51 / 255, 56 / 255)
|
||||
GRAY = (200 / 255, 200 / 255, 200 / 255)
|
||||
HIGHLIGHT_COLOR = (104 / 255, 107 / 255, 113 / 255)
|
||||
RED = (255 / 255, 70 / 255, 70 / 255)
|
||||
|
||||
@@ -11,15 +11,17 @@ CONNECT_4_AI_DELAY_SECONDS = 1
|
||||
CONNECT_4_CENTER_COLUMN_POINTS = 2
|
||||
CONNECT_4_N_COLUMNS = 7
|
||||
CONNECT_4_N_ROWS = 6
|
||||
|
||||
FLOOD_2s_LIMIT = 2
|
||||
FLOOD_7s_LIMIT = 4
|
||||
HEAT_PERIOD_SECONDS = datetime.timedelta(minutes=15).total_seconds()
|
||||
HELP_MINUTES_LIMIT = 1
|
||||
INSTAGRAM_BAN_SLEEP = datetime.timedelta(days=1)
|
||||
INSULT_PROBABILITY = 0.00166666667
|
||||
MAX_PLACE_QUERY_LENGTH = 50
|
||||
PUNISHMENT_INCREMENT_EXPONENT = 6
|
||||
PUNISHMENTS_RESET_TIME = datetime.timedelta(weeks=2)
|
||||
RECOVERY_DELETED_MESSAGE_BEFORE = datetime.timedelta(hours=1)
|
||||
SCRAPING_TIMEOUT_SECONDS = 10
|
||||
|
||||
BYE_PHRASES = ('Adiós.', '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')
|
||||
@@ -38,7 +40,6 @@ CHANGEABLE_ROLES = defaultdict(
|
||||
)
|
||||
|
||||
DISCORD_HEAT_NAMES = [
|
||||
'Canal Congelado',
|
||||
'Canal Fresquito',
|
||||
'Canal Templaillo',
|
||||
'Canal Calentito',
|
||||
@@ -96,12 +97,12 @@ KEYWORDS = {
|
||||
'fusila', 'hell', 'humos', 'infierno', 'jefe', 'jode', 'learn', 'leccion', 'lesson', 'manda', 'paliza',
|
||||
'purgatorio', 'purgatory', 'sancion', 'shoot', 'teach', 'whip'),
|
||||
'random': ('aleatorio', 'azar', 'random'),
|
||||
'scraping': ('api', 'aqui', 'busca', 'contenido', 'content', 'descarga', 'descargar', 'download', 'envia', 'habia',
|
||||
'media', 'redes', 'scrap', 'scraping', 'search', 'send', 'social', 'sociales', 'tenia', 'video',
|
||||
'videos'),
|
||||
'scraping': ('busca', 'contenido', 'content', 'descarga', 'descargar', 'descargues', 'download', 'envia', 'scrap',
|
||||
'scrapea', 'scrapees', 'scraping', 'search', 'send'),
|
||||
'self': (('contigo', 'contra', 'ti'), ('mismo', 'ti')),
|
||||
'song_info': ('aqui', 'cancion', 'data', 'datos', 'info', 'informacion', 'information', 'llama', 'media', 'name',
|
||||
'nombre', 'sonaba', 'sonando', 'song', 'sono', 'sound', 'suena', 'title', 'titulo', 'video'),
|
||||
'song_info': ('cancion', 'data', 'datos', 'info', 'informacion', 'information', 'sonaba', 'sonando', 'song', 'sono',
|
||||
'sound', 'suena'),
|
||||
'tunnel': ('canal', 'channel', 'tunel', 'tunnel'),
|
||||
'unpunish': ('absolve', 'forgive', 'innocent', 'inocente', 'perdona', 'spare'),
|
||||
'vote': ('votacion', 'votar', 'vote', 'voting', 'voto'),
|
||||
'weather': ('atmosfera', 'atmosferico', 'calle', 'calor', 'caloret', 'clima', 'climatologia', 'cloud', 'cloudless',
|
||||
|
||||
@@ -4,7 +4,7 @@ import datetime
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
from flanautils import DCMongoBase, FlanaBase
|
||||
from multibot.models.user import User
|
||||
from multibot import Platform, User
|
||||
|
||||
from flanabot.models.chat import Chat
|
||||
from flanabot.models.enums import Action
|
||||
@@ -17,6 +17,7 @@ class BotAction(DCMongoBase, FlanaBase):
|
||||
unique_keys = 'message'
|
||||
nullable_unique_keys = 'message'
|
||||
|
||||
platform: Platform = None
|
||||
action: Action = None
|
||||
message: Message = None
|
||||
author: User = None
|
||||
|
||||
@@ -7,17 +7,18 @@ from multibot import Chat as MultiBotChat
|
||||
|
||||
@dataclass(eq=False)
|
||||
class Chat(MultiBotChat):
|
||||
DEFAULT_CONFIG = {
|
||||
config: dict = field(default_factory=lambda: {
|
||||
'auto_insult': True,
|
||||
'auto_scraping': True,
|
||||
'auto_weather_chart': False,
|
||||
'check_flood': False,
|
||||
'punish': False,
|
||||
'scraping_delete_original': True
|
||||
}
|
||||
|
||||
config: dict[str, bool] = field(default_factory=dict)
|
||||
|
||||
def __post_init__(self):
|
||||
super().__post_init__()
|
||||
self.config = self.DEFAULT_CONFIG | self.config
|
||||
'scraping_delete_original': True,
|
||||
'ubereats': False
|
||||
})
|
||||
ubereats: dict = field(default_factory=lambda: {
|
||||
'cookies': [],
|
||||
'last_codes': [],
|
||||
'seconds': 86700,
|
||||
'next_execution': None
|
||||
})
|
||||
|
||||
@@ -5,12 +5,13 @@ async-timeout==4.0.2
|
||||
attrs==22.2.0
|
||||
beautifulsoup4==4.11.1
|
||||
Brotli==1.0.9
|
||||
browser_cookie3==0.18.1
|
||||
certifi==2022.12.7
|
||||
charset-normalizer==2.1.1
|
||||
click==8.1.3
|
||||
colorama==0.4.6
|
||||
cryptg==0.4.0
|
||||
discord.py==2.1.0
|
||||
discord.py==2.2.2
|
||||
dnspython==2.2.1
|
||||
fastapi==0.89.1
|
||||
flanaapis
|
||||
@@ -21,6 +22,7 @@ h11==0.14.0
|
||||
hachoir==3.2.0
|
||||
idna==3.4
|
||||
iso8601==1.1.0
|
||||
jeepney==0.8.0
|
||||
jellyfish==0.9.0
|
||||
kaleido==0.2.1
|
||||
mpmath==1.2.1
|
||||
@@ -28,7 +30,7 @@ multibot
|
||||
multidict==6.0.4
|
||||
mutagen==1.46.0
|
||||
Pillow==9.4.0
|
||||
playwright==1.29.1
|
||||
playwright==1.17.2
|
||||
plotly==5.11.0
|
||||
pyaes==1.6.1
|
||||
pyasn1==0.4.8
|
||||
@@ -45,7 +47,7 @@ sniffio==1.3.0
|
||||
soupsieve==2.3.2.post1
|
||||
starlette==0.22.0
|
||||
sympy==1.11.1
|
||||
Telethon==1.26.1
|
||||
Telethon==1.27.0
|
||||
tenacity==8.1.0
|
||||
twitchio==2.5.0
|
||||
typing_extensions==4.4.0
|
||||
@@ -54,4 +56,4 @@ urllib3==1.26.13
|
||||
uvicorn==0.20.0
|
||||
websockets==10.4
|
||||
yarl==1.8.2
|
||||
yt-dlp==2023.1.6
|
||||
yt-dlp==2023.3.4
|
||||
|
||||
Reference in New Issue
Block a user