Este é um guia de referência para as consultas SQL das medalhas padrão e suas informações de acionamento (quando disponíveis).
Medalhas Principais
Aniversário
(Esta possui uma lógica especial no backend para escolher as datas, mas a incluirei de qualquer forma)
start_date = start_date.iso8601(6)
end_date = end_date.iso8601(6)
SELECT u.id
FROM users AS u
JOIN posts AS p ON p.user_id = u.id
JOIN topics AS t ON p.topic_id = t.id
WHERE u.id > 0
AND u.active
AND NOT u.staged
AND (u.silenced_till IS NULL OR u.silenced_till < '#{start_date}')
AND (u.suspended_till IS NULL OR u.suspended_till < '#{start_date}')
AND u.created_at <= '#{start_date}'
AND NOT p.hidden
AND p.deleted_at IS NULL
AND p.created_at BETWEEN '#{start_date}' AND '#{end_date}'
AND t.visible
AND t.archetype <> 'private_message'
AND t.deleted_at IS NULL
AND NOT EXISTS (SELECT 1 FROM user_badges AS ub WHERE ub.user_id = u.id AND ub.badge_id = #{Badge::Anniversary} AND ub.granted_at BETWEEN '#{start_date}' AND '#{end_date}')
AND NOT EXISTS (SELECT 1 FROM anonymous_users AS au WHERE au.user_id = u.id)
GROUP BY u.id
HAVING COUNT(p.id) > 0
Executar consulta de revogação diariamente
A consulta tem como alvo postagens
Acionador
Atualizar diariamente
Apreciado (número de curtidas em várias postagens)
As medalhas Apreciado, Respeitado e Admirado seguem o mesmo padrão, mas com valores diferentes para p.like_count e HAVING COUNT(*).
SELECT p.user_id, CURRENT_TIMESTAMP AS granted_at
FROM posts AS p
WHERE p.like_count >= #{like_count}
AND (:backfill OR p.user_id IN (:user_ids))
GROUP BY p.user_id
HAVING COUNT(*) > #{post_count}
Executar consulta de revogação diariamente
A consulta tem como alvo postagens
Acionador
Atualizar diariamente
Autobiógrafo
SELECT u.id user_id, CURRENT_TIMESTAMP granted_at
FROM users u
JOIN user_profiles up on u.id = up.user_id
WHERE bio_raw IS NOT NULL AND LENGTH(TRIM(bio_raw)) > 10 AND
uploaded_avatar_id IS NOT NULL AND
(:backfill OR u.id IN (:user_ids) )
Executar consulta de revogação diariamente
A consulta tem como alvo postagens
Acionador
Quando um usuário é editado ou criado
Básico (Níveis de Confiança)
As medalhas Básico, Membro, Regular e Líder seguem o mesmo padrão, mas com um valor diferente de trust_level.
SELECT u.id user_id, current_timestamp granted_at FROM users u
WHERE trust_level >= #{level.to_i} AND (
:backfill OR u.id IN (:user_ids)
)
Executar consulta de revogação diariamente
A consulta tem como alvo postagens
Acionador
Quando um usuário altera o nível de confiança
Certificado e Licenciado
Estas não possuem consulta SQL. Elas fazem parte do Discourse Narrative Bot e são concedidas programaticamente quando um usuário conclui os tutoriais interativos (Discobot).
Editor
SELECT p.user_id, min(p.id) post_id, min(p.created_at) granted_at
FROM badge_posts p
WHERE p.self_edits > 0 AND
(:backfill OR p.id IN (:post_ids) )
GROUP BY p.user_id
Executar consulta de revogação diariamente
A consulta tem como alvo postagens
Acionador
Quando um usuário edita ou cria uma postagem
Entusiasta
As medalhas Entusiasta, Aficionado e Devoto seguem o mesmo padrão, mas com um limite diferente de HAVING COUNT(*).
WITH consecutive_visits AS (
SELECT user_id
, visited_at
, visited_at - (DENSE_RANK() OVER (PARTITION BY user_id ORDER BY visited_at))::int s
FROM user_visits
), visits AS (
SELECT user_id
, MIN(visited_at) "start"
, DENSE_RANK() OVER (PARTITION BY user_id ORDER BY s) "rank"
FROM consecutive_visits
GROUP BY user_id, s
HAVING COUNT(*) >= #{days}
)
SELECT user_id
, "start" + interval '#{days} days' "granted_at"
FROM visits
WHERE "rank" = 1
Executar consulta de revogação diariamente
A consulta tem como alvo postagens
Acionador
Atualizar diariamente
Primeiro Emoji
Não há consulta SQL — a medalha é concedida no momento em que uma postagem qualificada é processada via CookedPostProcessor#grant_badges.
Critério: A postagem processada deve conter pelo menos um elemento img.emoji que não esteja dentro de um bloco aside.quote. Em outras palavras, emojis digitados diretamente no corpo da postagem contam; emojis que aparecem apenas dentro de uma passagem citada não contam.
Executar consulta de revogação diariamente
A consulta tem como alvo postagens
Acionador
Processamento da postagem (CookedPostProcessor)
Primeira Bandeira
SELECT pa1.user_id, pa1.created_at granted_at, pa1.post_id
FROM (
SELECT pa.user_id, MIN(pa.id) id
FROM post_actions pa
JOIN badge_posts p on p.id = pa.post_id
WHERE post_action_type_id IN (
SELECT f.id
FROM flags f
WHERE name != 'like'
AND score_type IS FALSE
AND require_message IS FALSE
)
AND (:backfill OR pa.post_id IN (:post_ids))
GROUP BY pa.user_id
) x
JOIN post_actions pa1 on pa1.id = x.id
Executar consulta de revogação diariamente
A consulta tem como alvo postagens
Acionador
Quando um usuário age sobre uma postagem
Primeira Curtida
SELECT pa1.user_id, pa1.created_at granted_at, pa1.post_id
FROM (
SELECT pa.user_id, MIN(pa.id) id
FROM post_actions pa
JOIN badge_posts p on p.id = pa.post_id
WHERE post_action_type_id = 2 AND
(:backfill OR pa.post_id IN (:post_ids) )
GROUP BY pa.user_id
) x
JOIN post_actions pa1 on pa1.id = x.id
Executar consulta de revogação diariamente
A consulta tem como alvo postagens
Acionador
Quando um usuário age sobre uma postagem
Primeiro Link
SELECT l.user_id, l.post_id, l.created_at granted_at
FROM
(
SELECT MIN(l1.id) id
FROM topic_links l1
JOIN badge_posts p1 ON p1.id = l1.post_id
JOIN badge_posts p2 ON p2.id = l1.link_post_id
WHERE NOT reflection AND p1.topic_id <> p2.topic_id AND not quote AND
(:backfill OR ( p1.id in (:post_ids) ))
GROUP BY l1.user_id
) ids
JOIN topic_links l ON l.id = ids.id
Executar consulta de revogação diariamente
A consulta tem como alvo postagens
Acionador
Quando um usuário edita ou cria uma postagem
Primeira Menção
SELECT acting_user_id AS user_id, MIN(target_post_id) AS post_id, MIN(p.created_at) AS granted_at
FROM user_actions
JOIN posts p ON p.id = target_post_id
JOIN topics t ON t.id = topic_id
JOIN categories c on c.id = category_id
WHERE action_type = 7
AND NOT read_restricted
AND p.deleted_at IS NULL
AND t.deleted_at IS NULL
AND t.visible
AND t.archetype <> 'private_message'
AND (:backfill OR p.id IN (:post_ids))
GROUP BY acting_user_id
Critério: A postagem processada deve ter gerado pelo menos um onebox — uma prévia rica de link gerada quando uma URL nua em sua própria linha é resolvida para um recurso incorporável.
Executar consulta de revogação diariamente
A consulta tem como alvo postagens
Acionador
Processamento da postagem (CookedPostProcessor)
Primeira Citação
SELECT ids.user_id, q.post_id, p3.created_at granted_at
FROM
(
SELECT p1.user_id, MIN(q1.id) id
FROM quoted_posts q1
JOIN badge_posts p1 ON p1.id = q1.post_id
JOIN badge_posts p2 ON p2.id = q1.quoted_post_id
WHERE (:backfill OR ( p1.id IN (:post_ids) ))
GROUP BY p1.user_id
) ids
JOIN quoted_posts q ON q.id = ids.id
JOIN badge_posts p3 ON q.post_id = p3.id
Critério:post.is_reply_by_email? deve ser true — ou seja, a postagem foi enviada respondendo a um e-mail de notificação do Discourse e não através da interface web. Requer que o recurso de resposta por e-mail recebida esteja habilitado no site.
SELECT views.user_id, i2.post_id, i2.created_at granted_at
FROM
(
SELECT i.user_id, MIN(i.id) i_id
FROM incoming_links i
JOIN badge_posts p on p.id = i.post_id
JOIN users u on u.id = i.user_id
GROUP BY i.user_id
) as views
JOIN incoming_links i2 ON i2.id = views.i_id
Executar consulta de revogação diariamente
A consulta tem como alvo postagens
Acionador
Atualizar diariamente
Novo Usuário do Mês
Não há consulta SQL registrada contra a medalha. Concedida por um trabalho agendado diário (Jobs::GrantNewUserOfTheMonthBadges). O trabalho roda todos os dias, mas concede apenas para o mês civil anterior e apenas uma vez por mês.
Elegibilidade: O candidato deve ter criado sua conta durante o mês civil anterior, ser ativo e não ser um usuário em estágio, não ser administrador ou moderador e não estar suspenso. Também deve ter postado em pelo menos 2 tópicos distintos com pelo menos 2 postagens no total e recebido pelo menos 2 curtidas.
Pontuação: Os candidatos são classificados por uma pontuação ponderada de curtidas. Cada curtida recebida é ponderada pelo nível de confiança da pessoa que a deu:
Quem deu a curtida
Peso
Administrador ou Moderador
3.0
Nível de Confiança 4
2.0
Nível de Confiança 3
1.5
Nível de Confiança 2
1.0
Nível de Confiança 1
0.25
Nível de Confiança 0
0.1
A pontuação final é SUM(curtidas ponderadas) / (5 + contagem de postagens) — dividir pela contagem de postagens reduz a vantagem de usuários que postam muito. Até 2 usuários podem receber a medalha por mês. Cada vencedor também recebe uma mensagem do sistema.
Executar consulta de revogação diariamente
A consulta tem como alvo postagens
Acionador
Trabalho agendado diário (concede para o mês civil anterior)
Resposta Legal (Curtidas em uma Postagem)
As medalhas Resposta Legal, Boa Resposta e Ótima Resposta seguem o mesmo padrão, mas com um limite diferente para p.like_count.
SELECT p.user_id, p.id post_id, CURRENT_TIMESTAMP granted_at
FROM badge_posts p
WHERE p.post_number > 1 AND p.like_count >= #{count.to_i} AND
(:backfill OR p.id IN (:post_ids) )
Executar consulta de revogação diariamente
A consulta tem como alvo postagens
Acionador
Quando um usuário age sobre uma postagem
Compartilhamento Legal (Compartilhamento de Links)
As medalhas Compartilhamento Legal, Bom Compartilhamento e Ótimo Compartilhamento seguem o mesmo padrão, mas com um limite diferente de HAVING COUNT(*).
SELECT views.user_id, i2.post_id, CURRENT_TIMESTAMP granted_at
FROM
(
SELECT i.user_id, MIN(i.id) i_id
FROM incoming_links i
JOIN badge_posts p on p.id = i.post_id
JOIN users u on u.id = i.user_id
GROUP BY i.user_id,i.post_id
HAVING COUNT(DISTINCT(i.ip_address, i.current_user_id)) >= #{count}
) as views
JOIN incoming_links i2 ON i2.id = views.i_id
Executar consulta de revogação diariamente
A consulta tem como alvo postagens
Acionador
Atualizar diariamente
Tópico Legal (Curtidas em um Tópico)
As medalhas Tópico Legal, Bom Tópico e Ótimo Tópico seguem o mesmo padrão, mas com um limite diferente para p.like_count.
SELECT p.user_id, p.id post_id, CURRENT_TIMESTAMP granted_at
FROM badge_posts p
WHERE p.post_number = 1 AND p.like_count >= #{count.to_i} AND
(:backfill OR p.id IN (:post_ids) )
Executar consulta de revogação diariamente
A consulta tem como alvo postagens
Acionador
Quando um usuário age sobre uma postagem
Por Amor (Máximo de Curtidas em um dia)
As medalhas Por Amor, Amor Maior e Louco de Amor seguem o mesmo padrão, mas com um limite diferente de HAVING COUNT(*).
SELECT gdl.user_id, CURRENT_TIMESTAMP AS granted_at
FROM given_daily_likes AS gdl
WHERE gdl.limit_reached
AND (:backfill OR gdl.user_id IN (:user_ids))
GROUP BY gdl.user_id
HAVING COUNT(*) >= #{count}
Executar consulta de revogação diariamente
A consulta tem como alvo postagens
Acionador
Atualizar diariamente
Link Popular (Cliques em Links)
As medalhas Link Popular, Link Quente e Link Famoso seguem o mesmo padrão, mas com um limite diferente de tl.clicks.
SELECT tl.user_id, post_id, CURRENT_TIMESTAMP granted_at
FROM topic_links tl
JOIN badge_posts p ON p.id = post_id
WHERE NOT tl.internal
AND tl.clicks >= #{count}
GROUP BY tl.user_id, tl.post_id
Executar consulta de revogação diariamente
A consulta tem como alvo postagens
Acionador
Atualizar diariamente
Promotor (Convites)
As medalhas Promotor, Campanhista e Campeão seguem o mesmo padrão, mas com um valor diferente de Nível de Confiança necessário para os convidados e um limite diferente de HAVING COUNT(*).
SELECT u.id user_id, CURRENT_TIMESTAMP granted_at
FROM users u
WHERE u.id IN (
SELECT invited_by_id
FROM invites i
JOIN invited_users iu ON iu.invite_id = i.id
JOIN users u2 ON u2.id = iu.user_id
WHERE i.deleted_at IS NULL
AND i.invited_by_id <> u2.id
AND u2.active
AND u2.trust_level >= #{trust_level.to_i}
AND u2.silenced_till IS NULL
GROUP BY invited_by_id
HAVING COUNT(*) >= #{count.to_i}
) AND u.active AND u.silenced_till IS NULL AND u.id > 0 AND
(:backfill OR u.id IN (:user_ids) )
Executar consulta de revogação diariamente
A consulta tem como alvo postagens
Acionador
Atualizar diariamente
Ler Diretrizes
SELECT user_id, read_faq granted_at
FROM user_stats
WHERE read_faq IS NOT NULL AND (user_id IN (:user_ids) OR :backfill)
Executar consulta de revogação diariamente
A consulta tem como alvo postagens
Acionador
Quando um usuário é editado ou criado
Leitor
SELECT id user_id, CURRENT_TIMESTAMP granted_at
FROM users
WHERE id IN
(
SELECT pt.user_id
FROM post_timings pt
JOIN badge_posts b ON b.post_number = pt.post_number AND
b.topic_id = pt.topic_id
JOIN topics t ON t.id = pt.topic_id
LEFT JOIN user_badges ub ON ub.badge_id = 17 AND ub.user_id = pt.user_id
WHERE ub.id IS NULL AND t.posts_count > 100
GROUP BY pt.user_id, pt.topic_id, t.posts_count
HAVING COUNT(*) >= t.posts_count
Executar consulta de revogação diariamente
A consulta tem como alvo postagens
Acionador
Obrigado (Curtidas dadas + Curtidas recebidas)
As medalhas Obrigado, Devolve e Empático seguem o mesmo padrão, mas com valores diferentes para us.likes_given e HAVING COUNT(*).
SELECT us.user_id, CURRENT_TIMESTAMP granted_at
FROM user_stats AS us
INNER JOIN posts AS p ON p.user_id = us.user_id
WHERE p.like_count > 0
AND us.likes_given >= #{likes_given}
AND (:backfill OR us.user_id IN (:user_ids))
GROUP BY us.user_id, us.likes_given
HAVING COUNT(*) > #{likes_received}
Executar consulta de revogação diariamente
A consulta tem como alvo postagens
Acionador
Atualizar diariamente
Bem-vindo
SELECT p.user_id, MIN(post_id) post_id, MIN(pa.created_at) granted_at
FROM post_actions pa
JOIN badge_posts p on p.id = pa.post_id
WHERE post_action_type_id = 2 AND
(:backfill OR pa.post_id IN (:post_ids) )
GROUP BY p.user_id
Executar consulta de revogação diariamente
A consulta tem como alvo postagens
Acionador
Quando um usuário age sobre uma postagem
Editor de Wiki
SELECT pr2.user_id, pr2.post_id, pr2.created_at granted_at
FROM
(
SELECT MIN(pr.id) id
FROM post_revisions pr
JOIN badge_posts p on p.id = pr.post_id
WHERE p.wiki
AND NOT pr.hidden
AND (:backfill OR p.id IN (:post_ids))
GROUP BY pr.user_id
) as X
JOIN post_revisions pr2 ON pr2.id = X.id
SELECT user_id, created_at AS granted_at, post_id
FROM (
SELECT ru.post_id, ru.user_id, ru.created_at,
ROW_NUMBER() OVER (PARTITION BY ru.user_id ORDER BY ru.created_at) AS row_number
FROM discourse_reactions_reaction_users ru
JOIN badge_posts p ON ru.post_id = p.id
WHERE :backfill
OR ru.post_id IN (:post_ids)
) x
WHERE row_number = 1
SELECT post_id, user_id, created_at AS granted_at
FROM (
SELECT p.id AS post_id, p.user_id, dsst.created_at,
ROW_NUMBER() OVER (PARTITION BY p.user_id ORDER BY dsst.created_at) AS row_number
FROM discourse_solved_solved_topics dsst
JOIN badge_posts p ON dsst.answer_post_id = p.id
JOIN topics t ON p.topic_id = t.id
WHERE p.user_id <> t.user_id -- ignorar tópicos resolvidos pelo OP
AND (:backfill OR p.id IN (:post_ids))
) x
WHERE row_number = 1
Executar consulta de revogação diariamente
A consulta tem como alvo postagens
Acionador
Quando um usuário edita ou cria uma postagem
Orientador
As medalhas para Orientador, Sabichão e Instituição de Soluções seguem o mesmo padrão, mas com um limite diferente de HAVING COUNT (*) >=:
SELECT p.user_id, MAX(pcf.created_at) AS granted_at
FROM post_custom_fields pcf
JOIN badge_posts p ON pcf.post_id = p.id
JOIN topics t ON p.topic_id = t.id
WHERE pcf.name = 'is_accepted_answer'
AND p.user_id <> t.user_id -- ignorar tópicos resolvidos pelo OP
AND (:backfill OR p.id IN (:post_ids))
GROUP BY p.user_id
HAVING COUNT(*) >= #{min_count}