Files
ealmeida 46175594c1 feat(wordpress): nova skill fluent-crm (conhecimento profundo) + actualiza mcp-fluent-crm
fluent-crm documenta arquitectura Free/Pro, schema fc_*, FluentCrmApi(), guarda anti-escalada de status, wp-admin, Funnels/Dynamic Segments — companheira de conhecimento da mcp-fluent-crm (execução), mesmo padrão app-for-cloudflare/mcp-cloudflare-app.
2026-08-19 05:50:10 +01:00

222 lines
15 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
name: fluent-crm
description: Conhecimento profundo do FluentCRM (WPManageNinja, plugin `fluent-crm` + addon `fluentcampaign-pro`) — arquitectura, schema de BD (`fc_*`), API de desenvolvimento oficial (`FluentCrmApi()`), modelo de estados de contacto e as suas guardas reais, wp-admin (Subscribers/Tags/Lists/Companies/Campaigns/Automations), automações (Funnels, triggers/actions), Dynamic Segments (Pro), e o estado real em descomplicar.pt (291k contactos). Usar quando "fluentcrm", "email marketing wordpress", "automação de contactos", "funnels fluentcrm", "segmentos dinâmicos", "status de subscritor", "unsubscribe", "empresas crm wordpress", "campanha email estatísticas", ou qualquer trabalho de configuração/estratégia FluentCRM que não seja apenas chamar o MCP. Para execução programática (ler/escrever contactos, tags, listas, empresas, notas, campanhas), usar a skill `mcp-fluent-crm`.
layer: wiki
---
# /fluent-crm — FluentCRM (WPManageNinja) — conhecimento de plugin
Plugin `fluent-crm` (Free, `FLUENTCRM_PLUGIN_VERSION` 3.1.10) + addon comercial
`fluentcampaign-pro` (2.9.86, "FluentCRM Pro"), instalados e activos em **descomplicar.pt**
(produção). Mapeamento feito por leitura completa do código-fonte
(`wp-content/plugins/fluent-crm/app/` e `wp-content/plugins/fluentcampaign-pro/app/`) via SSH,
19-08-2026, antes de construir o MCP `mcp-fluent-crm`.
**Não confundir com FluentBooking** (`fluent-booking`/`fluent-booking-pro`, mesmo fabricante
WPManageNinja, também activo em descomplicar.pt) — é um produto de agendamento separado, sem
relação com contactos/email marketing além de estar na mesma "família Fluent".
---
## 0. As duas camadas — Free vs Pro
| | Free (`fluent-crm`) | Pro (`fluentcampaign-pro`) |
|---|---|---|
| Contactos, tags, listas, empresas | ✅ core | — |
| Campanhas de email | ✅ core | — |
| Automações (Funnels) — motor + triggers/actions base | ✅ core (`FluentFormSubmissionTrigger`, `UserRegistrationTrigger`, apply/detach tag/list/company, send email, wait) | Acrescenta dezenas de triggers/actions por integração (ver §5) |
| Dynamic Segments | — | ✅ (`WooCustomerSegment`, `EddActiveCustomerSegment`, `PMProMembersSegment`, `AffiliateWPSegment`, `WpUserSegment`, `CustomSegment`, `WooSubscriptionActiveSegment`) |
| Abandon Cart | — | ✅ (WooCommerce) |
| Integrações de terceiros (WooCommerce, EDD, SureCart, LearnDash, LifterLMS, TutorLMS, MemberPress, RCP, PMPro, AffiliateWP, WishlistMember, Voxel) | — | ✅ |
**Achado real (descomplicar.pt, 19-08-2026):** nenhuma das plataformas-alvo das integrações Pro
(WooCommerce, EDD, LearnDash, LifterLMS, TutorLMS, MemberPress, RCP, PMPro, AffiliateWP,
WishlistMember) está instalada no site — só **FluentForm** está activo. Logo, das dezenas de
triggers/actions que o Pro acrescenta, **só os de FluentForm têm alvo real hoje**; o resto do
addon Pro está dormente (código carregado, sem integração activável). Reavaliar se algum destes
plugins for instalado no futuro.
---
## 1. Onde vivem os dados
### 1.1 `wp_options` (config do plugin)
| Opção | Conteúdo |
|---|---|
| `_fluentcrm_db_version` | versão do schema de BD instalado |
| `fluentcrm-global-settings` | array serializado: `campaign.from.{name,email}`, `email.emails_per_second` |
| `fluentcrm_is_sending_emails` | flag de lock durante envio de campanha |
| `_fluentcrm_lock_minute_scheduler` / `_five_minute_scheduler` / `_hourly_scheduler` | locks dos cron schedulers internos |
### 1.2 Tabelas `fc_*` (prefixo do site + `fc_`, ex. `wpah_fc_subscribers` em descomplicar.pt)
Confirmado por `DESCRIBE`/`SHOW TABLES` em descomplicar.pt (19-08-2026):
| Tabela | Papel |
|---|---|
| `fc_subscribers` | contactos — `email` UNIQUE, `status`, `contact_type` (`lead`/`customer`), `company_id`, `hash`, `total_points`, `life_time_value`, geo (`ip`/`latitude`/`longitude`/`country`/...) |
| `fc_subscriber_meta` | custom fields por contacto |
| `fc_subscriber_pivot` | associações contacto↔tag/lista — `object_type` = FQCN literal (`FluentCrm\App\Models\Tag` ou `FluentCrm\App\Models\Lists`), `object_id` |
| `fc_subscriber_notes` | notas por contacto — `type` normal é `note`; `_company_note_`/`_system_log_` são tipos internos excluídos por omissão da UI (global scope no modelo) |
| `fc_tags` / `fc_lists` | taxonomias reais de segmentação (81 tags / 9 lists em descomplicar.pt) |
| `fc_terms` / `fc_term_relations` | **tabelas vazias em descomplicar.pt (0 linhas)** — schema preparado para uma futura unificação de taxonomias que ainda não está em uso; não confiar nelas, usar `fc_tags`/`fc_lists`/`fc_subscriber_pivot` |
| `fc_companies` | empresas (CRM B2B) — `name`, `industry`, `owner_id` (contacto responsável), `meta` (JSON com `custom_values`) |
| `fc_campaigns` | campanhas de email — `type='campaign'`, `status` (`draft`/`published`/`paused`/`archived`), `email_body`, `recipients_count` |
| `fc_campaign_emails` | um registo por (campanha × destinatário) — `is_open`, `click_counter`, `status` (`sent`/...) — é aqui que vivem as estatísticas reais de abertura/clique |
| `fc_campaign_url_metrics` | cliques agregados por URL dentro de uma campanha |
| `fc_funnels` / `fc_funnel_sequences` / `fc_funnel_subscribers` / `fc_funnel_metrics` | automações (ver §5) |
| `fc_event_tracking` | eventos custom rastreados via `event_tracker` API (§2) |
| `fc_smart_links` | links rastreáveis reutilizáveis (Pro) |
| `fc_sequence_tracker` | progresso de contactos dentro de sequências de email (automações/campanhas em sequência) |
| `fc_url_stores` | URLs longas por trás de smart links/tracking |
**Gotcha real confirmado (descomplicar.pt):** existem linhas em `fc_subscriber_pivot` com
`object_type` literalmente diferente do FQCN correcto (deteção via `SELECT object_type, COUNT(*)
GROUP BY object_type` — 107 linhas com um padrão de escaping incorrecto vs 325k+342k linhas
correctas). São dados órfãos de alguma migração/import antiga, invisíveis à própria UI do
FluentCRM (que também filtra por FQCN exacto) — não é bug do MCP nem motivo de alarme, só não
contar com essas 107 linhas em auditorias de integridade.
---
## 2. API de desenvolvimento oficial — `FluentCrmApi()`
**Único caminho seguro para escritas programáticas.** Nunca fazer `INSERT`/`UPDATE` directo nas
tabelas `fc_*` — perde-se guardas de estado, `do_action()` hooks (usados por automações/
integrações de terceiros) e invalidação de cache.
```php
FluentCrmApi('contacts') // Contacts — createOrUpdate, getContact, getContactByUserRef, query()
FluentCrmApi('tags') // Tags — importBulk (cria/actualiza por slug)
FluentCrmApi('lists') // Lists — importBulk (idem)
FluentCrmApi('companies') // Companies — createOrUpdate, attachContactsByIds, detachContactsByIds
FluentCrmApi('event_tracker') // Tracker — track($eventData, $isUnique)
FluentCrmApi('extender') // ponto de extensão para integrações de terceiros
```
Fonte: `app/Api/Classes/*.php`, registadas em `app/Api/config.php`. Cada classe expõe também os
métodos `all`/`get`/`find`/`first`/`paginate` do modelo Eloquent-like subjacente via `__call()`.
Métodos de instância directos no modelo `Subscriber` (não passam pela fábrica `FluentCrmApi()`,
chamam-se sobre um objecto `Subscriber` já resolvido): `attachTags($ids)`, `detachTags($ids)`,
`attachLists($ids)`, `detachLists($ids)`, `attachCompanies($ids)`, `detachCompanies($ids)`,
`updateStatus($status)` (mudança directa, sem guardas — ver §3).
---
## 3. Modelo de estados de contacto — e a guarda real contra escalada indevida
```php
fluentcrm_subscriber_statuses() // ['subscribed','pending','unsubscribed','transactional','bounced','complained','spammed']
fluentcrm_strict_statues() // ['unsubscribed','bounced','complained','spammed'] — "suprimidos"
```
`Contacts::createOrUpdate($data, $forceUpdate=false, ...)` → `Subscriber::updateOrCreate()`
implementa uma guarda documentada no próprio código-fonte (comentário explícito no ficheiro,
descrevendo a correcção de uma vulnerabilidade real): **sem `$forceUpdate=true` explícito, um
payload nunca rebaixa um contacto `subscribed` nem ressuscita um contacto suprimido** —
protege contra webhooks/migradores/formulários que resSuscitassem silenciosamente contactos que
se desinscreveram ou tiveram bounce/complaint. Mover **para** um estado suprimido a partir de
qualquer estado é sempre permitido (direcção "fail-safe").
`Subscriber::updateStatus($status)` é o método directo, **sem essas guardas** — dispara sempre
`do_action('fluent_crm/subscriber_status_changed', ...)` e `do_action('fluentcrm_subscriber_status_to_' . $status, ...)`.
É a via correcta quando a intenção de mudar o status é deliberada e já confirmada pelo chamador
(ex. um admin a reactivar manualmente um contacto).
**Implicação prática:** qualquer automação/import que crie/actualize contactos em massa deve usar
`createOrUpdate()` sem `forceUpdate`, salvo decisão explícita e documentada de contornar a guarda.
---
## 4. wp-admin — páginas principais
| Página | Conteúdo |
|---|---|
| **FluentCRM → Subscribers** | lista/pesquisa/filtra contactos, vista de detalhe com tags/listas/empresa/notas/timeline de actividade |
| **FluentCRM → Contact Companies** | CRM B2B — empresas, dono (owner_id), contactos associados (Pro-visível, dados core) |
| **FluentCRM → Email Campaigns** | criar/agendar/enviar campanhas, relatório de abertura/clique/bounce por campanha |
| **FluentCRM → Automations** | construtor visual de Funnels (trigger → sequência de actions/benchmarks/condições) |
| **FluentCRM → Forms** (se FluentForm activo) | mapeamento formulário↔lista/tag na submissão |
| **FluentCRM → Reports** | dashboards agregados (crescimento de lista, performance de campanhas) |
| **FluentCRM → Settings** | remetente por omissão, limite de emails/segundo, dupla-opt-in, campos custom |
| **FluentCRM → Dynamic Segments** (Pro) | segmentos calculados em tempo real a partir de dados externos (§6) |
---
## 5. Automações (Funnels) — arquitectura
Motor em `app/Services/Funnel/` (`FunnelProcessor.php`, `BaseTrigger.php`, `BaseAction.php`,
`BaseBenchMark.php`, `SequencePoints.php`). Um Funnel = 1 trigger + sequência ordenada de
actions/wait-steps/condições, persistida em `fc_funnels` (definição) +
`fc_funnel_sequences` (passos) + `fc_funnel_subscribers` (progresso por contacto) +
`fc_funnel_metrics` (contadores agregados).
**Triggers/Actions core (Free), confirmados no código:**
- Triggers: `FluentFormSubmissionTrigger`, `FluentFormSubscriptionCancelledTrigger`,
`FluentFormSubscriptionPaymentReceivedTrigger`, `UserRegistrationTrigger`.
- Actions: `ApplyTagAction`/`DetachTagAction`, `ApplyListAction`/`DetachListAction`,
`ApplyCompanyAction`/`DetachCompanyAction`, `SendEmailAction`, `WaitTimeAction`.
**Triggers/Actions internas do CRM (Pro, mas sem dependência de plugin externo — sempre
disponíveis com o addon activo):** `ContactCreatedTrigger`, `TagAppliedTrigger`/
`RemoveFromTagTrigger`, `ListAppliedTrigger`/`RemoveFromListTrigger`, `CompanyAppliedTrigger`/
`RemoveFromCompanyTrigger` (`app/Services/Integrations/CRM/`) — permitem encadear automações a
partir de mudanças de tag/lista/empresa feitas por outra automação ou pelo MCP.
**Triggers/Actions Pro dependentes de plugin externo (dormentes em descomplicar.pt — nenhum dos
plugins-alvo está instalado):** WooCommerce (order success/refund/complete, subscription
renew/start/cancel/expire, add order note, create coupon), EDD (payment success/refund, license
expired, recurring payment), SureCart, RCP, PMPro, AffiliateWP, LearnDash/LifterLMS/TutorLMS
(course/lesson/topic completed, enroll, group), MemberPress, WishlistMember, Voxel.
O MCP `mcp-fluent-crm` **não gere Funnels** (fora do v1, deliberado — sequências JSON complexas,
alto risco de erro de configuração). Gerir Funnels via wp-admin.
---
## 6. Dynamic Segments (Pro)
`app/Services/DynamicSegments/` — segmentos calculados em tempo real (não persistidos como lista
estática), 7 tipos confirmados no código: `CustomSegment` (query builder livre), `WpUserSegment`
(por role/meta WP), `WooCustomerSegment`, `WooSubscriptionActiveSegment`, `EddActiveCustomerSegment`,
`PMProMembersSegment`, `AffiliateWPSegment`. Os 5 últimos dependem do plugin-alvo estar instalado
(nenhum está, hoje). Não coberto pelo MCP — gerir via wp-admin.
---
## 7. Estado real em descomplicar.pt (verificado 19-08-2026)
- **291 441 contactos**: 81 881 `subscribed`, 209 559 `unsubscribed`, 1 `transactional`.
- **291 428 `lead`, 13 `customer`** (`contact_type`).
- **81 tags, 9 lists, 0 empresas** (tabela `fc_companies` vazia — CRM B2B nunca usado neste site).
- **37 campanhas** (31 `archived`/enviadas, 2 `draft`, 2 `paused`, 2 `published`).
- Nenhuma licença Pro visível em `cfLicenseKey`-equivalente verificada nesta sessão — addon
`fluentcampaign-pro` estava **instalado mas inactivo** até ser activado nesta mesma sessão via
`emcp-tools/activate-plugin` (não requer autorização de infra — só mutações de ficheiro por SSH
precisam do `infra-auth.sh`).
---
## 8. Gotchas
| Sintoma | Causa | Nota |
|---|---|---|
| `wp fluent_crm <cmd>` não tem `create-contact`/`add-tag`/etc. | O WP-CLI do plugin só cobre operações administrativas (`license_status`, `stats`, `sync_woo_customers`, `sync_edd_customers`, `reindex_wp_user_ids`, `reset_db`, `simulate_funnel`, `cli_send`) — nunca CRUD de contactos | Usar `FluentCrmApi()` (via MCP `mcp-fluent-crm`) ou o wp-admin |
| API REST (`/wp-json/fluent-crm/v2/...`) não responde/autentica | Precisa de uma API key gerada manualmente em **Settings → REST API** — não vinha configurada em nenhum site do bundle nesta verificação | Gerar a key no wp-admin se for necessário REST em vez de SSH; o MCP `mcp-fluent-crm` não depende disto |
| Query a `fc_terms`/`fc_term_relations` devolve sempre vazio | Tabelas preparadas para uma futura unificação de taxonomias, nunca populadas nesta versão | Usar sempre `fc_tags`/`fc_lists` + `fc_subscriber_pivot` |
| `object_type` em `fc_subscriber_pivot` não bate certo com o FQCN esperado numa pequena fracção de linhas | Dados órfãos de import/migração antiga (107 de ~650k linhas em descomplicar.pt) | Ignorar essas linhas — a própria UI do FluentCRM também as ignora |
| Contacto criado/actualizado via automação não muda de status apesar do payload trazer `status` | Guarda anti-escalada do `Contacts::createOrUpdate()` (§3) — comportamento correcto, não bug | Passar `forceUpdate=true` só com intenção explícita, ou usar `updateStatus()` directamente |
---
## Fonte
Leitura completa do código-fonte `fluent-crm` 3.1.10 + `fluentcampaign-pro` 2.9.86 via SSH em
descomplicar.pt (`app/Api/Classes/*.php`, `app/Models/Subscriber.php`, `app/Models/Company.php`,
`app/Models/SubscriberNote.php`, `app/Functions/helpers.php`, `app/Services/Funnel/*`,
`app/Services/Integrations/*`, `app/Services/DynamicSegments/*`), cruzada com `DESCRIBE`/
`SHOW TABLES`/agregações reais via SQL, 19-08-2026 — sessão de construção do MCP
`mcp-fluent-crm`. Ver essa skill para a lista completa de 20 tools programáticas.