chatgraph 1.2.2__tar.gz → 1.2.3__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.
Files changed (64) hide show
  1. {chatgraph-1.2.2 → chatgraph-1.2.3}/.github/skills/chatgraph-framework/SKILL.md +23 -1
  2. {chatgraph-1.2.2 → chatgraph-1.2.3}/.gitignore +1 -2
  3. {chatgraph-1.2.2 → chatgraph-1.2.3}/PKG-INFO +1 -1
  4. {chatgraph-1.2.2 → chatgraph-1.2.3}/chatgraph/logger/__init__.py +1 -0
  5. {chatgraph-1.2.2 → chatgraph-1.2.3}/chatgraph/logger/user_logger.py +9 -0
  6. {chatgraph-1.2.2 → chatgraph-1.2.3}/chatgraph/messages/message_consumer.py +20 -23
  7. chatgraph-1.2.3/docs/gaps-go-to-python.md +150 -0
  8. chatgraph-1.2.3/docs/gaps-python-to-go.md +65 -0
  9. chatgraph-1.2.3/example.py +98 -0
  10. {chatgraph-1.2.2 → chatgraph-1.2.3}/pyproject.toml +80 -80
  11. {chatgraph-1.2.2 → chatgraph-1.2.3}/tests/unit/test_message_consumer.py +22 -23
  12. {chatgraph-1.2.2 → chatgraph-1.2.3}/tests/unit/test_user_logger.py +34 -0
  13. {chatgraph-1.2.2 → chatgraph-1.2.3}/uv.lock +1139 -1139
  14. chatgraph-1.2.2/example.py +0 -132
  15. {chatgraph-1.2.2 → chatgraph-1.2.3}/.env.example +0 -0
  16. {chatgraph-1.2.2 → chatgraph-1.2.3}/.github/agents/SkillManager.agent.md +0 -0
  17. {chatgraph-1.2.2 → chatgraph-1.2.3}/.github/agents/architect.agent.md +0 -0
  18. {chatgraph-1.2.2 → chatgraph-1.2.3}/.github/agents/code-reviewer.agent.md +0 -0
  19. {chatgraph-1.2.2 → chatgraph-1.2.3}/.github/agents/developer.agent.md +0 -0
  20. {chatgraph-1.2.2 → chatgraph-1.2.3}/.github/copilot-instructions.md +0 -0
  21. {chatgraph-1.2.2 → chatgraph-1.2.3}/.python-version +0 -0
  22. {chatgraph-1.2.2 → chatgraph-1.2.3}/LICENSE +0 -0
  23. {chatgraph-1.2.2 → chatgraph-1.2.3}/README.md +0 -0
  24. {chatgraph-1.2.2 → chatgraph-1.2.3}/chatgraph/__init__.py +0 -0
  25. {chatgraph-1.2.2 → chatgraph-1.2.3}/chatgraph/auth/credentials.py +0 -0
  26. {chatgraph-1.2.2 → chatgraph-1.2.3}/chatgraph/bot/chatbot_model.py +0 -0
  27. {chatgraph-1.2.2 → chatgraph-1.2.3}/chatgraph/bot/chatbot_router.py +0 -0
  28. {chatgraph-1.2.2 → chatgraph-1.2.3}/chatgraph/bot/default_functions.py +0 -0
  29. {chatgraph-1.2.2 → chatgraph-1.2.3}/chatgraph/bot/default_guard.py +0 -0
  30. {chatgraph-1.2.2 → chatgraph-1.2.3}/chatgraph/cli/__init__.py +0 -0
  31. {chatgraph-1.2.2 → chatgraph-1.2.3}/chatgraph/container/container.py +0 -0
  32. {chatgraph-1.2.2 → chatgraph-1.2.3}/chatgraph/error/chatbot_error.py +0 -0
  33. {chatgraph-1.2.2 → chatgraph-1.2.3}/chatgraph/error/route_error.py +0 -0
  34. {chatgraph-1.2.2 → chatgraph-1.2.3}/chatgraph/gRPC/gRPCCall.py +0 -0
  35. {chatgraph-1.2.2 → chatgraph-1.2.3}/chatgraph/logger/logger.py +0 -0
  36. {chatgraph-1.2.2 → chatgraph-1.2.3}/chatgraph/models/actions.py +0 -0
  37. {chatgraph-1.2.2 → chatgraph-1.2.3}/chatgraph/models/http_responses.py +0 -0
  38. {chatgraph-1.2.2 → chatgraph-1.2.3}/chatgraph/models/message.py +0 -0
  39. {chatgraph-1.2.2 → chatgraph-1.2.3}/chatgraph/models/userstate.py +0 -0
  40. {chatgraph-1.2.2 → chatgraph-1.2.3}/chatgraph/pb/router.proto +0 -0
  41. {chatgraph-1.2.2 → chatgraph-1.2.3}/chatgraph/pb/router_pb2.py +0 -0
  42. {chatgraph-1.2.2 → chatgraph-1.2.3}/chatgraph/pb/router_pb2_grpc.py +0 -0
  43. {chatgraph-1.2.2 → chatgraph-1.2.3}/chatgraph/services/__init__.py +0 -0
  44. {chatgraph-1.2.2 → chatgraph-1.2.3}/chatgraph/services/router_http_client.py +0 -0
  45. {chatgraph-1.2.2 → chatgraph-1.2.3}/chatgraph/types/background_task.py +0 -0
  46. {chatgraph-1.2.2 → chatgraph-1.2.3}/chatgraph/types/end_types.py +0 -0
  47. {chatgraph-1.2.2 → chatgraph-1.2.3}/chatgraph/types/route.py +0 -0
  48. {chatgraph-1.2.2 → chatgraph-1.2.3}/chatgraph/types/usercall.py +0 -0
  49. {chatgraph-1.2.2 → chatgraph-1.2.3}/example2.py +0 -0
  50. {chatgraph-1.2.2 → chatgraph-1.2.3}/jsons/voll_return.json +0 -0
  51. {chatgraph-1.2.2 → chatgraph-1.2.3}/poetry.lock +0 -0
  52. {chatgraph-1.2.2 → chatgraph-1.2.3}/tests/__init__.py +0 -0
  53. {chatgraph-1.2.2 → chatgraph-1.2.3}/tests/integration/__init__.py +0 -0
  54. {chatgraph-1.2.2 → chatgraph-1.2.3}/tests/integration/conftest.py +0 -0
  55. {chatgraph-1.2.2 → chatgraph-1.2.3}/tests/integration/test_router_client_integration.py +0 -0
  56. {chatgraph-1.2.2 → chatgraph-1.2.3}/tests/unit/__init__.py +0 -0
  57. {chatgraph-1.2.2 → chatgraph-1.2.3}/tests/unit/conftest.py +0 -0
  58. {chatgraph-1.2.2 → chatgraph-1.2.3}/tests/unit/test_default_guard.py +0 -0
  59. {chatgraph-1.2.2 → chatgraph-1.2.3}/tests/unit/test_guard.py +0 -0
  60. {chatgraph-1.2.2 → chatgraph-1.2.3}/tests/unit/test_models_actions.py +0 -0
  61. {chatgraph-1.2.2 → chatgraph-1.2.3}/tests/unit/test_models_message.py +0 -0
  62. {chatgraph-1.2.2 → chatgraph-1.2.3}/tests/unit/test_models_userstate.py +0 -0
  63. {chatgraph-1.2.2 → chatgraph-1.2.3}/tests/unit/test_router_http_client.py +0 -0
  64. {chatgraph-1.2.2 → chatgraph-1.2.3}/tests/unit/test_usercall.py +0 -0
@@ -169,6 +169,19 @@ menu = await usercall.get_menu(description='Suporte TI') # por descrição
169
169
  # Setters diretos (síncronos — não persistem imediatamente, usam loop existente)
170
170
  usercall.observation = {'chave': 'valor'} # dict — substitui toda a observação
171
171
  usercall.content_message = 'nova mensagem' # str — sobrescreve o texto recebido
172
+
173
+ # Carga/atualização de dados remotos
174
+ identity = await usercall.load_identity() # recarrega UserIdentity da API
175
+ identity = await usercall.load_identity(cpf='cpf') # força busca por CPF específico
176
+ userstate = await usercall.load_userstate() # recarrega UserState completo da API
177
+
178
+ # Associação de CPF
179
+ await usercall.associate_cpf(
180
+ cpf='00000000000',
181
+ source='nome_do_menu', # origem da associação (ex: nome do menu)
182
+ phone='', # telefone da empresa (opcional)
183
+ device_id='', # ID do dispositivo (opcional)
184
+ )
172
185
  ```
173
186
 
174
187
  ---
@@ -295,11 +308,14 @@ file.send_type = SendType.VIDEO
295
308
  | Fora de rota (startup, módulo) | `_logger = get_system_logger()` | `chatgraph_logs/system.log` |
296
309
 
297
310
  ```python
298
- from chatgraph.logger import get_system_logger, set_level
311
+ from chatgraph.logger import get_system_logger, get_user_logger, set_level
299
312
 
300
313
  _logger = get_system_logger()
301
314
  _logger.info('App iniciada')
302
315
 
316
+ # Logger de usuário fora de uma rota (raramente necessário — prefira usercall.logger dentro de rotas)
317
+ user_log = get_user_logger('user123', 'empresa456')
318
+
303
319
  # Alterar nível de log em runtime (afeta todos os loggers existentes)
304
320
  set_level('DEBUG') # ou logging.DEBUG
305
321
  # Equivalente: UserLoggerManager.set_level('DEBUG')
@@ -340,6 +356,12 @@ async def suporte(usercall: UserCall):
340
356
  async def aguardar(usercall: UserCall):
341
357
  await usercall.send(f'Você disse: {usercall.content_message}')
342
358
  return RedirectResponse('start')
359
+
360
+ # auth_level também pode ser definido em rotas de router
361
+ @router.route('area_rh', auth_level='internal')
362
+ async def area_rh(usercall: UserCall):
363
+ await usercall.send(f'Olá, {usercall.user.internal.cargo}!')
364
+ return RedirectResponse('start')
343
365
  ```
344
366
 
345
367
  ```python
@@ -184,5 +184,4 @@ ROADMAP.md
184
184
  *.pdf
185
185
  *jpg
186
186
  *.jpeg
187
- *.png
188
- docs/
187
+ *.png
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: chatgraph
3
- Version: 1.2.2
3
+ Version: 1.2.3
4
4
  Summary: A user-friendly chatbot library
5
5
  Project-URL: Homepage, https://github.com/irissonnlima/chatgraph
6
6
  Project-URL: Repository, https://github.com/irissonnlima/chatgraph
@@ -8,3 +8,4 @@ logger_httpx.setLevel(logging.ERROR)
8
8
  get_system_logger = UserLoggerManager.get_system_logger
9
9
  get_user_logger = UserLoggerManager.get_user_logger
10
10
  set_level = UserLoggerManager.set_level
11
+ remove_user_logger = UserLoggerManager.remove_user_logger
@@ -52,6 +52,15 @@ class UserLoggerManager:
52
52
  cls._loggers[key] = logger
53
53
  return logger
54
54
 
55
+ @classmethod
56
+ def remove_user_logger(cls, user_id: str, company_id: str) -> None:
57
+ key = f"{user_id}_{company_id}"
58
+ logger = cls._loggers.pop(key, None)
59
+ if logger:
60
+ for handler in logger.handlers[:]:
61
+ handler.close()
62
+ logger.removeHandler(handler)
63
+
55
64
  @classmethod
56
65
  def get_system_logger(cls) -> logging.Logger:
57
66
  key = "chatgraph.system"
@@ -117,29 +117,22 @@ class MessageConsumer:
117
117
  return f'amqp://{user}:{pwd}@{self.__amqp_url}/{vhost}'
118
118
 
119
119
  async def __declare_queue(self, channel) -> aio_pika.abc.AbstractQueue:
120
- try:
121
- return await channel.declare_queue(
122
- self.__queue_consume, passive=True
123
- )
124
- except aio_pika.exceptions.ChannelNotFoundEntity:
125
- channel = await channel.connection.channel()
126
- await channel.set_qos(prefetch_count=self.__prefetch_count)
127
- arguments = {
128
- 'x-dead-letter-exchange': 'log_error',
129
- 'x-expires': 86400000,
130
- 'x-message-ttl': 300000,
131
- }
132
- queue = await channel.declare_queue(
133
- self.__queue_consume,
134
- durable=True,
135
- arguments=arguments,
136
- )
137
- routing_key = f'chatbot.{self.__queue_consume}'
138
- await queue.bind(
139
- exchange=self.__virtual_host,
140
- routing_key=routing_key,
141
- )
142
- return queue
120
+ arguments = {
121
+ 'x-dead-letter-exchange': 'log_error',
122
+ 'x-expires': 86400000,
123
+ 'x-message-ttl': 300000,
124
+ }
125
+ queue = await channel.declare_queue(
126
+ self.__queue_consume,
127
+ durable=True,
128
+ arguments=arguments,
129
+ )
130
+ routing_key = f'chatbot.{self.__queue_consume}'
131
+ await queue.bind(
132
+ exchange=self.__virtual_host,
133
+ routing_key=routing_key,
134
+ )
135
+ return queue
143
136
 
144
137
  async def __connect_and_consume(
145
138
  self, amqp_url: str, process_message: Callable
@@ -175,6 +168,7 @@ class MessageConsumer:
175
168
  await self.cleanup()
176
169
 
177
170
  async def on_request(self, body: bytes, process_message: Callable):
171
+ pure_message = None
178
172
  try:
179
173
  message = body.decode()
180
174
  message_json = json.loads(message)
@@ -182,6 +176,9 @@ class MessageConsumer:
182
176
  await process_message(pure_message)
183
177
  except Exception as e:
184
178
  _logger.error(f'Erro ao processar mensagem: {e}')
179
+ finally:
180
+ if pure_message is not None:
181
+ UserLoggerManager.remove_user_logger(pure_message.user_id, pure_message.company_id)
185
182
 
186
183
  async def __transform_message(self, message: dict) -> UserCall:
187
184
  user_state = message.get('user_state', {})
@@ -0,0 +1,150 @@
1
+ # Lacunas: Go → Python
2
+
3
+ Funcionalidades presentes no `chatgraph-go` que **não existem** no `chatgraph` (Python).
4
+ Ordenadas por impacto estimado em produção.
5
+
6
+ ---
7
+
8
+ ## 1. Timeout automático de handler
9
+
10
+ **Prioridade: Alta**
11
+
12
+ No Go, cada rota possui um `TimeoutRouteOps{Duration, Route}`. Quando o handler ultrapassa a duração configurada, a execução é cancelada via `context.Context` e o usuário é redirecionado para uma rota de timeout.
13
+
14
+ ```go
15
+ engine.RegisterRoute("slow_task", handler, chat.RouterHandlerOptions{
16
+ Timeout: &chat.TimeoutRouteOps{
17
+ Duration: 30 * time.Second,
18
+ Route: "timeout_route",
19
+ },
20
+ })
21
+ ```
22
+
23
+ **O que falta no Python:**
24
+ - Suporte a `timeout` por rota no decorador `@app.route()` / `@router.route()`
25
+ - Cancelamento do handler assíncrono quando o timeout é atingido
26
+ - Redirect automático para rota de fallback
27
+
28
+ ---
29
+
30
+ ## 2. Loop protection
31
+
32
+ **Prioridade: Alta**
33
+
34
+ No Go, o `Engine` rastreia quantas vezes consecutivas o mesmo redirect ocorreu. Se o limite for atingido (`LoopCountRouteOps{Count, Route}`), o usuário é enviado para uma rota de fallback.
35
+
36
+ ```go
37
+ // Padrão: 3 visitas consecutivas → redireciona para "loop_route"
38
+ engine.RegisterRoute("A", func(ctx *chat.Context[Obs]) chat.RouteReturn {
39
+ return &chat.RedirectResponse{TargetRoute: "A"} // loop detectado na 4ª vez
40
+ })
41
+ ```
42
+
43
+ **O que falta no Python:**
44
+ - Contador de redirects consecutivos para a mesma rota em `UserCall` / `ChatbotApp`
45
+ - Configuração de limite por rota ou global
46
+ - Redirect automático para rota de fallback ao atingir o limite
47
+
48
+ ---
49
+
50
+ ## 3. Route Triggers (regex globais por rota)
51
+
52
+ **Prioridade: Média-Alta**
53
+
54
+ No Go, `RouteTrigger{Regex, Route}` permite que qualquer mensagem que bata com um padrão seja redirecionada para uma rota específica **antes** da execução normal. Pode ser configurado globalmente no `Engine` ou por rota.
55
+
56
+ ```go
57
+ engine := chat.NewEngine[Obs](chat.RouterHandlerOptions{
58
+ Triggers: []chat.RouteTrigger{
59
+ {Regex: `(?i)^cancelar$`, Route: "cancelar_route"},
60
+ {Regex: `(?i)^ajuda$`, Route: "help_route"},
61
+ },
62
+ })
63
+ ```
64
+
65
+ **O que existe no Python (parcial):**
66
+ O `ChatbotApp` já possui `DEFAULT_FUNCTION` — um dicionário `{regex: callable}` executado antes das rotas (ex: `voltar`). Porém a implementação atual executa uma função diretamente em vez de redirecionar para uma rota nomeada, e não é configurável por rota individualmente.
67
+
68
+ **O que falta:**
69
+ - Suporte a triggers por rota individualmente
70
+ - Semântica de redirect para rota nomeada (em vez de executar função inline)
71
+
72
+ ---
73
+
74
+ ## 4. Route validation
75
+
76
+ **Prioridade: Média**
77
+
78
+ No Go, `engine.ValidateRoutes()` verifica em tempo de inicialização que todas as rotas referenciadas (timeout, loop, protected, triggers) estão de fato registradas, evitando erros silenciosos em produção.
79
+
80
+ ```go
81
+ if err := engine.ValidateRoutes(); err != nil {
82
+ log.Fatal(err)
83
+ }
84
+ ```
85
+
86
+ **O que falta no Python:**
87
+ - Método `validate_routes()` no `ChatbotApp` que levanta erro se qualquer rota referenciada (em redirects, transfers, etc.) não estiver registrada
88
+
89
+ ---
90
+
91
+ ## 5. `EngineTester` — helper de teste para handlers
92
+
93
+ **Prioridade: Média**
94
+
95
+ No Go, `EngineTester` fornece um `mockExecutor` que captura todas as ações executadas pelo handler (mensagens enviadas, observações salvas, rotas definidas, arquivos buscados/enviados) e permite validá-las em assertions.
96
+
97
+ ```go
98
+ tester := chat.NewEngineTester[Obs](t, engine)
99
+ tester.Execute(
100
+ userState,
101
+ message,
102
+ []chat.ExpectedAction{
103
+ {Type: chat.ExecSendMessage, Message: &expectedMsg},
104
+ {Type: chat.ExecSetRoute, Route: "next_route"},
105
+ },
106
+ expectedReturn,
107
+ )
108
+ ```
109
+
110
+ **O que falta no Python:**
111
+ - Classe `ChatbotTester` (ou similar) que mocka o `RouterHTTPClient` e captura chamadas em sequência
112
+ - Tipos `ExpectedAction` para `send_message`, `set_observation`, `set_route`, `get_file`, `upload_file`
113
+ - Integração com `pytest` e as fixtures existentes em `tests/unit/conftest.py`
114
+
115
+ ---
116
+
117
+ ## 6. Upload de arquivo a partir de bytes (`load_file_bytes`)
118
+
119
+ **Prioridade: Baixa**
120
+
121
+ No Go, `ctx.LoadFileBytes("nome.txt", []byte{...})` permite fazer upload de conteúdo gerado em memória sem precisar de um arquivo em disco.
122
+
123
+ **O que falta no Python:**
124
+ - Método equivalente em `UserCall` para upload a partir de `bytes` diretamente
125
+
126
+ ---
127
+
128
+ ## 7. Deduplicação de arquivos via SHA256
129
+
130
+ **Prioridade: Baixa**
131
+
132
+ No Go, o upload de um arquivo já enviado anteriormente retorna o registro cacheado (identificado por hash SHA256), evitando uploads duplicados.
133
+
134
+ **O que falta no Python:**
135
+ - Verificação de hash antes do upload em `RouterHTTPClient.upload_file()`
136
+
137
+ ---
138
+
139
+ ## 8. `DepartmentID` e `LastUpdate` em `EndChatResponse`
140
+
141
+ **Prioridade: Baixa**
142
+
143
+ O `EndAction` do Go possui dois campos extras que o `EndChatResponse` do Python não tem:
144
+
145
+ | Campo | Go (`EndAction`) | Python (`EndChatResponse`) |
146
+ |---|---|---|
147
+ | `DepartmentID` | `int` | ❌ ausente |
148
+ | `LastUpdate` | `string` (timestamp) | ❌ ausente |
149
+
150
+ ---
@@ -0,0 +1,65 @@
1
+ # Lacunas: Python → Go
2
+
3
+ Funcionalidades presentes no `chatgraph` (Python) que **não existem** no `chatgraph-go`.
4
+ Ordenadas por impacto estimado.
5
+
6
+ ---
7
+
8
+ ## 1. Sistema de autorização por rota (`auth_level` + `default_guard`)
9
+
10
+ **Prioridade: Alta**
11
+
12
+ No Python, cada rota pode declarar um nível de acesso mínimo. O `default_guard` verifica o `AuthLevel` do usuário e redireciona para `menu_id_positiva` se o acesso for insuficiente.
13
+
14
+ ```python
15
+ @app.route("dados_sensiveis", auth_level="write")
16
+ async def handler(usercall: UserCall):
17
+ ...
18
+ ```
19
+
20
+ `AuthLevel` é um enum ordenado: `blocked < unknown < read < write`, com suporte especial para `internal` (verificação de vínculo interno).
21
+
22
+ **O que existe no Go (parcial):**
23
+ `ProtectedRouteOps{Route}` apenas redireciona para uma rota se o usuário "não tem acesso", mas não define o que é "ter acesso" — a lógica de verificação fica fora do framework.
24
+
25
+ **O que falta:**
26
+ - Enum `AuthLevel` com ordenação (`blocked < unknown < read < write`)
27
+ - Campo `AuthLevel` em `UserState.User.Identity`
28
+ - Lógica de `guard` configurável no `Engine` (equivalente ao `default_guard`)
29
+ - Flag `internal` em `UserState.User`
30
+
31
+ ---
32
+
33
+ ## 2. Logger por usuário (`UserLoggerManager`)
34
+
35
+ **Prioridade: Média**
36
+
37
+ No Python, cada `UserCall` expõe um `logger` isolado por `(user_id, company_id)`, com suporte a log em arquivo por usuário via `UserLoggerManager`.
38
+
39
+ ```python
40
+ usercall.logger.info("Usuário entrou na rota de pedidos")
41
+ usercall.logger.debug(f"CPF={usercall.user.identity.cpf}")
42
+ ```
43
+
44
+ **O que falta no Go:**
45
+ - Logger estruturado por usuário acessível via `ctx`
46
+ - Suporte a log em arquivo por usuário (ex: `chatgraph_logs/<user_id>.log`)
47
+ - Nível de log configurável globalmente
48
+
49
+ ---
50
+
51
+ ## 3. `user_message` em `TransferToMenu`
52
+
53
+ **Prioridade: Baixa**
54
+
55
+ No Python, `TransferToMenu` recebe um `user_message` que é enviado automaticamente após a transferência.
56
+
57
+ ```python
58
+ return TransferToMenu(menu="menu_principal", user_message="inicio")
59
+ ```
60
+
61
+ **O que falta no Go:**
62
+ - Campo `UserMessage string` na struct `TransferToMenu`
63
+ - Envio automático da mensagem pelo adapter após a transferência
64
+
65
+ ---
@@ -0,0 +1,98 @@
1
+ from chatgraph import (
2
+ ChatbotApp,
3
+ UserCall,
4
+ Route,
5
+ EndChatResponse,
6
+ RedirectResponse,
7
+ Message,
8
+ File,
9
+ Button,
10
+ User,
11
+ UserIdentity,
12
+ UserData,
13
+ UserInternal,
14
+ TextMessage,
15
+ UserState,
16
+ TransferToMenu,
17
+ )
18
+ from chatgraph.logger import get_system_logger
19
+ from dotenv import load_dotenv
20
+ from dataclasses import dataclass
21
+
22
+
23
+ load_dotenv()
24
+ _logger = get_system_logger()
25
+ _logger.info('Aplicação inicializada')
26
+ app = ChatbotApp()
27
+
28
+
29
+ @dataclass
30
+ class Teste:
31
+ atributo1: str
32
+ atributo2: int
33
+
34
+
35
+ # Rota inicial com emojis
36
+ @app.route('start')
37
+ async def start(rota: Route, usercall: UserCall):
38
+ usercall.logger.info('Usuário entrou na rota start')
39
+ welcome_message = Message(
40
+ 'Bem-vindo ao nosso chatbot! 😊🚀\n Vou te redirecionar para uma área restrita',
41
+ )
42
+
43
+ await usercall.send(welcome_message)
44
+ return RedirectResponse('perguntar_cpf')
45
+
46
+
47
+ @app.route('perguntar_cpf')
48
+ async def perguntar_cpf(rota: Route, usercall: UserCall):
49
+ usercall.logger.info('Usuário entrou na rota perguntar_cpf')
50
+ await usercall.send('Por favor, informe seu CPF:')
51
+ return Route('receber_cpf')
52
+
53
+
54
+ @app.route('receber_cpf')
55
+ async def receber_cpf(rota: Route, usercall: UserCall):
56
+ cpf = usercall.content_message
57
+ usercall.logger.info(f'CPF recebido: {cpf}')
58
+ await usercall.add_observation({'cpf': cpf})
59
+ await usercall.send(f'CPF {cpf} recebido com sucesso!')
60
+ return RedirectResponse('area_restrita')
61
+
62
+
63
+ @app.route('area_restrita', auth_level='internal/read/write')
64
+ async def area_restrita(usercall: UserCall):
65
+ usercall.logger.info('Usuário acessou área restrita')
66
+ await usercall.send('Você está em uma área protegida. Acesso autorizado!')
67
+ return RedirectResponse('choice_start')
68
+
69
+
70
+ @app.route('enviar_btns')
71
+ async def enviar_btns(usercall: UserCall):
72
+ usercall.logger.info('Usuário entrou na rota enviar_btns')
73
+ buttons = [
74
+ Button('Reiniciar'),
75
+ Button('Encerrar'),
76
+ ]
77
+ message = Message(text_message='Escolha uma opção:', buttons=buttons)
78
+ await usercall.send(message)
79
+ return Route('receber_btns')
80
+
81
+
82
+ @app.route('receber_btns')
83
+ async def receber_btns(usercall: UserCall):
84
+ usercall.logger.info('Usuário entrou na rota receber_btns')
85
+ choice = usercall.content_message
86
+
87
+ usercall.logger.info(f'Opção escolhida: {choice}')
88
+ if choice == 'Reiniciar':
89
+ return RedirectResponse('start')
90
+ elif choice == 'Encerrar':
91
+ await usercall.send('Encerrando o chat. Até mais!')
92
+ return EndChatResponse('voll_ended')
93
+ else:
94
+ await usercall.send('Opção inválida. Por favor, escolha novamente.')
95
+ return Route('enviar_btns')
96
+
97
+
98
+ app.start()
@@ -1,80 +1,80 @@
1
- [project]
2
- name = "chatgraph"
3
- version = "1.2.2"
4
- description = "A user-friendly chatbot library"
5
- authors = [
6
- {name = "Irisson N. Lima", email = "irisson.lima@verdecard.com.br"}
7
- ]
8
- readme = "README.md"
9
- requires-python = ">=3.12,<4.0"
10
- keywords = ["chatbot", "rabbitmq", "messaging", "routing", "chatgraph", "python"]
11
- license = {text = "MIT"}
12
- dependencies = [
13
- "pika>=1.3.2",
14
- "rich>=13.8.1",
15
- "grpcio>=1.67.0",
16
- "grpcio-tools>=1.67.0",
17
- "python-dotenv>=1.0.1",
18
- "typer>=0.12.5",
19
- "matplotlib>=3.10.0",
20
- "networkx>=3.4.2",
21
- "protobuf>=6.31.1",
22
- "httpx>=0.28.1",
23
- "aio-pika>=9.5.8",
24
- ]
25
-
26
- dev-dependencies = [
27
- "ruff>=0.5.1",
28
- "pytest>=8.2.2",
29
- "pytest-cov>=5.0.0",
30
- "taskipy>=1.13.0",
31
- "pytest-asyncio>=1.3.0",
32
- "respx>=0.22.0",
33
- "pytest-httpx>=0.35.0",
34
- ]
35
-
36
-
37
- [project.urls]
38
- Homepage = "https://github.com/irissonnlima/chatgraph"
39
- Repository = "https://github.com/irissonnlima/chatgraph"
40
-
41
- [project.scripts]
42
- # chatgraph = "chatgraph.cli:main"
43
-
44
-
45
- [tool.pytest.ini_options]
46
- pythonpath = "."
47
- addopts = "-p no:warnings"
48
- testpaths = ["tests"]
49
- markers = [
50
- "unit: marks tests as unit tests (fast, isolated, uses mocks)",
51
- "integration: marks tests as integration tests (slow, requires external services)",
52
- ]
53
-
54
- [tool.ruff]
55
- line-length = 79
56
- extend-exclude = ['migrations']
57
-
58
- [tool.ruff.lint]
59
- preview = true
60
- select = ['I', 'F', 'E', 'W', 'PL', 'PT']
61
-
62
- [tool.ruff.format]
63
- preview = true
64
- quote-style = 'single'
65
-
66
- [tool.taskipy.tasks]
67
- test = 'pytest --cov=chatgraph --cov-report=html'
68
- start_cov = 'start htmlcov/index.html'
69
- lint = 'ruff check . && ruff check . --diff'
70
- format = 'ruff format . && ruff check . --fix'
71
-
72
- [build-system]
73
- requires = ["hatchling"]
74
- build-backend = "hatchling.build"
75
-
76
- [dependency-groups]
77
- dev = [
78
- "pytest>=9.0.3",
79
- "ruff>=0.15.12",
80
- ]
1
+ [project]
2
+ name = "chatgraph"
3
+ version = "1.2.3"
4
+ description = "A user-friendly chatbot library"
5
+ authors = [
6
+ {name = "Irisson N. Lima", email = "irisson.lima@verdecard.com.br"}
7
+ ]
8
+ readme = "README.md"
9
+ requires-python = ">=3.12,<4.0"
10
+ keywords = ["chatbot", "rabbitmq", "messaging", "routing", "chatgraph", "python"]
11
+ license = {text = "MIT"}
12
+ dependencies = [
13
+ "pika>=1.3.2",
14
+ "rich>=13.8.1",
15
+ "grpcio>=1.67.0",
16
+ "grpcio-tools>=1.67.0",
17
+ "python-dotenv>=1.0.1",
18
+ "typer>=0.12.5",
19
+ "matplotlib>=3.10.0",
20
+ "networkx>=3.4.2",
21
+ "protobuf>=6.31.1",
22
+ "httpx>=0.28.1",
23
+ "aio-pika>=9.5.8",
24
+ ]
25
+
26
+ dev-dependencies = [
27
+ "ruff>=0.5.1",
28
+ "pytest>=8.2.2",
29
+ "pytest-cov>=5.0.0",
30
+ "taskipy>=1.13.0",
31
+ "pytest-asyncio>=1.3.0",
32
+ "respx>=0.22.0",
33
+ "pytest-httpx>=0.35.0",
34
+ ]
35
+
36
+
37
+ [project.urls]
38
+ Homepage = "https://github.com/irissonnlima/chatgraph"
39
+ Repository = "https://github.com/irissonnlima/chatgraph"
40
+
41
+ [project.scripts]
42
+ # chatgraph = "chatgraph.cli:main"
43
+
44
+
45
+ [tool.pytest.ini_options]
46
+ pythonpath = "."
47
+ addopts = "-p no:warnings"
48
+ testpaths = ["tests"]
49
+ markers = [
50
+ "unit: marks tests as unit tests (fast, isolated, uses mocks)",
51
+ "integration: marks tests as integration tests (slow, requires external services)",
52
+ ]
53
+
54
+ [tool.ruff]
55
+ line-length = 79
56
+ extend-exclude = ['migrations']
57
+
58
+ [tool.ruff.lint]
59
+ preview = true
60
+ select = ['I', 'F', 'E', 'W', 'PL', 'PT']
61
+
62
+ [tool.ruff.format]
63
+ preview = true
64
+ quote-style = 'single'
65
+
66
+ [tool.taskipy.tasks]
67
+ test = 'pytest --cov=chatgraph --cov-report=html'
68
+ start_cov = 'start htmlcov/index.html'
69
+ lint = 'ruff check . && ruff check . --diff'
70
+ format = 'ruff format . && ruff check . --fix'
71
+
72
+ [build-system]
73
+ requires = ["hatchling"]
74
+ build-backend = "hatchling.build"
75
+
76
+ [dependency-groups]
77
+ dev = [
78
+ "pytest>=9.0.3",
79
+ "ruff>=0.15.12",
80
+ ]