Patreon não parece estar sincronizando em nossa instância

Oi,

Acho que posso ter encontrado a causa deste problema, ou pelo menos um problema de sincronização da API v2 do Patreon intimamente relacionado.

Tenho uma instância do Discourse usando a integração com o Patreon com a API v2. Os usuários existentes do Patreon continuam sendo atribuídos corretamente ao grupo patrons, mas um novo membro pagante não foi adicionado ao grupo, mesmo após executar manualmente Atualizar dados do Patreon. A sincronização foi concluída com sucesso e o endereço de e-mail no Patreon correspondia exatamente ao e-mail do usuário no Discourse.

Com patreon_verbose_log habilitado, a sincronização relata:

Patreon sync: found 4 rewards/tiers across 1 campaigns
Patreon sync complete: 1968 pledges, 0 users synced

Em seguida, inspecionei a resposta da API usada por ApiVersion::V2.

Para a primeira página com 1.000 membros:

Members in data: 1000
Included total: 1004
Included users: 1000
Keys for first included user: ["full_name"]
Included users with email: 0

No entanto, os próprios objetos member contêm o e-mail:

Members: 1000
Keys for first member:
["currently_entitled_amount_cents", "email", "full_name",
 "last_charge_date", "last_charge_status", "patron_status"]

Members with email: 928

Olhando para plugins/discourse-patreon/lib/api_version/v2.rb, MEMBER_FIELDS solicita explicitamente email, mas V2.extract apenas preenche o hash users a partir dos objetos user relacionados em included:

(member_data["included"] || []).each do |entry|
  if entry["type"] == "user" && entry["attributes"]["email"].present?
    users[entry["id"]] = entry["attributes"]["email"].downcase
  end
end

Parece que ele não usa entry["attributes"]["email"] dos objetos member, mesmo que seja esse o local onde o Patreon está retornando o e-mail na minha resposta da API.

Isso parece explicar por que a sincronização recupera com sucesso todas as 1.968 assinaturas, mas relata 0 users synced.

Também explica por que as associações existentes entre Patreon/Discourse podem continuar funcionando, enquanto novos membros não podem ser correspondidos por e-mail: os usuários existentes podem já ter seu patreon_id armazenado em user_custom_fields, enquanto um novo membro precisa que a associação entre ID do Patreon ↔ e-mail seja estabelecida.

Faria sentido que V2.extract preenchesse users diretamente ao processar cada membro, por exemplo:

patron_id = entry["relationships"]["user"]["data"]["id"]
attrs = entry["attributes"]

if attrs["email"].present?
  users[patron_id] = attrs["email"].downcase
end

mantendo o processamento atual de included como um fallback?