From 7e4b41d684fdea5e36414b8ecbf36499bd7d2939 Mon Sep 17 00:00:00 2001 From: Daniil Okhlopkov <5613295+ohld@users.noreply.github.com> Date: Fri, 13 Nov 2026 08:32:58 -0300 Subject: [PATCH] chore: prune redundant comments and preserve workaround rationale --- src/config.py | 7 ------- src/database.py | 10 +++------- src/flows/crossposting/meme.py | 11 ++--------- src/flows/storage/describe_memes.py | 2 -- src/localizer.py | 1 - src/main.py | 9 ++------- src/storage/etl.py | 17 ----------------- src/storage/upload.py | 4 +--- src/storage/watermark.py | 17 +---------------- src/tgbot/handlers/admin/forward_channel.py | 4 ---- src/tgbot/handlers/admin/user_info.py | 5 ----- src/tgbot/handlers/deep_link.py | 2 -- src/tgbot/handlers/upload/upload_meme.py | 5 ----- src/tgbot/repo/languages.py | 2 -- src/tgbot/repo/popups.py | 5 +++-- src/tgbot/senders/alerts.py | 3 --- src/tgbot/senders/keyboards.py | 3 --- 17 files changed, 12 insertions(+), 95 deletions(-) diff --git a/src/config.py b/src/config.py index d7f1d30d..ba5d7d7a 100644 --- a/src/config.py +++ b/src/config.py @@ -80,13 +80,6 @@ class Config(BaseSettings): TELEGRAM_API_HASH: str | None = None TELEGRAM_SESSION_STRING: str | None = None - # @model_validator(mode="after") - # def validate_sentry_non_local(self) -> "Config": - # if self.ENVIRONMENT.is_deployed and not self.SENTRY_DSN: - # raise ValueError("Sentry is not set") - - # return self - settings = Config() diff --git a/src/database.py b/src/database.py index c15f5787..8979eaae 100644 --- a/src/database.py +++ b/src/database.py @@ -697,13 +697,9 @@ def _is_transient_connection_error(exc: BaseException) -> bool: return _is_stale_connection_error(exc) or _is_concurrent_use_error(exc) -# Shared retry budget for stale / concurrent-use connection errors. -# Three retries (25 ms + 50 ms + 100 ms backoff) give the event loop enough -# time to dispose dirty connections under burst webhook concurrency. -# Under traffic spikes the LIFO pool can hand out several consecutively -# dirty connections; 2 retries proved insufficient (9 Sentry events on -# 2026-04-12). Bumping to 3 retries with wider backoff makes exhaustion -# astronomically unlikely while adding only ~175 ms worst-case latency. +# The LIFO pool can return consecutive stale/concurrent-use connections under load. +# Two retries proved insufficient (9 Sentry events on 2026-04-12); three retries +# allow 25 + 50 + 100 ms of backoff before propagating the failure. _TRANSIENT_MAX_RETRIES = 3 diff --git a/src/flows/crossposting/meme.py b/src/flows/crossposting/meme.py index 7e0d81a5..3488f8d9 100644 --- a/src/flows/crossposting/meme.py +++ b/src/flows/crossposting/meme.py @@ -136,11 +136,6 @@ def _get_ru_caption_for_crossposting_meme(meme: MemeData, channel: Channel) -> s cta = random.choice(CTAS) ref_link = "https://t.me/ffmemesbot?start=sc_{}_{}".format(meme.id, channel.value) - # emoji = get_random_emoji() - # referral_html = f"""{emoji} Fast Food Memes""" - # caption = escape(meme.caption, quote=False) if meme.caption else "" - # text = caption + "\n\n" + referral_html - text = cta + ": " + f"""@ffmemesbot""" return text @@ -220,8 +215,7 @@ async def post_meme_to_tgchannelen(): next_meme = MemeData(**meme_data) logger.info(f"Next meme for TG Channel EN: {next_meme.id}") - # Persist the ranker decision for retro analysis. Failure must NOT propagate - # โ€” Prefect retry would republish the meme. + # Ranker logging is best effort so analytics cannot block posting. if decision: try: await log_ranker_decision(**decision) @@ -280,8 +274,7 @@ async def post_meme_to_tgchannelru(): next_meme = MemeData(**meme_data) logger.info(f"Next meme for TG Channel RU: {next_meme.id}") - # Persist the ranker decision for retro analysis. Failure must NOT propagate - # โ€” Prefect retry would republish the meme. + # Ranker logging is best effort so analytics cannot block posting. if decision: try: await log_ranker_decision(**decision) diff --git a/src/flows/storage/describe_memes.py b/src/flows/storage/describe_memes.py index 284e1493..ff1f9c5c 100644 --- a/src/flows/storage/describe_memes.py +++ b/src/flows/storage/describe_memes.py @@ -70,7 +70,6 @@ async def describe_single_meme( file_id = meme_row["telegram_file_id"] existing_ocr = meme_row["ocr_result"] or {} - # Download image from Telegram try: image_bytes = await download_meme_content_from_tg(file_id) except Exception as e: @@ -80,7 +79,6 @@ async def describe_single_meme( image_b64 = base64.b64encode(image_bytes).decode("utf-8") - # Call vision model try: result = await call_openrouter_vision(image_b64, log, deadline=deadline, keys=keys) except Exception as e: diff --git a/src/localizer.py b/src/localizer.py index 28311c93..3c2de5c9 100644 --- a/src/localizer.py +++ b/src/localizer.py @@ -3,7 +3,6 @@ import yaml -# not sure where to put this const DEFAULT_LANG = "en" ALMOST_CIS_LANGUAGES = [ diff --git a/src/main.py b/src/main.py index 0ddc9f12..485c53f9 100644 --- a/src/main.py +++ b/src/main.py @@ -19,21 +19,16 @@ @asynccontextmanager async def lifespan(_application: FastAPI) -> AsyncGenerator: - # Startup tgbot_app.application = tgbot_app.setup_application(settings.ENVIRONMENT.is_deployed) await tgbot_app.application.initialize() - # flush all redis keys on startup for debug - # await redis.redis_client.flushall() - - # if is_webhook: # all gunicorn workers will call this and hit rate limit - # await bot.setup_webhook(bot.application) + # Do not register the webhook here: every Gunicorn worker runs lifespan, + # so startup registration would repeatedly hit Telegram's rate limit. yield if settings.ENVIRONMENT.is_testing: return - # Shutdown await redis.pool.disconnect() diff --git a/src/storage/etl.py b/src/storage/etl.py index 5b4ed540..1e89bf25 100644 --- a/src/storage/etl.py +++ b/src/storage/etl.py @@ -55,10 +55,6 @@ async def insert_parsed_posts_from_telegram( *, discover_candidates: bool = True, ) -> None: - # 1. find which memes are already in the database - # 2. update existing memes - # 3. insert new memes - result = await fetch_all( select(meme_raw_telegram.c.post_id) .where(meme_raw_telegram.c.meme_source_id == meme_source_id) @@ -202,10 +198,6 @@ async def etl_memes_from_raw_telegram_posts( *, fresh_only: bool = True, ) -> None: - # get transformed posts - # find ones that are already in the database - # create rows and update rows - # # Engagement filters before raw posts enter the meme table: # 1. From each source's latest 10 posts, only promote the top 5 by views. # Raw rows are still stored, so this threshold can be replayed later. @@ -325,15 +317,6 @@ async def etl_memes_from_raw_telegram_posts( }, ) - # find rows which already exist in db by two index columns: - # meme_source_id and raw_meme_id - # so we can update existing rows and create new ones - - # join two tables meme_raw_telegram and meme to get - # the meme_source_id and raw_meme_id pairs - # which are not present in memes table. - # That will indicate that we need to create new rows - memes_not_in_memes_table = await fetch_all( text( """ diff --git a/src/storage/upload.py b/src/storage/upload.py index 0e071d3b..97839998 100644 --- a/src/storage/upload.py +++ b/src/storage/upload.py @@ -78,7 +78,7 @@ async def _image_hash(meme_id: int, meme_type: MemeType, content: bytes, media: async def _upload_meme_content_to_tg( meme_id: int, meme_type: MemeType, - content: bytes, # ?? + content: bytes, ) -> dict[str, Any] | None: file_id = None media = None @@ -166,8 +166,6 @@ async def upload_meme_content_to_tg( *, observability_context: dict[str, Any] | None = None, ) -> dict[str, Any] | None: - # just a wrapper to handle retries - meme_result = None max_attempts = 3 content_size = len(content) diff --git a/src/storage/watermark.py b/src/storage/watermark.py index db4c35e7..2adc7ee4 100644 --- a/src/storage/watermark.py +++ b/src/storage/watermark.py @@ -16,10 +16,8 @@ def draw_text_with_outline( outline_colour: tuple, ) -> None: x, y = position - # Draw outline for adj in range(-1, 2): for ops in range(-1, 2): - # Avoid the center pixel if adj != 0 or ops != 0: draw.text((x + adj, y + ops), text, font=font, fill=outline_colour) draw.text(position, text, font=font, fill=text_colour) @@ -42,39 +40,27 @@ def find_least_detailed_corner(img: Image, text_bbox: tuple, margin: int) -> tup - A tuple (x, y) representing the top-left coordinate of the selected corner for text placement. """ - # Convert the original image to grayscale for intensity analysis gray_img = img.convert("L") img_w, img_h = img.size - # Calculate the width and height of the text bounding box text_width = text_bbox[2] - text_bbox[0] text_height = text_bbox[3] - text_bbox[1] - # Define dictionary with the coordinates of each corner with some margin applied corners = { "top_left": (margin, margin), "top_right": (img_w - text_width - margin, margin), "bottom_left": (margin, img_h - text_height - margin), "bottom_right": (img_w - text_width - margin, img_h - text_height - margin), } - # Initialize with infinity for comparison least_detailed_score = float("inf") - # To store the x, y coordinates for watermark selected_corner = None selected_wm_colour = None - # Iterate through each corner to find the one with the least detail background for _, (x, y) in corners.items(): - # Crop the image around the current corner - # according to the text bounding box size sector = gray_img.crop((x, y, x + text_width, y + text_height)) - # Calculate mean intensity of the cropped area mean_intensity = ImageStat.Stat(sector).mean[0] - # Determine how close the mean intensity is to either black (0) or white (255) - # indicates how uniform the background of the selected sector is + # Use mean brightness near black or white as a cheap proxy for a quiet background. distance_to_extreme = min(abs(0 - mean_intensity), abs(255 - mean_intensity)) - # Update if this corner has a lower score (closer to black or white) if distance_to_extreme < least_detailed_score: least_detailed_score = distance_to_extreme selected_corner = (x, y) - # Selecting watermark colour based on mean intensity selected_wm_colour = (0, 0, 0) if mean_intensity > 178 else (255, 255, 255) return selected_corner, selected_wm_colour @@ -110,7 +96,6 @@ def draw_corner_watermark( text_position, text_colour = find_least_detailed_corner(base, text_bbox, margin) outline_colour = (0, 0, 0, 255) if text_colour == (255, 255, 255) else (255, 255, 255, 255) draw_text_with_outline(d, text_position, text, font, text_colour, outline_colour) - # text opacity txt.putalpha(txt.getchannel("A").point(lambda x: x * text_opacity)) return Image.alpha_composite(base, txt).convert("RGB") diff --git a/src/tgbot/handlers/admin/forward_channel.py b/src/tgbot/handlers/admin/forward_channel.py index a91fdd84..ae09c93c 100644 --- a/src/tgbot/handlers/admin/forward_channel.py +++ b/src/tgbot/handlers/admin/forward_channel.py @@ -70,10 +70,6 @@ async def handle_forwarded_from_tgchannelru( await update.message.reply_text(f"Forwarding post to {len(users)}") await log(f"Forwarding post to {len(users)}", context.bot) - - # return await update.message.reply_text( - # f"Can't parse caption: {escape(str(update.message.to_dict()))}" - # ) else: url = urls[0] meme_id_str = url.replace("https://t.me/ffmemesbot?start=sc_", "").split("_")[0] diff --git a/src/tgbot/handlers/admin/user_info.py b/src/tgbot/handlers/admin/user_info.py index a057942b..d28b531c 100644 --- a/src/tgbot/handlers/admin/user_info.py +++ b/src/tgbot/handlers/admin/user_info.py @@ -71,10 +71,5 @@ async def delete_user_data_confirmation_page( async def delete_user_data(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: """Deletes all user data we have for testing purposes""" - # user = await get_user_info(update.effective_user.id) - # if user["type"] != UserType.ADMIN: - # return - - # TODO: "are you sure" button + callback await delete_user(update.effective_user.id) await update.effective_user.send_message("Ciao ๐Ÿ‘‹\n\n --> press /start to try again ๐Ÿ‘พ") diff --git a/src/tgbot/handlers/deep_link.py b/src/tgbot/handlers/deep_link.py index 4d88a079..a50f0f85 100644 --- a/src/tgbot/handlers/deep_link.py +++ b/src/tgbot/handlers/deep_link.py @@ -29,7 +29,6 @@ async def handle_invited_user( invitor_user_id = share_link.sharer_user_id - # get invitor user invitor_user = await get_user_by_id(invitor_user_id) if not invitor_user: return # Invitor doesn't exist @@ -45,7 +44,6 @@ async def handle_invited_user( was already invited by #{invited_user["inviter_id"]}""" ) - # set inviter id await update_user(invited_user["id"], inviter_id=invitor_user_id) # Don't reward if the inviter cannot receive the bot notification. diff --git a/src/tgbot/handlers/upload/upload_meme.py b/src/tgbot/handlers/upload/upload_meme.py index 2d793c04..c63fd961 100644 --- a/src/tgbot/handlers/upload/upload_meme.py +++ b/src/tgbot/handlers/upload/upload_meme.py @@ -271,7 +271,6 @@ async def handle_message_with_meme(update: Update, context: ContextTypes.DEFAULT raise Exception(f"not supported format: {update.message.to_json()}") -# callback: RULES_ACCEPTED_CALLBACK_DATA async def handle_rules_accepted_callback( update: Update, _: ContextTypes.DEFAULT_TYPE, @@ -292,7 +291,6 @@ async def handle_rules_accepted_callback( ) -# callback_data = LANG_SETTINGS_END_CALLBACK_DATA async def handle_meme_upload_lang_other( update: Update, _: ContextTypes.DEFAULT_TYPE, @@ -304,7 +302,6 @@ async def handle_meme_upload_lang_other( ) -# callback: RULES_ACCEPTED_CALLBACK_DATA async def handle_meme_upload_lang_selected( update: Update, context: ContextTypes.DEFAULT_TYPE, @@ -320,8 +317,6 @@ async def handle_meme_upload_lang_selected( await log(f"""๐Ÿ“ฅ Meme {meme["id"]} uploaded by #{update.effective_user.id}""", context.bot) - # TODO: create a meme object from meme_raw_upload - await _edit_upload_callback_caption( update.callback_query.message, localizer.t("upload.submitted", user_info["interface_lang"]), diff --git a/src/tgbot/repo/languages.py b/src/tgbot/repo/languages.py index 2b1b9aad..d03e11ba 100644 --- a/src/tgbot/repo/languages.py +++ b/src/tgbot/repo/languages.py @@ -33,8 +33,6 @@ async def add_user_languages( user_id: int, language_codes: Sequence[str], ) -> None: - # Prepare a list of dictionaries where each dictionary represents - # the values to be inserted for one row. values_to_insert = [ {"user_id": user_id, "language_code": language_code} for language_code in language_codes ] diff --git a/src/tgbot/repo/popups.py b/src/tgbot/repo/popups.py index 3199ebde..4a3ad6af 100644 --- a/src/tgbot/repo/popups.py +++ b/src/tgbot/repo/popups.py @@ -50,11 +50,12 @@ async def update_user_popup_log( user_popup_logs.update() .where(user_popup_logs.c.user_id == user_id) .where(user_popup_logs.c.popup_id == popup_id) - .where(user_popup_logs.c.reacted_at.is_(None)) # not sure abot that + # Only the first click may claim the reaction. + .where(user_popup_logs.c.reacted_at.is_(None)) .values(reacted_at=datetime.utcnow()) ) res = await execute(update_query) reaction_is_new = res.rowcount > 0 if not reaction_is_new: logging.warning(f"User {user_id} already reacted to popup {popup_id}!") - return reaction_is_new # so I can filter double clicks + return reaction_is_new diff --git a/src/tgbot/senders/alerts.py b/src/tgbot/senders/alerts.py index 2c7a3ffc..b3db2d5b 100644 --- a/src/tgbot/senders/alerts.py +++ b/src/tgbot/senders/alerts.py @@ -1,8 +1,6 @@ from telegram import Bot, Message from src import localizer - -# from src.tgbot.senders.keyboards import queue_empty_alert_keyboard from src.tgbot.user_info import get_user_info @@ -11,5 +9,4 @@ async def send_queue_preparing_alert(bot: Bot, user_id: int) -> Message: return await bot.send_message( chat_id=user_id, text=localizer.t("service.out_of_memes", user_info["interface_lang"]), - # reply_markup=queue_empty_alert_keyboard(), ) diff --git a/src/tgbot/senders/keyboards.py b/src/tgbot/senders/keyboards.py index 266b6f49..386cebc5 100644 --- a/src/tgbot/senders/keyboards.py +++ b/src/tgbot/senders/keyboards.py @@ -21,8 +21,6 @@ ) from src.tgbot.sharing import build_meme_share_button -# IDEA: use sometimes another emoji pair like ๐Ÿคฃ/๐Ÿคฎ - HEART_EMOJI = ["โค๏ธ", "โ™ฅ๏ธ", "๐Ÿ’™", "๐Ÿ’œ", "๐Ÿ’›", "๐Ÿงก", "๐Ÿ’š", "๐Ÿฉต"] @@ -36,7 +34,6 @@ def meme_reaction_keyboard( ): like = random.choice(HEART_EMOJI) dislike = "โฌ" - # like, dislike = "๐Ÿ‘", "๐Ÿ‘Ž" def reaction_callback(reaction_id: int) -> str: callback_data = MEME_BUTTON_CALLBACK_DATA_PATTERN.format(