16 Commits

Author SHA1 Message Date
AlberLC
a37c2ee1d7 Fix error message when inline weather 2022-01-31 06:22:02 +01:00
AlberLC
449df672b5 Update tests 2022-01-27 03:16:44 +01:00
AlberLC
d3b8c4f821 Update requirements.txt 2022-01-27 03:16:34 +01:00
AlberLC
acbd0e5ad1 Update requirements.txt 2022-01-24 05:01:57 +01:00
AlberLC
a0c693f5eb Improve string parse 2022-01-24 04:23:37 +01:00
AlberLC
5a500e71e3 Update Dockerfile 2022-01-23 01:35:42 +01:00
AlberLC
d6cdc1436f Add docker files 2022-01-21 20:07:46 +01:00
AlberLC
b9e61b296d Fix weather keywords 2022-01-18 22:05:20 +01:00
AlberLC
35583088ab Fix requirements.txt 2022-01-16 19:34:17 +01:00
AlberLC
73f2d4c485 Fix requirements.txt 2022-01-16 18:37:02 +01:00
AlberLC
a8cf321140 Fix requirements.txt encoding 2022-01-16 16:01:41 +01:00
AlberLC
944e473fac Fix .env load 2022-01-16 05:17:38 +01:00
AlberLC
f4ffa40263 Fix send scraping message when inline 2022-01-15 05:54:06 +01:00
AlberLC
296f0d625d Fix on_recover and bot_state_message in search_medias 2022-01-15 03:38:34 +01:00
AlberLC
a1858887de Update README.rst 2022-01-15 03:37:26 +01:00
AlberLC
641b36ee81 Update send_medias 2022-01-12 06:02:04 +01:00
11 changed files with 116 additions and 40 deletions

10
Dockerfile Normal file
View 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

View File

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

View File

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

View File

@@ -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...')

View File

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

View File

@@ -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__()

View File

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

Binary file not shown.

0
tests/unit/__init__.py Normal file
View File

View 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',