Help designing custom integration plugin

Hello everyone! I am researching different forum platforms in order to build a community for Brazilian competitive programmers (CP) but it seems I have found it!

One feature I’d like to have on this forum is an integration with Codeforces - an online platform that hosts contests and discussions about CP. Within Codeforces, contestants have a rating that changes accordingly to the contestant’s performance in rated contests. I would like to give my users the ability to display their hard earned ratings on their forum’s profile.

The first thing this plugin I want to develop would have to do, then, is to allow the user to input their Codeforces’ handle and then somehow authenticate that the handle belongs to the forum user. In order to do that I’m thinking about using the Custom Wizard Plugin to ask their handle, then send a random string via Codeforces Talks API which the user would have to input back correctly and if everything is ok, store their handle on a custom user field. Does this seem okay? Would you do this differently?

Now, I know my [future] users, after all I’m a CPer myself! They would enjoy displaying their rating through their forum’s handle color. That is, if you have rating higher than 1400, you have a cyan colored handle, if it is higher than 1600, it’s blue colored, and so on.

My quick research on how to customize handle’s colors on Discourse brought me to the following solution. I should create a user group for each rating bracket, and then dynamically assign my users to the correct group. (Is there a better/easier way?)

So, when the user initially links their Codeforces account to their profile, I could retrieve their rating and assign to them the correct group (specialist, expert, candidate master, …, international grandmaster). However, the rating might soon change and I’d like to automatically update the user’s group when their rating change.

Now, I thought about some ways of accomplishing that and I’d like suggestions on which one to follow, and, if possible, some guiding on the Discourse-specifics:

  • Have a regularly running Job that updates each users’ individual rating and group (a lot of external requests hitting Codeforces’ API)
  • Have a regularly running Job that processes contests as they happen. Since ratings can only change during a contest, if the user’s rating are initially consistent, by processing rating changes as they happen I can update only the ratings that changed and maintain consistency with a single external API hit
  • Have a custom rating and group resolver. So I’d have to store the last rating retrieval and upon the next GET rating/groups, if the rating is stale, I’d hit Codeforces API and update the user’s rating/groups. This is my preferred solution because it seems easy and it will always be eventually consistent. However I am not so sure how I would go about implementing this or the consequences of dynamically removing and adding a group to a user. Or even if this is possible at all as a plugin.

Welp, I apologize for the Wall of Text! I’d love to have any kind of input about all of this. Thanks for attention.

1 Like

Once you have their Codeforces handle in a user_custom_field then it should be easy enough to update whatever stuff you want from their profile (assign groups, stick whatever profile data from codeforces into user custom fields, and so on) when they log in.

Something like:

after_initialize do
  DiscourseEvent.on(:user_logged_in) do |user|
    # get stuff from codeforce
      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

This way is really easy and you’d update data only for people who are actually active in the community.

4 Likes

That’s lovely! I’m glad I asked around before implementing anything.

Thank you very much!

1 Like

If all your users are Codeforces users, I would implement single sign on (where Codeforces is the auth provider). This way there is no extra authentication step needed and your users would have a better sign up / authentication flow as well. You could even sync the groups like this!

I would do this from the source, i.e. where Codeforces knows the rating has changed, have it make an API call towards Discourse to update the rating (or whatever user property is influenced by that rating). This way everything will be always up to date. When you need to synchronize stuff polling always leads to problems one way or another, so avoid it where you can and use an event based approach.

3 Likes

I assumed that using codeforce as the SSO master wasn’t possible. If it is, you definitively want to do it this way!

1 Like

@pfaffman’s assumption that it wasn’t possible to use Codeforces as an auth provider was correct. Codeforces is basically maintained by one guy and I doubt he would implement anything for something that is still in the planning/funding phase.

I agree that polling is suboptimal! That is why the proposed solution of querying it when the user logs in seems to be a good balance between [user’s interest of] consistency and not hitting Codeforces’ servers enough to call attention and possibly being blocked off.

If our specific case for the Brazilian community is successful and other communities try to do the same, maybe Mike (the one guy responsible for Codeforces) will implement something like this.

Nonetheless, thank you very much for the suggestions! I will keep them in mind for the next time I meet Mike in June next year when I can try to convince him about helping out and possibly optimize whichever solution our forum might be using by then.

1 Like