40 Commits

Author SHA1 Message Date
AlberLC
0f5d3c333a Fix commands at private 2022-06-23 04:13:36 +02:00
AlberLC
5fb55404cf Fix commands at private 2022-06-23 03:10:49 +02:00
AlberLC
2768d8e949 Fix commands at private 2022-06-23 02:17:59 +02:00
AlberLC
b0ca5a2ded Add ports to db local access 2022-06-23 02:17:33 +02:00
AlberLC
697ba9e89e Add polls 2022-06-22 09:18:42 +02:00
AlberLC
09df75ea0b Update buttons logic (add ButtonsInfo) and add choose and dice functionalities 2022-06-19 13:34:12 +02:00
AlberLC
c2e7e68619 Fix _check_message_flood 2022-06-15 00:46:31 +02:00
AlberLC
f12d0c18c1 Fix insults 2022-06-14 04:41:26 +02:00
AlberLC
b55f933a32 Add new insults 2022-06-14 04:01:16 +02:00
AlberLC
5fa102b157 Fix _on_scraping with replied message 2022-06-12 00:13:33 +02:00
AlberLC
1e8a33c0c4 Improve button registration 2022-06-08 22:05:02 +02:00
AlberLC
daef039ce5 Change _manage_exceptions context arg 2022-06-06 04:22:26 +02:00
AlberLC
7ad4c4052e Remove twitch bot at main 2022-06-06 02:31:16 +02:00
AlberLC
641dc72738 Update requirements.txt 2022-06-04 03:33:59 +02:00
AlberLC
cafa7ce1c5 Fix recover original message 2022-06-04 02:58:32 +02:00
AlberLC
c9a46b2b07 Fix roles mentions in weather 2022-06-01 03:16:45 +02:00
AlberLC
92ee0e405d Fix env variable names 2022-05-31 23:08:36 +02:00
AlberLC
dc6bf7accb Refactor punishments and optimize chat objects 2022-05-31 22:11:16 +02:00
AlberLC
9e5f7a81ff Add user who pressed the button 2022-05-26 05:45:16 +02:00
AlberLC
28ff804d5a Improve discord user name printing 2022-05-25 05:30:09 +02:00
AlberLC
d47fdfb57e Fix bot insults ratio 2022-05-25 04:00:47 +02:00
AlberLC
20fb6e3223 Fix bot insults ratio 2022-05-25 02:55:28 +02:00
AlberLC
952672946b Add Discord bot 2022-05-25 00:46:32 +02:00
AlberLC
7c43db0867 Fix warning at implicit long weather request 2022-03-31 20:06:06 +02:00
AlberLC
f6093ec01e Fix resource file resolution 2022-03-17 03:50:33 +01:00
AlberLC
99d84a5d35 Fix query place with coordinates 2022-02-21 00:58:56 +01:00
AlberLC
02d4965efa Fix CommonWords property name 2022-02-11 21:42:11 +01:00
AlberLC
e41338b5f6 Update workflows 2022-02-04 23:32:43 +01:00
AlberLC
47e371f270 Update README.rst 2022-02-02 01:46:16 +01:00
AlberLC
5746e99b3f Update README.rst 2022-02-02 01:43:17 +01:00
AlberLC
e1c14f7512 Update README.rst 2022-02-02 01:40:47 +01:00
AlberLC
503dfb4215 Update README.rst 2022-02-02 01:40:10 +01:00
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
20 changed files with 589 additions and 540 deletions

View File

@@ -22,10 +22,10 @@ jobs:
- name: Update setup.cfg - name: Update setup.cfg
run: | run: |
sed -i " sed -i "
s/{project_name}/${{ github.event.repository.name }}/g; s|{project_name}|${{ github.event.repository.name }}|g;
s/{project_version}/${{ github.ref_name }}/g; s|{project_version}|${{ github.ref_name }}|g;
s/{author}/${{ github.repository_owner }}/g; s|{author}|${{ github.repository_owner }}|g;
s/{description}/${{ github.event.repository.description }}/g s|{description}|${{ github.event.repository.description }}|g
" setup.cfg " setup.cfg
- name: Build package - name: Build package

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

@@ -3,7 +3,9 @@ FlanaBot
|license| |project_version| |python_version| |license| |project_version| |python_version|
Flanagan's bot. Bot based on `github.com/AlberLC/multibot`_ to manage Discord, Telegram and Twitch chats, moderate them and add functionalities.
|
Installation Installation
------------ ------------
@@ -14,6 +16,22 @@ Python 3.10 or higher is required.
pip install flanabot pip install flanabot
|
Features
--------
- Talks to users.
- Delete message batches.
- It works both in groups and in private chats.
- Understands numbers and amounts of time textually expressed (useful for deleting a message batch or saying "flanabot ban john for one hour and 20 minutes").
- Shows interactive via buttons charts of past, current and forecast weather.
- Change user roles temporarily or forever.
- Mute users temporarily or forever.
- Ban users temporarily or forever.
- Configurable default behavior for each chat, just talk to him to configure it.
- Get media from twitter, instagram and tiktok and send it to the chat. From tiktok also obtains data about the song that is playing in the video.
.. |license| image:: https://img.shields.io/github/license/AlberLC/flanabot?style=flat .. |license| image:: https://img.shields.io/github/license/AlberLC/flanabot?style=flat
:target: https://github.com/AlberLC/flanabot/blob/main/LICENSE :target: https://github.com/AlberLC/flanabot/blob/main/LICENSE
@@ -25,4 +43,6 @@ Python 3.10 or higher is required.
.. |python_version| image:: https://img.shields.io/pypi/pyversions/flanabot .. |python_version| image:: https://img.shields.io/pypi/pyversions/flanabot
:target: https://www.python.org/downloads/ :target: https://www.python.org/downloads/
:alt: PyPI - Python Version :alt: PyPI - Python Version
.. _github.com/AlberLC/multibot: https://github.com/AlberLC/multibot

19
docker-compose.yaml Normal file
View File

@@ -0,0 +1,19 @@
services:
flanabot:
image: flanaganvaquero/flanabot
build: .
env_file:
- .env
mongodb:
image: mongo
ports:
- "27017:27017"
environment:
- MONGO_INITDB_ROOT_USERNAME=${MONGO_USER}
- MONGO_INITDB_ROOT_PASSWORD=${MONGO_PASSWORD}
volumes:
- mongodata:/data/db
volumes:
mongodata:

View File

@@ -1,148 +1,153 @@
__all__ = ['FlanaBot']
import asyncio import asyncio
import datetime import datetime
import itertools
import pprint
import random import random
import time import re
import time as time_module
from abc import ABC from abc import ABC
from asyncio import Future from asyncio import Future
from typing import Iterable, Iterator, Type from typing import Iterable, Sequence
import flanaapis.geolocation.functions import flanaapis.geolocation.functions
import flanaapis.weather.functions import flanaapis.weather.functions
import flanautils import flanautils
import plotly.graph_objects import plotly.graph_objects
import pymongo
from flanaapis import InstagramLoginError, MediaNotFoundError, Place, PlaceNotFoundError, WeatherEmoji, instagram, tiktok, twitter from flanaapis import InstagramLoginError, MediaNotFoundError, Place, PlaceNotFoundError, WeatherEmoji, instagram, tiktok, twitter
from flanautils import Media, MediaType, NotFoundError, OrderedSet, Source, TimeUnits, TraceMetadata, return_if_first_empty from flanautils import Media, MediaType, NotFoundError, OrderedSet, Source, TimeUnits, TraceMetadata, return_if_first_empty
from multibot import Action, BotAction, MultiBot, SendError, admin, bot_mentioned, constants as multibot_constants, group, ignore_self_message, inline, reply from multibot import Action, BadRoleError, BotAction, 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.exceptions import BadRoleError, UserDisconnectedError from flanabot.models import Chat, Message, Punishment, WeatherChart
from flanabot.models.chat import Chat
from flanabot.models.message import Message
from flanabot.models.punishments import Mute, Punishment, PunishmentBase
from flanabot.models.weather_chart import WeatherChart
# ----------------------------------------------------------------------------------------------------- # # ----------------------------------------------------------------------------------------------------- #
# --------------------------------------------- FLANA_BOT --------------------------------------------- # # --------------------------------------------- FLANA_BOT --------------------------------------------- #
# ----------------------------------------------------------------------------------------------------- # # ----------------------------------------------------------------------------------------------------- #
class FlanaBot(MultiBot, ABC): class FlanaBot(MultiBot, ABC):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.lock = asyncio.Lock()
# ----------------------------------------------------------- # # ----------------------------------------------------------- #
# -------------------- PROTECTED METHODS -------------------- # # -------------------- PROTECTED METHODS -------------------- #
# ----------------------------------------------------------- # # ----------------------------------------------------------- #
def _add_handlers(self): def _add_handlers(self):
super()._add_handlers() super()._add_handlers()
self.register(self._on_bye, constants.KEYWORDS['bye'], min_ratio=1) self.register(self._on_bye, multibot_constants.KEYWORDS['bye'])
self.register(self._on_config_list_show, constants.KEYWORDS['config']) self.register(self._on_choose, constants.KEYWORDS['choose'])
self.register(self._on_config_list_show, constants.KEYWORDS['help']) self.register(self._on_choose, constants.KEYWORDS['random'])
self.register(self._on_config_list_show, (constants.KEYWORDS['show'], constants.KEYWORDS['config'])) self.register(self._on_choose, (constants.KEYWORDS['choose'], constants.KEYWORDS['random']))
self.register(self._on_config_list_show, (constants.KEYWORDS['help'], constants.KEYWORDS['config']))
self.register(self._on_config_list_show, (constants.KEYWORDS['show'], constants.KEYWORDS['help'])) self.register(self._on_config_list_show, multibot_constants.KEYWORDS['config'])
self.register(self._on_config_list_show, (constants.KEYWORDS['show'], constants.KEYWORDS['help'], constants.KEYWORDS['config'])) self.register(self._on_config_list_show, (multibot_constants.KEYWORDS['show'], multibot_constants.KEYWORDS['config']))
self.register(self._on_covid_chart, constants.KEYWORDS['covid_chart']) self.register(self._on_covid_chart, constants.KEYWORDS['covid_chart'])
self.register(self._on_currency_chart, constants.KEYWORDS['currency_chart']) self.register(self._on_currency_chart, constants.KEYWORDS['currency_chart'])
self.register(self._on_currency_chart, (constants.KEYWORDS['show'], constants.KEYWORDS['currency_chart'])) self.register(self._on_currency_chart, (multibot_constants.KEYWORDS['show'], constants.KEYWORDS['currency_chart']))
self.register(self._on_currency_chart_config_activate, (constants.KEYWORDS['activate'], constants.KEYWORDS['currency_chart'])) self.register(self._on_currency_chart_config_activate, (multibot_constants.KEYWORDS['activate'], constants.KEYWORDS['currency_chart']))
self.register(self._on_currency_chart_config_activate, (constants.KEYWORDS['activate'], constants.KEYWORDS['currency_chart'], constants.KEYWORDS['config'])) self.register(self._on_currency_chart_config_activate, (multibot_constants.KEYWORDS['activate'], constants.KEYWORDS['currency_chart'], multibot_constants.KEYWORDS['config']))
self.register(self._on_currency_chart_config_change, (constants.KEYWORDS['change'], constants.KEYWORDS['currency_chart'])) self.register(self._on_currency_chart_config_change, (multibot_constants.KEYWORDS['change'], constants.KEYWORDS['currency_chart']))
self.register(self._on_currency_chart_config_change, (constants.KEYWORDS['change'], constants.KEYWORDS['currency_chart'], constants.KEYWORDS['config'])) self.register(self._on_currency_chart_config_change, (multibot_constants.KEYWORDS['change'], constants.KEYWORDS['currency_chart'], multibot_constants.KEYWORDS['config']))
self.register(self._on_currency_chart_config_deactivate, (constants.KEYWORDS['deactivate'], constants.KEYWORDS['currency_chart'])) self.register(self._on_currency_chart_config_deactivate, (multibot_constants.KEYWORDS['deactivate'], constants.KEYWORDS['currency_chart']))
self.register(self._on_currency_chart_config_deactivate, (constants.KEYWORDS['deactivate'], constants.KEYWORDS['currency_chart'], constants.KEYWORDS['config'])) self.register(self._on_currency_chart_config_deactivate, (multibot_constants.KEYWORDS['deactivate'], constants.KEYWORDS['currency_chart'], multibot_constants.KEYWORDS['config']))
self.register(self._on_currency_chart_config_show, (constants.KEYWORDS['currency_chart'], constants.KEYWORDS['config'])) self.register(self._on_currency_chart_config_show, (constants.KEYWORDS['currency_chart'], multibot_constants.KEYWORDS['config']))
self.register(self._on_currency_chart_config_show, (constants.KEYWORDS['show'], constants.KEYWORDS['currency_chart'], constants.KEYWORDS['config'])) self.register(self._on_currency_chart_config_show, (multibot_constants.KEYWORDS['show'], constants.KEYWORDS['currency_chart'], multibot_constants.KEYWORDS['config']))
self.register(self._on_delete_original_config_activate, (constants.KEYWORDS['activate'], multibot_constants.KEYWORDS['delete'])) self.register(self._on_delete_original_config_activate, (multibot_constants.KEYWORDS['activate'], multibot_constants.KEYWORDS['delete']))
self.register(self._on_delete_original_config_activate, (constants.KEYWORDS['activate'], multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['message'])) self.register(self._on_delete_original_config_activate, (multibot_constants.KEYWORDS['activate'], multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['message']))
self.register(self._on_delete_original_config_activate, (constants.KEYWORDS['activate'], multibot_constants.KEYWORDS['delete'], constants.KEYWORDS['config'])) self.register(self._on_delete_original_config_activate, (multibot_constants.KEYWORDS['activate'], multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['config']))
self.register(self._on_delete_original_config_activate, (constants.KEYWORDS['activate'], multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['message'], constants.KEYWORDS['config'])) self.register(self._on_delete_original_config_activate, (multibot_constants.KEYWORDS['activate'], multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['message'], multibot_constants.KEYWORDS['config']))
self.register(self._on_delete_original_config_change, (constants.KEYWORDS['change'], multibot_constants.KEYWORDS['delete'])) self.register(self._on_delete_original_config_change, (multibot_constants.KEYWORDS['change'], multibot_constants.KEYWORDS['delete']))
self.register(self._on_delete_original_config_change, (constants.KEYWORDS['change'], multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['message'])) self.register(self._on_delete_original_config_change, (multibot_constants.KEYWORDS['change'], multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['message']))
self.register(self._on_delete_original_config_change, (constants.KEYWORDS['change'], multibot_constants.KEYWORDS['delete'], constants.KEYWORDS['config'])) self.register(self._on_delete_original_config_change, (multibot_constants.KEYWORDS['change'], multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['config']))
self.register(self._on_delete_original_config_change, (constants.KEYWORDS['change'], multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['message'], constants.KEYWORDS['config'])) self.register(self._on_delete_original_config_change, (multibot_constants.KEYWORDS['change'], multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['message'], multibot_constants.KEYWORDS['config']))
self.register(self._on_delete_original_config_deactivate, (constants.KEYWORDS['deactivate'], multibot_constants.KEYWORDS['delete'])) self.register(self._on_delete_original_config_deactivate, (multibot_constants.KEYWORDS['deactivate'], multibot_constants.KEYWORDS['delete']))
self.register(self._on_delete_original_config_deactivate, (constants.KEYWORDS['deactivate'], multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['message'])) self.register(self._on_delete_original_config_deactivate, (multibot_constants.KEYWORDS['deactivate'], multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['message']))
self.register(self._on_delete_original_config_deactivate, (constants.KEYWORDS['deactivate'], multibot_constants.KEYWORDS['delete'], constants.KEYWORDS['config'])) self.register(self._on_delete_original_config_deactivate, (multibot_constants.KEYWORDS['deactivate'], multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['config']))
self.register(self._on_delete_original_config_deactivate, (constants.KEYWORDS['deactivate'], multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['message'], constants.KEYWORDS['config'])) self.register(self._on_delete_original_config_deactivate, (multibot_constants.KEYWORDS['deactivate'], multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['message'], multibot_constants.KEYWORDS['config']))
self.register(self._on_delete_original_config_show, (constants.KEYWORDS['show'], multibot_constants.KEYWORDS['delete'])) self.register(self._on_delete_original_config_show, (multibot_constants.KEYWORDS['show'], multibot_constants.KEYWORDS['delete']))
self.register(self._on_delete_original_config_show, (multibot_constants.KEYWORDS['delete'], constants.KEYWORDS['config'])) self.register(self._on_delete_original_config_show, (multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['config']))
self.register(self._on_delete_original_config_show, (constants.KEYWORDS['show'], multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['message'])) self.register(self._on_delete_original_config_show, (multibot_constants.KEYWORDS['show'], multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['message']))
self.register(self._on_delete_original_config_show, (constants.KEYWORDS['show'], multibot_constants.KEYWORDS['delete'], constants.KEYWORDS['config'])) self.register(self._on_delete_original_config_show, (multibot_constants.KEYWORDS['show'], multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['config']))
self.register(self._on_delete_original_config_show, (multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['message'], constants.KEYWORDS['config'])) self.register(self._on_delete_original_config_show, (multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['message'], multibot_constants.KEYWORDS['config']))
self.register(self._on_delete_original_config_show, (constants.KEYWORDS['show'], multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['message'], constants.KEYWORDS['config'])) self.register(self._on_delete_original_config_show, (multibot_constants.KEYWORDS['show'], multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['message'], multibot_constants.KEYWORDS['config']))
self.register(self._on_hello, constants.KEYWORDS['hello']) self.register(self._on_dice, constants.KEYWORDS['dice'])
self.register(self._on_mute, constants.KEYWORDS['mute']) self.register(self._on_hello, multibot_constants.KEYWORDS['hello'])
self.register(self._on_mute, (constants.KEYWORDS['deactivate'], constants.KEYWORDS['unmute']))
self.register(self._on_mute, (constants.KEYWORDS['deactivate'], constants.KEYWORDS['sound']))
self.register(self._on_new_message_default, default=True) self.register(self._on_new_message_default, default=True)
self.register(self._on_no_delete_original, (constants.KEYWORDS['negate'], multibot_constants.KEYWORDS['delete'])) self.register(self._on_no_delete_original, (multibot_constants.KEYWORDS['negate'], multibot_constants.KEYWORDS['delete']))
self.register(self._on_no_delete_original, (constants.KEYWORDS['negate'], multibot_constants.KEYWORDS['message'])) self.register(self._on_no_delete_original, (multibot_constants.KEYWORDS['negate'], multibot_constants.KEYWORDS['message']))
self.register(self._on_no_delete_original, (constants.KEYWORDS['negate'], multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['message'])) self.register(self._on_no_delete_original, (multibot_constants.KEYWORDS['negate'], multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['message']))
self.register(self._on_no_delete_original, (constants.KEYWORDS['deactivate'], multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['message'])) self.register(self._on_no_delete_original, (multibot_constants.KEYWORDS['deactivate'], multibot_constants.KEYWORDS['delete'], multibot_constants.KEYWORDS['message']))
self.register(self._on_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, (constants.KEYWORDS['deactivate'], constants.KEYWORDS['unpunish'])) self.register(self._on_punish, (multibot_constants.KEYWORDS['deactivate'], constants.KEYWORDS['unpunish']))
self.register(self._on_punish, (constants.KEYWORDS['deactivate'], constants.KEYWORDS['permission'])) self.register(self._on_punish, (multibot_constants.KEYWORDS['deactivate'], multibot_constants.KEYWORDS['permission']))
self.register(self._on_recover_message, (constants.KEYWORDS['reset'], multibot_constants.KEYWORDS['message'])) self.register(self._on_recover_message, (multibot_constants.KEYWORDS['reset'], multibot_constants.KEYWORDS['message']))
self.register(self._on_scraping, constants.KEYWORDS['scraping']) self.register(self._on_scraping, constants.KEYWORDS['scraping'])
self.register(self._on_scraping_config_activate, (constants.KEYWORDS['activate'], constants.KEYWORDS['scraping'])) self.register(self._on_scraping_config_activate, (multibot_constants.KEYWORDS['activate'], constants.KEYWORDS['scraping']))
self.register(self._on_scraping_config_activate, (constants.KEYWORDS['activate'], constants.KEYWORDS['scraping'], constants.KEYWORDS['config'])) self.register(self._on_scraping_config_activate, (multibot_constants.KEYWORDS['activate'], constants.KEYWORDS['scraping'], multibot_constants.KEYWORDS['config']))
self.register(self._on_scraping_config_change, (constants.KEYWORDS['change'], constants.KEYWORDS['scraping'])) self.register(self._on_scraping_config_change, (multibot_constants.KEYWORDS['change'], constants.KEYWORDS['scraping']))
self.register(self._on_scraping_config_change, (constants.KEYWORDS['change'], constants.KEYWORDS['scraping'], constants.KEYWORDS['config'])) self.register(self._on_scraping_config_change, (multibot_constants.KEYWORDS['change'], constants.KEYWORDS['scraping'], multibot_constants.KEYWORDS['config']))
self.register(self._on_scraping_config_deactivate, (constants.KEYWORDS['deactivate'], constants.KEYWORDS['scraping'])) self.register(self._on_scraping_config_deactivate, (multibot_constants.KEYWORDS['deactivate'], constants.KEYWORDS['scraping']))
self.register(self._on_scraping_config_deactivate, (constants.KEYWORDS['deactivate'], constants.KEYWORDS['scraping'], constants.KEYWORDS['config'])) self.register(self._on_scraping_config_deactivate, (multibot_constants.KEYWORDS['deactivate'], constants.KEYWORDS['scraping'], multibot_constants.KEYWORDS['config']))
self.register(self._on_scraping_config_show, (constants.KEYWORDS['show'], constants.KEYWORDS['scraping'])) self.register(self._on_scraping_config_show, (multibot_constants.KEYWORDS['show'], constants.KEYWORDS['scraping']))
self.register(self._on_scraping_config_show, (constants.KEYWORDS['scraping'], constants.KEYWORDS['config'])) self.register(self._on_scraping_config_show, (constants.KEYWORDS['scraping'], multibot_constants.KEYWORDS['config']))
self.register(self._on_scraping_config_show, (constants.KEYWORDS['show'], constants.KEYWORDS['scraping'], constants.KEYWORDS['config'])) self.register(self._on_scraping_config_show, (multibot_constants.KEYWORDS['show'], constants.KEYWORDS['scraping'], multibot_constants.KEYWORDS['config']))
self.register(self._on_song_info, constants.KEYWORDS['song_info']) self.register(self._on_song_info, constants.KEYWORDS['song_info'])
self.register(self._on_unmute, constants.KEYWORDS['unmute']) self.register(self._on_stop_poll, multibot_constants.KEYWORDS['deactivate'])
self.register(self._on_unmute, (constants.KEYWORDS['deactivate'], constants.KEYWORDS['mute'])) self.register(self._on_stop_poll, (multibot_constants.KEYWORDS['deactivate'], constants.KEYWORDS['poll']))
self.register(self._on_unmute, (constants.KEYWORDS['activate'], constants.KEYWORDS['sound'])) 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, (constants.KEYWORDS['deactivate'], constants.KEYWORDS['punish'])) self.register(self._on_unpunish, (multibot_constants.KEYWORDS['deactivate'], constants.KEYWORDS['punish']))
self.register(self._on_unpunish, (constants.KEYWORDS['activate'], constants.KEYWORDS['permission'])) self.register(self._on_unpunish, (multibot_constants.KEYWORDS['activate'], multibot_constants.KEYWORDS['permission']))
self.register(self._on_weather_chart, constants.KEYWORDS['weather_chart']) self.register(self._on_weather_chart, constants.KEYWORDS['weather_chart'])
self.register(self._on_weather_chart, (constants.KEYWORDS['show'], constants.KEYWORDS['weather_chart'])) self.register(self._on_weather_chart, (multibot_constants.KEYWORDS['show'], constants.KEYWORDS['weather_chart']))
self.register(self._on_weather_chart_config_activate, (constants.KEYWORDS['activate'], constants.KEYWORDS['weather_chart'])) self.register(self._on_weather_chart_config_activate, (multibot_constants.KEYWORDS['activate'], constants.KEYWORDS['weather_chart']))
self.register(self._on_weather_chart_config_activate, (constants.KEYWORDS['activate'], constants.KEYWORDS['weather_chart'], constants.KEYWORDS['config'])) self.register(self._on_weather_chart_config_activate, (multibot_constants.KEYWORDS['activate'], constants.KEYWORDS['weather_chart'], multibot_constants.KEYWORDS['config']))
self.register(self._on_weather_chart_config_change, (constants.KEYWORDS['change'], constants.KEYWORDS['weather_chart'])) self.register(self._on_weather_chart_config_change, (multibot_constants.KEYWORDS['change'], constants.KEYWORDS['weather_chart']))
self.register(self._on_weather_chart_config_change, (constants.KEYWORDS['change'], constants.KEYWORDS['weather_chart'], constants.KEYWORDS['config'])) self.register(self._on_weather_chart_config_change, (multibot_constants.KEYWORDS['change'], constants.KEYWORDS['weather_chart'], multibot_constants.KEYWORDS['config']))
self.register(self._on_weather_chart_config_deactivate, (constants.KEYWORDS['deactivate'], constants.KEYWORDS['weather_chart'])) self.register(self._on_weather_chart_config_deactivate, (multibot_constants.KEYWORDS['deactivate'], constants.KEYWORDS['weather_chart']))
self.register(self._on_weather_chart_config_deactivate, (constants.KEYWORDS['deactivate'], constants.KEYWORDS['weather_chart'], constants.KEYWORDS['config'])) self.register(self._on_weather_chart_config_deactivate, (multibot_constants.KEYWORDS['deactivate'], constants.KEYWORDS['weather_chart'], multibot_constants.KEYWORDS['config']))
self.register(self._on_weather_chart_config_show, (constants.KEYWORDS['weather_chart'], constants.KEYWORDS['config'])) self.register(self._on_weather_chart_config_show, (constants.KEYWORDS['weather_chart'], multibot_constants.KEYWORDS['config']))
self.register(self._on_weather_chart_config_show, (constants.KEYWORDS['show'], constants.KEYWORDS['weather_chart'], constants.KEYWORDS['config'])) self.register(self._on_weather_chart_config_show, (multibot_constants.KEYWORDS['show'], constants.KEYWORDS['weather_chart'], multibot_constants.KEYWORDS['config']))
self.register_button(self._on_config_button_press, ButtonsGroup.CONFIG)
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:
@@ -156,81 +161,36 @@ class FlanaBot(MultiBot, ABC):
@admin(False) @admin(False)
@group @group
async def _check_message_flood(self, message: Message): async def _check_message_flood(self, message: Message):
if message.author.is_punished: if await self.is_punished(message.author, message.chat):
return return
last_2s_messages = Message.find({ last_2s_messages = Message.find({
'platform': self.platform.value,
'author': message.author.object_id, 'author': message.author.object_id,
'last_update': { 'last_update': {'$gte': datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(seconds=2)}
'$gte': datetime.datetime.now() - datetime.timedelta(seconds=2)
}
}) })
last_7s_messages = Message.find({ last_7s_messages = Message.find({
'platform': self.platform.value,
'author': message.author.object_id, 'author': message.author.object_id,
'last_update': { 'last_update': {'$gte': datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(seconds=7)}
'$gte': datetime.datetime.now() - datetime.timedelta(seconds=7),
'$lt': datetime.datetime.now() - 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({'user_id': message.author.id, 'group_id': message.chat.group_id})) n_punishments = len(Punishment.find({
'platform': self.platform.value,
'user_id': message.author.id,
'group_id': message.chat.group_id
}))
punishment_seconds = (n_punishments + 2) ** constants.PUNISHMENT_INCREMENT_EXPONENT punishment_seconds = (n_punishments + 2) ** constants.PUNISHMENT_INCREMENT_EXPONENT
try: try:
await self.punish(message.author.id, message.chat.group_id, punishment_seconds, message) await self.punish(message.author.id, message.chat.group_id, punishment_seconds, message)
except BadRoleError as e: except BadRoleError as e:
await self._manage_exceptions(e, message) await self._manage_exceptions(e, message)
else: else:
# noinspection PyTypeChecker await self.send(f'Castigado durante {TimeUnits(seconds=punishment_seconds).to_words()}.', message)
Punishment(message.author.id, message.author.group_id, datetime.datetime.now() + datetime.timedelta(punishment_seconds)).save()
await self.send(f'Castigado durante {TimeUnits(punishment_seconds).to_words()}.', message)
@staticmethod
async def _check_messages():
Message.collection.delete_many({'last_update': {'$lte': datetime.datetime.now() - multibot_constants.MESSAGE_EXPIRATION_TIME}})
async def _check_mutes(self):
mute_groups = self._get_grouped_punishments(Mute)
now = datetime.datetime.now()
for (user_id, group_id), sorted_mutes in mute_groups:
if (last_mute := sorted_mutes[-1]).until <= now:
await self.unmute(user_id, group_id)
last_mute.delete()
async def _check_punishments(self):
punishment_groups = self._get_grouped_punishments(Punishment)
now = datetime.datetime.now()
for (user_id, group_id), sorted_punishments in punishment_groups:
if now < (last_punishment := sorted_punishments[-1]).until:
continue
if last_punishment.until + constants.PUNISHMENTS_RESET <= now:
for old_punishment in sorted_punishments:
old_punishment.delete()
if last_punishment.is_active:
await self.unpunish(user_id, group_id)
last_punishment.is_active = False
last_punishment.save()
async def _create_empty_message(self) -> Message:
return Message()
@staticmethod
def _get_grouped_punishments(PunishmentClass: Type[PunishmentBase]) -> tuple[tuple[tuple[int, int], list[PunishmentBase]]]:
sorted_punishments = PunishmentClass.find(sort_keys=('user_id', 'group_id', 'until'))
group_iterator: Iterator[
tuple[
tuple[int, int],
Iterator[PunishmentClass]
]
] = itertools.groupby(sorted_punishments, key=lambda punishment: (punishment.user_id, punishment.group_id))
return tuple(((user_id, group_id), list(group_)) for (user_id, group_id), group_ in group_iterator)
@return_if_first_empty(exclude_self_types='FlanaBot', globals_=globals()) @return_if_first_empty(exclude_self_types='FlanaBot', globals_=globals())
async def _manage_exceptions(self, exceptions: BaseException | Iterable[BaseException], message: Message): async def _manage_exceptions(self, exceptions: BaseException | Iterable[BaseException], context: Chat | Message):
if not isinstance(exceptions, Iterable): if not isinstance(exceptions, Iterable):
exceptions = (exceptions,) exceptions = (exceptions,)
@@ -238,15 +198,15 @@ class FlanaBot(MultiBot, ABC):
try: try:
raise exception raise exception
except BadRoleError as e: except BadRoleError as e:
await self.send_error(f'Rol no encontrado en {e}', message) await self.send_error(f'Rol no encontrado en {e}', context)
except InstagramLoginError as e: except InstagramLoginError as e:
await self.send_error(f'No me puedo loguear en Instagram {random.choice(multibot_constants.SAD_EMOJIS)} 👉 {e}', message) await self.send_error(f'No me puedo loguear en Instagram {random.choice(multibot_constants.SAD_EMOJIS)} 👉 {e}', context)
except MediaNotFoundError as e: except MediaNotFoundError as e:
await self.send_error(f'No he podido sacar nada de {e.source} {random.choice(multibot_constants.SAD_EMOJIS)}', message) await self.send_error(f'No he podido sacar nada de {e.source} {random.choice(multibot_constants.SAD_EMOJIS)}', context)
except PlaceNotFoundError as e: except PlaceNotFoundError as e:
await self.send_error(f'No he podido encontrar "{e}" {random.choice(multibot_constants.SAD_EMOJIS)}', message) await self.send_error(f'No he podido encontrar "{e}" {random.choice(multibot_constants.SAD_EMOJIS)}', context)
except Exception as e: except Exception as e:
await super()._manage_exceptions(e, message) await super()._manage_exceptions(e, context)
@staticmethod @staticmethod
def _medias_sended_info(medias: Iterable[Media]) -> str: def _medias_sended_info(medias: Iterable[Media]) -> str:
@@ -287,10 +247,7 @@ class FlanaBot(MultiBot, ABC):
new_line = ' ' if len(medias_sended_info) == 1 else '\n' new_line = ' ' if len(medias_sended_info) == 1 else '\n'
return f'{new_line}{medias_sended_info_joined}:' return f'{new_line}{medias_sended_info_joined}:'
async def _mute(self, user_id: int, group_id: int): async def _punish(self, user: int | str | User, group_: int | str | Chat | Message, message: Message = None):
pass
async def _punish(self, user_id: int, group_id: int):
pass pass
async def _search_and_send_medias(self, message: Message, send_song_info=False) -> list[Message]: async def _search_and_send_medias(self, message: Message, send_song_info=False) -> list[Message]:
@@ -303,7 +260,7 @@ class FlanaBot(MultiBot, ABC):
async def _search_medias(self, message: Message) -> OrderedSet[Media]: async def _search_medias(self, message: Message) -> OrderedSet[Media]:
bot_state_message: Message | None = None bot_state_message: Message | None = None
start_time = time.perf_counter() start_time = time_module.perf_counter()
results: Future = asyncio.gather( results: Future = asyncio.gather(
twitter.get_medias(message.text), twitter.get_medias(message.text),
@@ -312,9 +269,9 @@ 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.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)
break break
await asyncio.sleep(0.1) await asyncio.sleep(0.1)
@@ -332,27 +289,45 @@ class FlanaBot(MultiBot, ABC):
async def _show_config(self, config_name: str, message: Message): async def _show_config(self, config_name: str, message: Message):
await self.send(f"{config_name} está {'activado ✔' if message.chat.config.get(config_name) else 'desactivado ❌'}", message) await self.send(f"{config_name} está {'activado ✔' if message.chat.config.get(config_name) else 'desactivado ❌'}", message)
async def _unmute(self, user_id: int, group_id: int): async def _unpunish(self, user: int | str | User, group_: int | str | Chat | Message, message: Message = None):
pass
async def _unpunish(self, user_id: int, group_id: int):
pass pass
# ---------------------------------------------- # # ---------------------------------------------- #
# HANDLERS # # HANDLERS #
# ---------------------------------------------- # # ---------------------------------------------- #
async def _on_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_choose(self, message: Message):
if message.chat.is_group and not self.is_bot_mentioned(message):
return
discarded_words = {*constants.KEYWORDS['choose'], *constants.KEYWORDS['random'], self.name, f'<@{self.id}>'}
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)]:
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]
pressed_button = message.buttons_info[message.buttons_info.pressed_text]
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):
config_info = pprint.pformat(message.chat.config) buttons_texts = [(f"{'' if v else ''} {k}", v) for k, v in message.chat.config.items()]
config_info = flanautils.translate(config_info, {'{': None, '}': None, ',': None, 'True': '', "'": None, 'False': ''}) await self.send('<b>Estos son los ajustes del grupo:</b>\n\n', flanautils.chunks(buttons_texts, 3), message, buttons_key=ButtonsGroup.CONFIG)
config_info = config_info.splitlines()
config_info = '\n'.join(config_info_.strip() for config_info_ in config_info)
await self.send(f'<b>Estos son los ajustes del grupo:</b>\n\n<code>{config_info}</code>', message)
async def _on_covid_chart(self, message: Message): # todo2 async def _on_covid_chart(self, message: Message): # todo2
pass pass
@@ -408,74 +383,148 @@ 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_hello(self, message: Message): async def _on_dice(self, message: Message):
if not message.chat.is_group or self.is_bot_mentioned(message): if message.chat.is_group and not self.is_bot_mentioned(message):
await self.send_hello(message) return
@group if top_number := flanautils.sum_numbers_in_text(message.text):
@bot_mentioned await self.send(random.randint(1, top_number), message)
@admin(send_negative=True) else:
async def _on_mute(self, message: Message): await self.send(random.choice(('¿De cuántas caras?', '¿Y el número?', '?', '🤔')), message)
await self._update_punishment(self.mute, message, time=flanautils.words_to_time(message.text))
async def _on_hello(self, message: Message):
if message.chat.is_private or self.is_bot_mentioned(message):
await self.send_hello(message)
async def _on_new_message_default(self, message: Message): async def _on_new_message_default(self, message: Message):
if message.is_inline: if message.is_inline:
await self._on_scraping(message) await self._on_scraping(message)
elif ( elif (
message.chat.is_group (
message.chat.is_group
and
not self.is_bot_mentioned(message)
and
not message.chat.config['auto_scraping']
or
not await self._on_scraping(message)
)
and and
not self.is_bot_mentioned(message) (
and message.author.id != self.owner_id
not message.chat.config['auto_scraping'] and
or (
not await self._on_scraping(message) self.is_bot_mentioned(message)
or
(
message.chat.config['auto_insult']
and
random.random() < constants.INSULT_PROBABILITY
)
)
)
): ):
await self.send_bad_phrase(message) await self.send_insult(message)
@ignore_self_message @ignore_self_message
async def _on_new_message_raw(self, message: Message): async def _on_new_message_raw(self, message: Message):
await super()._on_new_message_raw(message) await super()._on_new_message_raw(message)
if not message.is_inline: if not message.is_inline:
await self._check_message_flood(message) async with self.lock:
await self._check_message_flood(message)
async def _on_no_delete_original(self, message: Message): async def _on_no_delete_original(self, message: Message):
if not await self._on_scraping(message, delete_original=False): if not await self._on_scraping(message, delete_original=False):
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)
async def _on_punish(self, message: Message): async def _on_punish(self, message: Message):
await self._update_punishment(self.punish, message, time=flanautils.words_to_time(message.text)) for user in await self._find_users_to_punish(message):
await self.punish(user, message, flanautils.words_to_time(message.text), message)
async def _on_ready(self): async def _on_ready(self):
await super()._on_ready() await super()._on_ready()
await flanautils.do_every(constants.CHECK_MUTES_EVERY_SECONDS, self._check_mutes) await flanautils.do_every(constants.CHECK_PUNISHMENTS_EVERY_SECONDS, Punishment.check_olds, self._unpunish, self.platform)
await flanautils.do_every(constants.CHECK_MESSAGE_EVERY_SECONDS, self._check_messages)
await flanautils.do_every(constants.CHECK_PUNISHMENTS_EVERY_SECONDS, self._check_punishments)
@inline(False) @inline(False)
async def _on_recover_message(self, message: Message): async def _on_recover_message(self, message: Message):
if message.replied_message: if message.replied_message:
message_deleted_bot_action = BotAction.find_one({'action': bytes(Action.MESSAGE_DELETED), 'chat': message.chat.object_id, 'affected_objects': message.replied_message.object_id}) message_deleted_bot_action = BotAction.find_one({'action': Action.MESSAGE_DELETED.value, 'chat': message.chat.object_id, 'affected_objects': message.replied_message.object_id})
elif self.is_bot_mentioned(message): elif self.is_bot_mentioned(message):
message_deleted_bot_action = BotAction.find_one({'action': bytes(Action.MESSAGE_DELETED), 'chat': message.chat.object_id, 'date': {'$gt': datetime.datetime.now() - constants.RECOVERY_DELETED_MESSAGE_BEFORE}}) message_deleted_bot_action = BotAction.find_one({
'action': Action.MESSAGE_DELETED.value,
'chat': message.chat.object_id,
'date': {'$gt': datetime.datetime.now(datetime.timezone.utc) - constants.RECOVERY_DELETED_MESSAGE_BEFORE}
}, sort_keys=(('date', pymongo.DESCENDING),))
else: else:
return return
if not message_deleted_bot_action: if not message_deleted_bot_action:
await self.send_error(random.choice(constants.RECOVER_PHRASES), message) await self.send_error('No hay nada que recuperar.', message)
return return
affected_object_ids = [affected_message_object_id for affected_message_object_id in message_deleted_bot_action.affected_objects] affected_object_ids = [affected_message_object_id for affected_message_object_id in message_deleted_bot_action.affected_objects]
deleted_messages: list[Message] = [affected_message for affected_object_id in affected_object_ids if (affected_message := Message.find_one({'_id': affected_object_id})).author.id != self.bot_id] deleted_messages: list[Message] = [affected_message for affected_object_id in affected_object_ids if (affected_message := Message.find_one({'platform': self.platform.value, '_id': affected_object_id})).author.id != self.id]
for deleted_message in deleted_messages: for deleted_message in deleted_messages:
await self.send(deleted_message.text, message) await self.send(deleted_message.text, message)
message_deleted_bot_action.delete()
async def _on_scraping(self, message: Message, delete_original: bool = None) -> OrderedSet[Media]: async def _on_scraping(self, message: Message, delete_original: bool = None) -> OrderedSet[Media]:
sended_media_messages = await self._search_and_send_medias(message.replied_message) if message.replied_message else OrderedSet() sended_media_messages = OrderedSet()
if message.replied_message:
word_matches = flanautils.cartesian_product_string_matching(message.text, constants.KEYWORDS['scraping'], min_ratio=multibot_constants.PARSE_CALLBACKS_MIN_RATIO_DEFAULT)
if sum(max(matches.values()) for matches in word_matches.values()):
sended_media_messages += await self._search_and_send_medias(message.replied_message)
sended_media_messages += await self._search_and_send_medias(message) sended_media_messages += await self._search_and_send_medias(message)
await self.send_inline_results(message) await self.send_inline_results(message)
@@ -488,6 +537,8 @@ class FlanaBot(MultiBot, ABC):
( (
delete_original is None delete_original is None
and and
not message.replied_message
and
message.chat.config['auto_delete_original'] message.chat.config['auto_delete_original']
) )
or or
@@ -535,20 +586,81 @@ 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)
@group async def _on_stop_poll(self, message: Message):
@bot_mentioned if poll_message := message.replied_message:
@admin(send_negative=True) if poll_message.contents.get('poll') is None:
async def _on_unmute(self, message: Message): return
await self._update_punishment(self.unmute, message) 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)
async def _on_unpunish(self, message: Message): async def _on_unpunish(self, message: Message):
await self._update_punishment(self.unpunish, message) for user in await self._find_users_to_punish(message):
await self.unpunish(user, message, message)
async def _on_weather_button_press(self, message: Message):
await self._accept_button_event(message)
match message.buttons_info.pressed_text:
case WeatherEmoji.ZOOM_IN.value:
message.weather_chart.zoom_in()
case WeatherEmoji.ZOOM_OUT.value:
message.weather_chart.zoom_out()
case WeatherEmoji.LEFT.value:
message.weather_chart.move_left()
case WeatherEmoji.RIGHT.value:
message.weather_chart.move_right()
case WeatherEmoji.PRECIPITATION_VOLUME.value:
message.weather_chart.trace_metadatas['rain_volume'].show = not message.weather_chart.trace_metadatas['rain_volume'].show
message.weather_chart.trace_metadatas['snow_volume'].show = not message.weather_chart.trace_metadatas['snow_volume'].show
case emoji if emoji in WeatherEmoji.values:
trace_metadata_name = WeatherEmoji(emoji).name.lower()
message.weather_chart.trace_metadatas[trace_metadata_name].show = not message.weather_chart.trace_metadatas[trace_metadata_name].show
case _:
return
message.weather_chart.apply_zoom()
message.weather_chart.draw()
image_bytes = message.weather_chart.to_image()
await self.edit(Media(image_bytes, MediaType.IMAGE), message)
async def _on_weather_chart(self, message: Message): async def _on_weather_chart(self, message: Message):
bot_state_message: Message | None = None bot_state_message: Message | None = None
@@ -556,7 +668,7 @@ class FlanaBot(MultiBot, ABC):
show_progress_state = False show_progress_state = False
elif message.chat.is_group and not self.is_bot_mentioned(message): elif message.chat.is_group and not self.is_bot_mentioned(message):
if message.chat.config['auto_weather_chart']: if message.chat.config['auto_weather_chart']:
if BotAction.find_one({'action': bytes(Action.AUTO_WEATHER_CHART), 'chat': message.chat.object_id, 'date': {'$gt': datetime.datetime.now() - constants.AUTO_WEATHER_EVERY}}): if BotAction.find_one({'action': Action.AUTO_WEATHER_CHART.value, 'chat': message.chat.object_id, 'date': {'$gt': datetime.datetime.now(datetime.timezone.utc) - constants.AUTO_WEATHER_EVERY}}):
return return
show_progress_state = False show_progress_state = False
else: else:
@@ -564,32 +676,42 @@ class FlanaBot(MultiBot, ABC):
else: else:
show_progress_state = True show_progress_state = True
user_names_with_at_sign = {user.name.lower() for user in message.chat.users} possible_mentioned_ids = []
user_names_without_at_sign = {user.name.lower().replace('@', '') for user in message.chat.users} for user in message.mentions:
possible_mentioned_ids.append(user.name.lower())
possible_mentioned_ids.append(user.name.split('#')[0].lower())
possible_mentioned_ids.append(f'@{user.id}')
if roles := await self.get_roles(message):
for role in roles:
possible_mentioned_ids.append(f'@{role.id}')
original_text_words = flanautils.remove_accents(message.text.lower()) original_text_words = flanautils.remove_accents(message.text.lower())
original_text_words = original_text_words.replace(',', ' ').replace(';', ' ').replace('-', ' -')
original_text_words = flanautils.translate( original_text_words = flanautils.translate(
original_text_words, original_text_words,
{symbol: None for symbol in set(flanautils.SYMBOLS) - {'-', '.'}} {symbol: None for symbol in set(flanautils.SYMBOLS) - {'-', '.'}}
).split() ).split()
place_words = ( place_words = (
OrderedSet(original_text_words) OrderedSet(original_text_words)
- flanautils.cartesian_product_string_matching(original_text_words, constants.KEYWORDS['show'], min_ratio=0.8).keys() - flanautils.cartesian_product_string_matching(original_text_words, multibot_constants.KEYWORDS['show'], min_ratio=0.85).keys()
- flanautils.cartesian_product_string_matching(original_text_words, constants.KEYWORDS['weather_chart'], min_ratio=0.8).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.8).keys() - flanautils.cartesian_product_string_matching(original_text_words, multibot_constants.KEYWORDS['date'], min_ratio=0.85).keys()
- flanautils.cartesian_product_string_matching(original_text_words, constants.KEYWORDS['thanks'], min_ratio=0.8).keys() - flanautils.cartesian_product_string_matching(original_text_words, multibot_constants.KEYWORDS['thanks'], min_ratio=0.85).keys()
- user_names_with_at_sign - possible_mentioned_ids
- user_names_without_at_sign - flanautils.CommonWords.all_words
- flanautils.CommonWords.words
) )
if not place_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 return
if 'calle' in original_text_words: if 'calle' in original_text_words:
place_words.insert(0, 'calle') place_words.insert(0, 'calle')
place_query = ' '.join(place_words) place_query = ' '.join(place_words)
if len(place_query) >= constants.MAX_PLACE_QUERY_LENGTH: if len(place_query) >= constants.MAX_PLACE_QUERY_LENGTH:
await self.send_error(Media('resources/mucho_texto.png'), message) if show_progress_state:
await self.send_error(Media(str(flanautils.resolve_path('resources/mucho_texto.png')), MediaType.IMAGE, Source.LOCAL), message, send_as_file=False)
return return
if show_progress_state: if show_progress_state:
bot_state_message = await self.send(f'Buscando "{place_query}" en el mapa 🧐...', message) bot_state_message = await self.send(f'Buscando "{place_query}" en el mapa 🧐...', message)
@@ -669,6 +791,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)
@@ -710,55 +833,13 @@ class FlanaBot(MultiBot, ABC):
# -------------------------------------------------------- # # -------------------------------------------------------- #
# -------------------- PUBLIC METHODS -------------------- # # -------------------- PUBLIC METHODS -------------------- #
# -------------------------------------------------------- # # -------------------------------------------------------- #
async def is_deaf(self, user_id: int, group_id: int) -> bool: async def is_punished(self, user: int | str | User, group_: int | str | Chat | Message) -> bool:
pass pass
async def is_muted(self, user_id: int, group_id: int) -> bool: async def punish(self, user: int | str | User, group_: int | str | Chat | Message, time: int | datetime.timedelta, message: Message = None):
pass # noinspection PyTypeChecker
punish = Punishment(self.platform, self.get_user_id(user), self.get_group_id(group_), time)
async def mute(self, user_id: int, group_id: int, time: int | datetime.timedelta, message: Message = None): await punish.punish(self._punish, self._unpunish, message)
if isinstance(time, int):
time = datetime.timedelta(seconds=time)
try:
await self._mute(user_id, group_id)
except UserDisconnectedError as e:
await self._manage_exceptions(e, message if message else Message(chat=Chat(group_id=group_id)))
else:
if time:
# noinspection PyTypeChecker
Mute(user_id, group_id, until=datetime.datetime.now() + time).save()
if datetime.timedelta() < time <= constants.TIME_THRESHOLD_TO_MANUAL_UNMUTE:
await flanautils.do_later(time, self._check_mutes)
else:
# noinspection PyTypeChecker
Mute(user_id, group_id).save()
async def punish(self, user_id: int, group_id: int, time: int | datetime.timedelta, message: Message = None):
if isinstance(time, int):
time = datetime.timedelta(seconds=time)
try:
await self._punish(user_id, group_id)
except BadRoleError as e:
await self._manage_exceptions(e, message if message else Message(chat=Chat(group_id=group_id)))
else:
if time:
# noinspection PyTypeChecker
Punishment(user_id, group_id, until=datetime.datetime.now() + time).save()
if datetime.timedelta() < time <= constants.TIME_THRESHOLD_TO_MANUAL_UNPUNISH:
await flanautils.do_later(time, self._check_punishments)
else:
# noinspection PyTypeChecker
Punishment(user_id, group_id).save()
async def send_bad_phrase(self, message: Message, probability=0.00166666667) -> multibot_constants.ORIGINAL_MESSAGE | None:
if not self.is_bot_mentioned(message) and random.random() >= probability or message.author.id == self.owner_id:
return
await self.typing_delay(message)
return await self.send(random.choice(constants.BAD_PHRASES), message)
async def send_bye(self, message: Message) -> multibot_constants.ORIGINAL_MESSAGE: async def send_bye(self, message: Message) -> multibot_constants.ORIGINAL_MESSAGE:
return await self.send(random.choice((*constants.BYE_PHRASES, flanautils.CommonWords.random_time_greeting())), message) return await self.send(random.choice((*constants.BYE_PHRASES, flanautils.CommonWords.random_time_greeting())), message)
@@ -766,6 +847,10 @@ class FlanaBot(MultiBot, ABC):
async def send_hello(self, message: Message) -> multibot_constants.ORIGINAL_MESSAGE: async def send_hello(self, message: Message) -> multibot_constants.ORIGINAL_MESSAGE:
return await self.send(random.choice((*constants.HELLO_PHRASES, flanautils.CommonWords.random_time_greeting())), message) return await self.send(random.choice((*constants.HELLO_PHRASES, flanautils.CommonWords.random_time_greeting())), message)
async def send_insult(self, message: Message) -> multibot_constants.ORIGINAL_MESSAGE | None:
await self.typing_delay(message)
return await self.send(random.choice(constants.INSULTS), message)
@return_if_first_empty(exclude_self_types='FlanaBot', globals_=globals()) @return_if_first_empty(exclude_self_types='FlanaBot', globals_=globals())
async def send_medias(self, medias: OrderedSet[Media], message: Message, send_song_info=False) -> tuple[list[Message], int]: async def send_medias(self, medias: OrderedSet[Media], message: Message, send_song_info=False) -> tuple[list[Message], int]:
sended_media_messages = [] sended_media_messages = []
@@ -777,7 +862,7 @@ class FlanaBot(MultiBot, ABC):
bot_state_message: Message = await self.send('Descargando...', message) bot_state_message: Message = await self.send('Descargando...', message)
if message.chat.is_group: if message.chat.is_group:
sended_info_message = await self.send(f'{message.author.name} compartió{self._medias_sended_info(medias)}', message) sended_info_message = await self.send(f"{message.author.name.split('#')[0]} compartió{self._medias_sended_info(medias)}", message)
for media in medias: for media in medias:
if not media.content: if not media.content:
@@ -788,10 +873,7 @@ class FlanaBot(MultiBot, ABC):
message.song_infos.add(media.song_info) message.song_infos.add(media.song_info)
message.save() message.save()
try: if not (bot_message := await self.send(media, message)):
bot_message = await self.send(media, message)
except SendError as e:
await self._manage_exceptions(e, message)
fails += 1 fails += 1
else: else:
sended_media_messages.append(bot_message) sended_media_messages.append(bot_message)
@@ -815,21 +897,14 @@ class FlanaBot(MultiBot, ABC):
attributes = ( attributes = (
f'Título: {song_info.title}\n' if song_info.title else '', f'Título: {song_info.title}\n' if song_info.title else '',
f'Autor: {song_info.author}\n' if song_info.author else '', f'Autor: {song_info.author}\n' if song_info.author else '',
f"{f'Álbum: {song_info.album}'}\n" if song_info.album else '', f'Álbum: {song_info.album}\n' if song_info.album else '',
f'Previa:' f'Previa:'
) )
await self.send(''.join(attributes), message) await self.send(''.join(attributes), message)
if song_info: if song_info:
await self.send(song_info, message) await self.send(song_info, message)
async def unmute(self, user_id: int, group_id: int, message: Message = None): async def unpunish(self, user: int | str | User, group_: int | str | Chat | Message, message: Message = None):
try: # noinspection PyTypeChecker
await self._unmute(user_id, group_id) punish = Punishment(self.platform, self.get_user_id(user), self.get_group_id(group_))
except UserDisconnectedError as e: await punish.unpunish(self._unpunish, message)
await self._manage_exceptions(e, message if message else Message(chat=Chat(group_id=group_id)))
async def unpunish(self, user_id: int, group_id: int, message: Message = None):
try:
await self._unpunish(user_id, group_id)
except BadRoleError as e:
await self._manage_exceptions(e, message if message else Message(chat=Chat(group_id=group_id)))

View File

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

View File

@@ -1,18 +1,17 @@
from __future__ import annotations # todo0 remove in 3.11 from __future__ import annotations # todo0 remove in 3.11
__all__ = ['whitelisted_event', 'FlanaTeleBot']
import functools import functools
import os import os
from typing import Callable from typing import Callable, Sequence
import flanautils
import telethon.tl.functions import telethon.tl.functions
from flanaapis.weather.constants import WeatherEmoji from multibot import TelegramBot, find_message, user_client
from flanautils import Media, MediaType, return_if_first_empty
from multibot import TelegramBot, constants as multibot_constants, find_message, user_client
from flanabot.bots.flana_bot import FlanaBot from flanabot.bots.flana_bot import FlanaBot
from flanabot.models.chat import Chat from flanabot.models import Message
from flanabot.models.message import Message
from flanabot.models.user import User
# ---------------------------------------------------------- # # ---------------------------------------------------------- #
@@ -48,19 +47,9 @@ class FlanaTeleBot(TelegramBot, FlanaBot):
# ----------------------------------------------------------- # # ----------------------------------------------------------- #
# -------------------- PROTECTED METHODS -------------------- # # -------------------- PROTECTED METHODS -------------------- #
# ----------------------------------------------------------- # # ----------------------------------------------------------- #
def _add_handlers(self): def _distribute_poll_buttons(self, texts: Sequence[str]) -> list[list[str]]:
super()._add_handlers() # noinspection PyTypeChecker
self.register_button(self._on_button_press) return flanautils.chunks(texts, 1)
@return_if_first_empty(exclude_self_types='FlanaTeleBot', globals_=globals())
async def _create_chat_from_telegram_chat(self, telegram_chat: multibot_constants.TELEGRAM_CHAT) -> Chat | None:
chat = await super()._create_chat_from_telegram_chat(telegram_chat)
chat.config = Chat.DEFAULT_CONFIG
return Chat.from_dict(chat.to_dict())
@return_if_first_empty(exclude_self_types='FlanaTeleBot', globals_=globals())
async def _create_user_from_telegram_user(self, original_user: multibot_constants.TELEGRAM_USER, group_id: int = None) -> User | None:
return User.from_dict((await super()._create_user_from_telegram_user(original_user, group_id)).to_dict())
@user_client @user_client
async def _get_contacts_ids(self) -> list[int]: async def _get_contacts_ids(self) -> list[int]:
@@ -71,43 +60,11 @@ class FlanaTeleBot(TelegramBot, FlanaBot):
@user_client @user_client
async def _update_whitelist(self): async def _update_whitelist(self):
self.whitelist_ids = [self.owner_id, self.bot_id] + await self._get_contacts_ids() self.whitelist_ids = [self.owner_id, self.id] + await self._get_contacts_ids()
# ---------------------------------------------- # # ---------------------------------------------- #
# HANDLERS # # HANDLERS #
# ---------------------------------------------- # # ---------------------------------------------- #
@whitelisted_event
async def _on_button_press(self, message: Message):
await message.original_event.answer()
match message.button_text:
case WeatherEmoji.ZOOM_IN.value:
message.weather_chart.zoom_in()
case WeatherEmoji.ZOOM_OUT.value:
message.weather_chart.zoom_out()
case WeatherEmoji.LEFT.value:
message.weather_chart.move_left()
case WeatherEmoji.RIGHT.value:
message.weather_chart.move_right()
case WeatherEmoji.PRECIPITATION_VOLUME.value:
message.weather_chart.trace_metadatas['rain_volume'].show = not message.weather_chart.trace_metadatas['rain_volume'].show
message.weather_chart.trace_metadatas['snow_volume'].show = not message.weather_chart.trace_metadatas['snow_volume'].show
case emoji if emoji in WeatherEmoji.values:
trace_metadata_name = WeatherEmoji(emoji).name.lower()
message.weather_chart.trace_metadatas[trace_metadata_name].show = not message.weather_chart.trace_metadatas[trace_metadata_name].show
message.weather_chart.apply_zoom()
message.weather_chart.draw()
message.save()
image_bytes = message.weather_chart.to_image()
file = await self._prepare_media_to_send(Media(image_bytes, MediaType.IMAGE))
try:
await message.original_object.edit(file=file)
except telethon.errors.rpcerrorlist.MessageNotModifiedError:
pass
@whitelisted_event @whitelisted_event
async def _on_inline_query_raw(self, message: Message): async def _on_inline_query_raw(self, message: Message):
await super()._on_new_message_raw(message) await super()._on_new_message_raw(message)

View File

@@ -3,111 +3,116 @@ import datetime
import flanautils import flanautils
AUTO_WEATHER_EVERY = datetime.timedelta(hours=6) AUTO_WEATHER_EVERY = datetime.timedelta(hours=6)
CHECK_MESSAGE_EVERY_SECONDS = datetime.timedelta(days=1).total_seconds()
CHECK_MUTES_EVERY_SECONDS = datetime.timedelta(hours=1).total_seconds()
CHECK_PUNISHMENTS_EVERY_SECONDS = datetime.timedelta(hours=1).total_seconds() CHECK_PUNISHMENTS_EVERY_SECONDS = datetime.timedelta(hours=1).total_seconds()
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
MAX_PLACE_QUERY_LENGTH = 50 MAX_PLACE_QUERY_LENGTH = 50
PUNISHMENT_INCREMENT_EXPONENT = 6 PUNISHMENT_INCREMENT_EXPONENT = 6
PUNISHMENTS_RESET = datetime.timedelta(weeks=6 * flanautils.WEEKS_IN_A_MONTH) PUNISHMENTS_RESET = datetime.timedelta(weeks=6 * flanautils.WEEKS_IN_A_MONTH)
RECOVERY_DELETED_MESSAGE_BEFORE = datetime.timedelta(hours=1) RECOVERY_DELETED_MESSAGE_BEFORE = datetime.timedelta(hours=1)
TIME_THRESHOLD_TO_MANUAL_UNMUTE = datetime.timedelta(days=3)
TIME_THRESHOLD_TO_MANUAL_UNPUNISH = datetime.timedelta(days=3)
SCRAPING_MESSAGE_WAITING_TIME = 0.1 SCRAPING_MESSAGE_WAITING_TIME = 0.1
BAD_PHRASES = (
'Cállate ya anda.',
'¿Quién te ha preguntado?',
'¿Tú eres así o te dan apagones cerebrales?',
'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.',
'Despídete de tu cuenta.',
'Flanagan es más guapo que tú.',
'jajaj',
'xd',
'Hay un concurso de hostias y tienes todas las papeletas.',
'¿Por qué no te callas?',
'Das penilla.',
'Deberían hacerte la táctica del C4.',
'Te voy romper las pelotas.',
'Más tonto y no naces.',
'Eres más tonto que peinar bombillas.',
'Eres más tonto que pellizcar cristales.',
'Eres más malo que pegarle a un padre.'
)
BYE_PHRASES = ('Adiós.', 'adieu', 'adio', 'adioh', 'adios', 'adió', 'adiós', 'agur', 'bye', 'byyeeee', 'chao', BYE_PHRASES = ('Adiós.', 'adieu', 'adio', 'adioh', 'adios', 'adió', 'adiós', 'agur', 'bye', 'byyeeee', 'chao',
'hasta la vista', 'hasta luego', 'hasta nunca', ' hasta pronto', 'hasta la próxima', 'hasta la vista', 'hasta luego', 'hasta nunca', ' hasta pronto', 'hasta la próxima',
'nos vemos', 'taluego') 'nos vemos', 'taluego')
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 = (
'._.',
'aha',
'Aléjate de mi.',
'Ante la duda mi dedo corazón te saluda.',
'Baneito pa ti en breve.',
'Calla noob.',
'Cansino.',
'Cuentame menos.',
'Cuentame más.',
'Cállate ya anda.',
'Cállate.',
'Das penilla.',
'De verdad. Estás para encerrarte.',
'Deberían hacerte la táctica del C4.',
'Despídete de tu cuenta.',
'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 pellizcar cristales.',
'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.',
'Que 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 = {
'activate': ('activa', 'activar', 'activate', 'deja', 'dejale', 'devuelve', 'devuelvele', 'enable', 'encender', 'choose': ('choose', 'elige'),
'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'),
'change': ('alter', 'alternar', 'alternate', 'cambiar', 'change', 'default', 'defecto', 'edit', 'editar',
'exchange', 'modificar', 'modify', 'permutar', 'predeterminado', 'shift', 'swap', 'switch', 'turn',
'vary'),
'config': ('ajustar', 'ajuste', 'ajustes', 'automatico', 'automatic', 'config', 'configs', 'configuracion',
'configuration', 'default', 'defecto', 'setting', 'settings'),
'covid_chart': ('case', 'caso', 'contagiado', 'contagio', 'corona', 'coronavirus', 'covid', 'covid19', 'death', 'covid_chart': ('case', 'caso', 'contagiado', 'contagio', 'corona', 'coronavirus', 'covid', 'covid19', 'death',
'disease', 'enfermedad', 'enfermos', 'fallecido', 'incidencia', 'jacovid', 'mascarilla', 'muerte', 'disease', 'enfermedad', 'enfermos', 'fallecido', 'incidencia', 'jacovid', 'mascarilla', 'muerte',
'muerto', 'pandemia', 'sick', 'virus'), 'muerto', 'pandemia', 'sick', 'virus'),
'currency_chart': ('argentina', 'bitcoin', 'cardano', 'cripto', 'crypto', 'criptodivisa', 'cryptodivisa', 'currency_chart': ('argentina', 'bitcoin', 'cardano', 'cripto', 'crypto', 'criptodivisa', 'cryptodivisa',
'cryptomoneda', 'cryptocurrency', 'currency', 'dinero', 'divisa', 'ethereum', 'inversion', 'cryptomoneda', 'cryptocurrency', 'currency', 'dinero', 'divisa', 'ethereum', 'inversion',
'moneda', 'pasta'), 'moneda', 'pasta'),
'date': ('ayer', 'de', 'domingo', 'fin', 'finde', 'friday', 'hoy', 'jueves', 'lunes', 'martes', 'mañana', 'dice': ('dado', 'dice'),
'miercoles', 'monday', 'pasado', 'sabado', 'saturday', 'semana', 'sunday', 'thursday', 'today', 'tomorrow', 'poll': ('encuesta', 'poll', 'quiz'),
'tuesday', 'viernes', 'wednesday', 'week', 'weekend', 'yesterday'),
'deactivate': ('apaga', 'apagar', 'deactivate', 'deactivate', 'desactivar', 'deshabilita', 'deshabilitar',
'disable', 'forbids', 'prohibe', 'quita', 'remove', 'return'),
'hello': ('alo', 'aloh', 'buenas', 'dias', 'hello', 'hey', 'hi', 'hola', 'holaaaaaa', 'ola', 'saludos', 'tardes'),
'help': ('ayuda', 'help'),
'mute': ('calla', 'calle', 'cierra', 'close', 'mute', 'mutea', 'mutealo', 'noise', 'ruido', 'shut', 'silence',
'silencia'),
'negate': ('no', 'ocurra', 'ocurre'),
'permission': ('permiso', 'permission'),
'punish': ('acaba', 'aprende', 'ataca', 'atalo', 'azota', 'boss', 'castiga', 'castigo', 'condena', 'controla', 'punish': ('acaba', 'aprende', 'ataca', 'atalo', 'azota', 'boss', 'castiga', 'castigo', 'condena', 'controla',
'destroy', 'destroza', 'duro', 'ejecuta', 'enseña', 'escarmiento', 'execute', 'finish', 'fuck', 'fusila', 'destroy', 'destroza', 'duro', 'ejecuta', 'enseña', 'escarmiento', 'execute', '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'),
'reset': ('recover', 'recovery', 'recupera', 'reinicia', 'reset', 'resetea', 'restart'), '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'),
'show': ('actual', 'enseña', 'estado', 'how', 'muestra', 'show', 'como'),
'song_info': ('aqui', 'cancion', 'data', 'datos', 'info', 'informacion', 'information', 'llama', 'media', 'name', 'song_info': ('aqui', 'cancion', 'data', 'datos', 'info', 'informacion', 'information', 'llama', 'media', 'name',
'nombre', 'sonaba', 'sonando', 'song', 'sono', 'sound', 'suena', 'title', 'titulo', 'nombre', 'sonaba', 'sonando', 'song', 'sono', 'sound', 'suena', 'title', 'titulo',
'video'), 'video'),
'sound': ('hablar', 'hable', 'micro', 'microfono', 'microphone', 'sonido', 'sound', 'talk', 'volumen'),
'thanks': ('gracia', 'gracias', 'grasia', 'grasias', 'grax', 'thank', 'thanks', 'ty'),
'unmute': ('desilencia', 'desmutea', 'desmutealo', 'unmute'),
'unpunish': ('absolve', 'forgive', 'innocent', 'inocente', 'perdona', 'spare'), 'unpunish': ('absolve', 'forgive', 'innocent', 'inocente', 'perdona', 'spare'),
'weather_chart': ('atmosfera', 'atmosferico', 'calle', 'calor', 'caloret', 'clima', 'climatologia', 'cloud', 'weather_chart': ('atmosfera', 'atmosferico', 'calle', 'calor', 'caloret', 'clima', 'climatologia', 'cloud',
'cloudless', 'cloudy', 'cold', 'congelar', 'congelado', 'denbora', 'despejado', 'diluvio', 'frio', 'cloudless', 'cloudy', 'cold', 'congelar', 'congelado', 'denbora', 'despejado', 'diluvio', 'frio',
'frost', 'hielo', 'humedad', 'llover', 'llueva', 'llueve', 'lluvia', 'nevada', 'nieva', 'nieve', 'frost', 'hielo', 'humedad', 'llover', 'llueva', 'llueve', 'lluvia', 'nevada', 'nieva', 'nieve',
'nube', 'nubes', 'nublado', 'meteorologia', 'rain', 'snow', 'snowfall', 'snowstorm', 'sol', 'nube', 'nubes', 'nublado', 'meteorologia', 'rain', 'snow', 'snowfall', 'snowstorm', 'sol',
'solano', 'storm', 'sun', 'temperatura', 'tempo', 'tiempo', 'tormenta', 've', 'ventisca', 'solano', 'storm', 'sun', 'temperatura', 'tempo', 'tiempo', 'tormenta', 'ventisca', 'weather',
'weather', 'wetter') 'wetter')
} }
RECOVER_PHRASES = (
'No hay nada que recuperar.',
'Ya lo he recuperado y enviado, así que callate ya.',
'Ya lo he recuperado y enviado, así que mejor estás antento antes de dar por culo.',
'Ya lo he recuperado y enviado, no lo voy a hacer dos veces.',
'Ya lo he recuperado y enviado. A ver si leemos más y jodemos menos.',
'Ya lo he reenviado.'
)
SCRAPING_PHRASES = ('Analizando...', 'Buscando...', 'Hackeando internet... 👀', 'Rebuscando en la web...', SCRAPING_PHRASES = ('Analizando...', 'Buscando...', 'Hackeando internet... 👀', 'Rebuscando en la web...',
'Robando cosas...', 'Scrapeando...', 'Scraping...') 'Robando cosas...', 'Scrapeando...', 'Scraping...')

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,13 +1,14 @@
from __future__ import annotations # todo0 remove in 3.11 from __future__ import annotations # todo0 remove in 3.11
__all__ = ['Message']
from dataclasses import dataclass, field from dataclasses import dataclass, field
from typing import Iterable from typing import Iterable
from flanautils import Media, OrderedSet from flanautils import Media, OrderedSet
from multibot import Message as MultiBotMessage from multibot.models import Message as MultiBotMessage, User
from flanabot.models.chat import Chat from flanabot.models.chat import Chat
from flanabot.models.user import User
from flanabot.models.weather_chart import WeatherChart from flanabot.models.weather_chart import WeatherChart

View File

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

View File

@@ -1,23 +0,0 @@
import datetime
from dataclasses import dataclass
from flanautils import DCMongoBase, FlanaBase
from multibot.models.database import db
@dataclass(eq=False)
class PunishmentBase(DCMongoBase, FlanaBase):
user_id: int = None
group_id: int = None
until: datetime.datetime = None
is_active: bool = True
@dataclass(eq=False)
class Punishment(PunishmentBase):
collection = db.punishment
@dataclass(eq=False)
class Mute(PunishmentBase):
collection = db.mute

View File

@@ -1,22 +0,0 @@
from dataclasses import dataclass
from multibot import User as MultiBotUser
from flanabot.models.punishments import Mute, Punishment
@dataclass(eq=False)
class User(MultiBotUser):
def is_muted_on(self, group_id: int):
return group_id in self.muted_on
def is_punished_on(self, group_id: int):
return group_id in self.punished_on
@property
def muted_on(self):
return {mute.group_id for mute in Mute.find({'user_id': self.id, 'is_active': True})}
@property
def punished_on(self):
return {punishment for punishment in Punishment.find({'user_id': self.id, 'is_active': True})}

View File

@@ -1,3 +1,5 @@
__all__ = ['Direction', 'WeatherChart']
import datetime import datetime
from dataclasses import dataclass, field from dataclasses import dataclass, field
@@ -44,7 +46,7 @@ class WeatherChart(DateChart):
if self.show_now_vertical_line and first_dt <= now <= last_dt: if self.show_now_vertical_line and first_dt <= now <= last_dt:
self.figure.add_vline(x=now, line_width=1, line_dash='dot') self.figure.add_vline(x=now, line_width=1, line_dash='dot')
self.figure.add_annotation(text="Ahora", yref="paper", x=now, y=0.01, showarrow=False) self.figure.add_annotation(text='Ahora', yref='paper', x=now, y=0.01, showarrow=False)
for day_weather in self.day_weathers: for day_weather in self.day_weathers:
date_time = datetime.datetime(year=day_weather.date.year, month=day_weather.date.month, day=day_weather.date.day, tzinfo=self.timezone) date_time = datetime.datetime(year=day_weather.date.year, month=day_weather.date.month, day=day_weather.date.day, tzinfo=self.timezone)

View File

@@ -1,10 +1,11 @@
aiohttp==3.7.4.post0 aiohttp==3.8.1
aiosignal==1.2.0 aiosignal==1.2.0
anyio==3.5.0 anyio==3.5.0
asgiref==3.4.1 asgiref==3.4.1
async-generator==1.10 async-generator==1.10
async-timeout==3.0.1 async-timeout==4.0.2
attrs==21.4.0 attrs==21.4.0
beautifulsoup4==4.10.0
certifi==2021.10.8 certifi==2021.10.8
cffi==1.15.0 cffi==1.15.0
chardet==4.0.0 chardet==4.0.0
@@ -13,7 +14,7 @@ click==8.0.3
colorama==0.4.4 colorama==0.4.4
cryptg==0.2.post4 cryptg==0.2.post4
cryptography==36.0.1 cryptography==36.0.1
discord.py==1.7.3 discord.py @ git+https://github.com/Rapptz/discord.py@b7e25645dc68bbb828bf1ede711d098c6b183237
fastapi==0.71.0 fastapi==0.71.0
flanaapis flanaapis
flanautils flanautils
@@ -23,6 +24,7 @@ h11==0.12.0
hachoir==3.1.2 hachoir==3.1.2
idna==3.3 idna==3.3
iso8601==1.0.2 iso8601==1.0.2
jellyfish==0.9.0
kaleido==0.2.1 kaleido==0.2.1
mpmath==1.2.1 mpmath==1.2.1
multibot multibot
@@ -45,6 +47,7 @@ selenium-stealth==1.0.6
six==1.16.0 six==1.16.0
sniffio==1.2.0 sniffio==1.2.0
sortedcontainers==2.4.0 sortedcontainers==2.4.0
soupsieve==2.3.1
starlette==0.17.1 starlette==0.17.1
sympy==1.9 sympy==1.9
Telethon==1.24.0 Telethon==1.24.0
@@ -52,7 +55,7 @@ tenacity==8.0.1
TikTokApi==4.1.0 TikTokApi==4.1.0
trio==0.19.0 trio==0.19.0
trio-websocket==0.9.2 trio-websocket==0.9.2
twitchio==2.1.4 twitchio==2.3.0
typing_extensions==4.0.1 typing_extensions==4.0.1
ujson==5.1.0 ujson==5.1.0
urllib3==1.26.8 urllib3==1.26.8

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

View File

@@ -1,16 +1,20 @@
import os
import flanautils
os.environ |= flanautils.find_environment_variables('../.env')
import unittest import unittest
from typing import Iterable from typing import Iterable
from flanabot import constants from flanabot.bots.flana_tele_bot import FlanaTeleBot
import flanautils
from flanabot.bots.flana_bots.flana_tele_bot import FlanaTeleBot
class TestParseCallbacks(unittest.TestCase): 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, 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, 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__}')
@@ -27,7 +31,10 @@ class TestParseCallbacks(unittest.TestCase):
'flanabot ajustes', 'flanabot ajustes',
'Flanabot ajustes', 'Flanabot ajustes',
'Flanabot qué puedo ajustar?', 'Flanabot qué puedo ajustar?',
'flanabot ayuda' 'config',
'configuracion',
'configuración',
'configuration'
] ]
self._test_no_always_callbacks(phrases, self.flana_tele_bot._on_config_list_show) self._test_no_always_callbacks(phrases, self.flana_tele_bot._on_config_list_show)
@@ -84,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)
@@ -96,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)
@@ -124,13 +129,6 @@ class TestParseCallbacks(unittest.TestCase):
] ]
self._test_no_always_callbacks(phrases, self.flana_tele_bot._on_mute) 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): def test_on_new_message_default(self):
phrases = [ phrases = [
'asdqwergf', 'asdqwergf',
@@ -157,7 +155,6 @@ class TestParseCallbacks(unittest.TestCase):
def test_on_punish(self): def test_on_punish(self):
phrases = [ phrases = [
'acabo con el',
'acaba con el', 'acaba con el',
'destrozalo', 'destrozalo',
'ataca', 'ataca',
@@ -169,8 +166,6 @@ class TestParseCallbacks(unittest.TestCase):
'castigalo', 'castigalo',
'castiga a', 'castiga a',
'castiga', 'castiga',
'banealo',
'banea',
'enseña quien manda' 'enseña quien manda'
] ]
self._test_no_always_callbacks(phrases, self.flana_tele_bot._on_punish) self._test_no_always_callbacks(phrases, self.flana_tele_bot._on_punish)
@@ -249,8 +244,7 @@ class TestParseCallbacks(unittest.TestCase):
'perdona a', 'perdona a',
'illo quitale a @flanagan el castigo', 'illo quitale a @flanagan el castigo',
'quita castigo', 'quita castigo',
'devuelve los permisos', 'devuelve los permisos'
'desbanea'
] ]
self._test_no_always_callbacks(phrases, self.flana_tele_bot._on_unpunish) self._test_no_always_callbacks(phrases, self.flana_tele_bot._on_unpunish)
@@ -265,6 +259,8 @@ class TestParseCallbacks(unittest.TestCase):
'sol', 'sol',
'temperatura', 'temperatura',
'humedad', 'humedad',
'que tiempo hara mañana',
'que tiempo hara manana',
'que tiempo hace en malaga', 'que tiempo hace en malaga',
'que tiempo hace en calle larios', 'que tiempo hace en calle larios',
'tiempo rusia', 'tiempo rusia',