Skip to content

Feature/game chat and cities - #174

Merged
denis1011101 merged 4 commits into
mainfrom
feature/game-chat-and-cities
Sep 1, 2026
Merged

Feature/game chat and cities#174
denis1011101 merged 4 commits into
mainfrom
feature/game-chat-and-cities

Conversation

@denis1011101

Copy link
Copy Markdown
Owner

No description provided.

denis1011101 and others added 3 commits September 1, 2026 20:13
normalized_city жил в LocationFilters — концерне контроллеров, поэтому до
него дотягивался только веб. Модель и телеграм-хендлеры сравнивали города
сырым downcase, и «Ekaterinburg» у пользователя не совпадал с
«Yekaterinburg» у кортов: Court.sorted_for_user, список турниров в боте и
поиск тренера молча считали город чужим.

Третья копия нашлась в TournamentsFlow: там ведущая Y перед гласной
срезалась регуляркой, то есть York превращался в «ork», а Yokohama — в
«okohama». Теперь все шесть точек зовут City.normalize_name, а таблица
вариантов написания лежит в модели рядом с canonical_name.

Заодно город в боте больше не проходит через titleize при выводе — он
ломает дефисные названия, а из справочника город и так приходит
каноническим.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fs32cQQXRoYnQCqdL8jTZK
Первый срез: текст, без медиа и без веб-зеркала.

Режим включается только явным нажатием кнопки — принятое приглашение само
по себе ничего не включает, иначе согласием оказывалось бы бездействие.
Игру выбираем сразу: одна — подтверждаем, несколько — показываем
клавиатуру, потому что неявный дефолт это ровно тот отказ, которого мы
избегаем, — сообщение уходит не в ту компанию.

Указатель активной игры живёт в кеше: его потеря безопасна, бот просто
снова спросит игру. Права он не даёт — участие и открытость чата
перепроверяются и перед постановкой в очередь, и перед каждой отправкой,
потому что человека могли вывести из состава в любой момент.

Дедуп по message_id обязателен: Poller держит offset только в памяти
процесса, после рестарта Telegram отдаёт неподтверждённые обновления
заново, а деплой ходит по крону.

Рассылка — по джобе на получателя, с переносом по retry_after на 429:
лимит примерно одно сообщение в секунду на чат, и упершаяся доставка не
должна задерживать остальных. Отправляем без parse_mode — это чужой
текст, а не наш шаблон, и одиночный `_` или `[` иначе роняет отправку.

Автовыход: конец дня игры, выход из состава (колбэки Participation) и
пакетный сброс в ResetParticipationsJob, который идёт мимо колбэков.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fs32cQQXRoYnQCqdL8jTZK
…постинг

Вложения к игре:
- game_media.show_in_feed: попадание в публичную ленту Tennis Life стало
  отдельным действием автора или админа, а не следствием загрузки. Права
  проверяет контроллер, а не факт отрисовки галки. Уже загруженное миграция
  помечает показанным: снимать с витрины задним числом чужие фото нельзя.
- game_media.title: необязательное название, видно подписью в карточке и в
  тексте уведомления.
- Уведомление о видео больше не называет обучающим ролик из обычной игры.
  Подпись автора собирает User#broadcast_label — имя и @ник, но никогда
  e-mail: письмо уходит всей команде.
- Нормализация телеграм-ника переехала из хелпера в User, хелпер делегирует.
- Раздел на странице игры оформлен как остальные секции: карточка с рамкой,
  удаление в подписи вместо оверлея, форма загрузки в своей рамке.

Кросспостинг: Social::* с общим интерфейсом для threads, bluesky и nostr,
ежедневная задача PostDailySocialPostJob и таблица social_posts с ключом
дедупликации.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fs32cQQXRoYnQCqdL8jTZK

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0603c05438

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread app/jobs/post_social_job.rb Outdated
Comment on lines +16 to +19
post_id = adapter.new(content: content, locale: Social.locale_for(network)).call
return log(kind, dedup_key, network, "adapter returned nothing") unless post_id

record(kind, dedup_key, network, post_id)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Reserve the dedup key before publishing

When two jobs for the same network/material overlap—for example after a rapid urgent-search off/on cycle—both can pass already_posted? and call the external adapter. The unique index is only exercised afterward, so rescuing RecordNotUnique prevents a duplicate database row but cannot undo the second public post. Claim or lock the dedup record before performing the external side effect.

Useful? React with 👍 / 👎.

Comment thread app/services/social/content/daily.rb Outdated
Comment on lines +36 to +40
def available?
case variant
# Дата в ключе, а джоба могла отлежаться в очереди — «Tomorrow» должно
# оставаться завтрашним днём, иначе анонс уйдёт задним числом.
when "upcoming" then game.present? && game.occurrence_date?(date + 1) && game.spots_left.positive?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Recheck cancellation before publishing upcoming games

If an occurrence is cancelled after DailyPlanner selects it but before the queued network jobs run, this check still considers the content available and publishes a “Tomorrow” invitation for the cancelled game. The planner initially excludes game.cancelled_on?(date + 1), so the delayed availability check needs to enforce the same condition.

Useful? React with 👍 / 👎.

Comment on lines +32 to +34
def handle_rate_limit(response, game_id, recipient_id, text)
return true if response.is_a?(Hash) && response["ok"]
return false unless response.is_a?(Hash) && response["error_code"].to_i == 429

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Retry transient non-429 Telegram failures

When Telegram returns a parsed failure response such as { "ok": false, "error_code": 500 }, Telegram::Api.post returns that hash, but this branch treats every non-429 error as a successful job completion. A transient Telegram server failure therefore silently loses the chat message instead of retrying; distinguish retryable 5xx responses from permanent request errors and raise or reschedule them.

Useful? React with 👍 / 👎.

PostSocialJob занимал ключ уже после похода в сеть, так что быстрый цикл
«выключил-включил срочный поиск» при нескольких воркерах мог дать два
публичных поста: уникальный индекс спасал базу, но не отменял второй пост.
Теперь заявка на dedup_key создаётся до вызова адаптера и снимается при
ошибке, а брошенная заявка перехватывается через 30 минут — иначе упавший
воркер заблокировал бы материал навсегда. Операции идут по токену заявки,
чтобы не затереть чужой перехват.

Social::Content::Daily перепроверяла дату занятия, но не отмену, и анонс
отменённой игры всё равно ушёл бы наружу.

Telegram::DeliverChatMessageJob считал успехом всё, кроме 429, поэтому
пятисотка от Telegram молча теряла сообщение чата. Временные ошибки
повторяются с backoff, постоянные логируются.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EUNN1ijLP4d1rm9c7hL4pa
@denis1011101
denis1011101 merged commit 5fccad1 into main Sep 1, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant