Помощь в разработке плагина для кастомной интеграции

Всем привет! Я изучаю различные платформы форумов, чтобы создать сообщество для бразильских конкурсных программистов (CP), и, кажется, я уже нашёл идеальное решение!

Одной из функций, которую я хотел бы реализовать на этом форуме, является интеграция с Codeforces — онлайн-платформой, где проходят соревнования и обсуждения по CP. На Codeforces у участников есть рейтинг, который меняется в зависимости от их результатов на рейтинговых соревнованиях. Я хочу дать пользователям возможность отображать свои заслуженные рейтинги в профилях на форуме.

Первое, что должен сделать этот плагин, который я планирую разработать, — это позволить пользователю ввести свой никнейм на Codeforces и каким-то образом подтвердить, что этот никнейм действительно принадлежит пользователю форума. Для этого я думаю использовать плагин Custom Wizard, чтобы запросить никнейм, затем отправить случайную строку через API Codeforces Talks, которую пользователь должен будет правильно ввести обратно. Если всё в порядке, сохранить никнейм в пользовательском поле. Выглядит ли это нормально? Сделали бы вы это по-другому?

Теперь я хорошо знаю своих [будущих] пользователей, ведь сам я CPer! Им понравится отображать свой рейтинг через цвет никнейма на форуме. То есть, если рейтинг выше 1400, никнейм будет голубым (cyan), если выше 1600 — синим, и так далее.

Моё быстрое исследование по кастомизации цветов никнеймов на Discourse привело меня к следующему решению: создать группу пользователей для каждого диапазона рейтинга, а затем динамически назначать пользователей в правильную группу. (Есть ли лучший или более простой способ?)

Итак, когда пользователь впервые свяжет свой аккаунт Codeforces со своим профилем, я могу получить его рейтинг и назначить соответствующую группу (specialist, expert, candidate master, …, international grandmaster). Однако рейтинг может быстро измениться, и я хотел бы автоматически обновлять группу пользователя при изменении его рейтинга.

Я придумал несколько способов реализации этого и хотел бы получить предложения, какой из них выбрать, а также, по возможности, руководство по специфике Discourse:

  • Запуск регулярной задачи (Job), которая обновляет индивидуальный рейтинг и группу каждого пользователя (много внешних запросов к API Codeforces).
  • Запуск регулярной задачи, которая обрабатывает соревнования по мере их проведения. Поскольку рейтинг может измениться только во время соревнования, если начальные рейтинги пользователей корректны, обрабатывая изменения рейтинга по мере их возникновения, я смогу обновлять только изменённые рейтинги и поддерживать согласованность с одним внешним запросом к API.
  • Реализация собственного резолвера рейтинга и групп. В этом случае я буду хранить дату последнего получения рейтинга, и при следующем запросе GET rating/groups, если рейтинг устарел, я сделаю запрос к API Codeforces и обновлю рейтинг и группу пользователя. Это моё предпочтительное решение, так как оно кажется простым и обеспечивает eventual consistency. Однако я не уверен, как его реализовать, каковы последствия динамического удаления и добавления групп пользователю, или даже возможно ли это вообще в виде плагина.

Извините за простыню текста! Буду рад любым комментариям по всем этим вопросам. Спасибо за внимание.

Как только вы получите их идентификатор Codeforces в поле user_custom_field, обновление любых данных из их профиля при входе в систему станет достаточно простым (назначение групп, сохранение любых данных профиля из Codeforces в пользовательские поля и так далее).

Что-то вроде этого:

after_initialize do
  DiscourseEvent.on(:user_logged_in) do |user|
    # получаем данные из Codeforces
      if codeforce_rating > 1400
          group = Group.find_by(name: group_1400)
          if group
            gu = GroupUser.find_by(group_id: group.id, user_id: user.id)
            GroupUser.create(group_id: group.id, user_id: user.id) unless gu
          end
        end
      end

  end

end

Этот способ очень прост, и вы будете обновлять данные только для тех, кто действительно активен в сообществе.

Это прекрасно! Я рад, что уточнил всё заранее, прежде чем что-то внедрять.

Огромное спасибо!

Если все ваши пользователи — пользователи Codeforces, я бы реализовал единый вход (SSO), где Codeforces выступает провайдером аутентификации. Таким образом, не потребуется дополнительного шага аутентификации, а процесс регистрации и входа для ваших пользователей станет удобнее. Более того, вы даже сможете синхронизировать группы!

Я бы реализовал это на стороне источника: там, где Codeforces обнаруживает изменение рейтинга, он должен делать API-запрос к Discourse для обновления рейтинга (или любого другого свойства пользователя, зависящего от рейтинга). Таким образом, всё всегда будет актуально. При необходимости синхронизации данных опрос (polling) в любом случае приводит к проблемам, поэтому избегайте его, где это возможно, и используйте событийно-ориентированный подход.

Я полагал, что использовать Codeforces в качестве главного провайдера единого входа (SSO) невозможно. Если это так, то вам определенно стоит сделать это именно таким образом!

Предположение @pfaffman о том, что использование Codeforces в качестве провайдера аутентификации невозможно, было верным. Codeforces фактически поддерживается одним человеком, и я сомневаюсь, что он реализует что-либо для проекта, который всё ещё находится на стадии планирования или поиска финансирования.

Я согласен, что опрос (polling) — это неоптимальное решение! Именно поэтому предложенный вариант — запрашивать данные при входе пользователя — кажется хорошим компромиссом между [интересом пользователя в] согласованности данных и тем, чтобы не перегружать серверы Codeforces, привлекая к себе внимание и рискуя быть заблокированным.

Если наш конкретный случай для бразильского сообщества окажется успешным, и другие сообщества попытаются сделать то же самое, возможно, Майк (тот самый человек, отвечающий за Codeforces), реализует что-то подобное.

Тем не менее, большое спасибо за предложения! Я буду иметь их в виду, когда в июне следующего года снова встречу Майка, чтобы попытаться убедить его помочь и, возможно, оптимизировать то решение, которое к тому времени будет использоваться на нашем форуме.