Ayuda para diseñar un plugin de integración personalizado

¡Hola a todos! Estoy investigando diferentes plataformas de foros para crear una comunidad para programadores competitivos (CP) brasileños, pero parece que ¡ya la encontré!

Una característica que me gustaría tener en este foro es una integración con Codeforces, una plataforma en línea que alberga concursos y discusiones sobre CP. Dentro de Codeforces, los concursantes tienen una puntuación que cambia según su rendimiento en concursos con puntuación. Me gustaría que mis usuarios pudieran mostrar sus puntuaciones ganadas con esfuerzo en el perfil de su foro.

Lo primero que tendría que hacer este plugin que quiero desarrollar, entonces, es permitir que el usuario ingrese su nombre de usuario (handle) de Codeforces y, de alguna manera, autenticar que ese nombre de usuario pertenece al usuario del foro. Para ello, estoy pensando en usar el plugin Custom Wizard para pedirles su nombre de usuario, luego enviar una cadena aleatoria a través de la API de Codeforces Talks que el usuario tendría que ingresar correctamente, y si todo está bien, guardar su nombre de usuario en un campo de usuario personalizado. ¿Esto te parece bien? ¿Lo harías de manera diferente?

Ahora, conozco a mis usuarios [futuros], ¡después de todo soy un CPer! Les encantaría mostrar su puntuación a través del color de su nombre de usuario en el foro. Es decir, si tienes una puntuación superior a 1400, tu nombre de usuario será de color cian; si es superior a 1600, será de color azul, y así sucesivamente.

Mi rápida investigación sobre cómo personalizar los colores de los nombres de usuario en Discourse me llevó a la siguiente solución. Debería crear un grupo de usuarios para cada rango de puntuación y luego asignar dinámicamente a mis usuarios al grupo correcto. ¿Hay una forma mejor o más fácil?

Así que, cuando el usuario vincule inicialmente su cuenta de Codeforces a su perfil, podría recuperar su puntuación y asignarle el grupo correcto (specialist, expert, candidate master, …, international grandmaster). Sin embargo, la puntuación podría cambiar pronto y me gustaría actualizar automáticamente el grupo del usuario cuando su puntuación cambie.

Ahora, pensé en algunas formas de lograrlo y me gustaría sugerencias sobre cuál seguir y, si es posible, alguna orientación sobre los aspectos específicos de Discourse:

  • Tener un Job que se ejecute regularmente y actualice la puntuación y el grupo individual de cada usuario (muchas solicitudes externas a la API de Codeforces).
  • Tener un Job que se ejecute regularmente y procese los concursos a medida que ocurren. Dado que las puntuaciones solo pueden cambiar durante un concurso, si las puntuaciones de los usuarios son inicialmente consistentes, al procesar los cambios de puntuación a medida que ocurren, puedo actualizar solo las puntuaciones que cambiaron y mantener la consistencia con una sola llamada a la API externa.
  • Tener un resolvedor personalizado de puntuación y grupo. En este caso, tendría que guardar la última recuperación de puntuación y, en la siguiente solicitud GET rating/groups, si la puntuación está desactualizada, llamaría a la API de Codeforces y actualizaría la puntuación/grupo del usuario. Esta es mi solución preferida porque parece sencilla y siempre será eventualmente consistente. Sin embargo, no estoy muy seguro de cómo implementarla ni de las consecuencias de agregar y eliminar dinámicamente un grupo de un usuario. O incluso si esto es posible en absoluto como un plugin.

Bueno, ¡me disculpo por el muro de texto! Me encantaría recibir cualquier tipo de opinión sobre todo esto. Gracias por su atención.

Una vez que tengas su identificador de Codeforces en un user_custom_field, debería ser lo suficientemente sencillo actualizar cualquier cosa que desees de su perfil (asignar grupos, insertar cualquier dato de perfil de Codeforces en los campos personalizados del usuario, etc.) cuando inicien sesión.

Algo así:

after_initialize do
  DiscourseEvent.on(:user_logged_in) do |user|
    # obtener datos de 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

De esta manera es realmente fácil y solo actualizarías los datos de las personas que realmente están activas en la comunidad.

¡Qué bonito! Me alegro de haber preguntado antes de implementar nada.

¡Muchas gracias!

Si todos tus usuarios son de Codeforces, implementaría un inicio de sesión único (donde Codeforces actúa como proveedor de autenticación). De esta manera, no se necesitaría un paso adicional de autenticación y tus usuarios tendrían un flujo de registro/autenticación más fluido. ¡Incluso podrías sincronizar los grupos así!

Lo haría desde la fuente; es decir, donde Codeforces detecta que la clasificación ha cambiado, que realice una llamada a la API hacia Discourse para actualizar la clasificación (o cualquier propiedad del usuario que se vea afectada por esa clasificación). De esta manera, todo siempre estará actualizado. Cuando necesitas sincronizar cosas, el sondeo (polling) siempre acaba generando problemas de una u otra forma, así que evítalo cuando puedas y utiliza un enfoque basado en eventos.

Asumí que no era posible usar Codeforces como maestro de SSO. ¡Si lo es, definitivamente querrás hacerlo de esta manera!

La suposición de @pfaffman de que no era posible utilizar Codeforces como proveedor de autenticación era correcta. Codeforces es mantenido básicamente por una sola persona y dudo que implemente algo para un proyecto que aún está en fase de planificación o financiación.

¡Coincido en que el sondeo (polling) es subóptimo! Por eso la solución propuesta de consultar los datos cuando el usuario inicia sesión parece ser un buen equilibrio entre [el interés del usuario de] consistencia y no saturar los servidores de Codeforces hasta llamar la atención y posiblemente ser bloqueado.

Si nuestro caso específico para la comunidad brasileña tiene éxito y otras comunidades intentan hacer lo mismo, quizás Mike (la única persona responsable de Codeforces) implemente algo así.

No obstante, ¡muchas gracias por las sugerencias! Las tendré en cuenta la próxima vez que me encuentre con Mike en junio del año que viene, cuando pueda intentar convencerlo de que nos ayude y, posiblemente, optimizar cualquier solución que nuestro foro esté utilizando para entonces.