chatgraph 0.4.2__py3-none-any.whl → 0.5.1__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.

Potentially problematic release.


This version of chatgraph might be problematic. Click here for more details.

chatgraph/__init__.py CHANGED
@@ -2,10 +2,11 @@ from .auth.credentials import Credential
2
2
  from .bot.chatbot_model import ChatbotApp
3
3
  from .bot.chatbot_router import ChatbotRouter
4
4
  from .messages.message_consumer import MessageConsumer
5
- from .types.request_types import UserCall, UserState
5
+ from .types.request_types import UserCall, UserState, ChatID
6
6
  from .types.end_types import RedirectResponse, EndChatResponse, TransferToHuman
7
- from .types.message_types import Message, Button, ListElements
7
+ from .types.message_types import Message, Button
8
8
  from .types.route import Route
9
+ from .types.background_task import BackgroundTask
9
10
 
10
11
  __all__ = [
11
12
  'ChatbotApp',
@@ -18,8 +19,9 @@ __all__ = [
18
19
  'Route',
19
20
  'EndChatResponse',
20
21
  'TransferToHuman',
22
+ 'ChatID',
21
23
  'UserState',
22
24
  'Message',
23
25
  'Button',
24
- 'ListElements'
26
+ 'BackgroundTask',
25
27
  ]
@@ -1,13 +1,12 @@
1
1
  import inspect
2
2
  from functools import wraps
3
- from logging import debug
4
- import json
5
- from logging import error
3
+ from logging import debug, error
4
+ import asyncio
6
5
 
7
- from ..error.chatbot_error import ChatbotError, ChatbotMessageError
6
+ from ..error.chatbot_error import ChatbotMessageError
8
7
  from ..messages.message_consumer import MessageConsumer
9
8
  from ..types.request_types import UserCall
10
- from ..types.message_types import messageTypes, Message, Button, ListElements
9
+ from ..types.message_types import Message, Button
11
10
  from ..types.end_types import RedirectResponse, EndChatResponse, TransferToHuman
12
11
  from ..types.route import Route
13
12
  from .chatbot_router import ChatbotRouter
@@ -27,20 +26,16 @@ class ChatbotApp:
27
26
  """
28
27
  if not message_consumer:
29
28
  message_consumer = MessageConsumer.load_dotenv()
30
-
29
+
31
30
  self.__message_consumer = message_consumer
32
31
  self.__routes = {}
33
-
32
+
34
33
  def include_router(self, router: ChatbotRouter):
35
34
  """
36
35
  Inclui um roteador de chatbot com um prefixo nas rotas da aplicação.
37
36
 
38
37
  Args:
39
38
  router (ChatbotRouter): O roteador contendo as rotas a serem adicionadas.
40
- prefix (str): O prefixo a ser adicionado às rotas do roteador.
41
-
42
- Raises:
43
- ChatbotError: Se a rota 'start' não for encontrada no roteador.
44
39
  """
45
40
  self.__routes.update(router.routes)
46
41
 
@@ -57,7 +52,7 @@ class ChatbotApp:
57
52
  route_name = route_name.strip().lower()
58
53
 
59
54
  def decorator(func):
60
- params = dict()
55
+ params = {}
61
56
  signature = inspect.signature(func)
62
57
  output_param = signature.return_annotation
63
58
 
@@ -65,20 +60,20 @@ class ChatbotApp:
65
60
  param_type = (
66
61
  param.annotation
67
62
  if param.annotation != inspect.Parameter.empty
68
- else 'Any'
63
+ else "Any"
69
64
  )
70
65
  params[param_type] = name
71
- debug(f'Parameter: {name}, Type: {param_type}')
66
+ debug(f"Parameter: {name}, Type: {param_type}")
72
67
 
73
68
  self.__routes[route_name] = {
74
- 'function': func,
75
- 'params': params,
76
- 'return': output_param,
69
+ "function": func,
70
+ "params": params,
71
+ "return": output_param,
77
72
  }
78
73
 
79
74
  @wraps(func)
80
- def wrapper(*args, **kwargs):
81
- return func(*args, **kwargs)
75
+ async def wrapper(*args, **kwargs):
76
+ return await func(*args, **kwargs)
82
77
 
83
78
  return wrapper
84
79
 
@@ -89,103 +84,102 @@ class ChatbotApp:
89
84
  Inicia o consumo de mensagens pelo chatbot, processando cada mensagem recebida.
90
85
  """
91
86
  self.__message_consumer.reprer()
92
- self.__message_consumer.start_consume(self.process_message)
93
-
94
- def process_message(self, userCall: UserCall):
87
+ asyncio.run(self.__message_consumer.start_consume(self.process_message))
88
+
89
+ async def process_message(self, userCall: UserCall):
95
90
  """
96
91
  Processa uma mensagem recebida, identificando a rota correspondente e executando a função associada.
97
92
 
98
93
  Args:
99
- userCall (Message): A mensagem a ser processada.
94
+ userCall (UserCall): A mensagem a ser processada.
100
95
 
101
96
  Raises:
102
97
  ChatbotMessageError: Se nenhuma rota for encontrada para o menu atual do usuário.
103
- ChatbotError: Se o tipo de retorno da função associada à rota for inválido.
104
-
105
- Returns:
106
- str: A resposta gerada pela função da rota, que pode ser uma mensagem ou o resultado de uma redireção.
107
98
  """
108
- customer_id = userCall.customer_id
99
+ user_id = userCall.user_id
109
100
  route = userCall.route.lower()
110
- route_handler = route.split('.')[-1]
111
- menu = userCall.menu.lower()
112
- obs = userCall.obs
101
+ route_handler = route.split(".")[-1]
102
+
113
103
  handler = self.__routes.get(route_handler, None)
114
104
 
115
105
  if not handler:
116
- raise ChatbotMessageError(
117
- customer_id, f'Rota não encontrada para {route}!'
118
- )
119
-
120
- func = handler['function']
121
- userCall_name = handler['params'].get(UserCall, None)
122
- route_state_name = handler['params'].get(Route, None)
106
+ raise ChatbotMessageError(user_id, f"Rota não encontrada para {route}!")
107
+
108
+ func = handler["function"]
109
+ userCall_name = handler["params"].get(UserCall, None)
110
+ route_state_name = handler["params"].get(Route, None)
123
111
 
124
- kwargs = dict()
112
+ kwargs = {}
125
113
  if userCall_name:
126
114
  kwargs[userCall_name] = userCall
127
115
  if route_state_name:
128
116
  kwargs[route_state_name] = Route(route, list(self.__routes.keys()))
129
117
 
130
- userCall_response = func(**kwargs)
131
-
118
+ if asyncio.iscoroutinefunction(func):
119
+ userCall_response = await func(**kwargs)
120
+ else:
121
+ loop = asyncio.get_running_loop()
122
+ userCall_response = await loop.run_in_executor(None, lambda: func(**kwargs))
123
+
132
124
  if isinstance(userCall_response, (list, tuple)):
133
125
  for response in userCall_response:
134
- self.__process_func_response(response, userCall, route=route)
126
+ await self.__process_func_response(response, userCall, route=route)
135
127
  else:
136
- self.__process_func_response(userCall_response, userCall, route=route)
128
+ await self.__process_func_response(userCall_response, userCall, route=route)
129
+
130
+ async def __process_func_response(
131
+ self, userCall_response, userCall: UserCall, route: str
132
+ ):
133
+ """
134
+ Processa a resposta de uma função associada a uma rota, enviando mensagens ou ajustando estados.
135
+
136
+ Args:
137
+ userCall_response: A resposta gerada pela função da rota.
138
+ userCall (UserCall): O objeto UserCall associado à mensagem processada.
139
+ route (str): O nome da rota atual.
140
+ """
141
+ loop = asyncio.get_running_loop()
137
142
 
138
- def __process_func_response(self, userCall_response, userCall: UserCall, route: str):
139
-
140
143
  if isinstance(userCall_response, (str, float, int)):
141
- userCall.send(Message(text=userCall_response))
144
+ # Envia o resultado como mensagem (executando a chamada síncrona no executor)
145
+ await loop.run_in_executor(None, userCall.send, Message(userCall_response))
142
146
  return
143
147
 
144
148
  elif isinstance(userCall_response, Route):
145
149
  userCall.route = userCall_response.current
146
150
  return
147
-
148
- elif isinstance(userCall_response, (Message, Button, ListElements)):
149
- userCall.send(userCall_response)
150
-
151
+
152
+ elif isinstance(userCall_response, (Message, Button)):
153
+ # Envia o objeto Message ou Button
154
+ await loop.run_in_executor(None, userCall.send, userCall_response)
151
155
  return
152
156
 
153
157
  elif isinstance(userCall_response, EndChatResponse):
154
- userCall.end_chat(userCall_response.observations, userCall_response.tabulation_id)
158
+ await loop.run_in_executor(
159
+ None,
160
+ userCall.end_chat,
161
+ userCall_response.observations,
162
+ userCall_response.tabulation_id,
163
+ )
155
164
  return
156
-
165
+
157
166
  elif isinstance(userCall_response, TransferToHuman):
158
- userCall.transfer_to_human(userCall_response.observations, userCall_response.campaign_id)
167
+ await loop.run_in_executor(
168
+ None,
169
+ userCall.transfer_to_human,
170
+ userCall_response.observations,
171
+ userCall_response.campaign_id,
172
+ )
159
173
  return
160
174
 
161
175
  elif isinstance(userCall_response, RedirectResponse):
162
- route = route + '.' + userCall_response.route
176
+ route = route + "." + userCall_response.route
163
177
  userCall.route = route
164
- return self.process_message(userCall)
178
+ await self.process_message(userCall)
165
179
 
166
180
  elif not userCall_response:
167
181
  return
168
182
 
169
183
  else:
170
- error('Tipo de retorno inválido!')
184
+ error("Tipo de retorno inválido!")
171
185
  return None
172
-
173
-
174
- def __adjust_route(self, route: str, absolute_route: str) -> str:
175
- """
176
- Ajusta a rota fornecida para incluir o prefixo necessário, se não estiver presente.
177
-
178
- Args:
179
- route (str): A rota que precisa ser ajustada.
180
- absolute_route (str): A rota completa atual, usada como referência.
181
-
182
- Returns:
183
- str: A rota ajustada.
184
- """
185
- if not route:
186
- return absolute_route
187
-
188
- if 'start' not in route:
189
- route = absolute_route + route
190
-
191
- return route
@@ -1,210 +1,137 @@
1
1
  import os
2
2
  import grpc
3
- import chatgraph.pb.userstate_pb2 as userstate_pb2
4
- import chatgraph.pb.userstate_pb2_grpc as userstate_pb2_grpc
3
+ import json
5
4
 
6
- import chatgraph.pb.voll_pb2 as whatsapp_pb2
7
- import chatgraph.pb.voll_pb2_grpc as whatsapp_pb2_grpc
5
+ import chatgraph.pb.router_pb2 as chatbot_pb2
6
+ import chatgraph.pb.router_pb2_grpc as chatbot_pb2_grpc
8
7
 
9
- class WhatsappServiceClient:
8
+
9
+ class RouterServiceClient:
10
10
  def __init__(self, grpc_uri=None):
11
-
12
- self.grpc_uri = grpc_uri
13
-
14
- if not grpc_uri:
15
- self.grpc_uri = os.getenv('GRPC_URI')
16
-
11
+ self.grpc_uri = grpc_uri or os.getenv("GRPC_URI")
12
+
17
13
  if not self.grpc_uri:
18
14
  raise ValueError("A variável de ambiente 'GRPC_URI' não está definida.")
19
-
15
+
20
16
  # Cria o canal gRPC
21
17
  self.channel = grpc.insecure_channel(self.grpc_uri)
22
-
23
- # Cria o stub (client) para o serviço gRPC
24
- self.stub = whatsapp_pb2_grpc.MessageServiceStub(self.channel)
25
- self.actions_stub = whatsapp_pb2_grpc.ActionsServiceStub(self.channel)
26
-
27
- def send_button(self, message_data):
28
- # Cria o request para o método SendButton
29
- request = whatsapp_pb2.MessageRequest(**message_data)
30
-
31
- # Faz a chamada ao serviço gRPC
32
- try:
33
- response = self.stub.SendButton(request)
34
- return response
35
- except grpc.RpcError as e:
36
- print(f"Erro ao fazer a requisição gRPC SendButton: {e}")
37
- return None
38
18
 
39
- def send_list(self, message_data):
40
- # Cria o request para o método SendList
41
- request = whatsapp_pb2.MessageRequest(**message_data)
19
+ # Cria os stubs para os serviços gRPC
20
+ self.user_state_stub = chatbot_pb2_grpc.UserStateServiceStub(self.channel)
21
+ self.send_message_stub = chatbot_pb2_grpc.SendMessageStub(self.channel)
22
+ self.transfer_stub = chatbot_pb2_grpc.TransferStub(self.channel)
23
+ self.end_chat_stub = chatbot_pb2_grpc.EndChatStub(self.channel)
42
24
 
43
- # Faz a chamada ao serviço gRPC
25
+ def insert_update_user_state(self, user_state_data):
26
+ request = chatbot_pb2.UserState(**user_state_data)
44
27
  try:
45
- response = self.stub.SendList(request)
28
+ response = self.user_state_stub.InsertUpdateUserState(request)
29
+ if not response.status:
30
+ print(f"Erro ao chamar SendMessage: {response.message}")
46
31
  return response
47
32
  except grpc.RpcError as e:
48
- print(f"Erro ao fazer a requisição gRPC SendList: {e}")
33
+ print(f"Erro ao chamar InsertUpdateUserState: {e}")
49
34
  return None
50
35
 
51
- def send_text(self, message_data):
52
- # Cria o request para o método SendText
53
- request = whatsapp_pb2.MessageRequest(**message_data)
54
-
55
- # Faz a chamada ao serviço gRPC
36
+ def delete_user_state(self, chat_id_data):
37
+ request = chatbot_pb2.ChatID(**chat_id_data)
56
38
  try:
57
- response = self.stub.SendText(request)
39
+ response = self.user_state_stub.DeleteUserState(request)
40
+ if not response.status:
41
+ print(f"Erro ao chamar SendMessage: {response.message}")
58
42
  return response
59
43
  except grpc.RpcError as e:
60
- print(f"Erro ao fazer a requisição gRPC SendText: {e}")
44
+ print(f"Erro ao chamar DeleteUserState: {e}")
61
45
  return None
62
-
63
- def transfer_to_human(self, message_data):
64
- # Cria o request para o método TransferToHuman
65
- request = whatsapp_pb2.MessageRequest(**message_data)
66
46
 
67
- # Faz a chamada ao serviço gRPC
47
+ def get_user_state(self, chat_id_data):
48
+ request = chatbot_pb2.ChatID(**chat_id_data)
68
49
  try:
69
- response = self.stub.TransferToHuman(request)
50
+ response = self.user_state_stub.GetUserState(request)
70
51
  return response
71
52
  except grpc.RpcError as e:
72
- print(f"Erro ao fazer a requisição gRPC TransferToHuman: {e}")
53
+ print(f"Erro ao chamar GetUserState: {e}")
73
54
  return None
74
-
75
- def end_chat(self, message_data):
76
- # Cria o request para o método EndChat
77
- request = whatsapp_pb2.MessageRequest(**message_data)
78
55
 
79
- # Faz a chamada ao serviço gRPC
80
- try:
81
- response = self.stub.EndChat(request)
82
- return response
83
- except grpc.RpcError as e:
84
- print(f"Erro ao fazer a requisição gRPC EndChat: {e}")
85
- return None
86
-
87
- def get_campaign_id(self, campaign_name):
88
- # Cria o request para o método GetCampaignID
89
- request = whatsapp_pb2.CampaignName(campaign_name=campaign_name)
90
-
91
- # Faz a chamada ao serviço gRPC
92
- try:
93
- response = self.actions_stub.GetCampaignID(request)
94
- return response
95
- except grpc.RpcError as e:
96
- print(f"Erro ao fazer a requisição gRPC GetCampaignID: {e}")
97
- return None
56
+ def send_message(self, message_data):
57
+ # print(json.dumps(message_data))
58
+
59
+ request = chatbot_pb2.Message(**message_data)
98
60
 
99
- def get_tabulation_id(self, tabulation_name):
100
- # Cria o request para o método GetTabulationID
101
- request = whatsapp_pb2.TabulationName(tabulation_name=tabulation_name)
102
-
103
- # Faz a chamada ao serviço gRPC
104
61
  try:
105
- response = self.actions_stub.GetTabulationID(request)
62
+ response = self.send_message_stub.SendMessage(request)
63
+ if not response.status:
64
+ print(f"Erro ao chamar SendMessage: {response.message}")
106
65
  return response
107
66
  except grpc.RpcError as e:
108
- print(f"Erro ao fazer a requisição gRPC GetTabulationID: {e}")
67
+ print(f"Erro ao chamar SendMessage: {e}")
109
68
  return None
110
69
 
111
- def get_all_campaigns(self):
112
- # Cria o request para o método GetAllCampaigns
113
- request = whatsapp_pb2.Void()
114
-
115
- # Faz a chamada ao serviço gRPC
70
+ def transfer_to_human(self, transfer_request_data):
71
+ request = chatbot_pb2.TransferToHumanRequest(**transfer_request_data)
116
72
  try:
117
- response = self.actions_stub.GetCampaignsList(request)
73
+ response = self.transfer_stub.TransferToHuman(request)
74
+ if not response.status:
75
+ print(f"Erro ao chamar SendMessage: {response.message}")
118
76
  return response
119
77
  except grpc.RpcError as e:
120
- print(f"Erro ao fazer a requisição gRPC GetAllCampaigns: {e}")
78
+ print(f"Erro ao chamar TransferToHuman: {e}")
121
79
  return None
122
-
123
- def get_all_tabulations(self):
124
- # Cria o request para o método GetAllTabulations
125
- request = whatsapp_pb2.Void()
126
80
 
127
- # Faz a chamada ao serviço gRPC
81
+ def transfer_to_menu(self, transfer_request_data):
82
+ request = chatbot_pb2.TransferToMenuRequest(**transfer_request_data)
128
83
  try:
129
- response = self.actions_stub.GetTabulationsList(request)
84
+ response = self.transfer_stub.TransferToMenu(request)
85
+ if not response.status:
86
+ print(f"Erro ao chamar SendMessage: {response.message}")
130
87
  return response
131
88
  except grpc.RpcError as e:
132
- print(f"Erro ao fazer a requisição gRPC GetAllTabulations: {e}")
89
+ print(f"Erro ao chamar TransferToMenu: {e}")
133
90
  return None
134
91
 
135
- class UserStateServiceClient:
136
- def __init__(self, grpc_uri=None):
137
-
138
- self.grpc_uri = grpc_uri
139
-
140
- if not grpc_uri:
141
- self.grpc_uri = os.getenv('GRPC_URI')
142
-
143
- if not self.grpc_uri:
144
- raise ValueError("A variável de ambiente 'GRPC_URI' não está definida.")
145
-
146
- # Cria o canal gRPC
147
- self.channel = grpc.insecure_channel(self.grpc_uri)
148
-
149
- # Cria o stub (client) para o serviço gRPC
150
- self.stub = userstate_pb2_grpc.UserStateServiceStub(self.channel)
151
-
152
- def select_user_state(self, user_id):
153
- # Cria o request para o método SelectUserState
154
- request = userstate_pb2.UserStateId(user_id=user_id)
155
-
156
- # Faz a chamada ao serviço gRPC
92
+ def end_chat(self, end_chat_request_data):
93
+ request = chatbot_pb2.EndChatRequest(**end_chat_request_data)
157
94
  try:
158
- response = self.stub.SelectUserState(request)
95
+ response = self.end_chat_stub.EndChat(request)
96
+ if not response.status:
97
+ print(f"Erro ao chamar SendMessage: {response.message}")
159
98
  return response
160
99
  except grpc.RpcError as e:
161
- print(f"Erro ao fazer a requisição gRPC SelectUserState: {e}")
100
+ print(f"Erro ao chamar EndChat: {e}")
162
101
  return None
163
102
 
164
- def insert_user_state(self, user_state_data):
165
- # Cria o request para o método InsertUserState
166
- request = userstate_pb2.UserState(**user_state_data)
167
-
168
- # Faz a chamada ao serviço gRPC
103
+ def get_campaign_id(self, campaign_name):
104
+ request = chatbot_pb2.CampaignName(**campaign_name)
169
105
  try:
170
- response = self.stub.InsertUserState(request)
106
+ response = self.transfer_stub.GetCampaignID(request)
171
107
  return response
172
108
  except grpc.RpcError as e:
173
- print(f"Erro ao fazer a requisição gRPC InsertUserState: {e}")
109
+ print(f"Erro ao chamar GetCampaignID: {e}")
174
110
  return None
175
111
 
176
- def update_user_state(self, user_state_data):
177
- # Cria o request para o método UpdateUserState
178
- request = userstate_pb2.UserState(**user_state_data)
179
-
180
- # Faz a chamada ao serviço gRPC
112
+ def get_all_campaigns(self):
113
+ request = chatbot_pb2.Void()
181
114
  try:
182
- response = self.stub.UpdateUserState(request)
115
+ response = self.transfer_stub.GetAllCampaigns(request)
183
116
  return response
184
117
  except grpc.RpcError as e:
185
- print(f"Erro ao fazer a requisição gRPC UpdateUserState: {e}")
118
+ print(f"Erro ao chamar GetAllCampaigns: {e}")
186
119
  return None
187
120
 
188
- def delete_user_state(self, user_id):
189
- # Cria o request para o método DeleteUserState
190
- request = userstate_pb2.UserStateId(user_id=user_id)
191
-
192
- # Faz a chamada ao serviço gRPC
121
+ def get_tabulation_id(self, tabulation_name):
122
+ request = chatbot_pb2.TabulationName(**tabulation_name)
193
123
  try:
194
- response = self.stub.DeleteUserState(request)
124
+ response = self.end_chat_stub.GetTabulationID(request)
195
125
  return response
196
126
  except grpc.RpcError as e:
197
- print(f"Erro ao fazer a requisição gRPC DeleteUserState: {e}")
127
+ print(f"Erro ao chamar GetTabulationID: {e}")
198
128
  return None
199
-
200
- def get_all_user_states(self):
201
- # Cria o request para o método GetAllUserStates
202
- request = userstate_pb2.Void()
203
129
 
204
- # Faz a chamada ao serviço gRPC
130
+ def get_all_tabulations(self):
131
+ request = chatbot_pb2.Void()
205
132
  try:
206
- response = self.stub.GetAllUserStates(request)
133
+ response = self.end_chat_stub.GetAllTabulations(request)
207
134
  return response
208
135
  except grpc.RpcError as e:
209
- print(f"Erro ao fazer a requisição gRPC GetAllUserStates: {e}")
210
- return None
136
+ print(f"Erro ao chamar GetAllTabulations: {e}")
137
+ return None