Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a37c2ee1d7 | ||
|
|
449df672b5 | ||
|
|
d3b8c4f821 | ||
|
|
acbd0e5ad1 | ||
|
|
a0c693f5eb | ||
|
|
5a500e71e3 | ||
|
|
d6cdc1436f | ||
|
|
b9e61b296d | ||
|
|
35583088ab | ||
|
|
73f2d4c485 | ||
|
|
a8cf321140 | ||
|
|
944e473fac | ||
|
|
f4ffa40263 | ||
|
|
296f0d625d | ||
|
|
a1858887de | ||
|
|
641b36ee81 |
10
Dockerfile
Normal file
10
Dockerfile
Normal file
@@ -0,0 +1,10 @@
|
||||
FROM flanaganvaquero/flanawright
|
||||
|
||||
WORKDIR /application
|
||||
|
||||
COPY flanabot flanabot
|
||||
COPY venv/lib/python3.10/site-packages /usr/local/lib/python3.10/site-packages
|
||||
|
||||
ENV PYTHONPATH=/application
|
||||
|
||||
CMD python3.10 flanabot/main.py
|
||||
@@ -10,7 +10,7 @@ Installation
|
||||
|
||||
Python 3.10 or higher is required.
|
||||
|
||||
.. code-block:: python
|
||||
.. code-block::
|
||||
|
||||
pip install flanabot
|
||||
|
||||
|
||||
17
docker-compose.yaml
Normal file
17
docker-compose.yaml
Normal file
@@ -0,0 +1,17 @@
|
||||
services:
|
||||
flanabot:
|
||||
image: flanaganvaquero/flanabot
|
||||
build: .
|
||||
env_file:
|
||||
- .env
|
||||
|
||||
mongodb:
|
||||
image: mongo
|
||||
environment:
|
||||
- MONGO_INITDB_ROOT_USERNAME=${MONGO_USER}
|
||||
- MONGO_INITDB_ROOT_PASSWORD=${MONGO_PASSWORD}
|
||||
volumes:
|
||||
- mongodata:/data/db
|
||||
|
||||
volumes:
|
||||
mongodata:
|
||||
@@ -3,7 +3,9 @@ import datetime
|
||||
import itertools
|
||||
import pprint
|
||||
import random
|
||||
import time as time_module
|
||||
from abc import ABC
|
||||
from asyncio import Future
|
||||
from typing import Iterable, Iterator, Type
|
||||
|
||||
import flanaapis.geolocation.functions
|
||||
@@ -34,7 +36,7 @@ class FlanaBot(MultiBot, ABC):
|
||||
def _add_handlers(self):
|
||||
super()._add_handlers()
|
||||
|
||||
self.register(self._on_bye, constants.KEYWORDS['bye'], min_ratio=1)
|
||||
self.register(self._on_bye, constants.KEYWORDS['bye'])
|
||||
|
||||
self.register(self._on_config_list_show, constants.KEYWORDS['config'])
|
||||
self.register(self._on_config_list_show, constants.KEYWORDS['help'])
|
||||
@@ -85,6 +87,7 @@ class FlanaBot(MultiBot, ABC):
|
||||
self.register(self._on_hello, constants.KEYWORDS['hello'])
|
||||
|
||||
self.register(self._on_mute, constants.KEYWORDS['mute'])
|
||||
self.register(self._on_mute, (('haz', 'se'), constants.KEYWORDS['mute']))
|
||||
self.register(self._on_mute, (constants.KEYWORDS['deactivate'], constants.KEYWORDS['unmute']))
|
||||
self.register(self._on_mute, (constants.KEYWORDS['deactivate'], constants.KEYWORDS['sound']))
|
||||
|
||||
@@ -252,7 +255,8 @@ class FlanaBot(MultiBot, ABC):
|
||||
Source.TWITTER: {MediaType.IMAGE: 0, MediaType.AUDIO: 0, MediaType.GIF: 0, MediaType.VIDEO: 0, None: 0, MediaType.ERROR: 0},
|
||||
Source.INSTAGRAM: {MediaType.IMAGE: 0, MediaType.AUDIO: 0, MediaType.GIF: 0, MediaType.VIDEO: 0, None: 0, MediaType.ERROR: 0},
|
||||
Source.TIKTOK: {MediaType.IMAGE: 0, MediaType.AUDIO: 0, MediaType.GIF: 0, MediaType.VIDEO: 0, None: 0, MediaType.ERROR: 0},
|
||||
Source.REDDIT: {MediaType.IMAGE: 0, MediaType.AUDIO: 0, MediaType.GIF: 0, MediaType.VIDEO: 0, None: 0, MediaType.ERROR: 0}
|
||||
Source.REDDIT: {MediaType.IMAGE: 0, MediaType.AUDIO: 0, MediaType.GIF: 0, MediaType.VIDEO: 0, None: 0, MediaType.ERROR: 0},
|
||||
None: {MediaType.IMAGE: 0, MediaType.AUDIO: 0, MediaType.GIF: 0, MediaType.VIDEO: 0, None: 0, MediaType.ERROR: 0}
|
||||
}
|
||||
for media in medias:
|
||||
medias_count[media.source][media.type_] += 1
|
||||
@@ -278,7 +282,7 @@ class FlanaBot(MultiBot, ABC):
|
||||
MediaType.ERROR: 'errores'}[media_type]
|
||||
source_medias_sended_info.append(f'{count} {type_text}')
|
||||
if source_medias_sended_info:
|
||||
medias_sended_info.append(f"{flanautils.join_last_separator(source_medias_sended_info, ', ', ' y ')} de {source.name}")
|
||||
medias_sended_info.append(f"{flanautils.join_last_separator(source_medias_sended_info, ', ', ' y ')} de {source.name if source else 'algún sitio'}")
|
||||
|
||||
medias_sended_info_joined = flanautils.join_last_separator(medias_sended_info, ',\n', ' y\n')
|
||||
new_line = ' ' if len(medias_sended_info) == 1 else '\n'
|
||||
@@ -292,19 +296,35 @@ class FlanaBot(MultiBot, ABC):
|
||||
|
||||
async def _search_and_send_medias(self, message: Message, send_song_info=False) -> list[Message]:
|
||||
sended_media_messages = []
|
||||
|
||||
if medias := await self._search_medias(message):
|
||||
sended_media_messages, _ = await self.send_medias(medias, message, send_song_info)
|
||||
|
||||
return sended_media_messages
|
||||
|
||||
async def _search_medias(self, message: Message) -> OrderedSet[Media]:
|
||||
results: tuple[OrderedSet[Media] | BaseException, ...] = await asyncio.gather(
|
||||
bot_state_message: Message | None = None
|
||||
start_time = time_module.perf_counter()
|
||||
|
||||
results: Future = asyncio.gather(
|
||||
twitter.get_medias(message.text),
|
||||
instagram.get_medias(message.text),
|
||||
tiktok.get_medias(message.text),
|
||||
return_exceptions=True
|
||||
)
|
||||
results, exceptions = flanautils.filter_exceptions(results)
|
||||
|
||||
if not message.is_inline and (self.is_bot_mentioned(message) or not message.chat.is_group):
|
||||
while not results.done():
|
||||
if constants.SCRAPING_MESSAGE_WAITING_TIME <= time_module.perf_counter() - start_time:
|
||||
bot_state_message = await self.send(random.choice(constants.SCRAPING_PHRASES), message)
|
||||
break
|
||||
await asyncio.sleep(0.1)
|
||||
|
||||
await results
|
||||
if bot_state_message:
|
||||
await self.delete_message(bot_state_message)
|
||||
|
||||
results, exceptions = flanautils.filter_exceptions(results.result())
|
||||
|
||||
await self._manage_exceptions(exceptions, message)
|
||||
|
||||
@@ -445,6 +465,7 @@ class FlanaBot(MultiBot, ABC):
|
||||
return
|
||||
|
||||
if not message_deleted_bot_action:
|
||||
await self.send_error(random.choice(constants.RECOVER_PHRASES), message)
|
||||
return
|
||||
|
||||
affected_object_ids = [affected_message_object_id for affected_message_object_id in message_deleted_bot_action.affected_objects]
|
||||
@@ -452,6 +473,8 @@ class FlanaBot(MultiBot, ABC):
|
||||
for deleted_message in deleted_messages:
|
||||
await self.send(deleted_message.text, message)
|
||||
|
||||
message_deleted_bot_action.delete()
|
||||
|
||||
async def _on_scraping(self, message: Message, delete_original: bool = None) -> OrderedSet[Media]:
|
||||
sended_media_messages = await self._search_and_send_medias(message.replied_message) if message.replied_message else OrderedSet()
|
||||
sended_media_messages += await self._search_and_send_medias(message)
|
||||
@@ -551,23 +574,25 @@ class FlanaBot(MultiBot, ABC):
|
||||
).split()
|
||||
place_words = (
|
||||
OrderedSet(original_text_words)
|
||||
- flanautils.cartesian_product_string_matching(original_text_words, constants.KEYWORDS['show'], min_ratio=0.8).keys()
|
||||
- flanautils.cartesian_product_string_matching(original_text_words, constants.KEYWORDS['weather_chart'], min_ratio=0.8).keys()
|
||||
- flanautils.cartesian_product_string_matching(original_text_words, constants.KEYWORDS['date'], min_ratio=0.8).keys()
|
||||
- flanautils.cartesian_product_string_matching(original_text_words, constants.KEYWORDS['thanks'], min_ratio=0.8).keys()
|
||||
- flanautils.cartesian_product_string_matching(original_text_words, constants.KEYWORDS['show'], min_ratio=0.85).keys()
|
||||
- flanautils.cartesian_product_string_matching(original_text_words, constants.KEYWORDS['weather_chart'], min_ratio=0.85).keys()
|
||||
- flanautils.cartesian_product_string_matching(original_text_words, constants.KEYWORDS['date'], min_ratio=0.85).keys()
|
||||
- flanautils.cartesian_product_string_matching(original_text_words, constants.KEYWORDS['thanks'], min_ratio=0.85).keys()
|
||||
- user_names_with_at_sign
|
||||
- user_names_without_at_sign
|
||||
- flanautils.CommonWords.words
|
||||
)
|
||||
if not place_words:
|
||||
await self.send_error(random.choice(('¿Tiempo dónde?', 'Indica el sitio.', 'Y el sitio?', 'y el sitio? me lo invento?')), message)
|
||||
if not message.is_inline:
|
||||
await self.send_error(random.choice(('¿Tiempo dónde?', 'Indica el sitio.', 'Y el sitio?', 'y el sitio? me lo invento?')), message)
|
||||
return
|
||||
|
||||
if 'calle' in original_text_words:
|
||||
place_words.insert(0, 'calle')
|
||||
place_query = ' '.join(place_words)
|
||||
if len(place_query) >= constants.MAX_PLACE_QUERY_LENGTH:
|
||||
await self.send_error(Media('resources/mucho_texto.png'), message)
|
||||
if not message.is_inline:
|
||||
await self.send_error(Media('resources/mucho_texto.png'), message)
|
||||
return
|
||||
if show_progress_state:
|
||||
bot_state_message = await self.send(f'Buscando "{place_query}" en el mapa 🧐...', message)
|
||||
@@ -758,6 +783,10 @@ class FlanaBot(MultiBot, ABC):
|
||||
sended_info_message = await self.send(f'{message.author.name} compartió{self._medias_sended_info(medias)}', message)
|
||||
|
||||
for media in medias:
|
||||
if not media.content:
|
||||
fails += 1
|
||||
continue
|
||||
|
||||
if media.song_info:
|
||||
message.song_infos.add(media.song_info)
|
||||
message.save()
|
||||
|
||||
@@ -13,6 +13,7 @@ PUNISHMENTS_RESET = datetime.timedelta(weeks=6 * flanautils.WEEKS_IN_A_MONTH)
|
||||
RECOVERY_DELETED_MESSAGE_BEFORE = datetime.timedelta(hours=1)
|
||||
TIME_THRESHOLD_TO_MANUAL_UNMUTE = datetime.timedelta(days=3)
|
||||
TIME_THRESHOLD_TO_MANUAL_UNPUNISH = datetime.timedelta(days=3)
|
||||
SCRAPING_MESSAGE_WAITING_TIME = 0.1
|
||||
|
||||
BAD_PHRASES = (
|
||||
'Cállate ya anda.',
|
||||
@@ -51,8 +52,8 @@ HELLO_PHRASES = ('alo', 'aloh', 'buenas', 'Hola.', 'hello', 'hey', 'hi', 'hola',
|
||||
KEYWORDS = {
|
||||
'activate': ('activa', 'activar', 'activate', 'deja', 'dejale', 'devuelve', 'devuelvele', 'enable', 'encender',
|
||||
'enciende', 'habilita', 'habilitar'),
|
||||
'bye': ('adieu', 'adio', 'adiooooo', 'adios', 'agur', 'buenas', 'bye', 'cama', 'chao', 'dias', 'farewell',
|
||||
'goodbye', 'hasta', 'luego', 'noches', 'pronto', 'taluego', 'taluegorl', 'tenga', 'vemos', 'vista', 'voy'),
|
||||
'bye': ('adieu', 'adio', 'adiooooo', 'adios', 'agur', 'buenas', 'bye', 'cama', 'chao', 'farewell', 'goodbye',
|
||||
'hasta', 'luego', 'noches', 'pronto', 'taluego', 'taluegorl', 'tenga', 'vemos', 'vista', 'voy'),
|
||||
'change': ('alter', 'alternar', 'alternate', 'cambiar', 'change', 'default', 'defecto', 'edit', 'editar',
|
||||
'exchange', 'modificar', 'modify', 'permutar', 'predeterminado', 'shift', 'swap', 'switch', 'turn',
|
||||
'vary'),
|
||||
@@ -83,7 +84,7 @@ KEYWORDS = {
|
||||
'scraping': ('api', 'aqui', 'busca', 'contenido', 'content', 'descarga', 'descargar', 'download', 'envia', 'habia',
|
||||
'media', 'redes', 'scrap', 'scraping', 'search', 'send', 'social', 'sociales', 'tenia', 'video',
|
||||
'videos'),
|
||||
'show': ('actual', 'enseña', 'estado', 'how', 'is', 'muestra', 'show', 'como'),
|
||||
'show': ('actual', 'enseña', 'estado', 'how', 'muestra', 'show', 'como'),
|
||||
'song_info': ('aqui', 'cancion', 'data', 'datos', 'info', 'informacion', 'information', 'llama', 'media', 'name',
|
||||
'nombre', 'sonaba', 'sonando', 'song', 'sono', 'sound', 'suena', 'title', 'titulo',
|
||||
'video'),
|
||||
@@ -95,6 +96,18 @@ KEYWORDS = {
|
||||
'cloudless', 'cloudy', 'cold', 'congelar', 'congelado', 'denbora', 'despejado', 'diluvio', 'frio',
|
||||
'frost', 'hielo', 'humedad', 'llover', 'llueva', 'llueve', 'lluvia', 'nevada', 'nieva', 'nieve',
|
||||
'nube', 'nubes', 'nublado', 'meteorologia', 'rain', 'snow', 'snowfall', 'snowstorm', 'sol',
|
||||
'solano', 'storm', 'sun', 'temperatura', 'tempo', 'tiempo', 'tormenta', 've', 'ventisca',
|
||||
'weather', 'wetter')
|
||||
'solano', 'storm', 'sun', 'temperatura', 'tempo', 'tiempo', 'tormenta', 'ventisca', 'weather',
|
||||
'wetter')
|
||||
}
|
||||
|
||||
RECOVER_PHRASES = (
|
||||
'No hay nada que recuperar.',
|
||||
'Ya lo he recuperado y enviado, así que callate ya.',
|
||||
'Ya lo he recuperado y enviado, así que mejor estás antento antes de dar por culo.',
|
||||
'Ya lo he recuperado y enviado, no lo voy a hacer dos veces.',
|
||||
'Ya lo he recuperado y enviado. A ver si leemos más y jodemos menos.',
|
||||
'Ya lo he reenviado.'
|
||||
)
|
||||
|
||||
SCRAPING_PHRASES = ('Analizando...', 'Buscando...', 'Hackeando internet... 👀', 'Rebuscando en la web...',
|
||||
'Robando cosas...', 'Scrapeando...', 'Scraping...')
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import asyncio
|
||||
import os
|
||||
|
||||
import flanautils
|
||||
|
||||
os.environ |= flanautils.find_environment_variables('../.env')
|
||||
|
||||
import asyncio
|
||||
|
||||
from flanabot.bots.flana_tele_bot import FlanaTeleBot
|
||||
|
||||
|
||||
async def main():
|
||||
os.environ |= flanautils.find_environment_variables('../.env')
|
||||
|
||||
flana_tele_bot = FlanaTeleBot()
|
||||
|
||||
await asyncio.gather(
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
from dataclasses import dataclass
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
from multibot import Chat as MultiBotChat
|
||||
|
||||
from flanabot.models.user import User
|
||||
|
||||
|
||||
@dataclass(eq=False)
|
||||
class Chat(MultiBotChat):
|
||||
@@ -11,6 +13,7 @@ class Chat(MultiBotChat):
|
||||
'auto_delete_original': True,
|
||||
'auto_scraping': True,
|
||||
'auto_weather_chart': True}
|
||||
users: list[User] = field(default_factory=list)
|
||||
|
||||
def __post_init__(self):
|
||||
super().__post_init__()
|
||||
|
||||
@@ -1,14 +1,21 @@
|
||||
from __future__ import annotations # todo0 remove in 3.11
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Iterable
|
||||
|
||||
from flanautils import Media, OrderedSet
|
||||
from multibot import Message as MultiBotMessage
|
||||
|
||||
from flanabot.models.chat import Chat
|
||||
from flanabot.models.user import User
|
||||
from flanabot.models.weather_chart import WeatherChart
|
||||
|
||||
|
||||
@dataclass(eq=False)
|
||||
class Message(MultiBotMessage):
|
||||
author: User = None
|
||||
mentions: Iterable[User] = field(default_factory=list)
|
||||
chat: Chat = None
|
||||
replied_message: Message = None
|
||||
weather_chart: WeatherChart = None
|
||||
song_infos: OrderedSet[Media] = field(default_factory=OrderedSet)
|
||||
|
||||
BIN
requirements.txt
BIN
requirements.txt
Binary file not shown.
0
tests/unit/__init__.py
Normal file
0
tests/unit/__init__.py
Normal file
@@ -1,16 +1,21 @@
|
||||
import os
|
||||
|
||||
import flanautils
|
||||
|
||||
os.environ |= flanautils.find_environment_variables('../.env')
|
||||
|
||||
import unittest
|
||||
from typing import Iterable
|
||||
|
||||
from flanabot import constants
|
||||
import flanautils
|
||||
from flanabot.bots.flana_bots.flana_tele_bot import FlanaTeleBot
|
||||
from multibot import constants as multibot_constants
|
||||
from flanabot.bots.flana_tele_bot import FlanaTeleBot
|
||||
|
||||
|
||||
class TestParseCallbacks(unittest.TestCase):
|
||||
def _test_no_always_callbacks(self, phrases: Iterable[str], callback: callable):
|
||||
for i, phrase in enumerate(phrases):
|
||||
with self.subTest(phrase):
|
||||
callbacks = [registered_callback.callback for registered_callback in self.flana_tele_bot._parse_callbacks(phrase, constants.RATIO_REWARD_EXPONENT, constants.KEYWORDS_LENGHT_PENALTY, constants.MINIMUM_RATIO_TO_MATCH)
|
||||
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)
|
||||
if not registered_callback.always]
|
||||
self.assertEqual(1, len(callbacks))
|
||||
self.assertEqual(callback, callbacks[0], f'\n\nExpected: {callback.__name__}\nActual: {callbacks[0].__name__}')
|
||||
@@ -111,9 +116,9 @@ class TestParseCallbacks(unittest.TestCase):
|
||||
|
||||
def test_on_mute(self):
|
||||
phrases = [
|
||||
'silencia',
|
||||
'silencia al pavo ese',
|
||||
'calla a ese pesao',
|
||||
# 'silencia',
|
||||
# 'silencia al pavo ese',
|
||||
# 'calla a ese pesao',
|
||||
'haz que se calle',
|
||||
'quitale el microfono a ese',
|
||||
'quitale el micro',
|
||||
@@ -124,13 +129,6 @@ class TestParseCallbacks(unittest.TestCase):
|
||||
]
|
||||
self._test_no_always_callbacks(phrases, self.flana_tele_bot._on_mute)
|
||||
|
||||
def test_on_new_message(self):
|
||||
for i in range(10):
|
||||
phrase = flanautils.random_string(0, 30, n_spaces=20)
|
||||
with self.subTest(phrase):
|
||||
callbacks = [registered_callback.callback for registered_callback in self.flana_tele_bot._parse_callbacks(phrase)]
|
||||
self.assertIn(self.flana_tele_bot._on_new_message, callbacks, f'\n\nExpected: {self.flana_tele_bot._on_new_message.__name__} in {callbacks}')
|
||||
|
||||
def test_on_new_message_default(self):
|
||||
phrases = [
|
||||
'asdqwergf',
|
||||
@@ -157,7 +155,6 @@ class TestParseCallbacks(unittest.TestCase):
|
||||
|
||||
def test_on_punish(self):
|
||||
phrases = [
|
||||
'acabo con el',
|
||||
'acaba con el',
|
||||
'destrozalo',
|
||||
'ataca',
|
||||
@@ -169,8 +166,6 @@ class TestParseCallbacks(unittest.TestCase):
|
||||
'castigalo',
|
||||
'castiga a',
|
||||
'castiga',
|
||||
'banealo',
|
||||
'banea',
|
||||
'enseña quien manda'
|
||||
]
|
||||
self._test_no_always_callbacks(phrases, self.flana_tele_bot._on_punish)
|
||||
@@ -249,8 +244,7 @@ class TestParseCallbacks(unittest.TestCase):
|
||||
'perdona a',
|
||||
'illo quitale a @flanagan el castigo',
|
||||
'quita castigo',
|
||||
'devuelve los permisos',
|
||||
'desbanea'
|
||||
'devuelve los permisos'
|
||||
]
|
||||
self._test_no_always_callbacks(phrases, self.flana_tele_bot._on_unpunish)
|
||||
|
||||
@@ -265,6 +259,8 @@ class TestParseCallbacks(unittest.TestCase):
|
||||
'sol',
|
||||
'temperatura',
|
||||
'humedad',
|
||||
'que tiempo hara mañana',
|
||||
'que tiempo hara manana',
|
||||
'que tiempo hace en malaga',
|
||||
'que tiempo hace en calle larios',
|
||||
'tiempo rusia',
|
||||
|
||||
Reference in New Issue
Block a user