clamav-tray 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Alfredo Neto
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT Alfredo NetoS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,181 @@
1
+ Metadata-Version: 2.4
2
+ Name: clamav-tray
3
+ Version: 0.2.0
4
+ Summary: Indicador de bandeja para ClamAV gerenciado por systemd
5
+ License-Expression: MIT
6
+ Project-URL: Homepage, https://github.com/JarnDev/clamav-tray
7
+ Project-URL: Documentation, https://github.com/JarnDev/clamav-tray/tree/main/docs
8
+ Project-URL: Changelog, https://github.com/JarnDev/clamav-tray/blob/main/CHANGELOG.md
9
+ Project-URL: Issues, https://github.com/JarnDev/clamav-tray/issues
10
+ Classifier: Environment :: X11 Applications :: GTK
11
+ Classifier: Operating System :: POSIX :: Linux
12
+ Classifier: Topic :: System :: Monitoring
13
+ Requires-Python: >=3.11
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+ Dynamic: license-file
17
+
18
+ # clamav-tray
19
+
20
+ Indicador de bandeja para uma instalação de ClamAV gerenciada por **systemd**.
21
+ Responde, sem abrir terminal: *os serviços estão de pé, tem varredura rodando
22
+ agora, e como terminou a última?*
23
+
24
+ ![Estados do ícone na bandeja](https://raw.githubusercontent.com/JarnDev/clamav-tray/main/docs/screenshots/tray-states.png)
25
+
26
+ Um clique abre o estado completo — serviços, varredura, mídia removível e
27
+ quarentena:
28
+
29
+ ![Estados do menu](https://raw.githubusercontent.com/JarnDev/clamav-tray/main/docs/screenshots/menu-states.png)
30
+
31
+ > As imagens são renderizações fiéis, montadas a partir da estrutura que o
32
+ > próprio programa produz, com os SVGs do tema Adwaita. Os ícones aparecem
33
+ > **brancos** porque é assim que são de fato: o indicador pede um ícone pelo
34
+ > nome (`security-high-symbolic`) e quem pinta é o shell, com a cor de primeiro
35
+ > plano do painel — o que distingue um estado do outro é a forma e o rótulo ao
36
+ > lado, não a cor. A barra de progresso usa as constantes do próprio
37
+ > `widgets.py`, lidas do código, para a imagem não poder divergir do programa.
38
+ > As duas são regeneráveis: `python3 docs/screenshots/make-tray-states.py` e
39
+ > `make-menu-states.py`.
40
+
41
+ ## Por que existe
42
+
43
+ O [ClamTk](https://github.com/dave-theunsub/clamtk) é a referência da categoria
44
+ (436★), mas é um scanner sob demanda: **zero** referências a systemd, **zero** a
45
+ bandeja, e o último *push* de código foi em março de 2024. Os demais projetos da
46
+ busca têm 0 ou 1 estrela.
47
+
48
+ Ninguém preencheu o espaço de *"meu ClamAV já roda por systemd, quero ver o
49
+ estado dele"*.
50
+
51
+ **Requer systemd.** É a premissa, não uma limitação temporária — o valor do
52
+ projeto é justamente ler o estado das unidades.
53
+
54
+ ## Instalação
55
+
56
+ ```sh
57
+ sudo apt install python3-gi gir1.2-ayatanaappindicator3-0.1 # Debian/Ubuntu
58
+ pipx install --system-site-packages clamav-tray
59
+ ```
60
+
61
+ **`--system-site-packages` é obrigatório.** O PyGObject (`gi`) vem da distro, não
62
+ do pip — compilá-lo num virtualenv exige toolchain e os cabeçalhos do GTK. Sem a
63
+ flag, o venv do pipx não enxerga o `python3-gi` do sistema; o programa detecta
64
+ isso e diz como corrigir.
65
+
66
+ **Dependências de sistema:** `python3-gi`, GTK 3 e um dos dois AppIndicator
67
+ (`gir1.2-ayatanaappindicator3-0.1` ou `gir1.2-appindicator3-0.1` — o programa
68
+ tenta os dois).
69
+
70
+ Do código-fonte: clone e `pipx install --system-site-packages .`
71
+
72
+ ### Subir sozinho no login
73
+
74
+ ```sh
75
+ ./packaging/install-autostart.sh # serviço de usuário, supervisionado
76
+ ./packaging/install-autostart.sh xdg # autostart XDG, sem supervisão
77
+ ```
78
+
79
+ Nenhum dos dois precisa de root. Prefira o primeiro: se o processo morrer, ele
80
+ volta, e o journal guarda o motivo. O autostart XDG dispara e esquece — foi assim
81
+ que o indicador que deu origem a este projeto sumiu da bandeja sem deixar rastro.
82
+
83
+ O instalador e os [extras](https://github.com/JarnDev/clamav-tray/tree/main/contrib/extras) ficam no repositório, não no pacote do PyPI: quem
84
+ instalou por pipx precisa clonar (ou baixar só o `packaging/`) para usá-los.
85
+
86
+ O instalador funciona **antes** de `pipx install`: sem o `clamav-tray` no PATH,
87
+ ele aponta a unidade para o código no diretório atual. E confere se o serviço
88
+ subiu — não afirma "ativado" sem olhar.
89
+
90
+ ### Bandeja no seu desktop
91
+
92
+ | Desktop | Situação |
93
+ |---|---|
94
+ | KDE, XFCE, Cinnamon, MATE | nativo |
95
+ | Ubuntu | funciona (a Canonical embarca `ubuntu-appindicators`) |
96
+ | GNOME puro | **precisa de extensão** — o GNOME removeu a bandeja na 3.26 |
97
+
98
+ ## O que funciona numa instalação limpa
99
+
100
+ O programa **observa** o que existe. Não instala unidade, não agenda varredura e
101
+ não decide o que varrer — é essa escolha que o mantém instalável sem root.
102
+
103
+ | | De fábrica |
104
+ |---|---|
105
+ | ClamAV Daemon, Freshclam, On-Access | **sim**, vêm nos pacotes |
106
+ | Varrer a home, varrer mídia removível | **sim**, precisa só do `clamd` no ar |
107
+ | Quarentena do usuário, barra de progresso | **sim** |
108
+ | Varredura agendada, "Próxima", "Última" | **não** — nenhuma distro entrega |
109
+ | Monitor de pasta, quarentena do sistema | **não** |
110
+
111
+ Metade de cima funciona; a metade da varredura nasce vazia. Não é bug: a distro
112
+ não agenda varredura, e o programa não deve inventar uma.
113
+
114
+ Para preencher, há extras **opcionais e independentes** em
115
+ [`contrib/extras/`](https://github.com/JarnDev/clamav-tray/tree/main/contrib/extras) — varredura diária, monitor de Downloads,
116
+ varredura de USB e publicação das estatísticas de quarentena. Cada um com
117
+ instalador próprio, que recusa sobrescrever e diz como desinstalar.
118
+
119
+ ## Configuração
120
+
121
+ Há um item **Configurações** no menu: ele cria o arquivo comentado se não existir
122
+ e abre no seu editor. O arquivo é **relido sozinho ao salvar**.
123
+
124
+ Nada é obrigatório — o programa descobre as unidades pelo glob `clam*` e os
125
+ caminhos pelo `clamconf`.
126
+
127
+ ## Idioma
128
+
129
+ **Inglês é o padrão e a fonte.** Traduzir é opcional por construção: chave sem
130
+ tradução aparece em inglês em vez de quebrar.
131
+
132
+ ```toml
133
+ [general]
134
+ language = "pt_BR" # ou "auto" para seguir o locale
135
+ ```
136
+
137
+ Para contribuir com um idioma, copie o bloco `pt_BR` em
138
+ [`clamav_tray/i18n.py`](https://github.com/JarnDev/clamav-tray/blob/main/clamav_tray/i18n.py) e traduza — sem instalar ferramenta
139
+ nenhuma. Não usa gettext de propósito; o porquê está em
140
+ [docs/design.md](https://github.com/JarnDev/clamav-tray/blob/main/docs/design.md).
141
+
142
+ ## Documentação
143
+
144
+ | | |
145
+ |---|---|
146
+ | [docs/design.md](https://github.com/JarnDev/clamav-tray/blob/main/docs/design.md) | decisões de desenho e o que elas custaram |
147
+ | [docs/dbusmenu.md](https://github.com/JarnDev/clamav-tray/blob/main/docs/dbusmenu.md) | o que atravessa o protocolo do menu, e o que não |
148
+ | [docs/quarantine.md](https://github.com/JarnDev/clamav-tray/blob/main/docs/quarantine.md) | as duas quarentenas e a convenção de estatísticas |
149
+ | [docs/progress.md](https://github.com/JarnDev/clamav-tray/blob/main/docs/progress.md) | progresso por contagem, não por relógio |
150
+ | [docs/removable-media.md](https://github.com/JarnDev/clamav-tray/blob/main/docs/removable-media.md) | detecção sem subprocesso |
151
+ | [ROADMAP.md](https://github.com/JarnDev/clamav-tray/blob/main/ROADMAP.md) | o produto, e a migração para Rust |
152
+ | [CONTRIBUTING.md](https://github.com/JarnDev/clamav-tray/blob/main/CONTRIBUTING.md) | como contribuir |
153
+
154
+ ---
155
+
156
+ ## Este projeto é também meu caderno de Rust
157
+
158
+ Decidi usar este projeto para **aprender Rust**, migrando **um módulo por vez**
159
+ até que não sobre Python.
160
+
161
+ - A migração acontece em **branch separada** (`rust`), nunca na `main`
162
+ - A `main` continua sendo a versão Python, funcional e mantida
163
+ - A **virada de chave só acontece quando eu tiver confiança na linguagem** — não
164
+ por prazo, não por metade dos módulos portados. Se essa confiança não vier, a
165
+ versão Python permanece e isso não é fracasso
166
+ - Quem instala usa Python e não precisa saber de nada disso
167
+
168
+ O desenho em módulos existe por causa disso: cada arquivo tem uma fronteira clara
169
+ e pode ser portado sozinho. A ordem e o critério estão no [ROADMAP](https://github.com/JarnDev/clamav-tray/blob/main/ROADMAP.md).
170
+
171
+ **Se você é contribuidor:** mande PR contra a `main`, em Python. A branch `rust`
172
+ é exercício pessoal e pode ser reescrita à força a qualquer momento.
173
+
174
+ ## Estado
175
+
176
+ v0.1, em uso diário por uma pessoa. Publicado porque o nicho estava vazio, não
177
+ porque seja maduro. Se você usar, espere arestas — e abra issue.
178
+
179
+ ## Licença
180
+
181
+ MIT.
@@ -0,0 +1,164 @@
1
+ # clamav-tray
2
+
3
+ Indicador de bandeja para uma instalação de ClamAV gerenciada por **systemd**.
4
+ Responde, sem abrir terminal: *os serviços estão de pé, tem varredura rodando
5
+ agora, e como terminou a última?*
6
+
7
+ ![Estados do ícone na bandeja](https://raw.githubusercontent.com/JarnDev/clamav-tray/main/docs/screenshots/tray-states.png)
8
+
9
+ Um clique abre o estado completo — serviços, varredura, mídia removível e
10
+ quarentena:
11
+
12
+ ![Estados do menu](https://raw.githubusercontent.com/JarnDev/clamav-tray/main/docs/screenshots/menu-states.png)
13
+
14
+ > As imagens são renderizações fiéis, montadas a partir da estrutura que o
15
+ > próprio programa produz, com os SVGs do tema Adwaita. Os ícones aparecem
16
+ > **brancos** porque é assim que são de fato: o indicador pede um ícone pelo
17
+ > nome (`security-high-symbolic`) e quem pinta é o shell, com a cor de primeiro
18
+ > plano do painel — o que distingue um estado do outro é a forma e o rótulo ao
19
+ > lado, não a cor. A barra de progresso usa as constantes do próprio
20
+ > `widgets.py`, lidas do código, para a imagem não poder divergir do programa.
21
+ > As duas são regeneráveis: `python3 docs/screenshots/make-tray-states.py` e
22
+ > `make-menu-states.py`.
23
+
24
+ ## Por que existe
25
+
26
+ O [ClamTk](https://github.com/dave-theunsub/clamtk) é a referência da categoria
27
+ (436★), mas é um scanner sob demanda: **zero** referências a systemd, **zero** a
28
+ bandeja, e o último *push* de código foi em março de 2024. Os demais projetos da
29
+ busca têm 0 ou 1 estrela.
30
+
31
+ Ninguém preencheu o espaço de *"meu ClamAV já roda por systemd, quero ver o
32
+ estado dele"*.
33
+
34
+ **Requer systemd.** É a premissa, não uma limitação temporária — o valor do
35
+ projeto é justamente ler o estado das unidades.
36
+
37
+ ## Instalação
38
+
39
+ ```sh
40
+ sudo apt install python3-gi gir1.2-ayatanaappindicator3-0.1 # Debian/Ubuntu
41
+ pipx install --system-site-packages clamav-tray
42
+ ```
43
+
44
+ **`--system-site-packages` é obrigatório.** O PyGObject (`gi`) vem da distro, não
45
+ do pip — compilá-lo num virtualenv exige toolchain e os cabeçalhos do GTK. Sem a
46
+ flag, o venv do pipx não enxerga o `python3-gi` do sistema; o programa detecta
47
+ isso e diz como corrigir.
48
+
49
+ **Dependências de sistema:** `python3-gi`, GTK 3 e um dos dois AppIndicator
50
+ (`gir1.2-ayatanaappindicator3-0.1` ou `gir1.2-appindicator3-0.1` — o programa
51
+ tenta os dois).
52
+
53
+ Do código-fonte: clone e `pipx install --system-site-packages .`
54
+
55
+ ### Subir sozinho no login
56
+
57
+ ```sh
58
+ ./packaging/install-autostart.sh # serviço de usuário, supervisionado
59
+ ./packaging/install-autostart.sh xdg # autostart XDG, sem supervisão
60
+ ```
61
+
62
+ Nenhum dos dois precisa de root. Prefira o primeiro: se o processo morrer, ele
63
+ volta, e o journal guarda o motivo. O autostart XDG dispara e esquece — foi assim
64
+ que o indicador que deu origem a este projeto sumiu da bandeja sem deixar rastro.
65
+
66
+ O instalador e os [extras](https://github.com/JarnDev/clamav-tray/tree/main/contrib/extras) ficam no repositório, não no pacote do PyPI: quem
67
+ instalou por pipx precisa clonar (ou baixar só o `packaging/`) para usá-los.
68
+
69
+ O instalador funciona **antes** de `pipx install`: sem o `clamav-tray` no PATH,
70
+ ele aponta a unidade para o código no diretório atual. E confere se o serviço
71
+ subiu — não afirma "ativado" sem olhar.
72
+
73
+ ### Bandeja no seu desktop
74
+
75
+ | Desktop | Situação |
76
+ |---|---|
77
+ | KDE, XFCE, Cinnamon, MATE | nativo |
78
+ | Ubuntu | funciona (a Canonical embarca `ubuntu-appindicators`) |
79
+ | GNOME puro | **precisa de extensão** — o GNOME removeu a bandeja na 3.26 |
80
+
81
+ ## O que funciona numa instalação limpa
82
+
83
+ O programa **observa** o que existe. Não instala unidade, não agenda varredura e
84
+ não decide o que varrer — é essa escolha que o mantém instalável sem root.
85
+
86
+ | | De fábrica |
87
+ |---|---|
88
+ | ClamAV Daemon, Freshclam, On-Access | **sim**, vêm nos pacotes |
89
+ | Varrer a home, varrer mídia removível | **sim**, precisa só do `clamd` no ar |
90
+ | Quarentena do usuário, barra de progresso | **sim** |
91
+ | Varredura agendada, "Próxima", "Última" | **não** — nenhuma distro entrega |
92
+ | Monitor de pasta, quarentena do sistema | **não** |
93
+
94
+ Metade de cima funciona; a metade da varredura nasce vazia. Não é bug: a distro
95
+ não agenda varredura, e o programa não deve inventar uma.
96
+
97
+ Para preencher, há extras **opcionais e independentes** em
98
+ [`contrib/extras/`](https://github.com/JarnDev/clamav-tray/tree/main/contrib/extras) — varredura diária, monitor de Downloads,
99
+ varredura de USB e publicação das estatísticas de quarentena. Cada um com
100
+ instalador próprio, que recusa sobrescrever e diz como desinstalar.
101
+
102
+ ## Configuração
103
+
104
+ Há um item **Configurações** no menu: ele cria o arquivo comentado se não existir
105
+ e abre no seu editor. O arquivo é **relido sozinho ao salvar**.
106
+
107
+ Nada é obrigatório — o programa descobre as unidades pelo glob `clam*` e os
108
+ caminhos pelo `clamconf`.
109
+
110
+ ## Idioma
111
+
112
+ **Inglês é o padrão e a fonte.** Traduzir é opcional por construção: chave sem
113
+ tradução aparece em inglês em vez de quebrar.
114
+
115
+ ```toml
116
+ [general]
117
+ language = "pt_BR" # ou "auto" para seguir o locale
118
+ ```
119
+
120
+ Para contribuir com um idioma, copie o bloco `pt_BR` em
121
+ [`clamav_tray/i18n.py`](https://github.com/JarnDev/clamav-tray/blob/main/clamav_tray/i18n.py) e traduza — sem instalar ferramenta
122
+ nenhuma. Não usa gettext de propósito; o porquê está em
123
+ [docs/design.md](https://github.com/JarnDev/clamav-tray/blob/main/docs/design.md).
124
+
125
+ ## Documentação
126
+
127
+ | | |
128
+ |---|---|
129
+ | [docs/design.md](https://github.com/JarnDev/clamav-tray/blob/main/docs/design.md) | decisões de desenho e o que elas custaram |
130
+ | [docs/dbusmenu.md](https://github.com/JarnDev/clamav-tray/blob/main/docs/dbusmenu.md) | o que atravessa o protocolo do menu, e o que não |
131
+ | [docs/quarantine.md](https://github.com/JarnDev/clamav-tray/blob/main/docs/quarantine.md) | as duas quarentenas e a convenção de estatísticas |
132
+ | [docs/progress.md](https://github.com/JarnDev/clamav-tray/blob/main/docs/progress.md) | progresso por contagem, não por relógio |
133
+ | [docs/removable-media.md](https://github.com/JarnDev/clamav-tray/blob/main/docs/removable-media.md) | detecção sem subprocesso |
134
+ | [ROADMAP.md](https://github.com/JarnDev/clamav-tray/blob/main/ROADMAP.md) | o produto, e a migração para Rust |
135
+ | [CONTRIBUTING.md](https://github.com/JarnDev/clamav-tray/blob/main/CONTRIBUTING.md) | como contribuir |
136
+
137
+ ---
138
+
139
+ ## Este projeto é também meu caderno de Rust
140
+
141
+ Decidi usar este projeto para **aprender Rust**, migrando **um módulo por vez**
142
+ até que não sobre Python.
143
+
144
+ - A migração acontece em **branch separada** (`rust`), nunca na `main`
145
+ - A `main` continua sendo a versão Python, funcional e mantida
146
+ - A **virada de chave só acontece quando eu tiver confiança na linguagem** — não
147
+ por prazo, não por metade dos módulos portados. Se essa confiança não vier, a
148
+ versão Python permanece e isso não é fracasso
149
+ - Quem instala usa Python e não precisa saber de nada disso
150
+
151
+ O desenho em módulos existe por causa disso: cada arquivo tem uma fronteira clara
152
+ e pode ser portado sozinho. A ordem e o critério estão no [ROADMAP](https://github.com/JarnDev/clamav-tray/blob/main/ROADMAP.md).
153
+
154
+ **Se você é contribuidor:** mande PR contra a `main`, em Python. A branch `rust`
155
+ é exercício pessoal e pode ser reescrita à força a qualquer momento.
156
+
157
+ ## Estado
158
+
159
+ v0.1, em uso diário por uma pessoa. Publicado porque o nicho estava vazio, não
160
+ porque seja maduro. Se você usar, espere arestas — e abra issue.
161
+
162
+ ## Licença
163
+
164
+ MIT.
@@ -0,0 +1,2 @@
1
+ """Indicador de bandeja para ClamAV gerenciado por systemd."""
2
+ __version__ = "0.2.0"
@@ -0,0 +1,60 @@
1
+ """Ponto de entrada. `python -m clamav_tray` ou o script `clamav-tray`."""
2
+ from __future__ import annotations
3
+
4
+ import sys
5
+
6
+
7
+ def main() -> int:
8
+ from . import config
9
+
10
+ cfg = config.load()
11
+
12
+ from . import i18n
13
+ i18n.set_language(cfg.language)
14
+
15
+ if "--dump" in sys.argv:
16
+ # Diagnostico sem GUI: mostra exatamente o que o tray veria. E tambem o
17
+ # embriao do `clamav-status --json` que a trilha de Rust vai produzir.
18
+ from . import history, progress, scan, text, units
19
+
20
+ ids = cfg.units or units.discover()
21
+ state = units.query_all(ids)
22
+ for unit in sorted(state.values(), key=lambda u: u.id):
23
+ extra = f" ha {scan.human_duration(unit.elapsed_secs)}" if unit.is_running_job and unit.elapsed_secs else ""
24
+ print(f"{unit.id:34} {unit.kind.value:7} {unit.active_state:10} {unit.sub_state:10}{extra}")
25
+ job = units.pick_scan_unit(state)
26
+ result = scan.parse_summary(history.last_summary(cfg.scan_log, job.id if job else None, user=bool(job and job.user_scope), output=progress.output_path() if job and job.user_scope else None) or "")
27
+ print(f"\nultima varredura: {text.describe(result)} [{result.verdict.value}]")
28
+ print(f"scan_log: {cfg.scan_log} daemon_log: {cfg.log_file} socket: {cfg.socket}")
29
+ return 0
30
+
31
+ try:
32
+ from .tray import Tray
33
+ except ModuleNotFoundError as err:
34
+ if err.name != "gi":
35
+ raise
36
+ # PyGObject vem da distro, nao do pip (ver pyproject.toml). O pipx cria um
37
+ # venv isolado que nao enxerga o python3-gi do sistema — testado num
38
+ # Ubuntu 24.04 limpo: sem a flag, o usuario recebia so o traceback.
39
+ print(
40
+ "clamav-tray: PyGObject (modulo 'gi') nao encontrado.\n"
41
+ " 1. instale pela distro: sudo apt install python3-gi gir1.2-ayatanaappindicator3-0.1\n"
42
+ " 2. se instalou com pipx, reinstale enxergando os pacotes do sistema:\n"
43
+ " pipx uninstall clamav-tray && pipx install --system-site-packages clamav-tray",
44
+ file=sys.stderr,
45
+ )
46
+ return 1
47
+ except RuntimeError as err:
48
+ print(f"clamav-tray: {err}", file=sys.stderr)
49
+ return 1
50
+
51
+ try:
52
+ Tray(cfg).run()
53
+ except RuntimeError as err:
54
+ print(f"clamav-tray: {err}", file=sys.stderr)
55
+ return 1
56
+ return 0
57
+
58
+
59
+ if __name__ == "__main__":
60
+ raise SystemExit(main())