Notificações em tempo real no GLPI com Smart Notify

No GLPI padrão, notificação é e-mail assíncrono que ninguém acompanha. O módulo gratuito Smart Notify coloca um sino na navbar que faz polling à base (modo delta ~93 bytes) e agrega dezasseis fontes de eventos ITIL. Veja como o poll funciona, o SQL de diagnóstico, a matriz de quais eventos deixar ligados e as armadilhas de terreno - do autoplay do Chrome ao erro 1064 do MySQL 8.

No GLPI padrão, notificação é e-mail assíncrono: cai no spam, o técnico não vive no webmail e o seguimento fica horas sem resposta. O Smart Notify troca esse modelo por um sino na própria barra do GLPI que faz polling à base de dados e agrega mais de uma dúzia de eventos ITIL quase em tempo real. Na sustentação de parques GLPI de clientes, aprendemos que o difícil não é ligar o sino - é calibrar o que ele mostra para não se tornar ruído. Este guia mostra como o poll funciona por dentro, um SQL de diagnóstico, a matriz de quais eventos deixar ligados e as armadilhas de terreno que mais deitam abaixo a instalação.

Porque o e-mail nativo não resolve a notificação operacional

As notificações nativas do GLPI dependem da fila queuednotification e de modelos de e-mail. É um modelo assíncrono e fora da ferramenta: o técnico tem de alternar entre o GLPI e a caixa de entrada, o e-mail transacional cai no spam empresarial e ninguém acompanha o webmail em tempo real. O resultado clássico da operação é uma validação parada durante horas e uma tarefa atribuída que ninguém viu.

O sino resolve isto para quem está autenticado no GLPI. Mas atenção à primeira armadilha: o sino não substitui o e-mail para o requerente. Notifica o técnico em sessão, não o cliente que abriu o ticket pelo self-service. O erro comum é desligar as notificações por e-mail a pensar que o sino cobre toda a gente, e o requerente deixa de ser avisado. Sino e e-mail são camadas complementares, não exclusivas.

Como o sino funciona por dentro (polling + delta)

O JavaScript do sino faz um poll a cada 30 segundos (refresh_interval por omissão) contra um endpoint AJAX. A cada ciclo o backend percorre até dezasseis fontes de eventos, monta a lista, aplica a retenção (retention_days, 90 dias por omissão), corta em max_notifications (50 por omissão) e ordena por data decrescente. Cada notificação recebe um hash estável (sha1(tipo_id_timestamp)): é ele que marca o item como lido sem acrescentar um campo novo ao ticket.

O que só quem opera repara é o custo desse poll a cada 30 s por técnico autenticado. Por isso o endpoint usa modo delta: o cliente devolve o hash do último estado (state=); se nada mudou, a resposta cai de cerca de 18 KB para ~93 bytes. E o read-check passou a ser um único SELECT em lote: antes era um SELECT por notificação, chegando a ~700 por poll em utilizadores com grande backlog. O GET também chama session_write_close() logo no início, para não segurar o lock de sessão do PHP enquanto percorre a base.

# O sino faz polling a cada 30s neste endpoint (encaminhado pelo module_ajax):
GET /plugins/nextool/ajax/module_ajax.php?module=smartnotify&file=notifications.php&action=get&state=${HASH}

# Sem novidade desde o ultimo poll, a resposta e um delta de ~93 bytes:
{ "success": true, "unchanged": true, "state": "...md5...", "unread_count": 4 }

# So quando algo muda vem o payload completo (~18KB) com o array notifications[].

As fontes de eventos e o que deixar ligado

A configuração global expõe dezasseis gatilhos e cada técnico ainda afina os seus na janela de preferências (acessível diretamente a partir do menu do sino). Duas regras de targeting importam: o seguimento que você escreveu não o notifica (a consulta filtra users_id != utilizador), e a notificação chega por envolvimento direto ou por grupo técnico (notify_group_involvement), respeitando sempre as entidades ativas da sessão.

Na sustentação, três gatilhos vêm desligados por omissão de propósito, e a experiência confirma a escolha: os documentos anexados são ruído em parques com muito carregamento; os novos problemas e as novas mudanças são eventos menos frequentes que poluem o sino de quem só trata de tickets. Ligar tudo é o caminho mais rápido para a fadiga de alerta: o técnico começa a ignorar o sino e então ele não serve para nada.

Família de eventoGatilho (config)Por omissãoNota de terreno
Seguimentos (ticket, problema, mudança)notify_*_followupLigadoÉ o que mais move o backlog; mantenha
Validações e respostas de validaçãonotify_*_validation(_response)LigadoUma aprovação parada é um gargalo silencioso
Tarefas atribuídasnotify_*_taskLigadoÉ a fila de trabalho do técnico
Soluções registadasnotify_solutionLigadoFecha o ciclo de quem acompanha
Documentos anexadosnotify_documentDesligadoRuidoso onde há muito anexo
Novos ticketsnotify_new_ticketLigadoSinal de entrada da fila
Novos problemas / mudançasnotify_new_problem / _changeDesligadoMenos comuns; ligue por perfil
Avisos manuais (broadcast)notify_manualLigadoManutenção, mudança de processo

Diagnóstico via SQL: quem silenciou o quê

Quando um cliente se queixa de que "o sino não avisa", o problema é quase sempre uma preferência individual ou a entidade errada, não um bug. Estas três consultas resolvem a triagem diretamente na base (tabelas reais do módulo):

-- 1. Quantos tecnicos silenciaram cada tipo de evento (acha ruido/fadiga):
SELECT
  SUM(ticket_followups = 0) AS mudo_followup_ticket,
  SUM(solutions = 0)        AS mudo_solucao,
  SUM(documents = 0)        AS mudo_documento,
  SUM(sound_enabled = 0)    AS som_desligado
FROM glpi_plugin_nextool_smartnotify_preferences;

-- 2. Registos de leitura por utilizador (backlog e ultima atividade):
SELECT users_id, COUNT(*) AS lidas_registadas, MAX(date_read) AS ultima_leitura
FROM glpi_plugin_nextool_smartnotify_read
GROUP BY users_id
ORDER BY ultima_leitura DESC;

-- 3. Broadcasts manuais ativos e o seu alvo (all/user/group/entity):
SELECT id, title, priority, target_type, target_id, entities_id, date_creation
FROM glpi_plugin_nextool_smartnotify_manual
WHERE is_active = 1
ORDER BY date_creation DESC;

Sons por prioridade e a armadilha do autoplay

As notificações emitem som via Web Audio API, com quatro timbres mapeados por prioridade. Antes de olhar para o ecrã, o técnico ouve se o que chegou é rotina ou urgência:

PrioridadeSomTimbre (aprox.)
BaixasubtleCurto e grave (440 Hz)
NormaldefaultPadrão (523 Hz)
AltaalertMédio (659 Hz)
UrgentealarmLongo e agudo (880 Hz)

A armadilha aqui não é do módulo, é do navegador: a política de autoplay do Chrome só permite criar o AudioContext depois do primeiro gesto do utilizador. Na prática, o primeiro som de uma sessão só sai depois do primeiro clique na página. Todos os meses alguém abre um ticket a dizer "o som não funciona": a resposta é clicar uma vez na página. Não é um bug, é a Web Audio API a respeitar o navegador.

Broadcasts manuais e limpeza automática

Os administradores enviam avisos manuais com targeting preciso: todos, um utilizador, um grupo ou uma entidade (a coluna target_type na tabela _manual). É o canal certo para uma janela de manutenção ou uma mudança de processo sem enviar e-mail para toda a base. A retenção é tratada por uma CronTask diária (smartnotify_cleanup), registada na instalação como MODE_EXTERNAL. Isto importa: em MODE_INTERNAL a tarefa corre por web-hit e pode bloquear em state=2. Dispare o cron do GLPI por fora (no GLPI 11, front/cron.php) e a limpeza acontece sozinha conforme retention_days e logs_retention_days.

# CronTask registada na instalacao (idempotente), 1x/dia, MODE_EXTERNAL:
#   Nome: smartnotify_cleanup   Frequencia: DAY_TIMESTAMP (86400s)
# Dispare o cron do GLPI por fora (nunca dependa do MODE_INTERNAL via web-hit):
* * * * * www-data /usr/bin/php /var/www/html/front/cron.php >/dev/null 2>&1

A armadilha que deita abaixo a instalação: MySQL 8 vs MariaDB

Um caso real de sustentação (junho de 2026, cliente em MySQL 8.0.46): a instalação do módulo tornava-se um HTTP 500 e o parque ficava sem sino. A causa raiz não estava no GLPI, estava no SQL: um bloco ALTER TABLE ... ADD COLUMN IF NOT EXISTS, sintaxe exclusiva do MariaDB. No MySQL 8 esse IF NOT EXISTS ao nível da coluna não existe e o statement falha com erro 1064, deitando abaixo toda a instalação. A lição vale para qualquer plugin GLPI: o DDL condicional não é portável entre MariaDB e MySQL. A correção (versão 2.2.1+) retirou o DDL condicional do .sql e passou as migrações para PHP com helpers portáveis do plugin base. Se corre MySQL 8, garanta o módulo na 2.2.1 ou superior.

Na NexTool implementamos e sustentamos ambientes GLPI de ponta a ponta. Se a sua equipa convive com tickets esquecidos e notificações por e-mail que ninguém abre, o Smart Notify é gratuito e liga em minutos pelo catálogo de módulos NexTool, e a nossa equipa de sustentação ajuda a calibrar o que cada perfil recebe para o sino passar a ser sinal, não ruído.


Este conteúdo foi produzido com o auxílio de inteligência artificial e revisto pela equipa Nextool Solutions.

Perguntas Frequentes

Não. O GLPI nativo depende da fila queuednotification e de modelos de e-mail, um modelo assíncrono e fora da ferramenta. O módulo gratuito Smart Notify acrescenta um sino na navbar que faz polling à base a cada 30 segundos e agrega dezasseis fontes de eventos ITIL (seguimentos, validações, tarefas, soluções, documentos, novos tickets e avisos manuais), com sons por prioridade.

Não, são camadas complementares. O sino avisa quem está autenticado no GLPI, tipicamente o técnico em sessão. O requerente que abriu o ticket pelo self-service continua a depender do e-mail. O erro comum é desligar as notificações por e-mail a pensar que o sino cobre toda a gente; então o cliente deixa de ser avisado.

Pouco, graças ao modo delta. O cliente devolve o hash do último estado (state=) e, se nada mudou, a resposta cai de cerca de 18 KB para ~93 bytes: a maioria dos polls não traz novidade. O read-check foi consolidado num único SELECT em lote (antes era um por notificação) e o GET liberta o lock de sessão com session_write_close(). O intervalo é configurável em refresh_interval.

Calibrando o que cada perfil recebe. Três gatilhos já vêm desligados por omissão (documentos anexados, novos problemas e novas mudanças) porque são ruidosos ou pouco frequentes. Cada técnico afina na janela de preferências, e o SQL sobre a tabela _preferences mostra quem silenciou o quê. Ligar tudo leva à fadiga de alerta e o técnico começa a ignorar o sino.

É a política de autoplay do Chrome, não um bug. O navegador só permite criar o AudioContext depois do primeiro gesto do utilizador, por isso o primeiro som de cada sessão só sai depois do primeiro clique na página. A partir daí, os sons por prioridade tocam normalmente.

Funciona em ambos a partir da versão 2.2.1. As versões anteriores traziam um ALTER TABLE ... ADD COLUMN IF NOT EXISTS no install.sql, sintaxe exclusiva do MariaDB que no MySQL 8 falha com erro 1064 e deita abaixo a instalação (HTTP 500). A correção passou as migrações para PHP com helpers portáveis. Em MySQL 8, use a 2.2.1 ou superior.

Precisa de ajuda?