chatgraph 0.3.1__py3-none-any.whl → 0.3.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.

Potentially problematic release.


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

@@ -1,214 +1,108 @@
1
- from ..gRPC.gRPCCall import WhatsappServiceClient, UserStateServiceClient
1
+ from typing import Optional, Union
2
2
 
3
- from dataclasses import dataclass
4
- from typing import Optional
5
- import json
3
+ messageTypes = Union[str, float, int]
4
+ MessageTypes = (str, float, int)
6
5
 
7
- @dataclass
8
- class UserState:
6
+ class Message:
9
7
  """
10
- Representa o estado de um usuário.
8
+ Representa uma mensagem enviada ou recebida pelo chatbot.
11
9
 
12
10
  Atributos:
13
- customer_id (str): O ID do cliente.
14
- menu (str): O menu atual.
15
- lst_update (str): A última atualização do menu.
16
- obs (dict): Observações adicionais sobre o estado do usuário.
11
+ message (str): O conteúdo textual da mensagem.
12
+ absolute_text (bool): Se True, o texto não será modificado.
17
13
  """
18
- customer_id: str
19
- menu: str
20
- route: str
21
- lst_update: str
22
- obs: Optional[dict] = None
23
-
24
-
25
- @dataclass
26
- class Element:
27
- """
28
- Representa um elemento de uma lista de opções.
29
14
 
30
- Atributos:
31
- title (str): O título do elemento.
32
- description (str): A descrição do elemento.
33
- """
34
- title: str
35
- description: Optional[str] = None
36
-
37
- class UserCall:
38
- """
39
- Representa uma mensagem recebida ou enviada pelo chatbot.
40
-
41
- Atributos:
42
- type (str): O tipo da mensagem (por exemplo, texto, imagem, etc.).
43
- text (str): O conteúdo textual da mensagem.
44
- UserState (UserState): O estado do usuário.
45
- channel (str): O canal pelo qual a mensagem foi enviada ou recebida (por exemplo, WhatsApp, SMS, etc.).
46
- customer_phone (str): O número de telefone do cliente.
47
- company_phone (str): O número de telefone da empresa que está enviando ou recebendo a mensagem.
48
- status (Optional[str]): O status da mensagem (por exemplo, enviada, recebida, lida, etc.). Este campo é opcional.
49
- """
50
15
  def __init__(
51
- self,
52
- type: str,
53
- text: str,
54
- user_state: UserState,
55
- channel: str,
56
- customer_phone: str,
57
- company_phone: str,
58
- grpc_uri: str,
59
- status: Optional[str] = None,
60
- ) -> None:
61
-
62
- self.type = type
63
- self.text = text
64
- self.__user_state = user_state
65
- self.channel = channel
66
- self.customer_phone = customer_phone
67
- self.company_phone = company_phone
68
- self.status = status
69
-
70
- self.grpc_uri = grpc_uri
16
+ self,
17
+ text:str,
18
+ absolute_text: bool = False,
19
+ ) -> None:
20
+ self.absolute_text = absolute_text
71
21
 
72
- self.__wpp_server_client = WhatsappServiceClient(self.grpc_uri)
73
- self.__user_state_client = UserStateServiceClient(self.grpc_uri)
74
-
75
- def send_text(self, text:str, abs_text:bool=False) -> None:
76
- if not abs_text:
22
+ if not absolute_text:
77
23
  text = text.replace('\t', '')
78
-
79
- response = self.__wpp_server_client.send_text(
80
- {
81
- "hook_id": self.company_phone,
82
- "enterprise_id": self.customer_phone,
83
- "unique_customer_id": self.__user_state.customer_id,
84
- "message_text": text
85
- }
86
- )
24
+
25
+ self.text = text
87
26
 
88
- if not response.status:
89
- raise ValueError("Erro ao enviar mensagem de texto.")
27
+ class Button:
28
+ """
29
+ Representa uma lista rápida de botões.
90
30
 
91
- def send_button(
31
+ Atributos:
32
+ text (str): O texto da mensagem.
33
+ buttons (list[str]): A lista de botões.
34
+ title (str): O título da mensagem.
35
+ caption (str): A legenda da mensagem.
36
+ absolute_text (bool): Se True, o texto não será modificado.
37
+
38
+ Limitações:
39
+ O número máximo de botões é 3.
40
+ O texto do botão deve ter no máximo 20 caracteres.
41
+ """
42
+ def __init__(
92
43
  self,
93
44
  text:str,
94
- buttons:list,
95
- title: str|None = None,
96
- caption: str|None = None,
45
+ buttons: list[str],
46
+ title: Optional[str] = None,
47
+ caption: Optional[str] = None,
48
+ absolute_text: bool = False,
97
49
  ) -> None:
98
- if len(buttons) > 3:
99
- raise ValueError("O número máximo de botões é 3.")
50
+ self.absolute_text = absolute_text
100
51
 
101
- response = self.__wpp_server_client.send_button(
102
- {
103
- "hook_id": self.company_phone,
104
- "enterprise_id": self.customer_phone,
105
- "unique_customer_id": self.__user_state.customer_id,
106
- "message_text": text,
107
- "button_title": title,
108
- "message_caption": caption,
109
- "message_title": title,
110
- "options": [{"title": b} for b in buttons],
111
- }
112
- )
113
-
114
- if not response.status:
115
- raise ValueError("Erro ao enviar mensagem de botões.")
116
-
117
- def send_list(
118
- self,
119
- text:str,
120
- title: str|None = None,
121
- button_title: str|None = None,
122
- element_list: list[Element] = None,
123
- caption: str|None = None,
124
- ) -> None:
52
+ if not absolute_text:
53
+ text = text.replace('\t', '')
125
54
 
126
- if len(element_list) > 20:
127
- raise ValueError("O número máximo de elementos é 20.")
55
+ assert len(buttons) <= 3, "O número máximo de botões é 3."
128
56
 
129
- response = self.__wpp_server_client.send_list(
130
- {
131
- "hook_id": self.company_phone,
132
- "enterprise_id": self.customer_phone,
133
- "unique_customer_id": self.__user_state.customer_id,
134
- "message_text": text,
135
- "button_title": button_title,
136
- "message_caption": caption,
137
- "message_title": title,
138
- "options": [{"title": e.title, "description": e.description} for e in element_list],
139
- }
140
- )
57
+ for button in buttons:
58
+ assert len(button) <= 20, "O texto do botão deve ter no máximo 20 caracteres."
59
+
60
+ self.text = text
61
+ self.buttons = buttons
62
+ self.title = title
63
+ self.caption = caption
141
64
 
142
- if not response.status:
143
- raise ValueError("Erro ao enviar mensagem de lista.")
65
+ class ListElements:
66
+ """
67
+ Representa uma lista de elementos.
144
68
 
145
- def delete_user_state(self) -> None:
146
- response = self.__user_state_client.delete_user_state(self.__user_state.customer_id)
147
-
148
- if not response.status:
149
- raise ValueError("Erro ao deletar estado do usuário.")
150
-
151
- def update_user_state(
69
+ atributos:
70
+ text (str): O texto da mensagem.
71
+ title (str): O título da mensagem.
72
+ button_title (str): O título do botão.
73
+ elements (list[dict]): A lista de elementos.
74
+ caption (str): A legenda da mensagem.
75
+ absolute_text (bool): Se True, o texto não será modificado.
76
+
77
+ Limitações:
78
+ O número máximo de elementos é 10.
79
+ O título do elemento deve ter no máximo 24 caracteres.
80
+ A descrição do elemento deve ter no máximo 72 caracteres.
81
+ """
82
+ def __init__(
152
83
  self,
153
- menu: str,
154
- route: str,
155
- obs: dict,
84
+ text:str,
85
+ title: Optional[str] = None,
86
+ button_title: Optional[str] = None,
87
+ elements: list[dict] = None,
88
+ caption: Optional[str] = None,
89
+ absolute_text: bool = False,
156
90
  ) -> None:
91
+ self.absolute_text = absolute_text
157
92
 
158
- response = self.__user_state_client.update_user_state({
159
- "user_id": self.__user_state.customer_id,
160
- "menu_id": menu,
161
- "route": route,
162
- "obs": json.dumps(obs),
163
- })
164
-
165
- if not response.status:
166
- raise ValueError("Erro ao atualizar estado do usuário.")
167
-
168
- self.__user_state.menu = menu
169
- self.__user_state.route = route
170
- self.__user_state.obs = obs
171
-
172
- @property
173
- def menu(self):
174
- return self.__user_state.menu
175
-
176
- @property
177
- def route(self):
178
- return self.__user_state.route
179
-
180
- @property
181
- def obs(self):
182
- return self.__user_state.obs
183
-
184
- @property
185
- def customer_id(self):
186
- return self.__user_state.customer_id
187
-
188
- @menu.setter
189
- def menu(self, menu):
93
+ if not absolute_text:
94
+ text = text.replace('\t', '')
190
95
 
191
- self.update_user_state(
192
- menu,
193
- self.__user_state.route,
194
- self.__user_state.obs
195
- )
196
-
197
- @route.setter
198
- def route(self, route):
96
+ assert len(elements) <= 10, "O número máximo de elementos é 10."
199
97
 
200
- self.update_user_state(
201
- self.__user_state.menu,
202
- route,
203
- self.__user_state.obs
204
- )
205
-
206
- @obs.setter
207
- def obs(self, obs):
98
+ for key, value in elements.items():
99
+ assert len(key) <= 24, "O título do elemento deve ter no máximo 24 caracteres."
100
+ assert len(value) <= 72, "A descrição do elemento deve ter no máximo 72 caracteres."
208
101
 
209
- self.update_user_state(
210
- self.__user_state.menu,
211
- self.__user_state.route,
212
- obs
213
- )
214
-
102
+ self.text = text
103
+ self.title = title
104
+ self.button_title = button_title
105
+ self.elements = elements
106
+ self.caption = caption
107
+
108
+
@@ -0,0 +1,265 @@
1
+ from ..gRPC.gRPCCall import WhatsappServiceClient, UserStateServiceClient
2
+ from .message_types import Message, Button, ListElements, messageTypes, MessageTypes
3
+ from dataclasses import dataclass
4
+ from typing import Optional
5
+ import json
6
+
7
+ @dataclass
8
+ class UserState:
9
+ """
10
+ Representa o estado de um usuário.
11
+
12
+ Atributos:
13
+ customer_id (str): O ID do cliente.
14
+ menu (str): O menu atual.
15
+ lst_update (str): A última atualização do menu.
16
+ obs (dict): Observações adicionais sobre o estado do usuário.
17
+ """
18
+ customer_id: str
19
+ menu: str
20
+ route: str
21
+ lst_update: str
22
+ obs: Optional[dict] = None
23
+
24
+
25
+ class UserCall:
26
+ """
27
+ Representa uma mensagem recebida ou enviada pelo chatbot.
28
+
29
+ Atributos:
30
+ type (str): O tipo da mensagem (por exemplo, texto, imagem, etc.).
31
+ text (str): O conteúdo textual da mensagem.
32
+ UserState (UserState): O estado do usuário.
33
+ channel (str): O canal pelo qual a mensagem foi enviada ou recebida (por exemplo, WhatsApp, SMS, etc.).
34
+ customer_phone (str): O número de telefone do cliente.
35
+ company_phone (str): O número de telefone da empresa que está enviando ou recebendo a mensagem.
36
+ status (Optional[str]): O status da mensagem (por exemplo, enviada, recebida, lida, etc.). Este campo é opcional.
37
+ """
38
+ def __init__(
39
+ self,
40
+ type: str,
41
+ text: str,
42
+ user_state: UserState,
43
+ channel: str,
44
+ customer_phone: str,
45
+ company_phone: str,
46
+ grpc_uri: str,
47
+ status: Optional[str] = None,
48
+ ) -> None:
49
+
50
+ self.type = type
51
+ self.text = text
52
+ self.__user_state = user_state
53
+ self.channel = channel
54
+ self.customer_phone = customer_phone
55
+ self.company_phone = company_phone
56
+ self.status = status
57
+
58
+ self.grpc_uri = grpc_uri
59
+
60
+ self.__wpp_server_client = WhatsappServiceClient(self.grpc_uri)
61
+ self.__user_state_client = UserStateServiceClient(self.grpc_uri)
62
+
63
+ def send(self, message: messageTypes|Message|Button|ListElements) -> None:
64
+ """
65
+ Envia uma mensagem ao cliente.
66
+
67
+ Args:
68
+ message (Message|Button|ListElements): A mensagem a ser enviada.
69
+ """
70
+ if isinstance(message, MessageTypes):
71
+ message = Message(message)
72
+
73
+ if isinstance(message, Message):
74
+ self.__send_text(message.text, message.absolute_text)
75
+
76
+ elif isinstance(message, Button):
77
+ self.__send_button(
78
+ message.text,
79
+ message.buttons,
80
+ message.title,
81
+ message.caption
82
+ )
83
+
84
+ elif isinstance(message, ListElements):
85
+ self.__send_list(
86
+ message.text,
87
+ message.title,
88
+ message.button_title,
89
+ message.element_list,
90
+ message.caption
91
+ )
92
+ else:
93
+ raise ValueError("Tipo de mensagem inválido.")
94
+
95
+ def __send_text(self, text:str, abs_text:bool=False) -> None:
96
+ if not abs_text:
97
+ text = text.replace('\t', '')
98
+
99
+ response = self.__wpp_server_client.send_text(
100
+ {
101
+ "hook_id": self.company_phone,
102
+ "enterprise_id": self.customer_phone,
103
+ "unique_customer_id": self.__user_state.customer_id,
104
+ "message_text": text,
105
+ "platform": self.channel,
106
+ }
107
+ )
108
+
109
+ if not response.status:
110
+ raise ValueError("Erro ao enviar mensagem de texto.")
111
+
112
+ def __send_button(
113
+ self,
114
+ text:str,
115
+ buttons:list,
116
+ title: str|None = None,
117
+ caption: str|None = None,
118
+ ) -> None:
119
+ if len(buttons) > 3:
120
+ raise ValueError("O número máximo de botões é 3.")
121
+
122
+ response = self.__wpp_server_client.send_button(
123
+ {
124
+ "hook_id": self.company_phone,
125
+ "enterprise_id": self.customer_phone,
126
+ "unique_customer_id": self.__user_state.customer_id,
127
+ "message_text": text,
128
+ "button_title": title,
129
+ "message_caption": caption,
130
+ "message_title": title,
131
+ "options": [{"title": b} for b in buttons],
132
+ }
133
+ )
134
+
135
+ if not response.status:
136
+ raise ValueError("Erro ao enviar mensagem de botões.")
137
+
138
+ def __send_list(
139
+ self,
140
+ text:str,
141
+ title: str|None = None,
142
+ button_title: str|None = None,
143
+ element_list: list[dict] = None,
144
+ caption: str|None = None,
145
+ ) -> None:
146
+
147
+ if len(element_list) > 20:
148
+ raise ValueError("O número máximo de elementos é 20.")
149
+
150
+ response = self.__wpp_server_client.send_list(
151
+ {
152
+ "hook_id": self.company_phone,
153
+ "enterprise_id": self.customer_phone,
154
+ "unique_customer_id": self.__user_state.customer_id,
155
+ "message_text": text,
156
+ "button_title": button_title,
157
+ "message_caption": caption,
158
+ "message_title": title,
159
+ "options": [{"title": k, "description": v} for k,v in element_list],
160
+ }
161
+ )
162
+
163
+ if not response.status:
164
+ raise ValueError("Erro ao enviar mensagem de lista.")
165
+
166
+ def transfer_to_human(self, message:str, campaign_name:str) -> None:
167
+ response = self.__wpp_server_client.transfer_to_human(
168
+ {
169
+ "hook_id": self.company_phone,
170
+ "enterprise_id": self.customer_phone,
171
+ "unique_customer_id": self.__user_state.customer_id,
172
+ "message_text": message,
173
+ "platform": self.channel,
174
+ campaign_name: campaign_name,
175
+ }
176
+ )
177
+
178
+ if not response.status:
179
+ raise ValueError("Erro ao transferir chat para humano.")
180
+
181
+ def end_chat(self, message:str, campaign_name:str) -> None:
182
+ response = self.__wpp_server_client.end_chat(
183
+ {
184
+ "hook_id": self.company_phone,
185
+ "enterprise_id": self.customer_phone,
186
+ "unique_customer_id": self.__user_state.customer_id,
187
+ "message_text": message,
188
+ "platform": self.channel,
189
+ campaign_name: campaign_name,
190
+ }
191
+ )
192
+
193
+ if not response.status:
194
+ raise ValueError("Erro ao encerrar chat.")
195
+
196
+ def delete_user_state(self) -> None:
197
+ response = self.__user_state_client.delete_user_state(self.__user_state.customer_id)
198
+
199
+ if not response.status:
200
+ raise ValueError("Erro ao deletar estado do usuário.")
201
+
202
+ def update_user_state(
203
+ self,
204
+ menu: str,
205
+ route: str,
206
+ obs: dict,
207
+ ) -> None:
208
+
209
+ response = self.__user_state_client.update_user_state({
210
+ "user_id": self.__user_state.customer_id,
211
+ "menu_id": menu,
212
+ "route": route,
213
+ "obs": json.dumps(obs),
214
+ })
215
+
216
+ if not response.status:
217
+ raise ValueError("Erro ao atualizar estado do usuário.")
218
+
219
+ self.__user_state.menu = menu
220
+ self.__user_state.route = route
221
+ self.__user_state.obs = obs
222
+
223
+ @property
224
+ def menu(self):
225
+ return self.__user_state.menu
226
+
227
+ @property
228
+ def route(self):
229
+ return self.__user_state.route
230
+
231
+ @property
232
+ def obs(self):
233
+ return self.__user_state.obs
234
+
235
+ @property
236
+ def customer_id(self):
237
+ return self.__user_state.customer_id
238
+
239
+ @menu.setter
240
+ def menu(self, menu):
241
+
242
+ self.update_user_state(
243
+ menu,
244
+ self.__user_state.route,
245
+ self.__user_state.obs
246
+ )
247
+
248
+ @route.setter
249
+ def route(self, route):
250
+
251
+ self.update_user_state(
252
+ self.__user_state.menu,
253
+ route,
254
+ self.__user_state.obs
255
+ )
256
+
257
+ @obs.setter
258
+ def obs(self, obs):
259
+
260
+ self.update_user_state(
261
+ self.__user_state.menu,
262
+ self.__user_state.route,
263
+ obs
264
+ )
265
+
chatgraph/types/route.py CHANGED
@@ -46,7 +46,7 @@ class Route:
46
46
  previous_route = self.separator.join(self.current.split(self.separator)[:-1])
47
47
  return previous_route
48
48
 
49
- def get_next(self, next_part: str) -> str:
49
+ def get_next(self, next_part: str) -> 'Route':
50
50
  """
51
51
  Monta e retorna o próximo caminho com base na parte fornecida.
52
52
 
@@ -57,13 +57,14 @@ class Route:
57
57
  RouteError: Se a próxima rota montada não estiver na lista de rotas disponíveis.
58
58
 
59
59
  Returns:
60
- str: O próximo caminho construído a partir da rota atual e da parte fornecida.
60
+ Route: O próximo caminho montado.
61
61
  """
62
62
  next_part = next_part.strip().lower()
63
63
  next_route = f"{self.current.rstrip(self.separator)}{next_part}"
64
64
  if next_route not in self.routes:
65
65
  raise RouteError(f'Rota não encontrada: {next_route}')
66
- return next_route
66
+
67
+ return Route(next_route, self.routes, self.separator)
67
68
 
68
69
  def __str__(self):
69
70
  """