Metadata-Version: 2.4
Name: botdtp
Version: 0.3.0
Summary: HTTP automation package for DTP and DAMSP portals.
Author: Italhub
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: requests>=2.32.0
Requires-Dist: beautifulsoup4>=4.12.0
Requires-Dist: lxml>=5.2.0
Requires-Dist: pillow>=10.0.0
Requires-Dist: langchain-core>=0.3.0
Requires-Dist: langchain-openai>=0.2.0
Requires-Dist: validate-docbr>=1.11.1
Requires-Dist: xhtml2pdf>=0.2.17
Dynamic: license-file

# botdtp

Pacote de automacao para os portais SGPT e DAMSP da Prefeitura de Sao Paulo.

## O que o projeto entrega

- `SGPTBot`: login, alteracao de senha, comprovante de inspecao, aprovacao/reprovacao e relatorios.
- `DAMSPBot`: fluxos CPF/CNPJ, emissao de licenca PF/PJ e guia PF/PJ.
- `HTTPClient` + `HTTPConfig`: retry, timeout, proxy, sleep entre requests e sessao.
- Persistencia de sessao em arquivo (`SessionStore`) com TTL (SGPT).
- Janela de acesso configuravel por dia/hora/fuso (SGPT).

## Arquitetura (serie 0.3.x)

A arquitetura foi separada por camadas para facilitar manutencao, testes e evolucao:

- `botdtp.core`: contratos centrais (`Operacao`, contexto), erros compartilhados e politica de acesso SGPT.
- `botdtp.sgpt`: autenticacao, operacoes de inspecao e relatorios do SGPT.
- `botdtp.damsp`: fluxos CPF/CNPJ, regras de servico/modalidade e resolucao de captcha.
- `http_ops`: transporte HTTP generico (session, retry, timeout, proxy e persistencia opcional).
- `aspx_ops`: operacoes ASPX reutilizaveis (`__VIEWSTATE`, postback, timeline e navegacao).

Essa separacao permite evoluir fluxos de negocio sem acoplar com detalhes de transporte e WebForms.

## Requisitos

- Python `>=3.11`

## Instalacao

```bash
pip install botdtp
```

## Dependencias

Dependencias diretas do pacote (em `pyproject.toml`):

- `requests>=2.32.0`
- `beautifulsoup4>=4.12.0`
- `lxml>=5.2.0`
- `pillow>=10.0.0`
- `langchain-core>=0.3.0`
- `langchain-openai>=0.2.0`
- `validate-docbr>=1.11.1`
- `xhtml2pdf>=0.2.17`

## Contrato padrao de retorno

Toda operacao retorna `Operacao`:

- `ok: bool`
- `mensagem: str`
- `data: dict`

Comportamento em falha:

- Operacoes publicas de `Bot` e `Client` retornam `Operacao(ok=False, ...)`.
- Nao ha propagacao de excecao para o chamador nesse contrato padrao.
- Em erros, `data` inclui `operacao`, `classe` e `detalhe`.

## Uso de alto nivel (recomendado)

### SGPTBot

```python
from botdtp import SGPTBot
from botdtp.sgpt import (
    SGPTLoginInput,
    SGPTAlterarSenhaInput,
    SGPTComprovanteInspecaoInput,
    SGPTAprovacaoEscolarAnualInput,
    SGPTAprovacaoEscolarSemestralInput,
    SGPTAprovacaoTaxiInput,
    SGPTReprovacaoEscolarAnualInput,
    SGPTReprovacaoEscolarSemestralInput,
    SGPTReprovacaoTaxiInput,
    SGPTReprovacaoItemInput,
    SGPTRelatorioInput,
)

login = SGPTLoginInput(
    base_url="https://vistoriadtp.prefeitura.sp.gov.br",
    codigo_empresa="CODIGO_DA_EMPRESA",
    usuario="USUARIO",
    senha="SUA_SENHA",
)

sgpt = SGPTBot(login=login, reautenticar=True)

sgpt.autenticar(login)

sgpt.alterar_senha(
    SGPTAlterarSenhaInput(
        base_url="https://vistoriadtp.prefeitura.sp.gov.br",
        codigo_empresa="0035",
        usuario="123456",
        senha_atual="SENHA_ATUAL",
        nova_senha="SENHA_NOVA",
        confirmacao_nova_senha="SENHA_NOVA",
    )
)

sgpt.gerar_comprovante_inspecao(
    SGPTComprovanteInspecaoInput(
        numero_guia="055721",
        ano_guia="2026",
        resultado="APROVADA",
    ),
    destino="tmp",
)

sgpt.aprovar_escolar_anual(
    SGPTAprovacaoEscolarAnualInput(
        placa="ABC1234",
        licenca="12345678",
        inspetor_codigo="111",
        inspetor_nome="INSPETOR",
        rt_codigo="222",
        rt_nome="RESPONSAVEL TECNICO",
        ano_guia="0000",
        numero_guia="00000",
        validade_extintor="00/0000",
        sn_tacografo="S",
    ),
    destino="tmp",
)

sgpt.aprovar_escolar_semestral(
    SGPTAprovacaoEscolarSemestralInput(
        placa="ABC1D23",
        licenca="12345678",
        inspetor_codigo="111",
        inspetor_nome="INSPETOR",
        rt_codigo="222",
        rt_nome="RESPONSAVEL TECNICO",
        validade_extintor="12/2026",
        sn_tacografo="S",
    ),
    destino="tmp",
)

sgpt.aprovar_taxi(
    SGPTAprovacaoTaxiInput(
        placa="ABC1234",
        licenca="12345678",
        inspetor_codigo="111",
        inspetor_nome="INSPETOR",
        rt_codigo="222",
        rt_nome="RESPONSAVEL TECNICO",
        ano_guia="2026",
        numero_guia="055721",
        radio_taxi_cod="",
        radio_taxi_nome="",
    ),
    destino_comprovante="tmp",
)

itens = [
    SGPTReprovacaoItemInput(
        grupo="X",
        item="X",
        subitem="X",
        valor="X",
        opcao="X",
        descricao="XXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
    )
]

sgpt.reprovar_escolar_anual(
    SGPTReprovacaoEscolarAnualInput(
        placa="ABC1234",
        licenca="12345678",
        inspetor_codigo="111",
        inspetor_nome="INSPETOR",
        rt_codigo="222",
        rt_nome="RESPONSAVEL TECNICO",
        ano_guia="2026",
        numero_guia="055721",
        itens_reprovacao=itens,
    )
)

sgpt.reprovar_escolar_semestral(
    SGPTReprovacaoEscolarSemestralInput(
        placa="ABC1D23",
        licenca="12345678",
        inspetor_codigo="111",
        inspetor_nome="INSPETOR",
        rt_codigo="222",
        rt_nome="RESPONSAVEL TECNICO",
        itens_reprovacao=itens,
    )
)

sgpt.reprovar_taxi(
    SGPTReprovacaoTaxiInput(
        placa="ABC1234",
        licenca="12345678",
        inspetor_codigo="111",
        inspetor_nome="INSPETOR",
        rt_codigo="222",
        rt_nome="RESPONSAVEL TECNICO",
        ano_guia="2026",
        numero_guia="055721",
        itens_reprovacao=itens,
    )
)

sgpt.gerar_relatorio_inspecoes(
    SGPTRelatorioInput(
        mes=5,
        ano=2026,
        modalidade="E",  # C, E, F, T ou M
        resultado="",     # "", A, P ou R
    ),
    destino="tmp",
    nome_arquivo="relatorio_sgpt_maio_2026",
    salvar_html=True,
)
```

Politica de autenticacao SGPT:

- Login obrigatorio: passe `login=SGPTLoginInput(...)` ao instanciar o bot.
- Manual: use `sgpt.autenticar(...)` para renovar explicitamente a sessao.
- Gerenciada: com `reautenticar=True`, o bot tenta reautenticar ao detectar `SGPTAuthenticationError`.
- Sessao: `SGPTBot` restaura/salva cookies via `SessionStore`.

### DAMSPBot

```python
from botdtp import DAMSPBot
from botdtp.damsp import (
    DAMSPCPFInput,
    DAMSPCPFGuiaInput,
    DAMSPCNPJInput,
    DAMSPCNPJGuiaInput,
    DAMSPModalidadeVeiculoEnum,
    DAMSPServicoEscolarEnum,
    DAMSPServicoTaxiEnum,
    OpenAICaptchaSolver,
)

damsp = DAMSPBot(captcha_solver=OpenAICaptchaSolver(api_key="SUA_OPENAI_KEY"))

# 1) Iniciar fluxo CPF
damsp.iniciar_fluxo_cpf(
    DAMSPCPFInput(
        cpf="529.982.247-25",
        licenca="123.456-78",
        modalidade=DAMSPModalidadeVeiculoEnum.TAXI,
    )
)

# 2) Iniciar fluxo CNPJ
damsp.iniciar_fluxo_cnpj(
    DAMSPCNPJInput(
        cpf_responsavel="529.982.247-25",
        numero_empresa="123456",
        licenca="123.456-78",
        modalidade=DAMSPModalidadeVeiculoEnum.ESCOLAR,
    )
)

# 3) Gerar licenca PF
damsp.gerar_licenca_pf(
    DAMSPCPFInput(
        cpf="529.982.247-25",
        licenca="123.456-78",
        modalidade=DAMSPModalidadeVeiculoEnum.TAXI,
    ),
    destino="tmp",
)

# 4) Gerar licenca PJ
damsp.gerar_licenca_pj(
    DAMSPCNPJInput(
        cpf_responsavel="529.982.247-25",
        numero_empresa="123456",
        licenca="123.456-78",
        modalidade=DAMSPModalidadeVeiculoEnum.ESCOLAR,
    ),
    destino="tmp",
)

# 5) Gerar guia PF
damsp.gerar_guia_pf(
    DAMSPCPFGuiaInput(
        cpf="529.982.247-25",
        licenca="123.456-78",
        codigo_servico=DAMSPServicoTaxiEnum.RENOVACAO_ALVARA,
        modalidade=DAMSPModalidadeVeiculoEnum.TAXI,
    ),
    destino="tmp",
)

# 6) Gerar guia PJ
damsp.gerar_guia_pj(
    DAMSPCNPJGuiaInput(
        cpf_responsavel="529.982.247-25",
        numero_empresa="123456",
        licenca="123.456-78",
        codigo_servico=DAMSPServicoEscolarEnum.RENOVACAO_CRM,
        modalidade=DAMSPModalidadeVeiculoEnum.ESCOLAR,
    ),
    destino="tmp",
)
```

## Uso de baixo nivel (infra)

```python
import requests

from botdtp import HTTPClient, HTTPConfig, DAMSPBot

http = HTTPClient(config=HTTPConfig(), session=requests.Session())
damsp = DAMSPBot(http=http)
```

## Captcha DAMSP

`botdtp.damsp.captcha` contem:

- `OpenAICaptchaSolver` (automatico via OpenAI/LangChain)
- `ManualCaptchaSolver` (entrada manual)
- `OpenAICaptchaConfig`
- `converter_para_bytes_png`

Configuracao recomendada:

- Forneca `api_key` explicitamente em `OpenAICaptchaSolver`.
- Ajuste preset do modelo via `OpenAICaptchaConfig(model=..., max_tokens=..., temperature=...)`.
- Se usar env, resolva no bootstrap da aplicacao e injete os valores no construtor.

## Janela de acesso (SGPT) e sessao

Janela padrao: segunda a sabado (`1,2,3,4,5,6`), `07:00` ate `20:00`, fuso `America/Sao_Paulo`.

Personalizacao via codigo:

- Janela: passe `ControleAcessoConfig(...)` ao criar `SGPTBot`.
- Persistencia em arquivo: disponivel no `SGPTBot` via `SessionPersistenceConfig(...)`.
- `DAMSPBot`: sem persistencia em disco; cada operacao refaz o fluxo de identificacao do perfil (PF/PJ).
- HTTP: use `HTTPConfig(...)` com timeout, retry, proxy e trace conforme necessidade.

## Scripts de runtime e testes

Exemplos de runtime:

- `examples/runtime_sgpt_smoke.py`
- `examples/runtime_sgpt_alterar_senha_smoke.py`
- `examples/runtime_sgpt_relatorio_smoke.py`
- `examples/runtime_damsp_licenca_pf_smoke.py`
- `examples/runtime_damsp_licenca_pj_smoke.py`
- `cli/sgpt_menu_cli.py`
- `cli/sgpt_menu_config.example.json`
- `cli/damsp_menu_cli.py`
- `cli/damsp_menu_config.example.json`
- `examples/django_adapter_example.py`

Testes:

```bash
pytest -q
```

## Publicacao da versao 0.3.0

Checklist recomendado para publicar a `0.3.0` refletindo a nova arquitetura:

1. Atualize versao e metadados no `pyproject.toml` (`version = "0.3.0"`).
2. Garanta que este README reflita a API publica atual e a divisao por camadas.
3. Registre mudancas de migracao no release notes (ex.: rename de metodos DAMSP de guia para `gerar_guia_pf` e `gerar_guia_pj`).
4. Execute a suite local:
   - `pytest -q`
5. Gere os artefatos:
   - `python -m build`
6. Valide distribuicoes:
   - `python -m twine check dist/*`
7. Publique (preferencialmente validando antes no TestPyPI):
   - `python -m twine upload dist/*`
8. Crie tag e release:
   - `git tag v0.3.0`
   - `git push origin v0.3.0`
