Compare commits

...

14 Commits
v2.0.1 ... main

11 changed files with 104 additions and 76 deletions

5
MIRRORS Normal file
View File

@ -0,0 +1,5 @@
# Avalaible mirrors
https://inex.dev/localhost_frssoft/FMN_bot.git
https://git.macaw.me/localhost_frssoft/FMN_bot.git
https://code.criminallycute.fi/localhost_frssoft/FMN_bot.git
https://git.poridge.club/localhost_frssoft/FMN_bot.git

View File

@ -50,9 +50,10 @@ Note: Рекомендуется использовать ссылки на imdb
Note2: Список доступных для приёма инстансов libremdb обновляется вручную и может не соотвествовать официальному. Note2: Список доступных для приёма инстансов libremdb обновляется вручную и может не соотвествовать официальному.
## Список поддерживаемых инстансов libremdb: ## Список поддерживаемых инстансов libremdb:
* https://libremdb.herokuapp.com * https://libremdb.leemoon.network/ (Спасибо [Саре](https://lamp.leemoon.network/@sarahquartz) в рамках self-host проекта [Leemoon Network 🍋](https://leemoon.network))
* https://libremdb.pussthecat.org * https://libremdb.herokuapp.com/
* https://libremdbeu.herokuapp.com * https://libremdb.pussthecat.org/
* https://libremdbeu.herokuapp.com/
* https://lmdb.tokhmi.xyz/ * https://lmdb.tokhmi.xyz/
* https://libremdb.esmailelbob.xyz/ * https://libremdb.esmailelbob.xyz/
* http://libremdb.lqs5fjmajyp7rvp4qvyubwofzi6d4imua7vs237rkc4m5qogitqwrgyd.onion/ * http://libremdb.lqs5fjmajyp7rvp4qvyubwofzi6d4imua7vs237rkc4m5qogitqwrgyd.onion/

Binary file not shown.

Before

Width:  |  Height:  |  Size: 788 KiB

BIN
src/FMN.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

BIN
src/FMN_new_year.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

View File

@ -1,6 +1,5 @@
from config import instance from config import instance
import time import time
import json
import requests import requests
from loguru import logger from loguru import logger
@ -35,7 +34,6 @@ def get_notifications():
logger.info('Retrying get notificatios...') logger.info('Retrying get notificatios...')
def mark_as_read_notification(id_notification): def mark_as_read_notification(id_notification):
success = 0 success = 0
while success == 0: while success == 0:
@ -51,18 +49,22 @@ def mark_as_read_notification(id_notification):
def get_status_context(status_id): def get_status_context(status_id):
retry = 0
success = 0 success = 0
while success == 0: while success == 0:
try: try:
r = s.get(instance_point + f"/statuses/{status_id}/context") r = s.get(instance_point + f"/statuses/{status_id}/context", timeout=30)
r.raise_for_status() r.raise_for_status()
success = 1 success = 1
return r.json() return r.json()
except: except Exception as E:
logger.exception(f'Ошибка получения контекста треда {status_id}') logger.exception(f'Ошибка получения контекста треда {status_id}')
time.sleep(30) time.sleep(30)
logger.info('Повторный запрос треда...') logger.info('Повторный запрос треда...')
retry += 1
if retry > 5:
raise IOError(f'Фетчинг треда поломан! {E}')
def get_status(status_id): def get_status(status_id):
success = 0 success = 0
@ -78,7 +80,6 @@ def get_status(status_id):
logger.info(f'Retrying get status {status_id}') logger.info(f'Retrying get status {status_id}')
def post_status(text, reply_to_status_id=None, poll_options=None, poll_expires=345600, attachments=None): def post_status(text, reply_to_status_id=None, poll_options=None, poll_expires=345600, attachments=None):
poll = None poll = None
if poll_options is not None: if poll_options is not None:
@ -116,17 +117,9 @@ def upload_attachment(file_path):
params = { params = {
"description": "Fediverse Movie Night\nВоскресенье, 21:00\nLIVE ON XXIV Production", "description": "Fediverse Movie Night\nВоскресенье, 21:00\nLIVE ON XXIV Production",
} }
success = 0 r = s.post(instance_point + "/media", params, files=file, timeout=30)
while success == 0: r.raise_for_status()
try: return r.json()['id']
r = s.post(instance_point + "/media", params, files=file)
r.raise_for_status()
success = 1
return r.json()['id']
except:
logger.exception(f'Error uploading {file_path} attachment')
time.sleep(5)
logger.info(f'Retrying upload {file_path}...')
def mute_user(acct_id=str, acct=str, duration=None): def mute_user(acct_id=str, acct=str, duration=None):
@ -144,4 +137,3 @@ def mute_user(acct_id=str, acct=str, duration=None):
logger.exception(f'Ошибка глушения {acct}') logger.exception(f'Ошибка глушения {acct}')
time.sleep(5) time.sleep(5)
logger.info(f'Повторное глушение {acct}...') logger.info(f'Повторное глушение {acct}...')

View File

@ -113,4 +113,3 @@ def reset_poll():
'''Сброс содержимого предложки-опроса''' '''Сброс содержимого предложки-опроса'''
c.execute("DELETE FROM poll") c.execute("DELETE FROM poll")
conn.commit() conn.commit()

View File

@ -1,5 +1,5 @@
from src.fedi_api import get_status, post_status, upload_attachment from src.fedi_api import get_status, post_status, upload_attachment
from src.fmn_states_db import read_states, write_states from src.fmn_states_db import states_stor, write_states
from src.fmn_database import get_movies_for_poll, write_votes, read_votes, mark_as_watched_movie, get_already_watched, rewrite_db, reset_poll, get_count_all_watched_movies, force_commit from src.fmn_database import get_movies_for_poll, write_votes, read_votes, mark_as_watched_movie, get_already_watched, rewrite_db, reset_poll, get_count_all_watched_movies, force_commit
from collections import Counter from collections import Counter
from loguru import logger from loguru import logger
@ -16,7 +16,9 @@ def text_create_poll():
return text_poll return text_poll
@logger.catch
def create_poll_movies(text=text_create_poll(), poll_expires=345600): def create_poll_movies(text=text_create_poll(), poll_expires=345600):
logger.debug('Creating poll')
formated_poll_options = [] formated_poll_options = []
raw_poll = get_movies_for_poll() raw_poll = get_movies_for_poll()
for i in raw_poll: for i in raw_poll:
@ -25,6 +27,7 @@ def create_poll_movies(text=text_create_poll(), poll_expires=345600):
ru_name = i[2] ru_name = i[2]
year = i[3] year = i[3]
poll_option_string = f"{ru_name} / {orig_name}, {year} ({acct})" poll_option_string = f"{ru_name} / {orig_name}, {year} ({acct})"
logger.debug(f"Adding option in poll: {poll_option_string}")
if ru_name is None: if ru_name is None:
poll_option_string = f"{orig_name}, {year} ({acct})" poll_option_string = f"{orig_name}, {year} ({acct})"
if orig_name is None: if orig_name is None:
@ -33,19 +36,25 @@ def create_poll_movies(text=text_create_poll(), poll_expires=345600):
poll_option_string = poll_option_string[0:199] # Обрезка на 200 символов. poll_option_string = poll_option_string[0:199] # Обрезка на 200 символов.
formated_poll_options.append(poll_option_string) formated_poll_options.append(poll_option_string)
attaches = []
try:
attaches = [upload_attachment('src/FMN.webp')]
except Exception as E:
logger.error(f"attachements can't do upload: {E}")
poll_status_id = post_status(text, None, formated_poll_options, poll_status_id = post_status(text, None, formated_poll_options,
poll_expires=poll_expires, attachments=[upload_attachment('src/FMN.png')]) poll_expires=poll_expires, attachments=attaches)
logger.success('Голосовалка создана') logger.success('Голосовалка создана')
states = read_states() states_stor.states['poll_expires_at'] = int(time.time()) + poll_expires
states['poll_expires_at'] = int(time.time()) + poll_expires states_stor.states['poll_status_id'] = poll_status_id['id']
states['poll_status_id'] = poll_status_id['id'] write_states(states_stor.states)
write_states(states)
return poll_status_id return poll_status_id
@logger.catch
def get_winner_movie(poll_status_id=str): def get_winner_movie(poll_status_id=str):
'''Отмечаем победивший фильм на голосовании как просмотренный или постим tie breaker''' '''Отмечаем победивший фильм на голосовании как просмотренный или постим tie breaker'''
states = read_states() states = states_stor.states
votes_counters = [] votes_counters = []
status_with_poll = get_status(poll_status_id) status_with_poll = get_status(poll_status_id)
poll = status_with_poll['poll'] poll = status_with_poll['poll']
@ -53,7 +62,7 @@ def get_winner_movie(poll_status_id=str):
for option in poll['options']: for option in poll['options']:
votes_count = option['votes_count'] votes_count = option['votes_count']
votes_counters.append(votes_count) votes_counters.append(votes_count)
write_votes(votes_counters) write_votes(votes_counters)
voted_movies = read_votes() voted_movies = read_votes()
max_vote = voted_movies[0][4] max_vote = voted_movies[0][4]
@ -83,19 +92,19 @@ def get_winner_movie(poll_status_id=str):
text_winned = f"{expired_poll_count} голосование завершилось! Победил вариант предложенный @{acct_suggested}:\n{win_variant}" text_winned = f"{expired_poll_count} голосование завершилось! Победил вариант предложенный @{acct_suggested}:\n{win_variant}"
logger.success("Победил " + str(movie)) logger.success("Победил " + str(movie))
post_status(text_winned, attachments=[upload_attachment('src/FMN.png')]) post_status(text_winned, attachments=[upload_attachment('src/FMN.webp')])
states_stor.states = {}
write_states() write_states()
reset_poll() reset_poll()
@logger.catch
def create_tie_breaker(count_tie=1): def create_tie_breaker(count_tie=1):
'''Создание tie breaker''' '''Создание tie breaker'''
if count_tie == 1: if count_tie == 1:
states = read_states() states_stor.states['tie_breaker'] = 1
states['tie_breaker'] = 1 write_states(states_stor.states)
write_states(states)
poll_expires = 8*60*60 poll_expires = 8*60*60
else: else:
poll_expires = 4*60*60 poll_expires = 4*60*60
tie_poll = create_poll_movies("TIE BREAKER!!!\n\nВыбираем из победителей!", poll_expires) tie_poll = create_poll_movies("TIE BREAKER!!!\n\nВыбираем из победителей!", poll_expires)

View File

@ -3,6 +3,11 @@ from loguru import logger
states_file = 'fmn_states.json' states_file = 'fmn_states.json'
class states_stor:
states = None
@logger.catch @logger.catch
def read_states(): def read_states():
try: try:
@ -16,10 +21,13 @@ def read_states():
@logger.catch @logger.catch
def write_states(states={}): def write_states(new_states={}):
with open(states_file, 'wt') as f: with open(states_file, 'wt') as f:
f.write(json.dumps(states, indent=4)) f.write(json.dumps(new_states, indent=4))
if states == {}: if new_states == {}:
logger.info('states empty wrote') logger.info('states empty wrote')
return states return new_states
if not states_stor.states:
states_stor.states = read_states()

View File

@ -3,13 +3,10 @@ from src.fedi_api import get_status_context, get_status, post_status, mute_user
from src.kinopoisk_api import get_kinopoisk_movie_to_imdb from src.kinopoisk_api import get_kinopoisk_movie_to_imdb
from src.imdb_datasets_worker import get_title_by_id 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_database import add_movie_to_poll, get_already_watched, get_suggested_movies_count
from src.fmn_states_db import read_states, write_states from src.fmn_states_db import states_stor
from src.fmn_poll import create_poll_movies, get_winner_movie from src.fmn_poll import create_poll_movies, get_winner_movie
import re import re
import time import time
from datetime import datetime
from dateutil.parser import parse as dateutilparse
from dateutil.relativedelta import relativedelta, TU
from collections import Counter from collections import Counter
from loguru import logger from loguru import logger
@ -23,24 +20,24 @@ def parse_links(text=str):
def parse_links_imdb(text=str): def parse_links_imdb(text=str):
regex = r"imdb\.com/|libremdb\.pussthecat\.org/|libremdb\.esmailelbob\.xyz/|libremdb\.herokuapp\.com/|libremdbeu\.herokuapp\.com/|lmdb\.tokhmi\.xyz/|libremdb\.lqs5fjmajyp7rvp4qvyubwofzi6d4imua7vs237rkc4m5qogitqwrgyd\.onion/" regex = r"imdb\.com/|libremdb\.leemoon\.network/|libremdb\.pussthecat\.org/|libremdb\.esmailelbob\.xyz/|libremdb\.herokuapp\.com/|libremdbeu\.herokuapp\.com/|lmdb\.tokhmi\.xyz/|libremdb\.lqs5fjmajyp7rvp4qvyubwofzi6d4imua7vs237rkc4m5qogitqwrgyd\.onion/"
if re.search(regex, text.lower(), flags=re.MULTILINE): if re.search(regex, text.lower(), flags=re.MULTILINE):
imdb_ids = re.findall(r"tt(\d{1,})", text.lower()) imdb_ids = re.findall(r"tt(\d{1,})", text.lower())
if imdb_ids != []: if imdb_ids != []:
return imdb_ids[:limit_movies_per_user] return imdb_ids[:limit_movies_per_user]
def scan_context_thread(): def scan_context_thread():
fail_limit = Counter() fail_limit = Counter()
while True: while True:
states = read_states() states = states_stor.states
status_id = states.get('last_thread_id') status_id = states.get('last_thread_id')
poll_created = states.get('poll_status_id') poll_created = states.get('poll_status_id')
stop_thread_scan = states.get('stop_thread_scan') stop_thread_scan = states.get('stop_thread_scan')
time_now = int(time.time()) time_now = int(time.time())
reserve_time = False reserve_time = False
while status_id is None or stop_thread_scan is None: while status_id is None or stop_thread_scan is None:
states = read_states() states = states_stor.states
fail_limit = Counter() fail_limit = Counter()
status_id = states.get('last_thread_id') status_id = states.get('last_thread_id')
stop_thread_scan = states.get('stop_thread_scan') stop_thread_scan = states.get('stop_thread_scan')
@ -51,14 +48,14 @@ def scan_context_thread():
logger.debug('Сбор завершён, сканирование треда на опоздавших') logger.debug('Сбор завершён, сканирование треда на опоздавших')
if poll_created is None: if poll_created is None:
create_poll_movies() create_poll_movies()
poll_created = states.get('poll_status_id') poll_created = states_stor.states.get('poll_status_id')
else: else:
if time_now >= int(states.get('poll_expires_at')): if time_now >= int(states.get('poll_expires_at')):
get_winner_movie(poll_created) get_winner_movie(poll_created)
else: else:
endings = int(stop_thread_scan) - time_now endings = int(stop_thread_scan) - time_now
logger.debug(f'Осталось до закрытия сбора: {endings}') logger.debug(f'Осталось до закрытия сбора: {endings}')
if reserve_time: # Reduce instance load if reserve_time: # Reduce instance load
time.sleep(30) time.sleep(30)
get_thread_time = time.time() get_thread_time = time.time()
descendants = get_status_context(status_id)['descendants'] descendants = get_status_context(status_id)['descendants']
@ -72,6 +69,10 @@ def scan_context_thread():
for status in descendants: for status in descendants:
id_st = status['id'] id_st = status['id']
visibility = status['visibility']
if visibility == 'direct':
# Игнорируем личку
continue
in_reply_acct = status['in_reply_to_account_id'] in_reply_acct = status['in_reply_to_account_id']
in_reply_id = status['in_reply_to_id'] in_reply_id = status['in_reply_to_id']
muted = status['muted'] muted = status['muted']
@ -79,14 +80,14 @@ def scan_context_thread():
acct_id = status['account']['id'] acct_id = status['account']['id']
content = status['pleroma']['content']['text/plain'] content = status['pleroma']['content']['text/plain']
if id_st in replyed: # Игнорировать уже отвеченное if id_st in replyed: # Игнорировать уже отвеченное
continue continue
if muted is True: if muted is True:
continue continue
if fail_limit[acct] >= max_fail_limit: # Игнорировать пользователя если он превысил fail limit if fail_limit[acct] >= max_fail_limit: # Игнорировать пользователя если он превысил fail limit
mute_user(acct_id, acct, int(states.get('max_mute_time')) - time_now) mute_user(acct_id, acct, int(states.get('max_mute_time')) - time_now)
logger.warning(f'{acct} игнорируется - превышение fail limit') logger.warning(f'{acct} игнорируется - превышение fail limit')
break # Нужно обновить тред, чтобы muted на заглушенном стал True break # Нужно обновить тред, чтобы muted на заглушенном стал True
parsed_result = parse_links(content) parsed_result = parse_links(content)
parsed_result_imdb = parse_links_imdb(content) parsed_result_imdb = parse_links_imdb(content)
@ -99,23 +100,34 @@ def scan_context_thread():
logger.info(f'{acct} был уведомлен о завершенной голосовалке') logger.info(f'{acct} был уведомлен о завершенной голосовалке')
fail_limit[acct] += 1 fail_limit[acct] += 1
continue continue
index_type = 1 index_type = 1
index_name = 2 index_name = 2
index_ru_name = 3 index_ru_name = 3
index_year = 4 index_year = 4
message_writer = []
success = False
if parsed_result and parsed_result_imdb:
post_status('Не смешивайте IMDB и кинопоиск в одном посте, пожалуйста.', id_st)
fail_limit[acct] += 1
continue
if parsed_result is not None: if parsed_result is not None:
print(parsed_result) print(parsed_result)
suggested_movies = get_kinopoisk_movie_to_imdb(parsed_result) suggested_movies = get_kinopoisk_movie_to_imdb(parsed_result)
message_writer.append('⚠️ внимание при использовании Кинопоиска стабильность может быть понижена')
if suggested_movies is None: if suggested_movies is None:
post_status('Не удалось выполнить запрос: возможно некорректный тип фильма, попробуйте использовать imdb.com', id_st) post_status('Не удалось выполнить запрос: возможно некорректный тип фильма, попробуйте использовать https://libremdb.leemoon.network', id_st)
fail_limit[acct] += 1 fail_limit[acct] += 1
continue continue
elif parsed_result_imdb is not None: elif parsed_result_imdb is not None:
suggested_movies = get_title_by_id(parsed_result_imdb) suggested_movies = get_title_by_id(parsed_result_imdb)
message_writer = [] if suggested_movies is None:
success = False post_status('❌ Фильм(ы) не найден в базе данных IMDB, пожалуйста обратитесь к администратору, чтобы обновить базу. Примечание: IMDB выкладывает новые изменения не сразу.', id_st)
fail_limit[acct] += 1
continue
for movie in suggested_movies: for movie in suggested_movies:
logger.debug(str(movie)) logger.debug(str(movie))
if movie[index_type] == "404": if movie[index_type] == "404":
@ -132,7 +144,7 @@ def scan_context_thread():
name_ru = movie[index_ru_name] name_ru = movie[index_ru_name]
year = movie[index_year] year = movie[index_year]
movie_string = f"{name_ru} / {name}, {year}" movie_string = f"{name_ru} / {name}, {year}"
if name is None: if name is None:
movie_string = f"{name_ru}, {year}" movie_string = f"{name_ru}, {year}"
if name_ru is None: if name_ru is None:
@ -146,8 +158,8 @@ def scan_context_thread():
logger.warning(f'Предложение {acct} было отклонено: количество уже предложенных фильмов превышает\равно {limit_all_movies_poll}') logger.warning(f'Предложение {acct} было отклонено: количество уже предложенных фильмов превышает\равно {limit_all_movies_poll}')
fail_limit[acct] += 1 fail_limit[acct] += 1
break break
if get_already_watched(name, name_ru, year) == True: if get_already_watched(name, name_ru, year) is True:
message_writer.append(f" Этот фильм уже был на FMN: {movie_string}") message_writer.append(f" Этот фильм уже был на FMN: {movie_string}")
logger.info(f'Попытка предложить уже просмотренный фильм: {acct} {name} {name_ru} {year}') logger.info(f'Попытка предложить уже просмотренный фильм: {acct} {name} {name_ru} {year}')
fail_limit[acct] += 1 fail_limit[acct] += 1
@ -173,5 +185,3 @@ def scan_context_thread():
post_status('\n'.join(message_writer) + message, id_st) post_status('\n'.join(message_writer) + message, id_st)
time.sleep(30) time.sleep(30)

View File

@ -1,24 +1,27 @@
from src.fedi_api import get_notifications, mark_as_read_notification, post_status, upload_attachment from src.fedi_api import get_notifications, mark_as_read_notification, post_status, upload_attachment
from src.fmn_states_db import write_states, read_states from src.fmn_states_db import write_states, states_stor
from config import admins_bot, limit_movies_per_user, limit_all_movies_poll, hour_poll_posting, fmn_next_watching_hour from config import admins_bot, limit_movies_per_user, limit_all_movies_poll, hour_poll_posting, fmn_next_watching_hour
import threading, time import threading
import time
from datetime import datetime from datetime import datetime
from dateutil.parser import parse as dateutilparse from dateutil.parser import parse as dateutilparse
from dateutil.relativedelta import relativedelta, TU, SU from dateutil.relativedelta import relativedelta, TU, SU
from loguru import logger from loguru import logger
@logger.catch
def get_control_mention(): def get_control_mention():
while True: while True:
states = read_states() states = states_stor.states
time.sleep(30) time.sleep(30)
time_now = datetime.now() time_now = datetime.now()
now_week = time_now.weekday() now_week = time_now.weekday()
now_hour = time_now.hour now_hour = time_now.hour
if now_week not in (0, 6): if now_week not in (0, 6):
continue continue
if now_week == 6 and now_hour < fmn_next_watching_hour: # Предотвращение работы в холстую до начала сеанса if now_week == 6 and now_hour < fmn_next_watching_hour:
# Предотвращение работы в холстую до начала сеанса
continue continue
post_exists = states.get('last_thread_id') post_exists = states.get('last_thread_id')
if post_exists: if post_exists:
@ -26,12 +29,12 @@ def get_control_mention():
logger.debug('Wait for from admin mention...') logger.debug('Wait for from admin mention...')
notif = get_notifications() notif = get_notifications()
for i in notif: for i in notif:
if i['type'] != "mention": if i['type'] not in ("mention"):
continue continue
seen = i['pleroma']['is_seen'] seen = i['pleroma']['is_seen']
acct_mention = i['account']['acct'] acct_mention = i['account']['acct']
reply_to_id = i['status']['in_reply_to_id'] reply_to_id = i['status']['in_reply_to_id']
if acct_mention in admins_bot and seen == False and reply_to_id == None and now_week in (0, 6): if acct_mention in admins_bot and seen is False and reply_to_id is None and now_week in (0, 6):
logger.success(f'Найдено упоминание от {acct_mention}') logger.success(f'Найдено упоминание от {acct_mention}')
st_id = i['status']['id'] st_id = i['status']['id']
st_date = i['status']['created_at'] st_date = i['status']['created_at']
@ -41,7 +44,7 @@ def get_control_mention():
stop_thread_scan = thread_created_at + delta stop_thread_scan = thread_created_at + delta
movies_accept_time = stop_thread_scan.strftime('%H:%M %d.%m.%Y по Москве') movies_accept_time = stop_thread_scan.strftime('%H:%M %d.%m.%Y по Москве')
stop_thread_scan = time.mktime(time.struct_time(stop_thread_scan.timetuple())) stop_thread_scan = time.mktime(time.struct_time(stop_thread_scan.timetuple()))
if now_week == 6: # Фикс стыков двух недель. Если вс, то расчитываем на следующую неделю if now_week == 6: # Фикс стыков двух недель. Если вс, то расчитываем на следующую неделю
next_week = 2 next_week = 2
else: else:
@ -50,15 +53,15 @@ def get_control_mention():
next_movie_watching = time_now + next_movie_watching_delta next_movie_watching = time_now + next_movie_watching_delta
max_mute_time = time.mktime(time.struct_time(next_movie_watching.timetuple())) # Глушение до следующего сеанса FMN. max_mute_time = time.mktime(time.struct_time(next_movie_watching.timetuple())) # Глушение до следующего сеанса FMN.
next_movie_watching = next_movie_watching.strftime('%d.%m.%Y') next_movie_watching = next_movie_watching.strftime('%d.%m.%Y')
post_status(start_collect_movies_text(movies_accept_time, next_movie_watching), st_id, attachments=[upload_attachment('src/FMN.png')]) post_status(start_collect_movies_text(movies_accept_time, next_movie_watching), st_id, attachments=[upload_attachment('src/FMN.webp')])
time.sleep(0.2) time.sleep(0.2)
mark_as_read_notification(i['id']) mark_as_read_notification(i['id'])
states['max_mute_time'] = int(max_mute_time) states_stor.states['max_mute_time'] = int(max_mute_time)
states['stop_thread_scan'] = int(stop_thread_scan) states_stor.states['stop_thread_scan'] = int(stop_thread_scan)
states['last_thread_id'] = st_id states_stor.states['last_thread_id'] = st_id
write_states(states) write_states(states_stor.states)
break break
time.sleep(30) time.sleep(30)
@ -69,19 +72,20 @@ def start_collect_movies_text(movies_accept_time=str, next_movie_watching=str):
Напоминаем правила: Напоминаем правила:
- Мы принимаем на просмотр полнометражные художественные фильмы; - Мы принимаем на просмотр полнометражные художественные фильмы;
- Прием варианта осуществляется путем публикации ссылки на этот фильм на IMDB (libremdb) или Кинопоиске в этом треде; - Прием варианта осуществляется путем публикации ссылки на этот фильм на IMDB https://libremdb.leemoon.network/ или Кинопоиске в этом треде;
- Нам не подходят: сериалы, короткометражные и документальные фильмы; - Нам не подходят: сериалы, короткометражные и документальные фильмы;
- Максимальное количество вариантов, предложенных одним человеком не должно превышать {limit_movies_per_user}; - Максимальное количество вариантов, предложенных одним человеком не должно превышать {limit_movies_per_user};
- Всего может быть собрано до {limit_all_movies_poll} фильмов; - Всего может быть собрано до {limit_all_movies_poll} фильмов;
- Заявки принимаются до крайнего срока, после чего будет объявлено голосование по собранным вариантам. - Заявки принимаются до крайнего срока, после чего будет объявлено голосование по собранным вариантам.
Крайний срок подачи заявки - {movies_accept_time}. Крайний срок подачи заявки - {movies_accept_time}.
Рекомендуем посетить список, чтобы случайно не предложить уже просмотренный фильм: https://pub.phreedom.club/~localhost/fmn_watched.gmi
Желаем удачи. Желаем удачи.
'''.replace('\t', '') '''.replace('\t', '')
return text return text
def run_scan_notif(): def run_scan_notif():
scan_notif = threading.Thread(target=get_control_mention, daemon=True) scan_notif = threading.Thread(target=get_control_mention, daemon=True)
scan_notif.start() scan_notif.start()