chatgraph 0.6.4__py3-none-any.whl

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.
chatgraph/__init__.py ADDED
@@ -0,0 +1,35 @@
1
+ from .auth.credentials import Credential
2
+ from .bot.chatbot_model import ChatbotApp
3
+ from .bot.chatbot_router import ChatbotRouter
4
+ from .messages.message_consumer import MessageConsumer
5
+ from .models.userstate import UserState, Menu
6
+ from .models.message import Message, Button, File, TextMessage
7
+ from .types.usercall import UserCall
8
+ from .types.end_types import (
9
+ RedirectResponse,
10
+ EndChatResponse,
11
+ TransferToHuman,
12
+ TransferToMenu,
13
+ )
14
+ from .types.route import Route
15
+ from .types.background_task import BackgroundTask
16
+
17
+ __all__ = [
18
+ "ChatbotApp",
19
+ "Credential",
20
+ "UserCall",
21
+ "ChatbotRouter",
22
+ "RedirectResponse",
23
+ "MessageConsumer",
24
+ "Route",
25
+ "EndChatResponse",
26
+ "TransferToHuman",
27
+ "TransferToMenu",
28
+ "UserState",
29
+ "Menu",
30
+ "Message",
31
+ "Button",
32
+ "File",
33
+ "TextMessage",
34
+ "BackgroundTask",
35
+ ]
@@ -0,0 +1,71 @@
1
+ import os
2
+
3
+
4
+ class Credential:
5
+ """
6
+ Classe para gerenciar credenciais de usuário e senha, com suporte a variáveis de ambiente.
7
+ """
8
+
9
+ def __init__(self, username: str | None = None, password: str | None = None):
10
+ """
11
+ Inicializa a classe Credential com um nome de usuário e senha.
12
+
13
+ Args:
14
+ username (str | None): O nome de usuário. Pode ser None se for carregado de uma variável de ambiente.
15
+ password (str | None): A senha do usuário. Pode ser None se for carregada de uma variável de ambiente.
16
+ """
17
+ self.__username = username
18
+ self.__password = password
19
+
20
+ @property
21
+ def password(self) -> str:
22
+ """
23
+ Retorna a senha do usuário.
24
+
25
+ Raises:
26
+ ValueError: Se a senha não estiver definida.
27
+
28
+ Returns:
29
+ str: A senha do usuário.
30
+ """
31
+ if not self.__password:
32
+ raise ValueError('Senha vazia!')
33
+ return self.__password
34
+
35
+ @property
36
+ def username(self) -> str:
37
+ """
38
+ Retorna o nome de usuário.
39
+
40
+ Raises:
41
+ ValueError: Se o nome de usuário não estiver definido.
42
+
43
+ Returns:
44
+ str: O nome de usuário.
45
+ """
46
+ if not self.__username:
47
+ raise ValueError('Usuário vazio!')
48
+ return self.__username
49
+
50
+ @classmethod
51
+ def load_dotenv(cls, user_env: str = 'CHATBOT_USER', pass_env: str = 'CHATBOT_PASS') -> 'Credential':
52
+ """
53
+ Carrega as credenciais de variáveis de ambiente e retorna uma instância da classe Credential.
54
+
55
+ Args:
56
+ user_env (str): Nome da variável de ambiente que armazena o nome de usuário. Padrão é 'CHATBOT_USER'.
57
+ pass_env (str): Nome da variável de ambiente que armazena a senha. Padrão é 'CHATBOT_PASS'.
58
+
59
+ Raises:
60
+ ValueError: Se o nome de usuário ou a senha não estiverem definidas nas variáveis de ambiente.
61
+
62
+ Returns:
63
+ Credential: Uma instância da classe Credential com as credenciais carregadas.
64
+ """
65
+ username = os.getenv(user_env)
66
+ password = os.getenv(pass_env)
67
+
68
+ if not username or not password:
69
+ raise ValueError('Corrija as variáveis de ambiente!')
70
+
71
+ return cls(username=username, password=password)
@@ -0,0 +1,238 @@
1
+ import asyncio
2
+ import inspect
3
+ import re
4
+ from functools import wraps
5
+ from logging import debug, error
6
+ from typing import Optional, Callable
7
+
8
+ from ..error.chatbot_error import ChatbotMessageError
9
+ from ..messages.message_consumer import MessageConsumer
10
+ from ..models.message import MessageTypes, Message, File
11
+ from ..types.usercall import UserCall
12
+ from ..types.end_types import (
13
+ RedirectResponse,
14
+ EndChatResponse,
15
+ TransferToMenu,
16
+ )
17
+ from ..types.route import Route
18
+ from .chatbot_router import ChatbotRouter
19
+ from ..types.background_task import BackgroundTask
20
+ from .default_functions import voltar
21
+
22
+ DEFAULT_FUNCTION: dict[str, Callable] = {
23
+ r'^\s*(voltar)\s*$': voltar,
24
+ }
25
+
26
+
27
+ class ChatbotApp:
28
+ """
29
+ Classe principal para a aplicação do chatbot, gerencia as rotas e a lógica de processamento de mensagens.
30
+ """
31
+
32
+ def __init__(
33
+ self,
34
+ message_consumer: Optional[MessageConsumer] = None,
35
+ default_functions: dict[str, Callable] = DEFAULT_FUNCTION,
36
+ ):
37
+ """
38
+ Inicializa a classe ChatbotApp com um estado de usuário e um consumidor de mensagens.
39
+
40
+ Args:
41
+ message_consumer (MessageConsumer): O consumidor de mensagens que lida com a entrada de mensagens no sistema.
42
+ default_functions (dict[str, callable]): Dicionário de funções padrão que podem ser usadas antes das rotas.
43
+ """
44
+ if not message_consumer:
45
+ message_consumer = MessageConsumer.load_dotenv()
46
+
47
+ self.default_functions = default_functions
48
+ self.__message_consumer = message_consumer
49
+ self.__routes = {}
50
+
51
+ def include_router(self, router: ChatbotRouter) -> None:
52
+ """
53
+ Inclui um roteador de chatbot com um prefixo nas rotas da aplicação.
54
+
55
+ Args:
56
+ router (ChatbotRouter): O roteador contendo as rotas a serem adicionadas.
57
+ """
58
+ self.__routes.update(router.routes)
59
+
60
+ def route(self, route_name: str) -> Callable:
61
+ """
62
+ Decorador para adicionar uma função como uma rota na aplicação do chatbot.
63
+
64
+ Args:
65
+ route_name (str): O nome da rota para a qual a função deve ser associada.
66
+
67
+ Returns:
68
+ function: O decorador que adiciona a função à rota especificada.
69
+ """
70
+ route_name = route_name.strip().lower()
71
+
72
+ def decorator(func):
73
+ params = {}
74
+ signature = inspect.signature(func)
75
+ output_param = signature.return_annotation
76
+
77
+ for name, param in signature.parameters.items():
78
+ param_type = (
79
+ param.annotation
80
+ if param.annotation != inspect.Parameter.empty
81
+ else 'Any'
82
+ )
83
+ params[param_type] = name
84
+ debug(f'Parameter: {name}, Type: {param_type}')
85
+
86
+ self.__routes[route_name] = {
87
+ 'function': func,
88
+ 'params': params,
89
+ 'return': output_param,
90
+ }
91
+
92
+ @wraps(func)
93
+ async def wrapper(*args, **kwargs):
94
+ return await func(*args, **kwargs)
95
+
96
+ return wrapper
97
+
98
+ return decorator
99
+
100
+ def start(self):
101
+ """
102
+ Inicia o consumo de mensagens pelo chatbot,
103
+ processando cada mensagem recebida.
104
+ """
105
+ self.__message_consumer.reprer()
106
+ asyncio.run(
107
+ self.__message_consumer.start_consume(self.process_message)
108
+ )
109
+
110
+ async def process_message(self, usercall: UserCall) -> None:
111
+ """
112
+ Processa uma mensagem recebida, identificando a rota correspondente
113
+ e executando a função associada.
114
+
115
+ Args:
116
+ usercall (UserCall): A mensagem a ser processada.
117
+
118
+ Raises:
119
+ ChatbotMessageError: Se nenhuma rota for encontrada para
120
+ o menu atual do usuário.
121
+ """
122
+ user_id = usercall.user_id
123
+ route = usercall.route.lower()
124
+ route_handler = route.split('.')[-1]
125
+
126
+ matchDefault = False
127
+
128
+ for regex, func in self.default_functions.items():
129
+ if re.match(regex, usercall.content_message):
130
+ matchDefault = True
131
+ debug(
132
+ f'Função padrão encontrada: {func.__name__} para a rota {route}'
133
+ )
134
+ handler = {
135
+ 'function': func,
136
+ 'params': {UserCall: 'usercall', Route: 'route'},
137
+ }
138
+ break
139
+
140
+ if not matchDefault:
141
+ handler = self.__routes.get(route_handler, None)
142
+
143
+ if not handler:
144
+ raise ChatbotMessageError(
145
+ user_id, f'Rota não encontrada para {route}!'
146
+ )
147
+
148
+ func = handler['function']
149
+ usercall_name = handler['params'].get(UserCall, None)
150
+ route_state_name = handler['params'].get(Route, None)
151
+
152
+ kwargs = {}
153
+ if usercall_name:
154
+ kwargs[usercall_name] = usercall
155
+ if route_state_name:
156
+ kwargs[route_state_name] = Route(route, list(self.__routes.keys()))
157
+
158
+ if asyncio.iscoroutinefunction(func):
159
+ usercall_response = await func(**kwargs)
160
+ else:
161
+ loop = asyncio.get_running_loop()
162
+ usercall_response = await loop.run_in_executor(
163
+ None, lambda: func(**kwargs)
164
+ )
165
+
166
+ if matchDefault:
167
+ usercall.content_message = ''
168
+
169
+ if isinstance(usercall_response, (list, tuple)):
170
+ for response in usercall_response:
171
+ await self.__process_func_response(
172
+ response, usercall, route=route
173
+ )
174
+ else:
175
+ await self.__process_func_response(
176
+ usercall_response, usercall, route=route
177
+ )
178
+
179
+ async def __process_func_response(
180
+ self,
181
+ usercall_response,
182
+ usercall: UserCall,
183
+ route: str,
184
+ ) -> None:
185
+ """
186
+ Processa a resposta de uma função associada a uma rota,
187
+ enviando mensagens ou ajustando estados.
188
+
189
+ Args:
190
+ usercall_response:
191
+ A resposta gerada pela função da rota.
192
+ usercall (UserCall):
193
+ O objeto UserCall associado à mensagem processada.
194
+ route (str):
195
+ O nome da rota atual.
196
+ """
197
+ loop = asyncio.get_running_loop()
198
+
199
+ if isinstance(usercall_response, (MessageTypes, Message, File)):
200
+ # Envia o resultado como mensagem (executando a chamada síncrona no executor)
201
+ await usercall.send(usercall_response)
202
+ return
203
+
204
+ if isinstance(usercall_response, Route):
205
+ await usercall.set_route(usercall_response.current)
206
+ return
207
+
208
+ if isinstance(usercall_response, EndChatResponse):
209
+ await usercall.end_chat(
210
+ usercall_response.end_chat_id,
211
+ end_action_name=usercall_response.end_chat_name,
212
+ )
213
+ return
214
+
215
+ if isinstance(usercall_response, TransferToMenu):
216
+ await usercall.transfer_to_menu(
217
+ usercall_response.menu,
218
+ usercall_response.user_message,
219
+ )
220
+ return
221
+
222
+ if isinstance(usercall_response, RedirectResponse):
223
+ await usercall.set_route(usercall_response.route)
224
+ await self.process_message(usercall)
225
+ return
226
+
227
+ if not usercall_response:
228
+ route = route + '.' + route.split('.')[-1]
229
+ await usercall.set_route(route)
230
+ return
231
+
232
+ if isinstance(usercall_response, BackgroundTask):
233
+ response = await usercall_response.run()
234
+ await self.__process_func_response(response, usercall, route=route)
235
+ return
236
+
237
+ error('Tipo de retorno inválido!')
238
+ return None
@@ -0,0 +1,85 @@
1
+
2
+ import inspect
3
+ from functools import wraps
4
+ from logging import debug
5
+
6
+ from ..error.chatbot_error import ChatbotError
7
+
8
+
9
+ class ChatbotRouter:
10
+ """
11
+ Classe responsável por gerenciar e registrar as rotas do chatbot, associando-as a funções específicas.
12
+
13
+ Atributos:
14
+ routes (dict): Um dicionário que armazena as rotas do chatbot e suas funções associadas.
15
+ """
16
+
17
+ def __init__(self):
18
+ """
19
+ Inicializa a classe ChatbotRouter com um dicionário vazio de rotas.
20
+ """
21
+ self.__routes = {}
22
+
23
+ @property
24
+ def routes(self):
25
+ """
26
+ Retorna as rotas registradas no roteador.
27
+
28
+ Returns:
29
+ dict: Um dicionário contendo as rotas e funções associadas.
30
+ """
31
+ return self.__routes
32
+
33
+ def route(self, route_name: str):
34
+ """
35
+ Decorador para adicionar uma função como uma rota no roteador do chatbot.
36
+
37
+ Args:
38
+ route_name (str): O nome da rota para a qual a função deve ser associada.
39
+
40
+ Returns:
41
+ function: O decorador que adiciona a função à rota especificada.
42
+ """
43
+
44
+ route_name = route_name.strip().lower()
45
+
46
+ def decorator(func):
47
+ params = dict()
48
+ signature = inspect.signature(func)
49
+ output_param = signature.return_annotation
50
+
51
+ for name, param in signature.parameters.items():
52
+ param_type = (
53
+ param.annotation
54
+ if param.annotation != inspect.Parameter.empty
55
+ else 'Any'
56
+ )
57
+ params[param_type] = name
58
+ debug(f'Parameter: {name}, Type: {param_type}')
59
+
60
+ self.__routes[route_name] = {
61
+ 'function': func,
62
+ 'params': params,
63
+ 'return': output_param,
64
+ }
65
+
66
+ @wraps(func)
67
+ def wrapper(*args, **kwargs):
68
+ return func(*args, **kwargs)
69
+
70
+ return wrapper
71
+
72
+ return decorator
73
+
74
+ def include_router(self, router: 'ChatbotRouter'):
75
+ """
76
+ Inclui outro roteador com um prefixo nas rotas do roteador atual.
77
+
78
+ Args:
79
+ router (ChatbotRouter): O roteador contendo as rotas a serem adicionadas.
80
+ prefix (str): O prefixo a ser adicionado às rotas do roteador.
81
+
82
+ Raises:
83
+ ChatbotError: Se a rota 'start' não for encontrada no roteador fornecido.
84
+ """
85
+ self.__routes.update(router.__routes)
@@ -0,0 +1,24 @@
1
+ from ..types.usercall import UserCall
2
+ from ..models.message import Message, Button
3
+ from ..types.end_types import (
4
+ RedirectResponse,
5
+ EndChatResponse,
6
+ TransferToHuman,
7
+ TransferToMenu,
8
+ )
9
+ from ..types.route import Route
10
+
11
+
12
+ async def voltar(route: Route, userCall: UserCall) -> RedirectResponse:
13
+ """
14
+ Função para voltar à rota anterior.
15
+ Args:
16
+ route (Route): A rota atual do chatbot.
17
+ usercall (UserCall): O objeto UserCall associado à chamada do usuário.
18
+ """
19
+
20
+ previous = route.get_previous()
21
+ userCall.console.print(
22
+ f"Voltando rota. ({route.current}) -> ({previous.current})", style="bold yellow"
23
+ )
24
+ return RedirectResponse(previous.current_node)
@@ -0,0 +1,187 @@
1
+ import typer
2
+ from rich.console import Console
3
+ from rich.table import Table
4
+ from dotenv import load_dotenv
5
+ from ..gRPC.gRPCCall import WhatsappServiceClient, UserStateServiceClient
6
+ import os, re
7
+
8
+ load_dotenv()
9
+ app = typer.Typer()
10
+
11
+ @app.command()
12
+ def configs():
13
+ """Recupera os IDs necessários."""
14
+ console = Console()
15
+
16
+ username = os.getenv('RABBIT_USER')
17
+ password = os.getenv('RABBIT_PASS')
18
+ url = os.getenv('RABBIT_URI')
19
+ queue = os.getenv('RABBIT_QUEUE')
20
+ prefetch = os.getenv('RABBIT_PREFETCH', 1)
21
+ vhost = os.getenv('RABBIT_VHOST', '/')
22
+ grpc = os.getenv('GRPC_URI')
23
+
24
+
25
+ tableRabbit = Table(show_header=True, header_style="bold magenta", title="RabbitMQ Consumer")
26
+ tableRabbit.add_column("Atributo", justify="center", style="cyan", no_wrap=True)
27
+ tableRabbit.add_column("Valor", justify="center", style="magenta")
28
+
29
+ tableRabbit.add_row("Virtual Host", vhost)
30
+ tableRabbit.add_row("Prefetch Count", str(prefetch))
31
+ tableRabbit.add_row("Queue Consume", queue)
32
+ tableRabbit.add_row("AMQP URL", url)
33
+ tableRabbit.add_row("Username", username)
34
+ tableRabbit.add_row("Password", "******")
35
+
36
+
37
+ tableGRPC = Table(show_header=True, header_style="bold magenta", title="gRPC Consumer")
38
+ tableGRPC.add_column("Atributo", justify="center", style="cyan", no_wrap=True)
39
+ tableGRPC.add_column("Valor", justify="center", style="magenta")
40
+
41
+ tableGRPC.add_row("URI", grpc)
42
+
43
+ console.print(tableGRPC, justify="center")
44
+ console.print(tableRabbit, justify="center")
45
+
46
+ @app.command()
47
+ def campaigns(regex: str = typer.Option(None, "--regex", "-r", help="Filtro regex para campanhas.")):
48
+ """Recupera as campanhas cadastradas."""
49
+ grpc = os.getenv('GRPC_URI')
50
+
51
+ wwp = WhatsappServiceClient(grpc)
52
+ campaigns = wwp.get_all_campaigns()
53
+
54
+ console = Console()
55
+ tableGRPC = Table(show_header=True, header_style="bold magenta", title="Campaigns")
56
+ tableGRPC.add_column("campaign_id", justify="center", style="cyan", no_wrap=True)
57
+ tableGRPC.add_column("campaign_name", justify="center", style="magenta")
58
+ tableGRPC.add_column("last_update", justify="center", style="magenta")
59
+
60
+ # Aplicar filtro de regex, se fornecido
61
+ filtered_campaigns = campaigns.campaigns
62
+ if regex:
63
+ pattern = re.compile(regex, re.IGNORECASE)
64
+ filtered_campaigns = [
65
+ campaign for campaign in campaigns.campaigns
66
+ if pattern.search(campaign.campaign_name)
67
+ ]
68
+
69
+ for campaign in filtered_campaigns:
70
+ tableGRPC.add_row(campaign.campaign_id, campaign.campaign_name, campaign.last_update)
71
+
72
+ console.print(tableGRPC, justify="center")
73
+
74
+ @app.command()
75
+ def tabulations(
76
+ regex: str = typer.Option(None, "--regex", "-r", help="Filtro regex para as tabulações.")
77
+ ):
78
+ """Recupera as tabulações cadastradas."""
79
+ grpc = os.getenv('GRPC_URI')
80
+
81
+ wwp = WhatsappServiceClient(grpc)
82
+ tabulations = wwp.get_all_tabulations()
83
+
84
+ console = Console()
85
+ tableGRPC = Table(show_header=True, header_style="bold magenta", title="Tabulations")
86
+ tableGRPC.add_column("tabulation_id", justify="center", style="cyan", no_wrap=True)
87
+ tableGRPC.add_column("tabulation_name", justify="center", style="magenta")
88
+ tableGRPC.add_column("tabulation_type", justify="center", style="magenta")
89
+ tableGRPC.add_column("group_name", justify="center", style="magenta")
90
+ tableGRPC.add_column("customer_service_survey_id", justify="center", style="magenta")
91
+ tableGRPC.add_column("last_update", justify="center", style="magenta")
92
+
93
+ # Aplicar filtro de regex, se fornecido
94
+ filtered_tabulations = tabulations.tabulations
95
+ if regex:
96
+ pattern = re.compile(regex, re.IGNORECASE)
97
+ filtered_tabulations = [
98
+ tabulation for tabulation in tabulations.tabulations
99
+ if (
100
+ pattern.search(tabulation.tabulation_id) or
101
+ pattern.search(tabulation.tabulation_name) or
102
+ pattern.search(tabulation.tabulation_type) or
103
+ pattern.search(tabulation.group_name) or
104
+ (tabulation.customer_service_survey_id and pattern.search(tabulation.customer_service_survey_id)) or
105
+ pattern.search(tabulation.last_update)
106
+ )
107
+ ]
108
+
109
+ for tabulation in filtered_tabulations:
110
+ tableGRPC.add_row(
111
+ tabulation.tabulation_id,
112
+ tabulation.tabulation_name,
113
+ tabulation.tabulation_type,
114
+ tabulation.group_name,
115
+ tabulation.customer_service_survey_id or "N/A",
116
+ tabulation.last_update,
117
+ )
118
+
119
+ console.print(tableGRPC, justify="center")
120
+
121
+
122
+ @app.command("ustate")
123
+ def user_state(
124
+ regex: str = typer.Option(None, "--regex", "-r", help="Filtro regex para os estados do usuário.")
125
+ ):
126
+ """Recupera os UserState em operação no momento."""
127
+ grpc = os.getenv('GRPC_URI')
128
+
129
+ ustate = UserStateServiceClient(grpc)
130
+ userstates = ustate.get_all_user_states()
131
+
132
+ console = Console()
133
+ tableGRPC = Table(show_header=True, header_style="bold magenta", title="User States")
134
+ tableGRPC.add_column("user_id", justify="center", style="cyan", no_wrap=True)
135
+ tableGRPC.add_column("menu_id", justify="center", style="magenta")
136
+ tableGRPC.add_column("route", justify="center", style="magenta")
137
+ tableGRPC.add_column("obs", justify="center", style="magenta")
138
+ tableGRPC.add_column("date", justify="center", style="magenta")
139
+ tableGRPC.add_column("direction", justify="center", style="magenta")
140
+
141
+ # Aplicar filtro de regex, se fornecido
142
+ filtered_user_states = userstates.user_states
143
+ if regex:
144
+ pattern = re.compile(regex, re.IGNORECASE)
145
+ filtered_user_states = [
146
+ userstate for userstate in userstates.user_states
147
+ if (
148
+ pattern.search(userstate.user_id) or
149
+ pattern.search(userstate.menu_id) or
150
+ pattern.search(userstate.route) or
151
+ pattern.search(userstate.obs) or
152
+ pattern.search(userstate.date) or
153
+ pattern.search(str(userstate.direction))
154
+ )
155
+ ]
156
+
157
+ for userstate in filtered_user_states:
158
+ tableGRPC.add_row(
159
+ userstate.user_id,
160
+ userstate.menu_id,
161
+ userstate.route,
162
+ userstate.obs,
163
+ userstate.date,
164
+ str(userstate.direction),
165
+ )
166
+
167
+ console.print(tableGRPC, justify="center")
168
+
169
+ @app.command("del-ustate")
170
+ def delete_user_state(user_id: str = typer.Argument(..., help="ID do UserState a ser deletado.")):
171
+ """Deleta um UserState em operação no momento."""
172
+ grpc = os.getenv('GRPC_URI')
173
+
174
+ ustate = UserStateServiceClient(grpc)
175
+
176
+ # Chama o método para deletar o UserState usando o ID fornecido
177
+ try:
178
+ success = ustate.delete_user_state(user_id)
179
+ if success:
180
+ typer.echo(f"UserState com ID '{user_id}' deletado com sucesso.")
181
+ else:
182
+ typer.echo(f"Falha ao deletar UserState com ID '{user_id}'.", err=True)
183
+ except Exception as e:
184
+ typer.echo(f"Erro ao tentar deletar UserState: {e}", err=True)
185
+
186
+ def main():
187
+ app()
@@ -0,0 +1,57 @@
1
+ class ChatbotError(Exception):
2
+ """
3
+ Exceção personalizada para erros gerais relacionados ao chatbot.
4
+
5
+ Atributos:
6
+ message (str): A mensagem de erro descrevendo o problema.
7
+ """
8
+
9
+ def __init__(self, message: str):
10
+ """
11
+ Inicializa a exceção ChatbotError com uma mensagem de erro.
12
+
13
+ Args:
14
+ message (str): A mensagem de erro descrevendo o problema.
15
+ """
16
+ self.message = message
17
+ super().__init__(self.message)
18
+
19
+ def __str__(self):
20
+ """
21
+ Retorna a representação em string da exceção ChatbotError.
22
+
23
+ Returns:
24
+ str: Uma string formatada que inclui o nome da exceção e a mensagem de erro.
25
+ """
26
+ return f'ChatbotError: {self.message}'
27
+
28
+
29
+ class ChatbotMessageError(Exception):
30
+ """
31
+ Exceção personalizada para erros relacionados a mensagens de clientes no chatbot.
32
+
33
+ Atributos:
34
+ customer_id (str): O ID do cliente relacionado ao erro.
35
+ message (str): A mensagem de erro descrevendo o problema, incluindo o ID do cliente.
36
+ """
37
+
38
+ def __init__(self, customer_id: str, message: str):
39
+ """
40
+ Inicializa a exceção ChatbotMessageError com um ID de cliente e uma mensagem de erro.
41
+
42
+ Args:
43
+ customer_id (str): O ID do cliente relacionado ao erro.
44
+ message (str): A mensagem de erro descrevendo o problema.
45
+ """
46
+ self.customer_id = customer_id
47
+ self.message = f'{message} ID recebido: {customer_id}'
48
+ super().__init__(self.message)
49
+
50
+ def __str__(self):
51
+ """
52
+ Retorna a representação em string da exceção ChatbotMessageError.
53
+
54
+ Returns:
55
+ str: Uma string formatada que inclui o nome da exceção e a mensagem de erro.
56
+ """
57
+ return f'ChatbotMessageError: {self.message}'