diff --git a/config.py b/config.py index e32537d..4a8232a 100644 --- a/config.py +++ b/config.py @@ -2,9 +2,16 @@ admins_bot = ('drq@mastodon.ml',) # Адреса админов бота, кот # Example: ('admin_user', 'another_admin_user2@example.example') or ('admin_user',) bot_acct = 'fmn' # Ник бота на инстансе instance = 'expired.mentality.rip' # Инстанс, где будет запущен бот + +# Лимиты limit_movies_per_user = 2 # Ограничение количества фильмов на одного пользователя limit_all_movies_poll = 20 # Сколько можно добавить всего фильмов -hour_poll_posting=16 # Час в который будет создан пост с голосовалкой +max_fail_limit = 4 # Игнорировать предложения пользователя при достижении указанного числа проваленных поток предложить фильм -logger_default_level=10 # Уровень логгирования 10 - DEBUG, 20 - INFO, 30 - WARN +# Часы +# Note: Если на ОС часовой пояс отличается от Мск+3, то рекомендуется сместить эти часы здесь или поменять часовой пояс в ОС +hour_poll_posting = 16 # Час в который будет создан пост с голосовалкой (и завершение сбора) +fmn_next_watching_hour = 21 # Час начала киносеанса + +logger_default_level = 10 # Уровень логгирования 10 - DEBUG, 20 - INFO, 30 - WARN diff --git a/src/fedi_api.py b/src/fedi_api.py index afd179a..db030e7 100644 --- a/src/fedi_api.py +++ b/src/fedi_api.py @@ -1,6 +1,9 @@ +from config import instance import json import requests -from config import instance +import logging + +logger = logging.getLogger('fedi_api') instance_point = f"https://{instance}/api/v1" @@ -67,4 +70,14 @@ def upload_attachment(file_path): r = requests.post(instance_point + "/media", params, files=file, headers=headers) return r.json()['id'] - + +def mute_user(acct_id=str, acct=str, duration=None): + params = { + "duration": duration + } + r = requests.post(instance_point + '/accounts' + f"/{acct_id}/mute", params, headers=headers) + if r.status_code == 200: + logger.info(f'Пользователь {acct} был заглушен на {duration} secs') + else: + logger.error(f'Ошибка глушения {r.status_code} - {acct}') + diff --git a/src/listener_context.py b/src/listener_context.py index d15395f..0d53b0e 100644 --- a/src/listener_context.py +++ b/src/listener_context.py @@ -1,20 +1,22 @@ -import time -from datetime import datetime -from dateutil.parser import parse as dateutilparse -from dateutil.relativedelta import relativedelta, TU - -import re -import logging -from config import hour_poll_posting, bot_acct, instance, limit_all_movies_poll -from src.fedi_api import get_status_context, get_status, post_status +from config import hour_poll_posting, bot_acct, instance, limit_all_movies_poll, max_fail_limit +from src.fedi_api import get_status_context, get_status, post_status, mute_user from src.kinopoisk_api import get_kinopoisk_movie from src.imdb_datasets_worker import get_title_by_id from src.fmn_database import add_movie_to_poll, get_already_watched, get_suggested_movies_count from src.fmn_states_db import get_state, add_state from src.fmn_poll import create_poll_movies, get_winner_movie +import time +from datetime import datetime +from dateutil.parser import parse as dateutilparse +from dateutil.relativedelta import relativedelta, TU +from collections import Counter +import re +import logging + logger = logging.getLogger('thread_listener') + def parse_links(text=str): regex = r"kinopoisk\.ru/" if re.search(regex, text.lower(), flags=re.MULTILINE): @@ -34,13 +36,14 @@ def parse_links_imdb(text=str): def scan_context_thread(): + fail_limit = Counter() while True: status_id = get_state('last_thread_id') poll_created = get_state('poll_status_id') stop_thread_scan = get_state('stop_thread_scan') - flag_scan = 0 time_now = int(time.time()) while status_id is None or stop_thread_scan is None: + fail_limit = Counter() status_id = get_state('last_thread_id') stop_thread_scan = get_state('stop_thread_scan') time.sleep(1) @@ -66,11 +69,20 @@ def scan_context_thread(): id_st = status['id'] in_reply_acct = status['in_reply_to_account_id'] in_reply_id = status['in_reply_to_id'] + muted = status['muted'] acct = status['account']['acct'] + acct_id = status['account']['id'] content = status['pleroma']['content']['text/plain'] if id_st in replyed: # Игнорировать уже отвеченное continue + if muted is True: + continue + if fail_limit[acct] >= max_fail_limit: # Игнорировать пользователя если он превысил fail limit + mute_user(acct_id, acct, int(get_state('max_mute_time')) - time_now) + logger.warning(f'{acct} игнорируется - превышение fail limit') + break # Нужно обновить тред, чтобы muted на заглушенном стал True + parsed_result = parse_links(content) parsed_result_imdb = parse_links_imdb(content) @@ -80,6 +92,7 @@ def scan_context_thread(): if poll_created: post_status(f'ℹ️ Приём заявок уже окончен.\n\nГолосовалка здесь: https://{instance}/notice/{poll_created}', id_st) logger.info(f'{acct} был уведомлен о завершенной голосовалке') + fail_limit[acct] += 1 continue if parsed_result is not None: @@ -100,10 +113,12 @@ def scan_context_thread(): logger.debug(str(movie)) if movie[index_type] == "404": message_writer.append("❌ Не найдено.") + fail_limit[acct] += 1 elif movie[index_type] not in ("movie", "FILM", "video"): type_of_title = movie[index_type] message_writer.append(f"❌ Не принято:\n- Нам не подходят: сериалы, короткометражные и документальные фильмы") logger.info(f'Предложение {acct} отклонено: не подходящий тип фильма: {type_of_title}') + fail_limit[acct] += 1 else: name = movie[index_name] @@ -119,6 +134,7 @@ def scan_context_thread(): if get_suggested_movies_count() >= limit_all_movies_poll: post_status('🎬 Мы не можем обработать ваше предложение: количество уже предложенных не помещается в лимит голосовалки.', id_st) logger.warning(f'Предложение {acct} было отклонено: количество уже предложенных фильмов превышает\равно {limit_all_movies_poll}') + fail_limit[acct] += 1 break if get_already_watched(name, name_ru, year) == True: @@ -132,6 +148,7 @@ def scan_context_thread(): else: post_status("❌ Вы не можете добавить больше 2х фильмов", id_st) logger.info(f'Предложение от {acct} было отлонено - лимит на пользователя') + fail_limit[acct] += 1 if message_writer != []: post_status('\n'.join(message_writer) + "\nБлагодарим за ваше предложение!", id_st) diff --git a/src/listener_mention.py b/src/listener_mention.py index b4fae96..6521aab 100644 --- a/src/listener_mention.py +++ b/src/listener_mention.py @@ -1,6 +1,6 @@ from src.fedi_api import get_notifications, mark_as_read_notification, post_status, upload_attachment from src.fmn_states_db import add_state, get_state -from config import admins_bot, limit_movies_per_user, limit_all_movies_poll, hour_poll_posting +from config import admins_bot, limit_movies_per_user, limit_all_movies_poll, hour_poll_posting, fmn_next_watching_hour import threading, time from datetime import datetime @@ -28,9 +28,10 @@ def get_control_mention(): st_date = i['status']['created_at'] thread_created_at = dateutilparse(st_date) delta = relativedelta(hour=hour_poll_posting, minute=0, second=0, weekday=TU(1)) - next_movie_watching_delta = relativedelta(hour=21, minute=0, second=0, weekday=SU(1)) + next_movie_watching_delta = relativedelta(hour=fmn_next_watching_hour, minute=0, second=0, weekday=SU(1)) stop_thread_scan = thread_created_at + delta next_movie_watching = thread_created_at + next_movie_watching_delta + max_mute_time = time.mktime(time.struct_time(next_movie_watching.timetuple())) next_movie_watching = next_movie_watching.strftime('%d.%m.%Y') movies_accept_time = stop_thread_scan.strftime('%H:%M %d.%m.%Y MSK') stop_thread_scan = time.mktime(time.struct_time(stop_thread_scan.timetuple())) @@ -38,6 +39,7 @@ def get_control_mention(): post_status(start_collect_movies_text(movies_accept_time, next_movie_watching), st_id, attachments=[upload_attachment('src/FMN.png')]) time.sleep(0.2) mark_as_read_notification(i['id']) + add_state('max_mute_time', int(max_mute_time)) add_state('stop_thread_scan', int(stop_thread_scan)) add_state('last_thread_id', st_id) break